Dolaylı çizim ve GPU itlafı
D3D12ExecuteIndirect örneği, içerik çizmek için dolaylı komutların nasıl kullanılacağını gösterir. Ayrıca, bu komutların verilmeden önce gpu üzerinde bir işlem gölgelendiricisinde nasıl işlenebileceğini gösterir.
- Dolaylı komutları tanımlama
- Grafik oluşturma ve işlem kök imzası
- İşlem gölgelendiricisi için gölgelendirici kaynak görünümü (SRV) oluşturma
- Dolaylı komut arabelleklerini oluşturma
- İşlem UAV'lerini oluşturma
- çerçeve çizmeyi
- Örnek çalıştırma
- İlgili konular
Örnek, 1024 çizim çağrılarını açıklayan bir komut arabelleği oluşturur. Her çizim çağrısı rastgele bir renk, konum ve hız ile bir üçgen oluşturur. Üçgenler ekranda sonsuz bir şekilde animasyon oluşturur. Bu örnekte iki mod vardır. İlk modda, işlem gölgelendiricisi dolaylı komutları inceler ve bu komutun hangi komutların yürütülmesi gerektiğini açıklayan sıralanmamış erişim görünümüne (UAV) eklenip eklenmeyeceğine karar verir. İkinci modda, tüm komutlar basitçe yürütülür. Ara çubuğuna basmak modlar arasında geçiş yapar.
Dolaylı komutları tanımlama
İlk olarak dolaylı komutların nasıl görünmesi gerektiğini tanımlıyoruz. Bu örnekte yürütmek istediğimiz komutlar şunlardır:
- 1. Sabit arabellek görünümünü (CBV) güncelleştirin.
2. Üçgeni çizin.
Bu çizim komutları, D3D12ExecuteIndirect sınıf tanımında aşağıdaki yapıyla temsil edilir. Komutlar, bu yapıda tanımlandığı sırayla sıralı olarak yürütülür.
// Data structure to match the command signature used for ExecuteIndirect.
struct IndirectCommand
{
D3D12_GPU_VIRTUAL_ADDRESS cbv;
D3D12_DRAW_ARGUMENTS drawArguments;
};
Çağrı akışı | Parametre |
---|---|
D3D12_GPU_VIRTUAL_ADDRESS (yalnızca UINT64) | |
D3D12_DRAW_ARGUMENTS |
Veri yapısına eşlik etmek için GPU'ya ExecuteIndirect API'sine geçirilen verilerin nasıl yorumlanduğunu gösteren bir komut imzası da oluşturulur. Bu ve aşağıdaki kodun çoğu LoadAssets yöntemine eklenir.
// Create the command signature used for indirect drawing.
{
// Each command consists of a CBV update and a DrawInstanced call.
D3D12_INDIRECT_ARGUMENT_DESC argumentDescs[2] = {};
argumentDescs[0].Type = D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT_BUFFER_VIEW;
argumentDescs[0].ConstantBufferView.RootParameterIndex = Cbv;
argumentDescs[1].Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW;
D3D12_COMMAND_SIGNATURE_DESC commandSignatureDesc = {};
commandSignatureDesc.pArgumentDescs = argumentDescs;
commandSignatureDesc.NumArgumentDescs = _countof(argumentDescs);
commandSignatureDesc.ByteStride = sizeof(IndirectCommand);
ThrowIfFailed(m_device->CreateCommandSignature(&commandSignatureDesc, m_rootSignature.Get(), IID_PPV_ARGS(&m_commandSignature)));
}
Çağrı akışı | Parametre |
---|---|
D3D12_INDIRECT_ARGUMENT_DESC | D3D12_INDIRECT_ARGUMENT_TYPE |
D3D12_COMMAND_SIGNATURE_DESC | |
CreateCommandSignature |
Grafik ve işlem kök imzası oluşturma
Ayrıca hem grafik hem de işlem kök imzası oluştururuz. Grafik kök imzası yalnızca bir kök CBV tanımlar. Komut imzası tanımlandığında bu kök parametrenin dizinini D3D12_INDIRECT_ARGUMENT_DESC (yukarıda gösterilmiştir) eşlediğimize dikkat edin. İşlem kök imzası aşağıdakileri tanımlar:
- Üç yuvalı ortak bir tanımlayıcı tablosu (iki SRV ve bir UAV):
- Bir SRV, sabit arabellekleri işlem gölgelendiricisine sunar
- Bir SRV, komut arabelleğinde işlem gölgelendiricisini kullanıma sunar
- UAV, işlem gölgelendiricisinin görünür üçgenler için komutları kaydettiği yerdir
- Dört kök sabit:
- Üçgenin bir tarafının genişliğinin yarısı
- Üçgen köşelerinin z konumu
- Homojen boşluktaki menteş düzlemin +/- x uzaklığı [-1,1]
- Komut arabelleğindeki dolaylı komutların sayısı
// Create the root signatures.
{
CD3DX12_ROOT_PARAMETER rootParameters[GraphicsRootParametersCount];
rootParameters[Cbv].InitAsConstantBufferView(0, 0, D3D12_SHADER_VISIBILITY_VERTEX);
CD3DX12_ROOT_SIGNATURE_DESC rootSignatureDesc;
rootSignatureDesc.Init(_countof(rootParameters), rootParameters, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
ComPtr<ID3DBlob> signature;
ComPtr<ID3DBlob> error;
ThrowIfFailed(D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error));
ThrowIfFailed(m_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&m_rootSignature)));
// Create compute signature.
CD3DX12_DESCRIPTOR_RANGE ranges[2];
ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 2, 0);
ranges[1].Init(D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 0);
CD3DX12_ROOT_PARAMETER computeRootParameters[ComputeRootParametersCount];
computeRootParameters[SrvUavTable].InitAsDescriptorTable(2, ranges);
computeRootParameters[RootConstants].InitAsConstants(4, 0);
CD3DX12_ROOT_SIGNATURE_DESC computeRootSignatureDesc;
computeRootSignatureDesc.Init(_countof(computeRootParameters), computeRootParameters);
ThrowIfFailed(D3D12SerializeRootSignature(&computeRootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error));
ThrowIfFailed(m_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&m_computeRootSignature)));
}
Çağrı akışı | Parametre |
---|---|
CD3DX12_ROOT_PARAMETER | D3D12_SHADER_VISIBILITY |
CD3DX12_ROOT_SIGNATURE_DESC | D3D12_ROOT_SIGNATURE_FLAGS |
ID3DBlob | |
D3D12SerializeRootSignature | D3D_ROOT_SIGNATURE_VERSION |
CreateRootSignature | |
CD3DX12_DESCRIPTOR_RANGE | D3D12_DESCRIPTOR_RANGE_TYPE |
CD3DX12_ROOT_PARAMETER | D3D12_SHADER_VISIBILITY |
CD3DX12_ROOT_SIGNATURE_DESC | D3D12_ROOT_SIGNATURE_FLAGS |
ID3DBlob | |
D3D12SerializeRootSignature | D3D_ROOT_SIGNATURE_VERSION |
CreateRootSignature |
İşlem gölgelendiricisi için gölgelendirici kaynak görünümü (SRV) oluşturma
İşlem hattı durumu nesneleri, köşe arabellekleri, derinlik kalıbı ve sabit arabellekler oluşturulduktan sonra örnek, işlem gölgelendiricisinin sabit arabellekteki verilere erişebilmesi için sabit arabelleğin gölgelendirici kaynak görünümünü (SRV) oluşturur.
// Create shader resource views (SRV) of the constant buffers for the
// compute shader to read from.
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
srvDesc.Format = DXGI_FORMAT_UNKNOWN;
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER;
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
srvDesc.Buffer.NumElements = TriangleCount;
srvDesc.Buffer.StructureByteStride = sizeof(ConstantBufferData);
srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_NONE;
CD3DX12_CPU_DESCRIPTOR_HANDLE cbvSrvHandle(m_cbvSrvUavHeap->GetCPUDescriptorHandleForHeapStart(), CbvSrvOffset, m_cbvSrvUavDescriptorSize);
for (UINT frame = 0; frame < FrameCount; frame++)
{
srvDesc.Buffer.FirstElement = frame * TriangleCount;
m_device->CreateShaderResourceView(m_constantBuffer.Get(), &srvDesc, cbvSrvHandle);
cbvSrvHandle.Offset(CbvSrvUavDescriptorCountPerFrame, m_cbvSrvUavDescriptorSize);
}
Çağrı akışı | Parametre |
---|---|
D3D12_SHADER_RESOURCE_VIEW_DESC | |
CD3DX12_CPU_DESCRIPTOR_HANDLE | GetCPUDescriptorHandleForHeapStart |
CreateShaderResourceView |
Dolaylı komut arabelleklerini oluşturma
Ardından dolaylı komut arabelleklerini oluşturur ve aşağıdaki kodu kullanarak içeriklerini tanımlarız. Aynı üçgen köşelerini 1024 kez çizeriz, ancak her çizim çağrısında farklı bir sabit arabellek konumuna işaret ederiz.
D3D12_GPU_VIRTUAL_ADDRESS gpuAddress = m_constantBuffer->GetGPUVirtualAddress();
UINT commandIndex = 0;
for (UINT frame = 0; frame < FrameCount; frame++)
{
for (UINT n = 0; n < TriangleCount; n++)
{
commands[commandIndex].cbv = gpuAddress;
commands[commandIndex].drawArguments.VertexCountPerInstance = 3;
commands[commandIndex].drawArguments.InstanceCount = 1;
commands[commandIndex].drawArguments.StartVertexLocation = 0;
commands[commandIndex].drawArguments.StartInstanceLocation = 0;
commandIndex++;
gpuAddress += sizeof(ConstantBufferData);
}
}
Çağrı akışı | Parametre |
---|---|
D3D12_GPU_VIRTUAL_ADDRESS | GetGPUVirtualAddress |
Komut arabelleklerini GPU'ya yükledikten sonra, işlem gölgelendiricisinin okunması için bunların bir SRV'sini de oluştururuz. Bu, sabit arabelleğin oluşturduğu SRV'ye çok benzer.
// Create SRVs for the command buffers.
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
srvDesc.Format = DXGI_FORMAT_UNKNOWN;
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER;
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
srvDesc.Buffer.NumElements = TriangleCount;
srvDesc.Buffer.StructureByteStride = sizeof(IndirectCommand);
srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_NONE;
CD3DX12_CPU_DESCRIPTOR_HANDLE commandsHandle(m_cbvSrvUavHeap->GetCPUDescriptorHandleForHeapStart(), CommandsOffset, m_cbvSrvUavDescriptorSize);
for (UINT frame = 0; frame < FrameCount; frame++)
{
srvDesc.Buffer.FirstElement = frame * TriangleCount;
m_device->CreateShaderResourceView(m_commandBuffer.Get(), &srvDesc, commandsHandle);
commandsHandle.Offset(CbvSrvUavDescriptorCountPerFrame, m_cbvSrvUavDescriptorSize);
}
Çağrı akışı | Parametre |
---|---|
D3D12_SHADER_RESOURCE_VIEW_DESC | |
CD3DX12_CPU_DESCRIPTOR_HANDLE | GetCPUDescriptorHandleForHeapStart |
CreateShaderResourceView |
İşlem UAV'lerini oluşturma
İşlem çalışmasının sonuçlarını depolayacak UAV'leri oluşturmamız gerekir. İşlem gölgelendiricisi tarafından bir üçgenin işleme hedefine görünür olduğu varsayıldığında, bu UAV'ye eklenir ve ardından ExecuteIndirect API'si tarafından kullanılır.
CD3DX12_CPU_DESCRIPTOR_HANDLE processedCommandsHandle(m_cbvSrvUavHeap->GetCPUDescriptorHandleForHeapStart(), ProcessedCommandsOffset, m_cbvSrvUavDescriptorSize);
for (UINT frame = 0; frame < FrameCount; frame++)
{
// Allocate a buffer large enough to hold all of the indirect commands
// for a single frame as well as a UAV counter.
commandBufferDesc = CD3DX12_RESOURCE_DESC::Buffer(CommandBufferSizePerFrame + sizeof(UINT), D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS);
CD3DX12_HEAP_PROPERTIES heapProps(D3D12_HEAP_TYPE_DEFAULT);
ThrowIfFailed(m_device->CreateCommittedResource(
&heapProps,
D3D12_HEAP_FLAG_NONE,
&commandBufferDesc,
D3D12_RESOURCE_STATE_COPY_DEST,
nullptr,
IID_PPV_ARGS(&m_processedCommandBuffers[frame])));
D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
uavDesc.Format = DXGI_FORMAT_UNKNOWN;
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
uavDesc.Buffer.FirstElement = 0;
uavDesc.Buffer.NumElements = TriangleCount;
uavDesc.Buffer.StructureByteStride = sizeof(IndirectCommand);
uavDesc.Buffer.CounterOffsetInBytes = CommandBufferSizePerFrame;
uavDesc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_NONE;
m_device->CreateUnorderedAccessView(
m_processedCommandBuffers[frame].Get(),
m_processedCommandBuffers[frame].Get(),
&uavDesc,
processedCommandsHandle);
processedCommandsHandle.Offset(CbvSrvUavDescriptorCountPerFrame, m_cbvSrvUavDescriptorSize);
}
Çağrı akışı | Parametre |
---|---|
CD3DX12_CPU_DESCRIPTOR_HANDLE | GetCPUDescriptorHandleForHeapStart |
CD3DX12_RESOURCE_DESC | D3D12_RESOURCE_FLAGS |
CreateCommittedResource | |
D3D12_UNORDERED_ACCESS_VIEW_DESC | |
CreateUnorderedAccessView |
Çerçeveyi çizme
Çerçeveyi çizme zamanı geldiğinde, işlem gölgelendiricisi çağrılırken ve dolaylı komutlar GPU tarafından işlenirken moddaysak, ilk olarak ExecuteIndirectiçin komut arabelleğimizi doldurmak için çalışan Dispatch. Aşağıdaki kod parçacıkları PopulateCommandLists yöntemine eklenir.
// Record the compute commands that will cull triangles and prevent them from being processed by the vertex shader.
if (m_enableCulling)
{
UINT frameDescriptorOffset = m_frameIndex * CbvSrvUavDescriptorCountPerFrame;
D3D12_GPU_DESCRIPTOR_HANDLE cbvSrvUavHandle = m_cbvSrvUavHeap->GetGPUDescriptorHandleForHeapStart();
m_computeCommandList->SetComputeRootSignature(m_computeRootSignature.Get());
ID3D12DescriptorHeap* ppHeaps[] = { m_cbvSrvUavHeap.Get() };
m_computeCommandList->SetDescriptorHeaps(_countof(ppHeaps), ppHeaps);
m_computeCommandList->SetComputeRootDescriptorTable(
SrvUavTable,
CD3DX12_GPU_DESCRIPTOR_HANDLE(cbvSrvUavHandle, CbvSrvOffset + frameDescriptorOffset, m_cbvSrvUavDescriptorSize));
m_computeCommandList->SetComputeRoot32BitConstants(RootConstants, 4, reinterpret_cast<void*>(&m_csRootConstants), 0);
// Reset the UAV counter for this frame.
m_computeCommandList->CopyBufferRegion(m_processedCommandBuffers[m_frameIndex].Get(), CommandBufferSizePerFrame, m_processedCommandBufferCounterReset.Get(), 0, sizeof(UINT));
D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_processedCommandBuffers[m_frameIndex].Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
m_computeCommandList->ResourceBarrier(1, &barrier);
m_computeCommandList->Dispatch(static_cast<UINT>(ceil(TriangleCount / float(ComputeThreadBlockSize))), 1, 1);
}
ThrowIfFailed(m_computeCommandList->Close());
Çağrı akışı | Parametre |
---|---|
D3D12_GPU_DESCRIPTOR_HANDLE | GetGPUDescriptorHandleForHeapStart |
SetComputeRootSignature | |
id3D12DescriptorHeap | |
SetDescriptorHeaps | |
SetComputeRootDescriptorTable | CD3DX12_GPU_DESCRIPTOR_HANDLE |
SetComputeRoot32BitConstants | |
CopyBufferRegion | |
D3D12_RESOURCE_BARRIER | |
ResourceBarrier | |
Dağıtma | |
Kapat'ı |
Ardından, komutları UAV(GPU bağlantı noktası etkin) veya tam komut arabelleğinde (GPU itlafı devre dışı) yürüteceğiz.
// Record the rendering commands.
{
// Set necessary state.
m_commandList->SetGraphicsRootSignature(m_rootSignature.Get());
ID3D12DescriptorHeap* ppHeaps[] = { m_cbvSrvUavHeap.Get() };
m_commandList->SetDescriptorHeaps(_countof(ppHeaps), ppHeaps);
m_commandList->RSSetViewports(1, &m_viewport);
m_commandList->RSSetScissorRects(1, m_enableCulling ? &m_cullingScissorRect : &m_scissorRect);
// Indicate that the command buffer will be used for indirect drawing
// and that the back buffer will be used as a render target.
D3D12_RESOURCE_BARRIER barriers[2] = {
CD3DX12_RESOURCE_BARRIER::Transition(
m_enableCulling ? m_processedCommandBuffers[m_frameIndex].Get() : m_commandBuffer.Get(),
m_enableCulling ? D3D12_RESOURCE_STATE_UNORDERED_ACCESS : D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT),
CD3DX12_RESOURCE_BARRIER::Transition(
m_renderTargets[m_frameIndex].Get(),
D3D12_RESOURCE_STATE_PRESENT,
D3D12_RESOURCE_STATE_RENDER_TARGET)
};
m_commandList->ResourceBarrier(_countof(barriers), barriers);
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(m_rtvHeap->GetCPUDescriptorHandleForHeapStart(), m_frameIndex, m_rtvDescriptorSize);
CD3DX12_CPU_DESCRIPTOR_HANDLE dsvHandle(m_dsvHeap->GetCPUDescriptorHandleForHeapStart());
m_commandList->OMSetRenderTargets(1, &rtvHandle, FALSE, &dsvHandle);
// Record commands.
const float clearColor[] = { 0.0f, 0.2f, 0.4f, 1.0f };
m_commandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr);
m_commandList->ClearDepthStencilView(dsvHandle, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr);
m_commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
m_commandList->IASetVertexBuffers(0, 1, &m_vertexBufferView);
if (m_enableCulling)
{
// Draw the triangles that have not been culled.
m_commandList->ExecuteIndirect(
m_commandSignature.Get(),
TriangleCount,
m_processedCommandBuffers[m_frameIndex].Get(),
0,
m_processedCommandBuffers[m_frameIndex].Get(),
CommandBufferSizePerFrame);
}
else
{
// Draw all of the triangles.
m_commandList->ExecuteIndirect(
m_commandSignature.Get(),
TriangleCount,
m_commandBuffer.Get(),
CommandBufferSizePerFrame * m_frameIndex,
nullptr,
0);
}
// Indicate that the command buffer may be used by the compute shader
// and that the back buffer will now be used to present.
barriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT;
barriers[0].Transition.StateAfter = m_enableCulling ? D3D12_RESOURCE_STATE_COPY_DEST : D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
barriers[1].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
barriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
m_commandList->ResourceBarrier(_countof(barriers), barriers);
ThrowIfFailed(m_commandList->Close());
}
Çağrı akışı | Parametre |
---|---|
SetGraphicsRootSignature | |
id3D12DescriptorHeap | |
SetDescriptorHeaps | |
RSSetViewports | |
RSSetScissorRects | |
D3D12_RESOURCE_BARRIER | |
ResourceBarrier | |
CD3DX12_CPU_DESCRIPTOR_HANDLE | GetCPUDescriptorHandleForHeapStart |
OMSetRenderTargets | |
ClearRenderTargetView | |
ClearDepthStencilView | D3D12_CLEAR_FLAGS |
IASetPrimitiveTopology | D3D_PRIMITIVE_TOPOLOGY |
IASetVertexBuffers | |
ExecuteIndirect | |
ResourceBarrier | D3D12_RESOURCE_STATES |
Kapat'ı |
GPU hesaplama modundaysak, grafik komut kuyruğunun dolaylı komutları yürütmeye başlamadan önce işlem çalışmasının tamamlanmasını beklemesini sağlarız. OnRender yöntemine aşağıdaki kod parçacığı eklenir.
// Execute the compute work.
if (m_enableCulling)
{
ID3D12CommandList* ppCommandLists[] = { m_computeCommandList.Get() };
m_computeCommandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);
m_computeCommandQueue->Signal(m_computeFence.Get(), m_fenceValues[m_frameIndex]);
// Execute the rendering work only when the compute work is complete.
m_commandQueue->Wait(m_computeFence.Get(), m_fenceValues[m_frameIndex]);
}
// Execute the rendering work.
ID3D12CommandList* ppCommandLists[] = { m_commandList.Get() };
m_commandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);
Çağrı akışı | Parametre |
---|---|
ID3D12CommandList | |
ExecuteCommandLists | |
Sinyal | |
Bekle | |
ID3D12CommandList | |
ExecuteCommandLists |
Örneği çalıştırma
GPU temel öğeli örnek.
gpuekran görüntüsü
GPU temel öğeli hesaplaması olmayan örnek.
gpuekran görüntüsü
İlgili konular