Freigeben über


Bindung in DirectML

In DirectML bezieht sich die Bindung auf die Anlage von Ressourcen an die Pipeline für die GPU, die während der Initialisierung und Ausführung Ihrer Machine Learning-Operatoren verwendet werden soll. Diese Ressourcen können z. B. Eingabe- und Ausgabe-Tensoren sowie alle temporären oder persistenten Ressourcen sein, die der Operator benötigt.

In diesem Thema werden die konzeptionellen und verfahrenstechnischen Details der Bindung behandelt. Wir empfehlen, dass Sie auch die Dokumentation für die APIs, die Sie aufrufen, einschließlich der Parameter und Anmerkungen, vollständig lesen.

Wichtige Ideen im Bereich Bindung

Die nachstehende Liste der Schritte enthält eine allgemeine Beschreibung der bindungsbezogenen Aufgaben. Sie müssen diese Schritte jedes Mal ausführen, wenn Sie einen ausführbaren Operator haben – ein ausführbarer Operator ist entweder ein Operatorinitialisierer oder ein kompilierter Operator. Diese Schritte führen die wichtigen Ideen, Strukturen und Methoden ein, die an der DirectML-Bindung beteiligt sind.

In den nachfolgenden Abschnitten dieses Themas werden diese Bindungsaufgaben ausführlicher erläutert, mit veranschaulichenden Codeausschnitten aus dem minimalen DirectML-Anwendungsbeispiel.

  • Rufen Sie IDMLDispatchable::GetBindingProperties für das Dispatch-Objekt auf, um zu bestimmen, wie viele Deskriptoren es benötigt, sowie seine temporären/persistenten Ressourcenanforderungen.
  • Erstellen Sie einen Direct3D 12-Deskriptorheap, der groß genug für die Deskriptoren ist, und binden Sie ihn an die Pipeline.
  • Rufen Sie IDMLDevice::CreateBindingTable auf, um eine DirectML-Bindungstabelle zu erstellen, um die an die Pipeline gebundenen Ressourcen darzustellen. Verwenden Sie die DML_BINDING_TABLE_DESC Struktur, um ihre Bindungstabelle zu beschreiben, einschließlich der Teilmenge der Deskriptoren, auf die sie im Deskriptor-Heap verweist.
  • Erstellen Sie temporäre/persistente Ressourcen als Direct3D 12-Pufferressourcen, beschreiben Sie sie mit DML_BUFFER_BINDING und DML_BINDING_DESC Strukturen, und fügen Sie sie der Bindungstabelle hinzu.
  • Wenn das verteilbare Element ein kompilierter Operator ist, erstellen Sie einen Puffer aus Tensorelementen als Direct3D 12-Pufferressource. Füllen Sie es auf / Laden Sie es hoch, beschreiben Sie es mit den DML_BUFFER_BINDING und DML_BINDING_DESC Strukturen und fügen Sie es der Bindungstabelle hinzu.
  • Übergeben Sie die Bindungstabelle als Parameter, wenn Sie IDMLCommandRecorder::RecordDispatch aufrufen.

Abrufen der Bindungseigenschaften eines verteilbaren Elements

Die DML_BINDING_PROPERTIES Struktur beschreibt die Bindungsanforderungen eines verteilbaren Operators (Operatorinitialisierer oder kompilierter Operator). Diese bindungsbezogenen Eigenschaften enthalten die Anzahl der Deskriptoren, die Sie an das verteilbare Element binden sollten, sowie die Größe (in Byte) aller temporären und/oder persistenten Ressourcen, die benötigt werden.

Hinweis

Selbst für mehrere Operatoren desselben Typs machen Sie keine Annahmen darüber, dass sie dieselben Bindungsanforderungen haben. Fragen Sie die Bindungseigenschaften für jeden von Ihnen erstellten Initialisierer und Operator ab.

Rufen Sie IDMLDispatchable::GetBindingProperties auf, um eine DML_BINDING_PROPERTIES abzurufen.

winrt::com_ptr<::IDMLCompiledOperator> dmlCompiledOperator;
// Code to create and compile a DirectML operator goes here.

DML_BINDING_PROPERTIES executeDmlBindingProperties{
    dmlCompiledOperator->GetBindingProperties()
};

winrt::com_ptr<::IDMLOperatorInitializer> dmlOperatorInitializer;
// Code to create a DirectML operator initializer goes here.

