1/*------------------------------------------------------------------------- 2 * OpenGL Conformance Test Suite 3 * ----------------------------- 4 * 5 * Copyright (c) 2017-2019 The Khronos Group Inc. 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 * 19 */ /*! 20 * \file glcSpirvUtils.cpp 21 * \brief Utility functions for using Glslang and Spirv-tools to work with 22 * SPIR-V shaders. 23 */ /*-------------------------------------------------------------------*/ 24 25#include "glcSpirvUtils.hpp" 26#include "deArrayUtil.hpp" 27#include "deSingleton.h" 28#include "deStringUtil.hpp" 29#include "gluContextInfo.hpp" 30#include "tcuTestLog.hpp" 31 32#include "SPIRV/GlslangToSpv.h" 33#include "SPIRV/disassemble.h" 34#include "SPIRV/doc.h" 35#include "glslang/MachineIndependent/localintermediate.h" 36#include "glslang/Public/ShaderLang.h" 37 38#include "spirv-tools/libspirv.hpp" 39#include "spirv-tools/optimizer.hpp" 40 41using namespace glu; 42 43namespace glc 44{ 45 46namespace spirvUtils 47{ 48 49void checkGlSpirvSupported(deqp::Context& m_context) 50{ 51 bool is_at_least_gl_46 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 6))); 52 bool is_arb_gl_spirv = m_context.getContextInfo().isExtensionSupported("GL_ARB_gl_spirv"); 53 54 if ((!is_at_least_gl_46) && (!is_arb_gl_spirv)) 55 TCU_THROW(NotSupportedError, "GL 4.6 or GL_ARB_gl_spirv is not supported"); 56} 57 58EShLanguage getGlslangStage(glu::ShaderType type) 59{ 60 static const EShLanguage stageMap[] = { 61 EShLangVertex, EShLangFragment, EShLangGeometry, EShLangTessControl, EShLangTessEvaluation, EShLangCompute, 62 EShLangRayGen, EShLangAnyHit, EShLangClosestHit, EShLangMiss, EShLangIntersect, EShLangCallable, EShLangTaskNV, 63 EShLangMeshNV 64 }; 65 66 return de::getSizedArrayElement<glu::SHADERTYPE_LAST>(stageMap, type); 67} 68 69static volatile deSingletonState s_glslangInitState = DE_SINGLETON_STATE_NOT_INITIALIZED; 70 71void initGlslang(void*) 72{ 73 // Main compiler 74 glslang::InitializeProcess(); 75 76 // SPIR-V disassembly 77 spv::Parameterize(); 78} 79 80void prepareGlslang(void) 81{ 82 deInitSingleton(&s_glslangInitState, initGlslang, DE_NULL); 83} 84 85void getDefaultLimits(TLimits* limits) 86{ 87 limits->nonInductiveForLoops = true; 88 limits->whileLoops = true; 89 limits->doWhileLoops = true; 90 limits->generalUniformIndexing = true; 91 limits->generalAttributeMatrixVectorIndexing = true; 92 limits->generalVaryingIndexing = true; 93 limits->generalSamplerIndexing = true; 94 limits->generalVariableIndexing = true; 95 limits->generalConstantMatrixVectorIndexing = true; 96} 97 98void getDefaultBuiltInResources(TBuiltInResource* builtin) 99{ 100 getDefaultLimits(&builtin->limits); 101 102 builtin->maxLights = 32; 103 builtin->maxClipPlanes = 6; 104 builtin->maxTextureUnits = 32; 105 builtin->maxTextureCoords = 32; 106 builtin->maxVertexAttribs = 64; 107 builtin->maxVertexUniformComponents = 4096; 108 builtin->maxVaryingFloats = 64; 109 builtin->maxVertexTextureImageUnits = 32; 110 builtin->maxCombinedTextureImageUnits = 80; 111 builtin->maxTextureImageUnits = 32; 112 builtin->maxFragmentUniformComponents = 4096; 113 builtin->maxDrawBuffers = 32; 114 builtin->maxVertexUniformVectors = 128; 115 builtin->maxVaryingVectors = 8; 116 builtin->maxFragmentUniformVectors = 16; 117 builtin->maxVertexOutputVectors = 16; 118 builtin->maxFragmentInputVectors = 15; 119 builtin->minProgramTexelOffset = -8; 120 builtin->maxProgramTexelOffset = 7; 121 builtin->maxClipDistances = 8; 122 builtin->maxComputeWorkGroupCountX = 65535; 123 builtin->maxComputeWorkGroupCountY = 65535; 124 builtin->maxComputeWorkGroupCountZ = 65535; 125 builtin->maxComputeWorkGroupSizeX = 1024; 126 builtin->maxComputeWorkGroupSizeY = 1024; 127 builtin->maxComputeWorkGroupSizeZ = 64; 128 builtin->maxComputeUniformComponents = 1024; 129 builtin->maxComputeTextureImageUnits = 16; 130 builtin->maxComputeImageUniforms = 8; 131 builtin->maxComputeAtomicCounters = 8; 132 builtin->maxComputeAtomicCounterBuffers = 1; 133 builtin->maxVaryingComponents = 60; 134 builtin->maxVertexOutputComponents = 64; 135 builtin->maxGeometryInputComponents = 64; 136 builtin->maxGeometryOutputComponents = 128; 137 builtin->maxFragmentInputComponents = 128; 138 builtin->maxImageUnits = 8; 139 builtin->maxCombinedImageUnitsAndFragmentOutputs = 8; 140 builtin->maxCombinedShaderOutputResources = 8; 141 builtin->maxImageSamples = 0; 142 builtin->maxVertexImageUniforms = 0; 143 builtin->maxTessControlImageUniforms = 0; 144 builtin->maxTessEvaluationImageUniforms = 0; 145 builtin->maxGeometryImageUniforms = 0; 146 builtin->maxFragmentImageUniforms = 8; 147 builtin->maxCombinedImageUniforms = 8; 148 builtin->maxGeometryTextureImageUnits = 16; 149 builtin->maxGeometryOutputVertices = 256; 150 builtin->maxGeometryTotalOutputComponents = 1024; 151 builtin->maxGeometryUniformComponents = 1024; 152 builtin->maxGeometryVaryingComponents = 64; 153 builtin->maxTessControlInputComponents = 128; 154 builtin->maxTessControlOutputComponents = 128; 155 builtin->maxTessControlTextureImageUnits = 16; 156 builtin->maxTessControlUniformComponents = 1024; 157 builtin->maxTessControlTotalOutputComponents = 4096; 158 builtin->maxTessEvaluationInputComponents = 128; 159 builtin->maxTessEvaluationOutputComponents = 128; 160 builtin->maxTessEvaluationTextureImageUnits = 16; 161 builtin->maxTessEvaluationUniformComponents = 1024; 162 builtin->maxTessPatchComponents = 120; 163 builtin->maxPatchVertices = 32; 164 builtin->maxTessGenLevel = 64; 165 builtin->maxViewports = 16; 166 builtin->maxVertexAtomicCounters = 0; 167 builtin->maxTessControlAtomicCounters = 0; 168 builtin->maxTessEvaluationAtomicCounters = 0; 169 builtin->maxGeometryAtomicCounters = 0; 170 builtin->maxFragmentAtomicCounters = 8; 171 builtin->maxCombinedAtomicCounters = 8; 172 builtin->maxAtomicCounterBindings = 1; 173 builtin->maxVertexAtomicCounterBuffers = 0; 174 builtin->maxTessControlAtomicCounterBuffers = 0; 175 builtin->maxTessEvaluationAtomicCounterBuffers = 0; 176 builtin->maxGeometryAtomicCounterBuffers = 0; 177 builtin->maxFragmentAtomicCounterBuffers = 1; 178 builtin->maxCombinedAtomicCounterBuffers = 1; 179 builtin->maxAtomicCounterBufferSize = 16384; 180 builtin->maxTransformFeedbackBuffers = 4; 181 builtin->maxTransformFeedbackInterleavedComponents = 64; 182 builtin->maxCullDistances = 8; 183 builtin->maxCombinedClipAndCullDistances = 8; 184 builtin->maxSamples = 4; 185 builtin->maxMeshOutputVerticesNV = 256; 186 builtin->maxMeshOutputPrimitivesNV = 256; 187 builtin->maxMeshWorkGroupSizeX_NV = 32; 188 builtin->maxMeshWorkGroupSizeY_NV = 1; 189 builtin->maxMeshWorkGroupSizeZ_NV = 1; 190 builtin->maxTaskWorkGroupSizeX_NV = 32; 191 builtin->maxTaskWorkGroupSizeY_NV = 1; 192 builtin->maxTaskWorkGroupSizeZ_NV = 1; 193 builtin->maxMeshViewCountNV = 4; 194 builtin->maxDualSourceDrawBuffersEXT = 1; 195}; 196 197glslang::EShTargetLanguageVersion getSpirvTargetVersion(SpirvVersion version) 198{ 199 switch(version) 200 { 201 default: 202 DE_FATAL("unhandled SPIRV target version"); 203 // fall-through 204 case SPIRV_VERSION_1_0: 205 return glslang::EShTargetSpv_1_0; 206 case SPIRV_VERSION_1_1: 207 return glslang::EShTargetSpv_1_1; 208 case SPIRV_VERSION_1_2: 209 return glslang::EShTargetSpv_1_2; 210 case SPIRV_VERSION_1_3: 211 return glslang::EShTargetSpv_1_3; 212 } 213} 214 215bool compileGlslToSpirV(tcu::TestLog& log, std::string source, glu::ShaderType type, ShaderBinaryDataType* dst, SpirvVersion version) 216{ 217 TBuiltInResource builtinRes; 218 219 prepareGlslang(); 220 getDefaultBuiltInResources(&builtinRes); 221 222 const EShLanguage shaderStage = getGlslangStage(type); 223 224 glslang::TShader shader(shaderStage); 225 glslang::TProgram program; 226 227 const char* src[] = { source.c_str() }; 228 229 shader.setStrings(src, 1); 230 shader.setEnvTarget(glslang::EshTargetSpv, getSpirvTargetVersion(version)); 231 program.addShader(&shader); 232 233 const int compileRes = shader.parse(&builtinRes, 100, false, EShMsgSpvRules); 234 if (compileRes != 0) 235 { 236 const int linkRes = program.link(EShMsgSpvRules); 237 238 if (linkRes != 0) 239 { 240 const glslang::TIntermediate* const intermediate = program.getIntermediate(shaderStage); 241 glslang::GlslangToSpv(*intermediate, *dst); 242 243 return true; 244 } 245 else 246 { 247 log << tcu::TestLog::Message << "Program linking error:\n" 248 << program.getInfoLog() << "\n" 249 << "Source:\n" 250 << source << "\n" 251 << tcu::TestLog::EndMessage; 252 } 253 } 254 else 255 { 256 log << tcu::TestLog::Message << "Shader compilation error:\n" 257 << shader.getInfoLog() << "\n" 258 << "Source:\n" 259 << source << "\n" 260 << tcu::TestLog::EndMessage; 261 } 262 263 return false; 264} 265 266void consumer(spv_message_level_t, const char*, const spv_position_t&, const char* m) 267{ 268 std::cerr << "error: " << m << std::endl; 269} 270 271void spirvAssemble(ShaderBinaryDataType& dst, const std::string& src) 272{ 273 spvtools::SpirvTools core(SPV_ENV_OPENGL_4_5); 274 275 core.SetMessageConsumer(consumer); 276 277 if (!core.Assemble(src, &dst)) 278 TCU_THROW(InternalError, "Failed to assemble Spir-V source."); 279} 280 281void spirvDisassemble(std::string& dst, const ShaderBinaryDataType& src) 282{ 283 spvtools::SpirvTools core(SPV_ENV_OPENGL_4_5); 284 285 core.SetMessageConsumer(consumer); 286 287 if (!core.Disassemble(src, &dst)) 288 TCU_THROW(InternalError, "Failed to disassemble Spir-V module."); 289} 290 291bool spirvValidate(ShaderBinaryDataType& dst, bool throwOnError) 292{ 293 spvtools::SpirvTools core(SPV_ENV_OPENGL_4_5); 294 295 if (throwOnError) 296 core.SetMessageConsumer(consumer); 297 298 if (!core.Validate(dst)) 299 { 300 if (throwOnError) 301 TCU_THROW(InternalError, "Failed to validate Spir-V module."); 302 return false; 303 } 304 305 return true; 306} 307 308ShaderBinary makeSpirV(tcu::TestLog& log, ShaderSource source, SpirvVersion version) 309{ 310 ShaderBinary binary; 311 312 if (!spirvUtils::compileGlslToSpirV(log, source.source, source.shaderType, &binary.binary, version)) 313 TCU_THROW(InternalError, "Failed to convert GLSL to Spir-V"); 314 315 binary << source.shaderType << "main"; 316 317 return binary; 318} 319 320/** Verifying if GLSL to SpirV mapping was performed correctly 321 * 322 * @param glslSource GLSL shader template 323 * @param spirVSource SpirV disassembled source 324 * @param mappings Glsl to SpirV mappings vector 325 * @param anyOf any occurence indicator 326 * 327 * @return true if GLSL code occurs as many times as all of SpirV code for each mapping if anyOf is false 328 * or true if SpirV code occurs at least once if GLSL code found, false otherwise. 329 **/ 330bool verifyMappings(std::string glslSource, std::string spirVSource, SpirVMapping& mappings, bool anyOf) 331{ 332 std::vector<std::string> spirVSourceLines = de::splitString(spirVSource, '\n'); 333 334 // Iterate through all glsl functions 335 for (SpirVMapping::iterator it = mappings.begin(); it != mappings.end(); it++) 336 { 337 int glslCodeCount = 0; 338 int spirVCodeCount = 0; 339 340 // To avoid finding functions with similar names (ie. "cos", "acos", "cosh") 341 // add characteristic characters that delimits finding results 342 std::string glslCode = it->first; 343 344 // Count GLSL code occurrences in GLSL source 345 size_t codePosition = glslSource.find(glslCode); 346 while (codePosition != std::string::npos) 347 { 348 glslCodeCount++; 349 codePosition = glslSource.find(glslCode, codePosition + 1); 350 } 351 352 if (glslCodeCount > 0) 353 { 354 // Count all SpirV code variants occurrences in SpirV source 355 for (int s = 0; s < (signed)it->second.size(); ++s) 356 { 357 std::vector<std::string> spirVCodes = de::splitString(it->second[s], ' '); 358 359 for (int v = 0; v < (signed)spirVSourceLines.size(); ++v) 360 { 361 std::vector<std::string> spirVLineCodes = de::splitString(spirVSourceLines[v], ' '); 362 363 bool matchAll = true; 364 for (int j = 0; j < (signed)spirVCodes.size(); ++j) 365 { 366 bool match = false; 367 for (int i = 0; i < (signed)spirVLineCodes.size(); ++i) 368 { 369 if (spirVLineCodes[i] == spirVCodes[j]) 370 match = true; 371 } 372 373 matchAll = matchAll && match; 374 } 375 376 if (matchAll) 377 spirVCodeCount++; 378 } 379 } 380 381 // Check if both counts match 382 if (anyOf && (glslCodeCount > 0 && spirVCodeCount == 0)) 383 return false; 384 else if (!anyOf && glslCodeCount != spirVCodeCount) 385 return false; 386 } 387 } 388 389 return true; 390} 391 392} // namespace spirvUtils 393 394} // namespace glc 395