Refactor new editor host resize pipeline
This commit is contained in:
@@ -124,8 +124,11 @@ target_link_libraries(XCUIEditorLib PUBLIC
|
|||||||
|
|
||||||
add_library(XCUIEditorHost STATIC
|
add_library(XCUIEditorHost STATIC
|
||||||
app/Host/AutoScreenshot.cpp
|
app/Host/AutoScreenshot.cpp
|
||||||
|
app/Host/D3D12HostDevice.cpp
|
||||||
app/Host/D3D12ShaderResourceDescriptorAllocator.cpp
|
app/Host/D3D12ShaderResourceDescriptorAllocator.cpp
|
||||||
|
app/Host/D3D12WindowInteropContext.cpp
|
||||||
app/Host/D3D12WindowRenderer.cpp
|
app/Host/D3D12WindowRenderer.cpp
|
||||||
|
app/Host/D3D12WindowSwapChainPresenter.cpp
|
||||||
app/Host/D3D12WindowRenderLoop.cpp
|
app/Host/D3D12WindowRenderLoop.cpp
|
||||||
app/Host/NativeRenderer.cpp
|
app/Host/NativeRenderer.cpp
|
||||||
app/Host/WindowMessageDispatcher.cpp
|
app/Host/WindowMessageDispatcher.cpp
|
||||||
|
|||||||
@@ -478,6 +478,7 @@ bool Application::Initialize(HINSTANCE hInstance, int nCmdShow) {
|
|||||||
"app",
|
"app",
|
||||||
"workspace initialized: " +
|
"workspace initialized: " +
|
||||||
m_editorContext.DescribeWorkspaceState(m_editorWorkspace.GetShellInteractionState()));
|
m_editorContext.DescribeWorkspaceState(m_editorWorkspace.GetShellInteractionState()));
|
||||||
|
m_renderReady = true;
|
||||||
|
|
||||||
ShowWindow(m_hwnd, nCmdShow);
|
ShowWindow(m_hwnd, nCmdShow);
|
||||||
UpdateWindow(m_hwnd);
|
UpdateWindow(m_hwnd);
|
||||||
@@ -493,6 +494,7 @@ bool Application::Initialize(HINSTANCE hInstance, int nCmdShow) {
|
|||||||
|
|
||||||
void Application::Shutdown() {
|
void Application::Shutdown() {
|
||||||
LogRuntimeTrace("app", "shutdown begin");
|
LogRuntimeTrace("app", "shutdown begin");
|
||||||
|
m_renderReady = false;
|
||||||
if (GetCapture() == m_hwnd) {
|
if (GetCapture() == m_hwnd) {
|
||||||
ReleaseCapture();
|
ReleaseCapture();
|
||||||
}
|
}
|
||||||
@@ -518,12 +520,10 @@ void Application::Shutdown() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Application::RenderFrame() {
|
void Application::RenderFrame() {
|
||||||
if (m_hwnd == nullptr) {
|
if (!m_renderReady || m_hwnd == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplyPendingWindowResize();
|
|
||||||
|
|
||||||
RECT clientRect = {};
|
RECT clientRect = {};
|
||||||
GetClientRect(m_hwnd, &clientRect);
|
GetClientRect(m_hwnd, &clientRect);
|
||||||
const unsigned int pixelWidth =
|
const unsigned int pixelWidth =
|
||||||
@@ -620,13 +620,8 @@ void Application::RenderFrame() {
|
|||||||
presentResult.framePresented);
|
presentResult.framePresented);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::OnDeferredRenderMessage() {
|
|
||||||
m_hostRuntime.ClearDeferredRenderRequest();
|
|
||||||
RenderFrame();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::OnPaintMessage() {
|
void Application::OnPaintMessage() {
|
||||||
if (m_hwnd == nullptr) {
|
if (!m_renderReady || m_hwnd == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -747,8 +742,8 @@ std::string Application::DescribeInputEvents(
|
|||||||
return stream.str();
|
return stream.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::OnResize() {
|
void Application::OnResize(UINT width, UINT height) {
|
||||||
QueueCurrentClientResize();
|
ApplyWindowResize(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::OnEnterSizeMove() {
|
void Application::OnEnterSizeMove() {
|
||||||
@@ -757,28 +752,16 @@ void Application::OnEnterSizeMove() {
|
|||||||
|
|
||||||
void Application::OnExitSizeMove() {
|
void Application::OnExitSizeMove() {
|
||||||
m_hostRuntime.EndInteractiveResize();
|
m_hostRuntime.EndInteractiveResize();
|
||||||
QueueCurrentClientResize();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::QueueWindowResize(UINT width, UINT height) {
|
|
||||||
m_hostRuntime.QueueWindowResize(width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::QueueCurrentClientResize() {
|
|
||||||
UINT width = 0u;
|
UINT width = 0u;
|
||||||
UINT height = 0u;
|
UINT height = 0u;
|
||||||
if (!QueryCurrentClientPixelSize(width, height)) {
|
if (QueryCurrentClientPixelSize(width, height)) {
|
||||||
return;
|
ApplyWindowResize(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
QueueWindowResize(width, height);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Application::ApplyPendingWindowResize() {
|
bool Application::ApplyWindowResize(UINT width, UINT height) {
|
||||||
UINT width = 0u;
|
if (!m_renderReady || width == 0u || height == 0u) {
|
||||||
UINT height = 0u;
|
return false;
|
||||||
if (!m_hostRuntime.ConsumePendingWindowResize(width, height)) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Host::D3D12WindowRenderLoopResizeResult resizeResult =
|
const Host::D3D12WindowRenderLoopResizeResult resizeResult =
|
||||||
@@ -834,7 +817,11 @@ void Application::OnDpiChanged(UINT dpi, const RECT& suggestedRect) {
|
|||||||
windowWidth,
|
windowWidth,
|
||||||
windowHeight,
|
windowHeight,
|
||||||
SWP_NOZORDER | SWP_NOACTIVATE);
|
SWP_NOZORDER | SWP_NOACTIVATE);
|
||||||
QueueCurrentClientResize();
|
UINT clientWidth = 0u;
|
||||||
|
UINT clientHeight = 0u;
|
||||||
|
if (QueryCurrentClientPixelSize(clientWidth, clientHeight)) {
|
||||||
|
ApplyWindowResize(clientWidth, clientHeight);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostringstream trace = {};
|
std::ostringstream trace = {};
|
||||||
|
|||||||
@@ -45,15 +45,12 @@ private:
|
|||||||
bool Initialize(HINSTANCE hInstance, int nCmdShow);
|
bool Initialize(HINSTANCE hInstance, int nCmdShow);
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
void RenderFrame();
|
void RenderFrame();
|
||||||
void OnDeferredRenderMessage();
|
|
||||||
void OnPaintMessage();
|
void OnPaintMessage();
|
||||||
void OnResize();
|
void OnResize(UINT width, UINT height);
|
||||||
void OnEnterSizeMove();
|
void OnEnterSizeMove();
|
||||||
void OnExitSizeMove();
|
void OnExitSizeMove();
|
||||||
void OnDpiChanged(UINT dpi, const RECT& suggestedRect);
|
void OnDpiChanged(UINT dpi, const RECT& suggestedRect);
|
||||||
void QueueWindowResize(UINT width, UINT height);
|
bool ApplyWindowResize(UINT width, UINT height);
|
||||||
void QueueCurrentClientResize();
|
|
||||||
bool ApplyPendingWindowResize();
|
|
||||||
bool QueryCurrentClientPixelSize(UINT& outWidth, UINT& outHeight) const;
|
bool QueryCurrentClientPixelSize(UINT& outWidth, UINT& outHeight) const;
|
||||||
bool IsPointerInsideClientArea() const;
|
bool IsPointerInsideClientArea() const;
|
||||||
bool ApplyCurrentCursor() const;
|
bool ApplyCurrentCursor() const;
|
||||||
@@ -94,6 +91,7 @@ private:
|
|||||||
App::ProductEditorWorkspace m_editorWorkspace = {};
|
App::ProductEditorWorkspace m_editorWorkspace = {};
|
||||||
std::vector<::XCEngine::UI::UIInputEvent> m_pendingInputEvents = {};
|
std::vector<::XCEngine::UI::UIInputEvent> m_pendingInputEvents = {};
|
||||||
bool m_trackingMouseLeave = false;
|
bool m_trackingMouseLeave = false;
|
||||||
|
bool m_renderReady = false;
|
||||||
::XCEngine::UI::Editor::Host::HostRuntimeState m_hostRuntime = {};
|
::XCEngine::UI::Editor::Host::HostRuntimeState m_hostRuntime = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
296
new_editor/app/Host/D3D12HostDevice.cpp
Normal file
296
new_editor/app/Host/D3D12HostDevice.cpp
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
#include "D3D12HostDevice.h"
|
||||||
|
|
||||||
|
#include <XCEngine/RHI/RHIFactory.h>
|
||||||
|
|
||||||
|
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<std::uint32_t>(::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<std::uint32_t>(::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<D3D12Device*>(m_device) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
D3D12CommandQueue* D3D12HostDevice::GetD3D12CommandQueue() const {
|
||||||
|
return m_commandQueue != nullptr ? static_cast<D3D12CommandQueue*>(m_commandQueue) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
D3D12CommandList* D3D12HostDevice::GetD3D12CommandList(std::uint32_t frameIndex) const {
|
||||||
|
RHICommandList* commandList = GetCommandList(frameIndex);
|
||||||
|
return commandList != nullptr ? static_cast<D3D12CommandList*>(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
|
||||||
64
new_editor/app/Host/D3D12HostDevice.h
Normal file
64
new_editor/app/Host/D3D12HostDevice.h
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef NOMINMAX
|
||||||
|
#define NOMINMAX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <XCEngine/Rendering/RenderContext.h>
|
||||||
|
#include <XCEngine/RHI/D3D12/D3D12CommandList.h>
|
||||||
|
#include <XCEngine/RHI/D3D12/D3D12CommandQueue.h>
|
||||||
|
#include <XCEngine/RHI/D3D12/D3D12Device.h>
|
||||||
|
#include <XCEngine/RHI/RHICommandList.h>
|
||||||
|
#include <XCEngine/RHI/RHICommandQueue.h>
|
||||||
|
#include <XCEngine/RHI/RHIDevice.h>
|
||||||
|
|
||||||
|
#include <d3d12.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#include <wrl/client.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
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<ID3D12Fence> m_frameCompletionFence = {};
|
||||||
|
HANDLE m_frameCompletionEvent = nullptr;
|
||||||
|
std::array<std::uint64_t, kFrameContextCount> m_frameFenceValues = {};
|
||||||
|
std::uint64_t m_lastSubmittedFrameValue = 0u;
|
||||||
|
std::string m_lastError = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace XCEngine::UI::Editor::Host
|
||||||
444
new_editor/app/Host/D3D12WindowInteropContext.cpp
Normal file
444
new_editor/app/Host/D3D12WindowInteropContext.cpp
Normal file
@@ -0,0 +1,444 @@
|
|||||||
|
#include "D3D12WindowInteropContext.h"
|
||||||
|
|
||||||
|
#include <XCEngine/RHI/D3D12/D3D12Texture.h>
|
||||||
|
#include <XCEngine/RHI/RHITexture.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
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<unsigned int>(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<std::uintptr_t> 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<IDXGISurface> 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<IDXGISurface> 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<ID2D1Bitmap>& 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<ID3D11Resource*>& 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<D3D_FEATURE_LEVEL, 4> featureLevels = {
|
||||||
|
D3D_FEATURE_LEVEL_12_1,
|
||||||
|
D3D_FEATURE_LEVEL_12_0,
|
||||||
|
D3D_FEATURE_LEVEL_11_1,
|
||||||
|
D3D_FEATURE_LEVEL_11_0
|
||||||
|
};
|
||||||
|
const std::array<IUnknown*, 1> commandQueues = {
|
||||||
|
reinterpret_cast<IUnknown*>(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<UINT>(featureLevels.size()),
|
||||||
|
commandQueues.data(),
|
||||||
|
static_cast<UINT>(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<UINT>(featureLevels.size()),
|
||||||
|
commandQueues.data(),
|
||||||
|
static_cast<UINT>(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<IDXGIDevice> 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
|
||||||
79
new_editor/app/Host/D3D12WindowInteropContext.h
Normal file
79
new_editor/app/Host/D3D12WindowInteropContext.h
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef NOMINMAX
|
||||||
|
#define NOMINMAX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "D3D12WindowRenderer.h"
|
||||||
|
|
||||||
|
#include <XCEngine/UI/DrawData.h>
|
||||||
|
|
||||||
|
#include <d2d1_1.h>
|
||||||
|
#include <d3d11_4.h>
|
||||||
|
#include <d3d11on12.h>
|
||||||
|
#include <wrl/client.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
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<ID2D1Bitmap>& 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<ID3D11Resource*>& 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<ID3D11Resource> wrappedResource = {};
|
||||||
|
Microsoft::WRL::ComPtr<ID2D1Bitmap1> targetBitmap = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SourceTextureResource {
|
||||||
|
std::uintptr_t key = 0u;
|
||||||
|
Microsoft::WRL::ComPtr<ID3D11Resource> wrappedResource = {};
|
||||||
|
Microsoft::WRL::ComPtr<ID2D1Bitmap1> bitmap = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
bool EnsureInterop();
|
||||||
|
void ReleaseInteropState();
|
||||||
|
|
||||||
|
D3D12WindowRenderer* m_windowRenderer = nullptr;
|
||||||
|
ID2D1Factory1* m_d2dFactory = nullptr;
|
||||||
|
Microsoft::WRL::ComPtr<ID3D11Device> m_d3d11Device = {};
|
||||||
|
Microsoft::WRL::ComPtr<ID3D11DeviceContext> m_d3d11DeviceContext = {};
|
||||||
|
Microsoft::WRL::ComPtr<ID3D11On12Device> m_d3d11On12Device = {};
|
||||||
|
Microsoft::WRL::ComPtr<ID2D1Device> m_d2dDevice = {};
|
||||||
|
Microsoft::WRL::ComPtr<ID2D1DeviceContext> m_d2dDeviceContext = {};
|
||||||
|
Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> m_interopBrush = {};
|
||||||
|
std::vector<BackBufferTarget> m_backBufferTargets = {};
|
||||||
|
std::vector<SourceTextureResource> m_activeSourceTextures = {};
|
||||||
|
std::unordered_map<std::uintptr_t, Microsoft::WRL::ComPtr<ID2D1Bitmap1>> m_activeBitmaps = {};
|
||||||
|
std::string m_lastError = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace XCEngine::UI::Editor::Host
|
||||||
@@ -56,7 +56,10 @@ D3D12WindowRenderLoopResizeResult D3D12WindowRenderLoop::ApplyResize(UINT width,
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_uiRenderer->Resize(width, height);
|
m_uiRenderer->Resize(width, height);
|
||||||
m_uiRenderer->DetachWindowRenderer();
|
const bool hadViewportSurfacePresentation = m_uiRenderer->HasAttachedWindowRenderer();
|
||||||
|
if (hadViewportSurfacePresentation) {
|
||||||
|
m_uiRenderer->ReleaseWindowRendererBackBufferTargets();
|
||||||
|
}
|
||||||
|
|
||||||
const bool resizedWindowRenderer =
|
const bool resizedWindowRenderer =
|
||||||
m_windowRenderer->Resize(static_cast<int>(width), static_cast<int>(height));
|
m_windowRenderer->Resize(static_cast<int>(width), static_cast<int>(height));
|
||||||
@@ -68,6 +71,28 @@ D3D12WindowRenderLoopResizeResult D3D12WindowRenderLoop::ApplyResize(UINT width,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!resizedWindowRenderer) {
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,30 +1,10 @@
|
|||||||
#include "D3D12WindowRenderer.h"
|
#include "D3D12WindowRenderer.h"
|
||||||
|
|
||||||
#include <XCEngine/RHI/RHIFactory.h>
|
|
||||||
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
namespace XCEngine::UI::Editor::Host {
|
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::RHIDevice;
|
||||||
using ::XCEngine::RHI::RHIDeviceDesc;
|
|
||||||
using ::XCEngine::RHI::RHIFactory;
|
|
||||||
using ::XCEngine::RHI::RHISwapChain;
|
using ::XCEngine::RHI::RHISwapChain;
|
||||||
using ::XCEngine::RHI::RHIType;
|
using ::XCEngine::RHI::D3D12Texture;
|
||||||
using ::XCEngine::RHI::SwapChainDesc;
|
|
||||||
|
|
||||||
bool D3D12WindowRenderer::Initialize(HWND hwnd, int width, int height) {
|
bool D3D12WindowRenderer::Initialize(HWND hwnd, int width, int height) {
|
||||||
Shutdown();
|
Shutdown();
|
||||||
@@ -34,211 +14,55 @@ bool D3D12WindowRenderer::Initialize(HWND hwnd, int width, int height) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_hwnd = hwnd;
|
if (!m_hostDevice.Initialize()) {
|
||||||
m_width = width;
|
m_lastError = m_hostDevice.GetLastError();
|
||||||
m_height = height;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
m_device = RHIFactory::CreateRHIDevice(RHIType::D3D12);
|
if (!m_presenter.Initialize(m_hostDevice, hwnd, width, height)) {
|
||||||
if (m_device == nullptr) {
|
m_lastError = m_presenter.GetLastError();
|
||||||
m_lastError = "Failed to create the D3D12 RHI device.";
|
|
||||||
Shutdown();
|
Shutdown();
|
||||||
return false;
|
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<std::uint32_t>(::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<std::uint32_t>(::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<std::uint32_t>(width);
|
|
||||||
swapChainDesc.height = static_cast<std::uint32_t>(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();
|
m_lastError.clear();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3D12WindowRenderer::Shutdown() {
|
void D3D12WindowRenderer::Shutdown() {
|
||||||
WaitForGpuIdle();
|
m_presenter.Shutdown();
|
||||||
ReleaseFrameCompletionFence();
|
m_hostDevice.Shutdown();
|
||||||
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_activeBackBufferIndex = 0u;
|
m_activeBackBufferIndex = 0u;
|
||||||
m_lastError.clear();
|
m_lastError.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool D3D12WindowRenderer::Resize(int width, int height) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_width == width && m_height == height) {
|
m_activeBackBufferIndex = m_presenter.GetCurrentBackBufferIndex();
|
||||||
m_lastError.clear();
|
m_lastError = m_presenter.GetLastError();
|
||||||
|
if (!m_lastError.empty()) {
|
||||||
return true;
|
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<std::uint32_t>(width),
|
|
||||||
static_cast<std::uint32_t>(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<unsigned long>(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<int>(backBufferTexture->GetWidth());
|
|
||||||
m_height = static_cast<int>(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();
|
m_lastError.clear();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool D3D12WindowRenderer::BeginFrame() {
|
bool D3D12WindowRenderer::BeginFrame() {
|
||||||
if (m_swapChain == nullptr) {
|
if (m_presenter.GetSwapChain() == nullptr) {
|
||||||
m_lastError = "BeginFrame requires an initialized swap chain.";
|
m_lastError = "BeginFrame requires an initialized swap chain.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_activeBackBufferIndex = m_swapChain->GetCurrentBackBufferIndex();
|
m_activeBackBufferIndex = m_presenter.GetCurrentBackBufferIndex();
|
||||||
WaitForBackBufferFrame(m_activeBackBufferIndex);
|
if (!m_hostDevice.BeginFrame(m_activeBackBufferIndex)) {
|
||||||
::XCEngine::RHI::RHICommandList* commandList = GetCurrentCommandList();
|
m_lastError = m_hostDevice.GetLastError();
|
||||||
D3D12CommandList* d3d12CommandList = GetD3D12CommandList();
|
|
||||||
if (commandList == nullptr || d3d12CommandList == nullptr) {
|
|
||||||
m_lastError = "BeginFrame could not resolve the active command list.";
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
commandList->Reset();
|
|
||||||
m_lastError.clear();
|
m_lastError.clear();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -250,88 +74,54 @@ bool D3D12WindowRenderer::PreparePresentSurface() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_swapChain == nullptr) {
|
const bool prepared = m_presenter.PreparePresentSurface(renderContext);
|
||||||
m_lastError = "PreparePresentSurface requires an initialized swap chain.";
|
m_lastError = prepared ? std::string() : m_presenter.GetLastError();
|
||||||
return false;
|
return prepared;
|
||||||
}
|
|
||||||
|
|
||||||
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 D3D12WindowRenderer::SubmitFrame(bool presentSwapChain) {
|
bool D3D12WindowRenderer::SubmitFrame(bool presentSwapChain) {
|
||||||
::XCEngine::RHI::RHICommandList* commandList = GetCurrentCommandList();
|
if (presentSwapChain && m_presenter.GetSwapChain() == nullptr) {
|
||||||
if (commandList == nullptr || m_commandQueue == nullptr) {
|
|
||||||
m_lastError = "SubmitFrame requires an initialized command list and queue.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (presentSwapChain && m_swapChain == nullptr) {
|
|
||||||
m_lastError = "SubmitFrame requested present without a swap chain.";
|
m_lastError = "SubmitFrame requested present without a swap chain.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
commandList->Close();
|
if (!m_hostDevice.SubmitFrame(m_activeBackBufferIndex)) {
|
||||||
void* commandLists[] = { commandList };
|
m_lastError = m_hostDevice.GetLastError();
|
||||||
m_commandQueue->ExecuteCommandLists(1, commandLists);
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (presentSwapChain) {
|
if (presentSwapChain) {
|
||||||
m_swapChain->Present(1, 0);
|
if (!m_presenter.PresentFrame()) {
|
||||||
|
m_lastError = m_presenter.GetLastError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
m_lastError.clear();
|
m_lastError.clear();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool D3D12WindowRenderer::SignalFrameCompletion() {
|
bool D3D12WindowRenderer::SignalFrameCompletion() {
|
||||||
ID3D12CommandQueue* commandQueue = GetCommandQueue();
|
if (!m_hostDevice.SignalFrameCompletion(m_activeBackBufferIndex)) {
|
||||||
if (commandQueue == nullptr || m_frameCompletionFence == nullptr) {
|
m_lastError = m_hostDevice.GetLastError();
|
||||||
return false;
|
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();
|
m_lastError.clear();
|
||||||
return true;
|
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 {
|
ID3D12Device* D3D12WindowRenderer::GetDevice() const {
|
||||||
const D3D12Device* device = GetD3D12Device();
|
return m_hostDevice.GetDevice();
|
||||||
return device != nullptr ? device->GetDevice() : nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ID3D12CommandQueue* D3D12WindowRenderer::GetCommandQueue() const {
|
ID3D12CommandQueue* D3D12WindowRenderer::GetCommandQueue() const {
|
||||||
const D3D12CommandQueue* queue = GetD3D12CommandQueue();
|
return m_hostDevice.GetCommandQueue();
|
||||||
return queue != nullptr ? queue->GetCommandQueue() : nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string& D3D12WindowRenderer::GetLastError() const {
|
const std::string& D3D12WindowRenderer::GetLastError() const {
|
||||||
@@ -339,235 +129,31 @@ const std::string& D3D12WindowRenderer::GetLastError() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
RHIDevice* D3D12WindowRenderer::GetRHIDevice() const {
|
RHIDevice* D3D12WindowRenderer::GetRHIDevice() const {
|
||||||
return m_device;
|
return m_hostDevice.GetRHIDevice();
|
||||||
}
|
}
|
||||||
|
|
||||||
RHISwapChain* D3D12WindowRenderer::GetSwapChain() const {
|
RHISwapChain* D3D12WindowRenderer::GetSwapChain() const {
|
||||||
return m_swapChain;
|
return m_presenter.GetSwapChain();
|
||||||
}
|
}
|
||||||
|
|
||||||
const ::XCEngine::Rendering::RenderSurface* D3D12WindowRenderer::GetCurrentRenderSurface() const {
|
const ::XCEngine::Rendering::RenderSurface* D3D12WindowRenderer::GetCurrentRenderSurface() const {
|
||||||
if (m_swapChain == nullptr) {
|
return m_presenter.GetCurrentRenderSurface();
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::uint32_t backBufferIndex = m_swapChain->GetCurrentBackBufferIndex();
|
|
||||||
if (backBufferIndex >= m_backBufferSurfaces.size()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return &m_backBufferSurfaces[backBufferIndex];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const D3D12Texture* D3D12WindowRenderer::GetCurrentBackBufferTexture() const {
|
const D3D12Texture* D3D12WindowRenderer::GetCurrentBackBufferTexture() const {
|
||||||
if (m_swapChain == nullptr) {
|
return m_presenter.GetCurrentBackBufferTexture();
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetBackBufferTexture(m_swapChain->GetCurrentBackBufferIndex());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const D3D12Texture* D3D12WindowRenderer::GetBackBufferTexture(std::uint32_t index) const {
|
const D3D12Texture* D3D12WindowRenderer::GetBackBufferTexture(std::uint32_t index) const {
|
||||||
const D3D12SwapChain* d3d12SwapChain = GetD3D12SwapChain();
|
return m_presenter.GetBackBufferTexture(index);
|
||||||
if (d3d12SwapChain == nullptr) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return d3d12SwapChain->TryGetBackBuffer(index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::uint32_t D3D12WindowRenderer::GetBackBufferCount() const {
|
std::uint32_t D3D12WindowRenderer::GetBackBufferCount() const {
|
||||||
return kSwapChainBufferCount;
|
return m_presenter.GetBackBufferCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
::XCEngine::Rendering::RenderContext D3D12WindowRenderer::GetRenderContext() const {
|
::XCEngine::Rendering::RenderContext D3D12WindowRenderer::GetRenderContext() const {
|
||||||
::XCEngine::Rendering::RenderContext context = {};
|
return m_hostDevice.GetRenderContext(m_activeBackBufferIndex);
|
||||||
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<D3D12Device*>(m_device) : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
D3D12CommandQueue* D3D12WindowRenderer::GetD3D12CommandQueue() const {
|
|
||||||
return m_commandQueue != nullptr ? static_cast<D3D12CommandQueue*>(m_commandQueue) : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
D3D12CommandList* D3D12WindowRenderer::GetD3D12CommandList() const {
|
|
||||||
::XCEngine::RHI::RHICommandList* commandList = GetCurrentCommandList();
|
|
||||||
return commandList != nullptr ? static_cast<D3D12CommandList*>(commandList) : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
D3D12SwapChain* D3D12WindowRenderer::GetD3D12SwapChain() const {
|
|
||||||
return m_swapChain != nullptr ? static_cast<D3D12SwapChain*>(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<std::uint32_t>(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<std::uint32_t>(m_width),
|
|
||||||
static_cast<std::uint32_t>(m_height));
|
|
||||||
surface.SetColorAttachment(m_backBufferViews[backBufferIndex]);
|
|
||||||
surface.SetAutoTransitionEnabled(false);
|
|
||||||
surface.SetColorStateBefore(ResourceStates::RenderTarget);
|
|
||||||
surface.SetColorStateAfter(ResourceStates::RenderTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace XCEngine::UI::Editor::Host
|
} // namespace XCEngine::UI::Editor::Host
|
||||||
|
|||||||
@@ -4,34 +4,24 @@
|
|||||||
#define NOMINMAX
|
#define NOMINMAX
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "D3D12HostDevice.h"
|
||||||
|
#include "D3D12WindowSwapChainPresenter.h"
|
||||||
|
|
||||||
#include <XCEngine/Rendering/RenderContext.h>
|
#include <XCEngine/Rendering/RenderContext.h>
|
||||||
#include <XCEngine/Rendering/RenderSurface.h>
|
#include <XCEngine/Rendering/RenderSurface.h>
|
||||||
#include <XCEngine/RHI/D3D12/D3D12CommandList.h>
|
|
||||||
#include <XCEngine/RHI/D3D12/D3D12CommandQueue.h>
|
|
||||||
#include <XCEngine/RHI/D3D12/D3D12Device.h>
|
|
||||||
#include <XCEngine/RHI/D3D12/D3D12SwapChain.h>
|
|
||||||
#include <XCEngine/RHI/D3D12/D3D12Texture.h>
|
#include <XCEngine/RHI/D3D12/D3D12Texture.h>
|
||||||
#include <XCEngine/RHI/RHICommandList.h>
|
|
||||||
#include <XCEngine/RHI/RHICommandQueue.h>
|
|
||||||
#include <XCEngine/RHI/RHIDescriptorPool.h>
|
|
||||||
#include <XCEngine/RHI/RHIDevice.h>
|
|
||||||
#include <XCEngine/RHI/RHIResourceView.h>
|
|
||||||
#include <XCEngine/RHI/RHITexture.h>
|
|
||||||
#include <XCEngine/RHI/RHISwapChain.h>
|
#include <XCEngine/RHI/RHISwapChain.h>
|
||||||
|
|
||||||
#include <d3d12.h>
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <wrl/client.h>
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace XCEngine::UI::Editor::Host {
|
namespace XCEngine::UI::Editor::Host {
|
||||||
|
|
||||||
class D3D12WindowRenderer {
|
class D3D12WindowRenderer {
|
||||||
public:
|
public:
|
||||||
static constexpr std::uint32_t kSwapChainBufferCount = 3;
|
static constexpr std::uint32_t kSwapChainBufferCount =
|
||||||
|
D3D12WindowSwapChainPresenter::kSwapChainBufferCount;
|
||||||
|
|
||||||
bool Initialize(HWND hwnd, int width, int height);
|
bool Initialize(HWND hwnd, int width, int height);
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
@@ -54,34 +44,9 @@ public:
|
|||||||
::XCEngine::Rendering::RenderContext GetRenderContext() const;
|
::XCEngine::Rendering::RenderContext GetRenderContext() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
::XCEngine::RHI::D3D12Device* GetD3D12Device() const;
|
D3D12HostDevice m_hostDevice = {};
|
||||||
::XCEngine::RHI::D3D12CommandQueue* GetD3D12CommandQueue() const;
|
D3D12WindowSwapChainPresenter m_presenter = {};
|
||||||
::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<ID3D12Fence> m_frameCompletionFence = {};
|
|
||||||
HANDLE m_frameCompletionEvent = nullptr;
|
|
||||||
std::array<std::uint64_t, kSwapChainBufferCount> m_backBufferFenceValues = {};
|
|
||||||
std::uint32_t m_activeBackBufferIndex = 0u;
|
std::uint32_t m_activeBackBufferIndex = 0u;
|
||||||
std::uint64_t m_lastSubmittedFrameValue = 0;
|
|
||||||
std::string m_lastError = {};
|
std::string m_lastError = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
371
new_editor/app/Host/D3D12WindowSwapChainPresenter.cpp
Normal file
371
new_editor/app/Host/D3D12WindowSwapChainPresenter.cpp
Normal file
@@ -0,0 +1,371 @@
|
|||||||
|
#include "D3D12WindowSwapChainPresenter.h"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
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<std::uint32_t>(width);
|
||||||
|
swapChainDesc.height = static_cast<std::uint32_t>(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<std::uint32_t>(width),
|
||||||
|
static_cast<std::uint32_t>(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<unsigned long>(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<int>(backBufferTexture->GetWidth());
|
||||||
|
m_height = static_cast<int>(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<D3D12SwapChain*>(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<std::uint32_t>(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<std::uint32_t>(m_width),
|
||||||
|
static_cast<std::uint32_t>(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
|
||||||
61
new_editor/app/Host/D3D12WindowSwapChainPresenter.h
Normal file
61
new_editor/app/Host/D3D12WindowSwapChainPresenter.h
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef NOMINMAX
|
||||||
|
#define NOMINMAX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "D3D12HostDevice.h"
|
||||||
|
|
||||||
|
#include <XCEngine/Rendering/RenderContext.h>
|
||||||
|
#include <XCEngine/Rendering/RenderSurface.h>
|
||||||
|
#include <XCEngine/RHI/D3D12/D3D12SwapChain.h>
|
||||||
|
#include <XCEngine/RHI/D3D12/D3D12Texture.h>
|
||||||
|
#include <XCEngine/RHI/RHIResourceView.h>
|
||||||
|
#include <XCEngine/RHI/RHISwapChain.h>
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
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
|
||||||
@@ -9,10 +9,6 @@ public:
|
|||||||
void Reset() {
|
void Reset() {
|
||||||
m_windowDpi = 96u;
|
m_windowDpi = 96u;
|
||||||
m_inInteractiveResize = false;
|
m_inInteractiveResize = false;
|
||||||
m_renderFrameQueued = false;
|
|
||||||
m_hasPendingWindowResize = false;
|
|
||||||
m_pendingWindowResizeWidth = 0u;
|
|
||||||
m_pendingWindowResizeHeight = 0u;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetWindowDpi(UINT dpi) {
|
void SetWindowDpi(UINT dpi) {
|
||||||
@@ -41,49 +37,9 @@ public:
|
|||||||
return m_inInteractiveResize;
|
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:
|
private:
|
||||||
UINT m_windowDpi = 96u;
|
UINT m_windowDpi = 96u;
|
||||||
bool m_inInteractiveResize = false;
|
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
|
} // namespace XCEngine::UI::Editor::Host
|
||||||
|
|||||||
@@ -185,11 +185,11 @@ bool NativeRenderer::AttachWindowRenderer(D3D12WindowRenderer& windowRenderer) {
|
|||||||
// fallback-only and should not stay alive while D3D11On12 interop is healthy.
|
// fallback-only and should not stay alive while D3D11On12 interop is healthy.
|
||||||
DiscardRenderTarget();
|
DiscardRenderTarget();
|
||||||
|
|
||||||
if (!m_backBufferInteropTargets.empty()) {
|
if (m_windowInterop.HasBackBufferTargets()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return RebuildBackBufferInteropTargets();
|
return m_windowInterop.RebuildBackBufferTargets();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NativeRenderer::DetachWindowRenderer() {
|
void NativeRenderer::DetachWindowRenderer() {
|
||||||
@@ -198,18 +198,7 @@ void NativeRenderer::DetachWindowRenderer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void NativeRenderer::ReleaseWindowRendererBackBufferTargets() {
|
void NativeRenderer::ReleaseWindowRendererBackBufferTargets() {
|
||||||
ClearActiveInteropSourceTextures();
|
m_windowInterop.ReleaseBackBufferTargets();
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NativeRenderer::RebuildWindowRendererBackBufferTargets() {
|
bool NativeRenderer::RebuildWindowRendererBackBufferTargets() {
|
||||||
@@ -219,14 +208,11 @@ bool NativeRenderer::RebuildWindowRendererBackBufferTargets() {
|
|||||||
|
|
||||||
DiscardRenderTarget();
|
DiscardRenderTarget();
|
||||||
ReleaseWindowRendererBackBufferTargets();
|
ReleaseWindowRendererBackBufferTargets();
|
||||||
return RebuildBackBufferInteropTargets();
|
return m_windowInterop.RebuildBackBufferTargets();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NativeRenderer::HasAttachedWindowRenderer() const {
|
bool NativeRenderer::HasAttachedWindowRenderer() const {
|
||||||
return m_windowRenderer != nullptr &&
|
return m_windowInterop.HasAttachedWindowRenderer();
|
||||||
m_d3d11On12Device != nullptr &&
|
|
||||||
m_d2dDeviceContext != nullptr &&
|
|
||||||
!m_backBufferInteropTargets.empty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NativeRenderer::Render(const ::XCEngine::UI::UIDrawData& drawData) {
|
bool NativeRenderer::Render(const ::XCEngine::UI::UIDrawData& drawData) {
|
||||||
@@ -262,19 +248,29 @@ bool NativeRenderer::RenderToWindowRenderer(const ::XCEngine::UI::UIDrawData& dr
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_backBufferInteropTargets.empty() &&
|
if (!m_windowInterop.HasBackBufferTargets() &&
|
||||||
!RebuildBackBufferInteropTargets()) {
|
!m_windowInterop.RebuildBackBufferTargets()) {
|
||||||
if (m_lastRenderError.empty()) {
|
if (m_lastRenderError.empty()) {
|
||||||
m_lastRenderError = "Window renderer back buffer interop targets are unavailable.";
|
m_lastRenderError = "Window renderer back buffer interop targets are unavailable.";
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::uint32_t backBufferIndex =
|
ID3D11On12Device* d3d11On12Device = m_windowInterop.GetD3D11On12Device();
|
||||||
m_windowRenderer->GetSwapChain() != nullptr
|
ID3D11DeviceContext* d3d11DeviceContext = m_windowInterop.GetD3D11DeviceContext();
|
||||||
? m_windowRenderer->GetSwapChain()->GetCurrentBackBufferIndex()
|
ID2D1DeviceContext* d2dDeviceContext = m_windowInterop.GetD2DDeviceContext();
|
||||||
: 0u;
|
ID2D1SolidColorBrush* interopBrush = m_windowInterop.GetInteropBrush();
|
||||||
if (backBufferIndex >= m_backBufferInteropTargets.size()) {
|
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.";
|
m_lastRenderError = "Back buffer interop target index is out of range.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -290,15 +286,15 @@ bool NativeRenderer::RenderToWindowRenderer(const ::XCEngine::UI::UIDrawData& dr
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!PrepareActiveInteropSourceTextures(drawData)) {
|
if (!m_windowInterop.PrepareSourceTextures(drawData)) {
|
||||||
ID3D11Resource* backBufferResource =
|
ID3D11Resource* backBufferResource =
|
||||||
m_backBufferInteropTargets[backBufferIndex].wrappedResource.Get();
|
m_windowInterop.GetWrappedBackBufferResource(backBufferIndex);
|
||||||
if (backBufferResource != nullptr) {
|
if (backBufferResource != nullptr) {
|
||||||
m_d3d11On12Device->AcquireWrappedResources(&backBufferResource, 1u);
|
d3d11On12Device->AcquireWrappedResources(&backBufferResource, 1u);
|
||||||
m_d3d11On12Device->ReleaseWrappedResources(&backBufferResource, 1u);
|
d3d11On12Device->ReleaseWrappedResources(&backBufferResource, 1u);
|
||||||
}
|
}
|
||||||
m_d3d11DeviceContext->Flush();
|
d3d11DeviceContext->Flush();
|
||||||
ClearActiveInteropSourceTextures();
|
m_windowInterop.ClearSourceTextures();
|
||||||
const bool signaled = m_windowRenderer->SignalFrameCompletion();
|
const bool signaled = m_windowRenderer->SignalFrameCompletion();
|
||||||
ReleaseWindowRendererInterop();
|
ReleaseWindowRendererInterop();
|
||||||
if (!signaled) {
|
if (!signaled) {
|
||||||
@@ -309,26 +305,26 @@ bool NativeRenderer::RenderToWindowRenderer(const ::XCEngine::UI::UIDrawData& dr
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ID3D11Resource*> acquiredResources = {};
|
std::vector<ID3D11Resource*> acquiredResources = {};
|
||||||
acquiredResources.reserve(1u + m_activeInteropSourceTextures.size());
|
m_windowInterop.BuildAcquiredResources(backBufferIndex, acquiredResources);
|
||||||
acquiredResources.push_back(m_backBufferInteropTargets[backBufferIndex].wrappedResource.Get());
|
if (acquiredResources.empty()) {
|
||||||
for (const D3D12SourceTextureInteropResource& texture : m_activeInteropSourceTextures) {
|
m_lastRenderError = "No wrapped interop resources were prepared for UI composition.";
|
||||||
acquiredResources.push_back(texture.wrappedResource.Get());
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_d3d11On12Device->AcquireWrappedResources(
|
d3d11On12Device->AcquireWrappedResources(
|
||||||
acquiredResources.data(),
|
acquiredResources.data(),
|
||||||
static_cast<UINT>(acquiredResources.size()));
|
static_cast<UINT>(acquiredResources.size()));
|
||||||
|
|
||||||
m_d2dDeviceContext->SetTarget(m_backBufferInteropTargets[backBufferIndex].targetBitmap.Get());
|
d2dDeviceContext->SetTarget(m_windowInterop.GetBackBufferTargetBitmap(backBufferIndex));
|
||||||
const bool rendered = RenderToTarget(*m_d2dDeviceContext.Get(), *m_interopBrush.Get(), drawData);
|
const bool rendered = RenderToTarget(*d2dDeviceContext, *interopBrush, drawData);
|
||||||
const HRESULT hr = m_d2dDeviceContext->EndDraw();
|
const HRESULT hr = d2dDeviceContext->EndDraw();
|
||||||
|
|
||||||
m_d3d11On12Device->ReleaseWrappedResources(
|
d3d11On12Device->ReleaseWrappedResources(
|
||||||
acquiredResources.data(),
|
acquiredResources.data(),
|
||||||
static_cast<UINT>(acquiredResources.size()));
|
static_cast<UINT>(acquiredResources.size()));
|
||||||
m_d3d11DeviceContext->Flush();
|
d3d11DeviceContext->Flush();
|
||||||
m_d2dDeviceContext->SetTarget(nullptr);
|
d2dDeviceContext->SetTarget(nullptr);
|
||||||
ClearActiveInteropSourceTextures();
|
m_windowInterop.ClearSourceTextures();
|
||||||
|
|
||||||
if (!rendered || FAILED(hr)) {
|
if (!rendered || FAILED(hr)) {
|
||||||
m_lastRenderError = FAILED(hr)
|
m_lastRenderError = FAILED(hr)
|
||||||
@@ -604,252 +600,18 @@ bool NativeRenderer::EnsureWindowRendererInterop() {
|
|||||||
m_lastRenderError = "EnsureWindowRendererInterop requires initialized D2D and DWrite factories.";
|
m_lastRenderError = "EnsureWindowRendererInterop requires initialized D2D and DWrite factories.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (m_d3d11On12Device != nullptr &&
|
|
||||||
m_d2dDeviceContext != nullptr &&
|
const bool attached = m_windowInterop.Attach(*m_windowRenderer, *m_d2dFactory.Get());
|
||||||
m_interopBrush != nullptr) {
|
if (!attached) {
|
||||||
return true;
|
m_lastRenderError = m_windowInterop.GetLastError();
|
||||||
|
} else {
|
||||||
|
m_lastRenderError.clear();
|
||||||
}
|
}
|
||||||
|
return attached;
|
||||||
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<D3D_FEATURE_LEVEL, 4> featureLevels = {
|
|
||||||
D3D_FEATURE_LEVEL_12_1,
|
|
||||||
D3D_FEATURE_LEVEL_12_0,
|
|
||||||
D3D_FEATURE_LEVEL_11_1,
|
|
||||||
D3D_FEATURE_LEVEL_11_0
|
|
||||||
};
|
|
||||||
const std::array<IUnknown*, 1> commandQueues = {
|
|
||||||
reinterpret_cast<IUnknown*>(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<UINT>(featureLevels.size()),
|
|
||||||
commandQueues.data(),
|
|
||||||
static_cast<UINT>(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<UINT>(featureLevels.size()),
|
|
||||||
commandQueues.data(),
|
|
||||||
static_cast<UINT>(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<IDXGIDevice> 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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NativeRenderer::ReleaseWindowRendererInterop() {
|
void NativeRenderer::ReleaseWindowRendererInterop() {
|
||||||
ReleaseWindowRendererBackBufferTargets();
|
m_windowInterop.Detach();
|
||||||
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<IDXGISurface> 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<IDXGISurface> 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NativeRenderer::EnsureRenderTarget() {
|
bool NativeRenderer::EnsureRenderTarget() {
|
||||||
@@ -1068,18 +830,7 @@ bool NativeRenderer::ResolveTextureBitmap(
|
|||||||
bool NativeRenderer::ResolveInteropBitmap(
|
bool NativeRenderer::ResolveInteropBitmap(
|
||||||
const ::XCEngine::UI::UITextureHandle& texture,
|
const ::XCEngine::UI::UITextureHandle& texture,
|
||||||
Microsoft::WRL::ComPtr<ID2D1Bitmap>& outBitmap) const {
|
Microsoft::WRL::ComPtr<ID2D1Bitmap>& outBitmap) const {
|
||||||
outBitmap.Reset();
|
return m_windowInterop.ResolveInteropBitmap(texture, outBitmap);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NativeRenderer::RenderToTarget(
|
bool NativeRenderer::RenderToTarget(
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#define NOMINMAX
|
#define NOMINMAX
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "D3D12WindowInteropContext.h"
|
||||||
#include "D3D12WindowRenderer.h"
|
#include "D3D12WindowRenderer.h"
|
||||||
|
|
||||||
#include <XCEditor/Foundation/UIEditorTextMeasurement.h>
|
#include <XCEditor/Foundation/UIEditorTextMeasurement.h>
|
||||||
@@ -65,15 +66,6 @@ private:
|
|||||||
UINT width = 0u;
|
UINT width = 0u;
|
||||||
UINT height = 0u;
|
UINT height = 0u;
|
||||||
};
|
};
|
||||||
struct D3D12BackBufferInteropTarget {
|
|
||||||
Microsoft::WRL::ComPtr<ID3D11Resource> wrappedResource = {};
|
|
||||||
Microsoft::WRL::ComPtr<ID2D1Bitmap1> targetBitmap = {};
|
|
||||||
};
|
|
||||||
struct D3D12SourceTextureInteropResource {
|
|
||||||
std::uintptr_t key = 0u;
|
|
||||||
Microsoft::WRL::ComPtr<ID3D11Resource> wrappedResource = {};
|
|
||||||
Microsoft::WRL::ComPtr<ID2D1Bitmap1> bitmap = {};
|
|
||||||
};
|
|
||||||
|
|
||||||
bool EnsureRenderTarget();
|
bool EnsureRenderTarget();
|
||||||
bool EnsureWindowRendererInterop();
|
bool EnsureWindowRendererInterop();
|
||||||
@@ -81,9 +73,6 @@ private:
|
|||||||
void DiscardRenderTarget();
|
void DiscardRenderTarget();
|
||||||
bool CreateDeviceResources();
|
bool CreateDeviceResources();
|
||||||
void ReleaseWindowRendererInterop();
|
void ReleaseWindowRendererInterop();
|
||||||
bool RebuildBackBufferInteropTargets();
|
|
||||||
void ClearActiveInteropSourceTextures();
|
|
||||||
bool PrepareActiveInteropSourceTextures(const ::XCEngine::UI::UIDrawData& drawData);
|
|
||||||
void InvalidateCachedTextureBitmaps(const ID2D1RenderTarget* renderTarget);
|
void InvalidateCachedTextureBitmaps(const ID2D1RenderTarget* renderTarget);
|
||||||
bool RenderToTarget(
|
bool RenderToTarget(
|
||||||
ID2D1RenderTarget& renderTarget,
|
ID2D1RenderTarget& renderTarget,
|
||||||
@@ -115,19 +104,11 @@ private:
|
|||||||
Microsoft::WRL::ComPtr<ID2D1Factory1> m_d2dFactory;
|
Microsoft::WRL::ComPtr<ID2D1Factory1> m_d2dFactory;
|
||||||
Microsoft::WRL::ComPtr<IDWriteFactory> m_dwriteFactory;
|
Microsoft::WRL::ComPtr<IDWriteFactory> m_dwriteFactory;
|
||||||
Microsoft::WRL::ComPtr<IWICImagingFactory> m_wicFactory;
|
Microsoft::WRL::ComPtr<IWICImagingFactory> m_wicFactory;
|
||||||
Microsoft::WRL::ComPtr<ID3D11Device> m_d3d11Device;
|
|
||||||
Microsoft::WRL::ComPtr<ID3D11DeviceContext> m_d3d11DeviceContext;
|
|
||||||
Microsoft::WRL::ComPtr<ID3D11On12Device> m_d3d11On12Device;
|
|
||||||
Microsoft::WRL::ComPtr<ID2D1Device> m_d2dDevice;
|
|
||||||
Microsoft::WRL::ComPtr<ID2D1DeviceContext> m_d2dDeviceContext;
|
|
||||||
Microsoft::WRL::ComPtr<ID2D1HwndRenderTarget> m_renderTarget;
|
Microsoft::WRL::ComPtr<ID2D1HwndRenderTarget> m_renderTarget;
|
||||||
Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> m_solidBrush;
|
Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> m_solidBrush;
|
||||||
Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> m_interopBrush;
|
|
||||||
std::vector<D3D12BackBufferInteropTarget> m_backBufferInteropTargets = {};
|
|
||||||
std::vector<D3D12SourceTextureInteropResource> m_activeInteropSourceTextures = {};
|
|
||||||
std::unordered_map<std::uintptr_t, Microsoft::WRL::ComPtr<ID2D1Bitmap1>> m_activeInteropBitmaps;
|
|
||||||
mutable std::unordered_map<int, Microsoft::WRL::ComPtr<IDWriteTextFormat>> m_textFormats;
|
mutable std::unordered_map<int, Microsoft::WRL::ComPtr<IDWriteTextFormat>> m_textFormats;
|
||||||
std::unordered_set<NativeTextureResource*> m_liveTextures;
|
std::unordered_set<NativeTextureResource*> m_liveTextures;
|
||||||
|
D3D12WindowInteropContext m_windowInterop = {};
|
||||||
std::string m_lastRenderError = {};
|
std::string m_lastRenderError = {};
|
||||||
bool m_wicComInitialized = false;
|
bool m_wicComInitialized = false;
|
||||||
float m_dpiScale = 1.0f;
|
float m_dpiScale = 1.0f;
|
||||||
|
|||||||
@@ -4,10 +4,6 @@
|
|||||||
|
|
||||||
namespace XCEngine::UI::Editor::Host {
|
namespace XCEngine::UI::Editor::Host {
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
constexpr UINT kDeferredRenderMessage = WM_APP + 1u;
|
|
||||||
|
|
||||||
void TryEnableNonClientDpiScaling(HWND hwnd) {
|
void TryEnableNonClientDpiScaling(HWND hwnd) {
|
||||||
if (hwnd == nullptr) {
|
if (hwnd == nullptr) {
|
||||||
return;
|
return;
|
||||||
@@ -27,8 +23,6 @@ void TryEnableNonClientDpiScaling(HWND hwnd) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
Application* WindowMessageDispatcher::GetApplicationFromWindow(HWND hwnd) {
|
Application* WindowMessageDispatcher::GetApplicationFromWindow(HWND hwnd) {
|
||||||
return reinterpret_cast<Application*>(GetWindowLongPtrW(hwnd, GWLP_USERDATA));
|
return reinterpret_cast<Application*>(GetWindowLongPtrW(hwnd, GWLP_USERDATA));
|
||||||
}
|
}
|
||||||
@@ -70,7 +64,9 @@ bool WindowMessageDispatcher::TryDispatch(
|
|||||||
application.OnDpiChanged(
|
application.OnDpiChanged(
|
||||||
static_cast<UINT>(LOWORD(wParam)),
|
static_cast<UINT>(LOWORD(wParam)),
|
||||||
*reinterpret_cast<const RECT*>(lParam));
|
*reinterpret_cast<const RECT*>(lParam));
|
||||||
RequestDeferredRenderFrame(application);
|
if (application.m_renderReady) {
|
||||||
|
application.RenderFrame();
|
||||||
|
}
|
||||||
outResult = 0;
|
outResult = 0;
|
||||||
return true;
|
return true;
|
||||||
case WM_ENTERSIZEMOVE:
|
case WM_ENTERSIZEMOVE:
|
||||||
@@ -79,20 +75,22 @@ bool WindowMessageDispatcher::TryDispatch(
|
|||||||
return true;
|
return true;
|
||||||
case WM_EXITSIZEMOVE:
|
case WM_EXITSIZEMOVE:
|
||||||
application.OnExitSizeMove();
|
application.OnExitSizeMove();
|
||||||
RequestDeferredRenderFrame(application);
|
if (application.m_renderReady) {
|
||||||
|
application.RenderFrame();
|
||||||
|
}
|
||||||
outResult = 0;
|
outResult = 0;
|
||||||
return true;
|
return true;
|
||||||
case WM_SIZE:
|
case WM_SIZE:
|
||||||
if (wParam != SIZE_MINIMIZED) {
|
if (wParam != SIZE_MINIMIZED) {
|
||||||
application.OnResize();
|
application.OnResize(
|
||||||
RequestDeferredRenderFrame(application);
|
static_cast<UINT>(LOWORD(lParam)),
|
||||||
|
static_cast<UINT>(HIWORD(lParam)));
|
||||||
|
if (application.m_renderReady) {
|
||||||
|
application.RenderFrame();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
outResult = 0;
|
outResult = 0;
|
||||||
return true;
|
return true;
|
||||||
case kDeferredRenderMessage:
|
|
||||||
application.OnDeferredRenderMessage();
|
|
||||||
outResult = 0;
|
|
||||||
return true;
|
|
||||||
case WM_PAINT:
|
case WM_PAINT:
|
||||||
application.OnPaintMessage();
|
application.OnPaintMessage();
|
||||||
outResult = 0;
|
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
|
} // namespace XCEngine::UI::Editor::Host
|
||||||
|
|||||||
@@ -26,9 +26,6 @@ public:
|
|||||||
WPARAM wParam,
|
WPARAM wParam,
|
||||||
LPARAM lParam,
|
LPARAM lParam,
|
||||||
LRESULT& outResult);
|
LRESULT& outResult);
|
||||||
|
|
||||||
private:
|
|
||||||
static void RequestDeferredRenderFrame(Application& application);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace XCEngine::UI::Editor::Host
|
} // namespace XCEngine::UI::Editor::Host
|
||||||
|
|||||||
Reference in New Issue
Block a user