Använda resursbarriärer för att synkronisera resurstillstånd i Direct3D 12
För att minska den totala cpu-användningen och aktivera drivrutinshantering med flera trådar och förbearbetning flyttar Direct3D 12 ansvaret för tillståndshantering per resurs från grafikdrivrutinen till programmet. Ett exempel på tillståndet per resurs är om en texturresurs för närvarande används så som via en skuggningsresursvy eller som en återgivningsmålvy. I Direct3D 11 krävdes drivrutiner för att spåra det här tillståndet i bakgrunden. Detta är dyrt ur ett CPU-perspektiv och komplicerar avsevärt alla typer av design med flera trådar. I Microsoft Direct3D 12 hanteras det mesta tillståndet per resurs av programmet med ett enda API, ID3D12GraphicsCommandList::ResourceBarrier.
- Använda ResourceBarrier-API:et för att hantera tillståndet per resurs
- implicita tillståndsövergångar
- Uppdelade Barriärer
- Exempelscenario för resursbarriär
- Exempel på vanlig statusförbättring och nedbrytning
- Exempel på delade barriärer
- Relaterade ämnen
Använda ResourceBarrier-API:et för att hantera tillståndet per resurs
ResourceBarrier meddelar grafikdrivrutinen om situationer där drivrutinen kan behöva synkronisera flera åtkomster till minnet där en resurs lagras. Metoden anropas med en eller flera beskrivningsstrukturer för resursbarriärer som anger vilken typ av resursbarriär som deklareras.
Det finns tre typer av resursbarriärer:
Övergångsbarriär – En övergångsbarriär anger att en uppsättning underresurser övergår mellan olika användningar. En D3D12_RESOURCE_TRANSITION_BARRIER struktur används för att ange den underresurs som övergår samt före och efter tillstånd för underresursen.
Systemet verifierar att underresursövergångar i en kommandolista överensstämmer med tidigare övergångar i samma kommandolista. Felsökningsskiktet spårar ytterligare subresource-tillstånd för att hitta andra fel, men den här valideringen är konservativ och inte uttömmande.
Observera att du kan använda flaggan D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES för att ange att alla underresurser i en resurs övergår.
Aliasbarriär – En aliasbarriär anger en övergång mellan användningar av två olika resurser som har överlappande mappningar till samma heap. Detta gäller både reserverade och placerade resurser. En D3D12_RESOURCE_ALIASING_BARRIER struktur används för att ange både före resurs och efter resurs.
Observera att en eller båda resurserna kan vara NULL, vilket indikerar att alla tillagda resurser kan orsaka alias. Mer information om hur du använder tillagda resurser finns i Tiled resources and Volume Tiled Resources.
UAV-barriär (Unordered Access View) – En UAV-barriär anger att alla UAV-åtkomster, både läs- och skrivåtkomster, till en viss resurs måste slutföras innan några framtida UAV-åtkomster, både läs- och skrivåtkomster. Det är inte nödvändigt för en app att placera en UAV-barriär mellan två anrop eller sändningsanrop som bara läses från en UAV. Dessutom är det inte nödvändigt att placera en UAV-barriär mellan två anrop eller sändningsanrop som skriver till samma UAV om programmet vet att det är säkert att köra UAV-åtkomsten i någon ordning. En D3D12_RESOURCE_UAV_BARRIER struktur används för att ange den UAV-resurs som barriären gäller för. Programmet kan ange NULL för barriärens UAV, vilket indikerar att all UAV-åtkomst kan kräva barriären.
När ResourceBarrier- anropas med en matris med beskrivningar av resursbarriärer fungerar API:et som om det anropades en gång för varje element, i den ordning de angavs.
Vid en viss tidpunkt är en underresurs i exakt ett tillstånd, som bestäms av uppsättningen med D3D12_RESOURCE_STATES flaggor som levereras till ResourceBarrier. Programmet måste se till att tillstånden före och efter av efterföljande anrop till ResourceBarrier överensstämmer.
Tips
Program bör batcha flera övergångar till ett API-anrop där det är möjligt.
Resurstillstånd
För en komplett lista över resurstillstånd som en resurs kan övergå mellan, se referensavsnittet för D3D12_RESOURCE_STATES enumerationen.
För delade resursbarriärer, se även D3D12_RESOURCE_BARRIER_FLAGS.
Ursprungliga tillstånd för resurser
Resurser kan skapas med alla användardefinierade inledande tillstånd (giltiga för resursbeskrivningen), med följande undantag:
- Uppladdningshögar måste börja i tillståndet D3D12_RESOURCE_STATE_GENERIC_READ som är en bitvis eller kombination av:
- D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER
- D3D12_RESOURCE_STATE_INDEX_BUFFER
- D3D12_RESOURCE_STATE_COPY_SOURCE
- D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE
- D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE
- D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT
- Återläsningshögar måste börja i tillståndet D3D12_RESOURCE_STATE_COPY_DEST.
- Swap-kedjans bakbuffertar börjar automatiskt i tillståndet D3D12_RESOURCE_STATE_COMMON.
Innan en heap kan vara målet för en GPU-kopieringsåtgärd måste heapen först överföras till D3D12_RESOURCE_STATE_COPY_DEST-tillstånd. Resurser som skapas på UPLOAD-heaps måste dock starta i tillståndet GENERIC_READ och kan inte ändras från detta, eftersom endast processorn kommer att skriva. Däremot måste tilldelade resurser som skapats i READBACK-heaps börja i COPY_DEST-tillståndet och får inte ändras därifrån.
Begränsningar för läs-/skrivresursstatus
Användningsbitarna för resurstillstånd som används för att beskriva ett resurstillstånd är indelade i skrivskyddade och läs/skriv-tillstånd. Referensavsnittet för D3D12_RESOURCE_STATES anger läs-/skrivåtkomstnivån för varje bit i uppräkningen.
Som mest kan endast en läs-/skrivbit anges för valfri resurs. Om en skrivbit har angetts kan ingen skrivskyddad bit anges för den resursen. Om ingen skrivbit har angetts kan valfritt antal läsbitar anges.
Resurstillägen för att presentera bakåtbuffertar
Innan en bakbuffer visas måste den vara i tillståndet D3D12_RESOURCE_STATE_COMMON. Observera att resurstillståndet D3D12_RESOURCE_STATE_PRESENT är synonymt med D3D12_RESOURCE_STATE_COMMON och båda har värdet 0. Om IDXGISwapChain::Present (eller IDXGISwapChain1::Present1) anropas på en resurs som för närvarande inte är i det här tillståndet, genereras en varning om felsökningslager.
Ta bort resurser
Alla underresurser i en resurs måste vara i RENDER_TARGET-läge eller DEPTH_WRITE-läge för återgivningsmål/djup-stencilresurser när ID3D12GraphicsCommandList::DiscardResource kallas.
Implicita tillståndsövergångar
Resurser kan bara "höjas upp" från D3D12_RESOURCE_STATE_COMMON. På samma sätt kommer resurser bara att "förfalla" till D3D12_RESOURCE_STATE_COMMON.
Allmän statushöjning
Alla buffertresurser samt texturer med D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS-flaggan uppgraderas implicit från D3D12_RESOURCE_STATE_COMMON till relevant tillstånd vid första GPU-åtkomsten, inklusive GENERIC_READ för att omfatta alla lässcenarier. Alla resurser i COMMON-tillståndet kan nås som om de vore i ett enda tillstånd med
- 1 SKRIV-flagga eller 1 eller fler LÄS-flaggor
Resurser kan höjas upp från COMMON-tillståndet baserat på följande tabell:
Delstatsflagga | Buffertar och Simultaneous-Access texturer | Icke-Simultaneous-Access-texturer |
---|---|---|
VERTEX_OCH_KONSTANT_BUFFER | Ja | Nej |
indexbuffert | Ja | Nej |
RENDER_MÅL | Ja | Nej |
OORDNAD_ÅTKOMST | Ja | Nej |
DEPTH_WRITE | Ingen* | Nej |
DEPTH_READ | Ingen* | Nej |
RESURS_IKKE_PIXEL_SKUGGNING | Ja | Ja |
PIXEL_SHADER_RESOURCE (pixelskuggningsresurs) | Ja | Ja |
STREAM_OUT | Ja | Nej |
INDIREKT_ARGUMENT | Ja | Nej |
KOPIERA_DESTINATION | Ja | Ja |
COPY_SOURCE | Ja | Ja |
RESOLVE_DEST | Ja | Nej |
ÅTGÄRDA_KÄLLA | Ja | Nej |
PREDIKAT | Ja | Nej |
*Resurser för djupstenciler måste vara icke-samtidiga åtkomststrukturer och kan därför aldrig implicit höjas upp.
När den här åtkomsten sker fungerar befordran som en implicit resursbarriär. För efterföljande åtkomster krävs resursbarriärer för att ändra resurstillståndet om det behövs. Observera att befordran från ett upphöjt lästillstånd till flera lästillstånd är giltigt, men så är inte fallet för skrivtillstånd.
Om till exempel en resurs i det allmänna tillståndet befordras till PIXEL_SHADER_RESOURCE i ett anrop kan den ändå uppgraderas till NON_PIXEL_SHADER_RESOURCE | PIXEL_SHADER_RESOURCE i ett annat anrop. Om den emellertid används i en skrivåtgärd, såsom ett kopieringsmål, krävs en övergångsbarriär för resurstillstånd från de kombinerade upphöjda lästillstånden, här NON_PIXEL_SHADER_RESOURCE | PIXEL_SHADER_RESOURCE, till COPY_DEST.
På samma sätt, om det överförs från COMMON till COPY_DEST, krävs det fortfarande en barriär för att övergå från COPY_DEST till RENDER_TARGET.
Observera att den vanliga tillståndshöjningen är "kostnadsfri" eftersom GPU:n inte behöver utföra några synkroniseringsväntningar. Befordran representerar det faktum att resurser i COMMON-tillståndet inte bör kräva ytterligare GPU-arbete eller drivrutinsspårning för att stödja vissa åtkomster.
Tillståndsförfall till vanligt
Nackdelen med uppflyttning till common state är återgång till D3D12_RESOURCE_STATE_COMMON. Resurser som uppfyller vissa krav anses vara tillståndslösa och återgår effektivt till det gemensamma tillståndet när GPU:n har slutfört körningen av en ExecuteCommandLists- åtgärd. Förfall sker inte mellan kommandolistor som körs tillsammans i samma ExecuteCommandLists-anrop.
Följande resurser kommer att minska i värde när en ExecuteCommandLists åtgärd har slutförts på GPU:n:
- Resurser som används i en kopieringskö, eller
- Buffertresurser på valfri kötyp, eller
- Texturresurser på vilken kötyp som helst som har flaggan D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS inställd, eller
- Alla resurser som implicit befordras till ett skrivskyddat tillstånd.
Precis som en vanlig statlig befordran är förfallet kostnadsfritt eftersom ingen ytterligare synkronisering behövs. Genom att kombinera 'common state promotion' och 'decay' kan man eliminera många onödiga ResourceBarrier övergångar. I vissa fall kan detta ge betydande prestandaförbättringar.
Buffertar och Simultaneous-Access resurser kommer att förfalla till det gemensamma tillståndet oavsett om de uttryckligen övergick med hjälp av resursbarriärer eller implicit främjades.
Prestandakonsekvenser
När du registrerar explicita ResourceBarrier övergångar av resurser i det gemensamma tillståndet är det korrekt att använda antingen D3D12_RESOURCE_STATE_COMMON eller något främjande tillstånd som värdet BeforeState i D3D12_RESOURCE_TRANSITION_BARRIER-strukturen. Detta möjliggör traditionell tillståndshantering som ignorerar automatiskt sönderfall av buffertar och texturer med samtidig åtkomst. Det kan dock vara oönskat, eftersom undvikande av övergångsanrop ResourceBarrier med resurser som är kända för att vara i det gemensamma tillståndet kan avsevärt förbättra prestandan. Resursbarriärer kan vara dyra. De är utformade för att framtvinga cachespolningar, ändringar i minneslayouten och annan synkronisering som kanske inte är nödvändig för resurser som redan är i det gemensamma tillståndet. En kommandolista som använder en resursbarriär från ett icke-vanligt tillstånd till ett annat icke-vanligt tillstånd på en resurs som för närvarande är i det gemensamma tillståndet kan medföra mycket onödiga omkostnader.
Undvik också att explicit använda ResourceBarrier övergångar till D3D12_RESOURCE_STATE_COMMON om det inte är absolut nödvändigt (t.ex. om nästa åtkomst sker i en COPY-kommandokö som kräver att resurserna startar i det gemensamma tillståndet). Överdrivna övergångar till det gemensamma tillståndet kan avsevärt göra GPU-prestanda långsammare.
Sammanfattningsvis kan du försöka förlita dig på common state promotion och decay när dess semantik låter dig komma undan utan att utfärda ResourceBarrier-anrop.
Delade barriärer
En resursövergångsbarriär med flaggan D3D12_RESOURCE_BARRIER_FLAG_BEGIN_ONLY påbörjar en delad barriär och övergångsbarriären sägs vara väntande. Så länge barriären är i väntan kan (under)resursen inte läsas eller skrivas av GPU:n. Den enda lagliga övergångsbarriär som kan tillämpas på en (under)resurs med en väntande barriär är en med samma före och efter tillstånd samt flaggan D3D12_RESOURCE_BARRIER_FLAG_END_ONLY, vilket gör att barriären slutför den väntande övergången.
Delade hinder ger tips till GPU:n om att en resurs i tillstånd A kommer att användas i tillståndet B någon gång senare. Detta ger GPU:n möjlighet att optimera arbetsbelastningen vid övergång, vilket kan minska eller eliminera stopp i körningen. Genom att utfärda slutbarriären garanteras att GPU-övergången har slutförts innan nästa kommando utförs.
Genom att använda uppdelade barriärer kan du förbättra prestandan, särskilt i multimotor-scenarier eller där resurser övergår mellan läs- och skrivlägen sparsamt i en eller flera kommandolistor.
Exempelscenario för resursbarriär
Följande kodfragment visar användningen av metoden ResourceBarrier i ett exempel med flera trådar.
Skapa djupstencilvyn och övergå till ett skrivbart tillstånd.
// Create the depth stencil.
{
CD3DX12_RESOURCE_DESC shadowTextureDesc(
D3D12_RESOURCE_DIMENSION_TEXTURE2D,
0,
static_cast<UINT>(m_viewport.Width),
static_cast<UINT>(m_viewport.Height),
1,
1,
DXGI_FORMAT_D32_FLOAT,
1,
0,
D3D12_TEXTURE_LAYOUT_UNKNOWN,
D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL | D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE);
D3D12_CLEAR_VALUE clearValue; // Performance tip: Tell the runtime at resource creation the desired clear value.
clearValue.Format = DXGI_FORMAT_D32_FLOAT;
clearValue.DepthStencil.Depth = 1.0f;
clearValue.DepthStencil.Stencil = 0;
ThrowIfFailed(m_device->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
D3D12_HEAP_FLAG_NONE,
&shadowTextureDesc,
D3D12_RESOURCE_STATE_DEPTH_WRITE,
&clearValue,
IID_PPV_ARGS(&m_depthStencil)));
// Create the depth stencil view.
m_device->CreateDepthStencilView(m_depthStencil.Get(), nullptr, m_dsvHeap->GetCPUDescriptorHandleForHeapStart());
}
Skapa hörnbuffertvyn, ändra den först från ett gemensamt tillstånd till ett mål och sedan från ett mål till ett allmänt läsbart tillstånd.
// Create the vertex buffer.
{
ThrowIfFailed(m_device->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
D3D12_HEAP_FLAG_NONE,
&CD3DX12_RESOURCE_DESC::Buffer(SampleAssets::VertexDataSize),
D3D12_RESOURCE_STATE_COPY_DEST,
nullptr,
IID_PPV_ARGS(&m_vertexBuffer)));
{
ThrowIfFailed(m_device->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
D3D12_HEAP_FLAG_NONE,
&CD3DX12_RESOURCE_DESC::Buffer(SampleAssets::VertexDataSize),
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&m_vertexBufferUpload)));
// Copy data to the upload heap and then schedule a copy
// from the upload heap to the vertex buffer.
D3D12_SUBRESOURCE_DATA vertexData = {};
vertexData.pData = pAssetData + SampleAssets::VertexDataOffset;
vertexData.RowPitch = SampleAssets::VertexDataSize;
vertexData.SlicePitch = vertexData.RowPitch;
PIXBeginEvent(commandList.Get(), 0, L"Copy vertex buffer data to default resource...");
UpdateSubresources<1>(commandList.Get(), m_vertexBuffer.Get(), m_vertexBufferUpload.Get(), 0, 0, 1, &vertexData);
commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_vertexBuffer.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER));
PIXEndEvent(commandList.Get());
}
Skapa indexbuffertvyn, ändra den först från ett vanligt tillstånd till ett mål och sedan från ett mål till ett allmänt läsbart tillstånd.
// Create the index buffer.
{
ThrowIfFailed(m_device->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
D3D12_HEAP_FLAG_NONE,
&CD3DX12_RESOURCE_DESC::Buffer(SampleAssets::IndexDataSize),
D3D12_RESOURCE_STATE_COPY_DEST,
nullptr,
IID_PPV_ARGS(&m_indexBuffer)));
{
ThrowIfFailed(m_device->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
D3D12_HEAP_FLAG_NONE,
&CD3DX12_RESOURCE_DESC::Buffer(SampleAssets::IndexDataSize),
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&m_indexBufferUpload)));
// Copy data to the upload heap and then schedule a copy
// from the upload heap to the index buffer.
D3D12_SUBRESOURCE_DATA indexData = {};
indexData.pData = pAssetData + SampleAssets::IndexDataOffset;
indexData.RowPitch = SampleAssets::IndexDataSize;
indexData.SlicePitch = indexData.RowPitch;
PIXBeginEvent(commandList.Get(), 0, L"Copy index buffer data to default resource...");
UpdateSubresources<1>(commandList.Get(), m_indexBuffer.Get(), m_indexBufferUpload.Get(), 0, 0, 1, &indexData);
commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_indexBuffer.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_INDEX_BUFFER));
PIXEndEvent(commandList.Get());
}
// Initialize the index buffer view.
m_indexBufferView.BufferLocation = m_indexBuffer->GetGPUVirtualAddress();
m_indexBufferView.SizeInBytes = SampleAssets::IndexDataSize;
m_indexBufferView.Format = SampleAssets::StandardIndexFormat;
}
Skapa texturer och skuggningsresursvyer. Strukturen ändras från ett vanligt tillstånd till ett mål och sedan från ett mål till en pixelskuggningsresurs.
// Create each texture and SRV descriptor.
const UINT srvCount = _countof(SampleAssets::Textures);
PIXBeginEvent(commandList.Get(), 0, L"Copy diffuse and normal texture data to default resources...");
for (int i = 0; i < srvCount; i++)
{
// Describe and create a Texture2D.
const SampleAssets::TextureResource &tex = SampleAssets::Textures[i];
CD3DX12_RESOURCE_DESC texDesc(
D3D12_RESOURCE_DIMENSION_TEXTURE2D,
0,
tex.Width,
tex.Height,
1,
static_cast<UINT16>(tex.MipLevels),
tex.Format,
1,
0,
D3D12_TEXTURE_LAYOUT_UNKNOWN,
D3D12_RESOURCE_FLAG_NONE);
ThrowIfFailed(m_device->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
D3D12_HEAP_FLAG_NONE,
&texDesc,
D3D12_RESOURCE_STATE_COPY_DEST,
nullptr,
IID_PPV_ARGS(&m_textures[i])));
{
const UINT subresourceCount = texDesc.DepthOrArraySize * texDesc.MipLevels;
UINT64 uploadBufferSize = GetRequiredIntermediateSize(m_textures[i].Get(), 0, subresourceCount);
ThrowIfFailed(m_device->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
D3D12_HEAP_FLAG_NONE,
&CD3DX12_RESOURCE_DESC::Buffer(uploadBufferSize),
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&m_textureUploads[i])));
// Copy data to the intermediate upload heap and then schedule a copy
// from the upload heap to the Texture2D.
D3D12_SUBRESOURCE_DATA textureData = {};
textureData.pData = pAssetData + tex.Data->Offset;
textureData.RowPitch = tex.Data->Pitch;
textureData.SlicePitch = tex.Data->Size;
UpdateSubresources(commandList.Get(), m_textures[i].Get(), m_textureUploads[i].Get(), 0, 0, subresourceCount, &textureData);
commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_textures[i].Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE));
}
// Describe and create an SRV.
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
srvDesc.Format = tex.Format;
srvDesc.Texture2D.MipLevels = tex.MipLevels;
srvDesc.Texture2D.MostDetailedMip = 0;
srvDesc.Texture2D.ResourceMinLODClamp = 0.0f;
m_device->CreateShaderResourceView(m_textures[i].Get(), &srvDesc, cbvSrvHandle);
// Move to the next descriptor slot.
cbvSrvHandle.Offset(cbvSrvDescriptorSize);
}
Påbörjar en ram; Detta använder inte bara ResourceBarrier- för att indikera att backbuffer ska användas som återgivningsmål, utan initierar även ramresursen (som anropar ResourceBarrier på djupstencilbufferten).
// Assemble the CommandListPre command list.
void D3D12Multithreading::BeginFrame()
{
m_pCurrentFrameResource->Init();
// Indicate that the back buffer will be used as a render target.
m_pCurrentFrameResource->m_commandLists[CommandListPre]->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_frameIndex].Get(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET));
// Clear the render target and depth stencil.
const float clearColor[] = { 0.0f, 0.0f, 0.0f, 1.0f };
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(m_rtvHeap->GetCPUDescriptorHandleForHeapStart(), m_frameIndex, m_rtvDescriptorSize);
m_pCurrentFrameResource->m_commandLists[CommandListPre]->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr);
m_pCurrentFrameResource->m_commandLists[CommandListPre]->ClearDepthStencilView(m_dsvHeap->GetCPUDescriptorHandleForHeapStart(), D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr);
ThrowIfFailed(m_pCurrentFrameResource->m_commandLists[CommandListPre]->Close());
}
// Assemble the CommandListMid command list.
void D3D12Multithreading::MidFrame()
{
// Transition our shadow map from the shadow pass to readable in the scene pass.
m_pCurrentFrameResource->SwapBarriers();
ThrowIfFailed(m_pCurrentFrameResource->m_commandLists[CommandListMid]->Close());
}
Avslutar en ram, vilket indikerar att den bakre bufferten nu används för att visas.
// Assemble the CommandListPost command list.
void D3D12Multithreading::EndFrame()
{
m_pCurrentFrameResource->Finish();
// Indicate that the back buffer will now be used to present.
m_pCurrentFrameResource->m_commandLists[CommandListPost]->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_frameIndex].Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT));
ThrowIfFailed(m_pCurrentFrameResource->m_commandLists[CommandListPost]->Close());
}
När en ramresurs initieras, vilket sker vid början av en ram, övergår djupstencillagret till att bli skrivbart.
void FrameResource::Init()
{
// Reset the command allocators and lists for the main thread.
for (int i = 0; i < CommandListCount; i++)
{
ThrowIfFailed(m_commandAllocators[i]->Reset());
ThrowIfFailed(m_commandLists[i]->Reset(m_commandAllocators[i].Get(), m_pipelineState.Get()));
}
// Clear the depth stencil buffer in preparation for rendering the shadow map.
m_commandLists[CommandListPre]->ClearDepthStencilView(m_shadowDepthView, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr);
// Reset the worker command allocators and lists.
for (int i = 0; i < NumContexts; i++)
{
ThrowIfFailed(m_shadowCommandAllocators[i]->Reset());
ThrowIfFailed(m_shadowCommandLists[i]->Reset(m_shadowCommandAllocators[i].Get(), m_pipelineStateShadowMap.Get()));
ThrowIfFailed(m_sceneCommandAllocators[i]->Reset());
ThrowIfFailed(m_sceneCommandLists[i]->Reset(m_sceneCommandAllocators[i].Get(), m_pipelineState.Get()));
}
}
Barriärer byts mellan ramar, vilket övergår skuggkartan från skrivbar till läsbar.
void FrameResource::SwapBarriers()
{
// Transition the shadow map from writeable to readable.
m_commandLists[CommandListMid]->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_shadowTexture.Get(), D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE));
}
Avslutsfunktionen anropas när en ram avslutas och skuggkartan övergår till ett vanligt tillstånd.
void FrameResource::Finish()
{
m_commandLists[CommandListPost]->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_shadowTexture.Get(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_DEPTH_WRITE));
}
Exempel på vanlig delstatsbefordran och nedbrytning
// Create a buffer resource using D3D12_RESOURCE_STATE_COMMON as the init state
ID3D12Resource *pResource;
CreateCommittedVertexBufferInCommonState(1024, &pResource);
// Copy data to the buffer without the need for a barrier.
// Promotes pResource state to D3D12_RESOURCE_STATE_COPY_DEST.
pCommandList->CopyBufferRegion(pResource, 0, pOtherResource, 0, 1024);
// To use pResource as a vertex buffer a transition barrier is needed.
// Note the StateBefore is D3D12_RESOURCE_STATE_COPY_DEST.
D3D12_RESOURCE_BARRIER BarrierDesc = {};
BarrierDesc.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
BarrierDesc.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
BarrierDesc.Transition.pResource = pResource;
BarrierDesc.Transition.Subresource = 0;
BarrierDesc.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
BarrierDesc.Transition.StateAfter = D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER;
pCommandList->ResourceBarrier( 1, &BarrierDesc );
// Use pResource as a vertex buffer
D3D12_VERTEX_BUFFER_VIEW vbView;
vbView.BufferLocation = pResource->GetGPUVirtualAddress();
vbView.SizeInBytes = 1024;
vbView.StrideInBytes = sizeof(MyVertex);
pCommandList->IASetVertexBuffers(0, 1, &vbView);
pCommandList->Draw();
pCommandQueue->ExecuteCommandLists(1, &pCommandList);
pCommandList->Reset(pAllocator, pPipelineState);
// Since the reset command list will be executed in a separate call to
// ExecuteCommandLists, the previous state of pResource
// will have decayed to D3D12_RESOURCE_STATE_COMMON so, again, no barrier is needed
pCommandList->CopyBufferRegion(pResource, 0, pDifferentResource, 0, 1024);
FinishRecordingCommandList(pCommandList);
pCommandQueue->ExecuteCommandLists(1, &pCommandList);
WaitForQueue(pCommandQueue);
// The previous ExecuteCommandLists call has finished so
// pResource has decayed to D3D12_RESOURCE_STATE_COMMON
Exempel på delade barriärer
I följande exempel visas hur du använder en splittrad barriär för att minska pipelinefördröjningar. Koden som följer använder inte delade hinder:
D3D12_RESOURCE_BARRIER BarrierDesc = {};
BarrierDesc.Type = D3D12_RESOURCE_BARRIER_TRANSITION;
BarrierDesc.Flags = D3D12_RESOURCE_BARRIER_NONE;
BarrierDesc.Transition.pResource = pResource;
BarrierDesc.Transition.Subresource = 0;
BarrierDesc.Transition.StateBefore = D3D12_RESOURCE_STATE_COMMON;
BarrierDesc.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
pCommandList->ResourceBarrier( 1, &BarrierDesc );
Write(pResource); // ... render to pResource
OtherStuff(); // .. other gpu work
// Transition pResource to PIXEL_SHADER_RESOURCE
BarrierDesc.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
BarrierDesc.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
pCommandList->ResourceBarrier( 1, &BarrierDesc );
Read(pResource); // ... read from pResource
Följande kod använder delade hinder:
D3D12_RESOURCE_BARRIER BarrierDesc = {};
BarrierDesc.Type = D3D12_RESOURCE_BARRIER_TRANSITION;
BarrierDesc.Flags = D3D12_RESOURCE_BARRIER_NONE;
BarrierDesc.Transition.pResource = pResource;
BarrierDesc.Transition.Subresource = 0;
BarrierDesc.Transition.StateBefore = D3D12_RESOURCE_STATE_COMMON;
BarrierDesc.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
pCommandList->ResourceBarrier( 1, &BarrierDesc );
Write(pResource); // ... render to pResource
// Done writing to pResource. Start barrier to PIXEL_SHADER_RESOURCE and
// then do other work
BarrierDesc.Flags = D3D12_RESOURCE_BARRIER_BEGIN_ONLY;
BarrierDesc.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
BarrierDesc.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
pCommandList->ResourceBarrier( 1, &BarrierDesc );
OtherStuff(); // .. other gpu work
// Need to read from pResource so end barrier
BarrierDesc.Flags = D3D12_RESOURCE_BARRIER_END_ONLY;
pCommandList->ResourceBarrier( 1, &BarrierDesc );
Read(pResource); // ... read from pResource
Relaterade ämnen
Avancerade videohandledningar i DirectX: Resursbarriärer och tillståndsspårning
synkronisering av flera motorer