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