Publish native hosted preview textures through XCUI compositor

This commit is contained in:
2026-04-05 14:36:02 +08:00
parent 231df6ee36
commit b05e76de0c
7 changed files with 605 additions and 36 deletions

View File

@@ -591,7 +591,7 @@ void Application::ShutdownRenderer() {
void Application::DestroyHostedPreviewSurfaces() {
for (HostedPreviewOffscreenSurface& previewSurface : m_hostedPreviewSurfaces) {
if (previewSurface.textureRegistration.cpuHandle.ptr != 0) {
if (Application::HasHostedPreviewTextureRegistration(previewSurface.textureRegistration)) {
if (m_windowCompositor != nullptr) {
m_windowCompositor->FreeTextureDescriptor(previewSurface.textureRegistration);
}
@@ -698,7 +698,7 @@ bool Application::EnsureHostedPreviewSurface(
return true;
}
if (previewSurface.textureRegistration.cpuHandle.ptr != 0) {
if (Application::HasHostedPreviewTextureRegistration(previewSurface.textureRegistration)) {
if (m_windowCompositor != nullptr) {
m_windowCompositor->FreeTextureDescriptor(previewSurface.textureRegistration);
}
@@ -980,6 +980,35 @@ bool Application::RenderHostedPreviewOffscreenSurface(
for (const NativeShellPanelLayout& panelLayout : panelLayouts) {
if (panelLayout.panelId == ShellPanelId::XCUIDemo) {
const ShellPanelChromeState* panelState = TryGetShellPanelState(ShellPanelId::XCUIDemo);
const bool nativeHostedPreview =
panelState != nullptr &&
panelState->hostedPreviewEnabled &&
IsNativeHostedPreviewEnabled(ShellPanelId::XCUIDemo);
::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewSurfaceDescriptor hostedSurfaceDescriptor = {};
const bool hasHostedSurfaceDescriptor =
nativeHostedPreview &&
panelState != nullptr &&
!panelState->previewDebugName.empty() &&
m_hostedPreviewSurfaceRegistry.TryGetSurfaceDescriptor(
panelState->previewDebugName.data(),
hostedSurfaceDescriptor);
::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewSurfaceImage hostedSurfaceImage = {};
const bool showHostedSurfaceImage =
nativeHostedPreview &&
panelState != nullptr &&
!panelState->previewDebugName.empty() &&
m_hostedPreviewSurfaceRegistry.TryGetSurfaceImage(
panelState->previewDebugName.data(),
hostedSurfaceImage);
const NativeHostedPreviewConsumption previewConsumption =
Application::ResolveNativeHostedPreviewConsumption(
nativeHostedPreview,
hasHostedSurfaceDescriptor,
showHostedSurfaceImage,
"Native XCUI preview pending",
"Waiting for queued native preview output to publish into the shell card.");
::XCEngine::Editor::XCUIBackend::XCUIPanelCanvasSession canvasSession = {};
canvasSession.hostRect = panelLayout.panelRect;
canvasSession.canvasRect = panelLayout.canvasRect;
@@ -994,6 +1023,12 @@ bool Application::RenderHostedPreviewOffscreenSurface(
canvasRequest.height = panelLayout.panelRect.height;
canvasRequest.topInset = panelLayout.canvasRect.y - panelLayout.panelRect.y;
canvasRequest.drawPreviewFrame = false;
canvasRequest.showSurfaceImage = previewConsumption.showSurfaceImage;
canvasRequest.surfaceImage = hostedSurfaceImage;
canvasRequest.placeholderTitle =
previewConsumption.placeholderTitle.empty() ? nullptr : previewConsumption.placeholderTitle.data();
canvasRequest.placeholderSubtitle =
previewConsumption.placeholderSubtitle.empty() ? nullptr : previewConsumption.placeholderSubtitle.data();
const ::XCEngine::Editor::XCUIBackend::XCUIPanelCanvasSession resolvedSession =
m_nativeDemoCanvasHost.BeginCanvas(canvasRequest);
@@ -1018,7 +1053,21 @@ bool Application::RenderHostedPreviewOffscreenSurface(
input.events = panelFrameDelta.events;
const auto& frame = m_nativeDemoRuntime.Update(input);
AppendDrawData(composedDrawData, frame.drawData);
if (previewConsumption.queueRuntimeFrame) {
::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewFrame previewFrame = {};
previewFrame.drawData = &frame.drawData;
previewFrame.canvasRect = resolvedSession.canvasRect;
previewFrame.logicalSize = UI::UISize(
resolvedSession.canvasRect.width,
resolvedSession.canvasRect.height);
previewFrame.debugName =
panelState != nullptr ? panelState->previewDebugName.data() : nullptr;
previewFrame.debugSource =
panelState != nullptr ? panelState->previewDebugSource.data() : nullptr;
m_hostedPreviewQueue.Submit(previewFrame);
} else if (previewConsumption.appendRuntimeDrawDataToShell) {
AppendDrawData(composedDrawData, frame.drawData);
}
if (IsShellViewToggleEnabled(ShellViewToggleId::HostedPreviewHud)) {
const auto& stats = frame.stats;
@@ -1049,22 +1098,33 @@ bool Application::RenderHostedPreviewOffscreenSurface(
textMuted);
}
const auto drawDebugRect =
[this, &stats](const std::string& elementId, const UI::UIColor& color, const char* label) {
if (elementId.empty()) {
return;
}
UI::UIRect rect = {};
if (!m_nativeDemoRuntime.TryGetElementRect(elementId, rect)) {
return;
}
m_nativeDemoCanvasHost.DrawOutlineRect(rect, color, 2.0f, 6.0f);
if (label != nullptr && label[0] != '\0') {
m_nativeDemoCanvasHost.DrawText(UI::UIPoint(rect.x + 4.0f, rect.y + 4.0f), label, color);
}
};
drawDebugRect(stats.hoveredElementId, UI::UIColor(1.0f, 195.0f / 255.0f, 64.0f / 255.0f, 1.0f), "hover");
drawDebugRect(stats.focusedElementId, UI::UIColor(64.0f / 255.0f, 214.0f / 255.0f, 1.0f, 1.0f), "focus");
if (previewConsumption.drawRuntimeDebugRects) {
const auto drawDebugRect =
[this](const std::string& elementId, const UI::UIColor& color, const char* label) {
if (elementId.empty()) {
return;
}
UI::UIRect rect = {};
if (!m_nativeDemoRuntime.TryGetElementRect(elementId, rect)) {
return;
}
m_nativeDemoCanvasHost.DrawOutlineRect(rect, color, 2.0f, 6.0f);
if (label != nullptr && label[0] != '\0') {
m_nativeDemoCanvasHost.DrawText(
UI::UIPoint(rect.x + 4.0f, rect.y + 4.0f),
label,
color);
}
};
drawDebugRect(
stats.hoveredElementId,
UI::UIColor(1.0f, 195.0f / 255.0f, 64.0f / 255.0f, 1.0f),
"hover");
drawDebugRect(
stats.focusedElementId,
UI::UIColor(64.0f / 255.0f, 214.0f / 255.0f, 1.0f, 1.0f),
"focus");
}
}
m_nativeDemoCanvasHost.EndCanvas();
@@ -1072,7 +1132,9 @@ bool Application::RenderHostedPreviewOffscreenSurface(
NativePanelFrameSummary summary = {};
summary.layout = panelLayout;
summary.lineA = m_nativeDemoReloadSucceeded
? frame.stats.statusMessage
? Application::ComposeNativeHostedPreviewStatusLine(
previewConsumption,
frame.stats.statusMessage)
: "Document reload failed; showing last retained runtime state.";
summary.lineB =
std::string(panelLayout.active ? "Active" : "Passive") +
@@ -1085,6 +1147,35 @@ bool Application::RenderHostedPreviewOffscreenSurface(
continue;
}
const ShellPanelChromeState* panelState = TryGetShellPanelState(ShellPanelId::XCUILayoutLab);
const bool nativeHostedPreview =
panelState != nullptr &&
panelState->hostedPreviewEnabled &&
IsNativeHostedPreviewEnabled(ShellPanelId::XCUILayoutLab);
::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewSurfaceDescriptor hostedSurfaceDescriptor = {};
const bool hasHostedSurfaceDescriptor =
nativeHostedPreview &&
panelState != nullptr &&
!panelState->previewDebugName.empty() &&
m_hostedPreviewSurfaceRegistry.TryGetSurfaceDescriptor(
panelState->previewDebugName.data(),
hostedSurfaceDescriptor);
::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewSurfaceImage hostedSurfaceImage = {};
const bool showHostedSurfaceImage =
nativeHostedPreview &&
panelState != nullptr &&
!panelState->previewDebugName.empty() &&
m_hostedPreviewSurfaceRegistry.TryGetSurfaceImage(
panelState->previewDebugName.data(),
hostedSurfaceImage);
const NativeHostedPreviewConsumption previewConsumption =
Application::ResolveNativeHostedPreviewConsumption(
nativeHostedPreview,
hasHostedSurfaceDescriptor,
showHostedSurfaceImage,
"Native layout preview pending",
"Waiting for queued native preview output to publish into the layout card.");
::XCEngine::Editor::XCUIBackend::XCUIPanelCanvasSession canvasSession = {};
canvasSession.hostRect = panelLayout.panelRect;
canvasSession.canvasRect = panelLayout.canvasRect;
@@ -1099,6 +1190,12 @@ bool Application::RenderHostedPreviewOffscreenSurface(
canvasRequest.height = panelLayout.panelRect.height;
canvasRequest.topInset = panelLayout.canvasRect.y - panelLayout.panelRect.y;
canvasRequest.drawPreviewFrame = false;
canvasRequest.showSurfaceImage = previewConsumption.showSurfaceImage;
canvasRequest.surfaceImage = hostedSurfaceImage;
canvasRequest.placeholderTitle =
previewConsumption.placeholderTitle.empty() ? nullptr : previewConsumption.placeholderTitle.data();
canvasRequest.placeholderSubtitle =
previewConsumption.placeholderSubtitle.empty() ? nullptr : previewConsumption.placeholderSubtitle.data();
const auto resolvedSession = m_nativeLayoutCanvasHost.BeginCanvas(canvasRequest);
const bool wantsKeyboard = panelLayout.active;
@@ -1119,13 +1216,29 @@ bool Application::RenderHostedPreviewOffscreenSurface(
panelLayout.active && ShouldCaptureKeyboardNavigation(resolvedSession, m_nativeLayoutRuntime.GetFrameResult()));
const auto& frame = m_nativeLayoutRuntime.Update(input);
AppendDrawData(composedDrawData, frame.drawData);
if (previewConsumption.queueRuntimeFrame) {
::XCEngine::Editor::XCUIBackend::XCUIHostedPreviewFrame previewFrame = {};
previewFrame.drawData = &frame.drawData;
previewFrame.canvasRect = resolvedSession.canvasRect;
previewFrame.logicalSize = UI::UISize(
resolvedSession.canvasRect.width,
resolvedSession.canvasRect.height);
previewFrame.debugName =
panelState != nullptr ? panelState->previewDebugName.data() : nullptr;
previewFrame.debugSource =
panelState != nullptr ? panelState->previewDebugSource.data() : nullptr;
m_hostedPreviewQueue.Submit(previewFrame);
} else if (previewConsumption.appendRuntimeDrawDataToShell) {
AppendDrawData(composedDrawData, frame.drawData);
}
m_nativeLayoutCanvasHost.EndCanvas();
NativePanelFrameSummary summary = {};
summary.layout = panelLayout;
summary.lineA = m_nativeLayoutReloadSucceeded
? frame.stats.statusMessage
? Application::ComposeNativeHostedPreviewStatusLine(
previewConsumption,
frame.stats.statusMessage)
: "Layout lab reload failed; showing last retained runtime state.";
summary.lineB =
std::to_string(frame.stats.rowCount) + " rows | " +
@@ -1197,8 +1310,9 @@ bool Application::RenderHostedPreviewOffscreenSurface(
}
void Application::FrameLegacyImGuiHost() {
m_hostedPreviewQueue.BeginFrame();
m_hostedPreviewSurfaceRegistry.BeginFrame();
Application::BeginHostedPreviewFrameLifecycle(
m_hostedPreviewQueue,
m_hostedPreviewSurfaceRegistry);
SyncHostedPreviewSurfaces();
if (m_windowCompositor == nullptr) {
m_xcuiInputSource.ClearFrameTransients();
@@ -1273,6 +1387,11 @@ void Application::FrameLegacyImGuiHost() {
}
void Application::FrameNativeXCUIHost() {
Application::BeginHostedPreviewFrameLifecycle(
m_hostedPreviewQueue,
m_hostedPreviewSurfaceRegistry);
SyncHostedPreviewSurfaces();
auto* nativeCompositor =
dynamic_cast<::XCEngine::Editor::XCUIBackend::NativeWindowUICompositor*>(m_windowCompositor.get());
if (nativeCompositor == nullptr) {
@@ -1287,6 +1406,7 @@ void Application::FrameNativeXCUIHost() {
const auto shellFrameDelta = DispatchShellShortcuts(shellSnapshot);
::XCEngine::UI::UIDrawData nativeShellDrawData = BuildNativeShellDrawData(shellSnapshot, shellFrameDelta);
nativeCompositor->SubmitRenderPacket(nativeShellDrawData, &m_hostedPreviewTextAtlasProvider);
SyncHostedPreviewSurfaces();
m_windowCompositor->RenderFrame(
kClearColor,
@@ -1294,6 +1414,8 @@ void Application::FrameNativeXCUIHost() {
[this](
const ::XCEngine::Rendering::RenderContext& renderContext,
const ::XCEngine::Rendering::RenderSurface& surface) {
RenderQueuedHostedPreviews(renderContext, surface);
MainWindowNativeBackdropRenderer::FrameState frameState = {};
frameState.elapsedSeconds = static_cast<float>(
std::chrono::duration<double>(std::chrono::steady_clock::now() - m_startTime).count());