support GL_ELEMENT_ARRAY_BUFFER

This commit is contained in:
falsycat 2022-10-25 17:35:21 +09:00
parent 419b9a98e2
commit 9dada90b78
4 changed files with 130 additions and 40 deletions

View File

@ -87,13 +87,13 @@ inline uint8_t GetCompCount(ColorComp c) noexcept {
enum class BufferTarget {
Array,
Element,
ElementArray,
};
template <>
struct EnumMeta<BufferTarget> {
static inline const std::unordered_map<BufferTarget, GLenum> glmap = {
{BufferTarget::Array, GL_ARRAY_BUFFER},
{BufferTarget::Element, GL_ELEMENT_ARRAY_BUFFER},
{BufferTarget::Array, GL_ARRAY_BUFFER},
{BufferTarget::ElementArray, GL_ELEMENT_ARRAY_BUFFER},
};
};

View File

@ -5,6 +5,7 @@
#include <exception>
#include <functional>
#include <memory>
#include <optional>
#include <sstream>
#include <vector>
@ -179,16 +180,34 @@ nf7::Future<std::shared_ptr<Obj<Obj_ProgramMeta>>> Obj_ProgramMeta::Create(
nf7::Future<std::shared_ptr<Obj<Obj_VertexArrayMeta>>> Obj_VertexArrayMeta::Create(
const std::shared_ptr<nf7::Context>& ctx, std::vector<Attr>&& attrs) noexcept
const std::shared_ptr<nf7::Context>& ctx,
const std::optional<Index>& index,
std::vector<Attr>&& attrs) noexcept
try {
if (index) {
if (index->numtype != gl::NumericType::U8 &&
index->numtype != gl::NumericType::U16 &&
index->numtype != gl::NumericType::U32) {
throw nf7::Exception {"invalid index buffer numtype (only u8/u16/u32 are allowed)"};
}
}
nf7::Future<std::shared_ptr<Obj<Obj_VertexArrayMeta>>>::Promise pro {ctx};
LockBuffers(ctx, attrs).Chain(
LockBuffers(ctx, index, attrs).Chain(
nf7::Env::kGL, ctx, pro,
[ctx, attrs = std::move(attrs), pro](auto& bufs) mutable {
[ctx, index, attrs = std::move(attrs), pro](auto& bufs) mutable {
// check all buffers
assert(attrs.size() == bufs.size());
for (auto& buf : bufs) {
if ((*buf.value()).meta().target != gl::BufferTarget::Array) {
if (index) {
assert(bufs.size() == attrs.size()+1);
const auto& m = (*bufs.back().value()).meta();
if (m.target != gl::BufferTarget::ElementArray) {
throw nf7::Exception {"index buffer is not ElementArray"};
}
} else {
assert(bufs.size() == attrs.size());
}
for (size_t i = 0; i < attrs.size(); ++i) {
if ((*bufs[i].value()).meta().target != gl::BufferTarget::Array) {
throw nf7::Exception {"buffer is not Array"};
}
}
@ -211,11 +230,15 @@ try {
attr.stride,
reinterpret_cast<GLvoid*>(static_cast<GLintptr>(attr.offset)));
}
if (index) {
const auto& buf = *bufs.back().value();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf.id());
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
assert(0 == glGetError());
return std::make_shared<Obj<Obj_VertexArrayMeta>>(ctx, id, std::move(attrs));
return std::make_shared<Obj<Obj_VertexArrayMeta>>(ctx, id, index, std::move(attrs));
});
return pro.future();
} catch (nf7::Exception&) {
@ -225,8 +248,9 @@ try {
nf7::Future<std::vector<nf7::Mutex::Resource<std::shared_ptr<gl::Buffer>>>>
Obj_VertexArrayMeta::LockBuffers(
const std::shared_ptr<nf7::Context>& ctx,
const std::optional<Index>& index,
const std::vector<Attr>& attrs,
size_t vcnt, size_t icnt) noexcept
const ValidationHint& vhint) noexcept
try {
nf7::AggregatePromise apro {ctx};
@ -235,12 +259,14 @@ try {
// lock array buffers
for (const auto& attr : attrs) {
size_t required = 0;
if (attr.divisor == 0 && vcnt > 0) {
required = static_cast<size_t>(attr.size)*vcnt*gl::GetByteSize(attr.type);
} else if (attr.divisor > 0 && icnt > 0) {
required = static_cast<size_t>(attr.stride)*(icnt-1) + attr.offset;
}
const size_t required =
// when non-instanced and no-index-buffer drawing
attr.divisor == 0 && vhint.vertices > 0 && !index?
static_cast<size_t>(attr.size) * vhint.vertices * gl::GetByteSize(attr.type):
// when instanced drawing
attr.divisor > 0 && vhint.instances > 0?
static_cast<size_t>(attr.stride) * (vhint.instances-1) + attr.offset:
size_t {0};
auto& factory = ctx->env().
GetFileOrThrow(attr.buffer).
@ -250,6 +276,18 @@ try {
fus.emplace_back(fu);
}
// lock index buffers (it must be the last element in `fus`)
if (index) {
const auto required = gl::GetByteSize(index->numtype) * vhint.vertices;
auto& factory = ctx->env().
GetFileOrThrow(index->buffer).
interfaceOrThrow<gl::BufferFactory>();
auto fu = LockAndValidate(ctx, factory, gl::BufferTarget::ElementArray, required);
apro.Add(fu);
fus.emplace_back(fu);
}
// wait for all registered futures
nf7::Future<std::vector<nf7::Mutex::Resource<std::shared_ptr<gl::Buffer>>>>::Promise pro {ctx};
apro.future().Chain(pro, [fus = std::move(fus)](auto&) {

View File

@ -3,6 +3,7 @@
#include <array>
#include <cassert>
#include <memory>
#include <optional>
#include <utility>
#include <vector>
@ -144,6 +145,10 @@ struct Obj_VertexArrayMeta final {
using LockedBuffersFuture =
nf7::Future<std::vector<nf7::Mutex::Resource<std::shared_ptr<gl::Buffer>>>>;
struct Index {
nf7::File::Id buffer;
gl::NumericType numtype;
};
struct Attr {
nf7::File::Id buffer;
GLuint location;
@ -154,32 +159,47 @@ struct Obj_VertexArrayMeta final {
uint64_t offset;
GLuint divisor;
};
struct ValidationHint {
ValidationHint() noexcept { }
size_t vertices = 0;
size_t instances = 0;
};
// must be called from main or sub task
static nf7::Future<std::shared_ptr<Obj<Obj_VertexArrayMeta>>> Create(
const std::shared_ptr<nf7::Context>& ctx,std::vector<Attr>&& attrs) noexcept;
const std::shared_ptr<nf7::Context>& ctx,
const std::optional<Index>& index,
std::vector<Attr>&& attrs) noexcept;
static void Delete(GLuint id) noexcept {
glDeleteVertexArrays(1, &id);
}
// must be called from main or sub task
// it's guaranteed that the last element of the returned vector is an index buffer if index != std::nullopt
static LockedBuffersFuture
LockBuffers(
const std::shared_ptr<nf7::Context>& ctx,
const std::optional<Index>& index,
const std::vector<Attr>& attrs,
size_t vcnt = 0,
size_t icnt = 0) noexcept;
const ValidationHint& vhint = {}) noexcept;
Obj_VertexArrayMeta(std::vector<Attr>&& a) noexcept : attrs(std::move(a)) {
Obj_VertexArrayMeta(const std::optional<Index>& idx,
std::vector<Attr>&& a) noexcept :
index(idx), attrs(std::move(a)) {
}
// must be called from main or sub task
// it's guaranteed that the last element of the returned vector is an index buffer if index != std::nullopt
nf7::Future<std::vector<nf7::Mutex::Resource<std::shared_ptr<gl::Buffer>>>> LockBuffers(
const std::shared_ptr<nf7::Context>& ctx, size_t vcnt = 0, size_t icnt = 0) const noexcept {
return LockBuffers(ctx, attrs, vcnt, icnt);
const std::shared_ptr<nf7::Context>& ctx,
const ValidationHint& vhint = {}) const noexcept {
return LockBuffers(ctx, index, attrs, vhint);
}
const std::vector<Attr> attrs;
const std::optional<Index> index;
const std::vector<Attr> attrs;
};
using VertexArray = Obj<Obj_VertexArrayMeta>;
using VertexArrayFactory = AsyncFactory<nf7::Mutex::Resource<std::shared_ptr<VertexArray>>>;

View File

@ -761,9 +761,13 @@ struct Program {
ResolveOrThrow(v.tuple("vao").string()).
interfaceOrThrow<nf7::gl::VertexArrayFactory>().Create();
nf7::gl::VertexArray::Meta::ValidationHint vhint;
vhint.vertices = static_cast<size_t>(count);
vhint.instances = static_cast<size_t>(inst);
nf7::gl::VertexArray::Meta::LockedBuffersFuture::Promise vao_lock_pro;
vao_fu->ThenIf([la, vao_lock_pro](auto& vao) mutable {
(**vao).meta().LockBuffers(la).Chain(vao_lock_pro);
vao_fu->ThenIf([la, vao_lock_pro, vhint](auto& vao) mutable {
(**vao).meta().LockBuffers(la, vhint).Chain(vao_lock_pro);
});
vao_lock_fu = vao_lock_pro.future();
@ -798,7 +802,12 @@ struct Program {
}
// draw
glDrawArraysInstanced(mode, 0, count, inst);
if (vao->meta().index) {
const auto numtype = gl::ToEnum(vao->meta().index->numtype);
glDrawElementsInstanced(mode, count, numtype, nullptr, inst);
} else {
glDrawArraysInstanced(mode, 0, count, inst);
}
const auto status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
// unbind all
@ -891,7 +900,7 @@ struct VertexArray {
using Product = nf7::gl::VertexArray;
struct Attr {
GLuint index = 0;
GLuint location = 0;
GLint size = 1;
gl::NumericType type = gl::NumericType::F32;
bool normalize = false;
@ -901,12 +910,12 @@ struct VertexArray {
nf7::File::Path buffer = {};
void serialize(auto& ar) {
ar(index, size, type, normalize, stride, offset, divisor, buffer);
ar(location, size, type, normalize, stride, offset, divisor, buffer);
}
const char* Validate() const noexcept {
if (index >= GL_MAX_VERTEX_ATTRIBS) {
return "too huge index";
if (location >= GL_MAX_VERTEX_ATTRIBS) {
return "too huge location";
}
if (size <= 0 || 4 < size) {
return "invalid size (1, 2, 3 or 4 are allowed)";
@ -927,10 +936,10 @@ struct VertexArray {
}
std::unordered_set<GLuint> idx;
for (auto& attr : attrs) {
const auto [itr, uniq] = idx.insert(attr.index);
const auto [itr, uniq] = idx.insert(attr.location);
(void) itr;
if (!uniq) {
throw nf7::Exception {"attribute index duplication"};
throw nf7::Exception {"attribute location duplicated"};
}
}
}
@ -943,19 +952,26 @@ struct VertexArray {
VertexArray& operator=(VertexArray&&) = default;
void serialize(auto& ar) {
ar(attrs_);
ar(index_, index_numtype_, attrs_);
Attr::Validate(attrs_);
}
std::string Stringify() noexcept {
YAML::Emitter st;
st << YAML::BeginMap;
st << YAML::Key << "index";
st << YAML::BeginMap;
st << YAML::Key << "buffer";
st << YAML::Value << index_.Stringify();
st << YAML::Key << "type";
st << YAML::Value << std::string {magic_enum::enum_name(index_numtype_)};
st << YAML::EndMap;
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 << "location";
st << YAML::Value << attr.location;
st << YAML::Key << "size";
st << YAML::Value << attr.size;
st << YAML::Key << "type";
@ -980,10 +996,15 @@ struct VertexArray {
try {
const auto yaml = YAML::Load(v);
auto index = nf7::File::Path::Parse(yaml["index"]["buffer"].as<std::string>());
const auto index_numtype = magic_enum::enum_cast<gl::NumericType>(
yaml["index"]["type"].as<std::string>()).value();
std::vector<Attr> attrs;
for (const auto& attr : yaml["attrs"]) {
attrs.push_back({
.index = attr["index"].as<GLuint>(),
.location = attr["location"].as<GLuint>(),
.size = attr["size"].as<GLint>(),
.type = magic_enum::enum_cast<gl::NumericType>(attr["type"].as<std::string>()).value(),
.normalize = attr["normalize"].as<bool>(),
@ -995,7 +1016,9 @@ struct VertexArray {
}
Attr::Validate(attrs);
attrs_ = std::move(attrs);
index_ = std::move(index);
index_numtype_ = index_numtype;
attrs_ = std::move(attrs);
} catch (std::bad_optional_access&) {
throw nf7::Exception {std::string {"invalid enum"}};
} catch (YAML::Exception& e) {
@ -1006,12 +1029,19 @@ struct VertexArray {
try {
auto& base = ctx->env().GetFileOrThrow(ctx->initiator());
std::optional<nf7::gl::VertexArray::Meta::Index> index;
if (index_.terms().size() > 0) {
index.emplace();
index->buffer = base.ResolveOrThrow(index_).id();
index->numtype = index_numtype_;
}
std::vector<Product::Meta::Attr> attrs;
attrs.reserve(attrs_.size());
for (auto& attr : attrs_) {
attrs.push_back({
.buffer = base.ResolveOrThrow(attr.buffer).id(),
.index = attr.index,
.location = attr.location,
.size = attr.size,
.type = attr.type,
.normalize = attr.normalize,
@ -1020,7 +1050,7 @@ struct VertexArray {
.divisor = attr.divisor,
});
}
return Product::Create(ctx, std::move(attrs));
return Product::Create(ctx, index, std::move(attrs));
} catch (nf7::Exception&) {
return {std::current_exception()};
}
@ -1038,6 +1068,8 @@ struct VertexArray {
}
private:
nf7::File::Path index_;
gl::NumericType index_numtype_;
std::vector<Attr> attrs_;
};
template <>