ShaderProgramResourceSystem.cpp 14 KB

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