2026-04-15 22:47:42 +08:00
|
|
|
#include "D3D12WindowInteropInternal.h"
|
2026-04-15 08:24:06 +08:00
|
|
|
|
|
|
|
|
#include <array>
|
|
|
|
|
|
|
|
|
|
namespace XCEngine::UI::Editor::Host {
|
|
|
|
|
|
2026-04-15 22:47:42 +08:00
|
|
|
using namespace D3D12WindowInteropInternal;
|
2026-04-15 08:24:06 +08:00
|
|
|
|
|
|
|
|
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
|