/* * GLSLToSPIRVConverter.cpp * * Copyright (c) 2014-2018 The Brenwill Workshop Ltd. (http://www.brenwill.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "GLSLToSPIRVConverter.h" #include "MVKCommonEnvironment.h" #include "SPIRVToMSLConverter.h" #include "MVKStrings.h" #include "GlslangToSpv.h" #include "../glslang/SPIRV/disassemble.h" #include "doc.h" #include using namespace std; using namespace mvk; #pragma mark - #pragma mark GLSLToSPIRVConverter /** Configures the specified limit resources structure used by the GLSL compiler. */ void configureGLSLCompilerResources(TBuiltInResource* glslCompilerResources); /** Returns the GLSL compiler language type corresponding to the specified MoltenVK shader stage. */ EShLanguage eshLanguageFromMVKShaderStage(const MVKShaderStage mvkShaderStage); MVK_PUBLIC_SYMBOL void GLSLToSPIRVConverter::setGLSL(const string& glslSrc) { _glsl = glslSrc; } MVK_PUBLIC_SYMBOL const string& GLSLToSPIRVConverter::getGLSL() { return _glsl; } MVK_PUBLIC_SYMBOL bool GLSLToSPIRVConverter::convert(MVKShaderStage shaderStage, bool shouldLogGLSL, bool shouldLogSPIRV) { _wasConverted = true; _resultLog.clear(); _spirv.clear(); if (shouldLogGLSL) { logGLSL("Converting"); } EShMessages messages = (EShMessages)(EShMsgDefault | EShMsgSpvRules | EShMsgVulkanRules); EShLanguage stage = eshLanguageFromMVKShaderStage(shaderStage); TBuiltInResource glslCompilerResources; configureGLSLCompilerResources(&glslCompilerResources); const char *glslStrings[1]; glslStrings[0] = _glsl.data(); // Create and compile a shader from the source code glslang::TShader glslShader(stage); glslShader.setStrings(glslStrings, 1); if (glslShader.parse(&glslCompilerResources, 100, false, messages)) { if (shouldLogGLSL) { logMsg(glslShader.getInfoLog()); logMsg(glslShader.getInfoDebugLog()); } } else { logError(glslShader.getInfoLog()); logError(glslShader.getInfoDebugLog()); return logError("Error compiling GLSL when converting GLSL to SPIR-V."); } // Create and link a shader program containing the single shader glslang::TProgram glslProgram; glslProgram.addShader(&glslShader); if ( !glslProgram.link(messages) ) { logError(glslProgram.getInfoLog()); logError(glslProgram.getInfoDebugLog()); return logError("Error creating GLSL program when converting GLSL to SPIR-V."); } // Output the SPIR-V code from the shader program glslang::GlslangToSpv(*glslProgram.getIntermediate(stage), _spirv); if (shouldLogSPIRV) { logSPIRV("Converted"); } return _wasConverted; } MVK_PUBLIC_SYMBOL const vector& GLSLToSPIRVConverter::getSPIRV() { return _spirv; } MVK_PUBLIC_SYMBOL bool GLSLToSPIRVConverter::getWasConverted() { return _wasConverted; } MVK_PUBLIC_SYMBOL const string& GLSLToSPIRVConverter::getResultLog() { return _resultLog; } /** Appends the message text to the result log. */ void GLSLToSPIRVConverter::logMsg(const char* logMsg) { string trimMsg = trim(logMsg); if ( !trimMsg.empty() ) { _resultLog += trimMsg; _resultLog += "\n\n"; } } /** Appends the error text to the result log, sets the wasConverted property to false, and returns it. */ bool GLSLToSPIRVConverter::logError(const char* errMsg) { logMsg(errMsg); _wasConverted = false; return _wasConverted; } /** Appends the SPIR-V to the result log, indicating whether it is being converted or was converted. */ void GLSLToSPIRVConverter::logSPIRV(const char* opDesc) { string spvLog; mvk::logSPIRV(_spirv, spvLog); _resultLog += opDesc; _resultLog += " SPIR-V:\n"; _resultLog += spvLog; _resultLog += "\nEnd SPIR-V\n\n"; } /** Validates that the SPIR-V code will disassemble during logging. */ bool GLSLToSPIRVConverter::validateSPIRV() { if (_spirv.size() < 5) { return false; } if (_spirv[0] != spv::MagicNumber) { return false; } if (_spirv[4] != 0) { return false; } return true; } /** Appends the GLSL to the result log, indicating whether it is being converted or was converted. */ void GLSLToSPIRVConverter::logGLSL(const char* opDesc) { _resultLog += opDesc; _resultLog += " GLSL:\n"; _resultLog += _glsl; _resultLog += "\nEnd GLSL\n\n"; } #pragma mark - #pragma mark Support functions void configureGLSLCompilerResources(TBuiltInResource* glslCompilerResources) { glslCompilerResources->maxLights = 32; glslCompilerResources->maxClipPlanes = 6; glslCompilerResources->maxTextureUnits = 32; glslCompilerResources->maxTextureCoords = 32; glslCompilerResources->maxVertexAttribs = 64; glslCompilerResources->maxVertexUniformComponents = 4096; glslCompilerResources->maxVaryingFloats = 64; glslCompilerResources->maxVertexTextureImageUnits = 32; glslCompilerResources->maxCombinedTextureImageUnits = 80; glslCompilerResources->maxTextureImageUnits = 32; glslCompilerResources->maxFragmentUniformComponents = 4096; glslCompilerResources->maxDrawBuffers = 32; glslCompilerResources->maxVertexUniformVectors = 128; glslCompilerResources->maxVaryingVectors = 8; glslCompilerResources->maxFragmentUniformVectors = 16; glslCompilerResources->maxVertexOutputVectors = 16; glslCompilerResources->maxFragmentInputVectors = 15; glslCompilerResources->minProgramTexelOffset = -8; glslCompilerResources->maxProgramTexelOffset = 7; glslCompilerResources->maxClipDistances = 8; glslCompilerResources->maxComputeWorkGroupCountX = 65535; glslCompilerResources->maxComputeWorkGroupCountY = 65535; glslCompilerResources->maxComputeWorkGroupCountZ = 65535; glslCompilerResources->maxComputeWorkGroupSizeX = 1024; glslCompilerResources->maxComputeWorkGroupSizeY = 1024; glslCompilerResources->maxComputeWorkGroupSizeZ = 64; glslCompilerResources->maxComputeUniformComponents = 1024; glslCompilerResources->maxComputeTextureImageUnits = 16; glslCompilerResources->maxComputeImageUniforms = 8; glslCompilerResources->maxComputeAtomicCounters = 8; glslCompilerResources->maxComputeAtomicCounterBuffers = 1; glslCompilerResources->maxVaryingComponents = 60; glslCompilerResources->maxVertexOutputComponents = 64; glslCompilerResources->maxGeometryInputComponents = 64; glslCompilerResources->maxGeometryOutputComponents = 128; glslCompilerResources->maxFragmentInputComponents = 128; glslCompilerResources->maxImageUnits = 8; glslCompilerResources->maxCombinedImageUnitsAndFragmentOutputs = 8; glslCompilerResources->maxCombinedShaderOutputResources = 8; glslCompilerResources->maxImageSamples = 0; glslCompilerResources->maxVertexImageUniforms = 0; glslCompilerResources->maxTessControlImageUniforms = 0; glslCompilerResources->maxTessEvaluationImageUniforms = 0; glslCompilerResources->maxGeometryImageUniforms = 0; glslCompilerResources->maxFragmentImageUniforms = 8; glslCompilerResources->maxCombinedImageUniforms = 8; glslCompilerResources->maxGeometryTextureImageUnits = 16; glslCompilerResources->maxGeometryOutputVertices = 256; glslCompilerResources->maxGeometryTotalOutputComponents = 1024; glslCompilerResources->maxGeometryUniformComponents = 1024; glslCompilerResources->maxGeometryVaryingComponents = 64; glslCompilerResources->maxTessControlInputComponents = 128; glslCompilerResources->maxTessControlOutputComponents = 128; glslCompilerResources->maxTessControlTextureImageUnits = 16; glslCompilerResources->maxTessControlUniformComponents = 1024; glslCompilerResources->maxTessControlTotalOutputComponents = 4096; glslCompilerResources->maxTessEvaluationInputComponents = 128; glslCompilerResources->maxTessEvaluationOutputComponents = 128; glslCompilerResources->maxTessEvaluationTextureImageUnits = 16; glslCompilerResources->maxTessEvaluationUniformComponents = 1024; glslCompilerResources->maxTessPatchComponents = 120; glslCompilerResources->maxPatchVertices = 32; glslCompilerResources->maxTessGenLevel = 64; glslCompilerResources->maxViewports = 16; glslCompilerResources->maxVertexAtomicCounters = 0; glslCompilerResources->maxTessControlAtomicCounters = 0; glslCompilerResources->maxTessEvaluationAtomicCounters = 0; glslCompilerResources->maxGeometryAtomicCounters = 0; glslCompilerResources->maxFragmentAtomicCounters = 8; glslCompilerResources->maxCombinedAtomicCounters = 8; glslCompilerResources->maxAtomicCounterBindings = 1; glslCompilerResources->maxVertexAtomicCounterBuffers = 0; glslCompilerResources->maxTessControlAtomicCounterBuffers = 0; glslCompilerResources->maxTessEvaluationAtomicCounterBuffers = 0; glslCompilerResources->maxGeometryAtomicCounterBuffers = 0; glslCompilerResources->maxFragmentAtomicCounterBuffers = 1; glslCompilerResources->maxCombinedAtomicCounterBuffers = 1; glslCompilerResources->maxAtomicCounterBufferSize = 16384; glslCompilerResources->maxTransformFeedbackBuffers = 4; glslCompilerResources->maxTransformFeedbackInterleavedComponents = 64; glslCompilerResources->maxCullDistances = 8; glslCompilerResources->maxCombinedClipAndCullDistances = 8; glslCompilerResources->maxSamples = 4; glslCompilerResources->limits.nonInductiveForLoops = 1; glslCompilerResources->limits.whileLoops = 1; glslCompilerResources->limits.doWhileLoops = 1; glslCompilerResources->limits.generalUniformIndexing = 1; glslCompilerResources->limits.generalAttributeMatrixVectorIndexing = 1; glslCompilerResources->limits.generalVaryingIndexing = 1; glslCompilerResources->limits.generalSamplerIndexing = 1; glslCompilerResources->limits.generalVariableIndexing = 1; glslCompilerResources->limits.generalConstantMatrixVectorIndexing = 1; } EShLanguage eshLanguageFromMVKShaderStage(const MVKShaderStage mvkShaderStage) { switch (mvkShaderStage) { case kMVKShaderStageVertex: return EShLangVertex; case kMVKShaderStageTessControl: return EShLangTessControl; case kMVKShaderStageTessEval: return EShLangTessEvaluation; case kMVKShaderStageGeometry: return EShLangGeometry; case kMVKShaderStageFragment: return EShLangFragment; case kMVKShaderStageCompute: return EShLangCompute; default: return EShLangVertex; } } #pragma mark Library initialization /** * Called automatically when the framework is loaded and initialized. * * Initialize the */ static bool _wasShaderConverterInitialized = false; __attribute__((constructor)) static void MVKShaderConverterInit() { if (_wasShaderConverterInitialized ) { return; } glslang::InitializeProcess(); _wasShaderConverterInitialized = true; }