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