DML_BINDING_PROPERTIES initializeDmlBindingProperties{
    dmlOperatorInitializer->GetBindingProperties()
};

UINT descriptorCount = ...

Der descriptorCount hier abgerufene Wert bestimmt die (minimale) Größe des Deskriptor-Heaps und der Bindungstabelle, die Sie in den nächsten beiden Schritten erstellen.

DML_BINDING_PROPERTIES enthält auch ein TemporaryResourceSize Element, das die Mindestgröße in Byte der temporären Ressource ist, die an die Bindungstabelle für dieses verteilbare Objekt gebunden werden muss. Ein Wert von Null bedeutet, dass eine temporäre Ressource nicht erforderlich ist.

Und ein PersistentResourceSize Element, das die Mindestgröße in Byte der persistenten Ressource ist, die an die Bindungstabelle für dieses verteilbare Objekt gebunden werden muss. Ein Wert von Null bedeutet, dass eine persistente Ressource nicht erforderlich ist. Eine persistente Ressource muss bei Bedarf während der Initialisierung eines kompilierten Operators (wo sie als Ausgabe des Operatorinitialisierers gebunden ist) sowie während der Ausführung bereitgestellt werden. Weitere Informationen hierzu finden Sie weiter unten in diesem Thema. Nur kompilierte Operatoren verfügen über persistente Ressourcen– Operatorinitialisierer geben immer den Wert 0 für dieses Element zurück.

Wenn Sie IDMLDispatchable::GetBindingProperties sowohl vor als auch nach einem Aufruf von IDMLOperatorInitializer::Reset aufrufen, sind die beiden abgerufenen Bindungseigenschaften nicht garantiert identisch.

Beschreiben, Erstellen und Binden eines Deskriptorheaps

In Bezug auf Deskriptoren beginnt und endet Ihre Verantwortung mit dem Deskriptorheap selbst. DirectML selbst kümmert sich um das Erstellen und Verwalten der Deskriptoren innerhalb des von Ihnen bereitgestellten Heaps.

Verwenden Sie daher eine D3D12_DESCRIPTOR_HEAP_DESC-Struktur, um einen Heap zu beschreiben, der groß genug für die Anzahl der Deskriptoren ist, die das verteilbare Element benötigt. Erstellen Sie es dann mit ID3D12Device::CreateDescriptorHeap. Rufen Sie schließlich ID3D12GraphicsCommandList::SetDescriptorHeaps auf, um Ihren Deskriptor-Heap an die Pipeline zu binden.

winrt::com_ptr<::ID3D12DescriptorHeap> d3D12DescriptorHeap;

D3D12_DESCRIPTOR_HEAP_DESC descriptorHeapDescription{};
descriptorHeapDescription.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
descriptorHeapDescription.NumDescriptors = descriptorCount;
descriptorHeapDescription.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;

winrt::check_hresult(
    d3D12Device->CreateDescriptorHeap(
        &descriptorHeapDescription,
        _uuidof(d3D12DescriptorHeap),
        d3D12DescriptorHeap.put_void()
    )
);

std::array<ID3D12DescriptorHeap*, 1> d3D12DescriptorHeaps{ d3D12DescriptorHeap.get() };
d3D12GraphicsCommandList->SetDescriptorHeaps(
    static_cast<UINT>(d3D12DescriptorHeaps.size()),
    d3D12DescriptorHeaps.data()
);

Beschreiben und Erstellen einer Bindungstabelle

Eine DirectML-Bindungstabelle stellt die Ressourcen dar, die Sie zur Verwendung durch ein verteilbares Element an die Pipeline binden. Diese Ressourcen können Eingabe- und Ausgabe-Tensoren (oder andere Parameter) für einen Operator oder verschiedene persistente und temporäre Ressourcen sein, mit denen ein verteilbares Element arbeitet.

