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