3
0

DownsampleSinglePassLuminancePass.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  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 <Atom/RHI/Factory.h>
  9. #include <Atom/RHI/RHIUtils.h>
  10. #include <Atom/RPI.Public/Pass/Specific/DownsampleSinglePassLuminancePass.h>
  11. #include <Atom/RPI.Reflect/Buffer/BufferAssetCreator.h>
  12. namespace AZ::RPI
  13. {
  14. Ptr<DownsampleSinglePassLuminancePass> DownsampleSinglePassLuminancePass::Create(const PassDescriptor& descriptor)
  15. {
  16. // Check capability of wave operation
  17. bool isWaveSupported = RHI::GetRHIDevice()->GetFeatures().m_waveOperation;
  18. const char* supervariantName = isWaveSupported ? "" : AZ::RPI::NoWaveSupervariantName;
  19. Ptr<DownsampleSinglePassLuminancePass> pass = aznew DownsampleSinglePassLuminancePass(descriptor, AZ::Name(supervariantName));
  20. return pass;
  21. }
  22. DownsampleSinglePassLuminancePass::DownsampleSinglePassLuminancePass(const PassDescriptor& descriptor, AZ::Name supervariant)
  23. : ComputePass(descriptor, supervariant)
  24. {
  25. BuildGlobalAtomicBuffer();
  26. }
  27. void DownsampleSinglePassLuminancePass::BuildInternal()
  28. {
  29. GetDestinationInfo();
  30. CalculateSpdThreadDimensionAndMips();
  31. BuildPassAttachment();
  32. ComputePass::BuildInternal();
  33. }
  34. void DownsampleSinglePassLuminancePass::ResetInternal()
  35. {
  36. m_indicesAreInitialized = false;
  37. m_spdMipLevelCountIndex.Reset();
  38. m_destinationMipLevelCountIndex.Reset();
  39. m_numWorkGroupsIndex.Reset();
  40. m_imageSizeIndex.Reset();
  41. m_imageDestinationIndex.Reset();
  42. m_mip6ImageIndex.Reset();
  43. m_globalAtomicIndex.Reset();
  44. ComputePass::ResetInternal();
  45. }
  46. void DownsampleSinglePassLuminancePass::FrameBeginInternal(FramePrepareParams params)
  47. {
  48. SetConstants();
  49. ComputePass::FrameBeginInternal(params);
  50. }
  51. void DownsampleSinglePassLuminancePass::CompileResources(const RHI::FrameGraphCompileContext& context)
  52. {
  53. if (!m_shaderResourceGroup)
  54. {
  55. return;
  56. }
  57. ShaderResourceGroup& srg = *m_shaderResourceGroup;
  58. static constexpr uint32_t ThreadGroupSizeX = 256;
  59. static constexpr uint32_t ArraySliceCount = 1;
  60. SetTargetThreadCounts(m_targetThreadCountWidth * ThreadGroupSizeX,
  61. m_targetThreadCountHeight,
  62. ArraySliceCount);
  63. // Input/Output Mip Slices
  64. PassAttachmentBinding& outBinding = GetOutputBinding(0);
  65. PassAttachment* attachment = outBinding.GetAttachment().get();
  66. if (!attachment)
  67. {
  68. return;
  69. }
  70. const uint32_t mipLevelCount = attachment->m_descriptor.m_image.m_mipLevels;
  71. RHI::AttachmentId attachmentId = attachment->GetAttachmentId();
  72. const RHI::Image* rhiImage = context.GetImage(attachmentId);
  73. if (!rhiImage)
  74. {
  75. return;
  76. }
  77. RHI::ImageViewDescriptor imageViewDescriptor;
  78. for (uint32_t mipIndex = 0; mipIndex < GetMin(mipLevelCount, SpdMipLevelCountMax); ++mipIndex)
  79. {
  80. imageViewDescriptor.m_mipSliceMin = static_cast<uint16_t>(mipIndex);
  81. imageViewDescriptor.m_mipSliceMax = static_cast<uint16_t>(mipIndex);
  82. Ptr<RHI::ImageView> imageView = const_cast<RHI::Image*>(rhiImage)->BuildImageView(imageViewDescriptor);
  83. srg.SetImageView(m_imageDestinationIndex, imageView.get(), mipIndex);
  84. m_imageViews[mipIndex] = imageView;
  85. }
  86. // Set Globally coherent image view.
  87. const RHI::ImageView* mip6ImageView = context.GetImageView(m_mip6PassAttachment->GetAttachmentId());
  88. srg.SetImageView(m_mip6ImageIndex, mip6ImageView);
  89. // Set Global Atomic buffer.
  90. srg.SetBuffer(m_globalAtomicIndex, m_globalAtomicBuffer);
  91. ComputePass::CompileResources(context);
  92. }
  93. void DownsampleSinglePassLuminancePass::BuildGlobalAtomicBuffer()
  94. {
  95. const SpdGlobalAtomicBuffer initialData = {0};
  96. RPI::CommonBufferDescriptor descriptor;
  97. descriptor.m_poolType = RPI::CommonBufferPoolType::ReadWrite;
  98. descriptor.m_bufferName = "DownsampleSinglePassMipChainPass GlobalAtomic";
  99. descriptor.m_elementSize = sizeof(SpdGlobalAtomicBuffer);
  100. descriptor.m_byteCount = sizeof(SpdGlobalAtomicBuffer);
  101. descriptor.m_bufferData = reinterpret_cast<const void*>(&initialData);
  102. m_globalAtomicBuffer = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(descriptor);
  103. AZ_Assert(m_globalAtomicBuffer, "DownsampleSinglePassMipChainPass Building Global Atomic Buffer failed.")
  104. }
  105. void DownsampleSinglePassLuminancePass::InitializeIndices()
  106. {
  107. if (!m_shaderResourceGroup)
  108. {
  109. return;
  110. }
  111. const ShaderResourceGroup& srg = *m_shaderResourceGroup;
  112. m_spdMipLevelCountIndex = srg.FindShaderInputConstantIndex(Name("m_spdMipLevelCount"));
  113. m_destinationMipLevelCountIndex = srg.FindShaderInputConstantIndex(Name("m_destinationMipLevelCount"));
  114. m_numWorkGroupsIndex = srg.FindShaderInputConstantIndex(Name("m_numWorkGroups"));
  115. m_imageSizeIndex = srg.FindShaderInputConstantIndex(Name("m_imageSize"));
  116. m_imageDestinationIndex = srg.FindShaderInputImageIndex(Name("m_imageDestination"));
  117. m_mip6ImageIndex = srg.FindShaderInputImageIndex(Mip6Name);
  118. m_globalAtomicIndex = srg.FindShaderInputBufferIndex(GlobalAtomicName);
  119. m_indicesAreInitialized = true;
  120. }
  121. void DownsampleSinglePassLuminancePass::GetDestinationInfo()
  122. {
  123. // Get the input attachment for this pass (at binding 0)
  124. AZ_Error(
  125. "DownsampleSinglePassMipChainPass",
  126. GetInputCount() > 0,
  127. "[DownsampleSinglePassMipChainPass '%s']: must have an input",
  128. GetPathName().GetCStr());
  129. PassAttachment* attachment = GetInputBinding(0).GetAttachment().get();
  130. if (attachment)
  131. {
  132. m_destinationImageSize[0] = attachment->m_descriptor.m_image.m_size.m_width;
  133. m_destinationImageSize[1] = attachment->m_descriptor.m_image.m_size.m_height;
  134. // m_mipLevels of the attachment has not been initialized yet, so it is calculated below:
  135. const uint32_t maxDimension = GetMax(m_destinationImageSize[0], m_destinationImageSize[1]);
  136. m_destinationMipLevelCount = static_cast<uint32_t>(floorf(log2f(maxDimension * 1.f)));
  137. }
  138. }
  139. void DownsampleSinglePassLuminancePass::CalculateSpdThreadDimensionAndMips()
  140. {
  141. // Each SPD thread group computes for sub-region of size 64x64 in mip level 0 slice
  142. // where 64 == (1 << GloballyCoherentMipIndex).
  143. static const uint32_t groupImageWidth = 1 << GloballyCoherentMipIndex;
  144. m_targetThreadCountWidth = (m_destinationImageSize[0] + groupImageWidth - 1) / groupImageWidth;
  145. m_targetThreadCountHeight = (m_destinationImageSize[1] + groupImageWidth - 1) / groupImageWidth;
  146. const uint32_t maxDimension = GetMax(m_destinationImageSize[0], m_destinationImageSize[1]);
  147. m_spdMipLevelCount = m_destinationMipLevelCount;
  148. if (static_cast<uint32_t>(1 << m_spdMipLevelCount) != maxDimension)
  149. {
  150. ++m_spdMipLevelCount;
  151. }
  152. }
  153. void DownsampleSinglePassLuminancePass::BuildPassAttachment()
  154. {
  155. // Build "Mip6" image attachment.
  156. {
  157. // SPD stores each mip level value into groupshared float arrays except mip 6,
  158. // and do into this "Mip6" image only for mip 6. So the precision of the image
  159. // should be same as float variable (32 bit float).
  160. m_mip6ImageDescriptor =
  161. RHI::ImageDescriptor::Create2D(
  162. RHI::ImageBindFlags::ShaderReadWrite,
  163. m_targetThreadCountWidth,
  164. m_targetThreadCountHeight,
  165. RHI::Format::R32G32B32A32_FLOAT);
  166. m_mip6PassAttachment = aznew PassAttachment();
  167. m_mip6PassAttachment->m_name = "Mip6";
  168. const Name attachmentPath(AZStd::string::format(
  169. "%s.%s",
  170. GetPathName().GetCStr(),
  171. m_mip6PassAttachment->m_name.GetCStr()));
  172. m_mip6PassAttachment->m_path = attachmentPath;
  173. m_mip6PassAttachment->m_lifetime = RHI::AttachmentLifetimeType::Transient;
  174. m_mip6PassAttachment->m_descriptor = m_mip6ImageDescriptor;
  175. m_ownedAttachments.push_back(m_mip6PassAttachment);
  176. PassAttachmentBinding binding;
  177. binding.m_name = m_mip6PassAttachment->m_name;
  178. binding.m_slotType = PassSlotType::InputOutput;
  179. binding.m_shaderInputName = Mip6Name;
  180. binding.m_scopeAttachmentUsage = RHI::ScopeAttachmentUsage::Shader;
  181. binding.SetAttachment(m_mip6PassAttachment);
  182. AddAttachmentBinding(binding);
  183. }
  184. // Build "GlobalAtomic" buffer attachment.
  185. {
  186. auto bufferDescriptor = RHI::BufferDescriptor(RHI::BufferBindFlags::ShaderReadWrite, 4);
  187. bufferDescriptor.m_alignment = 4;
  188. m_counterPassAttachment = aznew PassAttachment();
  189. m_counterPassAttachment->m_name = "GlobalAtomic";
  190. const Name attachmentPath(AZStd::string::format(
  191. "%s.%s",
  192. GetPathName().GetCStr(),
  193. m_counterPassAttachment->m_name.GetCStr()));
  194. m_counterPassAttachment->m_path = attachmentPath;
  195. m_counterPassAttachment->m_lifetime = RHI::AttachmentLifetimeType::Imported;
  196. m_counterPassAttachment->m_descriptor = bufferDescriptor;
  197. m_counterPassAttachment->m_importedResource = m_globalAtomicBuffer;
  198. m_ownedAttachments.push_back(m_counterPassAttachment);
  199. PassAttachmentBinding binding;
  200. binding.m_name = m_counterPassAttachment->m_name;
  201. binding.m_slotType = PassSlotType::InputOutput;
  202. binding.m_shaderInputName = GlobalAtomicName;
  203. binding.m_scopeAttachmentUsage = RHI::ScopeAttachmentUsage::Shader;
  204. binding.SetAttachment(m_counterPassAttachment);
  205. AddAttachmentBinding(binding);
  206. }
  207. }
  208. void DownsampleSinglePassLuminancePass::SetConstants()
  209. {
  210. if (!m_indicesAreInitialized)
  211. {
  212. InitializeIndices();
  213. }
  214. if (!m_shaderResourceGroup)
  215. {
  216. return;
  217. }
  218. // For the setting up of the parameter for SPD shader, refer to:
  219. // https://github.com/GPUOpen-Effects/FidelityFX-SPD/blob/c52944f547884774a1b33066f740e6bf89f927f5/ffx-spd/ffx_spd.h#L327
  220. [[maybe_unused]] bool succeeded = true;
  221. ShaderResourceGroup& srg = *m_shaderResourceGroup;
  222. succeeded &= srg.SetConstant(
  223. m_numWorkGroupsIndex,
  224. m_targetThreadCountWidth * m_targetThreadCountHeight);
  225. succeeded &= srg.SetConstant(m_spdMipLevelCountIndex, m_spdMipLevelCount);
  226. succeeded &= srg.SetConstant(m_destinationMipLevelCountIndex, m_destinationMipLevelCount);
  227. succeeded &= srg.SetConstantArray(m_imageSizeIndex, m_destinationImageSize);
  228. AZ_Assert(succeeded, "DownsampleSinglePassMipChainPass failed to set constants.");
  229. }
  230. } // namespace AZ::RPI