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