diff --git a/common/gl_obj.cc b/common/gl_obj.cc index 2fa05c4..060a2b2 100644 --- a/common/gl_obj.cc +++ b/common/gl_obj.cc @@ -1,6 +1,7 @@ #include "common/gl_obj.hh" #include +#include #include #include #include @@ -15,7 +16,7 @@ namespace nf7::gl { nf7::Future>> Obj_BufferMeta::Create( const std::shared_ptr& ctx, GLenum type) noexcept { nf7::Future>>::Promise pro {ctx}; - ctx->env().ExecGL(ctx, [ctx, type, pro]() mutable { + ctx->env().ExecGL(ctx, [=]() mutable { GLuint id; glGenBuffers(1, &id); pro.Return(std::make_shared>(ctx, id, type)); @@ -26,9 +27,9 @@ nf7::Future>> Obj_BufferMeta::Create( nf7::Future>> Obj_TextureMeta::Create( const std::shared_ptr& ctx, - GLenum type, GLint fmt, GLsizei w, GLsizei h, GLsizei d) noexcept { + GLenum type, GLint fmt, std::array size) noexcept { nf7::Future>>::Promise pro {ctx}; - ctx->env().ExecGL(ctx, [ctx, type, fmt, w, h, d, pro]() mutable { + ctx->env().ExecGL(ctx, [=]() mutable { GLuint id; glGenTextures(1, &id); @@ -37,10 +38,18 @@ nf7::Future>> Obj_TextureMeta::Create( glTexParameteri(type, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); - glTexImage2D(type, 0, fmt, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, nullptr); + switch (type) { + case GL_TEXTURE_2D: + case GL_TEXTURE_RECTANGLE: + glTexImage2D(type, 0, fmt, size[0], size[1], 0, GL_RED, GL_UNSIGNED_BYTE, nullptr); + break; + default: + assert(false && "unknown texture type"); + break; + } glBindTexture(type, 0); - pro.Return(std::make_shared>(ctx, id, type, fmt, w, h, d)); + pro.Return(std::make_shared>(ctx, id, type, fmt, size)); }); return pro.future(); } @@ -51,7 +60,7 @@ nf7::Future>> Obj_ShaderMeta::Create( GLenum type, const std::string& src) noexcept { nf7::Future>>::Promise pro {ctx}; - ctx->env().ExecGL(ctx, [ctx, type, src, pro]() mutable { + ctx->env().ExecGL(ctx, [=]() mutable { const auto id = glCreateShader(type); if (id == 0) { pro.Throw("failed to allocate new shader"); diff --git a/common/gl_obj.hh b/common/gl_obj.hh index 9cbc82b..8b42e67 100644 --- a/common/gl_obj.hh +++ b/common/gl_obj.hh @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -79,20 +80,20 @@ struct Obj_TextureMeta final { // must be called from main or sub task static nf7::Future>> Create( const std::shared_ptr& ctx, - GLenum type, GLint fmt, GLsizei w, GLsizei h, GLsizei d) noexcept; + GLenum type, GLint fmt, std::array size) noexcept; static void Delete(GLuint id) noexcept { glDeleteTextures(1, &id); } Obj_TextureMeta() = delete; - Obj_TextureMeta(GLenum t, GLint f, GLsizei w, GLsizei h, GLsizei d) noexcept : - type(t), format(f), w(w), h(h), d(d) { + Obj_TextureMeta(GLenum t, GLint f, std::array s) noexcept : + type(t), format(f), size(s) { } - const GLenum type; - const GLint format; - const GLsizei w, h, d; + const GLenum type; + const GLint format; + const std::array size; }; using Texture = Obj; using TextureFactory = AsyncFactory>>; diff --git a/file/gl_obj.cc b/file/gl_obj.cc index c14ff84..df287aa 100644 --- a/file/gl_obj.cc +++ b/file/gl_obj.cc @@ -1,4 +1,7 @@ +#include +#include #include +#include #include #include #include @@ -14,6 +17,7 @@ #include #include +#include #include #include #include @@ -372,19 +376,19 @@ struct Texture { using Product = nf7::gl::Texture; - enum class Type { + enum class Type : uint8_t { Tex2D = 0x20, Rect = 0x21, }; - enum class Format { + enum class Format : uint8_t { U8 = 0x01, F32 = 0x14, }; enum class Comp : uint8_t { - R = 1, - RG = 2, - RGB = 3, - RGBA = 4, + R = 0x01, + RG = 0x02, + RGB = 0x03, + RGBA = 0x04, }; Texture() = default; @@ -394,7 +398,7 @@ struct Texture { Texture& operator=(Texture&&) = default; void serialize(auto& ar) { - ar(type_, format_, comp_, w_, h_, d_); + ar(type_, format_, comp_, size_); } std::string Stringify() noexcept { @@ -407,7 +411,12 @@ struct Texture { st << YAML::Key << "comp"; st << YAML::Value << std::string {magic_enum::enum_name(comp_)}; st << YAML::Key << "size"; - st << YAML::Value << YAML::Flow << std::vector {w_, h_, d_}; + st << YAML::Value << YAML::Flow; + st << YAML::BeginSeq; + st << size_[0]; + st << size_[1]; + st << size_[2]; + st << YAML::EndSeq; st << YAML::EndMap; return std::string {st.c_str(), st.size()}; } @@ -434,9 +443,8 @@ struct Texture { format_ = format; comp_ = comp; - w_ = size.size() >= 1? size[0]: 0; - h_ = size.size() >= 2? size[1]: 0; - d_ = size.size() >= 3? size[2]: 0; + std::copy(size.begin(), size.begin()+dim, size_.begin()); + std::fill(size_.begin()+dim, size_.end(), 1); } catch (std::bad_optional_access&) { throw nf7::Exception {"unknown enum"}; } catch (YAML::Exception& e) { @@ -444,11 +452,11 @@ struct Texture { } nf7::Future> Create(const std::shared_ptr& ctx) noexcept { + std::array size; + std::transform(size_.begin(), size_.end(), size.begin(), + [](auto x) { return static_cast(x); }); return Product::Create( - ctx, FromType(type_), ToInternalFormat(format_, comp_), - static_cast(w_), - static_cast(h_), - static_cast(d_)); + ctx, FromType(type_), ToInternalFormat(format_, comp_), size); } bool Handle(const std::shared_ptr& handler, @@ -460,19 +468,26 @@ struct Texture { const auto vec = v.tuple("vec").vector(); auto& tex = **res; - uint32_t w = 0, h = 0, d = 0; - switch (type_) { - case Type::Tex2D: - case Type::Rect: - w = v.tuple("w").integer(); - h = v.tuple("h").integer(); - if (w == 0 || h == 0) return false; - break; + static const char* kOffsetNames[] = {"x", "y", "z"}; + static const char* kSizeNames[] = {"w", "h", "d"}; + std::array offset = {0}; + std::array size = {1, 1, 1}; + + const auto dim = static_cast(magic_enum::enum_integer(type_) >> 4); + for (size_t i = 0; i < dim; ++i) { + offset[i] = v.tupleOr(kOffsetNames[i], nf7::Value::Integer {0}).integer(); + size[i] = v.tuple(kSizeNames[i]).integer(); + if (size[i] == 0) { + return false; + } + if (offset[i]+size[i] > size_[i]) { + throw nf7::Exception {"texture size overflow"}; + } } - const auto vecsz = - w*h* // number of texels - magic_enum::enum_integer(comp_)* // number of color components + const auto texel = std::accumulate(size.begin(), size.end(), 1, std::multiplies {}); + const auto vecsz = texel* + (magic_enum::enum_integer(comp_) & 0xF)* // number of color components (magic_enum::enum_integer(format_) & 0xF); // size of a component if (vec->size() < static_cast(vecsz)) { throw nf7::Exception {"vector is too small"}; @@ -480,16 +495,18 @@ struct Texture { const auto fmt = ToFormat(comp_); const auto type = ToCompType(format_); - handler->env().ExecGL(handler, [handler, res, &tex, w, h, d, fmt, type, vec]() { + handler->env().ExecGL(handler, [=, &tex]() { const auto target = tex.meta().type; glBindTexture(target, tex.id()); switch (target) { case GL_TEXTURE_2D: case GL_TEXTURE_RECTANGLE: glTexSubImage2D(target, 0, - 0, 0, - static_cast(w), static_cast(h), - fmt, type, vec->data()); + static_cast(offset[0]), + static_cast(offset[1]), + static_cast(size[0]), + static_cast(size[1]), + fmt, type, vec->data()); break; default: assert(false); @@ -500,7 +517,7 @@ struct Texture { }); return true; } else if (in.name == "download") { - const auto fmt = ToReadFormat( + const auto fmt = ToFetchFormat( in.value.tupleOr("comp", nf7::Value {""s}).string()); const auto type = ToCompType(magic_enum::enum_cast( @@ -511,11 +528,11 @@ struct Texture { glGenBuffers(1, &pbo); glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo); - const auto& tex = **res; - const auto w = tex.meta().w; - const auto h = tex.meta().h; - const auto size = static_cast(w*h)*CalcReadPixelSize(fmt, type); - glBufferData(GL_PIXEL_PACK_BUFFER, static_cast(size), nullptr, GL_DYNAMIC_READ); + const auto& tex = **res; + const auto size = tex.meta().size; + const auto texel = std::accumulate(size.begin(), size.end(), 1, std::multiplies {}); + const auto bsize = static_cast(texel)*CalcFetchPixelSize(fmt, type); + glBufferData(GL_PIXEL_PACK_BUFFER, static_cast(bsize), nullptr, GL_DYNAMIC_READ); const auto target = tex.meta().type; glBindTexture(target, tex.id()); @@ -527,10 +544,10 @@ struct Texture { nf7::gl::ExecFenceSync(handler).ThenIf([=, &tex](auto&) { glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo); - auto buf = std::make_shared>(size); + auto buf = std::make_shared>(bsize); const auto ptr = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY); - std::memcpy(buf->data(), ptr, size); + std::memcpy(buf->data(), ptr, bsize); glUnmapBuffer(GL_PIXEL_PACK_BUFFER); glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); @@ -539,8 +556,9 @@ struct Texture { handler->env().ExecSub(handler, [=, &tex]() { auto v = nf7::Value {std::vector { - {"w", static_cast(w)}, - {"h", static_cast(h)}, + {"w", static_cast(size[0])}, + {"h", static_cast(size[1])}, + {"d", static_cast(size[2])}, {"vector", buf}, }}; sender->Handle("buffer", std::move(v), handler); @@ -565,18 +583,19 @@ struct Texture { static_cast(c.size()), c.data(), magic_enum::enum_integer(comp_)); + ImGui::Text("size : %" PRIu32 " x %" PRIu32 " x %" PRIu32, size_[0], size_[1], size_[2]); + ImGui::Spacing(); if (prod) { const auto id = static_cast(prod->id()); const auto& m = prod->meta(); - ImGui::Text(" id: %" PRIiPTR, id); - ImGui::Text("size: %" PRIu32 " x %" PRIu32 " x %" PRIu32, m.w, m.h, m.d); + ImGui::Text("id: %" PRIiPTR, id); if (m.type == GL_TEXTURE_2D) { ImGui::Spacing(); ImGui::TextUnformatted("preview:"); ImGui::Image(reinterpret_cast(id), - ImVec2 {static_cast(m.w), static_cast(m.h)}); + ImVec2 {static_cast(size_[0]), static_cast(size_[1])}); } } } @@ -586,7 +605,7 @@ struct Texture { Format format_ = Format::U8; Comp comp_ = Comp::RGBA; - uint32_t w_, h_, d_; + std::array size_ = {256, 256, 1}; static GLenum FromType(Type t) { return @@ -628,7 +647,7 @@ struct Texture { throw 0; } - static GLenum ToReadFormat(Comp c) { + static GLenum ToFetchFormat(Comp c) { return c == Comp::R? GL_RED: c == Comp::RG? GL_RG: @@ -636,16 +655,16 @@ struct Texture { c == Comp::RGBA? GL_RGBA: throw 0; } - GLenum ToReadFormat(const std::string& v) { + GLenum ToFetchFormat(const std::string& v) { // There's additional options for format enum for glGetTexture. const auto comp = magic_enum::enum_cast(v).value_or(comp_); return - v == ""? ToReadFormat(comp): + v == ""? ToFetchFormat(comp): v == "G"? GL_GREEN: v == "B"? GL_BLUE: throw nf7::Exception {"unknown comp specifier: "+v}; } - static size_t CalcReadPixelSize(GLenum fmt, GLenum type) noexcept { + static size_t CalcFetchPixelSize(GLenum fmt, GLenum type) noexcept { size_t comp = 0; switch (fmt) { case GL_RED: