Browse Source

use glslang to compile vulkan ShaderStage

Before this commit a system call to glslc was made to compile the shaders dynamically during runtime.
That was obviously a very hacky way, so now the proper way was implemented.
niki 3 years ago
parent
commit
b9496f6d43
1 changed files with 155 additions and 36 deletions
  1. 155 36
      src/modules/graphics/vulkan/ShaderStage.cpp

+ 155 - 36
src/modules/graphics/vulkan/ShaderStage.cpp

@@ -2,6 +2,9 @@
 
 #include "Graphics.h"
 
+#include "libraries/glslang/glslang/Public/ShaderLang.h"
+#include "libraries/glslang/SPIRV/GlslangToSpv.h"
+
 #include <iostream>
 #include <fstream>
 
@@ -11,68 +14,184 @@
 namespace love {
 	namespace graphics {
 		namespace vulkan {
-			static int someIndex = 0;
+			static const TBuiltInResource defaultTBuiltInResource = {
+				/* .MaxLights = */ 32,
+				/* .MaxClipPlanes = */ 6,
+				/* .MaxTextureUnits = */ 32,
+				/* .MaxTextureCoords = */ 32,
+				/* .MaxVertexAttribs = */ 64,
+				/* .MaxVertexUniformComponents = */ 16384,
+				/* .MaxVaryingFloats = */ 128,
+				/* .MaxVertexTextureImageUnits = */ 32,
+				/* .MaxCombinedTextureImageUnits = */ 80,
+				/* .MaxTextureImageUnits = */ 32,
+				/* .MaxFragmentUniformComponents = */ 16384,
+				/* .MaxDrawBuffers = */ 8,
+				/* .MaxVertexUniformVectors = */ 4096,
+				/* .MaxVaryingVectors = */ 32,
+				/* .MaxFragmentUniformVectors = */ 4096,
+				/* .MaxVertexOutputVectors = */ 32,
+				/* .MaxFragmentInputVectors = */ 31,
+				/* .MinProgramTexelOffset = */ -8,
+				/* .MaxProgramTexelOffset = */ 7,
+				/* .MaxClipDistances = */ 8,
+				/* .MaxComputeWorkGroupCountX = */ 65535,
+				/* .MaxComputeWorkGroupCountY = */ 65535,
+				/* .MaxComputeWorkGroupCountZ = */ 65535,
+				/* .MaxComputeWorkGroupSizeX = */ 1024,
+				/* .MaxComputeWorkGroupSizeY = */ 1024,
+				/* .MaxComputeWorkGroupSizeZ = */ 64,
+				/* .MaxComputeUniformComponents = */ 1024,
+				/* .MaxComputeTextureImageUnits = */ 32,
+				/* .MaxComputeImageUniforms = */ 16,
+				/* .MaxComputeAtomicCounters = */ 4096,
+				/* .MaxComputeAtomicCounterBuffers = */ 8,
+				/* .MaxVaryingComponents = */ 128,
+				/* .MaxVertexOutputComponents = */ 128,
+				/* .MaxGeometryInputComponents = */ 128,
+				/* .MaxGeometryOutputComponents = */ 128,
+				/* .MaxFragmentInputComponents = */ 128,
+				/* .MaxImageUnits = */ 192,
+				/* .MaxCombinedImageUnitsAndFragmentOutputs = */ 144,
+				/* .MaxCombinedShaderOutputResources = */ 144,
+				/* .MaxImageSamples = */ 32,
+				/* .MaxVertexImageUniforms = */ 16,
+				/* .MaxTessControlImageUniforms = */ 16,
+				/* .MaxTessEvaluationImageUniforms = */ 16,
+				/* .MaxGeometryImageUniforms = */ 16,
+				/* .MaxFragmentImageUniforms = */ 16,
+				/* .MaxCombinedImageUniforms = */ 80,
+				/* .MaxGeometryTextureImageUnits = */ 16,
+				/* .MaxGeometryOutputVertices = */ 256,
+				/* .MaxGeometryTotalOutputComponents = */ 1024,
+				/* .MaxGeometryUniformComponents = */ 1024,
+				/* .MaxGeometryVaryingComponents = */ 64,
+				/* .MaxTessControlInputComponents = */ 128,
+				/* .MaxTessControlOutputComponents = */ 128,
+				/* .MaxTessControlTextureImageUnits = */ 16,
+				/* .MaxTessControlUniformComponents = */ 1024,
+				/* .MaxTessControlTotalOutputComponents = */ 4096,
+				/* .MaxTessEvaluationInputComponents = */ 128,
+				/* .MaxTessEvaluationOutputComponents = */ 128,
+				/* .MaxTessEvaluationTextureImageUnits = */ 16,
+				/* .MaxTessEvaluationUniformComponents = */ 1024,
+				/* .MaxTessPatchComponents = */ 120,
+				/* .MaxPatchVertices = */ 32,
+				/* .MaxTessGenLevel = */ 64,
+				/* .MaxViewports = */ 16,
+				/* .MaxVertexAtomicCounters = */ 4096,
+				/* .MaxTessControlAtomicCounters = */ 4096,
+				/* .MaxTessEvaluationAtomicCounters = */ 4096,
+				/* .MaxGeometryAtomicCounters = */ 4096,
+				/* .MaxFragmentAtomicCounters = */ 4096,
+				/* .MaxCombinedAtomicCounters = */ 4096,
+				/* .MaxAtomicCounterBindings = */ 8,
+				/* .MaxVertexAtomicCounterBuffers = */ 8,
+				/* .MaxTessControlAtomicCounterBuffers = */ 8,
+				/* .MaxTessEvaluationAtomicCounterBuffers = */ 8,
+				/* .MaxGeometryAtomicCounterBuffers = */ 8,
+				/* .MaxFragmentAtomicCounterBuffers = */ 8,
+				/* .MaxCombinedAtomicCounterBuffers = */ 8,
+				/* .MaxAtomicCounterBufferSize = */ 16384,
+				/* .MaxTransformFeedbackBuffers = */ 4,
+				/* .MaxTransformFeedbackInterleavedComponents = */ 64,
+				/* .MaxCullDistances = */ 8,
+				/* .MaxCombinedClipAndCullDistances = */ 8,
+				/* .MaxSamples = */ 32,
+				/* .maxMeshOutputVerticesNV = */ 256,
+				/* .maxMeshOutputPrimitivesNV = */ 512,
+				/* .maxMeshWorkGroupSizeX_NV = */ 32,
+				/* .maxMeshWorkGroupSizeY_NV = */ 1,
+				/* .maxMeshWorkGroupSizeZ_NV = */ 1,
+				/* .maxTaskWorkGroupSizeX_NV = */ 32,
+				/* .maxTaskWorkGroupSizeY_NV = */ 1,
+				/* .maxTaskWorkGroupSizeZ_NV = */ 1,
+				/* .maxMeshViewCountNV = */ 4,
+				/* .maxDualSourceDrawBuffersEXT = */ 1,
+				/* .limits = */ {
+					/* .nonInductiveForLoops = */ 1,
+					/* .whileLoops = */ 1,
+					/* .doWhileLoops = */ 1,
+					/* .generalUniformIndexing = */ 1,
+					/* .generalAttributeMatrixVectorIndexing = */ 1,
+					/* .generalVaryingIndexing = */ 1,
+					/* .generalSamplerIndexing = */ 1,
+					/* .generalVariableIndexing = */ 1,
+					/* .generalConstantMatrixVectorIndexing = */ 1,
+				}
+			};
 
-			static std::string getFileEnding(ShaderStageType type) {
-				switch (type) {
+			static EShLanguage getGlslShaderType(ShaderStageType stage) {
+				switch (stage) {
 				case SHADERSTAGE_VERTEX:
-					return ".vert";
+					return EShLangVertex;
 				case SHADERSTAGE_PIXEL:
-					return ".frag";
+					return EShLangFragment;
+				case SHADERSTAGE_COMPUTE:
+					return EShLangCompute;
 				default:
-					throw love::Exception("unsupported shader stage type");
+					throw love::Exception("unkonwn shader stage type");
 				}
 			}
 
-			static std::vector<char> readFile(const std::string& filename) {
-				std::ifstream file(filename, std::ios::ate | std::ios::binary);
+			static std::vector<uint32_t> compileShaderWithGlslang(const std::string& glsl, ShaderStageType stage) {
+				using namespace glslang;
 
-				if (!file.is_open()) {
-					throw std::runtime_error("failed to open file!");
-				}
+				auto glslangShaderStage = getGlslShaderType(stage);
+				auto tshader = new TShader(glslangShaderStage);
 
-				size_t fileSize = (size_t)file.tellg();
-				std::vector<char> buffer(fileSize);
+				tshader->setEnvInput(EShSourceGlsl, glslangShaderStage, EShClientVulkan, 450);
+				tshader->setEnvClient(EShClientVulkan, EShTargetVulkan_1_2);
+				tshader->setEnvTarget(EshTargetSpv, EShTargetSpv_1_5);
+				tshader->setAutoMapLocations(true);
+				tshader->setAutoMapBindings(true);
 
-				file.seekg(0);
-				file.read(buffer.data(), fileSize);
+				/*
+				tshader->getEnvInputVulkanRulesRelaxed();
+				tshader->setGlobalUniformBinding(0);
+				tshader->setGlobalUniformSet(0);
+				*/
 
-				file.close();
+				const char* csrc = glsl.c_str();
+				const int sourceLength = glsl.length();
+				tshader->setStringsWithLengths(&csrc, &sourceLength, 1);
 
-				return buffer;
-			}
+				int defaultVersio = 450;
+				EProfile defaultProfile = ECoreProfile;
+				bool forceDefault = false;
+				bool forwardCompat = true;
 
-			static int shaderSourceId = 0;
+				if (!tshader->parse(&defaultTBuiltInResource, defaultVersio, defaultProfile, forceDefault, forwardCompat, EShMsgSuppressWarnings)) {
+					std::cout << "error while parsing shader" << std::endl;
 
-			std::vector<char> compileShader(const std::string& glsl, ShaderStageType stage) {
-				// fixme: use glslang or shaderc for this
-
-				std::string inputFileName = std::string("temp") + std::to_string(shaderSourceId++) + getFileEnding(stage);
-				std::string outputFileName = std::string("temp.spv");
+					throw love::Exception("error while parsing shader");
+				}
 
-				std::ofstream out(inputFileName);
-				out << glsl;
-				out.close();
+				auto intermediate = tshader->getIntermediate();
 
-				std::string command = std::string("glslc -fauto-bind-uniforms ") + inputFileName + " -o " + outputFileName;
-				system(command.c_str());
+				if (intermediate == nullptr) {
+					throw love::Exception("error when getting the intermediate");
+				}
+				spv::SpvBuildLogger logger;
+				SpvOptions opt;
+				opt.validate = true;
 
-				auto result = readFile(outputFileName);
+				std::vector<uint32_t> spirv;
+				GlslangToSpv(*intermediate, spirv, &logger, &opt);
 
-				std::remove(inputFileName.c_str());
-				std::remove(outputFileName.c_str());
+				delete tshader;
 
-				return result;
+				return spirv;
 			}
 
 			ShaderStage::ShaderStage(love::graphics::Graphics* gfx, ShaderStageType stage, const std::string& glsl, bool gles, const std::string& cachekey)
 				: love::graphics::ShaderStage(gfx, stage, glsl, gles, cachekey) {
-				auto code = compileShader(glsl, stage);
+				auto code = compileShaderWithGlslang(glsl, stage);
 
 				VkShaderModuleCreateInfo createInfo{};
 				createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
-				createInfo.codeSize = code.size();
-				createInfo.pCode = reinterpret_cast<uint32_t*>(code.data());
+				createInfo.codeSize = code.size() * sizeof(uint32_t);
+				createInfo.pCode = code.data();
 
 				Graphics* vkGfx = (Graphics*)gfx;
 				device = vkGfx->getDevice();