Add XCUI window compositor seam

This commit is contained in:
2026-04-05 06:15:24 +08:00
parent 0c24c7c611
commit da85109a31
6 changed files with 213 additions and 46 deletions

View File

@@ -15,6 +15,7 @@ Old `editor` replacement is explicitly out of scope for this phase.
- engine runtime coverage was tightened again around `UISystem` and concrete document-host rendering
- `LayoutLab` continues as the editor widget proving ground for tree/list/property-section style controls
- the demo sandbox and editor bridge APIs were tightened again without touching the old editor replacement scope
- `new_editor` now has an explicit window-level compositor seam through `IEditorHostCompositor` / `ImGuiHostCompositor`
- Old `editor` replacement remains deferred; all active execution still stays inside XCUI shared code and `new_editor`.
## Three-Layer Status
@@ -59,6 +60,7 @@ Current gap:
- `LayoutLab` now also covers editor-facing widget prototypes: `TreeView`, `TreeItem`, `ListView`, `ListItem`, `PropertySection`, and `FieldRow`.
- Panel diagnostics were expanded to clearly separate preview/runtime/input state and native vs legacy paths.
- The editor bridge layer now has smoke coverage for swapchain after-UI rendering hooks and SRV-backed ImGui texture descriptor registration.
- `Application` no longer owns the ImGui backend directly; window presentation now routes through `IEditorHostCompositor` with an `ImGuiHostCompositor` implementation.
- `XCNewEditor` builds successfully to `build/new_editor/bin/Debug/XCNewEditor.exe`.
Current gap:
@@ -116,6 +118,10 @@ Current gap:
- an `afterUiRender` swapchain callback hook in `D3D12WindowRenderer`
- SRV-view based texture descriptor registration in `ImGuiBackendBridge`
- smoke tests for window renderer, ImGui backend bridge, and console sink registration
- `new_editor` host presentation now has a first-class compositor seam:
- `IEditorHostCompositor`
- `ImGuiHostCompositor`
- `Application` frame/present flow routed through the compositor instead of direct `m_imguiBackend` ownership
- `new_editor` panel/shell diagnostics improvements for hosted preview state.
- XCUI asset document loading changed to prefer direct source compilation before `ResourceManager` fallback for the sandbox path, fixing the LayoutLab crash.
- `UIDocumentCompiler.cpp` repaired enough to restore full local builds after the duplicated schema-helper regression.
@@ -133,6 +139,6 @@ Current gap:
1. Expand runtime/game-layer ownership from the current document host + layered `UISystem` into reusable menu/HUD stack patterns and engine runtime integration.
2. Promote the current editor-facing widget prototypes out of authored `LayoutLab` content and into reusable XCUI widget/runtime modules, then continue with toolbar/menu and more native shell-owned chrome.
3. Start the window-level compositor split in `new_editor` so the editor shell can run through `ImGuiHostCompositor` first and then grow a native XCUI compositor path on the same seam.
4. Reduce remaining ImGui leakage in hosted preview surfaces and panel contracts after the compositor seam is in place.
3. Add a native XCUI host compositor on the existing window-level compositor seam so `new_editor` can present without going through ImGui-owned draw data.
4. Reduce remaining ImGui leakage in hosted preview surfaces and panel contracts now that the compositor seam is in place.
5. Continue phased validation, commit, push, and plan refresh after each stable batch.

View File

