You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
860 lines
33 KiB
860 lines
33 KiB
/*
|
|
* Copyright (c) 2015-2019 The Khronos Group Inc.
|
|
* Copyright (c) 2015-2019 Valve Corporation
|
|
* Copyright (c) 2015-2019 LunarG, Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
* Author: Chia-I Wu <olvaffe@gmail.com>
|
|
* Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
|
|
* Author: Tony Barbour <tony@LunarG.com>
|
|
*/
|
|
|
|
#include "vktestframework.h"
|
|
#include "vkrenderframework.h"
|
|
|
|
// For versions prior to VS 2015, suppress the warning
|
|
// caused by the inconsistent redefinition of snprintf
|
|
// between a vulkan header and a glslang header.
|
|
#if (defined(_MSC_VER) && _MSC_VER < 1900 /*vs2015*/)
|
|
#pragma warning(push)
|
|
#pragma warning(disable : 4005)
|
|
#endif
|
|
// TODO FIXME remove this once glslang doesn't define this
|
|
#undef BadValue
|
|
#include "SPIRV/GlslangToSpv.h"
|
|
#include "SPIRV/SPVRemapper.h"
|
|
#if (defined(_MSC_VER) && _MSC_VER < 1900 /*vs2015*/)
|
|
#pragma warning(pop)
|
|
#endif
|
|
#include <limits.h>
|
|
#include <math.h>
|
|
|
|
#if defined(PATH_MAX) && !defined(MAX_PATH)
|
|
#define MAX_PATH PATH_MAX
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
#define ERR_EXIT(err_msg, err_class) \
|
|
do { \
|
|
MessageBox(NULL, err_msg, err_class, MB_OK); \
|
|
exit(1); \
|
|
} while (0)
|
|
#else // _WIN32
|
|
|
|
#define ERR_EXIT(err_msg, err_class) \
|
|
do { \
|
|
printf(err_msg); \
|
|
fflush(stdout); \
|
|
exit(1); \
|
|
} while (0)
|
|
#endif // _WIN32
|
|
|
|
#define GET_INSTANCE_PROC_ADDR(inst, entrypoint) \
|
|
{ \
|
|
m_fp##entrypoint = (PFN_vk##entrypoint)vkGetInstanceProcAddr(inst, "vk" #entrypoint); \
|
|
if (m_fp##entrypoint == NULL) { \
|
|
ERR_EXIT("vkGetInstanceProcAddr failed to find vk" #entrypoint, "vkGetInstanceProcAddr Failure"); \
|
|
} \
|
|
}
|
|
|
|
#define GET_DEVICE_PROC_ADDR(dev, entrypoint) \
|
|
{ \
|
|
m_fp##entrypoint = (PFN_vk##entrypoint)vkGetDeviceProcAddr(dev, "vk" #entrypoint); \
|
|
if (m_fp##entrypoint == NULL) { \
|
|
ERR_EXIT("vkGetDeviceProcAddr failed to find vk" #entrypoint, "vkGetDeviceProcAddr Failure"); \
|
|
} \
|
|
}
|
|
|
|
// Command-line options
|
|
enum TOptions {
|
|
EOptionNone = 0x000,
|
|
EOptionIntermediate = 0x001,
|
|
EOptionSuppressInfolog = 0x002,
|
|
EOptionMemoryLeakMode = 0x004,
|
|
EOptionRelaxedErrors = 0x008,
|
|
EOptionGiveWarnings = 0x010,
|
|
EOptionLinkProgram = 0x020,
|
|
EOptionMultiThreaded = 0x040,
|
|
EOptionDumpConfig = 0x080,
|
|
EOptionDumpReflection = 0x100,
|
|
EOptionSuppressWarnings = 0x200,
|
|
EOptionDumpVersions = 0x400,
|
|
EOptionSpv = 0x800,
|
|
EOptionDefaultDesktop = 0x1000,
|
|
};
|
|
|
|
struct SwapchainBuffers {
|
|
VkImage image;
|
|
VkCommandBuffer cmd;
|
|
VkImageView view;
|
|
};
|
|
|
|
#ifndef _WIN32
|
|
|
|
#include <errno.h>
|
|
|
|
int fopen_s(FILE **pFile, const char *filename, const char *mode) {
|
|
if (!pFile || !filename || !mode) {
|
|
return EINVAL;
|
|
}
|
|
|
|
FILE *f = fopen(filename, mode);
|
|
if (!f) {
|
|
if (errno != 0) {
|
|
return errno;
|
|
} else {
|
|
return ENOENT;
|
|
}
|
|
}
|
|
*pFile = f;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
// Set up environment for GLSL compiler
|
|
// Must be done once per process
|
|
void TestEnvironment::SetUp() {
|
|
// Initialize GLSL to SPV compiler utility
|
|
glslang::InitializeProcess();
|
|
|
|
vk_testing::set_error_callback(test_error_callback);
|
|
}
|
|
|
|
void TestEnvironment::TearDown() { glslang::FinalizeProcess(); }
|
|
|
|
VkTestFramework::VkTestFramework() : m_compile_options(0), m_num_shader_strings(0) {}
|
|
|
|
VkTestFramework::~VkTestFramework() {}
|
|
|
|
// Define all the static elements
|
|
bool VkTestFramework::m_canonicalize_spv = false;
|
|
bool VkTestFramework::m_strip_spv = false;
|
|
bool VkTestFramework::m_do_everything_spv = false;
|
|
bool VkTestFramework::m_devsim_layer = false;
|
|
bool VkTestFramework::m_khronos_layer_disable = false;
|
|
int VkTestFramework::m_width = 0;
|
|
int VkTestFramework::m_height = 0;
|
|
|
|
bool VkTestFramework::optionMatch(const char *option, char *optionLine) {
|
|
if (strncmp(option, optionLine, strlen(option)) == 0)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
void VkTestFramework::InitArgs(int *argc, char *argv[]) {
|
|
int i, n;
|
|
|
|
for (i = 1, n = 1; i < *argc; i++) {
|
|
if (optionMatch("--strip-SPV", argv[i]))
|
|
m_strip_spv = true;
|
|
else if (optionMatch("--canonicalize-SPV", argv[i]))
|
|
m_canonicalize_spv = true;
|
|
else if (optionMatch("--devsim", argv[i]))
|
|
m_devsim_layer = true;
|
|
else if (optionMatch("--disable_uberlayer", argv[i]))
|
|
m_khronos_layer_disable = true;
|
|
else if (optionMatch("--help", argv[i]) || optionMatch("-h", argv[i])) {
|
|
printf("\nOther options:\n");
|
|
printf(
|
|
"\t--show-images\n"
|
|
"\t\tDisplay test images in viewer after tests complete.\n");
|
|
printf(
|
|
"\t--save-images\n"
|
|
"\t\tSave tests images as ppm files in current working directory.\n"
|
|
"\t\tUsed to generate golden images for compare-images.\n");
|
|
printf(
|
|
"\t--compare-images\n"
|
|
"\t\tCompare test images to 'golden' image in golden folder.\n"
|
|
"\t\tAlso saves the generated test image in current working\n"
|
|
"\t\t\tdirectory but only if the image is different from the golden\n"
|
|
"\t\tSetting RENDERTEST_GOLDEN_DIR environment variable can specify\n"
|
|
"\t\t\tdifferent directory for golden images\n"
|
|
"\t\tSignal test failure if different.\n");
|
|
printf(
|
|
"\t--no-SPV\n"
|
|
"\t\tUse built-in GLSL compiler rather than SPV code path.\n");
|
|
printf(
|
|
"\t--strip-SPV\n"
|
|
"\t\tStrip SPIR-V debug information (line numbers, names, etc).\n");
|
|
printf(
|
|
"\t--canonicalize-SPV\n"
|
|
"\t\tRemap SPIR-V ids before submission to aid compression.\n");
|
|
exit(0);
|
|
} else {
|
|
printf("\nUnrecognized option: %s\n", argv[i]);
|
|
printf("\nUse --help or -h for option list.\n");
|
|
exit(0);
|
|
}
|
|
|
|
/*
|
|
* Since the above "consume" inputs, update argv
|
|
* so that it contains the trimmed list of args for glutInit
|
|
*/
|
|
|
|
argv[n] = argv[i];
|
|
n++;
|
|
}
|
|
}
|
|
|
|
VkFormat VkTestFramework::GetFormat(VkInstance instance, vk_testing::Device *device) {
|
|
VkFormatProperties format_props;
|
|
|
|
vkGetPhysicalDeviceFormatProperties(device->phy().handle(), VK_FORMAT_B8G8R8A8_UNORM, &format_props);
|
|
if (format_props.linearTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT ||
|
|
format_props.optimalTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT) {
|
|
return VK_FORMAT_B8G8R8A8_UNORM;
|
|
}
|
|
vkGetPhysicalDeviceFormatProperties(device->phy().handle(), VK_FORMAT_R8G8B8A8_UNORM, &format_props);
|
|
if (format_props.linearTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT ||
|
|
format_props.optimalTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT) {
|
|
return VK_FORMAT_R8G8B8A8_UNORM;
|
|
}
|
|
printf("Error - device does not support VK_FORMAT_B8G8R8A8_UNORM nor VK_FORMAT_R8G8B8A8_UNORM - exiting\n");
|
|
exit(1);
|
|
}
|
|
|
|
void VkTestFramework::Finish() {}
|
|
|
|
//
|
|
// These are the default resources for TBuiltInResources, used for both
|
|
// - parsing this string for the case where the user didn't supply one
|
|
// - dumping out a template for user construction of a config file
|
|
//
|
|
static const char *DefaultConfig =
|
|
"MaxLights 32\n"
|
|
"MaxClipPlanes 6\n"
|
|
"MaxTextureUnits 32\n"
|
|
"MaxTextureCoords 32\n"
|
|
"MaxVertexAttribs 64\n"
|
|
"MaxVertexUniformComponents 4096\n"
|
|
"MaxVaryingFloats 64\n"
|
|
"MaxVertexTextureImageUnits 32\n"
|
|
"MaxCombinedTextureImageUnits 80\n"
|
|
"MaxTextureImageUnits 32\n"
|
|
"MaxFragmentUniformComponents 4096\n"
|
|
"MaxDrawBuffers 32\n"
|
|
"MaxVertexUniformVectors 128\n"
|
|
"MaxVaryingVectors 8\n"
|
|
"MaxFragmentUniformVectors 16\n"
|
|
"MaxVertexOutputVectors 16\n"
|
|
"MaxFragmentInputVectors 15\n"
|
|
"MinProgramTexelOffset -8\n"
|
|
"MaxProgramTexelOffset 7\n"
|
|
"MaxClipDistances 8\n"
|
|
"MaxComputeWorkGroupCountX 65535\n"
|
|
"MaxComputeWorkGroupCountY 65535\n"
|
|
"MaxComputeWorkGroupCountZ 65535\n"
|
|
"MaxComputeWorkGroupSizeX 1024\n"
|
|
"MaxComputeWorkGroupSizeY 1024\n"
|
|
"MaxComputeWorkGroupSizeZ 64\n"
|
|
"MaxComputeUniformComponents 1024\n"
|
|
"MaxComputeTextureImageUnits 16\n"
|
|
"MaxComputeImageUniforms 8\n"
|
|
"MaxComputeAtomicCounters 8\n"
|
|
"MaxComputeAtomicCounterBuffers 1\n"
|
|
"MaxVaryingComponents 60\n"
|
|
"MaxVertexOutputComponents 64\n"
|
|
"MaxGeometryInputComponents 64\n"
|
|
"MaxGeometryOutputComponents 128\n"
|
|
"MaxFragmentInputComponents 128\n"
|
|
"MaxImageUnits 8\n"
|
|
"MaxCombinedImageUnitsAndFragmentOutputs 8\n"
|
|
"MaxCombinedShaderOutputResources 8\n"
|
|
"MaxImageSamples 0\n"
|
|
"MaxVertexImageUniforms 0\n"
|
|
"MaxTessControlImageUniforms 0\n"
|
|
"MaxTessEvaluationImageUniforms 0\n"
|
|
"MaxGeometryImageUniforms 0\n"
|
|
"MaxFragmentImageUniforms 8\n"
|
|
"MaxCombinedImageUniforms 8\n"
|
|
"MaxGeometryTextureImageUnits 16\n"
|
|
"MaxGeometryOutputVertices 256\n"
|
|
"MaxGeometryTotalOutputComponents 1024\n"
|
|
"MaxGeometryUniformComponents 1024\n"
|
|
"MaxGeometryVaryingComponents 64\n"
|
|
"MaxTessControlInputComponents 128\n"
|
|
"MaxTessControlOutputComponents 128\n"
|
|
"MaxTessControlTextureImageUnits 16\n"
|
|
"MaxTessControlUniformComponents 1024\n"
|
|
"MaxTessControlTotalOutputComponents 4096\n"
|
|
"MaxTessEvaluationInputComponents 128\n"
|
|
"MaxTessEvaluationOutputComponents 128\n"
|
|
"MaxTessEvaluationTextureImageUnits 16\n"
|
|
"MaxTessEvaluationUniformComponents 1024\n"
|
|
"MaxTessPatchComponents 120\n"
|
|
"MaxPatchVertices 32\n"
|
|
"MaxTessGenLevel 64\n"
|
|
"MaxViewports 16\n"
|
|
"MaxVertexAtomicCounters 0\n"
|
|
"MaxTessControlAtomicCounters 0\n"
|
|
"MaxTessEvaluationAtomicCounters 0\n"
|
|
"MaxGeometryAtomicCounters 0\n"
|
|
"MaxFragmentAtomicCounters 8\n"
|
|
"MaxCombinedAtomicCounters 8\n"
|
|
"MaxAtomicCounterBindings 1\n"
|
|
"MaxVertexAtomicCounterBuffers 0\n"
|
|
"MaxTessControlAtomicCounterBuffers 0\n"
|
|
"MaxTessEvaluationAtomicCounterBuffers 0\n"
|
|
"MaxGeometryAtomicCounterBuffers 0\n"
|
|
"MaxFragmentAtomicCounterBuffers 1\n"
|
|
"MaxCombinedAtomicCounterBuffers 1\n"
|
|
"MaxAtomicCounterBufferSize 16384\n"
|
|
"MaxTransformFeedbackBuffers 4\n"
|
|
"MaxTransformFeedbackInterleavedComponents 64\n"
|
|
"MaxCullDistances 8\n"
|
|
"MaxCombinedClipAndCullDistances 8\n"
|
|
"MaxSamples 4\n"
|
|
"MaxMeshOutputVerticesNV 256\n"
|
|
"MaxMeshOutputPrimitivesNV 512\n"
|
|
"MaxMeshWorkGroupSizeX_NV 32\n"
|
|
"MaxMeshWorkGroupSizeY_NV 1\n"
|
|
"MaxMeshWorkGroupSizeZ_NV 1\n"
|
|
"MaxTaskWorkGroupSizeX_NV 32\n"
|
|
"MaxTaskWorkGroupSizeY_NV 1\n"
|
|
"MaxTaskWorkGroupSizeZ_NV 1\n"
|
|
"MaxMeshViewCountNV 4\n"
|
|
|
|
"nonInductiveForLoops 1\n"
|
|
"whileLoops 1\n"
|
|
"doWhileLoops 1\n"
|
|
"generalUniformIndexing 1\n"
|
|
"generalAttributeMatrixVectorIndexing 1\n"
|
|
"generalVaryingIndexing 1\n"
|
|
"generalSamplerIndexing 1\n"
|
|
"generalVariableIndexing 1\n"
|
|
"generalConstantMatrixVectorIndexing 1\n";
|
|
|
|
//
|
|
// *.conf => this is a config file that can set limits/resources
|
|
//
|
|
bool VkTestFramework::SetConfigFile(const std::string &name) {
|
|
if (name.size() < 5) return false;
|
|
|
|
if (name.compare(name.size() - 5, 5, ".conf") == 0) {
|
|
ConfigFile = name;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Parse either a .conf file provided by the user or the default string above.
|
|
//
|
|
void VkTestFramework::ProcessConfigFile() {
|
|
char **configStrings = 0;
|
|
char *config = 0;
|
|
if (ConfigFile.size() > 0) {
|
|
configStrings = ReadFileData(ConfigFile.c_str());
|
|
if (configStrings)
|
|
config = *configStrings;
|
|
else {
|
|
printf("Error opening configuration file; will instead use the default configuration\n");
|
|
}
|
|
}
|
|
|
|
if (config == 0) {
|
|
config = (char *)alloca(strlen(DefaultConfig) + 1);
|
|
strcpy(config, DefaultConfig);
|
|
}
|
|
|
|
const char *delims = " \t\n\r";
|
|
const char *token = strtok(config, delims);
|
|
while (token) {
|
|
const char *valueStr = strtok(0, delims);
|
|
if (valueStr == 0 || !(valueStr[0] == '-' || (valueStr[0] >= '0' && valueStr[0] <= '9'))) {
|
|
printf("Error: '%s' bad .conf file. Each name must be followed by one number.\n", valueStr ? valueStr : "");
|
|
return;
|
|
}
|
|
int value = atoi(valueStr);
|
|
|
|
if (strcmp(token, "MaxLights") == 0)
|
|
Resources.maxLights = value;
|
|
else if (strcmp(token, "MaxClipPlanes") == 0)
|
|
Resources.maxClipPlanes = value;
|
|
else if (strcmp(token, "MaxTextureUnits") == 0)
|
|
Resources.maxTextureUnits = value;
|
|
else if (strcmp(token, "MaxTextureCoords") == 0)
|
|
Resources.maxTextureCoords = value;
|
|
else if (strcmp(token, "MaxVertexAttribs") == 0)
|
|
Resources.maxVertexAttribs = value;
|
|
else if (strcmp(token, "MaxVertexUniformComponents") == 0)
|
|
Resources.maxVertexUniformComponents = value;
|
|
else if (strcmp(token, "MaxVaryingFloats") == 0)
|
|
Resources.maxVaryingFloats = value;
|
|
else if (strcmp(token, "MaxVertexTextureImageUnits") == 0)
|
|
Resources.maxVertexTextureImageUnits = value;
|
|
else if (strcmp(token, "MaxCombinedTextureImageUnits") == 0)
|
|
Resources.maxCombinedTextureImageUnits = value;
|
|
else if (strcmp(token, "MaxTextureImageUnits") == 0)
|
|
Resources.maxTextureImageUnits = value;
|
|
else if (strcmp(token, "MaxFragmentUniformComponents") == 0)
|
|
Resources.maxFragmentUniformComponents = value;
|
|
else if (strcmp(token, "MaxDrawBuffers") == 0)
|
|
Resources.maxDrawBuffers = value;
|
|
else if (strcmp(token, "MaxVertexUniformVectors") == 0)
|
|
Resources.maxVertexUniformVectors = value;
|
|
else if (strcmp(token, "MaxVaryingVectors") == 0)
|
|
Resources.maxVaryingVectors = value;
|
|
else if (strcmp(token, "MaxFragmentUniformVectors") == 0)
|
|
Resources.maxFragmentUniformVectors = value;
|
|
else if (strcmp(token, "MaxVertexOutputVectors") == 0)
|
|
Resources.maxVertexOutputVectors = value;
|
|
else if (strcmp(token, "MaxFragmentInputVectors") == 0)
|
|
Resources.maxFragmentInputVectors = value;
|
|
else if (strcmp(token, "MinProgramTexelOffset") == 0)
|
|
Resources.minProgramTexelOffset = value;
|
|
else if (strcmp(token, "MaxProgramTexelOffset") == 0)
|
|
Resources.maxProgramTexelOffset = value;
|
|
else if (strcmp(token, "MaxClipDistances") == 0)
|
|
Resources.maxClipDistances = value;
|
|
else if (strcmp(token, "MaxComputeWorkGroupCountX") == 0)
|
|
Resources.maxComputeWorkGroupCountX = value;
|
|
else if (strcmp(token, "MaxComputeWorkGroupCountY") == 0)
|
|
Resources.maxComputeWorkGroupCountY = value;
|
|
else if (strcmp(token, "MaxComputeWorkGroupCountZ") == 0)
|
|
Resources.maxComputeWorkGroupCountZ = value;
|
|
else if (strcmp(token, "MaxComputeWorkGroupSizeX") == 0)
|
|
Resources.maxComputeWorkGroupSizeX = value;
|
|
else if (strcmp(token, "MaxComputeWorkGroupSizeY") == 0)
|
|
Resources.maxComputeWorkGroupSizeY = value;
|
|
else if (strcmp(token, "MaxComputeWorkGroupSizeZ") == 0)
|
|
Resources.maxComputeWorkGroupSizeZ = value;
|
|
else if (strcmp(token, "MaxComputeUniformComponents") == 0)
|
|
Resources.maxComputeUniformComponents = value;
|
|
else if (strcmp(token, "MaxComputeTextureImageUnits") == 0)
|
|
Resources.maxComputeTextureImageUnits = value;
|
|
else if (strcmp(token, "MaxComputeImageUniforms") == 0)
|
|
Resources.maxComputeImageUniforms = value;
|
|
else if (strcmp(token, "MaxComputeAtomicCounters") == 0)
|
|
Resources.maxComputeAtomicCounters = value;
|
|
else if (strcmp(token, "MaxComputeAtomicCounterBuffers") == 0)
|
|
Resources.maxComputeAtomicCounterBuffers = value;
|
|
else if (strcmp(token, "MaxVaryingComponents") == 0)
|
|
Resources.maxVaryingComponents = value;
|
|
else if (strcmp(token, "MaxVertexOutputComponents") == 0)
|
|
Resources.maxVertexOutputComponents = value;
|
|
else if (strcmp(token, "MaxGeometryInputComponents") == 0)
|
|
Resources.maxGeometryInputComponents = value;
|
|
else if (strcmp(token, "MaxGeometryOutputComponents") == 0)
|
|
Resources.maxGeometryOutputComponents = value;
|
|
else if (strcmp(token, "MaxFragmentInputComponents") == 0)
|
|
Resources.maxFragmentInputComponents = value;
|
|
else if (strcmp(token, "MaxImageUnits") == 0)
|
|
Resources.maxImageUnits = value;
|
|
else if (strcmp(token, "MaxCombinedImageUnitsAndFragmentOutputs") == 0)
|
|
Resources.maxCombinedImageUnitsAndFragmentOutputs = value;
|
|
else if (strcmp(token, "MaxCombinedShaderOutputResources") == 0)
|
|
Resources.maxCombinedShaderOutputResources = value;
|
|
else if (strcmp(token, "MaxImageSamples") == 0)
|
|
Resources.maxImageSamples = value;
|
|
else if (strcmp(token, "MaxVertexImageUniforms") == 0)
|
|
Resources.maxVertexImageUniforms = value;
|
|
else if (strcmp(token, "MaxTessControlImageUniforms") == 0)
|
|
Resources.maxTessControlImageUniforms = value;
|
|
else if (strcmp(token, "MaxTessEvaluationImageUniforms") == 0)
|
|
Resources.maxTessEvaluationImageUniforms = value;
|
|
else if (strcmp(token, "MaxGeometryImageUniforms") == 0)
|
|
Resources.maxGeometryImageUniforms = value;
|
|
else if (strcmp(token, "MaxFragmentImageUniforms") == 0)
|
|
Resources.maxFragmentImageUniforms = value;
|
|
else if (strcmp(token, "MaxCombinedImageUniforms") == 0)
|
|
Resources.maxCombinedImageUniforms = value;
|
|
else if (strcmp(token, "MaxGeometryTextureImageUnits") == 0)
|
|
Resources.maxGeometryTextureImageUnits = value;
|
|
else if (strcmp(token, "MaxGeometryOutputVertices") == 0)
|
|
Resources.maxGeometryOutputVertices = value;
|
|
else if (strcmp(token, "MaxGeometryTotalOutputComponents") == 0)
|
|
Resources.maxGeometryTotalOutputComponents = value;
|
|
else if (strcmp(token, "MaxGeometryUniformComponents") == 0)
|
|
Resources.maxGeometryUniformComponents = value;
|
|
else if (strcmp(token, "MaxGeometryVaryingComponents") == 0)
|
|
Resources.maxGeometryVaryingComponents = value;
|
|
else if (strcmp(token, "MaxTessControlInputComponents") == 0)
|
|
Resources.maxTessControlInputComponents = value;
|
|
else if (strcmp(token, "MaxTessControlOutputComponents") == 0)
|
|
Resources.maxTessControlOutputComponents = value;
|
|
else if (strcmp(token, "MaxTessControlTextureImageUnits") == 0)
|
|
Resources.maxTessControlTextureImageUnits = value;
|
|
else if (strcmp(token, "MaxTessControlUniformComponents") == 0)
|
|
Resources.maxTessControlUniformComponents = value;
|
|
else if (strcmp(token, "MaxTessControlTotalOutputComponents") == 0)
|
|
Resources.maxTessControlTotalOutputComponents = value;
|
|
else if (strcmp(token, "MaxTessEvaluationInputComponents") == 0)
|
|
Resources.maxTessEvaluationInputComponents = value;
|
|
else if (strcmp(token, "MaxTessEvaluationOutputComponents") == 0)
|
|
Resources.maxTessEvaluationOutputComponents = value;
|
|
else if (strcmp(token, "MaxTessEvaluationTextureImageUnits") == 0)
|
|
Resources.maxTessEvaluationTextureImageUnits = value;
|
|
else if (strcmp(token, "MaxTessEvaluationUniformComponents") == 0)
|
|
Resources.maxTessEvaluationUniformComponents = value;
|
|
else if (strcmp(token, "MaxTessPatchComponents") == 0)
|
|
Resources.maxTessPatchComponents = value;
|
|
else if (strcmp(token, "MaxPatchVertices") == 0)
|
|
Resources.maxPatchVertices = value;
|
|
else if (strcmp(token, "MaxTessGenLevel") == 0)
|
|
Resources.maxTessGenLevel = value;
|
|
else if (strcmp(token, "MaxViewports") == 0)
|
|
Resources.maxViewports = value;
|
|
else if (strcmp(token, "MaxVertexAtomicCounters") == 0)
|
|
Resources.maxVertexAtomicCounters = value;
|
|
else if (strcmp(token, "MaxTessControlAtomicCounters") == 0)
|
|
Resources.maxTessControlAtomicCounters = value;
|
|
else if (strcmp(token, "MaxTessEvaluationAtomicCounters") == 0)
|
|
Resources.maxTessEvaluationAtomicCounters = value;
|
|
else if (strcmp(token, "MaxGeometryAtomicCounters") == 0)
|
|
Resources.maxGeometryAtomicCounters = value;
|
|
else if (strcmp(token, "MaxFragmentAtomicCounters") == 0)
|
|
Resources.maxFragmentAtomicCounters = value;
|
|
else if (strcmp(token, "MaxCombinedAtomicCounters") == 0)
|
|
Resources.maxCombinedAtomicCounters = value;
|
|
else if (strcmp(token, "MaxAtomicCounterBindings") == 0)
|
|
Resources.maxAtomicCounterBindings = value;
|
|
else if (strcmp(token, "MaxVertexAtomicCounterBuffers") == 0)
|
|
Resources.maxVertexAtomicCounterBuffers = value;
|
|
else if (strcmp(token, "MaxTessControlAtomicCounterBuffers") == 0)
|
|
Resources.maxTessControlAtomicCounterBuffers = value;
|
|
else if (strcmp(token, "MaxTessEvaluationAtomicCounterBuffers") == 0)
|
|
Resources.maxTessEvaluationAtomicCounterBuffers = value;
|
|
else if (strcmp(token, "MaxGeometryAtomicCounterBuffers") == 0)
|
|
Resources.maxGeometryAtomicCounterBuffers = value;
|
|
else if (strcmp(token, "MaxFragmentAtomicCounterBuffers") == 0)
|
|
Resources.maxFragmentAtomicCounterBuffers = value;
|
|
else if (strcmp(token, "MaxCombinedAtomicCounterBuffers") == 0)
|
|
Resources.maxCombinedAtomicCounterBuffers = value;
|
|
else if (strcmp(token, "MaxAtomicCounterBufferSize") == 0)
|
|
Resources.maxAtomicCounterBufferSize = value;
|
|
else if (strcmp(token, "MaxTransformFeedbackBuffers") == 0)
|
|
Resources.maxTransformFeedbackBuffers = value;
|
|
else if (strcmp(token, "MaxTransformFeedbackInterleavedComponents") == 0)
|
|
Resources.maxTransformFeedbackInterleavedComponents = value;
|
|
else if (strcmp(token, "MaxCullDistances") == 0)
|
|
Resources.maxCullDistances = value;
|
|
else if (strcmp(token, "MaxCombinedClipAndCullDistances") == 0)
|
|
Resources.maxCombinedClipAndCullDistances = value;
|
|
else if (strcmp(token, "MaxSamples") == 0)
|
|
Resources.maxSamples = value;
|
|
else if (strcmp(token, "MaxMeshOutputVerticesNV") == 0)
|
|
Resources.maxMeshOutputVerticesNV = value;
|
|
else if (strcmp(token, "MaxMeshOutputPrimitivesNV") == 0)
|
|
Resources.maxMeshOutputPrimitivesNV = value;
|
|
else if (strcmp(token, "MaxMeshWorkGroupSizeX_NV") == 0)
|
|
Resources.maxMeshWorkGroupSizeX_NV = value;
|
|
else if (strcmp(token, "MaxMeshWorkGroupSizeY_NV") == 0)
|
|
Resources.maxMeshWorkGroupSizeY_NV = value;
|
|
else if (strcmp(token, "MaxMeshWorkGroupSizeZ_NV") == 0)
|
|
Resources.maxMeshWorkGroupSizeZ_NV = value;
|
|
else if (strcmp(token, "MaxTaskWorkGroupSizeX_NV") == 0)
|
|
Resources.maxTaskWorkGroupSizeX_NV = value;
|
|
else if (strcmp(token, "MaxTaskWorkGroupSizeY_NV") == 0)
|
|
Resources.maxTaskWorkGroupSizeY_NV = value;
|
|
else if (strcmp(token, "MaxTaskWorkGroupSizeZ_NV") == 0)
|
|
Resources.maxTaskWorkGroupSizeZ_NV = value;
|
|
else if (strcmp(token, "MaxMeshViewCountNV") == 0)
|
|
Resources.maxMeshViewCountNV = value;
|
|
|
|
else if (strcmp(token, "nonInductiveForLoops") == 0)
|
|
Resources.limits.nonInductiveForLoops = (value != 0);
|
|
else if (strcmp(token, "whileLoops") == 0)
|
|
Resources.limits.whileLoops = (value != 0);
|
|
else if (strcmp(token, "doWhileLoops") == 0)
|
|
Resources.limits.doWhileLoops = (value != 0);
|
|
else if (strcmp(token, "generalUniformIndexing") == 0)
|
|
Resources.limits.generalUniformIndexing = (value != 0);
|
|
else if (strcmp(token, "generalAttributeMatrixVectorIndexing") == 0)
|
|
Resources.limits.generalAttributeMatrixVectorIndexing = (value != 0);
|
|
else if (strcmp(token, "generalVaryingIndexing") == 0)
|
|
Resources.limits.generalVaryingIndexing = (value != 0);
|
|
else if (strcmp(token, "generalSamplerIndexing") == 0)
|
|
Resources.limits.generalSamplerIndexing = (value != 0);
|
|
else if (strcmp(token, "generalVariableIndexing") == 0)
|
|
Resources.limits.generalVariableIndexing = (value != 0);
|
|
else if (strcmp(token, "generalConstantMatrixVectorIndexing") == 0)
|
|
Resources.limits.generalConstantMatrixVectorIndexing = (value != 0);
|
|
else
|
|
printf("Warning: unrecognized limit (%s) in configuration file.\n", token);
|
|
|
|
token = strtok(0, delims);
|
|
}
|
|
if (configStrings) FreeFileData(configStrings);
|
|
}
|
|
|
|
void VkTestFramework::SetMessageOptions(EShMessages &messages) {
|
|
if (m_compile_options & EOptionRelaxedErrors) messages = (EShMessages)(messages | EShMsgRelaxedErrors);
|
|
if (m_compile_options & EOptionIntermediate) messages = (EShMessages)(messages | EShMsgAST);
|
|
if (m_compile_options & EOptionSuppressWarnings) messages = (EShMessages)(messages | EShMsgSuppressWarnings);
|
|
}
|
|
|
|
//
|
|
// Malloc a string of sufficient size and read a string into it.
|
|
//
|
|
char **VkTestFramework::ReadFileData(const char *fileName) {
|
|
FILE *in;
|
|
#if defined(_WIN32) && defined(__GNUC__)
|
|
in = fopen(fileName, "r");
|
|
int errorCode = in ? 0 : 1;
|
|
#else
|
|
int errorCode = fopen_s(&in, fileName, "r");
|
|
#endif
|
|
|
|
char *fdata;
|
|
size_t count = 0;
|
|
const int maxSourceStrings = 5;
|
|
char **return_data = (char **)malloc(sizeof(char *) * (maxSourceStrings + 1));
|
|
|
|
if (errorCode) {
|
|
printf("Error: unable to open input file: %s\n", fileName);
|
|
return 0;
|
|
}
|
|
|
|
while (fgetc(in) != EOF) count++;
|
|
|
|
fseek(in, 0, SEEK_SET);
|
|
|
|
if (!(fdata = (char *)malloc(count + 2))) {
|
|
printf("Error allocating memory\n");
|
|
return 0;
|
|
}
|
|
if (fread(fdata, 1, count, in) != count) {
|
|
printf("Error reading input file: %s\n", fileName);
|
|
return 0;
|
|
}
|
|
fdata[count] = '\0';
|
|
fclose(in);
|
|
if (count == 0) {
|
|
return_data[0] = (char *)malloc(count + 2);
|
|
return_data[0][0] = '\0';
|
|
m_num_shader_strings = 0;
|
|
return return_data;
|
|
} else
|
|
m_num_shader_strings = 1;
|
|
|
|
size_t len = (int)(ceil)((float)count / (float)m_num_shader_strings);
|
|
size_t ptr_len = 0, i = 0;
|
|
while (count > 0) {
|
|
return_data[i] = (char *)malloc(len + 2);
|
|
memcpy(return_data[i], fdata + ptr_len, len);
|
|
return_data[i][len] = '\0';
|
|
count -= (len);
|
|
ptr_len += (len);
|
|
if (count < len) {
|
|
if (count == 0) {
|
|
m_num_shader_strings = (i + 1);
|
|
break;
|
|
}
|
|
len = count;
|
|
}
|
|
++i;
|
|
}
|
|
return return_data;
|
|
}
|
|
|
|
void VkTestFramework::FreeFileData(char **data) {
|
|
for (int i = 0; i < m_num_shader_strings; i++) free(data[i]);
|
|
}
|
|
|
|
//
|
|
// Deduce the language from the filename. Files must end in one of the
|
|
// following extensions:
|
|
//
|
|
// .vert = vertex
|
|
// .tesc = tessellation control
|
|
// .tese = tessellation evaluation
|
|
// .geom = geometry
|
|
// .frag = fragment
|
|
// .comp = compute
|
|
//
|
|
EShLanguage VkTestFramework::FindLanguage(const std::string &name) {
|
|
size_t ext = name.rfind('.');
|
|
if (ext == std::string::npos) {
|
|
return EShLangVertex;
|
|
}
|
|
|
|
std::string suffix = name.substr(ext + 1, std::string::npos);
|
|
if (suffix == "vert")
|
|
return EShLangVertex;
|
|
else if (suffix == "tesc")
|
|
return EShLangTessControl;
|
|
else if (suffix == "tese")
|
|
return EShLangTessEvaluation;
|
|
else if (suffix == "geom")
|
|
return EShLangGeometry;
|
|
else if (suffix == "frag")
|
|
return EShLangFragment;
|
|
else if (suffix == "comp")
|
|
return EShLangCompute;
|
|
|
|
return EShLangVertex;
|
|
}
|
|
|
|
//
|
|
// Convert VK shader type to compiler's
|
|
//
|
|
EShLanguage VkTestFramework::FindLanguage(const VkShaderStageFlagBits shader_type) {
|
|
switch (shader_type) {
|
|
case VK_SHADER_STAGE_VERTEX_BIT:
|
|
return EShLangVertex;
|
|
|
|
case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT:
|
|
return EShLangTessControl;
|
|
|
|
case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT:
|
|
return EShLangTessEvaluation;
|
|
|
|
case VK_SHADER_STAGE_GEOMETRY_BIT:
|
|
return EShLangGeometry;
|
|
|
|
case VK_SHADER_STAGE_FRAGMENT_BIT:
|
|
return EShLangFragment;
|
|
|
|
case VK_SHADER_STAGE_COMPUTE_BIT:
|
|
return EShLangCompute;
|
|
|
|
case VK_SHADER_STAGE_RAYGEN_BIT_NV:
|
|
return EShLangRayGenNV;
|
|
|
|
case VK_SHADER_STAGE_ANY_HIT_BIT_NV:
|
|
return EShLangAnyHitNV;
|
|
|
|
case VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV:
|
|
return EShLangClosestHitNV;
|
|
|
|
case VK_SHADER_STAGE_MISS_BIT_NV:
|
|
return EShLangMissNV;
|
|
|
|
case VK_SHADER_STAGE_INTERSECTION_BIT_NV:
|
|
return EShLangIntersectNV;
|
|
|
|
case VK_SHADER_STAGE_CALLABLE_BIT_NV:
|
|
return EShLangCallableNV;
|
|
|
|
case VK_SHADER_STAGE_TASK_BIT_NV:
|
|
return EShLangTaskNV;
|
|
|
|
case VK_SHADER_STAGE_MESH_BIT_NV:
|
|
return EShLangMeshNV;
|
|
|
|
default:
|
|
return EShLangVertex;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Compile a given string containing GLSL into SPV for use by VK
|
|
// Return value of false means an error was encountered.
|
|
//
|
|
bool VkTestFramework::GLSLtoSPV(const VkShaderStageFlagBits shader_type, const char *pshader, std::vector<unsigned int> &spirv,
|
|
bool debug) {
|
|
glslang::TProgram program;
|
|
const char *shaderStrings[1];
|
|
|
|
// TODO: Do we want to load a special config file depending on the
|
|
// shader source? Optional name maybe?
|
|
// SetConfigFile(fileName);
|
|
|
|
ProcessConfigFile();
|
|
|
|
EShMessages messages = EShMsgDefault;
|
|
SetMessageOptions(messages);
|
|
messages = static_cast<EShMessages>(messages | EShMsgSpvRules | EShMsgVulkanRules);
|
|
if (debug) {
|
|
messages = static_cast<EShMessages>(messages | EShMsgDebugInfo);
|
|
}
|
|
|
|
EShLanguage stage = FindLanguage(shader_type);
|
|
glslang::TShader *shader = new glslang::TShader(stage);
|
|
|
|
shaderStrings[0] = pshader;
|
|
shader->setStrings(shaderStrings, 1);
|
|
|
|
if (!shader->parse(&Resources, (m_compile_options & EOptionDefaultDesktop) ? 110 : 100, false, messages)) {
|
|
if (!(m_compile_options & EOptionSuppressInfolog)) {
|
|
puts(shader->getInfoLog());
|
|
puts(shader->getInfoDebugLog());
|
|
}
|
|
|
|
return false; // something didn't work
|
|
}
|
|
|
|
program.addShader(shader);
|
|
|
|
//
|
|
// Program-level processing...
|
|
//
|
|
|
|
if (!program.link(messages)) {
|
|
if (!(m_compile_options & EOptionSuppressInfolog)) {
|
|
puts(shader->getInfoLog());
|
|
puts(shader->getInfoDebugLog());
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
if (m_compile_options & EOptionDumpReflection) {
|
|
program.buildReflection();
|
|
program.dumpReflection();
|
|
}
|
|
|
|
glslang::SpvOptions spv_options;
|
|
if (debug) {
|
|
spv_options.generateDebugInfo = true;
|
|
}
|
|
glslang::GlslangToSpv(*program.getIntermediate(stage), spirv, &spv_options);
|
|
|
|
//
|
|
// Test the different modes of SPIR-V modification
|
|
//
|
|
if (this->m_canonicalize_spv) {
|
|
spv::spirvbin_t(0).remap(spirv, spv::spirvbin_t::ALL_BUT_STRIP);
|
|
}
|
|
|
|
if (this->m_strip_spv) {
|
|
spv::spirvbin_t(0).remap(spirv, spv::spirvbin_t::STRIP);
|
|
}
|
|
|
|
if (this->m_do_everything_spv) {
|
|
spv::spirvbin_t(0).remap(spirv, spv::spirvbin_t::DO_EVERYTHING);
|
|
}
|
|
|
|
delete shader;
|
|
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// Compile a given string containing SPIR-V assembly into SPV for use by VK
|
|
// Return value of false means an error was encountered.
|
|
//
|
|
bool VkTestFramework::ASMtoSPV(const spv_target_env target_env, const uint32_t options, const char *pasm,
|
|
std::vector<unsigned int> &spv) {
|
|
spv_binary binary;
|
|
spv_diagnostic diagnostic = nullptr;
|
|
spv_context context = spvContextCreate(target_env);
|
|
spv_result_t error = spvTextToBinaryWithOptions(context, pasm, strlen(pasm), options, &binary, &diagnostic);
|
|
spvContextDestroy(context);
|
|
if (error) {
|
|
spvDiagnosticPrint(diagnostic);
|
|
spvDiagnosticDestroy(diagnostic);
|
|
return false;
|
|
}
|
|
spv.insert(spv.end(), binary->code, binary->code + binary->wordCount);
|
|
spvBinaryDestroy(binary);
|
|
|
|
return true;
|
|
}
|