Verwenden Sie die DML_BINDING_TABLE_DESC Struktur, um Ihre Bindungstabelle zu beschreiben, einschließlich des Dispatchables, für den die Bindungstabelle die Bindungen darstellt, und den Bereich der Deskriptoren (vom soeben erstellten Deskriptor-Heap), auf den die Bindungstabelle verweisen soll (und in den DirectML möglicherweise Deskriptoren schreibt). Der descriptorCount Wert (eine der Bindungseigenschaften, die wir im ersten Schritt abgerufen haben) teilt uns mit, welche Mindestgröße in Deskriptoren der Bindungstabelle für das verteilbare Objekt erforderlich ist. Hier verwenden wir diesen Wert, um die maximale Anzahl von Deskriptoren anzugeben, die DirectML vom Anfang der bereitgestellten CPU- und GPU-Deskriptorhandles in unseren Heap schreiben darf.

Rufen Sie dann IDMLDevice::CreateBindingTable auf, um die DirectML-Bindungstabelle zu erstellen. Nachdem Sie weitere Ressourcen für das verteilbare Element erstellt haben, fügen Sie in späteren Schritten diese Ressourcen der Bindungstabelle hinzu.

Anstatt einen DML_BINDING_TABLE_DESC an diesen Aufruf zu übergeben, können Sie eine leere Bindungstabelle übergeben nullptr.

DML_BINDING_TABLE_DESC dmlBindingTableDesc{};
dmlBindingTableDesc.Dispatchable = dmlOperatorInitializer.get();
dmlBindingTableDesc.CPUDescriptorHandle = d3D12DescriptorHeap->GetCPUDescriptorHandleForHeapStart();
dmlBindingTableDesc.GPUDescriptorHandle = d3D12DescriptorHeap->GetGPUDescriptorHandleForHeapStart();
dmlBindingTableDesc.SizeInDescriptors = descriptorCount;

winrt::com_ptr<::IDMLBindingTable> dmlBindingTable;
winrt::check_hresult(
    dmlDevice->CreateBindingTable(
        &dmlBindingTableDesc,
        __uuidof(dmlBindingTable),
        dmlBindingTable.put_void()
    )
);

Die Reihenfolge, in der DirectML Deskriptoren in den Heap schreibt, ist nicht angegeben, sodass Ihre Anwendung darauf achten muss, die deskriptoren, die von der Bindungstabelle eingeschlossen sind, nicht zu überschreiben. Die bereitgestellten CPU- und GPU-Deskriptor-Handles können aus verschiedenen Heaps stammen. Es liegt jedoch in der Verantwortung Ihrer Anwendung, sicherzustellen, dass der gesamte Deskriptorbereich, auf den der CPU-Deskriptor-Handle verweist, vor der Ausführung unter Verwendung dieser Bindungstabelle in den Bereich kopiert wird, auf den der GPU-Deskriptor-Handle verweist. Der Deskriptorheap, aus dem die Handles bereitgestellt werden, muss vom Typ D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV sein. Darüber hinaus muss der Heap, auf den durch den GPUDescriptorHandle verwiesen wird, ein shader-sichtbarer Deskriptorheap sein.

Sie können eine Bindungstabelle zurücksetzen, um alle Ressourcen zu entfernen, die Sie ihr hinzugefügt haben, und gleichzeitig alle Eigenschaften ändern, die Sie für das anfängliche Element DML_BINDING_TABLE_DESC festgelegt haben (zum Umschließen eines neuen Bereichs von Deskriptoren oder zur erneuten Verwendung für ein anderes verteilbares Element). Nehmen Sie einfach die Änderungen an der Beschreibungsstruktur vor, und rufen Sie IDMLBindingTable::Reset auf.

dmlBindingTableDesc.Dispatchable = pIDMLCompiledOperator.get();

winrt::check_hresult(
    pIDMLBindingTable->Reset(
        &dmlBindingTableDesc
    )
);

Beschreiben und Binden von temporären/persistenten Ressourcen

Die DML_BINDING_PROPERTIES Struktur, die beim Abrufen der Bindungseigenschaften unserer verteilbaren Ressourcen aufgefüllt wurde, enthält die Größe in Byte aller temporären und/oder persistenten Ressourcen, die die verteilbaren Ressourcen benötigen. Wenn eine dieser Größen ungleich Null ist, erstellen Sie eine Direct3D 12-Pufferressource, und fügen Sie sie der Bindungstabelle hinzu.

Im folgenden Codebeispiel erstellen Sie eine temporäre Ressource (mit der Größe temporaryResourceSize Bytes) für das verteilbare Element. Wir beschreiben, wie wir die Ressource binden möchten, und dann fügen wir diese Bindung zur Bindungstabelle hinzu.

