From 617f0251995cfdd099334b96b35665bba547fa52 Mon Sep 17 00:00:00 2001 From: "prompt.ac/@jeffrey" Date: Tue, 1 Sep 2026 15:43:09 -0400 Subject: [PATCH] Put the renderer's two OS calls behind a seam MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Of the ~800 lines that create the D3D11 device, build the three pipelines and draw a frame, exactly two touch anything platform-specific: the display-size query and CreateSwapChainForCoreWindow. Both now go through render::SurfaceHost, and App.cpp supplies the WinRT half. This is the first cut of lifting the renderer out of the ref class. Nothing moves yet and no behaviour changes โ€” the point is to name the boundary and show it is two calls wide, because that is the difference between a Steam native build being a port and being a rewrite. On PC the other side of this seam is CreateSwapChainForHwnd, which the GDK smoke already proved works against the same D3D11 device that D2D and DirectWrite share. ReadPackageBytes still resolves shaders and fonts through Windows.ApplicationModel.Package. That is named in the header rather than hidden: it belongs on platform::Paths, whose package() already answers it. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01XnG7gt9GQv3h9aZjLJtRHN --- xbox/native-bios/App.cpp | 63 ++++++++++++++++++-------- xbox/native-bios/render/ac_surface.hpp | 46 +++++++++++++++++++ 2 files changed, 89 insertions(+), 20 deletions(-) create mode 100644 xbox/native-bios/render/ac_surface.hpp diff --git a/xbox/native-bios/App.cpp b/xbox/native-bios/App.cpp index 4b20e2348a..5b16844d63 100644 --- a/xbox/native-bios/App.cpp +++ b/xbox/native-bios/App.cpp @@ -3,6 +3,7 @@ #include "PhotoDiscService.hpp" #include "OskiewarLivePublisher.hpp" #include "../runtime/include/ac/image_effects.hpp" +#include "render/ac_surface.hpp" using Microsoft::WRL::ComPtr; using namespace Platform; @@ -287,6 +288,43 @@ static void Check(HRESULT hr) { if (FAILED(hr)) throw Exception::CreateException(hr); } +// The WinRT half of the render seam. A native class cannot hold a ref type, so +// the CoreWindow arrives as the IUnknown the swap-chain call wants anyway. +class CoreWindowSurface final : public ac::xbox::render::SurfaceHost { + public: + explicit CoreWindowSurface(IUnknown* window) : window_(window) {} + + void preferred_size(unsigned& width, unsigned& height) override { + try { + const auto hdmi = HdmiDisplayInformation::GetForCurrentView(); + const auto mode = hdmi ? hdmi->GetCurrentDisplayMode() : nullptr; + if (mode) { + width = (std::min)(3840u, mode->ResolutionWidthInRawPixels); + height = (std::min)(2160u, mode->ResolutionHeightInRawPixels); + } + const auto display = DisplayInformation::GetForCurrentView(); + const auto rawWidth = display->ScreenWidthInRawPixels; + const auto rawHeight = display->ScreenHeightInRawPixels; + if (width <= 1920 && rawWidth >= 1920 && rawHeight >= 1080) { + width = (std::min)(3840u, rawWidth); + height = (std::min)(2160u, rawHeight); + } + } catch (...) { + // Early Xbox activation can withhold display information. The caller's + // seeded values stand rather than DXGI's 8x8 placeholder. + } + } + + HRESULT create_swap_chain(IDXGIFactory2* factory, ID3D11Device* device, + const DXGI_SWAP_CHAIN_DESC1& desc, + IDXGISwapChain1** out) override { + return factory->CreateSwapChainForCoreWindow(device, window_, &desc, nullptr, out); + } + + private: + IUnknown* window_; +}; + ref class App sealed : public IFrameworkView { public: virtual void Initialize(CoreApplicationView^ view) { @@ -544,6 +582,8 @@ private: } void CreateGraphics() { + if (!m_surface) + m_surface = std::make_unique(reinterpret_cast(m_window)); UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; #if defined(_DEBUG) flags |= D3D11_CREATE_DEVICE_DEBUG; @@ -576,24 +616,7 @@ private: // hiding the mistake while every useful drawing coordinate gets clipped. unsigned requestedWidth = 1920; unsigned requestedHeight = 1080; - try { - const auto hdmi = HdmiDisplayInformation::GetForCurrentView(); - const auto mode = hdmi ? hdmi->GetCurrentDisplayMode() : nullptr; - if (mode) { - requestedWidth = (std::min)(3840u, mode->ResolutionWidthInRawPixels); - requestedHeight = (std::min)(2160u, mode->ResolutionHeightInRawPixels); - } - const auto display = DisplayInformation::GetForCurrentView(); - const auto rawWidth = display->ScreenWidthInRawPixels; - const auto rawHeight = display->ScreenHeightInRawPixels; - if (requestedWidth <= 1920 && rawWidth >= 1920 && rawHeight >= 1080) { - requestedWidth = (std::min)(3840u, rawWidth); - requestedHeight = (std::min)(2160u, rawHeight); - } - } catch (...) { - // Early Xbox activation can withhold display information. Keep the - // explicit 1080p fallback instead of accepting DXGI's 8x8 placeholder. - } + m_surface->preferred_size(requestedWidth, requestedHeight); desc.Width = requestedWidth; desc.Height = requestedHeight; desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; @@ -603,8 +626,7 @@ private: desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; desc.Scaling = DXGI_SCALING_STRETCH; desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; - Check(factory->CreateSwapChainForCoreWindow( - m_device.Get(), reinterpret_cast(m_window), &desc, nullptr, &m_swapChain)); + Check(m_surface->create_swap_chain(factory.Get(), m_device.Get(), desc, &m_swapChain)); Check(m_swapChain->GetBuffer(0, IID_PPV_ARGS(&m_backBuffer))); D3D11_TEXTURE2D_DESC backBufferDesc{}; m_backBuffer->GetDesc(&backBufferDesc); @@ -2556,6 +2578,7 @@ private: } CoreWindow^ m_window = nullptr; + std::unique_ptr m_surface; bool m_closed = false; bool m_needsIdleFrame = false; unsigned m_previousButtons = 0; diff --git a/xbox/native-bios/render/ac_surface.hpp b/xbox/native-bios/render/ac_surface.hpp new file mode 100644 index 0000000000..4521afb8fa --- /dev/null +++ b/xbox/native-bios/render/ac_surface.hpp @@ -0,0 +1,46 @@ +#pragma once + +// The render path's seam with the OS. +// +// `platform/ac_platform.hpp` faces the machine for the app as a whole. This +// faces it for the renderer alone, and it is deliberately much smaller: of the +// ~800 lines that create the D3D11 device, build the three pipelines and draw a +// frame, exactly two touch anything platform-specific. Both are here. +// +// Everything else in that path โ€” the device, the swap-chain description, the +// scene texture, Direct2D, DirectWrite, the shaders and every draw โ€” is plain +// desktop-and-console C++ that compiles unchanged against either backend. That +// is the whole reason a Steam build is not a renderer rewrite: on PC the +// answers are `CreateSwapChainForHwnd` and DirectWrite verbatim, and only the +// console loses D3D11 (see `xbox/GDK-PORT.md` ยง1a). +// +// Still outside this seam, and named rather than hidden: `ReadPackageBytes` +// resolves the packaged `.cso` shaders and TTFs through +// `Windows.ApplicationModel.Package`. That belongs on `platform::Paths`, whose +// `package()` already answers it, and moves when App.cpp adopts the platform +// layer. + +#include +#include + +namespace ac::xbox::render { + +class SurfaceHost { + public: + virtual ~SurfaceHost() = default; + + // The size to ask the swap chain for, in raw pixels. A backend that cannot + // answer must leave both untouched: the caller seeds them with an explicit + // 1080p, because DXGI hands back an 8x8 placeholder when a CoreWindow swap + // chain is created at zero, and solid clears still stretch fullscreen over + // that mistake while every useful drawing coordinate gets clipped. + virtual void preferred_size(unsigned& width, unsigned& height) = 0; + + // `CreateSwapChainForCoreWindow` under WinRT, `CreateSwapChainForHwnd` on + // desktop. The one call in the render path that cannot be written once. + virtual HRESULT create_swap_chain(IDXGIFactory2* factory, ID3D11Device* device, + const DXGI_SWAP_CHAIN_DESC1& desc, + IDXGISwapChain1** out) = 0; +}; + +} // namespace ac::xbox::render -- 2.51.2