MaterialResource.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
  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_prefilledLocalConstants);
  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. prefillLocalConstants();
  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
  182. {
  183. ANKI_RESOURCE_LOGE("Found unneeded technique in the shader: %s", t.m_name.getBegin());
  184. return Error::kUserData;
  185. }
  186. }
  187. // <mutation>
  188. XmlElement mutatorsEl;
  189. ANKI_CHECK(shaderProgramEl.getChildElementOptional("mutation", mutatorsEl));
  190. if(mutatorsEl)
  191. {
  192. ANKI_CHECK(parseMutators(mutatorsEl));
  193. }
  194. // And find the builtin mutators
  195. ANKI_CHECK(findBuiltinMutators());
  196. // Create the vars
  197. ANKI_CHECK(createVars());
  198. return Error::kNone;
  199. }
  200. Error MaterialResource::parseMutators(XmlElement mutatorsEl)
  201. {
  202. XmlElement mutatorEl;
  203. ANKI_CHECK(mutatorsEl.getChildElement("mutator", mutatorEl));
  204. U32 mutatorCount = 0;
  205. ANKI_CHECK(mutatorEl.getSiblingElementsCount(mutatorCount));
  206. ++mutatorCount;
  207. ANKI_ASSERT(mutatorCount > 0);
  208. m_partialMutation.resize(mutatorCount);
  209. mutatorCount = 0;
  210. do
  211. {
  212. PartialMutation& pmutation = m_partialMutation[mutatorCount];
  213. // name
  214. CString mutatorName;
  215. ANKI_CHECK(mutatorEl.getAttributeText("name", mutatorName));
  216. if(mutatorName.isEmpty())
  217. {
  218. ANKI_RESOURCE_LOGE("Mutator name is empty");
  219. return Error::kUserData;
  220. }
  221. for(BuiltinMutatorId id : EnumIterable<BuiltinMutatorId>())
  222. {
  223. if(id == BuiltinMutatorId::kNone)
  224. {
  225. continue;
  226. }
  227. if(mutatorName == kBuiltinMutatorNames[id])
  228. {
  229. ANKI_RESOURCE_LOGE("Materials shouldn't list builtin mutators: %s", mutatorName.cstr());
  230. return Error::kUserData;
  231. }
  232. }
  233. if(mutatorName.find("ANKI_") == 0)
  234. {
  235. ANKI_RESOURCE_LOGE("Mutators can't start with ANKI_: %s", mutatorName.cstr());
  236. return Error::kUserData;
  237. }
  238. // value
  239. ANKI_CHECK(mutatorEl.getAttributeNumber("value", pmutation.m_value));
  240. // Find mutator
  241. pmutation.m_mutator = m_prog->tryFindMutator(mutatorName);
  242. if(!pmutation.m_mutator)
  243. {
  244. ANKI_RESOURCE_LOGE("Mutator not found in program %s", &mutatorName[0]);
  245. return Error::kUserData;
  246. }
  247. if(!mutatorValueExists(*pmutation.m_mutator, pmutation.m_value))
  248. {
  249. ANKI_RESOURCE_LOGE("Value %d is not part of the mutator %s", pmutation.m_value, mutatorName.cstr());
  250. return Error::kUserData;
  251. }
  252. // Advance
  253. ++mutatorCount;
  254. ANKI_CHECK(mutatorEl.getNextSiblingElement("mutator", mutatorEl));
  255. } while(mutatorEl);
  256. ANKI_ASSERT(mutatorCount == m_partialMutation.getSize());
  257. return Error::kNone;
  258. }
  259. Error MaterialResource::findBuiltinMutators()
  260. {
  261. U builtinMutatorCount = 0;
  262. // ANKI_BONES
  263. CString bonesMutatorName = kBuiltinMutatorNames[BuiltinMutatorId::kBones];
  264. const ShaderBinaryMutator* bonesMutator = m_prog->tryFindMutator(bonesMutatorName);
  265. if(bonesMutator)
  266. {
  267. if(bonesMutator->m_values.getSize() != 2)
  268. {
  269. ANKI_RESOURCE_LOGE("Mutator %s should have 2 values in the program", bonesMutatorName.cstr());
  270. return Error::kUserData;
  271. }
  272. for(U32 i = 0; i < bonesMutator->m_values.getSize(); ++i)
  273. {
  274. if(bonesMutator->m_values[i] != MutatorValue(i))
  275. {
  276. ANKI_RESOURCE_LOGE("Values of the %s mutator in the program are not the expected", bonesMutatorName.cstr());
  277. return Error::kUserData;
  278. }
  279. }
  280. ++builtinMutatorCount;
  281. m_supportsSkinning = true;
  282. m_presentBuildinMutatorMask |= U32(1 << BuiltinMutatorId::kBones);
  283. }
  284. // VELOCITY
  285. CString velocityMutatorName = kBuiltinMutatorNames[BuiltinMutatorId::kVelocity];
  286. const ShaderBinaryMutator* velocityMutator = m_prog->tryFindMutator(velocityMutatorName);
  287. if(velocityMutator)
  288. {
  289. if(velocityMutator->m_values.getSize() != 2)
  290. {
  291. ANKI_RESOURCE_LOGE("Mutator %s should have 2 values in the program", velocityMutatorName.cstr());
  292. return Error::kUserData;
  293. }
  294. for(U32 i = 0; i < velocityMutator->m_values.getSize(); ++i)
  295. {
  296. if(velocityMutator->m_values[i] != MutatorValue(i))
  297. {
  298. ANKI_RESOURCE_LOGE("Values of the %s mutator in the program are not the expected", velocityMutatorName.cstr());
  299. return Error::kUserData;
  300. }
  301. }
  302. ++builtinMutatorCount;
  303. m_presentBuildinMutatorMask |= U32(1 << BuiltinMutatorId::kVelocity);
  304. }
  305. if(m_partialMutation.getSize() + builtinMutatorCount != m_prog->getBinary().m_mutators.getSize())
  306. {
  307. ANKI_RESOURCE_LOGE("Some mutatators are unacounted for");
  308. return Error::kUserData;
  309. }
  310. return Error::kNone;
  311. }
  312. Error MaterialResource::createVars()
  313. {
  314. const ShaderBinary& binary = m_prog->getBinary();
  315. // Find struct
  316. const ShaderBinaryStruct* localConstantsStruct = nullptr;
  317. for(const ShaderBinaryStruct& strct : binary.m_structs)
  318. {
  319. if(CString(strct.m_name.getBegin()) == "AnKiLocalConstants")
  320. {
  321. localConstantsStruct = &strct;
  322. break;
  323. }
  324. }
  325. // Create vars
  326. for(U32 i = 0; localConstantsStruct && i < localConstantsStruct->m_members.getSize(); ++i)
  327. {
  328. const ShaderBinaryStructMember& member = localConstantsStruct->m_members[i];
  329. const CString memberName = member.m_name.getBegin();
  330. MaterialVariable& var = *m_vars.emplaceBack();
  331. zeroMemory(var);
  332. var.m_name = memberName;
  333. var.m_dataType = member.m_type;
  334. var.m_offsetInLocalConstants = member.m_offset;
  335. }
  336. m_localConstantsSize = (localConstantsStruct) ? localConstantsStruct->m_size : 0;
  337. return Error::kNone;
  338. }
  339. Error MaterialResource::parseInput(XmlElement inputEl, Bool async, BitSet<128>& varsSet)
  340. {
  341. // Get var name
  342. CString varName;
  343. ANKI_CHECK(inputEl.getAttributeText("name", varName));
  344. // Try find var
  345. MaterialVariable* foundVar = tryFindVariable(varName);
  346. if(!foundVar)
  347. {
  348. ANKI_RESOURCE_LOGE("Input name is wrong, variable not found: %s", varName.cstr());
  349. return Error::kUserData;
  350. }
  351. // Set check
  352. const U32 idx = U32(foundVar - m_vars.getBegin());
  353. if(varsSet.get(idx))
  354. {
  355. ANKI_RESOURCE_LOGE("Input already has a value: %s", varName.cstr());
  356. return Error::kUserData;
  357. }
  358. varsSet.set(idx);
  359. // Set the value
  360. if(foundVar->m_dataType == ShaderVariableDataType::kU32)
  361. {
  362. // U32 is a bit special. It might be a number or a bindless texture
  363. CString value;
  364. ANKI_CHECK(inputEl.getAttributeText("value", value));
  365. // Check if the value has letters
  366. Bool containsAlpharithmetic = false;
  367. for(Char c : value)
  368. {
  369. if((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'a'))
  370. {
  371. containsAlpharithmetic = true;
  372. break;
  373. }
  374. }
  375. // If it has letters it's a texture
  376. if(containsAlpharithmetic)
  377. {
  378. ANKI_CHECK(ResourceManager::getSingleton().loadResource(value, foundVar->m_image, async));
  379. foundVar->m_U32 = foundVar->m_image->getTexture().getOrCreateBindlessTextureIndex(TextureSubresourceDesc::all());
  380. }
  381. else
  382. {
  383. ANKI_CHECK(GetAttribute<U32>()(inputEl, foundVar->m_U32));
  384. }
  385. }
  386. else
  387. {
  388. switch(foundVar->m_dataType)
  389. {
  390. #define ANKI_SVDT_MACRO(type, baseType, rowCount, columnCount, isIntagralType) \
  391. case ShaderVariableDataType::k##type: \
  392. ANKI_CHECK(GetAttribute<type>()(inputEl, foundVar->ANKI_CONCATENATE(m_, type))); \
  393. break;
  394. #include <AnKi/Gr/ShaderVariableDataType.def.h>
  395. #undef ANKI_SVDT_MACRO
  396. default:
  397. ANKI_ASSERT(0);
  398. break;
  399. }
  400. }
  401. return Error::kNone;
  402. }
  403. void MaterialResource::prefillLocalConstants()
  404. {
  405. if(m_localConstantsSize == 0)
  406. {
  407. return;
  408. }
  409. m_prefilledLocalConstants = ResourceMemoryPool::getSingleton().allocate(m_localConstantsSize, 1);
  410. memset(m_prefilledLocalConstants, 0, m_localConstantsSize);
  411. for(const MaterialVariable& var : m_vars)
  412. {
  413. switch(var.m_dataType)
  414. {
  415. #define ANKI_SVDT_MACRO(type, baseType, rowCount, columnCount, isIntagralType) \
  416. case ShaderVariableDataType::k##type: \
  417. ANKI_ASSERT(var.m_offsetInLocalConstants + sizeof(type) <= m_localConstantsSize); \
  418. memcpy(static_cast<U8*>(m_prefilledLocalConstants) + var.m_offsetInLocalConstants, &var.m_##type, sizeof(type)); \
  419. break;
  420. #include <AnKi/Gr/ShaderVariableDataType.def.h>
  421. #undef ANKI_SVDT_MACRO
  422. default:
  423. ANKI_ASSERT(0);
  424. break;
  425. }
  426. }
  427. }
  428. const MaterialVariant& MaterialResource::getOrCreateVariant(const RenderingKey& key_) const
  429. {
  430. RenderingKey key = key_;
  431. // Sanitize the key
  432. if(!(m_presentBuildinMutatorMask & U32(BuiltinMutatorId::kVelocity)) && key.getVelocity())
  433. {
  434. // Particles set their own velocity
  435. key.setVelocity(false);
  436. }
  437. if(key.getRenderingTechnique() != RenderingTechnique::kGBuffer && key.getVelocity())
  438. {
  439. // Only GBuffer technique can write to velocity buffers
  440. key.setVelocity(false);
  441. }
  442. const Bool meshShadersSupported = GrManager::getSingleton().getDeviceCapabilities().m_meshShaders;
  443. ANKI_ASSERT(!(key.getMeshletRendering() && (!meshShadersSupported && !g_meshletRenderingCVar))
  444. && "Can't be asking for meshlet rendering if mesh shaders or SW meshlet rendering are not supported/enabled");
  445. if(key.getMeshletRendering() && !(m_shaderTechniques & (ShaderTechniqueBit::kMeshSaders | ShaderTechniqueBit::kSwMeshletRendering)))
  446. {
  447. key.setMeshletRendering(false);
  448. }
  449. ANKI_ASSERT(!key.getSkinned() || !!(m_presentBuildinMutatorMask & U32(1 << BuiltinMutatorId::kBones)));
  450. ANKI_ASSERT(!key.getVelocity() || !!(m_presentBuildinMutatorMask & U32(1 << BuiltinMutatorId::kVelocity)));
  451. MaterialVariant& variant = m_variantMatrix[key.getRenderingTechnique()][key.getSkinned()][key.getVelocity()][key.getMeshletRendering()];
  452. // Check if it's initialized
  453. {
  454. RLockGuard<RWMutex> lock(m_variantMatrixMtx);
  455. if(variant.m_prog.isCreated()) [[likely]]
  456. {
  457. return variant;
  458. }
  459. }
  460. // Not initialized, init it
  461. WLockGuard<RWMutex> lock(m_variantMatrixMtx);
  462. // Check again
  463. if(variant.m_prog.isCreated())
  464. {
  465. return variant;
  466. }
  467. ShaderProgramResourceVariantInitInfo initInfo(m_prog);
  468. for(const PartialMutation& m : m_partialMutation)
  469. {
  470. initInfo.addMutation(m.m_mutator->m_name.getBegin(), m.m_value);
  471. }
  472. if(!!(m_presentBuildinMutatorMask & U32(1 << BuiltinMutatorId::kBones)))
  473. {
  474. initInfo.addMutation(kBuiltinMutatorNames[BuiltinMutatorId::kBones], MutatorValue(key.getSkinned()));
  475. }
  476. if(!!(m_presentBuildinMutatorMask & U32(1 << BuiltinMutatorId::kVelocity)))
  477. {
  478. initInfo.addMutation(kBuiltinMutatorNames[BuiltinMutatorId::kVelocity], MutatorValue(key.getVelocity()));
  479. }
  480. switch(key.getRenderingTechnique())
  481. {
  482. case RenderingTechnique::kGBuffer:
  483. if(key.getMeshletRendering() && meshShadersSupported)
  484. {
  485. initInfo.requestTechniqueAndTypes(ShaderTypeBit::kMesh | ShaderTypeBit::kPixel, "GBufferMeshShaders");
  486. }
  487. else if(key.getMeshletRendering())
  488. {
  489. initInfo.requestTechniqueAndTypes(ShaderTypeBit::kVertex | ShaderTypeBit::kPixel, "GBufferSwMeshletRendering");
  490. }
  491. else
  492. {
  493. initInfo.requestTechniqueAndTypes(ShaderTypeBit::kVertex | ShaderTypeBit::kPixel, "GBufferLegacy");
  494. }
  495. break;
  496. case RenderingTechnique::kDepth:
  497. if(key.getMeshletRendering() && meshShadersSupported)
  498. {
  499. initInfo.requestTechniqueAndTypes(ShaderTypeBit::kMesh | ShaderTypeBit::kPixel, "ShadowsMeshShaders");
  500. }
  501. else if(key.getMeshletRendering())
  502. {
  503. initInfo.requestTechniqueAndTypes(ShaderTypeBit::kVertex | ShaderTypeBit::kPixel, "ShadowsSwMeshletRendering");
  504. }
  505. else
  506. {
  507. initInfo.requestTechniqueAndTypes(ShaderTypeBit::kVertex | ShaderTypeBit::kPixel, "ShadowsLegacy");
  508. }
  509. break;
  510. case RenderingTechnique::kForward:
  511. initInfo.requestTechniqueAndTypes(ShaderTypeBit::kVertex | ShaderTypeBit::kPixel, "Forward");
  512. break;
  513. case RenderingTechnique::kRtShadow:
  514. initInfo.requestTechniqueAndTypes(ShaderTypeBit::kAllHit, "RtShadows");
  515. break;
  516. default:
  517. ANKI_ASSERT(0);
  518. }
  519. const ShaderProgramResourceVariant* progVariant = nullptr;
  520. m_prog->getOrCreateVariant(initInfo, progVariant);
  521. if(!progVariant)
  522. {
  523. ANKI_RESOURCE_LOGF("Fetched skipped mutation on program %s", getFilename().cstr());
  524. }
  525. variant.m_prog.reset(&progVariant->getProgram());
  526. if(!!(RenderingTechniqueBit(1 << key.getRenderingTechnique()) & RenderingTechniqueBit::kAllRt))
  527. {
  528. variant.m_rtShaderGroupHandleIndex = progVariant->getShaderGroupHandleIndex();
  529. }
  530. return variant;
  531. }
  532. } // end namespace anki