2
$\begingroup$

I'm hoping to port my existing toy raytracer to a window so that I can see samples accumulate over time. And then port over to Pixar's pmj02: https://graphics.pixar.com/library/ProgressiveMultiJitteredSampling/paper.pdf

I'm working within DX12 because the idea is to begin porting elements of the raytracer slowly into DX compute (or DXR), which I'm already familiar with.

I have a simple quad fragment shader that samples a single Texture2D and writes to a quad on screen. At the moment, I am only writing data on startup, so that I can prove that copying over arbitrary 2D data to a texture2D is possible. However, I can only seem to write a black quad. I've flipped the sampler in hlsl with

return 1.0f - g_WindowTexture.Sample(g_SamLinearWrap, pin.TexCoordF) 

and the quad went white, so the texture definitely has zeros for data. This leads me to believe that I'm not correctly copying data over to the GPU resource.

The window build code:

m_windowQuad.Initialize(m_D3dDevice, m_CommandList);

D3D12_DESCRIPTOR_HEAP_DESC SrvHeapDesc = {};
SrvHeapDesc.NumDescriptors = 1;
SrvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
SrvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
ThrowIfFailed(m_D3dDevice->CreateDescriptorHeap(&SrvHeapDesc, IID_PPV_ARGS(&m_SrvDescriptorHeap)));

// Fill heap with actual descriptor
CD3DX12_CPU_DESCRIPTOR_HANDLE hDescriptor(
    m_SrvDescriptorHeap->GetCPUDescriptorHandleForHeapStart());

// Load initial texture
auto texture = std::make_unique<ImageTexture>();
// texture data
const uint width = 16;
const uint pitch = 16 * sizeof(u8) * 4;
const uint height = 16;
char colors[width * height * 4];
DXGI_FORMAT format = DXGI_FORMAT_R8G8B8A8_UNORM;
for (int i = 0; i < height*width * 4; i++)
    colors[i] = i % 256;

D3D12_RESOURCE_DESC texDesc = {};
texDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
texDesc.Width = width;
texDesc.Height = height;
texDesc.DepthOrArraySize = 1;
texDesc.MipLevels = 1;
texDesc.Format = format;
texDesc.SampleDesc.Count = 1;
texDesc.SampleDesc.Quality = 0;
texDesc.Flags = D3D12_RESOURCE_FLAG_NONE;

CD3DX12_HEAP_PROPERTIES HeapProperties(D3D12_HEAP_TYPE_DEFAULT);

ThrowIfFailed(m_D3dDevice->CreateCommittedResource(&HeapProperties, D3D12_HEAP_FLAG_NONE, &texDesc, 
    D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(texture->Resource.ReleaseAndGetAddressOf())));

D3D12_SUBRESOURCE_DATA texResource;
texResource.pData = colors;
texResource.RowPitch = (pitch + (D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1)) & ~(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1);
texResource.SlicePitch = texResource.RowPitch * height;

D3D12_SHADER_RESOURCE_VIEW_DESC SrvDesc = { };
SrvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
SrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
SrvDesc.Texture2D.MostDetailedMip = 0;
SrvDesc.Texture2D.ResourceMinLODClamp = 0.0f;
SrvDesc.Format = texture->Resource->GetDesc().Format;
SrvDesc.Texture2D.MipLevels = texture->Resource->GetDesc().MipLevels;
SrvDesc.Texture2D.MostDetailedMip = 0;
SrvDesc.Texture2D.ResourceMinLODClamp = 0.0f;

Microsoft::WRL::ComPtr<ID3D12Resource> IntResource;
ThrowIfFailed(m_D3dDevice->CreateCommittedResource(
    &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
    D3D12_HEAP_FLAG_NONE,
    &CD3DX12_RESOURCE_DESC::Buffer(texResource.SlicePitch * 16),
    D3D12_RESOURCE_STATE_GENERIC_READ,
    nullptr,
    IID_PPV_ARGS(IntResource.ReleaseAndGetAddressOf())));

m_CommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(texture->Resource.Get(),
    D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_DEST));
UpdateSubresources(m_CommandList.Get(), texture->Resource.Get(), IntResource.Get(), 0, 0, 1, &texResource);
m_CommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(texture->Resource.Get(),
    D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_GENERIC_READ));

m_D3dDevice->CreateShaderResourceView(texture->Resource.Get(), &SrvDesc, hDescriptor);
texture->SRVHeapIndex = 0;
m_Textures["quad"] = std::move(texture);

And then per frame the following draw calls are made:

void PBRApp::DrawWindowQuad(ID3D12GraphicsCommandList* cmdList)
{
    cmdList->IASetVertexBuffers(0, 1, &m_windowQuad.VertexBufferView());
    cmdList->IASetIndexBuffer(&m_windowQuad.IndexBufferView());
    cmdList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

    CD3DX12_GPU_DESCRIPTOR_HANDLE Tex(m_SrvDescriptorHeap->GetGPUDescriptorHandleForHeapStart());
    Tex.Offset(0, m_cbvSrvDescriptorSize);

    cmdList->SetGraphicsRootDescriptorTable(0, Tex);
    cmdList->DrawIndexedInstanced(m_windowQuad.IndexCount, 1, 
        m_windowQuad.StartIndexLocation, m_windowQuad.BaseVertexLocation, 0);
}