Da wir eine einzelne Pufferressource binden, beschreiben wir unsere Bindung mit einer DML_BUFFER_BINDING Struktur. In dieser Struktur geben Sie die Direct3D 12-Pufferressource (die Ressource muss die Dimension D3D12_RESOURCE_DIMENSION_BUFFER haben) sowie ein Offset und eine Größe im Puffer an. Es ist auch möglich, eine Bindung für ein Array von Puffern (und nicht für einen einzelnen Puffer) zu beschreiben, und die DML_BUFFER_ARRAY_BINDING-Struktur ist zu diesem Zweck vorhanden.

Um den Unterschied zwischen einer Pufferbindung und einer Pufferarraybindung abstrahieren zu können, verwenden wir die DML_BINDING_DESC Struktur. Sie können das Type Element der DML_BINDING_DESC entweder auf DML_BINDING_TYPE_BUFFER oder DML_BINDING_TYPE_BUFFER_ARRAY festlegen. Anschließend können Sie das Desc Element je nach so einstellen, dass es entweder auf ein DML_BUFFER_BINDING oder auf ein Type zeigt.

Wir behandeln die temporäre Ressource in diesem Beispiel. Daher fügen wir sie der Bindungstabelle mit einem Aufruf von IDMLBindingTable::BindTemporaryResource hinzu.

D3D12_HEAP_PROPERTIES defaultHeapProperties{ CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT) };
winrt::com_ptr<::ID3D12Resource> temporaryBuffer;

D3D12_RESOURCE_DESC temporaryBufferDesc{ CD3DX12_RESOURCE_DESC::Buffer(temporaryResourceSize) };
winrt::check_hresult(
    d3D12Device->CreateCommittedResource(
        &defaultHeapProperties,
        D3D12_HEAP_FLAG_NONE,
        &temporaryBufferDesc,
        D3D12_RESOURCE_STATE_COMMON,
        nullptr,
        __uuidof(temporaryBuffer),
        temporaryBuffer.put_void()
    )
);

DML_BUFFER_BINDING bufferBinding{ temporaryBuffer.get(), 0, temporaryResourceSize };
DML_BINDING_DESC bindingDesc{ DML_BINDING_TYPE_BUFFER, &bufferBinding };
dmlBindingTable->BindTemporaryResource(&bindingDesc);

Bei einer temporären Ressource (falls erforderlich) handelt es sich um einen Zwischenspeicher, der intern während der Ausführung des Operators verwendet wird, sodass Sie sich über deren Inhalte keine Gedanken machen müssen. Sie müssen sie auch nicht aufbewahren, nachdem Ihr Aufruf von IDMLCommandRecorder::RecordDispatch auf der GPU abgeschlossen wurde. Dies bedeutet, dass Ihre Anwendung die temporäre Ressource zwischen den Ausführungen des kompilierten Operators freigeben oder überschreiben kann. Der bereitgestellte Pufferbereich, der als temporäre Ressource gebunden werden soll, muss den Anfangsoffset an DML_TEMPORARY_BUFFER_ALIGNMENT ausrichten. Der Typ des Heaps, der dem Puffer zugrunde liegt, muss D3D12_HEAP_TYPE_DEFAULT lauten.

Wenn das verteilbare Element jedoch eine Größe ungleich Null für die langlebigere persistente Ressource meldet, ist das Verfahren etwas anders. Sie sollten einen Puffer erstellen und eine Bindung nach demselben Muster beschreiben wie oben gezeigt. Fügen Sie sie jedoch der Bindungstabelle des Operatorinitialisierers mit einem Aufruf von IDMLBindingTable::BindOutputs hinzu, da es der Auftrag des Operatorinitialisierers ist, die persistente Ressource zu initialisieren. Fügen Sie sie dann der Bindungstabelle des kompilierten Operators mit einem Aufruf von IDMLBindingTable::BindPersistentResource hinzu. Sehen Sie sich das minimale DirectML-Anwendungscodebeispiel an, um diesen Workflow in Aktion zu sehen. Der Inhalt und die Lebensdauer der persistenten Ressource müssen beibehalten werden, solange der kompilierte Operator ausgeführt wird. Das heißt, wenn ein Operator eine persistente Ressource erfordert, muss die Anwendung sie während der Initialisierung bereitstellen und sie anschließend auch für alle zukünftigen Ausführungen des Operators bereitstellen, ohne deren Inhalt zu ändern. Die persistente Ressource wird in der Regel von DirectML verwendet, um Nachschlagetabellen oder andere langlebige Daten zu speichern, die während der Initialisierung eines Operators berechnet und bei zukünftigen Ausführungen dieses Operators wiederverwendet werden. Der bereitgestellte Pufferbereich, der gebunden werden soll, da der persistente Puffer seinen Startoffset an DML_PERSISTENT_BUFFER_ALIGNMENT ausgerichtet haben muss. Der Typ des Heaps, der dem Puffer zugrunde liegt, muss D3D12_HEAP_TYPE_DEFAULT lauten.

