aboutsummaryrefslogtreecommitdiff
path: root/src/brussel.engine/Shader.cpp
diff options
context:
space:
mode:
authorrtk0c <[email protected]>2023-10-19 22:50:07 -0700
committerrtk0c <[email protected]>2025-08-16 11:31:16 -0700
commit297232d21594b138bb368a42b5b0d085ff9ed6aa (patch)
tree075d5407e1e12a9d35cbee6e4c20ad34e0765c42 /src/brussel.engine/Shader.cpp
parentd5cd34ff69f7fd134d5450696f298af1a864afbc (diff)
The great renaming: switch to "module style"
Diffstat (limited to 'src/brussel.engine/Shader.cpp')
-rw-r--r--src/brussel.engine/Shader.cpp711
1 files changed, 711 insertions, 0 deletions
diff --git a/src/brussel.engine/Shader.cpp b/src/brussel.engine/Shader.cpp
new file mode 100644
index 0000000..9bf2e0e
--- /dev/null
+++ b/src/brussel.engine/Shader.cpp
@@ -0,0 +1,711 @@
+#include "Shader.hpp"
+
+#include "AppConfig.hpp"
+
+#include <Metadata.hpp>
+#include <RapidJsonHelper.hpp>
+#include <ScopeGuard.hpp>
+#include <Utils.hpp>
+
+#include <fmt/format.h>
+#include <imgui.h>
+#include <misc/cpp/imgui_stdlib.h>
+#include <rapidjson/document.h>
+#include <cassert>
+#include <cstddef>
+#include <cstdlib>
+#include <utility>
+
+using namespace std::literals;
+
+void ShaderMathVariable::ShowInfo() const {
+ ImGui::BulletText("Location: %d\nName: %s\nSemantic: %.*s\nType: %.*s %dx%d",
+ location,
+ name.c_str(),
+ PRINTF_STRING_VIEW(Metadata::EnumToString(semantic)),
+ PRINTF_STRING_VIEW(Tags::GLTypeToString(scalarType)),
+ width,
+ height);
+}
+
+void ShaderSamplerVariable::ShowInfo() const {
+ ImGui::BulletText("Location: %d\nName: %s\nSemantic: %.*s\nType: Sampler",
+ location,
+ name.c_str(),
+ PRINTF_STRING_VIEW(Metadata::EnumToString(semantic)));
+}
+
+namespace ProjectBrussel_UNITY_ID {
+GLuint FindLocation(const std::vector<ShaderMathVariable>& vars, Tags::VertexElementSemantic semantic) {
+ for (auto& var : vars) {
+ if (var.semantic == semantic) {
+ return var.location;
+ }
+ }
+ return Tags::kInvalidLocation;
+}
+
+constexpr auto kAfnTransform = "transform";
+constexpr auto kAfnTime = "time";
+constexpr auto kAfnDeltaTime = "deltaTime";
+constexpr auto kAfnTextureAtlas = "textureAtlas";
+
+void InitAutoFill(const char* name, GLuint program, GLuint& location) {
+ GLint result = glGetUniformLocation(program, name);
+ if (result != -1) {
+ location = result;
+ }
+}
+
+void InitAutoFills(Shader& shader) {
+ GLuint pg = shader.GetProgram();
+ InitAutoFill(kAfnTransform, pg, shader.autofill_Transform);
+ InitAutoFill(kAfnTime, pg, shader.autofill_Time);
+ InitAutoFill(kAfnDeltaTime, pg, shader.autofill_DeltaTime);
+ InitAutoFill(kAfnTextureAtlas, pg, shader.autofill_TextureAtlas);
+}
+} // namespace ProjectBrussel_UNITY_ID
+
+GLuint ShaderInfo::FindInputLocation(Tags::VertexElementSemantic semantic) {
+ using namespace ProjectBrussel_UNITY_ID;
+ return FindLocation(inputs, semantic);
+}
+
+GLuint ShaderInfo::FindOutputLocation(Tags::VertexElementSemantic semantic) {
+ using namespace ProjectBrussel_UNITY_ID;
+ return FindLocation(outputs, semantic);
+}
+
+Shader::Shader() {
+}
+
+Shader::~Shader() {
+ glDeleteProgram(mProgram);
+}
+
+namespace ProjectBrussel_UNITY_ID {
+// Grabs section [begin, end)
+Shader::ErrorCode CreateShader(GLuint& out, const char* src, int beginIdx, int endIdx, GLenum type) {
+ out = glCreateShader(type);
+
+ const GLchar* begin = &src[beginIdx];
+ const GLint len = endIdx - beginIdx;
+ glShaderSource(out, 1, &begin, &len);
+
+ glCompileShader(out);
+ GLint compileStatus;
+ glGetShaderiv(out, GL_COMPILE_STATUS, &compileStatus);
+ if (compileStatus == GL_FALSE) {
+ GLint len;
+ glGetShaderiv(out, GL_INFO_LOG_LENGTH, &len);
+
+ std::string log(len, '\0');
+ glGetShaderInfoLog(out, len, nullptr, log.data());
+
+ return Shader ::EC_CompilationFailed;
+ }
+
+ return Shader::EC_Success;
+}
+
+Shader::ErrorCode CreateShader(GLuint& out, std::string_view str, GLenum type) {
+ return CreateShader(out, str.data(), 0, str.size(), type);
+}
+
+Shader::ErrorCode LinkShaderProgram(GLuint program) {
+ glLinkProgram(program);
+ GLint linkStatus;
+ glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
+ if (linkStatus == GL_FALSE) {
+ GLint len;
+ glGetProgramiv(program, GL_INFO_LOG_LENGTH, &len);
+
+ std::string log(len, '\0');
+ glGetProgramInfoLog(program, len, nullptr, log.data());
+
+ return Shader::EC_LinkingFailed;
+ }
+
+ return Shader::EC_Success;
+}
+} // namespace ProjectBrussel_UNITY_ID
+
+#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))
+
+Shader::ErrorCode Shader::InitFromSources(const ShaderSources& sources) {
+ using namespace ProjectBrussel_UNITY_ID;
+
+ if (IsValid()) {
+ return EC_AlreadyInitialized;
+ }
+
+ GLuint program = glCreateProgram();
+ ScopeGuard sg = [&]() { glDeleteProgram(program); };
+
+ GLuint vertex = 0;
+ DEFER {
+ glDeleteShader(vertex);
+ };
+ if (!sources.vertex.empty()) {
+ CATCH_ERROR(CreateShader(vertex, sources.vertex, GL_VERTEX_SHADER));
+ glAttachShader(program, vertex);
+ }
+
+ GLuint geometry = 0;
+ DEFER {
+ glDeleteShader(geometry);
+ };
+ if (!sources.geometry.empty()) {
+ CATCH_ERROR(CreateShader(geometry, sources.geometry, GL_GEOMETRY_SHADER));
+ glAttachShader(program, geometry);
+ }
+
+ GLuint tessControl = 0;
+ DEFER {
+ glDeleteShader(tessControl);
+ };
+ if (!sources.tessControl.empty()) {
+ CATCH_ERROR(CreateShader(tessControl, sources.tessControl, GL_TESS_CONTROL_SHADER));
+ glAttachShader(program, tessControl);
+ }
+
+ GLuint tessEval = 0;
+ DEFER {
+ glDeleteShader(tessEval);
+ };
+ if (!sources.tessEval.empty()) {
+ CATCH_ERROR(CreateShader(tessEval, sources.tessEval, GL_TESS_EVALUATION_SHADER));
+ glAttachShader(program, tessEval);
+ }
+
+ GLuint fragment = 0;
+ DEFER {
+ glDeleteShader(fragment);
+ };
+ if (!sources.fragment.empty()) {
+ CATCH_ERROR(CreateShader(fragment, sources.fragment, GL_FRAGMENT_SHADER));
+ glAttachShader(program, fragment);
+ }
+
+ CATCH_ERROR(LinkShaderProgram(program));
+
+ sg.Dismiss();
+ mProgram = program;
+
+ InitAutoFills(*this);
+
+ return EC_Success;
+}
+
+Shader::ErrorCode Shader::InitFromSource(std::string_view source) {
+ using namespace ProjectBrussel_UNITY_ID;
+
+ GLuint vertex = 0;
+ DEFER {
+ glDeleteShader(vertex);
+ };
+
+ GLuint geometry = 0;
+ DEFER {
+ glDeleteShader(geometry);
+ };
+
+ GLuint tessControl = 0;
+ DEFER {
+ glDeleteShader(tessControl);
+ };
+
+ GLuint tessEval = 0;
+ DEFER {
+ glDeleteShader(tessEval);
+ };
+
+ GLuint fragment = 0;
+ DEFER {
+ glDeleteShader(fragment);
+ };
+
+ int prevBegin = -1; // Excluding #type marker
+ int prevEnd = -1; // [begin, end)
+ std::string prevShaderVariant;
+
+ auto CommitSection = [&]() -> ErrorCode {
+ if (prevBegin == -1 || prevEnd == -1) {
+ // Not actually "succeeding" here, but we just want to skip this call and continue
+ return EC_Success;
+ }
+
+ if (prevShaderVariant == "vertex" && !vertex) {
+ CATCH_ERROR(CreateShader(vertex, source.data(), prevBegin, prevEnd, GL_VERTEX_SHADER));
+ } else if (prevShaderVariant == "geometry" && !geometry) {
+ CATCH_ERROR(CreateShader(geometry, source.data(), prevBegin, prevEnd, GL_GEOMETRY_SHADER));
+ } else if (prevShaderVariant == "tessellation_control" && !tessControl) {
+ CATCH_ERROR(CreateShader(tessControl, source.data(), prevBegin, prevEnd, GL_TESS_CONTROL_SHADER));
+ } else if (prevShaderVariant == "tessellation_evaluation" && !tessEval) {
+ CATCH_ERROR(CreateShader(tessEval, source.data(), prevBegin, prevEnd, GL_TESS_EVALUATION_SHADER));
+ } else if (prevShaderVariant == "fragment" && !fragment) {
+ CATCH_ERROR(CreateShader(fragment, source.data(), prevBegin, prevEnd, GL_FRAGMENT_SHADER));
+ } else {
+ return EC_InvalidShaderVariant;
+ }
+
+ prevBegin = -1;
+ prevEnd = -1;
+ prevShaderVariant.clear();
+
+ return EC_Success;
+ };
+
+ constexpr const char* kMarker = "#type ";
+ bool matchingDirective = true; // If true, we are matching marker pattern; if false, we are accumulating shader variant identifier
+ int matchIndex = 0; // Current index of the pattern trying to match
+ std::string shaderVariant;
+
+ // Don't use utf8 iterator, shader sources are expected to be ASCII only
+ for (size_t i = 0; i < source.size(); ++i) {
+ char c = source[i];
+
+ if (matchingDirective) {
+ if (c == kMarker[matchIndex]) {
+ // Matched the expected character, go to next char in pattern
+ matchIndex++;
+
+ // If we are at the end of the marker pattern...
+ if (kMarker[matchIndex] == '\0') {
+ matchingDirective = false;
+ matchIndex = 0;
+ continue;
+ }
+
+ // This might be a shader variant directive -> might be end of a section
+ if (c == '#') {
+ prevEnd = i;
+ continue;
+ }
+ } else {
+ // Unexpected character, rollback to beginning
+ matchIndex = 0;
+ }
+ } else {
+ if (c == '\n') {
+ // Found complete shader variant directive
+
+ CATCH_ERROR(CommitSection()); // Try commit section, for the first apparent of #type this should do nothing, as `prevEnd` will still be -1
+ prevBegin = i + 1; // +1 to skip new line (technically not needed)
+ prevShaderVariant = std::move(shaderVariant);
+
+ matchingDirective = true;
+ shaderVariant.clear();
+ } else {
+ // Simply accumulate to shader variant buffer
+ shaderVariant += c;
+ }
+ }
+ }
+
+ // Commit the last section
+ prevEnd = static_cast<int>(source.size());
+ CATCH_ERROR(CommitSection());
+
+ GLuint program = glCreateProgram();
+ ScopeGuard sg = [&]() { glDeleteProgram(program); };
+
+ if (vertex) glAttachShader(program, vertex);
+ if (geometry) glAttachShader(program, geometry);
+ if (tessControl) glAttachShader(program, tessControl);
+ if (tessEval) glAttachShader(program, tessEval);
+ if (fragment) glAttachShader(program, fragment);
+
+ CATCH_ERROR(LinkShaderProgram(program));
+
+ sg.Dismiss();
+ mProgram = program;
+
+ InitAutoFills(*this);
+
+ return EC_Success;
+}
+
+#undef CATCH_ERROR
+
+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;
+
+ default: break;
+ }
+
+ 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:
+
+ 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;
+
+ default: break;
+ }
+
+ return false;
+}
+
+std::variant<ShaderMathVariable, ShaderSamplerVariable> CreateVariable(GLenum type, GLuint loc) {
+ GLenum scalarType;
+ int width;
+ int height;
+ if (QueryMathInfo(type, scalarType, width, height)) {
+ ShaderMathVariable res;
+ res.location = loc;
+ res.scalarType = type;
+ res.width = width;
+ res.height = height;
+ return res;
+ }
+
+ if (QuerySamplerInfo(type)) {
+ ShaderSamplerVariable res;
+ res.location = loc;
+ res.samplerType = type;
+ return res;
+ }
+
+ throw std::runtime_error(fmt::format("Unknown OpenGL shader uniform type {}", type));
+}
+} // namespace ProjectBrussel_UNITY_ID
+
+bool Shader::GatherInfoShaderIntrospection() {
+ using namespace ProjectBrussel_UNITY_ID;
+
+ mInfo = {};
+
+ // TODO handle differnt types of variables with the same name
+
+ // TODO work with OpenGL < 4.3, possibly with glslang
+ return true;
+
+ int inputCount;
+ glGetProgramInterfaceiv(mProgram, GL_PROGRAM_INPUT, GL_ACTIVE_RESOURCES, &inputCount);
+ int outputCount;
+ glGetProgramInterfaceiv(mProgram, GL_PROGRAM_OUTPUT, GL_ACTIVE_RESOURCES, &outputCount);
+ int uniformBlockCount;
+ glGetProgramInterfaceiv(mProgram, GL_UNIFORM_BLOCK, GL_ACTIVE_RESOURCES, &uniformBlockCount);
+ int uniformCount;
+ glGetProgramInterfaceiv(mProgram, GL_UNIFORM, GL_ACTIVE_RESOURCES, &uniformCount);
+
+ // Gather inputs
+ auto GatherMathVars = [&](int count, GLenum resourceType, ShaderThingId::Kind resourceKind, std::vector<ShaderMathVariable>& 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(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(mProgram, GL_UNIFORM, i, nameLength, nullptr, fieldName.data());
+
+ mInfo.things.try_emplace(fieldName, ShaderThingId{ resourceKind, i });
+
+ 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);
+
+ // 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(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;
+ }
+ auto& nameLength = props[1];
+ auto& type = props[2];
+ auto& loc = props[3];
+ auto& arrayLength = props[4];
+
+ std::string fieldName(nameLength - 1, '\0');
+ 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));
+ }
+
+ return true;
+}
+
+bool Shader::IsValid() const {
+ return mProgram != 0;
+}
+
+IresShader::IresShader()
+ : IresObject(KD_Shader) {
+ InvalidateInstance();
+}
+
+Shader* IresShader::GetInstance() const {
+ return mInstance.Get();
+}
+
+void IresShader::InvalidateInstance() {
+ if (mInstance != nullptr) {
+ mInstance->mIres = nullptr;
+ }
+ mInstance.Attach(new Shader());
+ mInstance->mIres = this;
+}
+
+void IresShader::ShowEditor(IEditor& editor) {
+ using namespace Tags;
+ using namespace ProjectBrussel_UNITY_ID;
+
+ IresObject::ShowEditor(editor);
+
+ if (ImGui::Button("Gather info")) {
+ mInstance->GatherInfoShaderIntrospection();
+ }
+
+ if (ImGui::InputText("Source file", &mSourceFile, ImGuiInputTextFlags_EnterReturnsTrue)) {
+ InvalidateInstance();
+ }
+ // In other cases, mSourceFile will be reverted to before edit
+
+ // TODO macros
+
+ ImGui::Separator();
+
+ auto& info = mInstance->GetInfo();
+ if (ImGui::CollapsingHeader("General")) {
+ ImGui::Text("OpenGL program ID: %u", mInstance->GetProgram());
+ }
+ if (ImGui::CollapsingHeader("Inputs")) {
+ for (auto& input : info.inputs) {
+ input.ShowInfo();
+ }
+ }
+ if (ImGui::CollapsingHeader("Outputs")) {
+ for (auto& output : info.outputs) {
+ output.ShowInfo();
+ }
+ }
+ if (ImGui::CollapsingHeader("Uniforms")) {
+ for (auto& uniform : info.uniforms) {
+ std::visit([](auto&& v) { v.ShowInfo(); }, uniform);
+ }
+ if (auto loc = mInstance->autofill_Transform; loc != kInvalidLocation) {
+ ImGui::BulletText("(Autofill)\nLocation: %d\nName: %s", loc, kAfnTransform);
+ }
+ if (auto loc = mInstance->autofill_Time; loc != kInvalidLocation) {
+ ImGui::BulletText("(Autofill)\nLocation: %d\nName: %s", loc, kAfnTime);
+ }
+ if (auto loc = mInstance->autofill_DeltaTime; loc != kInvalidLocation) {
+ ImGui::BulletText("(Autofill)\nLocation: %d\nName: %s", loc, kAfnDeltaTime);
+ }
+ if (auto loc = mInstance->autofill_TextureAtlas; loc != kInvalidLocation) {
+ ImGui::BulletText("(Autofill)\nLocation: %d\nName: %s", loc, kAfnTextureAtlas);
+ }
+ }
+}
+
+void IresShader::Write(IresWritingContext& ctx, rapidjson::Value& value, rapidjson::Document& root) const {
+ using namespace ProjectBrussel_UNITY_ID;
+
+ IresObject::Write(ctx, value, root);
+ json_dto::json_output_t out( value, root.GetAllocator() );
+ out << mInstance->mInfo;
+}
+
+void IresShader::Read(IresLoadingContext& ctx, const rapidjson::Value& value) {
+ using namespace ProjectBrussel_UNITY_ID;
+
+ IresObject::Read(ctx, value);
+
+ auto rvSourceFile = rapidjson::GetProperty(value, rapidjson::kStringType, "SourceFile"sv);
+ if (!rvSourceFile) {
+ return;
+ } else {
+ this->mSourceFile = rapidjson::AsString(*rvSourceFile);
+
+ char shaderFilePath[256];
+ snprintf(shaderFilePath, sizeof(shaderFilePath), "%s/%s", AppConfig::assetDir.c_str(), rvSourceFile->GetString());
+
+ auto shaderFile = Utils::OpenCstdioFile(shaderFilePath, Utils::Read);
+ if (!shaderFile) return;
+ DEFER {
+ fclose(shaderFile);
+ };
+
+ fseek(shaderFile, 0, SEEK_END);
+ auto shaderFileSize = ftell(shaderFile);
+ rewind(shaderFile);
+
+ // Also add \0 ourselves
+ auto buffer = std::make_unique<char[]>(shaderFileSize + 1);
+ fread(buffer.get(), shaderFileSize, 1, shaderFile);
+ buffer[shaderFileSize] = '\0';
+ std::string_view source(buffer.get(), shaderFileSize);
+
+ if (mInstance->InitFromSource(source) != Shader::EC_Success) {
+ return;
+ }
+ }
+
+ auto& shaderInfo = mInstance->mInfo;
+ auto shaderProgram = mInstance->GetProgram();
+
+ auto LoadMathVars = [&](std::string_view name, ShaderThingId::Kind kind, std::vector<ShaderMathVariable>& vars) {
+ auto rvThings = rapidjson::GetProperty(value, rapidjson::kArrayType, name);
+ if (!rvThings) return;
+
+ for (auto& rv : rvThings->GetArray()) {
+ if (!rv.IsObject()) continue;
+ ShaderMathVariable thing;
+ ReadShaderMathVariable(rv, thing);
+
+ shaderInfo.things.try_emplace(thing.name, ShaderThingId{ kind, (int)vars.size() });
+ vars.push_back(std::move(thing));
+ }
+ };
+ LoadMathVars("Inputs"sv, ShaderThingId::KD_Input, shaderInfo.inputs);
+ LoadMathVars("Outputs"sv, ShaderThingId::KD_Output, shaderInfo.outputs);
+
+ auto rvUniforms = rapidjson::GetProperty(value, rapidjson::kArrayType, "Uniforms"sv);
+ if (!rvUniforms) return;
+ 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<ShaderVariable> {
+ if (type == "Math"sv) {
+ auto uniform = std::make_unique<ShaderMathVariable>();
+ ReadShaderMathVariable(*rvValue, *uniform);
+
+ return uniform;
+ } else if (type == "Sampler"sv) {
+ auto uniform = std::make_unique<ShaderSamplerVariable>();
+ ReadShaderSamplerVariable(*rvValue, *uniform);
+
+ return uniform;
+ }
+
+ return nullptr;
+ }();
+ if (uniform) {
+ shaderInfo.things.try_emplace(uniform->name, ShaderThingId{ ShaderThingId::KD_Uniform, (int)shaderInfo.uniforms.size() });
+ shaderInfo.uniforms.emplace_back(std::move(uniform));
+ }
+ }
+
+ for (auto& uniform : shaderInfo.uniforms) {
+ uniform->location = glGetUniformLocation(shaderProgram, uniform->name.c_str());
+ }
+}