创建 DirectX12 纹理的正确方法

问题描述 投票:0回答:1

我实现了一个渲染图像的 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;
}

这里渲染矩形效果:

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 功能,但我不明白为什么。 此外,输出与输入纹理惊人地不同。

c++ directx
1个回答
0
投票

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

  • 将源指针前进

    image->getBytesPerRow()
    .

  • 将目标指针前进

    D3D12_SUBRESOURCE_INFO.RowPitch
    .

参见 D3DX12 实用程序助手中的 MemcpySubresource

© www.soinside.com 2019 - 2024. All rights reserved.