#include "D3D12WindowRenderer.h" #include #include #include namespace XCEngine::UI::Editor::Host { using ::XCEngine::RHI::RHIDevice; using ::XCEngine::RHI::RHISwapChain; using ::XCEngine::RHI::D3D12Texture; namespace { void TraceRenderer(std::string_view message, const void* renderer) { std::ostringstream stream = {}; stream << message << " renderer=0x" << std::hex << std::uppercase << reinterpret_cast(renderer); AppendUIEditorRuntimeTrace("window-close", stream.str()); } } // namespace bool D3D12WindowRenderer::Initialize(HWND hwnd, int width, int height) { Shutdown(); if (hwnd == nullptr || width <= 0 || height <= 0) { m_lastError = "Initialize rejected an invalid hwnd or size."; return false; } if (!m_hostDevice.Initialize()) { m_lastError = m_hostDevice.GetLastError(); return false; } if (!m_presenter.Initialize(m_hostDevice, hwnd, width, height)) { m_lastError = m_presenter.GetLastError(); Shutdown(); return false; } auto* device = m_hostDevice.GetRHIDevice(); if (device == nullptr || !m_textureAllocator.Initialize(*device, 512u)) { m_lastError = "Failed to initialize the UI texture descriptor allocator."; Shutdown(); return false; } m_lastError.clear(); return true; } void D3D12WindowRenderer::Shutdown() { TraceRenderer("D3D12WindowRenderer::Shutdown begin", this); m_textureCpuHandles.clear(); m_textureAllocator.Shutdown(); m_capture.Shutdown(); m_presenter.Shutdown(); m_hostDevice.Shutdown(); m_activeFrameSlot = 0u; m_nextFrameSlot = 0u; m_activeBackBufferIndex = 0u; m_lastError.clear(); TraceRenderer("D3D12WindowRenderer::Shutdown end", this); } bool D3D12WindowRenderer::Resize(int width, int height) { if (!m_presenter.Resize(width, height)) { m_lastError = m_presenter.GetLastError(); return false; } m_activeFrameSlot = 0u; m_nextFrameSlot = 0u; m_activeBackBufferIndex = m_presenter.GetCurrentBackBufferIndex(); m_lastError = m_presenter.GetLastError(); if (!m_lastError.empty()) { return true; } m_lastError.clear(); return true; } bool D3D12WindowRenderer::BeginFrame() { if (m_presenter.GetSwapChain() == nullptr) { m_lastError = "BeginFrame requires an initialized swap chain."; return false; } m_activeBackBufferIndex = m_presenter.GetCurrentBackBufferIndex(); const std::uint32_t frameSlot = m_nextFrameSlot; if (!m_hostDevice.BeginFrame(frameSlot)) { m_lastError = m_hostDevice.GetLastError(); return false; } m_activeFrameSlot = frameSlot; m_nextFrameSlot = (frameSlot + 1u) % kFrameContextCount; m_lastError.clear(); return true; } bool D3D12WindowRenderer::PrepareCurrentBackBufferForUiRender() { ::XCEngine::Rendering::RenderContext renderContext = GetRenderContext(); if (!renderContext.IsValid() || renderContext.commandList == nullptr) { m_lastError = "PrepareCurrentBackBufferForUiRender requires a valid render context."; return false; } const bool prepared = m_presenter.PrepareCurrentBackBufferForRender(renderContext); m_lastError = prepared ? std::string() : m_presenter.GetLastError(); return prepared; } bool D3D12WindowRenderer::FinalizeCurrentBackBufferForPresent() { ::XCEngine::Rendering::RenderContext renderContext = GetRenderContext(); if (!renderContext.IsValid() || renderContext.commandList == nullptr) { m_lastError = "FinalizeCurrentBackBufferForPresent requires a valid render context."; return false; } const bool finalized = m_presenter.FinalizeCurrentBackBufferForPresent(renderContext); m_lastError = finalized ? std::string() : m_presenter.GetLastError(); return finalized; } bool D3D12WindowRenderer::SubmitFrame() { if (!m_hostDevice.SubmitFrame(m_activeFrameSlot)) { m_lastError = m_hostDevice.GetLastError(); return false; } m_lastError.clear(); return true; } bool D3D12WindowRenderer::SignalFrameCompletion() { if (!m_hostDevice.SignalFrameCompletion(m_activeFrameSlot)) { m_lastError = m_hostDevice.GetLastError(); return false; } m_lastError.clear(); return true; } bool D3D12WindowRenderer::PresentFrame() { const bool presented = m_presenter.PresentFrame(); m_lastError = presented ? std::string() : m_presenter.GetLastError(); return presented; } bool D3D12WindowRenderer::CaptureCurrentBackBufferToPng( const std::filesystem::path& outputPath, std::string& outError) { const bool captured = m_capture.CaptureCurrentBackBufferToPng(*this, outputPath, outError); if (!captured && outError.empty()) { outError = "CaptureCurrentBackBufferToPng failed."; } return captured; } void D3D12WindowRenderer::WaitForGpuIdle() { TraceRenderer("D3D12WindowRenderer::WaitForGpuIdle begin", this); m_hostDevice.WaitForGpuIdle(); TraceRenderer("D3D12WindowRenderer::WaitForGpuIdle end", this); } ID3D12Device* D3D12WindowRenderer::GetDevice() const { return m_hostDevice.GetDevice(); } ID3D12CommandQueue* D3D12WindowRenderer::GetCommandQueue() const { return m_hostDevice.GetCommandQueue(); } const std::string& D3D12WindowRenderer::GetLastError() const { return m_lastError; } RHIDevice* D3D12WindowRenderer::GetRHIDevice() const { return m_hostDevice.GetRHIDevice(); } std::uint32_t D3D12WindowRenderer::GetViewportResourceRetirementSlotCount() const { return kFrameContextCount; } bool D3D12WindowRenderer::TryGetActiveViewportResourceRetirementSlot(std::uint32_t& outSlot) const { outSlot = 0u; if (GetRHIDevice() == nullptr) { return false; } outSlot = m_activeFrameSlot; return true; } bool D3D12WindowRenderer::CreateViewportTextureHandle( ::XCEngine::RHI::RHITexture& texture, std::uint32_t width, std::uint32_t height, ::XCEngine::UI::UITextureHandle& outTexture) { outTexture = {}; if (width == 0u || height == 0u || !m_textureAllocator.IsInitialized()) { return false; } D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle = {}; D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = {}; if (!m_textureAllocator.CreateTextureDescriptor( &texture, &cpuHandle, &gpuHandle) || cpuHandle.ptr == 0u || gpuHandle.ptr == 0u) { return false; } outTexture.nativeHandle = static_cast(gpuHandle.ptr); outTexture.width = width; outTexture.height = height; outTexture.kind = ::XCEngine::UI::UITextureHandleKind::ShaderResourceView; outTexture.resourceHandle = reinterpret_cast(&texture); m_textureCpuHandles[outTexture.nativeHandle] = cpuHandle; return true; } void D3D12WindowRenderer::ReleaseViewportTextureHandle( ::XCEngine::UI::UITextureHandle& texture) { if (!texture.IsValid()) { texture = {}; return; } const auto found = m_textureCpuHandles.find(texture.nativeHandle); if (found != m_textureCpuHandles.end()) { D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = {}; gpuHandle.ptr = static_cast(texture.nativeHandle); m_textureAllocator.Free(found->second, gpuHandle); m_textureCpuHandles.erase(found); } texture = {}; } RHISwapChain* D3D12WindowRenderer::GetSwapChain() const { return m_presenter.GetSwapChain(); } const ::XCEngine::Rendering::RenderSurface* D3D12WindowRenderer::GetCurrentRenderSurface() const { return m_presenter.GetCurrentRenderSurface(); } const D3D12Texture* D3D12WindowRenderer::GetCurrentBackBufferTexture() const { return m_presenter.GetCurrentBackBufferTexture(); } const D3D12Texture* D3D12WindowRenderer::GetBackBufferTexture(std::uint32_t index) const { return m_presenter.GetBackBufferTexture(index); } std::uint32_t D3D12WindowRenderer::GetBackBufferCount() const { return m_presenter.GetBackBufferCount(); } std::uint32_t D3D12WindowRenderer::GetActiveFrameSlot() const { return m_activeFrameSlot; } ::XCEngine::Rendering::RenderContext D3D12WindowRenderer::GetRenderContext() const { return m_hostDevice.GetRenderContext(m_activeFrameSlot); } D3D12ShaderResourceDescriptorAllocator& D3D12WindowRenderer::GetTextureDescriptorAllocator() { return m_textureAllocator; } const D3D12ShaderResourceDescriptorAllocator& D3D12WindowRenderer::GetTextureDescriptorAllocator() const { return m_textureAllocator; } } // namespace XCEngine::UI::Editor::Host