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