diff --git a/editor/app/Bootstrap/Application.cpp b/editor/app/Bootstrap/Application.cpp index 9eabbf28..4501ab76 100644 --- a/editor/app/Bootstrap/Application.cpp +++ b/editor/app/Bootstrap/Application.cpp @@ -20,9 +20,11 @@ #include "EnvironmentFlags.h" #include +#include #include #include #include +#include #include namespace XCEngine::UI::Editor { @@ -34,6 +36,24 @@ constexpr const wchar_t* kWindowTitle = L"Main Scene * - Main.xx - XCEngine Edit constexpr DWORD kBorderlessWindowStyle = WS_POPUP | WS_THICKFRAME; constexpr int kDefaultSmokeTestDurationSeconds = 12; +class UIEditorRuntimeTraceLogSink final : public ::XCEngine::Debug::ILogSink { +public: + void Log(const ::XCEngine::Debug::LogEntry& entry) override { + if (entry.category != ::XCEngine::Debug::LogCategory::Rendering || + entry.level < ::XCEngine::Debug::LogLevel::Warning) { + return; + } + + std::ostringstream stream = {}; + stream << ::XCEngine::Debug::LogLevelToString(entry.level) + << ' ' + << entry.message.CStr(); + AppendUIEditorRuntimeTrace("engine", stream.str()); + } + + void Flush() override {} +}; + bool HasEditorWorkspaceMarkers(const std::filesystem::path& root) { return std::filesystem::exists(root / "CMakeLists.txt") && std::filesystem::exists(root / "editor" / "resources") && @@ -160,6 +180,9 @@ bool Application::Initialize(HINSTANCE hInstance, int nCmdShow) { const std::filesystem::path logRoot = m_resourceService->GetExecutableDirectory() / "logs"; InitializeUIEditorRuntimeTrace(logRoot); + auto runtimeTraceLogSink = std::make_unique(); + m_runtimeTraceLogSink = runtimeTraceLogSink.get(); + ::XCEngine::Debug::Logger::Get().AddSink(std::move(runtimeTraceLogSink)); SetUnhandledExceptionFilter(&Application::HandleUnhandledException); AppendUIEditorRuntimeTrace("app", "initialize begin"); @@ -313,6 +336,10 @@ void Application::Shutdown() { m_smokeTestCloseRequested = false; AppendUIEditorRuntimeTrace("app", "shutdown end"); + if (m_runtimeTraceLogSink != nullptr) { + ::XCEngine::Debug::Logger::Get().RemoveSink(m_runtimeTraceLogSink); + m_runtimeTraceLogSink = nullptr; + } ShutdownUIEditorRuntimeTrace(); } diff --git a/editor/app/Bootstrap/Application.h b/editor/app/Bootstrap/Application.h index 79486d83..0e5ba8f9 100644 --- a/editor/app/Bootstrap/Application.h +++ b/editor/app/Bootstrap/Application.h @@ -12,6 +12,10 @@ #include #include +namespace XCEngine::Debug { +class ILogSink; +} + namespace XCEngine::UI::Editor { class EditorWindowSystem; @@ -69,6 +73,7 @@ private: std::unique_ptr m_resourceService = {}; std::unique_ptr m_windowManager = {}; std::unique_ptr m_systemInteractionHost = {}; + XCEngine::Debug::ILogSink* m_runtimeTraceLogSink = nullptr; int m_smokeTestFrameLimit = 0; int m_smokeTestRenderedFrameCount = 0; bool m_smokeTestEnabled = false; diff --git a/editor/app/Rendering/Viewport/ViewportHostService.cpp b/editor/app/Rendering/Viewport/ViewportHostService.cpp index 6fa41bc8..ecaba7c1 100644 --- a/editor/app/Rendering/Viewport/ViewportHostService.cpp +++ b/editor/app/Rendering/Viewport/ViewportHostService.cpp @@ -6,6 +6,7 @@ #include "Viewport/SceneViewportResourcePaths.h" #include "ViewportRenderHost.h" +#include #include #include @@ -330,6 +331,9 @@ void ViewportHostService::ApplyViewportFallback( const ::XCEngine::Rendering::RenderContext& renderContext, const ViewportRenderResult& renderResult) { entry.statusText = renderResult.statusText; + ::XCEngine::UI::Editor::AppendUIEditorRuntimeTrace( + "viewport", + "fallback clear: " + entry.statusText); entry.renderTargets.hasValidObjectIdFrame = false; entry.renderTargets.objectIdFrameSerial = 0u; ClearViewport( diff --git a/engine/include/XCEngine/Rendering/AGENTS.md b/engine/include/XCEngine/Rendering/AGENTS.md index fb58d568..816d4b5c 100644 --- a/engine/include/XCEngine/Rendering/AGENTS.md +++ b/engine/include/XCEngine/Rendering/AGENTS.md @@ -1,5 +1,7 @@ # XCEngine Rendering Agent Guide +Editor viewport 边界补充:SceneViewport 的 render request 是 editor 每帧快照,不属于 SRP fallback 兜底范围。workspace/panel frame events 若会打开或切换场景,必须在事件消费完成后、`RenderRequestedViewports()` 前重新同步 request;不要通过放宽 `ScriptableRenderPipelineHost` authoritative stage recorder 或恢复 backend fallback 来掩盖过期请求。 + 本文面向以后在 `engine/include/XCEngine/Rendering/**`、`engine/src/Rendering/**` 以及对应 managed SRP/URP 绑定上工作的 agent / 开发者。这里的目标不是写愿景,而是固定当前 checkout 的真实结构、Unity SRP/URP 对齐方向、职责边界和验证入口。 如果本文与代码、`engine/CMakeLists.txt`、`tests/Rendering/**` 或 managed API 冲突,以当前代码为准,并在同一次改动中更新本文。 diff --git a/engine/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineResources.cpp b/engine/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineResources.cpp index d4ac74dd..851c572e 100644 --- a/engine/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineResources.cpp +++ b/engine/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineResources.cpp @@ -189,7 +189,8 @@ BuiltinForwardPipeline::CachedDescriptorSet* BuiltinForwardPipeline::GetOrCreate } } - const Core::uint64 materialVersion = material != nullptr ? material->GetChangeVersion() : 0; + const Core::uint64 materialVersion = + material != nullptr ? material->GetChangeVersion() : 1u; if (setLayout.usesMaterial) { if (!passLayout.material.IsValid() || passLayout.material.set != setIndex) { return nullptr; diff --git a/engine/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineSurface.cpp b/engine/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineSurface.cpp index cf5986b9..93fda92c 100644 --- a/engine/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineSurface.cpp +++ b/engine/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineSurface.cpp @@ -25,6 +25,15 @@ namespace { constexpr float kForwardAmbientIntensity = 0.28f; constexpr float kSpotInnerAngleRatio = 0.8f; +struct BuiltinForwardMaterialConstants { + Math::Vector4 baseColorFactor = Math::Vector4::One(); + Math::Vector4 alphaCutoffParams = Math::Vector4(0.5f, 0.0f, 0.0f, 0.0f); +}; + +struct BuiltinUnlitMaterialConstants { + Math::Vector4 baseColorFactor = Math::Vector4::One(); +}; + Resources::ShaderKeywordSet ResolvePassKeywordSet( const RenderSceneData& sceneData, const Resources::Material* material) { @@ -106,6 +115,82 @@ bool UsesDynamicSurfaceDescriptorSet(const BuiltinPassSetLayoutMetadata& setLayo setLayout.usesMaterialBuffers; } +MaterialConstantPayloadView ResolveBuiltinForwardMaterialConstantPayload( + const Resources::Material* material, + BuiltinForwardMaterialConstants& outConstants, + Resources::MaterialConstantFieldDesc (&outLayout)[2]) { + outConstants.baseColorFactor = ResolveBuiltinBaseColorFactor(material); + outConstants.alphaCutoffParams = + Math::Vector4(ResolveBuiltinAlphaCutoff(material), 0.0f, 0.0f, 0.0f); + + outLayout[0].name = "gBaseColorFactor"; + outLayout[0].type = Resources::MaterialPropertyType::Float4; + outLayout[0].offset = 0u; + outLayout[0].size = static_cast(sizeof(Math::Vector4)); + outLayout[0].alignedSize = static_cast(sizeof(Math::Vector4)); + + outLayout[1].name = "gAlphaCutoffParams"; + outLayout[1].type = Resources::MaterialPropertyType::Float4; + outLayout[1].offset = static_cast(sizeof(Math::Vector4)); + outLayout[1].size = static_cast(sizeof(Math::Vector4)); + outLayout[1].alignedSize = static_cast(sizeof(Math::Vector4)); + + MaterialConstantLayoutView layoutView = {}; + layoutView.fields = outLayout; + layoutView.count = 2u; + layoutView.size = sizeof(BuiltinForwardMaterialConstants); + return { &outConstants, sizeof(BuiltinForwardMaterialConstants), layoutView }; +} + +MaterialConstantPayloadView ResolveBuiltinUnlitMaterialConstantPayload( + const Resources::Material* material, + BuiltinUnlitMaterialConstants& outConstants, + Resources::MaterialConstantFieldDesc (&outLayout)[1]) { + outConstants.baseColorFactor = ResolveBuiltinBaseColorFactor(material); + + outLayout[0].name = "gBaseColorFactor"; + outLayout[0].type = Resources::MaterialPropertyType::Float4; + outLayout[0].offset = 0u; + outLayout[0].size = static_cast(sizeof(Math::Vector4)); + outLayout[0].alignedSize = static_cast(sizeof(Math::Vector4)); + + MaterialConstantLayoutView layoutView = {}; + layoutView.fields = outLayout; + layoutView.count = 1u; + layoutView.size = sizeof(BuiltinUnlitMaterialConstants); + return { &outConstants, sizeof(BuiltinUnlitMaterialConstants), layoutView }; +} + +MaterialConstantPayloadView ResolveSurfaceMaterialConstantPayload( + const Resources::Shader* resolvedShader, + const Resources::Material* material, + const Resources::Shader* builtinForwardShader, + const Resources::Shader* builtinUnlitShader, + const Resources::Shader* builtinDepthOnlyShader, + const Resources::Shader* builtinShadowCasterShader, + BuiltinForwardMaterialConstants& outForwardConstants, + Resources::MaterialConstantFieldDesc (&outForwardLayout)[2], + BuiltinUnlitMaterialConstants& outUnlitConstants, + Resources::MaterialConstantFieldDesc (&outUnlitLayout)[1]) { + if (resolvedShader == builtinUnlitShader) { + return ResolveBuiltinUnlitMaterialConstantPayload( + material, + outUnlitConstants, + outUnlitLayout); + } + + if (resolvedShader == builtinForwardShader || + resolvedShader == builtinDepthOnlyShader || + resolvedShader == builtinShadowCasterShader) { + return ResolveBuiltinForwardMaterialConstantPayload( + material, + outForwardConstants, + outForwardLayout); + } + + return ResolveSchemaMaterialConstantPayload(material); +} + bool TryResolveRequestedSurfacePassType( const DrawSettings& drawSettings, BuiltinMaterialPass& outPass, @@ -557,7 +642,22 @@ bool BuiltinForwardPipeline::DrawVisibleItem( return false; } - MaterialConstantPayloadView materialConstants = ResolveSchemaMaterialConstantPayload(material); + BuiltinForwardMaterialConstants builtinForwardConstants = {}; + Resources::MaterialConstantFieldDesc builtinForwardLayout[2] = {}; + BuiltinUnlitMaterialConstants builtinUnlitConstants = {}; + Resources::MaterialConstantFieldDesc builtinUnlitLayout[1] = {}; + MaterialConstantPayloadView materialConstants = + ResolveSurfaceMaterialConstantPayload( + resolvedShaderPass.shader, + material, + m_builtinForwardShader.Get(), + m_builtinUnlitShader.Get(), + m_builtinDepthOnlyShader.Get(), + m_builtinShadowCasterShader.Get(), + builtinForwardConstants, + builtinForwardLayout, + builtinUnlitConstants, + builtinUnlitLayout); if (passLayout->material.IsValid() && !materialConstants.IsValid()) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering,