XRExampleComponent.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  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/XRExampleComponent.h>
  9. #include <Atom/RHI/CommandList.h>
  10. #include <Atom/RHI.Reflect/InputStreamLayoutBuilder.h>
  11. #include <Atom/RHI.Reflect/RenderAttachmentLayoutBuilder.h>
  12. #include <Atom/RPI.Public/Shader/Shader.h>
  13. #include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
  14. #include <AzCore/Math/Color.h>
  15. #include <AzCore/Serialization/SerializeContext.h>
  16. #include <SampleComponentManager.h>
  17. #include <Utils/Utils.h>
  18. namespace AtomSampleViewer
  19. {
  20. void XRExampleComponent::Reflect(AZ::ReflectContext* context)
  21. {
  22. if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  23. {
  24. serializeContext->Class<XRExampleComponent, AZ::Component>()->Version(0);
  25. }
  26. }
  27. XRExampleComponent::XRExampleComponent()
  28. {
  29. m_supportRHISamplePipeline = true;
  30. }
  31. void XRExampleComponent::Activate()
  32. {
  33. if (!AZ::RPI::RPISystemInterface::Get()->GetXRSystem())
  34. {
  35. return;
  36. }
  37. m_depthStencilID = AZ::RHI::AttachmentId{ AZStd::string::format("DepthStencilID_%llu", GetId()) };
  38. CreateCubeInputAssemblyBuffer();
  39. CreateCubePipeline();
  40. CreateScope();
  41. AZ::RHI::RHISystemNotificationBus::Handler::BusConnect();
  42. AZ::TickBus::Handler::BusConnect();
  43. AZ::RPI::XRSpaceNotificationBus::Handler::BusConnect();
  44. }
  45. void XRExampleComponent::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
  46. {
  47. m_time += deltaTime;
  48. }
  49. void XRExampleComponent::OnXRSpaceLocationsChanged(
  50. const AZ::Transform& baseSpaceToHeadTm, const AZ::Transform& headToLeftEyeTm, const AZ::Transform& headToRightEyeTm)
  51. {
  52. AZ::RPI::XRRenderingInterface* xrSystem = AZ::RPI::RPISystemInterface::Get()->GetXRSystem();
  53. if (!xrSystem || !xrSystem->ShouldRender())
  54. {
  55. return;
  56. }
  57. m_baseSpaceToHeadTm = baseSpaceToHeadTm;
  58. const AZ::Transform eyeWorldZupTm = (m_viewIndex == 0)
  59. ? baseSpaceToHeadTm * headToLeftEyeTm // This is true for both: main pipeline and left eye pipeline.
  60. : baseSpaceToHeadTm * headToRightEyeTm; // Right eye pipeline.
  61. // When rendering, the camera Up vector should be Y+, and the forward vector is Z-
  62. static const AZ::Transform zUpToYUp = AZ::Transform::CreateRotationX(AZ::Constants::HalfPi);
  63. const auto eyeWorldYupTm = eyeWorldZupTm * zUpToYUp;
  64. AZ::RPI::FovData fovData;
  65. [[maybe_unused]] AZ::RHI::ResultCode resultCode = xrSystem->GetViewFov(m_viewIndex, fovData);
  66. static const float clip_near = 0.05f;
  67. static const float clip_far = 100.0f;
  68. bool reverseDepth = false;
  69. const auto projectionMat44 = xrSystem->CreateStereoscopicProjection(
  70. fovData.m_angleLeft, fovData.m_angleRight, fovData.m_angleDown, fovData.m_angleUp, clip_near, clip_far, reverseDepth);
  71. AZ::Matrix4x4 viewMat = AZ::Matrix4x4::CreateFromTransform(eyeWorldYupTm.GetInverse());
  72. m_viewProjMatrix = projectionMat44 * viewMat;
  73. }
  74. void XRExampleComponent::OnFramePrepare(AZ::RHI::FrameGraphBuilder& frameGraphBuilder)
  75. {
  76. AZ::RPI::XRRenderingInterface* xrSystem = AZ::RPI::RPISystemInterface::Get()->GetXRSystem();
  77. if (xrSystem && xrSystem->ShouldRender())
  78. {
  79. const AZ::Matrix4x4 initialScaleMat = AZ::Matrix4x4::CreateScale(AZ::Vector3(0.1f, 0.1f, 0.1f));
  80. AZ::RPI::PoseData frontViewPoseData;
  81. //Model matrix for the cube related to the front view
  82. [[maybe_unused]] AZ::RHI::ResultCode resultCode = xrSystem->GetViewFrontPose(frontViewPoseData);
  83. m_modelMatrices[0] =
  84. AZ::Matrix4x4::CreateFromQuaternionAndTranslation(frontViewPoseData.m_orientation, frontViewPoseData.m_position) *
  85. initialScaleMat;
  86. AZ::Transform controllerLeftTm;
  87. resultCode = xrSystem->GetControllerTransform(0, controllerLeftTm);
  88. m_modelMatrices[1] = AZ::Matrix4x4::CreateFromTransform(m_baseSpaceToHeadTm * controllerLeftTm) * initialScaleMat;
  89. AZ::Transform controllerRightTm;
  90. resultCode = xrSystem->GetControllerTransform(1, controllerRightTm);
  91. m_modelMatrices[2] = AZ::Matrix4x4::CreateFromTransform(m_baseSpaceToHeadTm * controllerRightTm) * initialScaleMat;
  92. }
  93. for (int i = 0; i < NumberOfCubes; ++i)
  94. {
  95. m_shaderResourceGroups[i]->SetConstant(m_shaderIndexWorldMat, m_modelMatrices[i]);
  96. m_shaderResourceGroups[i]->SetConstant(m_shaderIndexViewProj, m_viewProjMatrix);
  97. m_shaderResourceGroups[i]->Compile();
  98. }
  99. BasicRHIComponent::OnFramePrepare(frameGraphBuilder);
  100. }
  101. XRExampleComponent::SingleCubeBufferData XRExampleComponent::CreateSingleCubeBufferData()
  102. {
  103. const AZStd::fixed_vector<AZ::Color, GeometryVertexCount> vertexColor =
  104. {
  105. //Top Face
  106. AZ::Colors::DarkBlue, AZ::Colors::DarkBlue, AZ::Colors::DarkBlue, AZ::Colors::DarkBlue,
  107. //bottom Face
  108. AZ::Colors::Blue, AZ::Colors::Blue, AZ::Colors::Blue, AZ::Colors::Blue,
  109. //Left Face
  110. AZ::Colors::DarkGreen, AZ::Colors::DarkGreen, AZ::Colors::DarkGreen, AZ::Colors::DarkGreen,
  111. //Right Face
  112. AZ::Colors::Green, AZ::Colors::Green, AZ::Colors::Green, AZ::Colors::Green,
  113. //Front Face
  114. AZ::Colors::DarkRed, AZ::Colors::DarkRed, AZ::Colors::DarkRed, AZ::Colors::DarkRed,
  115. //Back Face
  116. AZ::Colors::Red, AZ::Colors::Red, AZ::Colors::Red, AZ::Colors::Red,
  117. };
  118. // Create vertices, colors and normals for a cube and a plane
  119. SingleCubeBufferData bufferData;
  120. {
  121. const AZStd::fixed_vector<AZ::Vector3, GeometryVertexCount> vertices =
  122. {
  123. //Top Face
  124. AZ::Vector3(1.0, 1.0, 1.0), AZ::Vector3(-1.0, 1.0, 1.0), AZ::Vector3(-1.0, -1.0, 1.0), AZ::Vector3(1.0, -1.0, 1.0),
  125. //Bottom Face
  126. AZ::Vector3(1.0, 1.0, -1.0), AZ::Vector3(-1.0, 1.0, -1.0), AZ::Vector3(-1.0, -1.0, -1.0), AZ::Vector3(1.0, -1.0, -1.0),
  127. //Left Face
  128. AZ::Vector3(-1.0, 1.0, 1.0), AZ::Vector3(-1.0, -1.0, 1.0), AZ::Vector3(-1.0, -1.0, -1.0), AZ::Vector3(-1.0, 1.0, -1.0),
  129. //Right Face
  130. AZ::Vector3(1.0, 1.0, 1.0), AZ::Vector3(1.0, -1.0, 1.0), AZ::Vector3(1.0, -1.0, -1.0), AZ::Vector3(1.0, 1.0, -1.0),
  131. //Front Face
  132. AZ::Vector3(1.0, 1.0, 1.0), AZ::Vector3(-1.0, 1.0, 1.0), AZ::Vector3(-1.0, 1.0, -1.0), AZ::Vector3(1.0, 1.0, -1.0),
  133. //Back Face
  134. AZ::Vector3(1.0, -1.0, 1.0), AZ::Vector3(-1.0, -1.0, 1.0), AZ::Vector3(-1.0, -1.0, -1.0), AZ::Vector3(1.0, -1.0, -1.0),
  135. };
  136. for (int i = 0; i < GeometryVertexCount; ++i)
  137. {
  138. SetVertexPosition(bufferData.m_positions.data(), i, vertices[i]);
  139. SetVertexColor(bufferData.m_colors.data(), i, vertexColor[i].GetAsVector4());
  140. }
  141. bufferData.m_indices =
  142. {
  143. {
  144. //Top
  145. 2, 0, 1,
  146. 0, 2, 3,
  147. //Bottom
  148. 4, 6, 5,
  149. 6, 4, 7,
  150. //Left
  151. 8, 10, 9,
  152. 10, 8, 11,
  153. //Right
  154. 14, 12, 13,
  155. 15, 12, 14,
  156. //Front
  157. 16, 18, 17,
  158. 18, 16, 19,
  159. //Back
  160. 22, 20, 21,
  161. 23, 20, 22,
  162. }
  163. };
  164. }
  165. return bufferData;
  166. }
  167. void XRExampleComponent::CreateCubeInputAssemblyBuffer()
  168. {
  169. const AZ::RHI::Ptr<AZ::RHI::Device> device = Utils::GetRHIDevice();
  170. AZ::RHI::ResultCode result = AZ::RHI::ResultCode::Success;
  171. m_bufferPool = aznew AZ::RHI::BufferPool();
  172. AZ::RHI::BufferPoolDescriptor bufferPoolDesc;
  173. bufferPoolDesc.m_bindFlags = AZ::RHI::BufferBindFlags::InputAssembly;
  174. bufferPoolDesc.m_heapMemoryLevel = AZ::RHI::HeapMemoryLevel::Device;
  175. result = m_bufferPool->Init(bufferPoolDesc);
  176. if (result != AZ::RHI::ResultCode::Success)
  177. {
  178. AZ_Error("XRExampleComponent", false, "Failed to initialize buffer pool with error code %d", result);
  179. return;
  180. }
  181. SingleCubeBufferData bufferData = CreateSingleCubeBufferData();
  182. m_inputAssemblyBuffer = aznew AZ::RHI::Buffer();
  183. AZ::RHI::BufferInitRequest request;
  184. request.m_buffer = m_inputAssemblyBuffer.get();
  185. request.m_descriptor = AZ::RHI::BufferDescriptor{ AZ::RHI::BufferBindFlags::InputAssembly, sizeof(SingleCubeBufferData) };
  186. request.m_initialData = &bufferData;
  187. result = m_bufferPool->InitBuffer(request);
  188. if (result != AZ::RHI::ResultCode::Success)
  189. {
  190. AZ_Error("XRExampleComponent", false, "Failed to initialize buffer with error code %d", result);
  191. return;
  192. }
  193. m_geometryView.SetDrawArguments(AZ::RHI::DrawIndexed(0, GeometryIndexCount, 0));
  194. m_geometryView.AddStreamBufferView({
  195. *m_inputAssemblyBuffer,
  196. offsetof(SingleCubeBufferData, m_positions),
  197. sizeof(SingleCubeBufferData::m_positions),
  198. sizeof(VertexPosition)
  199. });
  200. m_geometryView.AddStreamBufferView({
  201. *m_inputAssemblyBuffer,
  202. offsetof(SingleCubeBufferData, m_colors),
  203. sizeof(SingleCubeBufferData::m_colors),
  204. sizeof(VertexColor)
  205. });
  206. m_geometryView.SetIndexBufferView({
  207. *m_inputAssemblyBuffer,
  208. offsetof(SingleCubeBufferData, m_indices),
  209. sizeof(SingleCubeBufferData::m_indices),
  210. AZ::RHI::IndexFormat::Uint16
  211. });
  212. AZ::RHI::InputStreamLayoutBuilder layoutBuilder;
  213. layoutBuilder.SetTopology(AZ::RHI::PrimitiveTopology::TriangleList);
  214. layoutBuilder.AddBuffer()->Channel("POSITION", AZ::RHI::Format::R32G32B32_FLOAT);
  215. layoutBuilder.AddBuffer()->Channel("COLOR", AZ::RHI::Format::R32G32B32A32_FLOAT);
  216. m_streamLayoutDescriptor.Clear();
  217. m_streamLayoutDescriptor = layoutBuilder.End();
  218. AZ::RHI::ValidateStreamBufferViews(m_streamLayoutDescriptor, m_geometryView, m_geometryView.GetFullStreamBufferIndices());
  219. }
  220. void XRExampleComponent::CreateCubePipeline()
  221. {
  222. const char* shaderFilePath = "Shaders/RHI/OpenXrSample.azshader";
  223. const char* sampleName = "XRExampleComponent";
  224. auto shader = LoadShader(shaderFilePath, sampleName);
  225. if (shader == nullptr)
  226. {
  227. return;
  228. }
  229. const AZ::RHI::Ptr<AZ::RHI::Device> device = Utils::GetRHIDevice();
  230. AZ::RHI::PipelineStateDescriptorForDraw pipelineDesc;
  231. shader->GetVariant(AZ::RPI::ShaderAsset::RootShaderVariantStableId).ConfigurePipelineState(pipelineDesc);
  232. pipelineDesc.m_inputStreamLayout = m_streamLayoutDescriptor;
  233. pipelineDesc.m_renderStates.m_depthStencilState.m_depth.m_enable = 1;
  234. pipelineDesc.m_renderStates.m_depthStencilState.m_depth.m_func = AZ::RHI::ComparisonFunc::LessEqual;
  235. AZ::RHI::RenderAttachmentLayoutBuilder attachmentsBuilder;
  236. attachmentsBuilder.AddSubpass()
  237. ->RenderTargetAttachment(m_outputFormat)
  238. ->DepthStencilAttachment(device->GetNearestSupportedFormat(AZ::RHI::Format::D24_UNORM_S8_UINT, AZ::RHI::FormatCapabilities::DepthStencil));
  239. [[maybe_unused]] AZ::RHI::ResultCode result = attachmentsBuilder.End(pipelineDesc.m_renderAttachmentConfiguration.m_renderAttachmentLayout);
  240. AZ_Assert(result == AZ::RHI::ResultCode::Success, "Failed to create render attachment layout");
  241. m_pipelineState = shader->AcquirePipelineState(pipelineDesc);
  242. if (!m_pipelineState)
  243. {
  244. AZ_Error("XRExampleComponent", false, "Failed to acquire default pipeline state for shader '%s'", shaderFilePath);
  245. return;
  246. }
  247. auto perInstanceSrgLayout = shader->FindShaderResourceGroupLayout(AZ::Name{ "OpenXrSrg" });
  248. if (!perInstanceSrgLayout)
  249. {
  250. AZ_Error("XRExampleComponent", false, "Failed to get shader resource group layout");
  251. return;
  252. }
  253. for (int i = 0; i < NumberOfCubes; ++i)
  254. {
  255. m_shaderResourceGroups[i] = CreateShaderResourceGroup(shader, "OpenXrSrg", sampleName);
  256. }
  257. // Using the first SRG to get the correct index as all the SRGs will have the same indices.
  258. FindShaderInputIndex(&m_shaderIndexWorldMat, m_shaderResourceGroups[0], AZ::Name{ "m_worldMatrix" }, "XRExampleComponent");
  259. FindShaderInputIndex(&m_shaderIndexViewProj, m_shaderResourceGroups[0], AZ::Name{ "m_viewProjMatrix" }, "XRExampleComponent");
  260. }
  261. void XRExampleComponent::CreateScope()
  262. {
  263. // Creates a scope for rendering the triangle.
  264. struct ScopeData
  265. {
  266. };
  267. const auto prepareFunction = [this](AZ::RHI::FrameGraphInterface frameGraph, [[maybe_unused]] ScopeData& scopeData)
  268. {
  269. // Binds the swap chain as a color attachment. Clears it to black.
  270. {
  271. AZ::RHI::ImageScopeAttachmentDescriptor descriptor;
  272. descriptor.m_attachmentId = m_outputAttachmentId;
  273. descriptor.m_loadStoreAction.m_loadAction = AZ::RHI::AttachmentLoadAction::Load;
  274. frameGraph.UseColorAttachment(descriptor);
  275. }
  276. // Create & Binds DepthStencil image
  277. {
  278. const AZ::RHI::Ptr<AZ::RHI::Device> device = Utils::GetRHIDevice();
  279. const AZ::RHI::ImageDescriptor imageDescriptor = AZ::RHI::ImageDescriptor::Create2D(
  280. AZ::RHI::ImageBindFlags::DepthStencil,
  281. m_outputWidth,
  282. m_outputHeight,
  283. device->GetNearestSupportedFormat(AZ::RHI::Format::D24_UNORM_S8_UINT, AZ::RHI::FormatCapabilities::DepthStencil));
  284. const AZ::RHI::TransientImageDescriptor transientImageDescriptor(m_depthStencilID, imageDescriptor);
  285. frameGraph.GetAttachmentDatabase().CreateTransientImage(transientImageDescriptor);
  286. AZ::RHI::ImageScopeAttachmentDescriptor dsDesc;
  287. dsDesc.m_attachmentId = m_depthStencilID;
  288. dsDesc.m_imageViewDescriptor.m_overrideFormat = device->GetNearestSupportedFormat(AZ::RHI::Format::D24_UNORM_S8_UINT, AZ::RHI::FormatCapabilities::DepthStencil);
  289. dsDesc.m_loadStoreAction.m_clearValue = AZ::RHI::ClearValue::CreateDepthStencil(1.0f, 0);
  290. dsDesc.m_loadStoreAction.m_loadAction = AZ::RHI::AttachmentLoadAction::Clear;
  291. dsDesc.m_loadStoreAction.m_loadActionStencil = AZ::RHI::AttachmentLoadAction::DontCare;
  292. frameGraph.UseDepthStencilAttachment(
  293. dsDesc, AZ::RHI::ScopeAttachmentAccess::Write,
  294. AZ::RHI::ScopeAttachmentStage::EarlyFragmentTest | AZ::RHI::ScopeAttachmentStage::LateFragmentTest);
  295. }
  296. // We will submit NumberOfCubes draw items.
  297. frameGraph.SetEstimatedItemCount(NumberOfCubes);
  298. };
  299. AZ::RHI::EmptyCompileFunction<ScopeData> compileFunction;
  300. const auto executeFunction = [this](const AZ::RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
  301. {
  302. AZ::RHI::CommandList* commandList = context.GetCommandList();
  303. // Set persistent viewport and scissor state.
  304. commandList->SetViewports(&m_viewport, 1);
  305. commandList->SetScissors(&m_scissor, 1);
  306. // Dividing NumberOfCubes by context.GetCommandListCount() to balance to number
  307. // of draw call equally between each thread.
  308. uint32_t numberOfCubesPerCommandList = NumberOfCubes / context.GetCommandListCount();
  309. uint32_t indexStart = context.GetCommandListIndex() * numberOfCubesPerCommandList;
  310. uint32_t indexEnd = indexStart + numberOfCubesPerCommandList;
  311. if (context.GetCommandListIndex() == context.GetCommandListCount() - 1)
  312. {
  313. indexEnd = NumberOfCubes;
  314. }
  315. for (uint32_t i = indexStart; i < indexEnd; ++i)
  316. {
  317. const AZ::RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
  318. m_shaderResourceGroups[i]->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
  319. };
  320. AZ::RHI::DeviceDrawItem drawItem;
  321. drawItem.m_geometryView = m_geometryView.GetDeviceGeometryView(context.GetDeviceIndex());
  322. drawItem.m_streamIndices = m_geometryView.GetFullStreamBufferIndices();
  323. drawItem.m_pipelineState = m_pipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
  324. drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(AZ::RHI::ArraySize(shaderResourceGroups));
  325. drawItem.m_shaderResourceGroups = shaderResourceGroups;
  326. commandList->Submit(drawItem);
  327. }
  328. };
  329. m_scopeProducers.emplace_back(
  330. aznew AZ::RHI::ScopeProducerFunction<
  331. ScopeData,
  332. decltype(prepareFunction),
  333. decltype(compileFunction),
  334. decltype(executeFunction)>(
  335. AZ::RHI::ScopeId{ AZStd::string::format("XRSample_Id_%llu", GetId()) },
  336. ScopeData{},
  337. prepareFunction,
  338. compileFunction,
  339. executeFunction));
  340. }
  341. void XRExampleComponent::Deactivate()
  342. {
  343. if (!AZ::RPI::RPISystemInterface::Get()->GetXRSystem())
  344. {
  345. return;
  346. }
  347. AZ::RPI::XRSpaceNotificationBus::Handler::BusDisconnect();
  348. m_inputAssemblyBuffer = nullptr;
  349. m_bufferPool = nullptr;
  350. m_pipelineState = nullptr;
  351. m_shaderResourceGroups.fill(nullptr);
  352. AZ::RHI::RHISystemNotificationBus::Handler::BusDisconnect();
  353. m_windowContext = nullptr;
  354. m_scopeProducers.clear();
  355. }
  356. }