MaterialResource.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  1. // Copyright (C) 2009-2020, 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/util/Xml.h>
  8. namespace anki
  9. {
  10. struct BuiltinVarInfo
  11. {
  12. const char* m_name;
  13. ShaderVariableDataType m_type;
  14. Bool m_instanced;
  15. };
  16. static const Array<BuiltinVarInfo, U(BuiltinMaterialVariableId::COUNT) - 1> BUILTIN_INFOS = {
  17. {{"MODEL_VIEW_PROJECTION_MATRIX", ShaderVariableDataType::MAT4, true},
  18. {"MODEL_VIEW_MATRIX", ShaderVariableDataType::MAT4, true},
  19. {"MODEL_MATRIX", ShaderVariableDataType::MAT4, true},
  20. {"VIEW_PROJECTION_MATRIX", ShaderVariableDataType::MAT4, false},
  21. {"VIEW_MATRIX", ShaderVariableDataType::MAT4, false},
  22. {"NORMAL_MATRIX", ShaderVariableDataType::MAT3, true},
  23. {"ROTATION_MATRIX", ShaderVariableDataType::MAT3, true},
  24. {"CAMERA_ROTATION_MATRIX", ShaderVariableDataType::MAT3, false},
  25. {"CAMERA_POSITION", ShaderVariableDataType::VEC3, false},
  26. {"PREVIOUS_MODEL_VIEW_PROJECTION_MATRIX", ShaderVariableDataType::MAT4, true},
  27. {"GLOBAL_SAMPLER", ShaderVariableDataType::SAMPLER, false}}};
  28. MaterialVariable::MaterialVariable()
  29. {
  30. m_mat4 = Mat4::getZero();
  31. }
  32. MaterialVariable::~MaterialVariable()
  33. {
  34. }
  35. MaterialResource::MaterialResource(ResourceManager* manager)
  36. : ResourceObject(manager)
  37. {
  38. }
  39. MaterialResource::~MaterialResource()
  40. {
  41. m_vars.destroy(getAllocator());
  42. m_constValues.destroy(getAllocator());
  43. m_mutations.destroy(getAllocator());
  44. }
  45. Error MaterialResource::load(const ResourceFilename& filename, Bool async)
  46. {
  47. XmlDocument doc;
  48. XmlElement el;
  49. Bool present = false;
  50. ANKI_CHECK(openFileParseXml(filename, doc));
  51. // <material>
  52. XmlElement rootEl;
  53. ANKI_CHECK(doc.getChildElement("material", rootEl));
  54. // shaderProgram
  55. CString fname;
  56. ANKI_CHECK(rootEl.getAttributeText("shaderProgram", fname));
  57. ANKI_CHECK(getManager().loadResource(fname, m_prog, async));
  58. m_descriptorSetIdx = U8(m_prog->getDescriptorSetIndex());
  59. // shadow
  60. ANKI_CHECK(rootEl.getAttributeNumberOptional("shadow", m_shadow, present));
  61. m_shadow = m_shadow != 0;
  62. // forwardShading
  63. ANKI_CHECK(rootEl.getAttributeNumberOptional("forwardShading", m_forwardShading, present));
  64. m_forwardShading = m_forwardShading != 0;
  65. // <mutators>
  66. XmlElement mutatorsEl;
  67. ANKI_CHECK(rootEl.getChildElementOptional("mutators", mutatorsEl));
  68. if(mutatorsEl)
  69. {
  70. ANKI_CHECK(parseMutators(mutatorsEl));
  71. }
  72. // <inputs>
  73. ANKI_CHECK(rootEl.getChildElementOptional("inputs", el));
  74. if(el)
  75. {
  76. ANKI_CHECK(parseInputs(el, async));
  77. }
  78. return Error::NONE;
  79. }
  80. Error MaterialResource::parseMutators(XmlElement mutatorsEl)
  81. {
  82. XmlElement mutatorEl;
  83. ANKI_CHECK(mutatorsEl.getChildElement("mutator", mutatorEl));
  84. U32 mutatorCount = 0;
  85. ANKI_CHECK(mutatorEl.getSiblingElementsCount(mutatorCount));
  86. ++mutatorCount;
  87. ANKI_ASSERT(mutatorCount > 0);
  88. m_mutations.create(getAllocator(), mutatorCount);
  89. mutatorCount = 0;
  90. do
  91. {
  92. ShaderProgramResourceMutation& mutation = m_mutations[mutatorCount];
  93. // name
  94. CString mutatorName;
  95. ANKI_CHECK(mutatorEl.getAttributeText("name", mutatorName));
  96. if(mutatorName.isEmpty())
  97. {
  98. ANKI_RESOURCE_LOGE("Mutator name is empty");
  99. return Error::USER_DATA;
  100. }
  101. if(mutatorName == "INSTANCE_COUNT" || mutatorName == "PASS" || mutatorName == "LOD" || mutatorName == "BONES"
  102. || mutatorName == "VELOCITY")
  103. {
  104. ANKI_RESOURCE_LOGE("Cannot list builtin mutator \"%s\"", &mutatorName[0]);
  105. return Error::USER_DATA;
  106. }
  107. // value
  108. ANKI_CHECK(mutatorEl.getAttributeNumber("value", mutation.m_value));
  109. // Find mutator
  110. mutation.m_mutator = m_prog->tryFindMutator(mutatorName);
  111. if(!mutation.m_mutator)
  112. {
  113. ANKI_RESOURCE_LOGE("Mutator not found in program %s", &mutatorName[0]);
  114. return Error::USER_DATA;
  115. }
  116. if(!mutation.m_mutator->valueExists(mutation.m_value))
  117. {
  118. ANKI_RESOURCE_LOGE("Value %d is not part of the mutator %s", mutation.m_value, &mutatorName[0]);
  119. return Error::USER_DATA;
  120. }
  121. // Advance
  122. ++mutatorCount;
  123. ANKI_CHECK(mutatorEl.getNextSiblingElement("mutator", mutatorEl));
  124. } while(mutatorEl);
  125. // Find the builtin mutators
  126. U builtinMutatorCount = 0;
  127. m_instanceMutator = m_prog->getInstancingMutator();
  128. if(m_instanceMutator)
  129. {
  130. if(m_instanceMutator->getName() != "INSTANCE_COUNT")
  131. {
  132. ANKI_RESOURCE_LOGE("If program is instanced then the instance mutator should be the INSTANCE_COUNT");
  133. return Error::USER_DATA;
  134. }
  135. if(m_instanceMutator->getValues().getSize() != MAX_INSTANCE_GROUPS)
  136. {
  137. ANKI_RESOURCE_LOGE("Mutator INSTANCE_COUNT should have %u values in the program", MAX_INSTANCE_GROUPS);
  138. return Error::USER_DATA;
  139. }
  140. for(U32 i = 0; i < MAX_INSTANCE_GROUPS; ++i)
  141. {
  142. if(m_instanceMutator->getValues()[i] != (1 << i))
  143. {
  144. ANKI_RESOURCE_LOGE("Values of the INSTANCE_COUNT mutator in the program are not the expected");
  145. return Error::USER_DATA;
  146. }
  147. }
  148. ++builtinMutatorCount;
  149. }
  150. m_passMutator = m_prog->tryFindMutator("PASS");
  151. if(m_passMutator)
  152. {
  153. if(m_passMutator->getValues().getSize() != U32(Pass::COUNT))
  154. {
  155. ANKI_RESOURCE_LOGE("Mutator PASS should have %u values in the program", U32(Pass::COUNT));
  156. return Error::USER_DATA;
  157. }
  158. for(U32 i = 0; i < U(Pass::COUNT); ++i)
  159. {
  160. if(m_passMutator->getValues()[i] != I(i))
  161. {
  162. ANKI_RESOURCE_LOGE("Values of the PASS mutator in the program are not the expected");
  163. return Error::USER_DATA;
  164. }
  165. }
  166. ++builtinMutatorCount;
  167. }
  168. if(!m_forwardShading && !m_passMutator)
  169. {
  170. ANKI_RESOURCE_LOGE("PASS mutator is required");
  171. return Error::USER_DATA;
  172. }
  173. m_lodMutator = m_prog->tryFindMutator("LOD");
  174. if(m_lodMutator)
  175. {
  176. if(m_lodMutator->getValues().getSize() > MAX_LOD_COUNT)
  177. {
  178. ANKI_RESOURCE_LOGE("Mutator LOD should have at least %u values in the program", U32(MAX_LOD_COUNT));
  179. return Error::USER_DATA;
  180. }
  181. for(U32 i = 0; i < m_lodMutator->getValues().getSize(); ++i)
  182. {
  183. if(m_lodMutator->getValues()[i] != I(i))
  184. {
  185. ANKI_RESOURCE_LOGE("Values of the LOD mutator in the program are not the expected");
  186. return Error::USER_DATA;
  187. }
  188. }
  189. m_lodCount = U8(m_lodMutator->getValues().getSize());
  190. ++builtinMutatorCount;
  191. }
  192. m_bonesMutator = m_prog->tryFindMutator("BONES");
  193. if(m_bonesMutator)
  194. {
  195. if(m_bonesMutator->getValues().getSize() != 2)
  196. {
  197. ANKI_RESOURCE_LOGE("Mutator BONES should have 2 values in the program");
  198. return Error::USER_DATA;
  199. }
  200. for(U32 i = 0; i < m_bonesMutator->getValues().getSize(); ++i)
  201. {
  202. if(m_bonesMutator->getValues()[i] != I(i))
  203. {
  204. ANKI_RESOURCE_LOGE("Values of the BONES mutator in the program are not the expected");
  205. return Error::USER_DATA;
  206. }
  207. }
  208. ++builtinMutatorCount;
  209. }
  210. m_velocityMutator = m_prog->tryFindMutator("VELOCITY");
  211. if(m_velocityMutator)
  212. {
  213. if(m_velocityMutator->getValues().getSize() != 2)
  214. {
  215. ANKI_RESOURCE_LOGE("Mutator VELOCITY should have 2 values in the program");
  216. return Error::USER_DATA;
  217. }
  218. for(U32 i = 0; i < m_velocityMutator->getValues().getSize(); ++i)
  219. {
  220. if(m_velocityMutator->getValues()[i] != I(i))
  221. {
  222. ANKI_RESOURCE_LOGE("Values of the VELOCITY mutator in the program are not the expected");
  223. return Error::USER_DATA;
  224. }
  225. }
  226. ++builtinMutatorCount;
  227. }
  228. if(m_mutations.getSize() + builtinMutatorCount != m_prog->getMutators().getSize())
  229. {
  230. ANKI_RESOURCE_LOGE("Some mutatators are unacounted for");
  231. return Error::USER_DATA;
  232. }
  233. return Error::NONE;
  234. }
  235. Error MaterialResource::parseInputs(XmlElement inputsEl, Bool async)
  236. {
  237. // Iterate the program's variables and get counts
  238. U32 constInputCount = 0;
  239. U32 nonConstInputCount = 0;
  240. for(const ShaderProgramResourceInputVariable& in : m_prog->getInputVariables())
  241. {
  242. if(!in.acceptAllMutations(m_mutations))
  243. {
  244. // Will not be used by this material at all
  245. continue;
  246. }
  247. if(in.isConstant())
  248. {
  249. ++constInputCount;
  250. }
  251. else
  252. {
  253. ++nonConstInputCount;
  254. }
  255. }
  256. m_vars.create(getAllocator(), nonConstInputCount);
  257. m_constValues.create(getAllocator(), constInputCount);
  258. // Connect the input variables
  259. XmlElement inputEl;
  260. ANKI_CHECK(inputsEl.getChildElementOptional("input", inputEl));
  261. while(inputEl)
  262. {
  263. // Get var name
  264. CString varName;
  265. ANKI_CHECK(inputEl.getAttributeText("shaderInput", varName));
  266. // Try find var
  267. const ShaderProgramResourceInputVariable* foundVar = m_prog->tryFindInputVariable(varName);
  268. if(foundVar == nullptr)
  269. {
  270. ANKI_RESOURCE_LOGE("Variable \"%s\" not found", &varName[0]);
  271. return Error::USER_DATA;
  272. }
  273. if(!foundVar->acceptAllMutations(m_mutations))
  274. {
  275. ANKI_RESOURCE_LOGE("Variable \"%s\" is not needed by the material's mutations", varName.cstr());
  276. return Error::USER_DATA;
  277. }
  278. // Process var
  279. if(foundVar->isConstant())
  280. {
  281. // Const
  282. ShaderProgramResourceConstantValue& constVal = m_constValues[--constInputCount];
  283. constVal.m_variable = foundVar;
  284. switch(foundVar->getShaderVariableDataType())
  285. {
  286. case ShaderVariableDataType::INT:
  287. ANKI_CHECK(inputEl.getAttributeNumber("value", constVal.m_int));
  288. break;
  289. case ShaderVariableDataType::IVEC2:
  290. ANKI_CHECK(inputEl.getAttributeNumbers("value", constVal.m_ivec2));
  291. break;
  292. case ShaderVariableDataType::IVEC3:
  293. ANKI_CHECK(inputEl.getAttributeNumbers("value", constVal.m_ivec3));
  294. break;
  295. case ShaderVariableDataType::IVEC4:
  296. ANKI_CHECK(inputEl.getAttributeNumbers("value", constVal.m_ivec4));
  297. break;
  298. case ShaderVariableDataType::UINT:
  299. ANKI_CHECK(inputEl.getAttributeNumber("value", constVal.m_uint));
  300. break;
  301. case ShaderVariableDataType::UVEC2:
  302. ANKI_CHECK(inputEl.getAttributeNumbers("value", constVal.m_uvec2));
  303. break;
  304. case ShaderVariableDataType::UVEC3:
  305. ANKI_CHECK(inputEl.getAttributeNumbers("value", constVal.m_uvec3));
  306. break;
  307. case ShaderVariableDataType::UVEC4:
  308. ANKI_CHECK(inputEl.getAttributeNumbers("value", constVal.m_uvec4));
  309. break;
  310. case ShaderVariableDataType::FLOAT:
  311. ANKI_CHECK(inputEl.getAttributeNumber("value", constVal.m_float));
  312. break;
  313. case ShaderVariableDataType::VEC2:
  314. ANKI_CHECK(inputEl.getAttributeNumbers("value", constVal.m_vec2));
  315. break;
  316. case ShaderVariableDataType::VEC3:
  317. ANKI_CHECK(inputEl.getAttributeNumbers("value", constVal.m_vec3));
  318. break;
  319. case ShaderVariableDataType::VEC4:
  320. ANKI_CHECK(inputEl.getAttributeNumbers("value", constVal.m_vec4));
  321. break;
  322. default:
  323. ANKI_ASSERT(0);
  324. break;
  325. }
  326. }
  327. else
  328. {
  329. // Builtin or not
  330. Bool builtinPresent = false;
  331. CString builtinStr;
  332. ANKI_CHECK(inputEl.getAttributeTextOptional("builtin", builtinStr, builtinPresent));
  333. if(builtinPresent)
  334. {
  335. // Builtin
  336. U i;
  337. for(i = 0; i < BUILTIN_INFOS.getSize(); ++i)
  338. {
  339. if(builtinStr == BUILTIN_INFOS[i].m_name)
  340. {
  341. break;
  342. }
  343. }
  344. if(i == BUILTIN_INFOS.getSize())
  345. {
  346. ANKI_RESOURCE_LOGE("Incorrect builtin variable: %s", &builtinStr[0]);
  347. return Error::USER_DATA;
  348. }
  349. if(BUILTIN_INFOS[i].m_type != foundVar->getShaderVariableDataType())
  350. {
  351. ANKI_RESOURCE_LOGE(
  352. "The type of the builtin variable in the shader program is not the correct one: %s",
  353. &builtinStr[0]);
  354. return Error::USER_DATA;
  355. }
  356. if(foundVar->isInstanced() && !BUILTIN_INFOS[i].m_instanced)
  357. {
  358. ANKI_RESOURCE_LOGE("Builtin variable %s cannot be instanced", BUILTIN_INFOS[i].m_name);
  359. return Error::USER_DATA;
  360. }
  361. MaterialVariable& mtlVar = m_vars[--nonConstInputCount];
  362. mtlVar.m_input = foundVar;
  363. mtlVar.m_builtin = BuiltinMaterialVariableId(i + 1);
  364. }
  365. else
  366. {
  367. // Not built-in
  368. if(foundVar->isInstanced())
  369. {
  370. ANKI_RESOURCE_LOGE("Only some builtin variables can be instanced: %s", &foundVar->getName()[0]);
  371. return Error::USER_DATA;
  372. }
  373. MaterialVariable& mtlVar = m_vars[--nonConstInputCount];
  374. mtlVar.m_input = foundVar;
  375. switch(foundVar->getShaderVariableDataType())
  376. {
  377. case ShaderVariableDataType::INT:
  378. ANKI_CHECK(inputEl.getAttributeNumber("value", mtlVar.m_int));
  379. break;
  380. case ShaderVariableDataType::IVEC2:
  381. ANKI_CHECK(inputEl.getAttributeNumbers("value", mtlVar.m_ivec2));
  382. break;
  383. case ShaderVariableDataType::IVEC3:
  384. ANKI_CHECK(inputEl.getAttributeNumbers("value", mtlVar.m_ivec3));
  385. break;
  386. case ShaderVariableDataType::IVEC4:
  387. ANKI_CHECK(inputEl.getAttributeNumbers("value", mtlVar.m_ivec4));
  388. break;
  389. case ShaderVariableDataType::UINT:
  390. ANKI_CHECK(inputEl.getAttributeNumber("value", mtlVar.m_uint));
  391. break;
  392. case ShaderVariableDataType::UVEC2:
  393. ANKI_CHECK(inputEl.getAttributeNumbers("value", mtlVar.m_uvec2));
  394. break;
  395. case ShaderVariableDataType::UVEC3:
  396. ANKI_CHECK(inputEl.getAttributeNumbers("value", mtlVar.m_uvec3));
  397. break;
  398. case ShaderVariableDataType::UVEC4:
  399. ANKI_CHECK(inputEl.getAttributeNumbers("value", mtlVar.m_uvec4));
  400. break;
  401. case ShaderVariableDataType::FLOAT:
  402. ANKI_CHECK(inputEl.getAttributeNumber("value", mtlVar.m_float));
  403. break;
  404. case ShaderVariableDataType::VEC2:
  405. ANKI_CHECK(inputEl.getAttributeNumbers("value", mtlVar.m_vec2));
  406. break;
  407. case ShaderVariableDataType::VEC3:
  408. ANKI_CHECK(inputEl.getAttributeNumbers("value", mtlVar.m_vec3));
  409. break;
  410. case ShaderVariableDataType::VEC4:
  411. ANKI_CHECK(inputEl.getAttributeNumbers("value", mtlVar.m_vec4));
  412. break;
  413. case ShaderVariableDataType::MAT3:
  414. ANKI_CHECK(inputEl.getAttributeNumbers("value", mtlVar.m_mat3));
  415. break;
  416. case ShaderVariableDataType::MAT4:
  417. ANKI_CHECK(inputEl.getAttributeNumbers("value", mtlVar.m_mat4));
  418. break;
  419. case ShaderVariableDataType::TEXTURE_2D:
  420. case ShaderVariableDataType::TEXTURE_2D_ARRAY:
  421. case ShaderVariableDataType::TEXTURE_3D:
  422. case ShaderVariableDataType::TEXTURE_CUBE:
  423. {
  424. CString texfname;
  425. ANKI_CHECK(inputEl.getAttributeText("value", texfname));
  426. ANKI_CHECK(getManager().loadResource(texfname, mtlVar.m_tex, async));
  427. break;
  428. }
  429. default:
  430. ANKI_ASSERT(0);
  431. break;
  432. }
  433. }
  434. }
  435. // Advance
  436. ANKI_CHECK(inputEl.getNextSiblingElement("input", inputEl));
  437. }
  438. if(nonConstInputCount != 0)
  439. {
  440. ANKI_RESOURCE_LOGE("Forgot to list %u shader program inputs in this material", nonConstInputCount);
  441. return Error::USER_DATA;
  442. }
  443. if(constInputCount != 0)
  444. {
  445. ANKI_RESOURCE_LOGE("Forgot to list %u constant shader program variables in this material", constInputCount);
  446. return Error::USER_DATA;
  447. }
  448. return Error::NONE;
  449. }
  450. const MaterialVariant& MaterialResource::getOrCreateVariant(const RenderingKey& key_) const
  451. {
  452. RenderingKey key = key_;
  453. key.setLod(min<U32>(m_lodCount - 1, key.getLod()));
  454. if(!isInstanced())
  455. {
  456. ANKI_ASSERT(key.getInstanceCount() == 1);
  457. }
  458. ANKI_ASSERT(!key.isSkinned() || m_bonesMutator);
  459. ANKI_ASSERT(!key.hasVelocity() || m_velocityMutator);
  460. key.setInstanceCount(1 << getInstanceGroupIdx(key.getInstanceCount()));
  461. MaterialVariant& variant = m_variantMatrix[key.getPass()][key.getLod()][getInstanceGroupIdx(key.getInstanceCount())]
  462. [key.isSkinned()][key.hasVelocity()];
  463. LockGuard<SpinLock> lock(m_variantMatrixMtx);
  464. if(variant.m_variant == nullptr)
  465. {
  466. const U32 mutatorCount = m_mutations.getSize() + ((m_instanceMutator) ? 1 : 0) + ((m_passMutator) ? 1 : 0)
  467. + ((m_lodMutator) ? 1 : 0) + ((m_bonesMutator) ? 1 : 0)
  468. + ((m_velocityMutator) ? 1 : 0);
  469. DynamicArrayAuto<ShaderProgramResourceMutation> mutations(getTempAllocator());
  470. mutations.create(mutatorCount);
  471. U32 count = m_mutations.getSize();
  472. if(m_mutations.getSize())
  473. {
  474. memcpy(&mutations[0], &m_mutations[0], m_mutations.getSize() * sizeof(m_mutations[0]));
  475. }
  476. if(m_instanceMutator)
  477. {
  478. mutations[count].m_mutator = m_instanceMutator;
  479. mutations[count].m_value = key.getInstanceCount();
  480. ++count;
  481. }
  482. if(m_passMutator)
  483. {
  484. mutations[count].m_mutator = m_passMutator;
  485. mutations[count].m_value = I32(key.getPass());
  486. ++count;
  487. }
  488. if(m_lodMutator)
  489. {
  490. mutations[count].m_mutator = m_lodMutator;
  491. mutations[count].m_value = I32(key.getLod());
  492. ++count;
  493. }
  494. if(m_bonesMutator)
  495. {
  496. mutations[count].m_mutator = m_bonesMutator;
  497. mutations[count].m_value = key.isSkinned() != 0;
  498. ++count;
  499. }
  500. if(m_velocityMutator)
  501. {
  502. mutations[count].m_mutator = m_velocityMutator;
  503. mutations[count].m_value = key.hasVelocity() != 0;
  504. ++count;
  505. }
  506. m_prog->getOrCreateVariant(
  507. ConstWeakArray<ShaderProgramResourceMutation>(mutations.getSize() ? &mutations[0] : nullptr, count),
  508. ConstWeakArray<ShaderProgramResourceConstantValue>(
  509. (m_constValues.getSize()) ? &m_constValues[0] : nullptr, m_constValues.getSize()),
  510. variant.m_variant);
  511. }
  512. return variant;
  513. }
  514. U32 MaterialResource::getInstanceGroupIdx(U32 instanceCount)
  515. {
  516. ANKI_ASSERT(instanceCount > 0);
  517. instanceCount = nextPowerOfTwo(instanceCount);
  518. ANKI_ASSERT(instanceCount <= MAX_INSTANCES);
  519. return U32(std::log2(F32(instanceCount)));
  520. }
  521. } // end namespace anki