feat(rendering): add managed fullscreen stage planning seam

This commit is contained in:
2026-04-17 23:39:08 +08:00
parent 4a4e921eb1
commit 6838b00d97
16 changed files with 935 additions and 18 deletions

View File

@@ -10,6 +10,7 @@
#include "Debug/Logger.h"
#include "Input/InputManager.h"
#include "Physics/PhysicsWorld.h"
#include "Rendering/Execution/CameraFramePlan.h"
#include "Rendering/Passes/BuiltinColorScalePostProcessPass.h"
#include "Rendering/Pipelines/BuiltinForwardPipeline.h"
#include "Rendering/Pipelines/BuiltinForwardSceneRecorder.h"
@@ -96,6 +97,11 @@ struct ManagedScriptableRenderContextState {
std::vector<Math::Vector4> queuedBuiltinColorScaleFullscreenPasses = {};
};
struct ManagedScriptableRenderPipelinePlanningContextState {
uint64_t handle = 0;
Rendering::CameraFramePlan* plan = nullptr;
};
uint64_t& GetManagedScriptableRenderContextNextHandle() {
static uint64_t nextHandle = 1;
return nextHandle;
@@ -135,6 +141,49 @@ void UnregisterManagedScriptableRenderContextState(uint64_t handle) {
GetManagedScriptableRenderContextRegistry().erase(handle);
}
uint64_t& GetManagedScriptableRenderPipelinePlanningContextNextHandle() {
static uint64_t nextHandle = 1;
return nextHandle;
}
std::unordered_map<uint64_t, ManagedScriptableRenderPipelinePlanningContextState*>&
GetManagedScriptableRenderPipelinePlanningContextRegistry() {
static std::unordered_map<uint64_t, ManagedScriptableRenderPipelinePlanningContextState*>
registry;
return registry;
}
ManagedScriptableRenderPipelinePlanningContextState*
FindManagedScriptableRenderPipelinePlanningContextState(
uint64_t handle) {
const auto it =
GetManagedScriptableRenderPipelinePlanningContextRegistry().find(handle);
return it != GetManagedScriptableRenderPipelinePlanningContextRegistry().end()
? it->second
: nullptr;
}
uint64_t RegisterManagedScriptableRenderPipelinePlanningContextState(
ManagedScriptableRenderPipelinePlanningContextState& state) {
uint64_t handle =
GetManagedScriptableRenderPipelinePlanningContextNextHandle()++;
if (handle == 0) {
handle = GetManagedScriptableRenderPipelinePlanningContextNextHandle()++;
}
state.handle = handle;
GetManagedScriptableRenderPipelinePlanningContextRegistry()[handle] = &state;
return handle;
}
void UnregisterManagedScriptableRenderPipelinePlanningContextState(uint64_t handle) {
if (handle == 0) {
return;
}
GetManagedScriptableRenderPipelinePlanningContextRegistry().erase(handle);
}
void CleanupMonoRootDomainAtExit() {
MonoRootState& rootState = GetMonoRootState();
if (!rootState.rootDomain) {
@@ -522,9 +571,7 @@ public:
std::unique_ptr<Rendering::RenderPipelineStageRecorder> CreateStageRecorder(
const Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor& descriptor) const override {
if (m_runtime == nullptr ||
m_runtimeLifetime.expired() ||
!descriptor.IsValid()) {
if (!IsRuntimeAlive() || !descriptor.IsValid()) {
return nullptr;
}
@@ -534,7 +581,98 @@ public:
descriptor);
}
void ConfigureCameraFramePlan(
const Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor& descriptor,
Rendering::CameraFramePlan& plan) const override {
if (!IsRuntimeAlive() || !descriptor.IsValid()) {
return;
}
uint32_t assetHandle = 0;
MonoObject* const assetObject =
CreateManagedAssetInstance(descriptor, assetHandle);
if (assetObject == nullptr || assetHandle == 0) {
if (assetHandle != 0) {
m_runtime->DestroyExternalManagedObject(assetHandle);
}
return;
}
MonoMethod* const method =
m_runtime->ResolveManagedMethod(
assetObject,
"ConfigureCameraFramePlan",
1);
if (method == nullptr) {
m_runtime->DestroyExternalManagedObject(assetHandle);
return;
}
ManagedScriptableRenderPipelinePlanningContextState planningContextState = {};
planningContextState.plan = &plan;
const uint64_t planningContextHandle =
RegisterManagedScriptableRenderPipelinePlanningContextState(
planningContextState);
MonoObject* const planningContextObject =
m_runtime->CreateManagedScriptableRenderPipelinePlanningContext(
planningContextHandle);
if (planningContextObject == nullptr) {
UnregisterManagedScriptableRenderPipelinePlanningContextState(
planningContextHandle);
m_runtime->DestroyExternalManagedObject(assetHandle);
return;
}
void* args[1] = { planningContextObject };
m_runtime->InvokeManagedMethod(
assetObject,
method,
args,
nullptr);
UnregisterManagedScriptableRenderPipelinePlanningContextState(
planningContextHandle);
m_runtime->DestroyExternalManagedObject(assetHandle);
}
private:
bool IsRuntimeAlive() const {
return m_runtime != nullptr &&
!m_runtimeLifetime.expired() &&
m_runtime->m_initialized;
}
MonoObject* CreateManagedAssetInstance(
const Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor& descriptor,
uint32_t& outAssetHandle) const {
outAssetHandle = 0;
MonoClass* assetClass = nullptr;
if (!m_runtime->ResolveManagedClass(
descriptor.assemblyName,
descriptor.namespaceName,
descriptor.className,
assetClass) ||
assetClass == nullptr) {
return nullptr;
}
if (!IsMonoClassOrSubclass(
assetClass,
m_runtime->m_scriptableRenderPipelineAssetClass)) {
m_runtime->SetError(
"Managed render pipeline asset must derive from ScriptableRenderPipelineAsset: " +
descriptor.GetFullName() + ".");
return nullptr;
}
if (!m_runtime->CreateExternalManagedObject(assetClass, outAssetHandle) ||
outAssetHandle == 0) {
return nullptr;
}
return m_runtime->GetManagedObject(outAssetHandle);
}
MonoScriptRuntime* m_runtime = nullptr;
std::weak_ptr<void> m_runtimeLifetime;
};
@@ -2307,6 +2445,68 @@ InternalCall_Rendering_ScriptableRenderContext_RecordBuiltinColorScaleFullscreen
return 1;
}
void InternalCall_Rendering_ScriptableRenderPipelinePlanningContext_ClearPostProcessStage(
uint64_t nativeHandle) {
ManagedScriptableRenderPipelinePlanningContextState* const state =
FindManagedScriptableRenderPipelinePlanningContextState(nativeHandle);
if (state == nullptr || state->plan == nullptr) {
return;
}
state->plan->ClearFullscreenStage(Rendering::CameraFrameStage::PostProcess);
}
mono_bool
InternalCall_Rendering_ScriptableRenderPipelinePlanningContext_RequestPostProcessStage(
uint64_t nativeHandle,
int32_t source,
mono_bool usesGraphManagedOutputColor) {
ManagedScriptableRenderPipelinePlanningContextState* const state =
FindManagedScriptableRenderPipelinePlanningContextState(nativeHandle);
if (state == nullptr ||
state->plan == nullptr ||
source == static_cast<int32_t>(Rendering::CameraFrameColorSource::ExplicitSurface)) {
return 0;
}
return state->plan->RequestFullscreenStage(
Rendering::CameraFrameStage::PostProcess,
static_cast<Rendering::CameraFrameColorSource>(source),
usesGraphManagedOutputColor != 0)
? 1
: 0;
}
void InternalCall_Rendering_ScriptableRenderPipelinePlanningContext_ClearFinalOutputStage(
uint64_t nativeHandle) {
ManagedScriptableRenderPipelinePlanningContextState* const state =
FindManagedScriptableRenderPipelinePlanningContextState(nativeHandle);
if (state == nullptr || state->plan == nullptr) {
return;
}
state->plan->ClearFullscreenStage(Rendering::CameraFrameStage::FinalOutput);
}
mono_bool
InternalCall_Rendering_ScriptableRenderPipelinePlanningContext_RequestFinalOutputStage(
uint64_t nativeHandle,
int32_t source) {
ManagedScriptableRenderPipelinePlanningContextState* const state =
FindManagedScriptableRenderPipelinePlanningContextState(nativeHandle);
if (state == nullptr ||
state->plan == nullptr ||
source == static_cast<int32_t>(Rendering::CameraFrameColorSource::ExplicitSurface)) {
return 0;
}
return state->plan->RequestFullscreenStage(
Rendering::CameraFrameStage::FinalOutput,
static_cast<Rendering::CameraFrameColorSource>(source))
? 1
: 0;
}
void RegisterInternalCalls() {
if (GetInternalCallRegistrationState()) {
return;
@@ -2440,6 +2640,10 @@ void RegisterInternalCalls() {
mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderContext_RecordBuiltinForwardScenePhase", reinterpret_cast<const void*>(&InternalCall_Rendering_ScriptableRenderContext_RecordBuiltinForwardScenePhase));
mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderContext_RecordBuiltinForwardInjectionPoint", reinterpret_cast<const void*>(&InternalCall_Rendering_ScriptableRenderContext_RecordBuiltinForwardInjectionPoint));
mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderContext_RecordBuiltinColorScaleFullscreenPass", reinterpret_cast<const void*>(&InternalCall_Rendering_ScriptableRenderContext_RecordBuiltinColorScaleFullscreenPass));
mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderPipelinePlanningContext_ClearPostProcessStage", reinterpret_cast<const void*>(&InternalCall_Rendering_ScriptableRenderPipelinePlanningContext_ClearPostProcessStage));
mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderPipelinePlanningContext_RequestPostProcessStage", reinterpret_cast<const void*>(&InternalCall_Rendering_ScriptableRenderPipelinePlanningContext_RequestPostProcessStage));
mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderPipelinePlanningContext_ClearFinalOutputStage", reinterpret_cast<const void*>(&InternalCall_Rendering_ScriptableRenderPipelinePlanningContext_ClearFinalOutputStage));
mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderPipelinePlanningContext_RequestFinalOutputStage", reinterpret_cast<const void*>(&InternalCall_Rendering_ScriptableRenderPipelinePlanningContext_RequestFinalOutputStage));
GetInternalCallRegistrationState() = true;
}
@@ -2494,6 +2698,8 @@ void MonoScriptRuntime::Shutdown() {
Rendering::Pipelines::ClearManagedRenderPipelineBridge();
GetManagedScriptableRenderContextRegistry().clear();
GetManagedScriptableRenderContextNextHandle() = 1;
GetManagedScriptableRenderPipelinePlanningContextRegistry().clear();
GetManagedScriptableRenderPipelinePlanningContextNextHandle() = 1;
ClearManagedInstances();
ClearExternalManagedObjects();
ClearClassCache();
@@ -2509,9 +2715,11 @@ void MonoScriptRuntime::Shutdown() {
m_scriptableRenderPipelineAssetClass = nullptr;
m_scriptableRenderPipelineClass = nullptr;
m_scriptableRenderContextClass = nullptr;
m_scriptableRenderPipelinePlanningContextClass = nullptr;
m_serializeFieldAttributeClass = nullptr;
m_gameObjectConstructor = nullptr;
m_scriptableRenderContextConstructor = nullptr;
m_scriptableRenderPipelinePlanningContextConstructor = nullptr;
m_managedGameObjectUUIDField = nullptr;
m_gameObjectUUIDField = nullptr;
m_scriptComponentUUIDField = nullptr;
@@ -3143,6 +3351,27 @@ bool MonoScriptRuntime::DiscoverScriptClasses() {
return false;
}
m_scriptableRenderPipelinePlanningContextClass = mono_class_from_name(
m_coreImage,
m_settings.baseNamespace.c_str(),
"ScriptableRenderPipelinePlanningContext");
if (!m_scriptableRenderPipelinePlanningContextClass) {
SetError(
"Failed to locate the managed ScriptableRenderPipelinePlanningContext type.");
return false;
}
m_scriptableRenderPipelinePlanningContextConstructor =
mono_class_get_method_from_name(
m_scriptableRenderPipelinePlanningContextClass,
".ctor",
1);
if (!m_scriptableRenderPipelinePlanningContextConstructor) {
SetError(
"Failed to locate the managed ScriptableRenderPipelinePlanningContext constructor.");
return false;
}
m_serializeFieldAttributeClass = mono_class_from_name(
m_coreImage,
m_settings.baseNamespace.c_str(),
@@ -3594,6 +3823,45 @@ MonoObject* MonoScriptRuntime::CreateManagedScriptableRenderContext(
return contextObject;
}
MonoObject* MonoScriptRuntime::CreateManagedScriptableRenderPipelinePlanningContext(
uint64_t nativeHandle) {
if (!m_initialized ||
nativeHandle == 0 ||
m_scriptableRenderPipelinePlanningContextClass == nullptr ||
m_scriptableRenderPipelinePlanningContextConstructor == nullptr) {
return nullptr;
}
SetCurrentDomain();
MonoObject* const contextObject =
mono_object_new(
m_appDomain,
m_scriptableRenderPipelinePlanningContextClass);
if (contextObject == nullptr) {
SetError(
"Mono failed to allocate a managed ScriptableRenderPipelinePlanningContext.");
return nullptr;
}
void* args[1];
uint64_t nativeHandleArgument = nativeHandle;
args[0] = &nativeHandleArgument;
MonoObject* exception = nullptr;
mono_runtime_invoke(
m_scriptableRenderPipelinePlanningContextConstructor,
contextObject,
args,
&exception);
if (exception != nullptr) {
RecordException(exception);
return nullptr;
}
return contextObject;
}
MonoScriptRuntime::InstanceData* MonoScriptRuntime::FindInstance(const ScriptRuntimeContext& context) {
const auto it = m_instances.find(InstanceKey{context.gameObjectUUID, context.scriptComponentUUID});
return it != m_instances.end() ? &it->second : nullptr;