@@ -50,6 +50,7 @@ set(NEW_EDITOR_SOURCES
src/Rendering/MainWindowBackdropPass.cpp
src/Rendering/MainWindowNativeBackdropRenderer.cpp
src/XCUIBackend/ImGuiXCUIInputAdapter.cpp
src/XCUIBackend/ImGuiHostCompositor.cpp
src/XCUIBackend/XCUIEditorFontSetup.cpp
src/XCUIBackend/XCUIAssetDocumentSource.cpp
src/XCUIBackend/XCUIInputBridge.cpp

View File

@@ -1,5 +1,7 @@
#include "Application.h"
#include "XCUIBackend/ImGuiWindowUICompositor.h"
#include <XCEngine/Core/Asset/ResourceManager.h>
#include <imgui.h>
@@ -179,7 +181,7 @@ int Application::Run(HINSTANCE instance, int nCmdShow) {
return -1;
}
InitializeImGui();
InitializeWindowCompositor();
m_demoPanel = std::make_unique<XCUIDemoPanel>(
&m_xcuiInputSource,
CreateHostedPreviewPresenter(m_showNativeDemoPanelPreview));
@@ -210,7 +212,7 @@ int Application::Run(HINSTANCE instance, int nCmdShow) {
m_demoPanel.reset();
m_layoutLabPanel.reset();
ShutdownImGui();
ShutdownWindowCompositor();
ShutdownRenderer();
resourceManager.Shutdown();
return static_cast<int>(message.wParam);
@@ -232,7 +234,8 @@ LRESULT CALLBACK Application::StaticWndProc(HWND hwnd, UINT message, WPARAM wPar
LRESULT Application::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
m_xcuiInputSource.HandleWindowMessage(hwnd, message, wParam, lParam);
if (::XCEngine::Editor::UI::ImGuiBackendBridge::HandleWindowMessage(hwnd, message, wParam, lParam)) {
if (m_windowCompositor != nullptr &&
m_windowCompositor->HandleWindowMessage(hwnd, message, wParam, lParam)) {
return true;
}
@@ -313,28 +316,22 @@ bool Application::InitializeRenderer() {
return initialized;
}
void Application::InitializeImGui() {
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
ConfigureFonts();
ImGui::StyleColorsDark();
m_imguiBackend.Initialize(
m_hwnd,
m_windowRenderer.GetDevice(),
m_windowRenderer.GetCommandQueue(),
m_windowRenderer.GetSrvHeap(),
m_windowRenderer.GetSrvDescriptorSize(),
m_windowRenderer.GetSrvDescriptorCount());
void Application::InitializeWindowCompositor() {
m_windowCompositor = ::XCEngine::Editor::XCUIBackend::CreateImGuiWindowUICompositor();
if (m_windowCompositor != nullptr) {
m_windowCompositor->Initialize(
m_hwnd,
m_windowRenderer,
[]() { ConfigureFonts(); });
}
}
void Application::ShutdownImGui() {
void Application::ShutdownWindowCompositor() {
DestroyHostedPreviewSurfaces();
m_imguiBackend.Shutdown();
ImGui::DestroyContext();
if (m_windowCompositor != nullptr) {
m_windowCompositor->Shutdown();
m_windowCompositor.reset();
}
}
void Application::ShutdownRenderer() {
@@ -347,7 +344,11 @@ void Application::ShutdownRenderer() {
void Application::DestroyHostedPreviewSurfaces() {
for (HostedPreviewOffscreenSurface& previewSurface : m_hostedPreviewSurfaces) {
if (previewSurface.imguiCpuHandle.ptr != 0) {
m_imguiBackend.FreeTextureDescriptor(previewSurface.imguiCpuHandle, previewSurface.imguiGpuHandle);
if (m_windowCompositor != nullptr) {
m_windowCompositor->FreeTextureDescriptor(
previewSurface.imguiCpuHandle,
previewSurface.imguiGpuHandle);
}
}
ShutdownAndDelete(previewSurface.colorView);
ShutdownAndDelete(previewSurface.colorTexture);
@@ -435,7 +436,11 @@ bool Application::EnsureHostedPreviewSurface(
}
if (previewSurface.imguiCpuHandle.ptr != 0) {
m_imguiBackend.FreeTextureDescriptor(previewSurface.imguiCpuHandle, previewSurface.imguiGpuHandle);
if (m_windowCompositor != nullptr) {
m_windowCompositor->FreeTextureDescriptor(
previewSurface.imguiCpuHandle,
previewSurface.imguiGpuHandle);
}
}
ShutdownAndDelete(previewSurface.colorView);
ShutdownAndDelete(previewSurface.colorTexture);
@@ -474,7 +479,8 @@ bool Application::EnsureHostedPreviewSurface(
return false;
}
if (!m_imguiBackend.CreateTextureDescriptor(
if (m_windowCompositor == nullptr ||
!m_windowCompositor->CreateTextureDescriptor(
m_windowRenderer.GetRHIDevice(),
previewSurface.colorTexture,
&previewSurface.imguiCpuHandle,
@@ -867,24 +873,27 @@ void Application::Frame() {
m_hostedPreviewQueue.BeginFrame();
m_hostedPreviewSurfaceRegistry.BeginFrame();
SyncHostedPreviewSurfaces();
m_imguiBackend.BeginFrame();
RenderShellChrome();
if (m_demoPanel) {
m_demoPanel->RenderIfVisible();
}
if (m_layoutLabPanel) {
m_layoutLabPanel->RenderIfVisible();
}
if (m_showImGuiDemoWindow) {
ImGui::ShowDemoWindow(&m_showImGuiDemoWindow);
if (m_windowCompositor == nullptr) {
m_xcuiInputSource.ClearFrameTransients();
return;
}
SyncHostedPreviewSurfaces();
ImGui::Render();
m_windowRenderer.Render(
m_imguiBackend,
m_windowCompositor->RenderFrame(
kClearColor,
[this]() {
RenderShellChrome();
if (m_demoPanel) {
m_demoPanel->RenderIfVisible();
}
if (m_layoutLabPanel) {
m_layoutLabPanel->RenderIfVisible();
}
if (m_showImGuiDemoWindow) {
ImGui::ShowDemoWindow(&m_showImGuiDemoWindow);
}
SyncHostedPreviewSurfaces();
},
[this](
const ::XCEngine::Rendering::RenderContext& renderContext,
const ::XCEngine::Rendering::RenderSurface& surface) {

View File

@@ -5,7 +5,7 @@
#include "Platform/D3D12WindowRenderer.h"
#include "Rendering/MainWindowNativeBackdropRenderer.h"
#include "UI/ImGuiBackendBridge.h"
#include "XCUIBackend/IWindowUICompositor.h"
#include "XCUIBackend/XCUIHostedPreviewPresenter.h"
#include "XCUIBackend/XCUIInputBridge.h"
#include "XCUIBackend/XCUILayoutLabRuntime.h"
@@ -77,8 +77,8 @@ private:
bool CreateMainWindow(HINSTANCE instance, int nCmdShow);
bool InitializeRenderer();
void InitializeImGui();
void ShutdownImGui();
void InitializeWindowCompositor();
void ShutdownWindowCompositor();
void ShutdownRenderer();
void DestroyHostedPreviewSurfaces();
void SyncHostedPreviewSurfaces();
@@ -113,7 +113,7 @@ private:
HWND m_hwnd = nullptr;
::XCEngine::Editor::Platform::D3D12WindowRenderer m_windowRenderer;
::XCEngine::Editor::UI::ImGuiBackendBridge m_imguiBackend;
std::unique_ptr<::XCEngine::Editor::XCUIBackend::IWindowUICompositor> m_windowCompositor;
std::unique_ptr<XCUIDemoPanel> m_demoPanel;
std::unique_ptr<XCUILayoutLabPanel> m_layoutLabPanel;
::XCEngine::Editor::XCUIBackend::XCUIWin32InputSource m_xcuiInputSource;

View File

@@ -0,0 +1,52 @@
#pragma once
#include "Platform/D3D12WindowRenderer.h"
#include <XCEngine/RHI/RHIDevice.h>
#include <XCEngine/RHI/RHITexture.h>
#include <imgui.h>
#include <functional>
#include <memory>
#include <windows.h>
namespace XCEngine {
namespace Editor {
namespace XCUIBackend {
class IEditorHostCompositor {
public:
using ConfigureFontsCallback = std::function<void()>;
using RenderCallback = ::XCEngine::Editor::Platform::D3D12WindowRenderer::RenderCallback;
virtual ~IEditorHostCompositor() = default;
virtual bool Initialize(
HWND hwnd,
::XCEngine::Editor::Platform::D3D12WindowRenderer& windowRenderer,
const ConfigureFontsCallback& configureFonts) = 0;
virtual void Shutdown() = 0;
virtual bool HandleWindowMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) = 0;
virtual void BeginFrame() = 0;
virtual void EndFrameAndPresent(
::XCEngine::Editor::Platform::D3D12WindowRenderer& windowRenderer,
const float clearColor[4],
const RenderCallback& beforeUiRender = {},
const RenderCallback& afterUiRender = {}) = 0;
virtual bool CreateTextureDescriptor(
::XCEngine::RHI::RHIDevice* device,
::XCEngine::RHI::RHITexture* texture,
D3D12_CPU_DESCRIPTOR_HANDLE* outCpuHandle,
D3D12_GPU_DESCRIPTOR_HANDLE* outGpuHandle,
ImTextureID* outTextureId) = 0;
virtual void FreeTextureDescriptor(
D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle,
D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle) = 0;
};
std::unique_ptr<IEditorHostCompositor> CreateImGuiHostCompositor();
} // namespace XCUIBackend
} // namespace Editor
} // namespace XCEngine

View File

@@ -0,0 +1,99 @@
#include "XCUIBackend/IEditorHostCompositor.h"
#include "UI/ImGuiBackendBridge.h"
#include <imgui.h>
namespace XCEngine {
namespace Editor {
namespace XCUIBackend {
namespace {
class ImGuiHostCompositor final : public IEditorHostCompositor {
public:
bool Initialize(
HWND hwnd,
::XCEngine::Editor::Platform::D3D12WindowRenderer& windowRenderer,
const ConfigureFontsCallback& configureFonts) override {
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
if (configureFonts) {
configureFonts();
}
ImGui::StyleColorsDark();
m_backend.Initialize(
hwnd,
windowRenderer.GetDevice(),
windowRenderer.GetCommandQueue(),
windowRenderer.GetSrvHeap(),
windowRenderer.GetSrvDescriptorSize(),
windowRenderer.GetSrvDescriptorCount());
return true;
}
void Shutdown() override {
m_backend.Shutdown();
ImGui::DestroyContext();
}
bool HandleWindowMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) override {
return ::XCEngine::Editor::UI::ImGuiBackendBridge::HandleWindowMessage(
hwnd,
message,
wParam,
lParam);
}
void BeginFrame() override {
m_backend.BeginFrame();
}
void EndFrameAndPresent(
::XCEngine::Editor::Platform::D3D12WindowRenderer& windowRenderer,
const float clearColor[4],
const RenderCallback& beforeUiRender,
const RenderCallback& afterUiRender) override {
ImGui::Render();
windowRenderer.Render(m_backend, clearColor, beforeUiRender, afterUiRender);
}
bool CreateTextureDescriptor(
::XCEngine::RHI::RHIDevice* device,
::XCEngine::RHI::RHITexture* texture,
D3D12_CPU_DESCRIPTOR_HANDLE* outCpuHandle,
D3D12_GPU_DESCRIPTOR_HANDLE* outGpuHandle,
ImTextureID* outTextureId) override {
return m_backend.CreateTextureDescriptor(
device,
texture,
outCpuHandle,
outGpuHandle,
outTextureId);
}
void FreeTextureDescriptor(
D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle,
D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle) override {
m_backend.FreeTextureDescriptor(cpuHandle, gpuHandle);
}
private:
::XCEngine::Editor::UI::ImGuiBackendBridge m_backend;
};
} // namespace
std::unique_ptr<IEditorHostCompositor> CreateImGuiHostCompositor() {
return std::make_unique<ImGuiHostCompositor>();
}
} // namespace XCUIBackend
} // namespace Editor
} // namespace XCEngine