1 /*
2  * Copyright (c) 2015-2016 The Khronos Group Inc.
3  * Copyright (c) 2015-2016 Valve Corporation
4  * Copyright (c) 2015-2016 LunarG, Inc.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and/or associated documentation files (the "Materials"), to
8  * deal in the Materials without restriction, including without limitation the
9  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10  * sell copies of the Materials, and to permit persons to whom the Materials are
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice(s) and this permission notice shall be included in
14  * all copies or substantial portions of the Materials.
15  *
16  * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19  *
20  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
21  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
22  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE
23  * USE OR OTHER DEALINGS IN THE MATERIALS.
24  *
25  * Author: Chia-I Wu <olvaffe@gmail.com>
26  * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
27  * Author: Tony Barbour <tony@LunarG.com>
28  */
29 
30 #include "vktestframework.h"
31 #include "vkrenderframework.h"
32 // TODO FIXME remove this once glslang doesn't define this
33 #undef BadValue
34 #include "SPIRV/GlslangToSpv.h"
35 #include "SPIRV/SPVRemapper.h"
36 #include <limits.h>
37 #include <math.h>
38 
39 #if defined(PATH_MAX) && !defined(MAX_PATH)
40 #define MAX_PATH PATH_MAX
41 #endif
42 
43 #ifdef _WIN32
44 #define ERR_EXIT(err_msg, err_class)                                           \
45     do {                                                                       \
46         MessageBox(NULL, err_msg, err_class, MB_OK);                           \
47         exit(1);                                                               \
48     } while (0)
49 #else // _WIN32
50 
51 #define ERR_EXIT(err_msg, err_class)                                           \
52     do {                                                                       \
53         printf(err_msg);                                                       \
54         fflush(stdout);                                                        \
55         exit(1);                                                               \
56     } while (0)
57 #endif // _WIN32
58 
59 #define GET_INSTANCE_PROC_ADDR(inst, entrypoint)                               \
60     {                                                                          \
61         m_fp##entrypoint =                                                     \
62             (PFN_vk##entrypoint)vkGetInstanceProcAddr(inst, "vk" #entrypoint); \
63         if (m_fp##entrypoint == NULL) {                                        \
64             ERR_EXIT("vkGetInstanceProcAddr failed to find vk" #entrypoint,    \
65                      "vkGetInstanceProcAddr Failure");                         \
66         }                                                                      \
67     }
68 
69 #define GET_DEVICE_PROC_ADDR(dev, entrypoint)                                  \
70     {                                                                          \
71         m_fp##entrypoint =                                                     \
72             (PFN_vk##entrypoint)vkGetDeviceProcAddr(dev, "vk" #entrypoint);    \
73         if (m_fp##entrypoint == NULL) {                                        \
74             ERR_EXIT("vkGetDeviceProcAddr failed to find vk" #entrypoint,      \
75                      "vkGetDeviceProcAddr Failure");                           \
76         }                                                                      \
77     }
78 
79 // Command-line options
80 enum TOptions {
81     EOptionNone = 0x000,
82     EOptionIntermediate = 0x001,
83     EOptionSuppressInfolog = 0x002,
84     EOptionMemoryLeakMode = 0x004,
85     EOptionRelaxedErrors = 0x008,
86     EOptionGiveWarnings = 0x010,
87     EOptionLinkProgram = 0x020,
88     EOptionMultiThreaded = 0x040,
89     EOptionDumpConfig = 0x080,
90     EOptionDumpReflection = 0x100,
91     EOptionSuppressWarnings = 0x200,
92     EOptionDumpVersions = 0x400,
93     EOptionSpv = 0x800,
94     EOptionDefaultDesktop = 0x1000,
95 };
96 
97 typedef struct _SwapchainBuffers {
98     VkImage image;
99     VkCommandBuffer cmd;
100     VkImageView view;
101 } SwapchainBuffers;
102 
103 #ifndef _WIN32
104 
105 #include <errno.h>
106 
fopen_s(FILE ** pFile,const char * filename,const char * mode)107 int fopen_s(FILE **pFile, const char *filename, const char *mode) {
108     if (!pFile || !filename || !mode) {
109         return EINVAL;
110     }
111 
112     FILE *f = fopen(filename, mode);
113     if (!f) {
114         if (errno != 0) {
115             return errno;
116         } else {
117             return ENOENT;
118         }
119     }
120     *pFile = f;
121 
122     return 0;
123 }
124 
125 #endif
126 
127 // Set up environment for GLSL compiler
128 // Must be done once per process
SetUp()129 void TestEnvironment::SetUp() {
130     // Initialize GLSL to SPV compiler utility
131     glslang::InitializeProcess();
132 
133     vk_testing::set_error_callback(test_error_callback);
134 }
135 
TearDown()136 void TestEnvironment::TearDown() { glslang::FinalizeProcess(); }
137 
VkTestFramework()138 VkTestFramework::VkTestFramework()
139     : m_compile_options(0), m_num_shader_strings(0) {}
140 
~VkTestFramework()141 VkTestFramework::~VkTestFramework() {}
142 
143 // Define all the static elements
144 bool VkTestFramework::m_use_glsl = false;
145 bool VkTestFramework::m_canonicalize_spv = false;
146 bool VkTestFramework::m_strip_spv = false;
147 bool VkTestFramework::m_do_everything_spv = false;
148 int VkTestFramework::m_width = 0;
149 int VkTestFramework::m_height = 0;
150 
optionMatch(const char * option,char * optionLine)151 bool VkTestFramework::optionMatch(const char *option, char *optionLine) {
152     if (strncmp(option, optionLine, strlen(option)) == 0)
153         return true;
154     else
155         return false;
156 }
157 
InitArgs(int * argc,char * argv[])158 void VkTestFramework::InitArgs(int *argc, char *argv[]) {
159     int i, n;
160 
161     for (i = 1, n = 1; i < *argc; i++) {
162         if (optionMatch("--no-SPV", argv[i]))
163             m_use_glsl = true;
164         else if (optionMatch("--strip-SPV", argv[i]))
165             m_strip_spv = true;
166         else if (optionMatch("--canonicalize-SPV", argv[i]))
167             m_canonicalize_spv = true;
168         else if (optionMatch("--help", argv[i]) || optionMatch("-h", argv[i])) {
169             printf("\nOther options:\n");
170             printf("\t--show-images\n"
171                    "\t\tDisplay test images in viewer after tests complete.\n");
172             printf("\t--save-images\n"
173                    "\t\tSave tests images as ppm files in current working "
174                    "directory.\n"
175                    "\t\tUsed to generate golden images for compare-images.\n");
176             printf(
177                 "\t--compare-images\n"
178                 "\t\tCompare test images to 'golden' image in golden folder.\n"
179                 "\t\tAlso saves the generated test image in current working\n"
180                 "\t\t\tdirectory but only if the image is different from the "
181                 "golden\n"
182                 "\t\tSetting RENDERTEST_GOLDEN_DIR environment variable can "
183                 "specify\n"
184                 "\t\t\tdifferent directory for golden images\n"
185                 "\t\tSignal test failure if different.\n");
186             printf(
187                 "\t--no-SPV\n"
188                 "\t\tUse built-in GLSL compiler rather than SPV code path.\n");
189             printf("\t--strip-SPV\n"
190                    "\t\tStrip SPIR-V debug information (line numbers, names, "
191                    "etc).\n");
192             printf(
193                 "\t--canonicalize-SPV\n"
194                 "\t\tRemap SPIR-V ids before submission to aid compression.\n");
195             exit(0);
196         } else {
197             printf("\nUnrecognized option: %s\n", argv[i]);
198             printf("\nUse --help or -h for option list.\n");
199             exit(0);
200         }
201 
202         /*
203          * Since the above "consume" inputs, update argv
204          * so that it contains the trimmed list of args for glutInit
205          */
206 
207         argv[n] = argv[i];
208         n++;
209     }
210 }
211 
GetFormat(VkInstance instance,vk_testing::Device * device)212 VkFormat VkTestFramework::GetFormat(VkInstance instance,
213                                     vk_testing::Device *device) {
214     VkFormatProperties format_props;
215 
216     vkGetPhysicalDeviceFormatProperties(
217         device->phy().handle(), VK_FORMAT_B8G8R8A8_UNORM, &format_props);
218     if (format_props.linearTilingFeatures &
219             VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT ||
220         format_props.optimalTilingFeatures &
221             VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT) {
222         return VK_FORMAT_B8G8R8A8_UNORM;
223     }
224     vkGetPhysicalDeviceFormatProperties(
225         device->phy().handle(), VK_FORMAT_R8G8B8A8_UNORM, &format_props);
226     if (format_props.linearTilingFeatures &
227             VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT ||
228         format_props.optimalTilingFeatures &
229             VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT) {
230         return VK_FORMAT_R8G8B8A8_UNORM;
231     }
232     printf("Error - device does not support VK_FORMAT_B8G8R8A8_UNORM nor "
233            "VK_FORMAT_R8G8B8A8_UNORM - exiting\n");
234     exit(0);
235 }
236 
Finish()237 void VkTestFramework::Finish() {}
238 
239 //
240 // These are the default resources for TBuiltInResources, used for both
241 //  - parsing this string for the case where the user didn't supply one
242 //  - dumping out a template for user construction of a config file
243 //
244 static const char *DefaultConfig =
245     "MaxLights 32\n"
246     "MaxClipPlanes 6\n"
247     "MaxTextureUnits 32\n"
248     "MaxTextureCoords 32\n"
249     "MaxVertexAttribs 64\n"
250     "MaxVertexUniformComponents 4096\n"
251     "MaxVaryingFloats 64\n"
252     "MaxVertexTextureImageUnits 32\n"
253     "MaxCombinedTextureImageUnits 80\n"
254     "MaxTextureImageUnits 32\n"
255     "MaxFragmentUniformComponents 4096\n"
256     "MaxDrawBuffers 32\n"
257     "MaxVertexUniformVectors 128\n"
258     "MaxVaryingVectors 8\n"
259     "MaxFragmentUniformVectors 16\n"
260     "MaxVertexOutputVectors 16\n"
261     "MaxFragmentInputVectors 15\n"
262     "MinProgramTexelOffset -8\n"
263     "MaxProgramTexelOffset 7\n"
264     "MaxClipDistances 8\n"
265     "MaxComputeWorkGroupCountX 65535\n"
266     "MaxComputeWorkGroupCountY 65535\n"
267     "MaxComputeWorkGroupCountZ 65535\n"
268     "MaxComputeWorkGroupSizeX 1024\n"
269     "MaxComputeWorkGroupSizeY 1024\n"
270     "MaxComputeWorkGroupSizeZ 64\n"
271     "MaxComputeUniformComponents 1024\n"
272     "MaxComputeTextureImageUnits 16\n"
273     "MaxComputeImageUniforms 8\n"
274     "MaxComputeAtomicCounters 8\n"
275     "MaxComputeAtomicCounterBuffers 1\n"
276     "MaxVaryingComponents 60\n"
277     "MaxVertexOutputComponents 64\n"
278     "MaxGeometryInputComponents 64\n"
279     "MaxGeometryOutputComponents 128\n"
280     "MaxFragmentInputComponents 128\n"
281     "MaxImageUnits 8\n"
282     "MaxCombinedImageUnitsAndFragmentOutputs 8\n"
283     "MaxCombinedShaderOutputResources 8\n"
284     "MaxImageSamples 0\n"
285     "MaxVertexImageUniforms 0\n"
286     "MaxTessControlImageUniforms 0\n"
287     "MaxTessEvaluationImageUniforms 0\n"
288     "MaxGeometryImageUniforms 0\n"
289     "MaxFragmentImageUniforms 8\n"
290     "MaxCombinedImageUniforms 8\n"
291     "MaxGeometryTextureImageUnits 16\n"
292     "MaxGeometryOutputVertices 256\n"
293     "MaxGeometryTotalOutputComponents 1024\n"
294     "MaxGeometryUniformComponents 1024\n"
295     "MaxGeometryVaryingComponents 64\n"
296     "MaxTessControlInputComponents 128\n"
297     "MaxTessControlOutputComponents 128\n"
298     "MaxTessControlTextureImageUnits 16\n"
299     "MaxTessControlUniformComponents 1024\n"
300     "MaxTessControlTotalOutputComponents 4096\n"
301     "MaxTessEvaluationInputComponents 128\n"
302     "MaxTessEvaluationOutputComponents 128\n"
303     "MaxTessEvaluationTextureImageUnits 16\n"
304     "MaxTessEvaluationUniformComponents 1024\n"
305     "MaxTessPatchComponents 120\n"
306     "MaxPatchVertices 32\n"
307     "MaxTessGenLevel 64\n"
308     "MaxViewports 16\n"
309     "MaxVertexAtomicCounters 0\n"
310     "MaxTessControlAtomicCounters 0\n"
311     "MaxTessEvaluationAtomicCounters 0\n"
312     "MaxGeometryAtomicCounters 0\n"
313     "MaxFragmentAtomicCounters 8\n"
314     "MaxCombinedAtomicCounters 8\n"
315     "MaxAtomicCounterBindings 1\n"
316     "MaxVertexAtomicCounterBuffers 0\n"
317     "MaxTessControlAtomicCounterBuffers 0\n"
318     "MaxTessEvaluationAtomicCounterBuffers 0\n"
319     "MaxGeometryAtomicCounterBuffers 0\n"
320     "MaxFragmentAtomicCounterBuffers 1\n"
321     "MaxCombinedAtomicCounterBuffers 1\n"
322     "MaxAtomicCounterBufferSize 16384\n"
323     "MaxTransformFeedbackBuffers 4\n"
324     "MaxTransformFeedbackInterleavedComponents 64\n"
325     "MaxCullDistances 8\n"
326     "MaxCombinedClipAndCullDistances 8\n"
327     "MaxSamples 4\n"
328 
329     "nonInductiveForLoops 1\n"
330     "whileLoops 1\n"
331     "doWhileLoops 1\n"
332     "generalUniformIndexing 1\n"
333     "generalAttributeMatrixVectorIndexing 1\n"
334     "generalVaryingIndexing 1\n"
335     "generalSamplerIndexing 1\n"
336     "generalVariableIndexing 1\n"
337     "generalConstantMatrixVectorIndexing 1\n";
338 
339 //
340 // *.conf => this is a config file that can set limits/resources
341 //
SetConfigFile(const std::string & name)342 bool VkTestFramework::SetConfigFile(const std::string &name) {
343     if (name.size() < 5)
344         return false;
345 
346     if (name.compare(name.size() - 5, 5, ".conf") == 0) {
347         ConfigFile = name;
348         return true;
349     }
350 
351     return false;
352 }
353 
354 //
355 // Parse either a .conf file provided by the user or the default string above.
356 //
ProcessConfigFile()357 void VkTestFramework::ProcessConfigFile() {
358     char **configStrings = 0;
359     char *config = 0;
360     if (ConfigFile.size() > 0) {
361         configStrings = ReadFileData(ConfigFile.c_str());
362         if (configStrings)
363             config = *configStrings;
364         else {
365             printf("Error opening configuration file; will instead use the "
366                    "default configuration\n");
367         }
368     }
369 
370     if (config == 0) {
371         config = (char *)alloca(strlen(DefaultConfig) + 1);
372         strcpy(config, DefaultConfig);
373     }
374 
375     const char *delims = " \t\n\r";
376     const char *token = strtok(config, delims);
377     while (token) {
378         const char *valueStr = strtok(0, delims);
379         if (valueStr == 0 ||
380             !(valueStr[0] == '-' ||
381               (valueStr[0] >= '0' && valueStr[0] <= '9'))) {
382             printf("Error: '%s' bad .conf file.  Each name must be followed by "
383                    "one number.\n",
384                    valueStr ? valueStr : "");
385             return;
386         }
387         int value = atoi(valueStr);
388 
389         if (strcmp(token, "MaxLights") == 0)
390             Resources.maxLights = value;
391         else if (strcmp(token, "MaxClipPlanes") == 0)
392             Resources.maxClipPlanes = value;
393         else if (strcmp(token, "MaxTextureUnits") == 0)
394             Resources.maxTextureUnits = value;
395         else if (strcmp(token, "MaxTextureCoords") == 0)
396             Resources.maxTextureCoords = value;
397         else if (strcmp(token, "MaxVertexAttribs") == 0)
398             Resources.maxVertexAttribs = value;
399         else if (strcmp(token, "MaxVertexUniformComponents") == 0)
400             Resources.maxVertexUniformComponents = value;
401         else if (strcmp(token, "MaxVaryingFloats") == 0)
402             Resources.maxVaryingFloats = value;
403         else if (strcmp(token, "MaxVertexTextureImageUnits") == 0)
404             Resources.maxVertexTextureImageUnits = value;
405         else if (strcmp(token, "MaxCombinedTextureImageUnits") == 0)
406             Resources.maxCombinedTextureImageUnits = value;
407         else if (strcmp(token, "MaxTextureImageUnits") == 0)
408             Resources.maxTextureImageUnits = value;
409         else if (strcmp(token, "MaxFragmentUniformComponents") == 0)
410             Resources.maxFragmentUniformComponents = value;
411         else if (strcmp(token, "MaxDrawBuffers") == 0)
412             Resources.maxDrawBuffers = value;
413         else if (strcmp(token, "MaxVertexUniformVectors") == 0)
414             Resources.maxVertexUniformVectors = value;
415         else if (strcmp(token, "MaxVaryingVectors") == 0)
416             Resources.maxVaryingVectors = value;
417         else if (strcmp(token, "MaxFragmentUniformVectors") == 0)
418             Resources.maxFragmentUniformVectors = value;
419         else if (strcmp(token, "MaxVertexOutputVectors") == 0)
420             Resources.maxVertexOutputVectors = value;
421         else if (strcmp(token, "MaxFragmentInputVectors") == 0)
422             Resources.maxFragmentInputVectors = value;
423         else if (strcmp(token, "MinProgramTexelOffset") == 0)
424             Resources.minProgramTexelOffset = value;
425         else if (strcmp(token, "MaxProgramTexelOffset") == 0)
426             Resources.maxProgramTexelOffset = value;
427         else if (strcmp(token, "MaxClipDistances") == 0)
428             Resources.maxClipDistances = value;
429         else if (strcmp(token, "MaxComputeWorkGroupCountX") == 0)
430             Resources.maxComputeWorkGroupCountX = value;
431         else if (strcmp(token, "MaxComputeWorkGroupCountY") == 0)
432             Resources.maxComputeWorkGroupCountY = value;
433         else if (strcmp(token, "MaxComputeWorkGroupCountZ") == 0)
434             Resources.maxComputeWorkGroupCountZ = value;
435         else if (strcmp(token, "MaxComputeWorkGroupSizeX") == 0)
436             Resources.maxComputeWorkGroupSizeX = value;
437         else if (strcmp(token, "MaxComputeWorkGroupSizeY") == 0)
438             Resources.maxComputeWorkGroupSizeY = value;
439         else if (strcmp(token, "MaxComputeWorkGroupSizeZ") == 0)
440             Resources.maxComputeWorkGroupSizeZ = value;
441         else if (strcmp(token, "MaxComputeUniformComponents") == 0)
442             Resources.maxComputeUniformComponents = value;
443         else if (strcmp(token, "MaxComputeTextureImageUnits") == 0)
444             Resources.maxComputeTextureImageUnits = value;
445         else if (strcmp(token, "MaxComputeImageUniforms") == 0)
446             Resources.maxComputeImageUniforms = value;
447         else if (strcmp(token, "MaxComputeAtomicCounters") == 0)
448             Resources.maxComputeAtomicCounters = value;
449         else if (strcmp(token, "MaxComputeAtomicCounterBuffers") == 0)
450             Resources.maxComputeAtomicCounterBuffers = value;
451         else if (strcmp(token, "MaxVaryingComponents") == 0)
452             Resources.maxVaryingComponents = value;
453         else if (strcmp(token, "MaxVertexOutputComponents") == 0)
454             Resources.maxVertexOutputComponents = value;
455         else if (strcmp(token, "MaxGeometryInputComponents") == 0)
456             Resources.maxGeometryInputComponents = value;
457         else if (strcmp(token, "MaxGeometryOutputComponents") == 0)
458             Resources.maxGeometryOutputComponents = value;
459         else if (strcmp(token, "MaxFragmentInputComponents") == 0)
460             Resources.maxFragmentInputComponents = value;
461         else if (strcmp(token, "MaxImageUnits") == 0)
462             Resources.maxImageUnits = value;
463         else if (strcmp(token, "MaxCombinedImageUnitsAndFragmentOutputs") == 0)
464             Resources.maxCombinedImageUnitsAndFragmentOutputs = value;
465         else if (strcmp(token, "MaxCombinedShaderOutputResources") == 0)
466             Resources.maxCombinedShaderOutputResources = value;
467         else if (strcmp(token, "MaxImageSamples") == 0)
468             Resources.maxImageSamples = value;
469         else if (strcmp(token, "MaxVertexImageUniforms") == 0)
470             Resources.maxVertexImageUniforms = value;
471         else if (strcmp(token, "MaxTessControlImageUniforms") == 0)
472             Resources.maxTessControlImageUniforms = value;
473         else if (strcmp(token, "MaxTessEvaluationImageUniforms") == 0)
474             Resources.maxTessEvaluationImageUniforms = value;
475         else if (strcmp(token, "MaxGeometryImageUniforms") == 0)
476             Resources.maxGeometryImageUniforms = value;
477         else if (strcmp(token, "MaxFragmentImageUniforms") == 0)
478             Resources.maxFragmentImageUniforms = value;
479         else if (strcmp(token, "MaxCombinedImageUniforms") == 0)
480             Resources.maxCombinedImageUniforms = value;
481         else if (strcmp(token, "MaxGeometryTextureImageUnits") == 0)
482             Resources.maxGeometryTextureImageUnits = value;
483         else if (strcmp(token, "MaxGeometryOutputVertices") == 0)
484             Resources.maxGeometryOutputVertices = value;
485         else if (strcmp(token, "MaxGeometryTotalOutputComponents") == 0)
486             Resources.maxGeometryTotalOutputComponents = value;
487         else if (strcmp(token, "MaxGeometryUniformComponents") == 0)
488             Resources.maxGeometryUniformComponents = value;
489         else if (strcmp(token, "MaxGeometryVaryingComponents") == 0)
490             Resources.maxGeometryVaryingComponents = value;
491         else if (strcmp(token, "MaxTessControlInputComponents") == 0)
492             Resources.maxTessControlInputComponents = value;
493         else if (strcmp(token, "MaxTessControlOutputComponents") == 0)
494             Resources.maxTessControlOutputComponents = value;
495         else if (strcmp(token, "MaxTessControlTextureImageUnits") == 0)
496             Resources.maxTessControlTextureImageUnits = value;
497         else if (strcmp(token, "MaxTessControlUniformComponents") == 0)
498             Resources.maxTessControlUniformComponents = value;
499         else if (strcmp(token, "MaxTessControlTotalOutputComponents") == 0)
500             Resources.maxTessControlTotalOutputComponents = value;
501         else if (strcmp(token, "MaxTessEvaluationInputComponents") == 0)
502             Resources.maxTessEvaluationInputComponents = value;
503         else if (strcmp(token, "MaxTessEvaluationOutputComponents") == 0)
504             Resources.maxTessEvaluationOutputComponents = value;
505         else if (strcmp(token, "MaxTessEvaluationTextureImageUnits") == 0)
506             Resources.maxTessEvaluationTextureImageUnits = value;
507         else if (strcmp(token, "MaxTessEvaluationUniformComponents") == 0)
508             Resources.maxTessEvaluationUniformComponents = value;
509         else if (strcmp(token, "MaxTessPatchComponents") == 0)
510             Resources.maxTessPatchComponents = value;
511         else if (strcmp(token, "MaxPatchVertices") == 0)
512             Resources.maxPatchVertices = value;
513         else if (strcmp(token, "MaxTessGenLevel") == 0)
514             Resources.maxTessGenLevel = value;
515         else if (strcmp(token, "MaxViewports") == 0)
516             Resources.maxViewports = value;
517         else if (strcmp(token, "MaxVertexAtomicCounters") == 0)
518             Resources.maxVertexAtomicCounters = value;
519         else if (strcmp(token, "MaxTessControlAtomicCounters") == 0)
520             Resources.maxTessControlAtomicCounters = value;
521         else if (strcmp(token, "MaxTessEvaluationAtomicCounters") == 0)
522             Resources.maxTessEvaluationAtomicCounters = value;
523         else if (strcmp(token, "MaxGeometryAtomicCounters") == 0)
524             Resources.maxGeometryAtomicCounters = value;
525         else if (strcmp(token, "MaxFragmentAtomicCounters") == 0)
526             Resources.maxFragmentAtomicCounters = value;
527         else if (strcmp(token, "MaxCombinedAtomicCounters") == 0)
528             Resources.maxCombinedAtomicCounters = value;
529         else if (strcmp(token, "MaxAtomicCounterBindings") == 0)
530             Resources.maxAtomicCounterBindings = value;
531         else if (strcmp(token, "MaxVertexAtomicCounterBuffers") == 0)
532             Resources.maxVertexAtomicCounterBuffers = value;
533         else if (strcmp(token, "MaxTessControlAtomicCounterBuffers") == 0)
534             Resources.maxTessControlAtomicCounterBuffers = value;
535         else if (strcmp(token, "MaxTessEvaluationAtomicCounterBuffers") == 0)
536             Resources.maxTessEvaluationAtomicCounterBuffers = value;
537         else if (strcmp(token, "MaxGeometryAtomicCounterBuffers") == 0)
538             Resources.maxGeometryAtomicCounterBuffers = value;
539         else if (strcmp(token, "MaxFragmentAtomicCounterBuffers") == 0)
540             Resources.maxFragmentAtomicCounterBuffers = value;
541         else if (strcmp(token, "MaxCombinedAtomicCounterBuffers") == 0)
542             Resources.maxCombinedAtomicCounterBuffers = value;
543         else if (strcmp(token, "MaxAtomicCounterBufferSize") == 0)
544             Resources.maxAtomicCounterBufferSize = value;
545         else if (strcmp(token, "MaxTransformFeedbackBuffers") == 0)
546             Resources.maxTransformFeedbackBuffers = value;
547         else if (strcmp(token, "MaxTransformFeedbackInterleavedComponents") ==
548                  0)
549             Resources.maxTransformFeedbackInterleavedComponents = value;
550         else if (strcmp(token, "MaxCullDistances") == 0)
551             Resources.maxCullDistances = value;
552         else if (strcmp(token, "MaxCombinedClipAndCullDistances") == 0)
553             Resources.maxCombinedClipAndCullDistances = value;
554         else if (strcmp(token, "MaxSamples") == 0)
555             Resources.maxSamples = value;
556 
557         else if (strcmp(token, "nonInductiveForLoops") == 0)
558             Resources.limits.nonInductiveForLoops = (value != 0);
559         else if (strcmp(token, "whileLoops") == 0)
560             Resources.limits.whileLoops = (value != 0);
561         else if (strcmp(token, "doWhileLoops") == 0)
562             Resources.limits.doWhileLoops = (value != 0);
563         else if (strcmp(token, "generalUniformIndexing") == 0)
564             Resources.limits.generalUniformIndexing = (value != 0);
565         else if (strcmp(token, "generalAttributeMatrixVectorIndexing") == 0)
566             Resources.limits.generalAttributeMatrixVectorIndexing =
567                 (value != 0);
568         else if (strcmp(token, "generalVaryingIndexing") == 0)
569             Resources.limits.generalVaryingIndexing = (value != 0);
570         else if (strcmp(token, "generalSamplerIndexing") == 0)
571             Resources.limits.generalSamplerIndexing = (value != 0);
572         else if (strcmp(token, "generalVariableIndexing") == 0)
573             Resources.limits.generalVariableIndexing = (value != 0);
574         else if (strcmp(token, "generalConstantMatrixVectorIndexing") == 0)
575             Resources.limits.generalConstantMatrixVectorIndexing = (value != 0);
576         else
577             printf("Warning: unrecognized limit (%s) in configuration file.\n",
578                    token);
579 
580         token = strtok(0, delims);
581     }
582     if (configStrings)
583         FreeFileData(configStrings);
584 }
585 
SetMessageOptions(EShMessages & messages)586 void VkTestFramework::SetMessageOptions(EShMessages &messages) {
587     if (m_compile_options & EOptionRelaxedErrors)
588         messages = (EShMessages)(messages | EShMsgRelaxedErrors);
589     if (m_compile_options & EOptionIntermediate)
590         messages = (EShMessages)(messages | EShMsgAST);
591     if (m_compile_options & EOptionSuppressWarnings)
592         messages = (EShMessages)(messages | EShMsgSuppressWarnings);
593 }
594 
595 //
596 //   Malloc a string of sufficient size and read a string into it.
597 //
ReadFileData(const char * fileName)598 char **VkTestFramework::ReadFileData(const char *fileName) {
599     FILE *in;
600 #if defined(_WIN32) && defined(__GNUC__)
601     in = fopen(fileName, "r");
602     int errorCode = in ? 0 : 1;
603 #else
604     int errorCode = fopen_s(&in, fileName, "r");
605 #endif
606 
607     char *fdata;
608     int count = 0;
609     const int maxSourceStrings = 5;
610     char **return_data =
611         (char **)malloc(sizeof(char *) * (maxSourceStrings + 1));
612 
613     if (errorCode) {
614         printf("Error: unable to open input file: %s\n", fileName);
615         return 0;
616     }
617 
618     while (fgetc(in) != EOF)
619         count++;
620 
621     fseek(in, 0, SEEK_SET);
622 
623     if (!(fdata = (char *)malloc(count + 2))) {
624         printf("Error allocating memory\n");
625         return 0;
626     }
627     if (fread(fdata, 1, count, in) != count) {
628         printf("Error reading input file: %s\n", fileName);
629         return 0;
630     }
631     fdata[count] = '\0';
632     fclose(in);
633     if (count == 0) {
634         return_data[0] = (char *)malloc(count + 2);
635         return_data[0][0] = '\0';
636         m_num_shader_strings = 0;
637         return return_data;
638     } else
639         m_num_shader_strings = 1;
640 
641     int len = (int)(ceil)((float)count / (float)m_num_shader_strings);
642     int ptr_len = 0, i = 0;
643     while (count > 0) {
644         return_data[i] = (char *)malloc(len + 2);
645         memcpy(return_data[i], fdata + ptr_len, len);
646         return_data[i][len] = '\0';
647         count -= (len);
648         ptr_len += (len);
649         if (count < len) {
650             if (count == 0) {
651                 m_num_shader_strings = (i + 1);
652                 break;
653             }
654             len = count;
655         }
656         ++i;
657     }
658     return return_data;
659 }
660 
FreeFileData(char ** data)661 void VkTestFramework::FreeFileData(char **data) {
662     for (int i = 0; i < m_num_shader_strings; i++)
663         free(data[i]);
664 }
665 
666 //
667 //   Deduce the language from the filename.  Files must end in one of the
668 //   following extensions:
669 //
670 //   .vert = vertex
671 //   .tesc = tessellation control
672 //   .tese = tessellation evaluation
673 //   .geom = geometry
674 //   .frag = fragment
675 //   .comp = compute
676 //
FindLanguage(const std::string & name)677 EShLanguage VkTestFramework::FindLanguage(const std::string &name) {
678     size_t ext = name.rfind('.');
679     if (ext == std::string::npos) {
680         return EShLangVertex;
681     }
682 
683     std::string suffix = name.substr(ext + 1, std::string::npos);
684     if (suffix == "vert")
685         return EShLangVertex;
686     else if (suffix == "tesc")
687         return EShLangTessControl;
688     else if (suffix == "tese")
689         return EShLangTessEvaluation;
690     else if (suffix == "geom")
691         return EShLangGeometry;
692     else if (suffix == "frag")
693         return EShLangFragment;
694     else if (suffix == "comp")
695         return EShLangCompute;
696 
697     return EShLangVertex;
698 }
699 
700 //
701 // Convert VK shader type to compiler's
702 //
703 EShLanguage
FindLanguage(const VkShaderStageFlagBits shader_type)704 VkTestFramework::FindLanguage(const VkShaderStageFlagBits shader_type) {
705     switch (shader_type) {
706     case VK_SHADER_STAGE_VERTEX_BIT:
707         return EShLangVertex;
708 
709     case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT:
710         return EShLangTessControl;
711 
712     case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT:
713         return EShLangTessEvaluation;
714 
715     case VK_SHADER_STAGE_GEOMETRY_BIT:
716         return EShLangGeometry;
717 
718     case VK_SHADER_STAGE_FRAGMENT_BIT:
719         return EShLangFragment;
720 
721     case VK_SHADER_STAGE_COMPUTE_BIT:
722         return EShLangCompute;
723 
724     default:
725         return EShLangVertex;
726     }
727 }
728 
729 //
730 // Compile a given string containing GLSL into SPV for use by VK
731 // Return value of false means an error was encountered.
732 //
GLSLtoSPV(const VkShaderStageFlagBits shader_type,const char * pshader,std::vector<unsigned int> & spirv)733 bool VkTestFramework::GLSLtoSPV(const VkShaderStageFlagBits shader_type,
734                                 const char *pshader,
735                                 std::vector<unsigned int> &spirv) {
736     glslang::TProgram program;
737     const char *shaderStrings[1];
738 
739     // TODO: Do we want to load a special config file depending on the
740     // shader source? Optional name maybe?
741     //    SetConfigFile(fileName);
742 
743     ProcessConfigFile();
744 
745     EShMessages messages = EShMsgDefault;
746     SetMessageOptions(messages);
747     messages =
748         static_cast<EShMessages>(messages | EShMsgSpvRules | EShMsgVulkanRules);
749 
750     EShLanguage stage = FindLanguage(shader_type);
751     glslang::TShader *shader = new glslang::TShader(stage);
752 
753     shaderStrings[0] = pshader;
754     shader->setStrings(shaderStrings, 1);
755 
756     if (!shader->parse(&Resources,
757                        (m_compile_options & EOptionDefaultDesktop) ? 110 : 100,
758                        false, messages)) {
759 
760         if (!(m_compile_options & EOptionSuppressInfolog)) {
761             puts(shader->getInfoLog());
762             puts(shader->getInfoDebugLog());
763         }
764 
765         return false; // something didn't work
766     }
767 
768     program.addShader(shader);
769 
770     //
771     // Program-level processing...
772     //
773 
774     if (!program.link(messages)) {
775 
776         if (!(m_compile_options & EOptionSuppressInfolog)) {
777             puts(shader->getInfoLog());
778             puts(shader->getInfoDebugLog());
779         }
780 
781         return false;
782     }
783 
784     if (m_compile_options & EOptionDumpReflection) {
785         program.buildReflection();
786         program.dumpReflection();
787     }
788 
789     glslang::GlslangToSpv(*program.getIntermediate(stage), spirv);
790 
791     //
792     // Test the different modes of SPIR-V modification
793     //
794     if (this->m_canonicalize_spv) {
795         spv::spirvbin_t(0).remap(spirv, spv::spirvbin_t::ALL_BUT_STRIP);
796     }
797 
798     if (this->m_strip_spv) {
799         spv::spirvbin_t(0).remap(spirv, spv::spirvbin_t::STRIP);
800     }
801 
802     if (this->m_do_everything_spv) {
803         spv::spirvbin_t(0).remap(spirv, spv::spirvbin_t::DO_EVERYTHING);
804     }
805 
806     delete shader;
807 
808     return true;
809 }
810