refactor(new_editor/app): reorganize host structure and add smoke test
This commit is contained in:
255
new_editor/app/Rendering/Native/NativeRendererLifecycle.cpp
Normal file
255
new_editor/app/Rendering/Native/NativeRendererLifecycle.cpp
Normal file
@@ -0,0 +1,255 @@
|
||||
#include "NativeRendererSupport.h"
|
||||
|
||||
namespace XCEngine::UI::Editor::Host {
|
||||
|
||||
using namespace NativeRendererSupport;
|
||||
|
||||
bool NativeRenderer::Initialize(HWND hwnd) {
|
||||
Shutdown();
|
||||
|
||||
if (hwnd == nullptr) {
|
||||
m_lastRenderError = "Initialize rejected a null hwnd.";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_hwnd = hwnd;
|
||||
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();
|
||||
return false;
|
||||
}
|
||||
|
||||
hr = DWriteCreateFactory(
|
||||
DWRITE_FACTORY_TYPE_SHARED,
|
||||
__uuidof(IDWriteFactory),
|
||||
reinterpret_cast<IUnknown**>(m_dwriteFactory.ReleaseAndGetAddressOf()));
|
||||
if (FAILED(hr)) {
|
||||
m_lastRenderError = HrToString("DWriteCreateFactory", hr);
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_lastRenderError.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
void NativeRenderer::Shutdown() {
|
||||
DetachWindowRenderer();
|
||||
while (!m_liveTextures.empty()) {
|
||||
auto it = m_liveTextures.begin();
|
||||
delete *it;
|
||||
m_liveTextures.erase(it);
|
||||
}
|
||||
m_textFormats.clear();
|
||||
m_solidBrush.Reset();
|
||||
m_renderTarget.Reset();
|
||||
m_wicFactory.Reset();
|
||||
m_dwriteFactory.Reset();
|
||||
m_d2dFactory.Reset();
|
||||
if (m_wicComInitialized) {
|
||||
CoUninitialize();
|
||||
m_wicComInitialized = false;
|
||||
}
|
||||
m_hwnd = nullptr;
|
||||
}
|
||||
|
||||
void NativeRenderer::SetDpiScale(float dpiScale) {
|
||||
m_dpiScale = ClampDpiScale(dpiScale);
|
||||
if (m_renderTarget) {
|
||||
m_renderTarget->SetDpi(kBaseDpi, kBaseDpi);
|
||||
}
|
||||
}
|
||||
|
||||
float NativeRenderer::GetDpiScale() const {
|
||||
return m_dpiScale;
|
||||
}
|
||||
|
||||
void NativeRenderer::Resize(UINT width, UINT height) {
|
||||
if (!m_renderTarget || width == 0 || height == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const HRESULT hr = m_renderTarget->Resize(D2D1::SizeU(width, height));
|
||||
if (hr == D2DERR_RECREATE_TARGET) {
|
||||
DiscardRenderTarget();
|
||||
}
|
||||
}
|
||||
|
||||
bool NativeRenderer::AttachWindowRenderer(D3D12WindowRenderer& windowRenderer) {
|
||||
if (m_windowRenderer != &windowRenderer) {
|
||||
ReleaseWindowRendererInterop();
|
||||
m_windowRenderer = &windowRenderer;
|
||||
}
|
||||
|
||||
if (!EnsureWindowRendererInterop()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DiscardRenderTarget();
|
||||
|
||||
if (m_windowInterop.HasBackBufferTargets()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return m_windowInterop.RebuildBackBufferTargets();
|
||||
}
|
||||
|
||||
void NativeRenderer::DetachWindowRenderer() {
|
||||
ReleaseWindowRendererInterop();
|
||||
m_windowRenderer = nullptr;
|
||||
}
|
||||
|
||||
void NativeRenderer::ReleaseWindowRendererBackBufferTargets() {
|
||||
m_windowInterop.ReleaseBackBufferTargets();
|
||||
}
|
||||
|
||||
bool NativeRenderer::RebuildWindowRendererBackBufferTargets() {
|
||||
if (!EnsureWindowRendererInterop()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DiscardRenderTarget();
|
||||
ReleaseWindowRendererBackBufferTargets();
|
||||
return m_windowInterop.RebuildBackBufferTargets();
|
||||
}
|
||||
|
||||
bool NativeRenderer::HasAttachedWindowRenderer() const {
|
||||
return m_windowInterop.HasAttachedWindowRenderer();
|
||||
}
|
||||
|
||||
const std::string& NativeRenderer::GetLastRenderError() const {
|
||||
return m_lastRenderError;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const bool attached = m_windowInterop.Attach(*m_windowRenderer, *m_d2dFactory.Get());
|
||||
if (!attached) {
|
||||
m_lastRenderError = m_windowInterop.GetLastError();
|
||||
} else {
|
||||
m_lastRenderError.clear();
|
||||
}
|
||||
return attached;
|
||||
}
|
||||
|
||||
void NativeRenderer::ReleaseWindowRendererInterop() {
|
||||
m_windowInterop.Detach();
|
||||
}
|
||||
|
||||
bool NativeRenderer::EnsureRenderTarget() {
|
||||
if (!m_hwnd || !m_d2dFactory || !m_dwriteFactory) {
|
||||
m_lastRenderError = "EnsureRenderTarget requires hwnd, D2D factory, and DWrite factory.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return CreateDeviceResources();
|
||||
}
|
||||
|
||||
bool NativeRenderer::EnsureWicFactory(std::string& outError) {
|
||||
outError.clear();
|
||||
if (m_wicFactory) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const HRESULT initHr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||
if (FAILED(initHr) && initHr != RPC_E_CHANGED_MODE) {
|
||||
outError = HrToString("CoInitializeEx", initHr);
|
||||
return false;
|
||||
}
|
||||
if (SUCCEEDED(initHr)) {
|
||||
m_wicComInitialized = true;
|
||||
}
|
||||
|
||||
const HRESULT factoryHr = CoCreateInstance(
|
||||
CLSID_WICImagingFactory,
|
||||
nullptr,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_PPV_ARGS(m_wicFactory.ReleaseAndGetAddressOf()));
|
||||
if (FAILED(factoryHr)) {
|
||||
outError = HrToString("CoCreateInstance(CLSID_WICImagingFactory)", factoryHr);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NativeRenderer::DiscardRenderTarget() {
|
||||
InvalidateCachedTextureBitmaps(m_renderTarget.Get());
|
||||
m_solidBrush.Reset();
|
||||
m_renderTarget.Reset();
|
||||
}
|
||||
|
||||
bool NativeRenderer::CreateDeviceResources() {
|
||||
if (m_renderTarget) {
|
||||
return true;
|
||||
}
|
||||
|
||||
RECT clientRect = {};
|
||||
GetClientRect(m_hwnd, &clientRect);
|
||||
const UINT width = static_cast<UINT>((std::max)(clientRect.right - clientRect.left, 1L));
|
||||
const UINT height = static_cast<UINT>((std::max)(clientRect.bottom - clientRect.top, 1L));
|
||||
|
||||
const D2D1_RENDER_TARGET_PROPERTIES renderTargetProps = D2D1::RenderTargetProperties(
|
||||
D2D1_RENDER_TARGET_TYPE_DEFAULT,
|
||||
D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
|
||||
kBaseDpi,
|
||||
kBaseDpi);
|
||||
const D2D1_HWND_RENDER_TARGET_PROPERTIES hwndProps = D2D1::HwndRenderTargetProperties(
|
||||
m_hwnd,
|
||||
D2D1::SizeU(width, height));
|
||||
|
||||
const HRESULT renderTargetHr = m_d2dFactory->CreateHwndRenderTarget(
|
||||
renderTargetProps,
|
||||
hwndProps,
|
||||
m_renderTarget.ReleaseAndGetAddressOf());
|
||||
if (FAILED(renderTargetHr)) {
|
||||
m_lastRenderError = HrToString("ID2D1Factory::CreateHwndRenderTarget", renderTargetHr);
|
||||
return false;
|
||||
}
|
||||
|
||||
const HRESULT brushHr = m_renderTarget->CreateSolidColorBrush(
|
||||
D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f),
|
||||
m_solidBrush.ReleaseAndGetAddressOf());
|
||||
if (FAILED(brushHr)) {
|
||||
m_lastRenderError = HrToString("ID2D1HwndRenderTarget::CreateSolidColorBrush", brushHr);
|
||||
DiscardRenderTarget();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_renderTarget->SetDpi(kBaseDpi, kBaseDpi);
|
||||
m_renderTarget->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE);
|
||||
m_lastRenderError.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
void NativeRenderer::InvalidateCachedTextureBitmaps(const ID2D1RenderTarget* renderTarget) {
|
||||
for (NativeTextureResource* texture : m_liveTextures) {
|
||||
if (texture == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (renderTarget == nullptr || texture->cachedTarget == renderTarget) {
|
||||
texture->cachedBitmap.Reset();
|
||||
texture->cachedTarget = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace XCEngine::UI::Editor::Host
|
||||
Reference in New Issue
Block a user