implement preprocessor for GLSL

This commit is contained in:
falsycat 2022-10-27 21:27:44 +09:00
parent dc3d8b15bf
commit c1f7328628
3 changed files with 132 additions and 2 deletions

View File

@ -82,6 +82,7 @@ target_sources(nf7
common/gl_fence.hh
common/gl_obj.hh
common/gl_obj.cc
common/gl_shader_preproc.hh
common/gui_config.hh
common/gui_context.hh
common/gui_dnd.hh

113
common/gl_shader_preproc.hh Normal file
View File

@ -0,0 +1,113 @@
#pragma once
#include <filesystem>
#include <fstream>
#include <memory>
#include <sstream>
#include <string_view>
#include <vector>
#include "nf7.hh"
#include "common/future.hh"
namespace nf7::gl {
class ShaderPreproc final : public nf7::Context,
public std::enable_shared_from_this<ShaderPreproc> {
public:
ShaderPreproc() = delete;
ShaderPreproc(const std::shared_ptr<nf7::Context>& ctx,
const std::shared_ptr<std::ostream>& ost,
const std::shared_ptr<std::istream>& ist,
std::filesystem::path&& path) noexcept :
nf7::Context(ctx->env(), ctx->initiator(), ctx),
pro_(ctx), ost_(ost), ist_(ist), path_(std::move(path)) {
}
ShaderPreproc(const ShaderPreproc&) = delete;
ShaderPreproc(ShaderPreproc&&) = delete;
ShaderPreproc& operator=(const ShaderPreproc&) = delete;
ShaderPreproc& operator=(ShaderPreproc&&) = delete;
void ExecProcess() noexcept {
env().ExecAsync(shared_from_this(), [this]() { Process(); });
}
nf7::Future<std::monostate> future() noexcept {
return pro_.future();
}
const std::vector<std::filesystem::path>& nfiles() const noexcept {
return *nfiles_;
}
private:
nf7::Future<std::monostate>::Promise pro_;
std::shared_ptr<std::ostream> ost_;
std::shared_ptr<std::istream> ist_;
std::filesystem::path path_;
size_t lnum_ = 1;
std::shared_ptr<std::vector<std::filesystem::path>> nfiles_;
void Process() noexcept
try {
*ost_ << "#line " << lnum_ << " \"" << path_.string() << "\"\n";
std::string line;
while (std::getline(*ist_, line)) {
++lnum_;
if (line.starts_with('#')) {
std::string_view tok {line.begin() + 1, line.end()};
while (!tok.empty() && !std::isalpha(tok.front())) {
tok.remove_prefix(1);
}
if (tok.starts_with("include ")) {
tok.remove_prefix(sizeof("include")-1);
auto begin = std::find(tok.begin(), tok.end(), '"');
auto end = std::find(begin+1, tok.end(), '"');
if (begin == end || end == tok.end()) {
throw nf7::Exception {"invalid include syntax: "+line};
}
if (depth() >= 100) {
throw nf7::Exception {
"recursion detected in include directives ("+path_.string()+")"};
}
const std::string name {begin+1, end};
const auto path = path_.parent_path() / name;
if (nfiles_ == nullptr) {
nfiles_ = std::make_shared<std::vector<std::filesystem::path>>();
}
nfiles_->push_back(path);
auto self = shared_from_this();
auto f = std::make_shared<std::ifstream>(path, std::ios::binary);
if (!*f) {
throw nf7::Exception {"missing include file: "+path.string()};
}
auto sub = std::make_shared<ShaderPreproc>(self, ost_, f, path.string());
sub->nfiles_ = nfiles_;
sub->Process();
sub->future().Chain(nf7::Env::kAsync, self, pro_,
[=, this](auto&) mutable { Process(); });
return;
}
}
*ost_ << line << "\n";
}
pro_.Return({});
} catch (...) {
pro_.Throw<nf7::Exception>("failed to preprocess GLSL");
}
};
} // namespace nf7::gl

View File

@ -35,6 +35,7 @@
#include "common/gl_enum.hh"
#include "common/gl_fence.hh"
#include "common/gl_obj.hh"
#include "common/gl_shader_preproc.hh"
#include "common/gui_config.hh"
#include "common/life.hh"
#include "common/logger_ref.hh"
@ -667,8 +668,23 @@ struct Shader {
}
nf7::Future<std::shared_ptr<Product>> Create(const CreateParam& p) noexcept {
// TODO: preprocessing GLSL source
return Product::Create(p.ctx, type_, src_);
nf7::Future<std::shared_ptr<Product>>::Promise pro {p.ctx};
auto ost = std::make_shared<std::ostringstream>();
auto ist = std::make_shared<std::istringstream>(src_);
auto path = p.ctx->env().npath() / "INLINE_TEXT";
auto preproc = std::make_shared<gl::ShaderPreproc>(p.ctx, ost, ist, std::move(path));
preproc->ExecProcess();
preproc->future().Chain(p.ctx, pro, [=, type = type_](auto&) mutable {
Product::Create(p.ctx, type, ost->str()).Chain(pro);
});
return pro.future().
ThenIf(p.ctx, [=](auto&) mutable {
for (const auto& npath : preproc->nfiles()) {
p.nwatch->Watch(npath);
}
});
}
bool Handle(const HandleParam<Product>&) {