Update Vulkan Headers
[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         , m_used        (false)
64 {
65 }
66
67 // Utils
68
69 namespace
70 {
71
72 bool isNativeSpirVBinaryEndianness (void)
73 {
74 #if (DE_ENDIANNESS == SPIRV_BINARY_ENDIANNESS)
75         return true;
76 #else
77         return false;
78 #endif
79 }
80
81 bool isSaneSpirVBinary (const ProgramBinary& binary)
82 {
83         const deUint32  spirvMagicWord  = 0x07230203;
84         const deUint32  spirvMagicBytes = isNativeSpirVBinaryEndianness()
85                                                                         ? spirvMagicWord
86                                                                         : deReverseBytes32(spirvMagicWord);
87
88         DE_ASSERT(binary.getFormat() == PROGRAM_FORMAT_SPIRV);
89
90         if (binary.getSize() % sizeof(deUint32) != 0)
91                 return false;
92
93         if (binary.getSize() < sizeof(deUint32))
94                 return false;
95
96         if (*(const deUint32*)binary.getBinary() != spirvMagicBytes)
97                 return false;
98
99         return true;
100 }
101
102 void optimizeCompiledBinary (vector<deUint32>& binary, int optimizationRecipe, const SpirvVersion spirvVersion)
103 {
104         spv_target_env targetEnv = SPV_ENV_VULKAN_1_0;
105
106         // Map SpirvVersion with spv_target_env:
107         switch (spirvVersion)
108         {
109                 case SPIRV_VERSION_1_0: targetEnv = SPV_ENV_VULKAN_1_0; break;
110                 case SPIRV_VERSION_1_1:
111                 case SPIRV_VERSION_1_2:
112                 case SPIRV_VERSION_1_3: targetEnv = SPV_ENV_VULKAN_1_1; break;
113                 case SPIRV_VERSION_1_4: targetEnv = SPV_ENV_VULKAN_1_1_SPIRV_1_4;       break;
114                 case SPIRV_VERSION_1_5: targetEnv = SPV_ENV_VULKAN_1_2; break;
115                 default:
116                         TCU_THROW(InternalError, "Unexpected SPIR-V version requested");
117         }
118
119         spvtools::Optimizer optimizer(targetEnv);
120
121         switch (optimizationRecipe)
122         {
123                 case 1:
124                         optimizer.RegisterPerformancePasses();
125                         break;
126                 case 2:
127                         optimizer.RegisterSizePasses();
128                         break;
129                 default:
130                         TCU_THROW(InternalError, "Unknown optimization recipe requested");
131         }
132
133         spvtools::OptimizerOptions optimizer_options;
134         optimizer_options.set_run_validator(false);
135         const bool ok = optimizer.Run(binary.data(), binary.size(), &binary, optimizer_options);
136
137         if (!ok)
138                 TCU_THROW(InternalError, "Optimizer call failed");
139 }
140
141 ProgramBinary* createProgramBinaryFromSpirV (const vector<deUint32>& binary)
142 {
143         DE_ASSERT(!binary.empty());
144
145         if (isNativeSpirVBinaryEndianness())
146                 return new ProgramBinary(PROGRAM_FORMAT_SPIRV, binary.size()*sizeof(deUint32), (const deUint8*)&binary[0]);
147         else
148                 TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
149 }
150
151 } // anonymous
152
153 void validateCompiledBinary(const vector<deUint32>& binary, glu::ShaderProgramInfo* buildInfo, const SpirvValidatorOptions& options)
154 {
155         std::ostringstream validationLog;
156
157         if (!validateSpirV(binary.size(), &binary[0], &validationLog, options))
158         {
159                 buildInfo->program.linkOk        = false;
160                 buildInfo->program.infoLog      += "\n" + validationLog.str();
161
162                 TCU_THROW(InternalError, "Validation failed for compiled SPIR-V binary");
163         }
164 }
165
166 void validateCompiledBinary(const vector<deUint32>& binary, SpirVProgramInfo* buildInfo, const SpirvValidatorOptions& options)
167 {
168         std::ostringstream validationLog;
169
170         if (!validateSpirV(binary.size(), &binary[0], &validationLog, options))
171         {
172                 buildInfo->compileOk = false;
173                 buildInfo->infoLog += "\n" + validationLog.str();
174
175                 TCU_THROW(InternalError, "Validation failed for compiled SPIR-V binary");
176         }
177 }
178
179 de::Mutex                                                       cacheFileMutex;
180 map<deUint32, vector<deUint32> >        cacheFileIndex;
181 bool                                                            cacheFileFirstRun = true;
182
183 void shaderCacheFirstRunCheck (const char* shaderCacheFile, bool truncate)
184 {
185         cacheFileMutex.lock();
186         if (cacheFileFirstRun)
187         {
188                 cacheFileFirstRun = false;
189                 if (truncate)
190                 {
191                         // Open file with "w" access to truncate it
192                         FILE* f = fopen(shaderCacheFile, "wb");
193                         if (f)
194                                 fclose(f);
195                 }
196                 else
197                 {
198                         // Parse chunked shader cache file for hashes and offsets
199                         FILE* file = fopen(shaderCacheFile, "rb");
200                         int count = 0;
201                         if (file)
202                         {
203                                 deUint32 chunksize      = 0;
204                                 deUint32 hash           = 0;
205                                 deUint32 offset         = 0;
206                                 bool ok                         = true;
207                                 while (ok)
208                                 {
209                                         offset = (deUint32)ftell(file);
210                                         if (ok) ok = fread(&chunksize, 1, 4, file)                              == 4;
211                                         if (ok) ok = fread(&hash, 1, 4, file)                                   == 4;
212                                         if (ok) cacheFileIndex[hash].push_back(offset);
213                                         if (ok) ok = fseek(file, offset + chunksize, SEEK_SET)  == 0;
214                                         count++;
215                                 }
216                                 fclose(file);
217                         }
218                 }
219         }
220         cacheFileMutex.unlock();
221 }
222
223 std::string intToString (deUint32 integer)
224 {
225         std::stringstream temp_sstream;
226
227         temp_sstream << integer;
228
229         return temp_sstream.str();
230 }
231
232 // 32-bit FNV-1 hash
233 deUint32 shadercacheHash (const char* str)
234 {
235         deUint32 hash = 0x811c9dc5;
236         deUint32 c;
237         while ((c = (deUint32)*str++) != 0)
238         {
239                 hash *= 16777619;
240                 hash ^= c;
241         }
242         return hash;
243 }
244
245 vk::ProgramBinary* shadercacheLoad (const std::string& shaderstring, const char* shaderCacheFilename)
246 {
247         deUint32                hash            = shadercacheHash(shaderstring.c_str());
248         deInt32                 format;
249         deInt32                 length;
250         deInt32                 sourcelength;
251         deUint32                i;
252         deUint32                temp;
253         deUint8*                bin                     = 0;
254         char*                   source          = 0;
255         deBool                  ok                      = true;
256         deBool                  diff            = true;
257         cacheFileMutex.lock();
258
259         if (cacheFileIndex.count(hash) == 0)
260         {
261                 cacheFileMutex.unlock();
262                 return 0;
263         }
264         FILE*                   file            = fopen(shaderCacheFilename, "rb");
265         ok                              = file                                                                                  != 0;
266
267         for (i = 0; i < cacheFileIndex[hash].size(); i++)
268         {
269                 if (ok) ok = fseek(file, cacheFileIndex[hash][i], SEEK_SET)     == 0;
270                 if (ok) ok = fread(&temp, 1, 4, file)                                           == 4; // Chunk size (skip)
271                 if (ok) ok = fread(&temp, 1, 4, file)                                           == 4; // Stored hash
272                 if (ok) ok = temp                                                                                       == hash; // Double check
273                 if (ok) ok = fread(&format, 1, 4, file)                                         == 4;
274                 if (ok) ok = fread(&length, 1, 4, file)                                         == 4;
275                 if (ok) ok = length                                                                                     > 0; // sanity check
276                 if (ok) bin = new deUint8[length];
277                 if (ok) ok = fread(bin, 1, length, file)                                        == (size_t)length;
278                 if (ok) ok = fread(&sourcelength, 1, 4, file)                           == 4;
279                 if (ok && sourcelength > 0)
280                 {
281                         source = new char[sourcelength + 1];
282                         ok = fread(source, 1, sourcelength, file)                               == (size_t)sourcelength;
283                         source[sourcelength] = 0;
284                         diff = shaderstring != std::string(source);
285                 }
286                 if (!ok || diff)
287                 {
288                         // Mismatch, but may still exist in cache if there were hash collisions
289                         delete[] source;
290                         delete[] bin;
291                 }
292                 else
293                 {
294                         delete[] source;
295                         if (file) fclose(file);
296                         cacheFileMutex.unlock();
297                         vk::ProgramBinary* res = new vk::ProgramBinary((vk::ProgramFormat)format, length, bin);
298                         delete[] bin;
299                         return res;
300                 }
301         }
302         if (file) fclose(file);
303         cacheFileMutex.unlock();
304         return 0;
305 }
306
307 void shadercacheSave (const vk::ProgramBinary* binary, const std::string& shaderstring, const char* shaderCacheFilename)
308 {
309         if (binary == 0)
310                 return;
311         deUint32                        hash            = shadercacheHash(shaderstring.c_str());
312         deInt32                         format          = binary->getFormat();
313         deUint32                        length          = (deUint32)binary->getSize();
314         deUint32                        chunksize;
315         deUint32                        offset;
316         const deUint8*          bin                     = binary->getBinary();
317         const de::FilePath      filePath        (shaderCacheFilename);
318
319         cacheFileMutex.lock();
320
321         if (cacheFileIndex[hash].size())
322         {
323                 FILE*                   file            = fopen(shaderCacheFilename, "rb");
324                 deBool                  ok                      = (file != 0);
325                 deBool                  diff            = DE_TRUE;
326                 deInt32                 sourcelength;
327                 deUint32                i;
328                 deUint32                temp;
329
330                 for (i = 0; i < cacheFileIndex[hash].size(); i++)
331                 {
332                         deUint32        cachedLength    = 0;
333
334                         if (ok) ok = fseek(file, cacheFileIndex[hash][i], SEEK_SET)     == 0;
335                         if (ok) ok = fread(&temp, 1, 4, file)                                           == 4; // Chunk size (skip)
336                         if (ok) ok = fread(&temp, 1, 4, file)                                           == 4; // Stored hash
337                         if (ok) ok = temp                                                                                       == hash; // Double check
338                         if (ok) ok = fread(&temp, 1, 4, file)                                           == 4;
339                         if (ok) ok = fread(&cachedLength, 1, 4, file)                           == 4;
340                         if (ok) ok = cachedLength                                                                       > 0; // sanity check
341                         if (ok) fseek(file, cachedLength, SEEK_CUR); // skip binary
342                         if (ok) ok = fread(&sourcelength, 1, 4, file)                           == 4;
343
344                         if (ok && sourcelength > 0)
345                         {
346                                 char* source;
347                                 source  = new char[sourcelength + 1];
348                                 ok              = fread(source, 1, sourcelength, file)                  == (size_t)sourcelength;
349                                 source[sourcelength] = 0;
350                                 diff    = shaderstring != std::string(source);
351                                 delete[] source;
352                         }
353
354                         if (ok && !diff)
355                         {
356                                 // Already in cache (written by another thread, probably)
357                                 fclose(file);
358                                 cacheFileMutex.unlock();
359                                 return;
360                         }
361                 }
362                 fclose(file);
363         }
364
365         if (!de::FilePath(filePath.getDirName()).exists())
366                 de::createDirectoryAndParents(filePath.getDirName().c_str());
367
368         FILE*                           file            = fopen(shaderCacheFilename, "ab");
369         if (!file)
370         {
371                 cacheFileMutex.unlock();
372                 return;
373         }
374         // Append mode starts writing from the end of the file,
375         // but unless we do a seek, ftell returns 0.
376         fseek(file, 0, SEEK_END);
377         offset          = (deUint32)ftell(file);
378         chunksize       = 4 + 4 + 4 + 4 + length + 4 + (deUint32)shaderstring.length();
379         fwrite(&chunksize, 1, 4, file);
380         fwrite(&hash, 1, 4, file);
381         fwrite(&format, 1, 4, file);
382         fwrite(&length, 1, 4, file);
383         fwrite(bin, 1, length, file);
384         length = (deUint32)shaderstring.length();
385         fwrite(&length, 1, 4, file);
386         fwrite(shaderstring.c_str(), 1, length, file);
387         fclose(file);
388         cacheFileIndex[hash].push_back(offset);
389
390         cacheFileMutex.unlock();
391 }
392
393 // Insert any information that may affect compilation into the shader string.
394 void getCompileEnvironment (std::string& shaderstring)
395 {
396         shaderstring += "GLSL:";
397         shaderstring += qpGetReleaseGlslName();
398         shaderstring += "\nSpir-v Tools:";
399         shaderstring += qpGetReleaseSpirvToolsName();
400         shaderstring += "\nSpir-v Headers:";
401         shaderstring += qpGetReleaseSpirvHeadersName();
402         shaderstring += "\n";
403 }
404
405 // Insert compilation options into the shader string.
406 void getBuildOptions (std::string& shaderstring, const ShaderBuildOptions& buildOptions, int optimizationRecipe)
407 {
408         shaderstring += "Target Spir-V ";
409         shaderstring += getSpirvVersionName(buildOptions.targetVersion);
410         shaderstring += "\n";
411         if (buildOptions.flags & ShaderBuildOptions::FLAG_ALLOW_RELAXED_OFFSETS)
412                 shaderstring += "Flag:Allow relaxed offsets\n";
413         if (buildOptions.flags & ShaderBuildOptions::FLAG_USE_STORAGE_BUFFER_STORAGE_CLASS)
414                 shaderstring += "Flag:Use storage buffer storage class\n";
415         if (optimizationRecipe != 0)
416         {
417                 shaderstring += "Optimization recipe ";
418                 shaderstring += de::toString(optimizationRecipe);
419                 shaderstring += "\n";
420         }
421 }
422
423 ProgramBinary* buildProgram (const GlslSource& program, glu::ShaderProgramInfo* buildInfo, const tcu::CommandLine& commandLine)
424 {
425         const SpirvVersion      spirvVersion            = program.buildOptions.targetVersion;
426         const bool                      validateBinary          = VALIDATE_BINARIES;
427         vector<deUint32>        binary;
428         std::string                     cachekey;
429         std::string                     shaderstring;
430         vk::ProgramBinary*      res                                     = 0;
431         const int                       optimizationRecipe      = commandLine.getOptimizationRecipe();
432
433         if (commandLine.isShadercacheEnabled())
434         {
435                 shaderCacheFirstRunCheck(commandLine.getShaderCacheFilename(), commandLine.isShaderCacheTruncateEnabled());
436                 getCompileEnvironment(cachekey);
437                 getBuildOptions(cachekey, program.buildOptions, optimizationRecipe);
438
439                 for (int i = 0; i < glu::SHADERTYPE_LAST; i++)
440                 {
441                         if (!program.sources[i].empty())
442                         {
443                                 cachekey += glu::getShaderTypeName((glu::ShaderType)i);
444
445                                 for (std::vector<std::string>::const_iterator it = program.sources[i].begin(); it != program.sources[i].end(); ++it)
446                                         shaderstring += *it;
447                         }
448                 }
449
450                 cachekey = cachekey + shaderstring;
451
452                 res = shadercacheLoad(cachekey, commandLine.getShaderCacheFilename());
453
454                 if (res)
455                 {
456                         buildInfo->program.infoLog              = "Loaded from cache";
457                         buildInfo->program.linkOk               = true;
458                         buildInfo->program.linkTimeUs   = 0;
459
460                         for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++)
461                         {
462                                 if (!program.sources[shaderType].empty())
463                                 {
464                                         glu::ShaderInfo shaderBuildInfo;
465
466                                         shaderBuildInfo.type                    = (glu::ShaderType)shaderType;
467                                         shaderBuildInfo.source                  = shaderstring;
468                                         shaderBuildInfo.compileTimeUs   = 0;
469                                         shaderBuildInfo.compileOk               = true;
470
471                                         buildInfo->shaders.push_back(shaderBuildInfo);
472                                 }
473                         }
474                 }
475         }
476
477         if (!res)
478         {
479                 {
480                         vector<deUint32> nonStrippedBinary;
481
482                         if (!compileGlslToSpirV(program, &nonStrippedBinary, buildInfo))
483                                 TCU_THROW(InternalError, "Compiling GLSL to SPIR-V failed");
484
485                         TCU_CHECK_INTERNAL(!nonStrippedBinary.empty());
486                         stripSpirVDebugInfo(nonStrippedBinary.size(), &nonStrippedBinary[0], &binary);
487                         TCU_CHECK_INTERNAL(!binary.empty());
488                 }
489
490                 if (optimizationRecipe != 0)
491                 {
492                         validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
493                         optimizeCompiledBinary(binary, optimizationRecipe, spirvVersion);
494                 }
495
496                 if (validateBinary)
497                 {
498                         validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
499                 }
500
501                 res = createProgramBinaryFromSpirV(binary);
502                 if (commandLine.isShadercacheEnabled())
503                         shadercacheSave(res, cachekey, commandLine.getShaderCacheFilename());
504         }
505         return res;
506 }
507
508 ProgramBinary* buildProgram (const HlslSource& program, glu::ShaderProgramInfo* buildInfo, const tcu::CommandLine& commandLine)
509 {
510         const SpirvVersion      spirvVersion            = program.buildOptions.targetVersion;
511         const bool                      validateBinary          = VALIDATE_BINARIES;
512         vector<deUint32>        binary;
513         std::string                     cachekey;
514         std::string                     shaderstring;
515         vk::ProgramBinary*      res                                     = 0;
516         const int                       optimizationRecipe      = commandLine.getOptimizationRecipe();
517
518         if (commandLine.isShadercacheEnabled())
519         {
520                 shaderCacheFirstRunCheck(commandLine.getShaderCacheFilename(), commandLine.isShaderCacheTruncateEnabled());
521                 getCompileEnvironment(cachekey);
522                 getBuildOptions(cachekey, program.buildOptions, optimizationRecipe);
523
524                 for (int i = 0; i < glu::SHADERTYPE_LAST; i++)
525                 {
526                         if (!program.sources[i].empty())
527                         {
528                                 cachekey += glu::getShaderTypeName((glu::ShaderType)i);
529
530                                 for (std::vector<std::string>::const_iterator it = program.sources[i].begin(); it != program.sources[i].end(); ++it)
531                                         shaderstring += *it;
532                         }
533                 }
534
535                 cachekey = cachekey + shaderstring;
536
537                 res = shadercacheLoad(cachekey, commandLine.getShaderCacheFilename());
538
539                 if (res)
540                 {
541                         buildInfo->program.infoLog              = "Loaded from cache";
542                         buildInfo->program.linkOk               = true;
543                         buildInfo->program.linkTimeUs   = 0;
544
545                         for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++)
546                         {
547                                 if (!program.sources[shaderType].empty())
548                                 {
549                                         glu::ShaderInfo shaderBuildInfo;
550
551                                         shaderBuildInfo.type                    = (glu::ShaderType)shaderType;
552                                         shaderBuildInfo.source                  = shaderstring;
553                                         shaderBuildInfo.compileTimeUs   = 0;
554                                         shaderBuildInfo.compileOk               = true;
555
556                                         buildInfo->shaders.push_back(shaderBuildInfo);
557                                 }
558                         }
559                 }
560         }
561
562         if (!res)
563         {
564                 {
565                         vector<deUint32> nonStrippedBinary;
566
567                         if (!compileHlslToSpirV(program, &nonStrippedBinary, buildInfo))
568                                 TCU_THROW(InternalError, "Compiling HLSL to SPIR-V failed");
569
570                         TCU_CHECK_INTERNAL(!nonStrippedBinary.empty());
571                         stripSpirVDebugInfo(nonStrippedBinary.size(), &nonStrippedBinary[0], &binary);
572                         TCU_CHECK_INTERNAL(!binary.empty());
573                 }
574
575                 if (optimizationRecipe != 0)
576                 {
577                         validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
578                         optimizeCompiledBinary(binary, optimizationRecipe, spirvVersion);
579                 }
580
581                 if (validateBinary)
582                 {
583                         validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
584                 }
585
586                 res = createProgramBinaryFromSpirV(binary);
587                 if (commandLine.isShadercacheEnabled())
588                         shadercacheSave(res, cachekey, commandLine.getShaderCacheFilename());
589         }
590         return res;
591 }
592
593 ProgramBinary* assembleProgram (const SpirVAsmSource& program, SpirVProgramInfo* buildInfo, const tcu::CommandLine& commandLine)
594 {
595         const SpirvVersion      spirvVersion            = program.buildOptions.targetVersion;
596         const bool                      validateBinary          = VALIDATE_BINARIES;
597         vector<deUint32>        binary;
598         vk::ProgramBinary*      res                                     = 0;
599         std::string                     cachekey;
600         const int                       optimizationRecipe      = commandLine.isSpirvOptimizationEnabled() ? commandLine.getOptimizationRecipe() : 0;
601
602         if (commandLine.isShadercacheEnabled())
603         {
604                 shaderCacheFirstRunCheck(commandLine.getShaderCacheFilename(), commandLine.isShaderCacheTruncateEnabled());
605                 getCompileEnvironment(cachekey);
606                 cachekey += "Target Spir-V ";
607                 cachekey += getSpirvVersionName(spirvVersion);
608                 cachekey += "\n";
609                 if (optimizationRecipe != 0)
610                 {
611                         cachekey += "Optimization recipe ";
612                         cachekey += de::toString(optimizationRecipe);
613                         cachekey += "\n";
614                 }
615
616                 cachekey += program.source;
617
618                 res = shadercacheLoad(cachekey, commandLine.getShaderCacheFilename());
619
620                 if (res)
621                 {
622                         buildInfo->source                       = program.source;
623                         buildInfo->compileOk            = true;
624                         buildInfo->compileTimeUs        = 0;
625                         buildInfo->infoLog                      = "Loaded from cache";
626                 }
627         }
628
629         if (!res)
630         {
631
632                 if (!assembleSpirV(&program, &binary, buildInfo, spirvVersion))
633                         TCU_THROW(InternalError, "Failed to assemble SPIR-V");
634
635                 if (optimizationRecipe != 0)
636                 {
637                         validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
638                         optimizeCompiledBinary(binary, optimizationRecipe, spirvVersion);
639                 }
640
641                 if (validateBinary)
642                 {
643                         validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
644                 }
645
646                 res = createProgramBinaryFromSpirV(binary);
647                 if (commandLine.isShadercacheEnabled())
648                         shadercacheSave(res, cachekey, commandLine.getShaderCacheFilename());
649         }
650         return res;
651 }
652
653 void disassembleProgram (const ProgramBinary& program, std::ostream* dst)
654 {
655         if (program.getFormat() == PROGRAM_FORMAT_SPIRV)
656         {
657                 TCU_CHECK_INTERNAL(isSaneSpirVBinary(program));
658
659                 if (isNativeSpirVBinaryEndianness())
660                         disassembleSpirV(program.getSize()/sizeof(deUint32), (const deUint32*)program.getBinary(), dst,
661                                                          extractSpirvVersion(program));
662                 else
663                         TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
664         }
665         else
666                 TCU_THROW(NotSupportedError, "Unsupported program format");
667 }
668
669 bool validateProgram (const ProgramBinary& program, std::ostream* dst, const SpirvValidatorOptions& options)
670 {
671         if (program.getFormat() == PROGRAM_FORMAT_SPIRV)
672         {
673                 if (!isSaneSpirVBinary(program))
674                 {
675                         *dst << "Binary doesn't look like SPIR-V at all";
676                         return false;
677                 }
678
679                 if (isNativeSpirVBinaryEndianness())
680                         return validateSpirV(program.getSize()/sizeof(deUint32), (const deUint32*)program.getBinary(), dst, options);
681                 else
682                         TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
683         }
684         else
685                 TCU_THROW(NotSupportedError, "Unsupported program format");
686 }
687
688 Move<VkShaderModule> createShaderModule (const DeviceInterface& deviceInterface, VkDevice device, const ProgramBinary& binary, VkShaderModuleCreateFlags flags)
689 {
690         if (binary.getFormat() == PROGRAM_FORMAT_SPIRV)
691         {
692                 const struct VkShaderModuleCreateInfo           shaderModuleInfo        =
693                 {
694                         VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
695                         DE_NULL,
696                         flags,
697                         (deUintptr)binary.getSize(),
698                         (const deUint32*)binary.getBinary(),
699                 };
700
701                 binary.setUsed();
702
703                 return createShaderModule(deviceInterface, device, &shaderModuleInfo);
704         }
705         else
706                 TCU_THROW(NotSupportedError, "Unsupported program format");
707 }
708
709 glu::ShaderType getGluShaderType (VkShaderStageFlagBits shaderStage)
710 {
711         switch (shaderStage)
712         {
713                 case VK_SHADER_STAGE_VERTEX_BIT:                                        return glu::SHADERTYPE_VERTEX;
714                 case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT:          return glu::SHADERTYPE_TESSELLATION_CONTROL;
715                 case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT:       return glu::SHADERTYPE_TESSELLATION_EVALUATION;
716                 case VK_SHADER_STAGE_GEOMETRY_BIT:                                      return glu::SHADERTYPE_GEOMETRY;
717                 case VK_SHADER_STAGE_FRAGMENT_BIT:                                      return glu::SHADERTYPE_FRAGMENT;
718                 case VK_SHADER_STAGE_COMPUTE_BIT:                                       return glu::SHADERTYPE_COMPUTE;
719                 default:
720                         DE_FATAL("Unknown shader stage");
721                         return glu::SHADERTYPE_LAST;
722         }
723 }
724
725 VkShaderStageFlagBits getVkShaderStage (glu::ShaderType shaderType)
726 {
727         static const VkShaderStageFlagBits s_shaderStages[] =
728         {
729                 VK_SHADER_STAGE_VERTEX_BIT,
730                 VK_SHADER_STAGE_FRAGMENT_BIT,
731                 VK_SHADER_STAGE_GEOMETRY_BIT,
732                 VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
733                 VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
734                 VK_SHADER_STAGE_COMPUTE_BIT,
735                 VK_SHADER_STAGE_RAYGEN_BIT_NV,
736                 VK_SHADER_STAGE_ANY_HIT_BIT_NV,
737                 VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV,
738                 VK_SHADER_STAGE_MISS_BIT_NV,
739                 VK_SHADER_STAGE_INTERSECTION_BIT_NV,
740                 VK_SHADER_STAGE_CALLABLE_BIT_NV,
741         };
742
743         return de::getSizedArrayElement<glu::SHADERTYPE_LAST>(s_shaderStages, shaderType);
744 }
745
746 // Baseline version, to be used for shaders which don't specify a version
747 vk::SpirvVersion getBaselineSpirvVersion (const deUint32 /* vulkanVersion */)
748 {
749         return vk::SPIRV_VERSION_1_0;
750 }
751
752 // Max supported versions for each Vulkan version, without requiring a Vulkan extension.
753 vk::SpirvVersion getMaxSpirvVersionForVulkan (const deUint32 vulkanVersion)
754 {
755         vk::SpirvVersion        result                  = vk::SPIRV_VERSION_LAST;
756
757         deUint32 vulkanVersionMajorMinor = VK_MAKE_VERSION(VK_API_VERSION_MAJOR(vulkanVersion), VK_API_VERSION_MINOR(vulkanVersion), 0);
758         if (vulkanVersionMajorMinor == VK_API_VERSION_1_0)
759                 result = vk::SPIRV_VERSION_1_0;
760         else if (vulkanVersionMajorMinor == VK_API_VERSION_1_1)
761                 result = vk::SPIRV_VERSION_1_3;
762         else if (vulkanVersionMajorMinor >= VK_API_VERSION_1_2)
763                 result = vk::SPIRV_VERSION_1_5;
764
765         DE_ASSERT(result < vk::SPIRV_VERSION_LAST);
766
767         return result;
768 }
769
770 vk::SpirvVersion getMaxSpirvVersionForAsm (const deUint32 vulkanVersion)
771 {
772         return getMaxSpirvVersionForVulkan(vulkanVersion);
773 }
774
775 vk::SpirvVersion getMaxSpirvVersionForGlsl (const deUint32 vulkanVersion)
776 {
777         return getMaxSpirvVersionForVulkan(vulkanVersion);
778 }
779
780 SpirvVersion extractSpirvVersion (const ProgramBinary& binary)
781 {
782         DE_STATIC_ASSERT(SPIRV_VERSION_1_5 + 1 == SPIRV_VERSION_LAST);
783
784         if (binary.getFormat() != PROGRAM_FORMAT_SPIRV)
785                 TCU_THROW(InternalError, "Binary is not in SPIR-V format");
786
787         if (!isSaneSpirVBinary(binary) || binary.getSize() < sizeof(SpirvBinaryHeader))
788                 TCU_THROW(InternalError, "Invalid SPIR-V header format");
789
790         const deUint32                          spirvBinaryVersion10    = 0x00010000;
791         const deUint32                          spirvBinaryVersion11    = 0x00010100;
792         const deUint32                          spirvBinaryVersion12    = 0x00010200;
793         const deUint32                          spirvBinaryVersion13    = 0x00010300;
794         const deUint32                          spirvBinaryVersion14    = 0x00010400;
795         const deUint32                          spirvBinaryVersion15    = 0x00010500;
796         const SpirvBinaryHeader*        header                                  = reinterpret_cast<const SpirvBinaryHeader*>(binary.getBinary());
797         const deUint32                          spirvVersion                    = isNativeSpirVBinaryEndianness()
798                                                                                                                 ? header->version
799                                                                                                                 : deReverseBytes32(header->version);
800         SpirvVersion                            result                                  = SPIRV_VERSION_LAST;
801
802         switch (spirvVersion)
803         {
804                 case spirvBinaryVersion10:      result = SPIRV_VERSION_1_0; break; //!< SPIR-V 1.0
805                 case spirvBinaryVersion11:      result = SPIRV_VERSION_1_1; break; //!< SPIR-V 1.1
806                 case spirvBinaryVersion12:      result = SPIRV_VERSION_1_2; break; //!< SPIR-V 1.2
807                 case spirvBinaryVersion13:      result = SPIRV_VERSION_1_3; break; //!< SPIR-V 1.3
808                 case spirvBinaryVersion14:      result = SPIRV_VERSION_1_4; break; //!< SPIR-V 1.4
809                 case spirvBinaryVersion15:      result = SPIRV_VERSION_1_5; break; //!< SPIR-V 1.5
810                 default:                                        TCU_THROW(InternalError, "Unknown SPIR-V version detected in binary");
811         }
812
813         return result;
814 }
815
816 std::string getSpirvVersionName (const SpirvVersion spirvVersion)
817 {
818         DE_STATIC_ASSERT(SPIRV_VERSION_1_5 + 1 == SPIRV_VERSION_LAST);
819         DE_ASSERT(spirvVersion < SPIRV_VERSION_LAST);
820
821         std::string result;
822
823         switch (spirvVersion)
824         {
825                 case SPIRV_VERSION_1_0: result = "1.0"; break; //!< SPIR-V 1.0
826                 case SPIRV_VERSION_1_1: result = "1.1"; break; //!< SPIR-V 1.1
827                 case SPIRV_VERSION_1_2: result = "1.2"; break; //!< SPIR-V 1.2
828                 case SPIRV_VERSION_1_3: result = "1.3"; break; //!< SPIR-V 1.3
829                 case SPIRV_VERSION_1_4: result = "1.4"; break; //!< SPIR-V 1.4
830                 case SPIRV_VERSION_1_5: result = "1.5"; break; //!< SPIR-V 1.5
831                 default:                                result = "Unknown";
832         }
833
834         return result;
835 }
836
837 SpirvVersion& operator++(SpirvVersion& spirvVersion)
838 {
839         if (spirvVersion == SPIRV_VERSION_LAST)
840                 spirvVersion = SPIRV_VERSION_1_0;
841         else
842                 spirvVersion = static_cast<SpirvVersion>(static_cast<deUint32>(spirvVersion) + 1);
843
844         return spirvVersion;
845 }
846
847 } // vk