@@ -31,7 +31,8 @@ D3D12HeterogeneousMultiadapter::D3D12HeterogeneousMultiadapter(int width, int he
3131 m_currentRenderFenceValue(1 ),
3232 m_currentCrossAdapterFenceValue(1 ),
3333 m_workloadConstantBufferData(),
34- m_blurWorkloadConstantBufferData()
34+ m_blurWorkloadConstantBufferData(),
35+ m_crossAdapterTextureSupport(false )
3536{
3637 ZeroMemory (m_rtvDescriptorSizes, sizeof (m_rtvDescriptorSizes));
3738 ZeroMemory (m_srvDescriptorSizes, sizeof (m_srvDescriptorSizes));
@@ -78,15 +79,6 @@ HRESULT D3D12HeterogeneousMultiadapter::GetHardwareAdapters(IDXGIFactory2* pFact
7879 ThrowIfFailed (pFactory->EnumAdapters1 (0 , ppSecondaryAdapter));
7980 ThrowIfFailed (pFactory->EnumAdapters1 (1 , ppPrimaryAdapter));
8081
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-
9082 return S_OK;
9183}
9284
@@ -122,6 +114,15 @@ void D3D12HeterogeneousMultiadapter::LoadPipeline()
122114 ThrowIfFailed (GetHardwareAdapters (factory.Get (), &primaryAdapter, &secondaryAdapter));
123115 }
124116
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+
125126 IDXGIAdapter1* ppAdapters[] = { primaryAdapter.Get (), secondaryAdapter.Get () };
126127 for (UINT i = 0 ; i < GraphicsAdaptersCount; i++)
127128 {
@@ -288,34 +289,96 @@ void D3D12HeterogeneousMultiadapter::LoadPipeline()
288289
289290 // Create cross-adapter shared resources on the primary adapter, and open the shared handles on the secondary adapter.
290291 {
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]));
294345
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.
295352 for (UINT n = 0 ; n < FrameCount; n++)
296353 {
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 ,
300357 &crossAdapterDesc,
301358 D3D12_RESOURCE_STATE_COPY_DEST,
302359 nullptr ,
303360 IID_PPV_ARGS (&m_crossAdapterResources[Primary][n])));
304361
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 ,
310367 nullptr ,
311- &heapHandle ));
368+ IID_PPV_ARGS (&m_crossAdapterResources[Secondary][n]) ));
312369
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+ }
319382 }
320383 }
321384
@@ -335,12 +398,13 @@ void D3D12HeterogeneousMultiadapter::LoadPipeline()
335398 m_devices[Secondary]->CreateRenderTargetView (m_intermediateBlurRenderTarget.Get (), nullptr , rtvHandle);
336399 }
337400
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.
339402 {
340403 CD3DX12_CPU_DESCRIPTOR_HANDLE srvHandle (m_cbvSrvUavHeap->GetCPUDescriptorHandleForHeapStart ());
341404 for (UINT n = 0 ; n < FrameCount; n++)
342405 {
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);
344408 srvHandle.Offset (m_srvDescriptorSizes[Secondary]);
345409 }
346410
@@ -691,7 +755,7 @@ void D3D12HeterogeneousMultiadapter::LoadAssets()
691755
692756 // Fence used by the primary adapter to signal its copy queue that it has completed rendering.
693757 // 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)));
695759
696760 // Cross-adapter shared fence used by both adapters.
697761 // 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()
10061070 ThrowIfFailed (m_copyCommandAllocators[m_frameIndex]->Reset ());
10071071 ThrowIfFailed (m_copyCommandList->Reset (m_copyCommandAllocators[m_frameIndex].Get (), nullptr ));
10081072
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+
10111102 ThrowIfFailed (m_copyCommandList->Close ());
10121103 }
10131104
@@ -1025,6 +1116,34 @@ void D3D12HeterogeneousMultiadapter::PopulateCommandLists()
10251116 // re-recording.
10261117 ThrowIfFailed (m_directCommandLists[adapter]->Reset (m_directCommandAllocators[adapter][m_frameIndex].Get (), m_blurPipelineStates[0 ].Get ()));
10271118
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+
10281147 // Get a timestamp at the start of the command list.
10291148 const UINT timestampHeapIndex = 2 * m_frameIndex;
10301149 m_directCommandLists[adapter]->EndQuery (m_timestampQueryHeaps[adapter].Get (), D3D12_QUERY_TYPE_TIMESTAMP, timestampHeapIndex);
@@ -1062,7 +1181,6 @@ void D3D12HeterogeneousMultiadapter::PopulateCommandLists()
10621181 {
10631182 m_directCommandLists[adapter]->SetPipelineState (m_blurPipelineStates[1 ].Get ());
10641183
1065- // Indicate that the intermediate render target will be used as a shader resource.
10661184 // Indicate that the back buffer will be used as a render target and the
10671185 // intermediate render target will be used as a SRV.
10681186 D3D12_RESOURCE_BARRIER barriers[] = {
0 commit comments