diff options
Diffstat (limited to 'source/Shader.cpp')
-rw-r--r-- | source/Shader.cpp | 411 |
1 files changed, 388 insertions, 23 deletions
diff --git a/source/Shader.cpp b/source/Shader.cpp index a2d1c97..abb68bd 100644 --- a/source/Shader.cpp +++ b/source/Shader.cpp @@ -1,20 +1,110 @@ #include "Shader.hpp" #include "AppConfig.hpp" +#include "RapidJsonHelper.hpp" #include "ScopeGuard.hpp" #include "Utils.hpp" #include <imgui.h> +#include <rapidjson/document.h> +#include <rapidjson/filereadstream.h> +#include <rapidjson/filewritestream.h> +#include <rapidjson/writer.h> #include <cstddef> #include <cstdlib> -#include <filesystem> #include <utility> namespace fs = std::filesystem; -using namespace std::literals::string_literals; -using namespace std::literals::string_view_literals; +using namespace std::literals; -void ShaderDetails::ShowInspector() { +bool ShaderInfo::SaveToFile(const fs::path& filePath) const { + rapidjson::Document root(rapidjson::kObjectType); + + auto SaveInputOutputThings = [&](const char* name, const std::vector<InputOutputThing>& things) { + rapidjson::Value rvThings(rapidjson::kArrayType); + for (auto& thing : things) { + rapidjson::Value rvThing(rapidjson::kObjectType); + rvThing.AddMember("Name", thing.variable.name, root.GetAllocator()); + rvThing.AddMember("Semantic", rapidjson::StringRef(Tags::NameOf(thing.semantic)), root.GetAllocator()); + rvThing.AddMember("ScalarType", rapidjson::StringRef(Tags::NameOfGLType(thing.variable.scalarType)), root.GetAllocator()); + rvThing.AddMember("Width", thing.variable.width, root.GetAllocator()); + rvThing.AddMember("Height", thing.variable.height, root.GetAllocator()); + rvThing.AddMember("ArrayLength", thing.variable.arrayLength, root.GetAllocator()); + rvThing.AddMember("OpenGLLocation", thing.variable.location, root.GetAllocator()); + + rvThings.PushBack(rvThing, root.GetAllocator()); + } + root.AddMember(rapidjson::StringRef(name), rvThings, root.GetAllocator()); + }; + SaveInputOutputThings("Inputs", inputs); + SaveInputOutputThings("Outputs", outputs); + + // TODO uniforms + + auto file = Utils::OpenCstdioFile(filePath, Utils::WriteTruncate); + if (!file) return false; + DEFER { fclose(file); }; + + char writerBuffer[65536]; + rapidjson::FileWriteStream stream(file, writerBuffer, sizeof(writerBuffer)); + rapidjson::Writer<rapidjson::FileWriteStream> writer(stream); + root.Accept(writer); + + return true; +} + +bool ShaderInfo::LoadFromFile(const fs::path& filePath) { + auto file = Utils::OpenCstdioFile(filePath, Utils::Read); + if (!file) return false; + DEFER { fclose(file); }; + + char readerBuffer[65536]; + rapidjson::FileReadStream stream(file, readerBuffer, sizeof(readerBuffer)); + + rapidjson::Document root; + root.ParseStream(stream); + + auto LoadInputOutputThings = [&](std::string_view name, std::vector<InputOutputThing>& things) { + auto rvThings = rapidjson::GetProperty(root, rapidjson::kObjectType, name); + if (!rvThings) return; + if (!rvThings->IsArray()) return; + + for (auto& elm : rvThings->GetArray()) { + if (!elm.IsObject()) continue; + InputOutputThing thing; + + BRUSSEL_JSON_GET(elm, "Name", std::string, thing.variable.name, continue); + { // Semantic + auto value = rapidjson::GetProperty(elm, rapidjson::kStringType, "Semantic"sv); + if (!value) { + thing.semantic = Tags::VES_Generic; + } else { + thing.semantic = Tags::FindVertexElementSemantic(rapidjson::AsStringView(*value)); + } + } + { // Scalar type + auto value = rapidjson::GetProperty(elm, rapidjson::kStringType, "ScalarType"sv); + if (!value) continue; + thing.variable.scalarType = Tags::FindGLType(rapidjson::AsStringView(*value)); + } + BRUSSEL_JSON_GET(elm, "Width", int, thing.variable.width, continue); + BRUSSEL_JSON_GET(elm, "Height", int, thing.variable.height, continue); + BRUSSEL_JSON_GET(elm, "OpenGLLocation", int, thing.variable.location, continue); + BRUSSEL_JSON_GET_DEFAULT(elm, "ArrayLength", int, thing.variable.arrayLength, 1); + + things.push_back(std::move(thing)); + } + }; + LoadInputOutputThings("Inputs"sv, inputs); + LoadInputOutputThings("Outputs"sv, outputs); + + // TODO uniforms + + return true; +} + +Shader::Shader(std::string name) + : mName{ name } { } Shader::~Shader() { @@ -247,14 +337,275 @@ Shader::ErrorCode Shader::InitFromSource(std::string_view source) { #undef CATCH_ERROR -void Shader::GatherDetailsIfAbsent() { - if (mDetails) return; +namespace ProjectBrussel_UNITY_ID { +bool QueryMathInfo(GLenum type, GLenum& scalarType, int& width, int& height) { + auto DoOutput = [&](GLenum scalarTypeIn, int widthIn, int heightIn) { + width = widthIn; + height = heightIn; + scalarType = scalarTypeIn; + }; + + switch (type) { + case GL_FLOAT: + case GL_DOUBLE: + case GL_INT: + case GL_UNSIGNED_INT: + case GL_BOOL: { + DoOutput(type, 1, 1); + return true; + } + + case GL_FLOAT_VEC2: DoOutput(GL_FLOAT, 1, 2); return true; + case GL_FLOAT_VEC3: DoOutput(GL_FLOAT, 1, 3); return true; + case GL_FLOAT_VEC4: DoOutput(GL_FLOAT, 1, 4); return true; + case GL_DOUBLE_VEC2: DoOutput(GL_DOUBLE, 1, 2); return true; + case GL_DOUBLE_VEC3: DoOutput(GL_DOUBLE, 1, 3); return true; + case GL_DOUBLE_VEC4: DoOutput(GL_DOUBLE, 1, 4); return true; + case GL_INT_VEC2: DoOutput(GL_INT, 1, 2); return true; + case GL_INT_VEC3: DoOutput(GL_INT, 1, 3); return true; + case GL_INT_VEC4: DoOutput(GL_INT, 1, 4); return true; + case GL_UNSIGNED_INT_VEC2: DoOutput(GL_UNSIGNED_INT, 1, 2); return true; + case GL_UNSIGNED_INT_VEC3: DoOutput(GL_UNSIGNED_INT, 1, 3); return true; + case GL_UNSIGNED_INT_VEC4: DoOutput(GL_UNSIGNED_INT, 1, 4); return true; + case GL_BOOL_VEC2: DoOutput(GL_BOOL, 1, 2); return true; + case GL_BOOL_VEC3: DoOutput(GL_BOOL, 1, 3); return true; + case GL_BOOL_VEC4: DoOutput(GL_BOOL, 1, 4); return true; + + case GL_FLOAT_MAT2: DoOutput(GL_FLOAT, 2, 2); return true; + case GL_FLOAT_MAT3: DoOutput(GL_FLOAT, 3, 3); return true; + case GL_FLOAT_MAT4: DoOutput(GL_FLOAT, 4, 4); return true; + case GL_FLOAT_MAT2x3: DoOutput(GL_FLOAT, 2, 3); return true; + case GL_FLOAT_MAT2x4: DoOutput(GL_FLOAT, 2, 4); return true; + case GL_FLOAT_MAT3x2: DoOutput(GL_FLOAT, 3, 2); return true; + case GL_FLOAT_MAT3x4: DoOutput(GL_FLOAT, 3, 4); return true; + case GL_FLOAT_MAT4x2: DoOutput(GL_FLOAT, 4, 2); return true; + case GL_FLOAT_MAT4x3: DoOutput(GL_FLOAT, 4, 3); return true; + + case GL_DOUBLE_MAT2: DoOutput(GL_DOUBLE, 2, 2); return true; + case GL_DOUBLE_MAT3: DoOutput(GL_DOUBLE, 3, 3); return true; + case GL_DOUBLE_MAT4: DoOutput(GL_DOUBLE, 4, 4); return true; + case GL_DOUBLE_MAT2x3: DoOutput(GL_DOUBLE, 2, 3); return true; + case GL_DOUBLE_MAT2x4: DoOutput(GL_DOUBLE, 2, 4); return true; + case GL_DOUBLE_MAT3x2: DoOutput(GL_DOUBLE, 3, 2); return true; + case GL_DOUBLE_MAT3x4: DoOutput(GL_DOUBLE, 3, 4); return true; + case GL_DOUBLE_MAT4x2: DoOutput(GL_DOUBLE, 4, 2); return true; + case GL_DOUBLE_MAT4x3: DoOutput(GL_DOUBLE, 4, 3); return true; + } + + return false; +} + +bool QuerySamplerInfo(GLenum type) { + switch (type) { + case GL_SAMPLER_1D: + case GL_SAMPLER_2D: + case GL_SAMPLER_3D: + case GL_SAMPLER_CUBE: + case GL_SAMPLER_1D_SHADOW: + case GL_SAMPLER_2D_SHADOW: + case GL_SAMPLER_1D_ARRAY: + case GL_SAMPLER_2D_ARRAY: + case GL_SAMPLER_1D_ARRAY_SHADOW: + case GL_SAMPLER_2D_ARRAY_SHADOW: + case GL_SAMPLER_2D_MULTISAMPLE: + case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_SAMPLER_CUBE_SHADOW: + case GL_SAMPLER_BUFFER: + case GL_SAMPLER_2D_RECT: + case GL_SAMPLER_2D_RECT_SHADOW: + + case GL_INT_SAMPLER_1D: + case GL_INT_SAMPLER_2D: + case GL_INT_SAMPLER_3D: + case GL_INT_SAMPLER_CUBE: + case GL_INT_SAMPLER_1D_ARRAY: + case GL_INT_SAMPLER_2D_ARRAY: + case GL_INT_SAMPLER_2D_MULTISAMPLE: + case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_INT_SAMPLER_BUFFER: + case GL_INT_SAMPLER_2D_RECT: - mDetails = std::make_unique<ShaderDetails>(); + case GL_UNSIGNED_INT_SAMPLER_1D: + case GL_UNSIGNED_INT_SAMPLER_2D: + case GL_UNSIGNED_INT_SAMPLER_3D: + case GL_UNSIGNED_INT_SAMPLER_CUBE: + case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_BUFFER: + case GL_UNSIGNED_INT_SAMPLER_2D_RECT: + return true; + } + + return false; } -const ShaderDetails* Shader::GetDetails() const { - return mDetails.get(); +std::unique_ptr<ShaderVariable> CreateVariable(GLenum type, GLuint loc) { + GLenum scalarType; + int width; + int height; + if (QueryMathInfo(type, scalarType, width, height)) { + auto res = std::make_unique<ShaderMathVariable>(); + res->location = loc; + res->scalarType = type; + res->width = width; + res->height = height; + return res; + } + + if (QuerySamplerInfo(type)) { + auto res = std::make_unique<ShaderSamplerVariable>(); + res->location = loc; + res->type = type; + return res; + } + + return nullptr; +} +} // namespace ProjectBrussel_UNITY_ID + +bool Shader::CreateEmptyInfoIfAbsent() { + if (mInfo || !IsValid()) { + return false; + } + + mInfo = std::make_unique<ShaderInfo>(); + return true; +} + +bool Shader::GatherInfoIfAbsent() { + using namespace ProjectBrussel_UNITY_ID; + using ThingId = ShaderInfo::ThingId; + + if (mInfo || !IsValid()) { + return false; + } + + mInfo = std::make_unique<ShaderInfo>(); + + // TODO handle differnt types of variables with the same name + + // TODO work with OpenGL < 4.3, possibly with glslang +#if 0 + int inputCount; + glGetProgramInterfaceiv(mHandle, GL_PROGRAM_INPUT, GL_ACTIVE_RESOURCES, &inputCount); + int outputCount; + glGetProgramInterfaceiv(mHandle, GL_PROGRAM_OUTPUT, GL_ACTIVE_RESOURCES, &outputCount); + int uniformBlockCount; + glGetProgramInterfaceiv(mHandle, GL_UNIFORM_BLOCK, GL_ACTIVE_RESOURCES, &uniformBlockCount); + int uniformCount; + glGetProgramInterfaceiv(mHandle, GL_UNIFORM, GL_ACTIVE_RESOURCES, &uniformCount); + + // Gather inputs + auto GatherMathVars = [&](int count, GLenum resourceType, ShaderInfo::ThingKind resourceKind, std::vector<ShaderInfo::InputOutputThing>& list) { + for (int i = 0; i < count; ++i) { + const GLenum query[] = { GL_NAME_LENGTH, GL_TYPE, GL_LOCATION, GL_TOP_LEVEL_ARRAY_SIZE }; + GLint props[std::size(query)]; + glGetProgramResourceiv(mHandle, resourceType, i, std::size(query), query, std::size(props), nullptr, props); + auto& nameLength = props[0]; + auto& type = props[1]; + auto& loc = props[2]; + auto& arrayLength = props[3]; + + std::string fieldName(nameLength - 1, '\0'); + glGetProgramResourceName(mHandle, GL_UNIFORM, i, nameLength, nullptr, fieldName.data()); + + mInfo->things.insert(fieldName, ThingId{ resourceKind, i }); + + auto& thing = list.emplace_back(ShaderInfo::InputOutputThing{}); + auto& var = thing.variable; + var.name = std::move(fieldName); + var.arrayLength = arrayLength; + QueryMathInfo(type, var.scalarType, var.width, var.height); + } + }; + GatherMathVars(inputCount, GL_PROGRAM_INPUT, ShaderInfo::TKD_Input, mInfo->inputs); + GatherMathVars(outputCount, GL_PROGRAM_OUTPUT, ShaderInfo::TKD_Output, mInfo->outputs); + + // Gather uniform variables + for (int i = 0; i < uniformCount; ++i) { + const GLenum query[] = { GL_BLOCK_INDEX, GL_NAME_LENGTH, GL_TYPE, GL_LOCATION, GL_TOP_LEVEL_ARRAY_SIZE }; + GLint props[std::size(query)]; + glGetProgramResourceiv(mHandle, GL_UNIFORM, i, std::size(query), query, std::size(props), nullptr, props); + auto& blockIndex = props[0]; // Index in interface block + if (blockIndex != -1) { // If this is an interface block uniform, skip because it will be handled by our uniform blocks inspector + continue; + } + auto& nameLength = props[1]; + auto& type = props[2]; + auto& loc = props[3]; + auto& arrayLength = props[4]; + + std::string fieldName(nameLength - 1, '\0'); + glGetProgramResourceName(mHandle, GL_UNIFORM, i, nameLength, nullptr, fieldName.data()); + + mInfo->things.insert(fieldName, ThingId{ ShaderInfo::TKD_Uniform, i }); + mInfo->uniforms.push_back(CreateVariable(type, loc)); + } + + // Gather uniform blocks + for (int i = 0; i < uniformBlockCount; ++i) { + const GLenum blockQuery[] = { GL_NAME_LENGTH, GL_NUM_ACTIVE_VARIABLES }; + GLint blockProps[std::size(blockQuery)]; + glGetProgramResourceiv(mHandle, GL_UNIFORM_BLOCK, i, std::size(blockQuery), blockQuery, std::size(blockProps), nullptr, blockProps); + auto& nameLength = blockProps[0]; + auto& fieldCount = blockProps[1]; + + // glGetProgramResourceiv returns the length including the null terminator + std::string blockName(nameLength - 1, '\0'); + glGetProgramResourceName(mHandle, GL_UNIFORM_BLOCK, i, nameLength, nullptr, blockName.data()); + + const GLenum fieldsQuery[] = { GL_ACTIVE_VARIABLES }; + std::vector<GLint> fieldIndices(fieldCount); + glGetProgramResourceiv(mHandle, GL_UNIFORM_BLOCK, i, std::size(fieldsQuery), fieldsQuery, fieldIndices.size(), nullptr, fieldIndices.data()); + + ShaderUniformBlockVariable block; + block.index = i; + block.name = std::move(blockName); + // NOTE: blockName is invalid from this point on + + for (GLint idx : fieldIndices) { + const GLenum fieldQuery[] = { GL_NAME_LENGTH, GL_TYPE, GL_LOCATION, GL_TOP_LEVEL_ARRAY_SIZE }; + GLint fieldProps[std::size(fieldQuery)]; + glGetProgramResourceiv(mHandle, GL_UNIFORM, idx, std::size(fieldQuery), fieldQuery, std::size(fieldProps), nullptr, fieldProps); + auto& nameLength = fieldProps[0]; + auto& type = fieldProps[1]; + auto& location = fieldProps[2]; + auto& arrayLength = fieldProps[3]; + + std::string fieldName(nameLength - 1, '\0'); + glGetProgramResourceName(mHandle, GL_UNIFORM, idx, nameLength, nullptr, fieldName.data()); + + auto var = CreateVariable(type, location); + if (var->kind == ShaderVariable::KD_Math) { + auto math = static_cast<ShaderMathVariable*>(var.get()); + math->name = std::move(fieldName); + math->arrayLength = arrayLength; + } else if (var->kind == ShaderMathVariable::KD_Sampler) { + auto sampler = static_cast<ShaderSamplerVariable*>(var.get()); + sampler->name = std::move(fieldName); + sampler->arrayLength = arrayLength; + } + block.items.push_back(std::move(var)); + } + + mInfo->things.insert(block.name, { ShaderInfo::TKD_UniformBlock, i }); + mInfo->uniformBlocks.push_back(std::move(block)); + } + + mInfo->things.shrink_to_fit(); +#endif + + return true; +} + +ShaderInfo* Shader::GetInfo() const { + return mInfo.get(); +} + +const std::string& Shader::GetName() const { + return mName; } GLuint Shader::GetProgram() const { @@ -269,30 +620,44 @@ void ShaderManager::DiscoverShaders() { mShaders.clear(); auto path = AppConfig::assetDirPath / "Shaders"; + if (!fs::exists(path)) { + return; + } + for (auto& item : fs::directory_iterator(path)) { if (item.is_regular_file() && item.path().extension() == ".glsl") { - auto file = Utils::OpenCstdioFile(item.path(), Utils::Read); - if (!file) continue; + auto shaderFile = Utils::OpenCstdioFile(item.path(), Utils::Read); + if (!shaderFile) continue; + DEFER { fclose(shaderFile); }; - fseek(file, 0, SEEK_END); - auto fileSize = ftell(file); - rewind(file); + fseek(shaderFile, 0, SEEK_END); + auto shaderFileSize = ftell(shaderFile); + rewind(shaderFile); // Also add \0 ourselves - auto buffer = std::make_unique<char[]>(fileSize + 1); - fread(buffer.get(), fileSize, 1, file); - fclose(file); - buffer[fileSize] = '\0'; - std::string_view source(buffer.get(), fileSize); + auto buffer = std::make_unique<char[]>(shaderFileSize + 1); + fread(buffer.get(), shaderFileSize, 1, shaderFile); + buffer[shaderFileSize] = '\0'; + std::string_view source(buffer.get(), shaderFileSize); + + RcPtr shader(new Shader(item.path().stem().string())); + std::string_view shaderName(shader->GetName()); - RcPtr shader(new Shader()); + // Load shader auto err = shader->InitFromSource(source); if (err != Shader::Success) { continue; } - auto shaderName = item.path().stem().string(); - mShaders.insert(shaderName, std::move(shader)); + // Load shader info if present + fs::path infoPath(item.path()); + infoPath.replace_extension(".json"); + if (fs::exists(infoPath)) { + shader->CreateEmptyInfoIfAbsent(); + shader->GetInfo()->LoadFromFile(infoPath); + } + + mShaders.try_emplace(shaderName, std::move(shader)); } } } @@ -300,7 +665,7 @@ void ShaderManager::DiscoverShaders() { Shader* ShaderManager::FindShader(std::string_view name) { auto iter = mShaders.find(name); if (iter != mShaders.end()) { - return iter.value().Get(); + return iter->second.Get(); } else { return nullptr; } |