Isolate XCNewEditor default build from ImGui headers

This commit is contained in:
2026-04-05 16:11:08 +08:00
parent 18f53bd920
commit 9db0d82082
17 changed files with 546 additions and 308 deletions

View File

@@ -32,7 +32,11 @@ Old `editor` replacement is explicitly out of scope for this phase.
- `Application.cpp` no longer directly includes `<imgui.h>`, even though the compatibility host path is still compiled into `new_editor` - `Application.cpp` no longer directly includes `<imgui.h>`, even though the compatibility host path is still compiled into `new_editor`
- The `new_editor` build now also has an explicit compatibility-source slice: - The `new_editor` build now also has an explicit compatibility-source slice:
- legacy ImGui shell sources and vendored ImGui backend sources are now grouped into a dedicated compatibility static library instead of being compiled directly as part of the main `XCNewEditor` source list - legacy ImGui shell sources and vendored ImGui backend sources are now grouped into a dedicated compatibility static library instead of being compiled directly as part of the main `XCNewEditor` source list
- the main `XCNewEditor` target still sees ImGui headers through older editor bridge headers, but its default implementation source list is now narrower than the compatibility source list - the main `XCNewEditor` target no longer compiles those legacy sources directly, but it still carries `${IMGUI_SOURCE_DIR}` / `${IMGUI_SOURCE_DIR}/backends` on its private include surface and still reaches `<imgui.h>` through older editor bridge headers such as `editor/src/UI/ImGuiBackendBridge.h`
- The next checkpoint to append after this file should be the main-target header-isolation milestone:
- `XCNewEditor` drops direct `${IMGUI_SOURCE_DIR}` / `${IMGUI_SOURCE_DIR}/backends` include-directory requirements
- the default native compile path no longer reaches `<imgui.h>` through `editor/src/UI/ImGuiBackendBridge.h` or equivalent bridge headers
- `XCNewEditorImGuiCompat` remains the explicit compatibility-only consumer of legacy ImGui headers and backend sources
- Old `editor` replacement remains deferred; all active execution still stays inside XCUI shared code and `new_editor`. - Old `editor` replacement remains deferred; all active execution still stays inside XCUI shared code and `new_editor`.
## Three-Layer Status ## Three-Layer Status
@@ -127,7 +131,7 @@ Current gap:
Current gap: Current gap:
- The default shell host is now native, but the legacy ImGui shell and panel path still exists as a compatibility host and is still compiled into `new_editor`. - The default shell host is now native, but the legacy ImGui shell and panel path still exists as a compatibility host and is still compiled into `new_editor`.
- The default native shell path is still compiled through an `Application` translation unit that directly includes legacy ImGui host/presenter/compositor code, and `new_editor/CMakeLists.txt` still treats those legacy ImGui sources and include paths as target-global defaults. - The default native shell path is now split away from direct `ImGui::*` calls at the translation-unit/source-list level, but the main `XCNewEditor` target still keeps ImGui on its header/include surface through `target_include_directories(...)` and older editor bridge headers.
- The native shell currently proves direct runtime composition, but its shell chrome is still a bespoke `Application`-side layout rather than a fully shared XCUI-authored editor shell document. - The native shell currently proves direct runtime composition, but its shell chrome is still a bespoke `Application`-side layout rather than a fully shared XCUI-authored editor shell document.
- Editor-specialized widgets are still incomplete at the shared-module level: the authored prototypes exist, but virtualization, multi-selection/focus traversal, toolbar/menu chrome, menu interaction widgets, and icon-atlas widgets are not yet extracted into reusable XCUI modules. - Editor-specialized widgets are still incomplete at the shared-module level: the authored prototypes exist, but virtualization, multi-selection/focus traversal, toolbar/menu chrome, menu interaction widgets, and icon-atlas widgets are not yet extracted into reusable XCUI modules.
- The default native text path now uses a standalone Windows/GDI atlas through `XCUIStandaloneTextAtlasProvider`, but that provider still lives inside `new_editor` and is not yet promoted into a shared/cross-platform text subsystem. - The default native text path now uses a standalone Windows/GDI atlas through `XCUIStandaloneTextAtlasProvider`, but that provider still lives inside `new_editor` and is not yet promoted into a shared/cross-platform text subsystem.
@@ -284,17 +288,18 @@ Current gap:
- standalone atlas coverage now includes reset/rebuild, non-nominal size resolution, lazy glyph insertion/fallback behavior, and smoke use without any ImGui context - standalone atlas coverage now includes reset/rebuild, non-nominal size resolution, lazy glyph insertion/fallback behavior, and smoke use without any ImGui context
- Legacy shell chrome / HUD rendering is now split out of the main `Application.cpp` translation unit: - Legacy shell chrome / HUD rendering is now split out of the main `Application.cpp` translation unit:
- the direct `ImGui::*` shell rendering path now lives in a dedicated legacy-only `Application` implementation file - the direct `ImGui::*` shell rendering path now lives in a dedicated legacy-only `Application` implementation file
- the main `Application.cpp` native host path no longer directly includes `<imgui.h>`, reducing default-path compile-time coupling before the larger compat-target split - the main `Application.cpp` native host path no longer directly includes `<imgui.h>`, reducing default-path compile-time coupling while the remaining main-target header/include cleanup stays open
- `new_editor` build composition is now split into main/native and compatibility slices: - `new_editor` build composition is now split into main/native and compatibility slices:
- the main `XCNewEditor` target no longer compiles legacy ImGui shell/panel/backend source files directly - the main `XCNewEditor` target no longer compiles legacy ImGui shell/panel/backend source files directly
- legacy ImGui shell/panel/backend sources plus vendored ImGui sources now build behind a dedicated compatibility static library that the main executable links - legacy ImGui shell/panel/backend sources plus vendored ImGui sources now build behind a dedicated compatibility static library that the main executable links
- the remaining build-side cleanup is narrower now: `XCNewEditor` still carries ImGui include directories and older editor bridge headers on its direct compile surface, so the next checkpoint is header/include isolation rather than another source-list split
## Phase Risks Still Open ## Phase Risks Still Open
- Schema instance validation is still open beyond `.xcschema` self-definition and artifact round-trip coverage. - Schema instance validation is still open beyond `.xcschema` self-definition and artifact round-trip coverage.
- `ScrollView` is still authored/static; no wheel-driven scrolling or virtualization yet. - `ScrollView` is still authored/static; no wheel-driven scrolling or virtualization yet.
- The default native shell path still compiles through an `Application.cpp` unit that directly pulls in ImGui compatibility host code, so the native default path is not yet isolated at the translation-unit boundary. - `Application.cpp` is now split away from direct `ImGui::*` calls, but `XCNewEditor` still depends on ImGui at the header/include level because `new_editor/CMakeLists.txt` still adds `${IMGUI_SOURCE_DIR}` / `${IMGUI_SOURCE_DIR}/backends` to the main target and the editor bridge chain still reaches `editor/src/UI/ImGuiBackendBridge.h` -> `<imgui.h>`.
- `new_editor/CMakeLists.txt` still builds the legacy ImGui host/compositor/backend sources and include directories as target-global defaults instead of a compatibility-only slice. - `XCNewEditorImGuiCompat` is now the compatibility-source slice, but the main executable still links it unconditionally and has not yet reduced its direct include surface to a compat-free default-path baseline.
- The default native text path now owns its atlas without ImGui, but the provider is still Windows-only and remains trapped inside `new_editor` instead of a shared/cross-platform text layer. - The default native text path now owns its atlas without ImGui, but the provider is still Windows-only and remains trapped inside `new_editor` instead of a shared/cross-platform text layer.
- Hosted-preview compatibility presentation still depends on an ImGui-only inline presenter path when not using the queued native surface path. - Hosted-preview compatibility presentation still depends on an ImGui-only inline presenter path when not using the queued native surface path.
- Editor widget coverage is still prototype-driven inside `LayoutLab`; it has not yet been promoted into a full reusable shared widget/runtime layer with command routing, virtualization, and property-edit transactions. - Editor widget coverage is still prototype-driven inside `LayoutLab`; it has not yet been promoted into a full reusable shared widget/runtime layer with command routing, virtualization, and property-edit transactions.
@@ -302,12 +307,12 @@ Current gap:
## Execution-Plan Alignment ## Execution-Plan Alignment
- Against `XCUI完整架构设计与执行计划.md`, current `new_editor` progress should be treated as an early `Phase 8` foothold rather than full `Milestone E` completion: - Against `XCUI完整架构设计与执行计划.md`, current `new_editor` progress should be treated as an early `Phase 8` foothold rather than full `Milestone E` completion:
- landed: `NativeWindowUICompositor`, native shell packet composition, native hosted-preview publication, XCUI-owned texture registrations, native panel surface-image presentation, standalone native text-atlas ownership inside `new_editor` - landed: `NativeWindowUICompositor`, native shell packet composition, native hosted-preview publication, XCUI-owned texture registrations, native panel surface-image presentation, standalone native text-atlas ownership inside `new_editor`, legacy `Application` TU split, and `XCNewEditorImGuiCompat`
- not yet landed: default-path translation-unit isolation from legacy ImGui host code, promotion of the native text-atlas path into a shared/cross-platform text subsystem, shared XCUI-authored editor shell chrome - not yet landed: main-target header/include isolation from ImGui, promotion of the native text-atlas path into a shared/cross-platform text subsystem, shared XCUI-authored editor shell chrome
- That means the next de-ImGui push should not keep centering on hosted-preview publication; that milestone is now effectively closed for the default native shell path. - That means the next de-ImGui push should not keep centering on hosted-preview publication; that milestone is now effectively closed for the default native shell path.
- The real remaining default-path blockers are: - The real remaining default-path blockers are:
- isolate legacy ImGui shell/compositor/presenter wiring from the default `Application` build path - remove the main `XCNewEditor` target's direct ImGui header dependency by cutting the `D3D12WindowRenderer` / `ImGuiBackendBridge` include chain off the default compile path
- stop treating ImGui include paths and backend sources as the default `new_editor` compilation surface - stop treating ImGui include paths as part of the default `new_editor` compilation surface, leaving them only on the compatibility slice
- harden and promote `XCUIStandaloneTextAtlasProvider` / editor font bootstrap into a shared native text subsystem - harden and promote `XCUIStandaloneTextAtlasProvider` / editor font bootstrap into a shared native text subsystem
- move native shell chrome out of bespoke `Application` layout code and into a shared XCUI shell model or authored shell document - move native shell chrome out of bespoke `Application` layout code and into a shared XCUI shell model or authored shell document
@@ -315,7 +320,7 @@ Current gap:
1. Expand runtime/game-layer ownership from the current `SceneRuntime` UI context into scene-declared HUD/menu bootstrapping, draw submission, and higher-level runtime UI policies. 1. Expand runtime/game-layer ownership from the current `SceneRuntime` UI context into scene-declared HUD/menu bootstrapping, draw submission, and higher-level runtime UI policies.
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 chrome, shell-state adoption, virtualization, and broader focus/multi-selection behavior. 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 chrome, shell-state adoption, virtualization, and broader focus/multi-selection behavior.
3. Isolate legacy ImGui shell/compositor/presenter wiring from the default native path first, starting with `Application` translation-unit separation and a `new_editor` build split that stops treating ImGui include paths and sources as the default compilation surface. 3. Finish the remaining main-target de-ImGui cleanup first: remove direct ImGui header/include requirements from `XCNewEditor`, keep ImGui confined to `XCNewEditorImGuiCompat`, and append the next checkpoint once the default native compile path no longer reaches `<imgui.h>`.
4. Promote the current standalone native text/font path out of `new_editor`, harden its atlas invalidation/caching contract, and remove the remaining default-path ImGui text/bootstrap ownership around compatibility-only code. 4. Promote the current standalone native text/font path out of `new_editor`, harden its atlas invalidation/caching contract, and remove the remaining default-path ImGui text/bootstrap ownership around compatibility-only code.
5. Promote the native shell chrome and card layout out of bespoke `Application` code into a shared XCUI/editor-layer shell model or authored shell document. 5. Promote the native shell chrome and card layout out of bespoke `Application` code into a shared XCUI/editor-layer shell model or authored shell document.
6. Continue phased validation, commit, push, and plan refresh after each stable batch. 6. Continue phased validation, commit, push, and plan refresh after each stable batch.

