Karol SuprynowiczFix GLES on Vulkan branch21fc10910 months agoHistory
645 lines · 25.1 KB · C++
RawDownload
////  ShaderTests.cpp//  tests/octree/src////  Created by Andrew Meadows on 2016.02.19//  Copyright 2016 High Fidelity, Inc.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// #include "ShaderTests.h" #include <fstream>#include <sstream>#include <regex>#include <algorithm> #include <QtCore/QJsonDocument>#include <QtConcurrent/QtConcurrent> #include <GLMHelpers.h> #include <test-utils/GLMTestUtils.h>#include <test-utils/QTestExtensions.h>#include <shared/FileUtils.h> #include <shaders/Shaders.h>#include <gl/Config.h>#include <gl/GLHelpers.h>#include <gpu/gl/GLBackend.h>#include <gpu/gl/GLShader.h>#include <gl/QOpenGLContextWrapper.h> #define RUNTIME_SHADER_COMPILE_TEST 1 #if RUNTIME_SHADER_COMPILE_TEST#include <spirv_cross.hpp>#include <glslang/Public/ShaderLang.h>#include <SPIRV/GlslangToSpv.h>#include <spirv-tools/libspirv.hpp>#include <spirv-tools/optimizer.hpp>#include <vulkan/vulkan.hpp> #ifdef DEBUG#define VCPKG_LIB_DIR "D:/hifi/vcpkg/hifi/installed/x64-windows/debug/lib/"#else#define VCPKG_LIB_DIR "D:/hifi/vcpkg/hifi/installed/x64-windows/lib/"#endif #pragma comment(lib, VCPKG_LIB_DIR "shaderc_combined.lib")#pragma comment(lib, VCPKG_LIB_DIR "spirv-cross-core.lib")#pragma comment(lib, VCPKG_LIB_DIR "spirv-cross-glsl.lib")#pragma comment(lib, VCPKG_LIB_DIR "spirv-cross-reflect.lib") #endif QTEST_MAIN(ShaderTests) void ShaderTests::initTestCase() {    _context = new ::gl::OffscreenContext();    getDefaultOpenGLSurfaceFormat();    _context->create();    if (!_context->makeCurrent()) {        qFatal("Unable to make test GL context current");    }    QOpenGLContextWrapper(_context->qglContext()).makeCurrent(_context->getWindow());    gl::initModuleGl();    if (!_context->makeCurrent()) {        qFatal("Unable to make test GL context current");    }    gpu::Context::init<gpu::gl::GLBackend>();    if (!_context->makeCurrent()) {        qFatal("Unable to make test GL context current");    }    _gpuContext = std::make_shared<gpu::Context>();} void ShaderTests::cleanupTestCase() {    qDebug() << "Done";} #if RUNTIME_SHADER_COMPILE_TEST template <typename C>QStringList toQStringList(const C& c) {    QStringList result;    for (const auto& v : c) {        result << v.c_str();    }    return result;} template <typename C, typename F>std::unordered_set<std::string> toStringSet(const C& c, F f) {    std::unordered_set<std::string> result;    for (const auto& v : c) {        result.insert(f(v));    }    return result;} template <typename C>bool isSubset(const C& parent, const C& child) {    for (const auto& v : child) {        if (0 == parent.count(v)) {            return false;        }    }    return true;} #if 0gpu::Shader::ReflectionMap mergeReflection(const std::initializer_list<const gpu::Shader::Source>& list) {    gpu::Shader::ReflectionMap result;    std::unordered_map<gpu::Shader::BindingType, std::unordered_map<uint32_t, std::string>> usedLocationsByType;    for (const auto& source : list) {        const auto& reflection = source.getReflection();        for (const auto& entry : reflection) {            const auto& type = entry.first;            if (entry.first == gpu::Shader::BindingType::INPUT || entry.first == gpu::Shader::BindingType::OUTPUT) {                continue;            }            auto& outLocationMap = result[type];            auto& usedLocations = usedLocationsByType[type];            const auto& locationMap = entry.second;            for (const auto& entry : locationMap) {                const auto& name = entry.first;                const auto& location = entry.second;                if (0 != usedLocations.count(location) && usedLocations[location] != name) {                    qWarning() << QString("Location %1 used by both %2 and %3")                                      .arg(location)                                      .arg(name.c_str())                                      .arg(usedLocations[location].c_str());                    throw std::runtime_error("Location collision");                }                usedLocations[location] = name;                outLocationMap[name] = location;            }        }    }    return result;}template <typename C>bool compareBindings(const C& actual, const gpu::Shader::LocationMap& expected) {    if (actual.size() != expected.size()) {        auto actualNames = toStringSet(actual, [](const auto& v) { return v.name; });        auto expectedNames = toStringSet(expected, [](const auto& v) { return v.first; });        if (!isSubset(expectedNames, actualNames)) {            qDebug() << "Found" << toQStringList(actualNames);            qDebug() << "Expected" << toQStringList(expectedNames);            return false;        }    }    return true;} #endif #endif template <typename K, typename V>std::unordered_map<V, K> invertMap(const std::unordered_map<K, V>& map) {    std::unordered_map<V, K> result;    for (const auto& entry : map) {        result[entry.second] = entry.first;    }    if (result.size() != map.size()) {        throw std::runtime_error("Map inversion failure, result size does not match input size");    }    return result;} static void verifyInterface(const gpu::Shader::Source& vertexSource,                            const gpu::Shader::Source& fragmentSource,                            shader::Dialect dialect,                            shader::Variant variant) {    const auto& fragmentReflection = fragmentSource.getReflection(dialect, variant);    if (fragmentReflection.inputs.empty()) {        return;    }     const auto& vertexReflection = vertexSource.getReflection(dialect, variant);    const auto& fragIn = fragmentReflection.inputs;    if (vertexReflection.outputs.empty()) {        throw std::runtime_error("No vertex outputs for fragment inputs");    }     const auto& vout = vertexReflection.outputs;    auto vrev = invertMap(vout);    for (const auto entry : fragIn) {        const auto& name = entry.first;        if (0 == vout.count(name)) {            throw std::runtime_error("Vertex outputs missing");        }        const auto& inLocation = entry.second;        const auto& outLocation = vout.at(name);        if (inLocation != outLocation) {            throw std::runtime_error("Mismatch in vertex / fragment interface");        }    }} static void verifyInterface(const gpu::Shader::Source& vertexSource, const gpu::Shader::Source& fragmentSource) {    for (const auto& dialect : shader::allDialects()) {        for (const auto& variant : shader::allVariants()) {            verifyInterface(vertexSource, fragmentSource, dialect, variant);        }    }} #if RUNTIME_SHADER_COMPILE_TEST void configureGLSLCompilerResources(TBuiltInResource* glslCompilerResources) {    glslCompilerResources->maxLights = 32;    glslCompilerResources->maxClipPlanes = 6;    glslCompilerResources->maxTextureUnits = 32;    glslCompilerResources->maxTextureCoords = 32;    glslCompilerResources->maxVertexAttribs = 64;    glslCompilerResources->maxVertexUniformComponents = 4096;    glslCompilerResources->maxVaryingFloats = 64;    glslCompilerResources->maxVertexTextureImageUnits = 32;    glslCompilerResources->maxCombinedTextureImageUnits = 80;    glslCompilerResources->maxTextureImageUnits = 32;    glslCompilerResources->maxFragmentUniformComponents = 4096;    glslCompilerResources->maxDrawBuffers = 32;    glslCompilerResources->maxVertexUniformVectors = 128;    glslCompilerResources->maxVaryingVectors = 8;    glslCompilerResources->maxFragmentUniformVectors = 16;    glslCompilerResources->maxVertexOutputVectors = 16;    glslCompilerResources->maxFragmentInputVectors = 15;    glslCompilerResources->minProgramTexelOffset = -8;    glslCompilerResources->maxProgramTexelOffset = 7;    glslCompilerResources->maxClipDistances = 8;    glslCompilerResources->maxComputeWorkGroupCountX = 65535;    glslCompilerResources->maxComputeWorkGroupCountY = 65535;    glslCompilerResources->maxComputeWorkGroupCountZ = 65535;    glslCompilerResources->maxComputeWorkGroupSizeX = 1024;    glslCompilerResources->maxComputeWorkGroupSizeY = 1024;    glslCompilerResources->maxComputeWorkGroupSizeZ = 64;    glslCompilerResources->maxComputeUniformComponents = 1024;    glslCompilerResources->maxComputeTextureImageUnits = 16;    glslCompilerResources->maxComputeImageUniforms = 8;    glslCompilerResources->maxComputeAtomicCounters = 8;    glslCompilerResources->maxComputeAtomicCounterBuffers = 1;    glslCompilerResources->maxVaryingComponents = 60;    glslCompilerResources->maxVertexOutputComponents = 64;    glslCompilerResources->maxGeometryInputComponents = 64;    glslCompilerResources->maxGeometryOutputComponents = 128;    glslCompilerResources->maxFragmentInputComponents = 128;    glslCompilerResources->maxImageUnits = 8;    glslCompilerResources->maxCombinedImageUnitsAndFragmentOutputs = 8;    glslCompilerResources->maxCombinedShaderOutputResources = 8;    glslCompilerResources->maxImageSamples = 0;    glslCompilerResources->maxVertexImageUniforms = 0;    glslCompilerResources->maxTessControlImageUniforms = 0;    glslCompilerResources->maxTessEvaluationImageUniforms = 0;    glslCompilerResources->maxGeometryImageUniforms = 0;    glslCompilerResources->maxFragmentImageUniforms = 8;    glslCompilerResources->maxCombinedImageUniforms = 8;    glslCompilerResources->maxGeometryTextureImageUnits = 16;    glslCompilerResources->maxGeometryOutputVertices = 256;    glslCompilerResources->maxGeometryTotalOutputComponents = 1024;    glslCompilerResources->maxGeometryUniformComponents = 1024;    glslCompilerResources->maxGeometryVaryingComponents = 64;    glslCompilerResources->maxTessControlInputComponents = 128;    glslCompilerResources->maxTessControlOutputComponents = 128;    glslCompilerResources->maxTessControlTextureImageUnits = 16;    glslCompilerResources->maxTessControlUniformComponents = 1024;    glslCompilerResources->maxTessControlTotalOutputComponents = 4096;    glslCompilerResources->maxTessEvaluationInputComponents = 128;    glslCompilerResources->maxTessEvaluationOutputComponents = 128;    glslCompilerResources->maxTessEvaluationTextureImageUnits = 16;    glslCompilerResources->maxTessEvaluationUniformComponents = 1024;    glslCompilerResources->maxTessPatchComponents = 120;    glslCompilerResources->maxPatchVertices = 32;    glslCompilerResources->maxTessGenLevel = 64;    glslCompilerResources->maxViewports = 16;    glslCompilerResources->maxVertexAtomicCounters = 0;    glslCompilerResources->maxTessControlAtomicCounters = 0;    glslCompilerResources->maxTessEvaluationAtomicCounters = 0;    glslCompilerResources->maxGeometryAtomicCounters = 0;    glslCompilerResources->maxFragmentAtomicCounters = 8;    glslCompilerResources->maxCombinedAtomicCounters = 8;    glslCompilerResources->maxAtomicCounterBindings = 1;    glslCompilerResources->maxVertexAtomicCounterBuffers = 0;    glslCompilerResources->maxTessControlAtomicCounterBuffers = 0;    glslCompilerResources->maxTessEvaluationAtomicCounterBuffers = 0;    glslCompilerResources->maxGeometryAtomicCounterBuffers = 0;    glslCompilerResources->maxFragmentAtomicCounterBuffers = 1;    glslCompilerResources->maxCombinedAtomicCounterBuffers = 1;    glslCompilerResources->maxAtomicCounterBufferSize = 16384;    glslCompilerResources->maxTransformFeedbackBuffers = 4;    glslCompilerResources->maxTransformFeedbackInterleavedComponents = 64;    glslCompilerResources->maxCullDistances = 8;    glslCompilerResources->maxCombinedClipAndCullDistances = 8;    glslCompilerResources->maxSamples = 4;    glslCompilerResources->limits.nonInductiveForLoops = 1;    glslCompilerResources->limits.whileLoops = 1;    glslCompilerResources->limits.doWhileLoops = 1;    glslCompilerResources->limits.generalUniformIndexing = 1;    glslCompilerResources->limits.generalAttributeMatrixVectorIndexing = 1;    glslCompilerResources->limits.generalVaryingIndexing = 1;    glslCompilerResources->limits.generalSamplerIndexing = 1;    glslCompilerResources->limits.generalVariableIndexing = 1;    glslCompilerResources->limits.generalConstantMatrixVectorIndexing = 1;} void writeSpirv(const std::string& filename, const std::vector<uint32_t>& spirv) {    std::ofstream o(filename, std::ios::trunc | std::ios::binary);    for (const auto& word : spirv) {        o.write((const char*)&word, sizeof(word));    }    o.close();} void write(const std::string& filename, const std::string& text) {    std::ofstream o(filename, std::ios::trunc);    o.write(text.c_str(), text.size());    o.close();} bool endsWith(const std::string& s, const std::string& f) {    auto end = s.substr(s.size() - f.size());    return (end == f);} EShLanguage getShaderStage(const std::string& shaderName) {    static const std::string VERT_EXT{ ".vert" };    static const std::string FRAG_EXT{ ".frag" };    static const std::string GEOM_EXT{ ".geom" };    static const size_t EXT_SIZE = VERT_EXT.size();    if (shaderName.size() < EXT_SIZE) {        throw std::runtime_error("Invalid shader name");    }    std::string ext = shaderName.substr(shaderName.size() - EXT_SIZE);    if (ext == VERT_EXT) {        return EShLangVertex;    } else if (ext == FRAG_EXT) {        return EShLangFragment;    } else if (ext == GEOM_EXT) {        return EShLangGeometry;    }    throw std::runtime_error("Invalid shader name");} void rebuildSource(shader::Dialect dialect, shader::Variant variant, const shader::Source& source) {    try {        using namespace glslang;        static const EShMessages messages = (EShMessages)(EShMsgDefault | EShMsgSpvRules | EShMsgVulkanRules);        auto shaderName = source.name;        const std::string baseOutName = "d:/shaders/" + shaderName;        auto stage = getShaderStage(shaderName);         TShader shader(stage);        std::vector<const char*> strings;        const auto& dialectVariantSource = source.dialectSources.at(dialect).variantSources.at(variant);        strings.push_back(dialectVariantSource.scribe.c_str());        shader.setStrings(strings.data(), (int)strings.size());        shader.setEnvInput(EShSourceGlsl, stage, EShClientOpenGL, 450);        shader.setEnvClient(EShClientVulkan, EShTargetVulkan_1_1);        shader.setEnvTarget(EShTargetSpv, EShTargetSpv_1_3);         //bool success = shader.parse(&glslCompilerResources, 450, false, messages);        QSKIP("Something wasn't fully implemented here, please fix me!");        bool success = false;        if (!success) {            qWarning() << "Failed to parse shader " << shaderName.c_str();            qWarning() << shader.getInfoLog();            qWarning() << shader.getInfoDebugLog();            throw std::runtime_error("Wrong");        }         // Create and link a shader program containing the single shader        glslang::TProgram program;        program.addShader(&shader);        if (!program.link(messages)) {            qWarning() << "Failed to compile shader " << shaderName.c_str();            qWarning() << program.getInfoLog();            qWarning() << program.getInfoDebugLog();            throw std::runtime_error("Wrong");        }         // Output the SPIR-V code from the shader program        std::vector<uint32_t> spirv;        glslang::GlslangToSpv(*program.getIntermediate(stage), spirv);         spvtools::SpirvTools core(SPV_ENV_VULKAN_1_1);        spvtools::Optimizer opt(SPV_ENV_VULKAN_1_1);         auto outputLambda = [](spv_message_level_t, const char*, const spv_position_t&, const char* m) { qWarning() << m; };        core.SetMessageConsumer(outputLambda);        opt.SetMessageConsumer(outputLambda);         if (!core.Validate(spirv)) {            throw std::runtime_error("invalid spirv");        }        writeSpirv(baseOutName + ".spv", spirv);         opt.RegisterPass(spvtools::CreateFreezeSpecConstantValuePass())            .RegisterPass(spvtools::CreateStrengthReductionPass())            .RegisterPass(spvtools::CreateEliminateDeadConstantPass())            .RegisterPass(spvtools::CreateEliminateDeadFunctionsPass())            .RegisterPass(spvtools::CreateUnifyConstantPass());         std::vector<uint32_t> optspirv;        if (!opt.Run(spirv.data(), spirv.size(), &optspirv)) {            throw std::runtime_error("bad optimize run");        }        writeSpirv(baseOutName + ".opt.spv", optspirv);         std::string disassembly;        if (!core.Disassemble(optspirv, &disassembly)) {            throw std::runtime_error("bad disassembly");        }         write(baseOutName + ".spv.txt", disassembly);    } catch (const std::runtime_error& error) {        qWarning() << error.what();    }} #endif template <typename K, typename V>std::unordered_map<V, K> invertMap(const std::unordered_map<K, V>& map) {    std::unordered_map<V, K> result;    for (const auto& entry : map) {        result[entry.second] = entry.first;    }    if (result.size() != map.size()) {        throw std::runtime_error("Map inversion failure, result size does not match input size");    }    return result;} uint32_t makeProgramId(uint32_t vertexId, uint32_t fragmentId) {    return (vertexId << 16) | fragmentId;} std::unordered_map<uint32_t, std::list<uint32_t>> shaderToProgramMap;std::unordered_map<uint32_t, std::unordered_set<uint32_t>> problemShaderIds;  static void verifyInterface(const gpu::Shader::Source& vertexSource,                            const gpu::Shader::Source& fragmentSource,                            shader::Dialect dialect,                            shader::Variant variant) {     auto programId = makeProgramId(vertexSource.id, fragmentSource.id);    shaderToProgramMap[vertexSource.id].push_back(programId);    shaderToProgramMap[fragmentSource.id].push_back(programId);     const auto& fragmentReflection = fragmentSource.getReflection(dialect, variant);    const auto& fragmentInputs = fragmentReflection.inputs;    const auto& vertexReflection = vertexSource.getReflection(dialect, variant);    const auto& vertexOutputs = vertexReflection.outputs;    bool named = false;    for (const auto& input : fragmentInputs) {        const auto& slot = input.second;        if (!vertexReflection.validOutput(slot)) {            problemShaderIds[fragmentSource.id].insert(vertexSource.id);            if (!named) {                named = true;                qWarning() << vertexSource.name.c_str() << " " << fragmentSource.name.c_str();            }            qWarning() << "Fragment shader reads slot " << slot << " not written by vertex shader";        }    }     for (const auto& output : vertexOutputs) {        const auto& slot = output.second;        if (!fragmentReflection.validInput(slot)) {            problemShaderIds[vertexSource.id].insert(fragmentSource.id);            if (!named) {                named = true;                qWarning() << vertexSource.name.c_str() << " " << fragmentSource.name.c_str();            }            qWarning() << "Vertex shader writes slot " << slot << " not read by fragment shader";        }    }     //if (fragmentInputs.empty()) {    //    return;    //}     //const auto& fragIn = fragmentReflection.inputs;    //if (vertexReflection.outputs.empty()) {    //    throw std::runtime_error("No vertex outputs for fragment inputs");    //}     //const auto& vout = vertexReflection.outputs;    //auto vrev = invertMap(vout);    //for (const auto entry : fragIn) {    //    const auto& name = entry.first;    //    if (0 == vout.count(name)) {    //        throw std::runtime_error("Vertex outputs missing");    //    }    //    const auto& inLocation = entry.second;    //    const auto& outLocation = vout.at(name);

Lines 1–500 of 645.