Align SRP boundaries and editor windowing

This commit is contained in:
2026-04-26 17:14:32 +08:00
parent a8256b05cd
commit b8599a8aff
38 changed files with 696 additions and 650 deletions

View File

@@ -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例如
`<XCEditor/Windowing/System/EditorWindowSystem.h>`
`<XCEditor/Windowing/Presentation/EditorWindowPresentationPolicy.h>`
`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` 加进 includeapp 通过 `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。

View File

@@ -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
)

View File

@@ -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<BuiltInIconKind>(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,

View File

@@ -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 <XCEditor/Foundation/UIEditorTheme.h>
#include <XCEngine/UI/DrawData.h>
@@ -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<UINT>(width), static_cast<UINT>(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<UINT>(width), static_cast<UINT>(height));
if (window.ApplyWindowResize(static_cast<UINT>(width), static_cast<UINT>(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);

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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 <XCEditor/Docking/UIEditorDockHostTransfer.h>
#include <XCEditor/Foundation/UIEditorRuntimeTrace.h>
#include <XCEditor/Shell/UIEditorShellInteraction.h>
@@ -88,7 +86,7 @@ EditorWindow::EditorWindow(
EditorWindowCategory category,
EditorWindowChromePolicy chromePolicy,
bool primary,
std::unique_ptr<EditorWindowContentController> contentController)
std::unique_ptr<EditorWindowRuntimeController> runtimeController)
: m_session(std::make_unique<EditorWindowSession>(
std::move(windowId),
std::move(title),
@@ -97,8 +95,7 @@ EditorWindow::EditorWindow(
primary))
, m_chromeController(std::make_unique<EditorWindowChromeController>())
, m_inputController(std::make_unique<EditorWindowInputController>())
, m_runtime(std::make_unique<EditorWindowRuntimeController>(
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<UIInputEvent> 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);

View File

@@ -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<EditorWindowContentController> contentController);
std::unique_ptr<EditorWindowRuntimeController> 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);

View File

@@ -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 <XCEditor/Foundation/UIEditorRuntimeTrace.h>
@@ -63,15 +62,15 @@ EditorWindowHostRuntime::EditorWindowHostRuntime(
EditorWindowHostRuntime::~EditorWindowHostRuntime() = default;
EditorWindow* EditorWindowHostRuntime::CreateHostWindow(
std::unique_ptr<EditorWindowContentController> contentController,
std::unique_ptr<EditorWindowRuntimeController> 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<WindowFrameTransferBatch> transferBatches = {};
transferBatches.reserve(m_windows.size());
for (const std::unique_ptr<EditorWindow>& 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;

View File

@@ -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<EditorWindowContentController> contentController,
std::unique_ptr<EditorWindowRuntimeController> 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;

View File

@@ -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;

View File

@@ -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 <cmath>
@@ -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<EditorWindowRuntimeController>(
m_editorContext,
m_contentFactory.CreateUtilityContentController(*descriptor)),
createParams);
if (utilityWindow == nullptr) {
LogRuntimeTrace("utility", "failed to create utility window");

View File

@@ -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;
};

View File

@@ -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 <XCEditor/Docking/UIEditorDockHostTransfer.h>
#include <XCEditor/Windowing/Presentation/EditorWindowPresentationPolicy.h>
@@ -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<EditorWindowRuntimeController>(
m_editorContext,
m_contentFactory.CreateWorkspaceContentController(action.create.windowState)),
createParams);
if (createdWindow == nullptr) {
for (const ExistingWindowSnapshot& snapshot : existingWindowSnapshots) {

View File

@@ -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;

View File

@@ -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 <XCEditor/Workspace/UIEditorWindowWorkspaceModel.h>
#include <utility>
#include <vector>
namespace XCEngine::UI::Editor::App {
@@ -21,11 +23,13 @@ EditorWindowManager::EditorWindowManager(
m_contentFactory = CreateDefaultEditorWindowContentFactory(windowSystem);
m_workspaceCoordinator =
std::make_unique<EditorWindowWorkspaceCoordinator>(
m_editorContext,
m_hostRuntime,
windowSystem,
*m_contentFactory);
m_utilityCoordinator =
std::make_unique<EditorUtilityWindowCoordinator>(
m_editorContext,
m_hostRuntime,
*m_contentFactory);
m_lifecycleCoordinator = std::make_unique<EditorWindowLifecycleCoordinator>(
@@ -52,7 +56,9 @@ EditorHostWindow* EditorWindowManager::CreateWorkspaceWindow(
}
EditorHostWindow* const window = m_hostRuntime.CreateHostWindow(
m_contentFactory->CreateWorkspaceContentController(windowState),
std::make_unique<EditorWindowRuntimeController>(
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<EditorWindowRuntimeController>(
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<WindowFrameTransferBatch> transferBatches = {};
const std::vector<EditorHostWindow*> 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 {

View File

@@ -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<EditorWindowContentFactory> m_contentFactory = {};

View File

@@ -3,18 +3,18 @@
#include "Windowing/Frame/EditorWindowTransferRequests.h"
#include "Windowing/Host/EditorWindowHostInterfaces.h"
#include <string_view>
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;

View File

@@ -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 <XCEditor/Windowing/Presentation/EditorWorkspaceWindowProjection.h>
#include <memory>
#include <filesystem>
#include <string>
#include <string_view>
#include <vector>
@@ -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<EditorWindowContentController> contentController,
std::unique_ptr<EditorWindowRuntimeController> 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

View File

@@ -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 <XCEditor/Docking/UIEditorDockHostTransfer.h>
#include <XCEditor/Foundation/UIEditorRuntimeTrace.h>
#include <algorithm>
#include <cassert>
#include <chrono>
#include <cmath>
#include <cstdio>
#include <string_view>
#include <utility>
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<EditorWindowContentController> 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<EditorWindowScreenPoint> 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(

View File

@@ -5,7 +5,7 @@
#endif
#include "Windowing/Content/EditorWindowContentController.h"
#include "Platform/Win32/Runtime/EditorWindowScreenshotController.h"
#include "Windowing/Runtime/EditorWindowScreenshotController.h"
#include <Rendering/D3D12/D3D12UiRenderer.h>
#include <Rendering/D3D12/D3D12UiTextSystem.h>
@@ -20,7 +20,9 @@
#include <chrono>
#include <filesystem>
#include <memory>
#include <optional>
#include <string>
#include <vector>
namespace XCEngine::UI {
@@ -34,7 +36,8 @@ class EditorContext;
class EditorWindowRuntimeController final {
public:
explicit EditorWindowRuntimeController(
EditorWindowRuntimeController(
EditorContext& editorContext,
std::unique_ptr<EditorWindowContentController> 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<EditorWindowScreenPoint> 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 = {};

View File

@@ -1,4 +1,4 @@
#include "Platform/Win32/Runtime/EditorWindowScreenshotController.h"
#include "Windowing/Runtime/EditorWindowScreenshotController.h"
#include "Support/ExecutablePath.h"

View File

@@ -4,12 +4,16 @@
#include <XCEditor/Menu/UIEditorMenuBar.h>
#include <XCEditor/Shell/UIEditorStatusBar.h>
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,

View File

@@ -1,7 +1,5 @@
#include <XCEditor/Shell/UIEditorShellCompose.h>
#include "Rendering/Assets/BuiltInIcons.h"
#include <algorithm>
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<App::BuiltInIconKind>(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<UIEditorShellToolbarButton>& 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(

View File

@@ -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 SRPnative 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 测试通过。

View File

@@ -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<Rendering::SceneRenderInjectionPoint>(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<const void*>(&InternalCall_Rendering_ScriptableRenderContext_GetFinalColorHasCameraOverrides));
mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderContext_RecordScenePhase", reinterpret_cast<const void*>(&InternalCall_Rendering_ScriptableRenderContext_RecordScenePhase));
mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderContext_DrawRenderersByDesc", reinterpret_cast<const void*>(&InternalCall_Rendering_ScriptableRenderContext_DrawRenderersByDesc));
mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderContext_RecordSceneInjectionPoint", reinterpret_cast<const void*>(&InternalCall_Rendering_ScriptableRenderContext_RecordSceneInjectionPoint));
mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderPipelinePlanningContext_GetRendererIndex", reinterpret_cast<const void*>(&InternalCall_Rendering_ScriptableRenderPipelinePlanningContext_GetRendererIndex));
mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderPipelinePlanningContext_IsStageRequested", reinterpret_cast<const void*>(&InternalCall_Rendering_ScriptableRenderPipelinePlanningContext_IsStageRequested));
mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderPipelinePlanningContext_GetStageColorSource", reinterpret_cast<const void*>(&InternalCall_Rendering_ScriptableRenderPipelinePlanningContext_GetStageColorSource));

View File

@@ -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<ManagedUniversalRenderPipelineProbeRendererData>();
rendererData.postProcessScale =
postProcessScale;
ApplyPostProcessScale();
return ProbeScriptableObjectFactory
.CreateRendererDataList(
rendererData);
m_rendererData);
}
private void ApplyPostProcessScale()
{
if (m_rendererData != null)
{
m_rendererData.postProcessScale =
postProcessScale;
}
}
}

View File

@@ -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",

View File

@@ -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;

View File

@@ -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)
{

View File

@@ -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;
}

View File

@@ -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(

View File

@@ -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(

View File

@@ -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(

View File

@@ -16,11 +16,72 @@
#include <array>
#include <cstdint>
#include <cstring>
#include <memory>
#include <string>
#include <vector>
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<TestRenderViewBackingTexture>(
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<TestRenderViewBackingTexture> m_texture;
};
class TestRenderTexture final : public XCEngine::RHI::RHITexture {

View File

@@ -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);