#include "NativeRendererSupport.h" namespace XCEngine::UI::Editor::Host { using namespace NativeRendererSupport; bool NativeRenderer::Initialize(HWND hwnd) { Shutdown(); if (hwnd == nullptr) { m_lastRenderError = "Initialize rejected a null hwnd."; return false; } m_hwnd = hwnd; D2D1_FACTORY_OPTIONS factoryOptions = {}; #ifdef _DEBUG factoryOptions.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION; #endif HRESULT hr = D2D1CreateFactory( D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory1), &factoryOptions, reinterpret_cast(m_d2dFactory.ReleaseAndGetAddressOf())); if (FAILED(hr)) { m_lastRenderError = HrToString("D2D1CreateFactory", hr); Shutdown(); return false; } hr = DWriteCreateFactory( DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast(m_dwriteFactory.ReleaseAndGetAddressOf())); if (FAILED(hr)) { m_lastRenderError = HrToString("DWriteCreateFactory", hr); Shutdown(); return false; } m_lastRenderError.clear(); return true; } void NativeRenderer::Shutdown() { DetachWindowRenderer(); while (!m_liveTextures.empty()) { auto it = m_liveTextures.begin(); delete *it; m_liveTextures.erase(it); } m_textFormats.clear(); m_solidBrush.Reset(); m_renderTarget.Reset(); m_wicFactory.Reset(); m_dwriteFactory.Reset(); m_d2dFactory.Reset(); if (m_wicComInitialized) { CoUninitialize(); m_wicComInitialized = false; } m_hwnd = nullptr; } void NativeRenderer::SetDpiScale(float dpiScale) { m_dpiScale = ClampDpiScale(dpiScale); if (m_renderTarget) { m_renderTarget->SetDpi(kBaseDpi, kBaseDpi); } } float NativeRenderer::GetDpiScale() const { return m_dpiScale; } void NativeRenderer::Resize(UINT width, UINT height) { if (!m_renderTarget || width == 0 || height == 0) { return; } const HRESULT hr = m_renderTarget->Resize(D2D1::SizeU(width, height)); if (hr == D2DERR_RECREATE_TARGET) { DiscardRenderTarget(); } } bool NativeRenderer::AttachWindowRenderer(D3D12WindowRenderer& windowRenderer) { if (m_windowRenderer != &windowRenderer) { ReleaseWindowRendererInterop(); m_windowRenderer = &windowRenderer; } if (!EnsureWindowRendererInterop()) { return false; } DiscardRenderTarget(); if (m_windowInterop.HasBackBufferTargets()) { return true; } return m_windowInterop.RebuildBackBufferTargets(); } void NativeRenderer::DetachWindowRenderer() { ReleaseWindowRendererInterop(); m_windowRenderer = nullptr; } void NativeRenderer::ReleaseWindowRendererBackBufferTargets() { m_windowInterop.ReleaseBackBufferTargets(); } bool NativeRenderer::RebuildWindowRendererBackBufferTargets() { if (!EnsureWindowRendererInterop()) { return false; } DiscardRenderTarget(); ReleaseWindowRendererBackBufferTargets(); return m_windowInterop.RebuildBackBufferTargets(); } bool NativeRenderer::HasAttachedWindowRenderer() const { return m_windowInterop.HasAttachedWindowRenderer(); } const std::string& NativeRenderer::GetLastRenderError() const { return m_lastRenderError; } bool NativeRenderer::EnsureWindowRendererInterop() { if (m_windowRenderer == nullptr) { m_lastRenderError = "EnsureWindowRendererInterop requires an attached D3D12 window renderer."; return false; } if (m_d2dFactory == nullptr || m_dwriteFactory == nullptr) { m_lastRenderError = "EnsureWindowRendererInterop requires initialized D2D and DWrite factories."; return false; } const bool attached = m_windowInterop.Attach(*m_windowRenderer, *m_d2dFactory.Get()); if (!attached) { m_lastRenderError = m_windowInterop.GetLastError(); } else { m_lastRenderError.clear(); } return attached; } void NativeRenderer::ReleaseWindowRendererInterop() { m_windowInterop.Detach(); } bool NativeRenderer::EnsureRenderTarget() { if (!m_hwnd || !m_d2dFactory || !m_dwriteFactory) { m_lastRenderError = "EnsureRenderTarget requires hwnd, D2D factory, and DWrite factory."; return false; } return CreateDeviceResources(); } bool NativeRenderer::EnsureWicFactory(std::string& outError) { outError.clear(); if (m_wicFactory) { return true; } const HRESULT initHr = CoInitializeEx(nullptr, COINIT_MULTITHREADED); if (FAILED(initHr) && initHr != RPC_E_CHANGED_MODE) { outError = HrToString("CoInitializeEx", initHr); return false; } if (SUCCEEDED(initHr)) { m_wicComInitialized = true; } const HRESULT factoryHr = CoCreateInstance( CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(m_wicFactory.ReleaseAndGetAddressOf())); if (FAILED(factoryHr)) { outError = HrToString("CoCreateInstance(CLSID_WICImagingFactory)", factoryHr); return false; } return true; } void NativeRenderer::DiscardRenderTarget() { InvalidateCachedTextureBitmaps(m_renderTarget.Get()); m_solidBrush.Reset(); m_renderTarget.Reset(); } bool NativeRenderer::CreateDeviceResources() { if (m_renderTarget) { return true; } RECT clientRect = {}; GetClientRect(m_hwnd, &clientRect); const UINT width = static_cast((std::max)(clientRect.right - clientRect.left, 1L)); const UINT height = static_cast((std::max)(clientRect.bottom - clientRect.top, 1L)); const D2D1_RENDER_TARGET_PROPERTIES renderTargetProps = D2D1::RenderTargetProperties( D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED), kBaseDpi, kBaseDpi); const D2D1_HWND_RENDER_TARGET_PROPERTIES hwndProps = D2D1::HwndRenderTargetProperties( m_hwnd, D2D1::SizeU(width, height)); const HRESULT renderTargetHr = m_d2dFactory->CreateHwndRenderTarget( renderTargetProps, hwndProps, m_renderTarget.ReleaseAndGetAddressOf()); if (FAILED(renderTargetHr)) { m_lastRenderError = HrToString("ID2D1Factory::CreateHwndRenderTarget", renderTargetHr); return false; } const HRESULT brushHr = m_renderTarget->CreateSolidColorBrush( D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f), m_solidBrush.ReleaseAndGetAddressOf()); if (FAILED(brushHr)) { m_lastRenderError = HrToString("ID2D1HwndRenderTarget::CreateSolidColorBrush", brushHr); DiscardRenderTarget(); return false; } m_renderTarget->SetDpi(kBaseDpi, kBaseDpi); m_renderTarget->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE); m_lastRenderError.clear(); return true; } void NativeRenderer::InvalidateCachedTextureBitmaps(const ID2D1RenderTarget* renderTarget) { for (NativeTextureResource* texture : m_liveTextures) { if (texture == nullptr) { continue; } if (renderTarget == nullptr || texture->cachedTarget == renderTarget) { texture->cachedBitmap.Reset(); texture->cachedTarget = nullptr; } } } } // namespace XCEngine::UI::Editor::Host