diff --git a/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h b/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h index 9dd177da..5bf549a5 100644 --- a/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h +++ b/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h @@ -2,13 +2,16 @@ #include +#include #include #include namespace XCEngine { namespace Rendering { +struct CameraRenderRequest; struct CameraFramePlan; +struct DirectionalShadowPlanningSettings; namespace Pipelines { @@ -38,6 +41,12 @@ public: } std::unique_ptr CreatePipeline() const override; + void ConfigureCameraRenderRequest( + CameraRenderRequest& request, + size_t renderedBaseCameraCount, + size_t renderedRequestCount, + const DirectionalShadowPlanningSettings& directionalShadowSettings) + const override; FinalColorSettings GetDefaultFinalColorSettings() const override; void ConfigureCameraFramePlan(CameraFramePlan& plan) const override; @@ -59,6 +68,13 @@ public: return nullptr; } + virtual void ConfigureCameraRenderRequest( + CameraRenderRequest&, + size_t, + size_t, + const DirectionalShadowPlanningSettings&) const { + } + virtual void ConfigureCameraFramePlan(CameraFramePlan&) const { } }; diff --git a/engine/include/XCEngine/Scripting/Mono/MonoScriptRuntime.h b/engine/include/XCEngine/Scripting/Mono/MonoScriptRuntime.h index 0e725e45..1871405b 100644 --- a/engine/include/XCEngine/Scripting/Mono/MonoScriptRuntime.h +++ b/engine/include/XCEngine/Scripting/Mono/MonoScriptRuntime.h @@ -222,6 +222,8 @@ private: uint32_t RetainExternalManagedObject(MonoObject* instance); void DestroyExternalManagedObject(uint32_t gcHandle); MonoObject* CreateManagedScriptableRenderContext(uint64_t nativeHandle); + MonoObject* CreateManagedScriptableRenderPipelineCameraRequestContext( + uint64_t nativeHandle); MonoObject* CreateManagedScriptableRenderPipelinePlanningContext( uint64_t nativeHandle); MonoObject* GetManagedObject(uint32_t gcHandle) const; @@ -263,10 +265,13 @@ private: MonoClass* m_scriptableRenderPipelineAssetClass = nullptr; MonoClass* m_scriptableRenderPipelineClass = nullptr; MonoClass* m_scriptableRenderContextClass = nullptr; + MonoClass* m_scriptableRenderPipelineCameraRequestContextClass = nullptr; MonoClass* m_scriptableRenderPipelinePlanningContextClass = nullptr; MonoClass* m_serializeFieldAttributeClass = nullptr; MonoMethod* m_gameObjectConstructor = nullptr; MonoMethod* m_scriptableRenderContextConstructor = nullptr; + MonoMethod* m_scriptableRenderPipelineCameraRequestContextConstructor = + nullptr; MonoMethod* m_scriptableRenderPipelinePlanningContextConstructor = nullptr; MonoClassField* m_managedGameObjectUUIDField = nullptr; MonoClassField* m_gameObjectUUIDField = nullptr; diff --git a/engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp b/engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp index a2e2f468..a15735c6 100644 --- a/engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp +++ b/engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp @@ -57,6 +57,28 @@ std::unique_ptr ManagedScriptableRenderPipelineAsset::CreatePipe return pipeline; } +void ManagedScriptableRenderPipelineAsset::ConfigureCameraRenderRequest( + CameraRenderRequest& request, + size_t renderedBaseCameraCount, + size_t renderedRequestCount, + const DirectionalShadowPlanningSettings& directionalShadowSettings) const { + RenderPipelineAsset::ConfigureCameraRenderRequest( + request, + renderedBaseCameraCount, + renderedRequestCount, + directionalShadowSettings); + + if (const std::shared_ptr runtime = + ResolveManagedAssetRuntime(); + runtime != nullptr) { + runtime->ConfigureCameraRenderRequest( + request, + renderedBaseCameraCount, + renderedRequestCount, + directionalShadowSettings); + } +} + FinalColorSettings ManagedScriptableRenderPipelineAsset::GetDefaultFinalColorSettings() const { return m_fallbackAsset.GetDefaultFinalColorSettings(); } diff --git a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp index 75820136..b5de0447 100644 --- a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp +++ b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp @@ -97,6 +97,13 @@ struct ManagedScriptableRenderContextState { std::vector queuedBuiltinColorScaleFullscreenPasses = {}; }; +struct ManagedScriptableRenderPipelineCameraRequestContextState { + uint64_t handle = 0; + Rendering::CameraRenderRequest* request = nullptr; + size_t renderedBaseCameraCount = 0u; + size_t renderedRequestCount = 0u; +}; + struct ManagedScriptableRenderPipelinePlanningContextState { uint64_t handle = 0; Rendering::CameraFramePlan* plan = nullptr; @@ -141,6 +148,52 @@ void UnregisterManagedScriptableRenderContextState(uint64_t handle) { GetManagedScriptableRenderContextRegistry().erase(handle); } +uint64_t& GetManagedScriptableRenderPipelineCameraRequestContextNextHandle() { + static uint64_t nextHandle = 1; + return nextHandle; +} + +std::unordered_map& +GetManagedScriptableRenderPipelineCameraRequestContextRegistry() { + static std::unordered_map + registry; + return registry; +} + +ManagedScriptableRenderPipelineCameraRequestContextState* +FindManagedScriptableRenderPipelineCameraRequestContextState( + uint64_t handle) { + const auto it = + GetManagedScriptableRenderPipelineCameraRequestContextRegistry().find(handle); + return it != GetManagedScriptableRenderPipelineCameraRequestContextRegistry().end() + ? it->second + : nullptr; +} + +uint64_t RegisterManagedScriptableRenderPipelineCameraRequestContextState( + ManagedScriptableRenderPipelineCameraRequestContextState& state) { + uint64_t handle = + GetManagedScriptableRenderPipelineCameraRequestContextNextHandle()++; + if (handle == 0) { + handle = + GetManagedScriptableRenderPipelineCameraRequestContextNextHandle()++; + } + + state.handle = handle; + GetManagedScriptableRenderPipelineCameraRequestContextRegistry()[handle] = + &state; + return handle; +} + +void UnregisterManagedScriptableRenderPipelineCameraRequestContextState( + uint64_t handle) { + if (handle == 0) { + return; + } + + GetManagedScriptableRenderPipelineCameraRequestContextRegistry().erase(handle); +} + uint64_t& GetManagedScriptableRenderPipelinePlanningContextNextHandle() { static uint64_t nextHandle = 1; return nextHandle; @@ -291,6 +344,13 @@ public: std::unique_ptr CreateStageRecorder() const override; + void ConfigureCameraRenderRequest( + Rendering::CameraRenderRequest& request, + size_t renderedBaseCameraCount, + size_t renderedRequestCount, + const Rendering::DirectionalShadowPlanningSettings& + directionalShadowSettings) const override; + void ConfigureCameraFramePlan( Rendering::CameraFramePlan& plan) const override; @@ -311,6 +371,8 @@ private: void ReleaseManagedAsset() const; MonoObject* GetManagedAssetObject() const; MonoMethod* ResolveCreatePipelineMethod(MonoObject* assetObject) const; + MonoMethod* ResolveConfigureCameraRenderRequestMethod( + MonoObject* assetObject) const; MonoMethod* ResolveConfigureCameraFramePlanMethod( MonoObject* assetObject) const; @@ -319,6 +381,7 @@ private: Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor m_descriptor; mutable uint32_t m_assetHandle = 0; mutable MonoMethod* m_createPipelineMethod = nullptr; + mutable MonoMethod* m_configureCameraRenderRequestMethod = nullptr; mutable MonoMethod* m_configureCameraFramePlanMethod = nullptr; mutable bool m_assetCreationAttempted = false; }; @@ -550,6 +613,49 @@ MonoManagedRenderPipelineAssetRuntime::CreateStageRecorder() const { shared_from_this()); } +void MonoManagedRenderPipelineAssetRuntime::ConfigureCameraRenderRequest( + Rendering::CameraRenderRequest& request, + size_t renderedBaseCameraCount, + size_t renderedRequestCount, + const Rendering::DirectionalShadowPlanningSettings&) const { + if (!EnsureManagedAsset()) { + return; + } + + MonoObject* const assetObject = GetManagedAssetObject(); + MonoMethod* const method = + ResolveConfigureCameraRenderRequestMethod(assetObject); + if (assetObject == nullptr || method == nullptr) { + return; + } + + ManagedScriptableRenderPipelineCameraRequestContextState requestContextState = + {}; + requestContextState.request = &request; + requestContextState.renderedBaseCameraCount = renderedBaseCameraCount; + requestContextState.renderedRequestCount = renderedRequestCount; + const uint64_t requestContextHandle = + RegisterManagedScriptableRenderPipelineCameraRequestContextState( + requestContextState); + MonoObject* const requestContextObject = + m_runtime->CreateManagedScriptableRenderPipelineCameraRequestContext( + requestContextHandle); + if (requestContextObject == nullptr) { + UnregisterManagedScriptableRenderPipelineCameraRequestContextState( + requestContextHandle); + return; + } + + void* args[1] = { requestContextObject }; + m_runtime->InvokeManagedMethod( + assetObject, + method, + args, + nullptr); + UnregisterManagedScriptableRenderPipelineCameraRequestContextState( + requestContextHandle); +} + void MonoManagedRenderPipelineAssetRuntime::ConfigureCameraFramePlan( Rendering::CameraFramePlan& plan) const { if (!EnsureManagedAsset()) { @@ -659,6 +765,7 @@ bool MonoManagedRenderPipelineAssetRuntime::EnsureManagedAsset() const { void MonoManagedRenderPipelineAssetRuntime::ReleaseManagedAsset() const { m_createPipelineMethod = nullptr; + m_configureCameraRenderRequestMethod = nullptr; m_configureCameraFramePlanMethod = nullptr; m_assetCreationAttempted = false; @@ -692,6 +799,20 @@ MonoMethod* MonoManagedRenderPipelineAssetRuntime::ResolveCreatePipelineMethod( return m_createPipelineMethod; } +MonoMethod* +MonoManagedRenderPipelineAssetRuntime::ResolveConfigureCameraRenderRequestMethod( + MonoObject* assetObject) const { + if (m_configureCameraRenderRequestMethod == nullptr) { + m_configureCameraRenderRequestMethod = + m_runtime->ResolveManagedMethod( + assetObject, + "ConfigureCameraRenderRequest", + 1); + } + + return m_configureCameraRenderRequestMethod; +} + MonoMethod* MonoManagedRenderPipelineAssetRuntime::ResolveConfigureCameraFramePlanMethod( MonoObject* assetObject) const { @@ -2509,6 +2630,49 @@ InternalCall_Rendering_ScriptableRenderContext_RecordBuiltinColorScaleFullscreen return 1; } +int32_t +InternalCall_Rendering_ScriptableRenderPipelineCameraRequestContext_GetRenderedBaseCameraCount( + uint64_t nativeHandle) { + const ManagedScriptableRenderPipelineCameraRequestContextState* const state = + FindManagedScriptableRenderPipelineCameraRequestContextState(nativeHandle); + return state != nullptr + ? static_cast(state->renderedBaseCameraCount) + : 0; +} + +int32_t +InternalCall_Rendering_ScriptableRenderPipelineCameraRequestContext_GetRenderedRequestCount( + uint64_t nativeHandle) { + const ManagedScriptableRenderPipelineCameraRequestContextState* const state = + FindManagedScriptableRenderPipelineCameraRequestContextState(nativeHandle); + return state != nullptr + ? static_cast(state->renderedRequestCount) + : 0; +} + +mono_bool +InternalCall_Rendering_ScriptableRenderPipelineCameraRequestContext_GetHasDirectionalShadow( + uint64_t nativeHandle) { + const ManagedScriptableRenderPipelineCameraRequestContextState* const state = + FindManagedScriptableRenderPipelineCameraRequestContextState(nativeHandle); + return state != nullptr && + state->request != nullptr && + state->request->directionalShadow.IsValid() + ? 1 + : 0; +} + +void InternalCall_Rendering_ScriptableRenderPipelineCameraRequestContext_ClearDirectionalShadow( + uint64_t nativeHandle) { + ManagedScriptableRenderPipelineCameraRequestContextState* const state = + FindManagedScriptableRenderPipelineCameraRequestContextState(nativeHandle); + if (state == nullptr || state->request == nullptr) { + return; + } + + state->request->directionalShadow = {}; +} + void InternalCall_Rendering_ScriptableRenderPipelinePlanningContext_ClearPostProcessStage( uint64_t nativeHandle) { ManagedScriptableRenderPipelinePlanningContextState* const state = @@ -2704,6 +2868,10 @@ void RegisterInternalCalls() { mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderContext_RecordBuiltinForwardScenePhase", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderContext_RecordBuiltinForwardScenePhase)); mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderContext_RecordBuiltinForwardInjectionPoint", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderContext_RecordBuiltinForwardInjectionPoint)); mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderContext_RecordBuiltinColorScaleFullscreenPass", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderContext_RecordBuiltinColorScaleFullscreenPass)); + mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderPipelineCameraRequestContext_GetRenderedBaseCameraCount", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderPipelineCameraRequestContext_GetRenderedBaseCameraCount)); + mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderPipelineCameraRequestContext_GetRenderedRequestCount", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderPipelineCameraRequestContext_GetRenderedRequestCount)); + mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderPipelineCameraRequestContext_GetHasDirectionalShadow", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderPipelineCameraRequestContext_GetHasDirectionalShadow)); + mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderPipelineCameraRequestContext_ClearDirectionalShadow", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderPipelineCameraRequestContext_ClearDirectionalShadow)); mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderPipelinePlanningContext_ClearPostProcessStage", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderPipelinePlanningContext_ClearPostProcessStage)); mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderPipelinePlanningContext_RequestPostProcessStage", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderPipelinePlanningContext_RequestPostProcessStage)); mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderPipelinePlanningContext_ClearFinalOutputStage", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderPipelinePlanningContext_ClearFinalOutputStage)); @@ -2762,6 +2930,8 @@ void MonoScriptRuntime::Shutdown() { Rendering::Pipelines::ClearManagedRenderPipelineBridge(); GetManagedScriptableRenderContextRegistry().clear(); GetManagedScriptableRenderContextNextHandle() = 1; + GetManagedScriptableRenderPipelineCameraRequestContextRegistry().clear(); + GetManagedScriptableRenderPipelineCameraRequestContextNextHandle() = 1; GetManagedScriptableRenderPipelinePlanningContextRegistry().clear(); GetManagedScriptableRenderPipelinePlanningContextNextHandle() = 1; ClearManagedInstances(); @@ -2779,10 +2949,12 @@ void MonoScriptRuntime::Shutdown() { m_scriptableRenderPipelineAssetClass = nullptr; m_scriptableRenderPipelineClass = nullptr; m_scriptableRenderContextClass = nullptr; + m_scriptableRenderPipelineCameraRequestContextClass = nullptr; m_scriptableRenderPipelinePlanningContextClass = nullptr; m_serializeFieldAttributeClass = nullptr; m_gameObjectConstructor = nullptr; m_scriptableRenderContextConstructor = nullptr; + m_scriptableRenderPipelineCameraRequestContextConstructor = nullptr; m_scriptableRenderPipelinePlanningContextConstructor = nullptr; m_managedGameObjectUUIDField = nullptr; m_gameObjectUUIDField = nullptr; @@ -3415,6 +3587,27 @@ bool MonoScriptRuntime::DiscoverScriptClasses() { return false; } + m_scriptableRenderPipelineCameraRequestContextClass = mono_class_from_name( + m_coreImage, + m_settings.baseNamespace.c_str(), + "ScriptableRenderPipelineCameraRequestContext"); + if (!m_scriptableRenderPipelineCameraRequestContextClass) { + SetError( + "Failed to locate the managed ScriptableRenderPipelineCameraRequestContext type."); + return false; + } + + m_scriptableRenderPipelineCameraRequestContextConstructor = + mono_class_get_method_from_name( + m_scriptableRenderPipelineCameraRequestContextClass, + ".ctor", + 1); + if (!m_scriptableRenderPipelineCameraRequestContextConstructor) { + SetError( + "Failed to locate the managed ScriptableRenderPipelineCameraRequestContext constructor."); + return false; + } + m_scriptableRenderPipelinePlanningContextClass = mono_class_from_name( m_coreImage, m_settings.baseNamespace.c_str(), @@ -3887,6 +4080,46 @@ MonoObject* MonoScriptRuntime::CreateManagedScriptableRenderContext( return contextObject; } +MonoObject* +MonoScriptRuntime::CreateManagedScriptableRenderPipelineCameraRequestContext( + uint64_t nativeHandle) { + if (!m_initialized || + nativeHandle == 0 || + m_scriptableRenderPipelineCameraRequestContextClass == nullptr || + m_scriptableRenderPipelineCameraRequestContextConstructor == nullptr) { + return nullptr; + } + + SetCurrentDomain(); + + MonoObject* const contextObject = + mono_object_new( + m_appDomain, + m_scriptableRenderPipelineCameraRequestContextClass); + if (contextObject == nullptr) { + SetError( + "Mono failed to allocate a managed ScriptableRenderPipelineCameraRequestContext."); + return nullptr; + } + + void* args[1]; + uint64_t nativeHandleArgument = nativeHandle; + args[0] = &nativeHandleArgument; + + MonoObject* exception = nullptr; + mono_runtime_invoke( + m_scriptableRenderPipelineCameraRequestContextConstructor, + contextObject, + args, + &exception); + if (exception != nullptr) { + RecordException(exception); + return nullptr; + } + + return contextObject; +} + MonoObject* MonoScriptRuntime::CreateManagedScriptableRenderPipelinePlanningContext( uint64_t nativeHandle) { if (!m_initialized || diff --git a/managed/CMakeLists.txt b/managed/CMakeLists.txt index 3fce933f..ea387736 100644 --- a/managed/CMakeLists.txt +++ b/managed/CMakeLists.txt @@ -125,6 +125,7 @@ set(XCENGINE_SCRIPT_CORE_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/SceneRenderInjectionPoint.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/ScriptableRenderPipeline.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/ScriptableRenderPipelineAsset.cs + ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/ScriptableRenderPipelineCameraRequestContext.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/ScriptableRenderPipelinePlanningContext.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/ScriptableRenderContext.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rigidbody.cs diff --git a/managed/GameScripts/RenderPipelineApiProbe.cs b/managed/GameScripts/RenderPipelineApiProbe.cs index 2c6f5f22..1f1fe612 100644 --- a/managed/GameScripts/RenderPipelineApiProbe.cs +++ b/managed/GameScripts/RenderPipelineApiProbe.cs @@ -50,6 +50,24 @@ namespace Gameplay } } + public sealed class ManagedCameraRequestConfiguredRenderPipelineProbeAsset + : ScriptableRenderPipelineAsset + { + protected override ScriptableRenderPipeline CreatePipeline() + { + return new ManagedRenderPipelineProbe(); + } + + protected override void ConfigureCameraRenderRequest( + ScriptableRenderPipelineCameraRequestContext context) + { + if (context != null && context.hasDirectionalShadow) + { + context.ClearDirectionalShadow(); + } + } + } + public sealed class ManagedRenderPipelineProbe : ScriptableRenderPipeline { public static int SupportsStageCallCount; diff --git a/managed/XCEngine.ScriptCore/InternalCalls.cs b/managed/XCEngine.ScriptCore/InternalCalls.cs index 4bbf645e..7c1b9637 100644 --- a/managed/XCEngine.ScriptCore/InternalCalls.cs +++ b/managed/XCEngine.ScriptCore/InternalCalls.cs @@ -409,6 +409,26 @@ namespace XCEngine ulong nativeHandle, ref Vector4 colorScale); + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern int + Rendering_ScriptableRenderPipelineCameraRequestContext_GetRenderedBaseCameraCount( + ulong nativeHandle); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern int + Rendering_ScriptableRenderPipelineCameraRequestContext_GetRenderedRequestCount( + ulong nativeHandle); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern bool + Rendering_ScriptableRenderPipelineCameraRequestContext_GetHasDirectionalShadow( + ulong nativeHandle); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern void + Rendering_ScriptableRenderPipelineCameraRequestContext_ClearDirectionalShadow( + ulong nativeHandle); + [MethodImpl(MethodImplOptions.InternalCall)] internal static extern void Rendering_ScriptableRenderPipelinePlanningContext_ClearPostProcessStage( diff --git a/managed/XCEngine.ScriptCore/ScriptableRenderPipelineAsset.cs b/managed/XCEngine.ScriptCore/ScriptableRenderPipelineAsset.cs index 7ea4da94..068ca54c 100644 --- a/managed/XCEngine.ScriptCore/ScriptableRenderPipelineAsset.cs +++ b/managed/XCEngine.ScriptCore/ScriptableRenderPipelineAsset.cs @@ -11,6 +11,11 @@ namespace XCEngine return null; } + protected internal virtual void ConfigureCameraRenderRequest( + ScriptableRenderPipelineCameraRequestContext context) + { + } + protected internal virtual void ConfigureCameraFramePlan( ScriptableRenderPipelinePlanningContext context) { diff --git a/managed/XCEngine.ScriptCore/ScriptableRenderPipelineCameraRequestContext.cs b/managed/XCEngine.ScriptCore/ScriptableRenderPipelineCameraRequestContext.cs new file mode 100644 index 00000000..23068402 --- /dev/null +++ b/managed/XCEngine.ScriptCore/ScriptableRenderPipelineCameraRequestContext.cs @@ -0,0 +1,34 @@ +namespace XCEngine +{ + public sealed class ScriptableRenderPipelineCameraRequestContext + { + private readonly ulong m_nativeHandle; + + internal ScriptableRenderPipelineCameraRequestContext(ulong nativeHandle) + { + m_nativeHandle = nativeHandle; + } + + public int renderedBaseCameraCount => + InternalCalls + .Rendering_ScriptableRenderPipelineCameraRequestContext_GetRenderedBaseCameraCount( + m_nativeHandle); + + public int renderedRequestCount => + InternalCalls + .Rendering_ScriptableRenderPipelineCameraRequestContext_GetRenderedRequestCount( + m_nativeHandle); + + public bool hasDirectionalShadow => + InternalCalls + .Rendering_ScriptableRenderPipelineCameraRequestContext_GetHasDirectionalShadow( + m_nativeHandle); + + public void ClearDirectionalShadow() + { + InternalCalls + .Rendering_ScriptableRenderPipelineCameraRequestContext_ClearDirectionalShadow( + m_nativeHandle); + } + } +} diff --git a/tests/Rendering/unit/test_camera_scene_renderer.cpp b/tests/Rendering/unit/test_camera_scene_renderer.cpp index 7501c326..410c7033 100644 --- a/tests/Rendering/unit/test_camera_scene_renderer.cpp +++ b/tests/Rendering/unit/test_camera_scene_renderer.cpp @@ -774,8 +774,16 @@ private: struct MockManagedRenderPipelineAssetRuntimeState { int createStageRecorderCalls = 0; + int configureCameraRenderRequestCalls = 0; int configureCameraFramePlanCalls = 0; + size_t lastRenderedBaseCameraCount = 0u; + size_t lastRenderedRequestCount = 0u; std::shared_ptr lastCreatedStageRecorderState; + std::function configureCameraRenderRequest = {}; std::function configureCameraFramePlan = {}; }; @@ -795,6 +803,24 @@ public: m_state->lastCreatedStageRecorderState); } + void ConfigureCameraRenderRequest( + CameraRenderRequest& request, + size_t renderedBaseCameraCount, + size_t renderedRequestCount, + const DirectionalShadowPlanningSettings& directionalShadowSettings) + const override { + ++m_state->configureCameraRenderRequestCalls; + m_state->lastRenderedBaseCameraCount = renderedBaseCameraCount; + m_state->lastRenderedRequestCount = renderedRequestCount; + if (m_state->configureCameraRenderRequest) { + m_state->configureCameraRenderRequest( + request, + renderedBaseCameraCount, + renderedRequestCount, + directionalShadowSettings); + } + } + void ConfigureCameraFramePlan(CameraFramePlan& plan) const override { ++m_state->configureCameraFramePlanCalls; if (m_state->configureCameraFramePlan) { @@ -810,6 +836,11 @@ struct MockManagedRenderPipelineBridgeState { int createAssetRuntimeCalls = 0; Pipelines::ManagedRenderPipelineAssetDescriptor lastDescriptor = {}; std::shared_ptr lastCreatedRuntimeState; + std::function configureCameraRenderRequest = {}; std::function configureCameraFramePlan = {}; }; @@ -828,6 +859,8 @@ public: m_state->lastDescriptor = descriptor; m_state->lastCreatedRuntimeState = std::make_shared(); + m_state->lastCreatedRuntimeState->configureCameraRenderRequest = + m_state->configureCameraRenderRequest; m_state->lastCreatedRuntimeState->configureCameraFramePlan = m_state->configureCameraFramePlan; return std::make_shared( @@ -4587,7 +4620,77 @@ TEST(ManagedScriptableRenderPipelineAsset_Test, LetsManagedBridgeRequestFullscre Pipelines::ClearManagedRenderPipelineBridge(); } -TEST(ManagedScriptableRenderPipelineAsset_Test, ReusesManagedAssetRuntimeAcrossPipelineAndPlanRequests) { +TEST(ManagedScriptableRenderPipelineAsset_Test, LetsManagedBridgeConfigureCameraRenderRequests) { + Pipelines::ClearManagedRenderPipelineBridge(); + + Scene scene("ManagedRenderPipelineCameraRequestConfigurationScene"); + + GameObject* cameraObject = scene.CreateGameObject("Camera"); + auto* camera = cameraObject->AddComponent(); + camera->SetPrimary(true); + + GameObject* lightObject = scene.CreateGameObject("Light"); + auto* light = lightObject->AddComponent(); + light->SetLightType(LightType::Directional); + light->SetCastsShadows(true); + + CameraRenderRequest baselineRequest = {}; + baselineRequest.scene = &scene; + baselineRequest.camera = camera; + baselineRequest.surface = RenderSurface(320, 180); + ApplyDefaultRenderPipelineAssetCameraRenderRequestPolicy( + baselineRequest, + 0u, + 0u, + DirectionalShadowPlanningSettings{}); + ASSERT_TRUE(baselineRequest.directionalShadow.IsValid()); + + const Pipelines::ManagedRenderPipelineAssetDescriptor descriptor = { + "GameScripts", + "Gameplay", + "ManagedCameraRequestConfiguredRenderPipelineProbeAsset" + }; + auto bridgeState = std::make_shared(); + bridgeState->configureCameraRenderRequest = + []( + CameraRenderRequest& request, + size_t, + size_t, + const DirectionalShadowPlanningSettings&) { + request.directionalShadow = {}; + }; + Pipelines::SetManagedRenderPipelineBridge( + std::make_shared(bridgeState)); + + CameraRenderRequest request = {}; + request.scene = &scene; + request.camera = camera; + request.surface = RenderSurface(320, 180); + + Pipelines::ManagedScriptableRenderPipelineAsset asset(descriptor); + asset.ConfigureCameraRenderRequest( + request, + 0u, + 0u, + DirectionalShadowPlanningSettings{}); + + EXPECT_EQ(bridgeState->createAssetRuntimeCalls, 1); + ASSERT_NE(bridgeState->lastCreatedRuntimeState, nullptr); + EXPECT_EQ( + bridgeState->lastCreatedRuntimeState->configureCameraRenderRequestCalls, + 1); + EXPECT_EQ( + bridgeState->lastCreatedRuntimeState->lastRenderedBaseCameraCount, + 0u); + EXPECT_EQ( + bridgeState->lastCreatedRuntimeState->lastRenderedRequestCount, + 0u); + EXPECT_FALSE(request.directionalShadow.IsValid()); + + Pipelines::ClearManagedRenderPipelineBridge(); +} + +TEST(ManagedScriptableRenderPipelineAsset_Test, ReusesManagedAssetRuntimeAcrossPipelineRequestAndPlanRequests) { Pipelines::ClearManagedRenderPipelineBridge(); const Pipelines::ManagedRenderPipelineAssetDescriptor descriptor = { @@ -4623,12 +4726,20 @@ TEST(ManagedScriptableRenderPipelineAsset_Test, ReusesManagedAssetRuntimeAcrossP CameraRenderRequest request = {}; request.surface = RenderSurface(128, 72); + asset.ConfigureCameraRenderRequest( + request, + 1u, + 2u, + DirectionalShadowPlanningSettings{}); CameraFramePlan firstPlan = CameraFramePlan::FromRequest(request); CameraFramePlan secondPlan = CameraFramePlan::FromRequest(request); asset.ConfigureCameraFramePlan(firstPlan); asset.ConfigureCameraFramePlan(secondPlan); EXPECT_EQ(bridgeState->createAssetRuntimeCalls, 1); + EXPECT_EQ(runtimeState->configureCameraRenderRequestCalls, 1); + EXPECT_EQ(runtimeState->lastRenderedBaseCameraCount, 1u); + EXPECT_EQ(runtimeState->lastRenderedRequestCount, 2u); EXPECT_EQ(runtimeState->configureCameraFramePlanCalls, 2); Pipelines::ClearManagedRenderPipelineBridge(); diff --git a/tests/scripting/test_mono_script_runtime.cpp b/tests/scripting/test_mono_script_runtime.cpp index 643a2a7c..4b933219 100644 --- a/tests/scripting/test_mono_script_runtime.cpp +++ b/tests/scripting/test_mono_script_runtime.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -577,6 +578,53 @@ TEST_F( EXPECT_TRUE(plan.IsFinalOutputStageValid()); } +TEST_F( + MonoScriptRuntimeTest, + ManagedRenderPipelineAssetConfiguresCameraRequestsThroughRequestContext) { + Scene* runtimeScene = CreateScene("ManagedRenderPipelineCameraRequestScene"); + + GameObject* cameraObject = runtimeScene->CreateGameObject("Camera"); + auto* camera = cameraObject->AddComponent(); + camera->SetPrimary(true); + + GameObject* lightObject = runtimeScene->CreateGameObject("Light"); + auto* light = lightObject->AddComponent(); + light->SetLightType(LightType::Directional); + light->SetCastsShadows(true); + + XCEngine::Rendering::CameraRenderRequest baselineRequest = {}; + baselineRequest.scene = runtimeScene; + baselineRequest.camera = camera; + baselineRequest.surface = XCEngine::Rendering::RenderSurface(64u, 64u); + XCEngine::Rendering::ApplyDefaultRenderPipelineAssetCameraRenderRequestPolicy( + baselineRequest, + 0u, + 0u, + XCEngine::Rendering::DirectionalShadowPlanningSettings{}); + ASSERT_TRUE(baselineRequest.directionalShadow.IsValid()); + + const XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor descriptor = { + "GameScripts", + "Gameplay", + "ManagedCameraRequestConfiguredRenderPipelineProbeAsset" + }; + + XCEngine::Rendering::Pipelines::ManagedScriptableRenderPipelineAsset asset( + descriptor); + + XCEngine::Rendering::CameraRenderRequest request = {}; + request.scene = runtimeScene; + request.camera = camera; + request.surface = XCEngine::Rendering::RenderSurface(64u, 64u); + asset.ConfigureCameraRenderRequest( + request, + 0u, + 0u, + XCEngine::Rendering::DirectionalShadowPlanningSettings{}); + + EXPECT_FALSE(request.directionalShadow.IsValid()); +} + TEST_F(MonoScriptRuntimeTest, ClassFieldDefaultValueQueryReturnsNullComponentReferences) { std::vector fields;