add GL/VertexArray
This commit is contained in:
parent
2c6608ea09
commit
0a55250f52
@ -1,5 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.18)
|
||||
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
|
||||
cmake_policy(SET CMP0077 NEW)
|
||||
cmake_policy(SET CMP0135 OLD)
|
||||
|
||||
# ---- configuration ----
|
||||
project(nf7 C CXX)
|
||||
@ -79,6 +80,7 @@ target_sources(nf7
|
||||
common/generic_type_info.hh
|
||||
common/generic_watcher.hh
|
||||
common/gl_obj.hh
|
||||
common/gl_obj.cc
|
||||
common/gui_config.hh
|
||||
common/gui_context.hh
|
||||
common/gui_dnd.hh
|
||||
|
@ -26,7 +26,7 @@ class AggregatePromise final :
|
||||
AggregatePromise& operator=(const AggregatePromise&) = delete;
|
||||
AggregatePromise& operator=(AggregatePromise&&) = delete;
|
||||
|
||||
AggregatePromise& Add(auto& fu) noexcept {
|
||||
AggregatePromise& Add(auto fu) noexcept {
|
||||
data_->Ref();
|
||||
fu.Then([data = data_](auto&) { data->Unref(); });
|
||||
return *this;
|
||||
|
57
common/gl_obj.cc
Normal file
57
common/gl_obj.cc
Normal file
@ -0,0 +1,57 @@
|
||||
#include "common/gl_obj.hh"
|
||||
|
||||
#include <algorithm>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "common/aggregate_promise.hh"
|
||||
#include "common/future.hh"
|
||||
#include "common/mutex.hh"
|
||||
|
||||
|
||||
namespace nf7::gl {
|
||||
|
||||
nf7::Future<std::vector<std::shared_ptr<nf7::Mutex::Lock>>>
|
||||
Obj_VertexArrayMeta::LockBuffers(
|
||||
const std::shared_ptr<nf7::Context>& ctx, size_t vcnt, size_t icnt) const noexcept
|
||||
try {
|
||||
std::vector<nf7::gl::BufferFactory::Product> fus;
|
||||
|
||||
nf7::AggregatePromise lock_pro {ctx};
|
||||
for (const auto& attr : attrs) {
|
||||
auto& f = ctx->env().GetFileOrThrow(attr.id);
|
||||
|
||||
// calculate size required to the buffer
|
||||
const auto required = std::max(attr.size_per_vertex*vcnt, attr.size_per_instance*icnt);
|
||||
|
||||
// validation after the lock
|
||||
nf7::Future<nf7::Mutex::Resource<std::shared_ptr<nf7::gl::Buffer>>>::Promise pro {ctx};
|
||||
f.interfaceOrThrow<nf7::gl::BufferFactory>().Create().
|
||||
Chain(pro, [pro, required](auto& v) mutable {
|
||||
if ((*v)->meta().size < required) {
|
||||
throw nf7::Exception {"buffer shortage"};
|
||||
}
|
||||
return v;
|
||||
});
|
||||
|
||||
// register a future of the validation
|
||||
lock_pro.Add(pro.future());
|
||||
fus.emplace_back(pro.future());
|
||||
}
|
||||
|
||||
// wait for all registered futures
|
||||
nf7::Future<std::vector<std::shared_ptr<nf7::Mutex::Lock>>>::Promise pro {ctx};
|
||||
lock_pro.future().Chain(pro, [fus = std::move(fus)](auto&) {
|
||||
std::vector<std::shared_ptr<nf7::Mutex::Lock>> ret;
|
||||
for (auto& fu : fus) {
|
||||
ret.emplace_back(fu.value().lock());
|
||||
}
|
||||
return ret;
|
||||
});
|
||||
return pro.future();
|
||||
} catch (nf7::Exception&) {
|
||||
return { std::current_exception() };
|
||||
}
|
||||
|
||||
} // namespace nf7::gl
|
@ -3,6 +3,7 @@
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
@ -18,6 +19,8 @@ namespace nf7::gl {
|
||||
template <typename T>
|
||||
class Obj final {
|
||||
public:
|
||||
using Meta = T;
|
||||
|
||||
template <typename... Args>
|
||||
Obj(const std::shared_ptr<nf7::Context>& ctx, GLuint id, Args&&... args) noexcept :
|
||||
ctx_(ctx), meta_(std::forward<Args>(args)...), id_(id? id: meta_.Gen()) {
|
||||
@ -123,4 +126,32 @@ struct Obj_ProgramMeta final {
|
||||
using Program = Obj<Obj_ProgramMeta>;
|
||||
using ProgramFactory = AsyncFactory<nf7::Mutex::Resource<std::shared_ptr<Program>>>;
|
||||
|
||||
|
||||
struct Obj_VertexArrayMeta final {
|
||||
public:
|
||||
struct Attr {
|
||||
nf7::File::Id id = 0;
|
||||
size_t size_per_vertex = 0;
|
||||
size_t size_per_instance = 0;
|
||||
};
|
||||
Obj_VertexArrayMeta(std::vector<Attr>&& a) noexcept : attrs(std::move(a)) {
|
||||
}
|
||||
|
||||
const std::vector<Attr> attrs;
|
||||
|
||||
nf7::Future<std::vector<std::shared_ptr<nf7::Mutex::Lock>>> LockBuffers(
|
||||
const std::shared_ptr<nf7::Context>& ctx, size_t vcnt, size_t icnt) const noexcept;
|
||||
|
||||
static GLuint Gen() noexcept {
|
||||
GLuint id;
|
||||
glGenVertexArrays(1, &id);
|
||||
return id;
|
||||
}
|
||||
static void Delete(GLuint id) noexcept {
|
||||
glDeleteVertexArrays(1, &id);
|
||||
}
|
||||
};
|
||||
using VertexArray = Obj<Obj_VertexArrayMeta>;
|
||||
using VertexArrayFactory = AsyncFactory<nf7::Mutex::Resource<std::shared_ptr<VertexArray>>>;
|
||||
|
||||
} // namespace nf7::gl
|
||||
|
271
file/gl_obj.cc
271
file/gl_obj.cc
@ -37,6 +37,8 @@
|
||||
#include "common/yas_enum.hh"
|
||||
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace nf7 {
|
||||
namespace {
|
||||
|
||||
@ -748,7 +750,6 @@ struct Program {
|
||||
nf7::Future<std::shared_ptr<Product>> Create(
|
||||
const std::shared_ptr<nf7::Context>& ctx, nf7::Env::Watcher&) noexcept
|
||||
try {
|
||||
// TODO: setup watcher
|
||||
auto& base = ctx->env().GetFileOrThrow(ctx->initiator());
|
||||
|
||||
nf7::AggregatePromise sh_pro {ctx};
|
||||
@ -761,10 +762,11 @@ struct Program {
|
||||
base.ResolveOrThrow(path).
|
||||
interfaceOrThrow<nf7::gl::ShaderFactory>().Create());
|
||||
sh_pro.Add(sh.back());
|
||||
// TODO: setup watcher
|
||||
}
|
||||
|
||||
nf7::Future<std::shared_ptr<Product>>::Promise pro {ctx};
|
||||
sh_pro.future().Then([ctx, pro, shaders = std::move(sh)](auto&) mutable {
|
||||
sh_pro.future().Then(nf7::Env::kGL, ctx, [ctx, pro, shaders = std::move(sh)](auto&) mutable {
|
||||
pro.Wrap([&]() {
|
||||
std::vector<std::shared_ptr<nf7::gl::Shader>> sh;
|
||||
for (auto& s : shaders) {
|
||||
@ -778,8 +780,8 @@ struct Program {
|
||||
return {std::current_exception()};
|
||||
}
|
||||
static std::shared_ptr<nf7::gl::Program> Link(
|
||||
const std::shared_ptr<nf7::Context>& ctx,
|
||||
const std::span<std::shared_ptr<nf7::gl::Shader>>& shaders) {
|
||||
const std::shared_ptr<nf7::Context>& ctx,
|
||||
std::span<std::shared_ptr<nf7::gl::Shader>> shaders) {
|
||||
auto prog = std::make_shared<nf7::gl::Program>(ctx, GLuint {0});
|
||||
assert(prog->id() > 0);
|
||||
for (auto& sh : shaders) {
|
||||
@ -822,6 +824,267 @@ struct ObjBase<Program>::TypeInfo final {
|
||||
static inline const nf7::GenericTypeInfo<ObjBase<Program>> kType = {"GL/Program", {"nf7::DirItem"}};
|
||||
};
|
||||
|
||||
|
||||
struct VertexArray {
|
||||
public:
|
||||
static void UpdateTypeTooltip() noexcept {
|
||||
ImGui::TextUnformatted("OpenGL Vertex Array Object");
|
||||
}
|
||||
|
||||
static inline const std::vector<std::string> kInputs = {
|
||||
};
|
||||
static inline const std::vector<std::string> kOutputs = {
|
||||
};
|
||||
|
||||
using Product = nf7::gl::VertexArray;
|
||||
|
||||
enum class AttrType {
|
||||
I8 = 0x11,
|
||||
U8 = 0x21,
|
||||
U16 = 0x12,
|
||||
I16 = 0x22,
|
||||
U32 = 0x14,
|
||||
I32 = 0x24,
|
||||
F16 = 0x32,
|
||||
F32 = 0x34,
|
||||
F64 = 0x38,
|
||||
};
|
||||
struct Attr {
|
||||
GLuint index = 0;
|
||||
GLint size = 1;
|
||||
AttrType type = AttrType::F32;
|
||||
bool normalize = false;
|
||||
GLsizei stride = 0;
|
||||
uint64_t offset = 0;
|
||||
GLuint divisor = 0;
|
||||
nf7::File::Path buffer = {};
|
||||
|
||||
void serialize(auto& ar) {
|
||||
ar(index, size, type, normalize, stride, offset, divisor, buffer);
|
||||
}
|
||||
|
||||
const char* Validate() const noexcept {
|
||||
if (index >= GL_MAX_VERTEX_ATTRIBS) {
|
||||
return "too huge index";
|
||||
}
|
||||
if (size <= 0 || 4 < size) {
|
||||
return "invalid size (1, 2, 3 or 4 are allowed)";
|
||||
}
|
||||
if (stride < 0) {
|
||||
return "negative stride";
|
||||
}
|
||||
if (offset > static_cast<uint64_t>(stride)) {
|
||||
return "offset overflow";
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
static void Validate(const std::vector<Attr>& attrs) {
|
||||
for (auto& attr : attrs) {
|
||||
if (const auto msg = attr.Validate()) {
|
||||
throw nf7::Exception {"invalid attribute: "s+msg};
|
||||
}
|
||||
}
|
||||
std::unordered_set<GLuint> idx;
|
||||
for (auto& attr : attrs) {
|
||||
const auto [itr, uniq] = idx.insert(attr.index);
|
||||
(void) itr;
|
||||
if (!uniq) {
|
||||
throw nf7::Exception {"attribute index duplication"};
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
VertexArray() = default;
|
||||
VertexArray(const VertexArray&) = default;
|
||||
VertexArray(VertexArray&&) = default;
|
||||
VertexArray& operator=(const VertexArray&) = default;
|
||||
VertexArray& operator=(VertexArray&&) = default;
|
||||
|
||||
void serialize(auto& ar) {
|
||||
ar(attrs_);
|
||||
Attr::Validate(attrs_);
|
||||
}
|
||||
|
||||
std::string Stringify() noexcept {
|
||||
YAML::Emitter st;
|
||||
st << YAML::BeginMap;
|
||||
st << YAML::Key << "attrs";
|
||||
st << YAML::Value << YAML::BeginSeq;
|
||||
for (const auto& attr : attrs_) {
|
||||
st << YAML::BeginMap;
|
||||
st << YAML::Key << "index";
|
||||
st << YAML::Value << attr.index;
|
||||
st << YAML::Key << "size";
|
||||
st << YAML::Value << attr.size;
|
||||
st << YAML::Key << "type";
|
||||
st << YAML::Value << std::string {magic_enum::enum_name(attr.type)};
|
||||
st << YAML::Key << "normalize";
|
||||
st << YAML::Value << attr.normalize;
|
||||
st << YAML::Key << "stride";
|
||||
st << YAML::Value << attr.stride;
|
||||
st << YAML::Key << "offset";
|
||||
st << YAML::Value << attr.offset;
|
||||
st << YAML::Key << "divisor";
|
||||
st << YAML::Value << attr.divisor;
|
||||
st << YAML::Key << "buffer";
|
||||
st << YAML::Value << attr.buffer.Stringify();
|
||||
st << YAML::EndMap;
|
||||
}
|
||||
st << YAML::EndSeq;
|
||||
st << YAML::EndMap;
|
||||
return std::string {st.c_str(), st.size()};
|
||||
}
|
||||
void Parse(const std::string& v)
|
||||
try {
|
||||
const auto yaml = YAML::Load(v);
|
||||
|
||||
std::vector<Attr> attrs;
|
||||
for (const auto& attr : yaml["attrs"]) {
|
||||
attrs.push_back({
|
||||
.index = attr["index"].as<GLuint>(),
|
||||
.size = attr["size"].as<GLint>(),
|
||||
.type = magic_enum::enum_cast<AttrType>(attr["type"].as<std::string>()).value(),
|
||||
.normalize = attr["normalize"].as<bool>(),
|
||||
.stride = attr["stride"].as<GLsizei>(),
|
||||
.offset = attr["offset"].as<uint64_t>(),
|
||||
.divisor = attr["divisor"].as<GLuint>(),
|
||||
.buffer = nf7::File::Path::Parse(attr["buffer"].as<std::string>()),
|
||||
});
|
||||
}
|
||||
Attr::Validate(attrs);
|
||||
|
||||
attrs_ = std::move(attrs);
|
||||
} catch (std::bad_optional_access&) {
|
||||
throw nf7::Exception {std::string {"invalid enum"}};
|
||||
} catch (YAML::Exception& e) {
|
||||
throw nf7::Exception {std::string {"YAML error: "}+e.what()};
|
||||
}
|
||||
|
||||
nf7::Future<std::shared_ptr<Product>> Create(
|
||||
const std::shared_ptr<nf7::Context>& ctx, nf7::Env::Watcher&) noexcept
|
||||
try {
|
||||
auto& base = ctx->env().GetFileOrThrow(ctx->initiator());
|
||||
auto attrs = attrs_; // copy it
|
||||
|
||||
nf7::AggregatePromise buf_pro {ctx};
|
||||
std::vector<
|
||||
std::pair<
|
||||
nf7::File::Id,
|
||||
nf7::Future<
|
||||
nf7::Mutex::Resource<
|
||||
std::shared_ptr<nf7::gl::Buffer>>>>> bufs;
|
||||
for (auto& attr : attrs) {
|
||||
auto& f = base.ResolveOrThrow(attr.buffer);
|
||||
auto fu = f.interfaceOrThrow<nf7::gl::BufferFactory>().Create();
|
||||
bufs.emplace_back(f.id(), fu);
|
||||
buf_pro.Add(fu);
|
||||
// TODO: setup watcher
|
||||
}
|
||||
|
||||
nf7::Future<std::shared_ptr<Product>>::Promise pro {ctx};
|
||||
buf_pro.future().Then(
|
||||
nf7::Env::kGL, ctx,
|
||||
[ctx, pro, attrs = std::move(attrs), bufs = std::move(bufs)](auto&) mutable {
|
||||
pro.Wrap([&]() { return Create(ctx, attrs, bufs); });
|
||||
});
|
||||
return pro.future();
|
||||
} catch (nf7::Exception&) {
|
||||
return {std::current_exception()};
|
||||
}
|
||||
static std::shared_ptr<nf7::gl::VertexArray> Create(
|
||||
const std::shared_ptr<nf7::Context>& ctx, auto& attrs, auto& bufs) {
|
||||
assert(attrs.size() == bufs.size());
|
||||
|
||||
std::vector<GLuint> bufids;
|
||||
bufids.reserve(attrs.size());
|
||||
|
||||
// build metadata of new VAO
|
||||
std::vector<nf7::gl::VertexArray::Meta::Attr> meta_attrs;
|
||||
meta_attrs.reserve(attrs.size());
|
||||
for (size_t i = 0; i < attrs.size(); ++i) {
|
||||
const auto& attr = attrs[i];
|
||||
const auto& buf = **bufs[i].second.value();
|
||||
bufids.push_back(buf.id());
|
||||
|
||||
// check buffer type
|
||||
if (buf.meta().type != GL_ARRAY_BUFFER) {
|
||||
throw nf7::Exception {
|
||||
"buffer ("+std::to_string(i)+") is not GL_ARRAY_BUFFER"
|
||||
};
|
||||
}
|
||||
|
||||
// calculate meta data
|
||||
const auto size = attr.size*(magic_enum::enum_integer(attr.type) & 0xF);
|
||||
const auto stride = attr.stride?
|
||||
static_cast<size_t>(attr.stride):
|
||||
static_cast<size_t>(size);
|
||||
meta_attrs.push_back({
|
||||
.id = bufs[i].first,
|
||||
.size_per_vertex = attr.divisor == 0? stride: 0,
|
||||
.size_per_instance = attr.divisor >= 1? stride: 0,
|
||||
});
|
||||
}
|
||||
|
||||
// setup VAO
|
||||
auto vao = std::make_shared<nf7::gl::VertexArray>(ctx, GLuint {0}, std::move(meta_attrs));
|
||||
glBindVertexArray(vao->id());
|
||||
for (size_t i = 0; i < attrs.size(); ++i) {
|
||||
const auto& attr = attrs[i];
|
||||
|
||||
// attach buffer
|
||||
glBindBuffer(GL_ARRAY_BUFFER, bufids[i]);
|
||||
glEnableVertexAttribArray(attr.index);
|
||||
glVertexAttribDivisor(attr.index, attr.divisor);
|
||||
glVertexAttribPointer(
|
||||
attr.index,
|
||||
attr.size,
|
||||
ToAttrType(attr.type),
|
||||
attr.normalize,
|
||||
attr.stride,
|
||||
reinterpret_cast<GLvoid*>(static_cast<GLintptr>(attr.offset)));
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
glBindVertexArray(0);
|
||||
|
||||
assert(0 == glGetError());
|
||||
return vao;
|
||||
}
|
||||
|
||||
bool Handle(const std::shared_ptr<nf7::Node::Lambda>&,
|
||||
const nf7::Mutex::Resource<std::shared_ptr<Product>>&,
|
||||
const nf7::Node::Lambda::Msg&) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void UpdateTooltip(const std::shared_ptr<Product>& prod) noexcept {
|
||||
if (prod) {
|
||||
ImGui::Text("id: %zu", static_cast<size_t>(prod->id()));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Attr> attrs_;
|
||||
|
||||
static GLenum ToAttrType(AttrType type) {
|
||||
return
|
||||
type == AttrType::U8? GL_UNSIGNED_BYTE:
|
||||
type == AttrType::I8? GL_BYTE:
|
||||
type == AttrType::U16? GL_UNSIGNED_SHORT:
|
||||
type == AttrType::I16? GL_SHORT:
|
||||
type == AttrType::U32? GL_UNSIGNED_INT:
|
||||
type == AttrType::I32? GL_INT:
|
||||
type == AttrType::F16? GL_HALF_FLOAT:
|
||||
type == AttrType::F32? GL_FLOAT:
|
||||
type == AttrType::F64? GL_DOUBLE:
|
||||
throw 0;
|
||||
}
|
||||
};
|
||||
template <>
|
||||
struct ObjBase<VertexArray>::TypeInfo final {
|
||||
static inline const nf7::GenericTypeInfo<ObjBase<VertexArray>> kType = {"GL/VertexArray", {"nf7::DirItem"}};
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nf7
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user