ShaderProgramResourceSystem.cpp 14 KB

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