Glslang.cpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. // Copyright (C) 2009-2022, 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/ShaderCompiler/Glslang.h>
  6. #include <AnKi/Util/StringList.h>
  7. #include <AnKi/Util/File.h>
  8. #include <AnKi/Util/Filesystem.h>
  9. #if ANKI_COMPILER_GCC_COMPATIBLE
  10. # pragma GCC diagnostic push
  11. # pragma GCC diagnostic ignored "-Wundef"
  12. # pragma GCC diagnostic ignored "-Wconversion"
  13. #endif
  14. #define ENABLE_OPT 0
  15. #include <Glslang/glslang/Public/ShaderLang.h>
  16. #include <Glslang/SPIRV/GlslangToSpv.h>
  17. #include <Glslang/StandAlone/DirStackFileIncluder.h>
  18. #if ANKI_COMPILER_GCC_COMPATIBLE
  19. # pragma GCC diagnostic pop
  20. #endif
  21. #define ANKI_GLSLANG_DUMP 0
  22. namespace anki {
  23. #if ANKI_GLSLANG_DUMP
  24. static Atomic<U32> g_dumpFileCount;
  25. #endif
  26. class GlslangCtx
  27. {
  28. public:
  29. GlslangCtx()
  30. {
  31. glslang::InitializeProcess();
  32. }
  33. ~GlslangCtx()
  34. {
  35. glslang::FinalizeProcess();
  36. }
  37. };
  38. GlslangCtx g_glslangCtx;
  39. static TBuiltInResource setGlslangLimits()
  40. {
  41. TBuiltInResource c = {};
  42. c.maxLights = 32;
  43. c.maxClipPlanes = 6;
  44. c.maxTextureUnits = 32;
  45. c.maxTextureCoords = 32;
  46. c.maxVertexAttribs = 64;
  47. c.maxVertexUniformComponents = 4096;
  48. c.maxVaryingFloats = 64;
  49. c.maxVertexTextureImageUnits = 32;
  50. c.maxCombinedTextureImageUnits = 80;
  51. c.maxTextureImageUnits = 32;
  52. c.maxFragmentUniformComponents = 4096;
  53. c.maxDrawBuffers = 32;
  54. c.maxVertexUniformVectors = 128;
  55. c.maxVaryingVectors = 8;
  56. c.maxFragmentUniformVectors = 16;
  57. c.maxVertexOutputVectors = 16;
  58. c.maxFragmentInputVectors = 15;
  59. c.minProgramTexelOffset = -8;
  60. c.maxProgramTexelOffset = 7;
  61. c.maxClipDistances = 8;
  62. c.maxComputeWorkGroupCountX = 65535;
  63. c.maxComputeWorkGroupCountY = 65535;
  64. c.maxComputeWorkGroupCountZ = 65535;
  65. c.maxComputeWorkGroupSizeX = 1024;
  66. c.maxComputeWorkGroupSizeY = 1024;
  67. c.maxComputeWorkGroupSizeZ = 64;
  68. c.maxComputeUniformComponents = 1024;
  69. c.maxComputeTextureImageUnits = 16;
  70. c.maxComputeImageUniforms = 8;
  71. c.maxComputeAtomicCounters = 8;
  72. c.maxComputeAtomicCounterBuffers = 1;
  73. c.maxVaryingComponents = 60;
  74. c.maxVertexOutputComponents = 64;
  75. c.maxGeometryInputComponents = 64;
  76. c.maxGeometryOutputComponents = 128;
  77. c.maxFragmentInputComponents = 128;
  78. c.maxImageUnits = 8;
  79. c.maxCombinedImageUnitsAndFragmentOutputs = 8;
  80. c.maxCombinedShaderOutputResources = 8;
  81. c.maxImageSamples = 0;
  82. c.maxVertexImageUniforms = 0;
  83. c.maxTessControlImageUniforms = 0;
  84. c.maxTessEvaluationImageUniforms = 0;
  85. c.maxGeometryImageUniforms = 0;
  86. c.maxFragmentImageUniforms = 8;
  87. c.maxCombinedImageUniforms = 8;
  88. c.maxGeometryTextureImageUnits = 16;
  89. c.maxGeometryOutputVertices = 256;
  90. c.maxGeometryTotalOutputComponents = 1024;
  91. c.maxGeometryUniformComponents = 1024;
  92. c.maxGeometryVaryingComponents = 64;
  93. c.maxTessControlInputComponents = 128;
  94. c.maxTessControlOutputComponents = 128;
  95. c.maxTessControlTextureImageUnits = 16;
  96. c.maxTessControlUniformComponents = 1024;
  97. c.maxTessControlTotalOutputComponents = 4096;
  98. c.maxTessEvaluationInputComponents = 128;
  99. c.maxTessEvaluationOutputComponents = 128;
  100. c.maxTessEvaluationTextureImageUnits = 16;
  101. c.maxTessEvaluationUniformComponents = 1024;
  102. c.maxTessPatchComponents = 120;
  103. c.maxPatchVertices = 32;
  104. c.maxTessGenLevel = 64;
  105. c.maxViewports = 16;
  106. c.maxVertexAtomicCounters = 0;
  107. c.maxTessControlAtomicCounters = 0;
  108. c.maxTessEvaluationAtomicCounters = 0;
  109. c.maxGeometryAtomicCounters = 0;
  110. c.maxFragmentAtomicCounters = 8;
  111. c.maxCombinedAtomicCounters = 8;
  112. c.maxAtomicCounterBindings = 1;
  113. c.maxVertexAtomicCounterBuffers = 0;
  114. c.maxTessControlAtomicCounterBuffers = 0;
  115. c.maxTessEvaluationAtomicCounterBuffers = 0;
  116. c.maxGeometryAtomicCounterBuffers = 0;
  117. c.maxFragmentAtomicCounterBuffers = 1;
  118. c.maxCombinedAtomicCounterBuffers = 1;
  119. c.maxAtomicCounterBufferSize = 16384;
  120. c.maxTransformFeedbackBuffers = 4;
  121. c.maxTransformFeedbackInterleavedComponents = 64;
  122. c.maxCullDistances = 8;
  123. c.maxCombinedClipAndCullDistances = 8;
  124. c.maxSamples = 4;
  125. c.limits.nonInductiveForLoops = 1;
  126. c.limits.whileLoops = 1;
  127. c.limits.doWhileLoops = 1;
  128. c.limits.generalUniformIndexing = 1;
  129. c.limits.generalAttributeMatrixVectorIndexing = 1;
  130. c.limits.generalVaryingIndexing = 1;
  131. c.limits.generalSamplerIndexing = 1;
  132. c.limits.generalVariableIndexing = 1;
  133. c.limits.generalConstantMatrixVectorIndexing = 1;
  134. return c;
  135. }
  136. static TBuiltInResource GLSLANG_LIMITS = setGlslangLimits();
  137. static EShLanguage ankiToGlslangShaderType(ShaderType shaderType)
  138. {
  139. EShLanguage gslangShader;
  140. switch(shaderType)
  141. {
  142. case ShaderType::VERTEX:
  143. gslangShader = EShLangVertex;
  144. break;
  145. case ShaderType::FRAGMENT:
  146. gslangShader = EShLangFragment;
  147. break;
  148. case ShaderType::TESSELLATION_EVALUATION:
  149. gslangShader = EShLangTessEvaluation;
  150. break;
  151. case ShaderType::TESSELLATION_CONTROL:
  152. gslangShader = EShLangTessControl;
  153. break;
  154. case ShaderType::GEOMETRY:
  155. gslangShader = EShLangGeometry;
  156. break;
  157. case ShaderType::COMPUTE:
  158. gslangShader = EShLangCompute;
  159. break;
  160. case ShaderType::RAY_GEN:
  161. gslangShader = EShLangRayGen;
  162. break;
  163. case ShaderType::ANY_HIT:
  164. gslangShader = EShLangAnyHit;
  165. break;
  166. case ShaderType::CLOSEST_HIT:
  167. gslangShader = EShLangClosestHit;
  168. break;
  169. case ShaderType::MISS:
  170. gslangShader = EShLangMiss;
  171. break;
  172. case ShaderType::INTERSECTION:
  173. gslangShader = EShLangIntersect;
  174. break;
  175. case ShaderType::CALLABLE:
  176. gslangShader = EShLangCallable;
  177. break;
  178. default:
  179. ANKI_ASSERT(0);
  180. gslangShader = EShLangCount;
  181. };
  182. return gslangShader;
  183. }
  184. /// Parse Glslang's error message for the line of the error.
  185. static Error parseErrorLine(CString error, GenericMemoryPoolAllocator<U8> alloc, U32& lineNumber)
  186. {
  187. lineNumber = MAX_U32;
  188. StringListAuto lines(alloc);
  189. lines.splitString(error, '\n');
  190. for(String& line : lines)
  191. {
  192. if(line.find("ERROR: ") == 0)
  193. {
  194. StringListAuto tokens(alloc);
  195. tokens.splitString(line, ':');
  196. if(tokens.getSize() < 3 || (tokens.getBegin() + 2)->toNumber(lineNumber) != Error::NONE)
  197. {
  198. ANKI_SHADER_COMPILER_LOGE("Failed to parse the GLSlang error message: %s", error.cstr());
  199. return Error::FUNCTION_FAILED;
  200. }
  201. else
  202. {
  203. break;
  204. }
  205. }
  206. }
  207. return Error::NONE;
  208. }
  209. static void createErrorLog(CString glslangError, CString source, GenericMemoryPoolAllocator<U8> alloc,
  210. StringAuto& outError)
  211. {
  212. U32 errorLineNumberu = 0;
  213. const Error err = parseErrorLine(glslangError, alloc, errorLineNumberu);
  214. const I32 errorLineNumber = (!err) ? I32(errorLineNumberu) : -1;
  215. constexpr I32 lineCountAroundError = 4;
  216. StringAuto prettySrc(alloc);
  217. StringListAuto lines(alloc);
  218. lines.splitString(source, '\n', true);
  219. I32 lineno = 0;
  220. for(auto it = lines.getBegin(); it != lines.getEnd(); ++it)
  221. {
  222. ++lineno;
  223. if(lineno >= errorLineNumber - lineCountAroundError && lineno <= errorLineNumber + lineCountAroundError)
  224. {
  225. prettySrc.append(StringAuto(alloc).sprintf("%s%s\n", (lineno == errorLineNumber) ? ">> " : " ",
  226. (it->isEmpty()) ? " " : (*it).cstr()));
  227. }
  228. }
  229. outError.sprintf("%sIn:\n%s\n", glslangError.cstr(), prettySrc.cstr());
  230. }
  231. Error preprocessGlsl(CString in, StringAuto& out)
  232. {
  233. glslang::TShader shader(EShLangVertex);
  234. Array<const char*, 1> csrc = {&in[0]};
  235. shader.setStrings(&csrc[0], 1);
  236. DirStackFileIncluder includer;
  237. EShMessages messages = EShMsgDefault;
  238. std::string glslangOut;
  239. if(!shader.preprocess(&GLSLANG_LIMITS, 450, ENoProfile, false, false, messages, &glslangOut, includer))
  240. {
  241. ANKI_SHADER_COMPILER_LOGE("Preprocessing failed:\n%s", shader.getInfoLog());
  242. return Error::USER_DATA;
  243. }
  244. out.append(glslangOut.c_str());
  245. return Error::NONE;
  246. }
  247. Error compilerGlslToSpirv(CString src, ShaderType shaderType, GenericMemoryPoolAllocator<U8> tmpAlloc,
  248. DynamicArrayAuto<U8>& spirv, StringAuto& errorMessage)
  249. {
  250. #if ANKI_GLSLANG_DUMP
  251. // Dump it
  252. const U32 dumpFileCount = g_dumpFileCount.fetchAdd(1);
  253. {
  254. File file;
  255. StringAuto tmpDir(tmpAlloc);
  256. ANKI_CHECK(getTempDirectory(tmpDir));
  257. StringAuto fname(tmpAlloc);
  258. fname.sprintf("%s/%u.glsl", tmpDir.cstr(), dumpFileCount);
  259. ANKI_SHADER_COMPILER_LOGW("GLSL dumping is enabled: %s", fname.cstr());
  260. ANKI_CHECK(file.open(fname, FileOpenFlag::WRITE));
  261. ANKI_CHECK(file.writeText("%s", src.cstr()));
  262. }
  263. #endif
  264. const EShLanguage stage = ankiToGlslangShaderType(shaderType);
  265. const EShMessages messages = EShMessages(EShMsgSpvRules | EShMsgVulkanRules);
  266. glslang::TShader shader(stage);
  267. Array<const char*, 1> csrc = {&src[0]};
  268. shader.setStrings(&csrc[0], 1);
  269. shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_1);
  270. shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_4);
  271. if(!shader.parse(&GLSLANG_LIMITS, 100, false, messages))
  272. {
  273. createErrorLog(shader.getInfoLog(), src, tmpAlloc, errorMessage);
  274. return Error::USER_DATA;
  275. }
  276. // Setup the program
  277. glslang::TProgram program;
  278. program.addShader(&shader);
  279. if(!program.link(messages))
  280. {
  281. errorMessage.create("glslang failed to link a shader");
  282. return Error::USER_DATA;
  283. }
  284. // Gen SPIRV
  285. glslang::SpvOptions spvOptions;
  286. spvOptions.optimizeSize = true;
  287. spvOptions.disableOptimizer = false;
  288. std::vector<unsigned int> glslangSpirv;
  289. glslang::GlslangToSpv(*program.getIntermediate(stage), glslangSpirv, &spvOptions);
  290. // Store
  291. spirv.resize(U32(glslangSpirv.size() * sizeof(unsigned int)));
  292. memcpy(&spirv[0], &glslangSpirv[0], spirv.getSizeInBytes());
  293. #if ANKI_GLSLANG_DUMP
  294. // Dump it
  295. {
  296. File file;
  297. StringAuto tmpDir(tmpAlloc);
  298. ANKI_CHECK(getTempDirectory(tmpDir));
  299. StringAuto fname(tmpAlloc);
  300. fname.sprintf("%s/%u.spv", tmpDir.cstr(), dumpFileCount);
  301. ANKI_SHADER_COMPILER_LOGW("GLSL dumping is enabled: %s", fname.cstr());
  302. ANKI_CHECK(file.open(fname, FileOpenFlag::WRITE | FileOpenFlag::BINARY));
  303. ANKI_CHECK(file.write(spirv.getBegin(), spirv.getSizeInBytes()));
  304. }
  305. #endif
  306. return Error::NONE;
  307. }
  308. } // end namespace anki