Beschreiben und Binden von Tensoren

Wenn Sie mit einem kompilierten Operator (statt mit einem Operatorinitialisierer) arbeiten, müssen Sie Eingabe- und Ausgaberessourcen (für Tensoren und andere Parameter) an die Bindungstabelle des Operators binden. Die Anzahl der Bindungen muss genau mit der Anzahl der Eingaben des Operators übereinstimmen, einschließlich optionaler Tensoren. Die spezifischen Eingabe- und Ausgabe-Tensoren und andere Parameter, die ein Operator verwendet, werden im Thema für diesen Operator dokumentiert (z. B. DML_ELEMENT_WISE_IDENTITY_OPERATOR_DESC).

Eine Tensorressource ist ein Puffer, der die einzelnen Elementwerte des Tensors enthält. Sie laden einen solchen Puffer mithilfe der regulären Direct3D 12-Techniken (Hochladen von Ressourcen und Lesen von Daten über einen Puffer) in/aus der GPU hoch und lesen diesen zurück. Sehen Sie sich das minimale DirectML-Anwendungscodebeispiel an, um diese Techniken in Aktion zu sehen.

Beschreiben Sie schließlich Ihre Eingabe- und Ausgaberessourcenbindungen mit DML_BUFFER_BINDING und DML_BINDING_DESC Strukturen, und fügen Sie sie dann der Bindungstabelle des kompilierten Operators mit Aufrufen von IDMLBindingTable::BindInputs und IDMLBindingTable::BindOutputs hinzu. Wenn Sie eine IDMLBindingTable::Bind*- Methode aufrufen, schreibt DirectML einen oder mehrere Deskriptoren in den Bereich der CPU-Deskriptoren.

DML_BUFFER_BINDING inputBufferBinding{ inputBuffer.get(), 0, tensorBufferSize };
DML_BINDING_DESC inputBindingDesc{ DML_BINDING_TYPE_BUFFER, &inputBufferBinding };
dmlBindingTable->BindInputs(1, &inputBindingDesc);

DML_BUFFER_BINDING outputBufferBinding{ outputBuffer.get(), 0, tensorBufferSize };
DML_BINDING_DESC outputBindingDesc{ DML_BINDING_TYPE_BUFFER, &outputBufferBinding };
dmlBindingTable->BindOutputs(1, &outputBindingDesc);

Einer der Schritte beim Erstellen eines DirectML-Operators (siehe IDMLDevice::CreateOperator) besteht darin, eine oder mehrere DML_BUFFER_TENSOR_DESC Strukturen zu deklarieren, um die Vom Operator benötigten und zurückgegebenen Tensordatenpuffer zu beschreiben. Neben dem Typ und der Größe des Tensorpuffers können Sie optional das DML_TENSOR_FLAG_OWNED_BY_DML Flag angeben.

DML_TENSOR_FLAG_OWNED_BY_DML weist darauf hin, dass die Tensordaten im Besitz von DirectML sein und von diesem verwaltet werden sollen. DirectML erstellt eine Kopie der Tensordaten während der Initialisierung des Operators und speichert sie in der persistenten Ressource. Dadurch kann DirectML die Neuformatierung der Tensordaten in andere, effizientere Formulare durchführen. Das Festlegen dieses Flags kann die Leistung erhöhen, ist aber in der Regel nur für Tensoren nützlich, deren Daten während der gesamten Lebensdauer des Operators unverändert bleiben (z. B. Gewichtungstensoren). Und das Kennzeichen kann nur für Eingabe-Tensoren verwendet werden. Wenn die Kennzeichnung für eine bestimmte Tensorbeschreibung festgelegt wird, muss der entsprechende Tensor während der Operatorinitialisierung an die Bindungstabelle gebunden werden, und nicht während der Ausführung (was zu einem Fehler führt). Das ist das Gegenteil des Standardverhaltens (das Verhalten ohne das DML_TENSOR_FLAG_OWNED_BY_DML Flag), bei dem der Tensor während der Ausführung und nicht während der Initialisierung gebunden werden soll. Alle an DirectML gebundenen Ressourcen müssen Heapressourcen vom Typ DEFAULT oder CUSTOM sein.

