improve ugly codes in locking OpenGL objects

This commit is contained in:
falsycat 2022-11-03 10:59:26 +09:00
parent 5e515e23fa
commit 8f6ff99136
3 changed files with 96 additions and 115 deletions

View File

@ -19,9 +19,9 @@
namespace nf7::gl {
template <typename T>
auto LockAndValidate(const std::shared_ptr<nf7::Context>& ctx,
nf7::AsyncFactory<nf7::Mutex::Resource<std::shared_ptr<T>>>& factory,
std::function<void(const T&)>&& validator) noexcept {
auto LockAndValidate(const std::shared_ptr<nf7::Context>& ctx,
typename T::Factory& factory,
std::function<void(const T&)>&& validator) noexcept {
typename nf7::Future<nf7::Mutex::Resource<std::shared_ptr<T>>>::Promise pro {ctx};
factory.Create().Chain(pro, [validator](auto& v) {
validator(**v);
@ -30,7 +30,7 @@ auto LockAndValidate(const std::shared_ptr<nf7::Context>&
return pro.future();
}
static auto LockAndValidate(const std::shared_ptr<nf7::Context>& ctx,
nf7::gl::BufferFactory& factory,
nf7::gl::Buffer::Factory& factory,
nf7::gl::BufferTarget target,
size_t required) noexcept {
return LockAndValidate<gl::Buffer>(ctx, factory, [target, required](auto& buf) {
@ -46,7 +46,7 @@ static auto LockAndValidate(const std::shared_ptr<nf7::Context>& ctx,
});
}
static auto LockAndValidate(const std::shared_ptr<nf7::Context>& ctx,
nf7::gl::TextureFactory& factory,
nf7::gl::Texture::Factory& factory,
nf7::gl::TextureTarget target) noexcept {
return LockAndValidate<gl::Texture>(ctx, factory, [target](auto& tex) {
if (tex.meta().target != target) {
@ -148,7 +148,7 @@ nf7::Future<std::shared_ptr<Obj<Obj_ProgramMeta>>> Obj_ProgramMeta::Create(
std::vector<nf7::Future<nf7::Mutex::Resource<std::shared_ptr<gl::Shader>>>> shs;
for (auto shader : shaders) {
shs.emplace_back(ctx->env().GetFileOrThrow(shader).
interfaceOrThrow<nf7::gl::ShaderFactory>().Create());
interfaceOrThrow<nf7::gl::Shader::Factory>().Create());
apro.Add(shs.back());
}
@ -212,21 +212,20 @@ try {
}
nf7::Future<std::shared_ptr<Obj<Obj_VertexArrayMeta>>>::Promise pro {ctx};
LockBuffers(ctx).Chain(
LockAttachments(ctx).Chain(
nf7::Env::kGL, ctx, pro,
[*this, ctx, pro](auto& bufs) mutable {
// check all buffers
if (index) {
assert(bufs.size() == attrs.size()+1);
const auto& m = (*bufs.back().value()).meta();
assert(bufs.index);
const auto& m = (***bufs.index).meta();
if (m.target != gl::BufferTarget::ElementArray) {
throw nf7::Exception {"index buffer is not ElementArray"};
}
} else {
assert(bufs.size() == attrs.size());
}
assert(bufs.attrs.size() == attrs.size());
for (size_t i = 0; i < attrs.size(); ++i) {
if ((*bufs[i].value()).meta().target != gl::BufferTarget::Array) {
if ((**bufs.attrs[i]).meta().target != gl::BufferTarget::Array) {
throw nf7::Exception {"buffer is not Array"};
}
}
@ -236,7 +235,7 @@ try {
glBindVertexArray(id);
for (size_t i = 0; i < attrs.size(); ++i) {
const auto& attr = attrs[i];
const auto& buf = *bufs[i].value();
const auto& buf = **bufs.attrs[i];
glBindBuffer(GL_ARRAY_BUFFER, buf.id());
glEnableVertexAttribArray(attr.location);
@ -250,8 +249,7 @@ try {
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_ELEMENT_ARRAY_BUFFER, (***bufs.index).id());
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
@ -264,17 +262,27 @@ try {
return {std::current_exception()};
}
Obj_VertexArrayMeta::LockedBuffersFuture Obj_VertexArrayMeta::LockBuffers(
Obj_VertexArrayMeta::LockedAttachmentsFuture Obj_VertexArrayMeta::LockAttachments(
const std::shared_ptr<nf7::Context>& ctx,
const ValidationHint& vhint) const noexcept
try {
const auto Lock = [&](nf7::File::Id id, gl::BufferTarget target, size_t req) {
auto& factory = ctx->env().
GetFileOrThrow(id).interfaceOrThrow<gl::Buffer::Factory>();
return LockAndValidate(ctx, factory, target, req);
};
nf7::AggregatePromise apro {ctx};
std::vector<nf7::gl::BufferFactory::Product> fus;
fus.reserve(1+attrs.size());
auto ret = std::make_shared<LockedAttachments>();
LockedAttachmentsFuture::Promise pro {ctx};
// lock array buffers
for (const auto& attr : attrs) {
std::vector<gl::Buffer::Factory::Product> attrs_fu;
attrs_fu.reserve(attrs.size());
for (size_t i = 0; i < attrs.size(); ++i) {
const auto& attr = attrs[i];
const size_t required =
// when non-instanced and no-index-buffer drawing
attr.divisor == 0 && vhint.vertices > 0 && !index?
@ -284,35 +292,24 @@ try {
static_cast<size_t>(attr.stride) * (vhint.instances-1) + attr.offset:
size_t {0};
auto& factory = ctx->env().
GetFileOrThrow(attr.buffer).
interfaceOrThrow<gl::BufferFactory>();
auto fu = LockAndValidate(ctx, factory, gl::BufferTarget::Array, required);
apro.Add(fu);
fus.emplace_back(fu);
attrs_fu.push_back(Lock(attr.buffer, gl::BufferTarget::Array, required));
apro.Add(attrs_fu.back());
}
// 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);
apro.Add(Lock(index->buffer, gl::BufferTarget::ElementArray, required).
Chain(pro, [ret](auto& buf) { ret->index = buf; }));
}
// 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&) {
std::vector<nf7::Mutex::Resource<std::shared_ptr<gl::Buffer>>> ret;
ret.reserve(fus.size());
for (auto& fu : fus) {
ret.emplace_back(fu.value());
// return ret
apro.future().Chain(pro, [ret, attrs_fu = std::move(attrs_fu)](auto&) {
ret->attrs.reserve(attrs_fu.size());
for (auto& fu : attrs_fu) {
ret->attrs.push_back(fu.value());
}
return ret;
return std::move(*ret);
});
return pro.future();
} catch (nf7::Exception&) {
@ -371,34 +368,34 @@ try {
auto ret = std::make_shared<LockedAttachments>();
nf7::AggregatePromise apro {ctx};
LockedAttachmentsFuture::Promise pro {ctx};
const auto lock = [&](nf7::File::Id id) {
const auto Lock = [&](nf7::File::Id id) {
auto& factory = ctx->env().
GetFileOrThrow(id).
interfaceOrThrow<nf7::gl::TextureFactory>();
interfaceOrThrow<gl::Texture::Factory>();
return LockAndValidate(ctx, factory, gl::TextureTarget::Tex2D);
};
for (size_t i = 0; i < colors.size(); ++i) {
const auto& color = colors[i];
if (color && color->tex) {
apro.Add(lock(color->tex).ThenIf([i, ret](auto& res) {
apro.Add(Lock(color->tex).Chain(pro, [i, ret](auto& res) {
ret->colors[i] = res;
}));
}
}
if (depth && depth->tex) {
apro.Add(lock(depth->tex).ThenIf([ret](auto& res) {
apro.Add(Lock(depth->tex).Chain(pro, [ret](auto& res) {
ret->depth = res;
}));
}
if (stencil && stencil->tex) {
apro.Add(lock(stencil->tex).ThenIf([ret](auto& res) {
apro.Add(Lock(stencil->tex).Chain(pro, [ret](auto& res) {
ret->stencil = res;
}));
}
LockedAttachmentsFuture::Promise pro {ctx};
apro.future().Chain(pro, [ret](auto&) { return std::move(*ret); });
return pro.future();
} catch (nf7::Exception&) {

View File

@ -22,8 +22,9 @@ namespace nf7::gl {
template <typename T>
class Obj final {
public:
using Meta = T;
using Param = typename Meta::Param;
using Meta = T;
using Param = typename Meta::Param;
using Factory = nf7::AsyncFactory<nf7::Mutex::Resource<std::shared_ptr<Obj<T>>>>;
Obj(const std::shared_ptr<nf7::Context>& ctx, GLuint id, const Meta& meta) noexcept :
ctx_(ctx), id_(id), meta_(meta) {
@ -60,14 +61,12 @@ struct Obj_BufferMeta final {
glDeleteBuffers(1, &id);
}
// must be called from main or sub task
nf7::Future<std::shared_ptr<Obj<Obj_BufferMeta>>> Create(
const std::shared_ptr<nf7::Context>& ctx) const noexcept;
gl::BufferTarget target;
};
using Buffer = Obj<Obj_BufferMeta>;
using BufferFactory = AsyncFactory<nf7::Mutex::Resource<std::shared_ptr<Buffer>>>;
using Buffer = Obj<Obj_BufferMeta>;
struct Obj_TextureMeta final {
@ -78,7 +77,6 @@ struct Obj_TextureMeta final {
glDeleteTextures(1, &id);
}
// must be called from main or sub task
nf7::Future<std::shared_ptr<Obj<Obj_TextureMeta>>> Create(
const std::shared_ptr<nf7::Context>& ctx) const noexcept;
@ -86,8 +84,7 @@ struct Obj_TextureMeta final {
gl::InternalFormat format;
std::array<GLsizei, 3> size;
};
using Texture = Obj<Obj_TextureMeta>;
using TextureFactory = AsyncFactory<nf7::Mutex::Resource<std::shared_ptr<Texture>>>;
using Texture = Obj<Obj_TextureMeta>;
struct Obj_ShaderMeta final {
@ -98,7 +95,6 @@ struct Obj_ShaderMeta final {
glDeleteShader(id);
}
// must be called from main or sub task
nf7::Future<std::shared_ptr<Obj<Obj_ShaderMeta>>> Create(
const std::shared_ptr<nf7::Context>& ctx,
const std::string& src) const noexcept;
@ -106,7 +102,6 @@ struct Obj_ShaderMeta final {
gl::ShaderType type;
};
using Shader = Obj<Obj_ShaderMeta>;
using ShaderFactory = AsyncFactory<nf7::Mutex::Resource<std::shared_ptr<Shader>>>;
struct Obj_ProgramMeta final {
@ -124,7 +119,6 @@ struct Obj_ProgramMeta final {
glDeleteProgram(id);
}
// must be called from main or sub task
nf7::Future<std::shared_ptr<Obj<Obj_ProgramMeta>>> Create(
const std::shared_ptr<nf7::Context>& ctx,
const std::vector<nf7::File::Id>& shaders) noexcept;
@ -135,15 +129,17 @@ struct Obj_ProgramMeta final {
std::optional<Depth> depth;
};
using Program = Obj<Obj_ProgramMeta>;
using ProgramFactory = AsyncFactory<nf7::Mutex::Resource<std::shared_ptr<Program>>>;
struct Obj_VertexArrayMeta final {
public:
struct Param { };
using LockedBuffersFuture =
nf7::Future<std::vector<nf7::Mutex::Resource<std::shared_ptr<gl::Buffer>>>>;
struct LockedAttachments {
std::optional<nf7::Mutex::Resource<std::shared_ptr<gl::Buffer>>> index;
std::vector<nf7::Mutex::Resource<std::shared_ptr<gl::Buffer>>> attrs;
};
using LockedAttachmentsFuture = nf7::Future<LockedAttachments>;
struct Index {
nf7::File::Id buffer;
@ -161,8 +157,6 @@ struct Obj_VertexArrayMeta final {
};
struct ValidationHint {
ValidationHint() noexcept { }
size_t vertices = 0;
size_t instances = 0;
};
@ -171,21 +165,18 @@ struct Obj_VertexArrayMeta final {
glDeleteVertexArrays(1, &id);
}
// must be called from main or sub task
nf7::Future<std::shared_ptr<Obj<Obj_VertexArrayMeta>>> Create(
const std::shared_ptr<nf7::Context>& ctx) const noexcept;
// 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
LockedBuffersFuture LockBuffers(
LockedAttachmentsFuture LockAttachments(
const std::shared_ptr<nf7::Context>& ctx,
const ValidationHint& vhint = {}) const noexcept;
const ValidationHint& vhint = {0, 0}) const noexcept;
std::optional<Index> index;
std::vector<Attr> attrs;
};
using VertexArray = Obj<Obj_VertexArrayMeta>;
using VertexArrayFactory = AsyncFactory<nf7::Mutex::Resource<std::shared_ptr<VertexArray>>>;
struct Obj_FramebufferMeta final {
@ -212,7 +203,6 @@ struct Obj_FramebufferMeta final {
glDeleteFramebuffers(1, &id);
}
// must be called from main or sub task
nf7::Future<std::shared_ptr<Obj<Obj_FramebufferMeta>>> Create(
const std::shared_ptr<nf7::Context>& ctx) const noexcept;
@ -224,6 +214,23 @@ struct Obj_FramebufferMeta final {
std::optional<Attachment> stencil;
};
using Framebuffer = Obj<Obj_FramebufferMeta>;
using FramebufferFactory = AsyncFactory<nf7::Mutex::Resource<std::shared_ptr<Framebuffer>>>;
// acquires locks of the object and all of its attachments
template <typename T, typename... Args>
auto LockRecursively(typename T::Factory& factory,
const std::shared_ptr<nf7::Context>& ctx,
Args&&... args) noexcept {
typename nf7::Future<std::pair<nf7::Mutex::Resource<std::shared_ptr<T>>,
typename T::Meta::LockedAttachments>>::Promise pro {ctx};
factory.Create().Chain(pro, [=, ...args = std::forward<Args>(args)](auto& obj) mutable {
(**obj).meta().LockAttachments(ctx, std::forward<Args>(args)...).
Chain(pro, [obj](auto& att) {
return std::make_pair(obj, att);
});
});
return pro.future();
}
} // namespace nf7::gl

View File

@ -911,66 +911,41 @@ struct Program {
nf7::AggregatePromise apro {p.la};
// find, fetch and lock FBO
std::optional<nf7::gl::FramebufferFactory::Product> fbo_fu;
std::optional<nf7::gl::Framebuffer::Meta::LockedAttachmentsFuture> fbo_lock_fu;
{
fbo_fu = v.tuple("fbo").file(base).
interfaceOrThrow<nf7::gl::FramebufferFactory>().Create();
nf7::gl::Framebuffer::Meta::LockedAttachmentsFuture::Promise fbo_lock_pro;
fbo_fu->ThenIf([la = p.la, fbo_lock_pro](auto& fbo) mutable {
(**fbo).meta().LockAttachments(la).Chain(fbo_lock_pro);
});
fbo_lock_fu = fbo_lock_pro.future();
apro.Add(*fbo_lock_fu);
}
auto fbo_fu = gl::LockRecursively<gl::Framebuffer>(
v.tuple("fbo").
file(base).
interfaceOrThrow<nf7::gl::Framebuffer::Factory>(),
p.la);
apro.Add(fbo_fu);
// find, fetch and lock VAO
std::optional<nf7::gl::VertexArrayFactory::Product> vao_fu;
std::optional<nf7::gl::VertexArray::Meta::LockedBuffersFuture> vao_lock_fu;
{
vao_fu = v.tuple("vao").file(base).
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 = p.la, vao_lock_pro, vhint](auto& vao) mutable {
(**vao).meta().LockBuffers(la, vhint).Chain(vao_lock_pro);
});
vao_lock_fu = vao_lock_pro.future();
apro.Add(*vao_lock_fu);
}
auto vao_fu = gl::LockRecursively<gl::VertexArray>(
v.tuple("vao").
file(base).
interfaceOrThrow<nf7::gl::VertexArray::Factory>(),
p.la,
gl::VertexArray::Meta::ValidationHint {
.vertices = static_cast<size_t>(count),
.instances = static_cast<size_t>(inst),
});
apro.Add(vao_fu);
// find, fetch and lock textures
std::vector<std::pair<std::string, nf7::gl::TextureFactory::Product>> tex_fu;
std::vector<std::pair<std::string, gl::Texture::Factory::Product>> tex_fu;
tex_fu.reserve(tex->size());
for (auto& pa : *tex) {
auto fu = base.
ResolveOrThrow(pa.second.string()).
interfaceOrThrow<nf7::gl::TextureFactory>().
interfaceOrThrow<gl::Texture::Factory>().
Create();
tex_fu.emplace_back(pa.first, fu);
apro.Add(fu);
}
// execute drawing after successful locking
apro.future().Then(nf7::Env::kGL, p.la, [=, tex_fu = std::move(tex_fu)](auto&) {
assert(fbo_lock_fu);
assert(vao_lock_fu);
try {
if (fbo_lock_fu->error()) fbo_lock_fu->value();
if (vao_lock_fu->error()) vao_lock_fu->value();
} catch (nf7::Exception&) {
p.log->Error("failed to acquire lock of VAO or FBO");
return;
}
const auto& fbo = *fbo_fu->value();
const auto& vao = *vao_fu->value();
apro.future().ThenIf(nf7::Env::kGL, p.la, [=, tex_fu = std::move(tex_fu)](auto&) {
const auto& fbo = *fbo_fu.value().first;
const auto& vao = *vao_fu.value().first;
const auto& prog = *p.obj;
// bind objects
@ -1027,6 +1002,8 @@ struct Program {
if (status != GL_FRAMEBUFFER_COMPLETE) {
p.log->Warn("framebuffer is broken");
}
}).Catch<nf7::Exception>([p](auto& e) {
p.log->Error(e);
});
return false;
} else {