So, I'm fairly confident in my root signature, PSO, and draw calls. But I can't figure out why I keep getting a black quad (possibly empty data) drawn to the screen.

Am I just going about this the wrong way?

$\endgroup$
1
$\begingroup$

So, I figured it out.

While UAV will be necessary when I start manipulating data in Compute Shaders, for the time being, SRV works fine provided the resource is read-only from the fragment shader.

The two big problems were

  1. Creating an ImageTexture that was receiving the data. Note that the data copy was occurring just fine, but the texture was never used in the rest of the code. The correct place was to copy the code into the resource within m_WindowQuad.

  2. Creating the ImageTexture (later m_WindowQuad) resource as

D3D12_RESOURCE_STATE_COPY_DEST

when it should have been defined as

D3D12_RESOURCE_STATE_COMMON

Near as I can tell, those two were the bulk of the problems. The code rewritten, now functional:

m_windowQuad.Initialize(m_D3dDevice, m_CommandList);

D3D12_DESCRIPTOR_HEAP_DESC HeapDesc = {};
HeapDesc.NumDescriptors = 1;
HeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
HeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
ThrowIfFailed(m_D3dDevice->CreateDescriptorHeap(&HeapDesc, IID_PPV_ARGS(&m_DescriptorHeap)));

// Fill heap with actual descriptor
CD3DX12_CPU_DESCRIPTOR_HANDLE hDescriptor(
    m_DescriptorHeap->GetCPUDescriptorHandleForHeapStart());

// texture data
const uint width = 16;
const uint pitch = 16 * sizeof(u8) * 4;
const uint height = 16;
char colors[width * height * 4];
DXGI_FORMAT format = DXGI_FORMAT_R8G8B8A8_UNORM;
for (int i = 0; i < height*width * 4; i++)
    colors[i] = i % 256;

D3D12_RESOURCE_DESC texDesc = {};
texDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
texDesc.Width = width;
texDesc.Height = height;
texDesc.DepthOrArraySize = 1;
texDesc.MipLevels = 1;
texDesc.Format = format;
texDesc.SampleDesc.Count = 1;
texDesc.SampleDesc.Quality = 0;
texDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;

D3D12_SUBRESOURCE_DATA texResource;
texResource.pData = colors;
texResource.RowPitch = pitch;
texResource.SlicePitch = texResource.RowPitch * height;


ThrowIfFailed(m_D3dDevice->CreateCommittedResource(
    &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
    D3D12_HEAP_FLAG_NONE, 
    &texDesc,
    D3D12_RESOURCE_STATE_COMMON,
    nullptr, 
    IID_PPV_ARGS(m_windowQuad.Texture.ReleaseAndGetAddressOf())));
m_windowQuad.Texture->SetName(L"Window Texture");

ThrowIfFailed(m_D3dDevice->CreateCommittedResource(
    &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
    D3D12_HEAP_FLAG_NONE,
    &CD3DX12_RESOURCE_DESC::Buffer(texResource.SlicePitch * 16),
    D3D12_RESOURCE_STATE_GENERIC_READ,
    nullptr,
    IID_PPV_ARGS(m_windowQuad.TextureUploader.ReleaseAndGetAddressOf())));
m_windowQuad.TextureUploader->SetName(L"Window Texture Uploader");

D3D12_SHADER_RESOURCE_VIEW_DESC SrvDesc = { };
SrvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
SrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
SrvDesc.Texture2D.MostDetailedMip = 0;
SrvDesc.Texture2D.ResourceMinLODClamp = 0.0f;
SrvDesc.Format = m_windowQuad.Texture->GetDesc().Format;
SrvDesc.Texture2D.MipLevels = m_windowQuad.Texture->GetDesc().MipLevels;
SrvDesc.Texture2D.MostDetailedMip = 0;
SrvDesc.Texture2D.ResourceMinLODClamp = 0.0f;

m_CommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_windowQuad.Texture.Get(),
    D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_DEST));
UpdateSubresources(m_CommandList.Get(), m_windowQuad.Texture.Get(), m_windowQuad.TextureUploader.Get(), 0, 0, 1, &texResource);
m_CommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_windowQuad.Texture.Get(),
    D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_GENERIC_READ));

m_D3dDevice->CreateShaderResourceView(m_windowQuad.Texture.Get(), &SrvDesc, hDescriptor);
$\endgroup$
0
$\begingroup$

Resource Views are read-only in DirectX. Have you tried using an unordered-access view instead?

The CPU-side setup for these is similar to standard resource views (at least in DX11, I haven't used DX12), just with more detail and slightly different objects:

https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_unordered_access_view_desc

https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/nf-d3d12-id3d12device-createunorderedaccessview

On the GPU side:

I can't see your shader, but I assume you've declared something like

Texture2D<float4> tex : register(t0)

If so you'll need to change the binding for tex into a write-allowed unordered-access texture, which would be:

RWTexture2D<float4> tex : register(u0)

$\endgroup$

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.