@@ -31,7 +31,8 @@ D3D12HeterogeneousMultiadapter::D3D12HeterogeneousMultiadapter(int width, int he
31
31
m_currentRenderFenceValue(1 ),
32
32
m_currentCrossAdapterFenceValue(1 ),
33
33
m_workloadConstantBufferData(),
34
- m_blurWorkloadConstantBufferData()
34
+ m_blurWorkloadConstantBufferData(),
35
+ m_crossAdapterTextureSupport(false )
35
36
{
36
37
ZeroMemory (m_rtvDescriptorSizes, sizeof (m_rtvDescriptorSizes));
37
38
ZeroMemory (m_srvDescriptorSizes, sizeof (m_srvDescriptorSizes));
@@ -78,15 +79,6 @@ HRESULT D3D12HeterogeneousMultiadapter::GetHardwareAdapters(IDXGIFactory2* pFact
78
79
ThrowIfFailed (pFactory->EnumAdapters1 (0 , ppSecondaryAdapter));
79
80
ThrowIfFailed (pFactory->EnumAdapters1 (1 , ppPrimaryAdapter));
80
81
81
- DXGI_ADAPTER_DESC1 desc;
82
- (*ppPrimaryAdapter)->GetDesc1 (&desc);
83
- if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)
84
- {
85
- // There is actually only one physical GPU on the system.
86
- // Reduce the starting triangle count to make the sample run better.
87
- m_triangleCount = MaxTriangleCount / 50 ;
88
- }
89
-
90
82
return S_OK;
91
83
}
92
84
@@ -122,6 +114,15 @@ void D3D12HeterogeneousMultiadapter::LoadPipeline()
122
114
ThrowIfFailed (GetHardwareAdapters (factory.Get (), &primaryAdapter, &secondaryAdapter));
123
115
}
124
116
117
+ DXGI_ADAPTER_DESC1 desc;
118
+ primaryAdapter->GetDesc1 (&desc);
119
+ if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)
120
+ {
121
+ // There is actually only one physical GPU on the system.
122
+ // Reduce the starting triangle count to make the sample run better.
123
+ m_triangleCount = MaxTriangleCount / 50 ;
124
+ }
125
+
125
126
IDXGIAdapter1* ppAdapters[] = { primaryAdapter.Get (), secondaryAdapter.Get () };
126
127
for (UINT i = 0 ; i < GraphicsAdaptersCount; i++)
127
128
{
@@ -288,34 +289,96 @@ void D3D12HeterogeneousMultiadapter::LoadPipeline()
288
289
289
290
// Create cross-adapter shared resources on the primary adapter, and open the shared handles on the secondary adapter.
290
291
{
291
- D3D12_RESOURCE_DESC crossAdapterDesc = m_renderTargets[Primary][0 ]->GetDesc ();
292
- crossAdapterDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_CROSS_ADAPTER;
293
- crossAdapterDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
292
+ // Check whether shared row-major textures can be directly sampled by the
293
+ // secondary adapter. Support of this feature (or the lack thereof) will
294
+ // determine our sharing strategy for the resource in question.
295
+ D3D12_FEATURE_DATA_D3D12_OPTIONS options = {};
296
+ ThrowIfFailed (m_devices[Secondary]->CheckFeatureSupport (D3D12_FEATURE_D3D12_OPTIONS, reinterpret_cast <void *>(&options), sizeof (options)));
297
+ m_crossAdapterTextureSupport = options.CrossAdapterRowMajorTextureSupported ;
298
+
299
+ UINT64 textureSize = 0 ;
300
+ D3D12_RESOURCE_DESC crossAdapterDesc;
301
+
302
+ if (m_crossAdapterTextureSupport)
303
+ {
304
+ // If cross-adapter row-major textures are supported by the adapter,
305
+ // then they can be sampled directly.
306
+ crossAdapterDesc = renderTargetDesc;
307
+ crossAdapterDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_CROSS_ADAPTER;
308
+ crossAdapterDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
309
+
310
+ D3D12_RESOURCE_ALLOCATION_INFO textureInfo = m_devices[Primary]->GetResourceAllocationInfo (0 , 1 , &crossAdapterDesc);
311
+ textureSize = textureInfo.SizeInBytes ;
312
+ }
313
+ else
314
+ {
315
+ // If cross-adapter row-major textures are not supported by the adapter,
316
+ // then they will be shared as buffers and then copied to a destination
317
+ // texture on the secondary adapter.
318
+
319
+ D3D12_PLACED_SUBRESOURCE_FOOTPRINT layout;
320
+ m_devices[Primary]->GetCopyableFootprints (&renderTargetDesc, 0 , 1 , 0 , &layout, nullptr , nullptr , nullptr );
321
+ textureSize = Align (layout.Footprint .RowPitch * layout.Footprint .Height );
322
+
323
+ // Create a buffer with the same layout as the render target texture.
324
+ crossAdapterDesc = CD3DX12_RESOURCE_DESC::Buffer (textureSize, D3D12_RESOURCE_FLAG_ALLOW_CROSS_ADAPTER);
325
+ }
326
+
327
+ // Create a heap that will be shared by both adapters.
328
+ CD3DX12_HEAP_DESC heapDesc (
329
+ textureSize * FrameCount,
330
+ D3D12_HEAP_TYPE_DEFAULT,
331
+ 0 ,
332
+ D3D12_HEAP_FLAG_SHARED | D3D12_HEAP_FLAG_SHARED_CROSS_ADAPTER);
333
+
334
+ ThrowIfFailed (m_devices[Primary]->CreateHeap (&heapDesc, IID_PPV_ARGS (&m_crossAdapterResourceHeaps[Primary])));
335
+
336
+ HANDLE heapHandle = nullptr ;
337
+ ThrowIfFailed (m_devices[Primary]->CreateSharedHandle (
338
+ m_crossAdapterResourceHeaps[Primary].Get (),
339
+ nullptr ,
340
+ GENERIC_ALL,
341
+ nullptr ,
342
+ &heapHandle));
343
+
344
+ HRESULT openSharedHandleResult = m_devices[Secondary]->OpenSharedHandle (heapHandle, IID_PPV_ARGS (&m_crossAdapterResourceHeaps[Secondary]));
294
345
346
+ // We can close the handle after opening the cross-adapter shared resource.
347
+ CloseHandle (heapHandle);
348
+
349
+ ThrowIfFailed (openSharedHandleResult);
350
+
351
+ // Create placed resources for each frame per adapter in the shared heap.
295
352
for (UINT n = 0 ; n < FrameCount; n++)
296
353
{
297
- ThrowIfFailed (m_devices[Primary]->CreateCommittedResource (
298
- & CD3DX12_HEAP_PROPERTIES (D3D12_HEAP_TYPE_DEFAULT ),
299
- D3D12_HEAP_FLAG_SHARED | D3D12_HEAP_FLAG_SHARED_CROSS_ADAPTER ,
354
+ ThrowIfFailed (m_devices[Primary]->CreatePlacedResource (
355
+ m_crossAdapterResourceHeaps[Primary]. Get ( ),
356
+ textureSize * n ,
300
357
&crossAdapterDesc,
301
358
D3D12_RESOURCE_STATE_COPY_DEST,
302
359
nullptr ,
303
360
IID_PPV_ARGS (&m_crossAdapterResources[Primary][n])));
304
361
305
- HANDLE heapHandle = nullptr ;
306
- ThrowIfFailed (m_devices[Primary]-> CreateSharedHandle (
307
- m_crossAdapterResources[Primary][n]. Get () ,
308
- nullptr ,
309
- GENERIC_ALL ,
362
+ ThrowIfFailed (m_devices[Secondary]-> CreatePlacedResource (
363
+ m_crossAdapterResourceHeaps[Secondary]. Get (),
364
+ textureSize * n ,
365
+ &crossAdapterDesc ,
366
+ m_crossAdapterTextureSupport ? D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE : D3D12_RESOURCE_STATE_COPY_SOURCE ,
310
367
nullptr ,
311
- &heapHandle ));
368
+ IID_PPV_ARGS (&m_crossAdapterResources[Secondary][n]) ));
312
369
313
- HRESULT openSharedHandleResult = m_devices[Secondary]->OpenSharedHandle (heapHandle, IID_PPV_ARGS (&m_crossAdapterResources[Secondary][n]));
314
-
315
- // We can close the handle after opening the cross-adapter shared resource.
316
- CloseHandle (heapHandle);
317
-
318
- ThrowIfFailed (openSharedHandleResult);
370
+ if (!m_crossAdapterTextureSupport)
371
+ {
372
+ // If the primary adapter's render target must be shared as a buffer,
373
+ // create a texture resource to copy it into on the secondary adapter.
374
+ ThrowIfFailed (m_devices[Secondary]->CreateCommittedResource (
375
+ &CD3DX12_HEAP_PROPERTIES (D3D12_HEAP_TYPE_DEFAULT),
376
+ D3D12_HEAP_FLAG_NONE,
377
+ &renderTargetDesc,
378
+ D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
379
+ nullptr ,
380
+ IID_PPV_ARGS (&m_secondaryAdapterTextures[n])));
381
+ }
319
382
}
320
383
}
321
384
@@ -335,12 +398,13 @@ void D3D12HeterogeneousMultiadapter::LoadPipeline()
335
398
m_devices[Secondary]->CreateRenderTargetView (m_intermediateBlurRenderTarget.Get (), nullptr , rtvHandle);
336
399
}
337
400
338
- // Create a SRV for the cross-adapter resources and intermediate render target on the secondary adapter.
401
+ // Create SRVs for the shared resources and intermediate render target on the secondary adapter.
339
402
{
340
403
CD3DX12_CPU_DESCRIPTOR_HANDLE srvHandle (m_cbvSrvUavHeap->GetCPUDescriptorHandleForHeapStart ());
341
404
for (UINT n = 0 ; n < FrameCount; n++)
342
405
{
343
- m_devices[Secondary]->CreateShaderResourceView (m_crossAdapterResources[Secondary][n].Get (), nullptr , srvHandle);
406
+ ID3D12Resource* pSrvResource = m_crossAdapterTextureSupport ? m_crossAdapterResources[Secondary][n].Get () : m_secondaryAdapterTextures[n].Get ();
407
+ m_devices[Secondary]->CreateShaderResourceView (pSrvResource, nullptr , srvHandle);
344
408
srvHandle.Offset (m_srvDescriptorSizes[Secondary]);
345
409
}
346
410
@@ -691,7 +755,7 @@ void D3D12HeterogeneousMultiadapter::LoadAssets()
691
755
692
756
// Fence used by the primary adapter to signal its copy queue that it has completed rendering.
693
757
// When this is signaled, the primary adapter's copy queue can begin copying to the cross-adapter shared resource.
694
- ThrowIfFailed (m_devices[Primary]->CreateFence (0 , D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS (&m_renderFence)));
758
+ ThrowIfFailed (m_devices[Primary]->CreateFence (0 , D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS (&m_renderFence)));
695
759
696
760
// Cross-adapter shared fence used by both adapters.
697
761
// Used by the primary adapter to signal the secondary adapter that it has completed copying to the cross-adapter shared resource.
@@ -1006,8 +1070,35 @@ void D3D12HeterogeneousMultiadapter::PopulateCommandLists()
1006
1070
ThrowIfFailed (m_copyCommandAllocators[m_frameIndex]->Reset ());
1007
1071
ThrowIfFailed (m_copyCommandList->Reset (m_copyCommandAllocators[m_frameIndex].Get (), nullptr ));
1008
1072
1009
- // Copy the resource to the cross-adapter shared resource
1010
- m_copyCommandList->CopyResource (m_crossAdapterResources[adapter][m_frameIndex].Get (), m_renderTargets[adapter][m_frameIndex].Get ());
1073
+ // Copy the intermediate render target to the cross-adapter shared resource.
1074
+ // Transition barriers are not required since there are fences guarding against
1075
+ // concurrent read/write access to the shared heap.
1076
+ if (m_crossAdapterTextureSupport)
1077
+ {
1078
+ // If cross-adapter row-major textures are supported by the adapter,
1079
+ // simply copy the texture into the cross-adapter texture.
1080
+ m_copyCommandList->CopyResource (m_crossAdapterResources[adapter][m_frameIndex].Get (), m_renderTargets[adapter][m_frameIndex].Get ());
1081
+ }
1082
+ else
1083
+ {
1084
+ // If cross-adapter row-major textures are not supported by the adapter,
1085
+ // the texture will be copied over as a buffer so that the texture row
1086
+ // pitch can be explicitly managed.
1087
+
1088
+ // Copy the intermediate render target into the shared buffer using the
1089
+ // memory layout prescribed by the render target.
1090
+ D3D12_RESOURCE_DESC renderTargetDesc = m_renderTargets[adapter][m_frameIndex]->GetDesc ();
1091
+ D3D12_PLACED_SUBRESOURCE_FOOTPRINT renderTargetLayout;
1092
+
1093
+ m_devices[adapter]->GetCopyableFootprints (&renderTargetDesc, 0 , 1 , 0 , &renderTargetLayout, nullptr , nullptr , nullptr );
1094
+
1095
+ CD3DX12_TEXTURE_COPY_LOCATION dest (m_crossAdapterResources[adapter][m_frameIndex].Get (), renderTargetLayout);
1096
+ CD3DX12_TEXTURE_COPY_LOCATION src (m_renderTargets[adapter][m_frameIndex].Get (), 0 );
1097
+ CD3DX12_BOX box (0 , 0 , m_width, m_height);
1098
+
1099
+ m_copyCommandList->CopyTextureRegion (&dest, 0 , 0 , 0 , &src, &box);
1100
+ }
1101
+
1011
1102
ThrowIfFailed (m_copyCommandList->Close ());
1012
1103
}
1013
1104
@@ -1025,6 +1116,34 @@ void D3D12HeterogeneousMultiadapter::PopulateCommandLists()
1025
1116
// re-recording.
1026
1117
ThrowIfFailed (m_directCommandLists[adapter]->Reset (m_directCommandAllocators[adapter][m_frameIndex].Get (), m_blurPipelineStates[0 ].Get ()));
1027
1118
1119
+ if (!m_crossAdapterTextureSupport)
1120
+ {
1121
+ // Copy the buffer in the shared heap into a texture that the secondary
1122
+ // adapter can sample from.
1123
+ D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition (
1124
+ m_secondaryAdapterTextures[m_frameIndex].Get (),
1125
+ D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
1126
+ D3D12_RESOURCE_STATE_COPY_DEST);
1127
+ m_directCommandLists[adapter]->ResourceBarrier (1 , &barrier);
1128
+
1129
+ // Copy the shared buffer contents into the texture using the memory
1130
+ // layout prescribed by the texture.
1131
+ D3D12_RESOURCE_DESC secondaryAdapterTexture = m_secondaryAdapterTextures[m_frameIndex]->GetDesc ();
1132
+ D3D12_PLACED_SUBRESOURCE_FOOTPRINT textureLayout;
1133
+
1134
+ m_devices[adapter]->GetCopyableFootprints (&secondaryAdapterTexture, 0 , 1 , 0 , &textureLayout, nullptr , nullptr , nullptr );
1135
+
1136
+ CD3DX12_TEXTURE_COPY_LOCATION dest (m_secondaryAdapterTextures[m_frameIndex].Get (), 0 );
1137
+ CD3DX12_TEXTURE_COPY_LOCATION src (m_crossAdapterResources[adapter][m_frameIndex].Get (), textureLayout);
1138
+ CD3DX12_BOX box (0 , 0 , m_width, m_height);
1139
+
1140
+ m_directCommandLists[adapter]->CopyTextureRegion (&dest, 0 , 0 , 0 , &src, &box);
1141
+
1142
+ barrier.Transition .StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
1143
+ barrier.Transition .StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
1144
+ m_directCommandLists[adapter]->ResourceBarrier (1 , &barrier);
1145
+ }
1146
+
1028
1147
// Get a timestamp at the start of the command list.
1029
1148
const UINT timestampHeapIndex = 2 * m_frameIndex;
1030
1149
m_directCommandLists[adapter]->EndQuery (m_timestampQueryHeaps[adapter].Get (), D3D12_QUERY_TYPE_TIMESTAMP, timestampHeapIndex);
@@ -1062,7 +1181,6 @@ void D3D12HeterogeneousMultiadapter::PopulateCommandLists()
1062
1181
{
1063
1182
m_directCommandLists[adapter]->SetPipelineState (m_blurPipelineStates[1 ].Get ());
1064
1183
1065
- // Indicate that the intermediate render target will be used as a shader resource.
1066
1184
// Indicate that the back buffer will be used as a render target and the
1067
1185
// intermediate render target will be used as a SRV.
1068
1186
D3D12_RESOURCE_BARRIER barriers[] = {
0 commit comments