support GL_ELEMENT_ARRAY_BUFFER
This commit is contained in:
parent
419b9a98e2
commit
9dada90b78
@ -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},
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -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&) {
|
||||
|
@ -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>>>;
|
||||
|
@ -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 <>
|
||||
|
Loading…
x
Reference in New Issue
Block a user