GLSLContext.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. #include "GLSLContext.h"
  2. #include <algorithm>
  3. #include <cassert>
  4. #include <cstdlib>
  5. #include <filesystem>
  6. #include <fstream>
  7. #include <iomanip>
  8. #include <memory>
  9. #include <mutex>
  10. #include <regex>
  11. #include <set>
  12. #include <sstream>
  13. #include <stdexcept>
  14. #include <unordered_map>
  15. #include "SPIRV/GlslangToSpv.h"
  16. #include "glslang/Include/ResourceLimits.h"
  17. #include "glslang/Public/ShaderLang.h"
  18. #include "spirv_glsl.hpp"
  19. using namespace WallpaperEngine::Render::Shaders;
  20. TBuiltInResource BuiltInResource = { .maxLights = 32,
  21. .maxClipPlanes = 6,
  22. .maxTextureUnits = 32,
  23. .maxTextureCoords = 32,
  24. .maxVertexAttribs = 64,
  25. .maxVertexUniformComponents = 4096,
  26. .maxVaryingFloats = 64,
  27. .maxVertexTextureImageUnits = 32,
  28. .maxCombinedTextureImageUnits = 80,
  29. .maxTextureImageUnits = 32,
  30. .maxFragmentUniformComponents = 4096,
  31. .maxDrawBuffers = 32,
  32. .maxVertexUniformVectors = 128,
  33. .maxVaryingVectors = 8,
  34. .maxFragmentUniformVectors = 16,
  35. .maxVertexOutputVectors = 16,
  36. .maxFragmentInputVectors = 15,
  37. .minProgramTexelOffset = -8,
  38. .maxProgramTexelOffset = 7,
  39. .maxClipDistances = 8,
  40. .maxComputeWorkGroupCountX = 65535,
  41. .maxComputeWorkGroupCountY = 65535,
  42. .maxComputeWorkGroupCountZ = 65535,
  43. .maxComputeWorkGroupSizeX = 1024,
  44. .maxComputeWorkGroupSizeY = 1024,
  45. .maxComputeWorkGroupSizeZ = 64,
  46. .maxComputeUniformComponents = 1024,
  47. .maxComputeTextureImageUnits = 16,
  48. .maxComputeImageUniforms = 8,
  49. .maxComputeAtomicCounters = 8,
  50. .maxComputeAtomicCounterBuffers = 1,
  51. .maxVaryingComponents = 60,
  52. .maxVertexOutputComponents = 64,
  53. .maxGeometryInputComponents = 64,
  54. .maxGeometryOutputComponents = 128,
  55. .maxFragmentInputComponents = 128,
  56. .maxImageUnits = 8,
  57. .maxCombinedImageUnitsAndFragmentOutputs = 8,
  58. .maxCombinedShaderOutputResources = 8,
  59. .maxImageSamples = 0,
  60. .maxVertexImageUniforms = 0,
  61. .maxTessControlImageUniforms = 0,
  62. .maxTessEvaluationImageUniforms = 0,
  63. .maxGeometryImageUniforms = 0,
  64. .maxFragmentImageUniforms = 8,
  65. .maxCombinedImageUniforms = 8,
  66. .maxGeometryTextureImageUnits = 16,
  67. .maxGeometryOutputVertices = 256,
  68. .maxGeometryTotalOutputComponents = 1024,
  69. .maxGeometryUniformComponents = 1024,
  70. .maxGeometryVaryingComponents = 64,
  71. .maxTessControlInputComponents = 128,
  72. .maxTessControlOutputComponents = 128,
  73. .maxTessControlTextureImageUnits = 16,
  74. .maxTessControlUniformComponents = 1024,
  75. .maxTessControlTotalOutputComponents = 4096,
  76. .maxTessEvaluationInputComponents = 128,
  77. .maxTessEvaluationOutputComponents = 128,
  78. .maxTessEvaluationTextureImageUnits = 16,
  79. .maxTessEvaluationUniformComponents = 1024,
  80. .maxTessPatchComponents = 120,
  81. .maxPatchVertices = 32,
  82. .maxTessGenLevel = 64,
  83. .maxViewports = 16,
  84. .maxVertexAtomicCounters = 0,
  85. .maxTessControlAtomicCounters = 0,
  86. .maxTessEvaluationAtomicCounters = 0,
  87. .maxGeometryAtomicCounters = 0,
  88. .maxFragmentAtomicCounters = 8,
  89. .maxCombinedAtomicCounters = 8,
  90. .maxAtomicCounterBindings = 1,
  91. .maxVertexAtomicCounterBuffers = 0,
  92. .maxTessControlAtomicCounterBuffers = 0,
  93. .maxTessEvaluationAtomicCounterBuffers = 0,
  94. .maxGeometryAtomicCounterBuffers = 0,
  95. .maxFragmentAtomicCounterBuffers = 1,
  96. .maxCombinedAtomicCounterBuffers = 1,
  97. .maxAtomicCounterBufferSize = 16384,
  98. .maxTransformFeedbackBuffers = 4,
  99. .maxTransformFeedbackInterleavedComponents = 64,
  100. .maxCullDistances = 8,
  101. .maxCombinedClipAndCullDistances = 8,
  102. .maxSamples = 4,
  103. .maxMeshOutputVerticesNV = 256,
  104. .maxMeshOutputPrimitivesNV = 512,
  105. .maxMeshWorkGroupSizeX_NV = 32,
  106. .maxMeshWorkGroupSizeY_NV = 1,
  107. .maxMeshWorkGroupSizeZ_NV = 1,
  108. .maxTaskWorkGroupSizeX_NV = 32,
  109. .maxTaskWorkGroupSizeY_NV = 1,
  110. .maxTaskWorkGroupSizeZ_NV = 1,
  111. .maxMeshViewCountNV = 4,
  112. .limits = {
  113. .nonInductiveForLoops = true,
  114. .whileLoops = true,
  115. .doWhileLoops = true,
  116. .generalUniformIndexing = true,
  117. .generalAttributeMatrixVectorIndexing = true,
  118. .generalVaryingIndexing = true,
  119. .generalSamplerIndexing = true,
  120. .generalVariableIndexing = true,
  121. .generalConstantMatrixVectorIndexing = true,
  122. } };
  123. GLSLContext::GLSLContext () {
  124. assert (this->sInstance == nullptr);
  125. glslang::InitializeProcess ();
  126. }
  127. GLSLContext::~GLSLContext () { glslang::FinalizeProcess (); }
  128. GLSLContext& GLSLContext::get () {
  129. if (sInstance == nullptr) {
  130. sInstance = std::make_unique<GLSLContext> ();
  131. }
  132. return *sInstance;
  133. }
  134. namespace {
  135. // glslang's log says "ERROR: 0:367: ..." against the exact string it was given, show those lines with some context
  136. std::string describeFailure (
  137. const std::string& name, const std::string& stage, const std::string& source, const std::string& log
  138. ) {
  139. static const std::regex errorLine (R"((?:ERROR|WARNING): \d+:(\d+):)");
  140. constexpr int context = 4;
  141. std::vector<std::string> lines;
  142. std::istringstream input (source);
  143. for (std::string line; std::getline (input, line);) {
  144. lines.push_back (line);
  145. }
  146. std::set<int> errors;
  147. for (auto it = std::sregex_iterator (log.cbegin (), log.cend (), errorLine); it != std::sregex_iterator (); ++it) {
  148. errors.insert (std::stoi ((*it)[1].str ()));
  149. }
  150. std::ostringstream out;
  151. out << "GLSL " << stage << " unit of " << (name.empty () ? "<unnamed shader>" : name) << " failed to parse:\n"
  152. << log;
  153. int shownUpTo = 0;
  154. for (const int error : errors) {
  155. const int from = std::max ({ 1, error - context, shownUpTo + 1 });
  156. const int to = std::min (static_cast<int> (lines.size ()), error + context);
  157. if (from > to) {
  158. continue;
  159. }
  160. if (from > shownUpTo + 1) {
  161. out << " ...\n";
  162. }
  163. for (int number = from; number <= to; number++) {
  164. out << (number == error ? ">" : " ") << std::setw (5) << number << ": " << lines[number - 1] << '\n';
  165. }
  166. shownUpTo = to;
  167. }
  168. // the whole thing is ~500 lines with includes, too much for the log every time
  169. if (const char* dir = std::getenv ("LWE_SHADER_DUMP_DIR"); dir != nullptr && *dir != '\0') {
  170. std::string file = name.empty () ? "shader" : name;
  171. std::replace (file.begin (), file.end (), '/', '_');
  172. const auto path = std::filesystem::path (dir) / (file + "." + stage + ".glsl");
  173. std::error_code ec;
  174. std::filesystem::create_directories (dir, ec);
  175. std::ofstream (path) << source;
  176. out << "full source written to " << path.string () << '\n';
  177. }
  178. return out.str ();
  179. }
  180. } // namespace
  181. std::pair<std::string, std::string> GLSLContext::toGlsl (
  182. const std::string& vertex, const std::string& fragment, const std::string& name
  183. ) {
  184. // pure function of the two sources, and passes get rebuilt often
  185. static std::mutex cacheMutex;
  186. static std::unordered_map<std::string, std::pair<std::string, std::string>> cache;
  187. std::string cacheKey = vertex;
  188. cacheKey += '\0';
  189. cacheKey += fragment;
  190. {
  191. std::lock_guard lock (cacheMutex);
  192. if (const auto cached = cache.find (cacheKey); cached != cache.end ()) {
  193. return cached->second;
  194. }
  195. }
  196. glslang::TShader vertexShader (EShLangVertex);
  197. const char* vertexSource = vertex.c_str ();
  198. vertexShader.setStrings (&vertexSource, 1);
  199. vertexShader.setEntryPoint ("main");
  200. vertexShader.setEnvInput (glslang::EShSourceGlsl, EShLangVertex, glslang::EShClientOpenGL, 330);
  201. vertexShader.setEnvClient (glslang::EShClientOpenGL, glslang::EShTargetOpenGL_450);
  202. vertexShader.setEnvTarget (glslang::EShTargetSpv, glslang::EShTargetSpv_1_5);
  203. vertexShader.setAutoMapLocations (true);
  204. vertexShader.setAutoMapBindings (true);
  205. if (!vertexShader.parse (&BuiltInResource, 100, false, EShMsgDefault)) {
  206. throw std::runtime_error (describeFailure (name, "vertex", vertex, vertexShader.getInfoLog ()));
  207. }
  208. glslang::TShader fragmentShader (EShLangFragment);
  209. const char* fragmentSource = fragment.c_str ();
  210. fragmentShader.setStrings (&fragmentSource, 1);
  211. fragmentShader.setEntryPoint ("main");
  212. fragmentShader.setEnvInput (glslang::EShSourceGlsl, EShLangFragment, glslang::EShClientOpenGL, 330);
  213. fragmentShader.setEnvClient (glslang::EShClientOpenGL, glslang::EShTargetOpenGL_450);
  214. fragmentShader.setEnvTarget (glslang::EShTargetSpv, glslang::EShTargetSpv_1_5);
  215. fragmentShader.setAutoMapLocations (true);
  216. fragmentShader.setAutoMapBindings (true);
  217. if (!fragmentShader.parse (&BuiltInResource, 100, false, EShMsgDefault)) {
  218. throw std::runtime_error (describeFailure (name, "fragment", fragment, fragmentShader.getInfoLog ()));
  219. }
  220. glslang::TProgram program;
  221. program.addShader (&vertexShader);
  222. program.addShader (&fragmentShader);
  223. if (!program.link (EShMsgDefault)) {
  224. throw std::runtime_error (
  225. "GLSL program " + (name.empty () ? std::string ("<unnamed shader>") : name) + " failed to link: "
  226. + program.getInfoLog ()
  227. );
  228. }
  229. std::vector<uint32_t> spirv;
  230. glslang::GlslangToSpv (*program.getIntermediate (EShLangVertex), spirv);
  231. spirv_cross::CompilerGLSL vertexCompiler (spirv);
  232. spirv_cross::CompilerGLSL::Options options;
  233. options.version = 330;
  234. options.es = false;
  235. options.force_zero_initialized_variables = true;
  236. vertexCompiler.set_common_options (options);
  237. spirv.clear ();
  238. glslang::GlslangToSpv (*program.getIntermediate (EShLangFragment), spirv);
  239. spirv_cross::CompilerGLSL fragmentCompiler (spirv);
  240. options.version = 330;
  241. options.es = false;
  242. options.force_zero_initialized_variables = true;
  243. fragmentCompiler.set_common_options (options);
  244. std::pair<std::string, std::string> result = { vertexCompiler.compile () + "#if 0\n" + vertex + "\n#endif",
  245. fragmentCompiler.compile () + "#if 0\n" + fragment + "\n#endif" };
  246. {
  247. std::lock_guard lock (cacheMutex);
  248. cache.emplace (std::move (cacheKey), result);
  249. }
  250. return result;
  251. }
  252. std::unique_ptr<GLSLContext> GLSLContext::sInstance = nullptr;