add offset parameters in uploading texture through GL/Texture

This commit is contained in:
falsycat 2022-10-24 09:04:19 +09:00
parent 1e33062e9a
commit c42b63da0c
3 changed files with 89 additions and 60 deletions

View File

@ -1,6 +1,7 @@
#include "common/gl_obj.hh"
#include <algorithm>
#include <array>
#include <exception>
#include <memory>
#include <vector>
@ -15,7 +16,7 @@ namespace nf7::gl {
nf7::Future<std::shared_ptr<Obj<Obj_BufferMeta>>> Obj_BufferMeta::Create(
const std::shared_ptr<nf7::Context>& ctx, GLenum type) noexcept {
nf7::Future<std::shared_ptr<Obj<Obj_BufferMeta>>>::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<Obj<Obj_BufferMeta>>(ctx, id, type));
@ -26,9 +27,9 @@ nf7::Future<std::shared_ptr<Obj<Obj_BufferMeta>>> Obj_BufferMeta::Create(
nf7::Future<std::shared_ptr<Obj<Obj_TextureMeta>>> Obj_TextureMeta::Create(
const std::shared_ptr<nf7::Context>& ctx,
GLenum type, GLint fmt, GLsizei w, GLsizei h, GLsizei d) noexcept {
GLenum type, GLint fmt, std::array<GLsizei, 3> size) noexcept {
nf7::Future<std::shared_ptr<Obj<Obj_TextureMeta>>>::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<std::shared_ptr<Obj<Obj_TextureMeta>>> 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<Obj<Obj_TextureMeta>>(ctx, id, type, fmt, w, h, d));
pro.Return(std::make_shared<Obj<Obj_TextureMeta>>(ctx, id, type, fmt, size));
});
return pro.future();
}
@ -51,7 +60,7 @@ nf7::Future<std::shared_ptr<Obj<Obj_ShaderMeta>>> Obj_ShaderMeta::Create(
GLenum type,
const std::string& src) noexcept {
nf7::Future<std::shared_ptr<Obj<Obj_ShaderMeta>>>::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<nf7::Exception>("failed to allocate new shader");

View File

@ -1,5 +1,6 @@
#pragma once
#include <array>
#include <cassert>
#include <memory>
#include <utility>
@ -79,20 +80,20 @@ struct Obj_TextureMeta final {
// must be called from main or sub task
static nf7::Future<std::shared_ptr<Obj<Obj_TextureMeta>>> Create(
const std::shared_ptr<nf7::Context>& ctx,
GLenum type, GLint fmt, GLsizei w, GLsizei h, GLsizei d) noexcept;
GLenum type, GLint fmt, std::array<GLsizei, 3> 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<GLsizei, 3> 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<GLsizei, 3> size;
};
using Texture = Obj<Obj_TextureMeta>;
using TextureFactory = AsyncFactory<nf7::Mutex::Resource<std::shared_ptr<Texture>>>;

View File

@ -1,4 +1,7 @@
#include <algorithm>
#include <array>
#include <cinttypes>
#include <numeric>
#include <optional>
#include <span>
#include <string>
@ -14,6 +17,7 @@
#include <yaml-cpp/yaml.h>
#include <yas/serialize.hpp>
#include <yas/types/std/array.hpp>
#include <yas/types/std/string.hpp>
#include <yas/types/std/vector.hpp>
#include <yas/types/utility/usertype.hpp>
@ -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<uint32_t> {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<std::shared_ptr<Product>> Create(const std::shared_ptr<nf7::Context>& ctx) noexcept {
std::array<GLsizei, 3> size;
std::transform(size_.begin(), size_.end(), size.begin(),
[](auto x) { return static_cast<GLsizei>(x); });
return Product::Create(
ctx, FromType(type_), ToInternalFormat(format_, comp_),
static_cast<GLsizei>(w_),
static_cast<GLsizei>(h_),
static_cast<GLsizei>(d_));
ctx, FromType(type_), ToInternalFormat(format_, comp_), size);
}
bool Handle(const std::shared_ptr<nf7::Node::Lambda>& 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<uint32_t>();
h = v.tuple("h").integer<uint32_t>();
if (w == 0 || h == 0) return false;
break;
static const char* kOffsetNames[] = {"x", "y", "z"};
static const char* kSizeNames[] = {"w", "h", "d"};
std::array<uint32_t, 3> offset = {0};
std::array<uint32_t, 3> size = {1, 1, 1};
const auto dim = static_cast<size_t>(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<uint32_t>();
size[i] = v.tuple(kSizeNames[i]).integer<uint32_t>();
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<uint32_t> {});
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<size_t>(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<GLsizei>(w), static_cast<GLsizei>(h),
fmt, type, vec->data());
static_cast<GLint>(offset[0]),
static_cast<GLint>(offset[1]),
static_cast<GLsizei>(size[0]),
static_cast<GLsizei>(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<Format>(
@ -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<size_t>(w*h)*CalcReadPixelSize(fmt, type);
glBufferData(GL_PIXEL_PACK_BUFFER, static_cast<GLsizeiptr>(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<uint32_t> {});
const auto bsize = static_cast<size_t>(texel)*CalcFetchPixelSize(fmt, type);
glBufferData(GL_PIXEL_PACK_BUFFER, static_cast<GLsizeiptr>(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<std::vector<uint8_t>>(size);
auto buf = std::make_shared<std::vector<uint8_t>>(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<nf7::Value::TuplePair> {
{"w", static_cast<nf7::Value::Integer>(w)},
{"h", static_cast<nf7::Value::Integer>(h)},
{"w", static_cast<nf7::Value::Integer>(size[0])},
{"h", static_cast<nf7::Value::Integer>(size[1])},
{"d", static_cast<nf7::Value::Integer>(size[2])},
{"vector", buf},
}};
sender->Handle("buffer", std::move(v), handler);
@ -565,18 +583,19 @@ struct Texture {
static_cast<int>(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<intptr_t>(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<void*>(id),
ImVec2 {static_cast<float>(m.w), static_cast<float>(m.h)});
ImVec2 {static_cast<float>(size_[0]), static_cast<float>(size_[1])});
}
}
}
@ -586,7 +605,7 @@ struct Texture {
Format format_ = Format::U8;
Comp comp_ = Comp::RGBA;
uint32_t w_, h_, d_;
std::array<uint32_t, 3> 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<Comp>(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: