Skip to content

Instantly share code, notes, and snippets.

@JuanDiegoMontoya
Last active March 28, 2024 12:26
Show Gist options
  • Save JuanDiegoMontoya/381dd5be0442b19f314513a2e2dc2194 to your computer and use it in GitHub Desktop.
Save JuanDiegoMontoya/381dd5be0442b19f314513a2e2dc2194 to your computer and use it in GitHub Desktop.
A shrimple little include handler for glslang that works (doesn't crash lol) for my subset of use cases. Supports nested relative includes with local include syntax (""). Does not support absolute paths for includees or system include syntax (<>).
#include <glslang/Public/ShaderLang.h>
#include <glslang/SPIRV/GlslangToSpv.h>
#include <vector>
#include <cassert>
#include <stdexcept>
#include <fstream>
#include <stdexcept>
#include <memory>
std::string LoadFile(const std::filesystem::path& path)
{
std::ifstream file{path};
if (!file)
{
throw std::runtime_error("File not found");
}
return {std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()};
}
// Hackity hack
size_t NumberOfPathComponents(std::filesystem::path path)
{
size_t parents = 0;
while (!path.empty())
{
parents++;
path = path.parent_path();
}
return parents > 0 ? parents - 1 : 0; // The path will contain a filename, which we will ignore
}
class IncludeHandler final : public glslang::TShader::Includer
{
public:
IncludeHandler(const std::filesystem::path& sourcePath)
{
// Seed the "stack" with just the parent directory of the top-level source
currentIncluderDir_ /= sourcePath.parent_path();
}
glslang::TShader::Includer::IncludeResult* includeLocal(
const char* requested_source,
[[maybe_unused]] const char* requesting_source,
[[maybe_unused]] size_t include_depth) override
{
// Everything will explode if this is not relative
assert(std::filesystem::path(requested_source).is_relative());
auto fullRequestedSource = currentIncluderDir_ / requested_source;
currentIncluderDir_ = fullRequestedSource.parent_path();
auto contentPtr = std::make_unique<std::string>(LoadFile(fullRequestedSource));
auto content = contentPtr.get();
auto sourcePathPtr = std::make_unique<std::string>(requested_source);
//auto sourcePath = sourcePathPtr.get();
contentStrings_.emplace_back(std::move(contentPtr));
sourcePathStrings_.emplace_back(std::move(sourcePathPtr));
return new glslang::TShader::Includer::IncludeResult(requested_source, content->c_str(), content->size(), nullptr);
}
void releaseInclude(glslang::TShader::Includer::IncludeResult* data) override
{
for (size_t i = 0; i < NumberOfPathComponents(data->headerName); i++)
{
currentIncluderDir_ = currentIncluderDir_.parent_path();
}
delete data;
}
private:
// Acts like a stack that we "push" path components to when include{Local, System} are invoked, and "pop" when releaseInclude is invoked
std::filesystem::path currentIncluderDir_;
std::vector<std::unique_ptr<std::string>> contentStrings_;
std::vector<std::unique_ptr<std::string>> sourcePathStrings_;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment