#include "Application.h" #include "XCUIBackend/ImGuiXCUIPanelCanvasHost.h" #include "XCUIBackend/ImGuiXCUIHostedPreviewPresenter.h" #include "XCUIBackend/ImGuiWindowUICompositor.h" #include #include #include #include namespace XCEngine { namespace NewEditor { namespace { constexpr wchar_t kWindowClassName[] = L"XCNewEditorWindowClass"; constexpr wchar_t kWindowTitle[] = L"XCNewEditor"; constexpr float kClearColor[4] = { 0.08f, 0.09f, 0.11f, 1.0f }; constexpr float kHostedPreviewClearColor[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; template void ShutdownAndDelete(ResourceType*& resource) { if (resource == nullptr) { return; } resource->Shutdown(); delete resource; resource = nullptr; } void ConfigureFonts() { ImGuiIO& io = ImGui::GetIO(); ImFont* uiFont = nullptr; ::XCEngine::Editor::XCUIBackend::BuildDefaultXCUIEditorFontAtlas(*io.Fonts, uiFont); io.FontDefault = uiFont; } 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"; } } // 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::CreateImGuiXCUIHostedPreviewPresenter(); } const Application::ShellPanelChromeState* Application::TryGetShellPanelState(ShellPanelId panelId) const { return m_shellChromeState.TryGetPanelState(panelId); } bool Application::IsShellViewToggleEnabled(ShellViewToggleId toggleId) const { return m_shellChromeState.GetViewToggle(toggleId); } void Application::SetShellViewToggleEnabled(ShellViewToggleId toggleId, bool enabled) { m_shellChromeState.SetViewToggle(toggleId, enabled); } bool Application::IsNativeHostedPreviewEnabled(ShellPanelId panelId) const { 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); } void Application::DispatchShellShortcuts() { ::XCEngine::Editor::XCUIBackend::XCUIInputBridgeCaptureOptions options = {}; ::XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameSnapshot snapshot = m_xcuiInputSource.CaptureSnapshot(options); ImGuiIO& io = ImGui::GetIO(); snapshot.wantCaptureKeyboard = io.WantCaptureKeyboard; snapshot.wantTextInput = io.WantTextInput; const ::XCEngine::Editor::XCUIBackend::XCUIInputBridgeFrameDelta frameDelta = m_shellInputBridge.Translate(snapshot); const ::XCEngine::Editor::XCUIBackend::XCUIEditorCommandInputSnapshot commandSnapshot = Application::BuildShellShortcutSnapshot(frameDelta); m_shellCommandRouter.InvokeMatchingShortcut({ &commandSnapshot }); } Application::HostedPreviewPanelDiagnostics Application::BuildHostedPreviewPanelDiagnostics( const char* debugName, const char* fallbackDebugSource, bool visible, bool hostedPreviewEnabled, bool nativeRequested, bool nativePresenterBound, const ::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewStats& previewStats) const { HostedPreviewPanelDiagnostics diagnostics = {}; if (debugName != nullptr) { diagnostics.debugName = debugName; } if (fallbackDebugSource != nullptr) { diagnostics.debugSource = fallbackDebugSource; } diagnostics.visible = visible; diagnostics.hostedPreviewEnabled = hostedPreviewEnabled; diagnostics.nativeRequested = nativeRequested; diagnostics.nativePresenterBound = nativePresenterBound; diagnostics.presentedThisFrame = previewStats.presented; diagnostics.queuedToNativePassThisFrame = previewStats.queuedToNativePass; diagnostics.submittedDrawListCount = previewStats.submittedDrawListCount; diagnostics.submittedCommandCount = previewStats.submittedCommandCount; diagnostics.flushedDrawListCount = previewStats.flushedDrawListCount; diagnostics.flushedCommandCount = previewStats.flushedCommandCount; ::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewSurfaceDescriptor descriptor = {}; if (!diagnostics.debugName.empty() && m_hostedPreviewSurfaceRegistry.TryGetSurfaceDescriptor(diagnostics.debugName.c_str(), descriptor)) { diagnostics.descriptorAvailable = true; diagnostics.surfaceImageAvailable = descriptor.image.IsValid(); diagnostics.surfaceWidth = descriptor.image.surfaceWidth; diagnostics.surfaceHeight = descriptor.image.surfaceHeight; diagnostics.logicalWidth = descriptor.logicalSize.width; diagnostics.logicalHeight = descriptor.logicalSize.height; diagnostics.queuedFrameIndex = descriptor.queuedFrameIndex; if (!descriptor.debugSource.empty()) { diagnostics.debugSource = descriptor.debugSource; } } if (!diagnostics.debugName.empty()) { const HostedPreviewOffscreenSurface* previewSurface = FindHostedPreviewSurface(diagnostics.debugName); if (previewSurface != nullptr) { diagnostics.surfaceAllocated = previewSurface->colorTexture != nullptr || previewSurface->colorView != nullptr || previewSurface->width > 0u || previewSurface->height > 0u; diagnostics.surfaceReady = previewSurface->IsReady(); if (diagnostics.surfaceWidth == 0u) { diagnostics.surfaceWidth = previewSurface->width; } if (diagnostics.surfaceHeight == 0u) { diagnostics.surfaceHeight = previewSurface->height; } } } return diagnostics; } int Application::Run(HINSTANCE instance, int nCmdShow) { if (!CreateMainWindow(instance, nCmdShow)) { return -1; } m_xcuiInputSource.Reset(); auto& resourceManager = ::XCEngine::Resources::ResourceManager::Get(); resourceManager.Initialize(); resourceManager.SetResourceRoot(XCENGINE_NEW_EDITOR_REPO_ROOT); if (!InitializeRenderer()) { resourceManager.Shutdown(); return -1; } InitializeWindowCompositor(); m_demoPanel = std::make_unique( &m_xcuiInputSource, CreateHostedPreviewPresenter(IsNativeHostedPreviewEnabled(ShellPanelId::XCUIDemo)), ::XCEngine::Editor::XCUIBackend::CreateImGuiXCUIPanelCanvasHost()); m_layoutLabPanel = std::make_unique( &m_xcuiInputSource, CreateHostedPreviewPresenter(IsNativeHostedPreviewEnabled(ShellPanelId::XCUILayoutLab)), ::XCEngine::Editor::XCUIBackend::CreateImGuiXCUIPanelCanvasHost()); ConfigureHostedPreviewPresenters(); m_shellInputBridge.Reset(); ConfigureShellCommandRouter(); m_running = true; m_renderReady = true; MSG message = {}; while (m_running) { while (PeekMessageW(&message, nullptr, 0, 0, PM_REMOVE)) { if (message.message == WM_QUIT) { m_running = false; break; } TranslateMessage(&message); DispatchMessageW(&message); } if (!m_running) { break; } Frame(); } m_demoPanel.reset(); m_layoutLabPanel.reset(); ShutdownWindowCompositor(); ShutdownRenderer(); resourceManager.Shutdown(); return static_cast(message.wParam); } LRESULT CALLBACK Application::StaticWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { Application* app = reinterpret_cast(GetWindowLongPtrW(hwnd, GWLP_USERDATA)); if (message == WM_NCCREATE) { const CREATESTRUCTW* createStruct = reinterpret_cast(lParam); app = reinterpret_cast(createStruct->lpCreateParams); SetWindowLongPtrW(hwnd, GWLP_USERDATA, reinterpret_cast(app)); } return app != nullptr ? app->WndProc(hwnd, message, wParam, lParam) : DefWindowProcW(hwnd, message, wParam, lParam); } LRESULT Application::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { m_xcuiInputSource.HandleWindowMessage(hwnd, message, wParam, lParam); if (m_windowCompositor != nullptr && m_windowCompositor->HandleWindowMessage(hwnd, message, wParam, lParam)) { return true; } switch (message) { case WM_SIZE: if (wParam != SIZE_MINIMIZED && m_renderReady) { m_windowRenderer.Resize(static_cast(LOWORD(lParam)), static_cast(HIWORD(lParam))); } return 0; case WM_CLOSE: DestroyWindow(hwnd); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; case WM_PAINT: { PAINTSTRUCT paintStruct = {}; BeginPaint(hwnd, &paintStruct); if (m_renderReady) { Frame(); } EndPaint(hwnd, &paintStruct); return 0; } default: return DefWindowProcW(hwnd, message, wParam, lParam); } } bool Application::CreateMainWindow(HINSTANCE instance, int nCmdShow) { WNDCLASSEXW windowClass = {}; windowClass.cbSize = sizeof(windowClass); windowClass.style = CS_HREDRAW | CS_VREDRAW; windowClass.lpfnWndProc = &Application::StaticWndProc; windowClass.hInstance = instance; windowClass.hCursor = LoadCursorW(nullptr, IDC_ARROW); windowClass.lpszClassName = kWindowClassName; if (!RegisterClassExW(&windowClass)) { return false; } m_hwnd = CreateWindowExW( 0, kWindowClassName, kWindowTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 1360, 840, nullptr, nullptr, instance, this); if (m_hwnd == nullptr) { return false; } ShowWindow(m_hwnd, nCmdShow); UpdateWindow(m_hwnd); return true; } bool Application::InitializeRenderer() { RECT clientRect = {}; if (!GetClientRect(m_hwnd, &clientRect)) { return false; } const int width = clientRect.right - clientRect.left; const int height = clientRect.bottom - clientRect.top; const bool initialized = width > 0 && height > 0 && m_windowRenderer.Initialize(m_hwnd, width, height); if (initialized) { m_startTime = std::chrono::steady_clock::now(); } return initialized; } void Application::InitializeWindowCompositor() { m_windowCompositor = ::XCEngine::Editor::XCUIBackend::CreateImGuiWindowUICompositor(); if (m_windowCompositor != nullptr) { m_windowCompositor->Initialize( m_hwnd, m_windowRenderer, []() { ConfigureFonts(); }); } } void Application::ShutdownWindowCompositor() { DestroyHostedPreviewSurfaces(); if (m_windowCompositor != nullptr) { m_windowCompositor->Shutdown(); m_windowCompositor.reset(); } } void Application::ShutdownRenderer() { m_renderReady = false; m_hostedPreviewRenderBackend.Shutdown(); m_nativeBackdropRenderer.Shutdown(); m_windowRenderer.Shutdown(); } void Application::DestroyHostedPreviewSurfaces() { for (HostedPreviewOffscreenSurface& previewSurface : m_hostedPreviewSurfaces) { if (previewSurface.textureRegistration.cpuHandle.ptr != 0) { if (m_windowCompositor != nullptr) { m_windowCompositor->FreeTextureDescriptor(previewSurface.textureRegistration); } } ShutdownAndDelete(previewSurface.colorView); ShutdownAndDelete(previewSurface.colorTexture); previewSurface = {}; } 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() { const auto isNativePreviewEnabled = [this](const std::string& debugName) { const ShellPanelChromeState* demoState = TryGetShellPanelState(ShellPanelId::XCUIDemo); if (demoState != nullptr && debugName == demoState->previewDebugName && IsNativeHostedPreviewEnabled(ShellPanelId::XCUIDemo)) { return true; } const ShellPanelChromeState* layoutLabState = TryGetShellPanelState(ShellPanelId::XCUILayoutLab); return layoutLabState != nullptr && debugName == layoutLabState->previewDebugName && IsNativeHostedPreviewEnabled(ShellPanelId::XCUILayoutLab); }; const auto syncSurfaceForNameAndSize = [this, &isNativePreviewEnabled]( const std::string& debugName, const ::XCEngine::UI::UISize& logicalSize) { if (!isNativePreviewEnabled(debugName)) { return; } const std::uint32_t width = logicalSize.width > 1.0f ? static_cast(std::ceil(logicalSize.width)) : 0u; const std::uint32_t height = logicalSize.height > 1.0f ? static_cast(std::ceil(logicalSize.height)) : 0u; if (width == 0u || height == 0u) { return; } HostedPreviewOffscreenSurface& previewSurface = FindOrAddHostedPreviewSurface(debugName); EnsureHostedPreviewSurface(previewSurface, width, height); }; for (const auto& descriptor : m_hostedPreviewSurfaceRegistry.GetDescriptors()) { syncSurfaceForNameAndSize(descriptor.debugName, descriptor.logicalSize); } for (const auto& queuedFrame : m_hostedPreviewQueue.GetQueuedFrames()) { syncSurfaceForNameAndSize(queuedFrame.debugName, queuedFrame.logicalSize); } } Application::HostedPreviewOffscreenSurface* Application::FindHostedPreviewSurface(const std::string& debugName) { for (HostedPreviewOffscreenSurface& previewSurface : m_hostedPreviewSurfaces) { if (previewSurface.debugName == debugName) { return &previewSurface; } } return nullptr; } const Application::HostedPreviewOffscreenSurface* Application::FindHostedPreviewSurface(const std::string& debugName) const { for (const HostedPreviewOffscreenSurface& previewSurface : m_hostedPreviewSurfaces) { if (previewSurface.debugName == debugName) { return &previewSurface; } } return nullptr; } Application::HostedPreviewOffscreenSurface& Application::FindOrAddHostedPreviewSurface(const std::string& debugName) { HostedPreviewOffscreenSurface* existingSurface = FindHostedPreviewSurface(debugName); if (existingSurface != nullptr) { return *existingSurface; } HostedPreviewOffscreenSurface previewSurface = {}; previewSurface.debugName = debugName; m_hostedPreviewSurfaces.push_back(std::move(previewSurface)); return m_hostedPreviewSurfaces.back(); } bool Application::EnsureHostedPreviewSurface( HostedPreviewOffscreenSurface& previewSurface, std::uint32_t width, std::uint32_t height) { if (previewSurface.IsReady() && previewSurface.width == width && previewSurface.height == height) { return true; } if (previewSurface.textureRegistration.cpuHandle.ptr != 0) { if (m_windowCompositor != nullptr) { m_windowCompositor->FreeTextureDescriptor(previewSurface.textureRegistration); } } ShutdownAndDelete(previewSurface.colorView); ShutdownAndDelete(previewSurface.colorTexture); previewSurface.textureRegistration = {}; previewSurface.width = width; previewSurface.height = height; previewSurface.colorState = ::XCEngine::RHI::ResourceStates::Common; if (width == 0u || height == 0u) { return false; } ::XCEngine::RHI::TextureDesc colorDesc = {}; colorDesc.width = width; colorDesc.height = height; colorDesc.depth = 1; colorDesc.mipLevels = 1; colorDesc.arraySize = 1; colorDesc.format = static_cast(::XCEngine::RHI::Format::R8G8B8A8_UNorm); colorDesc.textureType = static_cast(::XCEngine::RHI::TextureType::Texture2D); colorDesc.sampleCount = 1; previewSurface.colorTexture = m_windowRenderer.GetRHIDevice()->CreateTexture(colorDesc); if (previewSurface.colorTexture == nullptr) { return false; } ::XCEngine::RHI::ResourceViewDesc colorViewDesc = {}; colorViewDesc.format = static_cast(::XCEngine::RHI::Format::R8G8B8A8_UNorm); colorViewDesc.dimension = ::XCEngine::RHI::ResourceViewDimension::Texture2D; previewSurface.colorView = m_windowRenderer.GetRHIDevice()->CreateRenderTargetView(previewSurface.colorTexture, colorViewDesc); if (previewSurface.colorView == nullptr) { ShutdownAndDelete(previewSurface.colorTexture); return false; } if (m_windowCompositor == nullptr || !m_windowCompositor->CreateTextureDescriptor( m_windowRenderer.GetRHIDevice(), previewSurface.colorTexture, previewSurface.textureRegistration)) { ShutdownAndDelete(previewSurface.colorView); ShutdownAndDelete(previewSurface.colorTexture); previewSurface.textureRegistration = {}; return false; } return true; } bool Application::RenderHostedPreviewOffscreenSurface( HostedPreviewOffscreenSurface& previewSurface, const ::XCEngine::Rendering::RenderContext& renderContext, const ::XCEngine::UI::UIDrawData& drawData) { if (!previewSurface.IsReady() || renderContext.commandList == nullptr) { return false; } ::XCEngine::Rendering::RenderSurface renderSurface(previewSurface.width, previewSurface.height); renderSurface.SetColorAttachment(previewSurface.colorView); renderSurface.SetAutoTransitionEnabled(false); renderSurface.SetColorStateBefore(::XCEngine::RHI::ResourceStates::RenderTarget); renderSurface.SetColorStateAfter(::XCEngine::RHI::ResourceStates::RenderTarget); renderContext.commandList->TransitionBarrier( previewSurface.colorView, previewSurface.colorState, ::XCEngine::RHI::ResourceStates::RenderTarget); renderContext.commandList->SetRenderTargets(1, &previewSurface.colorView, nullptr); renderContext.commandList->ClearRenderTarget(previewSurface.colorView, kHostedPreviewClearColor); if (!m_hostedPreviewRenderBackend.Render(renderContext, renderSurface, drawData)) { previewSurface.colorState = ::XCEngine::RHI::ResourceStates::RenderTarget; return false; } renderContext.commandList->TransitionBarrier( previewSurface.colorView, ::XCEngine::RHI::ResourceStates::RenderTarget, ::XCEngine::RHI::ResourceStates::PixelShaderResource); previewSurface.colorState = ::XCEngine::RHI::ResourceStates::PixelShaderResource; return true; } void Application::RenderShellChrome() { SyncShellChromePanelStateFromPanels(); ImGuiViewport* viewport = ImGui::GetMainViewport(); if (viewport == nullptr) { return; } ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_MenuBar; ImGui::SetNextWindowPos(viewport->WorkPos); ImGui::SetNextWindowSize(viewport->WorkSize); ImGui::SetNextWindowViewport(viewport->ID); ImGui::SetNextWindowBgAlpha(0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); const bool opened = ImGui::Begin("XCNewEditorShell", nullptr, windowFlags); ImGui::PopStyleVar(3); if (opened) { if (ImGui::BeginMenuBar()) { if (ImGui::BeginMenu("View")) { const auto drawCommandMenuItem = [this](const char* label, const char* shortcut, bool selected, const char* commandId) { const bool enabled = m_shellCommandRouter.IsCommandEnabled(commandId); if (ImGui::MenuItem(label, shortcut, selected, enabled)) { m_shellCommandRouter.InvokeCommand(commandId); } }; drawCommandMenuItem( "XCUI Demo", "Ctrl+1", TryGetShellPanelState(ShellPanelId::XCUIDemo) != nullptr && TryGetShellPanelState(ShellPanelId::XCUIDemo)->visible, ShellCommandIds::ToggleXCUIDemoPanel); drawCommandMenuItem( "XCUI Layout Lab", "Ctrl+2", TryGetShellPanelState(ShellPanelId::XCUILayoutLab) != nullptr && TryGetShellPanelState(ShellPanelId::XCUILayoutLab)->visible, ShellCommandIds::ToggleXCUILayoutLabPanel); drawCommandMenuItem( "ImGui Demo", "Ctrl+3", IsShellViewToggleEnabled(ShellViewToggleId::ImGuiDemoWindow), ShellCommandIds::ToggleImGuiDemoWindow); ImGui::Separator(); drawCommandMenuItem( "Native Backdrop", "Ctrl+Shift+B", IsShellViewToggleEnabled(ShellViewToggleId::NativeBackdrop), ShellCommandIds::ToggleNativeBackdrop); drawCommandMenuItem( "Pulse Accent", "Ctrl+Shift+P", IsShellViewToggleEnabled(ShellViewToggleId::PulseAccent), ShellCommandIds::TogglePulseAccent); drawCommandMenuItem( "Native XCUI Overlay", "Ctrl+Shift+O", IsShellViewToggleEnabled(ShellViewToggleId::NativeXCUIOverlay), ShellCommandIds::ToggleNativeXCUIOverlay); drawCommandMenuItem( "Hosted Preview HUD", "Ctrl+Shift+H", IsShellViewToggleEnabled(ShellViewToggleId::HostedPreviewHud), ShellCommandIds::ToggleHostedPreviewHud); drawCommandMenuItem( "Native Demo Panel Preview", "Ctrl+Alt+1", IsNativeHostedPreviewEnabled(ShellPanelId::XCUIDemo), ShellCommandIds::ToggleNativeDemoPanelPreview); drawCommandMenuItem( "Native Layout Lab Preview", "Ctrl+Alt+2", IsNativeHostedPreviewEnabled(ShellPanelId::XCUILayoutLab), ShellCommandIds::ToggleNativeLayoutLabPreview); ImGui::EndMenu(); } ImGui::SeparatorText("XCUI Sandbox"); const MainWindowNativeBackdropRenderer::OverlayStats& nativeOverlayStats = m_nativeBackdropRenderer.GetLastOverlayStats(); const ::XCEngine::Editor::XCUIBackend::XCUILayoutLabFrameStats& overlayFrameStats = m_nativeOverlayRuntime.GetFrameResult().stats; const ::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewDrainStats& hostedPreviewStats = m_hostedPreviewQueue.GetLastDrainStats(); if (IsShellViewToggleEnabled(ShellViewToggleId::NativeXCUIOverlay)) { ImGui::TextDisabled( "Native XCUI overlay: %s | runtime %zu cmds (%zu fill, %zu outline, %zu text, %zu image, clips %zu/%zu)", overlayFrameStats.nativeOverlayReady ? "preflight OK" : "preflight issues", overlayFrameStats.commandCount, overlayFrameStats.filledRectCommandCount, overlayFrameStats.rectOutlineCommandCount, overlayFrameStats.textCommandCount, overlayFrameStats.imageCommandCount, overlayFrameStats.clipPushCommandCount, overlayFrameStats.clipPopCommandCount); ImGui::TextDisabled( "%s | supported %zu | unsupported %zu | prev native pass %zu cmds, %zu rendered, %zu skipped", overlayFrameStats.nativeOverlayStatusMessage.empty() ? "Overlay diagnostics unavailable" : overlayFrameStats.nativeOverlayStatusMessage.c_str(), overlayFrameStats.nativeSupportedCommandCount, overlayFrameStats.nativeUnsupportedCommandCount, nativeOverlayStats.commandCount, nativeOverlayStats.renderedCommandCount, nativeOverlayStats.skippedCommandCount); } else { ImGui::TextDisabled( IsShellViewToggleEnabled(ShellViewToggleId::NativeBackdrop) ? "Transition backend + runtime diagnostics + native backbuffer pass" : "Transition backend + runtime diagnostics"); } ImGui::TextDisabled( "Hosted preview queue: %zu frames | queued %zu cmds | rendered %zu cmds | skipped %zu cmds", hostedPreviewStats.queuedFrameCount, hostedPreviewStats.queuedCommandCount, hostedPreviewStats.renderedCommandCount, hostedPreviewStats.skippedCommandCount); std::size_t allocatedSurfaceCount = 0u; std::size_t readySurfaceCount = 0u; for (const HostedPreviewOffscreenSurface& previewSurface : m_hostedPreviewSurfaces) { if (previewSurface.colorTexture != nullptr || previewSurface.colorView != nullptr) { ++allocatedSurfaceCount; } if (previewSurface.IsReady()) { ++readySurfaceCount; } } ImGui::TextDisabled( "Hosted surfaces: %zu registry entries | %zu allocated | %zu ready", m_hostedPreviewSurfaceRegistry.GetDescriptors().size(), allocatedSurfaceCount, readySurfaceCount); if (m_demoPanel != nullptr) { ImGui::TextDisabled( "XCUI Demo preview: %s", IsNativeHostedPreviewEnabled(ShellPanelId::XCUIDemo) ? "native offscreen preview surface" : "hosted presenter"); } if (m_layoutLabPanel != nullptr) { ImGui::TextDisabled( "Layout Lab preview: %s", IsNativeHostedPreviewEnabled(ShellPanelId::XCUILayoutLab) ? "native offscreen preview surface" : "hosted presenter"); } ImGui::EndMenuBar(); } ImGui::DockSpace( ImGui::GetID("XCNewEditorDockSpace"), ImVec2(0.0f, 0.0f), ImGuiDockNodeFlags_PassthruCentralNode); } ImGui::End(); if (IsShellViewToggleEnabled(ShellViewToggleId::HostedPreviewHud)) { RenderHostedPreviewHud(); } } void Application::RenderHostedPreviewHud() { ImGuiViewport* viewport = ImGui::GetMainViewport(); if (viewport == nullptr) { return; } const ShellPanelChromeState* demoState = TryGetShellPanelState(ShellPanelId::XCUIDemo); const ShellPanelChromeState* layoutLabState = TryGetShellPanelState(ShellPanelId::XCUILayoutLab); const HostedPreviewPanelDiagnostics demoDiagnostics = BuildHostedPreviewPanelDiagnostics( demoState != nullptr ? demoState->previewDebugName.data() : "XCUI Demo", demoState != nullptr ? demoState->previewDebugSource.data() : "new_editor.panels.xcui_demo", demoState != nullptr && demoState->visible, demoState != nullptr && demoState->hostedPreviewEnabled, IsNativeHostedPreviewEnabled(ShellPanelId::XCUIDemo), m_demoPanel != nullptr && m_demoPanel->IsUsingNativeHostedPreview(), m_demoPanel != nullptr ? m_demoPanel->GetLastPreviewStats() : ::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewStats{}); const HostedPreviewPanelDiagnostics layoutLabDiagnostics = BuildHostedPreviewPanelDiagnostics( layoutLabState != nullptr ? layoutLabState->previewDebugName.data() : "XCUI Layout Lab", layoutLabState != nullptr ? layoutLabState->previewDebugSource.data() : "new_editor.panels.xcui_layout_lab", layoutLabState != nullptr && layoutLabState->visible, layoutLabState != nullptr && layoutLabState->hostedPreviewEnabled, IsNativeHostedPreviewEnabled(ShellPanelId::XCUILayoutLab), m_layoutLabPanel != nullptr && m_layoutLabPanel->IsUsingNativeHostedPreview(), m_layoutLabPanel != nullptr ? m_layoutLabPanel->GetLastPreviewStats() : ::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewStats{}); std::size_t allocatedSurfaceCount = 0u; std::size_t readySurfaceCount = 0u; for (const HostedPreviewOffscreenSurface& previewSurface : m_hostedPreviewSurfaces) { if (previewSurface.colorTexture != nullptr || previewSurface.colorView != nullptr) { ++allocatedSurfaceCount; } if (previewSurface.IsReady()) { ++readySurfaceCount; } } const ::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewDrainStats& drainStats = m_hostedPreviewQueue.GetLastDrainStats(); ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav; ImGui::SetNextWindowViewport(viewport->ID); ImGui::SetNextWindowPos( ImVec2(viewport->WorkPos.x + viewport->WorkSize.x - 18.0f, viewport->WorkPos.y + 42.0f), ImGuiCond_Always, ImVec2(1.0f, 0.0f)); ImGui::SetNextWindowBgAlpha(0.9f); if (!ImGui::Begin("XCUI Hosted Preview HUD", nullptr, windowFlags)) { ImGui::End(); return; } ImGui::TextUnformatted("XCUI Hosted Preview"); ImGui::Text( "Registry %zu | surfaces %zu/%zu ready | last native drain %zu rendered, %zu skipped", m_hostedPreviewSurfaceRegistry.GetDescriptors().size(), readySurfaceCount, allocatedSurfaceCount, drainStats.renderedFrameCount, drainStats.skippedFrameCount); ImGui::Separator(); const auto drawPanelRow = [](const HostedPreviewPanelDiagnostics& diagnostics) { const char* const pathLabel = GetHostedPreviewPathLabel(diagnostics.nativeRequested, diagnostics.nativePresenterBound); const char* const stateLabel = GetHostedPreviewStateLabel( diagnostics.hostedPreviewEnabled, diagnostics.nativePresenterBound, diagnostics.presentedThisFrame, diagnostics.queuedToNativePassThisFrame, diagnostics.surfaceImageAvailable, diagnostics.surfaceAllocated, diagnostics.surfaceReady, diagnostics.descriptorAvailable); ImGui::Text( "%s [%s] %s", diagnostics.debugName.c_str(), diagnostics.visible ? "visible" : "hidden", stateLabel); ImGui::TextDisabled("%s", pathLabel); ImGui::Text( "source %s | submit %zu lists / %zu cmds | flush %zu lists / %zu cmds", diagnostics.debugSource.empty() ? "n/a" : diagnostics.debugSource.c_str(), diagnostics.submittedDrawListCount, diagnostics.submittedCommandCount, diagnostics.flushedDrawListCount, diagnostics.flushedCommandCount); if (diagnostics.nativePresenterBound) { ImGui::Text( "surface %ux%u | logical %.0f x %.0f | descriptor %s | image %s | submit->native %s", diagnostics.surfaceWidth, diagnostics.surfaceHeight, diagnostics.logicalWidth, diagnostics.logicalHeight, diagnostics.descriptorAvailable ? "yes" : "no", diagnostics.surfaceImageAvailable ? "yes" : "no", diagnostics.queuedToNativePassThisFrame ? "yes" : "no"); } else { ImGui::Text( "legacy present %s | cached native surface %s", diagnostics.presentedThisFrame ? "yes" : "no", (diagnostics.surfaceAllocated || diagnostics.surfaceImageAvailable) ? "retained" : "none"); } }; drawPanelRow(demoDiagnostics); ImGui::Separator(); drawPanelRow(layoutLabDiagnostics); ImGui::End(); } void Application::RenderQueuedHostedPreviews( const ::XCEngine::Rendering::RenderContext& renderContext, const ::XCEngine::Rendering::RenderSurface& surface) { (void)surface; ::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewDrainStats drainStats = {}; const auto& queuedFrames = m_hostedPreviewQueue.GetQueuedFrames(); drainStats.queuedFrameCount = queuedFrames.size(); for (const auto& queuedFrame : queuedFrames) { drainStats.queuedDrawListCount += queuedFrame.drawData.GetDrawListCount(); drainStats.queuedCommandCount += queuedFrame.drawData.GetTotalCommandCount(); } if (queuedFrames.empty()) { m_hostedPreviewQueue.SetLastDrainStats(drainStats); return; } m_hostedPreviewRenderBackend.SetTextAtlasProvider(&m_hostedPreviewTextAtlasProvider); std::size_t queuedFrameIndex = 0u; for (const auto& queuedFrame : queuedFrames) { m_hostedPreviewSurfaceRegistry.RecordQueuedFrame(queuedFrame, queuedFrameIndex); if (queuedFrame.debugName.empty()) { ++drainStats.skippedFrameCount; ++queuedFrameIndex; continue; } const std::uint32_t expectedWidth = queuedFrame.logicalSize.width > 1.0f ? static_cast(std::ceil(queuedFrame.logicalSize.width)) : 0u; const std::uint32_t expectedHeight = queuedFrame.logicalSize.height > 1.0f ? static_cast(std::ceil(queuedFrame.logicalSize.height)) : 0u; if (expectedWidth == 0u || expectedHeight == 0u) { ++drainStats.skippedFrameCount; ++queuedFrameIndex; continue; } HostedPreviewOffscreenSurface& previewSurface = FindOrAddHostedPreviewSurface(queuedFrame.debugName); if (!EnsureHostedPreviewSurface(previewSurface, expectedWidth, expectedHeight) || !RenderHostedPreviewOffscreenSurface(previewSurface, renderContext, queuedFrame.drawData)) { ++drainStats.skippedFrameCount; ++queuedFrameIndex; continue; } ++drainStats.renderedFrameCount; const auto& overlayStats = m_hostedPreviewRenderBackend.GetLastOverlayStats(); drainStats.renderedDrawListCount += overlayStats.drawListCount; drainStats.renderedCommandCount += overlayStats.renderedCommandCount; drainStats.skippedCommandCount += overlayStats.skippedCommandCount; m_hostedPreviewSurfaceRegistry.UpdateSurface( queuedFrame.debugName, previewSurface.textureRegistration.texture, ::XCEngine::UI::UIRect( 0.0f, 0.0f, static_cast(previewSurface.width), static_cast(previewSurface.height))); ++queuedFrameIndex; } m_hostedPreviewQueue.SetLastDrainStats(drainStats); } void Application::Frame() { if (!m_renderReady || !m_windowRenderer.BeginFrame()) { m_xcuiInputSource.ClearFrameTransients(); return; } m_hostedPreviewQueue.BeginFrame(); m_hostedPreviewSurfaceRegistry.BeginFrame(); SyncHostedPreviewSurfaces(); if (m_windowCompositor == nullptr) { m_xcuiInputSource.ClearFrameTransients(); return; } m_windowCompositor->RenderFrame( kClearColor, [this]() { DispatchShellShortcuts(); RenderShellChrome(); if (m_demoPanel) { m_demoPanel->RenderIfVisible(); } if (m_layoutLabPanel) { m_layoutLabPanel->RenderIfVisible(); } bool showImGuiDemoWindow = IsShellViewToggleEnabled(ShellViewToggleId::ImGuiDemoWindow); if (showImGuiDemoWindow) { ImGui::ShowDemoWindow(&showImGuiDemoWindow); SetShellViewToggleEnabled(ShellViewToggleId::ImGuiDemoWindow, showImGuiDemoWindow); } SyncShellChromePanelStateFromPanels(); SyncHostedPreviewSurfaces(); }, [this]( const ::XCEngine::Rendering::RenderContext& renderContext, const ::XCEngine::Rendering::RenderSurface& surface) { RenderQueuedHostedPreviews(renderContext, surface); if (!IsShellViewToggleEnabled(ShellViewToggleId::NativeBackdrop) && !IsShellViewToggleEnabled(ShellViewToggleId::NativeXCUIOverlay)) { return; } MainWindowNativeBackdropRenderer::FrameState frameState = {}; frameState.elapsedSeconds = static_cast( std::chrono::duration(std::chrono::steady_clock::now() - m_startTime).count()); frameState.pulseAccent = IsShellViewToggleEnabled(ShellViewToggleId::PulseAccent); frameState.drawBackdrop = IsShellViewToggleEnabled(ShellViewToggleId::NativeBackdrop); if (IsShellViewToggleEnabled(ShellViewToggleId::NativeXCUIOverlay)) { const float width = static_cast(surface.GetWidth()); const float height = static_cast(surface.GetHeight()); const float horizontalMargin = (std::min)(width * 0.14f, 128.0f); const float topMargin = (std::min)(height * 0.15f, 132.0f); const float bottomMargin = (std::min)(height * 0.12f, 96.0f); ::XCEngine::Editor::XCUIBackend::XCUILayoutLabInputState overlayInput = {}; overlayInput.canvasRect = ::XCEngine::UI::UIRect( horizontalMargin, topMargin, (std::max)(0.0f, width - horizontalMargin * 2.0f), (std::max)(0.0f, height - topMargin - bottomMargin)); overlayInput.pointerPosition = m_xcuiInputSource.GetPointerPosition(); overlayInput.pointerInside = overlayInput.pointerPosition.x >= overlayInput.canvasRect.x && overlayInput.pointerPosition.y >= overlayInput.canvasRect.y && overlayInput.pointerPosition.x <= overlayInput.canvasRect.x + overlayInput.canvasRect.width && overlayInput.pointerPosition.y <= overlayInput.canvasRect.y + overlayInput.canvasRect.height; const auto& overlayFrame = m_nativeOverlayRuntime.Update(overlayInput); frameState.overlayDrawData = &overlayFrame.drawData; } m_nativeBackdropRenderer.Render(renderContext, surface, frameState); }); m_xcuiInputSource.ClearFrameTransients(); } } // namespace NewEditor } // namespace XCEngine