Merge vk-gl-cts/vulkan-cts-1.1.5 into vk-gl-cts/vulkan-cts-1.1.6
[platform/upstream/VK-GL-CTS.git] / external / vulkancts / framework / vulkan / vkPrograms.cpp
1 /*-------------------------------------------------------------------------
2  * Vulkan CTS Framework
3  * --------------------
4  *
5  * Copyright (c) 2019 Google 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
21  * \brief Program utilities.
22  *//*--------------------------------------------------------------------*/
23
24 #include "spirv-tools/optimizer.hpp"
25
26 #include "qpInfo.h"
27
28 #include "vkPrograms.hpp"
29 #include "vkShaderToSpirV.hpp"
30 #include "vkSpirVAsm.hpp"
31 #include "vkRefUtil.hpp"
32
33 #include "deMutex.hpp"
34 #include "deFilePath.hpp"
35 #include "deArrayUtil.hpp"
36 #include "deMemory.h"
37 #include "deInt32.h"
38
39 #include "tcuCommandLine.hpp"
40
41 #include <map>
42
43 namespace vk
44 {
45
46 using std::string;
47 using std::vector;
48 using std::map;
49
50 #if defined(DE_DEBUG)
51 #       define VALIDATE_BINARIES        true
52 #else
53 #       define VALIDATE_BINARIES        false
54 #endif
55
56 #define SPIRV_BINARY_ENDIANNESS DE_LITTLE_ENDIAN
57
58 // ProgramBinary
59
60 ProgramBinary::ProgramBinary (ProgramFormat format, size_t binarySize, const deUint8* binary)
61         : m_format      (format)
62         , m_binary      (binary, binary+binarySize)
63 {
64 }
65
66 // Utils
67
68 namespace
69 {
70
71 bool isNativeSpirVBinaryEndianness (void)
72 {
73 #if (DE_ENDIANNESS == SPIRV_BINARY_ENDIANNESS)
74         return true;
75 #else
76         return false;
77 #endif
78 }
79
80 bool isSaneSpirVBinary (const ProgramBinary& binary)
81 {
82         const deUint32  spirvMagicWord  = 0x07230203;
83         const deUint32  spirvMagicBytes = isNativeSpirVBinaryEndianness()
84                                                                         ? spirvMagicWord
85                                                                         : deReverseBytes32(spirvMagicWord);
86
87         DE_ASSERT(binary.getFormat() == PROGRAM_FORMAT_SPIRV);
88
89         if (binary.getSize() % sizeof(deUint32) != 0)
90                 return false;
91
92         if (binary.getSize() < sizeof(deUint32))
93                 return false;
94
95         if (*(const deUint32*)binary.getBinary() != spirvMagicBytes)
96                 return false;
97
98         return true;
99 }
100
101 void optimizeCompiledBinary (vector<deUint32>& binary, int optimizationRecipe, const SpirvVersion spirvVersion)
102 {
103         spv_target_env targetEnv = SPV_ENV_VULKAN_1_0;
104
105         // Map SpirvVersion with spv_target_env:
106         switch (spirvVersion)
107         {
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;
113                 default:
114                         TCU_THROW(InternalError, "Unexpected SPIR-V version requested");
115         }
116
117         spvtools::Optimizer optimizer(targetEnv);
118
119         switch (optimizationRecipe)
120         {
121                 case 1:
122                         optimizer.RegisterPerformancePasses();
123                         break;
124                 case 2:
125                         optimizer.RegisterSizePasses();
126                         break;
127                 default:
128                         TCU_THROW(InternalError, "Unknown optimization recipe requested");
129         }
130
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);
134
135         if (!ok)
136                 TCU_THROW(InternalError, "Optimizer call failed");
137 }
138
139 ProgramBinary* createProgramBinaryFromSpirV (const vector<deUint32>& binary)
140 {
141         DE_ASSERT(!binary.empty());
142
143         if (isNativeSpirVBinaryEndianness())
144                 return new ProgramBinary(PROGRAM_FORMAT_SPIRV, binary.size()*sizeof(deUint32), (const deUint8*)&binary[0]);
145         else
146                 TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
147 }
148
149 } // anonymous
150
151 void validateCompiledBinary(const vector<deUint32>& binary, glu::ShaderProgramInfo* buildInfo, const SpirvValidatorOptions& options)
152 {
153         std::ostringstream validationLog;
154
155         if (!validateSpirV(binary.size(), &binary[0], &validationLog, options))
156         {
157                 buildInfo->program.linkOk        = false;
158                 buildInfo->program.infoLog      += "\n" + validationLog.str();
159
160                 TCU_THROW(InternalError, "Validation failed for compiled SPIR-V binary");
161         }
162 }
163
164 void validateCompiledBinary(const vector<deUint32>& binary, SpirVProgramInfo* buildInfo, const SpirvValidatorOptions& options)
165 {
166         std::ostringstream validationLog;
167
168         if (!validateSpirV(binary.size(), &binary[0], &validationLog, options))
169         {
170                 buildInfo->compileOk = false;
171                 buildInfo->infoLog += "\n" + validationLog.str();
172
173                 TCU_THROW(InternalError, "Validation failed for compiled SPIR-V binary");
174         }
175 }
176
177 de::Mutex                                                       cacheFileMutex;
178 map<deUint32, vector<deUint32> >        cacheFileIndex;
179 bool                                                            cacheFileFirstRun = true;
180
181 void shaderCacheFirstRunCheck (const char* shaderCacheFile, bool truncate)
182 {
183         cacheFileMutex.lock();
184         if (cacheFileFirstRun)
185         {
186                 cacheFileFirstRun = false;
187                 if (truncate)
188                 {
189                         // Open file with "w" access to truncate it
190                         FILE* f = fopen(shaderCacheFile, "wb");
191                         if (f)
192                                 fclose(f);
193                 }
194                 else
195                 {
196                         // Parse chunked shader cache file for hashes and offsets
197                         FILE* file = fopen(shaderCacheFile, "rb");
198                         int count = 0;
199                         if (file)
200                         {
201                                 deUint32 chunksize      = 0;
202                                 deUint32 hash           = 0;
203                                 deUint32 offset         = 0;
204                                 bool ok                         = true;
205                                 while (ok)
206                                 {
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;
212                                         count++;
213                                 }
214                                 fclose(file);
215                         }
216                 }
217         }
218         cacheFileMutex.unlock();
219 }
220
221 std::string intToString (deUint32 integer)
222 {
223         std::stringstream temp_sstream;
224
225         temp_sstream << integer;
226
227         return temp_sstream.str();
228 }
229
230 // 32-bit FNV-1 hash
231 deUint32 shadercacheHash (const char* str)
232 {
233         deUint32 hash = 0x811c9dc5;
234         deUint32 c;
235         while ((c = (deUint32)*str++) != 0)
236         {
237                 hash *= 16777619;
238                 hash ^= c;
239         }
240         return hash;
241 }
242
243 vk::ProgramBinary* shadercacheLoad (const std::string& shaderstring, const char* shaderCacheFilename)
244 {
245         deUint32                hash            = shadercacheHash(shaderstring.c_str());
246         deInt32                 format;
247         deInt32                 length;
248         deInt32                 sourcelength;
249         deUint32                i;
250         deUint32                temp;
251         deUint8*                bin                     = 0;
252         char*                   source          = 0;
253         deBool                  ok                      = true;
254         deBool                  diff            = true;
255         cacheFileMutex.lock();
256
257         if (cacheFileIndex.count(hash) == 0)
258         {
259                 cacheFileMutex.unlock();
260                 return 0;
261         }
262         FILE*                   file            = fopen(shaderCacheFilename, "rb");
263         ok                              = file                                                                                  != 0;
264
265         for (i = 0; i < cacheFileIndex[hash].size(); i++)
266         {
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)
278                 {
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);
283                 }
284                 if (!ok || diff)
285                 {
286                         // Mismatch, but may still exist in cache if there were hash collisions
287                         delete[] source;
288                         delete[] bin;
289                 }
290                 else
291                 {
292                         delete[] source;
293                         if (file) fclose(file);
294                         cacheFileMutex.unlock();
295                         vk::ProgramBinary* res = new vk::ProgramBinary((vk::ProgramFormat)format, length, bin);
296                         delete[] bin;
297                         return res;
298                 }
299         }
300         if (file) fclose(file);
301         cacheFileMutex.unlock();
302         return 0;
303 }
304
305 void shadercacheSave (const vk::ProgramBinary* binary, const std::string& shaderstring, const char* shaderCacheFilename)
306 {
307         if (binary == 0)
308                 return;
309         deUint32                        hash            = shadercacheHash(shaderstring.c_str());
310         deInt32                         format          = binary->getFormat();
311         deUint32                        length          = (deUint32)binary->getSize();
312         deUint32                        chunksize;
313         deUint32                        offset;
314         const deUint8*          bin                     = binary->getBinary();
315         const de::FilePath      filePath        (shaderCacheFilename);
316
317         cacheFileMutex.lock();
318
319         if (cacheFileIndex[hash].size())
320         {
321                 FILE*                   file            = fopen(shaderCacheFilename, "rb");
322                 deBool                  ok                      = (file != 0);
323                 deBool                  diff            = DE_TRUE;
324                 deInt32                 sourcelength;
325                 deUint32                i;
326                 deUint32                temp;
327
328                 for (i = 0; i < cacheFileIndex[hash].size(); i++)
329                 {
330                         deUint32        cachedLength    = 0;
331
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;
341
342                         if (ok && sourcelength > 0)
343                         {
344                                 char* source;
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);
349                                 delete[] source;
350                         }
351
352                         if (ok && !diff)
353                         {
354                                 // Already in cache (written by another thread, probably)
355                                 fclose(file);
356                                 cacheFileMutex.unlock();
357                                 return;
358                         }
359                 }
360                 fclose(file);
361         }
362
363         if (!de::FilePath(filePath.getDirName()).exists())
364                 de::createDirectoryAndParents(filePath.getDirName().c_str());
365
366         FILE*                           file            = fopen(shaderCacheFilename, "ab");
367         if (!file)
368         {
369                 cacheFileMutex.unlock();
370                 return;
371         }
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);
385         fclose(file);
386         cacheFileIndex[hash].push_back(offset);
387
388         cacheFileMutex.unlock();
389 }
390
391 // Insert any information that may affect compilation into the shader string.
392 void getCompileEnvironment (std::string& shaderstring)
393 {
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";
401 }
402
403 // Insert compilation options into the shader string.
404 void getBuildOptions (std::string& shaderstring, const ShaderBuildOptions& buildOptions, int optimizationRecipe)
405 {
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)
414         {
415                 shaderstring += "Optimization recipe ";
416                 shaderstring += de::toString(optimizationRecipe);
417                 shaderstring += "\n";
418         }
419 }
420
421 ProgramBinary* buildProgram (const GlslSource& program, glu::ShaderProgramInfo* buildInfo, const tcu::CommandLine& commandLine)
422 {
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();
430
431         if (commandLine.isShadercacheEnabled())
432         {
433                 shaderCacheFirstRunCheck(commandLine.getShaderCacheFilename(), commandLine.isShaderCacheTruncateEnabled());
434                 getCompileEnvironment(cachekey);
435                 getBuildOptions(cachekey, program.buildOptions, optimizationRecipe);
436
437                 for (int i = 0; i < glu::SHADERTYPE_LAST; i++)
438                 {
439                         if (!program.sources[i].empty())
440                         {
441                                 cachekey += glu::getShaderTypeName((glu::ShaderType)i);
442
443                                 for (std::vector<std::string>::const_iterator it = program.sources[i].begin(); it != program.sources[i].end(); ++it)
444                                         shaderstring += *it;
445                         }
446                 }
447
448                 cachekey = cachekey + shaderstring;
449
450                 res = shadercacheLoad(cachekey, commandLine.getShaderCacheFilename());
451
452                 if (res)
453                 {
454                         buildInfo->program.infoLog              = "Loaded from cache";
455                         buildInfo->program.linkOk               = true;
456                         buildInfo->program.linkTimeUs   = 0;
457
458                         for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++)
459                         {
460                                 if (!program.sources[shaderType].empty())
461                                 {
462                                         glu::ShaderInfo shaderBuildInfo;
463
464                                         shaderBuildInfo.type                    = (glu::ShaderType)shaderType;
465                                         shaderBuildInfo.source                  = shaderstring;
466                                         shaderBuildInfo.compileTimeUs   = 0;
467                                         shaderBuildInfo.compileOk               = true;
468
469                                         buildInfo->shaders.push_back(shaderBuildInfo);
470                                 }
471                         }
472                 }
473         }
474
475         if (!res)
476         {
477                 {
478                         vector<deUint32> nonStrippedBinary;
479
480                         if (!compileGlslToSpirV(program, &nonStrippedBinary, buildInfo))
481                                 TCU_THROW(InternalError, "Compiling GLSL to SPIR-V failed");
482
483                         TCU_CHECK_INTERNAL(!nonStrippedBinary.empty());
484                         stripSpirVDebugInfo(nonStrippedBinary.size(), &nonStrippedBinary[0], &binary);
485                         TCU_CHECK_INTERNAL(!binary.empty());
486                 }
487
488                 if (optimizationRecipe != 0)
489                 {
490                         validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
491                         optimizeCompiledBinary(binary, optimizationRecipe, spirvVersion);
492                 }
493
494                 if (validateBinary)
495                 {
496                         validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
497                 }
498
499                 res = createProgramBinaryFromSpirV(binary);
500                 if (commandLine.isShadercacheEnabled())
501                         shadercacheSave(res, cachekey, commandLine.getShaderCacheFilename());
502         }
503         return res;
504 }
505
506 ProgramBinary* buildProgram (const HlslSource& program, glu::ShaderProgramInfo* buildInfo, const tcu::CommandLine& commandLine)
507 {
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();
515
516         if (commandLine.isShadercacheEnabled())
517         {
518                 shaderCacheFirstRunCheck(commandLine.getShaderCacheFilename(), commandLine.isShaderCacheTruncateEnabled());
519                 getCompileEnvironment(cachekey);
520                 getBuildOptions(cachekey, program.buildOptions, optimizationRecipe);
521
522                 for (int i = 0; i < glu::SHADERTYPE_LAST; i++)
523                 {
524                         if (!program.sources[i].empty())
525                         {
526                                 cachekey += glu::getShaderTypeName((glu::ShaderType)i);
527
528                                 for (std::vector<std::string>::const_iterator it = program.sources[i].begin(); it != program.sources[i].end(); ++it)
529                                         shaderstring += *it;
530                         }
531                 }
532
533                 cachekey = cachekey + shaderstring;
534
535                 res = shadercacheLoad(cachekey, commandLine.getShaderCacheFilename());
536
537                 if (res)
538                 {
539                         buildInfo->program.infoLog              = "Loaded from cache";
540                         buildInfo->program.linkOk               = true;
541                         buildInfo->program.linkTimeUs   = 0;
542
543                         for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++)
544                         {
545                                 if (!program.sources[shaderType].empty())
546                                 {
547                                         glu::ShaderInfo shaderBuildInfo;
548
549                                         shaderBuildInfo.type                    = (glu::ShaderType)shaderType;
550                                         shaderBuildInfo.source                  = shaderstring;
551                                         shaderBuildInfo.compileTimeUs   = 0;
552                                         shaderBuildInfo.compileOk               = true;
553
554                                         buildInfo->shaders.push_back(shaderBuildInfo);
555                                 }
556                         }
557                 }
558         }
559
560         if (!res)
561         {
562                 {
563                         vector<deUint32> nonStrippedBinary;
564
565                         if (!compileHlslToSpirV(program, &nonStrippedBinary, buildInfo))
566                                 TCU_THROW(InternalError, "Compiling HLSL to SPIR-V failed");
567
568                         TCU_CHECK_INTERNAL(!nonStrippedBinary.empty());
569                         stripSpirVDebugInfo(nonStrippedBinary.size(), &nonStrippedBinary[0], &binary);
570                         TCU_CHECK_INTERNAL(!binary.empty());
571                 }
572
573                 if (optimizationRecipe != 0)
574                 {
575                         validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
576                         optimizeCompiledBinary(binary, optimizationRecipe, spirvVersion);
577                 }
578
579                 if (validateBinary)
580                 {
581                         validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
582                 }
583
584                 res = createProgramBinaryFromSpirV(binary);
585                 if (commandLine.isShadercacheEnabled())
586                         shadercacheSave(res, cachekey, commandLine.getShaderCacheFilename());
587         }
588         return res;
589 }
590
591 ProgramBinary* assembleProgram (const SpirVAsmSource& program, SpirVProgramInfo* buildInfo, const tcu::CommandLine& commandLine)
592 {
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;
599
600         if (commandLine.isShadercacheEnabled())
601         {
602                 shaderCacheFirstRunCheck(commandLine.getShaderCacheFilename(), commandLine.isShaderCacheTruncateEnabled());
603                 getCompileEnvironment(cachekey);
604                 cachekey += "Target Spir-V ";
605                 cachekey += getSpirvVersionName(spirvVersion);
606                 cachekey += "\n";
607                 if (optimizationRecipe != 0)
608                 {
609                         cachekey += "Optimization recipe ";
610                         cachekey += de::toString(optimizationRecipe);
611                         cachekey += "\n";
612                 }
613
614                 cachekey += program.source;
615
616                 res = shadercacheLoad(cachekey, commandLine.getShaderCacheFilename());
617
618                 if (res)
619                 {
620                         buildInfo->source                       = program.source;
621                         buildInfo->compileOk            = true;
622                         buildInfo->compileTimeUs        = 0;
623                         buildInfo->infoLog                      = "Loaded from cache";
624                 }
625         }
626
627         if (!res)
628         {
629
630                 if (!assembleSpirV(&program, &binary, buildInfo, spirvVersion))
631                         TCU_THROW(InternalError, "Failed to assemble SPIR-V");
632
633                 if (optimizationRecipe != 0)
634                 {
635                         validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
636                         optimizeCompiledBinary(binary, optimizationRecipe, spirvVersion);
637                 }
638
639                 if (validateBinary)
640                 {
641                         validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
642                 }
643
644                 res = createProgramBinaryFromSpirV(binary);
645                 if (commandLine.isShadercacheEnabled())
646                         shadercacheSave(res, cachekey, commandLine.getShaderCacheFilename());
647         }
648         return res;
649 }
650
651 void disassembleProgram (const ProgramBinary& program, std::ostream* dst)
652 {
653         if (program.getFormat() == PROGRAM_FORMAT_SPIRV)
654         {
655                 TCU_CHECK_INTERNAL(isSaneSpirVBinary(program));
656
657                 if (isNativeSpirVBinaryEndianness())
658                         disassembleSpirV(program.getSize()/sizeof(deUint32), (const deUint32*)program.getBinary(), dst,
659                                                          extractSpirvVersion(program));
660                 else
661                         TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
662         }
663         else
664                 TCU_THROW(NotSupportedError, "Unsupported program format");
665 }
666
667 bool validateProgram (const ProgramBinary& program, std::ostream* dst, const SpirvValidatorOptions& options)
668 {
669         if (program.getFormat() == PROGRAM_FORMAT_SPIRV)
670         {
671                 if (!isSaneSpirVBinary(program))
672                 {
673                         *dst << "Binary doesn't look like SPIR-V at all";
674                         return false;
675                 }
676
677                 if (isNativeSpirVBinaryEndianness())
678                         return validateSpirV(program.getSize()/sizeof(deUint32), (const deUint32*)program.getBinary(), dst, options);
679                 else
680                         TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
681         }
682         else
683                 TCU_THROW(NotSupportedError, "Unsupported program format");
684 }
685
686 Move<VkShaderModule> createShaderModule (const DeviceInterface& deviceInterface, VkDevice device, const ProgramBinary& binary, VkShaderModuleCreateFlags flags)
687 {
688         if (binary.getFormat() == PROGRAM_FORMAT_SPIRV)
689         {
690                 const struct VkShaderModuleCreateInfo           shaderModuleInfo        =
691                 {
692                         VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
693                         DE_NULL,
694                         flags,
695                         (deUintptr)binary.getSize(),
696                         (const deUint32*)binary.getBinary(),
697                 };
698
699                 return createShaderModule(deviceInterface, device, &shaderModuleInfo);
700         }
701         else
702                 TCU_THROW(NotSupportedError, "Unsupported program format");
703 }
704
705 glu::ShaderType getGluShaderType (VkShaderStageFlagBits shaderStage)
706 {
707         switch (shaderStage)
708         {
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;
715                 default:
716                         DE_FATAL("Unknown shader stage");
717                         return glu::SHADERTYPE_LAST;
718         }
719 }
720
721 VkShaderStageFlagBits getVkShaderStage (glu::ShaderType shaderType)
722 {
723         static const VkShaderStageFlagBits s_shaderStages[] =
724         {
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,
737         };
738
739         return de::getSizedArrayElement<glu::SHADERTYPE_LAST>(s_shaderStages, shaderType);
740 }
741
742 // Baseline version, to be used for shaders which don't specify a version
743 vk::SpirvVersion getBaselineSpirvVersion (const deUint32 /* vulkanVersion */)
744 {
745         return vk::SPIRV_VERSION_1_0;
746 }
747
748 // Max supported versions for each Vulkan version, without requiring a Vulkan extension.
749 vk::SpirvVersion getMaxSpirvVersionForVulkan (const deUint32 vulkanVersion)
750 {
751         vk::SpirvVersion        result                  = vk::SPIRV_VERSION_LAST;
752
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;
758
759         DE_ASSERT(result < vk::SPIRV_VERSION_LAST);
760
761         return result;
762 }
763
764 vk::SpirvVersion getMaxSpirvVersionForAsm (const deUint32 vulkanVersion)
765 {
766         return getMaxSpirvVersionForVulkan(vulkanVersion);
767 }
768
769 vk::SpirvVersion getMaxSpirvVersionForGlsl (const deUint32 vulkanVersion)
770 {
771         return getMaxSpirvVersionForVulkan(vulkanVersion);
772 }
773
774 SpirvVersion extractSpirvVersion (const ProgramBinary& binary)
775 {
776         DE_STATIC_ASSERT(SPIRV_VERSION_1_4 + 1 == SPIRV_VERSION_LAST);
777
778         if (binary.getFormat() != PROGRAM_FORMAT_SPIRV)
779                 TCU_THROW(InternalError, "Binary is not in SPIR-V format");
780
781         if (!isSaneSpirVBinary(binary) || binary.getSize() < sizeof(SpirvBinaryHeader))
782                 TCU_THROW(InternalError, "Invalid SPIR-V header format");
783
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()
791                                                                                                                 ? header->version
792                                                                                                                 : deReverseBytes32(header->version);
793         SpirvVersion                            result                                  = SPIRV_VERSION_LAST;
794
795         switch (spirvVersion)
796         {
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");
803         }
804
805         return result;
806 }
807
808 std::string getSpirvVersionName (const SpirvVersion spirvVersion)
809 {
810         DE_STATIC_ASSERT(SPIRV_VERSION_1_4 + 1 == SPIRV_VERSION_LAST);
811         DE_ASSERT(spirvVersion < SPIRV_VERSION_LAST);
812
813         std::string result;
814
815         switch (spirvVersion)
816         {
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";
823         }
824
825         return result;
826 }
827
828 SpirvVersion& operator++(SpirvVersion& spirvVersion)
829 {
830         if (spirvVersion == SPIRV_VERSION_LAST)
831                 spirvVersion = SPIRV_VERSION_1_0;
832         else
833                 spirvVersion = static_cast<SpirvVersion>(static_cast<deUint32>(spirvVersion) + 1);
834
835         return spirvVersion;
836 }
837
838 } // vk