ShadowMapping.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870
  1. // Copyright (C) 2009-2019, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #include <anki/renderer/ShadowMapping.h>
  6. #include <anki/renderer/Renderer.h>
  7. #include <anki/renderer/RenderQueue.h>
  8. #include <anki/core/App.h>
  9. #include <anki/core/Trace.h>
  10. #include <anki/misc/ConfigSet.h>
  11. #include <anki/util/ThreadHive.h>
  12. namespace anki
  13. {
  14. class ShadowMapping::ScratchBufferWorkItem
  15. {
  16. public:
  17. Array<U32, 4> m_viewport;
  18. RenderQueue* m_renderQueue;
  19. U32 m_firstRenderableElement;
  20. U32 m_renderableElementCount;
  21. U32 m_threadPoolTaskIdx;
  22. };
  23. class ShadowMapping::LightToRenderToScratchInfo
  24. {
  25. public:
  26. Array<U32, 4> m_viewport;
  27. RenderQueue* m_renderQueue;
  28. U32 m_drawcallCount;
  29. };
  30. class ShadowMapping::EsmResolveWorkItem
  31. {
  32. public:
  33. Vec4 m_uvIn; ///< UV + size that point to the scratch buffer.
  34. Array<U32, 4> m_viewportOut; ///< Viewport in the ESM RT.
  35. F32 m_cameraNear;
  36. F32 m_cameraFar;
  37. Bool m_blur;
  38. Bool m_perspectiveProjection;
  39. };
  40. ShadowMapping::~ShadowMapping()
  41. {
  42. }
  43. Error ShadowMapping::init(const ConfigSet& config)
  44. {
  45. ANKI_R_LOGI("Initializing shadowmapping");
  46. Error err = initInternal(config);
  47. if(err)
  48. {
  49. ANKI_R_LOGE("Failed to initialize shadowmapping");
  50. }
  51. ANKI_R_LOGI("\tScratch size %ux%u. ESM atlas size %ux%u",
  52. m_scratchTileCountX * m_scratchTileResolution,
  53. m_scratchTileCountY * m_scratchTileResolution,
  54. m_esmTileCountBothAxis * m_esmTileResolution,
  55. m_esmTileCountBothAxis * m_esmTileResolution);
  56. return err;
  57. }
  58. Error ShadowMapping::initScratch(const ConfigSet& cfg)
  59. {
  60. // Init the shadowmaps and FBs
  61. {
  62. m_scratchTileCountX = cfg.getNumber("r.shadowMapping.scratchTileCountX");
  63. m_scratchTileCountY = cfg.getNumber("r.shadowMapping.scratchTileCountY");
  64. m_scratchTileResolution = cfg.getNumber("r.shadowMapping.tileResolution");
  65. // RT
  66. m_scratchRtDescr = m_r->create2DRenderTargetDescription(m_scratchTileResolution * m_scratchTileCountX,
  67. m_scratchTileResolution * m_scratchTileCountY,
  68. SHADOW_DEPTH_PIXEL_FORMAT,
  69. "Scratch ShadMap");
  70. m_scratchRtDescr.bake();
  71. // FB
  72. m_scratchFbDescr.m_depthStencilAttachment.m_loadOperation = AttachmentLoadOperation::CLEAR;
  73. m_scratchFbDescr.m_depthStencilAttachment.m_clearValue.m_depthStencil.m_depth = 1.0f;
  74. m_scratchFbDescr.m_depthStencilAttachment.m_aspect = DepthStencilAspectBit::DEPTH;
  75. m_scratchFbDescr.bake();
  76. }
  77. m_scratchTileAlloc.init(getAllocator(), m_scratchTileCountX, m_scratchTileCountY, m_lodCount, false);
  78. return Error::NONE;
  79. }
  80. Error ShadowMapping::initEsm(const ConfigSet& cfg)
  81. {
  82. // Init RTs and FBs
  83. {
  84. m_esmTileResolution = cfg.getNumber("r.shadowMapping.tileResolution");
  85. m_esmTileCountBothAxis = cfg.getNumber("r.shadowMapping.tileCountPerRowOrColumn");
  86. // RT
  87. TextureInitInfo texinit = m_r->create2DRenderTargetInitInfo(m_esmTileResolution * m_esmTileCountBothAxis,
  88. m_esmTileResolution * m_esmTileCountBothAxis,
  89. SHADOW_COLOR_PIXEL_FORMAT,
  90. TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE
  91. | TextureUsageBit::SAMPLED_COMPUTE,
  92. "esm");
  93. texinit.m_initialUsage = TextureUsageBit::SAMPLED_FRAGMENT;
  94. ClearValue clearVal;
  95. clearVal.m_colorf[0] = 1.0f;
  96. m_esmAtlas = m_r->createAndClearRenderTarget(texinit, clearVal);
  97. // FB
  98. m_esmFbDescr.m_colorAttachments[0].m_loadOperation = AttachmentLoadOperation::LOAD;
  99. m_esmFbDescr.m_colorAttachmentCount = 1;
  100. m_esmFbDescr.bake();
  101. }
  102. // Tiles
  103. m_esmTileAlloc.init(getAllocator(), m_esmTileCountBothAxis, m_esmTileCountBothAxis, m_lodCount, true);
  104. // Programs and shaders
  105. {
  106. ANKI_CHECK(
  107. getResourceManager().loadResource("shaders/ExponentialShadowmappingResolve.glslp", m_esmResolveProg));
  108. ShaderProgramResourceConstantValueInitList<1> consts(m_esmResolveProg);
  109. consts.add("INPUT_TEXTURE_SIZE",
  110. UVec2(m_scratchTileCountX * m_scratchTileResolution, m_scratchTileCountY * m_scratchTileResolution));
  111. const ShaderProgramResourceVariant* variant;
  112. m_esmResolveProg->getOrCreateVariant(consts.get(), variant);
  113. m_esmResolveGrProg = variant->getProgram();
  114. }
  115. return Error::NONE;
  116. }
  117. Error ShadowMapping::initInternal(const ConfigSet& cfg)
  118. {
  119. ANKI_CHECK(initScratch(cfg));
  120. ANKI_CHECK(initEsm(cfg));
  121. m_lodDistances[0] = cfg.getNumber("r.shadowMapping.lightLodDistance0");
  122. m_lodDistances[1] = cfg.getNumber("r.shadowMapping.lightLodDistance1");
  123. return Error::NONE;
  124. }
  125. void ShadowMapping::runEsm(RenderPassWorkContext& rgraphCtx)
  126. {
  127. ANKI_ASSERT(m_esmResolveWorkItems.getSize());
  128. ANKI_TRACE_SCOPED_EVENT(R_SM);
  129. CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
  130. cmdb->bindShaderProgram(m_esmResolveGrProg);
  131. cmdb->bindSampler(0, 0, m_r->getSamplers().m_trilinearClamp);
  132. rgraphCtx.bindTexture(0, 1, m_scratchRt, TextureSubresourceInfo(DepthStencilAspectBit::DEPTH));
  133. for(const EsmResolveWorkItem& workItem : m_esmResolveWorkItems)
  134. {
  135. ANKI_TRACE_INC_COUNTER(R_SHADOW_PASSES, 1);
  136. cmdb->setViewport(
  137. workItem.m_viewportOut[0], workItem.m_viewportOut[1], workItem.m_viewportOut[2], workItem.m_viewportOut[3]);
  138. cmdb->setScissor(
  139. workItem.m_viewportOut[0], workItem.m_viewportOut[1], workItem.m_viewportOut[2], workItem.m_viewportOut[3]);
  140. struct Uniforms
  141. {
  142. Vec2 m_uvScale;
  143. Vec2 m_uvTranslation;
  144. F32 m_near;
  145. F32 m_far;
  146. U32 m_renderingTechnique;
  147. U32 m_padding;
  148. } unis;
  149. unis.m_uvScale = workItem.m_uvIn.zw();
  150. unis.m_uvTranslation = workItem.m_uvIn.xy();
  151. unis.m_near = workItem.m_cameraNear;
  152. unis.m_far = workItem.m_cameraFar;
  153. if(workItem.m_perspectiveProjection)
  154. {
  155. unis.m_renderingTechnique = (workItem.m_blur) ? 0 : 1;
  156. }
  157. else
  158. {
  159. unis.m_renderingTechnique = (workItem.m_blur) ? 2 : 3;
  160. }
  161. cmdb->setPushConstants(&unis, sizeof(unis));
  162. drawQuad(cmdb);
  163. }
  164. // Restore GR state
  165. cmdb->setScissor(0, 0, MAX_U32, MAX_U32);
  166. }
  167. void ShadowMapping::runShadowMapping(RenderPassWorkContext& rgraphCtx)
  168. {
  169. ANKI_ASSERT(m_scratchWorkItems.getSize());
  170. ANKI_TRACE_SCOPED_EVENT(R_SM);
  171. CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
  172. const U threadIdx = rgraphCtx.m_currentSecondLevelCommandBufferIndex;
  173. for(ScratchBufferWorkItem& work : m_scratchWorkItems)
  174. {
  175. if(work.m_threadPoolTaskIdx != threadIdx)
  176. {
  177. continue;
  178. }
  179. // Set state
  180. cmdb->setViewport(work.m_viewport[0], work.m_viewport[1], work.m_viewport[2], work.m_viewport[3]);
  181. cmdb->setScissor(work.m_viewport[0], work.m_viewport[1], work.m_viewport[2], work.m_viewport[3]);
  182. m_r->getSceneDrawer().drawRange(Pass::SM,
  183. work.m_renderQueue->m_viewMatrix,
  184. work.m_renderQueue->m_viewProjectionMatrix,
  185. Mat4::getIdentity(), // Don't care about prev matrices here
  186. cmdb,
  187. work.m_renderQueue->m_renderables.getBegin() + work.m_firstRenderableElement,
  188. work.m_renderQueue->m_renderables.getBegin() + work.m_firstRenderableElement
  189. + work.m_renderableElementCount);
  190. }
  191. }
  192. void ShadowMapping::populateRenderGraph(RenderingContext& ctx)
  193. {
  194. ANKI_TRACE_SCOPED_EVENT(R_SM);
  195. // First process the lights
  196. U32 threadCountForScratchPass = 0;
  197. processLights(ctx, threadCountForScratchPass);
  198. // Build the render graph
  199. RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
  200. if(m_scratchWorkItems.getSize())
  201. {
  202. // Will have to create render passes
  203. // Scratch pass
  204. {
  205. // Compute render area
  206. const U32 minx = 0, miny = 0;
  207. const U32 height = m_scratchMaxViewportHeight;
  208. const U32 width = m_scratchMaxViewportWidth;
  209. GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass("SM scratch");
  210. m_scratchRt = rgraph.newRenderTarget(m_scratchRtDescr);
  211. pass.setFramebufferInfo(m_scratchFbDescr, {}, m_scratchRt, minx, miny, width, height);
  212. ANKI_ASSERT(
  213. threadCountForScratchPass && threadCountForScratchPass <= m_r->getThreadHive().getThreadCount());
  214. pass.setWork(
  215. [](RenderPassWorkContext& rgraphCtx) {
  216. static_cast<ShadowMapping*>(rgraphCtx.m_userData)->runShadowMapping(rgraphCtx);
  217. },
  218. this,
  219. threadCountForScratchPass);
  220. TextureSubresourceInfo subresource = TextureSubresourceInfo(DepthStencilAspectBit::DEPTH);
  221. pass.newDependency({m_scratchRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE, subresource});
  222. }
  223. // ESM pass
  224. {
  225. GraphicsRenderPassDescription& pass = rgraph.newGraphicsRenderPass("ESM");
  226. m_esmRt = rgraph.importRenderTarget(m_esmAtlas, TextureUsageBit::SAMPLED_FRAGMENT);
  227. pass.setFramebufferInfo(m_esmFbDescr, {{m_esmRt}}, {});
  228. pass.setWork(
  229. [](RenderPassWorkContext& rgraphCtx) {
  230. static_cast<ShadowMapping*>(rgraphCtx.m_userData)->runEsm(rgraphCtx);
  231. },
  232. this,
  233. 0);
  234. pass.newDependency(
  235. {m_scratchRt, TextureUsageBit::SAMPLED_FRAGMENT, TextureSubresourceInfo(DepthStencilAspectBit::DEPTH)});
  236. pass.newDependency({m_esmRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
  237. }
  238. }
  239. else
  240. {
  241. // No need for shadowmapping passes, just import the ESM atlas
  242. m_esmRt = rgraph.importRenderTarget(m_esmAtlas, TextureUsageBit::SAMPLED_FRAGMENT);
  243. }
  244. }
  245. Mat4 ShadowMapping::createSpotLightTextureMatrix(const Viewport& viewport) const
  246. {
  247. const F32 atlasSize = m_esmTileResolution * m_esmTileCountBothAxis;
  248. const Vec2 uv(F32(viewport[0]) / atlasSize, F32(viewport[1]) / atlasSize);
  249. ANKI_ASSERT(uv >= Vec2(0.0f) && uv <= Vec2(1.0f));
  250. ANKI_ASSERT(viewport[2] == viewport[3]);
  251. const F32 sizeTextureSpace = F32(viewport[2]) / atlasSize;
  252. return Mat4(sizeTextureSpace,
  253. 0.0f,
  254. 0.0f,
  255. uv.x(),
  256. 0.0f,
  257. sizeTextureSpace,
  258. 0.0f,
  259. uv.y(),
  260. 0.0f,
  261. 0.0f,
  262. 1.0f,
  263. 0.0f,
  264. 0.0f,
  265. 0.0f,
  266. 0.0f,
  267. 1.0f);
  268. }
  269. U ShadowMapping::choseLod(const Vec4& cameraOrigin, const PointLightQueueElement& light, Bool& blurEsm) const
  270. {
  271. const F32 distFromTheCamera = (cameraOrigin - light.m_worldPosition.xyz0()).getLength() - light.m_radius;
  272. if(distFromTheCamera < m_lodDistances[0])
  273. {
  274. ANKI_ASSERT(m_pointLightsMaxLod == 1);
  275. blurEsm = true;
  276. return 1;
  277. }
  278. else
  279. {
  280. blurEsm = false;
  281. return 0;
  282. }
  283. }
  284. U ShadowMapping::choseLod(const Vec4& cameraOrigin, const SpotLightQueueElement& light, Bool& blurEsm) const
  285. {
  286. // Get some data
  287. const Vec4 coneOrigin = light.m_worldTransform.getTranslationPart().xyz0();
  288. const Vec4 coneDir = -light.m_worldTransform.getZAxis().xyz0();
  289. const F32 coneAngle = light.m_outerAngle;
  290. // Compute the distance from the camera to the light cone
  291. const Vec4 V = cameraOrigin - coneOrigin;
  292. const F32 VlenSq = V.dot(V);
  293. const F32 V1len = V.dot(coneDir);
  294. const F32 distFromTheCamera = cos(coneAngle) * sqrt(VlenSq - V1len * V1len) - V1len * sin(coneAngle);
  295. U lod;
  296. if(distFromTheCamera < m_lodDistances[0])
  297. {
  298. blurEsm = true;
  299. lod = 2;
  300. }
  301. else if(distFromTheCamera < m_lodDistances[1])
  302. {
  303. blurEsm = false;
  304. lod = 1;
  305. }
  306. else
  307. {
  308. blurEsm = false;
  309. lod = 0;
  310. }
  311. return lod;
  312. }
  313. TileAllocatorResult ShadowMapping::allocateTilesAndScratchTiles(U64 lightUuid,
  314. U32 faceCount,
  315. const U64* faceTimestamps,
  316. const U32* faceIndices,
  317. const U32* drawcallsCount,
  318. const U32* lods,
  319. Viewport* esmTileViewports,
  320. Viewport* scratchTileViewports,
  321. TileAllocatorResult* subResults)
  322. {
  323. ANKI_ASSERT(lightUuid > 0);
  324. ANKI_ASSERT(faceCount > 0);
  325. ANKI_ASSERT(faceTimestamps);
  326. ANKI_ASSERT(faceIndices);
  327. ANKI_ASSERT(drawcallsCount);
  328. ANKI_ASSERT(lods);
  329. TileAllocatorResult res;
  330. // Allocate ESM tiles first. They may be cached and that will affect how many scratch tiles we'll need
  331. for(U i = 0; i < faceCount; ++i)
  332. {
  333. res = m_esmTileAlloc.allocate(m_r->getGlobalTimestamp(),
  334. faceTimestamps[i],
  335. lightUuid,
  336. faceIndices[i],
  337. drawcallsCount[i],
  338. lods[i],
  339. esmTileViewports[i]);
  340. if(res == TileAllocatorResult::ALLOCATION_FAILED)
  341. {
  342. ANKI_R_LOGW("There is not enough space in the shadow atlas for more shadow maps. "
  343. "Increase the r.shadowMapping.tileCountPerRowOrColumn or decrease the scene's shadow casters");
  344. // Invalidate cache entries for what we already allocated
  345. for(U j = 0; j < i; ++j)
  346. {
  347. m_esmTileAlloc.invalidateCache(lightUuid, faceIndices[j]);
  348. }
  349. return res;
  350. }
  351. subResults[i] = res;
  352. // Fix viewport
  353. esmTileViewports[i][0] *= m_esmTileResolution;
  354. esmTileViewports[i][1] *= m_esmTileResolution;
  355. esmTileViewports[i][2] *= m_esmTileResolution;
  356. esmTileViewports[i][3] *= m_esmTileResolution;
  357. }
  358. // Allocate scratch tiles
  359. for(U i = 0; i < faceCount; ++i)
  360. {
  361. if(subResults[i] == TileAllocatorResult::CACHED)
  362. {
  363. continue;
  364. }
  365. ANKI_ASSERT(subResults[i] == TileAllocatorResult::ALLOCATION_SUCCEEDED);
  366. res = m_scratchTileAlloc.allocate(m_r->getGlobalTimestamp(),
  367. faceTimestamps[i],
  368. lightUuid,
  369. faceIndices[i],
  370. drawcallsCount[i],
  371. lods[i],
  372. scratchTileViewports[i]);
  373. if(res == TileAllocatorResult::ALLOCATION_FAILED)
  374. {
  375. ANKI_R_LOGW("Don't have enough space in the scratch shadow mapping buffer. "
  376. "If you see this message too often increase r.shadowMapping.scratchTileCountX/Y");
  377. // Invalidate ESM tiles
  378. for(U j = 0; j < faceCount; ++j)
  379. {
  380. m_esmTileAlloc.invalidateCache(lightUuid, faceIndices[j]);
  381. }
  382. return res;
  383. }
  384. // Fix viewport
  385. scratchTileViewports[i][0] *= m_scratchTileResolution;
  386. scratchTileViewports[i][1] *= m_scratchTileResolution;
  387. scratchTileViewports[i][2] *= m_scratchTileResolution;
  388. scratchTileViewports[i][3] *= m_scratchTileResolution;
  389. // Update the max view width
  390. m_scratchMaxViewportWidth =
  391. max(m_scratchMaxViewportWidth, scratchTileViewports[i][0] + scratchTileViewports[i][2]);
  392. m_scratchMaxViewportHeight =
  393. max(m_scratchMaxViewportHeight, scratchTileViewports[i][1] + scratchTileViewports[i][3]);
  394. }
  395. return res;
  396. }
  397. void ShadowMapping::processLights(RenderingContext& ctx, U32& threadCountForScratchPass)
  398. {
  399. // Reset the scratch viewport width
  400. m_scratchMaxViewportWidth = 0;
  401. m_scratchMaxViewportHeight = 0;
  402. // Vars
  403. const Vec4 cameraOrigin = ctx.m_renderQueue->m_cameraTransform.getTranslationPart().xyz0();
  404. DynamicArrayAuto<LightToRenderToScratchInfo> lightsToRender(ctx.m_tempAllocator);
  405. U32 drawcallCount = 0;
  406. DynamicArrayAuto<EsmResolveWorkItem> esmWorkItems(ctx.m_tempAllocator);
  407. // First thing, allocate an empty tile for empty faces of point lights
  408. Viewport emptyTileViewport;
  409. {
  410. const TileAllocatorResult res = m_esmTileAlloc.allocate(
  411. m_r->getGlobalTimestamp(), 1, MAX_U64, 0, 1, m_pointLightsMaxLod, emptyTileViewport);
  412. (void)res;
  413. #if ANKI_ASSERTS_ENABLED
  414. static Bool firstRun = true;
  415. if(firstRun)
  416. {
  417. ANKI_ASSERT(res == TileAllocatorResult::ALLOCATION_SUCCEEDED);
  418. firstRun = false;
  419. }
  420. else
  421. {
  422. ANKI_ASSERT(res == TileAllocatorResult::CACHED);
  423. }
  424. #endif
  425. }
  426. // Process the directional light first.
  427. if(ctx.m_renderQueue->m_directionalLight.m_shadowCascadeCount > 0)
  428. {
  429. DirectionalLightQueueElement& light = ctx.m_renderQueue->m_directionalLight;
  430. Array<U64, MAX_SHADOW_CASCADES> timestamps;
  431. Array<U32, MAX_SHADOW_CASCADES> cascadeIndices;
  432. Array<U32, MAX_SHADOW_CASCADES> drawcallCounts;
  433. Array<Viewport, MAX_SHADOW_CASCADES> esmViewports;
  434. Array<Viewport, MAX_SHADOW_CASCADES> scratchViewports;
  435. Array<TileAllocatorResult, MAX_SHADOW_CASCADES> subResults;
  436. Array<U32, MAX_SHADOW_CASCADES> lods;
  437. Array<Bool, MAX_SHADOW_CASCADES> blurEsms;
  438. U activeCascades = 0;
  439. for(U cascade = 0; cascade < light.m_shadowCascadeCount; ++cascade)
  440. {
  441. ANKI_ASSERT(light.m_shadowRenderQueues[cascade]);
  442. if(light.m_shadowRenderQueues[cascade]->m_renderables.getSize() > 0)
  443. {
  444. // Cascade with drawcalls, will need tiles
  445. timestamps[activeCascades] = m_r->getGlobalTimestamp(); // This light is always updated
  446. cascadeIndices[activeCascades] = cascade;
  447. drawcallCounts[activeCascades] = 1; // Doesn't matter
  448. // Change the quality per cascade
  449. blurEsms[activeCascades] = (cascade <= 1);
  450. lods[activeCascades] = (cascade <= 1) ? (m_lodCount - 1) : (lods[0] - 1);
  451. ++activeCascades;
  452. }
  453. }
  454. const Bool allocationFailed = activeCascades == 0
  455. || allocateTilesAndScratchTiles(light.m_uuid,
  456. activeCascades,
  457. &timestamps[0],
  458. &cascadeIndices[0],
  459. &drawcallCounts[0],
  460. &lods[0],
  461. &esmViewports[0],
  462. &scratchViewports[0],
  463. &subResults[0])
  464. == TileAllocatorResult::ALLOCATION_FAILED;
  465. if(!allocationFailed)
  466. {
  467. activeCascades = 0;
  468. for(U cascade = 0; cascade < light.m_shadowCascadeCount; ++cascade)
  469. {
  470. if(light.m_shadowRenderQueues[cascade]->m_renderables.getSize() > 0)
  471. {
  472. // Cascade with drawcalls, push some work for it
  473. // Update the texture matrix to point to the correct region in the atlas
  474. light.m_textureMatrices[cascade] =
  475. createSpotLightTextureMatrix(esmViewports[activeCascades]) * light.m_textureMatrices[cascade];
  476. // Push work
  477. newScratchAndEsmResloveRenderWorkItems(esmViewports[activeCascades],
  478. scratchViewports[activeCascades],
  479. blurEsms[activeCascades],
  480. false,
  481. light.m_shadowRenderQueues[cascade],
  482. lightsToRender,
  483. esmWorkItems,
  484. drawcallCount);
  485. ++activeCascades;
  486. }
  487. else
  488. {
  489. // Empty cascade, point it to the empty tile
  490. light.m_textureMatrices[cascade] =
  491. createSpotLightTextureMatrix(emptyTileViewport) * light.m_textureMatrices[cascade];
  492. }
  493. }
  494. }
  495. else
  496. {
  497. // Light can't be a caster this frame
  498. light.m_shadowCascadeCount = 0;
  499. zeroMemory(light.m_shadowRenderQueues);
  500. }
  501. }
  502. // Process the point lights.
  503. for(PointLightQueueElement* light : ctx.m_renderQueue->m_shadowPointLights)
  504. {
  505. // Prepare data to allocate tiles and allocate
  506. Array<U64, 6> timestamps;
  507. Array<U32, 6> faceIndices;
  508. Array<U32, 6> drawcallCounts;
  509. Array<Viewport, 6> esmViewports;
  510. Array<Viewport, 6> scratchViewports;
  511. Array<TileAllocatorResult, 6> subResults;
  512. Array<U32, 6> lods;
  513. U numOfFacesThatHaveDrawcalls = 0;
  514. Bool blurEsm;
  515. const U lod = choseLod(cameraOrigin, *light, blurEsm);
  516. for(U face = 0; face < 6; ++face)
  517. {
  518. ANKI_ASSERT(light->m_shadowRenderQueues[face]);
  519. if(light->m_shadowRenderQueues[face]->m_renderables.getSize())
  520. {
  521. // Has renderables, need to allocate tiles for it so add it to the arrays
  522. faceIndices[numOfFacesThatHaveDrawcalls] = face;
  523. timestamps[numOfFacesThatHaveDrawcalls] =
  524. light->m_shadowRenderQueues[face]->m_shadowRenderablesLastUpdateTimestamp;
  525. drawcallCounts[numOfFacesThatHaveDrawcalls] =
  526. light->m_shadowRenderQueues[face]->m_renderables.getSize();
  527. lods[numOfFacesThatHaveDrawcalls] = lod;
  528. ++numOfFacesThatHaveDrawcalls;
  529. }
  530. }
  531. const Bool allocationFailed = numOfFacesThatHaveDrawcalls == 0
  532. || allocateTilesAndScratchTiles(light->m_uuid,
  533. numOfFacesThatHaveDrawcalls,
  534. &timestamps[0],
  535. &faceIndices[0],
  536. &drawcallCounts[0],
  537. &lods[0],
  538. &esmViewports[0],
  539. &scratchViewports[0],
  540. &subResults[0])
  541. == TileAllocatorResult::ALLOCATION_FAILED;
  542. if(!allocationFailed)
  543. {
  544. // All good, update the lights
  545. const F32 atlasResolution = F32(m_esmTileResolution * m_esmTileCountBothAxis);
  546. F32 superTileSize = esmViewports[0][2]; // Should be the same for all tiles and faces
  547. superTileSize -= 1.0f; // Remove 2 half texels to avoid bilinear filtering bleeding
  548. light->m_shadowAtlasTileSize = superTileSize / atlasResolution;
  549. numOfFacesThatHaveDrawcalls = 0;
  550. for(U face = 0; face < 6; ++face)
  551. {
  552. if(light->m_shadowRenderQueues[face]->m_renderables.getSize())
  553. {
  554. // Has drawcalls, asigned it to a tile
  555. const Viewport& esmViewport = esmViewports[numOfFacesThatHaveDrawcalls];
  556. const Viewport& scratchViewport = scratchViewports[numOfFacesThatHaveDrawcalls];
  557. // Add a half texel to the viewport's start to avoid bilinear filtering bleeding
  558. light->m_shadowAtlasTileOffsets[face].x() = (F32(esmViewport[0]) + 0.5f) / atlasResolution;
  559. light->m_shadowAtlasTileOffsets[face].y() = (F32(esmViewport[1]) + 0.5f) / atlasResolution;
  560. if(subResults[numOfFacesThatHaveDrawcalls] != TileAllocatorResult::CACHED)
  561. {
  562. newScratchAndEsmResloveRenderWorkItems(esmViewport,
  563. scratchViewport,
  564. blurEsm,
  565. true,
  566. light->m_shadowRenderQueues[face],
  567. lightsToRender,
  568. esmWorkItems,
  569. drawcallCount);
  570. }
  571. ++numOfFacesThatHaveDrawcalls;
  572. }
  573. else
  574. {
  575. // Doesn't have renderables, point the face to the empty tile
  576. Viewport esmViewport = emptyTileViewport;
  577. ANKI_ASSERT(esmViewport[2] <= superTileSize && esmViewport[3] <= superTileSize);
  578. esmViewport[2] = superTileSize;
  579. esmViewport[3] = superTileSize;
  580. light->m_shadowAtlasTileOffsets[face].x() = (F32(esmViewport[0]) + 0.5f) / atlasResolution;
  581. light->m_shadowAtlasTileOffsets[face].y() = (F32(esmViewport[1]) + 0.5f) / atlasResolution;
  582. }
  583. }
  584. }
  585. else
  586. {
  587. // Light can't be a caster this frame
  588. zeroMemory(light->m_shadowRenderQueues);
  589. }
  590. }
  591. // Process the spot lights
  592. for(SpotLightQueueElement* light : ctx.m_renderQueue->m_shadowSpotLights)
  593. {
  594. ANKI_ASSERT(light->m_shadowRenderQueue);
  595. // Allocate tiles
  596. U32 faceIdx = 0;
  597. TileAllocatorResult subResult;
  598. Viewport esmViewport;
  599. Viewport scratchViewport;
  600. const U32 localDrawcallCount = light->m_shadowRenderQueue->m_renderables.getSize();
  601. Bool blurEsm;
  602. const U32 lod = choseLod(cameraOrigin, *light, blurEsm);
  603. const Bool allocationFailed = localDrawcallCount == 0
  604. || allocateTilesAndScratchTiles(light->m_uuid,
  605. 1,
  606. &light->m_shadowRenderQueue->m_shadowRenderablesLastUpdateTimestamp,
  607. &faceIdx,
  608. &localDrawcallCount,
  609. &lod,
  610. &esmViewport,
  611. &scratchViewport,
  612. &subResult)
  613. == TileAllocatorResult::ALLOCATION_FAILED;
  614. if(!allocationFailed)
  615. {
  616. // All good, update the light
  617. // Update the texture matrix to point to the correct region in the atlas
  618. light->m_textureMatrix = createSpotLightTextureMatrix(esmViewport) * light->m_textureMatrix;
  619. if(subResult != TileAllocatorResult::CACHED)
  620. {
  621. newScratchAndEsmResloveRenderWorkItems(esmViewport,
  622. scratchViewport,
  623. blurEsm,
  624. true,
  625. light->m_shadowRenderQueue,
  626. lightsToRender,
  627. esmWorkItems,
  628. drawcallCount);
  629. }
  630. }
  631. else
  632. {
  633. // Doesn't have renderables or the allocation failed, won't be a shadow caster
  634. light->m_shadowRenderQueue = nullptr;
  635. }
  636. }
  637. // Split the work that will happen in the scratch buffer
  638. if(lightsToRender.getSize())
  639. {
  640. DynamicArrayAuto<ScratchBufferWorkItem> workItems(ctx.m_tempAllocator);
  641. LightToRenderToScratchInfo* lightToRender = lightsToRender.getBegin();
  642. U lightToRenderDrawcallCount = lightToRender->m_drawcallCount;
  643. const LightToRenderToScratchInfo* lightToRenderEnd = lightsToRender.getEnd();
  644. const U threadCount = computeNumberOfSecondLevelCommandBuffers(drawcallCount);
  645. threadCountForScratchPass = threadCount;
  646. for(U taskId = 0; taskId < threadCount; ++taskId)
  647. {
  648. PtrSize start, end;
  649. splitThreadedProblem(taskId, threadCount, drawcallCount, start, end);
  650. // While there are drawcalls in this task emit new work items
  651. U taskDrawcallCount = end - start;
  652. ANKI_ASSERT(taskDrawcallCount > 0 && "Because we used computeNumberOfSecondLevelCommandBuffers()");
  653. while(taskDrawcallCount)
  654. {
  655. ANKI_ASSERT(lightToRender != lightToRenderEnd);
  656. const U workItemDrawcallCount = min(lightToRenderDrawcallCount, taskDrawcallCount);
  657. ScratchBufferWorkItem workItem;
  658. workItem.m_viewport = lightToRender->m_viewport;
  659. workItem.m_renderQueue = lightToRender->m_renderQueue;
  660. workItem.m_firstRenderableElement = lightToRender->m_drawcallCount - lightToRenderDrawcallCount;
  661. workItem.m_renderableElementCount = workItemDrawcallCount;
  662. workItem.m_threadPoolTaskIdx = taskId;
  663. workItems.emplaceBack(workItem);
  664. // Decrease the drawcall counts for the task and the light
  665. ANKI_ASSERT(taskDrawcallCount >= workItemDrawcallCount);
  666. taskDrawcallCount -= workItemDrawcallCount;
  667. ANKI_ASSERT(lightToRenderDrawcallCount >= workItemDrawcallCount);
  668. lightToRenderDrawcallCount -= workItemDrawcallCount;
  669. // Move to the next light
  670. if(lightToRenderDrawcallCount == 0)
  671. {
  672. ++lightToRender;
  673. lightToRenderDrawcallCount =
  674. (lightToRender != lightToRenderEnd) ? lightToRender->m_drawcallCount : 0;
  675. }
  676. }
  677. }
  678. ANKI_ASSERT(lightToRender == lightToRenderEnd);
  679. ANKI_ASSERT(lightsToRender.getSize() <= workItems.getSize());
  680. // All good, store the work items for the threads to pick up
  681. {
  682. ScratchBufferWorkItem* items;
  683. PtrSize itemSize;
  684. PtrSize itemStorageSize;
  685. workItems.moveAndReset(items, itemSize, itemStorageSize);
  686. ANKI_ASSERT(items && itemSize && itemStorageSize);
  687. m_scratchWorkItems = WeakArray<ScratchBufferWorkItem>(items, itemSize);
  688. EsmResolveWorkItem* esmItems;
  689. esmWorkItems.moveAndReset(esmItems, itemSize, itemStorageSize);
  690. ANKI_ASSERT(esmItems && itemSize && itemStorageSize);
  691. m_esmResolveWorkItems = WeakArray<EsmResolveWorkItem>(esmItems, itemSize);
  692. }
  693. }
  694. else
  695. {
  696. m_scratchWorkItems = WeakArray<ScratchBufferWorkItem>();
  697. m_esmResolveWorkItems = WeakArray<EsmResolveWorkItem>();
  698. }
  699. }
  700. void ShadowMapping::newScratchAndEsmResloveRenderWorkItems(const Viewport& esmViewport,
  701. const Viewport& scratchVewport,
  702. Bool blurEsm,
  703. Bool perspectiveProjection,
  704. RenderQueue* lightRenderQueue,
  705. DynamicArrayAuto<LightToRenderToScratchInfo>& scratchWorkItem,
  706. DynamicArrayAuto<EsmResolveWorkItem>& esmResolveWorkItem,
  707. U32& drawcallCount) const
  708. {
  709. // Scratch work item
  710. {
  711. LightToRenderToScratchInfo toRender = {
  712. scratchVewport, lightRenderQueue, U32(lightRenderQueue->m_renderables.getSize())};
  713. scratchWorkItem.emplaceBack(toRender);
  714. drawcallCount += lightRenderQueue->m_renderables.getSize();
  715. }
  716. // ESM resolve work item
  717. {
  718. const F32 scratchAtlasWidth = m_scratchTileCountX * m_scratchTileResolution;
  719. const F32 scratchAtlasHeight = m_scratchTileCountY * m_scratchTileResolution;
  720. EsmResolveWorkItem esmItem;
  721. esmItem.m_uvIn[0] = F32(scratchVewport[0]) / scratchAtlasWidth;
  722. esmItem.m_uvIn[1] = F32(scratchVewport[1]) / scratchAtlasHeight;
  723. esmItem.m_uvIn[2] = F32(scratchVewport[2]) / scratchAtlasWidth;
  724. esmItem.m_uvIn[3] = F32(scratchVewport[3]) / scratchAtlasHeight;
  725. esmItem.m_viewportOut = esmViewport;
  726. esmItem.m_cameraFar = lightRenderQueue->m_cameraFar;
  727. esmItem.m_cameraNear = lightRenderQueue->m_cameraNear;
  728. esmItem.m_blur = blurEsm;
  729. esmItem.m_perspectiveProjection = perspectiveProjection;
  730. esmResolveWorkItem.emplaceBack(esmItem);
  731. }
  732. }
  733. } // end namespace anki