diff --git a/new_editor/CMakeLists.txt b/new_editor/CMakeLists.txt index 2f432116..23a0e3f6 100644 --- a/new_editor/CMakeLists.txt +++ b/new_editor/CMakeLists.txt @@ -124,8 +124,11 @@ target_link_libraries(XCUIEditorLib PUBLIC add_library(XCUIEditorHost STATIC app/Host/AutoScreenshot.cpp + app/Host/D3D12HostDevice.cpp app/Host/D3D12ShaderResourceDescriptorAllocator.cpp + app/Host/D3D12WindowInteropContext.cpp app/Host/D3D12WindowRenderer.cpp + app/Host/D3D12WindowSwapChainPresenter.cpp app/Host/D3D12WindowRenderLoop.cpp app/Host/NativeRenderer.cpp app/Host/WindowMessageDispatcher.cpp diff --git a/new_editor/app/Application.cpp b/new_editor/app/Application.cpp index eccbe0be..44cba9c6 100644 --- a/new_editor/app/Application.cpp +++ b/new_editor/app/Application.cpp @@ -478,6 +478,7 @@ bool Application::Initialize(HINSTANCE hInstance, int nCmdShow) { "app", "workspace initialized: " + m_editorContext.DescribeWorkspaceState(m_editorWorkspace.GetShellInteractionState())); + m_renderReady = true; ShowWindow(m_hwnd, nCmdShow); UpdateWindow(m_hwnd); @@ -493,6 +494,7 @@ bool Application::Initialize(HINSTANCE hInstance, int nCmdShow) { void Application::Shutdown() { LogRuntimeTrace("app", "shutdown begin"); + m_renderReady = false; if (GetCapture() == m_hwnd) { ReleaseCapture(); } @@ -518,12 +520,10 @@ void Application::Shutdown() { } void Application::RenderFrame() { - if (m_hwnd == nullptr) { + if (!m_renderReady || m_hwnd == nullptr) { return; } - ApplyPendingWindowResize(); - RECT clientRect = {}; GetClientRect(m_hwnd, &clientRect); const unsigned int pixelWidth = @@ -620,13 +620,8 @@ void Application::RenderFrame() { presentResult.framePresented); } -void Application::OnDeferredRenderMessage() { - m_hostRuntime.ClearDeferredRenderRequest(); - RenderFrame(); -} - void Application::OnPaintMessage() { - if (m_hwnd == nullptr) { + if (!m_renderReady || m_hwnd == nullptr) { return; } @@ -747,8 +742,8 @@ std::string Application::DescribeInputEvents( return stream.str(); } -void Application::OnResize() { - QueueCurrentClientResize(); +void Application::OnResize(UINT width, UINT height) { + ApplyWindowResize(width, height); } void Application::OnEnterSizeMove() { @@ -757,28 +752,16 @@ void Application::OnEnterSizeMove() { void Application::OnExitSizeMove() { m_hostRuntime.EndInteractiveResize(); - QueueCurrentClientResize(); -} - -void Application::QueueWindowResize(UINT width, UINT height) { - m_hostRuntime.QueueWindowResize(width, height); -} - -void Application::QueueCurrentClientResize() { UINT width = 0u; UINT height = 0u; - if (!QueryCurrentClientPixelSize(width, height)) { - return; + if (QueryCurrentClientPixelSize(width, height)) { + ApplyWindowResize(width, height); } - - QueueWindowResize(width, height); } -bool Application::ApplyPendingWindowResize() { - UINT width = 0u; - UINT height = 0u; - if (!m_hostRuntime.ConsumePendingWindowResize(width, height)) { - return true; +bool Application::ApplyWindowResize(UINT width, UINT height) { + if (!m_renderReady || width == 0u || height == 0u) { + return false; } const Host::D3D12WindowRenderLoopResizeResult resizeResult = @@ -834,7 +817,11 @@ void Application::OnDpiChanged(UINT dpi, const RECT& suggestedRect) { windowWidth, windowHeight, SWP_NOZORDER | SWP_NOACTIVATE); - QueueCurrentClientResize(); + UINT clientWidth = 0u; + UINT clientHeight = 0u; + if (QueryCurrentClientPixelSize(clientWidth, clientHeight)) { + ApplyWindowResize(clientWidth, clientHeight); + } } std::ostringstream trace = {}; diff --git a/new_editor/app/Application.h b/new_editor/app/Application.h index bfe44319..ef9dce75 100644 --- a/new_editor/app/Application.h +++ b/new_editor/app/Application.h @@ -45,15 +45,12 @@ private: bool Initialize(HINSTANCE hInstance, int nCmdShow); void Shutdown(); void RenderFrame(); - void OnDeferredRenderMessage(); void OnPaintMessage(); - void OnResize(); + void OnResize(UINT width, UINT height); void OnEnterSizeMove(); void OnExitSizeMove(); void OnDpiChanged(UINT dpi, const RECT& suggestedRect); - void QueueWindowResize(UINT width, UINT height); - void QueueCurrentClientResize(); - bool ApplyPendingWindowResize(); + bool ApplyWindowResize(UINT width, UINT height); bool QueryCurrentClientPixelSize(UINT& outWidth, UINT& outHeight) const; bool IsPointerInsideClientArea() const; bool ApplyCurrentCursor() const; @@ -94,6 +91,7 @@ private: App::ProductEditorWorkspace m_editorWorkspace = {}; std::vector<::XCEngine::UI::UIInputEvent> m_pendingInputEvents = {}; bool m_trackingMouseLeave = false; + bool m_renderReady = false; ::XCEngine::UI::Editor::Host::HostRuntimeState m_hostRuntime = {}; }; diff --git a/new_editor/app/Host/D3D12HostDevice.cpp b/new_editor/app/Host/D3D12HostDevice.cpp new file mode 100644 index 00000000..4701955f --- /dev/null +++ b/new_editor/app/Host/D3D12HostDevice.cpp @@ -0,0 +1,296 @@ +#include "D3D12HostDevice.h" + +#include + +namespace XCEngine::UI::Editor::Host { + +using ::XCEngine::RHI::CommandListDesc; +using ::XCEngine::RHI::CommandQueueDesc; +using ::XCEngine::RHI::D3D12CommandList; +using ::XCEngine::RHI::D3D12CommandQueue; +using ::XCEngine::RHI::D3D12Device; +using ::XCEngine::RHI::RHICommandList; +using ::XCEngine::RHI::RHICommandQueue; +using ::XCEngine::RHI::RHIDevice; +using ::XCEngine::RHI::RHIDeviceDesc; +using ::XCEngine::RHI::RHIFactory; +using ::XCEngine::RHI::RHIType; + +bool D3D12HostDevice::Initialize() { + Shutdown(); + + m_device = RHIFactory::CreateRHIDevice(RHIType::D3D12); + if (m_device == nullptr) { + m_lastError = "Failed to create the D3D12 RHI device."; + return false; + } + + RHIDeviceDesc deviceDesc = {}; +#ifdef _DEBUG + deviceDesc.enableDebugLayer = true; + deviceDesc.enableGPUValidation = true; +#endif + if (!m_device->Initialize(deviceDesc)) { + m_lastError = "Failed to initialize the D3D12 RHI device."; + Shutdown(); + return false; + } + + CommandQueueDesc queueDesc = {}; + queueDesc.queueType = static_cast(::XCEngine::RHI::CommandQueueType::Direct); + m_commandQueue = m_device->CreateCommandQueue(queueDesc); + if (m_commandQueue == nullptr || GetD3D12CommandQueue() == nullptr) { + m_lastError = "Failed to create the D3D12 command queue."; + Shutdown(); + return false; + } + + CommandListDesc commandListDesc = {}; + commandListDesc.commandListType = + static_cast(::XCEngine::RHI::CommandQueueType::Direct); + for (std::uint32_t commandListIndex = 0; + commandListIndex < kFrameContextCount; + ++commandListIndex) { + m_commandLists[commandListIndex] = m_device->CreateCommandList(commandListDesc); + if (m_commandLists[commandListIndex] == nullptr || + GetD3D12CommandList(commandListIndex) == nullptr) { + m_lastError = "Failed to create the D3D12 command list."; + Shutdown(); + return false; + } + + m_commandLists[commandListIndex]->Close(); + } + + if (!InitializeFrameCompletionFence()) { + m_lastError = "Failed to create the host frame completion fence."; + Shutdown(); + return false; + } + + m_frameFenceValues.fill(0u); + m_lastError.clear(); + return true; +} + +void D3D12HostDevice::Shutdown() { + WaitForGpuIdle(); + ReleaseFrameCompletionFence(); + + for (auto*& commandList : m_commandLists) { + if (commandList != nullptr) { + commandList->Shutdown(); + delete commandList; + commandList = nullptr; + } + } + + if (m_commandQueue != nullptr) { + m_commandQueue->Shutdown(); + delete m_commandQueue; + m_commandQueue = nullptr; + } + + if (m_device != nullptr) { + m_device->Shutdown(); + delete m_device; + m_device = nullptr; + } + + m_lastError.clear(); +} + +bool D3D12HostDevice::BeginFrame(std::uint32_t frameIndex) { + WaitForFrame(frameIndex); + + RHICommandList* commandList = GetCommandList(frameIndex); + D3D12CommandList* d3d12CommandList = GetD3D12CommandList(frameIndex); + if (commandList == nullptr || d3d12CommandList == nullptr) { + m_lastError = "BeginFrame could not resolve the active command list."; + return false; + } + + commandList->Reset(); + m_lastError.clear(); + return true; +} + +bool D3D12HostDevice::SubmitFrame(std::uint32_t frameIndex) { + RHICommandList* commandList = GetCommandList(frameIndex); + if (commandList == nullptr || m_commandQueue == nullptr) { + m_lastError = "SubmitFrame requires an initialized command list and queue."; + return false; + } + + commandList->Close(); + void* commandLists[] = { commandList }; + m_commandQueue->ExecuteCommandLists(1, commandLists); + m_lastError.clear(); + return true; +} + +bool D3D12HostDevice::SignalFrameCompletion(std::uint32_t frameIndex) { + ID3D12CommandQueue* commandQueue = GetCommandQueue(); + if (commandQueue == nullptr || + m_frameCompletionFence == nullptr || + frameIndex >= m_frameFenceValues.size()) { + return false; + } + + ++m_lastSubmittedFrameValue; + const HRESULT hr = commandQueue->Signal( + m_frameCompletionFence.Get(), + m_lastSubmittedFrameValue); + if (SUCCEEDED(hr)) { + m_frameFenceValues[frameIndex] = m_lastSubmittedFrameValue; + m_lastError.clear(); + } + return SUCCEEDED(hr); +} + +void D3D12HostDevice::WaitForFrame(std::uint32_t frameIndex) { + if (m_frameCompletionFence == nullptr || + m_frameCompletionEvent == nullptr || + frameIndex >= m_frameFenceValues.size()) { + return; + } + + const std::uint64_t fenceValue = m_frameFenceValues[frameIndex]; + if (fenceValue == 0u || + m_frameCompletionFence->GetCompletedValue() >= fenceValue) { + return; + } + + const HRESULT hr = m_frameCompletionFence->SetEventOnCompletion( + fenceValue, + m_frameCompletionEvent); + if (SUCCEEDED(hr)) { + WaitForSingleObject(m_frameCompletionEvent, INFINITE); + } +} + +void D3D12HostDevice::WaitForGpuIdle() { + ID3D12CommandQueue* commandQueue = GetCommandQueue(); + if (commandQueue == nullptr || + m_frameCompletionFence == nullptr || + m_frameCompletionEvent == nullptr) { + if (m_commandQueue != nullptr) { + m_commandQueue->WaitForIdle(); + } + return; + } + + ++m_lastSubmittedFrameValue; + const std::uint64_t fenceValue = m_lastSubmittedFrameValue; + const HRESULT signalHr = commandQueue->Signal( + m_frameCompletionFence.Get(), + fenceValue); + if (FAILED(signalHr)) { + return; + } + + if (m_frameCompletionFence->GetCompletedValue() >= fenceValue) { + return; + } + + const HRESULT waitHr = m_frameCompletionFence->SetEventOnCompletion( + fenceValue, + m_frameCompletionEvent); + if (SUCCEEDED(waitHr)) { + WaitForSingleObject(m_frameCompletionEvent, INFINITE); + } +} + +void D3D12HostDevice::ResetFrameTracking() { + m_frameFenceValues.fill(0u); +} + +ID3D12Device* D3D12HostDevice::GetDevice() const { + const D3D12Device* device = GetD3D12Device(); + return device != nullptr ? device->GetDevice() : nullptr; +} + +ID3D12CommandQueue* D3D12HostDevice::GetCommandQueue() const { + const D3D12CommandQueue* queue = GetD3D12CommandQueue(); + return queue != nullptr ? queue->GetCommandQueue() : nullptr; +} + +const std::string& D3D12HostDevice::GetLastError() const { + return m_lastError; +} + +RHIDevice* D3D12HostDevice::GetRHIDevice() const { + return m_device; +} + +RHICommandQueue* D3D12HostDevice::GetRHICommandQueue() const { + return m_commandQueue; +} + +RHICommandList* D3D12HostDevice::GetCommandList(std::uint32_t frameIndex) const { + return frameIndex < m_commandLists.size() + ? m_commandLists[frameIndex] + : nullptr; +} + +::XCEngine::Rendering::RenderContext D3D12HostDevice::GetRenderContext( + std::uint32_t frameIndex) const { + ::XCEngine::Rendering::RenderContext context = {}; + context.device = m_device; + context.commandList = GetCommandList(frameIndex); + context.commandQueue = m_commandQueue; + context.backendType = RHIType::D3D12; + return context; +} + +D3D12Device* D3D12HostDevice::GetD3D12Device() const { + return m_device != nullptr ? static_cast(m_device) : nullptr; +} + +D3D12CommandQueue* D3D12HostDevice::GetD3D12CommandQueue() const { + return m_commandQueue != nullptr ? static_cast(m_commandQueue) : nullptr; +} + +D3D12CommandList* D3D12HostDevice::GetD3D12CommandList(std::uint32_t frameIndex) const { + RHICommandList* commandList = GetCommandList(frameIndex); + return commandList != nullptr ? static_cast(commandList) : nullptr; +} + +bool D3D12HostDevice::InitializeFrameCompletionFence() { + ReleaseFrameCompletionFence(); + + ID3D12Device* device = GetDevice(); + if (device == nullptr) { + return false; + } + + const HRESULT hr = device->CreateFence( + 0u, + D3D12_FENCE_FLAG_NONE, + IID_PPV_ARGS(m_frameCompletionFence.ReleaseAndGetAddressOf())); + if (FAILED(hr)) { + return false; + } + + m_frameCompletionEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr); + if (m_frameCompletionEvent == nullptr) { + m_frameCompletionFence.Reset(); + return false; + } + + m_lastSubmittedFrameValue = 0u; + return true; +} + +void D3D12HostDevice::ReleaseFrameCompletionFence() { + if (m_frameCompletionEvent != nullptr) { + CloseHandle(m_frameCompletionEvent); + m_frameCompletionEvent = nullptr; + } + + m_frameCompletionFence.Reset(); + m_frameFenceValues.fill(0u); + m_lastSubmittedFrameValue = 0u; +} + +} // namespace XCEngine::UI::Editor::Host diff --git a/new_editor/app/Host/D3D12HostDevice.h b/new_editor/app/Host/D3D12HostDevice.h new file mode 100644 index 00000000..465eed0b --- /dev/null +++ b/new_editor/app/Host/D3D12HostDevice.h @@ -0,0 +1,64 @@ +#pragma once + +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +namespace XCEngine::UI::Editor::Host { + +class D3D12HostDevice { +public: + static constexpr std::uint32_t kFrameContextCount = 3u; + + bool Initialize(); + void Shutdown(); + + bool BeginFrame(std::uint32_t frameIndex); + bool SubmitFrame(std::uint32_t frameIndex); + bool SignalFrameCompletion(std::uint32_t frameIndex); + void WaitForFrame(std::uint32_t frameIndex); + void WaitForGpuIdle(); + void ResetFrameTracking(); + + ID3D12Device* GetDevice() const; + ID3D12CommandQueue* GetCommandQueue() const; + const std::string& GetLastError() const; + ::XCEngine::RHI::RHIDevice* GetRHIDevice() const; + ::XCEngine::RHI::RHICommandQueue* GetRHICommandQueue() const; + ::XCEngine::RHI::RHICommandList* GetCommandList(std::uint32_t frameIndex) const; + ::XCEngine::Rendering::RenderContext GetRenderContext(std::uint32_t frameIndex) const; + +private: + ::XCEngine::RHI::D3D12Device* GetD3D12Device() const; + ::XCEngine::RHI::D3D12CommandQueue* GetD3D12CommandQueue() const; + ::XCEngine::RHI::D3D12CommandList* GetD3D12CommandList(std::uint32_t frameIndex) const; + bool InitializeFrameCompletionFence(); + void ReleaseFrameCompletionFence(); + + ::XCEngine::RHI::RHIDevice* m_device = nullptr; + ::XCEngine::RHI::RHICommandQueue* m_commandQueue = nullptr; + std::array<::XCEngine::RHI::RHICommandList*, kFrameContextCount> m_commandLists = {}; + Microsoft::WRL::ComPtr m_frameCompletionFence = {}; + HANDLE m_frameCompletionEvent = nullptr; + std::array m_frameFenceValues = {}; + std::uint64_t m_lastSubmittedFrameValue = 0u; + std::string m_lastError = {}; +}; + +} // namespace XCEngine::UI::Editor::Host diff --git a/new_editor/app/Host/D3D12WindowInteropContext.cpp b/new_editor/app/Host/D3D12WindowInteropContext.cpp new file mode 100644 index 00000000..ce8f1137 --- /dev/null +++ b/new_editor/app/Host/D3D12WindowInteropContext.cpp @@ -0,0 +1,444 @@ +#include "D3D12WindowInteropContext.h" + +#include +#include + +#include +#include + +namespace XCEngine::UI::Editor::Host { + +namespace { + +std::string HrToInteropString(const char* operation, HRESULT hr) { + char buffer[128] = {}; + sprintf_s(buffer, "%s failed with hr=0x%08X.", operation, static_cast(hr)); + return buffer; +} + +D2D1_BITMAP_PROPERTIES1 BuildD2DBitmapProperties( + DXGI_FORMAT format, + D2D1_BITMAP_OPTIONS options) { + return D2D1::BitmapProperties1( + options, + D2D1::PixelFormat(format, D2D1_ALPHA_MODE_IGNORE), + 96.0f, + 96.0f); +} + +bool IsInteropTextureHandle(const ::XCEngine::UI::UITextureHandle& texture) { + return texture.kind == ::XCEngine::UI::UITextureHandleKind::ShaderResourceView && + texture.resourceHandle != 0u; +} + +void CollectInteropTextureHandles( + const ::XCEngine::UI::UIDrawData& drawData, + std::vector<::XCEngine::UI::UITextureHandle>& outTextures) { + outTextures.clear(); + std::unordered_set seenKeys = {}; + for (const ::XCEngine::UI::UIDrawList& drawList : drawData.GetDrawLists()) { + for (const ::XCEngine::UI::UIDrawCommand& command : drawList.GetCommands()) { + if (!IsInteropTextureHandle(command.texture) || + !seenKeys.insert(command.texture.resourceHandle).second) { + continue; + } + + outTextures.push_back(command.texture); + } + } +} + +} // namespace + +bool D3D12WindowInteropContext::Attach( + D3D12WindowRenderer& windowRenderer, + ID2D1Factory1& d2dFactory) { + if (m_windowRenderer != &windowRenderer) { + Detach(); + m_windowRenderer = &windowRenderer; + } + + m_d2dFactory = &d2dFactory; + if (!EnsureInterop()) { + return false; + } + + if (HasBackBufferTargets()) { + return true; + } + + return RebuildBackBufferTargets(); +} + +void D3D12WindowInteropContext::Detach() { + ReleaseInteropState(); + m_windowRenderer = nullptr; + m_d2dFactory = nullptr; + m_lastError.clear(); +} + +void D3D12WindowInteropContext::ReleaseBackBufferTargets() { + ClearSourceTextures(); + if (m_d2dDeviceContext != nullptr) { + m_d2dDeviceContext->SetTarget(nullptr); + } + if (m_d3d11DeviceContext != nullptr) { + m_d3d11DeviceContext->ClearState(); + } + m_backBufferTargets.clear(); + if (m_d2dDeviceContext != nullptr) { + D2D1_TAG firstTag = 0u; + D2D1_TAG secondTag = 0u; + m_d2dDeviceContext->Flush(&firstTag, &secondTag); + } + if (m_d3d11DeviceContext != nullptr) { + m_d3d11DeviceContext->Flush(); + } +} + +bool D3D12WindowInteropContext::RebuildBackBufferTargets() { + m_backBufferTargets.clear(); + if (m_windowRenderer == nullptr || m_d3d11On12Device == nullptr || m_d2dDeviceContext == nullptr) { + return false; + } + + const std::uint32_t backBufferCount = m_windowRenderer->GetBackBufferCount(); + m_backBufferTargets.resize(backBufferCount); + for (std::uint32_t index = 0u; index < backBufferCount; ++index) { + const ::XCEngine::RHI::D3D12Texture* backBufferTexture = + m_windowRenderer->GetBackBufferTexture(index); + if (backBufferTexture == nullptr || backBufferTexture->GetResource() == nullptr) { + m_lastError = "Failed to resolve a D3D12 swap chain back buffer."; + m_backBufferTargets.clear(); + return false; + } + + D3D11_RESOURCE_FLAGS resourceFlags = {}; + resourceFlags.BindFlags = D3D11_BIND_RENDER_TARGET; + HRESULT hr = m_d3d11On12Device->CreateWrappedResource( + backBufferTexture->GetResource(), + &resourceFlags, + D3D12_RESOURCE_STATE_RENDER_TARGET, + D3D12_RESOURCE_STATE_PRESENT, + IID_PPV_ARGS(m_backBufferTargets[index].wrappedResource.ReleaseAndGetAddressOf())); + if (FAILED(hr) || m_backBufferTargets[index].wrappedResource == nullptr) { + m_lastError = HrToInteropString("ID3D11On12Device::CreateWrappedResource(backbuffer)", hr); + m_backBufferTargets.clear(); + return false; + } + + Microsoft::WRL::ComPtr dxgiSurface = {}; + hr = m_backBufferTargets[index].wrappedResource.As(&dxgiSurface); + if (FAILED(hr) || dxgiSurface == nullptr) { + m_lastError = HrToInteropString("ID3D11Resource::QueryInterface(IDXGISurface)", hr); + m_backBufferTargets.clear(); + return false; + } + + const D2D1_BITMAP_PROPERTIES1 bitmapProperties = + BuildD2DBitmapProperties( + backBufferTexture->GetDesc().Format, + D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW); + hr = m_d2dDeviceContext->CreateBitmapFromDxgiSurface( + dxgiSurface.Get(), + &bitmapProperties, + m_backBufferTargets[index].targetBitmap.ReleaseAndGetAddressOf()); + if (FAILED(hr) || m_backBufferTargets[index].targetBitmap == nullptr) { + m_lastError = HrToInteropString( + "ID2D1DeviceContext::CreateBitmapFromDxgiSurface(backbuffer)", + hr); + m_backBufferTargets.clear(); + return false; + } + } + + m_lastError.clear(); + return true; +} + +bool D3D12WindowInteropContext::HasAttachedWindowRenderer() const { + return m_windowRenderer != nullptr && + m_d3d11On12Device != nullptr && + m_d2dDeviceContext != nullptr && + !m_backBufferTargets.empty(); +} + +bool D3D12WindowInteropContext::HasBackBufferTargets() const { + return !m_backBufferTargets.empty(); +} + +bool D3D12WindowInteropContext::PrepareSourceTextures( + const ::XCEngine::UI::UIDrawData& drawData) { + ClearSourceTextures(); + if (m_windowRenderer == nullptr || m_d3d11On12Device == nullptr || m_d2dDeviceContext == nullptr) { + return false; + } + + std::vector<::XCEngine::UI::UITextureHandle> textureHandles = {}; + CollectInteropTextureHandles(drawData, textureHandles); + m_activeSourceTextures.reserve(textureHandles.size()); + + for (const ::XCEngine::UI::UITextureHandle& textureHandle : textureHandles) { + auto* texture = + reinterpret_cast<::XCEngine::RHI::RHITexture*>(textureHandle.resourceHandle); + auto* nativeTexture = dynamic_cast<::XCEngine::RHI::D3D12Texture*>(texture); + if (nativeTexture == nullptr || nativeTexture->GetResource() == nullptr) { + m_lastError = "Failed to resolve a D3D12 source texture for UI composition."; + ClearSourceTextures(); + return false; + } + + D3D11_RESOURCE_FLAGS resourceFlags = {}; + resourceFlags.BindFlags = D3D11_BIND_SHADER_RESOURCE; + + SourceTextureResource resource = {}; + resource.key = textureHandle.resourceHandle; + HRESULT hr = m_d3d11On12Device->CreateWrappedResource( + nativeTexture->GetResource(), + &resourceFlags, + D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, + D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, + IID_PPV_ARGS(resource.wrappedResource.ReleaseAndGetAddressOf())); + if (FAILED(hr) || resource.wrappedResource == nullptr) { + m_lastError = HrToInteropString("ID3D11On12Device::CreateWrappedResource(source)", hr); + ClearSourceTextures(); + return false; + } + + Microsoft::WRL::ComPtr dxgiSurface = {}; + hr = resource.wrappedResource.As(&dxgiSurface); + if (FAILED(hr) || dxgiSurface == nullptr) { + m_lastError = HrToInteropString("ID3D11Resource::QueryInterface(IDXGISurface)", hr); + ClearSourceTextures(); + return false; + } + + const D2D1_BITMAP_PROPERTIES1 bitmapProperties = + BuildD2DBitmapProperties( + nativeTexture->GetDesc().Format, + D2D1_BITMAP_OPTIONS_NONE); + hr = m_d2dDeviceContext->CreateBitmapFromDxgiSurface( + dxgiSurface.Get(), + &bitmapProperties, + resource.bitmap.ReleaseAndGetAddressOf()); + if (FAILED(hr) || resource.bitmap == nullptr) { + m_lastError = HrToInteropString("ID2D1DeviceContext::CreateBitmapFromDxgiSurface(source)", hr); + ClearSourceTextures(); + return false; + } + + m_activeBitmaps.emplace(resource.key, resource.bitmap); + m_activeSourceTextures.push_back(std::move(resource)); + } + + m_lastError.clear(); + return true; +} + +void D3D12WindowInteropContext::ClearSourceTextures() { + m_activeBitmaps.clear(); + m_activeSourceTextures.clear(); +} + +bool D3D12WindowInteropContext::ResolveInteropBitmap( + const ::XCEngine::UI::UITextureHandle& texture, + Microsoft::WRL::ComPtr& outBitmap) const { + outBitmap.Reset(); + if (!IsInteropTextureHandle(texture)) { + return false; + } + + const auto found = m_activeBitmaps.find(texture.resourceHandle); + if (found == m_activeBitmaps.end() || found->second == nullptr) { + return false; + } + + outBitmap = found->second; + return true; +} + +D3D12WindowRenderer* D3D12WindowInteropContext::GetWindowRenderer() const { + return m_windowRenderer; +} + +ID3D11On12Device* D3D12WindowInteropContext::GetD3D11On12Device() const { + return m_d3d11On12Device.Get(); +} + +ID3D11DeviceContext* D3D12WindowInteropContext::GetD3D11DeviceContext() const { + return m_d3d11DeviceContext.Get(); +} + +ID2D1DeviceContext* D3D12WindowInteropContext::GetD2DDeviceContext() const { + return m_d2dDeviceContext.Get(); +} + +ID2D1SolidColorBrush* D3D12WindowInteropContext::GetInteropBrush() const { + return m_interopBrush.Get(); +} + +void D3D12WindowInteropContext::BuildAcquiredResources( + std::uint32_t backBufferIndex, + std::vector& outResources) const { + outResources.clear(); + + ID3D11Resource* backBufferResource = GetWrappedBackBufferResource(backBufferIndex); + if (backBufferResource != nullptr) { + outResources.push_back(backBufferResource); + } + + for (const SourceTextureResource& resource : m_activeSourceTextures) { + if (resource.wrappedResource != nullptr) { + outResources.push_back(resource.wrappedResource.Get()); + } + } +} + +ID3D11Resource* D3D12WindowInteropContext::GetWrappedBackBufferResource(std::uint32_t index) const { + return index < m_backBufferTargets.size() ? m_backBufferTargets[index].wrappedResource.Get() : nullptr; +} + +ID2D1Bitmap1* D3D12WindowInteropContext::GetBackBufferTargetBitmap(std::uint32_t index) const { + return index < m_backBufferTargets.size() ? m_backBufferTargets[index].targetBitmap.Get() : nullptr; +} + +std::uint32_t D3D12WindowInteropContext::GetCurrentBackBufferIndex() const { + return m_windowRenderer != nullptr && m_windowRenderer->GetSwapChain() != nullptr + ? m_windowRenderer->GetSwapChain()->GetCurrentBackBufferIndex() + : 0u; +} + +const std::string& D3D12WindowInteropContext::GetLastError() const { + return m_lastError; +} + +bool D3D12WindowInteropContext::EnsureInterop() { + if (m_windowRenderer == nullptr) { + m_lastError = "EnsureInterop requires an attached D3D12 window renderer."; + return false; + } + if (m_d2dFactory == nullptr) { + m_lastError = "EnsureInterop requires an initialized D2D factory."; + return false; + } + if (m_d3d11On12Device != nullptr && + m_d2dDeviceContext != nullptr && + m_interopBrush != nullptr) { + return true; + } + + ReleaseInteropState(); + + ID3D12Device* d3d12Device = m_windowRenderer->GetDevice(); + ID3D12CommandQueue* d3d12CommandQueue = m_windowRenderer->GetCommandQueue(); + if (d3d12Device == nullptr || d3d12CommandQueue == nullptr) { + m_lastError = "The attached D3D12 window renderer does not expose a native device/queue."; + return false; + } + + const std::array featureLevels = { + D3D_FEATURE_LEVEL_12_1, + D3D_FEATURE_LEVEL_12_0, + D3D_FEATURE_LEVEL_11_1, + D3D_FEATURE_LEVEL_11_0 + }; + const std::array commandQueues = { + reinterpret_cast(d3d12CommandQueue) + }; + + UINT createFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; +#ifdef _DEBUG + createFlags |= D3D11_CREATE_DEVICE_DEBUG; +#endif + + D3D_FEATURE_LEVEL actualFeatureLevel = D3D_FEATURE_LEVEL_11_0; + HRESULT hr = D3D11On12CreateDevice( + d3d12Device, + createFlags, + featureLevels.data(), + static_cast(featureLevels.size()), + commandQueues.data(), + static_cast(commandQueues.size()), + 0u, + m_d3d11Device.ReleaseAndGetAddressOf(), + m_d3d11DeviceContext.ReleaseAndGetAddressOf(), + &actualFeatureLevel); +#ifdef _DEBUG + if (FAILED(hr)) { + createFlags &= ~D3D11_CREATE_DEVICE_DEBUG; + hr = D3D11On12CreateDevice( + d3d12Device, + createFlags, + featureLevels.data(), + static_cast(featureLevels.size()), + commandQueues.data(), + static_cast(commandQueues.size()), + 0u, + m_d3d11Device.ReleaseAndGetAddressOf(), + m_d3d11DeviceContext.ReleaseAndGetAddressOf(), + &actualFeatureLevel); + } +#endif + if (FAILED(hr) || m_d3d11Device == nullptr || m_d3d11DeviceContext == nullptr) { + m_lastError = HrToInteropString("D3D11On12CreateDevice", hr); + ReleaseInteropState(); + return false; + } + + hr = m_d3d11Device.As(&m_d3d11On12Device); + if (FAILED(hr) || m_d3d11On12Device == nullptr) { + m_lastError = HrToInteropString("ID3D11Device::QueryInterface(ID3D11On12Device)", hr); + ReleaseInteropState(); + return false; + } + + Microsoft::WRL::ComPtr dxgiDevice = {}; + hr = m_d3d11Device.As(&dxgiDevice); + if (FAILED(hr) || dxgiDevice == nullptr) { + m_lastError = HrToInteropString("ID3D11Device::QueryInterface(IDXGIDevice)", hr); + ReleaseInteropState(); + return false; + } + + hr = m_d2dFactory->CreateDevice(dxgiDevice.Get(), m_d2dDevice.ReleaseAndGetAddressOf()); + if (FAILED(hr) || m_d2dDevice == nullptr) { + m_lastError = HrToInteropString("ID2D1Factory1::CreateDevice", hr); + ReleaseInteropState(); + return false; + } + + hr = m_d2dDevice->CreateDeviceContext( + D2D1_DEVICE_CONTEXT_OPTIONS_NONE, + m_d2dDeviceContext.ReleaseAndGetAddressOf()); + if (FAILED(hr) || m_d2dDeviceContext == nullptr) { + m_lastError = HrToInteropString("ID2D1Device::CreateDeviceContext", hr); + ReleaseInteropState(); + return false; + } + + hr = m_d2dDeviceContext->CreateSolidColorBrush( + D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f), + m_interopBrush.ReleaseAndGetAddressOf()); + if (FAILED(hr) || m_interopBrush == nullptr) { + m_lastError = HrToInteropString("ID2D1DeviceContext::CreateSolidColorBrush", hr); + ReleaseInteropState(); + return false; + } + + m_d2dDeviceContext->SetDpi(96.0f, 96.0f); + m_d2dDeviceContext->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE); + m_lastError.clear(); + return true; +} + +void D3D12WindowInteropContext::ReleaseInteropState() { + ReleaseBackBufferTargets(); + m_interopBrush.Reset(); + m_d2dDeviceContext.Reset(); + m_d2dDevice.Reset(); + m_d3d11On12Device.Reset(); + m_d3d11DeviceContext.Reset(); + m_d3d11Device.Reset(); +} + +} // namespace XCEngine::UI::Editor::Host diff --git a/new_editor/app/Host/D3D12WindowInteropContext.h b/new_editor/app/Host/D3D12WindowInteropContext.h new file mode 100644 index 00000000..7112b431 --- /dev/null +++ b/new_editor/app/Host/D3D12WindowInteropContext.h @@ -0,0 +1,79 @@ +#pragma once + +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#include "D3D12WindowRenderer.h" + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace XCEngine::UI::Editor::Host { + +class D3D12WindowInteropContext { +public: + bool Attach(D3D12WindowRenderer& windowRenderer, ID2D1Factory1& d2dFactory); + void Detach(); + void ReleaseBackBufferTargets(); + bool RebuildBackBufferTargets(); + bool HasAttachedWindowRenderer() const; + bool HasBackBufferTargets() const; + bool PrepareSourceTextures(const ::XCEngine::UI::UIDrawData& drawData); + void ClearSourceTextures(); + bool ResolveInteropBitmap( + const ::XCEngine::UI::UITextureHandle& texture, + Microsoft::WRL::ComPtr& outBitmap) const; + + D3D12WindowRenderer* GetWindowRenderer() const; + ID3D11On12Device* GetD3D11On12Device() const; + ID3D11DeviceContext* GetD3D11DeviceContext() const; + ID2D1DeviceContext* GetD2DDeviceContext() const; + ID2D1SolidColorBrush* GetInteropBrush() const; + void BuildAcquiredResources( + std::uint32_t backBufferIndex, + std::vector& outResources) const; + ID3D11Resource* GetWrappedBackBufferResource(std::uint32_t index) const; + ID2D1Bitmap1* GetBackBufferTargetBitmap(std::uint32_t index) const; + std::uint32_t GetCurrentBackBufferIndex() const; + const std::string& GetLastError() const; + +private: + struct BackBufferTarget { + Microsoft::WRL::ComPtr wrappedResource = {}; + Microsoft::WRL::ComPtr targetBitmap = {}; + }; + + struct SourceTextureResource { + std::uintptr_t key = 0u; + Microsoft::WRL::ComPtr wrappedResource = {}; + Microsoft::WRL::ComPtr bitmap = {}; + }; + + bool EnsureInterop(); + void ReleaseInteropState(); + + D3D12WindowRenderer* m_windowRenderer = nullptr; + ID2D1Factory1* m_d2dFactory = nullptr; + Microsoft::WRL::ComPtr m_d3d11Device = {}; + Microsoft::WRL::ComPtr m_d3d11DeviceContext = {}; + Microsoft::WRL::ComPtr m_d3d11On12Device = {}; + Microsoft::WRL::ComPtr m_d2dDevice = {}; + Microsoft::WRL::ComPtr m_d2dDeviceContext = {}; + Microsoft::WRL::ComPtr m_interopBrush = {}; + std::vector m_backBufferTargets = {}; + std::vector m_activeSourceTextures = {}; + std::unordered_map> m_activeBitmaps = {}; + std::string m_lastError = {}; +}; + +} // namespace XCEngine::UI::Editor::Host diff --git a/new_editor/app/Host/D3D12WindowRenderLoop.cpp b/new_editor/app/Host/D3D12WindowRenderLoop.cpp index 6b23dc4a..04e09832 100644 --- a/new_editor/app/Host/D3D12WindowRenderLoop.cpp +++ b/new_editor/app/Host/D3D12WindowRenderLoop.cpp @@ -56,7 +56,10 @@ D3D12WindowRenderLoopResizeResult D3D12WindowRenderLoop::ApplyResize(UINT width, } m_uiRenderer->Resize(width, height); - m_uiRenderer->DetachWindowRenderer(); + const bool hadViewportSurfacePresentation = m_uiRenderer->HasAttachedWindowRenderer(); + if (hadViewportSurfacePresentation) { + m_uiRenderer->ReleaseWindowRendererBackBufferTargets(); + } const bool resizedWindowRenderer = m_windowRenderer->Resize(static_cast(width), static_cast(height)); @@ -68,6 +71,28 @@ D3D12WindowRenderLoopResizeResult D3D12WindowRenderLoop::ApplyResize(UINT width, } if (!resizedWindowRenderer) { + if (hadViewportSurfacePresentation) { + result.hasViewportSurfacePresentation = + m_uiRenderer->RebuildWindowRendererBackBufferTargets(); + if (!result.hasViewportSurfacePresentation) { + const D3D12WindowRenderLoopAttachResult attachResult = + Attach(*m_uiRenderer, *m_windowRenderer); + result.hasViewportSurfacePresentation = attachResult.hasViewportSurfacePresentation; + result.interopWarning = attachResult.interopWarning; + } + } + return result; + } + + if (hadViewportSurfacePresentation) { + result.hasViewportSurfacePresentation = + m_uiRenderer->RebuildWindowRendererBackBufferTargets(); + if (!result.hasViewportSurfacePresentation) { + const D3D12WindowRenderLoopAttachResult attachResult = + Attach(*m_uiRenderer, *m_windowRenderer); + result.hasViewportSurfacePresentation = attachResult.hasViewportSurfacePresentation; + result.interopWarning = attachResult.interopWarning; + } return result; } diff --git a/new_editor/app/Host/D3D12WindowRenderer.cpp b/new_editor/app/Host/D3D12WindowRenderer.cpp index a2b584e2..24874056 100644 --- a/new_editor/app/Host/D3D12WindowRenderer.cpp +++ b/new_editor/app/Host/D3D12WindowRenderer.cpp @@ -1,30 +1,10 @@ #include "D3D12WindowRenderer.h" -#include - -#include - namespace XCEngine::UI::Editor::Host { -using ::XCEngine::RHI::CommandListDesc; -using ::XCEngine::RHI::CommandQueueDesc; -using ::XCEngine::RHI::D3D12CommandList; -using ::XCEngine::RHI::D3D12CommandQueue; -using ::XCEngine::RHI::D3D12Device; -using ::XCEngine::RHI::D3D12SwapChain; -using ::XCEngine::RHI::D3D12Texture; -using ::XCEngine::RHI::Format; -using ::XCEngine::RHI::ResourceStates; -using ::XCEngine::RHI::ResourceViewDesc; -using ::XCEngine::RHI::ResourceViewDimension; -using ::XCEngine::RHI::RHICommandList; -using ::XCEngine::RHI::RHICommandQueue; using ::XCEngine::RHI::RHIDevice; -using ::XCEngine::RHI::RHIDeviceDesc; -using ::XCEngine::RHI::RHIFactory; using ::XCEngine::RHI::RHISwapChain; -using ::XCEngine::RHI::RHIType; -using ::XCEngine::RHI::SwapChainDesc; +using ::XCEngine::RHI::D3D12Texture; bool D3D12WindowRenderer::Initialize(HWND hwnd, int width, int height) { Shutdown(); @@ -34,211 +14,55 @@ bool D3D12WindowRenderer::Initialize(HWND hwnd, int width, int height) { return false; } - m_hwnd = hwnd; - m_width = width; - m_height = height; + if (!m_hostDevice.Initialize()) { + m_lastError = m_hostDevice.GetLastError(); + return false; + } - m_device = RHIFactory::CreateRHIDevice(RHIType::D3D12); - if (m_device == nullptr) { - m_lastError = "Failed to create the D3D12 RHI device."; + if (!m_presenter.Initialize(m_hostDevice, hwnd, width, height)) { + m_lastError = m_presenter.GetLastError(); Shutdown(); return false; } - RHIDeviceDesc deviceDesc = {}; -#ifdef _DEBUG - deviceDesc.enableDebugLayer = true; - deviceDesc.enableGPUValidation = true; -#endif - if (!m_device->Initialize(deviceDesc)) { - m_lastError = "Failed to initialize the D3D12 RHI device."; - Shutdown(); - return false; - } - - CommandQueueDesc queueDesc = {}; - queueDesc.queueType = static_cast(::XCEngine::RHI::CommandQueueType::Direct); - m_commandQueue = m_device->CreateCommandQueue(queueDesc); - if (m_commandQueue == nullptr || GetD3D12CommandQueue() == nullptr) { - m_lastError = "Failed to create the D3D12 command queue."; - Shutdown(); - return false; - } - - CommandListDesc commandListDesc = {}; - commandListDesc.commandListType = static_cast(::XCEngine::RHI::CommandQueueType::Direct); - for (std::uint32_t commandListIndex = 0; commandListIndex < kSwapChainBufferCount; ++commandListIndex) { - m_commandLists[commandListIndex] = m_device->CreateCommandList(commandListDesc); - if (m_commandLists[commandListIndex] == nullptr) { - m_lastError = "Failed to create the D3D12 command list."; - Shutdown(); - return false; - } - - m_activeBackBufferIndex = commandListIndex; - if (GetD3D12CommandList() == nullptr) { - m_lastError = "Failed to resolve the D3D12 command list."; - Shutdown(); - return false; - } - - m_commandLists[commandListIndex]->Close(); - } - m_activeBackBufferIndex = 0u; - - SwapChainDesc swapChainDesc = {}; - swapChainDesc.windowHandle = hwnd; - swapChainDesc.width = static_cast(width); - swapChainDesc.height = static_cast(height); - swapChainDesc.bufferCount = kSwapChainBufferCount; - m_swapChain = m_device->CreateSwapChain(swapChainDesc, m_commandQueue); - if (m_swapChain == nullptr || GetD3D12SwapChain() == nullptr) { - m_lastError = "Failed to create the D3D12 swap chain."; - Shutdown(); - return false; - } - - if (!RecreateBackBufferViews()) { - m_lastError = "Failed to create swap chain back buffer views."; - Shutdown(); - return false; - } - - if (!InitializeFrameCompletionFence()) { - m_lastError = "Failed to create the host frame completion fence."; - Shutdown(); - return false; - } - - m_backBufferFenceValues.fill(0u); m_lastError.clear(); return true; } void D3D12WindowRenderer::Shutdown() { - WaitForGpuIdle(); - ReleaseFrameCompletionFence(); - ReleaseBackBufferViews(); - - if (m_swapChain != nullptr) { - m_swapChain->Shutdown(); - delete m_swapChain; - m_swapChain = nullptr; - } - - for (auto*& commandList : m_commandLists) { - if (commandList != nullptr) { - commandList->Shutdown(); - delete commandList; - commandList = nullptr; - } - } - - if (m_commandQueue != nullptr) { - m_commandQueue->Shutdown(); - delete m_commandQueue; - m_commandQueue = nullptr; - } - - if (m_device != nullptr) { - m_device->Shutdown(); - delete m_device; - m_device = nullptr; - } - - m_hwnd = nullptr; - m_width = 0; - m_height = 0; + m_presenter.Shutdown(); + m_hostDevice.Shutdown(); m_activeBackBufferIndex = 0u; m_lastError.clear(); } bool D3D12WindowRenderer::Resize(int width, int height) { - if (width <= 0 || height <= 0 || m_swapChain == nullptr) { + if (!m_presenter.Resize(width, height)) { + m_lastError = m_presenter.GetLastError(); return false; } - if (m_width == width && m_height == height) { - m_lastError.clear(); + m_activeBackBufferIndex = m_presenter.GetCurrentBackBufferIndex(); + m_lastError = m_presenter.GetLastError(); + if (!m_lastError.empty()) { return true; } - - WaitForGpuIdle(); - ReleaseBackBufferCommandReferences(); - ReleaseBackBufferViews(); - D3D12SwapChain* d3d12SwapChain = GetD3D12SwapChain(); - if (d3d12SwapChain == nullptr) { - m_lastError = "Resize could not resolve the native D3D12 swap chain."; - return false; - } - - d3d12SwapChain->Resize( - static_cast(width), - static_cast(height)); - const HRESULT resizeHr = d3d12SwapChain->GetLastResizeResult(); - if (FAILED(resizeHr)) { - std::ostringstream error = {}; - error << "ResizeBuffers failed. requested=" - << width - << 'x' - << height - << " hr=0x" - << std::uppercase - << std::hex - << static_cast(resizeHr); - m_lastError = error.str(); - return false; - } - - const D3D12Texture* backBufferTexture = GetBackBufferTexture(0u); - if (backBufferTexture == nullptr) { - m_lastError = "Resize failed to restore swap chain back buffers after ResizeBuffers."; - return false; - } - - m_width = static_cast(backBufferTexture->GetWidth()); - m_height = static_cast(backBufferTexture->GetHeight()); - m_activeBackBufferIndex = m_swapChain->GetCurrentBackBufferIndex(); - m_backBufferFenceValues.fill(0u); - if (!RecreateBackBufferViews()) { - m_lastError = "Resize failed to recreate swap chain back buffer views."; - return false; - } - - if (m_width != width || m_height != height) { - std::ostringstream warning = {}; - warning << "Resize applied the current swap chain size. requested=" - << width - << 'x' - << height - << " actual=" - << m_width - << 'x' - << m_height; - m_lastError = warning.str(); - return true; - } - m_lastError.clear(); return true; } bool D3D12WindowRenderer::BeginFrame() { - if (m_swapChain == nullptr) { + if (m_presenter.GetSwapChain() == nullptr) { m_lastError = "BeginFrame requires an initialized swap chain."; return false; } - m_activeBackBufferIndex = m_swapChain->GetCurrentBackBufferIndex(); - WaitForBackBufferFrame(m_activeBackBufferIndex); - ::XCEngine::RHI::RHICommandList* commandList = GetCurrentCommandList(); - D3D12CommandList* d3d12CommandList = GetD3D12CommandList(); - if (commandList == nullptr || d3d12CommandList == nullptr) { - m_lastError = "BeginFrame could not resolve the active command list."; + m_activeBackBufferIndex = m_presenter.GetCurrentBackBufferIndex(); + if (!m_hostDevice.BeginFrame(m_activeBackBufferIndex)) { + m_lastError = m_hostDevice.GetLastError(); return false; } - commandList->Reset(); m_lastError.clear(); return true; } @@ -250,88 +74,54 @@ bool D3D12WindowRenderer::PreparePresentSurface() { return false; } - if (m_swapChain == nullptr) { - m_lastError = "PreparePresentSurface requires an initialized swap chain."; - return false; - } - - const std::uint32_t backBufferIndex = m_swapChain->GetCurrentBackBufferIndex(); - if (backBufferIndex >= m_backBufferViews.size() || - m_backBufferViews[backBufferIndex] == nullptr) { - std::ostringstream error = {}; - error << "PreparePresentSurface could not find the current swap chain RTV. index=" - << backBufferIndex - << " rtvCount=" - << m_backBufferViews.size(); - m_lastError = error.str(); - return false; - } - - renderContext.commandList->TransitionBarrier( - m_backBufferViews[backBufferIndex], - ::XCEngine::RHI::ResourceStates::Present, - ::XCEngine::RHI::ResourceStates::RenderTarget); - m_lastError.clear(); - return true; + const bool prepared = m_presenter.PreparePresentSurface(renderContext); + m_lastError = prepared ? std::string() : m_presenter.GetLastError(); + return prepared; } bool D3D12WindowRenderer::SubmitFrame(bool presentSwapChain) { - ::XCEngine::RHI::RHICommandList* commandList = GetCurrentCommandList(); - if (commandList == nullptr || m_commandQueue == nullptr) { - m_lastError = "SubmitFrame requires an initialized command list and queue."; - return false; - } - - if (presentSwapChain && m_swapChain == nullptr) { + if (presentSwapChain && m_presenter.GetSwapChain() == nullptr) { m_lastError = "SubmitFrame requested present without a swap chain."; return false; } - commandList->Close(); - void* commandLists[] = { commandList }; - m_commandQueue->ExecuteCommandLists(1, commandLists); + if (!m_hostDevice.SubmitFrame(m_activeBackBufferIndex)) { + m_lastError = m_hostDevice.GetLastError(); + return false; + } + if (presentSwapChain) { - m_swapChain->Present(1, 0); + if (!m_presenter.PresentFrame()) { + m_lastError = m_presenter.GetLastError(); + return false; + } } m_lastError.clear(); return true; } bool D3D12WindowRenderer::SignalFrameCompletion() { - ID3D12CommandQueue* commandQueue = GetCommandQueue(); - if (commandQueue == nullptr || m_frameCompletionFence == nullptr) { + if (!m_hostDevice.SignalFrameCompletion(m_activeBackBufferIndex)) { + m_lastError = m_hostDevice.GetLastError(); return false; } - ++m_lastSubmittedFrameValue; - const HRESULT hr = commandQueue->Signal( - m_frameCompletionFence.Get(), - m_lastSubmittedFrameValue); - if (SUCCEEDED(hr) && m_activeBackBufferIndex < m_backBufferFenceValues.size()) { - m_backBufferFenceValues[m_activeBackBufferIndex] = m_lastSubmittedFrameValue; - } - return SUCCEEDED(hr); -} - -bool D3D12WindowRenderer::PresentFrame() { - if (m_swapChain == nullptr) { - m_lastError = "PresentFrame requires an initialized swap chain."; - return false; - } - - m_swapChain->Present(1, 0); m_lastError.clear(); return true; } +bool D3D12WindowRenderer::PresentFrame() { + const bool presented = m_presenter.PresentFrame(); + m_lastError = presented ? std::string() : m_presenter.GetLastError(); + return presented; +} + ID3D12Device* D3D12WindowRenderer::GetDevice() const { - const D3D12Device* device = GetD3D12Device(); - return device != nullptr ? device->GetDevice() : nullptr; + return m_hostDevice.GetDevice(); } ID3D12CommandQueue* D3D12WindowRenderer::GetCommandQueue() const { - const D3D12CommandQueue* queue = GetD3D12CommandQueue(); - return queue != nullptr ? queue->GetCommandQueue() : nullptr; + return m_hostDevice.GetCommandQueue(); } const std::string& D3D12WindowRenderer::GetLastError() const { @@ -339,235 +129,31 @@ const std::string& D3D12WindowRenderer::GetLastError() const { } RHIDevice* D3D12WindowRenderer::GetRHIDevice() const { - return m_device; + return m_hostDevice.GetRHIDevice(); } RHISwapChain* D3D12WindowRenderer::GetSwapChain() const { - return m_swapChain; + return m_presenter.GetSwapChain(); } const ::XCEngine::Rendering::RenderSurface* D3D12WindowRenderer::GetCurrentRenderSurface() const { - if (m_swapChain == nullptr) { - return nullptr; - } - - const std::uint32_t backBufferIndex = m_swapChain->GetCurrentBackBufferIndex(); - if (backBufferIndex >= m_backBufferSurfaces.size()) { - return nullptr; - } - - return &m_backBufferSurfaces[backBufferIndex]; + return m_presenter.GetCurrentRenderSurface(); } const D3D12Texture* D3D12WindowRenderer::GetCurrentBackBufferTexture() const { - if (m_swapChain == nullptr) { - return nullptr; - } - - return GetBackBufferTexture(m_swapChain->GetCurrentBackBufferIndex()); + return m_presenter.GetCurrentBackBufferTexture(); } const D3D12Texture* D3D12WindowRenderer::GetBackBufferTexture(std::uint32_t index) const { - const D3D12SwapChain* d3d12SwapChain = GetD3D12SwapChain(); - if (d3d12SwapChain == nullptr) { - return nullptr; - } - - return d3d12SwapChain->TryGetBackBuffer(index); + return m_presenter.GetBackBufferTexture(index); } std::uint32_t D3D12WindowRenderer::GetBackBufferCount() const { - return kSwapChainBufferCount; + return m_presenter.GetBackBufferCount(); } ::XCEngine::Rendering::RenderContext D3D12WindowRenderer::GetRenderContext() const { - ::XCEngine::Rendering::RenderContext context = {}; - context.device = m_device; - context.commandList = GetCurrentCommandList(); - context.commandQueue = m_commandQueue; - context.backendType = RHIType::D3D12; - return context; -} - -D3D12Device* D3D12WindowRenderer::GetD3D12Device() const { - return m_device != nullptr ? static_cast(m_device) : nullptr; -} - -D3D12CommandQueue* D3D12WindowRenderer::GetD3D12CommandQueue() const { - return m_commandQueue != nullptr ? static_cast(m_commandQueue) : nullptr; -} - -D3D12CommandList* D3D12WindowRenderer::GetD3D12CommandList() const { - ::XCEngine::RHI::RHICommandList* commandList = GetCurrentCommandList(); - return commandList != nullptr ? static_cast(commandList) : nullptr; -} - -D3D12SwapChain* D3D12WindowRenderer::GetD3D12SwapChain() const { - return m_swapChain != nullptr ? static_cast(m_swapChain) : nullptr; -} - -::XCEngine::RHI::RHICommandList* D3D12WindowRenderer::GetCurrentCommandList() const { - return m_activeBackBufferIndex < m_commandLists.size() - ? m_commandLists[m_activeBackBufferIndex] - : nullptr; -} - -bool D3D12WindowRenderer::InitializeFrameCompletionFence() { - ReleaseFrameCompletionFence(); - - ID3D12Device* device = GetDevice(); - if (device == nullptr) { - return false; - } - - const HRESULT hr = device->CreateFence( - 0u, - D3D12_FENCE_FLAG_NONE, - IID_PPV_ARGS(m_frameCompletionFence.ReleaseAndGetAddressOf())); - if (FAILED(hr)) { - return false; - } - - m_frameCompletionEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr); - if (m_frameCompletionEvent == nullptr) { - m_frameCompletionFence.Reset(); - return false; - } - - m_lastSubmittedFrameValue = 0u; - return true; -} - -void D3D12WindowRenderer::ReleaseFrameCompletionFence() { - if (m_frameCompletionEvent != nullptr) { - CloseHandle(m_frameCompletionEvent); - m_frameCompletionEvent = nullptr; - } - - m_frameCompletionFence.Reset(); - m_backBufferFenceValues.fill(0u); - m_activeBackBufferIndex = 0u; - m_lastSubmittedFrameValue = 0u; -} - -void D3D12WindowRenderer::WaitForBackBufferFrame(std::uint32_t backBufferIndex) { - if (m_frameCompletionFence == nullptr || - m_frameCompletionEvent == nullptr || - backBufferIndex >= m_backBufferFenceValues.size()) { - return; - } - - const std::uint64_t fenceValue = m_backBufferFenceValues[backBufferIndex]; - if (fenceValue == 0u || - m_frameCompletionFence->GetCompletedValue() >= fenceValue) { - return; - } - - const HRESULT hr = m_frameCompletionFence->SetEventOnCompletion( - fenceValue, - m_frameCompletionEvent); - if (SUCCEEDED(hr)) { - WaitForSingleObject(m_frameCompletionEvent, INFINITE); - } -} - -void D3D12WindowRenderer::WaitForGpuIdle() { - ID3D12CommandQueue* commandQueue = GetCommandQueue(); - if (commandQueue == nullptr || - m_frameCompletionFence == nullptr || - m_frameCompletionEvent == nullptr) { - if (m_commandQueue != nullptr) { - m_commandQueue->WaitForIdle(); - } - return; - } - - ++m_lastSubmittedFrameValue; - const std::uint64_t fenceValue = m_lastSubmittedFrameValue; - const HRESULT signalHr = commandQueue->Signal( - m_frameCompletionFence.Get(), - fenceValue); - if (FAILED(signalHr)) { - return; - } - - if (m_frameCompletionFence->GetCompletedValue() >= fenceValue) { - return; - } - - const HRESULT waitHr = m_frameCompletionFence->SetEventOnCompletion( - fenceValue, - m_frameCompletionEvent); - if (SUCCEEDED(waitHr)) { - WaitForSingleObject(m_frameCompletionEvent, INFINITE); - } -} - -void D3D12WindowRenderer::ReleaseBackBufferCommandReferences() { - for (auto* commandList : m_commandLists) { - if (commandList == nullptr) { - continue; - } - - commandList->Reset(); - commandList->Close(); - } -} - -void D3D12WindowRenderer::ReleaseBackBufferViews() { - for (auto* view : m_backBufferViews) { - if (view != nullptr) { - view->Shutdown(); - delete view; - } - } - m_backBufferViews.clear(); - m_backBufferSurfaces.clear(); -} - -bool D3D12WindowRenderer::RecreateBackBufferViews() { - D3D12SwapChain* d3d12SwapChain = GetD3D12SwapChain(); - if (m_device == nullptr || d3d12SwapChain == nullptr) { - return false; - } - - m_backBufferViews.resize(kSwapChainBufferCount, nullptr); - m_backBufferSurfaces.resize(kSwapChainBufferCount); - - ResourceViewDesc viewDesc = {}; - viewDesc.format = static_cast(Format::R8G8B8A8_UNorm); - viewDesc.dimension = ResourceViewDimension::Texture2D; - - for (std::uint32_t backBufferIndex = 0; backBufferIndex < kSwapChainBufferCount; ++backBufferIndex) { - D3D12Texture* backBufferTexture = d3d12SwapChain->TryGetBackBuffer(backBufferIndex); - if (backBufferTexture == nullptr) { - ReleaseBackBufferViews(); - m_lastError = "RecreateBackBufferViews could not resolve swap chain back buffer " + - std::to_string(backBufferIndex) + "."; - return false; - } - - m_backBufferViews[backBufferIndex] = m_device->CreateRenderTargetView( - backBufferTexture, - viewDesc); - if (m_backBufferViews[backBufferIndex] == nullptr) { - ReleaseBackBufferViews(); - m_lastError = "RecreateBackBufferViews failed to create RTV for swap chain back buffer " + - std::to_string(backBufferIndex) + "."; - return false; - } - - ::XCEngine::Rendering::RenderSurface& surface = m_backBufferSurfaces[backBufferIndex]; - surface = ::XCEngine::Rendering::RenderSurface( - static_cast(m_width), - static_cast(m_height)); - surface.SetColorAttachment(m_backBufferViews[backBufferIndex]); - surface.SetAutoTransitionEnabled(false); - surface.SetColorStateBefore(ResourceStates::RenderTarget); - surface.SetColorStateAfter(ResourceStates::RenderTarget); - } - - return true; + return m_hostDevice.GetRenderContext(m_activeBackBufferIndex); } } // namespace XCEngine::UI::Editor::Host diff --git a/new_editor/app/Host/D3D12WindowRenderer.h b/new_editor/app/Host/D3D12WindowRenderer.h index 2df79a3b..49921442 100644 --- a/new_editor/app/Host/D3D12WindowRenderer.h +++ b/new_editor/app/Host/D3D12WindowRenderer.h @@ -4,34 +4,24 @@ #define NOMINMAX #endif +#include "D3D12HostDevice.h" +#include "D3D12WindowSwapChainPresenter.h" + #include #include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include #include -#include #include -#include -#include #include -#include namespace XCEngine::UI::Editor::Host { class D3D12WindowRenderer { public: - static constexpr std::uint32_t kSwapChainBufferCount = 3; + static constexpr std::uint32_t kSwapChainBufferCount = + D3D12WindowSwapChainPresenter::kSwapChainBufferCount; bool Initialize(HWND hwnd, int width, int height); void Shutdown(); @@ -54,34 +44,9 @@ public: ::XCEngine::Rendering::RenderContext GetRenderContext() const; private: - ::XCEngine::RHI::D3D12Device* GetD3D12Device() const; - ::XCEngine::RHI::D3D12CommandQueue* GetD3D12CommandQueue() const; - ::XCEngine::RHI::D3D12CommandList* GetD3D12CommandList() const; - ::XCEngine::RHI::D3D12SwapChain* GetD3D12SwapChain() const; - ::XCEngine::RHI::RHICommandList* GetCurrentCommandList() const; - bool InitializeFrameCompletionFence(); - void ReleaseFrameCompletionFence(); - void WaitForBackBufferFrame(std::uint32_t backBufferIndex); - void WaitForGpuIdle(); - void ReleaseBackBufferCommandReferences(); - void ReleaseBackBufferViews(); - bool RecreateBackBufferViews(); - - HWND m_hwnd = nullptr; - int m_width = 0; - int m_height = 0; - - ::XCEngine::RHI::RHIDevice* m_device = nullptr; - ::XCEngine::RHI::RHICommandQueue* m_commandQueue = nullptr; - std::array<::XCEngine::RHI::RHICommandList*, kSwapChainBufferCount> m_commandLists = {}; - ::XCEngine::RHI::RHISwapChain* m_swapChain = nullptr; - std::vector<::XCEngine::RHI::RHIResourceView*> m_backBufferViews = {}; - std::vector<::XCEngine::Rendering::RenderSurface> m_backBufferSurfaces = {}; - Microsoft::WRL::ComPtr m_frameCompletionFence = {}; - HANDLE m_frameCompletionEvent = nullptr; - std::array m_backBufferFenceValues = {}; + D3D12HostDevice m_hostDevice = {}; + D3D12WindowSwapChainPresenter m_presenter = {}; std::uint32_t m_activeBackBufferIndex = 0u; - std::uint64_t m_lastSubmittedFrameValue = 0; std::string m_lastError = {}; }; diff --git a/new_editor/app/Host/D3D12WindowSwapChainPresenter.cpp b/new_editor/app/Host/D3D12WindowSwapChainPresenter.cpp new file mode 100644 index 00000000..39db7cf8 --- /dev/null +++ b/new_editor/app/Host/D3D12WindowSwapChainPresenter.cpp @@ -0,0 +1,371 @@ +#include "D3D12WindowSwapChainPresenter.h" + +#include + +namespace XCEngine::UI::Editor::Host { + +using ::XCEngine::RHI::D3D12SwapChain; +using ::XCEngine::RHI::D3D12Texture; +using ::XCEngine::RHI::Format; +using ::XCEngine::RHI::ResourceStates; +using ::XCEngine::RHI::ResourceViewDesc; +using ::XCEngine::RHI::ResourceViewDimension; +using ::XCEngine::RHI::RHISwapChain; +using ::XCEngine::RHI::SwapChainDesc; + +bool D3D12WindowSwapChainPresenter::Initialize( + D3D12HostDevice& hostDevice, + 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 (hostDevice.GetRHIDevice() == nullptr || hostDevice.GetRHICommandQueue() == nullptr) { + m_lastError = "Initialize requires an initialized host D3D12 device."; + return false; + } + + m_hwnd = hwnd; + m_width = width; + m_height = height; + m_hostDevice = &hostDevice; + + if (!CreateSwapChain(width, height)) { + Shutdown(); + return false; + } + + m_lastError.clear(); + return true; +} + +bool D3D12WindowSwapChainPresenter::CreateSwapChain(int width, int height) { + if (m_hostDevice == nullptr || m_hostDevice->GetRHIDevice() == nullptr || m_hostDevice->GetRHICommandQueue() == nullptr) { + m_lastError = "CreateSwapChain requires an initialized host D3D12 device."; + return false; + } + + SwapChainDesc swapChainDesc = {}; + swapChainDesc.windowHandle = m_hwnd; + swapChainDesc.width = static_cast(width); + swapChainDesc.height = static_cast(height); + swapChainDesc.bufferCount = kSwapChainBufferCount; + m_swapChain = m_hostDevice->GetRHIDevice()->CreateSwapChain( + swapChainDesc, + m_hostDevice->GetRHICommandQueue()); + if (m_swapChain == nullptr || GetD3D12SwapChain() == nullptr) { + m_lastError = "Failed to create the D3D12 swap chain."; + return false; + } + + if (!RecreateBackBufferViews()) { + m_lastError = "Failed to create swap chain back buffer views."; + return false; + } + + m_width = width; + m_height = height; + return true; +} + +void D3D12WindowSwapChainPresenter::DestroySwapChain() { + ReleaseBackBufferViews(); + + if (m_swapChain != nullptr) { + m_swapChain->Shutdown(); + delete m_swapChain; + m_swapChain = nullptr; + } +} + +bool D3D12WindowSwapChainPresenter::RecreateSwapChain(int width, int height) { + DestroySwapChain(); + m_hostDevice->ResetFrameTracking(); + return CreateSwapChain(width, height); +} + +void D3D12WindowSwapChainPresenter::Shutdown() { + if (m_hostDevice != nullptr) { + m_hostDevice->WaitForGpuIdle(); + } + + DestroySwapChain(); + + m_hwnd = nullptr; + m_width = 0; + m_height = 0; + m_hostDevice = nullptr; + m_lastError.clear(); +} + +bool D3D12WindowSwapChainPresenter::Resize(int width, int height) { + if (width <= 0 || height <= 0 || m_swapChain == nullptr || m_hostDevice == nullptr) { + return false; + } + + if (m_width == width && m_height == height) { + m_lastError.clear(); + return true; + } + + m_hostDevice->WaitForGpuIdle(); + ReleaseBackBufferCommandReferences(); + ReleaseBackBufferViews(); + + D3D12SwapChain* d3d12SwapChain = GetD3D12SwapChain(); + if (d3d12SwapChain == nullptr) { + m_lastError = "Resize could not resolve the native D3D12 swap chain."; + return false; + } + + d3d12SwapChain->Resize( + static_cast(width), + static_cast(height)); + const HRESULT resizeHr = d3d12SwapChain->GetLastResizeResult(); + if (FAILED(resizeHr)) { + if (RecreateSwapChain(width, height)) { + m_lastError.clear(); + return true; + } + + std::ostringstream error = {}; + error << "ResizeBuffers failed. requested=" + << width + << 'x' + << height + << " hr=0x" + << std::uppercase + << std::hex + << static_cast(resizeHr); + m_lastError = error.str(); + return false; + } + + const D3D12Texture* backBufferTexture = GetBackBufferTexture(0u); + if (backBufferTexture == nullptr) { + if (RecreateSwapChain(width, height)) { + m_lastError.clear(); + return true; + } + + m_lastError = "Resize failed to restore swap chain back buffers after ResizeBuffers."; + return false; + } + + m_width = static_cast(backBufferTexture->GetWidth()); + m_height = static_cast(backBufferTexture->GetHeight()); + m_hostDevice->ResetFrameTracking(); + if (!RecreateBackBufferViews()) { + if (RecreateSwapChain(width, height)) { + m_lastError.clear(); + return true; + } + + m_lastError = "Resize failed to recreate swap chain back buffer views."; + return false; + } + + if (m_width != width || m_height != height) { + if (RecreateSwapChain(width, height)) { + m_lastError.clear(); + return true; + } + + std::ostringstream error = {}; + error << "Resize ended with an unexpected swap chain size. requested=" + << width + << 'x' + << height + << " actual=" + << m_width + << 'x' + << m_height; + m_lastError = error.str(); + return false; + } + + m_lastError.clear(); + return true; +} + +bool D3D12WindowSwapChainPresenter::PreparePresentSurface( + const ::XCEngine::Rendering::RenderContext& renderContext) { + if (!renderContext.IsValid() || renderContext.commandList == nullptr) { + m_lastError = "PreparePresentSurface requires a valid render context."; + return false; + } + + if (m_swapChain == nullptr) { + m_lastError = "PreparePresentSurface requires an initialized swap chain."; + return false; + } + + const std::uint32_t backBufferIndex = m_swapChain->GetCurrentBackBufferIndex(); + if (backBufferIndex >= m_backBufferViews.size() || + m_backBufferViews[backBufferIndex] == nullptr) { + std::ostringstream error = {}; + error << "PreparePresentSurface could not find the current swap chain RTV. index=" + << backBufferIndex + << " rtvCount=" + << m_backBufferViews.size(); + m_lastError = error.str(); + return false; + } + + renderContext.commandList->TransitionBarrier( + m_backBufferViews[backBufferIndex], + ::XCEngine::RHI::ResourceStates::Present, + ::XCEngine::RHI::ResourceStates::RenderTarget); + m_lastError.clear(); + return true; +} + +bool D3D12WindowSwapChainPresenter::PresentFrame() { + if (m_swapChain == nullptr) { + m_lastError = "PresentFrame requires an initialized swap chain."; + return false; + } + + // Editor shell presentation prioritizes interaction latency over display sync. + // Blocking on every vblank makes host-window resize feel sticky even when the + // UI tree itself is cheap to rebuild. + m_swapChain->Present(0, 0); + m_lastError.clear(); + return true; +} + +const std::string& D3D12WindowSwapChainPresenter::GetLastError() const { + return m_lastError; +} + +RHISwapChain* D3D12WindowSwapChainPresenter::GetSwapChain() const { + return m_swapChain; +} + +const ::XCEngine::Rendering::RenderSurface* +D3D12WindowSwapChainPresenter::GetCurrentRenderSurface() const { + if (m_swapChain == nullptr) { + return nullptr; + } + + const std::uint32_t backBufferIndex = m_swapChain->GetCurrentBackBufferIndex(); + if (backBufferIndex >= m_backBufferSurfaces.size()) { + return nullptr; + } + + return &m_backBufferSurfaces[backBufferIndex]; +} + +const D3D12Texture* D3D12WindowSwapChainPresenter::GetCurrentBackBufferTexture() const { + if (m_swapChain == nullptr) { + return nullptr; + } + + return GetBackBufferTexture(m_swapChain->GetCurrentBackBufferIndex()); +} + +const D3D12Texture* D3D12WindowSwapChainPresenter::GetBackBufferTexture(std::uint32_t index) const { + const D3D12SwapChain* d3d12SwapChain = GetD3D12SwapChain(); + if (d3d12SwapChain == nullptr) { + return nullptr; + } + + return d3d12SwapChain->TryGetBackBuffer(index); +} + +std::uint32_t D3D12WindowSwapChainPresenter::GetBackBufferCount() const { + return kSwapChainBufferCount; +} + +std::uint32_t D3D12WindowSwapChainPresenter::GetCurrentBackBufferIndex() const { + return m_swapChain != nullptr ? m_swapChain->GetCurrentBackBufferIndex() : 0u; +} + +D3D12SwapChain* D3D12WindowSwapChainPresenter::GetD3D12SwapChain() const { + return m_swapChain != nullptr ? static_cast(m_swapChain) : nullptr; +} + +void D3D12WindowSwapChainPresenter::ReleaseBackBufferCommandReferences() { + if (m_hostDevice == nullptr) { + return; + } + + for (std::uint32_t frameIndex = 0u; + frameIndex < D3D12HostDevice::kFrameContextCount; + ++frameIndex) { + auto* commandList = m_hostDevice->GetCommandList(frameIndex); + if (commandList == nullptr) { + continue; + } + + commandList->Reset(); + commandList->Close(); + } +} + +void D3D12WindowSwapChainPresenter::ReleaseBackBufferViews() { + for (auto* view : m_backBufferViews) { + if (view != nullptr) { + view->Shutdown(); + delete view; + } + } + + m_backBufferViews.clear(); + m_backBufferSurfaces.clear(); +} + +bool D3D12WindowSwapChainPresenter::RecreateBackBufferViews() { + D3D12SwapChain* d3d12SwapChain = GetD3D12SwapChain(); + if (m_hostDevice == nullptr || m_hostDevice->GetRHIDevice() == nullptr || d3d12SwapChain == nullptr) { + return false; + } + + m_backBufferViews.resize(kSwapChainBufferCount, nullptr); + m_backBufferSurfaces.resize(kSwapChainBufferCount); + + ResourceViewDesc viewDesc = {}; + viewDesc.format = static_cast(Format::R8G8B8A8_UNorm); + viewDesc.dimension = ResourceViewDimension::Texture2D; + + for (std::uint32_t backBufferIndex = 0u; + backBufferIndex < kSwapChainBufferCount; + ++backBufferIndex) { + D3D12Texture* backBufferTexture = d3d12SwapChain->TryGetBackBuffer(backBufferIndex); + if (backBufferTexture == nullptr) { + ReleaseBackBufferViews(); + m_lastError = "RecreateBackBufferViews could not resolve swap chain back buffer " + + std::to_string(backBufferIndex) + "."; + return false; + } + + m_backBufferViews[backBufferIndex] = m_hostDevice->GetRHIDevice()->CreateRenderTargetView( + backBufferTexture, + viewDesc); + if (m_backBufferViews[backBufferIndex] == nullptr) { + ReleaseBackBufferViews(); + m_lastError = "RecreateBackBufferViews failed to create RTV for swap chain back buffer " + + std::to_string(backBufferIndex) + "."; + return false; + } + + ::XCEngine::Rendering::RenderSurface& surface = m_backBufferSurfaces[backBufferIndex]; + surface = ::XCEngine::Rendering::RenderSurface( + static_cast(m_width), + static_cast(m_height)); + surface.SetColorAttachment(m_backBufferViews[backBufferIndex]); + surface.SetAutoTransitionEnabled(false); + surface.SetColorStateBefore(ResourceStates::RenderTarget); + surface.SetColorStateAfter(ResourceStates::RenderTarget); + } + + m_lastError.clear(); + return true; +} + +} // namespace XCEngine::UI::Editor::Host diff --git a/new_editor/app/Host/D3D12WindowSwapChainPresenter.h b/new_editor/app/Host/D3D12WindowSwapChainPresenter.h new file mode 100644 index 00000000..8164ea3c --- /dev/null +++ b/new_editor/app/Host/D3D12WindowSwapChainPresenter.h @@ -0,0 +1,61 @@ +#pragma once + +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#include "D3D12HostDevice.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +namespace XCEngine::UI::Editor::Host { + +class D3D12WindowSwapChainPresenter { +public: + static constexpr std::uint32_t kSwapChainBufferCount = 3u; + + bool Initialize(D3D12HostDevice& hostDevice, HWND hwnd, int width, int height); + void Shutdown(); + bool Resize(int width, int height); + bool PreparePresentSurface(const ::XCEngine::Rendering::RenderContext& renderContext); + bool PresentFrame(); + + const std::string& GetLastError() const; + ::XCEngine::RHI::RHISwapChain* GetSwapChain() const; + const ::XCEngine::Rendering::RenderSurface* GetCurrentRenderSurface() const; + const ::XCEngine::RHI::D3D12Texture* GetCurrentBackBufferTexture() const; + const ::XCEngine::RHI::D3D12Texture* GetBackBufferTexture(std::uint32_t index) const; + std::uint32_t GetBackBufferCount() const; + std::uint32_t GetCurrentBackBufferIndex() const; + +private: + bool CreateSwapChain(int width, int height); + void DestroySwapChain(); + bool RecreateSwapChain(int width, int height); + ::XCEngine::RHI::D3D12SwapChain* GetD3D12SwapChain() const; + void ReleaseBackBufferCommandReferences(); + void ReleaseBackBufferViews(); + bool RecreateBackBufferViews(); + + HWND m_hwnd = nullptr; + int m_width = 0; + int m_height = 0; + D3D12HostDevice* m_hostDevice = nullptr; + ::XCEngine::RHI::RHISwapChain* m_swapChain = nullptr; + std::vector<::XCEngine::RHI::RHIResourceView*> m_backBufferViews = {}; + std::vector<::XCEngine::Rendering::RenderSurface> m_backBufferSurfaces = {}; + std::string m_lastError = {}; +}; + +} // namespace XCEngine::UI::Editor::Host diff --git a/new_editor/app/Host/HostRuntimeState.h b/new_editor/app/Host/HostRuntimeState.h index 25554f9b..c2594fcd 100644 --- a/new_editor/app/Host/HostRuntimeState.h +++ b/new_editor/app/Host/HostRuntimeState.h @@ -9,10 +9,6 @@ public: void Reset() { m_windowDpi = 96u; m_inInteractiveResize = false; - m_renderFrameQueued = false; - m_hasPendingWindowResize = false; - m_pendingWindowResizeWidth = 0u; - m_pendingWindowResizeHeight = 0u; } void SetWindowDpi(UINT dpi) { @@ -41,49 +37,9 @@ public: return m_inInteractiveResize; } - void QueueWindowResize(UINT width, UINT height) { - if (width == 0u || height == 0u) { - return; - } - - m_pendingWindowResizeWidth = width; - m_pendingWindowResizeHeight = height; - m_hasPendingWindowResize = true; - } - - bool ConsumePendingWindowResize(UINT& outWidth, UINT& outHeight) { - outWidth = 0u; - outHeight = 0u; - if (!m_hasPendingWindowResize) { - return false; - } - - m_hasPendingWindowResize = false; - outWidth = m_pendingWindowResizeWidth; - outHeight = m_pendingWindowResizeHeight; - return outWidth > 0u && outHeight > 0u; - } - - bool TryQueueDeferredRender() { - if (m_renderFrameQueued) { - return false; - } - - m_renderFrameQueued = true; - return true; - } - - void ClearDeferredRenderRequest() { - m_renderFrameQueued = false; - } - private: UINT m_windowDpi = 96u; bool m_inInteractiveResize = false; - bool m_renderFrameQueued = false; - bool m_hasPendingWindowResize = false; - UINT m_pendingWindowResizeWidth = 0u; - UINT m_pendingWindowResizeHeight = 0u; }; } // namespace XCEngine::UI::Editor::Host diff --git a/new_editor/app/Host/NativeRenderer.cpp b/new_editor/app/Host/NativeRenderer.cpp index 6017d586..32a3dea1 100644 --- a/new_editor/app/Host/NativeRenderer.cpp +++ b/new_editor/app/Host/NativeRenderer.cpp @@ -185,11 +185,11 @@ bool NativeRenderer::AttachWindowRenderer(D3D12WindowRenderer& windowRenderer) { // fallback-only and should not stay alive while D3D11On12 interop is healthy. DiscardRenderTarget(); - if (!m_backBufferInteropTargets.empty()) { + if (m_windowInterop.HasBackBufferTargets()) { return true; } - return RebuildBackBufferInteropTargets(); + return m_windowInterop.RebuildBackBufferTargets(); } void NativeRenderer::DetachWindowRenderer() { @@ -198,18 +198,7 @@ void NativeRenderer::DetachWindowRenderer() { } void NativeRenderer::ReleaseWindowRendererBackBufferTargets() { - ClearActiveInteropSourceTextures(); - if (m_d2dDeviceContext != nullptr) { - m_d2dDeviceContext->SetTarget(nullptr); - D2D1_TAG firstTag = 0u; - D2D1_TAG secondTag = 0u; - m_d2dDeviceContext->Flush(&firstTag, &secondTag); - } - if (m_d3d11DeviceContext != nullptr) { - m_d3d11DeviceContext->ClearState(); - m_d3d11DeviceContext->Flush(); - } - m_backBufferInteropTargets.clear(); + m_windowInterop.ReleaseBackBufferTargets(); } bool NativeRenderer::RebuildWindowRendererBackBufferTargets() { @@ -219,14 +208,11 @@ bool NativeRenderer::RebuildWindowRendererBackBufferTargets() { DiscardRenderTarget(); ReleaseWindowRendererBackBufferTargets(); - return RebuildBackBufferInteropTargets(); + return m_windowInterop.RebuildBackBufferTargets(); } bool NativeRenderer::HasAttachedWindowRenderer() const { - return m_windowRenderer != nullptr && - m_d3d11On12Device != nullptr && - m_d2dDeviceContext != nullptr && - !m_backBufferInteropTargets.empty(); + return m_windowInterop.HasAttachedWindowRenderer(); } bool NativeRenderer::Render(const ::XCEngine::UI::UIDrawData& drawData) { @@ -262,19 +248,29 @@ bool NativeRenderer::RenderToWindowRenderer(const ::XCEngine::UI::UIDrawData& dr return false; } - if (m_backBufferInteropTargets.empty() && - !RebuildBackBufferInteropTargets()) { + if (!m_windowInterop.HasBackBufferTargets() && + !m_windowInterop.RebuildBackBufferTargets()) { if (m_lastRenderError.empty()) { m_lastRenderError = "Window renderer back buffer interop targets are unavailable."; } return false; } - const std::uint32_t backBufferIndex = - m_windowRenderer->GetSwapChain() != nullptr - ? m_windowRenderer->GetSwapChain()->GetCurrentBackBufferIndex() - : 0u; - if (backBufferIndex >= m_backBufferInteropTargets.size()) { + ID3D11On12Device* d3d11On12Device = m_windowInterop.GetD3D11On12Device(); + ID3D11DeviceContext* d3d11DeviceContext = m_windowInterop.GetD3D11DeviceContext(); + ID2D1DeviceContext* d2dDeviceContext = m_windowInterop.GetD2DDeviceContext(); + ID2D1SolidColorBrush* interopBrush = m_windowInterop.GetInteropBrush(); + if (d3d11On12Device == nullptr || + d3d11DeviceContext == nullptr || + d2dDeviceContext == nullptr || + interopBrush == nullptr) { + m_lastRenderError = "Window renderer interop resources are incomplete."; + return false; + } + + const std::uint32_t backBufferIndex = m_windowInterop.GetCurrentBackBufferIndex(); + if (m_windowInterop.GetWrappedBackBufferResource(backBufferIndex) == nullptr || + m_windowInterop.GetBackBufferTargetBitmap(backBufferIndex) == nullptr) { m_lastRenderError = "Back buffer interop target index is out of range."; return false; } @@ -290,15 +286,15 @@ bool NativeRenderer::RenderToWindowRenderer(const ::XCEngine::UI::UIDrawData& dr return false; } - if (!PrepareActiveInteropSourceTextures(drawData)) { + if (!m_windowInterop.PrepareSourceTextures(drawData)) { ID3D11Resource* backBufferResource = - m_backBufferInteropTargets[backBufferIndex].wrappedResource.Get(); + m_windowInterop.GetWrappedBackBufferResource(backBufferIndex); if (backBufferResource != nullptr) { - m_d3d11On12Device->AcquireWrappedResources(&backBufferResource, 1u); - m_d3d11On12Device->ReleaseWrappedResources(&backBufferResource, 1u); + d3d11On12Device->AcquireWrappedResources(&backBufferResource, 1u); + d3d11On12Device->ReleaseWrappedResources(&backBufferResource, 1u); } - m_d3d11DeviceContext->Flush(); - ClearActiveInteropSourceTextures(); + d3d11DeviceContext->Flush(); + m_windowInterop.ClearSourceTextures(); const bool signaled = m_windowRenderer->SignalFrameCompletion(); ReleaseWindowRendererInterop(); if (!signaled) { @@ -309,26 +305,26 @@ bool NativeRenderer::RenderToWindowRenderer(const ::XCEngine::UI::UIDrawData& dr } std::vector acquiredResources = {}; - acquiredResources.reserve(1u + m_activeInteropSourceTextures.size()); - acquiredResources.push_back(m_backBufferInteropTargets[backBufferIndex].wrappedResource.Get()); - for (const D3D12SourceTextureInteropResource& texture : m_activeInteropSourceTextures) { - acquiredResources.push_back(texture.wrappedResource.Get()); + m_windowInterop.BuildAcquiredResources(backBufferIndex, acquiredResources); + if (acquiredResources.empty()) { + m_lastRenderError = "No wrapped interop resources were prepared for UI composition."; + return false; } - m_d3d11On12Device->AcquireWrappedResources( + d3d11On12Device->AcquireWrappedResources( acquiredResources.data(), static_cast(acquiredResources.size())); - m_d2dDeviceContext->SetTarget(m_backBufferInteropTargets[backBufferIndex].targetBitmap.Get()); - const bool rendered = RenderToTarget(*m_d2dDeviceContext.Get(), *m_interopBrush.Get(), drawData); - const HRESULT hr = m_d2dDeviceContext->EndDraw(); + d2dDeviceContext->SetTarget(m_windowInterop.GetBackBufferTargetBitmap(backBufferIndex)); + const bool rendered = RenderToTarget(*d2dDeviceContext, *interopBrush, drawData); + const HRESULT hr = d2dDeviceContext->EndDraw(); - m_d3d11On12Device->ReleaseWrappedResources( + d3d11On12Device->ReleaseWrappedResources( acquiredResources.data(), static_cast(acquiredResources.size())); - m_d3d11DeviceContext->Flush(); - m_d2dDeviceContext->SetTarget(nullptr); - ClearActiveInteropSourceTextures(); + d3d11DeviceContext->Flush(); + d2dDeviceContext->SetTarget(nullptr); + m_windowInterop.ClearSourceTextures(); if (!rendered || FAILED(hr)) { m_lastRenderError = FAILED(hr) @@ -604,252 +600,18 @@ bool NativeRenderer::EnsureWindowRendererInterop() { m_lastRenderError = "EnsureWindowRendererInterop requires initialized D2D and DWrite factories."; return false; } - if (m_d3d11On12Device != nullptr && - m_d2dDeviceContext != nullptr && - m_interopBrush != nullptr) { - return true; + + const bool attached = m_windowInterop.Attach(*m_windowRenderer, *m_d2dFactory.Get()); + if (!attached) { + m_lastRenderError = m_windowInterop.GetLastError(); + } else { + m_lastRenderError.clear(); } - - ReleaseWindowRendererInterop(); - - ID3D12Device* d3d12Device = m_windowRenderer->GetDevice(); - ID3D12CommandQueue* d3d12CommandQueue = m_windowRenderer->GetCommandQueue(); - if (d3d12Device == nullptr || d3d12CommandQueue == nullptr) { - m_lastRenderError = "The attached D3D12 window renderer does not expose a native device/queue."; - return false; - } - - const std::array featureLevels = { - D3D_FEATURE_LEVEL_12_1, - D3D_FEATURE_LEVEL_12_0, - D3D_FEATURE_LEVEL_11_1, - D3D_FEATURE_LEVEL_11_0 - }; - const std::array commandQueues = { - reinterpret_cast(d3d12CommandQueue) - }; - - UINT createFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; -#ifdef _DEBUG - createFlags |= D3D11_CREATE_DEVICE_DEBUG; -#endif - - D3D_FEATURE_LEVEL actualFeatureLevel = D3D_FEATURE_LEVEL_11_0; - HRESULT hr = D3D11On12CreateDevice( - d3d12Device, - createFlags, - featureLevels.data(), - static_cast(featureLevels.size()), - commandQueues.data(), - static_cast(commandQueues.size()), - 0u, - m_d3d11Device.ReleaseAndGetAddressOf(), - m_d3d11DeviceContext.ReleaseAndGetAddressOf(), - &actualFeatureLevel); -#ifdef _DEBUG - if (FAILED(hr)) { - createFlags &= ~D3D11_CREATE_DEVICE_DEBUG; - hr = D3D11On12CreateDevice( - d3d12Device, - createFlags, - featureLevels.data(), - static_cast(featureLevels.size()), - commandQueues.data(), - static_cast(commandQueues.size()), - 0u, - m_d3d11Device.ReleaseAndGetAddressOf(), - m_d3d11DeviceContext.ReleaseAndGetAddressOf(), - &actualFeatureLevel); - } -#endif - if (FAILED(hr) || m_d3d11Device == nullptr || m_d3d11DeviceContext == nullptr) { - m_lastRenderError = HrToString("D3D11On12CreateDevice", hr); - ReleaseWindowRendererInterop(); - return false; - } - - hr = m_d3d11Device.As(&m_d3d11On12Device); - if (FAILED(hr) || m_d3d11On12Device == nullptr) { - m_lastRenderError = HrToString("ID3D11Device::QueryInterface(ID3D11On12Device)", hr); - ReleaseWindowRendererInterop(); - return false; - } - - Microsoft::WRL::ComPtr dxgiDevice; - hr = m_d3d11Device.As(&dxgiDevice); - if (FAILED(hr) || dxgiDevice == nullptr) { - m_lastRenderError = HrToString("ID3D11Device::QueryInterface(IDXGIDevice)", hr); - ReleaseWindowRendererInterop(); - return false; - } - - hr = m_d2dFactory->CreateDevice(dxgiDevice.Get(), m_d2dDevice.ReleaseAndGetAddressOf()); - if (FAILED(hr) || m_d2dDevice == nullptr) { - m_lastRenderError = HrToString("ID2D1Factory1::CreateDevice", hr); - ReleaseWindowRendererInterop(); - return false; - } - - hr = m_d2dDevice->CreateDeviceContext( - D2D1_DEVICE_CONTEXT_OPTIONS_NONE, - m_d2dDeviceContext.ReleaseAndGetAddressOf()); - if (FAILED(hr) || m_d2dDeviceContext == nullptr) { - m_lastRenderError = HrToString("ID2D1Device::CreateDeviceContext", hr); - ReleaseWindowRendererInterop(); - return false; - } - - hr = m_d2dDeviceContext->CreateSolidColorBrush( - D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f), - m_interopBrush.ReleaseAndGetAddressOf()); - if (FAILED(hr) || m_interopBrush == nullptr) { - m_lastRenderError = HrToString("ID2D1DeviceContext::CreateSolidColorBrush", hr); - ReleaseWindowRendererInterop(); - return false; - } - - m_d2dDeviceContext->SetDpi(kBaseDpi, kBaseDpi); - m_d2dDeviceContext->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE); - return RebuildBackBufferInteropTargets(); + return attached; } void NativeRenderer::ReleaseWindowRendererInterop() { - ReleaseWindowRendererBackBufferTargets(); - m_interopBrush.Reset(); - m_d2dDeviceContext.Reset(); - m_d2dDevice.Reset(); - m_d3d11On12Device.Reset(); - m_d3d11DeviceContext.Reset(); - m_d3d11Device.Reset(); -} - -bool NativeRenderer::RebuildBackBufferInteropTargets() { - m_backBufferInteropTargets.clear(); - if (m_windowRenderer == nullptr || m_d3d11On12Device == nullptr || m_d2dDeviceContext == nullptr) { - return false; - } - - const std::uint32_t backBufferCount = m_windowRenderer->GetBackBufferCount(); - m_backBufferInteropTargets.resize(backBufferCount); - for (std::uint32_t index = 0; index < backBufferCount; ++index) { - const ::XCEngine::RHI::D3D12Texture* backBufferTexture = - m_windowRenderer->GetBackBufferTexture(index); - if (backBufferTexture == nullptr || backBufferTexture->GetResource() == nullptr) { - m_lastRenderError = "Failed to resolve a D3D12 swap chain back buffer."; - m_backBufferInteropTargets.clear(); - return false; - } - - D3D11_RESOURCE_FLAGS resourceFlags = {}; - resourceFlags.BindFlags = D3D11_BIND_RENDER_TARGET; - HRESULT hr = m_d3d11On12Device->CreateWrappedResource( - backBufferTexture->GetResource(), - &resourceFlags, - D3D12_RESOURCE_STATE_RENDER_TARGET, - D3D12_RESOURCE_STATE_PRESENT, - IID_PPV_ARGS(m_backBufferInteropTargets[index].wrappedResource.ReleaseAndGetAddressOf())); - if (FAILED(hr) || m_backBufferInteropTargets[index].wrappedResource == nullptr) { - m_lastRenderError = HrToString("ID3D11On12Device::CreateWrappedResource(backbuffer)", hr); - m_backBufferInteropTargets.clear(); - return false; - } - - Microsoft::WRL::ComPtr dxgiSurface; - hr = m_backBufferInteropTargets[index].wrappedResource.As(&dxgiSurface); - if (FAILED(hr) || dxgiSurface == nullptr) { - m_lastRenderError = HrToString("ID3D11Resource::QueryInterface(IDXGISurface)", hr); - m_backBufferInteropTargets.clear(); - return false; - } - - const D2D1_BITMAP_PROPERTIES1 bitmapProperties = - BuildD2DBitmapProperties( - backBufferTexture->GetDesc().Format, - D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW); - hr = m_d2dDeviceContext->CreateBitmapFromDxgiSurface( - dxgiSurface.Get(), - &bitmapProperties, - m_backBufferInteropTargets[index].targetBitmap.ReleaseAndGetAddressOf()); - if (FAILED(hr) || m_backBufferInteropTargets[index].targetBitmap == nullptr) { - m_lastRenderError = HrToString("ID2D1DeviceContext::CreateBitmapFromDxgiSurface(backbuffer)", hr); - m_backBufferInteropTargets.clear(); - return false; - } - } - - return true; -} - -void NativeRenderer::ClearActiveInteropSourceTextures() { - m_activeInteropBitmaps.clear(); - m_activeInteropSourceTextures.clear(); -} - -bool NativeRenderer::PrepareActiveInteropSourceTextures( - const ::XCEngine::UI::UIDrawData& drawData) { - ClearActiveInteropSourceTextures(); - if (m_windowRenderer == nullptr || m_d3d11On12Device == nullptr || m_d2dDeviceContext == nullptr) { - return false; - } - - std::vector<::XCEngine::UI::UITextureHandle> textureHandles = {}; - CollectInteropTextureHandles(drawData, textureHandles); - m_activeInteropSourceTextures.reserve(textureHandles.size()); - - for (const ::XCEngine::UI::UITextureHandle& textureHandle : textureHandles) { - auto* texture = - reinterpret_cast<::XCEngine::RHI::RHITexture*>(textureHandle.resourceHandle); - auto* nativeTexture = dynamic_cast<::XCEngine::RHI::D3D12Texture*>(texture); - if (nativeTexture == nullptr || nativeTexture->GetResource() == nullptr) { - m_lastRenderError = "Failed to resolve a D3D12 source texture for UI composition."; - ClearActiveInteropSourceTextures(); - return false; - } - - D3D11_RESOURCE_FLAGS resourceFlags = {}; - resourceFlags.BindFlags = D3D11_BIND_SHADER_RESOURCE; - - D3D12SourceTextureInteropResource resource = {}; - resource.key = textureHandle.resourceHandle; - HRESULT hr = m_d3d11On12Device->CreateWrappedResource( - nativeTexture->GetResource(), - &resourceFlags, - D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, - D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, - IID_PPV_ARGS(resource.wrappedResource.ReleaseAndGetAddressOf())); - if (FAILED(hr) || resource.wrappedResource == nullptr) { - m_lastRenderError = HrToString("ID3D11On12Device::CreateWrappedResource(source)", hr); - ClearActiveInteropSourceTextures(); - return false; - } - - Microsoft::WRL::ComPtr dxgiSurface; - hr = resource.wrappedResource.As(&dxgiSurface); - if (FAILED(hr) || dxgiSurface == nullptr) { - m_lastRenderError = HrToString("ID3D11Resource::QueryInterface(IDXGISurface)", hr); - ClearActiveInteropSourceTextures(); - return false; - } - - const D2D1_BITMAP_PROPERTIES1 bitmapProperties = - BuildD2DBitmapProperties( - nativeTexture->GetDesc().Format, - D2D1_BITMAP_OPTIONS_NONE); - hr = m_d2dDeviceContext->CreateBitmapFromDxgiSurface( - dxgiSurface.Get(), - &bitmapProperties, - resource.bitmap.ReleaseAndGetAddressOf()); - if (FAILED(hr) || resource.bitmap == nullptr) { - m_lastRenderError = HrToString("ID2D1DeviceContext::CreateBitmapFromDxgiSurface(source)", hr); - ClearActiveInteropSourceTextures(); - return false; - } - - m_activeInteropBitmaps.emplace(resource.key, resource.bitmap); - m_activeInteropSourceTextures.push_back(std::move(resource)); - } - - return true; + m_windowInterop.Detach(); } bool NativeRenderer::EnsureRenderTarget() { @@ -1068,18 +830,7 @@ bool NativeRenderer::ResolveTextureBitmap( bool NativeRenderer::ResolveInteropBitmap( const ::XCEngine::UI::UITextureHandle& texture, Microsoft::WRL::ComPtr& outBitmap) const { - outBitmap.Reset(); - if (!IsInteropTextureHandle(texture)) { - return false; - } - - const auto found = m_activeInteropBitmaps.find(texture.resourceHandle); - if (found == m_activeInteropBitmaps.end() || found->second == nullptr) { - return false; - } - - outBitmap = found->second; - return true; + return m_windowInterop.ResolveInteropBitmap(texture, outBitmap); } bool NativeRenderer::RenderToTarget( diff --git a/new_editor/app/Host/NativeRenderer.h b/new_editor/app/Host/NativeRenderer.h index 0594d625..80edd1a8 100644 --- a/new_editor/app/Host/NativeRenderer.h +++ b/new_editor/app/Host/NativeRenderer.h @@ -4,6 +4,7 @@ #define NOMINMAX #endif +#include "D3D12WindowInteropContext.h" #include "D3D12WindowRenderer.h" #include @@ -65,15 +66,6 @@ private: UINT width = 0u; UINT height = 0u; }; - struct D3D12BackBufferInteropTarget { - Microsoft::WRL::ComPtr wrappedResource = {}; - Microsoft::WRL::ComPtr targetBitmap = {}; - }; - struct D3D12SourceTextureInteropResource { - std::uintptr_t key = 0u; - Microsoft::WRL::ComPtr wrappedResource = {}; - Microsoft::WRL::ComPtr bitmap = {}; - }; bool EnsureRenderTarget(); bool EnsureWindowRendererInterop(); @@ -81,9 +73,6 @@ private: void DiscardRenderTarget(); bool CreateDeviceResources(); void ReleaseWindowRendererInterop(); - bool RebuildBackBufferInteropTargets(); - void ClearActiveInteropSourceTextures(); - bool PrepareActiveInteropSourceTextures(const ::XCEngine::UI::UIDrawData& drawData); void InvalidateCachedTextureBitmaps(const ID2D1RenderTarget* renderTarget); bool RenderToTarget( ID2D1RenderTarget& renderTarget, @@ -115,19 +104,11 @@ private: Microsoft::WRL::ComPtr m_d2dFactory; Microsoft::WRL::ComPtr m_dwriteFactory; Microsoft::WRL::ComPtr m_wicFactory; - Microsoft::WRL::ComPtr m_d3d11Device; - Microsoft::WRL::ComPtr m_d3d11DeviceContext; - Microsoft::WRL::ComPtr m_d3d11On12Device; - Microsoft::WRL::ComPtr m_d2dDevice; - Microsoft::WRL::ComPtr m_d2dDeviceContext; Microsoft::WRL::ComPtr m_renderTarget; Microsoft::WRL::ComPtr m_solidBrush; - Microsoft::WRL::ComPtr m_interopBrush; - std::vector m_backBufferInteropTargets = {}; - std::vector m_activeInteropSourceTextures = {}; - std::unordered_map> m_activeInteropBitmaps; mutable std::unordered_map> m_textFormats; std::unordered_set m_liveTextures; + D3D12WindowInteropContext m_windowInterop = {}; std::string m_lastRenderError = {}; bool m_wicComInitialized = false; float m_dpiScale = 1.0f; diff --git a/new_editor/app/Host/WindowMessageDispatcher.cpp b/new_editor/app/Host/WindowMessageDispatcher.cpp index da6ec629..1dd005cc 100644 --- a/new_editor/app/Host/WindowMessageDispatcher.cpp +++ b/new_editor/app/Host/WindowMessageDispatcher.cpp @@ -4,10 +4,6 @@ namespace XCEngine::UI::Editor::Host { -namespace { - -constexpr UINT kDeferredRenderMessage = WM_APP + 1u; - void TryEnableNonClientDpiScaling(HWND hwnd) { if (hwnd == nullptr) { return; @@ -27,8 +23,6 @@ void TryEnableNonClientDpiScaling(HWND hwnd) { } } -} // namespace - Application* WindowMessageDispatcher::GetApplicationFromWindow(HWND hwnd) { return reinterpret_cast(GetWindowLongPtrW(hwnd, GWLP_USERDATA)); } @@ -70,7 +64,9 @@ bool WindowMessageDispatcher::TryDispatch( application.OnDpiChanged( static_cast(LOWORD(wParam)), *reinterpret_cast(lParam)); - RequestDeferredRenderFrame(application); + if (application.m_renderReady) { + application.RenderFrame(); + } outResult = 0; return true; case WM_ENTERSIZEMOVE: @@ -79,20 +75,22 @@ bool WindowMessageDispatcher::TryDispatch( return true; case WM_EXITSIZEMOVE: application.OnExitSizeMove(); - RequestDeferredRenderFrame(application); + if (application.m_renderReady) { + application.RenderFrame(); + } outResult = 0; return true; case WM_SIZE: if (wParam != SIZE_MINIMIZED) { - application.OnResize(); - RequestDeferredRenderFrame(application); + application.OnResize( + static_cast(LOWORD(lParam)), + static_cast(HIWORD(lParam))); + if (application.m_renderReady) { + application.RenderFrame(); + } } outResult = 0; return true; - case kDeferredRenderMessage: - application.OnDeferredRenderMessage(); - outResult = 0; - return true; case WM_PAINT: application.OnPaintMessage(); outResult = 0; @@ -102,14 +100,4 @@ bool WindowMessageDispatcher::TryDispatch( } } -void WindowMessageDispatcher::RequestDeferredRenderFrame(Application& application) { - if (application.m_hwnd == nullptr || - !IsWindow(application.m_hwnd) || - !application.m_hostRuntime.TryQueueDeferredRender()) { - return; - } - - PostMessageW(application.m_hwnd, kDeferredRenderMessage, 0, 0); -} - } // namespace XCEngine::UI::Editor::Host diff --git a/new_editor/app/Host/WindowMessageDispatcher.h b/new_editor/app/Host/WindowMessageDispatcher.h index 1f48c1b7..4ec1b7aa 100644 --- a/new_editor/app/Host/WindowMessageDispatcher.h +++ b/new_editor/app/Host/WindowMessageDispatcher.h @@ -26,9 +26,6 @@ public: WPARAM wParam, LPARAM lParam, LRESULT& outResult); - -private: - static void RequestDeferredRenderFrame(Application& application); }; } // namespace XCEngine::UI::Editor::Host