aboutsummaryrefslogtreecommitdiff
path: root/source/Shader.cpp
diff options
context:
space:
mode:
authorhnOsmium0001 <[email protected]>2022-04-10 22:42:14 -0700
committerhnOsmium0001 <[email protected]>2022-04-10 23:00:36 -0700
commit67affc75a4824dfd8675cc5455d9ee71b1583c1c (patch)
treee6624157831039ed3c1668b6a4b21337633f41e6 /source/Shader.cpp
parent906557f094e407ce21d429ef647bc75fe3179cf1 (diff)
Add shader and corresponding editor components
Diffstat (limited to 'source/Shader.cpp')
-rw-r--r--source/Shader.cpp411
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;
}