ProjectedShadowFeatureProcessor.cpp 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884
  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 <Shadows/ProjectedShadowFeatureProcessor.h>
  9. #include <AzCore/Math/MatrixUtils.h>
  10. #include <AzCore/Name/NameDictionary.h>
  11. #include <Math/GaussianMathFilter.h>
  12. #include <Atom/RHI/DrawPacketBuilder.h>
  13. #include <Atom/RHI/RHISystemInterface.h>
  14. #include <Atom/RHI.Reflect/InputStreamLayoutBuilder.h>
  15. #include <Atom/RPI.Public/RenderPipeline.h>
  16. #include <Atom/RPI.Public/RPISystemInterface.h>
  17. #include <Atom/RPI.Public/Scene.h>
  18. #include <Atom/RPI.Public/View.h>
  19. #include <Atom/RPI.Public/Image/ImageSystemInterface.h>
  20. #include <Atom/RPI.Public/Pass/PassSystem.h>
  21. #include <Atom/RPI.Public/Pass/PassFilter.h>
  22. #include <Atom/RPI.Public/Shader/Shader.h>
  23. #include <Atom/RPI.Reflect/Asset/AssetUtils.h>
  24. #include <Atom/Feature/Mesh/MeshCommon.h>
  25. #include <CoreLights/Shadow.h>
  26. namespace AZ::Render
  27. {
  28. void ProjectedShadowFeatureProcessor::Reflect(ReflectContext* context)
  29. {
  30. if (auto* serializeContext = azrtti_cast<SerializeContext*>(context))
  31. {
  32. serializeContext
  33. ->Class<ProjectedShadowFeatureProcessor, FeatureProcessor>()
  34. ->Version(0);
  35. }
  36. }
  37. void ProjectedShadowFeatureProcessor::Activate()
  38. {
  39. const RHI::ShaderResourceGroupLayout* viewSrgLayout = RPI::RPISystemInterface::Get()->GetViewSrgLayout().get();
  40. GpuBufferHandler::Descriptor desc;
  41. desc.m_bufferName = "ProjectedShadowBuffer";
  42. desc.m_bufferSrgName = "m_projectedShadows";
  43. desc.m_elementCountSrgName = "";
  44. desc.m_elementSize = sizeof(ShadowData);
  45. desc.m_srgLayout = viewSrgLayout;
  46. m_shadowBufferHandler = GpuBufferHandler(desc);
  47. desc.m_bufferName = "ProjectedFilterParamsBuffer";
  48. desc.m_bufferSrgName = "m_projectedFilterParams";
  49. desc.m_elementCountSrgName = "";
  50. desc.m_elementSize = sizeof(EsmShadowmapsPass::FilterParameter);
  51. desc.m_srgLayout = viewSrgLayout;
  52. m_filterParamBufferHandler = GpuBufferHandler(desc);
  53. EnableSceneNotification();
  54. }
  55. void ProjectedShadowFeatureProcessor::Deactivate()
  56. {
  57. DisableSceneNotification();
  58. m_shadowData.Clear();
  59. m_shadowBufferHandler.Release();
  60. m_filterParamBufferHandler.Release();
  61. m_shadowProperties.Clear();
  62. m_projectedShadowmapsPasses.clear();
  63. m_esmShadowmapsPasses.clear();
  64. m_primaryProjectedShadowmapsPass = nullptr;
  65. if (m_primaryEsmShadowmapsPass)
  66. {
  67. m_primaryEsmShadowmapsPass->SetEnabledComputation(false);
  68. m_primaryEsmShadowmapsPass = nullptr;
  69. }
  70. }
  71. ProjectedShadowFeatureProcessor::ShadowId ProjectedShadowFeatureProcessor::AcquireShadow()
  72. {
  73. // Reserve a new slot in m_shadowData
  74. size_t index = m_shadowData.Reserve();
  75. if (index >= std::numeric_limits<ShadowId::IndexType>::max())
  76. {
  77. m_shadowData.Release(index);
  78. return ShadowId::Null;
  79. }
  80. ShadowId id = ShadowId(aznumeric_cast<ShadowId::IndexType>(index));
  81. InitializeShadow(id);
  82. return id;
  83. }
  84. void ProjectedShadowFeatureProcessor::ReleaseShadow(ShadowId id)
  85. {
  86. if (id.IsValid())
  87. {
  88. auto& shadowProperty = GetShadowPropertyFromShadowId(id);
  89. if (m_primaryProjectedShadowmapsPass)
  90. {
  91. m_primaryProjectedShadowmapsPass->QueueRemoveChild(shadowProperty.m_shadowmapPass);
  92. }
  93. m_shadowProperties.RemoveData(&shadowProperty);
  94. m_shadowData.Release(id.GetIndex());
  95. }
  96. m_filterParameterNeedsUpdate = true;
  97. m_shadowmapPassNeedsUpdate = true;
  98. }
  99. void ProjectedShadowFeatureProcessor::SetShadowTransform(ShadowId id, Transform transform)
  100. {
  101. ShadowProperty& shadowProperty = GetShadowPropertyFromShadowId(id);
  102. shadowProperty.m_desc.m_transform = transform;
  103. UpdateShadowView(shadowProperty);
  104. }
  105. void ProjectedShadowFeatureProcessor::SetNearFarPlanes(ShadowId id, float nearPlaneDistance, float farPlaneDistance)
  106. {
  107. AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetFrontBackPlanes().");
  108. ShadowProperty& shadowProperty = GetShadowPropertyFromShadowId(id);
  109. shadowProperty.m_desc.m_nearPlaneDistance = GetMax(nearPlaneDistance, 0.0001f);
  110. shadowProperty.m_desc.m_farPlaneDistance = GetMax(farPlaneDistance, nearPlaneDistance + 0.0001f);
  111. UpdateShadowView(shadowProperty);
  112. }
  113. void ProjectedShadowFeatureProcessor::SetAspectRatio(ShadowId id, float aspectRatio)
  114. {
  115. AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetAspectRatio().");
  116. ShadowProperty& shadowProperty = GetShadowPropertyFromShadowId(id);
  117. shadowProperty.m_desc.m_aspectRatio = aspectRatio;
  118. UpdateShadowView(shadowProperty);
  119. }
  120. void ProjectedShadowFeatureProcessor::SetFieldOfViewY(ShadowId id, float fieldOfViewYRadians)
  121. {
  122. AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetFieldOfViewY().");
  123. ShadowProperty& shadowProperty = GetShadowPropertyFromShadowId(id);
  124. shadowProperty.m_desc.m_fieldOfViewYRadians = fieldOfViewYRadians;
  125. UpdateShadowView(shadowProperty);
  126. }
  127. void ProjectedShadowFeatureProcessor::SetShadowBias(ShadowId id, float bias)
  128. {
  129. AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetShadowBias().");
  130. ShadowProperty& shadowProperty = GetShadowPropertyFromShadowId(id);
  131. shadowProperty.m_bias = bias;
  132. }
  133. void ProjectedShadowFeatureProcessor::SetNormalShadowBias(ShadowId id, float normalShadowBias)
  134. {
  135. AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetNormalShadowBias().");
  136. ShadowData& shadowData = m_shadowData.GetElement<ShadowDataIndex>(id.GetIndex());
  137. shadowData.m_normalShadowBias = normalShadowBias;
  138. m_deviceBufferNeedsUpdate = true;
  139. }
  140. void ProjectedShadowFeatureProcessor::SetShadowmapMaxResolution(ShadowId id, ShadowmapSize size)
  141. {
  142. AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetShadowmapMaxResolution().");
  143. AZ_Assert(size != ShadowmapSize::None, "Shadowmap size cannot be set to None, remove the shadow instead.");
  144. FilterParameter& esmData = m_shadowData.GetElement<FilterParamIndex>(id.GetIndex());
  145. esmData.m_shadowmapSize = aznumeric_cast<uint32_t>(size);
  146. m_deviceBufferNeedsUpdate = true;
  147. m_shadowmapPassNeedsUpdate = true;
  148. m_filterParameterNeedsUpdate = true;
  149. }
  150. void ProjectedShadowFeatureProcessor::SetEsmExponent(ShadowId id, float exponent)
  151. {
  152. AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetEsmExponent().");
  153. ShadowData& shadowData = m_shadowData.GetElement<ShadowDataIndex>(id.GetIndex());
  154. shadowData.m_esmExponent = exponent;
  155. m_deviceBufferNeedsUpdate = true;
  156. }
  157. void ProjectedShadowFeatureProcessor::SetShadowFilterMethod(ShadowId id, ShadowFilterMethod method)
  158. {
  159. AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetShadowFilterMethod().");
  160. ShadowProperty& shadowProperty = GetShadowPropertyFromShadowId(id);
  161. ShadowData& shadowData = m_shadowData.GetElement<ShadowDataIndex>(id.GetIndex());
  162. shadowData.m_shadowFilterMethod = aznumeric_cast<uint32_t>(method);
  163. UpdateShadowView(shadowProperty);
  164. m_shadowmapPassNeedsUpdate = true;
  165. m_filterParameterNeedsUpdate = true;
  166. }
  167. void ProjectedShadowFeatureProcessor::SetFilteringSampleCount(ShadowId id, uint16_t count)
  168. {
  169. AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetFilteringSampleCount().");
  170. AZ_Warning("ProjectedShadowFeatureProcessor", count <= Shadow::MaxPcfSamplingCount, "Sampling count exceed the limit.");
  171. count = GetMin(count, Shadow::MaxPcfSamplingCount);
  172. ShadowData& shadowData = m_shadowData.GetElement<ShadowDataIndex>(id.GetIndex());
  173. shadowData.m_filteringSampleCount = count;
  174. m_deviceBufferNeedsUpdate = true;
  175. }
  176. void ProjectedShadowFeatureProcessor::SetUseCachedShadows(ShadowId id, bool useCachedShadows)
  177. {
  178. AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetUseCachedShadows().");
  179. ShadowProperty& shadowProperty = GetShadowPropertyFromShadowId(id);
  180. shadowProperty.m_useCachedShadows = useCachedShadows;
  181. m_shadowmapPassNeedsUpdate = true;
  182. }
  183. void ProjectedShadowFeatureProcessor::SetShadowProperties(ShadowId id, const ProjectedShadowDescriptor& descriptor)
  184. {
  185. AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetShadowProperties().");
  186. ShadowProperty& shadowProperty = GetShadowPropertyFromShadowId(id);
  187. if (shadowProperty.m_desc != descriptor)
  188. {
  189. shadowProperty.m_desc = descriptor;
  190. UpdateShadowView(shadowProperty);
  191. // Don't set m_shadowmapPassNeedsUpdate=true here because that would cause the pass to rebuild every time a light moves
  192. // Don't set m_filterParameterNeedsUpdate=true here because that's handled by UpdateShadowView(), and only when filtering is relevant
  193. }
  194. }
  195. auto ProjectedShadowFeatureProcessor::GetShadowProperties(ShadowId id) -> const ProjectedShadowDescriptor&
  196. {
  197. AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::GetShadowProperties().");
  198. return GetShadowPropertyFromShadowId(id).m_desc;
  199. }
  200. void ProjectedShadowFeatureProcessor::UpdateShadowView(ShadowProperty& shadowProperty)
  201. {
  202. const ProjectedShadowDescriptor& desc = shadowProperty.m_desc;
  203. float nearDist = desc.m_nearPlaneDistance;
  204. float farDist = desc.m_farPlaneDistance;
  205. // Adjust the near plane if it's too close to ensure accuracy.
  206. constexpr float NearFarRatio = 1000.0f;
  207. const float minDist = desc.m_farPlaneDistance / NearFarRatio;
  208. nearDist = GetMax(minDist, nearDist);
  209. Matrix4x4 viewToClipMatrix;
  210. MakePerspectiveFovMatrixRH(
  211. viewToClipMatrix,
  212. GetMax(desc.m_fieldOfViewYRadians, MinimumFieldOfView),
  213. desc.m_aspectRatio,
  214. nearDist,
  215. farDist);
  216. RPI::ViewPtr view = shadowProperty.m_shadowmapView;
  217. view->SetViewToClipMatrix(viewToClipMatrix);
  218. view->SetCameraTransform(Matrix3x4::CreateFromTransform(desc.m_transform));
  219. ShadowData& shadowData = m_shadowData.GetElement<ShadowDataIndex>(shadowProperty.m_shadowId.GetIndex());
  220. // Adjust the manually set bias to a more appropriate range for the shader. Scale the bias by the
  221. // near plane so that the bias appears consistent as other light properties change.
  222. shadowData.m_bias = nearDist * shadowProperty.m_bias * 0.01f;
  223. FilterParameter& esmData = m_shadowData.GetElement<FilterParamIndex>(shadowProperty.m_shadowId.GetIndex());
  224. // Set parameters to calculate linear depth if ESM is used.
  225. esmData.m_n_f_n = nearDist / (farDist - nearDist);
  226. esmData.m_n_f = nearDist - farDist;
  227. esmData.m_f = farDist;
  228. esmData.m_isEnabled = FilterMethodIsEsm(shadowData);
  229. m_filterParameterNeedsUpdate = m_filterParameterNeedsUpdate || esmData.m_isEnabled;
  230. // Set depth bias matrix.
  231. const Matrix4x4& worldToLightClipMatrix = view->GetWorldToClipMatrix();
  232. const Matrix4x4 depthBiasMatrix = Shadow::GetClipToShadowmapTextureMatrix() * worldToLightClipMatrix;
  233. shadowData.m_depthBiasMatrix = depthBiasMatrix;
  234. shadowData.m_unprojectConstants[0] = view->GetViewToClipMatrix().GetRow(2).GetElement(2);
  235. shadowData.m_unprojectConstants[1] = view->GetViewToClipMatrix().GetRow(2).GetElement(3);
  236. if (shadowProperty.m_useCachedShadows && m_primaryProjectedShadowmapsPass)
  237. {
  238. shadowProperty.m_shadowmapPass->ForceRenderNextFrame();
  239. }
  240. m_deviceBufferNeedsUpdate = true;
  241. }
  242. void ProjectedShadowFeatureProcessor::InitializeShadow(ShadowId shadowId)
  243. {
  244. m_deviceBufferNeedsUpdate = true;
  245. m_shadowmapPassNeedsUpdate = true;
  246. // Reserve a slot in m_shadowProperties, and store that index in m_shadowData's second vector
  247. uint16_t shadowPropertyIndex = m_shadowProperties.GetFreeSlotIndex();
  248. m_shadowData.GetElement<ShadowPropertyIdIndex>(shadowId.GetIndex()) = shadowPropertyIndex;
  249. ShadowProperty& shadowProperty = m_shadowProperties.GetData(shadowPropertyIndex);
  250. shadowProperty.m_shadowId = shadowId;
  251. Name viewName(AZStd::string::format("ProjectedShadowView (shadowId:%d)", shadowId.GetIndex()));
  252. shadowProperty.m_shadowmapView = RPI::View::CreateView(viewName, RPI::View::UsageShadow);
  253. UpdateShadowView(shadowProperty);
  254. if (m_primaryProjectedShadowmapsPass)
  255. {
  256. shadowProperty.m_shadowmapPass = CreateShadowmapPass(shadowId.GetIndex());
  257. m_primaryProjectedShadowmapsPass->QueueAddChild(shadowProperty.m_shadowmapPass);
  258. }
  259. }
  260. void ProjectedShadowFeatureProcessor::OnRenderPipelineChanged([[maybe_unused]] RPI::RenderPipeline* renderPipeline,
  261. [[maybe_unused]] RPI::SceneNotification::RenderPipelineChangeType changeType)
  262. {
  263. if (changeType == RPI::SceneNotification::RenderPipelineChangeType::Removed)
  264. {
  265. // Check for cases where the pipeline containing the primary render passes has been removed, which means the pointers
  266. // to those passes are no longer valid.
  267. CheckRemovePrimaryPasses(renderPipeline);
  268. }
  269. if (changeType == RPI::SceneNotification::RenderPipelineChangeType::Removed || changeType == RPI::SceneNotification::RenderPipelineChangeType::PassChanged)
  270. {
  271. RemoveCachedPasses(renderPipeline);
  272. }
  273. if (changeType == RPI::SceneNotification::RenderPipelineChangeType::Added || changeType == RPI::SceneNotification::RenderPipelineChangeType::PassChanged)
  274. {
  275. CachePasses(renderPipeline);
  276. }
  277. // Check to see if the primary passes have changed, and if so removes the children from the old primary pass and creates them
  278. // on the new primary pass. This is necessary if an earlier render pipeline adds or removes references to the shadow map
  279. // passes, forcing this feature processor to change which pipeline it uses to render shadows for all pipelines in the scene.
  280. UpdatePrimaryPasses();
  281. }
  282. void ProjectedShadowFeatureProcessor::CheckRemovePrimaryPasses(RPI::RenderPipeline* renderPipeline)
  283. {
  284. auto projItr = m_projectedShadowmapsPasses.find(renderPipeline);
  285. if (projItr != m_projectedShadowmapsPasses.end() && projItr->second == m_primaryProjectedShadowmapsPass)
  286. {
  287. m_primaryProjectedShadowmapsPass = nullptr;
  288. }
  289. auto esmItr = m_esmShadowmapsPasses.find(renderPipeline);
  290. if (esmItr != m_esmShadowmapsPasses.end() && esmItr->second == m_primaryEsmShadowmapsPass)
  291. {
  292. m_primaryEsmShadowmapsPass = nullptr;
  293. }
  294. }
  295. void ProjectedShadowFeatureProcessor::RemoveCachedPasses(RPI::RenderPipeline* renderPipeline)
  296. {
  297. m_projectedShadowmapsPasses.erase(renderPipeline);
  298. m_esmShadowmapsPasses.erase(renderPipeline);
  299. // Handle the case where the render pipeline containing the primary projected shadow pass is changed, and the
  300. // projected shadow pass was altered or removed as part of that change.
  301. if (renderPipeline == m_primaryShadowPipeline && m_primaryProjectedShadowmapsPass != nullptr)
  302. {
  303. RPI::PassFilter projectedPassFilter = RPI::PassFilter::CreateWithTemplateName(AZ_NAME_LITERAL("ProjectedShadowmapsTemplate"), renderPipeline);
  304. bool primaryPassChanged = true;
  305. RPI::PassSystemInterface::Get()->ForEachPass(projectedPassFilter,
  306. [&](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
  307. {
  308. primaryPassChanged = m_primaryProjectedShadowmapsPass != pass;
  309. return RPI::PassFilterExecutionFlow::StopVisitingPasses;
  310. }
  311. );
  312. if (primaryPassChanged)
  313. {
  314. m_primaryProjectedShadowmapsPass = nullptr;
  315. // Check to see if the esm pass still exists on this pipeline. If so, turn it off before setting the pointer to null.
  316. RPI::PassFilter esmPassFilter = RPI::PassFilter::CreateWithTemplateName(AZ_NAME_LITERAL("EsmShadowmapsTemplate"), renderPipeline);
  317. RPI::PassSystemInterface::Get()->ForEachPass(esmPassFilter,
  318. [&](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
  319. {
  320. if (pass == m_primaryEsmShadowmapsPass)
  321. {
  322. m_primaryEsmShadowmapsPass->SetEnabledComputation(false);
  323. }
  324. return RPI::PassFilterExecutionFlow::StopVisitingPasses;
  325. }
  326. );
  327. m_primaryEsmShadowmapsPass = nullptr;
  328. }
  329. }
  330. }
  331. void ProjectedShadowFeatureProcessor::CachePasses(RPI::RenderPipeline* renderPipeline)
  332. {
  333. // Find the Projected Shadow pass in a given render pipeline and update it.
  334. RPI::PassFilter projectedPassFilter = RPI::PassFilter::CreateWithTemplateName(AZ_NAME_LITERAL("ProjectedShadowmapsTemplate"), renderPipeline);
  335. RPI::PassSystemInterface::Get()->ForEachPass(projectedPassFilter,
  336. [&](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
  337. {
  338. if (m_projectedShadowmapsPasses.contains(renderPipeline))
  339. {
  340. AZ_Error("ProjectedShadowFeatureProcessor", false, "Found multiple projected shadowmap passes in pipeline.");
  341. return RPI::PassFilterExecutionFlow::StopVisitingPasses;
  342. }
  343. ProjectedShadowmapsPass* shadowmapPass = static_cast<ProjectedShadowmapsPass*>(pass);
  344. shadowmapPass->SetAtlasAttachmentImage(m_atlasImage);
  345. m_projectedShadowmapsPasses[renderPipeline] = shadowmapPass;
  346. return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; // continue to check for multiple (error case)
  347. }
  348. );
  349. // Find the ESM shadow pass in a given render pipeline and update it.
  350. RPI::PassFilter esmPassFilter = RPI::PassFilter::CreateWithTemplateName(AZ_NAME_LITERAL("EsmShadowmapsTemplate"), renderPipeline);
  351. RPI::PassSystemInterface::Get()->ForEachPass(esmPassFilter,
  352. [&](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
  353. {
  354. EsmShadowmapsPass* esmShadowmapsPass = static_cast<EsmShadowmapsPass*>(pass);
  355. if (esmShadowmapsPass->GetLightTypeName() == Name("projected"))
  356. {
  357. if (m_esmShadowmapsPasses.contains(renderPipeline))
  358. {
  359. AZ_Error("ProjectedShadowFeatureProcessor", false, "Found multiple esm shadowmap passes for projected shadows in pipeline.");
  360. return RPI::PassFilterExecutionFlow::StopVisitingPasses;
  361. }
  362. m_esmShadowmapsPasses[renderPipeline] = esmShadowmapsPass;
  363. if (esmShadowmapsPass != m_primaryEsmShadowmapsPass)
  364. {
  365. esmShadowmapsPass->SetEnabledComputation(false);
  366. }
  367. esmShadowmapsPass->SetAtlasAttachmentImage(m_esmAtlasImage);
  368. }
  369. return RPI::PassFilterExecutionFlow::ContinueVisitingPasses; // continue to check for multiple (error case)
  370. }
  371. );
  372. }
  373. void ProjectedShadowFeatureProcessor::UpdatePrimaryPasses()
  374. {
  375. // Find a new m_primaryProjectedShadowmapsPass. This needs to be the first ProjectedShadowmapsPass
  376. // in the list of pipelines to ensure it calculates the shadows before any other pipelines need it.
  377. bool found = false;
  378. for (RPI::RenderPipelinePtr pipeline : GetParentScene()->GetRenderPipelines())
  379. {
  380. auto itr = m_projectedShadowmapsPasses.find(pipeline.get());
  381. if (itr != m_projectedShadowmapsPasses.end())
  382. {
  383. ProjectedShadowmapsPass* pass = itr->second;
  384. if (m_primaryProjectedShadowmapsPass != pass)
  385. {
  386. if (m_primaryProjectedShadowmapsPass != nullptr)
  387. {
  388. for (RPI::Ptr<RPI::Pass> child : m_primaryProjectedShadowmapsPass->GetChildren())
  389. {
  390. child->QueueForRemoval();
  391. }
  392. }
  393. m_primaryProjectedShadowmapsPass = pass;
  394. for (auto& shadowProperty : m_shadowProperties.GetDataVector())
  395. {
  396. size_t shadowIndex = shadowProperty.m_shadowId.GetIndex();
  397. shadowProperty.m_shadowmapPass = CreateShadowmapPass(shadowIndex);
  398. m_primaryProjectedShadowmapsPass->QueueAddChild(shadowProperty.m_shadowmapPass);
  399. }
  400. }
  401. m_primaryShadowPipeline = pipeline.get();
  402. found = true;
  403. break;
  404. }
  405. }
  406. if (!found)
  407. {
  408. m_primaryProjectedShadowmapsPass = nullptr;
  409. m_primaryShadowPipeline = nullptr;
  410. }
  411. if (found && m_esmShadowmapsPasses.contains(m_primaryProjectedShadowmapsPass->GetRenderPipeline()))
  412. {
  413. // Update the primary esm pass to be the one that's on the same pipeline as the primary projected shadowmaps pass.
  414. EsmShadowmapsPass* firstEsmShadomapsPass = m_esmShadowmapsPasses.at(m_primaryProjectedShadowmapsPass->GetRenderPipeline());
  415. if (firstEsmShadomapsPass != m_primaryEsmShadowmapsPass)
  416. {
  417. if (m_primaryEsmShadowmapsPass != nullptr)
  418. {
  419. m_primaryEsmShadowmapsPass->SetEnabledComputation(false);
  420. }
  421. m_primaryEsmShadowmapsPass = firstEsmShadomapsPass;
  422. // This will enable computation of the primary esm shadow pass later if necessary.
  423. m_filterParameterNeedsUpdate = m_shadowProperties.GetDataCount() > 0;
  424. }
  425. }
  426. else if (m_primaryEsmShadowmapsPass != nullptr)
  427. {
  428. // Either there's no primary projected shadowmaps pass, or there is but there's no esm pass on the same pipeline, so disable
  429. // the primary esm pass if necessary.
  430. RPI::PassFilter esmPassFilter = RPI::PassFilter::CreateWithTemplateName(AZ_NAME_LITERAL("EsmShadowmapsTemplate"), m_primaryShadowPipeline);
  431. RPI::PassSystemInterface::Get()->ForEachPass(esmPassFilter,
  432. [&](RPI::Pass* pass) -> RPI::PassFilterExecutionFlow
  433. {
  434. if (pass == m_primaryEsmShadowmapsPass)
  435. {
  436. m_primaryEsmShadowmapsPass->SetEnabledComputation(false);
  437. }
  438. return RPI::PassFilterExecutionFlow::StopVisitingPasses;
  439. }
  440. );
  441. m_primaryEsmShadowmapsPass = nullptr;
  442. }
  443. if (m_primaryProjectedShadowmapsPass && !m_clearShadowDrawPacket)
  444. {
  445. CreateClearShadowDrawPacket();
  446. }
  447. m_shadowmapPassNeedsUpdate = true;
  448. }
  449. void ProjectedShadowFeatureProcessor::UpdateFilterParameters()
  450. {
  451. if (m_filterParameterNeedsUpdate)
  452. {
  453. UpdateEsmPassEnabled();
  454. SetFilterParameterToPass();
  455. m_filterParameterNeedsUpdate = false;
  456. }
  457. }
  458. void ProjectedShadowFeatureProcessor::UpdateEsmPassEnabled()
  459. {
  460. if (m_primaryEsmShadowmapsPass == nullptr)
  461. {
  462. return;
  463. }
  464. bool anyShadowsUseEsm = false;
  465. for (const auto& shadowProperty : m_shadowProperties.GetDataVector())
  466. {
  467. FilterParameter& esmData = m_shadowData.GetElement<FilterParamIndex>(shadowProperty.m_shadowId.GetIndex());
  468. if (esmData.m_isEnabled)
  469. {
  470. anyShadowsUseEsm = true;
  471. break;
  472. }
  473. }
  474. m_primaryEsmShadowmapsPass->SetEnabledComputation(anyShadowsUseEsm);
  475. }
  476. void ProjectedShadowFeatureProcessor::SetFilterParameterToPass()
  477. {
  478. static uint32_t nameIndex = 0;
  479. // Create index table buffer.
  480. // [GFX TODO ATOM-14851] Should not be creating a new buffer here, just map the data or orphan with new data.
  481. const AZStd::string indexTableBufferName = AZStd::string::format("IndexTableBuffer(Projected) %d", nameIndex++);
  482. const Data::Instance<RPI::Buffer> indexTableBuffer = m_atlas.CreateShadowmapIndexTableBuffer(indexTableBufferName);
  483. m_filterParamBufferHandler.UpdateBuffer(m_shadowData.GetRawData<FilterParamIndex>(), static_cast<uint32_t>(m_shadowData.GetSize()));
  484. if (m_primaryEsmShadowmapsPass)
  485. {
  486. m_primaryEsmShadowmapsPass->SetShadowmapIndexTableBuffer(indexTableBuffer);
  487. m_primaryEsmShadowmapsPass->SetFilterParameterBuffer(m_filterParamBufferHandler.GetBuffer());
  488. }
  489. }
  490. void ProjectedShadowFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& /*packet*/)
  491. {
  492. AZ_PROFILE_SCOPE(RPI, "ProjectedShadowFeatureProcessor: Simulate");
  493. if (m_shadowmapPassNeedsUpdate && m_primaryProjectedShadowmapsPass)
  494. {
  495. UpdateAtlas();
  496. UpdateShadowPasses();
  497. auto& shadowProperties = m_shadowProperties.GetDataVector();
  498. for (const auto& shadowProperty : shadowProperties)
  499. {
  500. const int16_t shadowIndexInSrg = shadowProperty.m_shadowId.GetIndex();
  501. ShadowData& shadowData = m_shadowData.GetElement<ShadowDataIndex>(shadowIndexInSrg);
  502. FilterParameter& filterData = m_shadowData.GetElement<FilterParamIndex>(shadowIndexInSrg);
  503. const ShadowmapAtlas::Origin origin = m_atlas.GetOrigin(shadowIndexInSrg);
  504. shadowData.m_shadowmapArraySlice = origin.m_arraySlice;
  505. filterData.m_shadowmapOriginInSlice = origin.m_originInSlice;
  506. m_deviceBufferNeedsUpdate = true;
  507. }
  508. if (m_primaryEsmShadowmapsPass != nullptr)
  509. {
  510. m_primaryEsmShadowmapsPass->QueueForBuildAndInitialization();
  511. }
  512. m_shadowmapPassNeedsUpdate = false;
  513. }
  514. // This has to be called after UpdateShadowmapSizes().
  515. UpdateFilterParameters();
  516. if (m_deviceBufferNeedsUpdate)
  517. {
  518. m_shadowBufferHandler.UpdateBuffer(m_shadowData.GetRawData<ShadowDataIndex>(), static_cast<uint32_t>(m_shadowData.GetSize()));
  519. m_deviceBufferNeedsUpdate = false;
  520. }
  521. // Turn off cached esm shadow maps for next frame
  522. for (const auto& shadowProperty : m_shadowProperties.GetDataVector())
  523. {
  524. if (shadowProperty.m_useCachedShadows)
  525. {
  526. FilterParameter& esmData = m_shadowData.GetElement<FilterParamIndex>(shadowProperty.m_shadowId.GetIndex());
  527. if (esmData.m_isEnabled != 0)
  528. {
  529. esmData.m_isEnabled = false;
  530. m_filterParameterNeedsUpdate = true;
  531. }
  532. }
  533. }
  534. }
  535. void ProjectedShadowFeatureProcessor::PrepareViews(const PrepareViewsPacket&, AZStd::vector<AZStd::pair<RPI::PipelineViewTag, RPI::ViewPtr>>& outViews)
  536. {
  537. if (m_primaryProjectedShadowmapsPass != nullptr)
  538. {
  539. RPI::RenderPipeline* renderPipeline = m_primaryProjectedShadowmapsPass->GetRenderPipeline();
  540. if (renderPipeline)
  541. {
  542. auto& shadowProperties = m_shadowProperties.GetDataVector();
  543. for (ShadowProperty& shadowProperty : shadowProperties)
  544. {
  545. uint16_t shadowIndex = shadowProperty.m_shadowId.GetIndex();
  546. const FilterParameter& filterData = m_shadowData.GetElement<FilterParamIndex>(shadowIndex);
  547. if (filterData.m_shadowmapSize == aznumeric_cast<uint32_t>(ShadowmapSize::None))
  548. {
  549. continue;
  550. }
  551. const RPI::PipelineViewTag& viewTag = shadowProperty.m_shadowmapPass->GetPipelineViewTag();
  552. const RHI::DrawListMask drawListMask = renderPipeline->GetDrawListMask(viewTag);
  553. if (shadowProperty.m_shadowmapView->GetDrawListMask() != drawListMask)
  554. {
  555. shadowProperty.m_shadowmapView->Reset();
  556. shadowProperty.m_shadowmapView->SetDrawListMask(drawListMask);
  557. }
  558. outViews.emplace_back(AZStd::make_pair(viewTag, shadowProperty.m_shadowmapView));
  559. }
  560. }
  561. }
  562. }
  563. void ProjectedShadowFeatureProcessor::Render(const FeatureProcessor::RenderPacket& packet)
  564. {
  565. AZ_PROFILE_SCOPE(RPI, "ProjectedShadowFeatureProcessor: Render");
  566. if (m_primaryProjectedShadowmapsPass != nullptr)
  567. {
  568. for (const RPI::ViewPtr& view : packet.m_views)
  569. {
  570. if (view->GetUsageFlags() & RPI::View::UsageFlags::UsageCamera)
  571. {
  572. RPI::ShaderResourceGroup* srg = view->GetShaderResourceGroup().get();
  573. float shadowMapAtlasSize = static_cast<float>(m_atlas.GetBaseShadowmapSize());
  574. srg->SetConstant(m_shadowmapAtlasSizeIndex, shadowMapAtlasSize);
  575. const float invShadowmapSize = 1.0f / shadowMapAtlasSize;
  576. srg->SetConstant(m_invShadowmapAtlasSizeIndex, invShadowmapSize);
  577. m_shadowBufferHandler.UpdateSrg(srg);
  578. m_filterParamBufferHandler.UpdateSrg(srg);
  579. }
  580. }
  581. }
  582. }
  583. bool ProjectedShadowFeatureProcessor::FilterMethodIsEsm(const ShadowData& shadowData) const
  584. {
  585. return
  586. aznumeric_cast<ShadowFilterMethod>(shadowData.m_shadowFilterMethod) == ShadowFilterMethod::Esm ||
  587. aznumeric_cast<ShadowFilterMethod>(shadowData.m_shadowFilterMethod) == ShadowFilterMethod::EsmPcf;
  588. }
  589. auto ProjectedShadowFeatureProcessor::GetShadowPropertyFromShadowId(ShadowId id) -> ShadowProperty&
  590. {
  591. AZ_Assert(id.IsValid(), "Error: Invalid ShadowId");
  592. uint16_t shadowPropertyId = m_shadowData.GetElement<ShadowPropertyIdIndex>(id.GetIndex());
  593. return m_shadowProperties.GetData(shadowPropertyId);
  594. }
  595. void ProjectedShadowFeatureProcessor::CreateClearShadowDrawPacket()
  596. {
  597. // Force load of shader to clear shadow maps.
  598. const AZStd::string clearShadowShaderFilePath = "Shaders/Shadow/ClearShadow.azshader";
  599. Data::Asset<RPI::ShaderAsset> shaderAsset = RPI::AssetUtils::LoadCriticalAsset<RPI::ShaderAsset>
  600. (clearShadowShaderFilePath, RPI::AssetUtils::TraceLevel::Assert);
  601. m_clearShadowShader = RPI::Shader::FindOrCreate(shaderAsset);
  602. const RPI::ShaderVariant& variant = m_clearShadowShader->GetRootVariant();
  603. RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor;
  604. variant.ConfigurePipelineState(pipelineStateDescriptor);
  605. [[maybe_unused]] bool foundPipelineState = GetParentScene()->ConfigurePipelineState(m_clearShadowShader->GetDrawListTag(), pipelineStateDescriptor);
  606. AZ_Assert(foundPipelineState, "Could not find pipeline state for ClearShadow shader's draw list '%s'", shaderAsset->GetDrawListName().GetCStr())
  607. RHI::InputStreamLayoutBuilder layoutBuilder;
  608. pipelineStateDescriptor.m_inputStreamLayout = layoutBuilder.End();
  609. const RHI::PipelineState* pipelineState = m_clearShadowShader->AcquirePipelineState(pipelineStateDescriptor);
  610. if (!pipelineState)
  611. {
  612. AZ_Assert(false, "Shader '%s'. Failed to acquire default pipeline state", shaderAsset->GetName().GetCStr());
  613. return;
  614. }
  615. RHI::DrawPacketBuilder drawPacketBuilder;
  616. drawPacketBuilder.Begin(nullptr);
  617. drawPacketBuilder.SetDrawArguments(RHI::DrawLinear(1, 0, 3, 0));
  618. RHI::DrawPacketBuilder::DrawRequest drawRequest;
  619. drawRequest.m_listTag = m_clearShadowShader->GetDrawListTag();
  620. drawRequest.m_pipelineState = pipelineState;
  621. drawRequest.m_sortKey = AZStd::numeric_limits<RHI::DrawItemSortKey>::min();
  622. drawPacketBuilder.AddDrawItem(drawRequest);
  623. m_clearShadowDrawPacket = drawPacketBuilder.End();
  624. }
  625. void ProjectedShadowFeatureProcessor::UpdateAtlas()
  626. {
  627. // Currently when something changes, the atlas is completely reset. This is ok when most shadows are dynamic,
  628. // but isn't ideal for cached shadows which will need to re-render on the next frame.
  629. m_atlas.Initialize();
  630. auto& shadowProperties = m_shadowProperties.GetDataVector();
  631. bool needsEsm = false;
  632. for (const auto& shadowProperty : shadowProperties)
  633. {
  634. uint16_t shadowIndex = shadowProperty.m_shadowId.GetIndex();
  635. FilterParameter& filterData = m_shadowData.GetElement<FilterParamIndex>(shadowIndex);
  636. needsEsm = needsEsm || filterData.m_isEnabled;
  637. m_atlas.SetShadowmapSize(shadowIndex, static_cast<ShadowmapSize>(filterData.m_shadowmapSize));
  638. }
  639. m_atlas.Finalize();
  640. auto createAtlas = [&](RHI::Format format, RHI::ImageBindFlags bindFlags, RHI::ImageAspectFlags aspectFlags, AZStd::string name)
  641. ->Data::Instance<RPI::AttachmentImage>
  642. {
  643. RHI::ImageDescriptor imageDescriptor;
  644. const uint32_t shadowmapSize = static_cast<uint32_t>(m_atlas.GetBaseShadowmapSize());
  645. imageDescriptor.m_size = RHI::Size(shadowmapSize, shadowmapSize, 1);
  646. imageDescriptor.m_format = format;
  647. imageDescriptor.m_arraySize = m_atlas.GetArraySliceCount();
  648. imageDescriptor.m_bindFlags |= bindFlags;
  649. imageDescriptor.m_sharedQueueMask = RHI::HardwareQueueClassMask::Graphics;
  650. // The ImageViewDescriptor must be specified to make sure the frame graph compiler doesn't treat this as a transient image.
  651. RHI::ImageViewDescriptor viewDesc = RHI::ImageViewDescriptor::Create(imageDescriptor.m_format, 0, 0);
  652. viewDesc.m_aspectFlags = aspectFlags;
  653. RPI::CreateAttachmentImageRequest createImageRequest;
  654. createImageRequest.m_imagePool = RPI::ImageSystemInterface::Get()->GetSystemAttachmentPool().get();
  655. createImageRequest.m_imageDescriptor = imageDescriptor;
  656. createImageRequest.m_imageName = AZStd::string::format("%s.%s", name.c_str(), GetParentScene()->GetName().GetCStr());
  657. createImageRequest.m_imageViewDescriptor = &viewDesc;
  658. return RPI::AttachmentImage::Create(createImageRequest);
  659. };
  660. m_atlasImage = createAtlas(RHI::Format::D32_FLOAT, RHI::ImageBindFlags::Depth, RHI::ImageAspectFlags::Depth, "ProjectedShadowAtlas");
  661. for (auto& [key, projectedShadowmapsPass] : m_projectedShadowmapsPasses)
  662. {
  663. projectedShadowmapsPass->SetAtlasAttachmentImage(m_atlasImage);
  664. projectedShadowmapsPass->QueueForBuildAndInitialization();
  665. }
  666. if (needsEsm)
  667. {
  668. m_esmAtlasImage = createAtlas(RHI::Format::R16_FLOAT, RHI::ImageBindFlags::ShaderReadWrite, RHI::ImageAspectFlags::Color, "ProjectedShadowAtlasESM");
  669. for (auto& [key, esmShadowmapsPass] : m_esmShadowmapsPasses)
  670. {
  671. esmShadowmapsPass->SetAtlasAttachmentImage(m_esmAtlasImage);
  672. esmShadowmapsPass->QueueForBuildAndInitialization();
  673. }
  674. }
  675. else
  676. {
  677. m_esmAtlasImage = {};
  678. }
  679. }
  680. RPI::Ptr<ShadowmapPass> ProjectedShadowFeatureProcessor::CreateShadowmapPass(size_t childIndex)
  681. {
  682. const Name passName{ AZStd::string::format("ProjectedShadowmapPass.%zu", childIndex) };
  683. RHI::RHISystemInterface* rhiSystem = RHI::RHISystemInterface::Get();
  684. auto passData = AZStd::make_shared<RPI::RasterPassData>();
  685. passData->m_drawListTag = rhiSystem->GetDrawListTagRegistry()->GetName(m_primaryProjectedShadowmapsPass->GetDrawListTag());
  686. passData->m_pipelineViewTag = AZStd::string::format("%s.%zu", m_primaryProjectedShadowmapsPass->GetPipelineViewTag().GetCStr(), childIndex);
  687. return ShadowmapPass::CreateWithPassRequest(passName, passData);
  688. }
  689. void ProjectedShadowFeatureProcessor::UpdateShadowPasses()
  690. {
  691. struct SliceInfo
  692. {
  693. bool m_hasStaticShadows = false;
  694. AZStd::vector<ShadowmapPass*> m_shadowPasses;
  695. };
  696. AZStd::vector<SliceInfo> sliceInfo(m_atlas.GetArraySliceCount());
  697. for (const auto& it : m_shadowProperties.GetDataVector())
  698. {
  699. // This index indicates the execution order of the passes.
  700. // The first pass to render a slice should clear the slice.
  701. size_t shadowIndex = it.m_shadowId.GetIndex();
  702. auto* pass = it.m_shadowmapPass.get();
  703. const ShadowmapAtlas::Origin origin = m_atlas.GetOrigin(shadowIndex);
  704. pass->SetArraySlice(origin.m_arraySlice);
  705. pass->SetIsStatic(it.m_useCachedShadows);
  706. pass->ForceRenderNextFrame();
  707. const auto& filterData = m_shadowData.GetElement<FilterParamIndex>(shadowIndex);
  708. if (filterData.m_shadowmapSize != static_cast<uint32_t>(ShadowmapSize::None))
  709. {
  710. const RHI::Viewport viewport(
  711. origin.m_originInSlice[0] * 1.f,
  712. (origin.m_originInSlice[0] + filterData.m_shadowmapSize) * 1.f,
  713. origin.m_originInSlice[1] * 1.f,
  714. (origin.m_originInSlice[1] + filterData.m_shadowmapSize) * 1.f);
  715. const RHI::Scissor scissor(
  716. origin.m_originInSlice[0],
  717. origin.m_originInSlice[1],
  718. origin.m_originInSlice[0] + filterData.m_shadowmapSize,
  719. origin.m_originInSlice[1] + filterData.m_shadowmapSize);
  720. pass->SetViewportScissor(viewport, scissor);
  721. pass->SetClearEnabled(false);
  722. SliceInfo& sliceInfoItem = sliceInfo.at(origin.m_arraySlice);
  723. sliceInfoItem.m_shadowPasses.push_back(pass);
  724. sliceInfoItem.m_hasStaticShadows = sliceInfoItem.m_hasStaticShadows || it.m_useCachedShadows;
  725. }
  726. }
  727. RHI::Handle<uint32_t> casterMovedBit = GetParentScene()->GetViewTagBitRegistry().FindTag(MeshCommon::MeshMovedName);
  728. for (const auto& it : sliceInfo)
  729. {
  730. if (!it.m_hasStaticShadows)
  731. {
  732. if (!it.m_shadowPasses.empty())
  733. {
  734. // no static shadows in this slice, so have the first pass clear the atlas on load.
  735. it.m_shadowPasses.at(0)->SetClearEnabled(true);
  736. }
  737. }
  738. else
  739. {
  740. // There's at least one static shadow in this slice, so passes need to clear themselves using a draw.
  741. for (auto* pass : it.m_shadowPasses)
  742. {
  743. pass->SetClearShadowDrawPacket(m_clearShadowDrawPacket);
  744. pass->SetCasterMovedBit(casterMovedBit);
  745. }
  746. }
  747. }
  748. }
  749. }