Weitere Informationen finden Sie unter IDMLBindingTable::BindInputs und IDMLBindingTable::BindOutputs.

Ausführen des verteilbaren Elements

Übergeben Sie die Bindungstabelle als Parameter, wenn Sie IDMLCommandRecorder::RecordDispatch aufrufen.

Wenn Sie die Bindungstabelle während eines Aufrufs von IDMLCommandRecorder::RecordDispatch verwenden, bindet DirectML die entsprechenden GPU-Deskriptoren an die Pipeline. Die CPU- und GPU-Deskriptorhandles müssen nicht auf die gleichen Einträge in einem Deskriptor-Heap verweisen. Es liegt jedoch in der Verantwortung Ihrer Anwendung, sicherzustellen, dass der gesamte vom CPU-Deskriptorhandpunkt referenzierte Deskriptorbereich vor der Ausführung mithilfe dieser Bindungstabelle in den bereich kopiert wird, auf den der GPU-Deskriptor-Handle verweist.

winrt::com_ptr<::ID3D12GraphicsCommandList> d3D12GraphicsCommandList;
// Code to create a Direct3D 12 command list goes here.

winrt::com_ptr<::IDMLCommandRecorder> dmlCommandRecorder;
// Code to create a DirectML command recorder goes here.

dmlCommandRecorder->RecordDispatch(
    d3D12GraphicsCommandList.get(),
    dmlOperatorInitializer.get(),
    dmlBindingTable.get()
);

Schließen Sie schließlich Ihre Direct3D 12-Befehlsliste, und übermitteln Sie sie für die Ausführung wie jede andere Befehlsliste.

Vor der Ausführung von RecordDispatch auf der GPU müssen Sie alle gebundenen Ressourcen in den D3D12_RESOURCE_STATE_UNORDERED_ACCESS-Zustand oder auf einen Zustand übertragen, der implizit für D3D12_RESOURCE_STATE_UNORDERED_ACCESS verfügbar ist, z. B. D3D12_RESOURCE_STATE_COMMON. Nach Abschluss dieses Aufrufs, verbleiben die Ressourcen im D3D12_RESOURCE_STATE_UNORDERED_ACCESS-Zustand. Die einzige Ausnahme gilt für Uploadheaps, die gebunden werden, wenn ein Operatorinitialisierer ausgeführt wird und für mindestens einen Tensor das Flag DML_TENSOR_FLAG_OWNED_BY_DML festgelegt ist. In diesem Fall müssen alle Upload-Heaps, die für die Eingabe gebunden sind, im Zustand D3D12_RESOURCE_STATE_GENERIC_READ sein und müssen in diesem Zustand bleiben, wie es für alle Upload-Heaps erforderlich ist. Wenn DML_EXECUTION_FLAG_DESCRIPTORS_VOLATILE beim Kompilieren des Operators nicht festgelegt wurde, müssen alle Bindungen für die Bindungstabelle festgelegt werden, bevor RecordDispatch aufgerufen wird, andernfalls ist das Verhalten nicht definiert. Wenn ein Operator eine späte Bindung unterstützt, kann die Bindung von Ressourcen zurückgestellt werden, bis die Direct3D 12-Befehlsliste zur Ausführung an die Befehlswarteschlange übermittelt wird.

RecordDispatch wirkt logisch wie ein Aufruf von ID3D12GraphicsCommandList::Dispatch. Daher sind ungeordnete Zugriffsansichtsbarrieren (UAV) erforderlich, um die korrekte Reihenfolge sicherzustellen, wenn zwischen Ausführungen Datenabhängigkeiten bestehen. Diese Methode fügt keine UAV-Barrieren für Eingabe- oder Ausgaberessourcen ein. Ihre Anwendung muss sicherstellen, dass die richtigen UAV-Barrieren für alle Eingaben ausgeführt werden, wenn ihre Inhalte von einer Upstreamverteilung abhängen, und für alle Ausgaben, wenn nachgelagerte Verteilungen vorhanden sind, die von diesen Ausgaben abhängen.

