ShaderProgramResourceSystem.cpp 13 KB

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