MaterialResource.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636
  1. // Copyright (C) 2009-present, 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/Resource/MaterialResource.h>
  6. #include <AnKi/Resource/ResourceManager.h>
  7. #include <AnKi/Resource/ImageResource.h>
  8. #include <AnKi/Core/App.h>
  9. #include <AnKi/Util/Xml.h>
  10. namespace anki {
  11. inline constexpr Array<CString, U32(BuiltinMutatorId::kCount)> kBuiltinMutatorNames = {{"NONE", "ANKI_BONES", "ANKI_VELOCITY"}};
  12. inline constexpr Array<CString, U(RenderingTechnique::kCount)> kTechniqueNames = {{"GBuffer", "Depth", "Forward", "RtShadow"}};
  13. // This is some trickery to select calling between XmlElement::getAttributeNumber and XmlElement::getAttributeNumbers
  14. namespace {
  15. template<typename T>
  16. class IsShaderVarDataTypeAnArray
  17. {
  18. public:
  19. static constexpr Bool kValue = false;
  20. };
  21. #define ANKI_SVDT_MACRO(type, baseType, rowCount, columnCount, isIntagralType) \
  22. template<> \
  23. class IsShaderVarDataTypeAnArray<type> \
  24. { \
  25. public: \
  26. static constexpr Bool kValue = rowCount * columnCount > 1; \
  27. };
  28. #include <AnKi/Gr/ShaderVariableDataType.def.h>
  29. #undef ANKI_SVDT_MACRO
  30. template<typename T, Bool isArray = IsShaderVarDataTypeAnArray<T>::kValue>
  31. class GetAttribute
  32. {
  33. public:
  34. Error operator()(const XmlElement& el, T& out)
  35. {
  36. return el.getAttributeNumbers("value", out);
  37. }
  38. };
  39. template<typename T>
  40. class GetAttribute<T, false>
  41. {
  42. public:
  43. Error operator()(const XmlElement& el, T& out)
  44. {
  45. return el.getAttributeNumber("value", out);
  46. }
  47. };
  48. } // namespace
  49. static Bool mutatorValueExists(const ShaderBinaryMutator& m, MutatorValue val)
  50. {
  51. for(MutatorValue v : m.m_values)
  52. {
  53. if(v == val)
  54. {
  55. return true;
  56. }
  57. }
  58. return false;
  59. }
  60. MaterialVariable::MaterialVariable()
  61. {
  62. m_Mat4 = Mat4::getZero();
  63. }
  64. MaterialVariable::~MaterialVariable()
  65. {
  66. }
  67. MaterialResource::MaterialResource()
  68. {
  69. }
  70. MaterialResource::~MaterialResource()
  71. {
  72. ResourceMemoryPool::getSingleton().free(m_prefilledLocalUniforms);
  73. }
  74. const MaterialVariable* MaterialResource::tryFindVariableInternal(CString name) const
  75. {
  76. for(const MaterialVariable& v : m_vars)
  77. {
  78. if(v.m_name == name)
  79. {
  80. return &v;
  81. }
  82. }
  83. return nullptr;
  84. }
  85. Error MaterialResource::load(const ResourceFilename& filename, Bool async)
  86. {
  87. ResourceXmlDocument doc;
  88. XmlElement el;
  89. ANKI_CHECK(openFileParseXml(filename, doc));
  90. // <material>
  91. XmlElement rootEl;
  92. ANKI_CHECK(doc.getChildElement("material", rootEl));
  93. // <shaderPrograms>
  94. XmlElement shaderProgramEl;
  95. ANKI_CHECK(rootEl.getChildElement("shaderProgram", shaderProgramEl));
  96. ANKI_CHECK(parseShaderProgram(shaderProgramEl, async));
  97. ANKI_ASSERT(!!m_techniquesMask);
  98. // <inputs>
  99. BitSet<128> varsSet(false);
  100. ANKI_CHECK(rootEl.getChildElementOptional("inputs", el));
  101. if(el)
  102. {
  103. XmlElement inputEl;
  104. ANKI_CHECK(el.getChildElement("input", inputEl));
  105. do
  106. {
  107. ANKI_CHECK(parseInput(inputEl, async, varsSet));
  108. ANKI_CHECK(inputEl.getNextSiblingElement("input", inputEl));
  109. } while(inputEl);
  110. }
  111. if(varsSet.getSetBitCount() != m_vars.getSize())
  112. {
  113. ANKI_RESOURCE_LOGV("Material doesn't contain default value for %u input variables", U32(m_vars.getSize() - varsSet.getSetBitCount()));
  114. // Remove unreferenced variables
  115. ResourceDynamicArray<MaterialVariable> newVars;
  116. for(U32 i = 0; i < m_vars.getSize(); ++i)
  117. {
  118. if(varsSet.get(i))
  119. {
  120. newVars.emplaceBack(std::move(m_vars[i]));
  121. }
  122. }
  123. m_vars = std::move(newVars);
  124. }
  125. prefillLocalUniforms();
  126. return Error::kNone;
  127. }
  128. Error MaterialResource::parseShaderProgram(XmlElement shaderProgramEl, Bool async)
  129. {
  130. // name
  131. CString shaderName;
  132. ANKI_CHECK(shaderProgramEl.getAttributeText("name", shaderName));
  133. ResourceString fname;
  134. fname.sprintf("ShaderBinaries/%s.ankiprogbin", shaderName.cstr());
  135. ANKI_CHECK(ResourceManager::getSingleton().loadResource(fname, m_prog, async));
  136. // Find present techniques
  137. for(const ShaderBinaryTechnique& t : m_prog->getBinary().m_techniques)
  138. {
  139. if(t.m_name.getBegin() == CString("GBufferLegacy"))
  140. {
  141. m_techniquesMask |= RenderingTechniqueBit::kGBuffer;
  142. m_shaderTechniques |= ShaderTechniqueBit::kLegacy;
  143. }
  144. else if(t.m_name.getBegin() == CString("GBufferMeshShaders"))
  145. {
  146. m_techniquesMask |= RenderingTechniqueBit::kGBuffer;
  147. m_shaderTechniques |= ShaderTechniqueBit::kMeshSaders;
  148. }
  149. else if(t.m_name.getBegin() == CString("GBufferSwMeshletRendering"))
  150. {
  151. m_techniquesMask |= RenderingTechniqueBit::kGBuffer;
  152. m_shaderTechniques |= ShaderTechniqueBit::kSwMeshletRendering;
  153. }
  154. else if(t.m_name.getBegin() == CString("ShadowsLegacy"))
  155. {
  156. m_techniquesMask |= RenderingTechniqueBit::kDepth;
  157. m_shaderTechniques |= ShaderTechniqueBit::kLegacy;
  158. }
  159. else if(t.m_name.getBegin() == CString("ShadowsMeshShaders"))
  160. {
  161. m_techniquesMask |= RenderingTechniqueBit::kDepth;
  162. m_shaderTechniques |= ShaderTechniqueBit::kMeshSaders;
  163. }
  164. else if(t.m_name.getBegin() == CString("ShadowsSwMeshletRendering"))
  165. {
  166. m_techniquesMask |= RenderingTechniqueBit::kDepth;
  167. m_shaderTechniques |= ShaderTechniqueBit::kSwMeshletRendering;
  168. }
  169. else if(t.m_name.getBegin() == CString("RtShadows"))
  170. {
  171. if(GrManager::getSingleton().getDeviceCapabilities().m_rayTracingEnabled)
  172. {
  173. m_techniquesMask |= RenderingTechniqueBit::kRtShadow;
  174. }
  175. }
  176. else if(t.m_name.getBegin() == CString("Forward"))
  177. {
  178. m_techniquesMask |= RenderingTechniqueBit::kForward;
  179. m_shaderTechniques |= ShaderTechniqueBit::kLegacy;
  180. }
  181. else if(t.m_name.getBegin() == CString("CommonTask"))
  182. {
  183. // Ignore
  184. }
  185. else
  186. {
  187. ANKI_RESOURCE_LOGE("Found unneeded technique in the shader: %s", t.m_name.getBegin());
  188. return Error::kUserData;
  189. }
  190. }
  191. // <mutation>
  192. XmlElement mutatorsEl;
  193. ANKI_CHECK(shaderProgramEl.getChildElementOptional("mutation", mutatorsEl));
  194. if(mutatorsEl)
  195. {
  196. ANKI_CHECK(parseMutators(mutatorsEl));
  197. }
  198. // And find the builtin mutators
  199. ANKI_CHECK(findBuiltinMutators());
  200. // Create the vars
  201. ANKI_CHECK(createVars());
  202. return Error::kNone;
  203. }
  204. Error MaterialResource::parseMutators(XmlElement mutatorsEl)
  205. {
  206. XmlElement mutatorEl;
  207. ANKI_CHECK(mutatorsEl.getChildElement("mutator", mutatorEl));
  208. U32 mutatorCount = 0;
  209. ANKI_CHECK(mutatorEl.getSiblingElementsCount(mutatorCount));
  210. ++mutatorCount;
  211. ANKI_ASSERT(mutatorCount > 0);
  212. m_partialMutation.resize(mutatorCount);
  213. mutatorCount = 0;
  214. do
  215. {
  216. PartialMutation& pmutation = m_partialMutation[mutatorCount];
  217. // name
  218. CString mutatorName;
  219. ANKI_CHECK(mutatorEl.getAttributeText("name", mutatorName));
  220. if(mutatorName.isEmpty())
  221. {
  222. ANKI_RESOURCE_LOGE("Mutator name is empty");
  223. return Error::kUserData;
  224. }
  225. for(BuiltinMutatorId id : EnumIterable<BuiltinMutatorId>())
  226. {
  227. if(id == BuiltinMutatorId::kNone)
  228. {
  229. continue;
  230. }
  231. if(mutatorName == kBuiltinMutatorNames[id])
  232. {
  233. ANKI_RESOURCE_LOGE("Materials shouldn't list builtin mutators: %s", mutatorName.cstr());
  234. return Error::kUserData;
  235. }
  236. }
  237. if(mutatorName.find("ANKI_") == 0)
  238. {
  239. ANKI_RESOURCE_LOGE("Mutators can't start with ANKI_: %s", mutatorName.cstr());
  240. return Error::kUserData;
  241. }
  242. // value
  243. ANKI_CHECK(mutatorEl.getAttributeNumber("value", pmutation.m_value));
  244. // Find mutator
  245. pmutation.m_mutator = m_prog->tryFindMutator(mutatorName);
  246. if(!pmutation.m_mutator)
  247. {
  248. ANKI_RESOURCE_LOGE("Mutator not found in program %s", &mutatorName[0]);
  249. return Error::kUserData;
  250. }
  251. if(!mutatorValueExists(*pmutation.m_mutator, pmutation.m_value))
  252. {
  253. ANKI_RESOURCE_LOGE("Value %d is not part of the mutator %s", pmutation.m_value, mutatorName.cstr());
  254. return Error::kUserData;
  255. }
  256. // Advance
  257. ++mutatorCount;
  258. ANKI_CHECK(mutatorEl.getNextSiblingElement("mutator", mutatorEl));
  259. } while(mutatorEl);
  260. ANKI_ASSERT(mutatorCount == m_partialMutation.getSize());
  261. return Error::kNone;
  262. }
  263. Error MaterialResource::findBuiltinMutators()
  264. {
  265. U builtinMutatorCount = 0;
  266. // ANKI_BONES
  267. CString bonesMutatorName = kBuiltinMutatorNames[BuiltinMutatorId::kBones];
  268. const ShaderBinaryMutator* bonesMutator = m_prog->tryFindMutator(bonesMutatorName);
  269. if(bonesMutator)
  270. {
  271. if(bonesMutator->m_values.getSize() != 2)
  272. {
  273. ANKI_RESOURCE_LOGE("Mutator %s should have 2 values in the program", bonesMutatorName.cstr());
  274. return Error::kUserData;
  275. }
  276. for(U32 i = 0; i < bonesMutator->m_values.getSize(); ++i)
  277. {
  278. if(bonesMutator->m_values[i] != MutatorValue(i))
  279. {
  280. ANKI_RESOURCE_LOGE("Values of the %s mutator in the program are not the expected", bonesMutatorName.cstr());
  281. return Error::kUserData;
  282. }
  283. }
  284. ++builtinMutatorCount;
  285. m_supportsSkinning = true;
  286. m_presentBuildinMutatorMask |= U32(1 << BuiltinMutatorId::kBones);
  287. }
  288. // VELOCITY
  289. CString velocityMutatorName = kBuiltinMutatorNames[BuiltinMutatorId::kVelocity];
  290. const ShaderBinaryMutator* velocityMutator = m_prog->tryFindMutator(velocityMutatorName);
  291. if(velocityMutator)
  292. {
  293. if(velocityMutator->m_values.getSize() != 2)
  294. {
  295. ANKI_RESOURCE_LOGE("Mutator %s should have 2 values in the program", velocityMutatorName.cstr());
  296. return Error::kUserData;
  297. }
  298. for(U32 i = 0; i < velocityMutator->m_values.getSize(); ++i)
  299. {
  300. if(velocityMutator->m_values[i] != MutatorValue(i))
  301. {
  302. ANKI_RESOURCE_LOGE("Values of the %s mutator in the program are not the expected", velocityMutatorName.cstr());
  303. return Error::kUserData;
  304. }
  305. }
  306. ++builtinMutatorCount;
  307. m_presentBuildinMutatorMask |= U32(1 << BuiltinMutatorId::kVelocity);
  308. }
  309. if(m_partialMutation.getSize() + builtinMutatorCount != m_prog->getBinary().m_mutators.getSize())
  310. {
  311. ANKI_RESOURCE_LOGE("Some mutatators are unacounted for");
  312. return Error::kUserData;
  313. }
  314. return Error::kNone;
  315. }
  316. Error MaterialResource::createVars()
  317. {
  318. const ShaderBinary& binary = m_prog->getBinary();
  319. // Find struct
  320. const ShaderBinaryStruct* localUniformsStruct = nullptr;
  321. for(const ShaderBinaryStruct& strct : binary.m_structs)
  322. {
  323. if(CString(strct.m_name.getBegin()) == "AnKiLocalUniforms")
  324. {
  325. localUniformsStruct = &strct;
  326. break;
  327. }
  328. }
  329. // Create vars
  330. for(U32 i = 0; localUniformsStruct && i < localUniformsStruct->m_members.getSize(); ++i)
  331. {
  332. const ShaderBinaryStructMember& member = localUniformsStruct->m_members[i];
  333. const CString memberName = member.m_name.getBegin();
  334. MaterialVariable& var = *m_vars.emplaceBack();
  335. zeroMemory(var);
  336. var.m_name = memberName;
  337. var.m_dataType = member.m_type;
  338. var.m_offsetInLocalUniforms = member.m_offset;
  339. }
  340. m_localUniformsSize = (localUniformsStruct) ? localUniformsStruct->m_size : 0;
  341. return Error::kNone;
  342. }
  343. Error MaterialResource::parseInput(XmlElement inputEl, Bool async, BitSet<128>& varsSet)
  344. {
  345. // Get var name
  346. CString varName;
  347. ANKI_CHECK(inputEl.getAttributeText("name", varName));
  348. // Try find var
  349. MaterialVariable* foundVar = tryFindVariable(varName);
  350. if(!foundVar)
  351. {
  352. ANKI_RESOURCE_LOGE("Input name is wrong, variable not found: %s", varName.cstr());
  353. return Error::kUserData;
  354. }
  355. // Set check
  356. const U32 idx = U32(foundVar - m_vars.getBegin());
  357. if(varsSet.get(idx))
  358. {
  359. ANKI_RESOURCE_LOGE("Input already has a value: %s", varName.cstr());
  360. return Error::kUserData;
  361. }
  362. varsSet.set(idx);
  363. // Set the value
  364. if(foundVar->m_dataType == ShaderVariableDataType::kU32)
  365. {
  366. // U32 is a bit special. It might be a number or a bindless texture
  367. CString value;
  368. ANKI_CHECK(inputEl.getAttributeText("value", value));
  369. // Check if the value has letters
  370. Bool containsAlpharithmetic = false;
  371. for(Char c : value)
  372. {
  373. if((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'a'))
  374. {
  375. containsAlpharithmetic = true;
  376. break;
  377. }
  378. }
  379. // If it has letters it's a texture
  380. if(containsAlpharithmetic)
  381. {
  382. ANKI_CHECK(ResourceManager::getSingleton().loadResource(value, foundVar->m_image, async));
  383. foundVar->m_U32 = foundVar->m_image->getTexture().getOrCreateBindlessTextureIndex(TextureSubresourceDesc::all());
  384. }
  385. else
  386. {
  387. ANKI_CHECK(GetAttribute<U32>()(inputEl, foundVar->m_U32));
  388. }
  389. }
  390. else
  391. {
  392. switch(foundVar->m_dataType)
  393. {
  394. #define ANKI_SVDT_MACRO(type, baseType, rowCount, columnCount, isIntagralType) \
  395. case ShaderVariableDataType::k##type: \
  396. ANKI_CHECK(GetAttribute<type>()(inputEl, foundVar->ANKI_CONCATENATE(m_, type))); \
  397. break;
  398. #include <AnKi/Gr/ShaderVariableDataType.def.h>
  399. #undef ANKI_SVDT_MACRO
  400. default:
  401. ANKI_ASSERT(0);
  402. break;
  403. }
  404. }
  405. return Error::kNone;
  406. }
  407. void MaterialResource::prefillLocalUniforms()
  408. {
  409. if(m_localUniformsSize == 0)
  410. {
  411. return;
  412. }
  413. m_prefilledLocalUniforms = ResourceMemoryPool::getSingleton().allocate(m_localUniformsSize, 1);
  414. memset(m_prefilledLocalUniforms, 0, m_localUniformsSize);
  415. for(const MaterialVariable& var : m_vars)
  416. {
  417. switch(var.m_dataType)
  418. {
  419. #define ANKI_SVDT_MACRO(type, baseType, rowCount, columnCount, isIntagralType) \
  420. case ShaderVariableDataType::k##type: \
  421. ANKI_ASSERT(var.m_offsetInLocalUniforms + sizeof(type) <= m_localUniformsSize); \
  422. memcpy(static_cast<U8*>(m_prefilledLocalUniforms) + var.m_offsetInLocalUniforms, &var.m_##type, sizeof(type)); \
  423. break;
  424. #include <AnKi/Gr/ShaderVariableDataType.def.h>
  425. #undef ANKI_SVDT_MACRO
  426. default:
  427. ANKI_ASSERT(0);
  428. break;
  429. }
  430. }
  431. }
  432. const MaterialVariant& MaterialResource::getOrCreateVariant(const RenderingKey& key_) const
  433. {
  434. RenderingKey key = key_;
  435. // Sanitize the key
  436. if(!(m_presentBuildinMutatorMask & U32(BuiltinMutatorId::kVelocity)) && key.getVelocity())
  437. {
  438. // Particles set their own velocity
  439. key.setVelocity(false);
  440. }
  441. if(key.getRenderingTechnique() != RenderingTechnique::kGBuffer && key.getVelocity())
  442. {
  443. // Only GBuffer technique can write to velocity buffers
  444. key.setVelocity(false);
  445. }
  446. const Bool meshShadersSupported = GrManager::getSingleton().getDeviceCapabilities().m_meshShaders;
  447. ANKI_ASSERT(!(key.getMeshletRendering() && (!meshShadersSupported && !g_meshletRenderingCVar.get()))
  448. && "Can't be asking for meshlet rendering if mesh shaders or SW meshlet rendering are not supported/enabled");
  449. if(key.getMeshletRendering() && !(m_shaderTechniques & (ShaderTechniqueBit::kMeshSaders | ShaderTechniqueBit::kSwMeshletRendering)))
  450. {
  451. key.setMeshletRendering(false);
  452. }
  453. ANKI_ASSERT(!key.getSkinned() || !!(m_presentBuildinMutatorMask & U32(1 << BuiltinMutatorId::kBones)));
  454. ANKI_ASSERT(!key.getVelocity() || !!(m_presentBuildinMutatorMask & U32(1 << BuiltinMutatorId::kVelocity)));
  455. MaterialVariant& variant = m_variantMatrix[key.getRenderingTechnique()][key.getSkinned()][key.getVelocity()][key.getMeshletRendering()];
  456. // Check if it's initialized
  457. {
  458. RLockGuard<RWMutex> lock(m_variantMatrixMtx);
  459. if(variant.m_prog.isCreated()) [[likely]]
  460. {
  461. return variant;
  462. }
  463. }
  464. // Not initialized, init it
  465. WLockGuard<RWMutex> lock(m_variantMatrixMtx);
  466. // Check again
  467. if(variant.m_prog.isCreated())
  468. {
  469. return variant;
  470. }
  471. ShaderProgramResourceVariantInitInfo initInfo(m_prog);
  472. for(const PartialMutation& m : m_partialMutation)
  473. {
  474. initInfo.addMutation(m.m_mutator->m_name.getBegin(), m.m_value);
  475. }
  476. if(!!(m_presentBuildinMutatorMask & U32(1 << BuiltinMutatorId::kBones)))
  477. {
  478. initInfo.addMutation(kBuiltinMutatorNames[BuiltinMutatorId::kBones], MutatorValue(key.getSkinned()));
  479. }
  480. if(!!(m_presentBuildinMutatorMask & U32(1 << BuiltinMutatorId::kVelocity)))
  481. {
  482. initInfo.addMutation(kBuiltinMutatorNames[BuiltinMutatorId::kVelocity], MutatorValue(key.getVelocity()));
  483. }
  484. switch(key.getRenderingTechnique())
  485. {
  486. case RenderingTechnique::kGBuffer:
  487. if(key.getMeshletRendering() && meshShadersSupported)
  488. {
  489. initInfo.requestTechniqueAndTypes(ShaderTypeBit::kMesh | ShaderTypeBit::kFragment, "GBufferMeshShaders");
  490. initInfo.requestTechniqueAndTypes(ShaderTypeBit::kTask, "CommonTask");
  491. }
  492. else if(key.getMeshletRendering())
  493. {
  494. initInfo.requestTechniqueAndTypes(ShaderTypeBit::kVertex | ShaderTypeBit::kFragment, "GBufferSwMeshletRendering");
  495. }
  496. else
  497. {
  498. initInfo.requestTechniqueAndTypes(ShaderTypeBit::kVertex | ShaderTypeBit::kFragment, "GBufferLegacy");
  499. }
  500. break;
  501. case RenderingTechnique::kDepth:
  502. if(key.getMeshletRendering() && meshShadersSupported)
  503. {
  504. initInfo.requestTechniqueAndTypes(ShaderTypeBit::kMesh | ShaderTypeBit::kFragment, "ShadowsMeshShaders");
  505. initInfo.requestTechniqueAndTypes(ShaderTypeBit::kTask, "CommonTask");
  506. }
  507. else if(key.getMeshletRendering())
  508. {
  509. initInfo.requestTechniqueAndTypes(ShaderTypeBit::kVertex | ShaderTypeBit::kFragment, "ShadowsSwMeshletRendering");
  510. }
  511. else
  512. {
  513. initInfo.requestTechniqueAndTypes(ShaderTypeBit::kVertex | ShaderTypeBit::kFragment, "ShadowsLegacy");
  514. }
  515. break;
  516. case RenderingTechnique::kForward:
  517. initInfo.requestTechniqueAndTypes(ShaderTypeBit::kVertex | ShaderTypeBit::kFragment, "Forward");
  518. break;
  519. case RenderingTechnique::kRtShadow:
  520. initInfo.requestTechniqueAndTypes(ShaderTypeBit::kAllHit, "RtShadows");
  521. break;
  522. default:
  523. ANKI_ASSERT(0);
  524. }
  525. const ShaderProgramResourceVariant* progVariant = nullptr;
  526. m_prog->getOrCreateVariant(initInfo, progVariant);
  527. if(!progVariant)
  528. {
  529. ANKI_RESOURCE_LOGF("Fetched skipped mutation on program %s", getFilename().cstr());
  530. }
  531. variant.m_prog.reset(&progVariant->getProgram());
  532. if(!!(RenderingTechniqueBit(1 << key.getRenderingTechnique()) & RenderingTechniqueBit::kAllRt))
  533. {
  534. variant.m_rtShaderGroupHandleIndex = progVariant->getShaderGroupHandleIndex();
  535. }
  536. return variant;
  537. }
  538. } // end namespace anki