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