refactor: move scene view readback into rhi device and sync float3 format mappings

This commit is contained in:
2026-04-01 17:05:48 +08:00
parent 67d5390121
commit 409e08d03c
7 changed files with 218 additions and 184 deletions

View File

@@ -15,7 +15,6 @@
#include <XCEngine/Components/CameraComponent.h>
#include <XCEngine/Components/GameObject.h>
#include <XCEngine/RHI/D3D12/D3D12CommandQueue.h>
#include <XCEngine/RHI/D3D12/D3D12Device.h>
#include <XCEngine/RHI/D3D12/D3D12Texture.h>
#include <XCEngine/RHI/RHIEnums.h>
@@ -89,181 +88,6 @@ inline uint32_t ClampViewportPixelCoordinate(float value, uint32_t extent) {
return static_cast<uint32_t>(std::floor(clamped));
}
inline bool WaitForD3D12QueueIdle(
ID3D12Device* device,
ID3D12CommandQueue* commandQueue) {
if (device == nullptr || commandQueue == nullptr) {
return false;
}
ID3D12Fence* fence = nullptr;
if (FAILED(device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence)))) {
return false;
}
HANDLE fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (fenceEvent == nullptr) {
fence->Release();
return false;
}
constexpr UINT64 kFenceValue = 1;
const HRESULT signalResult = commandQueue->Signal(fence, kFenceValue);
if (FAILED(signalResult)) {
CloseHandle(fenceEvent);
fence->Release();
return false;
}
if (fence->GetCompletedValue() < kFenceValue) {
if (FAILED(fence->SetEventOnCompletion(kFenceValue, fenceEvent))) {
CloseHandle(fenceEvent);
fence->Release();
return false;
}
WaitForSingleObject(fenceEvent, INFINITE);
}
CloseHandle(fenceEvent);
fence->Release();
return true;
}
inline bool ReadBackD3D12TexturePixel(
RHI::D3D12Device& device,
RHI::D3D12CommandQueue& commandQueue,
RHI::D3D12Texture& texture,
RHI::ResourceStates sourceState,
uint32_t pixelX,
uint32_t pixelY,
std::array<uint8_t, 4>& outRgba) {
ID3D12Device* nativeDevice = device.GetDevice();
ID3D12CommandQueue* nativeCommandQueue = commandQueue.GetCommandQueue();
ID3D12Resource* sourceResource = texture.GetResource();
if (nativeDevice == nullptr ||
nativeCommandQueue == nullptr ||
sourceResource == nullptr ||
pixelX >= texture.GetWidth() ||
pixelY >= texture.GetHeight()) {
return false;
}
ID3D12CommandAllocator* commandAllocator = nullptr;
if (FAILED(nativeDevice->CreateCommandAllocator(
D3D12_COMMAND_LIST_TYPE_DIRECT,
IID_PPV_ARGS(&commandAllocator)))) {
return false;
}
ID3D12GraphicsCommandList* commandList = nullptr;
if (FAILED(nativeDevice->CreateCommandList(
0,
D3D12_COMMAND_LIST_TYPE_DIRECT,
commandAllocator,
nullptr,
IID_PPV_ARGS(&commandList)))) {
commandAllocator->Release();
return false;
}
D3D12_HEAP_PROPERTIES heapProperties = {};
heapProperties.Type = D3D12_HEAP_TYPE_READBACK;
constexpr UINT kRowPitch = D3D12_TEXTURE_DATA_PITCH_ALIGNMENT;
D3D12_RESOURCE_DESC readbackDesc = {};
readbackDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
readbackDesc.Width = kRowPitch;
readbackDesc.Height = 1;
readbackDesc.DepthOrArraySize = 1;
readbackDesc.MipLevels = 1;
readbackDesc.SampleDesc.Count = 1;
readbackDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
ID3D12Resource* readbackBuffer = nullptr;
if (FAILED(nativeDevice->CreateCommittedResource(
&heapProperties,
D3D12_HEAP_FLAG_NONE,
&readbackDesc,
D3D12_RESOURCE_STATE_COPY_DEST,
nullptr,
IID_PPV_ARGS(&readbackBuffer)))) {
commandList->Release();
commandAllocator->Release();
return false;
}
D3D12_RESOURCE_BARRIER barrier = {};
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrier.Transition.pResource = sourceResource;
barrier.Transition.StateBefore = RHI::ToD3D12(sourceState);
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE;
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
commandList->ResourceBarrier(1, &barrier);
D3D12_TEXTURE_COPY_LOCATION srcLocation = {};
srcLocation.pResource = sourceResource;
srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
srcLocation.SubresourceIndex = 0;
D3D12_RESOURCE_DESC sourceDesc = sourceResource->GetDesc();
D3D12_TEXTURE_COPY_LOCATION dstLocation = {};
dstLocation.pResource = readbackBuffer;
dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
dstLocation.PlacedFootprint.Offset = 0;
dstLocation.PlacedFootprint.Footprint.Format = sourceDesc.Format;
dstLocation.PlacedFootprint.Footprint.Width = 1;
dstLocation.PlacedFootprint.Footprint.Height = 1;
dstLocation.PlacedFootprint.Footprint.Depth = 1;
dstLocation.PlacedFootprint.Footprint.RowPitch = kRowPitch;
D3D12_BOX sourceBox = {};
sourceBox.left = pixelX;
sourceBox.top = pixelY;
sourceBox.front = 0;
sourceBox.right = pixelX + 1u;
sourceBox.bottom = pixelY + 1u;
sourceBox.back = 1;
commandList->CopyTextureRegion(&dstLocation, 0, 0, 0, &srcLocation, &sourceBox);
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE;
barrier.Transition.StateAfter = RHI::ToD3D12(sourceState);
commandList->ResourceBarrier(1, &barrier);
if (FAILED(commandList->Close())) {
readbackBuffer->Release();
commandList->Release();
commandAllocator->Release();
return false;
}
ID3D12CommandList* commandLists[] = { commandList };
nativeCommandQueue->ExecuteCommandLists(1, commandLists);
if (!WaitForD3D12QueueIdle(nativeDevice, nativeCommandQueue)) {
readbackBuffer->Release();
commandList->Release();
commandAllocator->Release();
return false;
}
void* mappedData = nullptr;
D3D12_RANGE readRange = { 0, 4 };
if (FAILED(readbackBuffer->Map(0, &readRange, &mappedData))) {
readbackBuffer->Release();
commandList->Release();
commandAllocator->Release();
return false;
}
std::memcpy(outRgba.data(), mappedData, outRgba.size());
D3D12_RANGE writeRange = { 0, 0 };
readbackBuffer->Unmap(0, &writeRange);
readbackBuffer->Release();
commandList->Release();
commandAllocator->Release();
return true;
}
Math::Vector3 GetSceneViewportOrientationAxisVector(SceneViewportOrientationAxis axis) {
switch (axis) {
case SceneViewportOrientationAxis::PositiveX:
@@ -1191,19 +1015,17 @@ private:
}
auto* commandQueue =
static_cast<RHI::D3D12CommandQueue*>(m_sceneViewLastRenderContext.commandQueue);
auto* objectIdTexture = static_cast<RHI::D3D12Texture*>(entry.objectIdTexture);
if (commandQueue == nullptr || objectIdTexture == nullptr) {
m_sceneViewLastRenderContext.commandQueue;
if (commandQueue == nullptr) {
return false;
}
const uint32_t pixelX = ClampViewportPixelCoordinate(viewportMousePosition.x, entry.width);
const uint32_t pixelY = ClampViewportPixelCoordinate(viewportMousePosition.y, entry.height);
std::array<uint8_t, 4> rgba = {};
if (!ReadBackD3D12TexturePixel(
*m_device,
*commandQueue,
*objectIdTexture,
if (!m_device->ReadTexturePixelRGBA8(
commandQueue,
entry.objectIdTexture,
entry.objectIdState,
pixelX,
pixelY,