c++ 创建DirectX12纹理的正确方法

k3bvogb1  于 2023-04-13  发布在  其他
关注(0)|答案(1)|浏览(232)

我实现了一个CPU路径跟踪器,它可以渲染成一个图像。这个图像稍后会使用DirectX 12在屏幕上进行blit。
代码已经工作了多年,直到我昨天尝试。我突然得到文物
例如,这里是由路径跟踪生成的浮点图像:

我在屏幕

上进行blitting后获得的内容
我的代码条款,这里我的基本图像类:

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;
}

下面是渲染矩形效果:

void RenderRectangle::pushDrawCommands(RenderRectanglePushArgs& data, ID3D12GraphicsCommandList* commandList, nbInt32 frameIndex)
{    
    commandList->SetPipelineState(m_PSO);
    commandList->SetGraphicsRootSignature(m_rootSignature);
    commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

    {
        PixelShaderCB cb;
        cb.color = { data.color.x, data.color.y, data.color.z, data.color.w };
        cb.hasTexture = !!data.textureHandle.buffer;
        memcpy(m_pixelShaderCBGPUAddress[frameIndex], &cb, sizeof(PixelShaderCB));
    }

    commandList->SetGraphicsRootConstantBufferView(0, m_pixelShaderCBUploadHeaps[frameIndex]->GetGPUVirtualAddress());

    if (data.textureHandle.buffer)
    {
        commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(data.textureHandle.buffer, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE));
        commandList->SetGraphicsRootDescriptorTable(1, data.textureHandle.descriptorHandle.getGpuHandle());
    }

    commandList->IASetVertexBuffers(0, 1, &m_vtxBuffer.bufferView);
    commandList->IASetIndexBuffer(&m_idxBuffer.bufferView);
    commandList->DrawIndexedInstanced(6, 1, 0, 0, 0);

    if (data.textureHandle.buffer)
    {
        commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(data.textureHandle.buffer, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_COMMON));
    }
}

这里是渲染矩形着色器:

/////////////////// VERTEX SHADER  ////////////////////
struct VS_INPUT
{
    float4 position : POSITION;
    float2 texCoord: TEXCOORD;
};

struct VS_OUTPUT
{
    float4 position: SV_POSITION;
    float2 texCoord: TEXCOORD;
};

VS_OUTPUT main(VS_INPUT input)
{
    VS_OUTPUT output;
    output.texCoord = input.texCoord;
    output.position = input.position;

    return output;
}

/////////////////// PIXEL SHADER  ////////////////////

struct VS_OUTPUT
{
    float4 position: SV_POSITION;
    float2 texCoord: TEXCOORD;
};

cbuffer PixelShaderCB : register(b0)
{
    float4 color;
    bool hasTexture;
};

Texture2D billboardTex : register(t0);
SamplerState tSampler : register(s0);

float4 main(VS_OUTPUT input) : SV_TARGET
{
    if (hasTexture)
        return billboardTex.Sample(tSampler, input.texCoord) * color;

    return color;
}

我检查了渲染文档来验证输入纹理是否有问题。我得到了以下输入:

这是输出:

输入的纹理明显是错误的。我相信这是由于createTexture2D函数,但我不明白为什么。而且输出的纹理与输入的纹理有惊人的不同。

jc3wubiy

jc3wubiy1#

这里的错误是你假设纹理资源的行间距与源图像相同。这不一定是真的。你需要逐行复制并将两个指针按各自的rowPitch前进:

  • 将源指针前进image->getBytesPerRow()
  • 将目标指针前进D3D12_SUBRESOURCE_INFO.RowPitch

请参见D3DX12实用工具helper中的MemcpySubresource

相关问题