1 /*-------------------------------------------------------------------------
2 * Vulkan CTS Framework
3 * --------------------
4 *
5 * Copyright (c) 2015 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 #if defined(DEQP_HAVE_SPIRV_TOOLS)
25 #include "spirv-tools/optimizer.hpp"
26 #endif
27
28 #include "qpInfo.h"
29
30 #include "vkPrograms.hpp"
31 #include "vkShaderToSpirV.hpp"
32 #include "vkSpirVAsm.hpp"
33 #include "vkRefUtil.hpp"
34
35 #include "deMutex.hpp"
36 #include "deFilePath.hpp"
37 #include "deArrayUtil.hpp"
38 #include "deMemory.h"
39 #include "deInt32.h"
40
41 #include "tcuCommandLine.hpp"
42
43 #include <map>
44
45 namespace vk
46 {
47
48 using std::string;
49 using std::vector;
50 using std::map;
51
52 #if defined(DE_DEBUG) && defined(DEQP_HAVE_SPIRV_TOOLS)
53 # define VALIDATE_BINARIES true
54 #else
55 # define VALIDATE_BINARIES false
56 #endif
57
58 #define SPIRV_BINARY_ENDIANNESS DE_LITTLE_ENDIAN
59
60 // ProgramBinary
61
ProgramBinary(ProgramFormat format,size_t binarySize,const deUint8 * binary)62 ProgramBinary::ProgramBinary (ProgramFormat format, size_t binarySize, const deUint8* binary)
63 : m_format (format)
64 , m_binary (binary, binary+binarySize)
65 {
66 }
67
68 // Utils
69
70 namespace
71 {
72
isNativeSpirVBinaryEndianness(void)73 bool isNativeSpirVBinaryEndianness (void)
74 {
75 #if (DE_ENDIANNESS == SPIRV_BINARY_ENDIANNESS)
76 return true;
77 #else
78 return false;
79 #endif
80 }
81
isSaneSpirVBinary(const ProgramBinary & binary)82 bool isSaneSpirVBinary (const ProgramBinary& binary)
83 {
84 const deUint32 spirvMagicWord = 0x07230203;
85 const deUint32 spirvMagicBytes = isNativeSpirVBinaryEndianness()
86 ? spirvMagicWord
87 : deReverseBytes32(spirvMagicWord);
88
89 DE_ASSERT(binary.getFormat() == PROGRAM_FORMAT_SPIRV);
90
91 if (binary.getSize() % sizeof(deUint32) != 0)
92 return false;
93
94 if (binary.getSize() < sizeof(deUint32))
95 return false;
96
97 if (*(const deUint32*)binary.getBinary() != spirvMagicBytes)
98 return false;
99
100 return true;
101 }
102
103 #if defined(DEQP_HAVE_SPIRV_TOOLS)
104
optimizeCompiledBinary(vector<deUint32> & binary,int optimizationRecipe,const SpirvVersion spirvVersion)105 void optimizeCompiledBinary (vector<deUint32>& binary, int optimizationRecipe, const SpirvVersion spirvVersion)
106 {
107 spv_target_env targetEnv = SPV_ENV_VULKAN_1_0;
108
109 // Map SpirvVersion with spv_target_env:
110 switch (spirvVersion)
111 {
112 case SPIRV_VERSION_1_0: targetEnv = SPV_ENV_VULKAN_1_0; break;
113 case SPIRV_VERSION_1_1:
114 case SPIRV_VERSION_1_2:
115 case SPIRV_VERSION_1_3: targetEnv = SPV_ENV_VULKAN_1_1; break;
116 default:
117 TCU_THROW(InternalError, "Unexpected SPIR-V version requested");
118 }
119
120 spvtools::Optimizer optimizer(targetEnv);
121
122 switch (optimizationRecipe)
123 {
124 case 1:
125 // The example recipe from:
126 // https://www.lunarg.com/wp-content/uploads/2017/08/SPIR-V-Shader-Size-Reduction-Using-spirv-opt_v1.0.pdf
127 optimizer.RegisterPass(spvtools::CreateInlineExhaustivePass()); // --inline-entry-points-exhaustive
128 optimizer.RegisterPass(spvtools::CreateLocalAccessChainConvertPass()); // --convert-local-access-chains
129 optimizer.RegisterPass(spvtools::CreateLocalSingleBlockLoadStoreElimPass());// --eliminate-local-single-block
130 optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass()); // --eliminate-local-single-store
131 optimizer.RegisterPass(spvtools::CreateInsertExtractElimPass()); // --eliminate-insert-extract
132 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass()); // --eliminate-dead-code-aggressive
133 optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass()); // --eliminate-dead-branches
134 optimizer.RegisterPass(spvtools::CreateBlockMergePass()); // --merge-blocks
135 optimizer.RegisterPass(spvtools::CreateLocalSingleBlockLoadStoreElimPass());// --eliminate-local-single-block
136 optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass()); // --eliminate-local-single-store
137 optimizer.RegisterPass(spvtools::CreateLocalMultiStoreElimPass()); // --eliminate-local-multi-store
138 optimizer.RegisterPass(spvtools::CreateInsertExtractElimPass()); // --eliminate-insert-extract
139 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass()); // --eliminate-dead-code-aggressive
140 optimizer.RegisterPass(spvtools::CreateCommonUniformElimPass()); // --eliminate-common-uniform
141 break;
142 case 2: // RegisterPerformancePasses from commandline optimizer tool october 2017
143 optimizer.RegisterPass(spvtools::CreateInlineExhaustivePass());
144 optimizer.RegisterPass(spvtools::CreateLocalAccessChainConvertPass());
145 optimizer.RegisterPass(spvtools::CreateLocalSingleBlockLoadStoreElimPass());
146 optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass());
147 optimizer.RegisterPass(spvtools::CreateInsertExtractElimPass());
148 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
149 optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass());
150 optimizer.RegisterPass(spvtools::CreateBlockMergePass());
151 optimizer.RegisterPass(spvtools::CreateLocalMultiStoreElimPass());
152 optimizer.RegisterPass(spvtools::CreateInsertExtractElimPass());
153 optimizer.RegisterPass(spvtools::CreateCommonUniformElimPass());
154 break;
155 case 3: // RegisterSizePasses from commandline optimizer tool october 2017
156 optimizer.RegisterPass(spvtools::CreateInlineExhaustivePass());
157 optimizer.RegisterPass(spvtools::CreateLocalAccessChainConvertPass());
158 optimizer.RegisterPass(spvtools::CreateLocalSingleBlockLoadStoreElimPass());
159 optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass());
160 optimizer.RegisterPass(spvtools::CreateInsertExtractElimPass());
161 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
162 optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass());
163 optimizer.RegisterPass(spvtools::CreateBlockMergePass());
164 optimizer.RegisterPass(spvtools::CreateLocalMultiStoreElimPass());
165 optimizer.RegisterPass(spvtools::CreateInsertExtractElimPass());
166 optimizer.RegisterPass(spvtools::CreateCommonUniformElimPass());
167 break;
168 case 4: // RegisterLegalizationPasses from commandline optimizer tool April 2018
169 optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass());
170 optimizer.RegisterPass(spvtools::CreateMergeReturnPass());
171 optimizer.RegisterPass(spvtools::CreateInlineExhaustivePass());
172 optimizer.RegisterPass(spvtools::CreateEliminateDeadFunctionsPass());
173 optimizer.RegisterPass(spvtools::CreatePrivateToLocalPass());
174 optimizer.RegisterPass(spvtools::CreateLocalSingleBlockLoadStoreElimPass());
175 optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass());
176 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
177 optimizer.RegisterPass(spvtools::CreateScalarReplacementPass());
178 optimizer.RegisterPass(spvtools::CreateLocalSingleBlockLoadStoreElimPass());
179 optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass());
180 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
181 optimizer.RegisterPass(spvtools::CreateLocalMultiStoreElimPass());
182 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
183 optimizer.RegisterPass(spvtools::CreateCCPPass());
184 optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass());
185 optimizer.RegisterPass(spvtools::CreateSimplificationPass());
186 optimizer.RegisterPass(spvtools::CreateInsertExtractElimPass());
187 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
188 optimizer.RegisterPass(spvtools::CreateCopyPropagateArraysPass());
189 optimizer.RegisterPass(spvtools::CreateDeadInsertElimPass());
190 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
191 break;
192 case 5: // RegisterPerformancePasses from commandline optimizer tool April 2018
193 optimizer.RegisterPass(spvtools::CreateRemoveDuplicatesPass());
194 optimizer.RegisterPass(spvtools::CreateMergeReturnPass());
195 optimizer.RegisterPass(spvtools::CreateInlineExhaustivePass());
196 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
197 optimizer.RegisterPass(spvtools::CreateLocalSingleBlockLoadStoreElimPass());
198 optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass());
199 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
200 optimizer.RegisterPass(spvtools::CreateScalarReplacementPass());
201 optimizer.RegisterPass(spvtools::CreateLocalAccessChainConvertPass());
202 optimizer.RegisterPass(spvtools::CreateLocalSingleBlockLoadStoreElimPass());
203 optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass());
204 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
205 optimizer.RegisterPass(spvtools::CreateLocalMultiStoreElimPass());
206 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
207 optimizer.RegisterPass(spvtools::CreateCCPPass());
208 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
209 optimizer.RegisterPass(spvtools::CreateRedundancyEliminationPass());
210 optimizer.RegisterPass(spvtools::CreateInsertExtractElimPass());
211 optimizer.RegisterPass(spvtools::CreateDeadInsertElimPass());
212 optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass());
213 optimizer.RegisterPass(spvtools::CreateSimplificationPass());
214 optimizer.RegisterPass(spvtools::CreateIfConversionPass());
215 optimizer.RegisterPass(spvtools::CreateCopyPropagateArraysPass());
216 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
217 optimizer.RegisterPass(spvtools::CreateBlockMergePass());
218 optimizer.RegisterPass(spvtools::CreateRedundancyEliminationPass());
219 optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass());
220 optimizer.RegisterPass(spvtools::CreateBlockMergePass());
221 optimizer.RegisterPass(spvtools::CreateInsertExtractElimPass());
222 // comment from tool:
223 // Currently exposing driver bugs resulting in crashes (#946)
224 // .RegisterPass(CreateCommonUniformElimPass())
225 break;
226 case 6: // RegisterPerformancePasses from commandline optimizer tool April 2018 with CreateCommonUniformElimPass
227 optimizer.RegisterPass(spvtools::CreateRemoveDuplicatesPass());
228 optimizer.RegisterPass(spvtools::CreateMergeReturnPass());
229 optimizer.RegisterPass(spvtools::CreateInlineExhaustivePass());
230 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
231 optimizer.RegisterPass(spvtools::CreateLocalSingleBlockLoadStoreElimPass());
232 optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass());
233 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
234 optimizer.RegisterPass(spvtools::CreateScalarReplacementPass());
235 optimizer.RegisterPass(spvtools::CreateLocalAccessChainConvertPass());
236 optimizer.RegisterPass(spvtools::CreateLocalSingleBlockLoadStoreElimPass());
237 optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass());
238 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
239 optimizer.RegisterPass(spvtools::CreateLocalMultiStoreElimPass());
240 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
241 optimizer.RegisterPass(spvtools::CreateCCPPass());
242 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
243 optimizer.RegisterPass(spvtools::CreateRedundancyEliminationPass());
244 optimizer.RegisterPass(spvtools::CreateInsertExtractElimPass());
245 optimizer.RegisterPass(spvtools::CreateDeadInsertElimPass());
246 optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass());
247 optimizer.RegisterPass(spvtools::CreateSimplificationPass());
248 optimizer.RegisterPass(spvtools::CreateIfConversionPass());
249 optimizer.RegisterPass(spvtools::CreateCopyPropagateArraysPass());
250 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
251 optimizer.RegisterPass(spvtools::CreateBlockMergePass());
252 optimizer.RegisterPass(spvtools::CreateRedundancyEliminationPass());
253 optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass());
254 optimizer.RegisterPass(spvtools::CreateBlockMergePass());
255 optimizer.RegisterPass(spvtools::CreateInsertExtractElimPass());
256 optimizer.RegisterPass(spvtools::CreateCommonUniformElimPass());
257 break;
258 case 7: // RegisterSizePasses from commandline optimizer tool April 2018
259 optimizer.RegisterPass(spvtools::CreateRemoveDuplicatesPass());
260 optimizer.RegisterPass(spvtools::CreateMergeReturnPass());
261 optimizer.RegisterPass(spvtools::CreateInlineExhaustivePass());
262 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
263 optimizer.RegisterPass(spvtools::CreateScalarReplacementPass());
264 optimizer.RegisterPass(spvtools::CreateLocalAccessChainConvertPass());
265 optimizer.RegisterPass(spvtools::CreateLocalSingleBlockLoadStoreElimPass());
266 optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass());
267 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
268 optimizer.RegisterPass(spvtools::CreateInsertExtractElimPass());
269 optimizer.RegisterPass(spvtools::CreateDeadInsertElimPass());
270 optimizer.RegisterPass(spvtools::CreateLocalMultiStoreElimPass());
271 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
272 optimizer.RegisterPass(spvtools::CreateCCPPass());
273 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
274 optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass());
275 optimizer.RegisterPass(spvtools::CreateIfConversionPass());
276 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
277 optimizer.RegisterPass(spvtools::CreateBlockMergePass());
278 optimizer.RegisterPass(spvtools::CreateInsertExtractElimPass());
279 optimizer.RegisterPass(spvtools::CreateDeadInsertElimPass());
280 optimizer.RegisterPass(spvtools::CreateRedundancyEliminationPass());
281 optimizer.RegisterPass(spvtools::CreateCFGCleanupPass());
282 // comment from tool:
283 // Currently exposing driver bugs resulting in crashes (#946)
284 // .RegisterPass(CreateCommonUniformElimPass())
285 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
286 break;
287 case 8: // RegisterSizePasses from commandline optimizer tool April 2018 with CreateCommonUniformElimPass
288 optimizer.RegisterPass(spvtools::CreateRemoveDuplicatesPass());
289 optimizer.RegisterPass(spvtools::CreateMergeReturnPass());
290 optimizer.RegisterPass(spvtools::CreateInlineExhaustivePass());
291 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
292 optimizer.RegisterPass(spvtools::CreateScalarReplacementPass());
293 optimizer.RegisterPass(spvtools::CreateLocalAccessChainConvertPass());
294 optimizer.RegisterPass(spvtools::CreateLocalSingleBlockLoadStoreElimPass());
295 optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass());
296 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
297 optimizer.RegisterPass(spvtools::CreateInsertExtractElimPass());
298 optimizer.RegisterPass(spvtools::CreateDeadInsertElimPass());
299 optimizer.RegisterPass(spvtools::CreateLocalMultiStoreElimPass());
300 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
301 optimizer.RegisterPass(spvtools::CreateCCPPass());
302 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
303 optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass());
304 optimizer.RegisterPass(spvtools::CreateIfConversionPass());
305 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
306 optimizer.RegisterPass(spvtools::CreateBlockMergePass());
307 optimizer.RegisterPass(spvtools::CreateInsertExtractElimPass());
308 optimizer.RegisterPass(spvtools::CreateDeadInsertElimPass());
309 optimizer.RegisterPass(spvtools::CreateRedundancyEliminationPass());
310 optimizer.RegisterPass(spvtools::CreateCFGCleanupPass());
311 optimizer.RegisterPass(spvtools::CreateCommonUniformElimPass());
312 optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
313 break;
314 default:
315 TCU_THROW(InternalError, "Unknown optimization recipe requested");
316 }
317
318 spvtools::OptimizerOptions optimizer_options;
319 optimizer_options.set_run_validator(false);
320 const bool ok = optimizer.Run(binary.data(), binary.size(), &binary, optimizer_options);
321
322 if (!ok)
323 TCU_THROW(InternalError, "Optimizer call failed");
324 }
325
createProgramBinaryFromSpirV(const vector<deUint32> & binary)326 ProgramBinary* createProgramBinaryFromSpirV (const vector<deUint32>& binary)
327 {
328 DE_ASSERT(!binary.empty());
329
330 if (isNativeSpirVBinaryEndianness())
331 return new ProgramBinary(PROGRAM_FORMAT_SPIRV, binary.size()*sizeof(deUint32), (const deUint8*)&binary[0]);
332 else
333 TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
334 }
335
336 #endif // defined(DEQP_HAVE_SPIRV_TOOLS)
337
338 } // anonymous
339
340 #if defined(DEQP_HAVE_SPIRV_TOOLS)
341
validateCompiledBinary(const vector<deUint32> & binary,glu::ShaderProgramInfo * buildInfo,const SpirvValidatorOptions & options)342 void validateCompiledBinary(const vector<deUint32>& binary, glu::ShaderProgramInfo* buildInfo, const SpirvValidatorOptions& options)
343 {
344 std::ostringstream validationLog;
345
346 if (!validateSpirV(binary.size(), &binary[0], &validationLog, options))
347 {
348 buildInfo->program.linkOk = false;
349 buildInfo->program.infoLog += "\n" + validationLog.str();
350
351 TCU_THROW(InternalError, "Validation failed for compiled SPIR-V binary");
352 }
353 }
354
validateCompiledBinary(const vector<deUint32> & binary,SpirVProgramInfo * buildInfo,const SpirvValidatorOptions & options)355 void validateCompiledBinary(const vector<deUint32>& binary, SpirVProgramInfo* buildInfo, const SpirvValidatorOptions& options)
356 {
357 std::ostringstream validationLog;
358
359 if (!validateSpirV(binary.size(), &binary[0], &validationLog, options))
360 {
361 buildInfo->compileOk = false;
362 buildInfo->infoLog += "\n" + validationLog.str();
363
364 TCU_THROW(InternalError, "Validation failed for compiled SPIR-V binary");
365 }
366 }
367
368 de::Mutex cacheFileMutex;
369 map<deUint32, vector<deUint32> > cacheFileIndex;
370 bool cacheFileFirstRun = true;
371
shaderCacheFirstRunCheck(const char * shaderCacheFile,bool truncate)372 void shaderCacheFirstRunCheck (const char* shaderCacheFile, bool truncate)
373 {
374 cacheFileMutex.lock();
375 if (cacheFileFirstRun)
376 {
377 cacheFileFirstRun = false;
378 if (truncate)
379 {
380 // Open file with "w" access to truncate it
381 FILE* f = fopen(shaderCacheFile, "wb");
382 if (f)
383 fclose(f);
384 }
385 else
386 {
387 // Parse chunked shader cache file for hashes and offsets
388 FILE* file = fopen(shaderCacheFile, "rb");
389 int count = 0;
390 if (file)
391 {
392 deUint32 chunksize = 0;
393 deUint32 hash = 0;
394 deUint32 offset = 0;
395 bool ok = true;
396 while (ok)
397 {
398 offset = (deUint32)ftell(file);
399 if (ok) ok = fread(&chunksize, 1, 4, file) == 4;
400 if (ok) ok = fread(&hash, 1, 4, file) == 4;
401 if (ok) cacheFileIndex[hash].push_back(offset);
402 if (ok) ok = fseek(file, offset + chunksize, SEEK_SET) == 0;
403 count++;
404 }
405 fclose(file);
406 }
407 }
408 }
409 cacheFileMutex.unlock();
410 }
411
intToString(deUint32 integer)412 std::string intToString (deUint32 integer)
413 {
414 std::stringstream temp_sstream;
415
416 temp_sstream << integer;
417
418 return temp_sstream.str();
419 }
420
shadercacheLoad(const std::string & shaderstring,const char * shaderCacheFilename)421 vk::ProgramBinary* shadercacheLoad (const std::string& shaderstring, const char* shaderCacheFilename)
422 {
423 deUint32 hash = deStringHash(shaderstring.c_str());
424 deInt32 format;
425 deInt32 length;
426 deInt32 sourcelength;
427 deUint32 i;
428 deUint32 temp;
429 deUint8* bin = 0;
430 char* source = 0;
431 bool ok = true;
432 cacheFileMutex.lock();
433
434 if (cacheFileIndex.count(hash) == 0)
435 {
436 cacheFileMutex.unlock();
437 return 0;
438 }
439 FILE* file = fopen(shaderCacheFilename, "rb");
440 ok = file != 0;
441
442 for (i = 0; i < cacheFileIndex[hash].size(); i++)
443 {
444 if (ok) ok = fseek(file, cacheFileIndex[hash][i], SEEK_SET) == 0;
445 if (ok) ok = fread(&temp, 1, 4, file) == 4; // Chunk size (skip)
446 if (ok) ok = fread(&temp, 1, 4, file) == 4; // Stored hash
447 if (ok) ok = temp == hash; // Double check
448 if (ok) ok = fread(&format, 1, 4, file) == 4;
449 if (ok) ok = fread(&length, 1, 4, file) == 4;
450 if (ok) ok = length > 0; // sanity check
451 if (ok) bin = new deUint8[length];
452 if (ok) ok = fread(bin, 1, length, file) == (size_t)length;
453 if (ok) ok = fread(&sourcelength, 1, 4, file) == 4;
454 if (ok && sourcelength > 0)
455 {
456 source = new char[sourcelength + 1];
457 ok = fread(source, 1, sourcelength, file) == (size_t)sourcelength;
458 source[sourcelength] = 0;
459 }
460 if (!ok || shaderstring != std::string(source))
461 {
462 // Mismatch, but may still exist in cache if there were hash collisions
463 delete[] source;
464 delete[] bin;
465 }
466 else
467 {
468 delete[] source;
469 if (file) fclose(file);
470 cacheFileMutex.unlock();
471 vk::ProgramBinary* res = new vk::ProgramBinary((vk::ProgramFormat)format, length, bin);
472 delete[] bin;
473 return res;
474 }
475 }
476 if (file) fclose(file);
477 cacheFileMutex.unlock();
478 return 0;
479 }
480
shadercacheSave(const vk::ProgramBinary * binary,const std::string & shaderstring,const char * shaderCacheFilename)481 void shadercacheSave (const vk::ProgramBinary* binary, const std::string& shaderstring, const char* shaderCacheFilename)
482 {
483 if (binary == 0)
484 return;
485 deUint32 hash = deStringHash(shaderstring.c_str());
486 deInt32 format = binary->getFormat();
487 deUint32 length = (deUint32)binary->getSize();
488 deUint32 chunksize;
489 deUint32 offset;
490 const deUint8* bin = binary->getBinary();
491 const de::FilePath filePath (shaderCacheFilename);
492
493 cacheFileMutex.lock();
494
495 if (!de::FilePath(filePath.getDirName()).exists())
496 de::createDirectoryAndParents(filePath.getDirName().c_str());
497
498 FILE* file = fopen(shaderCacheFilename, "ab");
499 if (!file)
500 {
501 cacheFileMutex.unlock();
502 return;
503 }
504 // Append mode starts writing from the end of the file,
505 // but unless we do a seek, ftell returns 0.
506 fseek(file, 0, SEEK_END);
507 offset = (deUint32)ftell(file);
508 chunksize = 4 + 4 + 4 + 4 + length + 4 + (deUint32)shaderstring.length();
509 fwrite(&chunksize, 1, 4, file);
510 fwrite(&hash, 1, 4, file);
511 fwrite(&format, 1, 4, file);
512 fwrite(&length, 1, 4, file);
513 fwrite(bin, 1, length, file);
514 length = (deUint32)shaderstring.length();
515 fwrite(&length, 1, 4, file);
516 fwrite(shaderstring.c_str(), 1, length, file);
517 fclose(file);
518 cacheFileIndex[hash].push_back(offset);
519
520 cacheFileMutex.unlock();
521 }
522
523 // Insert any information that may affect compilation into the shader string.
getCompileEnvironment(std::string & shaderstring)524 void getCompileEnvironment (std::string& shaderstring)
525 {
526 shaderstring += "GLSL:";
527 shaderstring += qpGetReleaseGlslName();
528 shaderstring += "\nSpir-v Tools:";
529 shaderstring += qpGetReleaseSpirvToolsName();
530 shaderstring += "\nSpir-v Headers:";
531 shaderstring += qpGetReleaseSpirvHeadersName();
532 shaderstring += "\n";
533 }
534
535 // Insert compilation options into the shader string.
getBuildOptions(std::string & shaderstring,const ShaderBuildOptions & buildOptions,int optimizationRecipe)536 void getBuildOptions (std::string& shaderstring, const ShaderBuildOptions& buildOptions, int optimizationRecipe)
537 {
538 shaderstring += "Target Spir-V ";
539 shaderstring += getSpirvVersionName(buildOptions.targetVersion);
540 shaderstring += "\n";
541 if (buildOptions.flags & ShaderBuildOptions::FLAG_ALLOW_RELAXED_OFFSETS)
542 shaderstring += "Flag:Allow relaxed offsets\n";
543 if (buildOptions.flags & ShaderBuildOptions::FLAG_USE_STORAGE_BUFFER_STORAGE_CLASS)
544 shaderstring += "Flag:Use storage buffer storage class\n";
545 if (optimizationRecipe != 0)
546 {
547 shaderstring += "Optimization recipe ";
548 shaderstring += optimizationRecipe;
549 shaderstring += "\n";
550 }
551 }
552
buildProgram(const GlslSource & program,glu::ShaderProgramInfo * buildInfo,const tcu::CommandLine & commandLine)553 ProgramBinary* buildProgram (const GlslSource& program, glu::ShaderProgramInfo* buildInfo, const tcu::CommandLine& commandLine)
554 {
555 const SpirvVersion spirvVersion = program.buildOptions.targetVersion;
556 const bool validateBinary = VALIDATE_BINARIES;
557 vector<deUint32> binary;
558 std::string cachekey;
559 std::string shaderstring;
560 vk::ProgramBinary* res = 0;
561 const int optimizationRecipe = commandLine.getOptimizationRecipe();
562
563 if (commandLine.isShadercacheEnabled())
564 {
565 shaderCacheFirstRunCheck(commandLine.getShaderCacheFilename(), commandLine.isShaderCacheTruncateEnabled());
566 getCompileEnvironment(cachekey);
567 getBuildOptions(cachekey, program.buildOptions, optimizationRecipe);
568
569 for (int i = 0; i < glu::SHADERTYPE_LAST; i++)
570 {
571 if (!program.sources[i].empty())
572 {
573 cachekey += glu::getShaderTypeName((glu::ShaderType)i);
574
575 for (std::vector<std::string>::const_iterator it = program.sources[i].begin(); it != program.sources[i].end(); ++it)
576 shaderstring += *it;
577 }
578 }
579
580 cachekey = cachekey + shaderstring;
581
582 res = shadercacheLoad(cachekey, commandLine.getShaderCacheFilename());
583
584 if (res)
585 {
586 buildInfo->program.infoLog = "Loaded from cache";
587 buildInfo->program.linkOk = true;
588 buildInfo->program.linkTimeUs = 0;
589
590 for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++)
591 {
592 if (!program.sources[shaderType].empty())
593 {
594 glu::ShaderInfo shaderBuildInfo;
595
596 shaderBuildInfo.type = (glu::ShaderType)shaderType;
597 shaderBuildInfo.source = shaderstring;
598 shaderBuildInfo.compileTimeUs = 0;
599 shaderBuildInfo.compileOk = true;
600
601 buildInfo->shaders.push_back(shaderBuildInfo);
602 }
603 }
604 }
605 }
606
607 if (!res)
608 {
609 {
610 vector<deUint32> nonStrippedBinary;
611
612 if (!compileGlslToSpirV(program, &nonStrippedBinary, buildInfo))
613 TCU_THROW(InternalError, "Compiling GLSL to SPIR-V failed");
614
615 TCU_CHECK_INTERNAL(!nonStrippedBinary.empty());
616 stripSpirVDebugInfo(nonStrippedBinary.size(), &nonStrippedBinary[0], &binary);
617 TCU_CHECK_INTERNAL(!binary.empty());
618 }
619
620 if (optimizationRecipe != 0)
621 {
622 validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
623 optimizeCompiledBinary(binary, optimizationRecipe, spirvVersion);
624 }
625
626 if (validateBinary)
627 {
628 validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
629 }
630
631 res = createProgramBinaryFromSpirV(binary);
632 if (commandLine.isShadercacheEnabled())
633 shadercacheSave(res, cachekey, commandLine.getShaderCacheFilename());
634 }
635 return res;
636 }
637
buildProgram(const HlslSource & program,glu::ShaderProgramInfo * buildInfo,const tcu::CommandLine & commandLine)638 ProgramBinary* buildProgram (const HlslSource& program, glu::ShaderProgramInfo* buildInfo, const tcu::CommandLine& commandLine)
639 {
640 const SpirvVersion spirvVersion = program.buildOptions.targetVersion;
641 const bool validateBinary = VALIDATE_BINARIES;
642 vector<deUint32> binary;
643 std::string cachekey;
644 std::string shaderstring;
645 vk::ProgramBinary* res = 0;
646 const int optimizationRecipe = commandLine.getOptimizationRecipe();
647
648 if (commandLine.isShadercacheEnabled())
649 {
650 shaderCacheFirstRunCheck(commandLine.getShaderCacheFilename(), commandLine.isShaderCacheTruncateEnabled());
651 getCompileEnvironment(cachekey);
652 getBuildOptions(cachekey, program.buildOptions, optimizationRecipe);
653
654 for (int i = 0; i < glu::SHADERTYPE_LAST; i++)
655 {
656 if (!program.sources[i].empty())
657 {
658 cachekey += glu::getShaderTypeName((glu::ShaderType)i);
659
660 for (std::vector<std::string>::const_iterator it = program.sources[i].begin(); it != program.sources[i].end(); ++it)
661 shaderstring += *it;
662 }
663 }
664
665 cachekey = cachekey + shaderstring;
666
667 res = shadercacheLoad(cachekey, commandLine.getShaderCacheFilename());
668
669 if (res)
670 {
671 buildInfo->program.infoLog = "Loaded from cache";
672 buildInfo->program.linkOk = true;
673 buildInfo->program.linkTimeUs = 0;
674
675 for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++)
676 {
677 if (!program.sources[shaderType].empty())
678 {
679 glu::ShaderInfo shaderBuildInfo;
680
681 shaderBuildInfo.type = (glu::ShaderType)shaderType;
682 shaderBuildInfo.source = shaderstring;
683 shaderBuildInfo.compileTimeUs = 0;
684 shaderBuildInfo.compileOk = true;
685
686 buildInfo->shaders.push_back(shaderBuildInfo);
687 }
688 }
689 }
690 }
691
692 if (!res)
693 {
694 {
695 vector<deUint32> nonStrippedBinary;
696
697 if (!compileHlslToSpirV(program, &nonStrippedBinary, buildInfo))
698 TCU_THROW(InternalError, "Compiling HLSL to SPIR-V failed");
699
700 TCU_CHECK_INTERNAL(!nonStrippedBinary.empty());
701 stripSpirVDebugInfo(nonStrippedBinary.size(), &nonStrippedBinary[0], &binary);
702 TCU_CHECK_INTERNAL(!binary.empty());
703 }
704
705 if (optimizationRecipe != 0)
706 {
707 validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
708 optimizeCompiledBinary(binary, optimizationRecipe, spirvVersion);
709 }
710
711 if (validateBinary)
712 {
713 validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
714 }
715
716 res = createProgramBinaryFromSpirV(binary);
717 if (commandLine.isShadercacheEnabled())
718 shadercacheSave(res, cachekey, commandLine.getShaderCacheFilename());
719 }
720 return res;
721 }
722
assembleProgram(const SpirVAsmSource & program,SpirVProgramInfo * buildInfo,const tcu::CommandLine & commandLine)723 ProgramBinary* assembleProgram (const SpirVAsmSource& program, SpirVProgramInfo* buildInfo, const tcu::CommandLine& commandLine)
724 {
725 const SpirvVersion spirvVersion = program.buildOptions.targetVersion;
726 const bool validateBinary = VALIDATE_BINARIES;
727 vector<deUint32> binary;
728 vk::ProgramBinary* res = 0;
729 std::string cachekey;
730 const int optimizationRecipe = commandLine.isSpirvOptimizationEnabled() ? commandLine.getOptimizationRecipe() : 0;
731
732 if (commandLine.isShadercacheEnabled())
733 {
734 shaderCacheFirstRunCheck(commandLine.getShaderCacheFilename(), commandLine.isShaderCacheTruncateEnabled());
735 getCompileEnvironment(cachekey);
736 cachekey += "Target Spir-V ";
737 cachekey += getSpirvVersionName(spirvVersion);
738 cachekey += "\n";
739 if (optimizationRecipe != 0)
740 {
741 cachekey += "Optimization recipe ";
742 cachekey += optimizationRecipe;
743 cachekey += "\n";
744 }
745
746 cachekey += program.source;
747
748 res = shadercacheLoad(cachekey, commandLine.getShaderCacheFilename());
749
750 if (res)
751 {
752 buildInfo->source = program.source;
753 buildInfo->compileOk = true;
754 buildInfo->compileTimeUs = 0;
755 buildInfo->infoLog = "Loaded from cache";
756 }
757 }
758
759 if (!res)
760 {
761
762 if (!assembleSpirV(&program, &binary, buildInfo, spirvVersion))
763 TCU_THROW(InternalError, "Failed to assemble SPIR-V");
764
765 if (optimizationRecipe != 0)
766 {
767 validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
768 optimizeCompiledBinary(binary, optimizationRecipe, spirvVersion);
769 }
770
771 if (validateBinary)
772 {
773 validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
774 }
775
776 res = createProgramBinaryFromSpirV(binary);
777 if (commandLine.isShadercacheEnabled())
778 shadercacheSave(res, cachekey, commandLine.getShaderCacheFilename());
779 }
780 return res;
781 }
782
783 #else // !DEQP_HAVE_SPIRV_TOOLS
784
buildProgram(const GlslSource &,glu::ShaderProgramInfo *,const tcu::CommandLine &)785 ProgramBinary* buildProgram (const GlslSource&, glu::ShaderProgramInfo*, const tcu::CommandLine&)
786 {
787 TCU_THROW(NotSupportedError, "GLSL to SPIR-V compilation not supported (DEQP_HAVE_GLSLANG not defined)");
788 }
789
buildProgram(const HlslSource &,glu::ShaderProgramInfo *,const tcu::CommandLine &)790 ProgramBinary* buildProgram (const HlslSource&, glu::ShaderProgramInfo*, const tcu::CommandLine&)
791 {
792 TCU_THROW(NotSupportedError, "HLSL to SPIR-V compilation not supported (DEQP_HAVE_GLSLANG not defined)");
793 }
794
assembleProgram(const SpirVAsmSource &,SpirVProgramInfo *,const tcu::CommandLine &)795 ProgramBinary* assembleProgram (const SpirVAsmSource&, SpirVProgramInfo*, const tcu::CommandLine&)
796 {
797 TCU_THROW(NotSupportedError, "SPIR-V assembly not supported (DEQP_HAVE_SPIRV_TOOLS not defined)");
798 }
799 #endif
800
disassembleProgram(const ProgramBinary & program,std::ostream * dst)801 void disassembleProgram (const ProgramBinary& program, std::ostream* dst)
802 {
803 if (program.getFormat() == PROGRAM_FORMAT_SPIRV)
804 {
805 TCU_CHECK_INTERNAL(isSaneSpirVBinary(program));
806
807 if (isNativeSpirVBinaryEndianness())
808 disassembleSpirV(program.getSize()/sizeof(deUint32), (const deUint32*)program.getBinary(), dst,
809 extractSpirvVersion(program));
810 else
811 TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
812 }
813 else
814 TCU_THROW(NotSupportedError, "Unsupported program format");
815 }
816
validateProgram(const ProgramBinary & program,std::ostream * dst,const SpirvValidatorOptions & options)817 bool validateProgram (const ProgramBinary& program, std::ostream* dst, const SpirvValidatorOptions& options)
818 {
819 if (program.getFormat() == PROGRAM_FORMAT_SPIRV)
820 {
821 if (!isSaneSpirVBinary(program))
822 {
823 *dst << "Binary doesn't look like SPIR-V at all";
824 return false;
825 }
826
827 if (isNativeSpirVBinaryEndianness())
828 return validateSpirV(program.getSize()/sizeof(deUint32), (const deUint32*)program.getBinary(), dst, options);
829 else
830 TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
831 }
832 else
833 TCU_THROW(NotSupportedError, "Unsupported program format");
834 }
835
createShaderModule(const DeviceInterface & deviceInterface,VkDevice device,const ProgramBinary & binary,VkShaderModuleCreateFlags flags)836 Move<VkShaderModule> createShaderModule (const DeviceInterface& deviceInterface, VkDevice device, const ProgramBinary& binary, VkShaderModuleCreateFlags flags)
837 {
838 if (binary.getFormat() == PROGRAM_FORMAT_SPIRV)
839 {
840 const struct VkShaderModuleCreateInfo shaderModuleInfo =
841 {
842 VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
843 DE_NULL,
844 flags,
845 (deUintptr)binary.getSize(),
846 (const deUint32*)binary.getBinary(),
847 };
848
849 return createShaderModule(deviceInterface, device, &shaderModuleInfo);
850 }
851 else
852 TCU_THROW(NotSupportedError, "Unsupported program format");
853 }
854
getGluShaderType(VkShaderStageFlagBits shaderStage)855 glu::ShaderType getGluShaderType (VkShaderStageFlagBits shaderStage)
856 {
857 switch (shaderStage)
858 {
859 case VK_SHADER_STAGE_VERTEX_BIT: return glu::SHADERTYPE_VERTEX;
860 case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT: return glu::SHADERTYPE_TESSELLATION_CONTROL;
861 case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT: return glu::SHADERTYPE_TESSELLATION_EVALUATION;
862 case VK_SHADER_STAGE_GEOMETRY_BIT: return glu::SHADERTYPE_GEOMETRY;
863 case VK_SHADER_STAGE_FRAGMENT_BIT: return glu::SHADERTYPE_FRAGMENT;
864 case VK_SHADER_STAGE_COMPUTE_BIT: return glu::SHADERTYPE_COMPUTE;
865 default:
866 DE_FATAL("Unknown shader stage");
867 return glu::SHADERTYPE_LAST;
868 }
869 }
870
getVkShaderStage(glu::ShaderType shaderType)871 VkShaderStageFlagBits getVkShaderStage (glu::ShaderType shaderType)
872 {
873 static const VkShaderStageFlagBits s_shaderStages[] =
874 {
875 VK_SHADER_STAGE_VERTEX_BIT,
876 VK_SHADER_STAGE_FRAGMENT_BIT,
877 VK_SHADER_STAGE_GEOMETRY_BIT,
878 VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
879 VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
880 VK_SHADER_STAGE_COMPUTE_BIT
881 };
882
883 return de::getSizedArrayElement<glu::SHADERTYPE_LAST>(s_shaderStages, shaderType);
884 }
885
886 // Baseline version, to be used for shaders which don't specify a version
getBaselineSpirvVersion(const deUint32)887 vk::SpirvVersion getBaselineSpirvVersion (const deUint32 /* vulkanVersion */)
888 {
889 return vk::SPIRV_VERSION_1_0;
890 }
891
892 // Max supported versions for each vulkan version
getMaxSpirvVersionForAsm(const deUint32 vulkanVersion)893 vk::SpirvVersion getMaxSpirvVersionForAsm (const deUint32 vulkanVersion)
894 {
895 vk::SpirvVersion result = vk::SPIRV_VERSION_LAST;
896
897 deUint32 vulkanVersionMajorMinor = VK_MAKE_VERSION(VK_VERSION_MAJOR(vulkanVersion), VK_VERSION_MINOR(vulkanVersion), 0);
898 if (vulkanVersionMajorMinor == VK_API_VERSION_1_0)
899 result = vk::SPIRV_VERSION_1_0;
900 else if (vulkanVersionMajorMinor >= VK_API_VERSION_1_1)
901 result = vk::SPIRV_VERSION_1_3;
902
903 DE_ASSERT(result < vk::SPIRV_VERSION_LAST);
904
905 return result;
906 }
907
getMaxSpirvVersionForGlsl(const deUint32 vulkanVersion)908 vk::SpirvVersion getMaxSpirvVersionForGlsl (const deUint32 vulkanVersion)
909 {
910 vk::SpirvVersion result = vk::SPIRV_VERSION_LAST;
911
912 deUint32 vulkanVersionMajorMinor = VK_MAKE_VERSION(VK_VERSION_MAJOR(vulkanVersion), VK_VERSION_MINOR(vulkanVersion), 0);
913 if (vulkanVersionMajorMinor == VK_API_VERSION_1_0)
914 result = vk::SPIRV_VERSION_1_0;
915 else if (vulkanVersionMajorMinor >= VK_API_VERSION_1_1)
916 result = vk::SPIRV_VERSION_1_3;
917
918 DE_ASSERT(result < vk::SPIRV_VERSION_LAST);
919
920 return result;
921 }
922
extractSpirvVersion(const ProgramBinary & binary)923 SpirvVersion extractSpirvVersion (const ProgramBinary& binary)
924 {
925 DE_STATIC_ASSERT(SPIRV_VERSION_1_3 + 1 == SPIRV_VERSION_LAST);
926
927 if (binary.getFormat() != PROGRAM_FORMAT_SPIRV)
928 TCU_THROW(InternalError, "Binary is not in SPIR-V format");
929
930 if (!isSaneSpirVBinary(binary) || binary.getSize() < sizeof(SpirvBinaryHeader))
931 TCU_THROW(InternalError, "Invalid SPIR-V header format");
932
933 const deUint32 spirvBinaryVersion10 = 0x00010000;
934 const deUint32 spirvBinaryVersion11 = 0x00010100;
935 const deUint32 spirvBinaryVersion12 = 0x00010200;
936 const deUint32 spirvBinaryVersion13 = 0x00010300;
937 const SpirvBinaryHeader* header = reinterpret_cast<const SpirvBinaryHeader*>(binary.getBinary());
938 const deUint32 spirvVersion = isNativeSpirVBinaryEndianness()
939 ? header->version
940 : deReverseBytes32(header->version);
941 SpirvVersion result = SPIRV_VERSION_LAST;
942
943 switch (spirvVersion)
944 {
945 case spirvBinaryVersion10: result = SPIRV_VERSION_1_0; break; //!< SPIR-V 1.0
946 case spirvBinaryVersion11: result = SPIRV_VERSION_1_1; break; //!< SPIR-V 1.1
947 case spirvBinaryVersion12: result = SPIRV_VERSION_1_2; break; //!< SPIR-V 1.2
948 case spirvBinaryVersion13: result = SPIRV_VERSION_1_3; break; //!< SPIR-V 1.3
949 default: TCU_THROW(InternalError, "Unknown SPIR-V version detected in binary");
950 }
951
952 return result;
953 }
954
getSpirvVersionName(const SpirvVersion spirvVersion)955 std::string getSpirvVersionName (const SpirvVersion spirvVersion)
956 {
957 DE_STATIC_ASSERT(SPIRV_VERSION_1_3 + 1 == SPIRV_VERSION_LAST);
958 DE_ASSERT(spirvVersion < SPIRV_VERSION_LAST);
959
960 std::string result;
961
962 switch (spirvVersion)
963 {
964 case SPIRV_VERSION_1_0: result = "1.0"; break; //!< SPIR-V 1.0
965 case SPIRV_VERSION_1_1: result = "1.1"; break; //!< SPIR-V 1.1
966 case SPIRV_VERSION_1_2: result = "1.2"; break; //!< SPIR-V 1.2
967 case SPIRV_VERSION_1_3: result = "1.3"; break; //!< SPIR-V 1.3
968 default: result = "Unknown";
969 }
970
971 return result;
972 }
973
operator ++(SpirvVersion & spirvVersion)974 SpirvVersion& operator++(SpirvVersion& spirvVersion)
975 {
976 if (spirvVersion == SPIRV_VERSION_LAST)
977 spirvVersion = SPIRV_VERSION_1_0;
978 else
979 spirvVersion = static_cast<SpirvVersion>(static_cast<deUint32>(spirvVersion) + 1);
980
981 return spirvVersion;
982 }
983
984 } // vk
985