diff --git a/editor/AGENTS.md b/editor/AGENTS.md index 7a9d5f7f..bacd846c 100644 --- a/editor/AGENTS.md +++ b/editor/AGENTS.md @@ -160,6 +160,11 @@ logic. - First decide whether the change belongs to XCEditor framework, app semantics, app windowing, Win32 host, or rendering host. - Keep public framework headers free of `App::*`, Win32, and D3D12 host types. +- Do not add `editor/app` as an include directory for `XCUIEditorLib`. + App-only services must cross into framework code through framework-owned + interfaces and app-side adapters. +- Do not compile `${XCUI_EDITOR_SHARED_SOURCES}` directly into `XCUIEditorApp`; + the executable should consume shared framework code through `XCUIEditorLib`. - Use `editor/include/XCEditor/Windowing` and `editor/src/Windowing` for generic window authority, synchronization, validation, and presentation projection. @@ -186,6 +191,12 @@ owns the app-internal content controller factory, coordinators, frame transfer flow, host contracts, and manager. The Win32 host is the concrete native adapter and creates native host windows from content supplied by app windowing. +The framework/app compile boundary is also enforced. `XCUIEditorLib` does not +receive `editor/app` includes, and `XCUIEditorApp` does not directly compile +the shared framework sources. When framework UI code needs app-provided data +such as loaded icons, expose a framework-owned interface and adapt it in app +composition code. + The main remaining debt is promotion discipline around the app runtime surface. `XCUIEditorAppWindowing` is still app-internal and may depend on app semantics such as `EditorContext`, `EditorShellRuntime`, utility window descriptors, and @@ -194,25 +205,17 @@ content controllers to `XCEditor` until they are generic enough to expose. ## Validation -Useful local targets for editor/windowing changes: - -- `cmake --build build --config Debug --target XCUIEditorApp` -- `cmake --build build --config Debug --target editor_windowing_phase1_tests` -- `cmake --build build --config Debug --target editor_ui_tests` - -Useful test executables when they are present in the configured build tree: - -- `build/tests/UI/Editor/unit/Debug/editor_windowing_phase1_tests.exe` -- `build/tests/UI/Editor/unit/Debug/editor_ui_tests.exe` - -`xcui_editor_app_smoke` is a useful supplemental smoke test when the app target -and smoke test are configured, but it is not the default windowing unit-test -entry point. In the current Debug build, the direct 12-second smoke command is: +Default editor validation is only the app build plus the 12-second smoke run: ```powershell +cmake --build build --config Debug --target editor_ui_smoke_targets build\tests\UI\Editor\smoke\Debug\editor_ui_smoke_runner.exe build\editor\Debug\XCEngine.exe ``` +Do not run `editor_windowing_phase1_tests`, `editor_ui_tests`, or broader test +targets by default. Run them only when the user explicitly asks for them or a +separate targeted change makes them necessary. + The runner sets `XCUIEDITOR_SMOKE_TEST_DURATION_SECONDS=12`, waits for the editor to launch, lets the app auto-exit, and treats a clean editor exit as success. @@ -248,38 +251,24 @@ Start with these files for editor/windowing work: - `editor/app/Platform/Win32/Windowing/EditorWindow.*` - `editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.*` - `editor/app/Platform/Win32/Windowing/EditorWindowMessageDispatcher.*` -- `tests/UI/Editor/unit/test_editor_window_synchronization_planner.cpp` -- `tests/UI/Editor/unit/CMakeLists.txt` - `tests/UI/Editor/smoke/CMakeLists.txt` -## 上一刀 +## 前面几刀 -上一刀把窗口权威核心从 app-internal 层真正切到 framework 层。具体来说: -`EditorWindowSystem`、`EditorWindowSynchronizationPlan`、 -`EditorWindowSynchronizationPlanner`、`EditorWorkspaceWindowProjection` 和 -`EditorWindowPresentationPolicy` 的公共头移到 -`editor/include/XCEditor/Windowing`;对应实现移到 `editor/src/Windowing`。 -`EditorWindowWorkspaceStore` 只作为 framework 私有实现保留在 -`editor/src/Windowing/System`,没有暴露到 public include 面。 - -构建边界也同步收紧:这些 window authority / synchronization / presentation -sources 现在编进 `XCUIEditorLib`,不再属于 `XCUIEditorAppWindowing`; -`XCUIEditorAppWindowing` 的 `editor/app` include 从 `PUBLIC` 改成 `PRIVATE`。 -这意味着下游不能再因为链接 app windowing 目标而顺手拿到整个 app include 面。 - -app 层只改成依赖新的 framework include,例如 -`` 和 -``。 -`editor/app/Windowing` 仍然保留 app-specific 的 content controllers、 -coordinators、frame transfer、host contracts 和 `EditorWindowManager`; -`editor/app/Platform/Win32` 仍然只做 native host/window/message/runtime 适配。 - -测试边界也跟着改了:`editor_windowing_phase1_tests` 现在只链接 -`XCUIEditorLib`,不再链接 `XCUIEditorAppWindowing`,也不再把 `editor/app` -和 `editor/src` 作为测试 include 目录。这个测试因此只验证公开的 -`XCEditor/Windowing` 权威模型,而不是穿透 app 内部目录。 - -这一刀没有把整个 `app/Windowing` 提升到 `XCEditor`。故意没有提升的部分包括 -`EditorContext`、`EditorShellRuntime`、utility window descriptor/panel、 -frame transfer request、host interface、content controller、coordinator 和 Win32 host。 -这些仍然是产品运行时/宿主编排职责,不是 framework windowing kernel。 +- 最近一刀封死 framework -> app 的反向依赖:`UIEditorShellCompose` + 不再认识 `App::BuiltInIcons`,改由 `UIEditorShellIconResolver` 抽象取图标, + app 在 `EditorShellDrawComposer` 里用 adapter 连接 `BuiltInIcons`。 +- 同一刀收紧构建边界:`XCUIEditorLib` 不再拥有 `editor/app` include, + `XCUIEditorApp` 不再直接编译 `${XCUI_EDITOR_SHARED_SOURCES}`,也不再把 + `editor/src` 加进 include;app 通过 `XCUIEditorLib` 消费 framework。 +- 前一刀把窗口权威核心切到 framework:`EditorWindowSystem`、同步计划/规划器、 + workspace store 和 presentation projection 位于 `editor/include/XCEditor/Windowing` + 与 `editor/src/Windowing`。 +- `editor/app/Windowing` 保留 app-specific 的 content controllers、coordinators、 + frame transfer、host contracts 和 `EditorWindowManager`;Win32 host 仍只做 + native adapter。 +- 未提升到 `XCEditor` 的内容包括 `EditorContext`、`EditorShellRuntime`、utility + descriptors/panels、host interfaces、content controllers、coordinators、 + frame transfer 和 Win32/D3D12 host。 +- 默认验证也收口:只构建 `XCUIEditorApp` 相关 smoke 目标并跑 12 秒 smoke; + 需要专项覆盖时再单独运行 windowing/unit targets。 diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index f5d0d412..6e3efd8a 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -167,7 +167,6 @@ target_include_directories(XCUIEditorLib PUBLIC ) target_include_directories(XCUIEditorLib PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/app ${CMAKE_CURRENT_SOURCE_DIR}/src ) @@ -187,6 +186,8 @@ set(XCUI_EDITOR_APP_WINDOWING_SOURCES app/Windowing/Content/EditorUtilityWindowContentController.cpp app/Windowing/Content/EditorWorkspaceWindowContentController.cpp app/Windowing/Frame/EditorWindowFrameOrchestrator.cpp + app/Windowing/Runtime/EditorWindowRuntimeController.cpp + app/Windowing/Runtime/EditorWindowScreenshotController.cpp ) add_library(XCUIEditorAppWindowing STATIC @@ -209,7 +210,6 @@ set(XCUI_EDITOR_HOST_PLATFORM_SOURCES ) set(XCUI_EDITOR_HOST_RENDERING_SOURCES - app/Platform/Win32/Runtime/EditorWindowScreenshotController.cpp app/Rendering/D3D12/D3D12HostDevice.cpp app/Rendering/D3D12/D3D12WindowCapture.cpp app/Rendering/D3D12/D3D12UiRenderer.cpp @@ -297,9 +297,7 @@ if(XCENGINE_BUILD_XCUI_EDITOR_APP) app/Platform/Win32/Windowing/EditorFloatingWindowPlacement.cpp app/Platform/Win32/Windowing/EditorWindowSession.cpp app/Platform/Win32/Chrome/EditorWindowChromeController.cpp - app/Platform/Win32/Runtime/EditorWindowFrameDriver.cpp app/Platform/Win32/Runtime/EditorWindowInputController.cpp - app/Platform/Win32/Runtime/EditorWindowRuntimeController.cpp app/Platform/Win32/System/Win32SystemInteractionHost.cpp app/Platform/Win32/Windowing/EditorWindowHostRuntime.cpp app/Platform/Win32/Windowing/EditorWindowMessageDispatcher.cpp @@ -315,7 +313,6 @@ if(XCENGINE_BUILD_XCUI_EDITOR_APP) ) set(XCUI_EDITOR_APP_INTERNAL_SOURCES - ${XCUI_EDITOR_SHARED_SOURCES} ${XCUI_EDITOR_HOST_PLATFORM_SOURCES} ${XCUI_EDITOR_HOST_RENDERING_SOURCES} ${XCUI_EDITOR_APP_CORE_SOURCES} @@ -330,7 +327,6 @@ if(XCENGINE_BUILD_XCUI_EDITOR_APP) target_include_directories(XCUIEditorApp PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/app ${CMAKE_CURRENT_SOURCE_DIR}/include - ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/engine/include ${CMAKE_SOURCE_DIR}/engine/third_party/stb ) diff --git a/editor/app/Composition/EditorShellDrawComposer.cpp b/editor/app/Composition/EditorShellDrawComposer.cpp index 23bf44ac..724548d2 100644 --- a/editor/app/Composition/EditorShellDrawComposer.cpp +++ b/editor/app/Composition/EditorShellDrawComposer.cpp @@ -20,6 +20,20 @@ namespace { using ::XCEngine::UI::UIDrawData; using ::XCEngine::UI::UIDrawList; +class BuiltInIconResolver final : public UIEditorShellIconResolver { +public: + explicit BuiltInIconResolver(const BuiltInIcons& icons) + : m_icons(icons) {} + + const ::XCEngine::UI::UITextureHandle* TryResolveIcon( + std::uint8_t iconKind) const override { + return &m_icons.Resolve(static_cast(iconKind)); + } + +private: + const BuiltInIcons& m_icons; +}; + UIEditorShellComposeModel BuildShellComposeModelFromFrame( const UIEditorShellInteractionFrame& frame) { UIEditorShellComposeModel model = {}; @@ -80,6 +94,7 @@ void EditorShellDrawComposer::Append( const auto& palette = ResolveUIEditorShellInteractionPalette(); const UIEditorShellComposeModel shellComposeModel = BuildShellComposeModelFromFrame(context.shellFrame); + const BuiltInIconResolver iconResolver(context.builtInIcons); AppendDrawPacket( drawData, @@ -92,7 +107,7 @@ void EditorShellDrawComposer::Append( context.shellInteractionState.composeState, palette.shellPalette, metrics.shellMetrics, - &context.builtInIcons); + &iconResolver); }); AppendDrawPacket( drawData, diff --git a/editor/app/Platform/Win32/Chrome/EditorWindowChromeController.cpp b/editor/app/Platform/Win32/Chrome/EditorWindowChromeController.cpp index f99b8e2c..c77d0f3d 100644 --- a/editor/app/Platform/Win32/Chrome/EditorWindowChromeController.cpp +++ b/editor/app/Platform/Win32/Chrome/EditorWindowChromeController.cpp @@ -1,9 +1,9 @@ #include "Platform/Win32/Chrome/EditorWindowChromeController.h" #include "Platform/Win32/Windowing/EditorWindow.h" -#include "Platform/Win32/Runtime/EditorWindowFrameDriver.h" -#include "Platform/Win32/Runtime/EditorWindowRuntimeController.h" #include "Platform/Win32/Windowing/EditorWindowSupport.h" +#include "Windowing/Host/EditorWindowHostCoordinator.h" +#include "Windowing/Runtime/EditorWindowRuntimeController.h" #include #include @@ -250,17 +250,16 @@ void EditorWindowChromeController::InitializeWindowChrome(EditorWindow& window) bool EditorWindowChromeController::HandleSystemCommand( EditorWindow& window, - EditorContext& editorContext, - bool globalTabDragActive, + EditorWindowHostCoordinator& hostCoordinator, WPARAM wParam) { const HWND hwnd = window.GetHwnd(); switch (wParam & 0xFFF0u) { case SC_MAXIMIZE: - ToggleMaximizeRestore(window, editorContext, globalTabDragActive); + ToggleMaximizeRestore(window, hostCoordinator); return true; case SC_RESTORE: if (hwnd != nullptr && !IsIconic(hwnd)) { - ToggleMaximizeRestore(window, editorContext, globalTabDragActive); + ToggleMaximizeRestore(window, hostCoordinator); return true; } return false; @@ -338,8 +337,7 @@ bool EditorWindowChromeController::HandleResizeButtonUp(EditorWindow& window) { bool EditorWindowChromeController::HandleResizePointerMove( EditorWindow& window, - EditorContext& editorContext, - bool globalTabDragActive) { + EditorWindowHostCoordinator& hostCoordinator) { const HWND hwnd = window.GetHwnd(); if (!IsBorderlessResizeActive() || hwnd == nullptr) { return false; @@ -372,10 +370,7 @@ bool EditorWindowChromeController::HandleResizePointerMove( if (window.ApplyWindowResize(static_cast(width), static_cast(height))) { const auto immediateFrameBegin = std::chrono::steady_clock::now(); window.QueueCompletedImmediateFrame( - EditorWindowFrameDriver::DriveImmediateFrame( - window, - editorContext, - globalTabDragActive)); + hostCoordinator.DriveImmediateWindowFrame(window)); const auto immediateFrameEnd = std::chrono::steady_clock::now(); MarkPredictedClientPixelSizePresented(); if (IsVerboseResizeTraceEnabled()) { @@ -535,8 +530,7 @@ bool EditorWindowChromeController::HandleChromeButtonDown(EditorWindow& window, bool EditorWindowChromeController::HandleChromeButtonUp( EditorWindow& window, - EditorContext& editorContext, - bool globalTabDragActive, + EditorWindowHostCoordinator& hostCoordinator, LPARAM lParam) { if (IsBorderlessWindowDragRestoreArmed()) { ClearChromeDragRestoreState(window); @@ -554,15 +548,14 @@ bool EditorWindowChromeController::HandleChromeButtonUp( window.InvalidateHostWindow(); if (pressedTarget == releasedTarget) { - ExecuteChromeAction(window, editorContext, globalTabDragActive, pressedTarget); + ExecuteChromeAction(window, hostCoordinator, pressedTarget); } return true; } bool EditorWindowChromeController::HandleChromeDoubleClick( EditorWindow& window, - EditorContext& editorContext, - bool globalTabDragActive, + EditorWindowHostCoordinator& hostCoordinator, LPARAM lParam) { if (IsBorderlessWindowDragRestoreArmed()) { ClearChromeDragRestoreState(window); @@ -574,16 +567,14 @@ bool EditorWindowChromeController::HandleChromeDoubleClick( ExecuteChromeAction( window, - editorContext, - globalTabDragActive, + hostCoordinator, Host::BorderlessWindowChromeHitTarget::MaximizeRestoreButton); return true; } bool EditorWindowChromeController::HandleChromeDragRestorePointerMove( EditorWindow& window, - EditorContext& editorContext, - bool globalTabDragActive) { + EditorWindowHostCoordinator& hostCoordinator) { const HWND hwnd = window.GetHwnd(); if (!IsBorderlessWindowDragRestoreArmed() || hwnd == nullptr) { return false; @@ -647,7 +638,7 @@ bool EditorWindowChromeController::HandleChromeDragRestorePointerMove( }; SetBorderlessWindowMaximized(false); - ApplyPredictedWindowRectTransition(window, editorContext, globalTabDragActive, targetRect); + ApplyPredictedWindowRectTransition(window, hostCoordinator, targetRect); ClearChromeDragRestoreState(window); SendMessageW(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, 0); return true; @@ -891,8 +882,7 @@ bool EditorWindowChromeController::QueryBorderlessWindowWorkAreaRect( bool EditorWindowChromeController::ApplyPredictedWindowRectTransition( EditorWindow& window, - EditorContext& editorContext, - bool globalTabDragActive, + EditorWindowHostCoordinator& hostCoordinator, const RECT& targetRect) { const HWND hwnd = window.GetHwnd(); if (hwnd == nullptr) { @@ -908,10 +898,7 @@ bool EditorWindowChromeController::ApplyPredictedWindowRectTransition( SetPredictedClientPixelSize(static_cast(width), static_cast(height)); if (window.ApplyWindowResize(static_cast(width), static_cast(height))) { window.QueueCompletedImmediateFrame( - EditorWindowFrameDriver::DriveImmediateFrame( - window, - editorContext, - globalTabDragActive)); + hostCoordinator.DriveImmediateWindowFrame(window)); MarkPredictedClientPixelSizePresented(); } SetWindowPos( @@ -957,8 +944,7 @@ bool EditorWindowChromeController::ApplyWindowTopmost(EditorWindow& window, bool void EditorWindowChromeController::ToggleMaximizeRestore( EditorWindow& window, - EditorContext& editorContext, - bool globalTabDragActive) { + EditorWindowHostCoordinator& hostCoordinator) { if (window.GetHwnd() == nullptr) { return; } @@ -973,7 +959,7 @@ void EditorWindowChromeController::ToggleMaximizeRestore( SetBorderlessWindowRestoreRect(currentRect); SetBorderlessWindowMaximized(true); - ApplyPredictedWindowRectTransition(window, editorContext, globalTabDragActive, workAreaRect); + ApplyPredictedWindowRectTransition(window, hostCoordinator, workAreaRect); return; } @@ -983,13 +969,12 @@ void EditorWindowChromeController::ToggleMaximizeRestore( } SetBorderlessWindowMaximized(false); - ApplyPredictedWindowRectTransition(window, editorContext, globalTabDragActive, restoreRect); + ApplyPredictedWindowRectTransition(window, hostCoordinator, restoreRect); } void EditorWindowChromeController::ExecuteChromeAction( EditorWindow& window, - EditorContext& editorContext, - bool globalTabDragActive, + EditorWindowHostCoordinator& hostCoordinator, Host::BorderlessWindowChromeHitTarget target) { const HWND hwnd = window.GetHwnd(); if (hwnd == nullptr) { @@ -1004,7 +989,7 @@ void EditorWindowChromeController::ExecuteChromeAction( ShowWindow(hwnd, SW_MINIMIZE); break; case Host::BorderlessWindowChromeHitTarget::MaximizeRestoreButton: - ToggleMaximizeRestore(window, editorContext, globalTabDragActive); + ToggleMaximizeRestore(window, hostCoordinator); break; case Host::BorderlessWindowChromeHitTarget::CloseButton: PostMessageW(hwnd, WM_CLOSE, 0, 0); diff --git a/editor/app/Platform/Win32/Chrome/EditorWindowChromeController.h b/editor/app/Platform/Win32/Chrome/EditorWindowChromeController.h index e74570ea..82fff432 100644 --- a/editor/app/Platform/Win32/Chrome/EditorWindowChromeController.h +++ b/editor/app/Platform/Win32/Chrome/EditorWindowChromeController.h @@ -11,8 +11,8 @@ class UIDrawList; namespace XCEngine::UI::Editor::App { -class EditorContext; class EditorWindow; +class EditorWindowHostCoordinator; } // namespace XCEngine::UI::Editor::App @@ -78,8 +78,7 @@ public: bool HandleSystemCommand( EditorWindow& window, - EditorContext& editorContext, - bool globalTabDragActive, + EditorWindowHostCoordinator& hostCoordinator, WPARAM wParam); bool HandleGetMinMaxInfo(const EditorWindow& window, LPARAM lParam) const; LRESULT HandleNcCalcSize(const EditorWindow& window, WPARAM wParam, LPARAM lParam) const; @@ -89,8 +88,7 @@ public: bool HandleResizeButtonUp(EditorWindow& window); bool HandleResizePointerMove( EditorWindow& window, - EditorContext& editorContext, - bool globalTabDragActive); + EditorWindowHostCoordinator& hostCoordinator); void ClearResizeState(EditorWindow& window); void ForceClearResizeState(EditorWindow& window); Host::BorderlessWindowResizeEdge HitTestResizeEdge( @@ -101,18 +99,15 @@ public: bool HandleChromeButtonDown(EditorWindow& window, LPARAM lParam); bool HandleChromeButtonUp( EditorWindow& window, - EditorContext& editorContext, - bool globalTabDragActive, + EditorWindowHostCoordinator& hostCoordinator, LPARAM lParam); bool HandleChromeDoubleClick( EditorWindow& window, - EditorContext& editorContext, - bool globalTabDragActive, + EditorWindowHostCoordinator& hostCoordinator, LPARAM lParam); bool HandleChromeDragRestorePointerMove( EditorWindow& window, - EditorContext& editorContext, - bool globalTabDragActive); + EditorWindowHostCoordinator& hostCoordinator); void ClearChromeDragRestoreState(EditorWindow& window); void ClearChromeState(EditorWindow& window); Host::BorderlessWindowChromeHitTarget HitTestChrome( @@ -131,20 +126,17 @@ public: bool QueryBorderlessWindowWorkAreaRect(const EditorWindow& window, RECT& outRect) const; bool ApplyPredictedWindowRectTransition( EditorWindow& window, - EditorContext& editorContext, - bool globalTabDragActive, + EditorWindowHostCoordinator& hostCoordinator, const RECT& targetRect); void SetWindowTopmost(bool topmost); bool IsWindowTopmost() const; bool ApplyWindowTopmost(EditorWindow& window, bool topmost); void ToggleMaximizeRestore( EditorWindow& window, - EditorContext& editorContext, - bool globalTabDragActive); + EditorWindowHostCoordinator& hostCoordinator); void ExecuteChromeAction( EditorWindow& window, - EditorContext& editorContext, - bool globalTabDragActive, + EditorWindowHostCoordinator& hostCoordinator, Host::BorderlessWindowChromeHitTarget target); private: diff --git a/editor/app/Platform/Win32/Runtime/EditorWindowFrameDriver.cpp b/editor/app/Platform/Win32/Runtime/EditorWindowFrameDriver.cpp deleted file mode 100644 index e4104db5..00000000 --- a/editor/app/Platform/Win32/Runtime/EditorWindowFrameDriver.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "Platform/Win32/Runtime/EditorWindowFrameDriver.h" - -#include "Platform/Win32/Chrome/EditorWindowChromeController.h" -#include "Platform/Win32/Windowing/EditorWindow.h" - -namespace XCEngine::UI::Editor::App { - -EditorWindowFrameTransferRequests EditorWindowFrameDriver::DriveFrameInternal( - EditorWindow& window, - EditorContext& editorContext, - bool globalTabDragActive, - bool requestSkipNextSteadyStateFrame) { - if (!window.IsRenderReady() || - window.GetHwnd() == nullptr || - window.GetLifecycleState() != EditorWindowLifecycleState::Running) { - return {}; - } - - EditorWindowFrameTransferRequests transferRequests = - window.RenderFrame(editorContext, globalTabDragActive); - if (const HWND hwnd = window.GetHwnd(); - hwnd != nullptr && IsWindow(hwnd)) { - ValidateRect(hwnd, nullptr); - } - if (requestSkipNextSteadyStateFrame) { - window.RequestSkipNextSteadyStateFrame(); - } - - return transferRequests; -} - -EditorWindowFrameTransferRequests EditorWindowFrameDriver::DriveFrame( - EditorWindow& window, - EditorContext& editorContext, - bool globalTabDragActive) { - return DriveFrameInternal(window, editorContext, globalTabDragActive, false); -} - -EditorWindowFrameTransferRequests EditorWindowFrameDriver::DriveImmediateFrame( - EditorWindow& window, - EditorContext& editorContext, - bool globalTabDragActive) { - return DriveFrameInternal(window, editorContext, globalTabDragActive, true); -} - -} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Platform/Win32/Runtime/EditorWindowFrameDriver.h b/editor/app/Platform/Win32/Runtime/EditorWindowFrameDriver.h deleted file mode 100644 index 905ddb11..00000000 --- a/editor/app/Platform/Win32/Runtime/EditorWindowFrameDriver.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "Windowing/Frame/EditorWindowTransferRequests.h" - -namespace XCEngine::UI::Editor::App { - -class EditorContext; -class EditorWindow; - -class EditorWindowFrameDriver final { -public: - static EditorWindowFrameTransferRequests DriveFrame( - EditorWindow& window, - EditorContext& editorContext, - bool globalTabDragActive); - static EditorWindowFrameTransferRequests DriveImmediateFrame( - EditorWindow& window, - EditorContext& editorContext, - bool globalTabDragActive); - -private: - static EditorWindowFrameTransferRequests DriveFrameInternal( - EditorWindow& window, - EditorContext& editorContext, - bool globalTabDragActive, - bool requestSkipNextSteadyStateFrame); -}; - -} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Platform/Win32/Windowing/EditorWindow.cpp b/editor/app/Platform/Win32/Windowing/EditorWindow.cpp index d22e79e5..830d5524 100644 --- a/editor/app/Platform/Win32/Windowing/EditorWindow.cpp +++ b/editor/app/Platform/Win32/Windowing/EditorWindow.cpp @@ -1,12 +1,10 @@ #include "Platform/Win32/Windowing/EditorWindow.h" #include "Bootstrap/EditorResources.h" #include "Platform/Win32/Chrome/EditorWindowChromeController.h" -#include "Windowing/Content/EditorWindowContentController.h" -#include "Platform/Win32/Runtime/EditorWindowFrameDriver.h" #include "Platform/Win32/Windowing/EditorWindowSession.h" #include "Platform/Win32/Windowing/EditorWindowSupport.h" #include "Platform/Win32/Runtime/EditorWindowInputController.h" -#include "Platform/Win32/Runtime/EditorWindowRuntimeController.h" +#include "Windowing/Runtime/EditorWindowRuntimeController.h" #include #include #include @@ -88,7 +86,7 @@ EditorWindow::EditorWindow( EditorWindowCategory category, EditorWindowChromePolicy chromePolicy, bool primary, - std::unique_ptr contentController) + std::unique_ptr runtimeController) : m_session(std::make_unique( std::move(windowId), std::move(title), @@ -97,8 +95,7 @@ EditorWindow::EditorWindow( primary)) , m_chromeController(std::make_unique()) , m_inputController(std::make_unique()) - , m_runtime(std::make_unique( - std::move(contentController))) {} + , m_runtime(std::move(runtimeController)) {} EditorWindow::~EditorWindow() = default; @@ -226,11 +223,8 @@ void EditorWindow::InvalidateHostWindow() const { } } -bool EditorWindow::Initialize( - const std::filesystem::path& repoRoot, - EditorContext& editorContext, - const std::filesystem::path& captureRoot, - bool autoCaptureOnStartup) { +bool EditorWindow::InitializeRuntime( + const EditorHostWindowRuntimeInitializationParams& params) { if (m_session->GetHwnd() == nullptr) { LogRuntimeTrace("app", "window initialize skipped: hwnd is null"); return false; @@ -250,10 +244,9 @@ bool EditorWindow::Initialize( MarkInitializing(); const bool initialized = m_runtime->Initialize( m_session->GetHwnd(), - repoRoot, - editorContext, - captureRoot, - autoCaptureOnStartup); + params.repoRoot, + params.captureRoot, + params.autoCaptureOnStartup); if (initialized) { MarkRunning(); } else { @@ -614,8 +607,7 @@ std::uint8_t ResolveExpectedShellCaptureButtons( } // namespace -EditorWindowFrameTransferRequests EditorWindow::RenderFrame( - EditorContext& editorContext, +EditorWindowFrameTransferRequests EditorWindow::RenderHostFrame( bool globalTabDragActive) { if (!m_runtime->IsReady() || m_session->GetHwnd() == nullptr) { return {}; @@ -638,12 +630,12 @@ EditorWindowFrameTransferRequests EditorWindow::RenderFrame( kShellSurfaceColor); EditorWindowFrameTransferRequests transferRequests = {}; - if (m_runtime->IsEditorContextValid(editorContext)) { + if (m_runtime->IsEditorContextValid()) { transferRequests = - RenderRuntimeFrame(editorContext, globalTabDragActive, workspaceBounds, drawData); + RenderRuntimeFrame(globalTabDragActive, workspaceBounds, drawData); } else { UIDrawList& invalidDrawList = drawData.EmplaceDrawList("XCEditorWindow.Invalid"); - m_runtime->AppendInvalidFrame(editorContext, invalidDrawList); + m_runtime->AppendInvalidFrame(invalidDrawList); } UIDrawList& windowChromeDrawList = drawData.EmplaceDrawList("XCEditorWindow.Chrome"); @@ -656,22 +648,11 @@ EditorWindowFrameTransferRequests EditorWindow::RenderFrame( return transferRequests; } -EditorWindowFrameTransferRequests EditorWindow::OnPaintMessage( - EditorContext& editorContext, - bool globalTabDragActive) { - if (!m_runtime->IsReady() || m_session->GetHwnd() == nullptr) { - return {}; +void EditorWindow::ValidateHostFrame() const { + if (const HWND hwnd = m_session->GetHwnd(); + hwnd != nullptr && IsWindow(hwnd)) { + ValidateRect(hwnd, nullptr); } - - PAINTSTRUCT paintStruct = {}; - BeginPaint(m_session->GetHwnd(), &paintStruct); - const EditorWindowFrameTransferRequests transferRequests = - EditorWindowFrameDriver::DriveImmediateFrame( - *this, - editorContext, - globalTabDragActive); - EndPaint(m_session->GetHwnd(), &paintStruct); - return transferRequests; } void EditorWindow::QueueCompletedImmediateFrame( @@ -710,7 +691,6 @@ UIRect EditorWindow::ResolveWorkspaceBounds(float clientWidthDips, float clientH } EditorWindowFrameTransferRequests EditorWindow::RenderRuntimeFrame( - EditorContext& editorContext, bool globalTabDragActive, const UIRect& workspaceBounds, UIDrawData& drawData) { @@ -718,7 +698,7 @@ EditorWindowFrameTransferRequests EditorWindow::RenderRuntimeFrame( std::vector frameEvents = m_inputController->TakePendingEvents(); const bool useDetachedTitleBarTabStrip = m_chromeController->ShouldUseDetachedTitleBarTabStrip(*this); - m_runtime->PrepareEditorContext(editorContext); + m_runtime->PrepareEditorContext(); const Host::D3D12WindowRenderLoopFrameContext frameContext = m_runtime->BeginFrame(); if (!frameContext.warning.empty()) { LogRuntimeTrace("viewport", frameContext.warning); @@ -726,16 +706,12 @@ EditorWindowFrameTransferRequests EditorWindow::RenderRuntimeFrame( const EditorWindowFrameTransferRequests transferRequests = m_runtime->UpdateAndAppend( - EditorWindowContentFrameContext{ - .editorContext = editorContext, - .bounds = workspaceBounds, - .inputEvents = frameEvents, - .cursorScreenPoint = QueryCursorScreenPoint(), - .captureStatusText = m_runtime->BuildCaptureStatusText(), - .primary = m_session->IsPrimary(), - .globalTabDragActive = globalTabDragActive, - .useDetachedTitleBarTabStrip = useDetachedTitleBarTabStrip, - }, + workspaceBounds, + frameEvents, + QueryCursorScreenPoint(), + m_session->IsPrimary(), + globalTabDragActive, + useDetachedTitleBarTabStrip, drawData); if (frameContext.canRenderViewports) { m_runtime->RenderRequestedViewports(frameContext.renderContext); diff --git a/editor/app/Platform/Win32/Windowing/EditorWindow.h b/editor/app/Platform/Win32/Windowing/EditorWindow.h index 2b04e6f1..1d73f68a 100644 --- a/editor/app/Platform/Win32/Windowing/EditorWindow.h +++ b/editor/app/Platform/Win32/Windowing/EditorWindow.h @@ -49,11 +49,8 @@ struct UIEditorDockHostDropPreviewState; namespace XCEngine::UI::Editor::App { -class EditorContext; -class EditorWindowContentController; class EditorWindowChromeController; class EditorWindowDockHostBinding; -class EditorWindowFrameDriver; class EditorWindowHostRuntime; class EditorWindowInputController; class EditorWindowLifecycleCoordinator; @@ -70,7 +67,7 @@ public: EditorWindowCategory category, EditorWindowChromePolicy chromePolicy, bool primary, - std::unique_ptr contentController); + std::unique_ptr runtimeController); ~EditorWindow(); EditorWindow(const EditorWindow&) = delete; @@ -100,7 +97,6 @@ public: private: friend class EditorWindowChromeController; - friend class EditorWindowFrameDriver; friend class EditorWindowHostRuntime; friend class EditorWindowMessageDispatcher; friend class EditorWindowLifecycleCoordinator; @@ -127,11 +123,8 @@ private: void ApplyHostWindowTitle() override; void RefreshWorkspaceProjection(EditorWorkspaceWindowProjection projection) override; - bool Initialize( - const std::filesystem::path& repoRoot, - EditorContext& editorContext, - const std::filesystem::path& captureRoot, - bool autoCaptureOnStartup); + bool InitializeRuntime( + const EditorHostWindowRuntimeInitializationParams& params) override; void Shutdown() override; void ResetInteractionState() override; bool TryGetHostScreenRect(EditorWindowScreenRect& outRect) const override; @@ -140,18 +133,15 @@ private: void PostCloseToHost() override; void DestroyHostWindow() override; - EditorWindowFrameTransferRequests RenderFrame( - EditorContext& editorContext, - bool globalTabDragActive); - EditorWindowFrameTransferRequests OnPaintMessage( - EditorContext& editorContext, - bool globalTabDragActive); + EditorWindowFrameTransferRequests RenderHostFrame( + bool globalTabDragActive) override; + void ValidateHostFrame() const override; void QueueCompletedImmediateFrame( EditorWindowFrameTransferRequests transferRequests); bool HasQueuedCompletedImmediateFrame() const; EditorWindowFrameTransferRequests ConsumeQueuedCompletedImmediateFrameTransferRequests(); - void RequestSkipNextSteadyStateFrame(); - bool ConsumeSkipNextSteadyStateFrame(); + void RequestSkipNextSteadyStateFrame() override; + bool ConsumeSkipNextSteadyStateFrame() override; bool OnResize(UINT width, UINT height); void OnEnterSizeMove(); bool OnExitSizeMove(); @@ -186,7 +176,6 @@ private: static POINT ToNativePoint(const EditorWindowScreenPoint& screenPoint); static EditorWindowScreenPoint FromNativePoint(const POINT& screenPoint); EditorWindowFrameTransferRequests RenderRuntimeFrame( - EditorContext& editorContext, bool globalTabDragActive, const ::XCEngine::UI::UIRect& workspaceBounds, ::XCEngine::UI::UIDrawData& drawData); diff --git a/editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.cpp b/editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.cpp index 35453b18..c6463ed9 100644 --- a/editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.cpp +++ b/editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.cpp @@ -4,9 +4,8 @@ #include "Platform/Win32/Chrome/EditorWindowChromeController.h" #include "Platform/Win32/Windowing/EditorFloatingWindowPlacement.h" #include "Platform/Win32/Windowing/EditorWindow.h" -#include "Windowing/Content/EditorWindowContentController.h" #include "Windowing/Host/EditorWindowHostCoordinator.h" -#include "Platform/Win32/Runtime/EditorWindowFrameDriver.h" +#include "Windowing/Runtime/EditorWindowRuntimeController.h" #include @@ -63,15 +62,15 @@ EditorWindowHostRuntime::EditorWindowHostRuntime( EditorWindowHostRuntime::~EditorWindowHostRuntime() = default; EditorWindow* EditorWindowHostRuntime::CreateHostWindow( - std::unique_ptr contentController, + std::unique_ptr runtimeController, const EditorWindowCreateParams& params) { - if (contentController == nullptr) { - LogRuntimeTrace("window", "window creation failed: content controller is null"); + if (runtimeController == nullptr) { + LogRuntimeTrace("window", "window creation failed: runtime controller is null"); return nullptr; } const EditorWindowContentCapabilities capabilities = - contentController->GetCapabilities(); + runtimeController->GetCapabilities(); if (params.category == EditorWindowCategory::Workspace && !capabilities.workspace) { LogRuntimeTrace("window", "workspace window creation rejected: content is not workspace"); return nullptr; @@ -87,7 +86,7 @@ EditorWindow* EditorWindowHostRuntime::CreateHostWindow( params.category, params.chromePolicy, params.primary, - std::move(contentController)); + std::move(runtimeController)); EditorWindow* const rawWindow = windowPtr.get(); m_windows.push_back(std::move(windowPtr)); @@ -177,11 +176,13 @@ EditorWindow* EditorWindowHostRuntime::CreateHostWindow( return failWindowInitialization("managed window initialization failed: coordinator missing"); } - if (!rawWindow->Initialize( - m_repoRoot, - m_hostCoordinator->GetEditorContext(), - m_captureRoot, - params.autoCaptureOnStartup)) { + if (!m_hostCoordinator->InitializeHostWindow( + *rawWindow, + EditorHostWindowRuntimeInitializationParams{ + .repoRoot = m_repoRoot, + .captureRoot = m_captureRoot, + .autoCaptureOnStartup = params.autoCaptureOnStartup, + })) { return failWindowInitialization("managed window initialization failed"); } @@ -313,60 +314,6 @@ std::string EditorWindowHostRuntime::DescribeWindows() const { return DescribeHostWindows(m_windows); } -void EditorWindowHostRuntime::RenderAllWindows() { - if (m_hostCoordinator == nullptr) { - return; - } - - struct WindowFrameTransferBatch { - EditorWindow* sourceWindow = nullptr; - EditorWindowFrameTransferRequests requests = {}; - }; - - std::vector transferBatches = {}; - transferBatches.reserve(m_windows.size()); - - for (const std::unique_ptr& window : m_windows) { - if (window == nullptr || - !window->HasLiveHostWindow() || - window->GetLifecycleState() != EditorWindowLifecycleState::Running) { - continue; - } - - if (window->ConsumeSkipNextSteadyStateFrame()) { - m_hostCoordinator->RefreshWindowPresentation(*window); - continue; - } - - EditorWindowFrameTransferRequests transferRequests = - EditorWindowFrameDriver::DriveFrame( - *window, - m_hostCoordinator->GetEditorContext(), - m_hostCoordinator->IsGlobalTabDragActive()); - m_hostCoordinator->RefreshWindowPresentation(*window); - if (!transferRequests.HasPendingRequests()) { - continue; - } - - transferBatches.push_back(WindowFrameTransferBatch{ - window.get(), - std::move(transferRequests), - }); - } - - for (WindowFrameTransferBatch& batch : transferBatches) { - if (batch.sourceWindow == nullptr || - !batch.sourceWindow->HasLiveHostWindow() || - batch.sourceWindow->GetLifecycleState() != EditorWindowLifecycleState::Running) { - continue; - } - - m_hostCoordinator->DispatchWindowFrameTransferRequests( - *batch.sourceWindow, - batch.requests); - } -} - EditorWindow* EditorWindowHostRuntime::FindWindow(HWND hwnd) { if (hwnd == nullptr) { return nullptr; diff --git a/editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.h b/editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.h index fc259d0d..71c8dc7c 100644 --- a/editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.h +++ b/editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.h @@ -13,7 +13,7 @@ namespace XCEngine::UI::Editor::App { class EditorWindow; -class EditorWindowContentController; +class EditorWindowRuntimeController; class EditorWindowHostRuntime final : public EditorWindowHostRuntimeServices { public: @@ -24,7 +24,7 @@ public: ~EditorWindowHostRuntime(); EditorWindow* CreateHostWindow( - std::unique_ptr contentController, + std::unique_ptr runtimeController, const EditorWindowCreateParams& params) override; void BindHostCoordinator(EditorWindowHostCoordinator& hostCoordinator) override; void HandlePendingNativeWindowCreated(HWND hwnd); @@ -51,7 +51,6 @@ public: const EditorWindowScreenPoint& screenPoint) const override; void ReapDestroyedWindows() override; std::string DescribeWindows() const override; - void RenderAllWindows() override; const EditorWindowHostConfig& GetHostConfig() const { return m_hostConfig; diff --git a/editor/app/Platform/Win32/Windowing/EditorWindowMessageDispatcher.cpp b/editor/app/Platform/Win32/Windowing/EditorWindowMessageDispatcher.cpp index 35dc3e87..bab4c5b0 100644 --- a/editor/app/Platform/Win32/Windowing/EditorWindowMessageDispatcher.cpp +++ b/editor/app/Platform/Win32/Windowing/EditorWindowMessageDispatcher.cpp @@ -4,8 +4,7 @@ #include "Platform/Win32/Chrome/EditorWindowChromeController.h" #include "Platform/Win32/Runtime/EditorWindowInputController.h" #include "Platform/Win32/Windowing/EditorWindow.h" -#include "Platform/Win32/Runtime/EditorWindowFrameDriver.h" -#include "Platform/Win32/Runtime/EditorWindowRuntimeController.h" +#include "Windowing/Runtime/EditorWindowRuntimeController.h" #include "Platform/Win32/Windowing/EditorWindowPointerCapture.h" #include "Windowing/Host/EditorWindowHostCoordinator.h" @@ -61,10 +60,7 @@ void EditorWindowMessageDispatcher::FlushQueuedCompletedImmediateFrame( void EditorWindowMessageDispatcher::RenderAndHandleWindowFrame(const DispatchContext& context) { FinalizeImmediateFrame( context, - EditorWindowFrameDriver::DriveImmediateFrame( - context.window, - context.hostCoordinator.GetEditorContext(), - context.hostCoordinator.IsGlobalTabDragActive())); + context.hostCoordinator.DriveImmediateWindowFrame(context.window)); } bool EditorWindowMessageDispatcher::EnsureTrackingMouseLeave(const DispatchContext& context) { @@ -152,8 +148,7 @@ bool EditorWindowMessageDispatcher::TryDispatchWindowPointerMessage( inputController.GetPointerCaptureOwner()) && chromeController.HandleResizePointerMove( context.window, - context.hostCoordinator.GetEditorContext(), - context.hostCoordinator.IsGlobalTabDragActive())) { + context.hostCoordinator)) { outResult = 0; return true; } @@ -161,8 +156,7 @@ bool EditorWindowMessageDispatcher::TryDispatchWindowPointerMessage( inputController.GetPointerCaptureOwner()) && chromeController.HandleChromeDragRestorePointerMove( context.window, - context.hostCoordinator.GetEditorContext(), - context.hostCoordinator.IsGlobalTabDragActive())) { + context.hostCoordinator)) { outResult = 0; return true; } @@ -247,8 +241,7 @@ bool EditorWindowMessageDispatcher::TryDispatchWindowPointerMessage( inputController.GetPointerCaptureOwner()) && chromeController.HandleChromeButtonUp( context.window, - context.hostCoordinator.GetEditorContext(), - context.hostCoordinator.IsGlobalTabDragActive(), + context.hostCoordinator, lParam)) { outResult = 0; return true; @@ -279,8 +272,7 @@ bool EditorWindowMessageDispatcher::TryDispatchWindowPointerMessage( case WM_LBUTTONDBLCLK: if (chromeController.HandleChromeDoubleClick( context.window, - context.hostCoordinator.GetEditorContext(), - context.hostCoordinator.IsGlobalTabDragActive(), + context.hostCoordinator, lParam)) { outResult = 0; return true; @@ -437,13 +429,16 @@ bool EditorWindowMessageDispatcher::TryDispatchWindowLifecycleMessage( outResult = 0; return true; case WM_PAINT: - FinalizeImmediateFrame( - context, - context.window.OnPaintMessage( - context.hostCoordinator.GetEditorContext(), - context.hostCoordinator.IsGlobalTabDragActive())); + { + PAINTSTRUCT paintStruct = {}; + BeginPaint(context.hwnd, &paintStruct); + const EditorWindowFrameTransferRequests transferRequests = + context.hostCoordinator.DriveImmediateWindowFrame(context.window); + EndPaint(context.hwnd, &paintStruct); + FinalizeImmediateFrame(context, transferRequests); outResult = 0; return true; + } case WM_ERASEBKGND: outResult = 1; return true; @@ -488,8 +483,7 @@ bool EditorWindowMessageDispatcher::TryDispatchWindowChromeMessage( case WM_SYSCOMMAND: if (chromeController.HandleSystemCommand( context.window, - context.hostCoordinator.GetEditorContext(), - context.hostCoordinator.IsGlobalTabDragActive(), + context.hostCoordinator, wParam)) { outResult = 0; return true; diff --git a/editor/app/Windowing/Coordinator/EditorUtilityWindowCoordinator.cpp b/editor/app/Windowing/Coordinator/EditorUtilityWindowCoordinator.cpp index d194f15e..54afe2cf 100644 --- a/editor/app/Windowing/Coordinator/EditorUtilityWindowCoordinator.cpp +++ b/editor/app/Windowing/Coordinator/EditorUtilityWindowCoordinator.cpp @@ -4,6 +4,7 @@ #include "Windowing/Content/EditorWindowContentController.h" #include "Windowing/Content/EditorWindowContentFactory.h" #include "Windowing/Coordinator/EditorWindowLifecycleCoordinator.h" +#include "Windowing/Runtime/EditorWindowRuntimeController.h" #include @@ -26,9 +27,11 @@ int ResolveOuterDimension(float value, int fallback) { } EditorUtilityWindowCoordinator::EditorUtilityWindowCoordinator( + EditorContext& editorContext, EditorWindowHost& hostRuntime, EditorWindowContentFactory& contentFactory) : m_hostRuntime(hostRuntime), + m_editorContext(editorContext), m_contentFactory(contentFactory) {} EditorUtilityWindowCoordinator::~EditorUtilityWindowCoordinator() = default; @@ -122,7 +125,9 @@ bool EditorUtilityWindowCoordinator::TryProcessOpenUtilityWindowRequest( } EditorHostWindow* utilityWindow = m_hostRuntime.CreateHostWindow( - m_contentFactory.CreateUtilityContentController(*descriptor), + std::make_unique( + m_editorContext, + m_contentFactory.CreateUtilityContentController(*descriptor)), createParams); if (utilityWindow == nullptr) { LogRuntimeTrace("utility", "failed to create utility window"); diff --git a/editor/app/Windowing/Coordinator/EditorUtilityWindowCoordinator.h b/editor/app/Windowing/Coordinator/EditorUtilityWindowCoordinator.h index 5de58a27..9a8112f3 100644 --- a/editor/app/Windowing/Coordinator/EditorUtilityWindowCoordinator.h +++ b/editor/app/Windowing/Coordinator/EditorUtilityWindowCoordinator.h @@ -7,12 +7,14 @@ namespace XCEngine::UI::Editor::App { +class EditorContext; class EditorWindowContentFactory; class EditorWindowLifecycleCoordinator; class EditorUtilityWindowCoordinator final { public: EditorUtilityWindowCoordinator( + EditorContext& editorContext, EditorWindowHost& hostRuntime, EditorWindowContentFactory& contentFactory); ~EditorUtilityWindowCoordinator(); @@ -29,6 +31,7 @@ private: void LogRuntimeTrace(std::string_view channel, std::string_view message) const; EditorWindowHost& m_hostRuntime; + EditorContext& m_editorContext; EditorWindowContentFactory& m_contentFactory; EditorWindowLifecycleCoordinator* m_lifecycleCoordinator = nullptr; }; diff --git a/editor/app/Windowing/Coordinator/EditorWindowWorkspaceCoordinator.cpp b/editor/app/Windowing/Coordinator/EditorWindowWorkspaceCoordinator.cpp index b08e92b1..6128ca84 100644 --- a/editor/app/Windowing/Coordinator/EditorWindowWorkspaceCoordinator.cpp +++ b/editor/app/Windowing/Coordinator/EditorWindowWorkspaceCoordinator.cpp @@ -3,6 +3,7 @@ #include "Windowing/Content/EditorWindowContentFactory.h" #include "Windowing/Content/EditorWindowContentController.h" #include "Windowing/Coordinator/EditorWindowLifecycleCoordinator.h" +#include "Windowing/Runtime/EditorWindowRuntimeController.h" #include #include @@ -88,10 +89,12 @@ std::string DescribeWindowSetState(const UIEditorWindowWorkspaceSet& windowSet) } // namespace EditorWindowWorkspaceCoordinator::EditorWindowWorkspaceCoordinator( + EditorContext& editorContext, EditorWindowHost& hostRuntime, EditorWindowSystem& windowSystem, EditorWindowContentFactory& contentFactory) : m_hostRuntime(hostRuntime), + m_editorContext(editorContext), m_windowSystem(windowSystem), m_contentFactory(contentFactory) {} @@ -365,7 +368,9 @@ bool EditorWindowWorkspaceCoordinator::ApplySynchronizationPlan( } EditorHostWindow* const createdWindow = m_hostRuntime.CreateHostWindow( - m_contentFactory.CreateWorkspaceContentController(action.create.windowState), + std::make_unique( + m_editorContext, + m_contentFactory.CreateWorkspaceContentController(action.create.windowState)), createParams); if (createdWindow == nullptr) { for (const ExistingWindowSnapshot& snapshot : existingWindowSnapshots) { diff --git a/editor/app/Windowing/Coordinator/EditorWindowWorkspaceCoordinator.h b/editor/app/Windowing/Coordinator/EditorWindowWorkspaceCoordinator.h index 6cad5129..b8a7f885 100644 --- a/editor/app/Windowing/Coordinator/EditorWindowWorkspaceCoordinator.h +++ b/editor/app/Windowing/Coordinator/EditorWindowWorkspaceCoordinator.h @@ -22,12 +22,14 @@ class EditorWindowSystem; namespace XCEngine::UI::Editor::App { +class EditorContext; class EditorWindowContentFactory; class EditorWindowLifecycleCoordinator; class EditorWindowWorkspaceCoordinator final { public: EditorWindowWorkspaceCoordinator( + EditorContext& editorContext, EditorWindowHost& hostRuntime, EditorWindowSystem& windowSystem, EditorWindowContentFactory& contentFactory); @@ -114,6 +116,7 @@ private: void LogRuntimeTrace(std::string_view channel, std::string_view message) const; EditorWindowHost& m_hostRuntime; + EditorContext& m_editorContext; EditorWindowSystem& m_windowSystem; EditorWindowContentFactory& m_contentFactory; EditorWindowLifecycleCoordinator* m_lifecycleCoordinator = nullptr; diff --git a/editor/app/Windowing/EditorWindowManager.cpp b/editor/app/Windowing/EditorWindowManager.cpp index 3a8d9df9..95562f5a 100644 --- a/editor/app/Windowing/EditorWindowManager.cpp +++ b/editor/app/Windowing/EditorWindowManager.cpp @@ -5,10 +5,12 @@ #include "Windowing/Coordinator/EditorWindowLifecycleCoordinator.h" #include "Windowing/Coordinator/EditorUtilityWindowCoordinator.h" #include "Windowing/Coordinator/EditorWindowWorkspaceCoordinator.h" +#include "Windowing/Runtime/EditorWindowRuntimeController.h" #include #include +#include namespace XCEngine::UI::Editor::App { @@ -21,11 +23,13 @@ EditorWindowManager::EditorWindowManager( m_contentFactory = CreateDefaultEditorWindowContentFactory(windowSystem); m_workspaceCoordinator = std::make_unique( + m_editorContext, m_hostRuntime, windowSystem, *m_contentFactory); m_utilityCoordinator = std::make_unique( + m_editorContext, m_hostRuntime, *m_contentFactory); m_lifecycleCoordinator = std::make_unique( @@ -52,7 +56,9 @@ EditorHostWindow* EditorWindowManager::CreateWorkspaceWindow( } EditorHostWindow* const window = m_hostRuntime.CreateHostWindow( - m_contentFactory->CreateWorkspaceContentController(windowState), + std::make_unique( + m_editorContext, + m_contentFactory->CreateWorkspaceContentController(windowState)), params); if (window != nullptr) { m_workspaceCoordinator->RegisterExistingWindow(*window); @@ -68,7 +74,9 @@ EditorHostWindow* EditorWindowManager::CreateUtilityWindow( } EditorHostWindow* const window = m_hostRuntime.CreateHostWindow( - m_contentFactory->CreateUtilityContentController(descriptor), + std::make_unique( + m_editorContext, + m_contentFactory->CreateUtilityContentController(descriptor)), params); if (window != nullptr) { m_workspaceCoordinator->RegisterExistingWindow(*window); @@ -107,15 +115,85 @@ void EditorWindowManager::DestroyClosedWindows() { } void EditorWindowManager::RenderAllWindows() { - m_hostRuntime.RenderAllWindows(); + struct WindowFrameTransferBatch { + EditorHostWindow* sourceWindow = nullptr; + EditorWindowFrameTransferRequests requests = {}; + }; + + std::vector transferBatches = {}; + const std::vector windows = m_hostRuntime.GetWindows(); + transferBatches.reserve(windows.size()); + + for (EditorHostWindow* window : windows) { + if (window == nullptr || + !window->HasLiveHostWindow() || + window->GetLifecycleState() != EditorWindowLifecycleState::Running) { + continue; + } + + if (window->ConsumeSkipNextSteadyStateFrame()) { + RefreshWindowPresentation(*window); + continue; + } + + EditorWindowFrameTransferRequests transferRequests = DriveWindowFrame(*window); + RefreshWindowPresentation(*window); + if (!transferRequests.HasPendingRequests()) { + continue; + } + + transferBatches.push_back(WindowFrameTransferBatch{ + window, + std::move(transferRequests), + }); + } + + for (WindowFrameTransferBatch& batch : transferBatches) { + if (batch.sourceWindow == nullptr || + !batch.sourceWindow->HasLiveHostWindow() || + batch.sourceWindow->GetLifecycleState() != EditorWindowLifecycleState::Running) { + continue; + } + + DispatchWindowFrameTransferRequests( + *batch.sourceWindow, + batch.requests); + } } -EditorContext& EditorWindowManager::GetEditorContext() { - return m_editorContext; +bool EditorWindowManager::InitializeHostWindow( + EditorHostWindow& window, + const EditorHostWindowRuntimeInitializationParams& params) { + return window.InitializeRuntime(params); } -const EditorContext& EditorWindowManager::GetEditorContext() const { - return m_editorContext; +EditorWindowFrameTransferRequests EditorWindowManager::DriveWindowFrame( + EditorHostWindow& window) { + return DriveWindowFrameInternal(window, false); +} + +EditorWindowFrameTransferRequests EditorWindowManager::DriveImmediateWindowFrame( + EditorHostWindow& window) { + return DriveWindowFrameInternal(window, true); +} + +EditorWindowFrameTransferRequests EditorWindowManager::DriveWindowFrameInternal( + EditorHostWindow& window, + bool requestSkipNextSteadyStateFrame) { + if (!window.IsRenderReady() || + !window.HasLiveHostWindow() || + window.GetLifecycleState() != EditorWindowLifecycleState::Running) { + return {}; + } + + EditorWindowFrameTransferRequests transferRequests = + window.RenderHostFrame(IsGlobalTabDragActive()); + window.ValidateHostFrame(); + if (requestSkipNextSteadyStateFrame) { + window.RequestSkipNextSteadyStateFrame(); + } + + return transferRequests; } bool EditorWindowManager::IsGlobalTabDragActive() const { diff --git a/editor/app/Windowing/EditorWindowManager.h b/editor/app/Windowing/EditorWindowManager.h index 42a1ce12..f81ce152 100644 --- a/editor/app/Windowing/EditorWindowManager.h +++ b/editor/app/Windowing/EditorWindowManager.h @@ -55,8 +55,12 @@ public: void RenderAllWindows(); private: - EditorContext& GetEditorContext() override; - const EditorContext& GetEditorContext() const override; + bool InitializeHostWindow( + EditorHostWindow& window, + const EditorHostWindowRuntimeInitializationParams& params) override; + EditorWindowFrameTransferRequests DriveWindowFrame(EditorHostWindow& window) override; + EditorWindowFrameTransferRequests DriveImmediateWindowFrame( + EditorHostWindow& window) override; bool IsGlobalTabDragActive() const override; bool OwnsActiveGlobalTabDrag(std::string_view windowId) const override; bool HandleGlobalTabDragPointerMove(EditorHostWindow& window) override; @@ -71,6 +75,10 @@ private: void AbortUnregisteredWindow(EditorHostWindow& window) override; void ReapDestroyedWindows() override; + EditorWindowFrameTransferRequests DriveWindowFrameInternal( + EditorHostWindow& window, + bool requestSkipNextSteadyStateFrame); + EditorContext& m_editorContext; EditorWindowHostRuntimeServices& m_hostRuntime; std::unique_ptr m_contentFactory = {}; diff --git a/editor/app/Windowing/Host/EditorWindowHostCoordinator.h b/editor/app/Windowing/Host/EditorWindowHostCoordinator.h index f04ebf7f..78139a5b 100644 --- a/editor/app/Windowing/Host/EditorWindowHostCoordinator.h +++ b/editor/app/Windowing/Host/EditorWindowHostCoordinator.h @@ -3,18 +3,18 @@ #include "Windowing/Frame/EditorWindowTransferRequests.h" #include "Windowing/Host/EditorWindowHostInterfaces.h" -#include - namespace XCEngine::UI::Editor::App { -class EditorContext; - class EditorWindowHostCoordinator { public: virtual ~EditorWindowHostCoordinator() = default; - virtual EditorContext& GetEditorContext() = 0; - virtual const EditorContext& GetEditorContext() const = 0; + virtual bool InitializeHostWindow( + EditorHostWindow& window, + const EditorHostWindowRuntimeInitializationParams& params) = 0; + virtual EditorWindowFrameTransferRequests DriveWindowFrame(EditorHostWindow& window) = 0; + virtual EditorWindowFrameTransferRequests DriveImmediateWindowFrame( + EditorHostWindow& window) = 0; virtual bool IsGlobalTabDragActive() const = 0; virtual bool OwnsActiveGlobalTabDrag(std::string_view windowId) const = 0; diff --git a/editor/app/Windowing/Host/EditorWindowHostInterfaces.h b/editor/app/Windowing/Host/EditorWindowHostInterfaces.h index 1accf8b9..6f618b06 100644 --- a/editor/app/Windowing/Host/EditorWindowHostInterfaces.h +++ b/editor/app/Windowing/Host/EditorWindowHostInterfaces.h @@ -1,6 +1,7 @@ #pragma once #include "Windowing/EditorWindowShared.h" +#include "Windowing/Frame/EditorWindowTransferRequests.h" #include "Windowing/Host/EditorWindowHostTypes.h" #include "Windowing/Host/EditorWindowPointerCapture.h" #include "Windowing/Host/EditorWindowTypes.h" @@ -8,6 +9,7 @@ #include #include +#include #include #include #include @@ -27,8 +29,14 @@ struct UIEditorDockHostTabDropTarget; namespace XCEngine::UI::Editor::App { class EditorWindowHostCoordinator; -class EditorWindowContentController; class EditorWindowDockHostBinding; +class EditorWindowRuntimeController; + +struct EditorHostWindowRuntimeInitializationParams { + std::filesystem::path repoRoot = {}; + std::filesystem::path captureRoot = {}; + bool autoCaptureOnStartup = false; +}; class EditorHostWindow { public: @@ -67,6 +75,13 @@ public: virtual void MarkClosing() = 0; virtual void MarkDestroyed() = 0; virtual bool IsRenderReady() const = 0; + virtual bool InitializeRuntime( + const EditorHostWindowRuntimeInitializationParams& params) = 0; + virtual EditorWindowFrameTransferRequests RenderHostFrame( + bool globalTabDragActive) = 0; + virtual void ValidateHostFrame() const = 0; + virtual void RequestSkipNextSteadyStateFrame() = 0; + virtual bool ConsumeSkipNextSteadyStateFrame() = 0; virtual void Shutdown() = 0; virtual bool TryGetHostScreenRect(EditorWindowScreenRect& outRect) const = 0; virtual void SetHostScreenPosition(const EditorWindowScreenPoint& screenPoint) = 0; @@ -80,7 +95,7 @@ public: virtual ~EditorWindowHost() = default; virtual EditorHostWindow* CreateHostWindow( - std::unique_ptr contentController, + std::unique_ptr runtimeController, const EditorWindowCreateParams& params) = 0; virtual EditorHostWindow* FindWindowById(std::string_view windowId) = 0; virtual const EditorHostWindow* FindWindowById(std::string_view windowId) const = 0; @@ -106,7 +121,6 @@ public: virtual ~EditorWindowHostRuntimeServices() = default; virtual void BindHostCoordinator(EditorWindowHostCoordinator& hostCoordinator) = 0; - virtual void RenderAllWindows() = 0; }; } // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Platform/Win32/Runtime/EditorWindowRuntimeController.cpp b/editor/app/Windowing/Runtime/EditorWindowRuntimeController.cpp similarity index 87% rename from editor/app/Platform/Win32/Runtime/EditorWindowRuntimeController.cpp rename to editor/app/Windowing/Runtime/EditorWindowRuntimeController.cpp index fba2e142..35992765 100644 --- a/editor/app/Platform/Win32/Runtime/EditorWindowRuntimeController.cpp +++ b/editor/app/Windowing/Runtime/EditorWindowRuntimeController.cpp @@ -1,33 +1,41 @@ -#include "Platform/Win32/Runtime/EditorWindowRuntimeController.h" +#include "Windowing/Runtime/EditorWindowRuntimeController.h" #include "Bootstrap/EditorResources.h" -#include "Platform/Win32/Windowing/EditorWindowSupport.h" #include "Support/EmbeddedPngLoader.h" +#include "Support/TextFormat.h" #include +#include #include #include #include #include #include +#include #include namespace XCEngine::UI::Editor::App { using App::LoadEmbeddedPngTexture; -using namespace EditorWindowSupport; +using App::TruncateText; namespace { constexpr float kFrameTimeSmoothingFactor = 0.12f; constexpr float kFrameStatsDisplayRefreshIntervalSeconds = 0.25f; +void LogRuntimeTrace(std::string_view channel, std::string_view message) { + AppendUIEditorRuntimeTrace(channel, message); +} + } EditorWindowRuntimeController::EditorWindowRuntimeController( + EditorContext& editorContext, std::unique_ptr contentController) - : m_contentController(std::move(contentController)) {} + : m_editorContext(editorContext) + , m_contentController(std::move(contentController)) {} EditorWindowRuntimeController::~EditorWindowRuntimeController() = default; @@ -35,6 +43,12 @@ bool EditorWindowRuntimeController::IsReady() const { return m_ready; } +EditorWindowContentCapabilities EditorWindowRuntimeController::GetCapabilities() const { + return m_contentController != nullptr + ? m_contentController->GetCapabilities() + : EditorWindowContentCapabilities{}; +} + EditorWindowWorkspaceBinding* EditorWindowRuntimeController::TryGetWorkspaceBinding() { return m_contentController != nullptr ? m_contentController->TryGetWorkspaceBinding() @@ -110,7 +124,6 @@ const ::XCEngine::UI::UITextureHandle& EditorWindowRuntimeController::GetTitleBa bool EditorWindowRuntimeController::Initialize( HWND hwnd, const std::filesystem::path& repoRoot, - EditorContext& editorContext, const std::filesystem::path& captureRoot, bool autoCaptureOnStartup) { if (hwnd == nullptr) { @@ -157,10 +170,10 @@ bool EditorWindowRuntimeController::Initialize( } assert(m_contentController != nullptr); - m_contentController->PrepareEditorContext(editorContext, m_textSystem); + m_contentController->PrepareEditorContext(m_editorContext, m_textSystem); m_contentController->Initialize(EditorWindowContentInitializationContext{ .repoRoot = repoRoot, - .editorContext = editorContext, + .editorContext = m_editorContext, .textureHost = m_textureHost, .textMeasurer = m_textSystem, .viewportRenderer = m_windowRenderer, @@ -192,7 +205,7 @@ bool EditorWindowRuntimeController::Initialize( m_screenshotController.Initialize(captureRoot); if (autoCaptureOnStartup) { m_screenshotController.RequestCapture("startup"); - m_contentController->NotifyStartupCaptureRequested(editorContext); + m_contentController->NotifyStartupCaptureRequested(m_editorContext); } return true; @@ -253,23 +266,21 @@ bool EditorWindowRuntimeController::ApplyResize(UINT width, UINT height) { return resizeResult.hasViewportSurfacePresentation; } -void EditorWindowRuntimeController::PrepareEditorContext(EditorContext& editorContext) { +void EditorWindowRuntimeController::PrepareEditorContext() { if (m_contentController != nullptr) { - m_contentController->PrepareEditorContext(editorContext, m_textSystem); + m_contentController->PrepareEditorContext(m_editorContext, m_textSystem); } } -bool EditorWindowRuntimeController::IsEditorContextValid( - const EditorContext& editorContext) const { +bool EditorWindowRuntimeController::IsEditorContextValid() const { return m_contentController != nullptr && - m_contentController->IsEditorContextValid(editorContext); + m_contentController->IsEditorContextValid(m_editorContext); } void EditorWindowRuntimeController::AppendInvalidFrame( - const EditorContext& editorContext, ::XCEngine::UI::UIDrawList& drawList) const { if (m_contentController != nullptr) { - m_contentController->AppendInvalidFrame(editorContext, drawList); + m_contentController->AppendInvalidFrame(m_editorContext, drawList); } } @@ -311,10 +322,26 @@ Host::D3D12WindowRenderLoopPresentResult EditorWindowRuntimeController::Present( } EditorWindowFrameTransferRequests EditorWindowRuntimeController::UpdateAndAppend( - const EditorWindowContentFrameContext& context, + const ::XCEngine::UI::UIRect& bounds, + const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents, + std::optional cursorScreenPoint, + bool primary, + bool globalTabDragActive, + bool useDetachedTitleBarTabStrip, ::XCEngine::UI::UIDrawData& drawData) { assert(m_contentController != nullptr); - return m_contentController->UpdateAndAppend(context, drawData); + return m_contentController->UpdateAndAppend( + EditorWindowContentFrameContext{ + .editorContext = m_editorContext, + .bounds = bounds, + .inputEvents = inputEvents, + .cursorScreenPoint = cursorScreenPoint, + .captureStatusText = BuildCaptureStatusText(), + .primary = primary, + .globalTabDragActive = globalTabDragActive, + .useDetachedTitleBarTabStrip = useDetachedTitleBarTabStrip, + }, + drawData); } void EditorWindowRuntimeController::RenderRequestedViewports( diff --git a/editor/app/Platform/Win32/Runtime/EditorWindowRuntimeController.h b/editor/app/Windowing/Runtime/EditorWindowRuntimeController.h similarity index 85% rename from editor/app/Platform/Win32/Runtime/EditorWindowRuntimeController.h rename to editor/app/Windowing/Runtime/EditorWindowRuntimeController.h index 2ef0afa1..962bd8d3 100644 --- a/editor/app/Platform/Win32/Runtime/EditorWindowRuntimeController.h +++ b/editor/app/Windowing/Runtime/EditorWindowRuntimeController.h @@ -5,7 +5,7 @@ #endif #include "Windowing/Content/EditorWindowContentController.h" -#include "Platform/Win32/Runtime/EditorWindowScreenshotController.h" +#include "Windowing/Runtime/EditorWindowScreenshotController.h" #include #include @@ -20,7 +20,9 @@ #include #include #include +#include #include +#include namespace XCEngine::UI { @@ -34,7 +36,8 @@ class EditorContext; class EditorWindowRuntimeController final { public: - explicit EditorWindowRuntimeController( + EditorWindowRuntimeController( + EditorContext& editorContext, std::unique_ptr contentController); ~EditorWindowRuntimeController(); @@ -44,6 +47,7 @@ public: EditorWindowRuntimeController& operator=(EditorWindowRuntimeController&&) = delete; bool IsReady() const; + EditorWindowContentCapabilities GetCapabilities() const; EditorWindowWorkspaceBinding* TryGetWorkspaceBinding(); const EditorWindowWorkspaceBinding* TryGetWorkspaceBinding() const; @@ -64,23 +68,25 @@ public: bool Initialize( HWND hwnd, const std::filesystem::path& repoRoot, - EditorContext& editorContext, const std::filesystem::path& captureRoot, bool autoCaptureOnStartup); void Shutdown(); void ResetInteractionState(); bool ApplyResize(UINT width, UINT height); - void PrepareEditorContext(EditorContext& editorContext); - bool IsEditorContextValid(const EditorContext& editorContext) const; - void AppendInvalidFrame( - const EditorContext& editorContext, - ::XCEngine::UI::UIDrawList& drawList) const; + void PrepareEditorContext(); + bool IsEditorContextValid() const; + void AppendInvalidFrame(::XCEngine::UI::UIDrawList& drawList) const; Host::D3D12WindowRenderLoopFrameContext BeginFrame(); Host::D3D12WindowRenderLoopPresentResult Present( const ::XCEngine::UI::UIDrawData& drawData); EditorWindowFrameTransferRequests UpdateAndAppend( - const EditorWindowContentFrameContext& context, + const ::XCEngine::UI::UIRect& bounds, + const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents, + std::optional cursorScreenPoint, + bool primary, + bool globalTabDragActive, + bool useDetachedTitleBarTabStrip, ::XCEngine::UI::UIDrawData& drawData); void RenderRequestedViewports( const ::XCEngine::Rendering::RenderContext& renderContext); @@ -94,6 +100,7 @@ private: void UpdateFrameTiming(); void RefreshDisplayedFrameStats(); + EditorContext& m_editorContext; Host::D3D12WindowRenderer m_windowRenderer = {}; Host::D3D12UiTextureHost m_textureHost = {}; Host::D3D12UiTextSystem m_textSystem = {}; diff --git a/editor/app/Platform/Win32/Runtime/EditorWindowScreenshotController.cpp b/editor/app/Windowing/Runtime/EditorWindowScreenshotController.cpp similarity index 98% rename from editor/app/Platform/Win32/Runtime/EditorWindowScreenshotController.cpp rename to editor/app/Windowing/Runtime/EditorWindowScreenshotController.cpp index 99fafa75..b3964a88 100644 --- a/editor/app/Platform/Win32/Runtime/EditorWindowScreenshotController.cpp +++ b/editor/app/Windowing/Runtime/EditorWindowScreenshotController.cpp @@ -1,4 +1,4 @@ -#include "Platform/Win32/Runtime/EditorWindowScreenshotController.h" +#include "Windowing/Runtime/EditorWindowScreenshotController.h" #include "Support/ExecutablePath.h" diff --git a/editor/app/Platform/Win32/Runtime/EditorWindowScreenshotController.h b/editor/app/Windowing/Runtime/EditorWindowScreenshotController.h similarity index 100% rename from editor/app/Platform/Win32/Runtime/EditorWindowScreenshotController.h rename to editor/app/Windowing/Runtime/EditorWindowScreenshotController.h diff --git a/editor/include/XCEditor/Shell/UIEditorShellCompose.h b/editor/include/XCEditor/Shell/UIEditorShellCompose.h index a2896ca0..19978ab0 100644 --- a/editor/include/XCEditor/Shell/UIEditorShellCompose.h +++ b/editor/include/XCEditor/Shell/UIEditorShellCompose.h @@ -4,12 +4,16 @@ #include #include -namespace XCEngine::UI::Editor::App { -class BuiltInIcons; -} - namespace XCEngine::UI::Editor { +class UIEditorShellIconResolver { +public: + virtual ~UIEditorShellIconResolver() = default; + + virtual const ::XCEngine::UI::UITextureHandle* TryResolveIcon( + std::uint8_t iconKind) const = 0; +}; + struct UIEditorShellToolbarButton { std::string buttonId = {}; std::uint8_t iconKind = 0; @@ -143,7 +147,7 @@ void AppendUIEditorShellCompose( const UIEditorShellComposeState& state, const UIEditorShellComposePalette& palette = {}, const UIEditorShellComposeMetrics& metrics = {}, - const App::BuiltInIcons* builtInIcons = nullptr); + const UIEditorShellIconResolver* iconResolver = nullptr); void AppendUIEditorShellComposeBase( ::XCEngine::UI::UIDrawList& drawList, @@ -152,7 +156,7 @@ void AppendUIEditorShellComposeBase( const UIEditorShellComposeState& state, const UIEditorShellComposePalette& palette = {}, const UIEditorShellComposeMetrics& metrics = {}, - const App::BuiltInIcons* builtInIcons = nullptr); + const UIEditorShellIconResolver* iconResolver = nullptr); void AppendUIEditorShellComposeStatusBar( ::XCEngine::UI::UIDrawList& drawList, diff --git a/editor/src/Shell/UIEditorShellCompose.cpp b/editor/src/Shell/UIEditorShellCompose.cpp index e9fd4c6b..c0f70a56 100644 --- a/editor/src/Shell/UIEditorShellCompose.cpp +++ b/editor/src/Shell/UIEditorShellCompose.cpp @@ -1,7 +1,5 @@ #include -#include "Rendering/Assets/BuiltInIcons.h" - #include namespace XCEngine::UI::Editor { @@ -77,14 +75,15 @@ void AppendToolbarGlyph( ::XCEngine::UI::UIDrawList& drawList, const UIRect& rect, std::uint8_t iconKind, - const App::BuiltInIcons* icons, + const UIEditorShellIconResolver* iconResolver, const UIColor& tintColor) { - if (icons == nullptr) { + if (iconResolver == nullptr) { return; } - const ::XCEngine::UI::UITextureHandle& texture = icons->Resolve(static_cast(iconKind)); - if (!texture.IsValid()) { + const ::XCEngine::UI::UITextureHandle* const texture = + iconResolver->TryResolveIcon(iconKind); + if (texture == nullptr || !texture->IsValid()) { return; } @@ -94,7 +93,7 @@ void AppendToolbarGlyph( rect.y + inset, rect.width - inset * 2.0f, rect.height - inset * 2.0f); - drawList.AddImage(iconRect, texture, tintColor); + drawList.AddImage(iconRect, *texture, tintColor); } void AppendUIEditorShellToolbar( @@ -103,7 +102,7 @@ void AppendUIEditorShellToolbar( const std::vector& buttons, const UIEditorShellToolbarPalette& palette, const UIEditorShellToolbarMetrics& metrics, - const App::BuiltInIcons* builtInIcons) { + const UIEditorShellIconResolver* iconResolver) { if (layout.bounds.width <= 0.0f || layout.bounds.height <= 0.0f) { return; } @@ -135,7 +134,7 @@ void AppendUIEditorShellToolbar( drawList, buttonRect, buttons[index].iconKind, - builtInIcons, + iconResolver, palette.iconColor); } } @@ -292,7 +291,7 @@ void AppendUIEditorShellCompose( const UIEditorShellComposeState& state, const UIEditorShellComposePalette& palette, const UIEditorShellComposeMetrics& metrics, - const App::BuiltInIcons* builtInIcons) { + const UIEditorShellIconResolver* iconResolver) { AppendUIEditorShellComposeBase( drawList, frame, @@ -300,7 +299,7 @@ void AppendUIEditorShellCompose( state, palette, metrics, - builtInIcons); + iconResolver); AppendUIEditorWorkspaceCompose( drawList, @@ -327,7 +326,7 @@ void AppendUIEditorShellComposeBase( const UIEditorShellComposeState& state, const UIEditorShellComposePalette& palette, const UIEditorShellComposeMetrics& metrics, - const App::BuiltInIcons* builtInIcons) { + const UIEditorShellIconResolver* iconResolver) { drawList.AddFilledRect( frame.layout.bounds, palette.surfaceColor, @@ -359,7 +358,7 @@ void AppendUIEditorShellComposeBase( model.toolbarButtons, palette.toolbarPalette, metrics.toolbarMetrics, - builtInIcons); + iconResolver); } void AppendUIEditorShellComposeStatusBar( diff --git a/engine/include/XCEngine/Rendering/AGENTS.md b/engine/include/XCEngine/Rendering/AGENTS.md index 3aa9da6c..6a105a29 100644 --- a/engine/include/XCEngine/Rendering/AGENTS.md +++ b/engine/include/XCEngine/Rendering/AGENTS.md @@ -34,16 +34,25 @@ SceneRenderer `ScriptableRenderPipelineAsset -> ScriptableRendererData -> ScriptableRenderer -> ScriptableRendererFeature/Pass` - builtin 和 SRP 现在都必须是显式可切换的顶层路径,而不是“SRP 隐式优先,builtin 只是兜底”。 - `ScriptableRenderPipelineHost` 里的 native backend 是 managed SRP 的绘制后端,不是 SRP stage 录制失败时的顶层兜底。 +- managed `ScriptableRenderContext` 的公开场景接口只表达绘制能力:`DrawRenderers(...)`、`DrawSkybox()`、`DrawOpaqueRenderers()`、`DrawTransparentRenderers()`。 +- renderer-backed SRP 的 frame stage 组织权归 `ScriptableRenderer` / `ScriptableRendererFeature`,包括 shadow caster、depth only、post-process、final output 等可选 stage。 ## 5. 当前硬边界 - `RenderPipelineFactory` 在无显式 asset、无托管配置、创建失败回退时,都必须先回 builtin,而不是回 `ScriptableRenderPipelineHost`。 - `MonoScriptRuntime::OnRuntimeStart()` 不得隐式写入 render pipeline 选择。 - builtin 私有 native feature 通道只能留在 native 内部使用。 - managed SRP runtime 或 stage recorder 存在时,stage 组织权归 managed SRP;native backend 不得在 host 层隐式接管 `RecordStageRenderGraph`、`Render(...)` 或 scene-pass standalone stage。 +- `RendererBackedRenderPipelineAsset` 进入 frame planning 时必须先清掉 native 默认请求出来的可选 stage,再由 renderer / feature 显式请求需要的 stage。 - 下列能力不得再暴露给 managed / URP 公开层: `NativeSceneFeaturePassId` `ScriptableRenderContext.RecordNativeSceneFeaturePass(...)` + `ScriptableRenderContext.RecordScene(...)` + `ScriptableRenderContext.RecordScenePhase(...)` + `ScriptableRenderContext.RecordSceneInjectionPoint(...)` + `ScriptableRenderContext.RecordOpaque/Skybox/TransparentScenePhase(...)` + `ScriptableRenderContext.RecordBefore/After*Injection(...)` 依赖上述接口的 managed wrapper feature / controller +- `CameraRenderRequestContext.hasDirectionalShadow` 和 `CameraRenderRequestContext.ClearDirectionalShadow()` 不得作为 ScriptCore public surface 暴露;core 层只能保留内部桥接,产品层策略放在 URP renderer asset / renderer data / feature。 - URP 默认 renderer feature 工厂不得自动注入 builtin 私有 feature wrapper。 ## 6. native / managed 分层 @@ -57,7 +66,9 @@ SceneRenderer renderer 选择 stage planning renderer feature / pass 组织 + renderer feature 的 frame-plan / scene-setup / shadow-execution 配置分发 camera 级策略 + shadow caster / depth only 等可选 stage 的显式请求 产品层行为差异 - 判断原则: 如果新增的是“绘制能力”或“后端 contract”,优先放 C++。 @@ -66,9 +77,11 @@ SceneRenderer ## 7. 禁止回退的实现方式 - 不要再引入“项目默认 SRP asset 自动生效”的隐式逻辑。 - 不要再把 builtin 私有 native feature 包成 public managed API 方便调用。 +- 不要让 managed `ScriptableRenderContext.RecordScene(...)` 或任何 managed pass 录制 native scene injection point。 - 不要再让 “null 选择” 默认落到 `ScriptableRenderPipelineHost`。 - 不要把非通用产品层策略继续直接塞回 `BuiltinForwardPipeline`。 - 不要让 `ScriptableRenderPipelineHost` 在 managed SRP 未支持或未录制某个 stage 时自动回退到 native backend;需要绘制时应通过 managed `ScriptableRenderContext` 显式调用 native draw 能力。 +- 不要让 renderer-backed SRP 继续继承 native planner 默认请求出来的 shadow/depth stage;需要这些 stage 时必须由 managed renderer / feature 显式请求并录制。 ## 8. 关键文件 - `engine/src/Rendering/Internal/RenderPipelineFactory.cpp` @@ -89,7 +102,21 @@ SceneRenderer 无 managed 选择时默认走 builtin。 设置 managed descriptor 后切到 `ScriptableRenderPipelineHost`。 清空 managed descriptor 后退回 builtin。 +- `tests/scripting/test_mono_script_runtime.cpp` + `ScriptableRenderContextPublicApiSurfaceUsesDirectContextModel` 保证 public managed surface 不再暴露 native scene injection / phase wrapper。 + `DefaultSceneRendererUsesManagedUniversalPipelineForPlannedMainSceneAndPostProcessRender` 保证 managed URP 通过显式 renderer/pass 录制主场景和后处理。 + `ManagedRenderContextExposes*ThroughRenderingData` 保证 managed renderer 可通过 `RenderingData` 观察 camera / lighting / shadow / environment / final color / stage color 数据。 + `ManagedStageRecorderRecordsMainSceneThroughScriptableRenderContext` 保证主场景通过 `ScriptableRenderContext` 的显式 draw 能力录制。 ## 10. 后续演进方向 - 继续补齐 URP 上层组织能力,但不能破坏 builtin / programmable 的显式切换语义。 - 如果未来继续向 Unity 收口,优先收口“入口语义”和“分层边界”,不要靠隐式 fallback 和私有桥接维持表面形似。 + +## 过去几刀 + +- 收回 managed native scene injection 公开面:渲染模块目标向 Unity SRP / URP 对齐,但 managed / URP 层仍公开 native builtin scene injection point 和 phase wrapper,导致产品层像是在组织 URP,实质上仍在调用 builtin 私有时序。 +- 移除 `ScriptableRenderContext.RecordSceneInjectionPoint(...)`、`RecordBefore/After*Injection(...)`、`RecordOpaque/Skybox/TransparentScenePhase(...)` 等 public managed API;移除对应 Mono internal call;`RecordScene()` / `RecordScenePhase(...)` 收回 internal。 +- 确立 managed 公开层只通过 `DrawRenderers(...)`、`DrawSkybox()`、`DrawOpaqueRenderers()`、`DrawTransparentRenderers()` 表达 native draw 能力;pass / feature / stage 的组织权归 managed SRP / URP。 +- `ScriptableRenderer` 统一驱动 renderer feature 的 frame-plan、scene-setup、directional-shadow execution 配置;`RendererBackedRenderPipelineAsset` 清掉 native 默认 optional stage 后再让 renderer / feature 显式请求。 +- `CameraRenderRequestContext.hasDirectionalShadow` 和 `ClearDirectionalShadow()` 从 ScriptCore public surface 收回 internal,避免 core public API 泄漏 native shadow 规划控制。 +- 当时验证:`xcengine_managed_assemblies`、`scripting_tests` 构建通过;聚焦 `MonoScriptRuntimeTest` 12 项 SRP / URP / API surface 测试通过。 diff --git a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp index 699389b5..c9318b97 100644 --- a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp +++ b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp @@ -5594,25 +5594,6 @@ InternalCall_Rendering_ScriptableRenderContext_RecordScenePhase( : 0; } -mono_bool -InternalCall_Rendering_ScriptableRenderContext_RecordSceneInjectionPoint( - uint64_t nativeHandle, - int32_t injectionPoint) { - ManagedScriptableRenderContextState* const state = - FindManagedScriptableRenderContextState(nativeHandle); - if (state == nullptr || - state->graphContext == nullptr || - state->sceneRecorder == nullptr || - state->stage != Rendering::CameraFrameStage::MainScene) { - return 0; - } - - return state->sceneRecorder->RecordInjectionPoint( - static_cast(injectionPoint)) - ? 1 - : 0; -} - mono_bool InternalCall_Rendering_ScriptableRenderContext_DrawRenderersByDesc( uint64_t nativeHandle, @@ -6576,7 +6557,6 @@ void RegisterInternalCalls() { mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderContext_GetFinalColorHasCameraOverrides", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderContext_GetFinalColorHasCameraOverrides)); mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderContext_RecordScenePhase", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderContext_RecordScenePhase)); mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderContext_DrawRenderersByDesc", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderContext_DrawRenderersByDesc)); - mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderContext_RecordSceneInjectionPoint", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderContext_RecordSceneInjectionPoint)); mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderPipelinePlanningContext_GetRendererIndex", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderPipelinePlanningContext_GetRendererIndex)); mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderPipelinePlanningContext_IsStageRequested", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderPipelinePlanningContext_IsStageRequested)); mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderPipelinePlanningContext_GetStageColorSource", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderPipelinePlanningContext_GetStageColorSource)); diff --git a/managed/GameScripts/RenderPipelineApiProbe.cs b/managed/GameScripts/RenderPipelineApiProbe.cs index 41404918..b8dcb353 100644 --- a/managed/GameScripts/RenderPipelineApiProbe.cs +++ b/managed/GameScripts/RenderPipelineApiProbe.cs @@ -134,16 +134,6 @@ namespace Gameplay } } - internal enum SceneInjectionKind - { - BeforeOpaque, - AfterOpaque, - BeforeSkybox, - AfterSkybox, - BeforeTransparent, - AfterTransparent - } - internal enum ScenePhaseKind { Opaque, @@ -157,51 +147,6 @@ namespace Gameplay ShaderVector } - internal sealed class SceneInjectionPass : ScriptableRenderPass - { - private readonly SceneInjectionKind m_injectionKind; - - public SceneInjectionPass( - RenderPassEvent passEvent, - SceneInjectionKind injectionKind) - { - renderPassEvent = passEvent; - m_injectionKind = injectionKind; - } - - protected override bool RecordRenderGraph( - ScriptableRenderContext context, - RenderingData renderingData) - { - return context != null && - renderingData != null && - renderingData.isMainSceneStage && - RecordInjection(context); - } - - private bool RecordInjection( - ScriptableRenderContext context) - { - switch (m_injectionKind) - { - case SceneInjectionKind.BeforeOpaque: - return context.RecordBeforeOpaqueInjection(); - case SceneInjectionKind.AfterOpaque: - return context.RecordAfterOpaqueInjection(); - case SceneInjectionKind.BeforeSkybox: - return context.RecordBeforeSkyboxInjection(); - case SceneInjectionKind.AfterSkybox: - return context.RecordAfterSkyboxInjection(); - case SceneInjectionKind.BeforeTransparent: - return context.RecordBeforeTransparentInjection(); - case SceneInjectionKind.AfterTransparent: - return context.RecordAfterTransparentInjection(); - default: - return false; - } - } - } - internal sealed class ScenePhasePass : ScriptableRenderPass { private readonly ScenePhaseKind m_phaseKind; @@ -239,11 +184,11 @@ namespace Gameplay switch (m_phaseKind) { case ScenePhaseKind.Opaque: - return context.RecordOpaqueScenePhase(); + return context.DrawOpaqueRenderers(); case ScenePhaseKind.Skybox: - return context.RecordSkyboxScenePhase(); + return context.DrawSkybox(); case ScenePhaseKind.Transparent: - return context.RecordTransparentScenePhase(); + return context.DrawTransparentRenderers(); default: return false; } @@ -368,56 +313,26 @@ namespace Gameplay internal sealed class DefaultSceneFeature : ScriptableRendererFeature { - private readonly SceneInjectionPass m_beforeOpaquePass; private readonly ScenePhasePass m_opaquePass; - private readonly SceneInjectionPass m_afterOpaquePass; - private readonly SceneInjectionPass m_beforeSkyboxPass; private readonly ScenePhasePass m_skyboxPass; - private readonly SceneInjectionPass m_afterSkyboxPass; - private readonly SceneInjectionPass m_beforeTransparentPass; private readonly ScenePhasePass m_transparentPass; - private readonly SceneInjectionPass m_afterTransparentPass; public DefaultSceneFeature( Action onOpaqueRecorded = null) { - m_beforeOpaquePass = - new SceneInjectionPass( - RenderPassEvent.BeforeRenderingOpaques, - SceneInjectionKind.BeforeOpaque); m_opaquePass = new ScenePhasePass( RenderPassEvent.RenderOpaques, ScenePhaseKind.Opaque, onOpaqueRecorded); - m_afterOpaquePass = - new SceneInjectionPass( - RenderPassEvent.AfterRenderingOpaques, - SceneInjectionKind.AfterOpaque); - m_beforeSkyboxPass = - new SceneInjectionPass( - RenderPassEvent.BeforeRenderingSkybox, - SceneInjectionKind.BeforeSkybox); m_skyboxPass = new ScenePhasePass( RenderPassEvent.RenderSkybox, ScenePhaseKind.Skybox); - m_afterSkyboxPass = - new SceneInjectionPass( - RenderPassEvent.AfterRenderingSkybox, - SceneInjectionKind.AfterSkybox); - m_beforeTransparentPass = - new SceneInjectionPass( - RenderPassEvent.BeforeRenderingTransparents, - SceneInjectionKind.BeforeTransparent); m_transparentPass = new ScenePhasePass( RenderPassEvent.RenderTransparents, ScenePhaseKind.Transparent); - m_afterTransparentPass = - new SceneInjectionPass( - RenderPassEvent.AfterRenderingTransparents, - SceneInjectionKind.AfterTransparent); } public override void AddRenderPasses( @@ -431,15 +346,60 @@ namespace Gameplay return; } - renderer.EnqueuePass(m_beforeOpaquePass); renderer.EnqueuePass(m_opaquePass); - renderer.EnqueuePass(m_afterOpaquePass); - renderer.EnqueuePass(m_beforeSkyboxPass); renderer.EnqueuePass(m_skyboxPass); - renderer.EnqueuePass(m_afterSkyboxPass); - renderer.EnqueuePass(m_beforeTransparentPass); renderer.EnqueuePass(m_transparentPass); - renderer.EnqueuePass(m_afterTransparentPass); + } + } + + internal sealed class DefaultShadowCasterFeature + : ScriptableRendererFeature + { + private readonly DrawObjectsPass m_shadowCasterPass = + new DrawObjectsPass( + RenderPassEvent.BeforeRenderingShadows, + SceneRenderPhase.Opaque, + RendererListDesc.CreateDefault( + RendererListType.ShadowCaster)); + + public override void ConfigureCameraFramePlan( + ScriptableRenderPipelinePlanningContext context) + { + if (context != null) + { + context.RequestShadowCasterStage(); + } + } + + public override void ConfigureDirectionalShadowExecutionState( + DirectionalShadowExecutionContext context) + { + if (context == null) + { + return; + } + + if (context.hasPlannedMainDirectionalShadow) + { + context.UseDefaultMainDirectionalShadowExecution(); + return; + } + + context.ClearDirectionalShadowExecution(); + } + + public override void AddRenderPasses( + ScriptableRenderer renderer, + RenderingData renderingData) + { + if (renderer == null || + renderingData == null || + !renderingData.isShadowCasterStage) + { + return; + } + + renderer.EnqueuePass(m_shadowCasterPass); } } @@ -570,6 +530,7 @@ namespace Gameplay Vector4 firstVectorPayload, Vector4 secondVectorPayload) { + AddFeature(new DefaultSceneFeature()); AddFeature( new FullscreenFeature( new FullscreenPass( @@ -1076,7 +1037,9 @@ namespace Gameplay finalColorData.requiresProcessing; } RecordCallCount++; - return context.RecordScene(); + return context.DrawOpaqueRenderers() && + context.DrawSkybox() && + context.DrawTransparentRenderers(); } } @@ -1106,6 +1069,7 @@ namespace Gameplay { public CameraDataObservationRenderer() { + AddFeature(new DefaultShadowCasterFeature()); AddFeature(new CameraDataObservationFeature()); } } @@ -1765,6 +1729,8 @@ namespace Gameplay { public static int CreatePipelineCallCount; public Vector4 postProcessScale = new Vector4(1.03f, 0.98f, 0.94f, 1.0f); + private ManagedUniversalRenderPipelineProbeRendererData + m_rendererData; public ManagedUniversalRenderPipelineProbeAsset() { @@ -1774,20 +1740,35 @@ namespace Gameplay protected override ScriptableRenderPipeline CreatePipeline() { CreatePipelineCallCount++; - rendererDataList = CreateRendererDataList(); + ApplyPostProcessScale(); return base.CreatePipeline(); } + protected override void ConfigureCameraFramePlan( + ScriptableRenderPipelinePlanningContext context) + { + ApplyPostProcessScale(); + base.ConfigureCameraFramePlan(context); + } + private ScriptableRendererData[] CreateRendererDataList() { - ManagedUniversalRenderPipelineProbeRendererData rendererData = + m_rendererData = ProbeScriptableObjectFactory .Create(); - rendererData.postProcessScale = - postProcessScale; + ApplyPostProcessScale(); return ProbeScriptableObjectFactory .CreateRendererDataList( - rendererData); + m_rendererData); + } + + private void ApplyPostProcessScale() + { + if (m_rendererData != null) + { + m_rendererData.postProcessScale = + postProcessScale; + } } } diff --git a/managed/GameScripts/ScriptableRenderContextApiSurfaceProbe.cs b/managed/GameScripts/ScriptableRenderContextApiSurfaceProbe.cs index aa202a09..6b60961d 100644 --- a/managed/GameScripts/ScriptableRenderContextApiSurfaceProbe.cs +++ b/managed/GameScripts/ScriptableRenderContextApiSurfaceProbe.cs @@ -109,6 +109,54 @@ namespace Gameplay public bool HasRenderPassEventUnityNumericOrder; public bool HasRenderPassEventEngineExtensionOrder; + private static bool HasMethod( + System.Type type, + string name, + BindingFlags flags) + { + if (type == null) + { + return false; + } + + MethodInfo[] methods = type.GetMethods(flags); + for (int i = 0; i < methods.Length; ++i) + { + MethodInfo method = methods[i]; + if (method != null && + method.Name == name) + { + return true; + } + } + + return false; + } + + private static bool HasProperty( + System.Type type, + string name, + BindingFlags flags) + { + if (type == null) + { + return false; + } + + PropertyInfo[] properties = type.GetProperties(flags); + for (int i = 0; i < properties.Length; ++i) + { + PropertyInfo property = properties[i]; + if (property != null && + property.Name == name) + { + return true; + } + } + + return false; + } + public void Start() { const BindingFlags PublicInstanceMethodFlags = @@ -334,38 +382,38 @@ namespace Gameplay HasScriptableObjectType = scriptableObjectType != null; HasScriptableObjectCreateInstance = - scriptableObjectType != null && - scriptableObjectType.GetMethod( + HasMethod( + scriptableObjectType, "CreateInstance", BindingFlags.Static | - BindingFlags.Public) != null; + BindingFlags.Public); HasRenderPipelineAssetScriptableObjectBase = scriptableObjectType != null && pipelineAssetType.BaseType == scriptableObjectType; HasPlanningContextType = - contextType.Assembly.GetType( - "XCEngine.Rendering.ScriptableRenderPipelinePlanningContext") != null; + typeof(ScriptableRenderPipelinePlanningContext) != null; HasRendererFeatureConfigureCameraFramePlan = - rendererFeatureType.GetMethod( + HasMethod( + rendererFeatureType, "ConfigureCameraFramePlan", BindingFlags.Instance | BindingFlags.Public | - BindingFlags.NonPublic) != null; + BindingFlags.NonPublic); HasRendererRecordingContextType = - System.Type.GetType( - "XCEngine.Rendering.Universal.RendererRecordingContext, XCEngine.RenderPipelines.Universal") != null; + universalAssembly.GetType( + "XCEngine.Rendering.Universal.RendererRecordingContext") != null; HasRendererCameraRequestContextType = - System.Type.GetType( - "XCEngine.Rendering.Universal.RendererCameraRequestContext, XCEngine.RenderPipelines.Universal") != null; + universalAssembly.GetType( + "XCEngine.Rendering.Universal.RendererCameraRequestContext") != null; HasRendererBackedRenderPipelineAssetType = - System.Type.GetType( - "XCEngine.Rendering.Universal.RendererBackedRenderPipelineAsset, XCEngine.RenderPipelines.Universal") != null; + universalAssembly.GetType( + "XCEngine.Rendering.Universal.RendererBackedRenderPipelineAsset") != null; HasRendererBackedRenderPipelineType = - System.Type.GetType( - "XCEngine.Rendering.Universal.RendererBackedRenderPipeline, XCEngine.RenderPipelines.Universal") != null; + universalAssembly.GetType( + "XCEngine.Rendering.Universal.RendererBackedRenderPipeline") != null; HasRendererDrivenRenderPipelineType = - System.Type.GetType( - "XCEngine.Rendering.Universal.RendererDrivenRenderPipeline, XCEngine.RenderPipelines.Universal") != null; + universalAssembly.GetType( + "XCEngine.Rendering.Universal.RendererDrivenRenderPipeline") != null; HasRendererDataScriptableObjectBase = scriptableObjectType != null && rendererDataType.BaseType == scriptableObjectType; @@ -373,31 +421,36 @@ namespace Gameplay scriptableObjectType != null && rendererFeatureType.BaseType == scriptableObjectType; HasRendererDataSetupRenderer = - rendererDataType.GetMethod( + HasMethod( + rendererDataType, "SetupRenderer", BindingFlags.Instance | - BindingFlags.NonPublic) != null; + BindingFlags.NonPublic); HasRendererDataSetDirty = - rendererDataType.GetMethod( + HasMethod( + rendererDataType, "SetDirty", BindingFlags.Instance | - BindingFlags.NonPublic) != null; + BindingFlags.NonPublic); HasRendererDataIsInvalidated = - rendererDataType.GetProperty( + HasProperty( + rendererDataType, "isInvalidated", BindingFlags.Instance | BindingFlags.NonPublic | - BindingFlags.Public) != null; + BindingFlags.Public); HasRendererSupportsRendererRecording = - rendererType.GetMethod( + HasMethod( + rendererType, "SupportsRendererRecording", BindingFlags.Instance | - BindingFlags.NonPublic) != null; + BindingFlags.NonPublic); HasRendererRecordRenderer = - rendererType.GetMethod( + HasMethod( + rendererType, "RecordRenderer", BindingFlags.Instance | - BindingFlags.NonPublic) != null; + BindingFlags.NonPublic); HasPublicRendererSupportsStageRenderGraph = rendererType.GetMethod( "SupportsStageRenderGraph", diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/RendererBackedRenderPipelineAsset.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/RendererBackedRenderPipelineAsset.cs index 2df35b2e..e571baf9 100644 --- a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/RendererBackedRenderPipelineAsset.cs +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/RendererBackedRenderPipelineAsset.cs @@ -55,10 +55,12 @@ namespace XCEngine.Rendering.Universal protected override void ConfigureCameraFramePlan( ScriptableRenderPipelinePlanningContext context) { - if (UsesExplicitFullscreenStagePlanning() && + if (UsesExplicitCameraFrameStagePlanning() && context != null) { - // Renderer-backed SRP owns fullscreen stage planning explicitly. + // Renderer-backed SRP owns optional stage planning explicitly. + context.ClearShadowCasterStage(); + context.ClearDepthOnlyStage(); context.ClearFullscreenStage( CameraFrameStage.PostProcess); context.ClearFullscreenStage( @@ -165,6 +167,11 @@ namespace XCEngine.Rendering.Universal : -1); } + protected virtual bool UsesExplicitCameraFrameStagePlanning() + { + return UsesExplicitFullscreenStagePlanning(); + } + protected virtual bool UsesExplicitFullscreenStagePlanning() { return true; diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRenderer.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRenderer.cs index ee8acc30..6451d3a7 100644 --- a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRenderer.cs +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRenderer.cs @@ -114,6 +114,7 @@ namespace XCEngine.Rendering.Universal ScriptableRenderPipelinePlanningContext context) { ConfigureCameraFramePlan(context); + ConfigureRendererFeaturesCameraFramePlan(context); } internal void FinalizeCameraFramePlanInstance( @@ -126,12 +127,15 @@ namespace XCEngine.Rendering.Universal RenderSceneSetupContext context) { ConfigureRenderSceneSetup(context); + ConfigureRendererFeaturesRenderSceneSetup(context); } internal void ConfigureDirectionalShadowExecutionStateInstance( DirectionalShadowExecutionContext context) { ConfigureDirectionalShadowExecutionState(context); + ConfigureRendererFeaturesDirectionalShadowExecutionState( + context); } internal bool RecordRendererInstance( @@ -211,6 +215,49 @@ namespace XCEngine.Rendering.Universal { } + private void ConfigureRendererFeaturesCameraFramePlan( + ScriptableRenderPipelinePlanningContext context) + { + for (int i = 0; i < m_features.Count; ++i) + { + ScriptableRendererFeature feature = m_features[i]; + if (feature != null && + feature.isActive) + { + feature.ConfigureCameraFramePlan(context); + } + } + } + + private void ConfigureRendererFeaturesRenderSceneSetup( + RenderSceneSetupContext context) + { + for (int i = 0; i < m_features.Count; ++i) + { + ScriptableRendererFeature feature = m_features[i]; + if (feature != null && + feature.isActive) + { + feature.ConfigureRenderSceneSetup(context); + } + } + } + + private void ConfigureRendererFeaturesDirectionalShadowExecutionState( + DirectionalShadowExecutionContext context) + { + for (int i = 0; i < m_features.Count; ++i) + { + ScriptableRendererFeature feature = m_features[i]; + if (feature != null && + feature.isActive) + { + feature.ConfigureDirectionalShadowExecutionState( + context); + } + } + } + private bool HasRendererBlock( RendererBlock block) { diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererData.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererData.cs index 63688873..f62eb335 100644 --- a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererData.cs +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererData.cs @@ -105,10 +105,6 @@ namespace XCEngine.Rendering.Universal context); } - m_featureCollection.ConfigureCameraFramePlan( - ref rendererFeatures, - context); - if (renderer != null) { renderer.FinalizeCameraFramePlanInstance( @@ -129,10 +125,6 @@ namespace XCEngine.Rendering.Universal context); } - m_featureCollection.ConfigureRenderSceneSetup( - ref rendererFeatures, - context); - return context != null && context.isConfigured; } @@ -152,11 +144,6 @@ namespace XCEngine.Rendering.Universal context); } - m_featureCollection - .ConfigureDirectionalShadowExecutionState( - ref rendererFeatures, - context); - return context != null && context.isConfigured; } diff --git a/managed/XCEngine.ScriptCore/InternalCalls.cs b/managed/XCEngine.ScriptCore/InternalCalls.cs index adb43d03..1e5e1681 100644 --- a/managed/XCEngine.ScriptCore/InternalCalls.cs +++ b/managed/XCEngine.ScriptCore/InternalCalls.cs @@ -904,12 +904,6 @@ namespace XCEngine string shaderPassName, ref Rendering.RenderStateBlock renderStateBlock); - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool - Rendering_ScriptableRenderContext_RecordSceneInjectionPoint( - ulong nativeHandle, - int injectionPoint); - [MethodImpl(MethodImplOptions.InternalCall)] internal static extern int Rendering_CameraRenderRequestContext_GetRenderedBaseCameraCount( diff --git a/managed/XCEngine.ScriptCore/Rendering/Core/CameraRenderRequestContext.cs b/managed/XCEngine.ScriptCore/Rendering/Core/CameraRenderRequestContext.cs index 455a0d67..a070bed4 100644 --- a/managed/XCEngine.ScriptCore/Rendering/Core/CameraRenderRequestContext.cs +++ b/managed/XCEngine.ScriptCore/Rendering/Core/CameraRenderRequestContext.cs @@ -53,7 +53,7 @@ namespace XCEngine.Rendering value); } - public bool hasDirectionalShadow => + internal bool hasDirectionalShadow => InternalCalls .Rendering_CameraRenderRequestContext_GetHasDirectionalShadow( m_nativeHandle); @@ -78,7 +78,7 @@ namespace XCEngine.Rendering } } - public void ClearDirectionalShadow() + internal void ClearDirectionalShadow() { InternalCalls .Rendering_CameraRenderRequestContext_ClearDirectionalShadow( diff --git a/managed/XCEngine.ScriptCore/Rendering/Core/ScriptableRenderContext.cs b/managed/XCEngine.ScriptCore/Rendering/Core/ScriptableRenderContext.cs index 90dc9fd9..27a82ba6 100644 --- a/managed/XCEngine.ScriptCore/Rendering/Core/ScriptableRenderContext.cs +++ b/managed/XCEngine.ScriptCore/Rendering/Core/ScriptableRenderContext.cs @@ -2,7 +2,7 @@ using XCEngine; namespace XCEngine.Rendering { - // SRP v1 managed recording surface. Scene recording still delegates to the + // SRP v1 managed recording surface. Scene draws still delegate to the // native SceneDrawBackend, and raster-pass recording is limited to the // fullscreen PostProcess / FinalOutput stages. public sealed class ScriptableRenderContext @@ -41,23 +41,14 @@ namespace XCEngine.Rendering .Rendering_ScriptableRenderContext_GetDepthTargetHandle( m_nativeHandle)); - // Records the default native scene sequence for MainScene or explicit - // scene-pass-request stages. This does not replace the native scene - // extraction / culling / draw backend. - public bool RecordScene() + internal bool RecordScene() { - return RecordBeforeOpaqueInjection() && - DrawOpaqueRenderers() && - RecordAfterOpaqueInjection() && - RecordBeforeSkyboxInjection() && + return DrawOpaqueRenderers() && DrawSkybox() && - RecordAfterSkyboxInjection() && - RecordBeforeTransparentInjection() && - DrawTransparentRenderers() && - RecordAfterTransparentInjection(); + DrawTransparentRenderers(); } - public bool RecordScenePhase( + internal bool RecordScenePhase( SceneRenderPhase scenePhase) { return InternalCalls @@ -66,15 +57,6 @@ namespace XCEngine.Rendering (int)scenePhase); } - public bool RecordSceneInjectionPoint( - SceneRenderInjectionPoint injectionPoint) - { - return InternalCalls - .Rendering_ScriptableRenderContext_RecordSceneInjectionPoint( - m_nativeHandle, - (int)injectionPoint); - } - public bool DrawRenderers( SceneRenderPhase scenePhase, RendererListType rendererListType) @@ -126,66 +108,12 @@ namespace XCEngine.Rendering ref renderStateBlock); } - public bool RecordOpaqueScenePhase() - { - return RecordScenePhase( - SceneRenderPhase.Opaque); - } - - public bool RecordSkyboxScenePhase() - { - return RecordScenePhase( - SceneRenderPhase.Skybox); - } - public bool DrawSkybox() { return RecordScenePhase( SceneRenderPhase.Skybox); } - public bool RecordTransparentScenePhase() - { - return RecordScenePhase( - SceneRenderPhase.Transparent); - } - - public bool RecordBeforeOpaqueInjection() - { - return RecordSceneInjectionPoint( - SceneRenderInjectionPoint.BeforeOpaque); - } - - public bool RecordAfterOpaqueInjection() - { - return RecordSceneInjectionPoint( - SceneRenderInjectionPoint.AfterOpaque); - } - - public bool RecordBeforeSkyboxInjection() - { - return RecordSceneInjectionPoint( - SceneRenderInjectionPoint.BeforeSkybox); - } - - public bool RecordAfterSkyboxInjection() - { - return RecordSceneInjectionPoint( - SceneRenderInjectionPoint.AfterSkybox); - } - - public bool RecordBeforeTransparentInjection() - { - return RecordSceneInjectionPoint( - SceneRenderInjectionPoint.BeforeTransparent); - } - - public bool RecordAfterTransparentInjection() - { - return RecordSceneInjectionPoint( - SceneRenderInjectionPoint.AfterTransparent); - } - public bool DrawOpaqueRenderers() { return DrawRenderers( diff --git a/tests/fixtures/RenderTestRhiStubs.h b/tests/fixtures/RenderTestRhiStubs.h index 2a6dbe37..e5a5d46e 100644 --- a/tests/fixtures/RenderTestRhiStubs.h +++ b/tests/fixtures/RenderTestRhiStubs.h @@ -16,11 +16,72 @@ #include #include #include +#include #include #include namespace XCTest { +class TestRenderViewBackingTexture final : public XCEngine::RHI::RHITexture { +public: + TestRenderViewBackingTexture( + XCEngine::RHI::ResourceViewDimension dimension, + XCEngine::RHI::Format format) + : m_format(format) + , m_textureType(ResolveTextureType(dimension)) { + } + + uint32_t GetWidth() const override { return 1u; } + uint32_t GetHeight() const override { return 1u; } + uint32_t GetDepth() const override { return 1u; } + uint32_t GetMipLevels() const override { return 1u; } + XCEngine::RHI::Format GetFormat() const override { return m_format; } + XCEngine::RHI::TextureType GetTextureType() const override { + return m_textureType; + } + XCEngine::RHI::ResourceStates GetState() const override { + return m_state; + } + void SetState(XCEngine::RHI::ResourceStates state) override { + m_state = state; + } + void* GetNativeHandle() override { return nullptr; } + const std::string& GetName() const override { return m_name; } + void SetName(const std::string& name) override { m_name = name; } + void Shutdown() override { + } + +private: + static XCEngine::RHI::TextureType ResolveTextureType( + XCEngine::RHI::ResourceViewDimension dimension) { + switch (dimension) { + case XCEngine::RHI::ResourceViewDimension::Texture1D: + return XCEngine::RHI::TextureType::Texture1D; + case XCEngine::RHI::ResourceViewDimension::Texture2DArray: + case XCEngine::RHI::ResourceViewDimension::Texture2DMSArray: + return XCEngine::RHI::TextureType::Texture2DArray; + case XCEngine::RHI::ResourceViewDimension::Texture3D: + return XCEngine::RHI::TextureType::Texture3D; + case XCEngine::RHI::ResourceViewDimension::TextureCube: + return XCEngine::RHI::TextureType::TextureCube; + case XCEngine::RHI::ResourceViewDimension::TextureCubeArray: + return XCEngine::RHI::TextureType::TextureCubeArray; + case XCEngine::RHI::ResourceViewDimension::Texture2D: + case XCEngine::RHI::ResourceViewDimension::Texture2DMS: + default: + return XCEngine::RHI::TextureType::Texture2D; + } + } + + XCEngine::RHI::Format m_format = + XCEngine::RHI::Format::Unknown; + XCEngine::RHI::TextureType m_textureType = + XCEngine::RHI::TextureType::Texture2D; + XCEngine::RHI::ResourceStates m_state = + XCEngine::RHI::ResourceStates::Common; + std::string m_name; +}; + class TestRenderResourceView final : public XCEngine::RHI::RHIResourceView { public: TestRenderResourceView( @@ -29,7 +90,11 @@ public: XCEngine::RHI::Format format) : m_viewType(viewType) , m_dimension(dimension) - , m_format(format) { + , m_format(format) + , m_texture( + std::make_unique( + dimension, + format)) { } void Shutdown() override { @@ -55,6 +120,10 @@ public: return m_format; } + XCEngine::RHI::RHITexture* GetTextureResource() const override { + return m_texture.get(); + } + private: XCEngine::RHI::ResourceViewType m_viewType = XCEngine::RHI::ResourceViewType::ShaderResource; @@ -62,6 +131,7 @@ private: XCEngine::RHI::ResourceViewDimension::Texture2D; XCEngine::RHI::Format m_format = XCEngine::RHI::Format::Unknown; + std::unique_ptr m_texture; }; class TestRenderTexture final : public XCEngine::RHI::RHITexture { diff --git a/tests/scripting/test_mono_script_runtime.cpp b/tests/scripting/test_mono_script_runtime.cpp index 7cc448a9..61f522c9 100644 --- a/tests/scripting/test_mono_script_runtime.cpp +++ b/tests/scripting/test_mono_script_runtime.cpp @@ -1153,6 +1153,8 @@ TEST_F( engine->OnUpdate(0.016f); bool hasPublicContextRecordScene = false; + bool hasPublicContextRecordScenePhase = false; + bool hasPublicContextRecordSceneInjectionPoint = false; bool hasPublicContextRecordOpaqueScenePhase = false; bool hasPublicContextRecordBeforeOpaqueInjection = false; bool hasPublicContextRecordShaderVectorFullscreenPass = false; @@ -1192,6 +1194,14 @@ TEST_F( selectionScript, "HasPublicContextRecordScene", hasPublicContextRecordScene)); + EXPECT_TRUE(runtime->TryGetFieldValue( + selectionScript, + "HasPublicContextRecordScenePhase", + hasPublicContextRecordScenePhase)); + EXPECT_TRUE(runtime->TryGetFieldValue( + selectionScript, + "HasPublicContextRecordSceneInjectionPoint", + hasPublicContextRecordSceneInjectionPoint)); EXPECT_TRUE(runtime->TryGetFieldValue( selectionScript, "HasPublicContextRecordOpaqueScenePhase", @@ -1330,6 +1340,8 @@ TEST_F( hasPublicRendererRecordStageRenderGraph)); EXPECT_FALSE(hasPublicContextRecordScene); + EXPECT_FALSE(hasPublicContextRecordScenePhase); + EXPECT_FALSE(hasPublicContextRecordSceneInjectionPoint); EXPECT_FALSE(hasPublicContextRecordOpaqueScenePhase); EXPECT_FALSE(hasPublicContextRecordBeforeOpaqueInjection); EXPECT_FALSE(hasPublicContextRecordShaderVectorFullscreenPass);