1 /*-------------------------------------------------------------------------
5 * Copyright (c) 2019 Google Inc.
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 * \brief Program utilities.
22 *//*--------------------------------------------------------------------*/
24 #include "spirv-tools/optimizer.hpp"
28 #include "vkPrograms.hpp"
29 #include "vkShaderToSpirV.hpp"
30 #include "vkSpirVAsm.hpp"
31 #include "vkRefUtil.hpp"
33 #include "deMutex.hpp"
34 #include "deFilePath.hpp"
35 #include "deArrayUtil.hpp"
39 #include "tcuCommandLine.hpp"
51 # define VALIDATE_BINARIES true
53 # define VALIDATE_BINARIES false
56 #define SPIRV_BINARY_ENDIANNESS DE_LITTLE_ENDIAN
60 ProgramBinary::ProgramBinary (ProgramFormat format, size_t binarySize, const deUint8* binary)
62 , m_binary (binary, binary+binarySize)
71 bool isNativeSpirVBinaryEndianness (void)
73 #if (DE_ENDIANNESS == SPIRV_BINARY_ENDIANNESS)
80 bool isSaneSpirVBinary (const ProgramBinary& binary)
82 const deUint32 spirvMagicWord = 0x07230203;
83 const deUint32 spirvMagicBytes = isNativeSpirVBinaryEndianness()
85 : deReverseBytes32(spirvMagicWord);
87 DE_ASSERT(binary.getFormat() == PROGRAM_FORMAT_SPIRV);
89 if (binary.getSize() % sizeof(deUint32) != 0)
92 if (binary.getSize() < sizeof(deUint32))
95 if (*(const deUint32*)binary.getBinary() != spirvMagicBytes)
101 void optimizeCompiledBinary (vector<deUint32>& binary, int optimizationRecipe, const SpirvVersion spirvVersion)
103 spv_target_env targetEnv = SPV_ENV_VULKAN_1_0;
105 // Map SpirvVersion with spv_target_env:
106 switch (spirvVersion)
108 case SPIRV_VERSION_1_0: targetEnv = SPV_ENV_VULKAN_1_0; break;
109 case SPIRV_VERSION_1_1:
110 case SPIRV_VERSION_1_2:
111 case SPIRV_VERSION_1_3: targetEnv = SPV_ENV_VULKAN_1_1; break;
112 case SPIRV_VERSION_1_4: targetEnv = SPV_ENV_VULKAN_1_1_SPIRV_1_4; break;
114 TCU_THROW(InternalError, "Unexpected SPIR-V version requested");
117 spvtools::Optimizer optimizer(targetEnv);
119 switch (optimizationRecipe)
122 optimizer.RegisterPerformancePasses();
125 optimizer.RegisterSizePasses();
128 TCU_THROW(InternalError, "Unknown optimization recipe requested");
131 spvtools::OptimizerOptions optimizer_options;
132 optimizer_options.set_run_validator(false);
133 const bool ok = optimizer.Run(binary.data(), binary.size(), &binary, optimizer_options);
136 TCU_THROW(InternalError, "Optimizer call failed");
139 ProgramBinary* createProgramBinaryFromSpirV (const vector<deUint32>& binary)
141 DE_ASSERT(!binary.empty());
143 if (isNativeSpirVBinaryEndianness())
144 return new ProgramBinary(PROGRAM_FORMAT_SPIRV, binary.size()*sizeof(deUint32), (const deUint8*)&binary[0]);
146 TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
151 void validateCompiledBinary(const vector<deUint32>& binary, glu::ShaderProgramInfo* buildInfo, const SpirvValidatorOptions& options)
153 std::ostringstream validationLog;
155 if (!validateSpirV(binary.size(), &binary[0], &validationLog, options))
157 buildInfo->program.linkOk = false;
158 buildInfo->program.infoLog += "\n" + validationLog.str();
160 TCU_THROW(InternalError, "Validation failed for compiled SPIR-V binary");
164 void validateCompiledBinary(const vector<deUint32>& binary, SpirVProgramInfo* buildInfo, const SpirvValidatorOptions& options)
166 std::ostringstream validationLog;
168 if (!validateSpirV(binary.size(), &binary[0], &validationLog, options))
170 buildInfo->compileOk = false;
171 buildInfo->infoLog += "\n" + validationLog.str();
173 TCU_THROW(InternalError, "Validation failed for compiled SPIR-V binary");
177 de::Mutex cacheFileMutex;
178 map<deUint32, vector<deUint32> > cacheFileIndex;
179 bool cacheFileFirstRun = true;
181 void shaderCacheFirstRunCheck (const char* shaderCacheFile, bool truncate)
183 cacheFileMutex.lock();
184 if (cacheFileFirstRun)
186 cacheFileFirstRun = false;
189 // Open file with "w" access to truncate it
190 FILE* f = fopen(shaderCacheFile, "wb");
196 // Parse chunked shader cache file for hashes and offsets
197 FILE* file = fopen(shaderCacheFile, "rb");
201 deUint32 chunksize = 0;
207 offset = (deUint32)ftell(file);
208 if (ok) ok = fread(&chunksize, 1, 4, file) == 4;
209 if (ok) ok = fread(&hash, 1, 4, file) == 4;
210 if (ok) cacheFileIndex[hash].push_back(offset);
211 if (ok) ok = fseek(file, offset + chunksize, SEEK_SET) == 0;
218 cacheFileMutex.unlock();
221 std::string intToString (deUint32 integer)
223 std::stringstream temp_sstream;
225 temp_sstream << integer;
227 return temp_sstream.str();
231 deUint32 shadercacheHash (const char* str)
233 deUint32 hash = 0x811c9dc5;
235 while ((c = (deUint32)*str++) != 0)
243 vk::ProgramBinary* shadercacheLoad (const std::string& shaderstring, const char* shaderCacheFilename)
245 deUint32 hash = shadercacheHash(shaderstring.c_str());
248 deInt32 sourcelength;
255 cacheFileMutex.lock();
257 if (cacheFileIndex.count(hash) == 0)
259 cacheFileMutex.unlock();
262 FILE* file = fopen(shaderCacheFilename, "rb");
265 for (i = 0; i < cacheFileIndex[hash].size(); i++)
267 if (ok) ok = fseek(file, cacheFileIndex[hash][i], SEEK_SET) == 0;
268 if (ok) ok = fread(&temp, 1, 4, file) == 4; // Chunk size (skip)
269 if (ok) ok = fread(&temp, 1, 4, file) == 4; // Stored hash
270 if (ok) ok = temp == hash; // Double check
271 if (ok) ok = fread(&format, 1, 4, file) == 4;
272 if (ok) ok = fread(&length, 1, 4, file) == 4;
273 if (ok) ok = length > 0; // sanity check
274 if (ok) bin = new deUint8[length];
275 if (ok) ok = fread(bin, 1, length, file) == (size_t)length;
276 if (ok) ok = fread(&sourcelength, 1, 4, file) == 4;
277 if (ok && sourcelength > 0)
279 source = new char[sourcelength + 1];
280 ok = fread(source, 1, sourcelength, file) == (size_t)sourcelength;
281 source[sourcelength] = 0;
282 diff = shaderstring != std::string(source);
286 // Mismatch, but may still exist in cache if there were hash collisions
293 if (file) fclose(file);
294 cacheFileMutex.unlock();
295 vk::ProgramBinary* res = new vk::ProgramBinary((vk::ProgramFormat)format, length, bin);
300 if (file) fclose(file);
301 cacheFileMutex.unlock();
305 void shadercacheSave (const vk::ProgramBinary* binary, const std::string& shaderstring, const char* shaderCacheFilename)
309 deUint32 hash = shadercacheHash(shaderstring.c_str());
310 deInt32 format = binary->getFormat();
311 deUint32 length = (deUint32)binary->getSize();
314 const deUint8* bin = binary->getBinary();
315 const de::FilePath filePath (shaderCacheFilename);
317 cacheFileMutex.lock();
319 if (cacheFileIndex[hash].size())
321 FILE* file = fopen(shaderCacheFilename, "rb");
322 deBool ok = (file != 0);
323 deBool diff = DE_TRUE;
324 deInt32 sourcelength;
328 for (i = 0; i < cacheFileIndex[hash].size(); i++)
330 deUint32 cachedLength = 0;
332 if (ok) ok = fseek(file, cacheFileIndex[hash][i], SEEK_SET) == 0;
333 if (ok) ok = fread(&temp, 1, 4, file) == 4; // Chunk size (skip)
334 if (ok) ok = fread(&temp, 1, 4, file) == 4; // Stored hash
335 if (ok) ok = temp == hash; // Double check
336 if (ok) ok = fread(&temp, 1, 4, file) == 4;
337 if (ok) ok = fread(&cachedLength, 1, 4, file) == 4;
338 if (ok) ok = cachedLength > 0; // sanity check
339 if (ok) fseek(file, cachedLength, SEEK_CUR); // skip binary
340 if (ok) ok = fread(&sourcelength, 1, 4, file) == 4;
342 if (ok && sourcelength > 0)
345 source = new char[sourcelength + 1];
346 ok = fread(source, 1, sourcelength, file) == (size_t)sourcelength;
347 source[sourcelength] = 0;
348 diff = shaderstring != std::string(source);
354 // Already in cache (written by another thread, probably)
356 cacheFileMutex.unlock();
363 if (!de::FilePath(filePath.getDirName()).exists())
364 de::createDirectoryAndParents(filePath.getDirName().c_str());
366 FILE* file = fopen(shaderCacheFilename, "ab");
369 cacheFileMutex.unlock();
372 // Append mode starts writing from the end of the file,
373 // but unless we do a seek, ftell returns 0.
374 fseek(file, 0, SEEK_END);
375 offset = (deUint32)ftell(file);
376 chunksize = 4 + 4 + 4 + 4 + length + 4 + (deUint32)shaderstring.length();
377 fwrite(&chunksize, 1, 4, file);
378 fwrite(&hash, 1, 4, file);
379 fwrite(&format, 1, 4, file);
380 fwrite(&length, 1, 4, file);
381 fwrite(bin, 1, length, file);
382 length = (deUint32)shaderstring.length();
383 fwrite(&length, 1, 4, file);
384 fwrite(shaderstring.c_str(), 1, length, file);
386 cacheFileIndex[hash].push_back(offset);
388 cacheFileMutex.unlock();
391 // Insert any information that may affect compilation into the shader string.
392 void getCompileEnvironment (std::string& shaderstring)
394 shaderstring += "GLSL:";
395 shaderstring += qpGetReleaseGlslName();
396 shaderstring += "\nSpir-v Tools:";
397 shaderstring += qpGetReleaseSpirvToolsName();
398 shaderstring += "\nSpir-v Headers:";
399 shaderstring += qpGetReleaseSpirvHeadersName();
400 shaderstring += "\n";
403 // Insert compilation options into the shader string.
404 void getBuildOptions (std::string& shaderstring, const ShaderBuildOptions& buildOptions, int optimizationRecipe)
406 shaderstring += "Target Spir-V ";
407 shaderstring += getSpirvVersionName(buildOptions.targetVersion);
408 shaderstring += "\n";
409 if (buildOptions.flags & ShaderBuildOptions::FLAG_ALLOW_RELAXED_OFFSETS)
410 shaderstring += "Flag:Allow relaxed offsets\n";
411 if (buildOptions.flags & ShaderBuildOptions::FLAG_USE_STORAGE_BUFFER_STORAGE_CLASS)
412 shaderstring += "Flag:Use storage buffer storage class\n";
413 if (optimizationRecipe != 0)
415 shaderstring += "Optimization recipe ";
416 shaderstring += de::toString(optimizationRecipe);
417 shaderstring += "\n";
421 ProgramBinary* buildProgram (const GlslSource& program, glu::ShaderProgramInfo* buildInfo, const tcu::CommandLine& commandLine)
423 const SpirvVersion spirvVersion = program.buildOptions.targetVersion;
424 const bool validateBinary = VALIDATE_BINARIES;
425 vector<deUint32> binary;
426 std::string cachekey;
427 std::string shaderstring;
428 vk::ProgramBinary* res = 0;
429 const int optimizationRecipe = commandLine.getOptimizationRecipe();
431 if (commandLine.isShadercacheEnabled())
433 shaderCacheFirstRunCheck(commandLine.getShaderCacheFilename(), commandLine.isShaderCacheTruncateEnabled());
434 getCompileEnvironment(cachekey);
435 getBuildOptions(cachekey, program.buildOptions, optimizationRecipe);
437 for (int i = 0; i < glu::SHADERTYPE_LAST; i++)
439 if (!program.sources[i].empty())
441 cachekey += glu::getShaderTypeName((glu::ShaderType)i);
443 for (std::vector<std::string>::const_iterator it = program.sources[i].begin(); it != program.sources[i].end(); ++it)
448 cachekey = cachekey + shaderstring;
450 res = shadercacheLoad(cachekey, commandLine.getShaderCacheFilename());
454 buildInfo->program.infoLog = "Loaded from cache";
455 buildInfo->program.linkOk = true;
456 buildInfo->program.linkTimeUs = 0;
458 for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++)
460 if (!program.sources[shaderType].empty())
462 glu::ShaderInfo shaderBuildInfo;
464 shaderBuildInfo.type = (glu::ShaderType)shaderType;
465 shaderBuildInfo.source = shaderstring;
466 shaderBuildInfo.compileTimeUs = 0;
467 shaderBuildInfo.compileOk = true;
469 buildInfo->shaders.push_back(shaderBuildInfo);
478 vector<deUint32> nonStrippedBinary;
480 if (!compileGlslToSpirV(program, &nonStrippedBinary, buildInfo))
481 TCU_THROW(InternalError, "Compiling GLSL to SPIR-V failed");
483 TCU_CHECK_INTERNAL(!nonStrippedBinary.empty());
484 stripSpirVDebugInfo(nonStrippedBinary.size(), &nonStrippedBinary[0], &binary);
485 TCU_CHECK_INTERNAL(!binary.empty());
488 if (optimizationRecipe != 0)
490 validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
491 optimizeCompiledBinary(binary, optimizationRecipe, spirvVersion);
496 validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
499 res = createProgramBinaryFromSpirV(binary);
500 if (commandLine.isShadercacheEnabled())
501 shadercacheSave(res, cachekey, commandLine.getShaderCacheFilename());
506 ProgramBinary* buildProgram (const HlslSource& program, glu::ShaderProgramInfo* buildInfo, const tcu::CommandLine& commandLine)
508 const SpirvVersion spirvVersion = program.buildOptions.targetVersion;
509 const bool validateBinary = VALIDATE_BINARIES;
510 vector<deUint32> binary;
511 std::string cachekey;
512 std::string shaderstring;
513 vk::ProgramBinary* res = 0;
514 const int optimizationRecipe = commandLine.getOptimizationRecipe();
516 if (commandLine.isShadercacheEnabled())
518 shaderCacheFirstRunCheck(commandLine.getShaderCacheFilename(), commandLine.isShaderCacheTruncateEnabled());
519 getCompileEnvironment(cachekey);
520 getBuildOptions(cachekey, program.buildOptions, optimizationRecipe);
522 for (int i = 0; i < glu::SHADERTYPE_LAST; i++)
524 if (!program.sources[i].empty())
526 cachekey += glu::getShaderTypeName((glu::ShaderType)i);
528 for (std::vector<std::string>::const_iterator it = program.sources[i].begin(); it != program.sources[i].end(); ++it)
533 cachekey = cachekey + shaderstring;
535 res = shadercacheLoad(cachekey, commandLine.getShaderCacheFilename());
539 buildInfo->program.infoLog = "Loaded from cache";
540 buildInfo->program.linkOk = true;
541 buildInfo->program.linkTimeUs = 0;
543 for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++)
545 if (!program.sources[shaderType].empty())
547 glu::ShaderInfo shaderBuildInfo;
549 shaderBuildInfo.type = (glu::ShaderType)shaderType;
550 shaderBuildInfo.source = shaderstring;
551 shaderBuildInfo.compileTimeUs = 0;
552 shaderBuildInfo.compileOk = true;
554 buildInfo->shaders.push_back(shaderBuildInfo);
563 vector<deUint32> nonStrippedBinary;
565 if (!compileHlslToSpirV(program, &nonStrippedBinary, buildInfo))
566 TCU_THROW(InternalError, "Compiling HLSL to SPIR-V failed");
568 TCU_CHECK_INTERNAL(!nonStrippedBinary.empty());
569 stripSpirVDebugInfo(nonStrippedBinary.size(), &nonStrippedBinary[0], &binary);
570 TCU_CHECK_INTERNAL(!binary.empty());
573 if (optimizationRecipe != 0)
575 validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
576 optimizeCompiledBinary(binary, optimizationRecipe, spirvVersion);
581 validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
584 res = createProgramBinaryFromSpirV(binary);
585 if (commandLine.isShadercacheEnabled())
586 shadercacheSave(res, cachekey, commandLine.getShaderCacheFilename());
591 ProgramBinary* assembleProgram (const SpirVAsmSource& program, SpirVProgramInfo* buildInfo, const tcu::CommandLine& commandLine)
593 const SpirvVersion spirvVersion = program.buildOptions.targetVersion;
594 const bool validateBinary = VALIDATE_BINARIES;
595 vector<deUint32> binary;
596 vk::ProgramBinary* res = 0;
597 std::string cachekey;
598 const int optimizationRecipe = commandLine.isSpirvOptimizationEnabled() ? commandLine.getOptimizationRecipe() : 0;
600 if (commandLine.isShadercacheEnabled())
602 shaderCacheFirstRunCheck(commandLine.getShaderCacheFilename(), commandLine.isShaderCacheTruncateEnabled());
603 getCompileEnvironment(cachekey);
604 cachekey += "Target Spir-V ";
605 cachekey += getSpirvVersionName(spirvVersion);
607 if (optimizationRecipe != 0)
609 cachekey += "Optimization recipe ";
610 cachekey += de::toString(optimizationRecipe);
614 cachekey += program.source;
616 res = shadercacheLoad(cachekey, commandLine.getShaderCacheFilename());
620 buildInfo->source = program.source;
621 buildInfo->compileOk = true;
622 buildInfo->compileTimeUs = 0;
623 buildInfo->infoLog = "Loaded from cache";
630 if (!assembleSpirV(&program, &binary, buildInfo, spirvVersion))
631 TCU_THROW(InternalError, "Failed to assemble SPIR-V");
633 if (optimizationRecipe != 0)
635 validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
636 optimizeCompiledBinary(binary, optimizationRecipe, spirvVersion);
641 validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
644 res = createProgramBinaryFromSpirV(binary);
645 if (commandLine.isShadercacheEnabled())
646 shadercacheSave(res, cachekey, commandLine.getShaderCacheFilename());
651 void disassembleProgram (const ProgramBinary& program, std::ostream* dst)
653 if (program.getFormat() == PROGRAM_FORMAT_SPIRV)
655 TCU_CHECK_INTERNAL(isSaneSpirVBinary(program));
657 if (isNativeSpirVBinaryEndianness())
658 disassembleSpirV(program.getSize()/sizeof(deUint32), (const deUint32*)program.getBinary(), dst,
659 extractSpirvVersion(program));
661 TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
664 TCU_THROW(NotSupportedError, "Unsupported program format");
667 bool validateProgram (const ProgramBinary& program, std::ostream* dst, const SpirvValidatorOptions& options)
669 if (program.getFormat() == PROGRAM_FORMAT_SPIRV)
671 if (!isSaneSpirVBinary(program))
673 *dst << "Binary doesn't look like SPIR-V at all";
677 if (isNativeSpirVBinaryEndianness())
678 return validateSpirV(program.getSize()/sizeof(deUint32), (const deUint32*)program.getBinary(), dst, options);
680 TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
683 TCU_THROW(NotSupportedError, "Unsupported program format");
686 Move<VkShaderModule> createShaderModule (const DeviceInterface& deviceInterface, VkDevice device, const ProgramBinary& binary, VkShaderModuleCreateFlags flags)
688 if (binary.getFormat() == PROGRAM_FORMAT_SPIRV)
690 const struct VkShaderModuleCreateInfo shaderModuleInfo =
692 VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
695 (deUintptr)binary.getSize(),
696 (const deUint32*)binary.getBinary(),
699 return createShaderModule(deviceInterface, device, &shaderModuleInfo);
702 TCU_THROW(NotSupportedError, "Unsupported program format");
705 glu::ShaderType getGluShaderType (VkShaderStageFlagBits shaderStage)
709 case VK_SHADER_STAGE_VERTEX_BIT: return glu::SHADERTYPE_VERTEX;
710 case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT: return glu::SHADERTYPE_TESSELLATION_CONTROL;
711 case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT: return glu::SHADERTYPE_TESSELLATION_EVALUATION;
712 case VK_SHADER_STAGE_GEOMETRY_BIT: return glu::SHADERTYPE_GEOMETRY;
713 case VK_SHADER_STAGE_FRAGMENT_BIT: return glu::SHADERTYPE_FRAGMENT;
714 case VK_SHADER_STAGE_COMPUTE_BIT: return glu::SHADERTYPE_COMPUTE;
716 DE_FATAL("Unknown shader stage");
717 return glu::SHADERTYPE_LAST;
721 VkShaderStageFlagBits getVkShaderStage (glu::ShaderType shaderType)
723 static const VkShaderStageFlagBits s_shaderStages[] =
725 VK_SHADER_STAGE_VERTEX_BIT,
726 VK_SHADER_STAGE_FRAGMENT_BIT,
727 VK_SHADER_STAGE_GEOMETRY_BIT,
728 VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
729 VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
730 VK_SHADER_STAGE_COMPUTE_BIT,
731 VK_SHADER_STAGE_RAYGEN_BIT_NV,
732 VK_SHADER_STAGE_ANY_HIT_BIT_NV,
733 VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV,
734 VK_SHADER_STAGE_MISS_BIT_NV,
735 VK_SHADER_STAGE_INTERSECTION_BIT_NV,
736 VK_SHADER_STAGE_CALLABLE_BIT_NV,
739 return de::getSizedArrayElement<glu::SHADERTYPE_LAST>(s_shaderStages, shaderType);
742 // Baseline version, to be used for shaders which don't specify a version
743 vk::SpirvVersion getBaselineSpirvVersion (const deUint32 /* vulkanVersion */)
745 return vk::SPIRV_VERSION_1_0;
748 // Max supported versions for each Vulkan version, without requiring a Vulkan extension.
749 vk::SpirvVersion getMaxSpirvVersionForVulkan (const deUint32 vulkanVersion)
751 vk::SpirvVersion result = vk::SPIRV_VERSION_LAST;
753 deUint32 vulkanVersionMajorMinor = VK_MAKE_VERSION(VK_VERSION_MAJOR(vulkanVersion), VK_VERSION_MINOR(vulkanVersion), 0);
754 if (vulkanVersionMajorMinor == VK_API_VERSION_1_0)
755 result = vk::SPIRV_VERSION_1_0;
756 else if (vulkanVersionMajorMinor >= VK_API_VERSION_1_1)
757 result = vk::SPIRV_VERSION_1_3;
759 DE_ASSERT(result < vk::SPIRV_VERSION_LAST);
764 vk::SpirvVersion getMaxSpirvVersionForAsm (const deUint32 vulkanVersion)
766 return getMaxSpirvVersionForVulkan(vulkanVersion);
769 vk::SpirvVersion getMaxSpirvVersionForGlsl (const deUint32 vulkanVersion)
771 return getMaxSpirvVersionForVulkan(vulkanVersion);
774 SpirvVersion extractSpirvVersion (const ProgramBinary& binary)
776 DE_STATIC_ASSERT(SPIRV_VERSION_1_4 + 1 == SPIRV_VERSION_LAST);
778 if (binary.getFormat() != PROGRAM_FORMAT_SPIRV)
779 TCU_THROW(InternalError, "Binary is not in SPIR-V format");
781 if (!isSaneSpirVBinary(binary) || binary.getSize() < sizeof(SpirvBinaryHeader))
782 TCU_THROW(InternalError, "Invalid SPIR-V header format");
784 const deUint32 spirvBinaryVersion10 = 0x00010000;
785 const deUint32 spirvBinaryVersion11 = 0x00010100;
786 const deUint32 spirvBinaryVersion12 = 0x00010200;
787 const deUint32 spirvBinaryVersion13 = 0x00010300;
788 const deUint32 spirvBinaryVersion14 = 0x00010400;
789 const SpirvBinaryHeader* header = reinterpret_cast<const SpirvBinaryHeader*>(binary.getBinary());
790 const deUint32 spirvVersion = isNativeSpirVBinaryEndianness()
792 : deReverseBytes32(header->version);
793 SpirvVersion result = SPIRV_VERSION_LAST;
795 switch (spirvVersion)
797 case spirvBinaryVersion10: result = SPIRV_VERSION_1_0; break; //!< SPIR-V 1.0
798 case spirvBinaryVersion11: result = SPIRV_VERSION_1_1; break; //!< SPIR-V 1.1
799 case spirvBinaryVersion12: result = SPIRV_VERSION_1_2; break; //!< SPIR-V 1.2
800 case spirvBinaryVersion13: result = SPIRV_VERSION_1_3; break; //!< SPIR-V 1.3
801 case spirvBinaryVersion14: result = SPIRV_VERSION_1_4; break; //!< SPIR-V 1.4
802 default: TCU_THROW(InternalError, "Unknown SPIR-V version detected in binary");
808 std::string getSpirvVersionName (const SpirvVersion spirvVersion)
810 DE_STATIC_ASSERT(SPIRV_VERSION_1_4 + 1 == SPIRV_VERSION_LAST);
811 DE_ASSERT(spirvVersion < SPIRV_VERSION_LAST);
815 switch (spirvVersion)
817 case SPIRV_VERSION_1_0: result = "1.0"; break; //!< SPIR-V 1.0
818 case SPIRV_VERSION_1_1: result = "1.1"; break; //!< SPIR-V 1.1
819 case SPIRV_VERSION_1_2: result = "1.2"; break; //!< SPIR-V 1.2
820 case SPIRV_VERSION_1_3: result = "1.3"; break; //!< SPIR-V 1.3
821 case SPIRV_VERSION_1_4: result = "1.4"; break; //!< SPIR-V 1.4
822 default: result = "Unknown";
828 SpirvVersion& operator++(SpirvVersion& spirvVersion)
830 if (spirvVersion == SPIRV_VERSION_LAST)
831 spirvVersion = SPIRV_VERSION_1_0;
833 spirvVersion = static_cast<SpirvVersion>(static_cast<deUint32>(spirvVersion) + 1);