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

@@ -95,6 +95,13 @@ public:
RHIResourceView* CreateDepthStencilView(RHITexture* texture, const ResourceViewDesc& desc) override;
RHIResourceView* CreateShaderResourceView(RHITexture* texture, const ResourceViewDesc& desc) override;
RHIResourceView* CreateUnorderedAccessView(RHITexture* texture, const ResourceViewDesc& desc) override;
bool ReadTexturePixelRGBA8(
RHICommandQueue* commandQueue,
RHITexture* texture,
ResourceStates sourceState,
uint32_t pixelX,
uint32_t pixelY,
std::array<uint8_t, 4>& outRgba) override;
const RHICapabilities& GetCapabilities() const override;
const RHIDeviceInfo& GetDeviceInfo() const override;

View File

@@ -144,6 +144,7 @@ inline DXGI_FORMAT ToD3D12(Format format) {
case Format::R16_Float: return DXGI_FORMAT_R16_FLOAT;
case Format::R32_Float: return DXGI_FORMAT_R32_FLOAT;
case Format::R32G32_Float: return DXGI_FORMAT_R32G32_FLOAT;
case Format::R32G32B32_Float: return DXGI_FORMAT_R32G32B32_FLOAT;
case Format::D16_UNorm: return DXGI_FORMAT_D16_UNORM;
case Format::D24_UNorm_S8_UInt: return DXGI_FORMAT_D24_UNORM_S8_UINT;
case Format::D32_Float: return DXGI_FORMAT_D32_FLOAT;
@@ -172,6 +173,7 @@ inline Format FromD3D12(DXGI_FORMAT format) {
case DXGI_FORMAT_R16_FLOAT: return Format::R16_Float;
case DXGI_FORMAT_R32_FLOAT: return Format::R32_Float;
case DXGI_FORMAT_R32G32_FLOAT: return Format::R32G32_Float;
case DXGI_FORMAT_R32G32B32_FLOAT: return Format::R32G32B32_Float;
case DXGI_FORMAT_D16_UNORM: return Format::D16_UNorm;
case DXGI_FORMAT_D24_UNORM_S8_UINT: return Format::D24_UNorm_S8_UInt;
case DXGI_FORMAT_D32_FLOAT: return Format::D32_Float;

View File

@@ -6,6 +6,8 @@
#include "RHIDescriptorSet.h"
#include "RHIRenderPass.h"
#include "RHIFramebuffer.h"
#include <array>
#include <string>
namespace XCEngine {
@@ -65,6 +67,22 @@ public:
virtual RHIResourceView* CreateShaderResourceView(RHITexture* texture, const ResourceViewDesc& desc) = 0;
virtual RHIResourceView* CreateUnorderedAccessView(RHITexture* texture, const ResourceViewDesc& desc) = 0;
virtual bool ReadTexturePixelRGBA8(
RHICommandQueue* commandQueue,
RHITexture* texture,
ResourceStates sourceState,
uint32_t pixelX,
uint32_t pixelY,
std::array<uint8_t, 4>& outRgba) {
(void)commandQueue;
(void)texture;
(void)sourceState;
(void)pixelX;
(void)pixelY;
outRgba = {};
return false;
}
virtual const RHICapabilities& GetCapabilities() const = 0;
virtual const RHIDeviceInfo& GetDeviceInfo() const = 0;

View File

@@ -316,7 +316,8 @@ enum class Format : uint32_t {
BC7_UNorm,
R32G32B32A32_UInt,
R32_UInt,
R32G32_Float
R32G32_Float,
R32G32B32_Float
};
enum class ResourceStates : uint32_t {

View File

@@ -91,6 +91,8 @@ inline VkFormat ToVulkanFormat(Format format) {
return VK_FORMAT_R32_UINT;
case Format::R32G32_Float:
return VK_FORMAT_R32G32_SFLOAT;
case Format::R32G32B32_Float:
return VK_FORMAT_R32G32B32_SFLOAT;
case Format::R32G32B32A32_Float:
return VK_FORMAT_R32G32B32A32_SFLOAT;
default:
@@ -116,6 +118,8 @@ inline uint32_t GetFormatSize(Format format) {
return 4;
case Format::R32G32_Float:
return 8;
case Format::R32G32B32_Float:
return 12;
case Format::R32G32B32A32_Float:
return 16;
default:
@@ -146,6 +150,8 @@ inline Format ToRHIFormat(VkFormat format) {
return Format::R32_UInt;
case VK_FORMAT_R32G32_SFLOAT:
return Format::R32G32_Float;
case VK_FORMAT_R32G32B32_SFLOAT:
return Format::R32G32B32_Float;
case VK_FORMAT_R32G32B32A32_SFLOAT:
return Format::R32G32B32A32_Float;
default:

View File

@@ -61,6 +61,46 @@ bool CompileD3D12Shader(const ShaderCompileDesc& desc, D3D12Shader& shader) {
return false;
}
bool WaitForQueueIdle(
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;
}
uint32_t GetFormatBytesPerPixel(Format format) {
switch (format) {
case Format::R8_UNorm:
@@ -77,6 +117,8 @@ uint32_t GetFormatBytesPerPixel(Format format) {
return 4;
case Format::R32G32_Float:
return 8;
case Format::R32G32B32_Float:
return 12;
case Format::R32G32B32A32_Float:
return 16;
default:
@@ -383,6 +425,142 @@ void* D3D12Device::GetNativeDevice() {
return m_device.Get();
}
bool D3D12Device::ReadTexturePixelRGBA8(
RHICommandQueue* commandQueue,
RHITexture* texture,
ResourceStates sourceState,
uint32_t pixelX,
uint32_t pixelY,
std::array<uint8_t, 4>& outRgba) {
outRgba = {};
auto* d3d12Queue = static_cast<D3D12CommandQueue*>(commandQueue);
auto* d3d12Texture = static_cast<D3D12Texture*>(texture);
if (m_device == nullptr ||
d3d12Queue == nullptr ||
d3d12Texture == nullptr ||
d3d12Texture->GetResource() == nullptr ||
pixelX >= d3d12Texture->GetWidth() ||
pixelY >= d3d12Texture->GetHeight()) {
return false;
}
ID3D12CommandAllocator* commandAllocator = nullptr;
if (FAILED(m_device->CreateCommandAllocator(
D3D12_COMMAND_LIST_TYPE_DIRECT,
IID_PPV_ARGS(&commandAllocator)))) {
return false;
}
ID3D12GraphicsCommandList* commandList = nullptr;
if (FAILED(m_device->CreateCommandList(
0,
D3D12_COMMAND_LIST_TYPE_DIRECT,
commandAllocator,
nullptr,
IID_PPV_ARGS(&commandList)))) {
commandAllocator->Release();
return false;
}
constexpr UINT kRowPitch = D3D12_TEXTURE_DATA_PITCH_ALIGNMENT;
D3D12_HEAP_PROPERTIES heapProperties = {};
heapProperties.Type = D3D12_HEAP_TYPE_READBACK;
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(m_device->CreateCommittedResource(
&heapProperties,
D3D12_HEAP_FLAG_NONE,
&readbackDesc,
D3D12_RESOURCE_STATE_COPY_DEST,
nullptr,
IID_PPV_ARGS(&readbackBuffer)))) {
commandList->Release();
commandAllocator->Release();
return false;
}
ID3D12Resource* sourceResource = d3d12Texture->GetResource();
D3D12_RESOURCE_BARRIER barrier = {};
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrier.Transition.pResource = sourceResource;
barrier.Transition.StateBefore = 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_TEXTURE_COPY_LOCATION dstLocation = {};
dstLocation.pResource = readbackBuffer;
dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
dstLocation.PlacedFootprint.Offset = 0;
dstLocation.PlacedFootprint.Footprint.Format = sourceResource->GetDesc().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 = ToD3D12(sourceState);
commandList->ResourceBarrier(1, &barrier);
if (FAILED(commandList->Close())) {
readbackBuffer->Release();
commandList->Release();
commandAllocator->Release();
return false;
}
ID3D12CommandList* commandLists[] = { commandList };
d3d12Queue->GetCommandQueue()->ExecuteCommandLists(1, commandLists);
if (!WaitForQueueIdle(m_device.Get(), d3d12Queue->GetCommandQueue())) {
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;
}
const RHICapabilities& D3D12Device::GetCapabilities() const {
return m_capabilities;
}