From d43508ba4843801cbbf1f42a27af260d4eef5701 Mon Sep 17 00:00:00 2001 From: hnOsmium0001 Date: Sun, 17 Apr 2022 20:08:57 -0700 Subject: Initial work on sprites and texture system --- source/Shader.cpp | 396 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 238 insertions(+), 158 deletions(-) (limited to 'source/Shader.cpp') diff --git a/source/Shader.cpp b/source/Shader.cpp index 4a576d0..61e80a1 100644 --- a/source/Shader.cpp +++ b/source/Shader.cpp @@ -18,114 +18,56 @@ namespace fs = std::filesystem; using namespace std::literals; -bool ShaderThingId::IsValid() const { - return kind == KD_Invalid; +void ShaderMathVariable::ShowInfo() const { + ImGui::BulletText("Location: %d\nName: %s\nSemantic: %s\nType: %s %dx%d", + location, + name.c_str(), + Tags::NameOf(semantic).data(), + Tags::NameOfGLType(scalarType).data(), + width, + height); } -ShaderVariable* ShaderInfo::FindVariable(const ShaderThingId& thing) { - switch (thing.kind) { - case ShaderThingId::KD_Input: return &inputs[thing.index].variable; - case ShaderThingId::KD_Output: return &outputs[thing.index].variable; - case ShaderThingId::KD_Uniform: return uniforms[thing.index].get(); - case ShaderThingId::KD_Invalid: break; - } - return nullptr; +void ShaderSamplerVariable::ShowInfo() const { + ImGui::BulletText("Location: %d\nName: %s\nSemantic: %s\nType: Sampler", + location, + name.c_str(), + Tags::NameOf(semantic).data()); } -bool ShaderInfo::SaveToFile(const fs::path& filePath) const { - rapidjson::Document root(rapidjson::kObjectType); - - auto SaveInputOutputThings = [&](const char* name, const std::vector& 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 writer(stream); - root.Accept(writer); - - return true; +bool ShaderThingId::IsValid() const { + return kind == KD_Invalid; } -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& 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)); +namespace ProjectBrussel_UNITY_ID { +GLuint FindLocation(const std::vector& vars, Tags::VertexElementSemantic semantic) { + for (auto& var : vars) { + if (var.semantic == semantic) { + return var.location; } - }; - LoadInputOutputThings("Inputs"sv, inputs); - LoadInputOutputThings("Outputs"sv, outputs); - - // TODO uniforms + } + return Tags::kInvalidLocation; +} +} // namespace ProjectBrussel_UNITY_ID - return true; +GLuint ShaderInfo::FindInputLocation(Tags::VertexElementSemantic semantic) { + using namespace ProjectBrussel_UNITY_ID; + return FindLocation(inputs, semantic); } -void ShaderInfo::LoadLocations(const Shader& ownerShader) { - GLuint program = ownerShader.GetProgram(); +GLuint ShaderInfo::FindOutputLocation(Tags::VertexElementSemantic semantic) { + using namespace ProjectBrussel_UNITY_ID; + return FindLocation(outputs, semantic); +} - // TODO inputs - // TODO outputs - for (auto& uniform : uniforms) { - uniform->location = glGetUniformLocation(ownerShader.GetProgram(), uniform->name.c_str()); +ShaderVariable* ShaderInfo::FindVariable(const ShaderThingId& thing) { + switch (thing.kind) { + case ShaderThingId::KD_Input: return &inputs[thing.index]; + case ShaderThingId::KD_Output: return &outputs[thing.index]; + case ShaderThingId::KD_Uniform: return uniforms[thing.index].get(); + case ShaderThingId::KD_Invalid: break; } + return nullptr; } Shader::Shader(std::string name) @@ -133,7 +75,7 @@ Shader::Shader(std::string name) } Shader::~Shader() { - glDeleteProgram(mHandle); + glDeleteProgram(mProgram); } namespace ProjectBrussel_UNITY_ID { @@ -155,10 +97,10 @@ Shader::ErrorCode CreateShader(GLuint& out, const char* src, int beginIdx, int e std::string log(len, '\0'); glGetShaderInfoLog(out, len, nullptr, log.data()); - return Shader ::CompilationFailed; + return Shader ::EC_CompilationFailed; } - return Shader::Success; + return Shader::EC_Success; } Shader::ErrorCode CreateShader(GLuint& out, std::string_view str, GLenum type) { @@ -176,17 +118,17 @@ Shader::ErrorCode LinkShaderProgram(GLuint program) { std::string log(len, '\0'); glGetProgramInfoLog(program, len, nullptr, log.data()); - return Shader::LinkingFailed; + return Shader::EC_LinkingFailed; } - return Shader::Success; + return Shader::EC_Success; } } // namespace ProjectBrussel_UNITY_ID -#define CATCH_ERROR_IMPL(x, name) \ - auto name = x; \ - if (name != Shader::Success) { \ - return name; \ +#define CATCH_ERROR_IMPL(x, name) \ + auto name = x; \ + if (name != Shader::EC_Success) { \ + return name; \ } #define CATCH_ERROR(x) CATCH_ERROR_IMPL(x, UNIQUE_NAME(result)) @@ -194,7 +136,7 @@ Shader::ErrorCode Shader::InitFromSources(const ShaderSources& sources) { using namespace ProjectBrussel_UNITY_ID; if (IsValid()) { - return ShaderAlreadyCreated; + return EC_AlreadyInitialized; } GLuint program = glCreateProgram(); @@ -238,9 +180,9 @@ Shader::ErrorCode Shader::InitFromSources(const ShaderSources& sources) { CATCH_ERROR(LinkShaderProgram(program)); sg.Dismiss(); - mHandle = program; + mProgram = program; - return Success; + return EC_Success; } Shader::ErrorCode Shader::InitFromSource(std::string_view source) { @@ -268,7 +210,7 @@ Shader::ErrorCode Shader::InitFromSource(std::string_view source) { auto CommitSection = [&]() -> ErrorCode { if (prevBegin == -1 || prevEnd == -1) { // Not actually "succeeding" here, but we just want to skip this call and continue - return Success; + return EC_Success; } if (prevShaderVariant == "vertex" && !vertex) { @@ -282,14 +224,14 @@ Shader::ErrorCode Shader::InitFromSource(std::string_view source) { } else if (prevShaderVariant == "fragment" && !fragment) { CATCH_ERROR(CreateShader(fragment, source.data(), prevBegin, prevEnd, GL_FRAGMENT_SHADER)); } else { - return InvalidShaderVariant; + return EC_InvalidShaderVariant; } prevBegin = -1; prevEnd = -1; prevShaderVariant.clear(); - return Success; + return EC_Success; }; constexpr const char* kMarker = "#type "; @@ -355,9 +297,9 @@ Shader::ErrorCode Shader::InitFromSource(std::string_view source) { CATCH_ERROR(LinkShaderProgram(program)); sg.Dismiss(); - mHandle = program; + mProgram = program; - return Success; + return EC_Success; } #undef CATCH_ERROR @@ -498,23 +440,10 @@ std::unique_ptr CreateVariable(GLenum type, GLuint loc) { } } // namespace ProjectBrussel_UNITY_ID -bool Shader::CreateEmptyInfoIfAbsent() { - if (mInfo || !IsValid()) { - return false; - } - - mInfo = std::make_unique(); - return true; -} - -bool Shader::GatherInfoIfAbsent() { +bool Shader::GatherInfoShaderIntrospection() { using namespace ProjectBrussel_UNITY_ID; - if (mInfo || !IsValid()) { - return false; - } - - mInfo = std::make_unique(); + mInfo = {}; // TODO handle differnt types of variables with the same name @@ -522,45 +451,44 @@ bool Shader::GatherInfoIfAbsent() { return true; int inputCount; - glGetProgramInterfaceiv(mHandle, GL_PROGRAM_INPUT, GL_ACTIVE_RESOURCES, &inputCount); + glGetProgramInterfaceiv(mProgram, GL_PROGRAM_INPUT, GL_ACTIVE_RESOURCES, &inputCount); int outputCount; - glGetProgramInterfaceiv(mHandle, GL_PROGRAM_OUTPUT, GL_ACTIVE_RESOURCES, &outputCount); + glGetProgramInterfaceiv(mProgram, GL_PROGRAM_OUTPUT, GL_ACTIVE_RESOURCES, &outputCount); int uniformBlockCount; - glGetProgramInterfaceiv(mHandle, GL_UNIFORM_BLOCK, GL_ACTIVE_RESOURCES, &uniformBlockCount); + glGetProgramInterfaceiv(mProgram, GL_UNIFORM_BLOCK, GL_ACTIVE_RESOURCES, &uniformBlockCount); int uniformCount; - glGetProgramInterfaceiv(mHandle, GL_UNIFORM, GL_ACTIVE_RESOURCES, &uniformCount); + glGetProgramInterfaceiv(mProgram, GL_UNIFORM, GL_ACTIVE_RESOURCES, &uniformCount); // Gather inputs - auto GatherMathVars = [&](int count, GLenum resourceType, ShaderThingId::Kind resourceKind, std::vector& list) { + auto GatherMathVars = [&](int count, GLenum resourceType, ShaderThingId::Kind resourceKind, std::vector& 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); + glGetProgramResourceiv(mProgram, 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()); + glGetProgramResourceName(mProgram, GL_UNIFORM, i, nameLength, nullptr, fieldName.data()); - mInfo->things.try_emplace(fieldName, ShaderThingId{ resourceKind, i }); + mInfo.things.try_emplace(fieldName, ShaderThingId{ 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); + auto& thing = list.emplace_back(); + thing.name = std::move(fieldName); + thing.arrayLength = arrayLength; + QueryMathInfo(type, thing.scalarType, thing.width, thing.height); } }; - GatherMathVars(inputCount, GL_PROGRAM_INPUT, ShaderThingId::KD_Input, mInfo->inputs); - GatherMathVars(outputCount, GL_PROGRAM_OUTPUT, ShaderThingId::KD_Output, mInfo->outputs); + GatherMathVars(inputCount, GL_PROGRAM_INPUT, ShaderThingId::KD_Input, mInfo.inputs); + GatherMathVars(outputCount, GL_PROGRAM_OUTPUT, ShaderThingId::KD_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); + glGetProgramResourceiv(mProgram, 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; @@ -571,29 +499,182 @@ bool Shader::GatherInfoIfAbsent() { auto& arrayLength = props[4]; std::string fieldName(nameLength - 1, '\0'); - glGetProgramResourceName(mHandle, GL_UNIFORM, i, nameLength, nullptr, fieldName.data()); + glGetProgramResourceName(mProgram, GL_UNIFORM, i, nameLength, nullptr, fieldName.data()); - mInfo->things.try_emplace(fieldName, ShaderThingId{ ShaderThingId::KD_Uniform, i }); - mInfo->uniforms.push_back(CreateVariable(type, loc)); + mInfo.things.try_emplace(fieldName, ShaderThingId{ ShaderThingId::KD_Uniform, i }); + mInfo.uniforms.push_back(CreateVariable(type, loc)); } return true; } -ShaderInfo* Shader::GetInfo() const { - return mInfo.get(); +bool Shader::IsAnnoymous() const { + return mName.empty(); } -const std::string& Shader::GetName() const { - return mName; +bool Shader::IsValid() const { + return mProgram != 0; } -GLuint Shader::GetProgram() const { - return mHandle; +namespace ProjectBrussel_UNITY_ID { +void WriteShaderVariable(rapidjson::Value& value, rapidjson::Document& root, const ShaderVariable& var) { + value.AddMember("Name", var.name, root.GetAllocator()); + value.AddMember("Semantic", rapidjson::StringRef(Tags::NameOf(var.semantic)), root.GetAllocator()); + value.AddMember("OpenGLLocation", var.location, root.GetAllocator()); } -bool Shader::IsValid() const { - return mHandle != 0; +bool ReadShaderVariable(const rapidjson::Value& value, ShaderVariable& var) { + BRUSSEL_JSON_GET(value, "Name", std::string, var.name, return false); + { // Semantic + auto rvSemantic = rapidjson::GetProperty(value, rapidjson::kStringType, "Semantic"sv); + if (!rvSemantic) { + var.semantic = Tags::VES_Generic; + } else { + var.semantic = Tags::FindVertexElementSemantic(rapidjson::AsStringView(*rvSemantic)); + } + } + BRUSSEL_JSON_GET_DEFAULT(value, "OpenGLLocation", int, var.location, 0); + return true; +} + +void WriteShaderMathVariable(rapidjson::Value& value, rapidjson::Document& root, const ShaderMathVariable& var) { + WriteShaderVariable(value, root, var); + value.AddMember("ScalarType", rapidjson::StringRef(Tags::NameOfGLType(var.scalarType)), root.GetAllocator()); + value.AddMember("Width", var.width, root.GetAllocator()); + value.AddMember("Height", var.height, root.GetAllocator()); + value.AddMember("ArrayLength", var.arrayLength, root.GetAllocator()); +} + +bool ReadShaderMathVariable(const rapidjson::Value& value, ShaderMathVariable& var) { + if (!ReadShaderVariable(value, var)) return false; + { + auto rvScalar = rapidjson::GetProperty(value, rapidjson::kStringType, "ScalarType"sv); + if (!rvScalar) return false; + var.scalarType = Tags::FindGLType(rapidjson::AsStringView(*rvScalar)); + } + BRUSSEL_JSON_GET(value, "Width", int, var.width, return false); + BRUSSEL_JSON_GET(value, "Height", int, var.height, return false); + BRUSSEL_JSON_GET_DEFAULT(value, "ArrayLength", int, var.arrayLength, 1); + return true; +} + +void WriteShaderSamplerVariable(rapidjson::Value& value, rapidjson::Document& root, const ShaderSamplerVariable& var) { + WriteShaderVariable(value, root, var); + // TODO +} + +bool ReadShaderSamplerVariable(const rapidjson::Value& value, ShaderSamplerVariable& var) { + if (!ReadShaderVariable(value, var)) return false; + BRUSSEL_JSON_GET_DEFAULT(value, "ArrayLength", int, var.arrayLength, 1); + return true; +} +} // namespace ProjectBrussel_UNITY_ID + +bool Shader::SaveMetadataToFile(const fs::path& filePath) const { + using namespace ProjectBrussel_UNITY_ID; + + rapidjson::Document root(rapidjson::kObjectType); + + auto SaveMathVars = [&](const char* name, const std::vector& vars) { + rapidjson::Value rvThings(rapidjson::kArrayType); + for (auto& thing : vars) { + rapidjson::Value rvThing(rapidjson::kObjectType); + WriteShaderMathVariable(rvThing, root, thing); + + rvThings.PushBack(rvThing, root.GetAllocator()); + } + root.AddMember(rapidjson::StringRef(name), rvThings, root.GetAllocator()); + }; + SaveMathVars("Inputs", mInfo.inputs); + SaveMathVars("Outputs", mInfo.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 writer(stream); + root.Accept(writer); + + return true; +} + +bool Shader::LoadMetadataFromFile(const fs::path& filePath) { + using namespace ProjectBrussel_UNITY_ID; + + mInfo = {}; + + 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 LoadMathVars = [&](std::string_view name, ShaderThingId::Kind kind, std::vector& vars) { + auto rvThings = rapidjson::GetProperty(root, rapidjson::kArrayType, name); + if (!rvThings) return; + + for (auto& rv : rvThings->GetArray()) { + if (!rv.IsObject()) continue; + ShaderMathVariable thing; + ReadShaderMathVariable(rv, thing); + + mInfo.things.try_emplace(thing.name, ShaderThingId{ kind, (int)vars.size() }); + vars.push_back(std::move(thing)); + } + }; + LoadMathVars("Inputs"sv, ShaderThingId::KD_Input, mInfo.inputs); + LoadMathVars("Outputs"sv, ShaderThingId::KD_Output, mInfo.outputs); + + auto rvUniforms = rapidjson::GetProperty(root, rapidjson::kArrayType, "Uniforms"sv); + if (!rvUniforms) return false; + for (auto& rvUniform : rvUniforms->GetArray()) { + if (!rvUniform.IsObject()) continue; + + auto rvType = rapidjson::GetProperty(rvUniform, rapidjson::kStringType, "Type"sv); + if (!rvType) continue; + auto type = rapidjson::AsStringView(*rvType); + + auto rvValue = rapidjson::GetProperty(rvUniform, rapidjson::kObjectType, "Value"sv); + if (!rvValue) continue; + + bool autoFill; // TODO store autofill uniforms somewhere else + BRUSSEL_JSON_GET_DEFAULT(rvUniform, "AutoFill", bool, autoFill, false); + if (autoFill) continue; + + auto uniform = [&]() -> std::unique_ptr { + if (type == "Math"sv) { + auto uniform = std::make_unique(); + ReadShaderMathVariable(*rvValue, *uniform); + + return uniform; + } else if (type == "Sampler"sv) { + auto uniform = std::make_unique(); + ReadShaderSamplerVariable(*rvValue, *uniform); + + return uniform; + } + + return nullptr; + }(); + if (uniform) { + mInfo.things.try_emplace(uniform->name, ShaderThingId{ ShaderThingId::KD_Uniform, (int)mInfo.uniforms.size() }); + mInfo.uniforms.emplace_back(std::move(uniform)); + } + } + + for (auto& uniform : mInfo.uniforms) { + uniform->location = glGetUniformLocation(mProgram, uniform->name.c_str()); + } + + return true; } void ShaderManager::DiscoverShaders() { @@ -625,7 +706,7 @@ void ShaderManager::DiscoverShaders() { // Load shader auto err = shader->InitFromSource(source); - if (err != Shader::Success) { + if (err != Shader::EC_Success) { continue; } @@ -633,8 +714,7 @@ void ShaderManager::DiscoverShaders() { fs::path infoPath(item.path()); infoPath.replace_extension(".json"); if (fs::exists(infoPath)) { - shader->CreateEmptyInfoIfAbsent(); - shader->GetInfo()->LoadFromFile(infoPath); + shader->LoadMetadataFromFile(infoPath); } mShaders.try_emplace(shaderName, std::move(shader)); -- cgit v1.2.3-70-g09d2