Home | History | Annotate | Download | only in tests
      1 /*
      2  * Copyright (c) 2015-2019 The Khronos Group Inc.
      3  * Copyright (c) 2015-2019 Valve Corporation
      4  * Copyright (c) 2015-2019 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 (at) gmail.com>
     19  * Author: Courtney Goeltzenleuchter <courtney (at) LunarG.com>
     20  * Author: Tony Barbour <tony (at) 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
     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 struct SwapchainBuffers {
     98     VkImage image;
     99     VkCommandBuffer cmd;
    100     VkImageView view;
    101 };
    102 
    103 #ifndef _WIN32
    104 
    105 #include <errno.h>
    106 
    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
    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 
    136 void TestEnvironment::TearDown() { glslang::FinalizeProcess(); }
    137 
    138 VkTestFramework::VkTestFramework() : m_compile_options(0), m_num_shader_strings(0) {}
    139 
    140 VkTestFramework::~VkTestFramework() {}
    141 
    142 // Define all the static elements
    143 bool VkTestFramework::m_canonicalize_spv = false;
    144 bool VkTestFramework::m_strip_spv = false;
    145 bool VkTestFramework::m_do_everything_spv = false;
    146 bool VkTestFramework::m_devsim_layer = false;
    147 int VkTestFramework::m_width = 0;
    148 int VkTestFramework::m_height = 0;
    149 
    150 bool 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 
    157 void 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 
    210 VkFormat 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 
    227 void 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 //
    234 static 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     "MaxMeshOutputVerticesNV 256\n"
    319     "MaxMeshOutputPrimitivesNV 512\n"
    320     "MaxMeshWorkGroupSizeX_NV 32\n"
    321     "MaxMeshWorkGroupSizeY_NV 1\n"
    322     "MaxMeshWorkGroupSizeZ_NV 1\n"
    323     "MaxTaskWorkGroupSizeX_NV 32\n"
    324     "MaxTaskWorkGroupSizeY_NV 1\n"
    325     "MaxTaskWorkGroupSizeZ_NV 1\n"
    326     "MaxMeshViewCountNV 4\n"
    327 
    328     "nonInductiveForLoops 1\n"
    329     "whileLoops 1\n"
    330     "doWhileLoops 1\n"
    331     "generalUniformIndexing 1\n"
    332     "generalAttributeMatrixVectorIndexing 1\n"
    333     "generalVaryingIndexing 1\n"
    334     "generalSamplerIndexing 1\n"
    335     "generalVariableIndexing 1\n"
    336     "generalConstantMatrixVectorIndexing 1\n";
    337 
    338 //
    339 // *.conf => this is a config file that can set limits/resources
    340 //
    341 bool VkTestFramework::SetConfigFile(const std::string &name) {
    342     if (name.size() < 5) return false;
    343 
    344     if (name.compare(name.size() - 5, 5, ".conf") == 0) {
    345         ConfigFile = name;
    346         return true;
    347     }
    348 
    349     return false;
    350 }
    351 
    352 //
    353 // Parse either a .conf file provided by the user or the default string above.
    354 //
    355 void VkTestFramework::ProcessConfigFile() {
    356     char **configStrings = 0;
    357     char *config = 0;
    358     if (ConfigFile.size() > 0) {
    359         configStrings = ReadFileData(ConfigFile.c_str());
    360         if (configStrings)
    361             config = *configStrings;
    362         else {
    363             printf("Error opening configuration file; will instead use the default configuration\n");
    364         }
    365     }
    366 
    367     if (config == 0) {
    368         config = (char *)alloca(strlen(DefaultConfig) + 1);
    369         strcpy(config, DefaultConfig);
    370     }
    371 
    372     const char *delims = " \t\n\r";
    373     const char *token = strtok(config, delims);
    374     while (token) {
    375         const char *valueStr = strtok(0, delims);
    376         if (valueStr == 0 || !(valueStr[0] == '-' || (valueStr[0] >= '0' && valueStr[0] <= '9'))) {
    377             printf("Error: '%s' bad .conf file.  Each name must be followed by one number.\n", valueStr ? valueStr : "");
    378             return;
    379         }
    380         int value = atoi(valueStr);
    381 
    382         if (strcmp(token, "MaxLights") == 0)
    383             Resources.maxLights = value;
    384         else if (strcmp(token, "MaxClipPlanes") == 0)
    385             Resources.maxClipPlanes = value;
    386         else if (strcmp(token, "MaxTextureUnits") == 0)
    387             Resources.maxTextureUnits = value;
    388         else if (strcmp(token, "MaxTextureCoords") == 0)
    389             Resources.maxTextureCoords = value;
    390         else if (strcmp(token, "MaxVertexAttribs") == 0)
    391             Resources.maxVertexAttribs = value;
    392         else if (strcmp(token, "MaxVertexUniformComponents") == 0)
    393             Resources.maxVertexUniformComponents = value;
    394         else if (strcmp(token, "MaxVaryingFloats") == 0)
    395             Resources.maxVaryingFloats = value;
    396         else if (strcmp(token, "MaxVertexTextureImageUnits") == 0)
    397             Resources.maxVertexTextureImageUnits = value;
    398         else if (strcmp(token, "MaxCombinedTextureImageUnits") == 0)
    399             Resources.maxCombinedTextureImageUnits = value;
    400         else if (strcmp(token, "MaxTextureImageUnits") == 0)
    401             Resources.maxTextureImageUnits = value;
    402         else if (strcmp(token, "MaxFragmentUniformComponents") == 0)
    403             Resources.maxFragmentUniformComponents = value;
    404         else if (strcmp(token, "MaxDrawBuffers") == 0)
    405             Resources.maxDrawBuffers = value;
    406         else if (strcmp(token, "MaxVertexUniformVectors") == 0)
    407             Resources.maxVertexUniformVectors = value;
    408         else if (strcmp(token, "MaxVaryingVectors") == 0)
    409             Resources.maxVaryingVectors = value;
    410         else if (strcmp(token, "MaxFragmentUniformVectors") == 0)
    411             Resources.maxFragmentUniformVectors = value;
    412         else if (strcmp(token, "MaxVertexOutputVectors") == 0)
    413             Resources.maxVertexOutputVectors = value;
    414         else if (strcmp(token, "MaxFragmentInputVectors") == 0)
    415             Resources.maxFragmentInputVectors = value;
    416         else if (strcmp(token, "MinProgramTexelOffset") == 0)
    417             Resources.minProgramTexelOffset = value;
    418         else if (strcmp(token, "MaxProgramTexelOffset") == 0)
    419             Resources.maxProgramTexelOffset = value;
    420         else if (strcmp(token, "MaxClipDistances") == 0)
    421             Resources.maxClipDistances = value;
    422         else if (strcmp(token, "MaxComputeWorkGroupCountX") == 0)
    423             Resources.maxComputeWorkGroupCountX = value;
    424         else if (strcmp(token, "MaxComputeWorkGroupCountY") == 0)
    425             Resources.maxComputeWorkGroupCountY = value;
    426         else if (strcmp(token, "MaxComputeWorkGroupCountZ") == 0)
    427             Resources.maxComputeWorkGroupCountZ = value;
    428         else if (strcmp(token, "MaxComputeWorkGroupSizeX") == 0)
    429             Resources.maxComputeWorkGroupSizeX = value;
    430         else if (strcmp(token, "MaxComputeWorkGroupSizeY") == 0)
    431             Resources.maxComputeWorkGroupSizeY = value;
    432         else if (strcmp(token, "MaxComputeWorkGroupSizeZ") == 0)
    433             Resources.maxComputeWorkGroupSizeZ = value;
    434         else if (strcmp(token, "MaxComputeUniformComponents") == 0)
    435             Resources.maxComputeUniformComponents = value;
    436         else if (strcmp(token, "MaxComputeTextureImageUnits") == 0)
    437             Resources.maxComputeTextureImageUnits = value;
    438         else if (strcmp(token, "MaxComputeImageUniforms") == 0)
    439             Resources.maxComputeImageUniforms = value;
    440         else if (strcmp(token, "MaxComputeAtomicCounters") == 0)
    441             Resources.maxComputeAtomicCounters = value;
    442         else if (strcmp(token, "MaxComputeAtomicCounterBuffers") == 0)
    443             Resources.maxComputeAtomicCounterBuffers = value;
    444         else if (strcmp(token, "MaxVaryingComponents") == 0)
    445             Resources.maxVaryingComponents = value;
    446         else if (strcmp(token, "MaxVertexOutputComponents") == 0)
    447             Resources.maxVertexOutputComponents = value;
    448         else if (strcmp(token, "MaxGeometryInputComponents") == 0)
    449             Resources.maxGeometryInputComponents = value;
    450         else if (strcmp(token, "MaxGeometryOutputComponents") == 0)
    451             Resources.maxGeometryOutputComponents = value;
    452         else if (strcmp(token, "MaxFragmentInputComponents") == 0)
    453             Resources.maxFragmentInputComponents = value;
    454         else if (strcmp(token, "MaxImageUnits") == 0)
    455             Resources.maxImageUnits = value;
    456         else if (strcmp(token, "MaxCombinedImageUnitsAndFragmentOutputs") == 0)
    457             Resources.maxCombinedImageUnitsAndFragmentOutputs = value;
    458         else if (strcmp(token, "MaxCombinedShaderOutputResources") == 0)
    459             Resources.maxCombinedShaderOutputResources = value;
    460         else if (strcmp(token, "MaxImageSamples") == 0)
    461             Resources.maxImageSamples = value;
    462         else if (strcmp(token, "MaxVertexImageUniforms") == 0)
    463             Resources.maxVertexImageUniforms = value;
    464         else if (strcmp(token, "MaxTessControlImageUniforms") == 0)
    465             Resources.maxTessControlImageUniforms = value;
    466         else if (strcmp(token, "MaxTessEvaluationImageUniforms") == 0)
    467             Resources.maxTessEvaluationImageUniforms = value;
    468         else if (strcmp(token, "MaxGeometryImageUniforms") == 0)
    469             Resources.maxGeometryImageUniforms = value;
    470         else if (strcmp(token, "MaxFragmentImageUniforms") == 0)
    471             Resources.maxFragmentImageUniforms = value;
    472         else if (strcmp(token, "MaxCombinedImageUniforms") == 0)
    473             Resources.maxCombinedImageUniforms = value;
    474         else if (strcmp(token, "MaxGeometryTextureImageUnits") == 0)
    475             Resources.maxGeometryTextureImageUnits = value;
    476         else if (strcmp(token, "MaxGeometryOutputVertices") == 0)
    477             Resources.maxGeometryOutputVertices = value;
    478         else if (strcmp(token, "MaxGeometryTotalOutputComponents") == 0)
    479             Resources.maxGeometryTotalOutputComponents = value;
    480         else if (strcmp(token, "MaxGeometryUniformComponents") == 0)
    481             Resources.maxGeometryUniformComponents = value;
    482         else if (strcmp(token, "MaxGeometryVaryingComponents") == 0)
    483             Resources.maxGeometryVaryingComponents = value;
    484         else if (strcmp(token, "MaxTessControlInputComponents") == 0)
    485             Resources.maxTessControlInputComponents = value;
    486         else if (strcmp(token, "MaxTessControlOutputComponents") == 0)
    487             Resources.maxTessControlOutputComponents = value;
    488         else if (strcmp(token, "MaxTessControlTextureImageUnits") == 0)
    489             Resources.maxTessControlTextureImageUnits = value;
    490         else if (strcmp(token, "MaxTessControlUniformComponents") == 0)
    491             Resources.maxTessControlUniformComponents = value;
    492         else if (strcmp(token, "MaxTessControlTotalOutputComponents") == 0)
    493             Resources.maxTessControlTotalOutputComponents = value;
    494         else if (strcmp(token, "MaxTessEvaluationInputComponents") == 0)
    495             Resources.maxTessEvaluationInputComponents = value;
    496         else if (strcmp(token, "MaxTessEvaluationOutputComponents") == 0)
    497             Resources.maxTessEvaluationOutputComponents = value;
    498         else if (strcmp(token, "MaxTessEvaluationTextureImageUnits") == 0)
    499             Resources.maxTessEvaluationTextureImageUnits = value;
    500         else if (strcmp(token, "MaxTessEvaluationUniformComponents") == 0)
    501             Resources.maxTessEvaluationUniformComponents = value;
    502         else if (strcmp(token, "MaxTessPatchComponents") == 0)
    503             Resources.maxTessPatchComponents = value;
    504         else if (strcmp(token, "MaxPatchVertices") == 0)
    505             Resources.maxPatchVertices = value;
    506         else if (strcmp(token, "MaxTessGenLevel") == 0)
    507             Resources.maxTessGenLevel = value;
    508         else if (strcmp(token, "MaxViewports") == 0)
    509             Resources.maxViewports = value;
    510         else if (strcmp(token, "MaxVertexAtomicCounters") == 0)
    511             Resources.maxVertexAtomicCounters = value;
    512         else if (strcmp(token, "MaxTessControlAtomicCounters") == 0)
    513             Resources.maxTessControlAtomicCounters = value;
    514         else if (strcmp(token, "MaxTessEvaluationAtomicCounters") == 0)
    515             Resources.maxTessEvaluationAtomicCounters = value;
    516         else if (strcmp(token, "MaxGeometryAtomicCounters") == 0)
    517             Resources.maxGeometryAtomicCounters = value;
    518         else if (strcmp(token, "MaxFragmentAtomicCounters") == 0)
    519             Resources.maxFragmentAtomicCounters = value;
    520         else if (strcmp(token, "MaxCombinedAtomicCounters") == 0)
    521             Resources.maxCombinedAtomicCounters = value;
    522         else if (strcmp(token, "MaxAtomicCounterBindings") == 0)
    523             Resources.maxAtomicCounterBindings = value;
    524         else if (strcmp(token, "MaxVertexAtomicCounterBuffers") == 0)
    525             Resources.maxVertexAtomicCounterBuffers = value;
    526         else if (strcmp(token, "MaxTessControlAtomicCounterBuffers") == 0)
    527             Resources.maxTessControlAtomicCounterBuffers = value;
    528         else if (strcmp(token, "MaxTessEvaluationAtomicCounterBuffers") == 0)
    529             Resources.maxTessEvaluationAtomicCounterBuffers = value;
    530         else if (strcmp(token, "MaxGeometryAtomicCounterBuffers") == 0)
    531             Resources.maxGeometryAtomicCounterBuffers = value;
    532         else if (strcmp(token, "MaxFragmentAtomicCounterBuffers") == 0)
    533             Resources.maxFragmentAtomicCounterBuffers = value;
    534         else if (strcmp(token, "MaxCombinedAtomicCounterBuffers") == 0)
    535             Resources.maxCombinedAtomicCounterBuffers = value;
    536         else if (strcmp(token, "MaxAtomicCounterBufferSize") == 0)
    537             Resources.maxAtomicCounterBufferSize = value;
    538         else if (strcmp(token, "MaxTransformFeedbackBuffers") == 0)
    539             Resources.maxTransformFeedbackBuffers = value;
    540         else if (strcmp(token, "MaxTransformFeedbackInterleavedComponents") == 0)
    541             Resources.maxTransformFeedbackInterleavedComponents = value;
    542         else if (strcmp(token, "MaxCullDistances") == 0)
    543             Resources.maxCullDistances = value;
    544         else if (strcmp(token, "MaxCombinedClipAndCullDistances") == 0)
    545             Resources.maxCombinedClipAndCullDistances = value;
    546         else if (strcmp(token, "MaxSamples") == 0)
    547             Resources.maxSamples = value;
    548         else if (strcmp(token, "MaxMeshOutputVerticesNV") == 0)
    549             Resources.maxMeshOutputVerticesNV = value;
    550         else if (strcmp(token, "MaxMeshOutputPrimitivesNV") == 0)
    551             Resources.maxMeshOutputPrimitivesNV = value;
    552         else if (strcmp(token, "MaxMeshWorkGroupSizeX_NV") == 0)
    553             Resources.maxMeshWorkGroupSizeX_NV = value;
    554         else if (strcmp(token, "MaxMeshWorkGroupSizeY_NV") == 0)
    555             Resources.maxMeshWorkGroupSizeY_NV = value;
    556         else if (strcmp(token, "MaxMeshWorkGroupSizeZ_NV") == 0)
    557             Resources.maxMeshWorkGroupSizeZ_NV = value;
    558         else if (strcmp(token, "MaxTaskWorkGroupSizeX_NV") == 0)
    559             Resources.maxTaskWorkGroupSizeX_NV = value;
    560         else if (strcmp(token, "MaxTaskWorkGroupSizeY_NV") == 0)
    561             Resources.maxTaskWorkGroupSizeY_NV = value;
    562         else if (strcmp(token, "MaxTaskWorkGroupSizeZ_NV") == 0)
    563             Resources.maxTaskWorkGroupSizeZ_NV = value;
    564         else if (strcmp(token, "MaxMeshViewCountNV") == 0)
    565             Resources.maxMeshViewCountNV = value;
    566 
    567         else if (strcmp(token, "nonInductiveForLoops") == 0)
    568             Resources.limits.nonInductiveForLoops = (value != 0);
    569         else if (strcmp(token, "whileLoops") == 0)
    570             Resources.limits.whileLoops = (value != 0);
    571         else if (strcmp(token, "doWhileLoops") == 0)
    572             Resources.limits.doWhileLoops = (value != 0);
    573         else if (strcmp(token, "generalUniformIndexing") == 0)
    574             Resources.limits.generalUniformIndexing = (value != 0);
    575         else if (strcmp(token, "generalAttributeMatrixVectorIndexing") == 0)
    576             Resources.limits.generalAttributeMatrixVectorIndexing = (value != 0);
    577         else if (strcmp(token, "generalVaryingIndexing") == 0)
    578             Resources.limits.generalVaryingIndexing = (value != 0);
    579         else if (strcmp(token, "generalSamplerIndexing") == 0)
    580             Resources.limits.generalSamplerIndexing = (value != 0);
    581         else if (strcmp(token, "generalVariableIndexing") == 0)
    582             Resources.limits.generalVariableIndexing = (value != 0);
    583         else if (strcmp(token, "generalConstantMatrixVectorIndexing") == 0)
    584             Resources.limits.generalConstantMatrixVectorIndexing = (value != 0);
    585         else
    586             printf("Warning: unrecognized limit (%s) in configuration file.\n", token);
    587 
    588         token = strtok(0, delims);
    589     }
    590     if (configStrings) FreeFileData(configStrings);
    591 }
    592 
    593 void VkTestFramework::SetMessageOptions(EShMessages &messages) {
    594     if (m_compile_options & EOptionRelaxedErrors) messages = (EShMessages)(messages | EShMsgRelaxedErrors);
    595     if (m_compile_options & EOptionIntermediate) messages = (EShMessages)(messages | EShMsgAST);
    596     if (m_compile_options & EOptionSuppressWarnings) messages = (EShMessages)(messages | EShMsgSuppressWarnings);
    597 }
    598 
    599 //
    600 //   Malloc a string of sufficient size and read a string into it.
    601 //
    602 char **VkTestFramework::ReadFileData(const char *fileName) {
    603     FILE *in;
    604 #if defined(_WIN32) && defined(__GNUC__)
    605     in = fopen(fileName, "r");
    606     int errorCode = in ? 0 : 1;
    607 #else
    608     int errorCode = fopen_s(&in, fileName, "r");
    609 #endif
    610 
    611     char *fdata;
    612     size_t count = 0;
    613     const int maxSourceStrings = 5;
    614     char **return_data = (char **)malloc(sizeof(char *) * (maxSourceStrings + 1));
    615 
    616     if (errorCode) {
    617         printf("Error: unable to open input file: %s\n", fileName);
    618         return 0;
    619     }
    620 
    621     while (fgetc(in) != EOF) count++;
    622 
    623     fseek(in, 0, SEEK_SET);
    624 
    625     if (!(fdata = (char *)malloc(count + 2))) {
    626         printf("Error allocating memory\n");
    627         return 0;
    628     }
    629     if (fread(fdata, 1, count, in) != count) {
    630         printf("Error reading input file: %s\n", fileName);
    631         return 0;
    632     }
    633     fdata[count] = '\0';
    634     fclose(in);
    635     if (count == 0) {
    636         return_data[0] = (char *)malloc(count + 2);
    637         return_data[0][0] = '\0';
    638         m_num_shader_strings = 0;
    639         return return_data;
    640     } else
    641         m_num_shader_strings = 1;
    642 
    643     size_t len = (int)(ceil)((float)count / (float)m_num_shader_strings);
    644     size_t ptr_len = 0, i = 0;
    645     while (count > 0) {
    646         return_data[i] = (char *)malloc(len + 2);
    647         memcpy(return_data[i], fdata + ptr_len, len);
    648         return_data[i][len] = '\0';
    649         count -= (len);
    650         ptr_len += (len);
    651         if (count < len) {
    652             if (count == 0) {
    653                 m_num_shader_strings = (i + 1);
    654                 break;
    655             }
    656             len = count;
    657         }
    658         ++i;
    659     }
    660     return return_data;
    661 }
    662 
    663 void VkTestFramework::FreeFileData(char **data) {
    664     for (int i = 0; i < m_num_shader_strings; i++) free(data[i]);
    665 }
    666 
    667 //
    668 //   Deduce the language from the filename.  Files must end in one of the
    669 //   following extensions:
    670 //
    671 //   .vert = vertex
    672 //   .tesc = tessellation control
    673 //   .tese = tessellation evaluation
    674 //   .geom = geometry
    675 //   .frag = fragment
    676 //   .comp = compute
    677 //
    678 EShLanguage VkTestFramework::FindLanguage(const std::string &name) {
    679     size_t ext = name.rfind('.');
    680     if (ext == std::string::npos) {
    681         return EShLangVertex;
    682     }
    683 
    684     std::string suffix = name.substr(ext + 1, std::string::npos);
    685     if (suffix == "vert")
    686         return EShLangVertex;
    687     else if (suffix == "tesc")
    688         return EShLangTessControl;
    689     else if (suffix == "tese")
    690         return EShLangTessEvaluation;
    691     else if (suffix == "geom")
    692         return EShLangGeometry;
    693     else if (suffix == "frag")
    694         return EShLangFragment;
    695     else if (suffix == "comp")
    696         return EShLangCompute;
    697 
    698     return EShLangVertex;
    699 }
    700 
    701 //
    702 // Convert VK shader type to compiler's
    703 //
    704 EShLanguage 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         case VK_SHADER_STAGE_RAYGEN_BIT_NV:
    725             return EShLangRayGenNV;
    726 
    727         case VK_SHADER_STAGE_ANY_HIT_BIT_NV:
    728             return EShLangAnyHitNV;
    729 
    730         case VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV:
    731             return EShLangClosestHitNV;
    732 
    733         case VK_SHADER_STAGE_MISS_BIT_NV:
    734             return EShLangMissNV;
    735 
    736         case VK_SHADER_STAGE_INTERSECTION_BIT_NV:
    737             return EShLangIntersectNV;
    738 
    739         case VK_SHADER_STAGE_CALLABLE_BIT_NV:
    740             return EShLangCallableNV;
    741 
    742         case VK_SHADER_STAGE_TASK_BIT_NV:
    743             return EShLangTaskNV;
    744 
    745         case VK_SHADER_STAGE_MESH_BIT_NV:
    746             return EShLangMeshNV;
    747 
    748         default:
    749             return EShLangVertex;
    750     }
    751 }
    752 
    753 //
    754 // Compile a given string containing GLSL into SPV for use by VK
    755 // Return value of false means an error was encountered.
    756 //
    757 bool VkTestFramework::GLSLtoSPV(const VkShaderStageFlagBits shader_type, const char *pshader, std::vector<unsigned int> &spirv,
    758                                 bool debug) {
    759     glslang::TProgram program;
    760     const char *shaderStrings[1];
    761 
    762     // TODO: Do we want to load a special config file depending on the
    763     // shader source? Optional name maybe?
    764     //    SetConfigFile(fileName);
    765 
    766     ProcessConfigFile();
    767 
    768     EShMessages messages = EShMsgDefault;
    769     SetMessageOptions(messages);
    770     messages = static_cast<EShMessages>(messages | EShMsgSpvRules | EShMsgVulkanRules);
    771     if (debug) {
    772         messages = static_cast<EShMessages>(messages | EShMsgDebugInfo);
    773     }
    774 
    775     EShLanguage stage = FindLanguage(shader_type);
    776     glslang::TShader *shader = new glslang::TShader(stage);
    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 //
    840 bool VkTestFramework::ASMtoSPV(const spv_target_env target_env, const uint32_t options, const char *pasm,
    841                                std::vector<unsigned int> &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