740 lines
21 KiB
C++
740 lines
21 KiB
C++
#include "pch.h"
|
|
#include "log.h"
|
|
#include "rtt.h"
|
|
#include "util.h"
|
|
#include "app.h"
|
|
#include "renderer_gl/opengl_capabilities.h"
|
|
|
|
#include <cstdint>
|
|
|
|
namespace {
|
|
|
|
[[nodiscard]] GLenum texture_2d_target() noexcept
|
|
{
|
|
return static_cast<GLenum>(pp::renderer::gl::texture_2d_target());
|
|
}
|
|
|
|
[[nodiscard]] GLenum renderbuffer_target() noexcept
|
|
{
|
|
return static_cast<GLenum>(pp::renderer::gl::renderbuffer_target());
|
|
}
|
|
|
|
[[nodiscard]] GLenum framebuffer_target() noexcept
|
|
{
|
|
return static_cast<GLenum>(pp::renderer::gl::framebuffer_target());
|
|
}
|
|
|
|
void query_opengl_integer(std::uint32_t name, std::int32_t* value) noexcept
|
|
{
|
|
glGetIntegerv(static_cast<GLenum>(name), reinterpret_cast<GLint*>(value));
|
|
}
|
|
|
|
void bind_opengl_framebuffer(std::uint32_t target, std::uint32_t framebuffer) noexcept
|
|
{
|
|
glBindFramebuffer(static_cast<GLenum>(target), static_cast<GLuint>(framebuffer));
|
|
}
|
|
|
|
void blit_opengl_framebuffer(
|
|
std::int32_t source_x0,
|
|
std::int32_t source_y0,
|
|
std::int32_t source_x1,
|
|
std::int32_t source_y1,
|
|
std::int32_t destination_x0,
|
|
std::int32_t destination_y0,
|
|
std::int32_t destination_x1,
|
|
std::int32_t destination_y1,
|
|
std::uint32_t mask,
|
|
std::uint32_t filter) noexcept
|
|
{
|
|
glBlitFramebuffer(
|
|
static_cast<GLint>(source_x0),
|
|
static_cast<GLint>(source_y0),
|
|
static_cast<GLint>(source_x1),
|
|
static_cast<GLint>(source_y1),
|
|
static_cast<GLint>(destination_x0),
|
|
static_cast<GLint>(destination_y0),
|
|
static_cast<GLint>(destination_x1),
|
|
static_cast<GLint>(destination_y1),
|
|
static_cast<GLbitfield>(mask),
|
|
static_cast<GLenum>(filter));
|
|
}
|
|
|
|
void read_opengl_pixels(
|
|
std::int32_t x,
|
|
std::int32_t y,
|
|
std::int32_t width,
|
|
std::int32_t height,
|
|
std::uint32_t pixel_format,
|
|
std::uint32_t component_type,
|
|
void* pixels) noexcept
|
|
{
|
|
glReadPixels(
|
|
static_cast<GLint>(x),
|
|
static_cast<GLint>(y),
|
|
static_cast<GLsizei>(width),
|
|
static_cast<GLsizei>(height),
|
|
static_cast<GLenum>(pixel_format),
|
|
static_cast<GLenum>(component_type),
|
|
pixels);
|
|
}
|
|
|
|
}
|
|
|
|
RTT& RTT::operator=(RTT&& other)
|
|
{
|
|
int_fmt = other.int_fmt;
|
|
texID = other.texID;
|
|
fboID = other.fboID;
|
|
rboID = other.rboID;
|
|
w = other.w;
|
|
h = other.h;
|
|
bound = other.bound;
|
|
|
|
other.int_fmt = 0;
|
|
other.texID = 0;
|
|
other.fboID = 0;
|
|
other.rboID = 0;
|
|
other.w = 0;
|
|
other.h = 0;
|
|
other.bound = false;
|
|
|
|
return *this;
|
|
}
|
|
|
|
RTT::RTT()
|
|
{
|
|
int_fmt = 0;
|
|
fboID = 0;
|
|
rboID = 0;
|
|
w = 0;
|
|
h = 0;
|
|
bound = false;
|
|
}
|
|
|
|
RTT::RTT(RTT&& other)
|
|
{
|
|
int_fmt = other.int_fmt;
|
|
texID = other.texID;
|
|
fboID = other.fboID;
|
|
rboID = other.rboID;
|
|
w = other.w;
|
|
h = other.h;
|
|
bound = other.bound;
|
|
|
|
other.int_fmt = 0;
|
|
other.texID = 0;
|
|
other.fboID = 0;
|
|
other.rboID = 0;
|
|
other.w = 0;
|
|
other.h = 0;
|
|
other.bound = false;
|
|
}
|
|
|
|
RTT::~RTT()
|
|
{
|
|
#ifdef _DEBUG
|
|
if (valid())
|
|
LOG("RTT not destroyed");
|
|
#endif // _DEBUG
|
|
destroy();
|
|
}
|
|
|
|
bool RTT::resize(int width, int height)
|
|
{
|
|
bool ret = false;
|
|
RTT new_rtt;
|
|
|
|
App::I->render_task([&]
|
|
{
|
|
ret = new_rtt.create(width, height, -1, int_fmt, rboID != 0);
|
|
if (!ret)
|
|
{
|
|
LOG("failed to resize rtt from %dx%d to %dx%d", w, h, width, height);
|
|
return;
|
|
}
|
|
|
|
const auto status = pp::renderer::gl::blit_opengl_framebuffer(
|
|
pp::renderer::gl::OpenGlFramebufferBlit {
|
|
.source_framebuffer = static_cast<std::uint32_t>(fboID),
|
|
.destination_framebuffer = static_cast<std::uint32_t>(new_rtt.fboID),
|
|
.source_rect = pp::renderer::gl::OpenGlFramebufferRect { .x1 = w, .y1 = h },
|
|
.destination_rect = pp::renderer::gl::OpenGlFramebufferRect {
|
|
.x1 = new_rtt.w,
|
|
.y1 = new_rtt.h,
|
|
},
|
|
.mask = pp::renderer::gl::framebuffer_color_buffer_mask(),
|
|
.filter = pp::renderer::gl::framebuffer_blit_filter(true),
|
|
},
|
|
pp::renderer::gl::OpenGlFramebufferBlitDispatch {
|
|
.get_integer = query_opengl_integer,
|
|
.bind_framebuffer = bind_opengl_framebuffer,
|
|
.blit_framebuffer = blit_opengl_framebuffer,
|
|
});
|
|
if (!status.ok()) {
|
|
LOG("RTT::resize blit failed because: %s", status.message);
|
|
ret = false;
|
|
return;
|
|
}
|
|
|
|
destroy();
|
|
});
|
|
|
|
if (!ret)
|
|
return false;
|
|
|
|
oldRFboID = 0;
|
|
oldDFboID = 0;
|
|
*this = std::move(new_rtt);
|
|
|
|
return true;
|
|
}
|
|
|
|
void RTT::destroy()
|
|
{
|
|
if (!valid())
|
|
return;
|
|
App::I->render_task_async([rboID=rboID, texID=texID, fboID=fboID]
|
|
{
|
|
if (rboID)
|
|
{
|
|
glDeleteRenderbuffers(1, &rboID);
|
|
}
|
|
if (texID)
|
|
{
|
|
//unbindTexture();
|
|
glDeleteTextures(1, &texID);
|
|
//LOG("TEX rtt destroy %d", texID)
|
|
}
|
|
if (fboID)
|
|
{
|
|
//unbindFramebuffer();
|
|
glDeleteFramebuffers(1, &fboID);
|
|
//LOG("RTT DESTROY %d", fboID);
|
|
}
|
|
});
|
|
texID = 0;
|
|
fboID = 0;
|
|
rboID = 0;
|
|
// w = 0;
|
|
// h = 0;
|
|
}
|
|
|
|
void RTT::copy(const RTT & source)
|
|
{
|
|
if (!valid() || !source.valid())
|
|
return;
|
|
App::I->render_task([&]
|
|
{
|
|
const auto status = pp::renderer::gl::blit_opengl_framebuffer(
|
|
pp::renderer::gl::OpenGlFramebufferBlit {
|
|
.source_framebuffer = static_cast<std::uint32_t>(source.fboID),
|
|
.destination_framebuffer = static_cast<std::uint32_t>(fboID),
|
|
.source_rect = pp::renderer::gl::OpenGlFramebufferRect { .x1 = source.w, .y1 = source.h },
|
|
.destination_rect = pp::renderer::gl::OpenGlFramebufferRect { .x1 = w, .y1 = h },
|
|
.mask = pp::renderer::gl::framebuffer_color_buffer_mask(),
|
|
.filter = pp::renderer::gl::framebuffer_blit_filter(true),
|
|
},
|
|
pp::renderer::gl::OpenGlFramebufferBlitDispatch {
|
|
.get_integer = query_opengl_integer,
|
|
.bind_framebuffer = bind_opengl_framebuffer,
|
|
.blit_framebuffer = blit_opengl_framebuffer,
|
|
});
|
|
if (!status.ok())
|
|
LOG("RTT::copy blit failed because: %s", status.message);
|
|
});
|
|
}
|
|
|
|
void RTT::copy(const RTT& source, const glm::vec4& rect)
|
|
{
|
|
if (!valid() || !source.valid())
|
|
return;
|
|
App::I->render_task([&]
|
|
{
|
|
auto r = rect_intersection(rect, { 0, 0, w, h });
|
|
const auto status = pp::renderer::gl::blit_opengl_framebuffer(
|
|
pp::renderer::gl::OpenGlFramebufferBlit {
|
|
.source_framebuffer = static_cast<std::uint32_t>(source.fboID),
|
|
.destination_framebuffer = static_cast<std::uint32_t>(fboID),
|
|
.source_rect = pp::renderer::gl::OpenGlFramebufferRect {
|
|
.x0 = static_cast<std::int32_t>(r.x),
|
|
.y0 = static_cast<std::int32_t>(r.y),
|
|
.x1 = static_cast<std::int32_t>(r.z),
|
|
.y1 = static_cast<std::int32_t>(r.w),
|
|
},
|
|
.destination_rect = pp::renderer::gl::OpenGlFramebufferRect {
|
|
.x0 = static_cast<std::int32_t>(r.x),
|
|
.y0 = static_cast<std::int32_t>(r.y),
|
|
.x1 = static_cast<std::int32_t>(r.z),
|
|
.y1 = static_cast<std::int32_t>(r.w),
|
|
},
|
|
.mask = pp::renderer::gl::framebuffer_color_buffer_mask(),
|
|
.filter = pp::renderer::gl::framebuffer_blit_filter(false),
|
|
},
|
|
pp::renderer::gl::OpenGlFramebufferBlitDispatch {
|
|
.get_integer = query_opengl_integer,
|
|
.bind_framebuffer = bind_opengl_framebuffer,
|
|
.blit_framebuffer = blit_opengl_framebuffer,
|
|
});
|
|
if (!status.ok())
|
|
LOG("RTT::copy region blit failed because: %s", status.message);
|
|
});
|
|
}
|
|
|
|
RTT RTT::clone() const noexcept
|
|
{
|
|
RTT dup;
|
|
dup.create(w, h);
|
|
dup.copy(*this);
|
|
return dup;
|
|
}
|
|
|
|
bool RTT::create(int width, int height)
|
|
{
|
|
return create(
|
|
width,
|
|
height,
|
|
-1,
|
|
static_cast<GLint>(pp::renderer::gl::rgba8_internal_format()));
|
|
}
|
|
|
|
bool RTT::create(int width, int height, int tex)
|
|
{
|
|
return create(
|
|
width,
|
|
height,
|
|
tex,
|
|
static_cast<GLint>(pp::renderer::gl::rgba8_internal_format()));
|
|
}
|
|
|
|
bool RTT::create(int width, int height, int tex/* = -1*/, GLint internal_format, bool depth_buffer /*= false*/)
|
|
{
|
|
GLenum status = 0;
|
|
App::I->render_task([&]
|
|
{
|
|
// Destroy any previously created object
|
|
destroy();
|
|
|
|
w = width;
|
|
h = height;
|
|
int_fmt = internal_format;
|
|
|
|
if (tex == -1)
|
|
{
|
|
glGenTextures(1, &texID);
|
|
//LOG("TEX rtt create %d", texID);
|
|
}
|
|
else
|
|
{
|
|
texID = tex;
|
|
}
|
|
|
|
const auto ifmt = static_cast<GLenum>(
|
|
pp::renderer::gl::texture_upload_type_for_internal_format(static_cast<std::uint32_t>(internal_format)));
|
|
|
|
glBindTexture(texture_2d_target(), texID);
|
|
if (tex == -1)
|
|
glTexImage2D(
|
|
texture_2d_target(),
|
|
0,
|
|
internal_format,
|
|
width,
|
|
height,
|
|
0,
|
|
static_cast<GLenum>(pp::renderer::gl::rgba_pixel_format()),
|
|
ifmt,
|
|
0);
|
|
for (const auto parameter : pp::renderer::gl::default_render_target_texture_parameters())
|
|
{
|
|
glTexParameterf(
|
|
texture_2d_target(),
|
|
static_cast<GLenum>(parameter.name),
|
|
static_cast<GLfloat>(parameter.value));
|
|
}
|
|
glBindTexture(texture_2d_target(), 0);
|
|
|
|
// Create a renderbuffer object to store depth info
|
|
if (depth_buffer)
|
|
{
|
|
glGenRenderbuffers(1, &rboID);
|
|
glBindRenderbuffer(renderbuffer_target(), rboID);
|
|
glRenderbufferStorage(
|
|
renderbuffer_target(),
|
|
static_cast<GLenum>(pp::renderer::gl::depth_component24_format()),
|
|
width,
|
|
height);
|
|
glBindRenderbuffer(renderbuffer_target(), 0);
|
|
}
|
|
|
|
GLint oldFboID;
|
|
glGetIntegerv(static_cast<GLenum>(pp::renderer::gl::draw_framebuffer_binding_query()), &oldFboID);
|
|
|
|
// Create a framebuffer object
|
|
glGenFramebuffers(1, &fboID);
|
|
glBindFramebuffer(framebuffer_target(), fboID);
|
|
//LOG("RTT CREATE %d - tex %d", fboID, texID);
|
|
|
|
// Attach the texture to FBO color attachment point
|
|
glFramebufferTexture2D(
|
|
framebuffer_target(),
|
|
static_cast<GLenum>(pp::renderer::gl::framebuffer_color_attachment()),
|
|
texture_2d_target(),
|
|
texID,
|
|
0);
|
|
if (depth_buffer)
|
|
{
|
|
// Attach the renderbuffer to depth attachment point
|
|
glFramebufferRenderbuffer(
|
|
framebuffer_target(),
|
|
static_cast<GLenum>(pp::renderer::gl::framebuffer_depth_attachment()),
|
|
renderbuffer_target(),
|
|
rboID);
|
|
}
|
|
|
|
// Check FBO status
|
|
status = glCheckFramebufferStatus(framebuffer_target());
|
|
if (status != static_cast<GLenum>(pp::renderer::gl::framebuffer_complete_status()))
|
|
LOG("RTT::create failed because: %s",
|
|
pp::renderer::gl::framebuffer_status_name(static_cast<std::uint32_t>(status)));
|
|
|
|
// Switch back to window-system-provided framebuffer
|
|
glBindFramebuffer(framebuffer_target(), oldFboID);
|
|
oldRFboID = 0;
|
|
oldDFboID = 0;
|
|
});
|
|
|
|
return status == static_cast<GLenum>(pp::renderer::gl::framebuffer_complete_status());
|
|
}
|
|
|
|
void RTT::bindFramebuffer()
|
|
{
|
|
assert(App::I->is_render_thread());
|
|
#ifdef _DEBUG
|
|
if (bound)
|
|
{
|
|
LOG("framebuffer bound twice!");
|
|
#ifdef _WIN32
|
|
__debugbreak();
|
|
#endif
|
|
}
|
|
#endif // _DEBUG
|
|
const auto binding = pp::renderer::gl::bind_opengl_framebuffer_for_draw_read(
|
|
static_cast<std::uint32_t>(fboID),
|
|
pp::renderer::gl::OpenGlFramebufferBindDispatch {
|
|
.get_integer = query_opengl_integer,
|
|
.bind_framebuffer = bind_opengl_framebuffer,
|
|
});
|
|
if (!binding.ok()) {
|
|
LOG("RTT::bindFramebuffer() failed because: %s", binding.status().message);
|
|
return;
|
|
}
|
|
|
|
oldDFboID = static_cast<GLint>(binding.value().draw_framebuffer);
|
|
oldRFboID = static_cast<GLint>(binding.value().read_framebuffer);
|
|
bound = true;
|
|
}
|
|
|
|
void RTT::unbindFramebuffer()
|
|
{
|
|
assert(App::I->is_render_thread());
|
|
if (!bound)
|
|
return;
|
|
const auto status = pp::renderer::gl::restore_opengl_framebuffer_binding(
|
|
pp::renderer::gl::OpenGlFramebufferBindingState {
|
|
.draw_framebuffer = oldDFboID,
|
|
.read_framebuffer = oldRFboID,
|
|
},
|
|
pp::renderer::gl::OpenGlFramebufferRestoreDispatch {
|
|
.bind_framebuffer = bind_opengl_framebuffer,
|
|
});
|
|
if (!status.ok()) {
|
|
LOG("RTT::unbindFramebuffer() failed because: %s", status.message);
|
|
return;
|
|
}
|
|
|
|
oldRFboID = 0;
|
|
oldDFboID = 0;
|
|
bound = false;
|
|
}
|
|
|
|
void RTT::clear(glm::vec4 color)
|
|
{
|
|
assert(App::I->is_render_thread());
|
|
glClearColor(color.r, color.g, color.b, color.a);
|
|
glClear(static_cast<GLbitfield>(
|
|
pp::renderer::gl::framebuffer_color_buffer_mask()
|
|
| pp::renderer::gl::framebuffer_depth_buffer_mask()));
|
|
}
|
|
|
|
void RTT::clear_mask(glm::bool4 mask, glm::vec4 color)
|
|
{
|
|
assert(App::I->is_render_thread());
|
|
// save old state
|
|
std::array<GLboolean, 4> old_mask;
|
|
glGetBooleanv(static_cast<GLenum>(pp::renderer::gl::color_write_mask_query()), old_mask.data());
|
|
|
|
// clear with mask
|
|
glColorMask(mask.r, mask.g, mask.b, mask.a);
|
|
glClearColor(color.r, color.g, color.b, color.a);
|
|
glClear(static_cast<GLbitfield>(pp::renderer::gl::framebuffer_color_buffer_mask()));
|
|
|
|
// restore old state
|
|
glColorMask(old_mask[0], old_mask[1], old_mask[2], old_mask[3]);
|
|
}
|
|
|
|
glm::ivec4 RTT::calc_bounds() const noexcept
|
|
{
|
|
if (!valid())
|
|
return glm::vec4(0);
|
|
|
|
auto data = std::unique_ptr<glm::u8vec4[]>(reinterpret_cast<glm::u8vec4*>(readTextureData()));
|
|
glm::ivec2 bbmin(w, h);
|
|
glm::ivec2 bbmax(0);
|
|
for (int y = 0; y < h; y++)
|
|
{
|
|
for (int x = 0; x < w; x++)
|
|
{
|
|
if (data[x + y * w].a > 0)
|
|
{
|
|
bbmin = glm::min(bbmin, { x, y });
|
|
bbmax = glm::max(bbmax, { x + 1, y + 1 });
|
|
}
|
|
}
|
|
}
|
|
return { bbmin, bbmax };
|
|
}
|
|
|
|
uint8_t* RTT::readTextureData(uint8_t* buffer) const noexcept
|
|
{
|
|
if (!valid())
|
|
return nullptr;
|
|
if (!buffer)
|
|
buffer = createBuffer();
|
|
App::I->render_task([&]
|
|
{
|
|
const auto readback = pp::renderer::gl::rgba8_readback_format();
|
|
const auto status = pp::renderer::gl::readback_opengl_framebuffer(
|
|
pp::renderer::gl::OpenGlFramebufferReadback {
|
|
.framebuffer = static_cast<std::uint32_t>(fboID),
|
|
.width = w,
|
|
.height = h,
|
|
.format = readback,
|
|
.pixels = buffer,
|
|
},
|
|
pp::renderer::gl::OpenGlFramebufferReadbackDispatch {
|
|
.get_integer = query_opengl_integer,
|
|
.bind_framebuffer = bind_opengl_framebuffer,
|
|
.read_pixels = read_opengl_pixels,
|
|
});
|
|
if (!status.ok())
|
|
LOG("RTT::readTextureData() failed because: %s", status.message);
|
|
});
|
|
return buffer;
|
|
}
|
|
|
|
float* RTT::readTextureDataFloat(float* buffer) const noexcept
|
|
{
|
|
if (!valid())
|
|
return nullptr;
|
|
if (!buffer)
|
|
buffer = createBufferFloat();
|
|
App::I->render_task([&]
|
|
{
|
|
const auto readback = pp::renderer::gl::rgba32f_readback_format();
|
|
const auto status = pp::renderer::gl::readback_opengl_framebuffer(
|
|
pp::renderer::gl::OpenGlFramebufferReadback {
|
|
.framebuffer = static_cast<std::uint32_t>(fboID),
|
|
.width = w,
|
|
.height = h,
|
|
.format = readback,
|
|
.pixels = buffer,
|
|
},
|
|
pp::renderer::gl::OpenGlFramebufferReadbackDispatch {
|
|
.get_integer = query_opengl_integer,
|
|
.bind_framebuffer = bind_opengl_framebuffer,
|
|
.read_pixels = read_opengl_pixels,
|
|
});
|
|
if (!status.ok())
|
|
LOG("RTT::readTextureDataFloat() failed because: %s", status.message);
|
|
});
|
|
return buffer;
|
|
}
|
|
|
|
uint8_t* RTT::createBuffer() const noexcept
|
|
{
|
|
const auto readback = pp::renderer::gl::rgba8_readback_format();
|
|
return new uint8_t[pp::renderer::gl::readback_byte_count(
|
|
readback,
|
|
static_cast<std::uint32_t>(w),
|
|
static_cast<std::uint32_t>(h))];
|
|
}
|
|
|
|
float * RTT::createBufferFloat() const noexcept
|
|
{
|
|
const auto readback = pp::renderer::gl::rgba32f_readback_format();
|
|
return new float[pp::renderer::gl::readback_byte_count(
|
|
readback,
|
|
static_cast<std::uint32_t>(w),
|
|
static_cast<std::uint32_t>(h))
|
|
/ sizeof(float)];
|
|
}
|
|
|
|
void RTT::bindTexture()
|
|
{
|
|
assert(App::I->is_render_thread());
|
|
glBindTexture(texture_2d_target(), texID);
|
|
}
|
|
|
|
void RTT::unbindTexture()
|
|
{
|
|
assert(App::I->is_render_thread());
|
|
glBindTexture(texture_2d_target(), 0);
|
|
}
|
|
|
|
Image RTT::get_image() const noexcept
|
|
{
|
|
Image ret;
|
|
ret.create(w, h, readTextureData());
|
|
return ret;
|
|
}
|
|
|
|
bool RTT::valid() const noexcept
|
|
{
|
|
return texID || rboID || fboID;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
PBO::PBO(PBO&& other) noexcept
|
|
{
|
|
bound_slot = other.bound_slot;
|
|
buffer_id = other.buffer_id;
|
|
mapped_ptr = other.mapped_ptr;
|
|
width = other.width;
|
|
height = other.height;
|
|
other.bound_slot = 0;
|
|
other.buffer_id = 0;
|
|
other.mapped_ptr = nullptr;
|
|
other.width = 0;
|
|
other.height = 0;
|
|
}
|
|
|
|
PBO& PBO::operator=(PBO&& other) noexcept
|
|
{
|
|
bound_slot = other.bound_slot;
|
|
buffer_id = other.buffer_id;
|
|
mapped_ptr = other.mapped_ptr;
|
|
width = other.width;
|
|
height = other.height;
|
|
other.bound_slot = 0;
|
|
other.buffer_id = 0;
|
|
other.mapped_ptr = nullptr;
|
|
other.width = 0;
|
|
other.height = 0;
|
|
return *this;
|
|
}
|
|
|
|
PBO::~PBO() noexcept
|
|
{
|
|
destroy();
|
|
}
|
|
|
|
bool PBO::create() noexcept
|
|
{
|
|
App::I->render_task([this] {
|
|
glGenBuffers(1, &buffer_id);
|
|
});
|
|
return true;
|
|
}
|
|
|
|
bool PBO::create(RTT& rtt) noexcept
|
|
{
|
|
App::I->render_task([this, &rtt] {
|
|
width = rtt.getWidth();
|
|
height = rtt.getHeight();
|
|
const auto readback = pp::renderer::gl::rgba8_readback_format();
|
|
const auto buffer_target = static_cast<GLenum>(pp::renderer::gl::pixel_pack_buffer_target());
|
|
rtt.bindFramebuffer();
|
|
glGenBuffers(1, &buffer_id);
|
|
glBindBuffer(buffer_target, buffer_id);
|
|
glBufferData(
|
|
buffer_target,
|
|
static_cast<GLsizeiptr>(pp::renderer::gl::readback_byte_count(
|
|
readback,
|
|
static_cast<std::uint32_t>(width),
|
|
static_cast<std::uint32_t>(height))),
|
|
0,
|
|
static_cast<GLenum>(pp::renderer::gl::pixel_buffer_stream_read_usage()));
|
|
glReadPixels(
|
|
0,
|
|
0,
|
|
width,
|
|
height,
|
|
static_cast<GLenum>(readback.pixel_format),
|
|
static_cast<GLenum>(readback.component_type),
|
|
0);
|
|
glBindBuffer(buffer_target, 0);
|
|
rtt.unbindFramebuffer();
|
|
});
|
|
return true;
|
|
}
|
|
|
|
void PBO::destroy() noexcept
|
|
{
|
|
if (buffer_id)
|
|
{
|
|
App::I->render_task_async([id=buffer_id] {
|
|
glDeleteBuffers(1, &id);
|
|
});
|
|
buffer_id = 0;
|
|
bound_slot = 0;
|
|
width = 0;
|
|
height = 0;
|
|
mapped_ptr = nullptr;
|
|
}
|
|
}
|
|
|
|
/*
|
|
void PBO::bind_read() noexcept
|
|
{
|
|
App::I->render_task([this] {
|
|
const auto buffer_target = static_cast<GLenum>(pp::renderer::gl::pixel_unpack_buffer_target());
|
|
glBindBuffer(buffer_target, buffer_id);
|
|
bound_slot = buffer_target;
|
|
});
|
|
}
|
|
|
|
void PBO::unbind() noexcept
|
|
{
|
|
App::I->render_task([this] {
|
|
glBindBuffer(bound_slot, buffer_id);
|
|
});
|
|
}
|
|
*/
|
|
|
|
glm::uint8_t* PBO::map() noexcept
|
|
{
|
|
App::I->render_task([this] {
|
|
const auto readback = pp::renderer::gl::rgba8_readback_format();
|
|
const auto buffer_target = static_cast<GLenum>(pp::renderer::gl::pixel_pack_buffer_target());
|
|
glBindBuffer(buffer_target, buffer_id);
|
|
mapped_ptr = (GLubyte*)glMapBufferRange(buffer_target, 0,
|
|
static_cast<GLsizeiptr>(pp::renderer::gl::readback_byte_count(
|
|
readback,
|
|
static_cast<std::uint32_t>(width),
|
|
static_cast<std::uint32_t>(height))),
|
|
static_cast<GLbitfield>(pp::renderer::gl::pixel_buffer_map_read_access()));
|
|
glBindBuffer(buffer_target, 0);
|
|
});
|
|
return mapped_ptr;
|
|
}
|
|
|
|
void PBO::unmap() noexcept
|
|
{
|
|
App::I->render_task([this] {
|
|
const auto buffer_target = static_cast<GLenum>(pp::renderer::gl::pixel_pack_buffer_target());
|
|
glBindBuffer(buffer_target, buffer_id);
|
|
glUnmapBuffer(buffer_target);
|
|
glBindBuffer(buffer_target, 0);
|
|
});
|
|
}
|