View File

@@ -1,4 +1,5 @@
#include "Application.h" #include "Application.h"
#include "Platform/D3D12WindowRendererImGuiInterop.h"
#include "Core/EditorLoggingSetup.h" #include "Core/EditorLoggingSetup.h"
#include "Core/ProjectRootResolver.h" #include "Core/ProjectRootResolver.h"
#include "Core/EditorWindowTitle.h" #include "Core/EditorWindowTitle.h"
@@ -264,7 +265,8 @@ void Application::RenderEditorFrame() {
m_layerStack.onUIRender(); m_layerStack.onUIRender();
UpdateWindowTitle(); UpdateWindowTitle();
ImGui::Render(); ImGui::Render();
m_windowRenderer.Render( Platform::RenderImGuiFrame(
m_windowRenderer,
m_imguiBackend, m_imguiBackend,
kClearColor, kClearColor,
[this](const Rendering::RenderContext& renderContext, const Rendering::RenderSurface&) { [this](const Rendering::RenderContext& renderContext, const Rendering::RenderSurface&) {

View File

@@ -1,7 +1,5 @@
#pragma once #pragma once
#include "UI/ImGuiBackendBridge.h"
#include <XCEngine/Rendering/RenderContext.h> #include <XCEngine/Rendering/RenderContext.h>
#include <XCEngine/Rendering/RenderSurface.h> #include <XCEngine/Rendering/RenderSurface.h>
#include <XCEngine/RHI/RHICommandList.h> #include <XCEngine/RHI/RHICommandList.h>
@@ -16,6 +14,10 @@
#include <XCEngine/RHI/D3D12/D3D12Device.h> #include <XCEngine/RHI/D3D12/D3D12Device.h>
#include <XCEngine/RHI/D3D12/D3D12SwapChain.h> #include <XCEngine/RHI/D3D12/D3D12SwapChain.h>
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <d3d12.h> #include <d3d12.h>
#include <functional> #include <functional>
#include <vector> #include <vector>
@@ -179,66 +181,6 @@ public:
return true; return true;
} }
void Render(
UI::ImGuiBackendBridge& imguiBackend,
const float clearColor[4],
const RenderCallback& beforeUiRender = {},
const RenderCallback& afterUiRender = {}) {
auto* d3d12Queue = GetD3D12CommandQueue();
auto* d3d12CommandList = GetD3D12CommandList();
if (m_swapChain == nullptr ||
d3d12Queue == nullptr ||
d3d12CommandList == nullptr ||
m_srvHeap == nullptr) {
return;
}
const uint32_t backBufferIndex = m_swapChain->GetCurrentBackBufferIndex();
if (backBufferIndex >= m_backBufferViews.size() ||
backBufferIndex >= m_backBufferSurfaces.size() ||
m_backBufferViews[backBufferIndex] == nullptr) {
return;
}
RHI::RHIResourceView* renderTargetView = m_backBufferViews[backBufferIndex];
Rendering::RenderSurface& renderSurface = m_backBufferSurfaces[backBufferIndex];
d3d12CommandList->TransitionBarrier(
renderTargetView,
RHI::ResourceStates::Present,
RHI::ResourceStates::RenderTarget);
d3d12CommandList->SetRenderTargets(1, &renderTargetView, nullptr);
d3d12CommandList->ClearRenderTarget(renderTargetView, clearColor);
// Host-owned swapchain surfaces are handed to the callback already bound and ready for rendering.
// The callback must leave the color attachment in RenderTarget state so ImGui can render after it.
if (beforeUiRender) {
beforeUiRender(GetRenderContext(), renderSurface);
// Viewport rendering can bind offscreen render targets on the shared command list.
// Rebind the swapchain backbuffer so the subsequent ImGui draw data lands on the main window.
d3d12CommandList->SetRenderTargets(1, &renderTargetView, nullptr);
}
ID3D12DescriptorHeap* descriptorHeaps[] = { m_srvHeap->GetDescriptorHeap() };
d3d12CommandList->SetDescriptorHeaps(1, descriptorHeaps);
imguiBackend.RenderDrawData(d3d12CommandList->GetCommandList());
if (afterUiRender) {
d3d12CommandList->SetRenderTargets(1, &renderTargetView, nullptr);
afterUiRender(GetRenderContext(), renderSurface);
}
d3d12CommandList->TransitionBarrier(
renderTargetView,
RHI::ResourceStates::RenderTarget,
RHI::ResourceStates::Present);
m_commandList->Close();
void* commandLists[] = { m_commandList };
m_commandQueue->ExecuteCommandLists(1, commandLists);
m_swapChain->Present(1, 0);
}
ID3D12Device* GetDevice() const { ID3D12Device* GetDevice() const {
const auto* device = GetD3D12Device(); const auto* device = GetD3D12Device();
return device ? device->GetDevice() : nullptr; return device ? device->GetDevice() : nullptr;

View File

@@ -0,0 +1,82 @@
#pragma once
#include "D3D12WindowRenderer.h"
#ifdef min
#undef min
#endif
#ifdef max
#undef max
#endif
#include "../UI/ImGuiBackendBridge.h"
namespace XCEngine {
namespace Editor {
namespace Platform {
inline void RenderImGuiFrame(
D3D12WindowRenderer& windowRenderer,
UI::ImGuiBackendBridge& imguiBackend,
const float clearColor[4],
const D3D12WindowRenderer::RenderCallback& beforeUiRender = {},
const D3D12WindowRenderer::RenderCallback& afterUiRender = {}) {
const Rendering::RenderSurface* renderSurface = windowRenderer.GetCurrentRenderSurface();
Rendering::RenderContext renderContext = windowRenderer.GetRenderContext();
if (!renderContext.IsValid() ||
renderContext.commandList == nullptr ||
renderContext.commandQueue == nullptr ||
windowRenderer.GetSwapChain() == nullptr ||
renderSurface == nullptr ||
windowRenderer.GetSrvHeap() == nullptr) {
return;
}
auto* d3d12CommandList = static_cast<RHI::D3D12CommandList*>(renderContext.commandList);
const auto& colorAttachments = renderSurface->GetColorAttachments();
if (colorAttachments.empty() || colorAttachments[0] == nullptr) {
return;
}
RHI::RHIResourceView* renderTargetView = colorAttachments[0];
renderContext.commandList->TransitionBarrier(
renderTargetView,
RHI::ResourceStates::Present,
RHI::ResourceStates::RenderTarget);
renderContext.commandList->SetRenderTargets(1, &renderTargetView, nullptr);
renderContext.commandList->ClearRenderTarget(renderTargetView, clearColor);
// Host-owned swapchain surfaces are handed to the callback already bound and ready for rendering.
// The callback must leave the color attachment in RenderTarget state so ImGui can render after it.
if (beforeUiRender) {
beforeUiRender(renderContext, *renderSurface);
// Viewport rendering can bind offscreen render targets on the shared command list.
// Rebind the swapchain backbuffer so the subsequent ImGui draw data lands on the main window.
renderContext.commandList->SetRenderTargets(1, &renderTargetView, nullptr);
}
ID3D12DescriptorHeap* descriptorHeaps[] = { windowRenderer.GetSrvHeap() };
d3d12CommandList->SetDescriptorHeaps(1, descriptorHeaps);
imguiBackend.RenderDrawData(d3d12CommandList->GetCommandList());
if (afterUiRender) {
renderContext.commandList->SetRenderTargets(1, &renderTargetView, nullptr);
afterUiRender(renderContext, *renderSurface);
}
renderContext.commandList->TransitionBarrier(
renderTargetView,
RHI::ResourceStates::RenderTarget,
RHI::ResourceStates::Present);
renderContext.commandList->Close();
void* commandLists[] = { renderContext.commandList };
renderContext.commandQueue->ExecuteCommandLists(1, commandLists);
windowRenderer.GetSwapChain()->Present(1, 0);
}
} // namespace Platform
} // namespace Editor
} // namespace XCEngine

View File

@@ -79,27 +79,27 @@ set(NEW_EDITOR_IMGUI_COMPAT_SOURCES
add_library(XCNewEditorImGuiCompat STATIC ${NEW_EDITOR_IMGUI_COMPAT_SOURCES}) add_library(XCNewEditorImGuiCompat STATIC ${NEW_EDITOR_IMGUI_COMPAT_SOURCES})
add_executable(${PROJECT_NAME} WIN32 ${NEW_EDITOR_SOURCES}) add_executable(${PROJECT_NAME} WIN32 ${NEW_EDITOR_SOURCES})
target_include_directories(XCNewEditorImGuiCompat PRIVATE set(NEW_EDITOR_COMMON_INCLUDE_DIRS
${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/src
${XCENGINE_ROOT_DIR}/engine/include ${XCENGINE_ROOT_DIR}/engine/include
${XCENGINE_ROOT_DIR}/editor/src )
set(NEW_EDITOR_IMGUI_COMPAT_INCLUDE_DIRS
${NEW_EDITOR_COMMON_INCLUDE_DIRS}
${IMGUI_SOURCE_DIR} ${IMGUI_SOURCE_DIR}
${IMGUI_SOURCE_DIR}/backends ${IMGUI_SOURCE_DIR}/backends
) )
target_include_directories(${PROJECT_NAME} PRIVATE target_include_directories(XCNewEditorImGuiCompat PRIVATE ${NEW_EDITOR_IMGUI_COMPAT_INCLUDE_DIRS})
${CMAKE_CURRENT_SOURCE_DIR}/src
${XCENGINE_ROOT_DIR}/engine/include target_include_directories(${PROJECT_NAME} PRIVATE ${NEW_EDITOR_COMMON_INCLUDE_DIRS})
${XCENGINE_ROOT_DIR}/editor/src
${IMGUI_SOURCE_DIR}
${IMGUI_SOURCE_DIR}/backends
)
file(TO_CMAKE_PATH "${XCENGINE_ROOT_DIR}" XCENGINE_ROOT_DIR_CMAKE) file(TO_CMAKE_PATH "${XCENGINE_ROOT_DIR}" XCENGINE_ROOT_DIR_CMAKE)
target_compile_definitions(XCNewEditorImGuiCompat PRIVATE target_compile_definitions(XCNewEditorImGuiCompat PRIVATE
UNICODE UNICODE
_UNICODE _UNICODE
NOMINMAX
XCENGINE_NEW_EDITOR_REPO_ROOT="${XCENGINE_ROOT_DIR_CMAKE}" XCENGINE_NEW_EDITOR_REPO_ROOT="${XCENGINE_ROOT_DIR_CMAKE}"
) )
target_compile_options(XCNewEditorImGuiCompat PRIVATE /utf-8) target_compile_options(XCNewEditorImGuiCompat PRIVATE /utf-8)
@@ -107,6 +107,7 @@ target_compile_options(XCNewEditorImGuiCompat PRIVATE /utf-8)
target_compile_definitions(${PROJECT_NAME} PRIVATE target_compile_definitions(${PROJECT_NAME} PRIVATE
UNICODE UNICODE
_UNICODE _UNICODE
NOMINMAX
XCENGINE_NEW_EDITOR_REPO_ROOT="${XCENGINE_ROOT_DIR_CMAKE}" XCENGINE_NEW_EDITOR_REPO_ROOT="${XCENGINE_ROOT_DIR_CMAKE}"
) )
target_compile_options(${PROJECT_NAME} PRIVATE /utf-8) target_compile_options(${PROJECT_NAME} PRIVATE /utf-8)
@@ -123,7 +124,12 @@ if(MSVC)
COMPILE_PDB_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/new_editor/compile-pdb-compat/Debug" COMPILE_PDB_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/new_editor/compile-pdb-compat/Debug"
COMPILE_PDB_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/new_editor/compile-pdb-compat/Release" COMPILE_PDB_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/new_editor/compile-pdb-compat/Release"
COMPILE_PDB_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_BINARY_DIR}/new_editor/compile-pdb-compat/MinSizeRel" COMPILE_PDB_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_BINARY_DIR}/new_editor/compile-pdb-compat/MinSizeRel"
COMPILE_PDB_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_BINARY_DIR}/new_editor/compile-pdb-compat/RelWithDebInfo") COMPILE_PDB_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_BINARY_DIR}/new_editor/compile-pdb-compat/RelWithDebInfo"
PDB_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/new_editor/pdb-compat"
PDB_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/new_editor/pdb-compat/Debug"
PDB_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/new_editor/pdb-compat/Release"
PDB_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_BINARY_DIR}/new_editor/pdb-compat/MinSizeRel"
PDB_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_BINARY_DIR}/new_editor/pdb-compat/RelWithDebInfo")
target_link_options(${PROJECT_NAME} PRIVATE target_link_options(${PROJECT_NAME} PRIVATE
$<$<CONFIG:Debug,RelWithDebInfo>:/INCREMENTAL:NO>) $<$<CONFIG:Debug,RelWithDebInfo>:/INCREMENTAL:NO>)
set_property(TARGET ${PROJECT_NAME} PROPERTY set_property(TARGET ${PROJECT_NAME} PROPERTY

View File

@@ -1,5 +1,4 @@
#include "Application.h" #include "Application.h"
#include "XCUIBackend/LegacyImGuiHostInterop.h"
#include "XCUIBackend/NativeWindowUICompositor.h" #include "XCUIBackend/NativeWindowUICompositor.h"
#include <XCEngine/Core/Asset/ResourceManager.h> #include <XCEngine/Core/Asset/ResourceManager.h>
@@ -9,6 +8,10 @@
#include <cmath> #include <cmath>
#include <sstream> #include <sstream>
#ifdef DrawText
#undef DrawText
#endif
namespace XCEngine { namespace XCEngine {
namespace NewEditor { namespace NewEditor {
@@ -37,48 +40,6 @@ void ShutdownAndDelete(ResourceType*& resource) {
resource = nullptr; resource = nullptr;
} }
const char* GetHostedPreviewPathLabel(bool nativeRequested, bool nativePresenterBound) {
if (nativeRequested && nativePresenterBound) {
return "native queued offscreen surface";
}
if (nativeRequested) {
return "native requested, hosted presenter bound";
}
if (nativePresenterBound) {
return "hosted presenter requested, native presenter still bound";
}
return "hosted presenter";
}
const char* GetHostedPreviewStateLabel(
bool hostedPreviewEnabled,
bool nativePresenterBound,
bool presentedThisFrame,
bool queuedToNativePassThisFrame,
bool surfaceImageAvailable,
bool surfaceAllocated,
bool surfaceReady,
bool descriptorAvailable) {
if (!hostedPreviewEnabled) {
return "disabled";
}
if (nativePresenterBound) {
if (surfaceImageAvailable && surfaceReady) {
return "live";
}
if (queuedToNativePassThisFrame || surfaceAllocated || descriptorAvailable) {
return "warming";
}
return "awaiting submit";
}
if (presentedThisFrame) {
return "live";
}
return "idle";
}
bool ContainsPoint(const UI::UIRect& rect, const UI::UIPoint& point) { bool ContainsPoint(const UI::UIRect& rect, const UI::UIPoint& point) {
return point.x >= rect.x && return point.x >= rect.x &&
point.y >= rect.y && point.y >= rect.y &&
@@ -174,42 +135,10 @@ NativeShellPanelLayout MakePanelLayout(
} }
} // namespace } // namespace
std::unique_ptr<::XCEngine::Editor::XCUIBackend::IXCUIHostedPreviewPresenter>
Application::CreateHostedPreviewPresenter(bool nativePreview) {
if (nativePreview) {
return ::XCEngine::Editor::XCUIBackend::CreateQueuedNativeXCUIHostedPreviewPresenter(
m_hostedPreviewQueue,
m_hostedPreviewSurfaceRegistry);
}
return ::XCEngine::Editor::XCUIBackend::CreateLegacyImGuiHostedPreviewPresenter();
}
bool Application::IsNativeWindowHostEnabled() const { bool Application::IsNativeWindowHostEnabled() const {
return m_windowHostMode == WindowHostMode::NativeXCUI; return m_windowHostMode == WindowHostMode::NativeXCUI;
} }
void Application::InitializePanelsForActiveWindowHost() {
if (IsNativeWindowHostEnabled()) {
InitializeNativeShell();
return;
}
InitializeLegacyImGuiPanels();
ConfigureHostedPreviewPresenters();
}
void Application::InitializeLegacyImGuiPanels() {
m_demoPanel = std::make_unique<XCUIDemoPanel>(
&m_xcuiInputSource,
CreateHostedPreviewPresenter(IsNativeHostedPreviewEnabled(ShellPanelId::XCUIDemo)),
::XCEngine::Editor::XCUIBackend::CreateLegacyImGuiPanelCanvasHost());
m_layoutLabPanel = std::make_unique<XCUILayoutLabPanel>(
&m_xcuiInputSource,
CreateHostedPreviewPresenter(IsNativeHostedPreviewEnabled(ShellPanelId::XCUILayoutLab)),
::XCEngine::Editor::XCUIBackend::CreateLegacyImGuiPanelCanvasHost());
}
void Application::InitializeNativeShell() { void Application::InitializeNativeShell() {
m_nativeActivePanel = m_shellChromeState.IsPanelVisible(ShellPanelId::XCUIDemo) m_nativeActivePanel = m_shellChromeState.IsPanelVisible(ShellPanelId::XCUIDemo)
? ShellPanelId::XCUIDemo ? ShellPanelId::XCUIDemo
@@ -238,115 +167,6 @@ bool Application::IsNativeHostedPreviewEnabled(ShellPanelId panelId) const {
return m_shellChromeState.IsNativeHostedPreviewActive(panelId); return m_shellChromeState.IsNativeHostedPreviewActive(panelId);
} }
void Application::ConfigureHostedPreviewPresenters() {
const ShellPanelChromeState* demoState = TryGetShellPanelState(ShellPanelId::XCUIDemo);
if (m_demoPanel != nullptr) {
m_demoPanel->SetVisible(demoState != nullptr && demoState->visible);
m_demoPanel->SetHostedPreviewEnabled(demoState == nullptr || demoState->hostedPreviewEnabled);
m_demoPanel->SetHostedPreviewPresenter(CreateHostedPreviewPresenter(IsNativeHostedPreviewEnabled(ShellPanelId::XCUIDemo)));
}
const ShellPanelChromeState* layoutLabState = TryGetShellPanelState(ShellPanelId::XCUILayoutLab);
if (m_layoutLabPanel != nullptr) {
m_layoutLabPanel->SetVisible(layoutLabState != nullptr && layoutLabState->visible);
m_layoutLabPanel->SetHostedPreviewEnabled(layoutLabState == nullptr || layoutLabState->hostedPreviewEnabled);
m_layoutLabPanel->SetHostedPreviewPresenter(
CreateHostedPreviewPresenter(IsNativeHostedPreviewEnabled(ShellPanelId::XCUILayoutLab)));
}
}
void Application::ConfigureShellCommandRouter() {
m_shellCommandRouter.Clear();
ShellCommandBindings bindings = {};
bindings.getXCUIDemoPanelVisible = [this]() {
return m_shellChromeState.IsPanelVisible(ShellPanelId::XCUIDemo);
};
bindings.setXCUIDemoPanelVisible = [this](bool visible) {
m_shellChromeState.SetPanelVisible(ShellPanelId::XCUIDemo, visible);
if (m_demoPanel != nullptr) {
m_demoPanel->SetVisible(visible);
}
};
bindings.getXCUILayoutLabPanelVisible = [this]() {
return m_shellChromeState.IsPanelVisible(ShellPanelId::XCUILayoutLab);
};
bindings.setXCUILayoutLabPanelVisible = [this](bool visible) {
m_shellChromeState.SetPanelVisible(ShellPanelId::XCUILayoutLab, visible);
if (m_layoutLabPanel != nullptr) {
m_layoutLabPanel->SetVisible(visible);
}
};
bindings.getImGuiDemoWindowVisible = [this]() {
return IsShellViewToggleEnabled(ShellViewToggleId::ImGuiDemoWindow);
};
bindings.setImGuiDemoWindowVisible = [this](bool visible) {
SetShellViewToggleEnabled(ShellViewToggleId::ImGuiDemoWindow, visible);
};
bindings.getNativeBackdropVisible = [this]() {
return IsShellViewToggleEnabled(ShellViewToggleId::NativeBackdrop);
};
bindings.setNativeBackdropVisible = [this](bool visible) {
SetShellViewToggleEnabled(ShellViewToggleId::NativeBackdrop, visible);
};
bindings.getPulseAccentEnabled = [this]() {
return IsShellViewToggleEnabled(ShellViewToggleId::PulseAccent);
};
bindings.setPulseAccentEnabled = [this](bool enabled) {
SetShellViewToggleEnabled(ShellViewToggleId::PulseAccent, enabled);
};
bindings.getNativeXCUIOverlayVisible = [this]() {
return IsShellViewToggleEnabled(ShellViewToggleId::NativeXCUIOverlay);
};
bindings.setNativeXCUIOverlayVisible = [this](bool visible) {
SetShellViewToggleEnabled(ShellViewToggleId::NativeXCUIOverlay, visible);
};
bindings.getHostedPreviewHudVisible = [this]() {
return IsShellViewToggleEnabled(ShellViewToggleId::HostedPreviewHud);
};
bindings.setHostedPreviewHudVisible = [this](bool visible) {
SetShellViewToggleEnabled(ShellViewToggleId::HostedPreviewHud, visible);
};
bindings.getNativeDemoPanelPreviewEnabled = [this]() {
return IsNativeHostedPreviewEnabled(ShellPanelId::XCUIDemo);
};
bindings.setNativeDemoPanelPreviewEnabled = [this](bool enabled) {
m_shellChromeState.SetHostedPreviewMode(
ShellPanelId::XCUIDemo,
enabled ? ShellHostedPreviewMode::NativeOffscreen : ShellHostedPreviewMode::HostedPresenter);
};
bindings.getNativeLayoutLabPreviewEnabled = [this]() {
return IsNativeHostedPreviewEnabled(ShellPanelId::XCUILayoutLab);
};
bindings.setNativeLayoutLabPreviewEnabled = [this](bool enabled) {
m_shellChromeState.SetHostedPreviewMode(
ShellPanelId::XCUILayoutLab,
enabled ? ShellHostedPreviewMode::NativeOffscreen : ShellHostedPreviewMode::HostedPresenter);
};
bindings.onHostedPreviewModeChanged = [this]() { ConfigureHostedPreviewPresenters(); };
Application::RegisterShellViewCommands(m_shellCommandRouter, bindings);
}
::XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameDelta
Application::DispatchShellShortcuts(
const ::XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameSnapshot& snapshot) {
::XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameSnapshot shellSnapshot = snapshot;
if (!IsNativeWindowHostEnabled()) {
::XCEngine::Editor::XCUIBackend::ApplyLegacyImGuiHostInputCapture(shellSnapshot);
}
if (!m_shellInputBridge.HasBaseline()) {
m_shellInputBridge.Prime(shellSnapshot);
}
const ::XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameDelta frameDelta =
m_shellInputBridge.Translate(shellSnapshot);
const ::XCEngine::Editor::XCUIBackend::XCUIEditorCommandInputSnapshot commandSnapshot =
Application::BuildShellShortcutSnapshot(frameDelta);
m_shellCommandRouter.InvokeMatchingShortcut({ &commandSnapshot });
return frameDelta;
}
Application::HostedPreviewPanelDiagnostics Application::BuildHostedPreviewPanelDiagnostics( Application::HostedPreviewPanelDiagnostics Application::BuildHostedPreviewPanelDiagnostics(
const char* debugName, const char* debugName,
const char* fallbackDebugSource, const char* fallbackDebugSource,
@@ -452,8 +272,7 @@ int Application::Run(HINSTANCE instance, int nCmdShow) {
Frame(); Frame();
} }
m_demoPanel.reset(); ResetLegacyPanels();
m_layoutLabPanel.reset();
ShutdownWindowCompositor(); ShutdownWindowCompositor();
ShutdownRenderer(); ShutdownRenderer();
resourceManager.Shutdown(); resourceManager.Shutdown();
@@ -558,18 +377,6 @@ bool Application::InitializeRenderer() {
return initialized; return initialized;
} }
void Application::InitializeWindowCompositor() {
m_windowCompositor = IsNativeWindowHostEnabled()
? ::XCEngine::Editor::XCUIBackend::CreateNativeWindowUICompositor()
: ::XCEngine::Editor::XCUIBackend::CreateLegacyImGuiWindowUICompositor();
if (m_windowCompositor != nullptr) {
m_windowCompositor->Initialize(
m_hwnd,
m_windowRenderer,
[]() { (void)::XCEngine::Editor::XCUIBackend::ConfigureLegacyImGuiHostFonts(); });
}
}
void Application::ShutdownWindowCompositor() { void Application::ShutdownWindowCompositor() {
DestroyHostedPreviewSurfaces(); DestroyHostedPreviewSurfaces();
if (m_windowCompositor != nullptr) { if (m_windowCompositor != nullptr) {
@@ -599,15 +406,6 @@ void Application::DestroyHostedPreviewSurfaces() {
m_hostedPreviewSurfaces.clear(); m_hostedPreviewSurfaces.clear();
} }
void Application::SyncShellChromePanelStateFromPanels() {
m_shellChromeState.SetPanelVisible(
ShellPanelId::XCUIDemo,
m_demoPanel != nullptr && m_demoPanel->IsVisible());
m_shellChromeState.SetPanelVisible(
ShellPanelId::XCUILayoutLab,
m_layoutLabPanel != nullptr && m_layoutLabPanel->IsVisible());
}
void Application::SyncHostedPreviewSurfaces() { void Application::SyncHostedPreviewSurfaces() {
const auto isNativePreviewEnabled = [this](const std::string& debugName) { const auto isNativePreviewEnabled = [this](const std::string& debugName) {
const ShellPanelChromeState* demoState = TryGetShellPanelState(ShellPanelId::XCUIDemo); const ShellPanelChromeState* demoState = TryGetShellPanelState(ShellPanelId::XCUIDemo);

View File

@@ -3,12 +3,6 @@
#include <XCEngine/Input/InputTypes.h> #include <XCEngine/Input/InputTypes.h>
#include "XCUIBackend/XCUIEditorCommandRouter.h" #include "XCUIBackend/XCUIEditorCommandRouter.h"
#include "panels/XCUIDemoPanel.h"
#include "panels/XCUILayoutLabPanel.h"
#include "Platform/D3D12WindowRenderer.h"
#include "Rendering/MainWindowNativeBackdropRenderer.h"
#include "XCUIBackend/IWindowUICompositor.h"
#include "XCUIBackend/NativeXCUIPanelCanvasHost.h" #include "XCUIBackend/NativeXCUIPanelCanvasHost.h"
#include "XCUIBackend/XCUIDemoRuntime.h" #include "XCUIBackend/XCUIDemoRuntime.h"
#include "XCUIBackend/XCUIHostedPreviewPresenter.h" #include "XCUIBackend/XCUIHostedPreviewPresenter.h"
@@ -17,6 +11,9 @@
#include "XCUIBackend/XCUIRHIRenderBackend.h" #include "XCUIBackend/XCUIRHIRenderBackend.h"
#include "XCUIBackend/XCUIShellChromeState.h" #include "XCUIBackend/XCUIShellChromeState.h"
#include "XCUIBackend/XCUIStandaloneTextAtlasProvider.h" #include "XCUIBackend/XCUIStandaloneTextAtlasProvider.h"
#include "XCUIBackend/UITextureRegistration.h"
#include "Platform/D3D12WindowRenderer.h"
#include "Rendering/MainWindowNativeBackdropRenderer.h"
#include <chrono> #include <chrono>
#include <cstdint> #include <cstdint>
@@ -29,10 +26,24 @@
#include <windows.h> #include <windows.h>
namespace XCEngine { namespace XCEngine {
namespace Editor {
namespace XCUIBackend {
class IWindowUICompositor;
} // namespace XCUIBackend
} // namespace Editor
namespace NewEditor { namespace NewEditor {
class XCUIDemoPanel;
class XCUILayoutLabPanel;
class Application { class Application {
public: public:
Application();
~Application();
using ShellChromeState = ::XCEngine::Editor::XCUIBackend::XCUIShellChromeState; using ShellChromeState = ::XCEngine::Editor::XCUIBackend::XCUIShellChromeState;
using ShellPanelId = ::XCEngine::Editor::XCUIBackend::XCUIShellPanelId; using ShellPanelId = ::XCEngine::Editor::XCUIBackend::XCUIShellPanelId;
using ShellViewToggleId = ::XCEngine::Editor::XCUIBackend::XCUIShellViewToggleId; using ShellViewToggleId = ::XCEngine::Editor::XCUIBackend::XCUIShellViewToggleId;
@@ -416,6 +427,7 @@ private:
HostedPreviewOffscreenSurface& previewSurface, HostedPreviewOffscreenSurface& previewSurface,
const ::XCEngine::Rendering::RenderContext& renderContext, const ::XCEngine::Rendering::RenderContext& renderContext,
const ::XCEngine::UI::UIDrawData& drawData); const ::XCEngine::UI::UIDrawData& drawData);
void ResetLegacyPanels();
void ConfigureShellCommandRouter(); void ConfigureShellCommandRouter();
::XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameDelta DispatchShellShortcuts( ::XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameDelta DispatchShellShortcuts(
const ::XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameSnapshot& snapshot); const ::XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameSnapshot& snapshot);

View File

@@ -1,6 +1,8 @@
#include "Application.h" #include "Application.h"
#include "XCUIBackend/LegacyImGuiHostInterop.h" #include "XCUIBackend/LegacyImGuiHostInterop.h"
#include "panels/XCUIDemoPanel.h"
#include "panels/XCUILayoutLabPanel.h"
#include <imgui.h> #include <imgui.h>
@@ -65,6 +67,176 @@ const char* GetHostedPreviewStateLabel(
} // namespace } // namespace
Application::Application() = default;
Application::~Application() = default;
std::unique_ptr<::XCEngine::Editor::XCUIBackend::IXCUIHostedPreviewPresenter>
Application::CreateHostedPreviewPresenter(bool nativePreview) {
if (nativePreview) {
return ::XCEngine::Editor::XCUIBackend::CreateQueuedNativeXCUIHostedPreviewPresenter(
m_hostedPreviewQueue,
m_hostedPreviewSurfaceRegistry);
}
return ::XCEngine::Editor::XCUIBackend::CreateLegacyImGuiHostedPreviewPresenter();
}
void Application::InitializePanelsForActiveWindowHost() {
if (IsNativeWindowHostEnabled()) {
InitializeNativeShell();
return;
}
InitializeLegacyImGuiPanels();
ConfigureHostedPreviewPresenters();
}
void Application::InitializeLegacyImGuiPanels() {
m_demoPanel = std::make_unique<XCUIDemoPanel>(
&m_xcuiInputSource,
CreateHostedPreviewPresenter(IsNativeHostedPreviewEnabled(ShellPanelId::XCUIDemo)),
::XCEngine::Editor::XCUIBackend::CreateLegacyImGuiPanelCanvasHost());
m_layoutLabPanel = std::make_unique<XCUILayoutLabPanel>(
&m_xcuiInputSource,
CreateHostedPreviewPresenter(IsNativeHostedPreviewEnabled(ShellPanelId::XCUILayoutLab)),
::XCEngine::Editor::XCUIBackend::CreateLegacyImGuiPanelCanvasHost());
}
void Application::ConfigureHostedPreviewPresenters() {
const ShellPanelChromeState* demoState = TryGetShellPanelState(ShellPanelId::XCUIDemo);
if (m_demoPanel != nullptr) {
m_demoPanel->SetVisible(demoState != nullptr && demoState->visible);
m_demoPanel->SetHostedPreviewEnabled(demoState == nullptr || demoState->hostedPreviewEnabled);
m_demoPanel->SetHostedPreviewPresenter(CreateHostedPreviewPresenter(IsNativeHostedPreviewEnabled(ShellPanelId::XCUIDemo)));
}
const ShellPanelChromeState* layoutLabState = TryGetShellPanelState(ShellPanelId::XCUILayoutLab);
if (m_layoutLabPanel != nullptr) {
m_layoutLabPanel->SetVisible(layoutLabState != nullptr && layoutLabState->visible);
m_layoutLabPanel->SetHostedPreviewEnabled(layoutLabState == nullptr || layoutLabState->hostedPreviewEnabled);
m_layoutLabPanel->SetHostedPreviewPresenter(
CreateHostedPreviewPresenter(IsNativeHostedPreviewEnabled(ShellPanelId::XCUILayoutLab)));
}
}
void Application::ResetLegacyPanels() {
m_demoPanel.reset();
m_layoutLabPanel.reset();
}
void Application::SyncShellChromePanelStateFromPanels() {
m_shellChromeState.SetPanelVisible(
ShellPanelId::XCUIDemo,
m_demoPanel != nullptr && m_demoPanel->IsVisible());
m_shellChromeState.SetPanelVisible(
ShellPanelId::XCUILayoutLab,
m_layoutLabPanel != nullptr && m_layoutLabPanel->IsVisible());
}
void Application::ConfigureShellCommandRouter() {
m_shellCommandRouter.Clear();
ShellCommandBindings bindings = {};
bindings.getXCUIDemoPanelVisible = [this]() {
return m_shellChromeState.IsPanelVisible(ShellPanelId::XCUIDemo);
};
bindings.setXCUIDemoPanelVisible = [this](bool visible) {
m_shellChromeState.SetPanelVisible(ShellPanelId::XCUIDemo, visible);
if (m_demoPanel != nullptr) {
m_demoPanel->SetVisible(visible);
}
};
bindings.getXCUILayoutLabPanelVisible = [this]() {
return m_shellChromeState.IsPanelVisible(ShellPanelId::XCUILayoutLab);
};
bindings.setXCUILayoutLabPanelVisible = [this](bool visible) {
m_shellChromeState.SetPanelVisible(ShellPanelId::XCUILayoutLab, visible);
if (m_layoutLabPanel != nullptr) {
m_layoutLabPanel->SetVisible(visible);
}
};
bindings.getImGuiDemoWindowVisible = [this]() {
return IsShellViewToggleEnabled(ShellViewToggleId::ImGuiDemoWindow);
};
bindings.setImGuiDemoWindowVisible = [this](bool visible) {
SetShellViewToggleEnabled(ShellViewToggleId::ImGuiDemoWindow, visible);
};
bindings.getNativeBackdropVisible = [this]() {
return IsShellViewToggleEnabled(ShellViewToggleId::NativeBackdrop);
};
bindings.setNativeBackdropVisible = [this](bool visible) {
SetShellViewToggleEnabled(ShellViewToggleId::NativeBackdrop, visible);
};
bindings.getPulseAccentEnabled = [this]() {
return IsShellViewToggleEnabled(ShellViewToggleId::PulseAccent);
};
bindings.setPulseAccentEnabled = [this](bool enabled) {
SetShellViewToggleEnabled(ShellViewToggleId::PulseAccent, enabled);
};
bindings.getNativeXCUIOverlayVisible = [this]() {
return IsShellViewToggleEnabled(ShellViewToggleId::NativeXCUIOverlay);
};
bindings.setNativeXCUIOverlayVisible = [this](bool visible) {
SetShellViewToggleEnabled(ShellViewToggleId::NativeXCUIOverlay, visible);
};
bindings.getHostedPreviewHudVisible = [this]() {
return IsShellViewToggleEnabled(ShellViewToggleId::HostedPreviewHud);
};
bindings.setHostedPreviewHudVisible = [this](bool visible) {
SetShellViewToggleEnabled(ShellViewToggleId::HostedPreviewHud, visible);
};
bindings.getNativeDemoPanelPreviewEnabled = [this]() {
return IsNativeHostedPreviewEnabled(ShellPanelId::XCUIDemo);
};
bindings.setNativeDemoPanelPreviewEnabled = [this](bool enabled) {
m_shellChromeState.SetHostedPreviewMode(
ShellPanelId::XCUIDemo,
enabled ? ShellHostedPreviewMode::NativeOffscreen : ShellHostedPreviewMode::HostedPresenter);
};
bindings.getNativeLayoutLabPreviewEnabled = [this]() {
return IsNativeHostedPreviewEnabled(ShellPanelId::XCUILayoutLab);
};
bindings.setNativeLayoutLabPreviewEnabled = [this](bool enabled) {
m_shellChromeState.SetHostedPreviewMode(
ShellPanelId::XCUILayoutLab,
enabled ? ShellHostedPreviewMode::NativeOffscreen : ShellHostedPreviewMode::HostedPresenter);
};
bindings.onHostedPreviewModeChanged = [this]() { ConfigureHostedPreviewPresenters(); };
Application::RegisterShellViewCommands(m_shellCommandRouter, bindings);
}
::XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameDelta
Application::DispatchShellShortcuts(
const ::XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameSnapshot& snapshot) {
::XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameSnapshot shellSnapshot = snapshot;
if (!IsNativeWindowHostEnabled()) {
::XCEngine::Editor::XCUIBackend::ApplyLegacyImGuiHostInputCapture(shellSnapshot);
}
if (!m_shellInputBridge.HasBaseline()) {
m_shellInputBridge.Prime(shellSnapshot);
}
const ::XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameDelta frameDelta =
m_shellInputBridge.Translate(shellSnapshot);
const ::XCEngine::Editor::XCUIBackend::XCUIEditorCommandInputSnapshot commandSnapshot =
Application::BuildShellShortcutSnapshot(frameDelta);
m_shellCommandRouter.InvokeMatchingShortcut({ &commandSnapshot });
return frameDelta;
}
void Application::InitializeWindowCompositor() {
m_windowCompositor = IsNativeWindowHostEnabled()
? ::XCEngine::Editor::XCUIBackend::CreateNativeWindowUICompositor()
: ::XCEngine::Editor::XCUIBackend::CreateLegacyImGuiWindowUICompositor();
if (m_windowCompositor != nullptr) {
m_windowCompositor->Initialize(
m_hwnd,
m_windowRenderer,
[]() { (void)::XCEngine::Editor::XCUIBackend::ConfigureLegacyImGuiHostFonts(); });
}
}
void Application::FrameLegacyImGuiHost() { void Application::FrameLegacyImGuiHost() {
Application::BeginHostedPreviewFrameLifecycle( Application::BeginHostedPreviewFrameLifecycle(
m_hostedPreviewQueue, m_hostedPreviewQueue,

View File

@@ -0,0 +1,7 @@
#pragma once
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include "../../../editor/src/Platform/D3D12WindowRenderer.h"

View File

@@ -0,0 +1,83 @@
#pragma once
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include "D3D12WindowRenderer.h"
#include "../UI/ImGuiBackendBridge.h"
#include <XCEngine/RHI/D3D12/D3D12CommandList.h>
#ifdef min
#undef min
#endif
#ifdef max
#undef max
#endif
namespace XCEngine {
namespace Editor {
namespace Platform {
inline void RenderImGuiFrame(
D3D12WindowRenderer& windowRenderer,
UI::ImGuiBackendBridge& imguiBackend,
const float clearColor[4],
const D3D12WindowRenderer::RenderCallback& beforeUiRender = {},
const D3D12WindowRenderer::RenderCallback& afterUiRender = {}) {
const Rendering::RenderSurface* renderSurface = windowRenderer.GetCurrentRenderSurface();
Rendering::RenderContext renderContext = windowRenderer.GetRenderContext();
auto* d3d12CommandList = dynamic_cast<RHI::D3D12CommandList*>(renderContext.commandList);
if (!renderContext.IsValid() ||
d3d12CommandList == nullptr ||
renderContext.commandQueue == nullptr ||
windowRenderer.GetSwapChain() == nullptr ||
renderSurface == nullptr ||
windowRenderer.GetSrvHeap() == nullptr) {
return;
}
const auto& colorAttachments = renderSurface->GetColorAttachments();
if (colorAttachments.empty() || colorAttachments[0] == nullptr) {
return;
}
RHI::RHIResourceView* renderTargetView = colorAttachments[0];
renderContext.commandList->TransitionBarrier(
renderTargetView,
RHI::ResourceStates::Present,
RHI::ResourceStates::RenderTarget);
renderContext.commandList->SetRenderTargets(1, &renderTargetView, nullptr);
renderContext.commandList->ClearRenderTarget(renderTargetView, clearColor);
if (beforeUiRender) {
beforeUiRender(renderContext, *renderSurface);
renderContext.commandList->SetRenderTargets(1, &renderTargetView, nullptr);
}
ID3D12DescriptorHeap* descriptorHeaps[] = { windowRenderer.GetSrvHeap() };
d3d12CommandList->SetDescriptorHeaps(1, descriptorHeaps);
imguiBackend.RenderDrawData(d3d12CommandList->GetCommandList());
if (afterUiRender) {
renderContext.commandList->SetRenderTargets(1, &renderTargetView, nullptr);
afterUiRender(renderContext, *renderSurface);
}
renderContext.commandList->TransitionBarrier(
renderTargetView,
RHI::ResourceStates::RenderTarget,
RHI::ResourceStates::Present);
renderContext.commandList->Close();
void* commandLists[] = { renderContext.commandList };
renderContext.commandQueue->ExecuteCommandLists(1, commandLists);
windowRenderer.GetSwapChain()->Present(1, 0);
}
} // namespace Platform
} // namespace Editor
} // namespace XCEngine

View File

@@ -1,23 +1,44 @@
#pragma once #pragma once
#include "Platform/D3D12WindowRenderer.h" #ifndef NOMINMAX
#define NOMINMAX
#endif
#include "XCUIBackend/UITextureRegistration.h" #include "XCUIBackend/UITextureRegistration.h"
#include <XCEngine/Rendering/RenderContext.h>
#include <XCEngine/Rendering/RenderSurface.h>
#include <XCEngine/RHI/RHIDevice.h> #include <XCEngine/RHI/RHIDevice.h>
#include <XCEngine/RHI/RHITexture.h> #include <XCEngine/RHI/RHITexture.h>
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <windows.h> #include <windows.h>
#ifdef min
#undef min
#endif
#ifdef max
#undef max
#endif
namespace XCEngine { namespace XCEngine {
namespace Editor { namespace Editor {
namespace Platform {
class D3D12WindowRenderer;
} // namespace Platform
namespace XCUIBackend { namespace XCUIBackend {
class IEditorHostCompositor { class IEditorHostCompositor {
public: public:
using ConfigureFontsCallback = std::function<void()>; using ConfigureFontsCallback = std::function<void()>;
using RenderCallback = ::XCEngine::Editor::Platform::D3D12WindowRenderer::RenderCallback; using RenderCallback =
std::function<void(const ::XCEngine::Rendering::RenderContext&, const ::XCEngine::Rendering::RenderSurface&)>;
virtual ~IEditorHostCompositor() = default; virtual ~IEditorHostCompositor() = default;

View File

@@ -1,24 +1,45 @@
#pragma once #pragma once
#include "Platform/D3D12WindowRenderer.h" #ifndef NOMINMAX
#define NOMINMAX
#endif
#include "XCUIBackend/UITextureRegistration.h" #include "XCUIBackend/UITextureRegistration.h"
#include <XCEngine/Rendering/RenderContext.h>
#include <XCEngine/Rendering/RenderSurface.h>
#include <XCEngine/RHI/RHIDevice.h> #include <XCEngine/RHI/RHIDevice.h>
#include <XCEngine/RHI/RHITexture.h> #include <XCEngine/RHI/RHITexture.h>
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <windows.h> #include <windows.h>
#ifdef min
#undef min
#endif
#ifdef max
#undef max
#endif
namespace XCEngine { namespace XCEngine {
namespace Editor { namespace Editor {
namespace Platform {
class D3D12WindowRenderer;
} // namespace Platform
namespace XCUIBackend { namespace XCUIBackend {
class IWindowUICompositor { class IWindowUICompositor {
public: public:
using ConfigureFontsCallback = std::function<void()>; using ConfigureFontsCallback = std::function<void()>;
using UiRenderCallback = std::function<void()>; using UiRenderCallback = std::function<void()>;
using RenderCallback = ::XCEngine::Editor::Platform::D3D12WindowRenderer::RenderCallback; using RenderCallback =
std::function<void(const ::XCEngine::Rendering::RenderContext&, const ::XCEngine::Rendering::RenderSurface&)>;
virtual ~IWindowUICompositor() = default; virtual ~IWindowUICompositor() = default;

View File

@@ -1,5 +1,6 @@
#include "XCUIBackend/IEditorHostCompositor.h" #include "XCUIBackend/IEditorHostCompositor.h"
#include "Platform/D3D12WindowRendererImGuiInterop.h"
#include "UI/ImGuiBackendBridge.h" #include "UI/ImGuiBackendBridge.h"
#include <imgui.h> #include <imgui.h>
@@ -61,7 +62,12 @@ public:
const RenderCallback& beforeUiRender, const RenderCallback& beforeUiRender,
const RenderCallback& afterUiRender) override { const RenderCallback& afterUiRender) override {
ImGui::Render(); ImGui::Render();
windowRenderer.Render(m_backend, clearColor, beforeUiRender, afterUiRender); ::XCEngine::Editor::Platform::RenderImGuiFrame(
windowRenderer,
m_backend,
clearColor,
beforeUiRender,
afterUiRender);
} }
bool CreateTextureDescriptor( bool CreateTextureDescriptor(

View File

@@ -1,5 +1,7 @@
#include "XCUIBackend/NativeWindowUICompositor.h" #include "XCUIBackend/NativeWindowUICompositor.h"
#include "Platform/D3D12WindowRenderer.h"
#include <XCEngine/Rendering/RenderContext.h> #include <XCEngine/Rendering/RenderContext.h>
#include <XCEngine/Rendering/RenderSurface.h> #include <XCEngine/Rendering/RenderSurface.h>
#include <XCEngine/RHI/RHICommandList.h> #include <XCEngine/RHI/RHICommandList.h>

View File

@@ -85,6 +85,9 @@ set(NEW_EDITOR_SHELL_CHROME_STATE_HEADER
set(NEW_EDITOR_SHELL_CHROME_STATE_SOURCE set(NEW_EDITOR_SHELL_CHROME_STATE_SOURCE
${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/XCUIShellChromeState.cpp ${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/XCUIShellChromeState.cpp
) )
set(NEW_EDITOR_NATIVE_SHELL_LAYOUT_HEADER
${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/XCUINativeShellLayout.h
)
set(NEW_EDITOR_APPLICATION_HEADER set(NEW_EDITOR_APPLICATION_HEADER
${CMAKE_SOURCE_DIR}/new_editor/src/Application.h ${CMAKE_SOURCE_DIR}/new_editor/src/Application.h
) )
@@ -115,6 +118,9 @@ set(NEW_EDITOR_IMGUI_TEXT_ATLAS_PROVIDER_HEADER
set(NEW_EDITOR_IMGUI_TEXT_ATLAS_PROVIDER_SOURCE set(NEW_EDITOR_IMGUI_TEXT_ATLAS_PROVIDER_SOURCE
${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/ImGuiTextAtlasProvider.cpp ${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/ImGuiTextAtlasProvider.cpp
) )
set(NEW_EDITOR_IMGUI_HOST_COMPOSITOR_SOURCE
${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/ImGuiHostCompositor.cpp
)
set(NEW_EDITOR_FONT_SETUP_HEADER set(NEW_EDITOR_FONT_SETUP_HEADER
${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/XCUIEditorFontSetup.h ${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/XCUIEditorFontSetup.h
) )
@@ -130,6 +136,12 @@ set(NEW_EDITOR_STANDALONE_TEXT_ATLAS_PROVIDER_SOURCE
set(NEW_EDITOR_HOSTED_PREVIEW_PRESENTER_HEADER set(NEW_EDITOR_HOSTED_PREVIEW_PRESENTER_HEADER
${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/XCUIHostedPreviewPresenter.h ${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/XCUIHostedPreviewPresenter.h
) )
set(NEW_EDITOR_LEGACY_IMGUI_HOST_INTEROP_HEADER
${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/LegacyImGuiHostInterop.h
)
set(NEW_EDITOR_LEGACY_IMGUI_HOST_INTEROP_SOURCE
${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/LegacyImGuiHostInterop.cpp
)
set(NEW_EDITOR_WINDOW_UI_COMPOSITOR_HEADER set(NEW_EDITOR_WINDOW_UI_COMPOSITOR_HEADER
${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/IWindowUICompositor.h ${CMAKE_SOURCE_DIR}/new_editor/src/XCUIBackend/IWindowUICompositor.h
) )
@@ -411,6 +423,51 @@ else()
message(STATUS "Skipping new_editor_xcui_hosted_preview_presenter_tests because presenter header or ImGui sources are missing.") message(STATUS "Skipping new_editor_xcui_hosted_preview_presenter_tests because presenter header or ImGui sources are missing.")
endif() endif()
if(EXISTS "${NEW_EDITOR_LEGACY_IMGUI_HOST_INTEROP_HEADER}" AND
EXISTS "${NEW_EDITOR_LEGACY_IMGUI_HOST_INTEROP_SOURCE}" AND
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test_legacy_imgui_host_interop.cpp" AND
EXISTS "${CMAKE_BINARY_DIR}/_deps/imgui-src/imgui.cpp")
add_executable(new_editor_legacy_imgui_host_interop_tests
test_legacy_imgui_host_interop.cpp
${NEW_EDITOR_LEGACY_IMGUI_HOST_INTEROP_SOURCE}
${NEW_EDITOR_IMGUI_HOST_COMPOSITOR_SOURCE}
${NEW_EDITOR_FONT_SETUP_SOURCE}
${CMAKE_BINARY_DIR}/_deps/imgui-src/imgui.cpp
${CMAKE_BINARY_DIR}/_deps/imgui-src/imgui_demo.cpp
${CMAKE_BINARY_DIR}/_deps/imgui-src/imgui_draw.cpp
${CMAKE_BINARY_DIR}/_deps/imgui-src/imgui_tables.cpp
${CMAKE_BINARY_DIR}/_deps/imgui-src/imgui_widgets.cpp
${CMAKE_BINARY_DIR}/_deps/imgui-src/backends/imgui_impl_win32.cpp
${CMAKE_BINARY_DIR}/_deps/imgui-src/backends/imgui_impl_dx12.cpp
)
xcengine_configure_new_editor_test_target(new_editor_legacy_imgui_host_interop_tests)
target_link_libraries(new_editor_legacy_imgui_host_interop_tests
PRIVATE
XCEngine
GTest::gtest
GTest::gtest_main
d3d12
dxgi
user32
gdi32
comdlg32
)
target_include_directories(new_editor_legacy_imgui_host_interop_tests PRIVATE
${CMAKE_SOURCE_DIR}/engine/include
${CMAKE_SOURCE_DIR}/new_editor/src
${CMAKE_SOURCE_DIR}/editor/src
${CMAKE_BINARY_DIR}/_deps/imgui-src
${CMAKE_BINARY_DIR}/_deps/imgui-src/backends
)
xcengine_discover_new_editor_gtests(new_editor_legacy_imgui_host_interop_tests)
else()
message(STATUS "Skipping new_editor_legacy_imgui_host_interop_tests because helper, test source, or ImGui sources are missing.")
endif()
if(EXISTS "${NEW_EDITOR_WINDOW_UI_COMPOSITOR_HEADER}" AND if(EXISTS "${NEW_EDITOR_WINDOW_UI_COMPOSITOR_HEADER}" AND
EXISTS "${NEW_EDITOR_IMGUI_WINDOW_UI_COMPOSITOR_HEADER}" AND EXISTS "${NEW_EDITOR_IMGUI_WINDOW_UI_COMPOSITOR_HEADER}" AND
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test_imgui_window_ui_compositor.cpp") EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test_imgui_window_ui_compositor.cpp")
@@ -469,9 +526,6 @@ if(EXISTS "${NEW_EDITOR_WINDOW_UI_COMPOSITOR_HEADER}" AND
target_include_directories(new_editor_native_window_ui_compositor_tests PRIVATE target_include_directories(new_editor_native_window_ui_compositor_tests PRIVATE
${CMAKE_SOURCE_DIR}/engine/include ${CMAKE_SOURCE_DIR}/engine/include
${CMAKE_SOURCE_DIR}/new_editor/src ${CMAKE_SOURCE_DIR}/new_editor/src
${CMAKE_SOURCE_DIR}/editor/src
${CMAKE_BINARY_DIR}/_deps/imgui-src
${CMAKE_BINARY_DIR}/_deps/imgui-src/backends
) )
xcengine_discover_new_editor_gtests(new_editor_native_window_ui_compositor_tests) xcengine_discover_new_editor_gtests(new_editor_native_window_ui_compositor_tests)
@@ -586,6 +640,31 @@ else()
message(STATUS "Skipping new_editor_xcui_shell_chrome_state_tests because shell chrome state files or the test source are missing.") message(STATUS "Skipping new_editor_xcui_shell_chrome_state_tests because shell chrome state files or the test source are missing.")
endif() endif()
if(EXISTS "${NEW_EDITOR_NATIVE_SHELL_LAYOUT_HEADER}" AND
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test_xcui_native_shell_layout.cpp")
add_executable(new_editor_xcui_native_shell_layout_tests
test_xcui_native_shell_layout.cpp
)
xcengine_configure_new_editor_test_target(new_editor_xcui_native_shell_layout_tests)
target_link_libraries(new_editor_xcui_native_shell_layout_tests
PRIVATE
XCEngine
GTest::gtest
GTest::gtest_main
)
target_include_directories(new_editor_xcui_native_shell_layout_tests PRIVATE
${CMAKE_SOURCE_DIR}/engine/include
${CMAKE_SOURCE_DIR}/new_editor/src
)
xcengine_discover_new_editor_gtests(new_editor_xcui_native_shell_layout_tests)
else()
message(STATUS "Skipping new_editor_xcui_native_shell_layout_tests because the layout helper header or the test source is missing.")
endif()
if(EXISTS "${NEW_EDITOR_APPLICATION_HEADER}" AND if(EXISTS "${NEW_EDITOR_APPLICATION_HEADER}" AND
EXISTS "${NEW_EDITOR_COMMAND_ROUTER_SOURCE}" AND EXISTS "${NEW_EDITOR_COMMAND_ROUTER_SOURCE}" AND
EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test_application_shell_command_bindings.cpp") EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test_application_shell_command_bindings.cpp")
@@ -606,9 +685,6 @@ if(EXISTS "${NEW_EDITOR_APPLICATION_HEADER}" AND
target_include_directories(new_editor_application_shell_command_bindings_tests PRIVATE target_include_directories(new_editor_application_shell_command_bindings_tests PRIVATE
${CMAKE_SOURCE_DIR}/engine/include ${CMAKE_SOURCE_DIR}/engine/include
${CMAKE_SOURCE_DIR}/new_editor/src ${CMAKE_SOURCE_DIR}/new_editor/src
${CMAKE_SOURCE_DIR}/editor/src
${CMAKE_BINARY_DIR}/_deps/imgui-src
${CMAKE_BINARY_DIR}/_deps/imgui-src/backends
) )
xcengine_discover_new_editor_gtests(new_editor_application_shell_command_bindings_tests) xcengine_discover_new_editor_gtests(new_editor_application_shell_command_bindings_tests)

View File

@@ -1,5 +1,6 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include "Platform/D3D12WindowRenderer.h"
#include "XCUIBackend/IWindowUICompositor.h" #include "XCUIBackend/IWindowUICompositor.h"
#include "XCUIBackend/NativeWindowUICompositor.h" #include "XCUIBackend/NativeWindowUICompositor.h"
#include "XCUIBackend/UITextureRegistration.h" #include "XCUIBackend/UITextureRegistration.h"

View File

@@ -1,6 +1,7 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include "Platform/D3D12WindowRenderer.h" #include "Platform/D3D12WindowRenderer.h"
#include "Platform/D3D12WindowRendererImGuiInterop.h"
#include <type_traits> #include <type_traits>
@@ -16,7 +17,8 @@ TEST(D3D12WindowRendererApiTest, ExposesSurfaceAwareRenderCallbackAndAccessor) {
decltype(std::declval<D3D12WindowRenderer&>().GetCurrentRenderSurface()), decltype(std::declval<D3D12WindowRenderer&>().GetCurrentRenderSurface()),
const RenderSurface*>); const RenderSurface*>);
static_assert(std::is_same_v< static_assert(std::is_same_v<
decltype(std::declval<D3D12WindowRenderer&>().Render( decltype(::XCEngine::Editor::Platform::RenderImGuiFrame(
std::declval<D3D12WindowRenderer&>(),
std::declval<::XCEngine::Editor::UI::ImGuiBackendBridge&>(), std::declval<::XCEngine::Editor::UI::ImGuiBackendBridge&>(),
std::declval<const float*>(), std::declval<const float*>(),
std::declval<const Callback&>())), std::declval<const Callback&>())),