3
0

ProjectedShadowFeatureProcessor.cpp 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887
  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. AZ_Error("ProjectedShadowFeatureProcessor", false, "Cannot find a required pass.");
  463. return;
  464. }
  465. bool anyShadowsUseEsm = false;
  466. for (const auto& shadowProperty : m_shadowProperties.GetDataVector())
  467. {
  468. FilterParameter& esmData = m_shadowData.GetElement<FilterParamIndex>(shadowProperty.m_shadowId.GetIndex());
  469. if (esmData.m_isEnabled)
  470. {
  471. anyShadowsUseEsm = true;
  472. break;
  473. }
  474. }
  475. m_primaryEsmShadowmapsPass->SetEnabledComputation(anyShadowsUseEsm);
  476. }
  477. void ProjectedShadowFeatureProcessor::SetFilterParameterToPass()
  478. {
  479. static uint32_t nameIndex = 0;
  480. if (m_primaryProjectedShadowmapsPass == nullptr || m_primaryEsmShadowmapsPass == nullptr)
  481. {
  482. AZ_Error("ProjectedShadowFeatureProcessor", false, "Cannot find a required pass.");
  483. return;
  484. }
  485. // Create index table buffer.
  486. // [GFX TODO ATOM-14851] Should not be creating a new buffer here, just map the data or orphan with new data.
  487. const AZStd::string indexTableBufferName = AZStd::string::format("IndexTableBuffer(Projected) %d", nameIndex++);
  488. const Data::Instance<RPI::Buffer> indexTableBuffer = m_atlas.CreateShadowmapIndexTableBuffer(indexTableBufferName);
  489. m_filterParamBufferHandler.UpdateBuffer(m_shadowData.GetRawData<FilterParamIndex>(), static_cast<uint32_t>(m_shadowData.GetSize()));
  490. m_primaryEsmShadowmapsPass->SetShadowmapIndexTableBuffer(indexTableBuffer);
  491. m_primaryEsmShadowmapsPass->SetFilterParameterBuffer(m_filterParamBufferHandler.GetBuffer());
  492. }
  493. void ProjectedShadowFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& /*packet*/)
  494. {
  495. AZ_PROFILE_SCOPE(RPI, "ProjectedShadowFeatureProcessor: Simulate");
  496. if (m_shadowmapPassNeedsUpdate && m_primaryProjectedShadowmapsPass)
  497. {
  498. UpdateAtlas();
  499. UpdateShadowPasses();
  500. auto& shadowProperties = m_shadowProperties.GetDataVector();
  501. for (const auto& shadowProperty : shadowProperties)
  502. {
  503. const int16_t shadowIndexInSrg = shadowProperty.m_shadowId.GetIndex();
  504. ShadowData& shadowData = m_shadowData.GetElement<ShadowDataIndex>(shadowIndexInSrg);
  505. FilterParameter& filterData = m_shadowData.GetElement<FilterParamIndex>(shadowIndexInSrg);
  506. const ShadowmapAtlas::Origin origin = m_atlas.GetOrigin(shadowIndexInSrg);
  507. shadowData.m_shadowmapArraySlice = origin.m_arraySlice;
  508. filterData.m_shadowmapOriginInSlice = origin.m_originInSlice;
  509. m_deviceBufferNeedsUpdate = true;
  510. }
  511. if (m_primaryEsmShadowmapsPass != nullptr)
  512. {
  513. m_primaryEsmShadowmapsPass->QueueForBuildAndInitialization();
  514. }
  515. m_shadowmapPassNeedsUpdate = false;
  516. }
  517. // This has to be called after UpdateShadowmapSizes().
  518. UpdateFilterParameters();
  519. if (m_deviceBufferNeedsUpdate)
  520. {
  521. m_shadowBufferHandler.UpdateBuffer(m_shadowData.GetRawData<ShadowDataIndex>(), static_cast<uint32_t>(m_shadowData.GetSize()));
  522. m_deviceBufferNeedsUpdate = false;
  523. }
  524. // Turn off cached esm shadow maps for next frame
  525. for (const auto& shadowProperty : m_shadowProperties.GetDataVector())
  526. {
  527. if (shadowProperty.m_useCachedShadows)
  528. {
  529. FilterParameter& esmData = m_shadowData.GetElement<FilterParamIndex>(shadowProperty.m_shadowId.GetIndex());
  530. if (esmData.m_isEnabled != 0)
  531. {
  532. esmData.m_isEnabled = false;
  533. m_filterParameterNeedsUpdate = true;
  534. }
  535. }
  536. }
  537. }
  538. void ProjectedShadowFeatureProcessor::PrepareViews(const PrepareViewsPacket&, AZStd::vector<AZStd::pair<RPI::PipelineViewTag, RPI::ViewPtr>>& outViews)
  539. {
  540. if (m_primaryProjectedShadowmapsPass != nullptr)
  541. {
  542. RPI::RenderPipeline* renderPipeline = m_primaryProjectedShadowmapsPass->GetRenderPipeline();
  543. if (renderPipeline)
  544. {
  545. auto& shadowProperties = m_shadowProperties.GetDataVector();
  546. for (ShadowProperty& shadowProperty : shadowProperties)
  547. {
  548. uint16_t shadowIndex = shadowProperty.m_shadowId.GetIndex();
  549. const FilterParameter& filterData = m_shadowData.GetElement<FilterParamIndex>(shadowIndex);
  550. if (filterData.m_shadowmapSize == aznumeric_cast<uint32_t>(ShadowmapSize::None))
  551. {
  552. continue;
  553. }
  554. const RPI::PipelineViewTag& viewTag = shadowProperty.m_shadowmapPass->GetPipelineViewTag();
  555. const RHI::DrawListMask drawListMask = renderPipeline->GetDrawListMask(viewTag);
  556. if (shadowProperty.m_shadowmapView->GetDrawListMask() != drawListMask)
  557. {
  558. shadowProperty.m_shadowmapView->Reset();
  559. shadowProperty.m_shadowmapView->SetDrawListMask(drawListMask);
  560. }
  561. outViews.emplace_back(AZStd::make_pair(viewTag, shadowProperty.m_shadowmapView));
  562. }
  563. }
  564. }
  565. }
  566. void ProjectedShadowFeatureProcessor::Render(const FeatureProcessor::RenderPacket& packet)
  567. {
  568. AZ_PROFILE_SCOPE(RPI, "ProjectedShadowFeatureProcessor: Render");
  569. if (m_primaryProjectedShadowmapsPass != nullptr)
  570. {
  571. for (const RPI::ViewPtr& view : packet.m_views)
  572. {
  573. if (view->GetUsageFlags() & RPI::View::UsageFlags::UsageCamera)
  574. {
  575. RPI::ShaderResourceGroup* srg = view->GetShaderResourceGroup().get();
  576. float shadowMapAtlasSize = static_cast<float>(m_atlas.GetBaseShadowmapSize());
  577. srg->SetConstant(m_shadowmapAtlasSizeIndex, shadowMapAtlasSize);
  578. const float invShadowmapSize = 1.0f / shadowMapAtlasSize;
  579. srg->SetConstant(m_invShadowmapAtlasSizeIndex, invShadowmapSize);
  580. m_shadowBufferHandler.UpdateSrg(srg);
  581. m_filterParamBufferHandler.UpdateSrg(srg);
  582. }
  583. }
  584. }
  585. }
  586. bool ProjectedShadowFeatureProcessor::FilterMethodIsEsm(const ShadowData& shadowData) const
  587. {
  588. return
  589. aznumeric_cast<ShadowFilterMethod>(shadowData.m_shadowFilterMethod) == ShadowFilterMethod::Esm ||
  590. aznumeric_cast<ShadowFilterMethod>(shadowData.m_shadowFilterMethod) == ShadowFilterMethod::EsmPcf;
  591. }
  592. auto ProjectedShadowFeatureProcessor::GetShadowPropertyFromShadowId(ShadowId id) -> ShadowProperty&
  593. {
  594. AZ_Assert(id.IsValid(), "Error: Invalid ShadowId");
  595. uint16_t shadowPropertyId = m_shadowData.GetElement<ShadowPropertyIdIndex>(id.GetIndex());
  596. return m_shadowProperties.GetData(shadowPropertyId);
  597. }
  598. void ProjectedShadowFeatureProcessor::CreateClearShadowDrawPacket()
  599. {
  600. // Force load of shader to clear shadow maps.
  601. const AZStd::string clearShadowShaderFilePath = "Shaders/Shadow/ClearShadow.azshader";
  602. Data::Asset<RPI::ShaderAsset> shaderAsset = RPI::AssetUtils::LoadCriticalAsset<RPI::ShaderAsset>
  603. (clearShadowShaderFilePath, RPI::AssetUtils::TraceLevel::Assert);
  604. m_clearShadowShader = RPI::Shader::FindOrCreate(shaderAsset);
  605. const RPI::ShaderVariant& variant = m_clearShadowShader->GetRootVariant();
  606. RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor;
  607. variant.ConfigurePipelineState(pipelineStateDescriptor);
  608. [[maybe_unused]] bool foundPipelineState = GetParentScene()->ConfigurePipelineState(m_clearShadowShader->GetDrawListTag(), pipelineStateDescriptor);
  609. AZ_Assert(foundPipelineState, "Could not find pipeline state for ClearShadow shader's draw list '%s'", shaderAsset->GetDrawListName().GetCStr())
  610. RHI::InputStreamLayoutBuilder layoutBuilder;
  611. pipelineStateDescriptor.m_inputStreamLayout = layoutBuilder.End();
  612. const RHI::PipelineState* pipelineState = m_clearShadowShader->AcquirePipelineState(pipelineStateDescriptor);
  613. if (!pipelineState)
  614. {
  615. AZ_Assert(false, "Shader '%s'. Failed to acquire default pipeline state", shaderAsset->GetName().GetCStr());
  616. return;
  617. }
  618. RHI::DrawPacketBuilder drawPacketBuilder;
  619. drawPacketBuilder.Begin(nullptr);
  620. drawPacketBuilder.SetDrawArguments(RHI::DrawLinear(1, 0, 3, 0));
  621. RHI::DrawPacketBuilder::DrawRequest drawRequest;
  622. drawRequest.m_listTag = m_clearShadowShader->GetDrawListTag();
  623. drawRequest.m_pipelineState = pipelineState;
  624. drawRequest.m_sortKey = AZStd::numeric_limits<RHI::DrawItemSortKey>::min();
  625. drawPacketBuilder.AddDrawItem(drawRequest);
  626. m_clearShadowDrawPacket = drawPacketBuilder.End();
  627. }
  628. void ProjectedShadowFeatureProcessor::UpdateAtlas()
  629. {
  630. // Currently when something changes, the atlas is completely reset. This is ok when most shadows are dynamic,
  631. // but isn't ideal for cached shadows which will need to re-render on the next frame.
  632. m_atlas.Initialize();
  633. auto& shadowProperties = m_shadowProperties.GetDataVector();
  634. bool needsEsm = false;
  635. for (const auto& shadowProperty : shadowProperties)
  636. {
  637. uint16_t shadowIndex = shadowProperty.m_shadowId.GetIndex();
  638. FilterParameter& filterData = m_shadowData.GetElement<FilterParamIndex>(shadowIndex);
  639. needsEsm = needsEsm || filterData.m_isEnabled;
  640. m_atlas.SetShadowmapSize(shadowIndex, static_cast<ShadowmapSize>(filterData.m_shadowmapSize));
  641. }
  642. m_atlas.Finalize();
  643. auto createAtlas = [&](RHI::Format format, RHI::ImageBindFlags bindFlags, RHI::ImageAspectFlags aspectFlags, AZStd::string name)
  644. ->Data::Instance<RPI::AttachmentImage>
  645. {
  646. RHI::ImageDescriptor imageDescriptor;
  647. const uint32_t shadowmapSize = static_cast<uint32_t>(m_atlas.GetBaseShadowmapSize());
  648. imageDescriptor.m_size = RHI::Size(shadowmapSize, shadowmapSize, 1);
  649. imageDescriptor.m_format = format;
  650. imageDescriptor.m_arraySize = m_atlas.GetArraySliceCount();
  651. imageDescriptor.m_bindFlags |= bindFlags;
  652. imageDescriptor.m_sharedQueueMask = RHI::HardwareQueueClassMask::Graphics;
  653. // The ImageViewDescriptor must be specified to make sure the frame graph compiler doesn't treat this as a transient image.
  654. RHI::ImageViewDescriptor viewDesc = RHI::ImageViewDescriptor::Create(imageDescriptor.m_format, 0, 0);
  655. viewDesc.m_aspectFlags = aspectFlags;
  656. RPI::CreateAttachmentImageRequest createImageRequest;
  657. createImageRequest.m_imagePool = RPI::ImageSystemInterface::Get()->GetSystemAttachmentPool().get();
  658. createImageRequest.m_imageDescriptor = imageDescriptor;
  659. createImageRequest.m_imageName = AZStd::string::format("%s.%s", name.c_str(), GetParentScene()->GetName().GetCStr());
  660. createImageRequest.m_imageViewDescriptor = &viewDesc;
  661. return RPI::AttachmentImage::Create(createImageRequest);
  662. };
  663. m_atlasImage = createAtlas(RHI::Format::D32_FLOAT, RHI::ImageBindFlags::Depth, RHI::ImageAspectFlags::Depth, "ProjectedShadowAtlas");
  664. for (auto& [key, projectedShadowmapsPass] : m_projectedShadowmapsPasses)
  665. {
  666. projectedShadowmapsPass->SetAtlasAttachmentImage(m_atlasImage);
  667. projectedShadowmapsPass->QueueForBuildAndInitialization();
  668. }
  669. if (needsEsm)
  670. {
  671. m_esmAtlasImage = createAtlas(RHI::Format::R16_FLOAT, RHI::ImageBindFlags::ShaderReadWrite, RHI::ImageAspectFlags::Color, "ProjectedShadowAtlasESM");
  672. for (auto& [key, esmShadowmapsPass] : m_esmShadowmapsPasses)
  673. {
  674. esmShadowmapsPass->SetAtlasAttachmentImage(m_esmAtlasImage);
  675. esmShadowmapsPass->QueueForBuildAndInitialization();
  676. }
  677. }
  678. else
  679. {
  680. m_esmAtlasImage = {};
  681. }
  682. }
  683. RPI::Ptr<ShadowmapPass> ProjectedShadowFeatureProcessor::CreateShadowmapPass(size_t childIndex)
  684. {
  685. const Name passName{ AZStd::string::format("ProjectedShadowmapPass.%zu", childIndex) };
  686. RHI::RHISystemInterface* rhiSystem = RHI::RHISystemInterface::Get();
  687. auto passData = AZStd::make_shared<RPI::RasterPassData>();
  688. passData->m_drawListTag = rhiSystem->GetDrawListTagRegistry()->GetName(m_primaryProjectedShadowmapsPass->GetDrawListTag());
  689. passData->m_pipelineViewTag = AZStd::string::format("%s.%zu", m_primaryProjectedShadowmapsPass->GetPipelineViewTag().GetCStr(), childIndex);
  690. return ShadowmapPass::CreateWithPassRequest(passName, passData);
  691. }
  692. void ProjectedShadowFeatureProcessor::UpdateShadowPasses()
  693. {
  694. struct SliceInfo
  695. {
  696. bool m_hasStaticShadows = false;
  697. AZStd::vector<ShadowmapPass*> m_shadowPasses;
  698. };
  699. AZStd::vector<SliceInfo> sliceInfo(m_atlas.GetArraySliceCount());
  700. for (const auto& it : m_shadowProperties.GetDataVector())
  701. {
  702. // This index indicates the execution order of the passes.
  703. // The first pass to render a slice should clear the slice.
  704. size_t shadowIndex = it.m_shadowId.GetIndex();
  705. auto* pass = it.m_shadowmapPass.get();
  706. const ShadowmapAtlas::Origin origin = m_atlas.GetOrigin(shadowIndex);
  707. pass->SetArraySlice(origin.m_arraySlice);
  708. pass->SetIsStatic(it.m_useCachedShadows);
  709. pass->ForceRenderNextFrame();
  710. const auto& filterData = m_shadowData.GetElement<FilterParamIndex>(shadowIndex);
  711. if (filterData.m_shadowmapSize != static_cast<uint32_t>(ShadowmapSize::None))
  712. {
  713. const RHI::Viewport viewport(
  714. origin.m_originInSlice[0] * 1.f,
  715. (origin.m_originInSlice[0] + filterData.m_shadowmapSize) * 1.f,
  716. origin.m_originInSlice[1] * 1.f,
  717. (origin.m_originInSlice[1] + filterData.m_shadowmapSize) * 1.f);
  718. const RHI::Scissor scissor(
  719. origin.m_originInSlice[0],
  720. origin.m_originInSlice[1],
  721. origin.m_originInSlice[0] + filterData.m_shadowmapSize,
  722. origin.m_originInSlice[1] + filterData.m_shadowmapSize);
  723. pass->SetViewportScissor(viewport, scissor);
  724. pass->SetClearEnabled(false);
  725. SliceInfo& sliceInfoItem = sliceInfo.at(origin.m_arraySlice);
  726. sliceInfoItem.m_shadowPasses.push_back(pass);
  727. sliceInfoItem.m_hasStaticShadows = sliceInfoItem.m_hasStaticShadows || it.m_useCachedShadows;
  728. }
  729. }
  730. RHI::Handle<uint32_t> casterMovedBit = GetParentScene()->GetViewTagBitRegistry().FindTag(MeshCommon::MeshMovedName);
  731. for (const auto& it : sliceInfo)
  732. {
  733. if (!it.m_hasStaticShadows)
  734. {
  735. if (!it.m_shadowPasses.empty())
  736. {
  737. // no static shadows in this slice, so have the first pass clear the atlas on load.
  738. it.m_shadowPasses.at(0)->SetClearEnabled(true);
  739. }
  740. }
  741. else
  742. {
  743. // There's at least one static shadow in this slice, so passes need to clear themselves using a draw.
  744. for (auto* pass : it.m_shadowPasses)
  745. {
  746. pass->SetClearShadowDrawPacket(m_clearShadowDrawPacket);
  747. pass->SetCasterMovedBit(casterMovedBit);
  748. }
  749. }
  750. }
  751. }
  752. }