Hochladen verschiedener Arten von Ressourcen
Zeigt, wie Sie einen Puffer verwenden, um sowohl konstante Pufferdaten als auch Vertexpufferdaten in die GPU hochzuladen, und wie Sie Daten ordnungsgemäß unterteilen und in Puffern platzieren. Die Verwendung eines einzelnen Puffers erhöht die Flexibilität bei der Speicherauslastung und bietet Anwendungen eine engere Kontrolle über die Speicherauslastung. Zeigt außerdem die Unterschiede zwischen den Direct3D 11- und Direct3D 12-Modellen zum Hochladen verschiedener Ressourcentypen.
Hochladen verschiedener Arten von Ressourcen
In Direct3D 12 erstellen Sie einen Puffer, um verschiedene Arten von Ressourcendaten zum Hochladen aufzunehmen, und Sie kopieren Ressourcendaten auf ähnliche Weise in denselben Puffer für unterschiedliche Ressourcendaten. Anschließend werden einzelne Ansichten erstellt, um diese Ressourcendaten an die Grafikpipeline im Direct3D 12-Ressourcenbindungsmodell zu binden.
In Direct3D 11 erstellen Sie separate Puffer für verschiedene Arten von Ressourcendaten (beachten Sie die unterschiedlichen BindFlags
, die im folgenden Direct3D 11-Beispielcode verwendet werden), binden jeden Ressourcenpuffer explizit an die Grafikpipeline und aktualisieren die Ressourcendaten mit unterschiedlichen Methoden basierend auf verschiedenen Ressourcentypen.
Sowohl in Direct3D 12 als auch in Direct3D 11 sollten Sie Ressourcen nur dort hochladen, wo die CPU die Daten einmal schreibt, und die GPU liest sie einmal.
In einigen Fällen
- liest die GPU die Daten mehrmals oder
- liest die GPU die Daten nicht linear oder
- das Rendering ist bereits erheblich GPU-begrenzt.
In diesen Fällen ist die bessere Option möglicherweise die Verwendung von ID3D12GraphicsCommandList::CopyTextureRegion oder ID3D12GraphicsCommandList::CopyBufferRegion, um die Uploadpufferdaten in eine Standardressource zu kopieren.
Eine Standardressource kann sich im physischen Videospeicher auf separaten GPUs befinden.
Codebeispiel: 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
);
// ...
}
Codebeispiel: 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) );
}
Beachten Sie die Verwendung der Hilfsstrukturen CD3DX12_HEAP_PROPERTIES und CD3DX12_RESOURCE_DESC.
Konstanten
Verwenden Sie die folgenden APIs, um Konstanten, Scheitelpunkte und Indizes innerhalb eines Upload- oder Readback-Heaps festzulegen.
- ID3D12Device::CreateConstantBufferView
- ID3D12GraphicsCommandList::IASetVertexBuffers
- ID3D12GraphicsCommandList::IASetIndexBuffer
Ressourcen
Ressourcen sind das Direct3D-Konzept, das die Verwendung des physischen GPU-Speichers abstrahiert. Ressourcen erfordern einen virtuellen GPU-Adressraum, um auf physischen Speicher zuzugreifen. Die Ressourcenerstellung ist Freethread.
Es gibt drei Arten von Ressourcen in Bezug auf die Erstellung virtueller Adressen und Flexibilität in Direct3D 12.
Zugesicherte Ressourcen
Zugesicherte Ressourcen sind die am häufigsten verwendete Idee von Direct3D-Ressourcen über die Generationen hinweg. Durch die Erstellung einer solchen Ressource wird der virtuelle Adressbereich zugewiesen, ein impliziter Heap groß genug, um die gesamte Ressource aufzunehmen, und der virtuelle Adressbereich wird in den durch den Heap gekapselten physischen Speicher übertragen. Die impliziten Heap-Eigenschaften müssen übergeben werden, um der funktionalen Parität mit früheren Direct3D-Versionen zu entsprechen. Verweisen Sie auf ID3D12Device::CreateCommittedResource.
Reservierte Ressourcen
Reservierte Ressourcen entsprechen Direct3D 11-Kachel-Ressourcen. Bei der Erstellung wird nur ein virtueller Adressbereich zugewiesen und keinem Heap zugeordnet. Die Anwendung ordnet diese Ressourcen später zu Heaps zu. Die Funktionen dieser Ressourcen sind derzeit unverändert von Direct3D 11, da sie einem Heap mit einer Granularität von 64 KB mit UpdateTileMappings zugeordnet werden können. Verweisen Sie auf ID3D12Device::CreateReservedResource.
Platzierte Ressourcen
Neu für Direct3D 12, können Sie Heaps getrennt von Ressourcen erstellen. Anschließend können Sie mehrere Ressourcen innerhalb eines einzelnen Heaps suchen. Sie können dies tun, ohne gekachelte oder reservierte Ressourcen zu erstellen und die Funktionen für alle Ressourcentypen zu aktivieren, die direkt von Ihrer Anwendung erstellt werden können. Mehrere Ressourcen können sich überlappen, und Sie müssen die ID3D12GraphicsCommandList::ResourceBarrier verwenden, um den physischen Arbeitsspeicher ordnungsgemäß zu verwenden. Verweisen Sie auf ID3D12Device::CreatePlacedResource.
Spiegelung der Ressourcengröße
Sie müssen die Ressourcengrößenspiegelung verwenden, um zu verstehen, wie viel Raumtexturen mit unbekannten Texturlayouts in Heaps erforderlich sind. Puffer werden auch unterstützt, aber hauptsächlich als Komfort.
Beachten Sie wichtige Ausrichtungsabweichungen, um Ressourcen dichter zu packen.
Beispielsweise gibt ein Single-Element-Array mit einem 1-Byte-Puffer einer Größe von 64 KB und einer Ausrichtung von 64 KB, da Puffer nur 64 KB ausgerichtet sein können.
Ein Array mit drei Elementen mit zwei Single-Texel 64 KB ausgerichteten Texturen und einem Single-Texel 4 MB ausgerichteten Texturberichten unterscheidet sich je nach Reihenfolge des Arrays. Wenn sich die 4 MB ausgerichteten Texturen in der Mitte befindet, beträgt die resultierende Größe 12 MB. Andernfalls beträgt die resultierende Größe 8 MB. Die zurückgegebene Ausrichtung wäre immer 4 MB, die Obermenge aller Ausrichtungen im Ressourcenarray.
Verweisen Sie auf die folgenden APIs.
Ausrichtung des Puffers
Einschränkungen für die Pufferausrichtung wurden von Direct3D 11 nicht geändert, insbesondere:
- 4 MB für Textur mit mehreren Beispielen.
- 64 KB für Texturen und Puffer mit einem Beispiel.