CopyQueueComponent.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  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/CopyQueueComponent.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/Image/ImageSystemInterface.h>
  15. #include <Atom/RPI.Public/Image/StreamingImage.h>
  16. #include <Atom/RPI.Public/Image/StreamingImagePool.h>
  17. #include <Atom/RPI.Public/Shader/Shader.h>
  18. #include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
  19. #include <AzCore/Serialization/SerializeContext.h>
  20. namespace AtomSampleViewer
  21. {
  22. void CopyQueueComponent::Reflect(AZ::ReflectContext* context)
  23. {
  24. if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  25. {
  26. serializeContext->Class<CopyQueueComponent, AZ::Component>()
  27. ->Version(0)
  28. ;
  29. }
  30. }
  31. CopyQueueComponent::CopyQueueComponent()
  32. {
  33. m_supportRHISamplePipeline = true;
  34. }
  35. void CopyQueueComponent::OnFramePrepare(AZ::RHI::FrameGraphBuilder& frameGraphBuilder)
  36. {
  37. m_processingState.m_time += ProcessingState::TickAmount;
  38. m_processingState.m_timeUntilChange -= ProcessingState::TickAmount;
  39. if (m_processingState.m_timeUntilChange < 0.0f)
  40. {
  41. m_shaderResourceGroup->Compile();
  42. int nextTexture = m_processingState.m_changeCount % ProcessingState::TextureCount;
  43. if (!m_shaderResourceGroup->SetImage(m_textureInputIndex, m_images[nextTexture]))
  44. {
  45. AZ_Error("CopyQueueExample", false, "Failed to set image into shader resource group");
  46. return;
  47. };
  48. UpdateVertexPositions(m_processingState.m_time);
  49. UploadVertexBuffer();
  50. m_processingState.m_timeUntilChange = ProcessingState::ChangeDelay;
  51. m_processingState.m_changeCount++;
  52. }
  53. BasicRHIComponent::OnFramePrepare(frameGraphBuilder);
  54. }
  55. void CopyQueueComponent::Activate()
  56. {
  57. using namespace AZ;
  58. const RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
  59. m_bufferData = BufferData();
  60. const auto positionBufSize = static_cast<uint32_t>(m_bufferData.m_positions.size() * sizeof(VertexPosition));
  61. const auto indexBufSize = static_cast<uint32_t>(m_bufferData.m_indices.size() * sizeof(uint16_t));
  62. const auto uvBufSize = static_cast<uint32_t>(m_bufferData.m_uvs.size() * sizeof(VertexUV));
  63. AZ::RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor;
  64. {
  65. m_bufferPool = RHI::Factory::Get().CreateBufferPool();
  66. RHI::BufferPoolDescriptor bufferPoolDesc;
  67. bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::InputAssembly;
  68. bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
  69. m_bufferPool->Init(*device, bufferPoolDesc);
  70. UpdateVertexPositions(0);
  71. m_bufferData.m_indices = { { 0, 3, 1, 1, 3, 2 } };
  72. SetVertexUV(m_bufferData.m_uvs.data(), 0, 0.0f, 0.0f);
  73. SetVertexUV(m_bufferData.m_uvs.data(), 1, 0.0f, 1.0f);
  74. SetVertexUV(m_bufferData.m_uvs.data(), 2, 1.0f, 1.0f);
  75. SetVertexUV(m_bufferData.m_uvs.data(), 3, 1.0f, 0.0f);
  76. m_positionBuffer = RHI::Factory::Get().CreateBuffer();
  77. m_indexBuffer = RHI::Factory::Get().CreateBuffer();
  78. m_uvBuffer = RHI::Factory::Get().CreateBuffer();
  79. RHI::ResultCode result = RHI::ResultCode::Success;
  80. RHI::BufferInitRequest request;
  81. request.m_buffer = m_indexBuffer.get();
  82. request.m_descriptor = RHI::BufferDescriptor{ RHI::BufferBindFlags::InputAssembly, indexBufSize };
  83. request.m_initialData = m_bufferData.m_indices.data();
  84. result = m_bufferPool->InitBuffer(request);
  85. if (result != RHI::ResultCode::Success)
  86. {
  87. AZ_Error("CopyQueueExample", false, "Failed to initialize index buffer with error code %d", result);
  88. return;
  89. }
  90. // We specifically make the position buffer *not* the first one, to test a specific failure that
  91. // can occur in AsyncUploadQueue: when we call BufferPool::StreamBuffer to update the position
  92. // buffer, AsyncUploadQueue::QueueUpload needs to account for paged allocation offsets.
  93. request.m_buffer = m_positionBuffer.get();
  94. request.m_descriptor = RHI::BufferDescriptor{ RHI::BufferBindFlags::InputAssembly, positionBufSize };
  95. request.m_initialData = m_bufferData.m_positions.data();
  96. result = m_bufferPool->InitBuffer(request);
  97. if (result != RHI::ResultCode::Success)
  98. {
  99. AZ_Error("CopyQueueExample", false, "Failed to initialize position buffer with error code %d", result);
  100. return;
  101. }
  102. request.m_buffer = m_uvBuffer.get();
  103. request.m_descriptor = RHI::BufferDescriptor{ RHI::BufferBindFlags::InputAssembly, uvBufSize };
  104. request.m_initialData = m_bufferData.m_uvs.data();
  105. result = m_bufferPool->InitBuffer(request);
  106. if (result != RHI::ResultCode::Success)
  107. {
  108. AZ_Error("CopyQueueExample", false, "Failed to initialize uv buffer with error code %d", result);
  109. return;
  110. }
  111. m_streamBufferViews[0] = {
  112. *m_positionBuffer,
  113. 0,
  114. positionBufSize,
  115. sizeof(VertexPosition)
  116. };
  117. m_streamBufferViews[1] = {
  118. *m_uvBuffer,
  119. 0,
  120. uvBufSize,
  121. sizeof(VertexUV)
  122. };
  123. RHI::InputStreamLayoutBuilder layoutBuilder;
  124. layoutBuilder.AddBuffer()->Channel("POSITION", RHI::Format::R32G32B32_FLOAT);
  125. layoutBuilder.AddBuffer()->Channel("UV", RHI::Format::R32G32_FLOAT);
  126. pipelineStateDescriptor.m_inputStreamLayout = layoutBuilder.End();
  127. RHI::ValidateStreamBufferViews(pipelineStateDescriptor.m_inputStreamLayout, m_streamBufferViews);
  128. }
  129. {
  130. const char* copyQueueShaderFilePath = "Shaders/RHI/CopyQueue.azshader";
  131. const char* sampleName = "CopyQueueExample";
  132. auto shader = LoadShader(copyQueueShaderFilePath, sampleName);
  133. if (shader == nullptr)
  134. return;
  135. auto shaderVariant = shader->GetVariant(RPI::ShaderAsset::RootShaderVariantStableId);
  136. shaderVariant.ConfigurePipelineState(pipelineStateDescriptor);
  137. RHI::RenderAttachmentLayoutBuilder attachmentsBuilder;
  138. attachmentsBuilder.AddSubpass()
  139. ->RenderTargetAttachment(m_outputFormat);
  140. [[maybe_unused]] RHI::ResultCode result = attachmentsBuilder.End(pipelineStateDescriptor.m_renderAttachmentConfiguration.m_renderAttachmentLayout);
  141. AZ_Assert(result == RHI::ResultCode::Success, "Failed to create render attachment layout");
  142. m_pipelineState = shader->AcquirePipelineState(pipelineStateDescriptor);
  143. if (!m_pipelineState)
  144. {
  145. AZ_Error(sampleName, false, "Failed to acquire default pipeline state for shader '%s'", copyQueueShaderFilePath);
  146. return;
  147. }
  148. m_shaderResourceGroup = CreateShaderResourceGroup(shader, "CopyQueueSrg", sampleName);
  149. FindShaderInputIndex(&m_textureInputIndex, m_shaderResourceGroup, AZ::Name{"m_texture"}, sampleName);
  150. for (int index = 0; index < numberOfPaths; index++)
  151. {
  152. UploadTextureAsAsset(m_filePaths[index], index);
  153. }
  154. if (!m_shaderResourceGroup->SetImage(m_textureInputIndex, m_images[0]))
  155. {
  156. AZ_Error(sampleName, false, "Failed to set image into shader resource group");
  157. return;
  158. }
  159. }
  160. {
  161. struct ScopeData
  162. {
  163. //UserDataParam - Empty for this samples
  164. };
  165. const auto prepareFunction = [this](RHI::FrameGraphInterface frameGraph, [[maybe_unused]] const ScopeData& scopeData)
  166. {
  167. {
  168. RHI::ImageScopeAttachmentDescriptor desc;
  169. desc.m_attachmentId = m_outputAttachmentId;
  170. frameGraph.UseColorAttachment(desc);
  171. }
  172. frameGraph.SetEstimatedItemCount(1);
  173. };
  174. RHI::EmptyCompileFunction<ScopeData> compileFunction;
  175. const auto executeFunction = [=](const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
  176. {
  177. RHI::CommandList* commandList = context.GetCommandList();
  178. commandList->SetViewports(&m_viewport, 1);
  179. commandList->SetScissors(&m_scissor, 1);
  180. const RHI::IndexBufferView indexBufferView =
  181. {
  182. *m_indexBuffer,
  183. 0,
  184. indexBufSize,
  185. RHI::IndexFormat::Uint16
  186. };
  187. RHI::DrawIndexed drawIndexed;
  188. drawIndexed.m_indexCount = 6;
  189. drawIndexed.m_instanceCount = 1;
  190. const RHI::ShaderResourceGroup* shaderResourceGroups[] = { m_shaderResourceGroup->GetRHIShaderResourceGroup() };
  191. RHI::DrawItem drawItem;
  192. drawItem.m_arguments = drawIndexed;
  193. drawItem.m_pipelineState = m_pipelineState.get();
  194. drawItem.m_indexBufferView = &indexBufferView;
  195. drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
  196. drawItem.m_shaderResourceGroups = shaderResourceGroups;
  197. drawItem.m_streamBufferViewCount = static_cast<uint8_t>(m_streamBufferViews.size());
  198. drawItem.m_streamBufferViews = m_streamBufferViews.data();
  199. commandList->Submit(drawItem);
  200. };
  201. m_scopeProducers.emplace_back(aznew RHI::ScopeProducerFunction<
  202. ScopeData,
  203. decltype(prepareFunction),
  204. decltype(compileFunction),
  205. decltype(executeFunction)>(
  206. RHI::ScopeId{"CopyQueue"},
  207. ScopeData{},
  208. prepareFunction,
  209. compileFunction,
  210. executeFunction));
  211. }
  212. m_processingState = ProcessingState{};
  213. AZ::RHI::RHISystemNotificationBus::Handler::BusConnect();
  214. }
  215. void CopyQueueComponent::Deactivate()
  216. {
  217. m_positionBuffer = nullptr;
  218. m_indexBuffer = nullptr;
  219. m_uvBuffer = nullptr;
  220. m_bufferPool = nullptr;
  221. m_pipelineState = nullptr;
  222. m_shaderResourceGroup = nullptr;
  223. AZ::RHI::RHISystemNotificationBus::Handler::BusDisconnect();
  224. m_windowContext = nullptr;
  225. m_scopeProducers.clear();
  226. }
  227. void CopyQueueComponent::UploadTextureAsAsset(const char* filePath, int index)
  228. {
  229. using namespace AZ;
  230. // Load a texture asset from the cache
  231. Data::AssetId streamingImageAssetId;
  232. Data::AssetCatalogRequestBus::BroadcastResult(
  233. streamingImageAssetId, &Data::AssetCatalogRequestBus::Events::GetAssetIdByPath,
  234. filePath, azrtti_typeid<RPI::StreamingImageAsset>(), false);
  235. if (!streamingImageAssetId.IsValid())
  236. {
  237. AZ_Error("CopyQueueExample", false, "Failed to get shader asset id with path %s", filePath);
  238. return;
  239. }
  240. auto streamingImageAsset = Data::AssetManager::Instance().GetAsset<RPI::StreamingImageAsset>(
  241. streamingImageAssetId, AZ::Data::AssetLoadBehavior::PreLoad);
  242. streamingImageAsset.BlockUntilLoadComplete();
  243. if (!streamingImageAsset.IsReady())
  244. {
  245. AZ_Error("CopyQueueExample", false, "Failed to get asset '%s'", filePath);
  246. return;
  247. }
  248. auto image = RPI::StreamingImage::FindOrCreate(streamingImageAsset);
  249. if (!image)
  250. {
  251. AZ_Error("CopyQueueExample", false, "Failed to find or create an image instance from from image asset '%s'", filePath);
  252. return;
  253. }
  254. m_images[index] = image;
  255. }
  256. void CopyQueueComponent::UpdateVertexPositions(float timeValue)
  257. {
  258. float scale = 1.0f + sin(timeValue) * 0.1f;
  259. SetVertexPosition(m_bufferData.m_positions.data(), 0, scale * AZ::Vector3(-0.5f, -0.5f, 0.0f));
  260. SetVertexPosition(m_bufferData.m_positions.data(), 1, scale * AZ::Vector3(-0.5f, 0.5f, 0.0f));
  261. SetVertexPosition(m_bufferData.m_positions.data(), 2, scale * AZ::Vector3(0.5f, 0.5f, 0.0f));
  262. SetVertexPosition(m_bufferData.m_positions.data(), 3, scale * AZ::Vector3(0.5f, -0.5f, 0.0f));
  263. }
  264. void CopyQueueComponent::UploadVertexBuffer()
  265. {
  266. AZ::RHI::BufferStreamRequest request;
  267. request.m_buffer = m_positionBuffer.get();
  268. request.m_byteCount = static_cast<uint32_t>(m_bufferData.m_positions.size() * sizeof(VertexPosition));
  269. request.m_sourceData = m_bufferData.m_positions.data();
  270. AZ::RHI::ResultCode resultCode = m_bufferPool->StreamBuffer(request);
  271. if (resultCode != AZ::RHI::ResultCode::Success)
  272. {
  273. AZ_Error("CopyQueueExample", false, "UploadVertexBuffer() failed to stream buffer contents to GPU.");
  274. }
  275. }
  276. } // namespace AtomSampleViewer