我实现了一个渲染图像的 CPU 路径跟踪器。该图像稍后使用 DirectX 12 在屏幕上传输。
在我昨天尝试之前,该代码已经工作了多年。 我突然得到了人工制品.
例如,这里通过路径跟踪生成的浮点图像:
我在代码方面,这里是我的基本图像类:
class Image
{
public:
Image(const Image& src);
virtual ~Image() {};
nbUint32 getWidth() const;
nbUint32 getHeight() const;
nbUint64 getBytesPerRow() const;
const unsigned char* getRawData() const;
ImageFormat getFormat() const;
nbUint32 getNbPixels() const;
protected:
Image(ImageFormat imFormat);
Image(nbUint32 width, nbUint32 height, ImageFormat imFormat);
cv::Mat m_image;
ImageFormat m_format;
};
这是我在屏幕上绘制图像的渲染器类。
class DX12Renderer
{
public:
void drawImage(const Texture::Image& image) override;
void createTexture2D(const Texture::Image* image, Dx12TextureHandle& dst);
void releaseTexture(Dx12TextureHandle& textureHandle) const;
//etc....
private:
void prepareViewportRender();
enum class CommandType
{
Direct,
Copy
};
struct CommandBuffer
{
ID3D12CommandAllocator* commandAllocator[SwapChainBufferCount];
ID3D12GraphicsCommandList* commandList;
ID3D12CommandQueue* commandQueue;
ID3D12Fence* fences[SwapChainBufferCount];
HANDLE fenceEvent;
UINT64 fenceValue[SwapChainBufferCount];
nbInt32 frameIndex;
};
std::unordered_map<CommandType, CommandBuffer> m_commandBuffers;
Dx12TextureHandle m_blittingBuffers[SwapChainBufferCount];
ID3D12Resource* m_renderTargets[SwapChainBufferCount];
// Descriptor allocators
std::unique_ptr<Descriptor::CBV_SRV_UAV_DescriptorAllocator> m_cbs_srv_uavAllocator;
std::unique_ptr<Descriptor::RTV_DescriptorAllocator> m_rtvAllocator;
std::unique_ptr<Descriptor::DSV_DescriptorAllocator> m_dsvAllocator;
//etc....
}
inline void DX12Renderer::drawImage(const Texture::Image& image)
{
prepareViewportRender();
auto& commandList = m_commandBuffers[CommandType::Direct].commandList;
const nbInt32 frameIdx = m_commandBuffers[CommandType::Direct].frameIndex;
Dx12TextureHandle& blitBuffer = m_blittingBuffers[frameIdx];
// TODO : Only recreate texture when image size is different.
// Release current blit buffer
if (blitBuffer.buffer)
releaseTexture(blitBuffer);
// Create texture from image
createTexture2D(&image, blitBuffer);
// Now draw
commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[frameIdx], D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET));
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(m_rtvDescriptorsHandle.getCpuHandle(),
frameIdx,
m_rtvAllocator->getDescriptorSize());
commandList->OMSetRenderTargets(1, &rtvHandle, FALSE, nullptr);
{
Effect::RenderRectanglePushArgs args = { blitBuffer };
m_renderRectangleEffect->pushDrawCommands(args, commandList, frameIdx);
}
commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[frameIdx], D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT));
}
inline void DX12Renderer::prepareViewportRender()
{
auto& commandList = m_commandBuffers[CommandType::Direct].commandList;
const nbInt32 frameIdx = m_commandBuffers[CommandType::Direct].frameIndex;
// Set the viewport
commandList->RSSetViewports(1, &m_viewport);
// Set the scissor rect
commandList->RSSetScissorRects(1, &m_scissorRect);
// Set the descriptor heaps
ID3D12DescriptorHeap* descriptorHeaps[] = { m_cbs_srv_uavAllocator->getHeap() };
commandList->SetDescriptorHeaps(_countof(descriptorHeaps), descriptorHeaps);
}
inline void DX12Renderer::createTexture2D(const Texture::Image* image, Dx12TextureHandle& dst)
{
const nbUint32 width = image->getWidth();
const nbUint32 height = image->getHeight();
if (width > D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION)
throw CreateTextureException("Image width is too high.");
if (height > D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION)
throw CreateTextureException("Image height is too high");
D3D12_RESOURCE_DESC textureDesc = {};
textureDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
textureDesc.Alignment = 0; // may be 0, 4KB, 64KB, or 4MB. 0 will let runtime decide between 64KB and 4MB (4MB for multi-sampled textures)
textureDesc.Width = width;
textureDesc.Height = height;
textureDesc.DepthOrArraySize = 1; // if 3d image, depth of 3d image. Otherwise an array of 1D or 2D textures.
textureDesc.MipLevels = 1; // Number of mipmaps. We are not generating mipmaps for this texture, so we have only one level.
const Texture::ImageFormat imageFormat = image->getFormat();
switch (imageFormat)
{
case Texture::ImageFormat::RGBA8:
textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
break;
case Texture::ImageFormat::RGB32F:
textureDesc.Format = DXGI_FORMAT_R32G32B32_FLOAT;
break;
case Texture::ImageFormat::RGBA32F:
textureDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
break;
default:
throw CreateTextureException("Unsupported image format");
}
textureDesc.SampleDesc.Count = 1; // This is the number of samples per pixel, we just want 1 sample
textureDesc.SampleDesc.Quality = 0; // The quality level of the samples. Higher is better quality, but worse performance
textureDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; // The arrangement of the pixels. Setting to unknown lets the driver choose the most efficient one
textureDesc.Flags = D3D12_RESOURCE_FLAG_NONE; // no flags
// Create a default heap.
HRESULT hr = D3D12Device->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
D3D12_HEAP_FLAG_NONE,
&textureDesc,
D3D12_RESOURCE_STATE_COPY_DEST, // We will copy the texture from the upload heap to here, so we start it out in a copy dest state
nullptr,
IID_PPV_ARGS(&dst.buffer));
if (FAILED(hr))
{
return;
}
dst.format = textureDesc.Format;
UINT64 textureUploadBufferSize;
D3D12Device->GetCopyableFootprints(&textureDesc, 0, 1, 0, nullptr, nullptr, nullptr, &textureUploadBufferSize);
// Now we create an upload heap to upload our texture to the GPU
ID3D12Resource* textureBufferUploadHeap;
hr = D3D12Device->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
D3D12_HEAP_FLAG_NONE,
&CD3DX12_RESOURCE_DESC::Buffer(textureUploadBufferSize),
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&textureBufferUploadHeap));
if (FAILED(hr))
{
return;
}
// Initialize subresources
D3D12_SUBRESOURCE_DATA subResourceData;
subResourceData.pData = image->getRawData();
subResourceData.RowPitch = image->getBytesPerRow();
subResourceData.SlicePitch = image->getBytesPerRow() * height;
// Now we copy the upload buffer contents to the default heap
UpdateSubresources(m_commandBuffers[CommandType::Direct].commandList, dst.buffer, textureBufferUploadHeap, 0, 0, 1, &subResourceData);
m_commandBuffers[CommandType::Direct].commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(dst.buffer, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COMMON));
// Create buffer view
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
srvDesc.Format = dst.format;
srvDesc.Texture2D.MipLevels = 1;
dst.descriptorHandle = m_cbs_srv_uavAllocator->allocate({ dst.buffer }, srvDesc);
}
inline void releaseTexture(Dx12TextureHandle& textureHandle) const
{
m_cbs_srv_uavAllocator->release(textureHandle.descriptorHandle);
textureHandle.buffer->Release();
textureHandle.buffer = nullptr;
}
我检查了渲染文档以验证输入纹理是问题所在。 我得到这个作为输入:
输入的纹理明显错误。我相信这是由于 createTexture2D 功能。 此外,输出与输入纹理惊人地不同。