ShaderProgramResourceSystem.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. // Copyright (C) 2009-2023, 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/ShaderProgramResourceSystem.h>
  6. #include <AnKi/Resource/ResourceFilesystem.h>
  7. #include <AnKi/Resource/ResourceManager.h>
  8. #include <AnKi/Util/Tracer.h>
  9. #include <AnKi/Gr/GrManager.h>
  10. #include <AnKi/ShaderCompiler/ShaderProgramCompiler.h>
  11. #include <AnKi/Util/Filesystem.h>
  12. #include <AnKi/Util/ThreadHive.h>
  13. #include <AnKi/Util/System.h>
  14. #include <AnKi/Util/BitSet.h>
  15. #include <AnKi/Core/ConfigSet.h>
  16. namespace anki {
  17. U64 ShaderProgramRaytracingLibrary::generateShaderGroupGroupHash(CString resourceFilename, U64 mutationHash)
  18. {
  19. ANKI_ASSERT(resourceFilename.getLength() > 0);
  20. StringRaii basename(&ResourceMemoryPool::getSingleton());
  21. getFilepathFilename(resourceFilename, basename);
  22. const U64 hash = appendHash(basename.cstr(), basename.getLength(), mutationHash);
  23. return hash;
  24. }
  25. Error ShaderProgramResourceSystem::init(GrManager& gr)
  26. {
  27. if(!gr.getDeviceCapabilities().m_rayTracingEnabled)
  28. {
  29. return Error::kNone;
  30. }
  31. // Create RT pipeline libraries
  32. const Error err = createRayTracingPrograms(gr, m_rtLibraries);
  33. if(err)
  34. {
  35. ANKI_RESOURCE_LOGE("Failed to create ray tracing programs");
  36. }
  37. return err;
  38. }
  39. Error ShaderProgramResourceSystem::createRayTracingPrograms(
  40. GrManager& gr, ResourceDynamicArray<ShaderProgramRaytracingLibrary>& outLibs)
  41. {
  42. ANKI_RESOURCE_LOGI("Creating ray tracing programs");
  43. U32 rtProgramCount = 0;
  44. class Shader
  45. {
  46. public:
  47. ShaderPtr m_shader;
  48. U64 m_hash = 0; ///< Hash of the binary.
  49. };
  50. class ShaderGroup
  51. {
  52. public:
  53. U32 m_rayGen = kMaxU32;
  54. U32 m_miss = kMaxU32;
  55. U32 m_chit = kMaxU32;
  56. U32 m_ahit = kMaxU32;
  57. U64 m_hitGroupHash = 0;
  58. };
  59. class Lib
  60. {
  61. public:
  62. GrManager* m_gr;
  63. ResourceString m_name;
  64. ResourceDynamicArray<Shader> m_shaders;
  65. ResourceDynamicArray<ShaderGroup> m_shaderGroups;
  66. ShaderTypeBit m_presentStages = ShaderTypeBit::kNone;
  67. U32 m_rayTypeCount = 0;
  68. BitSet<64> m_rayTypeMask = {false};
  69. U32 m_rayGenShaderGroupCount = 0;
  70. U32 m_missShaderGroupCount = 0;
  71. U32 m_hitShaderGroupCount = 0;
  72. Lib(GrManager* gr)
  73. : m_gr(gr)
  74. {
  75. }
  76. U32 addShader(const ShaderProgramBinaryCodeBlock& codeBlock, CString progName, ShaderType shaderType)
  77. {
  78. Shader* shader = nullptr;
  79. for(Shader& s : m_shaders)
  80. {
  81. if(s.m_hash == codeBlock.m_hash)
  82. {
  83. shader = &s;
  84. break;
  85. }
  86. }
  87. if(shader == nullptr)
  88. {
  89. shader = m_shaders.emplaceBack();
  90. ShaderInitInfo inf(progName);
  91. inf.m_shaderType = shaderType;
  92. inf.m_binary = codeBlock.m_binary;
  93. shader->m_shader = m_gr->newShader(inf);
  94. shader->m_hash = codeBlock.m_hash;
  95. m_presentStages |= ShaderTypeBit(1 << shaderType);
  96. }
  97. return U32(shader - m_shaders.getBegin());
  98. }
  99. void addGroup(CString filename, U64 mutationHash, U32 rayGen, U32 miss, U32 chit, U32 ahit)
  100. {
  101. const U64 groupHash = ShaderProgramRaytracingLibrary::generateShaderGroupGroupHash(filename, mutationHash);
  102. #if ANKI_ENABLE_ASSERTIONS
  103. for(const ShaderGroup& group : m_shaderGroups)
  104. {
  105. ANKI_ASSERT(group.m_hitGroupHash != groupHash && "Shouldn't find group with the same hash");
  106. }
  107. #endif
  108. ShaderGroup group;
  109. group.m_rayGen = rayGen;
  110. group.m_miss = miss;
  111. group.m_chit = chit;
  112. group.m_ahit = ahit;
  113. group.m_hitGroupHash = groupHash;
  114. m_shaderGroups.emplaceBack(group);
  115. if(rayGen < kMaxU32)
  116. {
  117. ++m_rayGenShaderGroupCount;
  118. }
  119. else if(miss < kMaxU32)
  120. {
  121. ++m_missShaderGroupCount;
  122. }
  123. else
  124. {
  125. ANKI_ASSERT(chit < kMaxU32 || ahit < kMaxU32);
  126. ++m_hitShaderGroupCount;
  127. }
  128. }
  129. };
  130. ResourceDynamicArray<Lib> libs;
  131. ANKI_CHECK(ResourceManager::getSingleton().getFilesystem().iterateAllFilenames([&](CString filename) -> Error {
  132. // Check file extension
  133. StringRaii extension(&ResourceMemoryPool::getSingleton());
  134. getFilepathExtension(filename, extension);
  135. const Char binExtension[] = "ankiprogbin";
  136. if(extension.getLength() != sizeof(binExtension) - 1 || extension != binExtension)
  137. {
  138. return Error::kNone;
  139. }
  140. if(filename.find("ShaderBinaries/Rt") != 0)
  141. {
  142. // Doesn't start with the expected path, skip it
  143. return Error::kNone;
  144. }
  145. // Get the binary
  146. ResourceFilePtr file;
  147. ANKI_CHECK(ResourceManager::getSingleton().getFilesystem().openFile(filename, file));
  148. ShaderProgramBinaryWrapper binaryw(&ResourceMemoryPool::getSingleton());
  149. ANKI_CHECK(binaryw.deserializeFromAnyFile(*file));
  150. const ShaderProgramBinary& binary = binaryw.getBinary();
  151. if(!(binary.m_presentShaderTypes & ShaderTypeBit::kAllRayTracing))
  152. {
  153. return Error::kNone;
  154. }
  155. // Checks
  156. if(binary.m_libraryName[0] == '\0')
  157. {
  158. ANKI_RESOURCE_LOGE("Library is missing from program: %s", filename.cstr());
  159. return Error::kUserData;
  160. }
  161. // Create the program name
  162. StringRaii progName(&ResourceMemoryPool::getSingleton());
  163. getFilepathFilename(filename, progName);
  164. // Find or create the lib
  165. Lib* lib = nullptr;
  166. {
  167. for(Lib& l : libs)
  168. {
  169. if(l.m_name == CString(&binary.m_libraryName[0]))
  170. {
  171. lib = &l;
  172. break;
  173. }
  174. }
  175. if(lib == nullptr)
  176. {
  177. libs.emplaceBack(&gr);
  178. lib = &libs.getBack();
  179. lib->m_name.create(CString(&binary.m_libraryName[0]));
  180. }
  181. }
  182. // Update the ray type
  183. const U32 rayTypeNumber = binary.m_rayType;
  184. if(rayTypeNumber != kMaxU32)
  185. {
  186. lib->m_rayTypeCount = max(lib->m_rayTypeCount, rayTypeNumber + 1);
  187. lib->m_rayTypeMask.set(rayTypeNumber);
  188. }
  189. // Ray gen
  190. if(!!(binary.m_presentShaderTypes & ShaderTypeBit::kRayGen))
  191. {
  192. if(!!(binary.m_presentShaderTypes & ~ShaderTypeBit::kRayGen))
  193. {
  194. ANKI_RESOURCE_LOGE("Ray gen can't co-exist with other types: %s", filename.cstr());
  195. return Error::kUserData;
  196. }
  197. if(binary.m_constants.getSize())
  198. {
  199. ANKI_RESOURCE_LOGE("Ray gen can't have spec constants ATM: %s", filename.cstr());
  200. return Error::kUserData;
  201. }
  202. // Iterate all mutations
  203. ConstWeakArray<ShaderProgramBinaryMutation> mutations;
  204. ShaderProgramBinaryMutation dummyMutation;
  205. if(binary.m_mutations.getSize() > 1)
  206. {
  207. mutations = binary.m_mutations;
  208. }
  209. else
  210. {
  211. dummyMutation.m_hash = 0;
  212. dummyMutation.m_variantIndex = 0;
  213. mutations = ConstWeakArray<ShaderProgramBinaryMutation>(&dummyMutation, 1);
  214. }
  215. for(const ShaderProgramBinaryMutation& mutation : mutations)
  216. {
  217. const ShaderProgramBinaryVariant& variant = binary.m_variants[mutation.m_variantIndex];
  218. const U32 codeBlockIndex = variant.m_codeBlockIndices[ShaderType::kRayGen];
  219. ANKI_ASSERT(codeBlockIndex != kMaxU32);
  220. const U32 shaderIdx =
  221. lib->addShader(binary.m_codeBlocks[codeBlockIndex], progName, ShaderType::kRayGen);
  222. lib->addGroup(filename, mutation.m_hash, shaderIdx, kMaxU32, kMaxU32, kMaxU32);
  223. }
  224. }
  225. // Miss shaders
  226. if(!!(binary.m_presentShaderTypes & ShaderTypeBit::kMiss))
  227. {
  228. if(!!(binary.m_presentShaderTypes & ~ShaderTypeBit::kMiss))
  229. {
  230. ANKI_RESOURCE_LOGE("Miss shaders can't co-exist with other types: %s", filename.cstr());
  231. return Error::kUserData;
  232. }
  233. if(binary.m_constants.getSize())
  234. {
  235. ANKI_RESOURCE_LOGE("Miss can't have spec constants ATM: %s", filename.cstr());
  236. return Error::kUserData;
  237. }
  238. if(rayTypeNumber == kMaxU32)
  239. {
  240. ANKI_RESOURCE_LOGE("Miss shader should have set the ray type: %s", filename.cstr());
  241. return Error::kUserData;
  242. }
  243. // Iterate all mutations
  244. ConstWeakArray<ShaderProgramBinaryMutation> mutations;
  245. ShaderProgramBinaryMutation dummyMutation;
  246. if(binary.m_mutations.getSize() > 1)
  247. {
  248. mutations = binary.m_mutations;
  249. }
  250. else
  251. {
  252. dummyMutation.m_hash = 0;
  253. dummyMutation.m_variantIndex = 0;
  254. mutations = ConstWeakArray<ShaderProgramBinaryMutation>(&dummyMutation, 1);
  255. }
  256. for(const ShaderProgramBinaryMutation& mutation : mutations)
  257. {
  258. const ShaderProgramBinaryVariant& variant = binary.m_variants[mutation.m_variantIndex];
  259. const U32 codeBlockIndex = variant.m_codeBlockIndices[ShaderType::kMiss];
  260. ANKI_ASSERT(codeBlockIndex != kMaxU32);
  261. const U32 shaderIdx = lib->addShader(binary.m_codeBlocks[codeBlockIndex], progName, ShaderType::kMiss);
  262. lib->addGroup(filename, mutation.m_hash, kMaxU32, shaderIdx, kMaxU32, kMaxU32);
  263. }
  264. }
  265. // Hit shaders
  266. if(!!(binary.m_presentShaderTypes & (ShaderTypeBit::kAnyHit | ShaderTypeBit::kClosestHit)))
  267. {
  268. if(!!(binary.m_presentShaderTypes & ~(ShaderTypeBit::kAnyHit | ShaderTypeBit::kClosestHit)))
  269. {
  270. ANKI_RESOURCE_LOGE("Hit shaders can't co-exist with other types: %s", filename.cstr());
  271. return Error::kUserData;
  272. }
  273. if(rayTypeNumber == kMaxU32)
  274. {
  275. ANKI_RESOURCE_LOGE("Hit shaders should have set the ray type: %s", filename.cstr());
  276. return Error::kUserData;
  277. }
  278. // Before you iterate the mutations do some work if there are none
  279. ConstWeakArray<ShaderProgramBinaryMutation> mutations;
  280. ShaderProgramBinaryMutation dummyMutation;
  281. if(binary.m_mutations.getSize() > 1)
  282. {
  283. mutations = binary.m_mutations;
  284. }
  285. else
  286. {
  287. dummyMutation.m_hash = 0;
  288. dummyMutation.m_variantIndex = 0;
  289. mutations = ConstWeakArray<ShaderProgramBinaryMutation>(&dummyMutation, 1);
  290. }
  291. // Iterate all mutations
  292. for(const ShaderProgramBinaryMutation& mutation : mutations)
  293. {
  294. const ShaderProgramBinaryVariant& variant = binary.m_variants[mutation.m_variantIndex];
  295. const U32 ahitCodeBlockIndex = variant.m_codeBlockIndices[ShaderType::kAnyHit];
  296. const U32 chitCodeBlockIndex = variant.m_codeBlockIndices[ShaderType::kClosestHit];
  297. ANKI_ASSERT(ahitCodeBlockIndex != kMaxU32 || chitCodeBlockIndex != kMaxU32);
  298. const U32 ahitShaderIdx =
  299. (ahitCodeBlockIndex != kMaxU32)
  300. ? lib->addShader(binary.m_codeBlocks[ahitCodeBlockIndex], progName, ShaderType::kAnyHit)
  301. : kMaxU32;
  302. const U32 chitShaderIdx =
  303. (chitCodeBlockIndex != kMaxU32)
  304. ? lib->addShader(binary.m_codeBlocks[chitCodeBlockIndex], progName, ShaderType::kClosestHit)
  305. : kMaxU32;
  306. lib->addGroup(filename, mutation.m_hash, kMaxU32, kMaxU32, chitShaderIdx, ahitShaderIdx);
  307. }
  308. }
  309. return Error::kNone;
  310. })); // For all RT filenames
  311. // Create the libraries the value that goes to the m_resourceHashToShaderGroupHandleIndex hashmap is the index of
  312. // the shader handle inside the program. Leverage the fact that there is a predefined order between shader types.
  313. // See the ShaderProgram class for info.
  314. if(libs.getSize() != 0)
  315. {
  316. outLibs.create(libs.getSize());
  317. for(U32 libIdx = 0; libIdx < libs.getSize(); ++libIdx)
  318. {
  319. ShaderProgramRaytracingLibrary& outLib = outLibs[libIdx];
  320. const Lib& inLib = libs[libIdx];
  321. if(inLib.m_presentStages
  322. != (ShaderTypeBit::kRayGen | ShaderTypeBit::kMiss | ShaderTypeBit::kClosestHit | ShaderTypeBit::kAnyHit))
  323. {
  324. ANKI_RESOURCE_LOGE("The libray is missing shader shader types: %s", inLib.m_name.cstr());
  325. return Error::kUserData;
  326. }
  327. if(inLib.m_rayTypeCount != inLib.m_rayTypeMask.getEnabledBitCount())
  328. {
  329. ANKI_RESOURCE_LOGE("Ray types are not contiguous for library: %s", inLib.m_name.cstr());
  330. return Error::kUserData;
  331. }
  332. outLib.m_libraryName.create(inLib.m_name);
  333. outLib.m_rayTypeCount = inLib.m_rayTypeCount;
  334. ResourceDynamicArray<RayTracingHitGroup> initInfoHitGroups;
  335. ResourceDynamicArray<ShaderPtr> missShaders;
  336. ResourceDynamicArray<ShaderPtr> rayGenShaders;
  337. // Add the hitgroups to the init info
  338. for(U32 shaderGroupIdx = 0; shaderGroupIdx < inLib.m_shaderGroups.getSize(); ++shaderGroupIdx)
  339. {
  340. const ShaderGroup& inShaderGroup = inLib.m_shaderGroups[shaderGroupIdx];
  341. ANKI_ASSERT(inShaderGroup.m_hitGroupHash != 0);
  342. if(inShaderGroup.m_ahit < kMaxU32 || inShaderGroup.m_chit < kMaxU32)
  343. {
  344. // Hit shaders
  345. ANKI_ASSERT(inShaderGroup.m_miss == kMaxU32 && inShaderGroup.m_rayGen == kMaxU32);
  346. RayTracingHitGroup* infoHitGroup = initInfoHitGroups.emplaceBack();
  347. if(inShaderGroup.m_ahit < kMaxU32)
  348. {
  349. infoHitGroup->m_anyHitShader = inLib.m_shaders[inShaderGroup.m_ahit].m_shader;
  350. }
  351. if(inShaderGroup.m_chit < kMaxU32)
  352. {
  353. infoHitGroup->m_closestHitShader = inLib.m_shaders[inShaderGroup.m_chit].m_shader;
  354. }
  355. // The hit shaders are after ray gen and miss shaders
  356. const U32 idx =
  357. inLib.m_rayGenShaderGroupCount + inLib.m_missShaderGroupCount + initInfoHitGroups.getSize() - 1;
  358. outLib.m_resourceHashToShaderGroupHandleIndex.emplace(inShaderGroup.m_hitGroupHash, idx);
  359. }
  360. else if(inShaderGroup.m_miss < kMaxU32)
  361. {
  362. // Miss shader
  363. ANKI_ASSERT(inShaderGroup.m_ahit == kMaxU32 && inShaderGroup.m_chit == kMaxU32
  364. && inShaderGroup.m_rayGen == kMaxU32);
  365. missShaders.emplaceBack(inLib.m_shaders[inShaderGroup.m_miss].m_shader);
  366. // The miss shaders are after ray gen
  367. const U32 idx = inLib.m_rayGenShaderGroupCount + missShaders.getSize() - 1;
  368. outLib.m_resourceHashToShaderGroupHandleIndex.emplace(inShaderGroup.m_hitGroupHash, idx);
  369. }
  370. else
  371. {
  372. // Ray gen shader
  373. ANKI_ASSERT(inShaderGroup.m_ahit == kMaxU32 && inShaderGroup.m_chit == kMaxU32
  374. && inShaderGroup.m_miss == kMaxU32 && inShaderGroup.m_rayGen < kMaxU32);
  375. rayGenShaders.emplaceBack(inLib.m_shaders[inShaderGroup.m_rayGen].m_shader);
  376. // Ray gen shaders are first
  377. const U32 idx = rayGenShaders.getSize() - 1;
  378. outLib.m_resourceHashToShaderGroupHandleIndex.emplace(inShaderGroup.m_hitGroupHash, idx);
  379. }
  380. } // end for all groups
  381. // Create the program
  382. ShaderProgramInitInfo inf(inLib.m_name);
  383. inf.m_rayTracingShaders.m_rayGenShaders = rayGenShaders;
  384. inf.m_rayTracingShaders.m_missShaders = missShaders;
  385. inf.m_rayTracingShaders.m_hitGroups = initInfoHitGroups;
  386. outLib.m_program = gr.newShaderProgram(inf);
  387. ++rtProgramCount;
  388. }
  389. }
  390. ANKI_RESOURCE_LOGI("Created %u ray tracing programs", rtProgramCount);
  391. return Error::kNone;
  392. }
  393. } // end namespace anki