Fix new editor window resize presentation
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#include "NativeRenderer.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
@@ -53,6 +54,41 @@ float ResolveStrokePixelOffset(float thickness) {
|
||||
return std::fmod(roundedThickness, 2.0f) == 1.0f ? 0.5f : 0.0f;
|
||||
}
|
||||
|
||||
D2D1_BITMAP_PROPERTIES1 BuildD2DBitmapProperties(
|
||||
DXGI_FORMAT format,
|
||||
D2D1_BITMAP_OPTIONS options,
|
||||
D2D1_ALPHA_MODE alphaMode = D2D1_ALPHA_MODE_IGNORE) {
|
||||
return D2D1::BitmapProperties1(
|
||||
options,
|
||||
D2D1::PixelFormat(format, alphaMode),
|
||||
kBaseDpi,
|
||||
kBaseDpi);
|
||||
}
|
||||
|
||||
bool IsInteropTextureHandle(const ::XCEngine::UI::UITextureHandle& texture) {
|
||||
return texture.kind == ::XCEngine::UI::UITextureHandleKind::ShaderResourceView &&
|
||||
texture.resourceHandle != 0u;
|
||||
}
|
||||
|
||||
bool 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);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool NativeRenderer::Initialize(HWND hwnd) {
|
||||
@@ -64,7 +100,15 @@ bool NativeRenderer::Initialize(HWND hwnd) {
|
||||
}
|
||||
|
||||
m_hwnd = hwnd;
|
||||
HRESULT hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, m_d2dFactory.ReleaseAndGetAddressOf());
|
||||
D2D1_FACTORY_OPTIONS factoryOptions = {};
|
||||
#ifdef _DEBUG
|
||||
factoryOptions.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
|
||||
#endif
|
||||
HRESULT hr = D2D1CreateFactory(
|
||||
D2D1_FACTORY_TYPE_SINGLE_THREADED,
|
||||
__uuidof(ID2D1Factory1),
|
||||
&factoryOptions,
|
||||
reinterpret_cast<void**>(m_d2dFactory.ReleaseAndGetAddressOf()));
|
||||
if (FAILED(hr)) {
|
||||
m_lastRenderError = HrToString("D2D1CreateFactory", hr);
|
||||
Shutdown();
|
||||
@@ -82,10 +126,11 @@ bool NativeRenderer::Initialize(HWND hwnd) {
|
||||
}
|
||||
|
||||
m_lastRenderError.clear();
|
||||
return EnsureRenderTarget();
|
||||
return true;
|
||||
}
|
||||
|
||||
void NativeRenderer::Shutdown() {
|
||||
DetachWindowRenderer();
|
||||
while (!m_liveTextures.empty()) {
|
||||
auto it = m_liveTextures.begin();
|
||||
delete *it;
|
||||
@@ -126,6 +171,64 @@ void NativeRenderer::Resize(UINT width, UINT height) {
|
||||
}
|
||||
}
|
||||
|
||||
bool NativeRenderer::AttachWindowRenderer(D3D12WindowRenderer& windowRenderer) {
|
||||
if (m_windowRenderer != &windowRenderer) {
|
||||
ReleaseWindowRendererInterop();
|
||||
m_windowRenderer = &windowRenderer;
|
||||
}
|
||||
|
||||
if (!EnsureWindowRendererInterop()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Keep a single real window presentation path. The hwnd render target is
|
||||
// fallback-only and should not stay alive while D3D11On12 interop is healthy.
|
||||
DiscardRenderTarget();
|
||||
|
||||
if (!m_backBufferInteropTargets.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return RebuildBackBufferInteropTargets();
|
||||
}
|
||||
|
||||
void NativeRenderer::DetachWindowRenderer() {
|
||||
ReleaseWindowRendererInterop();
|
||||
m_windowRenderer = nullptr;
|
||||
}
|
||||
|
||||
void NativeRenderer::ReleaseWindowRendererBackBufferTargets() {
|
||||
ClearActiveInteropSourceTextures();
|
||||
if (m_d2dDeviceContext != nullptr) {
|
||||
m_d2dDeviceContext->SetTarget(nullptr);
|
||||
D2D1_TAG firstTag = 0u;
|
||||
D2D1_TAG secondTag = 0u;
|
||||
m_d2dDeviceContext->Flush(&firstTag, &secondTag);
|
||||
}
|
||||
if (m_d3d11DeviceContext != nullptr) {
|
||||
m_d3d11DeviceContext->ClearState();
|
||||
m_d3d11DeviceContext->Flush();
|
||||
}
|
||||
m_backBufferInteropTargets.clear();
|
||||
}
|
||||
|
||||
bool NativeRenderer::RebuildWindowRendererBackBufferTargets() {
|
||||
if (!EnsureWindowRendererInterop()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DiscardRenderTarget();
|
||||
ReleaseWindowRendererBackBufferTargets();
|
||||
return RebuildBackBufferInteropTargets();
|
||||
}
|
||||
|
||||
bool NativeRenderer::HasAttachedWindowRenderer() const {
|
||||
return m_windowRenderer != nullptr &&
|
||||
m_d3d11On12Device != nullptr &&
|
||||
m_d2dDeviceContext != nullptr &&
|
||||
!m_backBufferInteropTargets.empty();
|
||||
}
|
||||
|
||||
bool NativeRenderer::Render(const ::XCEngine::UI::UIDrawData& drawData) {
|
||||
if (!EnsureRenderTarget()) {
|
||||
if (m_lastRenderError.empty()) {
|
||||
@@ -151,6 +254,110 @@ bool NativeRenderer::Render(const ::XCEngine::UI::UIDrawData& drawData) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NativeRenderer::RenderToWindowRenderer(const ::XCEngine::UI::UIDrawData& drawData) {
|
||||
if (!EnsureWindowRendererInterop()) {
|
||||
if (m_lastRenderError.empty()) {
|
||||
m_lastRenderError = "Window renderer interop is not available.";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_backBufferInteropTargets.empty() &&
|
||||
!RebuildBackBufferInteropTargets()) {
|
||||
if (m_lastRenderError.empty()) {
|
||||
m_lastRenderError = "Window renderer back buffer interop targets are unavailable.";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::uint32_t backBufferIndex =
|
||||
m_windowRenderer->GetSwapChain() != nullptr
|
||||
? m_windowRenderer->GetSwapChain()->GetCurrentBackBufferIndex()
|
||||
: 0u;
|
||||
if (backBufferIndex >= m_backBufferInteropTargets.size()) {
|
||||
m_lastRenderError = "Back buffer interop target index is out of range.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_windowRenderer->PreparePresentSurface()) {
|
||||
m_lastRenderError = "Failed to prepare the D3D12 present surface: " +
|
||||
m_windowRenderer->GetLastError();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_windowRenderer->SubmitFrame(false)) {
|
||||
m_lastRenderError = "Failed to submit the D3D12 frame before UI composition.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!PrepareActiveInteropSourceTextures(drawData)) {
|
||||
ID3D11Resource* backBufferResource =
|
||||
m_backBufferInteropTargets[backBufferIndex].wrappedResource.Get();
|
||||
if (backBufferResource != nullptr) {
|
||||
m_d3d11On12Device->AcquireWrappedResources(&backBufferResource, 1u);
|
||||
m_d3d11On12Device->ReleaseWrappedResources(&backBufferResource, 1u);
|
||||
}
|
||||
m_d3d11DeviceContext->Flush();
|
||||
ClearActiveInteropSourceTextures();
|
||||
const bool signaled = m_windowRenderer->SignalFrameCompletion();
|
||||
ReleaseWindowRendererInterop();
|
||||
if (!signaled) {
|
||||
m_lastRenderError =
|
||||
"Failed to signal D3D12 frame completion after interop preparation failed.";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<ID3D11Resource*> acquiredResources = {};
|
||||
acquiredResources.reserve(1u + m_activeInteropSourceTextures.size());
|
||||
acquiredResources.push_back(m_backBufferInteropTargets[backBufferIndex].wrappedResource.Get());
|
||||
for (const D3D12SourceTextureInteropResource& texture : m_activeInteropSourceTextures) {
|
||||
acquiredResources.push_back(texture.wrappedResource.Get());
|
||||
}
|
||||
|
||||
m_d3d11On12Device->AcquireWrappedResources(
|
||||
acquiredResources.data(),
|
||||
static_cast<UINT>(acquiredResources.size()));
|
||||
|
||||
m_d2dDeviceContext->SetTarget(m_backBufferInteropTargets[backBufferIndex].targetBitmap.Get());
|
||||
const bool rendered = RenderToTarget(*m_d2dDeviceContext.Get(), *m_interopBrush.Get(), drawData);
|
||||
const HRESULT hr = m_d2dDeviceContext->EndDraw();
|
||||
|
||||
m_d3d11On12Device->ReleaseWrappedResources(
|
||||
acquiredResources.data(),
|
||||
static_cast<UINT>(acquiredResources.size()));
|
||||
m_d3d11DeviceContext->Flush();
|
||||
m_d2dDeviceContext->SetTarget(nullptr);
|
||||
ClearActiveInteropSourceTextures();
|
||||
|
||||
if (!rendered || FAILED(hr)) {
|
||||
m_lastRenderError = FAILED(hr)
|
||||
? HrToString("ID2D1DeviceContext::EndDraw", hr)
|
||||
: "RenderToTarget failed during D3D11On12 composition.";
|
||||
const bool signaled = m_windowRenderer->SignalFrameCompletion();
|
||||
ReleaseWindowRendererInterop();
|
||||
if (!signaled) {
|
||||
m_lastRenderError =
|
||||
"Failed to signal D3D12 frame completion after UI composition failed.";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_windowRenderer->SignalFrameCompletion()) {
|
||||
m_lastRenderError = "Failed to signal D3D12 frame completion after UI composition.";
|
||||
ReleaseWindowRendererInterop();
|
||||
return false;
|
||||
}
|
||||
if (!m_windowRenderer->PresentFrame()) {
|
||||
m_lastRenderError = "Failed to present the D3D12 swap chain.";
|
||||
ReleaseWindowRendererInterop();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_lastRenderError.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string& NativeRenderer::GetLastRenderError() const {
|
||||
return m_lastRenderError;
|
||||
}
|
||||
@@ -388,6 +595,263 @@ bool NativeRenderer::CaptureToPng(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NativeRenderer::EnsureWindowRendererInterop() {
|
||||
if (m_windowRenderer == nullptr) {
|
||||
m_lastRenderError = "EnsureWindowRendererInterop requires an attached D3D12 window renderer.";
|
||||
return false;
|
||||
}
|
||||
if (m_d2dFactory == nullptr || m_dwriteFactory == nullptr) {
|
||||
m_lastRenderError = "EnsureWindowRendererInterop requires initialized D2D and DWrite factories.";
|
||||
return false;
|
||||
}
|
||||
if (m_d3d11On12Device != nullptr &&
|
||||
m_d2dDeviceContext != nullptr &&
|
||||
m_interopBrush != nullptr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
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() {
|
||||
ReleaseWindowRendererBackBufferTargets();
|
||||
m_interopBrush.Reset();
|
||||
m_d2dDeviceContext.Reset();
|
||||
m_d2dDevice.Reset();
|
||||
m_d3d11On12Device.Reset();
|
||||
m_d3d11DeviceContext.Reset();
|
||||
m_d3d11Device.Reset();
|
||||
}
|
||||
|
||||
bool NativeRenderer::RebuildBackBufferInteropTargets() {
|
||||
m_backBufferInteropTargets.clear();
|
||||
if (m_windowRenderer == nullptr || m_d3d11On12Device == nullptr || m_d2dDeviceContext == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::uint32_t backBufferCount = m_windowRenderer->GetBackBufferCount();
|
||||
m_backBufferInteropTargets.resize(backBufferCount);
|
||||
for (std::uint32_t index = 0; index < backBufferCount; ++index) {
|
||||
const ::XCEngine::RHI::D3D12Texture* backBufferTexture =
|
||||
m_windowRenderer->GetBackBufferTexture(index);
|
||||
if (backBufferTexture == nullptr || backBufferTexture->GetResource() == nullptr) {
|
||||
m_lastRenderError = "Failed to resolve a D3D12 swap chain back buffer.";
|
||||
m_backBufferInteropTargets.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
D3D11_RESOURCE_FLAGS resourceFlags = {};
|
||||
resourceFlags.BindFlags = D3D11_BIND_RENDER_TARGET;
|
||||
HRESULT hr = m_d3d11On12Device->CreateWrappedResource(
|
||||
backBufferTexture->GetResource(),
|
||||
&resourceFlags,
|
||||
D3D12_RESOURCE_STATE_RENDER_TARGET,
|
||||
D3D12_RESOURCE_STATE_PRESENT,
|
||||
IID_PPV_ARGS(m_backBufferInteropTargets[index].wrappedResource.ReleaseAndGetAddressOf()));
|
||||
if (FAILED(hr) || m_backBufferInteropTargets[index].wrappedResource == nullptr) {
|
||||
m_lastRenderError = HrToString("ID3D11On12Device::CreateWrappedResource(backbuffer)", hr);
|
||||
m_backBufferInteropTargets.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
Microsoft::WRL::ComPtr<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() {
|
||||
if (!m_hwnd || !m_d2dFactory || !m_dwriteFactory) {
|
||||
m_lastRenderError = "EnsureRenderTarget requires hwnd, D2D factory, and DWrite factory.";
|
||||
@@ -601,6 +1065,23 @@ bool NativeRenderer::ResolveTextureBitmap(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NativeRenderer::ResolveInteropBitmap(
|
||||
const ::XCEngine::UI::UITextureHandle& texture,
|
||||
Microsoft::WRL::ComPtr<ID2D1Bitmap>& outBitmap) const {
|
||||
outBitmap.Reset();
|
||||
if (!IsInteropTextureHandle(texture)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto found = m_activeInteropBitmaps.find(texture.resourceHandle);
|
||||
if (found == m_activeInteropBitmaps.end() || found->second == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
outBitmap = found->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NativeRenderer::RenderToTarget(
|
||||
ID2D1RenderTarget& renderTarget,
|
||||
ID2D1SolidColorBrush& solidBrush,
|
||||
@@ -798,23 +1279,33 @@ void NativeRenderer::RenderCommand(
|
||||
break;
|
||||
}
|
||||
|
||||
auto* texture = reinterpret_cast<NativeTextureResource*>(command.texture.nativeHandle);
|
||||
if (texture == nullptr || m_liveTextures.find(texture) == m_liveTextures.end()) {
|
||||
const D2D1_RECT_F rect = ToD2DRect(command.rect, dpiScale);
|
||||
renderTarget.DrawRectangle(rect, &solidBrush, 1.0f);
|
||||
break;
|
||||
}
|
||||
|
||||
Microsoft::WRL::ComPtr<ID2D1Bitmap> bitmap;
|
||||
if (!ResolveTextureBitmap(renderTarget, *texture, bitmap) || !bitmap) {
|
||||
break;
|
||||
if (command.texture.kind == ::XCEngine::UI::UITextureHandleKind::ShaderResourceView) {
|
||||
if (!ResolveInteropBitmap(command.texture, bitmap) || !bitmap) {
|
||||
const D2D1_RECT_F rect = ToD2DRect(command.rect, dpiScale);
|
||||
renderTarget.DrawRectangle(rect, &solidBrush, 1.0f);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
auto* texture = reinterpret_cast<NativeTextureResource*>(command.texture.nativeHandle);
|
||||
if (texture == nullptr || m_liveTextures.find(texture) == m_liveTextures.end()) {
|
||||
const D2D1_RECT_F rect = ToD2DRect(command.rect, dpiScale);
|
||||
renderTarget.DrawRectangle(rect, &solidBrush, 1.0f);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ResolveTextureBitmap(renderTarget, *texture, bitmap) || !bitmap) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const D2D1_RECT_F rect = ToD2DRect(command.rect, dpiScale);
|
||||
const float sourceLeft = static_cast<float>(texture->width) * std::clamp(command.uvMin.x, 0.0f, 1.0f);
|
||||
const float sourceTop = static_cast<float>(texture->height) * std::clamp(command.uvMin.y, 0.0f, 1.0f);
|
||||
const float sourceRight = static_cast<float>(texture->width) * std::clamp(command.uvMax.x, 0.0f, 1.0f);
|
||||
const float sourceBottom = static_cast<float>(texture->height) * std::clamp(command.uvMax.y, 0.0f, 1.0f);
|
||||
const float sourceWidth = static_cast<float>(command.texture.width);
|
||||
const float sourceHeight = static_cast<float>(command.texture.height);
|
||||
const float sourceLeft = sourceWidth * std::clamp(command.uvMin.x, 0.0f, 1.0f);
|
||||
const float sourceTop = sourceHeight * std::clamp(command.uvMin.y, 0.0f, 1.0f);
|
||||
const float sourceRight = sourceWidth * std::clamp(command.uvMax.x, 0.0f, 1.0f);
|
||||
const float sourceBottom = sourceHeight * std::clamp(command.uvMax.y, 0.0f, 1.0f);
|
||||
renderTarget.DrawBitmap(
|
||||
bitmap.Get(),
|
||||
rect,
|
||||
|
||||
Reference in New Issue
Block a user