Wire viewport shell host chain and move host under app
This commit is contained in:
@@ -48,7 +48,7 @@
|
|||||||
|
|
||||||
1. `new_editor/app/Shell/ProductShellAsset.cpp` 里,`scene` 和 `game` 仍然只是 `HostedContent`,还没有切到正式的 `ViewportShell` 主线。
|
1. `new_editor/app/Shell/ProductShellAsset.cpp` 里,`scene` 和 `game` 仍然只是 `HostedContent`,还没有切到正式的 `ViewportShell` 主线。
|
||||||
2. `new_editor/app/Workspace/ProductEditorWorkspace.cpp` 里,没有真正的 viewport service owner,也没有 scene/game render request 生命周期。
|
2. `new_editor/app/Workspace/ProductEditorWorkspace.cpp` 里,没有真正的 viewport service owner,也没有 scene/game render request 生命周期。
|
||||||
3. `new_editor/Host/NativeRenderer.h/.cpp` 当前是 Direct2D/DirectWrite 宿主,只支持它自己的位图纹理路径。
|
3. `new_editor/app/Host/NativeRenderer.h/.cpp` 当前是 Direct2D/DirectWrite 宿主,只支持它自己的位图纹理路径。
|
||||||
4. `engine/include/XCEngine/UI/Types.h` 虽然已经有 `UITextureHandleKind::ShaderResourceView`,但 `NativeRenderer` 目前并没有真正消费这类引擎 render target。
|
4. `engine/include/XCEngine/UI/Types.h` 虽然已经有 `UITextureHandleKind::ShaderResourceView`,但 `NativeRenderer` 目前并没有真正消费这类引擎 render target。
|
||||||
|
|
||||||
### 2.3 根因结论
|
### 2.3 根因结论
|
||||||
@@ -132,7 +132,7 @@
|
|||||||
|
|
||||||
主要工作:
|
主要工作:
|
||||||
|
|
||||||
1. 在 `new_editor/Host` 下建立正式的 window renderer
|
1. 在 `new_editor/app/Host` 下建立正式的 window renderer
|
||||||
2. 对齐旧 `editor/src/Platform/D3D12WindowRenderer.h` 的能力边界
|
2. 对齐旧 `editor/src/Platform/D3D12WindowRenderer.h` 的能力边界
|
||||||
3. 让 `Application` 能拿到:
|
3. 让 `Application` 能拿到:
|
||||||
- `RHIDevice`
|
- `RHIDevice`
|
||||||
|
|||||||
@@ -123,13 +123,15 @@ target_link_libraries(XCUIEditorLib PUBLIC
|
|||||||
)
|
)
|
||||||
|
|
||||||
add_library(XCUIEditorHost STATIC
|
add_library(XCUIEditorHost STATIC
|
||||||
Host/AutoScreenshot.cpp
|
app/Host/AutoScreenshot.cpp
|
||||||
Host/NativeRenderer.cpp
|
app/Host/D3D12WindowRenderer.cpp
|
||||||
|
app/Host/D3D12WindowRenderLoop.cpp
|
||||||
|
app/Host/NativeRenderer.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(XCUIEditorHost
|
target_include_directories(XCUIEditorHost
|
||||||
PUBLIC
|
PUBLIC
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}
|
${CMAKE_CURRENT_SOURCE_DIR}/app
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||||
${CMAKE_SOURCE_DIR}/engine/include
|
${CMAKE_SOURCE_DIR}/engine/include
|
||||||
)
|
)
|
||||||
@@ -139,7 +141,10 @@ xcui_editor_apply_common_target_settings(XCUIEditorHost PUBLIC)
|
|||||||
target_link_libraries(XCUIEditorHost PUBLIC
|
target_link_libraries(XCUIEditorHost PUBLIC
|
||||||
XCEngine
|
XCEngine
|
||||||
d2d1.lib
|
d2d1.lib
|
||||||
|
d3d12.lib
|
||||||
|
d3dcompiler.lib
|
||||||
dwrite.lib
|
dwrite.lib
|
||||||
|
dxgi.lib
|
||||||
windowscodecs.lib
|
windowscodecs.lib
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -158,6 +163,8 @@ if(XCENGINE_BUILD_XCUI_EDITOR_APP)
|
|||||||
app/Panels/ProductProjectPanel.cpp
|
app/Panels/ProductProjectPanel.cpp
|
||||||
app/Project/ProductProjectBrowserModel.cpp
|
app/Project/ProductProjectBrowserModel.cpp
|
||||||
app/Shell/ProductShellAsset.cpp
|
app/Shell/ProductShellAsset.cpp
|
||||||
|
app/Viewport/ProductViewportHostService.cpp
|
||||||
|
app/Viewport/ProductViewportRenderTargets.cpp
|
||||||
app/Workspace/ProductEditorWorkspace.cpp
|
app/Workspace/ProductEditorWorkspace.cpp
|
||||||
app/Workspace/ProductEditorWorkspaceEventRouter.cpp
|
app/Workspace/ProductEditorWorkspaceEventRouter.cpp
|
||||||
)
|
)
|
||||||
|
|||||||
63
new_editor/app/Host/D3D12WindowRenderLoop.cpp
Normal file
63
new_editor/app/Host/D3D12WindowRenderLoop.cpp
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
#include "D3D12WindowRenderLoop.h"
|
||||||
|
|
||||||
|
namespace XCEngine::UI::Editor::Host {
|
||||||
|
|
||||||
|
bool RenderD3D12WindowFrame(
|
||||||
|
D3D12WindowRenderer& windowRenderer,
|
||||||
|
const float clearColor[4],
|
||||||
|
const D3D12WindowRenderCallback& beforePresent,
|
||||||
|
const D3D12WindowRenderCallback& afterPresent) {
|
||||||
|
const ::XCEngine::Rendering::RenderSurface* renderSurface =
|
||||||
|
windowRenderer.GetCurrentRenderSurface();
|
||||||
|
::XCEngine::Rendering::RenderContext renderContext =
|
||||||
|
windowRenderer.GetRenderContext();
|
||||||
|
if (!renderContext.IsValid() ||
|
||||||
|
renderContext.commandList == nullptr ||
|
||||||
|
renderContext.commandQueue == nullptr ||
|
||||||
|
windowRenderer.GetSwapChain() == nullptr ||
|
||||||
|
renderSurface == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* d3d12CommandList =
|
||||||
|
static_cast<::XCEngine::RHI::D3D12CommandList*>(renderContext.commandList);
|
||||||
|
if (d3d12CommandList == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& colorAttachments = renderSurface->GetColorAttachments();
|
||||||
|
if (colorAttachments.empty() || colorAttachments[0] == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
::XCEngine::RHI::RHIResourceView* renderTargetView = colorAttachments[0];
|
||||||
|
renderContext.commandList->TransitionBarrier(
|
||||||
|
renderTargetView,
|
||||||
|
::XCEngine::RHI::ResourceStates::Present,
|
||||||
|
::XCEngine::RHI::ResourceStates::RenderTarget);
|
||||||
|
renderContext.commandList->SetRenderTargets(1, &renderTargetView, nullptr);
|
||||||
|
renderContext.commandList->ClearRenderTarget(renderTargetView, clearColor);
|
||||||
|
|
||||||
|
if (beforePresent) {
|
||||||
|
beforePresent(renderContext, *renderSurface);
|
||||||
|
renderContext.commandList->SetRenderTargets(1, &renderTargetView, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (afterPresent) {
|
||||||
|
afterPresent(renderContext, *renderSurface);
|
||||||
|
renderContext.commandList->SetRenderTargets(1, &renderTargetView, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderContext.commandList->TransitionBarrier(
|
||||||
|
renderTargetView,
|
||||||
|
::XCEngine::RHI::ResourceStates::RenderTarget,
|
||||||
|
::XCEngine::RHI::ResourceStates::Present);
|
||||||
|
renderContext.commandList->Close();
|
||||||
|
|
||||||
|
void* commandLists[] = { renderContext.commandList };
|
||||||
|
renderContext.commandQueue->ExecuteCommandLists(1, commandLists);
|
||||||
|
windowRenderer.GetSwapChain()->Present(1, 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace XCEngine::UI::Editor::Host
|
||||||
20
new_editor/app/Host/D3D12WindowRenderLoop.h
Normal file
20
new_editor/app/Host/D3D12WindowRenderLoop.h
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "D3D12WindowRenderer.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace XCEngine::UI::Editor::Host {
|
||||||
|
|
||||||
|
using D3D12WindowRenderCallback =
|
||||||
|
std::function<void(
|
||||||
|
const ::XCEngine::Rendering::RenderContext&,
|
||||||
|
const ::XCEngine::Rendering::RenderSurface&)>;
|
||||||
|
|
||||||
|
bool RenderD3D12WindowFrame(
|
||||||
|
D3D12WindowRenderer& windowRenderer,
|
||||||
|
const float clearColor[4],
|
||||||
|
const D3D12WindowRenderCallback& beforePresent = {},
|
||||||
|
const D3D12WindowRenderCallback& afterPresent = {});
|
||||||
|
|
||||||
|
} // namespace XCEngine::UI::Editor::Host
|
||||||
400
new_editor/app/Host/D3D12WindowRenderer.cpp
Normal file
400
new_editor/app/Host/D3D12WindowRenderer.cpp
Normal file
@@ -0,0 +1,400 @@
|
|||||||
|
#include "D3D12WindowRenderer.h"
|
||||||
|
|
||||||
|
#include <XCEngine/RHI/RHIFactory.h>
|
||||||
|
|
||||||
|
namespace XCEngine::UI::Editor::Host {
|
||||||
|
|
||||||
|
using ::XCEngine::RHI::CommandListDesc;
|
||||||
|
using ::XCEngine::RHI::CommandQueueDesc;
|
||||||
|
using ::XCEngine::RHI::DescriptorHeapType;
|
||||||
|
using ::XCEngine::RHI::DescriptorPoolDesc;
|
||||||
|
using ::XCEngine::RHI::D3D12CommandList;
|
||||||
|
using ::XCEngine::RHI::D3D12CommandQueue;
|
||||||
|
using ::XCEngine::RHI::D3D12DescriptorHeap;
|
||||||
|
using ::XCEngine::RHI::D3D12Device;
|
||||||
|
using ::XCEngine::RHI::D3D12SwapChain;
|
||||||
|
using ::XCEngine::RHI::D3D12Texture;
|
||||||
|
using ::XCEngine::RHI::Format;
|
||||||
|
using ::XCEngine::RHI::ResourceStates;
|
||||||
|
using ::XCEngine::RHI::ResourceViewDesc;
|
||||||
|
using ::XCEngine::RHI::ResourceViewDimension;
|
||||||
|
using ::XCEngine::RHI::RHICommandList;
|
||||||
|
using ::XCEngine::RHI::RHICommandQueue;
|
||||||
|
using ::XCEngine::RHI::RHIDevice;
|
||||||
|
using ::XCEngine::RHI::RHIDeviceDesc;
|
||||||
|
using ::XCEngine::RHI::RHIFactory;
|
||||||
|
using ::XCEngine::RHI::RHISwapChain;
|
||||||
|
using ::XCEngine::RHI::RHIType;
|
||||||
|
using ::XCEngine::RHI::SwapChainDesc;
|
||||||
|
|
||||||
|
bool D3D12WindowRenderer::Initialize(HWND hwnd, int width, int height) {
|
||||||
|
Shutdown();
|
||||||
|
|
||||||
|
if (hwnd == nullptr || width <= 0 || height <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_hwnd = hwnd;
|
||||||
|
m_width = width;
|
||||||
|
m_height = height;
|
||||||
|
|
||||||
|
m_device = RHIFactory::CreateRHIDevice(RHIType::D3D12);
|
||||||
|
if (m_device == nullptr) {
|
||||||
|
Shutdown();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RHIDeviceDesc deviceDesc = {};
|
||||||
|
#ifdef _DEBUG
|
||||||
|
deviceDesc.enableDebugLayer = true;
|
||||||
|
deviceDesc.enableGPUValidation = true;
|
||||||
|
#endif
|
||||||
|
if (!m_device->Initialize(deviceDesc)) {
|
||||||
|
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) {
|
||||||
|
Shutdown();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandListDesc commandListDesc = {};
|
||||||
|
commandListDesc.commandListType = static_cast<std::uint32_t>(::XCEngine::RHI::CommandQueueType::Direct);
|
||||||
|
m_commandList = m_device->CreateCommandList(commandListDesc);
|
||||||
|
if (m_commandList == nullptr || GetD3D12CommandList() == nullptr) {
|
||||||
|
Shutdown();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_commandList->Close();
|
||||||
|
|
||||||
|
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) {
|
||||||
|
Shutdown();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DescriptorPoolDesc srvPoolDesc = {};
|
||||||
|
srvPoolDesc.type = DescriptorHeapType::CBV_SRV_UAV;
|
||||||
|
srvPoolDesc.descriptorCount = kSrvDescriptorCount;
|
||||||
|
srvPoolDesc.shaderVisible = true;
|
||||||
|
m_srvPool = m_device->CreateDescriptorPool(srvPoolDesc);
|
||||||
|
m_srvHeap = dynamic_cast<D3D12DescriptorHeap*>(m_srvPool);
|
||||||
|
if (m_srvPool == nullptr || m_srvHeap == nullptr) {
|
||||||
|
Shutdown();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_srvDescriptorSize = m_srvHeap->GetDescriptorSize();
|
||||||
|
m_srvUsage.assign(kSrvDescriptorCount, false);
|
||||||
|
if (!RecreateBackBufferViews()) {
|
||||||
|
Shutdown();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D12WindowRenderer::Shutdown() {
|
||||||
|
WaitForGpuIdle();
|
||||||
|
ReleaseBackBufferViews();
|
||||||
|
|
||||||
|
if (m_srvPool != nullptr) {
|
||||||
|
m_srvPool->Shutdown();
|
||||||
|
delete m_srvPool;
|
||||||
|
m_srvPool = nullptr;
|
||||||
|
}
|
||||||
|
m_srvHeap = nullptr;
|
||||||
|
m_srvUsage.clear();
|
||||||
|
|
||||||
|
if (m_swapChain != nullptr) {
|
||||||
|
m_swapChain->Shutdown();
|
||||||
|
delete m_swapChain;
|
||||||
|
m_swapChain = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_commandList != nullptr) {
|
||||||
|
m_commandList->Shutdown();
|
||||||
|
delete m_commandList;
|
||||||
|
m_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_srvDescriptorSize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D12WindowRenderer::Resize(int width, int height) {
|
||||||
|
if (width <= 0 || height <= 0 || m_swapChain == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitForGpuIdle();
|
||||||
|
ReleaseBackBufferViews();
|
||||||
|
|
||||||
|
m_swapChain->Resize(static_cast<std::uint32_t>(width), static_cast<std::uint32_t>(height));
|
||||||
|
m_width = width;
|
||||||
|
m_height = height;
|
||||||
|
RecreateBackBufferViews();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D12WindowRenderer::BeginFrame() {
|
||||||
|
D3D12CommandQueue* d3d12Queue = GetD3D12CommandQueue();
|
||||||
|
D3D12CommandList* d3d12CommandList = GetD3D12CommandList();
|
||||||
|
if (m_swapChain == nullptr ||
|
||||||
|
d3d12Queue == nullptr ||
|
||||||
|
d3d12CommandList == nullptr ||
|
||||||
|
m_srvHeap == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
d3d12Queue->WaitForPreviousFrame();
|
||||||
|
m_commandList->Reset();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ID3D12Device* D3D12WindowRenderer::GetDevice() const {
|
||||||
|
const D3D12Device* device = GetD3D12Device();
|
||||||
|
return device != nullptr ? device->GetDevice() : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ID3D12DescriptorHeap* D3D12WindowRenderer::GetSrvHeap() const {
|
||||||
|
return m_srvHeap != nullptr ? m_srvHeap->GetDescriptorHeap() : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ID3D12CommandQueue* D3D12WindowRenderer::GetCommandQueue() const {
|
||||||
|
const D3D12CommandQueue* queue = GetD3D12CommandQueue();
|
||||||
|
return queue != nullptr ? queue->GetCommandQueue() : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D12WindowRenderer::AllocateShaderResourceDescriptor(
|
||||||
|
D3D12_CPU_DESCRIPTOR_HANDLE* outCpuHandle,
|
||||||
|
D3D12_GPU_DESCRIPTOR_HANDLE* outGpuHandle) {
|
||||||
|
AllocateShaderResourceDescriptorInternal(outCpuHandle, outGpuHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool D3D12WindowRenderer::CreateShaderResourceTextureDescriptor(
|
||||||
|
RHIDevice* device,
|
||||||
|
::XCEngine::RHI::RHITexture* texture,
|
||||||
|
D3D12_CPU_DESCRIPTOR_HANDLE* outCpuHandle,
|
||||||
|
D3D12_GPU_DESCRIPTOR_HANDLE* outGpuHandle) {
|
||||||
|
if (device == nullptr ||
|
||||||
|
texture == nullptr ||
|
||||||
|
outCpuHandle == nullptr ||
|
||||||
|
outGpuHandle == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*outCpuHandle = {};
|
||||||
|
*outGpuHandle = {};
|
||||||
|
|
||||||
|
auto* nativeDevice = dynamic_cast<D3D12Device*>(device);
|
||||||
|
auto* nativeTexture = dynamic_cast<D3D12Texture*>(texture);
|
||||||
|
if (nativeDevice == nullptr ||
|
||||||
|
nativeTexture == nullptr ||
|
||||||
|
nativeTexture->GetResource() == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AllocateShaderResourceDescriptorInternal(outCpuHandle, outGpuHandle);
|
||||||
|
if (outCpuHandle->ptr == 0 || outGpuHandle->ptr == 0) {
|
||||||
|
*outCpuHandle = {};
|
||||||
|
*outGpuHandle = {};
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
|
||||||
|
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
||||||
|
srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||||
|
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
|
||||||
|
srvDesc.Texture2D.MipLevels = 1;
|
||||||
|
nativeDevice->GetDevice()->CreateShaderResourceView(
|
||||||
|
nativeTexture->GetResource(),
|
||||||
|
&srvDesc,
|
||||||
|
*outCpuHandle);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D12WindowRenderer::FreeShaderResourceDescriptor(
|
||||||
|
D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle,
|
||||||
|
D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle) {
|
||||||
|
FreeShaderResourceDescriptorInternal(cpuHandle, gpuHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT D3D12WindowRenderer::GetSrvDescriptorSize() const {
|
||||||
|
return m_srvDescriptorSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT D3D12WindowRenderer::GetSrvDescriptorCount() const {
|
||||||
|
return kSrvDescriptorCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
RHIDevice* D3D12WindowRenderer::GetRHIDevice() const {
|
||||||
|
return m_device;
|
||||||
|
}
|
||||||
|
|
||||||
|
RHISwapChain* D3D12WindowRenderer::GetSwapChain() const {
|
||||||
|
return m_swapChain;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ::XCEngine::Rendering::RenderSurface* D3D12WindowRenderer::GetCurrentRenderSurface() const {
|
||||||
|
if (m_swapChain == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::uint32_t backBufferIndex = m_swapChain->GetCurrentBackBufferIndex();
|
||||||
|
if (backBufferIndex >= m_backBufferSurfaces.size()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &m_backBufferSurfaces[backBufferIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
::XCEngine::Rendering::RenderContext D3D12WindowRenderer::GetRenderContext() const {
|
||||||
|
::XCEngine::Rendering::RenderContext context = {};
|
||||||
|
context.device = m_device;
|
||||||
|
context.commandList = m_commandList;
|
||||||
|
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 {
|
||||||
|
return m_commandList != nullptr ? static_cast<D3D12CommandList*>(m_commandList) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
D3D12SwapChain* D3D12WindowRenderer::GetD3D12SwapChain() const {
|
||||||
|
return m_swapChain != nullptr ? static_cast<D3D12SwapChain*>(m_swapChain) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D12WindowRenderer::AllocateShaderResourceDescriptorInternal(
|
||||||
|
D3D12_CPU_DESCRIPTOR_HANDLE* outCpuHandle,
|
||||||
|
D3D12_GPU_DESCRIPTOR_HANDLE* outGpuHandle) {
|
||||||
|
if (outCpuHandle == nullptr || outGpuHandle == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
*outCpuHandle = {};
|
||||||
|
*outGpuHandle = {};
|
||||||
|
if (m_srvHeap == nullptr ||
|
||||||
|
m_srvDescriptorSize == 0 ||
|
||||||
|
m_srvUsage.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::size_t index = 0; index < m_srvUsage.size(); ++index) {
|
||||||
|
if (m_srvUsage[index]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_srvUsage[index] = true;
|
||||||
|
outCpuHandle->ptr =
|
||||||
|
m_srvHeap->GetCPUDescriptorHandleForHeapStart().ptr +
|
||||||
|
static_cast<SIZE_T>(index) * m_srvDescriptorSize;
|
||||||
|
outGpuHandle->ptr =
|
||||||
|
m_srvHeap->GetGPUDescriptorHandleForHeapStart().ptr +
|
||||||
|
static_cast<UINT64>(index) * m_srvDescriptorSize;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D12WindowRenderer::FreeShaderResourceDescriptorInternal(
|
||||||
|
D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle,
|
||||||
|
D3D12_GPU_DESCRIPTOR_HANDLE) {
|
||||||
|
if (m_srvHeap == nullptr ||
|
||||||
|
m_srvDescriptorSize == 0 ||
|
||||||
|
cpuHandle.ptr < m_srvHeap->GetCPUDescriptorHandleForHeapStart().ptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SIZE_T offset =
|
||||||
|
cpuHandle.ptr - m_srvHeap->GetCPUDescriptorHandleForHeapStart().ptr;
|
||||||
|
const std::size_t index =
|
||||||
|
static_cast<std::size_t>(offset / m_srvDescriptorSize);
|
||||||
|
if (index < m_srvUsage.size()) {
|
||||||
|
m_srvUsage[index] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void D3D12WindowRenderer::WaitForGpuIdle() {
|
||||||
|
if (m_commandQueue != nullptr) {
|
||||||
|
m_commandQueue->WaitForIdle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
m_backBufferViews[backBufferIndex] = m_device->CreateRenderTargetView(
|
||||||
|
&d3d12SwapChain->GetBackBuffer(backBufferIndex),
|
||||||
|
viewDesc);
|
||||||
|
if (m_backBufferViews[backBufferIndex] == nullptr) {
|
||||||
|
ReleaseBackBufferViews();
|
||||||
|
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
|
||||||
92
new_editor/app/Host/D3D12WindowRenderer.h
Normal file
92
new_editor/app/Host/D3D12WindowRenderer.h
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef NOMINMAX
|
||||||
|
#define NOMINMAX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <XCEngine/Rendering/RenderContext.h>
|
||||||
|
#include <XCEngine/Rendering/RenderSurface.h>
|
||||||
|
#include <XCEngine/RHI/D3D12/D3D12CommandList.h>
|
||||||
|
#include <XCEngine/RHI/D3D12/D3D12CommandQueue.h>
|
||||||
|
#include <XCEngine/RHI/D3D12/D3D12DescriptorHeap.h>
|
||||||
|
#include <XCEngine/RHI/D3D12/D3D12Device.h>
|
||||||
|
#include <XCEngine/RHI/D3D12/D3D12SwapChain.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 <d3d12.h>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace XCEngine::UI::Editor::Host {
|
||||||
|
|
||||||
|
class D3D12WindowRenderer {
|
||||||
|
public:
|
||||||
|
static constexpr UINT kSrvDescriptorCount = 64;
|
||||||
|
static constexpr std::uint32_t kSwapChainBufferCount = 3;
|
||||||
|
|
||||||
|
bool Initialize(HWND hwnd, int width, int height);
|
||||||
|
void Shutdown();
|
||||||
|
void Resize(int width, int height);
|
||||||
|
bool BeginFrame();
|
||||||
|
|
||||||
|
ID3D12Device* GetDevice() const;
|
||||||
|
ID3D12DescriptorHeap* GetSrvHeap() const;
|
||||||
|
ID3D12CommandQueue* GetCommandQueue() const;
|
||||||
|
void AllocateShaderResourceDescriptor(
|
||||||
|
D3D12_CPU_DESCRIPTOR_HANDLE* outCpuHandle,
|
||||||
|
D3D12_GPU_DESCRIPTOR_HANDLE* outGpuHandle);
|
||||||
|
bool CreateShaderResourceTextureDescriptor(
|
||||||
|
::XCEngine::RHI::RHIDevice* device,
|
||||||
|
::XCEngine::RHI::RHITexture* texture,
|
||||||
|
D3D12_CPU_DESCRIPTOR_HANDLE* outCpuHandle,
|
||||||
|
D3D12_GPU_DESCRIPTOR_HANDLE* outGpuHandle);
|
||||||
|
void FreeShaderResourceDescriptor(
|
||||||
|
D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle,
|
||||||
|
D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle);
|
||||||
|
UINT GetSrvDescriptorSize() const;
|
||||||
|
UINT GetSrvDescriptorCount() const;
|
||||||
|
::XCEngine::RHI::RHIDevice* GetRHIDevice() const;
|
||||||
|
::XCEngine::RHI::RHISwapChain* GetSwapChain() const;
|
||||||
|
const ::XCEngine::Rendering::RenderSurface* GetCurrentRenderSurface() const;
|
||||||
|
::XCEngine::Rendering::RenderContext GetRenderContext() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
::XCEngine::RHI::D3D12Device* GetD3D12Device() const;
|
||||||
|
::XCEngine::RHI::D3D12CommandQueue* GetD3D12CommandQueue() const;
|
||||||
|
::XCEngine::RHI::D3D12CommandList* GetD3D12CommandList() const;
|
||||||
|
::XCEngine::RHI::D3D12SwapChain* GetD3D12SwapChain() const;
|
||||||
|
void AllocateShaderResourceDescriptorInternal(
|
||||||
|
D3D12_CPU_DESCRIPTOR_HANDLE* outCpuHandle,
|
||||||
|
D3D12_GPU_DESCRIPTOR_HANDLE* outGpuHandle);
|
||||||
|
void FreeShaderResourceDescriptorInternal(
|
||||||
|
D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle,
|
||||||
|
D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle);
|
||||||
|
void WaitForGpuIdle();
|
||||||
|
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;
|
||||||
|
::XCEngine::RHI::RHICommandList* m_commandList = nullptr;
|
||||||
|
::XCEngine::RHI::RHISwapChain* m_swapChain = nullptr;
|
||||||
|
::XCEngine::RHI::RHIDescriptorPool* m_srvPool = nullptr;
|
||||||
|
::XCEngine::RHI::D3D12DescriptorHeap* m_srvHeap = nullptr;
|
||||||
|
std::vector<bool> m_srvUsage = {};
|
||||||
|
std::vector<::XCEngine::RHI::RHIResourceView*> m_backBufferViews = {};
|
||||||
|
std::vector<::XCEngine::Rendering::RenderSurface> m_backBufferSurfaces = {};
|
||||||
|
UINT m_srvDescriptorSize = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace XCEngine::UI::Editor::Host
|
||||||
@@ -21,8 +21,8 @@ UIEditorPanelRegistry BuildPanelRegistry() {
|
|||||||
UIEditorPanelRegistry registry = {};
|
UIEditorPanelRegistry registry = {};
|
||||||
registry.panels = {
|
registry.panels = {
|
||||||
{ "hierarchy", "Hierarchy", UIEditorPanelPresentationKind::HostedContent, true, false, false },
|
{ "hierarchy", "Hierarchy", UIEditorPanelPresentationKind::HostedContent, true, false, false },
|
||||||
{ "scene", "Scene", UIEditorPanelPresentationKind::HostedContent, false, false, false },
|
{ "scene", "Scene", UIEditorPanelPresentationKind::ViewportShell, false, false, false },
|
||||||
{ "game", "Game", UIEditorPanelPresentationKind::HostedContent, false, false, false },
|
{ "game", "Game", UIEditorPanelPresentationKind::ViewportShell, false, false, false },
|
||||||
{ "inspector", "Inspector", UIEditorPanelPresentationKind::HostedContent, true, false, false },
|
{ "inspector", "Inspector", UIEditorPanelPresentationKind::HostedContent, true, false, false },
|
||||||
{ "console", "Console", UIEditorPanelPresentationKind::HostedContent, true, false, false },
|
{ "console", "Console", UIEditorPanelPresentationKind::HostedContent, true, false, false },
|
||||||
{ "project", "Project", UIEditorPanelPresentationKind::HostedContent, false, false, false }
|
{ "project", "Project", UIEditorPanelPresentationKind::HostedContent, false, false, false }
|
||||||
@@ -421,6 +421,22 @@ UIEditorWorkspacePanelPresentationModel BuildHostedContentPresentation(
|
|||||||
return presentation;
|
return presentation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UIEditorWorkspacePanelPresentationModel BuildViewportPresentation(
|
||||||
|
std::string panelId,
|
||||||
|
std::string title,
|
||||||
|
std::string subtitle) {
|
||||||
|
UIEditorWorkspacePanelPresentationModel presentation = {};
|
||||||
|
presentation.panelId = std::move(panelId);
|
||||||
|
presentation.kind = UIEditorPanelPresentationKind::ViewportShell;
|
||||||
|
presentation.viewportShellModel.spec.chrome.title = std::move(title);
|
||||||
|
presentation.viewportShellModel.spec.chrome.subtitle = std::move(subtitle);
|
||||||
|
presentation.viewportShellModel.spec.chrome.showTopBar = true;
|
||||||
|
presentation.viewportShellModel.spec.chrome.showBottomBar = true;
|
||||||
|
presentation.viewportShellModel.frame.statusText =
|
||||||
|
"Viewport request chain is active.";
|
||||||
|
return presentation;
|
||||||
|
}
|
||||||
|
|
||||||
UIEditorShellInteractionDefinition BuildBaseShellDefinition() {
|
UIEditorShellInteractionDefinition BuildBaseShellDefinition() {
|
||||||
UIEditorShellInteractionDefinition definition = {};
|
UIEditorShellInteractionDefinition definition = {};
|
||||||
definition.menuModel = BuildMenuModel();
|
definition.menuModel = BuildMenuModel();
|
||||||
@@ -432,8 +448,8 @@ UIEditorShellInteractionDefinition BuildBaseShellDefinition() {
|
|||||||
definition.statusSegments = {};
|
definition.statusSegments = {};
|
||||||
definition.workspacePresentations = {
|
definition.workspacePresentations = {
|
||||||
BuildHostedContentPresentation("hierarchy"),
|
BuildHostedContentPresentation("hierarchy"),
|
||||||
BuildHostedContentPresentation("scene"),
|
BuildViewportPresentation("scene", "Scene", "New Editor viewport shell"),
|
||||||
BuildHostedContentPresentation("game"),
|
BuildViewportPresentation("game", "Game", "New Editor viewport shell"),
|
||||||
BuildHostedContentPresentation("inspector"),
|
BuildHostedContentPresentation("inspector"),
|
||||||
BuildHostedContentPresentation("console"),
|
BuildHostedContentPresentation("console"),
|
||||||
BuildHostedContentPresentation("project")
|
BuildHostedContentPresentation("project")
|
||||||
|
|||||||
225
new_editor/app/Viewport/ProductViewportHostService.cpp
Normal file
225
new_editor/app/Viewport/ProductViewportHostService.cpp
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
#include "ProductViewportHostService.h"
|
||||||
|
|
||||||
|
#include <XCEngine/RHI/RHICommandList.h>
|
||||||
|
|
||||||
|
namespace XCEngine::UI::Editor::App {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using ::XCEngine::RHI::ResourceStates;
|
||||||
|
|
||||||
|
std::string BuildViewportPendingStatus(ProductViewportKind kind) {
|
||||||
|
return kind == ProductViewportKind::Scene
|
||||||
|
? "Scene viewport host pending D3D12 presenter integration."
|
||||||
|
: "Game viewport host pending D3D12 presenter integration.";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string BuildViewportReadyStatus(ProductViewportKind kind) {
|
||||||
|
return kind == ProductViewportKind::Scene
|
||||||
|
? "Scene viewport render target ready; presenter still uses NativeRenderer."
|
||||||
|
: "Game viewport render target ready; presenter still uses NativeRenderer.";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string BuildViewportPresentedStatus(ProductViewportKind kind) {
|
||||||
|
return kind == ProductViewportKind::Scene
|
||||||
|
? "Scene viewport frame ready."
|
||||||
|
: "Game viewport frame ready.";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void ProductViewportHostService::AttachWindowRenderer(
|
||||||
|
Host::D3D12WindowRenderer& windowRenderer) {
|
||||||
|
if (m_windowRenderer == &windowRenderer) {
|
||||||
|
m_device = windowRenderer.GetRHIDevice();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Shutdown();
|
||||||
|
m_windowRenderer = &windowRenderer;
|
||||||
|
m_device = windowRenderer.GetRHIDevice();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProductViewportHostService::DetachWindowRenderer() {
|
||||||
|
Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProductViewportHostService::SetSurfacePresentationEnabled(bool enabled) {
|
||||||
|
m_surfacePresentationEnabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProductViewportHostService::Shutdown() {
|
||||||
|
for (ViewportEntry& entry : m_entries) {
|
||||||
|
DestroyViewportEntry(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_windowRenderer = nullptr;
|
||||||
|
m_device = nullptr;
|
||||||
|
m_surfacePresentationEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProductViewportHostService::BeginFrame() {
|
||||||
|
for (ViewportEntry& entry : m_entries) {
|
||||||
|
entry.requestedWidth = 0;
|
||||||
|
entry.requestedHeight = 0;
|
||||||
|
entry.requestedThisFrame = false;
|
||||||
|
entry.renderedThisFrame = false;
|
||||||
|
entry.kind = (&entry == &m_entries[0]) ? ProductViewportKind::Scene : ProductViewportKind::Game;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProductViewportFrame ProductViewportHostService::RequestViewport(
|
||||||
|
ProductViewportKind kind,
|
||||||
|
const ::XCEngine::UI::UISize& requestedSize) {
|
||||||
|
ViewportEntry& entry = GetEntry(kind);
|
||||||
|
entry.requestedThisFrame = requestedSize.width > 1.0f && requestedSize.height > 1.0f;
|
||||||
|
entry.requestedWidth = entry.requestedThisFrame
|
||||||
|
? static_cast<std::uint32_t>(requestedSize.width)
|
||||||
|
: 0u;
|
||||||
|
entry.requestedHeight = entry.requestedThisFrame
|
||||||
|
? static_cast<std::uint32_t>(requestedSize.height)
|
||||||
|
: 0u;
|
||||||
|
|
||||||
|
if (!entry.requestedThisFrame) {
|
||||||
|
entry.statusText = "Viewport is waiting for a visible surface.";
|
||||||
|
return BuildFrame(entry, requestedSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_windowRenderer == nullptr || m_device == nullptr) {
|
||||||
|
entry.statusText = BuildViewportPendingStatus(kind);
|
||||||
|
return BuildFrame(entry, requestedSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!EnsureViewportResources(entry)) {
|
||||||
|
entry.statusText = "Failed to create viewport render targets.";
|
||||||
|
return BuildFrame(entry, requestedSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.statusText = m_surfacePresentationEnabled
|
||||||
|
? BuildViewportPresentedStatus(kind)
|
||||||
|
: BuildViewportReadyStatus(kind);
|
||||||
|
return BuildFrame(entry, requestedSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProductViewportHostService::RenderRequestedViewports(
|
||||||
|
const ::XCEngine::Rendering::RenderContext& renderContext) {
|
||||||
|
if (m_windowRenderer == nullptr || m_device == nullptr || !renderContext.IsValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ViewportEntry& entry : m_entries) {
|
||||||
|
if (!entry.requestedThisFrame || !EnsureViewportResources(entry)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.kind == ProductViewportKind::Scene) {
|
||||||
|
ClearViewport(entry, renderContext, 0.16f, 0.17f, 0.19f, 1.0f);
|
||||||
|
} else {
|
||||||
|
ClearViewport(entry, renderContext, 0.11f, 0.11f, 0.12f, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.renderedThisFrame = true;
|
||||||
|
entry.statusText = m_surfacePresentationEnabled
|
||||||
|
? BuildViewportPresentedStatus(entry.kind)
|
||||||
|
: BuildViewportReadyStatus(entry.kind);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProductViewportHostService::ViewportEntry& ProductViewportHostService::GetEntry(
|
||||||
|
ProductViewportKind kind) {
|
||||||
|
const std::size_t index = kind == ProductViewportKind::Scene ? 0u : 1u;
|
||||||
|
ViewportEntry& entry = m_entries[index];
|
||||||
|
entry.kind = kind;
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ProductViewportHostService::ViewportEntry& ProductViewportHostService::GetEntry(
|
||||||
|
ProductViewportKind kind) const {
|
||||||
|
const std::size_t index = kind == ProductViewportKind::Scene ? 0u : 1u;
|
||||||
|
return m_entries[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProductViewportHostService::DestroyViewportEntry(ViewportEntry& entry) {
|
||||||
|
DestroyProductViewportRenderTargets(m_windowRenderer, entry.renderTargets);
|
||||||
|
entry = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ProductViewportHostService::EnsureViewportResources(ViewportEntry& entry) {
|
||||||
|
const ProductViewportResourceReuseQuery reuseQuery =
|
||||||
|
BuildProductViewportRenderTargetsReuseQuery(
|
||||||
|
entry.kind,
|
||||||
|
entry.renderTargets,
|
||||||
|
entry.requestedWidth,
|
||||||
|
entry.requestedHeight);
|
||||||
|
if (CanReuseProductViewportResources(reuseQuery)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_windowRenderer == nullptr ||
|
||||||
|
m_device == nullptr ||
|
||||||
|
entry.requestedWidth == 0u ||
|
||||||
|
entry.requestedHeight == 0u) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CreateProductViewportRenderTargets(
|
||||||
|
entry.kind,
|
||||||
|
entry.requestedWidth,
|
||||||
|
entry.requestedHeight,
|
||||||
|
m_device,
|
||||||
|
*m_windowRenderer,
|
||||||
|
entry.renderTargets);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProductViewportHostService::ClearViewport(
|
||||||
|
ViewportEntry& entry,
|
||||||
|
const ::XCEngine::Rendering::RenderContext& renderContext,
|
||||||
|
float r,
|
||||||
|
float g,
|
||||||
|
float b,
|
||||||
|
float a) {
|
||||||
|
if (renderContext.commandList == nullptr ||
|
||||||
|
entry.renderTargets.colorView == nullptr ||
|
||||||
|
entry.renderTargets.depthView == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* commandList = renderContext.commandList;
|
||||||
|
auto* colorView = entry.renderTargets.colorView;
|
||||||
|
const float clearColor[4] = { r, g, b, a };
|
||||||
|
commandList->TransitionBarrier(
|
||||||
|
colorView,
|
||||||
|
entry.renderTargets.colorState,
|
||||||
|
ResourceStates::RenderTarget);
|
||||||
|
commandList->SetRenderTargets(1, &colorView, entry.renderTargets.depthView);
|
||||||
|
commandList->ClearRenderTarget(colorView, clearColor);
|
||||||
|
commandList->ClearDepthStencil(entry.renderTargets.depthView, 1.0f, 0);
|
||||||
|
commandList->TransitionBarrier(
|
||||||
|
colorView,
|
||||||
|
ResourceStates::RenderTarget,
|
||||||
|
ResourceStates::PixelShaderResource);
|
||||||
|
entry.renderTargets.colorState = ResourceStates::PixelShaderResource;
|
||||||
|
entry.renderTargets.hasValidObjectIdFrame = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProductViewportFrame ProductViewportHostService::BuildFrame(
|
||||||
|
const ViewportEntry& entry,
|
||||||
|
const ::XCEngine::UI::UISize& requestedSize) const {
|
||||||
|
ProductViewportFrame frame = {};
|
||||||
|
frame.requestedSize = requestedSize;
|
||||||
|
frame.renderSize = ::XCEngine::UI::UISize(
|
||||||
|
static_cast<float>(entry.renderTargets.width),
|
||||||
|
static_cast<float>(entry.renderTargets.height));
|
||||||
|
frame.wasRequested = entry.requestedThisFrame;
|
||||||
|
frame.statusText = entry.statusText;
|
||||||
|
|
||||||
|
if (m_surfacePresentationEnabled &&
|
||||||
|
entry.renderTargets.textureHandle.IsValid()) {
|
||||||
|
frame.texture = entry.renderTargets.textureHandle;
|
||||||
|
frame.hasTexture = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace XCEngine::UI::Editor::App
|
||||||
64
new_editor/app/Viewport/ProductViewportHostService.h
Normal file
64
new_editor/app/Viewport/ProductViewportHostService.h
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ProductViewportRenderTargets.h"
|
||||||
|
|
||||||
|
#include <Host/D3D12WindowRenderer.h>
|
||||||
|
|
||||||
|
#include <XCEngine/Rendering/RenderContext.h>
|
||||||
|
#include <XCEngine/UI/Types.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace XCEngine::UI::Editor::App {
|
||||||
|
|
||||||
|
class ProductViewportHostService {
|
||||||
|
public:
|
||||||
|
void AttachWindowRenderer(Host::D3D12WindowRenderer& windowRenderer);
|
||||||
|
void DetachWindowRenderer();
|
||||||
|
void SetSurfacePresentationEnabled(bool enabled);
|
||||||
|
|
||||||
|
void Shutdown();
|
||||||
|
void BeginFrame();
|
||||||
|
|
||||||
|
ProductViewportFrame RequestViewport(
|
||||||
|
ProductViewportKind kind,
|
||||||
|
const ::XCEngine::UI::UISize& requestedSize);
|
||||||
|
|
||||||
|
void RenderRequestedViewports(
|
||||||
|
const ::XCEngine::Rendering::RenderContext& renderContext);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct ViewportEntry {
|
||||||
|
ProductViewportKind kind = ProductViewportKind::Scene;
|
||||||
|
std::uint32_t requestedWidth = 0;
|
||||||
|
std::uint32_t requestedHeight = 0;
|
||||||
|
bool requestedThisFrame = false;
|
||||||
|
bool renderedThisFrame = false;
|
||||||
|
ProductViewportRenderTargets renderTargets = {};
|
||||||
|
std::string statusText = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
ViewportEntry& GetEntry(ProductViewportKind kind);
|
||||||
|
const ViewportEntry& GetEntry(ProductViewportKind kind) const;
|
||||||
|
void DestroyViewportEntry(ViewportEntry& entry);
|
||||||
|
bool EnsureViewportResources(ViewportEntry& entry);
|
||||||
|
void ClearViewport(
|
||||||
|
ViewportEntry& entry,
|
||||||
|
const ::XCEngine::Rendering::RenderContext& renderContext,
|
||||||
|
float r,
|
||||||
|
float g,
|
||||||
|
float b,
|
||||||
|
float a);
|
||||||
|
ProductViewportFrame BuildFrame(
|
||||||
|
const ViewportEntry& entry,
|
||||||
|
const ::XCEngine::UI::UISize& requestedSize) const;
|
||||||
|
|
||||||
|
Host::D3D12WindowRenderer* m_windowRenderer = nullptr;
|
||||||
|
::XCEngine::RHI::RHIDevice* m_device = nullptr;
|
||||||
|
bool m_surfacePresentationEnabled = false;
|
||||||
|
std::array<ViewportEntry, 2> m_entries = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace XCEngine::UI::Editor::App
|
||||||
1
new_editor/app/Viewport/ProductViewportRenderTargets.cpp
Normal file
1
new_editor/app/Viewport/ProductViewportRenderTargets.cpp
Normal file
@@ -0,0 +1 @@
|
|||||||
|
#include "ProductViewportRenderTargets.h"
|
||||||
301
new_editor/app/Viewport/ProductViewportRenderTargets.h
Normal file
301
new_editor/app/Viewport/ProductViewportRenderTargets.h
Normal file
@@ -0,0 +1,301 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ProductViewportSurfaceUtils.h"
|
||||||
|
|
||||||
|
#include <Host/D3D12WindowRenderer.h>
|
||||||
|
|
||||||
|
#include <XCEngine/RHI/RHIDevice.h>
|
||||||
|
#include <XCEngine/RHI/RHIResourceView.h>
|
||||||
|
#include <XCEngine/RHI/RHITexture.h>
|
||||||
|
#include <XCEngine/UI/Types.h>
|
||||||
|
|
||||||
|
namespace XCEngine::UI::Editor::App {
|
||||||
|
|
||||||
|
struct ProductViewportRenderTargets {
|
||||||
|
std::uint32_t width = 0;
|
||||||
|
std::uint32_t height = 0;
|
||||||
|
::XCEngine::RHI::RHITexture* colorTexture = nullptr;
|
||||||
|
::XCEngine::RHI::RHIResourceView* colorView = nullptr;
|
||||||
|
::XCEngine::RHI::RHITexture* depthTexture = nullptr;
|
||||||
|
::XCEngine::RHI::RHIResourceView* depthView = nullptr;
|
||||||
|
::XCEngine::RHI::RHIResourceView* depthShaderView = nullptr;
|
||||||
|
::XCEngine::RHI::RHITexture* objectIdTexture = nullptr;
|
||||||
|
::XCEngine::RHI::RHITexture* objectIdDepthTexture = nullptr;
|
||||||
|
::XCEngine::RHI::RHIResourceView* objectIdDepthView = nullptr;
|
||||||
|
::XCEngine::RHI::RHIResourceView* objectIdView = nullptr;
|
||||||
|
::XCEngine::RHI::RHIResourceView* objectIdShaderView = nullptr;
|
||||||
|
::XCEngine::RHI::RHITexture* selectionMaskTexture = nullptr;
|
||||||
|
::XCEngine::RHI::RHIResourceView* selectionMaskView = nullptr;
|
||||||
|
::XCEngine::RHI::RHIResourceView* selectionMaskShaderView = nullptr;
|
||||||
|
D3D12_CPU_DESCRIPTOR_HANDLE srvCpuHandle = {};
|
||||||
|
D3D12_GPU_DESCRIPTOR_HANDLE srvGpuHandle = {};
|
||||||
|
::XCEngine::UI::UITextureHandle textureHandle = {};
|
||||||
|
::XCEngine::RHI::ResourceStates colorState = ::XCEngine::RHI::ResourceStates::Common;
|
||||||
|
::XCEngine::RHI::ResourceStates objectIdState = ::XCEngine::RHI::ResourceStates::Common;
|
||||||
|
::XCEngine::RHI::ResourceStates selectionMaskState = ::XCEngine::RHI::ResourceStates::Common;
|
||||||
|
bool hasValidObjectIdFrame = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline ProductViewportResourceReuseQuery BuildProductViewportRenderTargetsReuseQuery(
|
||||||
|
ProductViewportKind kind,
|
||||||
|
const ProductViewportRenderTargets& targets,
|
||||||
|
std::uint32_t requestedWidth,
|
||||||
|
std::uint32_t requestedHeight) {
|
||||||
|
ProductViewportResourceReuseQuery query = {};
|
||||||
|
query.kind = kind;
|
||||||
|
query.width = targets.width;
|
||||||
|
query.height = targets.height;
|
||||||
|
query.requestedWidth = requestedWidth;
|
||||||
|
query.requestedHeight = requestedHeight;
|
||||||
|
query.resources.hasColorTexture = targets.colorTexture != nullptr;
|
||||||
|
query.resources.hasColorView = targets.colorView != nullptr;
|
||||||
|
query.resources.hasDepthTexture = targets.depthTexture != nullptr;
|
||||||
|
query.resources.hasDepthView = targets.depthView != nullptr;
|
||||||
|
query.resources.hasDepthShaderView = targets.depthShaderView != nullptr;
|
||||||
|
query.resources.hasObjectIdTexture = targets.objectIdTexture != nullptr;
|
||||||
|
query.resources.hasObjectIdDepthTexture = targets.objectIdDepthTexture != nullptr;
|
||||||
|
query.resources.hasObjectIdDepthView = targets.objectIdDepthView != nullptr;
|
||||||
|
query.resources.hasObjectIdView = targets.objectIdView != nullptr;
|
||||||
|
query.resources.hasObjectIdShaderView = targets.objectIdShaderView != nullptr;
|
||||||
|
query.resources.hasSelectionMaskTexture = targets.selectionMaskTexture != nullptr;
|
||||||
|
query.resources.hasSelectionMaskView = targets.selectionMaskView != nullptr;
|
||||||
|
query.resources.hasSelectionMaskShaderView = targets.selectionMaskShaderView != nullptr;
|
||||||
|
query.resources.hasTextureDescriptor = targets.textureHandle.IsValid();
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ::XCEngine::Rendering::RenderSurface BuildProductViewportColorSurface(
|
||||||
|
const ProductViewportRenderTargets& targets) {
|
||||||
|
return BuildProductViewportRenderSurface(
|
||||||
|
targets.width,
|
||||||
|
targets.height,
|
||||||
|
targets.colorView,
|
||||||
|
targets.depthView,
|
||||||
|
targets.colorState);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ::XCEngine::Rendering::RenderSurface BuildProductViewportObjectIdSurface(
|
||||||
|
const ProductViewportRenderTargets& targets) {
|
||||||
|
return BuildProductViewportRenderSurface(
|
||||||
|
targets.width,
|
||||||
|
targets.height,
|
||||||
|
targets.objectIdView,
|
||||||
|
targets.objectIdDepthView,
|
||||||
|
targets.objectIdState);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ::XCEngine::Rendering::RenderSurface BuildProductViewportSelectionMaskSurface(
|
||||||
|
const ProductViewportRenderTargets& targets) {
|
||||||
|
return BuildProductViewportRenderSurface(
|
||||||
|
targets.width,
|
||||||
|
targets.height,
|
||||||
|
targets.selectionMaskView,
|
||||||
|
targets.depthView,
|
||||||
|
targets.selectionMaskState);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
template <typename ResourceType>
|
||||||
|
inline void ShutdownAndDeleteViewportResource(ResourceType*& resource) {
|
||||||
|
if (resource == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resource->Shutdown();
|
||||||
|
delete resource;
|
||||||
|
resource = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool CreateViewportColorResources(
|
||||||
|
::XCEngine::RHI::RHIDevice* device,
|
||||||
|
ProductViewportRenderTargets& targets) {
|
||||||
|
const auto colorDesc =
|
||||||
|
BuildProductViewportTextureDesc(targets.width, targets.height, ::XCEngine::RHI::Format::R8G8B8A8_UNorm);
|
||||||
|
targets.colorTexture = device->CreateTexture(colorDesc);
|
||||||
|
if (targets.colorTexture == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto colorViewDesc =
|
||||||
|
BuildProductViewportTextureViewDesc(::XCEngine::RHI::Format::R8G8B8A8_UNorm);
|
||||||
|
targets.colorView = device->CreateRenderTargetView(targets.colorTexture, colorViewDesc);
|
||||||
|
return targets.colorView != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool CreateViewportDepthResources(
|
||||||
|
::XCEngine::RHI::RHIDevice* device,
|
||||||
|
ProductViewportRenderTargets& targets) {
|
||||||
|
const auto depthDesc =
|
||||||
|
BuildProductViewportTextureDesc(targets.width, targets.height, ::XCEngine::RHI::Format::D24_UNorm_S8_UInt);
|
||||||
|
targets.depthTexture = device->CreateTexture(depthDesc);
|
||||||
|
if (targets.depthTexture == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto depthViewDesc =
|
||||||
|
BuildProductViewportTextureViewDesc(::XCEngine::RHI::Format::D24_UNorm_S8_UInt);
|
||||||
|
targets.depthView = device->CreateDepthStencilView(targets.depthTexture, depthViewDesc);
|
||||||
|
if (targets.depthView == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
::XCEngine::RHI::ResourceViewDesc depthShaderViewDesc = {};
|
||||||
|
depthShaderViewDesc.dimension = ::XCEngine::RHI::ResourceViewDimension::Texture2D;
|
||||||
|
depthShaderViewDesc.mipLevel = 0;
|
||||||
|
targets.depthShaderView = device->CreateShaderResourceView(
|
||||||
|
targets.depthTexture,
|
||||||
|
depthShaderViewDesc);
|
||||||
|
return targets.depthShaderView != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool CreateViewportObjectIdResources(
|
||||||
|
::XCEngine::RHI::RHIDevice* device,
|
||||||
|
ProductViewportRenderTargets& targets) {
|
||||||
|
const auto objectIdDesc =
|
||||||
|
BuildProductViewportTextureDesc(targets.width, targets.height, ::XCEngine::RHI::Format::R8G8B8A8_UNorm);
|
||||||
|
targets.objectIdTexture = device->CreateTexture(objectIdDesc);
|
||||||
|
if (targets.objectIdTexture == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto objectIdViewDesc =
|
||||||
|
BuildProductViewportTextureViewDesc(::XCEngine::RHI::Format::R8G8B8A8_UNorm);
|
||||||
|
targets.objectIdView = device->CreateRenderTargetView(targets.objectIdTexture, objectIdViewDesc);
|
||||||
|
if (targets.objectIdView == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
targets.objectIdShaderView = device->CreateShaderResourceView(
|
||||||
|
targets.objectIdTexture,
|
||||||
|
objectIdViewDesc);
|
||||||
|
if (targets.objectIdShaderView == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto objectIdDepthDesc =
|
||||||
|
BuildProductViewportTextureDesc(targets.width, targets.height, ::XCEngine::RHI::Format::D24_UNorm_S8_UInt);
|
||||||
|
targets.objectIdDepthTexture = device->CreateTexture(objectIdDepthDesc);
|
||||||
|
if (targets.objectIdDepthTexture == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto objectIdDepthViewDesc =
|
||||||
|
BuildProductViewportTextureViewDesc(::XCEngine::RHI::Format::D24_UNorm_S8_UInt);
|
||||||
|
targets.objectIdDepthView = device->CreateDepthStencilView(
|
||||||
|
targets.objectIdDepthTexture,
|
||||||
|
objectIdDepthViewDesc);
|
||||||
|
return targets.objectIdDepthView != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool CreateViewportSelectionMaskResources(
|
||||||
|
::XCEngine::RHI::RHIDevice* device,
|
||||||
|
ProductViewportRenderTargets& targets) {
|
||||||
|
const auto selectionMaskDesc =
|
||||||
|
BuildProductViewportTextureDesc(targets.width, targets.height, ::XCEngine::RHI::Format::R8G8B8A8_UNorm);
|
||||||
|
targets.selectionMaskTexture = device->CreateTexture(selectionMaskDesc);
|
||||||
|
if (targets.selectionMaskTexture == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto selectionMaskViewDesc =
|
||||||
|
BuildProductViewportTextureViewDesc(::XCEngine::RHI::Format::R8G8B8A8_UNorm);
|
||||||
|
targets.selectionMaskView = device->CreateRenderTargetView(
|
||||||
|
targets.selectionMaskTexture,
|
||||||
|
selectionMaskViewDesc);
|
||||||
|
if (targets.selectionMaskView == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
targets.selectionMaskShaderView = device->CreateShaderResourceView(
|
||||||
|
targets.selectionMaskTexture,
|
||||||
|
selectionMaskViewDesc);
|
||||||
|
return targets.selectionMaskShaderView != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool CreateViewportTextureDescriptor(
|
||||||
|
Host::D3D12WindowRenderer& windowRenderer,
|
||||||
|
::XCEngine::RHI::RHIDevice* device,
|
||||||
|
ProductViewportRenderTargets& targets) {
|
||||||
|
if (!windowRenderer.CreateShaderResourceTextureDescriptor(
|
||||||
|
device,
|
||||||
|
targets.colorTexture,
|
||||||
|
&targets.srvCpuHandle,
|
||||||
|
&targets.srvGpuHandle)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
targets.textureHandle.nativeHandle = static_cast<std::uintptr_t>(targets.srvGpuHandle.ptr);
|
||||||
|
targets.textureHandle.width = targets.width;
|
||||||
|
targets.textureHandle.height = targets.height;
|
||||||
|
targets.textureHandle.kind = ::XCEngine::UI::UITextureHandleKind::ShaderResourceView;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
|
||||||
|
inline void DestroyProductViewportRenderTargets(
|
||||||
|
Host::D3D12WindowRenderer* windowRenderer,
|
||||||
|
ProductViewportRenderTargets& targets) {
|
||||||
|
if (windowRenderer != nullptr && targets.srvCpuHandle.ptr != 0) {
|
||||||
|
windowRenderer->FreeShaderResourceDescriptor(targets.srvCpuHandle, targets.srvGpuHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
Internal::ShutdownAndDeleteViewportResource(targets.objectIdView);
|
||||||
|
Internal::ShutdownAndDeleteViewportResource(targets.objectIdShaderView);
|
||||||
|
Internal::ShutdownAndDeleteViewportResource(targets.objectIdDepthView);
|
||||||
|
Internal::ShutdownAndDeleteViewportResource(targets.objectIdDepthTexture);
|
||||||
|
Internal::ShutdownAndDeleteViewportResource(targets.objectIdTexture);
|
||||||
|
Internal::ShutdownAndDeleteViewportResource(targets.selectionMaskView);
|
||||||
|
Internal::ShutdownAndDeleteViewportResource(targets.selectionMaskShaderView);
|
||||||
|
Internal::ShutdownAndDeleteViewportResource(targets.selectionMaskTexture);
|
||||||
|
Internal::ShutdownAndDeleteViewportResource(targets.depthShaderView);
|
||||||
|
Internal::ShutdownAndDeleteViewportResource(targets.depthView);
|
||||||
|
Internal::ShutdownAndDeleteViewportResource(targets.depthTexture);
|
||||||
|
Internal::ShutdownAndDeleteViewportResource(targets.colorView);
|
||||||
|
Internal::ShutdownAndDeleteViewportResource(targets.colorTexture);
|
||||||
|
|
||||||
|
targets.width = 0;
|
||||||
|
targets.height = 0;
|
||||||
|
targets.srvCpuHandle = {};
|
||||||
|
targets.srvGpuHandle = {};
|
||||||
|
targets.textureHandle = {};
|
||||||
|
targets.colorState = ::XCEngine::RHI::ResourceStates::Common;
|
||||||
|
targets.objectIdState = ::XCEngine::RHI::ResourceStates::Common;
|
||||||
|
targets.selectionMaskState = ::XCEngine::RHI::ResourceStates::Common;
|
||||||
|
targets.hasValidObjectIdFrame = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool CreateProductViewportRenderTargets(
|
||||||
|
ProductViewportKind kind,
|
||||||
|
std::uint32_t width,
|
||||||
|
std::uint32_t height,
|
||||||
|
::XCEngine::RHI::RHIDevice* device,
|
||||||
|
Host::D3D12WindowRenderer& windowRenderer,
|
||||||
|
ProductViewportRenderTargets& targets) {
|
||||||
|
if (width == 0u || height == 0u || device == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DestroyProductViewportRenderTargets(&windowRenderer, targets);
|
||||||
|
targets.width = width;
|
||||||
|
targets.height = height;
|
||||||
|
|
||||||
|
if (!Internal::CreateViewportColorResources(device, targets) ||
|
||||||
|
!Internal::CreateViewportDepthResources(device, targets) ||
|
||||||
|
(ProductViewportRequiresObjectIdResources(kind) &&
|
||||||
|
(!Internal::CreateViewportObjectIdResources(device, targets) ||
|
||||||
|
!Internal::CreateViewportSelectionMaskResources(device, targets))) ||
|
||||||
|
!Internal::CreateViewportTextureDescriptor(windowRenderer, device, targets)) {
|
||||||
|
DestroyProductViewportRenderTargets(&windowRenderer, targets);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
targets.colorState = ::XCEngine::RHI::ResourceStates::Common;
|
||||||
|
targets.objectIdState = ::XCEngine::RHI::ResourceStates::Common;
|
||||||
|
targets.selectionMaskState = ::XCEngine::RHI::ResourceStates::Common;
|
||||||
|
targets.hasValidObjectIdFrame = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace XCEngine::UI::Editor::App
|
||||||
130
new_editor/app/Viewport/ProductViewportSurfaceUtils.h
Normal file
130
new_editor/app/Viewport/ProductViewportSurfaceUtils.h
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ProductViewportTypes.h"
|
||||||
|
|
||||||
|
#include <XCEngine/RHI/RHIEnums.h>
|
||||||
|
#include <XCEngine/RHI/RHIResourceView.h>
|
||||||
|
#include <XCEngine/RHI/RHITypes.h>
|
||||||
|
#include <XCEngine/Rendering/RenderSurface.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace XCEngine::UI::Editor::App {
|
||||||
|
|
||||||
|
struct ProductViewportResourcePresence {
|
||||||
|
bool hasColorTexture = false;
|
||||||
|
bool hasColorView = false;
|
||||||
|
bool hasDepthTexture = false;
|
||||||
|
bool hasDepthView = false;
|
||||||
|
bool hasDepthShaderView = false;
|
||||||
|
bool hasObjectIdTexture = false;
|
||||||
|
bool hasObjectIdDepthTexture = false;
|
||||||
|
bool hasObjectIdDepthView = false;
|
||||||
|
bool hasObjectIdView = false;
|
||||||
|
bool hasObjectIdShaderView = false;
|
||||||
|
bool hasSelectionMaskTexture = false;
|
||||||
|
bool hasSelectionMaskView = false;
|
||||||
|
bool hasSelectionMaskShaderView = false;
|
||||||
|
bool hasTextureDescriptor = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ProductViewportResourceReuseQuery {
|
||||||
|
ProductViewportKind kind = ProductViewportKind::Scene;
|
||||||
|
std::uint32_t width = 0;
|
||||||
|
std::uint32_t height = 0;
|
||||||
|
std::uint32_t requestedWidth = 0;
|
||||||
|
std::uint32_t requestedHeight = 0;
|
||||||
|
ProductViewportResourcePresence resources = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
inline bool ProductViewportRequiresObjectIdResources(ProductViewportKind kind) {
|
||||||
|
return kind == ProductViewportKind::Scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::uint32_t ClampViewportPixelCoordinate(float value, std::uint32_t extent) {
|
||||||
|
if (extent == 0u) {
|
||||||
|
return 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float maxCoordinate = static_cast<float>(extent - 1u);
|
||||||
|
const float clamped = (std::max)(0.0f, (std::min)(value, maxCoordinate));
|
||||||
|
return static_cast<std::uint32_t>(std::floor(clamped));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool CanReuseProductViewportResources(const ProductViewportResourceReuseQuery& query) {
|
||||||
|
if (query.requestedWidth == 0u || query.requestedHeight == 0u) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query.width != query.requestedWidth || query.height != query.requestedHeight) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!query.resources.hasColorTexture ||
|
||||||
|
!query.resources.hasColorView ||
|
||||||
|
!query.resources.hasDepthTexture ||
|
||||||
|
!query.resources.hasDepthView ||
|
||||||
|
!query.resources.hasTextureDescriptor) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ProductViewportRequiresObjectIdResources(query.kind)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return query.resources.hasObjectIdTexture &&
|
||||||
|
query.resources.hasObjectIdDepthTexture &&
|
||||||
|
query.resources.hasObjectIdDepthView &&
|
||||||
|
query.resources.hasObjectIdView &&
|
||||||
|
query.resources.hasObjectIdShaderView &&
|
||||||
|
query.resources.hasDepthShaderView &&
|
||||||
|
query.resources.hasSelectionMaskTexture &&
|
||||||
|
query.resources.hasSelectionMaskView &&
|
||||||
|
query.resources.hasSelectionMaskShaderView;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ::XCEngine::RHI::TextureDesc BuildProductViewportTextureDesc(
|
||||||
|
std::uint32_t width,
|
||||||
|
std::uint32_t height,
|
||||||
|
::XCEngine::RHI::Format format) {
|
||||||
|
::XCEngine::RHI::TextureDesc desc = {};
|
||||||
|
desc.width = width;
|
||||||
|
desc.height = height;
|
||||||
|
desc.depth = 1;
|
||||||
|
desc.mipLevels = 1;
|
||||||
|
desc.arraySize = 1;
|
||||||
|
desc.format = static_cast<std::uint32_t>(format);
|
||||||
|
desc.textureType = static_cast<std::uint32_t>(::XCEngine::RHI::TextureType::Texture2D);
|
||||||
|
desc.sampleCount = 1;
|
||||||
|
desc.sampleQuality = 0;
|
||||||
|
desc.flags = 0;
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ::XCEngine::RHI::ResourceViewDesc BuildProductViewportTextureViewDesc(
|
||||||
|
::XCEngine::RHI::Format format) {
|
||||||
|
::XCEngine::RHI::ResourceViewDesc desc = {};
|
||||||
|
desc.format = static_cast<std::uint32_t>(format);
|
||||||
|
desc.dimension = ::XCEngine::RHI::ResourceViewDimension::Texture2D;
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ::XCEngine::Rendering::RenderSurface BuildProductViewportRenderSurface(
|
||||||
|
std::uint32_t width,
|
||||||
|
std::uint32_t height,
|
||||||
|
::XCEngine::RHI::RHIResourceView* colorView,
|
||||||
|
::XCEngine::RHI::RHIResourceView* depthView,
|
||||||
|
::XCEngine::RHI::ResourceStates colorStateBefore,
|
||||||
|
::XCEngine::RHI::ResourceStates colorStateAfter =
|
||||||
|
::XCEngine::RHI::ResourceStates::PixelShaderResource) {
|
||||||
|
::XCEngine::Rendering::RenderSurface surface(width, height);
|
||||||
|
surface.SetColorAttachment(colorView);
|
||||||
|
surface.SetDepthAttachment(depthView);
|
||||||
|
surface.SetColorStateBefore(colorStateBefore);
|
||||||
|
surface.SetColorStateAfter(colorStateAfter);
|
||||||
|
return surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace XCEngine::UI::Editor::App
|
||||||
24
new_editor/app/Viewport/ProductViewportTypes.h
Normal file
24
new_editor/app/Viewport/ProductViewportTypes.h
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <XCEngine/UI/Types.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace XCEngine::UI::Editor::App {
|
||||||
|
|
||||||
|
enum class ProductViewportKind : std::uint8_t {
|
||||||
|
Scene = 0,
|
||||||
|
Game
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ProductViewportFrame {
|
||||||
|
::XCEngine::UI::UITextureHandle texture = {};
|
||||||
|
::XCEngine::UI::UISize requestedSize = {};
|
||||||
|
::XCEngine::UI::UISize renderSize = {};
|
||||||
|
bool hasTexture = false;
|
||||||
|
bool wasRequested = false;
|
||||||
|
std::string statusText = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace XCEngine::UI::Editor::App
|
||||||
@@ -16,6 +16,55 @@ using ::XCEngine::UI::UIDrawList;
|
|||||||
using ::XCEngine::UI::UIInputEvent;
|
using ::XCEngine::UI::UIInputEvent;
|
||||||
using ::XCEngine::UI::UIInputEventType;
|
using ::XCEngine::UI::UIInputEventType;
|
||||||
|
|
||||||
|
bool IsProductViewportPanel(std::string_view panelId) {
|
||||||
|
return panelId == "scene" || panelId == "game";
|
||||||
|
}
|
||||||
|
|
||||||
|
ProductViewportKind ResolveProductViewportKind(std::string_view panelId) {
|
||||||
|
return panelId == "game"
|
||||||
|
? ProductViewportKind::Game
|
||||||
|
: ProductViewportKind::Scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplyViewportFrameToPresentation(
|
||||||
|
const ProductViewportFrame& viewportFrame,
|
||||||
|
UIEditorWorkspacePanelPresentationModel& presentation) {
|
||||||
|
presentation.viewportShellModel.frame.texture = viewportFrame.texture;
|
||||||
|
presentation.viewportShellModel.frame.requestedSize = viewportFrame.requestedSize;
|
||||||
|
presentation.viewportShellModel.frame.presentedSize = viewportFrame.renderSize;
|
||||||
|
presentation.viewportShellModel.frame.hasTexture = viewportFrame.hasTexture;
|
||||||
|
presentation.viewportShellModel.frame.statusText = viewportFrame.statusText;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<UIEditorWorkspacePanelPresentationModel> BuildWorkspacePresentations(
|
||||||
|
const UIEditorShellInteractionDefinition& definition,
|
||||||
|
const UIEditorShellInteractionRequest& shellRequest,
|
||||||
|
ProductViewportHostService& viewportHostService) {
|
||||||
|
std::vector<UIEditorWorkspacePanelPresentationModel> presentations =
|
||||||
|
definition.workspacePresentations;
|
||||||
|
for (UIEditorWorkspacePanelPresentationModel& presentation : presentations) {
|
||||||
|
if (presentation.kind != UIEditorPanelPresentationKind::ViewportShell ||
|
||||||
|
!IsProductViewportPanel(presentation.panelId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UIEditorWorkspaceViewportComposeRequest* viewportRequest =
|
||||||
|
FindUIEditorWorkspaceViewportPresentationRequest(
|
||||||
|
shellRequest.shellRequest.workspaceRequest,
|
||||||
|
presentation.panelId);
|
||||||
|
const ::XCEngine::UI::UISize requestedSize =
|
||||||
|
viewportRequest != nullptr
|
||||||
|
? viewportRequest->viewportShellRequest.requestedViewportSize
|
||||||
|
: ::XCEngine::UI::UISize();
|
||||||
|
ApplyViewportFrameToPresentation(
|
||||||
|
viewportHostService.RequestViewport(
|
||||||
|
ResolveProductViewportKind(presentation.panelId),
|
||||||
|
requestedSize),
|
||||||
|
presentation);
|
||||||
|
}
|
||||||
|
return presentations;
|
||||||
|
}
|
||||||
|
|
||||||
UIEditorShellComposeModel BuildShellComposeModelFromFrame(
|
UIEditorShellComposeModel BuildShellComposeModelFromFrame(
|
||||||
const UIEditorShellInteractionFrame& frame) {
|
const UIEditorShellInteractionFrame& frame) {
|
||||||
UIEditorShellComposeModel model = {};
|
UIEditorShellComposeModel model = {};
|
||||||
@@ -93,6 +142,7 @@ void ProductEditorWorkspace::Shutdown() {
|
|||||||
m_shellFrame = {};
|
m_shellFrame = {};
|
||||||
m_shellInteractionState = {};
|
m_shellInteractionState = {};
|
||||||
m_traceEntries.clear();
|
m_traceEntries.clear();
|
||||||
|
m_viewportHostService.Shutdown();
|
||||||
m_builtInIcons.Shutdown();
|
m_builtInIcons.Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,8 +153,21 @@ void ProductEditorWorkspace::Update(
|
|||||||
std::string_view captureText) {
|
std::string_view captureText) {
|
||||||
const auto& metrics = ResolveUIEditorShellInteractionMetrics();
|
const auto& metrics = ResolveUIEditorShellInteractionMetrics();
|
||||||
context.SyncSessionFromWorkspace();
|
context.SyncSessionFromWorkspace();
|
||||||
const UIEditorShellInteractionDefinition definition =
|
UIEditorShellInteractionDefinition definition =
|
||||||
context.BuildShellDefinition(captureText);
|
context.BuildShellDefinition(captureText);
|
||||||
|
m_viewportHostService.BeginFrame();
|
||||||
|
const UIEditorShellInteractionRequest shellRequest =
|
||||||
|
ResolveUIEditorShellInteractionRequest(
|
||||||
|
bounds,
|
||||||
|
context.GetWorkspaceController(),
|
||||||
|
definition,
|
||||||
|
m_shellInteractionState,
|
||||||
|
metrics,
|
||||||
|
context.GetShellServices());
|
||||||
|
definition.workspacePresentations = BuildWorkspacePresentations(
|
||||||
|
definition,
|
||||||
|
shellRequest,
|
||||||
|
m_viewportHostService);
|
||||||
const std::vector<UIInputEvent> hostedContentEvents = inputEvents;
|
const std::vector<UIInputEvent> hostedContentEvents = inputEvents;
|
||||||
const std::vector<UIInputEvent> shellEvents =
|
const std::vector<UIInputEvent> shellEvents =
|
||||||
HasHostedContentCapture()
|
HasHostedContentCapture()
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "Panels/ProductHierarchyPanel.h"
|
#include "Panels/ProductHierarchyPanel.h"
|
||||||
#include "Panels/ProductInspectorPanel.h"
|
#include "Panels/ProductInspectorPanel.h"
|
||||||
#include "Panels/ProductProjectPanel.h"
|
#include "Panels/ProductProjectPanel.h"
|
||||||
|
#include "Viewport/ProductViewportHostService.h"
|
||||||
#include "Workspace/ProductEditorWorkspaceEventRouter.h"
|
#include "Workspace/ProductEditorWorkspaceEventRouter.h"
|
||||||
|
|
||||||
#include <Host/NativeRenderer.h>
|
#include <Host/NativeRenderer.h>
|
||||||
@@ -50,6 +51,7 @@ public:
|
|||||||
bool HasInteractiveCapture() const;
|
bool HasInteractiveCapture() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
ProductViewportHostService m_viewportHostService = {};
|
||||||
ProductBuiltInIcons m_builtInIcons = {};
|
ProductBuiltInIcons m_builtInIcons = {};
|
||||||
ProductConsolePanel m_consolePanel = {};
|
ProductConsolePanel m_consolePanel = {};
|
||||||
ProductHierarchyPanel m_hierarchyPanel = {};
|
ProductHierarchyPanel m_hierarchyPanel = {};
|
||||||
|
|||||||
@@ -5,14 +5,16 @@ project(XCEngine_EditorUITests)
|
|||||||
set(XCENGINE_EDITOR_UI_TESTS_EDITOR_ROOT
|
set(XCENGINE_EDITOR_UI_TESTS_EDITOR_ROOT
|
||||||
"${CMAKE_SOURCE_DIR}/new_editor")
|
"${CMAKE_SOURCE_DIR}/new_editor")
|
||||||
|
|
||||||
if(NOT EXISTS "${XCENGINE_EDITOR_UI_TESTS_EDITOR_ROOT}/Host/AutoScreenshot.h"
|
if(NOT EXISTS "${XCENGINE_EDITOR_UI_TESTS_EDITOR_ROOT}/app/Host/AutoScreenshot.h"
|
||||||
OR NOT EXISTS "${XCENGINE_EDITOR_UI_TESTS_EDITOR_ROOT}/Host/InputModifierTracker.h"
|
OR NOT EXISTS "${XCENGINE_EDITOR_UI_TESTS_EDITOR_ROOT}/app/Host/InputModifierTracker.h"
|
||||||
OR NOT EXISTS "${XCENGINE_EDITOR_UI_TESTS_EDITOR_ROOT}/Host/NativeRenderer.h")
|
OR NOT EXISTS "${XCENGINE_EDITOR_UI_TESTS_EDITOR_ROOT}/app/Host/NativeRenderer.h")
|
||||||
message(FATAL_ERROR
|
message(FATAL_ERROR
|
||||||
"Editor UI tests expect host headers under "
|
"Editor UI tests expect host headers under "
|
||||||
"${XCENGINE_EDITOR_UI_TESTS_EDITOR_ROOT}/Host.")
|
"${XCENGINE_EDITOR_UI_TESTS_EDITOR_ROOT}/app/Host.")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
include_directories("${XCENGINE_EDITOR_UI_TESTS_EDITOR_ROOT}/app")
|
||||||
|
|
||||||
add_subdirectory(unit)
|
add_subdirectory(unit)
|
||||||
add_subdirectory(integration)
|
add_subdirectory(integration)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user