CommandListValidator.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  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/CommandListValidator.h>
  9. #include <Atom/RHI/Scope.h>
  10. #include <Atom/RHI/DeviceShaderResourceGroup.h>
  11. #include <Atom/RHI/DeviceShaderResourceGroupPool.h>
  12. #include <Atom/RHI/DeviceResourcePool.h>
  13. #include <Atom/RHI/DeviceImagePoolBase.h>
  14. #include <Atom/RHI/DeviceImageView.h>
  15. #include <Atom/RHI/Buffer.h>
  16. #include <Atom/RHI/Image.h>
  17. #include <Atom/RHI/DeviceResourceView.h>
  18. #include <Atom/RHI/DeviceResource.h>
  19. #include <Atom/RHI/FrameGraph.h>
  20. #include <Atom/RHI/ScopeAttachment.h>
  21. #include <Atom/RHI/FrameAttachment.h>
  22. #include <Atom/RHI.Reflect/PipelineLayoutDescriptor.h>
  23. namespace AZ::RHI
  24. {
  25. void CommandListValidator::BeginScope(const Scope& scope)
  26. {
  27. if (!Validation::IsEnabled())
  28. {
  29. return;
  30. }
  31. AZ_PROFILE_FUNCTION(RHI);
  32. AZ_Assert(m_scope == nullptr, "BeginScope called twice.");
  33. m_scope = &scope;
  34. for (const ScopeAttachment* scopeAttachment : scope.GetAttachments())
  35. {
  36. auto resource = scopeAttachment->GetResourceView()->GetResource();
  37. m_attachments[resource].push_back(scopeAttachment);
  38. }
  39. }
  40. void CommandListValidator::EndScope()
  41. {
  42. if (!Validation::IsEnabled())
  43. {
  44. return;
  45. }
  46. m_scope = nullptr;
  47. m_attachments.clear();
  48. }
  49. bool CommandListValidator::ValidateShaderResourceGroup(const DeviceShaderResourceGroup& shaderResourceGroup, const ShaderResourceGroupBindingInfo& bindingInfo) const
  50. {
  51. if (!Validation::IsEnabled())
  52. {
  53. return true;
  54. }
  55. ValidateViewContext context;
  56. context.m_scopeName = m_scope->GetId().GetCStr();
  57. context.m_srgName = shaderResourceGroup.GetName().GetCStr();
  58. if (shaderResourceGroup.IsQueuedForCompile())
  59. {
  60. AZ_Warning("CommandListValidator", false,
  61. "[Scope '%s']: SRG '%s' is queued for compilation. This means the parent pool '%s' was not "
  62. "imported into the frame scheduler. This will result in SRG contents not being uploaded to "
  63. "the GPU.",
  64. context.m_scopeName,
  65. context.m_srgName,
  66. shaderResourceGroup.GetPool()->GetName().GetCStr());
  67. return false;
  68. }
  69. bool isSuccess = true;
  70. const DeviceShaderResourceGroupData& groupData = shaderResourceGroup.GetData();
  71. const ShaderResourceGroupLayout& groupLayout = *groupData.GetLayout();
  72. // Validate buffers
  73. const auto& bufferInputs = groupLayout.GetShaderInputListForBuffers();
  74. for (uint32_t shaderInputIndex = 0; shaderInputIndex < bufferInputs.size(); ++shaderInputIndex)
  75. {
  76. const RHI::ShaderInputBufferDescriptor& shaderInputBuffer = bufferInputs[shaderInputIndex];
  77. // First check if the buffer is being used.
  78. auto findIt = bindingInfo.m_resourcesRegisterMap.find(shaderInputBuffer.m_name);
  79. if (findIt == bindingInfo.m_resourcesRegisterMap.end() || findIt->second.m_shaderStageMask == RHI::ShaderStageMask::None)
  80. {
  81. continue;
  82. }
  83. context.m_shaderInputTypeName = GetShaderInputAccessName(shaderInputBuffer.m_access);
  84. context.m_scopeAttachmentAccess = GetAttachmentAccess(shaderInputBuffer.m_access);
  85. const RHI::ShaderInputBufferIndex bufferInputIndex(shaderInputIndex);
  86. auto bufferViews = groupData.GetBufferViewArray(bufferInputIndex);
  87. for (auto& bufferView : bufferViews)
  88. {
  89. if (bufferView)
  90. {
  91. context.m_resourceView = bufferView.get();
  92. isSuccess &= ValidateView(context, bufferView->IgnoreFrameAttachmentValidation());
  93. }
  94. }
  95. }
  96. // Validate images
  97. const auto& imageInputs = groupLayout.GetShaderInputListForImages();
  98. for (uint32_t shaderInputIndex = 0; shaderInputIndex < imageInputs.size(); ++shaderInputIndex)
  99. {
  100. const RHI::ShaderInputImageDescriptor& shaderInputImage = imageInputs[shaderInputIndex];
  101. // First check if the image is being used.
  102. auto findIt = bindingInfo.m_resourcesRegisterMap.find(shaderInputImage.m_name);
  103. if (findIt == bindingInfo.m_resourcesRegisterMap.end() || findIt->second.m_shaderStageMask == RHI::ShaderStageMask::None)
  104. {
  105. continue;
  106. }
  107. context.m_shaderInputTypeName = GetShaderInputAccessName(shaderInputImage.m_access);
  108. context.m_scopeAttachmentAccess = GetAttachmentAccess(shaderInputImage.m_access);
  109. const RHI::ShaderInputImageIndex imageInputIndex(shaderInputIndex);
  110. auto imageViews = groupData.GetImageViewArray(imageInputIndex);
  111. for (auto& imageView : imageViews)
  112. {
  113. if (imageView)
  114. {
  115. context.m_resourceView = imageView.get();
  116. isSuccess &= ValidateView(context, false);
  117. }
  118. }
  119. ++shaderInputIndex;
  120. }
  121. return isSuccess;
  122. }
  123. bool CommandListValidator::ValidateAttachment(
  124. const ValidateViewContext& context,
  125. const FrameAttachment* frameAttachment) const
  126. {
  127. AZ_Assert(frameAttachment, "Frame attachment is null.");
  128. [[maybe_unused]] const char* attachmentName = frameAttachment->GetId().GetCStr();
  129. const AZStd::vector<const ScopeAttachment*>* scopeAttachments = nullptr;
  130. auto findIt = m_attachments.find(frameAttachment->GetResource());
  131. if (findIt != m_attachments.end())
  132. {
  133. scopeAttachments = &findIt->second;
  134. }
  135. if (scopeAttachments && scopeAttachments->size() > 0)
  136. {
  137. bool isValidUsage = false;
  138. bool isValidAccess = false;
  139. for (const ScopeAttachment* scopeAttachment : *scopeAttachments)
  140. {
  141. isValidUsage = scopeAttachment->GetUsage() == ScopeAttachmentUsage::Shader || scopeAttachment->GetUsage() == ScopeAttachmentUsage::SubpassInput;
  142. isValidAccess = scopeAttachment->GetAccess() == context.m_scopeAttachmentAccess;
  143. if (isValidUsage && isValidAccess)
  144. {
  145. return true;
  146. }
  147. }
  148. // We couldn't find a scope attachment that matches the usage, output warning
  149. AZ_Warning("CommandListValidator", false,
  150. "[Scope '%s', SRG '%s']: Failed to find a matching usage for attachment '%s'. Mismatches are as follows:",
  151. context.m_scopeName,
  152. context.m_srgName,
  153. attachmentName);
  154. // Output mismatch for each of the scope attachments in the list
  155. for (const ScopeAttachment* scopeAttachment : *scopeAttachments)
  156. {
  157. isValidUsage = scopeAttachment->GetUsage() == ScopeAttachmentUsage::Shader || scopeAttachment->GetUsage() == ScopeAttachmentUsage::SubpassInput;
  158. isValidAccess = scopeAttachment->GetAccess() == context.m_scopeAttachmentAccess;
  159. AZ_Warning("CommandListValidator", isValidUsage,
  160. "[Scope '%s', SRG '%s']: Attachment '%s' is used as ['%s'], but usage needs to be 'Shader'",
  161. context.m_scopeName,
  162. context.m_srgName,
  163. attachmentName,
  164. scopeAttachment->GetTypeName());
  165. AZ_Warning("CommandListValidator", isValidAccess,
  166. "[Scope '%s', SRG '%s']: Attachment '%s' is marked for '%s' access, but the scope declared ['%s'] access.",
  167. context.m_scopeName,
  168. context.m_srgName,
  169. attachmentName,
  170. ToString(context.m_scopeAttachmentAccess),
  171. scopeAttachment->GetTypeName());
  172. }
  173. return false;
  174. }
  175. AZ_Warning(
  176. "CommandListValidator", false,
  177. "[Scope '%s', SRG '%s']: Attachment '%s' not declared for usage in this scope. Actual usage: '%s'",
  178. context.m_scopeName,
  179. context.m_srgName,
  180. attachmentName,
  181. context.m_shaderInputTypeName);
  182. return false;
  183. }
  184. ScopeAttachmentAccess CommandListValidator::GetAttachmentAccess(ShaderInputBufferAccess access)
  185. {
  186. return
  187. (access == ShaderInputBufferAccess::ReadWrite)
  188. ? ScopeAttachmentAccess::ReadWrite
  189. : ScopeAttachmentAccess::Read;
  190. }
  191. ScopeAttachmentAccess CommandListValidator::GetAttachmentAccess(ShaderInputImageAccess access)
  192. {
  193. return
  194. (access == ShaderInputImageAccess::ReadWrite)
  195. ? ScopeAttachmentAccess::ReadWrite
  196. : ScopeAttachmentAccess::Read;
  197. }
  198. bool CommandListValidator::ValidateView(const ValidateViewContext& context, bool ignoreAttachmentValidation) const
  199. {
  200. const DeviceResourceView& resourceView = *context.m_resourceView;
  201. const DeviceResource& resource = resourceView.GetResource();
  202. [[maybe_unused]] const char* resourceViewName = resourceView.GetName().GetCStr();
  203. [[maybe_unused]] const char* resourceName = resource.GetName().GetCStr();
  204. if (resourceView.IsStale())
  205. {
  206. AZ_Warning(
  207. "CommandListValidator", false,
  208. "[Scope '%s', SRG '%s']: DeviceResourceView '%s' of DeviceResource '%s' is stale! This indicates that the SRG was not properly "
  209. "compiled, or was invalidated after compilation during the command list recording phase.",
  210. context.m_scopeName,
  211. context.m_srgName,
  212. resourceViewName,
  213. resourceName);
  214. return false;
  215. }
  216. if (resource.IsAttachment())
  217. {
  218. return ValidateAttachment(context, resource.GetFrameAttachment());
  219. }
  220. // DeviceResource is not an attachment. It must be in a read-only state.
  221. if (!ignoreAttachmentValidation && context.m_scopeAttachmentAccess != ScopeAttachmentAccess::Read)
  222. {
  223. AZ_Warning(
  224. "CommandListValidator", false,
  225. "[Scope '%s', SRG '%s']: DeviceResourceView '%s' of DeviceResource '%s' is declared as '%s', but this type "
  226. "requires that the resource be an attachment.",
  227. context.m_scopeName,
  228. context.m_srgName,
  229. resourceViewName,
  230. resourceName,
  231. context.m_shaderInputTypeName);
  232. return false;
  233. }
  234. return true;
  235. }
  236. }