Lebensdauer und Synchronisierung von Deskriptoren und Bindungstabellen

Ein gutes mentales Modell für das Binden in DirectML besteht darin, dass hinter den Kulissen die DirectML-Bindungstabelle selbst die Deskriptoren für ungeordnete Zugriffsansichten (UAVs) innerhalb des von Ihnen bereitgestellten Deskriptorskachels erstellt und verwaltet. Daher gelten alle üblichen Direct3D 12-Regeln für die Synchronisierung des Zugriffs auf diesen Heap und deren Deskriptoren. Es liegt in der Verantwortung Ihrer Anwendung, die korrekte Synchronisierung zwischen CPU und GPU durchzuführen, die eine Bindungstabelle verwendet.

Eine Bindungstabelle kann einen Deskriptor nicht überschreiben, während der Deskriptor verwendet wird (z. B. durch einen vorherigen Frame). Wenn Sie also einen bereits gebundenen Deskriptor-Heap wiederverwenden möchten (z. B. durch erneutes Aufrufen von Bind*in einer Bindungstabelle, die darauf verweist, oder durch manuelles Überschreiben des Deskriptors) sollten Sie auf die Verteiler warten, die derzeit den Deskriptor heap verwendet, um die Ausführung auf der GPU abzuschließen. Eine Bindungstabelle behält keinen starken Verweis auf den Deskriptorheap bei, in den sie schreibt. Daher dürfen Sie den unterstützenden für den Shader sichtbaren Deskriptorheap erst freigeben, wenn alle Aufgaben, die diese Bindungstabelle nutzen, auf der GPU abgeschlossen sind.

Wenn eine Bindungstabelle hingegen einen Deskriptorheap angibt und verwaltet, enthält die Tabelle selbst keinen dieser Speicher. Sie können also jederzeit eine Bindungstabelle freigeben oder zurücksetzen, nachdem Sie IDMLCommandRecorder::RecordDispatch damit aufgerufen haben (Sie müssen nicht warten, bis dieser Aufruf für die GPU abgeschlossen ist, solange die zugrunde liegenden Deskriptoren gültig bleiben).

Die Bindungstabelle behält keine starken Verweise auf ressourcengebundene Ressourcen bei. Ihre Anwendung muss sicherstellen, dass Ressourcen nicht gelöscht werden, während sie von der GPU weiterhin verwendet werden. Außerdem ist eine Bindungstabelle nicht threadsicher – Ihre Anwendung darf keine Methoden für eine Bindungstabelle gleichzeitig aus verschiedenen Threads ohne Synchronisierung aufrufen.

Und denken Sie daran, dass in jedem Fall eine Erneute Bindung nur erforderlich ist, wenn Sie ändern, welche Ressourcen gebunden sind. Wenn Sie die gebundenen Ressourcen nicht ändern müssen, können Sie beim Start einmal binden und bei jedem Aufruf von RecordDispatch dieselbe Bindungstabelle übergeben.

Stellen Sie für das Verflechten von Machine-Learning- und Rendering-Arbeitslasten nur sicher, dass die Bindungstabellen jedes Frames auf Bereiche des Deskriptor-Heaps verweisen, die auf der GPU noch nicht verwendet werden.

Optionales Angeben von spät gebundenen Operatorbindungen

Wenn Sie mit einem kompilierten Operator (statt mit einem Operatorinitialisierer) arbeiten, haben Sie die Möglichkeit, eine späte Bindung für den Operator anzugeben. Ohne späte Bindung müssen Sie alle Bindungen in der Bindungstabelle festlegen, bevor Sie einen Operator in eine Befehlsliste aufnehmen. Mit verspäteter Bindung können Sie Bindungen für Operatoren festlegen (oder ändern), die Sie bereits in einer Befehlsliste aufgezeichnet haben, bevor sie an die Befehlswarteschlange übermittelt wurde.

Rufen Sie zum Angeben einer späten Bindung IDMLDevice::CompileOperator mit dem flags-Argument DML_EXECUTION_FLAG_DESCRIPTORS_VOLATILE auf.

Siehe auch