Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Ukazuje, jak pomocí jedné vyrovnávací paměti nahrát data jak z konstantní, tak z vrcholové vyrovnávací paměti do GPU, a jak správně rozdělit a umístit data uvnitř vyrovnávacích pamětí. Použití jedné vyrovnávací paměti zvyšuje flexibilitu využití paměti a poskytuje aplikacím užší kontrolu nad využitím paměti. Ukazuje také rozdíly mezi modely Direct3D 11 a Direct3D 12 pro nahrávání různých typů prostředků.
Nahrát různé typy zdrojů
V Direct3D 12 vytvoříte jednu vyrovnávací paměť, která bude vyhovovat různým typům dat prostředků pro nahrání, a data prostředků zkopírujete do stejné vyrovnávací paměti podobným způsobem pro různá data prostředků. Poté se vytvoří jednotlivá zobrazení, která vážou data těchto prostředků na grafický řetězec v modelu vazby prostředků Direct3D 12.
V Direct3D 11 vytvoříte samostatné vyrovnávací paměti pro různé typy dat prostředků (všimněte si různých BindFlags používaných v níže uvedeném vzorovém kódu Direct3D 11), explicitně provážete každou vyrovnávací paměť prostředků s grafickým kanálem a aktualizujete data prostředků různými metodami na základě různých typů prostředků.
V direct3D 12 i Direct3D 11 byste měli použít prostředky pro nahrání jenom tam, kde procesor zapíše data jednou a GPU je přečte jednou.
V některých případech,
- GPU bude data číst vícekrát, nebo
- GPU nečte data lineárně nebo
- vykreslování je již výrazně omezené na GPU.
V takových případech může být vhodnější použít ID3D12GraphicsCommandList::CopyTextureRegion nebo ID3D12GraphicsCommandList::CopyBufferRegion k překopírování dat z vyrovnávací paměti pro upload do výchozího prostředku.
Výchozí prostředek se může nacházet ve fyzické paměti videa v samostatných grafických procesorech.
Příklad kódu: Direct3D 11
// Direct3D 11: Separate buffers for each resource type.
void main()
{
// ...
// Create a constant buffer.
float constantBufferData[] = ...;
D3D11_BUFFER_DESC constantBufferDesc = {0};
constantBufferDesc.ByteWidth = sizeof(constantBufferData);
constantBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
constantBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
constantBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
ComPtr<ID3D11Buffer> constantBuffer;
d3dDevice->CreateBuffer(
&constantBufferDesc,
NULL,
&constantBuffer
);
// Create a vertex buffer.
float vertexBufferData[] = ...;
D3D11_BUFFER_DESC vertexBufferDesc = { 0 };
vertexBufferDesc.ByteWidth = sizeof(vertexBufferData);
vertexBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
ComPtr<ID3D11Buffer> vertexBuffer;
d3dDevice->CreateBuffer(
&vertexBufferDesc,
NULL,
&vertexBuffer
);
// ...
}
void DrawFrame()
{
// ...
// Bind buffers to the graphics pipeline.
d3dDeviceContext->VSSetConstantBuffers(0, 1, constantBuffer.Get());
d3dDeviceContext->IASetVertexBuffers(0, 1, vertexBuffer.Get(), ...);
// Update the constant buffer.
D3D11_MAPPED_SUBRESOURCE mappedResource;
d3dDeviceContext->Map(
constantBuffer.Get(),
0,
D3D11_MAP_WRITE_DISCARD,
0,
&mappedResource
);
memcpy(mappedResource.pData, constantBufferData,
sizeof(contatnBufferData));
d3dDeviceContext->Unmap(constantBuffer.Get(), 0);
// Update the vertex buffer.
d3dDeviceContext->UpdateSubresource(
vertexBuffer.Get(),
0,
NULL,
vertexBufferData,
sizeof(vertexBufferData),
0
);
// ...
}
Příklad kódu: Direct3D 12
// Direct3D 12: One buffer to accommodate different types of resources
ComPtr<ID3D12Resource> m_spUploadBuffer;
UINT8* m_pDataBegin = nullptr; // starting position of upload buffer
UINT8* m_pDataCur = nullptr; // current position of upload buffer
UINT8* m_pDataEnd = nullptr; // ending position of upload buffer
void main()
{
//
// Initialize an upload buffer
//
InitializeUploadBuffer(64 * 1024);
// ...
}
void DrawFrame()
{
// ...
// Set vertices data to the upload buffer.
float vertices[] = ...;
UINT verticesOffset = 0;
ThrowIfFailed(
SetDataToUploadBuffer(
vertices, sizeof(float), sizeof(vertices) / sizeof(float),
sizeof(float),
verticesOffset
));
// Set constant data to the upload buffer.
float constants[] = ...;
UINT constantsOffset = 0;
ThrowIfFailed(
SetDataToUploadBuffer(
constants, sizeof(float), sizeof(constants) / sizeof(float),
D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT,
constantsOffset
));
// Create vertex buffer views for the new binding model.
D3D12_VERTEX_BUFFER_VIEW vertexBufferViewDesc = {
m_spUploadBuffer->GetGPUVirtualAddress() + verticesOffset,
sizeof(vertices), // size
sizeof(float) * 4, // stride
};
commandList->IASetVertexBuffers(
0,
1,
&vertexBufferViewDesc,
));
// Create constant buffer views for the new binding model.
D3D12_CONSTANT_BUFFER_VIEW_DESC constantBufferViewDesc = {
m_spUploadBuffer->GetGPUVirtualAddress() + constantsOffset,
sizeof(constants) // size
};
d3dDevice->CreateConstantBufferView(
&constantBufferViewDesc,
...
));
// Continue command list building and execution ...
}
//
// Create an upload buffer and keep it always mapped.
//
HRESULT InitializeUploadBuffer(SIZE_T uSize)
{
HRESULT hr = d3dDevice->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES( D3D12_HEAP_TYPE_UPLOAD ),
D3D12_HEAP_FLAG_NONE,
&CD3DX12_RESOURCE_DESC::Buffer( uSize ),
D3D12_RESOURCE_STATE_GENERIC_READ, nullptr,
IID_PPV_ARGS( &m_spUploadBuffer ) );
if (SUCCEEDED(hr))
{
void* pData;
//
// No CPU reads will be done from the resource.
//
CD3DX12_RANGE readRange(0, 0);
m_spUploadBuffer->Map( 0, &readRange, &pData );
m_pDataCur = m_pDataBegin = reinterpret_cast< UINT8* >( pData );
m_pDataEnd = m_pDataBegin + uSize;
}
return hr;
}
//
// Sub-allocate from the buffer, with offset aligned.
//
HRESULT SuballocateFromBuffer(SIZE_T uSize, UINT uAlign)
{
m_pDataCur = reinterpret_cast< UINT8* >(
Align(reinterpret_cast< SIZE_T >(m_pDataCur), uAlign)
);
return (m_pDataCur + uSize > m_pDataEnd) ? E_INVALIDARG : S_OK;
}
//
// Place and copy data to the upload buffer.
//
HRESULT SetDataToUploadBuffer(
const void* pData,
UINT bytesPerData,
UINT dataCount,
UINT alignment,
UINT& byteOffset
)
{
SIZE_T byteSize = bytesPerData * dataCount;
HRESULT hr = SuballocateFromBuffer(byteSize, alignment);
if (SUCCEEDED(hr))
{
byteOffset = UINT(m_pDataCur - m_pDataBegin);
memcpy(m_pDataCur, pData, byteSize);
m_pDataCur += byteSize;
}
return hr;
}
//
// Align uLocation to the next multiple of uAlign.
//
UINT Align(UINT uLocation, UINT uAlign)
{
if ( (0 == uAlign) || (uAlign & (uAlign-1)) )
{
ThrowException("non-pow2 alignment");
}
return ( (uLocation + (uAlign-1)) & ~(uAlign-1) );
}
Všimněte si použití pomocných struktur CD3DX12_HEAP_PROPERTIES a CD3DX12_RESOURCE_DESC.
Konstanty
Pokud chcete nastavit konstanty, vrcholy a indexy v haldě pro nahrávání nebo čtení, použijte následující rozhraní API.
- ID3D12Device::CreateConstantBufferView
- ID3D12GraphicsCommandList::IASetVertexBuffers
- ID3D12GraphicsCommandList::IASetIndexBuffer
Prostředky
Prostředky jsou konceptem Direct3D, který abstrahuje využití fyzické paměti GPU. Prostředky vyžadují pro přístup k fyzické paměti virtuální adresní prostor GPU. Vytváření prostředků je bez vláken.
Existují tři typy prostředků s ohledem na vytváření virtuálních adres a flexibilitu v Direct3D 12.
Potvrzené prostředky
Alokované prostředky jsou nejběžnějším konceptem zdrojů Direct3D napříč generacemi. Vytvoření takového prostředku přiděluje rozsah virtuálních adres, implicitní haldu dostatečně velkou, aby odpovídala celému prostředku, a potvrdí rozsah virtuálních adres do fyzické paměti zapouzdřené haldou. Implicitní vlastnosti haldy musí být předány tak, aby dosáhly funkční shody s předchozími verzemi Direct3D. Odkaz na ID3D12Device::CreateCommittedResource.
Rezervované prostředky
Rezervované prostředky jsou ekvivalentní prostředkům s dlaždicí Direct3D 11. Při jejich vytvoření je přidělen pouze rozsah virtuálních adres, ale nejsou mapovány na žádnou paměťovou haldu. Aplikace později namapuje tyto prostředky na paměťové haldy. Možnosti těchto prostředků se v současné době nemění od Direct3D 11, protože je možné je mapovat na haldu v členitosti dlaždice 64 kB s UpdateTileMappings. Odkaz na ID3D12Device::CreateReservedResource.
Umístěné prostředky
Novinky pro Direct3D 12: můžete vytvářet haldy oddělené od prostředků. Potom můžete vyhledat více prostředků v jedné hromadě. Můžete to udělat bez vytváření dlaždic nebo rezervovaných prostředků, což umožňuje vytvářet funkce pro všechny typy prostředků, které může vaše aplikace vytvářet přímo. Může se překrývat více prostředků a ke správnému využití fyzické paměti musíte použít ID3D12GraphicsCommandList::ResourceBarrier. Projděte si ID3D12Device::CreatePlacedResource.
Odraz velikosti prostředků
Musíte použít reflexi velikosti prostředku, abyste pochopili, kolik místa textury s neznámými rozloženími textur vyžadují v haldách. Vyrovnávací paměti jsou také podporovány, ale spíše pro usnadnění.
Měli byste si být vědomi zásadních nesrovnalostí v zarovnání, aby bylo možné prostředky zabalit hustěji.
Například pole s jedním prvkem a jednobajtovým vyrovnávacím pamětí vrátí velikost 64 kB a zarovnání 64 kB, protože vyrovnávací paměti mohou být zarovnány pouze na 64 kB.
Také pole tří prvků s dvěma jedno-texelovými texturami zarovnanými na 64KB a jednou jedno-texelovou texturou zarovnanou na 4MB vykazuje různé velikosti v závislosti na pořadí pole. Pokud jsou 4 MB zarovnané textury uprostřed, výsledná Velikost je 12 MB. V opačném případě je výsledná velikost 8 MB. Vrácené zarovnání by vždy bylo 4 MB a bylo by nadmnožinou všech zarovnání v poli prostředků.
Odkaz na následující rozhraní API.
Zarovnání vyrovnávací paměti
Omezení zarovnání vyrovnávací paměti se ve srovnání s Direct3D 11 nezměnila, zejména:
- 4 MB pro vícevzorkové textury.
- 64 kB pro textury s jedním vzorkem a vyrovnávací paměti.