diff --git a/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h b/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h index 0d0baab5..68fb9c52 100644 --- a/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h +++ b/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h @@ -76,6 +76,10 @@ public: const DirectionalShadowPlanningSettings&) const { } + virtual bool TryGetDefaultFinalColorSettings(FinalColorSettings&) const { + return false; + } + virtual void ConfigureCameraFramePlan(CameraFramePlan&) const { } }; diff --git a/engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp b/engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp index 79d674c8..f60a0f77 100644 --- a/engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp +++ b/engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp @@ -96,6 +96,15 @@ void ManagedScriptableRenderPipelineAsset::ConfigureCameraRenderRequest( } FinalColorSettings ManagedScriptableRenderPipelineAsset::GetDefaultFinalColorSettings() const { + if (const std::shared_ptr runtime = + ResolveManagedAssetRuntime(); + runtime != nullptr) { + FinalColorSettings settings = {}; + if (runtime->TryGetDefaultFinalColorSettings(settings)) { + return settings; + } + } + return m_fallbackAsset.GetDefaultFinalColorSettings(); } diff --git a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp index a1d19946..9525fc5e 100644 --- a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp +++ b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp @@ -318,6 +318,84 @@ bool TryUnboxManagedBoolean(MonoObject* boxedValue, bool& outValue) { return true; } +struct ManagedFinalColorSettingsData { + uint8_t outputTransferMode = 0; + uint8_t exposureMode = 0; + uint16_t exposurePadding = 0; + float exposureValue = 1.0f; + uint8_t toneMappingMode = 0; + uint8_t toneMappingPadding[3] = {}; + XCEngine::Math::Vector4 finalColorScale = XCEngine::Math::Vector4::One(); +}; + +static_assert( + sizeof(ManagedFinalColorSettingsData) == + sizeof(Rendering::FinalColorSettings), + "Managed final color bridge layout must match native FinalColorSettings."); + +Rendering::FinalColorOutputTransferMode ResolveManagedFinalColorOutputTransferMode( + uint8_t value) { + switch (value) { + case 1: + return Rendering::FinalColorOutputTransferMode::LinearToSRGB; + default: + return Rendering::FinalColorOutputTransferMode::Disabled; + } +} + +Rendering::FinalColorExposureMode ResolveManagedFinalColorExposureMode( + uint8_t value) { + switch (value) { + case 1: + return Rendering::FinalColorExposureMode::Fixed; + default: + return Rendering::FinalColorExposureMode::Disabled; + } +} + +Rendering::FinalColorToneMappingMode ResolveManagedFinalColorToneMappingMode( + uint8_t value) { + switch (value) { + case 1: + return Rendering::FinalColorToneMappingMode::Neutral; + case 2: + return Rendering::FinalColorToneMappingMode::ACES; + default: + return Rendering::FinalColorToneMappingMode::Disabled; + } +} + +bool TryUnboxManagedFinalColorSettings( + MonoObject* boxedValue, + Rendering::FinalColorSettings& outSettings) { + outSettings = {}; + outSettings.exposureValue = 1.0f; + outSettings.finalColorScale = XCEngine::Math::Vector4::One(); + if (!boxedValue) { + return false; + } + + void* const rawValue = mono_object_unbox(boxedValue); + if (!rawValue) { + return false; + } + + const auto* const managedSettings = + static_cast(rawValue); + outSettings.outputTransferMode = + ResolveManagedFinalColorOutputTransferMode( + managedSettings->outputTransferMode); + outSettings.exposureMode = + ResolveManagedFinalColorExposureMode( + managedSettings->exposureMode); + outSettings.exposureValue = managedSettings->exposureValue; + outSettings.toneMappingMode = + ResolveManagedFinalColorToneMappingMode( + managedSettings->toneMappingMode); + outSettings.finalColorScale = managedSettings->finalColorScale; + return true; +} + bool SupportsManagedRenderPipelineStageGraphRecording( Rendering::CameraFrameStage stage) { return Rendering::SupportsCameraFramePipelineGraphRecording(stage) || @@ -394,6 +472,9 @@ public: void ConfigureCameraFramePlan( Rendering::CameraFramePlan& plan) const override; + bool TryGetDefaultFinalColorSettings( + Rendering::FinalColorSettings& settings) const override; + MonoScriptRuntime* GetRuntime() const { return m_runtime; } @@ -415,6 +496,8 @@ private: MonoObject* assetObject) const; MonoMethod* ResolveConfigureCameraFramePlanMethod( MonoObject* assetObject) const; + MonoMethod* ResolveGetDefaultFinalColorSettingsMethod( + MonoObject* assetObject) const; MonoScriptRuntime* m_runtime = nullptr; std::weak_ptr m_runtimeLifetime; @@ -423,6 +506,7 @@ private: mutable MonoMethod* m_createPipelineMethod = nullptr; mutable MonoMethod* m_configureCameraRenderRequestMethod = nullptr; mutable MonoMethod* m_configureCameraFramePlanMethod = nullptr; + mutable MonoMethod* m_getDefaultFinalColorSettingsMethod = nullptr; mutable bool m_assetCreationAttempted = false; }; @@ -738,6 +822,36 @@ void MonoManagedRenderPipelineAssetRuntime::ConfigureCameraFramePlan( planningContextHandle); } +bool MonoManagedRenderPipelineAssetRuntime::TryGetDefaultFinalColorSettings( + Rendering::FinalColorSettings& settings) const { + settings = {}; + settings.exposureValue = 1.0f; + settings.finalColorScale = XCEngine::Math::Vector4::One(); + if (!EnsureManagedAsset()) { + return false; + } + + MonoObject* const assetObject = GetManagedAssetObject(); + MonoMethod* const method = + ResolveGetDefaultFinalColorSettingsMethod(assetObject); + if (assetObject == nullptr || method == nullptr) { + return false; + } + + MonoObject* managedSettings = nullptr; + if (!m_runtime->InvokeManagedMethod( + assetObject, + method, + nullptr, + &managedSettings)) { + return false; + } + + return TryUnboxManagedFinalColorSettings( + managedSettings, + settings); +} + bool MonoManagedRenderPipelineAssetRuntime::CreateManagedPipeline( uint32_t& outPipelineHandle) const { outPipelineHandle = 0; @@ -812,6 +926,7 @@ void MonoManagedRenderPipelineAssetRuntime::ReleaseManagedAsset() const { m_createPipelineMethod = nullptr; m_configureCameraRenderRequestMethod = nullptr; m_configureCameraFramePlanMethod = nullptr; + m_getDefaultFinalColorSettingsMethod = nullptr; m_assetCreationAttempted = false; if (!IsRuntimeAlive()) { @@ -872,6 +987,20 @@ MonoManagedRenderPipelineAssetRuntime::ResolveConfigureCameraFramePlanMethod( return m_configureCameraFramePlanMethod; } +MonoMethod* +MonoManagedRenderPipelineAssetRuntime::ResolveGetDefaultFinalColorSettingsMethod( + MonoObject* assetObject) const { + if (m_getDefaultFinalColorSettingsMethod == nullptr) { + m_getDefaultFinalColorSettingsMethod = + m_runtime->ResolveManagedMethod( + assetObject, + "GetDefaultFinalColorSettings", + 0); + } + + return m_getDefaultFinalColorSettingsMethod; +} + class MonoManagedRenderPipelineBridge final : public Rendering::Pipelines::ManagedRenderPipelineBridge { public: diff --git a/managed/CMakeLists.txt b/managed/CMakeLists.txt index 7b30c92e..57e7215d 100644 --- a/managed/CMakeLists.txt +++ b/managed/CMakeLists.txt @@ -105,6 +105,10 @@ set(XCENGINE_SCRIPT_CORE_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/CameraFrameStage.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Component.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Debug.cs + ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/FinalColorExposureMode.cs + ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/FinalColorOutputTransferMode.cs + ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/FinalColorSettings.cs + ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/FinalColorToneMappingMode.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/ForceMode.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/FullscreenPassDescriptor.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/GraphicsSettings.cs diff --git a/managed/GameScripts/RenderPipelineApiProbe.cs b/managed/GameScripts/RenderPipelineApiProbe.cs index 7d12c5e6..5eabee37 100644 --- a/managed/GameScripts/RenderPipelineApiProbe.cs +++ b/managed/GameScripts/RenderPipelineApiProbe.cs @@ -98,6 +98,26 @@ namespace Gameplay } } + public sealed class ManagedFinalColorRenderPipelineProbeAsset + : ScriptableRenderPipelineAsset + { + protected override ScriptableRenderPipeline CreatePipeline() + { + return new ManagedRenderPipelineProbe(); + } + + protected override FinalColorSettings GetDefaultFinalColorSettings() + { + FinalColorSettings settings = FinalColorSettings.CreateDefault(); + settings.outputTransferMode = FinalColorOutputTransferMode.LinearToSRGB; + settings.exposureMode = FinalColorExposureMode.Fixed; + settings.exposureValue = 1.75f; + settings.toneMappingMode = FinalColorToneMappingMode.ACES; + settings.finalColorScale = new Vector4(0.90f, 1.10f, 1.20f, 1.0f); + return settings; + } + } + public sealed class ManagedRenderPipelineProbe : ScriptableRenderPipeline { public static int SupportsStageCallCount; diff --git a/managed/XCEngine.ScriptCore/FinalColorExposureMode.cs b/managed/XCEngine.ScriptCore/FinalColorExposureMode.cs new file mode 100644 index 00000000..55abe52f --- /dev/null +++ b/managed/XCEngine.ScriptCore/FinalColorExposureMode.cs @@ -0,0 +1,8 @@ +namespace XCEngine +{ + public enum FinalColorExposureMode : byte + { + Disabled = 0, + Fixed = 1 + } +} diff --git a/managed/XCEngine.ScriptCore/FinalColorOutputTransferMode.cs b/managed/XCEngine.ScriptCore/FinalColorOutputTransferMode.cs new file mode 100644 index 00000000..9239a70d --- /dev/null +++ b/managed/XCEngine.ScriptCore/FinalColorOutputTransferMode.cs @@ -0,0 +1,8 @@ +namespace XCEngine +{ + public enum FinalColorOutputTransferMode : byte + { + Disabled = 0, + LinearToSRGB = 1 + } +} diff --git a/managed/XCEngine.ScriptCore/FinalColorSettings.cs b/managed/XCEngine.ScriptCore/FinalColorSettings.cs new file mode 100644 index 00000000..de03c1b3 --- /dev/null +++ b/managed/XCEngine.ScriptCore/FinalColorSettings.cs @@ -0,0 +1,22 @@ +using System.Runtime.InteropServices; + +namespace XCEngine +{ + [StructLayout(LayoutKind.Sequential)] + public struct FinalColorSettings + { + public FinalColorOutputTransferMode outputTransferMode; + public FinalColorExposureMode exposureMode; + public float exposureValue; + public FinalColorToneMappingMode toneMappingMode; + public Vector4 finalColorScale; + + public static FinalColorSettings CreateDefault() + { + FinalColorSettings settings = new FinalColorSettings(); + settings.exposureValue = 1.0f; + settings.finalColorScale = new Vector4(1.0f, 1.0f, 1.0f, 1.0f); + return settings; + } + } +} diff --git a/managed/XCEngine.ScriptCore/FinalColorToneMappingMode.cs b/managed/XCEngine.ScriptCore/FinalColorToneMappingMode.cs new file mode 100644 index 00000000..6e63e25e --- /dev/null +++ b/managed/XCEngine.ScriptCore/FinalColorToneMappingMode.cs @@ -0,0 +1,9 @@ +namespace XCEngine +{ + public enum FinalColorToneMappingMode : byte + { + Disabled = 0, + Neutral = 1, + ACES = 2 + } +} diff --git a/managed/XCEngine.ScriptCore/ScriptableRenderPipelineAsset.cs b/managed/XCEngine.ScriptCore/ScriptableRenderPipelineAsset.cs index 068ca54c..9ef076f7 100644 --- a/managed/XCEngine.ScriptCore/ScriptableRenderPipelineAsset.cs +++ b/managed/XCEngine.ScriptCore/ScriptableRenderPipelineAsset.cs @@ -20,5 +20,10 @@ namespace XCEngine ScriptableRenderPipelinePlanningContext context) { } + + protected internal virtual FinalColorSettings GetDefaultFinalColorSettings() + { + return FinalColorSettings.CreateDefault(); + } } } diff --git a/tests/Rendering/unit/test_camera_scene_renderer.cpp b/tests/Rendering/unit/test_camera_scene_renderer.cpp index ea99dc61..ca86a730 100644 --- a/tests/Rendering/unit/test_camera_scene_renderer.cpp +++ b/tests/Rendering/unit/test_camera_scene_renderer.cpp @@ -776,6 +776,9 @@ struct MockManagedRenderPipelineAssetRuntimeState { int createStageRecorderCalls = 0; int configureCameraRenderRequestCalls = 0; int configureCameraFramePlanCalls = 0; + int getDefaultFinalColorSettingsCalls = 0; + bool hasDefaultFinalColorSettings = false; + FinalColorSettings defaultFinalColorSettings = {}; size_t lastRenderedBaseCameraCount = 0u; size_t lastRenderedRequestCount = 0u; std::shared_ptr lastCreatedStageRecorderState; @@ -828,6 +831,17 @@ public: } } + bool TryGetDefaultFinalColorSettings( + FinalColorSettings& settings) const override { + ++m_state->getDefaultFinalColorSettingsCalls; + if (!m_state->hasDefaultFinalColorSettings) { + return false; + } + + settings = m_state->defaultFinalColorSettings; + return true; + } + private: std::shared_ptr m_state; }; @@ -836,6 +850,8 @@ struct MockManagedRenderPipelineBridgeState { int createAssetRuntimeCalls = 0; Pipelines::ManagedRenderPipelineAssetDescriptor lastDescriptor = {}; std::shared_ptr lastCreatedRuntimeState; + bool hasDefaultFinalColorSettings = false; + FinalColorSettings defaultFinalColorSettings = {}; std::functionlastDescriptor = descriptor; m_state->lastCreatedRuntimeState = std::make_shared(); + m_state->lastCreatedRuntimeState->hasDefaultFinalColorSettings = + m_state->hasDefaultFinalColorSettings; + m_state->lastCreatedRuntimeState->defaultFinalColorSettings = + m_state->defaultFinalColorSettings; m_state->lastCreatedRuntimeState->configureCameraRenderRequest = m_state->configureCameraRenderRequest; m_state->lastCreatedRuntimeState->configureCameraFramePlan = @@ -4690,6 +4710,48 @@ TEST(ManagedScriptableRenderPipelineAsset_Test, LetsManagedBridgeConfigureCamera Pipelines::ClearManagedRenderPipelineBridge(); } +TEST(ManagedScriptableRenderPipelineAsset_Test, LetsManagedBridgeProvideDefaultFinalColorSettings) { + Pipelines::ClearManagedRenderPipelineBridge(); + + const Pipelines::ManagedRenderPipelineAssetDescriptor descriptor = { + "GameScripts", + "Gameplay", + "ManagedFinalColorRenderPipelineProbeAsset" + }; + auto bridgeState = std::make_shared(); + bridgeState->hasDefaultFinalColorSettings = true; + bridgeState->defaultFinalColorSettings.outputTransferMode = + FinalColorOutputTransferMode::LinearToSRGB; + bridgeState->defaultFinalColorSettings.exposureMode = + FinalColorExposureMode::Fixed; + bridgeState->defaultFinalColorSettings.exposureValue = 1.75f; + bridgeState->defaultFinalColorSettings.toneMappingMode = + FinalColorToneMappingMode::ACES; + bridgeState->defaultFinalColorSettings.finalColorScale = + XCEngine::Math::Vector4(0.90f, 1.10f, 1.20f, 1.0f); + Pipelines::SetManagedRenderPipelineBridge( + std::make_shared(bridgeState)); + + Pipelines::ManagedScriptableRenderPipelineAsset asset(descriptor); + const FinalColorSettings settings = asset.GetDefaultFinalColorSettings(); + + EXPECT_EQ(bridgeState->createAssetRuntimeCalls, 1); + ASSERT_NE(bridgeState->lastCreatedRuntimeState, nullptr); + EXPECT_EQ( + bridgeState->lastCreatedRuntimeState->getDefaultFinalColorSettingsCalls, + 1); + EXPECT_EQ(settings.outputTransferMode, FinalColorOutputTransferMode::LinearToSRGB); + EXPECT_EQ(settings.exposureMode, FinalColorExposureMode::Fixed); + EXPECT_FLOAT_EQ(settings.exposureValue, 1.75f); + EXPECT_EQ(settings.toneMappingMode, FinalColorToneMappingMode::ACES); + EXPECT_FLOAT_EQ(settings.finalColorScale.x, 0.90f); + EXPECT_FLOAT_EQ(settings.finalColorScale.y, 1.10f); + EXPECT_FLOAT_EQ(settings.finalColorScale.z, 1.20f); + EXPECT_FLOAT_EQ(settings.finalColorScale.w, 1.0f); + + Pipelines::ClearManagedRenderPipelineBridge(); +} + TEST(ManagedScriptableRenderPipelineAsset_Test, ReusesManagedAssetRuntimeAcrossPipelineRequestAndPlanRequests) { Pipelines::ClearManagedRenderPipelineBridge();