MultiGPUExampleComponent.cpp 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <RHI/MultiGPUExampleComponent.h>
  9. #include <Utils/Utils.h>
  10. #include <SampleComponentManager.h>
  11. #include <Atom/RHI/CommandList.h>
  12. #include <Atom/RHI.Reflect/InputStreamLayoutBuilder.h>
  13. #include <Atom/RHI.Reflect/RenderAttachmentLayoutBuilder.h>
  14. #include <Atom/RPI.Public/Shader/Shader.h>
  15. #include <Atom/RHI.Reflect/ImageScopeAttachmentDescriptor.h>
  16. #include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
  17. #include <AzCore/Serialization/SerializeContext.h>
  18. #include <Atom/RHI/DrawItem.h>
  19. #include <Atom/RHI/CopyItem.h>
  20. #include <Atom/RHI.Reflect/BufferDescriptor.h>
  21. using namespace AZ;
  22. namespace AtomSampleViewer
  23. {
  24. void MultiGPUExampleComponent::Reflect(AZ::ReflectContext* context)
  25. {
  26. if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  27. {
  28. serializeContext->Class<MultiGPUExampleComponent, AZ::Component>()
  29. ->Version(0)
  30. ;
  31. }
  32. }
  33. void MultiGPUExampleComponent::OnFramePrepare(AZ::RHI::FrameGraphBuilder& frameGraphBuilder)
  34. {
  35. static float time = 0.0f;
  36. time += 0.005f;
  37. // Move the triangle around.
  38. AZ::Vector3 translation(
  39. sinf(time) * 0.25f,
  40. cosf(time) * 0.25f,
  41. 0.0f);
  42. if (m_shaderResourceGroupShared)
  43. {
  44. [[maybe_unused]] bool success =
  45. m_shaderResourceGroupShared->SetConstant(m_objectMatrixConstantIndex, AZ::Matrix4x4::CreateTranslation(translation));
  46. AZ_Warning("MultiGPUExampleComponent", success, "Failed to set SRG Constant m_objectMatrix");
  47. m_shaderResourceGroupShared->Compile();
  48. }
  49. BasicRHIComponent::OnFramePrepare(frameGraphBuilder);
  50. }
  51. void MultiGPUExampleComponent::FrameBeginInternal(AZ::RHI::FrameGraphBuilder& frameGraphBuilder)
  52. {
  53. if (m_outputWidth != m_imageWidth || m_outputHeight != m_imageHeight)
  54. {
  55. // Image used as color attachment only on first device
  56. {
  57. m_images[0] = aznew RHI::Image;
  58. RHI::ImageInitRequest initImageRequest;
  59. initImageRequest.m_image = m_images[0].get();
  60. initImageRequest.m_descriptor = RHI::ImageDescriptor::Create2D(
  61. RHI::ImageBindFlags::Color | RHI::ImageBindFlags::ShaderReadWrite, m_outputWidth, m_outputHeight, m_outputFormat);
  62. initImageRequest.m_deviceMask = m_deviceMask_1;
  63. m_imagePool->InitImage(initImageRequest);
  64. }
  65. // Image used as color attachment on both devices (rendered on device 1 and copied to device 0 for compositing)
  66. {
  67. m_images[1] = aznew RHI::Image;
  68. RHI::ImageInitRequest initImageRequest;
  69. initImageRequest.m_image = m_images[1].get();
  70. initImageRequest.m_descriptor = RHI::ImageDescriptor::Create2D(
  71. RHI::ImageBindFlags::Color | RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead | RHI::ImageBindFlags::CopyWrite, m_outputWidth, m_outputHeight, m_outputFormat);
  72. m_imagePool->InitImage(initImageRequest);
  73. }
  74. RHI::BufferBindFlags stagingBufferBindFlags{ RHI::BufferBindFlags::CopyWrite | RHI::BufferBindFlags::CopyRead };
  75. {
  76. m_stagingBufferToGPU = aznew RHI::Buffer;
  77. AZStd::vector<unsigned int> initialData(m_outputWidth * m_outputHeight, 0xFFFF00FFu);
  78. RHI::BufferInitRequest request;
  79. request.m_buffer = m_stagingBufferToGPU.get();
  80. request.m_descriptor = RHI::BufferDescriptor{stagingBufferBindFlags, initialData.size() * sizeof(unsigned int)};
  81. request.m_initialData = initialData.data();
  82. // This buffer is only necessary on device 0, but we test UpdateBufferDeviceMask below
  83. request.m_deviceMask = RHI::MultiDevice::AllDevices;
  84. if (m_stagingBufferPool->InitBuffer(request) != RHI::ResultCode::Success)
  85. {
  86. AZ_Error("MultiGPUExampleComponent", false, "StagingBufferToGPU was not created");
  87. }
  88. auto bufferViewDescriptor{ RHI::BufferViewDescriptor::CreateRaw(0, request.m_descriptor.m_byteCount) };
  89. auto bufferView = m_stagingBufferToGPU->BuildBufferView(bufferViewDescriptor);
  90. bufferView->GetDeviceBufferView(0);
  91. bufferView->GetDeviceBufferView(1);
  92. RHI::BufferDeviceMaskRequest updateRequest;
  93. updateRequest.m_buffer = m_stagingBufferToGPU.get();
  94. updateRequest.m_initialData = initialData.data();
  95. updateRequest.m_deviceMask = m_deviceMask_1;
  96. if (m_stagingBufferPool->UpdateBufferDeviceMask(updateRequest) != RHI::ResultCode::Success)
  97. {
  98. AZ_Error("MultiGPUExampleComponent", false, "StagingBufferToGPU was not created");
  99. }
  100. bufferView->GetDeviceBufferView(0);
  101. }
  102. {
  103. m_stagingBufferToCPU = aznew RHI::Buffer;
  104. RHI::BufferInitRequest request;
  105. request.m_buffer = m_stagingBufferToCPU.get();
  106. request.m_descriptor =
  107. RHI::BufferDescriptor{ stagingBufferBindFlags, m_outputWidth * m_outputHeight * sizeof(unsigned int) };
  108. // This buffer is necessary on device 1, but we test UpdateBufferDeviceMask below
  109. request.m_deviceMask = RHI::MultiDevice::NoDevices;
  110. if (m_stagingBufferPool->InitBuffer(request) != RHI::ResultCode::Success)
  111. {
  112. AZ_Error("MultiGPUExampleComponent", false, "StagingBufferToCPU was not created");
  113. }
  114. RHI::BufferDeviceMaskRequest updateRequest;
  115. updateRequest.m_buffer = m_stagingBufferToCPU.get();
  116. updateRequest.m_deviceMask = m_deviceMask_2;
  117. if (m_stagingBufferPool->UpdateBufferDeviceMask(updateRequest) != RHI::ResultCode::Success)
  118. {
  119. AZ_Error("MultiGPUExampleComponent", false, "StagingBufferToCPU was not created");
  120. }
  121. }
  122. m_scissors[0].m_minX = 0;
  123. m_scissors[0].m_minY = 0;
  124. m_scissors[0].m_maxX = m_outputWidth / 2 + 1;
  125. m_scissors[0].m_maxY = m_outputHeight;
  126. m_scissors[1].m_minX = m_outputWidth / 2;
  127. m_scissors[1].m_minY = 0;
  128. m_scissors[1].m_maxX = m_outputWidth;
  129. m_scissors[1].m_maxY = m_outputHeight;
  130. m_imageWidth = m_outputWidth;
  131. m_imageHeight = m_outputHeight;
  132. }
  133. frameGraphBuilder.GetAttachmentDatabase().ImportImage(
  134. m_imageAttachmentIds[0], m_images[0]);
  135. frameGraphBuilder.GetAttachmentDatabase().ImportImage(
  136. m_imageAttachmentIds[1], m_images[1]);
  137. frameGraphBuilder.GetAttachmentDatabase().ImportBuffer(
  138. m_bufferAttachmentIds[0], m_stagingBufferToCPU);
  139. frameGraphBuilder.GetAttachmentDatabase().ImportBuffer(
  140. m_bufferAttachmentIds[1], m_stagingBufferToGPU);
  141. RHI::DeviceBufferMapRequest request{};
  142. request.m_buffer = m_stagingBufferToCPU->GetDeviceBuffer(1).get();
  143. request.m_byteCount = m_imageWidth * m_imageHeight * sizeof(uint32_t);
  144. RHI::DeviceBufferMapResponse response{};
  145. m_stagingBufferPool->GetDeviceBufferPool(1)->MapBuffer(request, response);
  146. [[maybe_unused]] uint32_t* source = reinterpret_cast<uint32_t*>(response.m_data);
  147. request.m_buffer = m_stagingBufferToGPU->GetDeviceBuffer(0).get();
  148. m_stagingBufferPool->GetDeviceBufferPool(0)->MapBuffer(request, response);
  149. uint32_t* destination = reinterpret_cast<uint32_t*>(response.m_data);
  150. memcpy(destination, source, request.m_byteCount);
  151. m_stagingBufferPool->GetDeviceBufferPool(1)->UnmapBuffer(*m_stagingBufferToCPU->GetDeviceBuffer(1));
  152. m_stagingBufferPool->GetDeviceBufferPool(0)->UnmapBuffer(*m_stagingBufferToGPU->GetDeviceBuffer(0));
  153. }
  154. MultiGPUExampleComponent::MultiGPUExampleComponent()
  155. {
  156. m_supportRHISamplePipeline = true;
  157. }
  158. void MultiGPUExampleComponent::Activate()
  159. {
  160. AZ_Error("MultiGPUExampleComponent", RHI::RHISystemInterface::Get()->GetDeviceCount() >= 2, "At least 2 devices required to run this sample");
  161. m_device_1 = RHI::RHISystemInterface::Get()->GetDevice(0);
  162. m_device_2 = RHI::RHISystemInterface::Get()->GetDevice(1);
  163. m_deviceMask_1 = RHI::MultiDevice::DeviceMask{ 1u << 0 };
  164. m_deviceMask_2 = RHI::MultiDevice::DeviceMask{ 1u << 1 };
  165. m_deviceMask = m_deviceMask_1 | m_deviceMask_2;
  166. // Create multi-device resources
  167. RHI::ImageBindFlags imageBindFlags{ RHI::ImageBindFlags::Color | RHI::ImageBindFlags::ShaderReadWrite | RHI::ImageBindFlags::CopyRead | RHI::ImageBindFlags::CopyWrite };
  168. // ImagePool for both devices
  169. {
  170. m_imagePool = aznew RHI::ImagePool;
  171. m_imagePool->SetName(Name("MultiDeviceTexturePool"));
  172. RHI::ImagePoolDescriptor imagePoolDescriptor{};
  173. imagePoolDescriptor.m_bindFlags = imageBindFlags;
  174. imagePoolDescriptor.m_deviceMask = m_deviceMask;
  175. if (m_imagePool->Init(imagePoolDescriptor) != RHI::ResultCode::Success)
  176. {
  177. AZ_Error("MultiGPUExampleComponent", false, "Failed to initialize render texture image pool.");
  178. return;
  179. }
  180. }
  181. RHI::BufferBindFlags stagingBufferBindFlags{ RHI::BufferBindFlags::CopyWrite | RHI::BufferBindFlags::CopyRead };
  182. // Create staging buffer pool for buffer copy to the CPU and to GPU
  183. {
  184. m_stagingBufferPool = aznew RHI::BufferPool;
  185. RHI::BufferPoolDescriptor bufferPoolDesc;
  186. bufferPoolDesc.m_bindFlags = stagingBufferBindFlags;
  187. bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Host;
  188. bufferPoolDesc.m_hostMemoryAccess = RHI::HostMemoryAccess::Write;
  189. bufferPoolDesc.m_deviceMask = m_deviceMask;
  190. if (m_stagingBufferPool->Init(bufferPoolDesc) != RHI::ResultCode::Success)
  191. {
  192. AZ_Error("MultiGPUExampleComponent", false, "StagingBufferPool was not initialized");
  193. }
  194. }
  195. // Setup main and secondary pipeline
  196. CreateRenderScopeProducer();
  197. CreateCopyToCPUScopeProducer();
  198. CreateCopyToGPUScopeProducer();
  199. CreateCompositeScopeProducer();
  200. RHI::RHISystemNotificationBus::Handler::BusConnect();
  201. }
  202. void MultiGPUExampleComponent::Deactivate()
  203. {
  204. m_inputAssemblyBuffer = nullptr;
  205. m_inputAssemblyBufferPool = nullptr;
  206. m_pipelineState = nullptr;
  207. m_shaderResourceGroupShared = nullptr;
  208. m_stagingBufferPool = nullptr;
  209. m_stagingBufferToGPU = nullptr;
  210. m_stagingBufferToCPU = nullptr;
  211. m_inputAssemblyBufferComposite = nullptr;
  212. m_pipelineStateComposite = nullptr;
  213. m_shaderResourceGroupComposite = nullptr;
  214. m_shaderResourceGroupDataComposite = RHI::ShaderResourceGroupData{};
  215. m_shaderResourceGroupPoolComposite = nullptr;
  216. RHI::RHISystemNotificationBus::Handler::BusDisconnect();
  217. m_windowContext = nullptr;
  218. m_scopeProducers.clear();
  219. m_secondaryScopeProducers.clear();
  220. }
  221. void MultiGPUExampleComponent::CreateRenderScopeProducer()
  222. {
  223. RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor;
  224. {
  225. m_inputAssemblyBufferPool = aznew RHI::BufferPool;
  226. RHI::BufferPoolDescriptor bufferPoolDesc;
  227. bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::InputAssembly;
  228. bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
  229. bufferPoolDesc.m_deviceMask = m_deviceMask;
  230. m_inputAssemblyBufferPool->Init(bufferPoolDesc);
  231. BufferDataTrianglePass bufferData;
  232. SetVertexPosition(bufferData.m_positions.data(), 0, 0.0, 0.5, 0.0);
  233. SetVertexPosition(bufferData.m_positions.data(), 1, -0.5, -0.5, 0.0);
  234. SetVertexPosition(bufferData.m_positions.data(), 2, 0.5, -0.5, 0.0);
  235. SetVertexColor(bufferData.m_colors.data(), 0, 1.0, 0.0, 0.0, 1.0);
  236. SetVertexColor(bufferData.m_colors.data(), 1, 0.0, 1.0, 0.0, 1.0);
  237. SetVertexColor(bufferData.m_colors.data(), 2, 0.0, 0.0, 1.0, 1.0);
  238. SetVertexIndexIncreasing(bufferData.m_indices.data(), bufferData.m_indices.size());
  239. m_inputAssemblyBuffer = aznew RHI::Buffer;
  240. RHI::BufferInitRequest request;
  241. request.m_buffer = m_inputAssemblyBuffer.get();
  242. request.m_descriptor = RHI::BufferDescriptor{ RHI::BufferBindFlags::InputAssembly, sizeof(bufferData) };
  243. request.m_initialData = &bufferData;
  244. m_inputAssemblyBufferPool->InitBuffer(request);
  245. m_streamBufferViews[0] = { *m_inputAssemblyBuffer,
  246. offsetof(BufferDataTrianglePass, m_positions), sizeof(BufferDataTrianglePass::m_positions),
  247. sizeof(VertexPosition) };
  248. m_streamBufferViews[1] = { *m_inputAssemblyBuffer,
  249. offsetof(BufferDataTrianglePass, m_colors), sizeof(BufferDataTrianglePass::m_colors),
  250. sizeof(VertexColor) };
  251. RHI::InputStreamLayoutBuilder layoutBuilder;
  252. layoutBuilder.AddBuffer()->Channel("POSITION", RHI::Format::R32G32B32_FLOAT);
  253. layoutBuilder.AddBuffer()->Channel("COLOR", RHI::Format::R32G32B32A32_FLOAT);
  254. pipelineStateDescriptor.m_inputStreamLayout = layoutBuilder.End();
  255. RHI::ValidateStreamBufferViews(pipelineStateDescriptor.m_inputStreamLayout, m_streamBufferViews);
  256. }
  257. {
  258. const char* triangleShaderFilePath = "Shaders/RHI/triangle.azshader";
  259. const char* sampleName = "MultiGPUExample";
  260. auto shader = LoadShader(triangleShaderFilePath, sampleName);
  261. if (shader == nullptr)
  262. return;
  263. auto shaderOptionGroup = shader->CreateShaderOptionGroup();
  264. shaderOptionGroup.SetUnspecifiedToDefaultValues();
  265. // This is an example of how to set different shader options when searching for the shader variant you want to display
  266. // Searching by id is simple, but suboptimal. Here it's only used to demonstrate the principle,
  267. // but in practice the ShaderOptionIndex and the ShaderOptionValue should be cached for better performance
  268. // You can also try DrawMode::Green, DrawMode::Blue or DrawMode::White. The specified color will appear on top of the triangle.
  269. shaderOptionGroup.SetValue(AZ::Name("o_drawMode"), AZ::Name("DrawMode::Red"));
  270. auto shaderVariant = shader->GetVariant(shaderOptionGroup.GetShaderVariantId());
  271. shaderVariant.ConfigurePipelineState(pipelineStateDescriptor);
  272. RHI::RenderAttachmentLayoutBuilder attachmentsBuilder;
  273. attachmentsBuilder.AddSubpass()
  274. ->RenderTargetAttachment(m_outputFormat);
  275. [[maybe_unused]] RHI::ResultCode result = attachmentsBuilder.End(pipelineStateDescriptor.m_renderAttachmentConfiguration.m_renderAttachmentLayout);
  276. AZ_Assert(result == RHI::ResultCode::Success, "Failed to create render attachment layout");
  277. m_pipelineState = shader->AcquirePipelineState(pipelineStateDescriptor);
  278. if (!m_pipelineState)
  279. {
  280. AZ_Error(sampleName, false, "Failed to acquire default pipeline state for shader '%s'", triangleShaderFilePath);
  281. return;
  282. }
  283. m_shaderResourceGroupShared = CreateShaderResourceGroup(shader, "TriangleInstanceSrg", sampleName);
  284. const Name objectMatrixConstantId{ "m_objectMatrix" };
  285. FindShaderInputIndex(&m_objectMatrixConstantIndex, m_shaderResourceGroupShared, objectMatrixConstantId, sampleName);
  286. // In practice m_shaderResourceGroupShared should be one of the cached SRGs owned by the DrawItem
  287. if (!shaderVariant.IsFullyBaked() && m_shaderResourceGroupShared->HasShaderVariantKeyFallbackEntry())
  288. {
  289. // Normally if the requested variant isn't an exact match we have to set it by SetShaderVariantKeyFallbackValue
  290. // In most cases this should be the preferred behavior:
  291. m_shaderResourceGroupShared->SetShaderVariantKeyFallbackValue(shaderOptionGroup.GetShaderVariantKeyFallbackValue());
  292. AZ_Warning(
  293. sampleName, false, "Check the Triangle.shader file - some program variants haven't been baked ('%s')",
  294. triangleShaderFilePath);
  295. }
  296. }
  297. // Creates two scopes for rendering the halves of the triangle.
  298. {
  299. struct ScopeData
  300. {
  301. bool second{false};
  302. };
  303. const auto prepareFunction = [this](RHI::FrameGraphInterface frameGraph, [[maybe_unused]] ScopeData& scopeData)
  304. {
  305. // Binds the swap chain as a color attachment. Clears it to black.
  306. RHI::ImageScopeAttachmentDescriptor descriptor;
  307. descriptor.m_attachmentId = m_imageAttachmentIds[scopeData.second];
  308. descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Clear;
  309. descriptor.m_loadStoreAction.m_storeAction = RHI::AttachmentStoreAction::Store;
  310. descriptor.m_loadStoreAction.m_clearValue.m_vector4Uint = {0, 0, 0, 0};
  311. frameGraph.UseColorAttachment(descriptor);
  312. // We will submit a single draw item.
  313. frameGraph.SetEstimatedItemCount(1);
  314. };
  315. RHI::EmptyCompileFunction<ScopeData> compileFunction;
  316. const auto executeFunction = [this](const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
  317. {
  318. RHI::CommandList* commandList = context.GetCommandList();
  319. // Set persistent viewport and scissor state.
  320. commandList->SetViewports(&m_viewport, 1);
  321. commandList->SetScissors(&m_scissors[int(scopeData.second)], 1);
  322. const RHI::DeviceIndexBufferView indexBufferView = { *m_inputAssemblyBuffer->GetDeviceBuffer(context.GetDeviceIndex()),
  323. offsetof(BufferDataTrianglePass, m_indices),
  324. sizeof(BufferDataTrianglePass::m_indices), RHI::IndexFormat::Uint16 };
  325. RHI::DrawIndexed drawIndexed;
  326. drawIndexed.m_indexCount = 3;
  327. drawIndexed.m_instanceCount = 1;
  328. const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
  329. m_shaderResourceGroupShared->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
  330. };
  331. RHI::DeviceDrawItem drawItem;
  332. drawItem.m_arguments = drawIndexed;
  333. drawItem.m_pipelineState = m_pipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
  334. drawItem.m_indexBufferView = &indexBufferView;
  335. drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
  336. drawItem.m_shaderResourceGroups = shaderResourceGroups;
  337. drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_streamBufferViews.size());
  338. AZStd::array<RHI::DeviceStreamBufferView, 2> deviceStreamBufferViews{
  339. m_streamBufferViews[0].GetDeviceStreamBufferView(context.GetDeviceIndex()),
  340. m_streamBufferViews[1].GetDeviceStreamBufferView(context.GetDeviceIndex())
  341. };
  342. drawItem.m_streamBufferViews = deviceStreamBufferViews.data();
  343. // Submit the triangle draw item.
  344. commandList->Submit(drawItem);
  345. };
  346. m_scopeProducers.emplace_back(
  347. aznew
  348. RHI::ScopeProducerFunction<ScopeData, decltype(prepareFunction), decltype(compileFunction), decltype(executeFunction)>(
  349. RHI::ScopeId{ "MultiGPUTriangle0" }, ScopeData{}, prepareFunction, compileFunction, executeFunction, 0));
  350. m_scopeProducers.emplace_back(
  351. aznew
  352. RHI::ScopeProducerFunction<ScopeData, decltype(prepareFunction), decltype(compileFunction), decltype(executeFunction)>(
  353. RHI::ScopeId{ "MultiGPUTriangle1" }, ScopeData{true}, prepareFunction, compileFunction, executeFunction, 1));
  354. }
  355. }
  356. void MultiGPUExampleComponent::CreateCompositeScopeProducer()
  357. {
  358. BufferDataCompositePass bufferData;
  359. RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor;
  360. // Setup input assembly for fullscreen pass
  361. {
  362. SetFullScreenRect(bufferData.m_positions.data(), bufferData.m_uvs.data(), bufferData.m_indices.data());
  363. m_inputAssemblyBufferComposite = aznew RHI::Buffer;
  364. RHI::BufferInitRequest request;
  365. request.m_buffer = m_inputAssemblyBufferComposite.get();
  366. request.m_descriptor = RHI::BufferDescriptor{ RHI::BufferBindFlags::InputAssembly, sizeof(bufferData) };
  367. request.m_initialData = &bufferData;
  368. request.m_deviceMask = m_deviceMask_1;
  369. m_inputAssemblyBufferPool->InitBuffer(request);
  370. m_streamBufferViewsComposite[0] = { *m_inputAssemblyBufferComposite,
  371. offsetof(BufferDataCompositePass, m_positions),
  372. sizeof(BufferDataCompositePass::m_positions), sizeof(VertexPosition) };
  373. m_streamBufferViewsComposite[1] = { *m_inputAssemblyBufferComposite,
  374. offsetof(BufferDataCompositePass, m_uvs), sizeof(BufferDataCompositePass::m_uvs),
  375. sizeof(VertexUV) };
  376. RHI::InputStreamLayoutBuilder layoutBuilder;
  377. layoutBuilder.AddBuffer()->Channel("POSITION", RHI::Format::R32G32B32_FLOAT);
  378. layoutBuilder.AddBuffer()->Channel("UV", RHI::Format::R32G32_FLOAT);
  379. pipelineStateDescriptor.m_inputStreamLayout = layoutBuilder.End();
  380. RHI::ValidateStreamBufferViews(pipelineStateDescriptor.m_inputStreamLayout, m_streamBufferViewsComposite);
  381. }
  382. // Load shader and connect inputs
  383. {
  384. const char* compositeShaderFilePath = "Shaders/RHI/multigpucomposite.azshader";
  385. const char* sampleName = "MultiGPUExample";
  386. auto shader = LoadShader(compositeShaderFilePath, sampleName);
  387. if (shader == nullptr)
  388. {
  389. AZ_Error("MultiGPUExampleComponent", false, "Could not load shader");
  390. return;
  391. }
  392. auto shaderVariant = shader->GetVariant(RPI::ShaderAsset::RootShaderVariantStableId);
  393. shaderVariant.ConfigurePipelineState(pipelineStateDescriptor);
  394. RHI::RenderAttachmentLayoutBuilder attachmentsBuilder;
  395. attachmentsBuilder.AddSubpass()->RenderTargetAttachment(m_outputFormat);
  396. [[maybe_unused]] RHI::ResultCode result =
  397. attachmentsBuilder.End(pipelineStateDescriptor.m_renderAttachmentConfiguration.m_renderAttachmentLayout);
  398. AZ_Assert(result == RHI::ResultCode::Success, "Failed to create render attachment layout");
  399. m_pipelineStateComposite = shader->AcquirePipelineState(pipelineStateDescriptor);
  400. if (!m_pipelineStateComposite)
  401. {
  402. AZ_Error(sampleName, false, "Failed to acquire default pipeline state for shader '%s'", compositeShaderFilePath);
  403. return;
  404. }
  405. RHI::ShaderResourceGroupPoolDescriptor srgPoolDescriptor{};
  406. srgPoolDescriptor.m_layout = shader->GetAsset()->FindShaderResourceGroupLayout(AZ::Name { "CompositeSrg" }, shader->GetSupervariantIndex()).get();
  407. srgPoolDescriptor.m_deviceMask = m_deviceMask_1;
  408. m_shaderResourceGroupPoolComposite = aznew RHI::ShaderResourceGroupPool;
  409. m_shaderResourceGroupPoolComposite->Init(srgPoolDescriptor);
  410. m_shaderResourceGroupComposite = aznew RHI::ShaderResourceGroup;
  411. m_shaderResourceGroupPoolComposite->InitGroup(*m_shaderResourceGroupComposite);
  412. m_shaderResourceGroupDataComposite = RHI::ShaderResourceGroupData{*m_shaderResourceGroupPoolComposite};
  413. {
  414. const AZ::Name inputTextureShaderInput{ "m_inputTextureLeft" };
  415. m_textureInputIndices[0] = srgPoolDescriptor.m_layout->FindShaderInputImageIndex(inputTextureShaderInput);
  416. }
  417. {
  418. const AZ::Name inputTextureShaderInput{ "m_inputTextureRight" };
  419. m_textureInputIndices[1] = srgPoolDescriptor.m_layout->FindShaderInputImageIndex(inputTextureShaderInput);
  420. }
  421. }
  422. // Setup ScopeProducer
  423. {
  424. struct ScopeData
  425. {
  426. };
  427. const auto prepareFunction = [this](RHI::FrameGraphInterface frameGraph, [[maybe_unused]] ScopeData& scopeData)
  428. {
  429. {
  430. RHI::ImageScopeAttachmentDescriptor descriptor{};
  431. descriptor.m_attachmentId = m_imageAttachmentIds[0];
  432. descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
  433. descriptor.m_loadStoreAction.m_storeAction = RHI::AttachmentStoreAction::DontCare;
  434. frameGraph.UseShaderAttachment(descriptor, RHI::ScopeAttachmentAccess::Read, RHI::ScopeAttachmentStage::FragmentShader);
  435. }
  436. {
  437. RHI::ImageScopeAttachmentDescriptor descriptor{};
  438. descriptor.m_attachmentId = m_imageAttachmentIds[1];
  439. descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
  440. descriptor.m_loadStoreAction.m_storeAction = RHI::AttachmentStoreAction::DontCare;
  441. frameGraph.UseShaderAttachment(descriptor, RHI::ScopeAttachmentAccess::Read, RHI::ScopeAttachmentStage::FragmentShader);
  442. }
  443. {
  444. RHI::ImageScopeAttachmentDescriptor desc{};
  445. desc.m_attachmentId = m_outputAttachmentId;
  446. frameGraph.UseColorAttachment(desc);
  447. }
  448. frameGraph.SetEstimatedItemCount(1);
  449. };
  450. const auto compileFunction = [this](const RHI::FrameGraphCompileContext& context, [[maybe_unused]] const ScopeData& scopeData)
  451. {
  452. m_shaderResourceGroupDataComposite.SetImageView(m_textureInputIndices[0], context.GetImageView(m_imageAttachmentIds[0]));
  453. m_shaderResourceGroupDataComposite.SetImageView(m_textureInputIndices[1], context.GetImageView(m_imageAttachmentIds[1]));
  454. m_shaderResourceGroupComposite->Compile(m_shaderResourceGroupDataComposite);
  455. };
  456. const auto executeFunction = [=](const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
  457. {
  458. RHI::CommandList* commandList = context.GetCommandList();
  459. commandList->SetViewports(&m_viewport, 1);
  460. commandList->SetScissors(&m_scissor, 1);
  461. const RHI::DeviceIndexBufferView indexBufferView = {
  462. *m_inputAssemblyBufferComposite->GetDeviceBuffer(context.GetDeviceIndex()),
  463. offsetof(BufferDataCompositePass, m_indices), sizeof(BufferDataCompositePass::m_indices), RHI::IndexFormat::Uint16
  464. };
  465. RHI::DrawIndexed drawIndexed;
  466. drawIndexed.m_indexCount = 6;
  467. drawIndexed.m_instanceCount = 1;
  468. const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
  469. m_shaderResourceGroupComposite->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
  470. };
  471. RHI::DeviceDrawItem drawItem;
  472. drawItem.m_arguments = drawIndexed;
  473. drawItem.m_pipelineState = m_pipelineStateComposite->GetDevicePipelineState(context.GetDeviceIndex()).get();
  474. drawItem.m_indexBufferView = &indexBufferView;
  475. drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
  476. drawItem.m_shaderResourceGroups = shaderResourceGroups;
  477. drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_streamBufferViewsComposite.size());
  478. AZStd::array<RHI::DeviceStreamBufferView, 2> deviceStreamBufferViews{
  479. m_streamBufferViewsComposite[0].GetDeviceStreamBufferView(context.GetDeviceIndex()),
  480. m_streamBufferViewsComposite[1].GetDeviceStreamBufferView(context.GetDeviceIndex())
  481. };
  482. drawItem.m_streamBufferViews = deviceStreamBufferViews.data();
  483. commandList->Submit(drawItem);
  484. };
  485. m_scopeProducers.emplace_back(
  486. aznew
  487. RHI::ScopeProducerFunction<ScopeData, decltype(prepareFunction), decltype(compileFunction), decltype(executeFunction)>(
  488. RHI::ScopeId{ "MultiGPUComposite" }, ScopeData{}, prepareFunction, compileFunction, executeFunction));
  489. }
  490. }
  491. void MultiGPUExampleComponent::CreateCopyToGPUScopeProducer()
  492. {
  493. struct ScopeData
  494. {
  495. };
  496. const auto prepareFunction = [this]([[maybe_unused]] RHI::FrameGraphInterface frameGraph, [[maybe_unused]] ScopeData& scopeData)
  497. {
  498. {
  499. RHI::BufferScopeAttachmentDescriptor descriptor{};
  500. descriptor.m_attachmentId = m_bufferAttachmentIds[1];
  501. descriptor.m_bufferViewDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, static_cast<uint32_t>(m_stagingBufferToGPU->GetDescriptor().m_byteCount));
  502. descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
  503. descriptor.m_loadStoreAction.m_storeAction = RHI::AttachmentStoreAction::DontCare;
  504. frameGraph.UseCopyAttachment(descriptor, RHI::ScopeAttachmentAccess::Read);
  505. }
  506. {
  507. RHI::ImageScopeAttachmentDescriptor descriptor{};
  508. descriptor.m_attachmentId = m_imageAttachmentIds[1];
  509. descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::DontCare;
  510. descriptor.m_loadStoreAction.m_storeAction = RHI::AttachmentStoreAction::Store;
  511. frameGraph.UseCopyAttachment(descriptor, RHI::ScopeAttachmentAccess::Write);
  512. }
  513. };
  514. const auto compileFunction = []([[maybe_unused]] const RHI::FrameGraphCompileContext& context, [[maybe_unused]] const ScopeData& scopeData)
  515. {
  516. };
  517. const auto executeFunction = [this](const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
  518. {
  519. RHI::DeviceCopyBufferToImageDescriptor copyDescriptor{};
  520. copyDescriptor.m_sourceBuffer = m_stagingBufferToGPU->GetDeviceBuffer(context.GetDeviceIndex()).get();
  521. copyDescriptor.m_sourceOffset = 0;
  522. copyDescriptor.m_sourceBytesPerRow = m_imageWidth * sizeof(uint32_t);
  523. copyDescriptor.m_sourceBytesPerImage = static_cast<uint32_t>(m_stagingBufferToGPU->GetDescriptor().m_byteCount);
  524. copyDescriptor.m_sourceSize = RHI::Size{ m_imageWidth, m_imageHeight, 1 };
  525. copyDescriptor.m_destinationImage = m_images[1]->GetDeviceImage(context.GetDeviceIndex()).get();
  526. RHI::DeviceCopyItem copyItem(copyDescriptor);
  527. context.GetCommandList()->Submit(copyItem);
  528. };
  529. m_scopeProducers.emplace_back(
  530. aznew RHI::ScopeProducerFunction<ScopeData, decltype(prepareFunction), decltype(compileFunction), decltype(executeFunction)>(
  531. RHI::ScopeId{ "MultiGPUCopyToGPU" }, ScopeData{}, prepareFunction, compileFunction, executeFunction));
  532. }
  533. void MultiGPUExampleComponent::CreateCopyToCPUScopeProducer()
  534. {
  535. struct ScopeData
  536. {
  537. };
  538. const auto prepareFunction = [this]([[maybe_unused]] RHI::FrameGraphInterface frameGraph, [[maybe_unused]] ScopeData& scopeData)
  539. {
  540. {
  541. RHI::BufferScopeAttachmentDescriptor descriptor{};
  542. descriptor.m_attachmentId = m_bufferAttachmentIds[0];
  543. descriptor.m_bufferViewDescriptor = RHI::BufferViewDescriptor::CreateRaw(0, static_cast<uint32_t>(m_stagingBufferToCPU->GetDescriptor().m_byteCount));
  544. descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::DontCare;
  545. descriptor.m_loadStoreAction.m_storeAction = RHI::AttachmentStoreAction::Store;
  546. frameGraph.UseCopyAttachment(descriptor, RHI::ScopeAttachmentAccess::Write);
  547. }
  548. {
  549. RHI::ImageScopeAttachmentDescriptor descriptor{};
  550. descriptor.m_attachmentId = m_imageAttachmentIds[1];
  551. descriptor.m_loadStoreAction.m_loadAction = RHI::AttachmentLoadAction::Load;
  552. descriptor.m_loadStoreAction.m_storeAction = RHI::AttachmentStoreAction::DontCare;
  553. frameGraph.UseCopyAttachment(descriptor, RHI::ScopeAttachmentAccess::Read);
  554. }
  555. };
  556. const auto compileFunction = []([[maybe_unused]] const RHI::FrameGraphCompileContext& context, [[maybe_unused]] const ScopeData& scopeData)
  557. {
  558. };
  559. const auto executeFunction = [this](const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
  560. {
  561. RHI::DeviceCopyImageToBufferDescriptor copyDescriptor{};
  562. copyDescriptor.m_sourceImage = m_images[1]->GetDeviceImage(context.GetDeviceIndex()).get();
  563. copyDescriptor.m_sourceSize = RHI::Size{ m_imageWidth, m_imageHeight, 1 };
  564. copyDescriptor.m_destinationBuffer = m_stagingBufferToCPU->GetDeviceBuffer(context.GetDeviceIndex()).get();
  565. copyDescriptor.m_destinationOffset = 0;
  566. copyDescriptor.m_destinationBytesPerRow = m_imageWidth * sizeof(uint32_t);
  567. copyDescriptor.m_destinationBytesPerImage = static_cast<uint32_t>(m_stagingBufferToCPU->GetDescriptor().m_byteCount);
  568. copyDescriptor.m_destinationFormat = m_outputFormat;
  569. RHI::DeviceCopyItem copyItem(copyDescriptor);
  570. context.GetCommandList()->Submit(copyItem);
  571. };
  572. m_scopeProducers.emplace_back(
  573. aznew RHI::ScopeProducerFunction<ScopeData, decltype(prepareFunction), decltype(compileFunction), decltype(executeFunction)>(
  574. RHI::ScopeId{ "MultiGPUCopyToCPU" }, ScopeData{}, prepareFunction, compileFunction, executeFunction, 1));
  575. }
  576. } // namespace AtomSampleViewer