Files
panopainter/src/rtt.cpp

704 lines
21 KiB
C++

#include "pch.h"
#include "log.h"
#include "rtt.h"
#include "util.h"
#include "app.h"
#include "legacy_gl_framebuffer_dispatch.h"
#include "legacy_gl_pixel_buffer_dispatch.h"
#include "legacy_gl_renderbuffer_dispatch.h"
#include "legacy_gl_texture_dispatch.h"
#include "legacy_ui_gl_dispatch.h"
#include "renderer_gl/opengl_capabilities.h"
#include <cstdint>
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::legacy::gl_framebuffer::framebuffer_blit_dispatch());
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)
{
pp::legacy::gl_renderbuffer::delete_renderbuffer(
static_cast<std::uint32_t>(rboID),
"RTT::destroy renderbuffer delete");
}
if (texID)
{
//unbindTexture();
pp::legacy::gl_texture::delete_texture_2d(
static_cast<std::uint32_t>(texID),
"RTT::destroy texture delete");
//LOG("TEX rtt destroy %d", texID)
}
if (fboID)
{
//unbindFramebuffer();
const auto status = pp::renderer::gl::delete_opengl_framebuffer(
static_cast<std::uint32_t>(fboID),
pp::legacy::gl_framebuffer::framebuffer_delete_dispatch());
if (!status.ok())
LOG("RTT::destroy framebuffer delete failed because: %s", status.message);
//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::legacy::gl_framebuffer::framebuffer_blit_dispatch());
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::legacy::gl_framebuffer::framebuffer_blit_dispatch());
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*/)
{
std::uint32_t status = 0;
App::I->render_task([&]
{
// Destroy any previously created object
destroy();
w = width;
h = height;
int_fmt = internal_format;
if (tex == -1)
{
const auto texture = pp::legacy::gl_texture::allocate_texture_2d(
pp::renderer::gl::OpenGlTexture2DAllocation {
.width = width,
.height = height,
.internal_format = internal_format,
.pixel_format = pp::renderer::gl::rgba_pixel_format(),
.component_type = pp::renderer::gl::texture_upload_type_for_internal_format(
static_cast<std::uint32_t>(internal_format)),
.data = nullptr,
},
"RTT::create texture allocation");
if (texture == 0U)
{
status = 0;
return;
}
texID = static_cast<GLuint>(texture);
//LOG("TEX rtt create %d", texID);
}
else
{
texID = tex;
}
if (!pp::legacy::gl_texture::set_texture_2d_parameters(
static_cast<std::uint32_t>(texID),
pp::renderer::gl::default_render_target_texture_parameters(),
"RTT::create texture parameter setup"))
{
status = 0;
return;
}
// Create a renderbuffer object to store depth info
if (depth_buffer)
{
const auto renderbuffer = pp::legacy::gl_renderbuffer::allocate_depth_renderbuffer(
width,
height,
"RTT::create depth renderbuffer allocation");
if (renderbuffer == 0U)
{
status = 0;
return;
}
rboID = static_cast<GLuint>(renderbuffer);
}
// Create a framebuffer object
const auto framebuffer = pp::renderer::gl::allocate_opengl_render_target_framebuffer(
static_cast<std::uint32_t>(texID),
static_cast<std::uint32_t>(rboID),
pp::legacy::gl_framebuffer::render_target_allocation_dispatch(
pp::legacy::gl_renderbuffer::attach_opengl_framebuffer_renderbuffer));
if (!framebuffer.ok())
{
LOG("RTT::create framebuffer allocation failed because: %s", framebuffer.status().message);
status = 0;
return;
}
fboID = static_cast<GLuint>(framebuffer.value().framebuffer_id);
status = framebuffer.value().framebuffer_status;
//LOG("RTT CREATE %d - tex %d", fboID, texID);
// Check FBO status
if (status != pp::renderer::gl::framebuffer_complete_status())
LOG("RTT::create failed because: %s",
pp::renderer::gl::framebuffer_status_name(status));
oldRFboID = 0;
oldDFboID = 0;
});
return status == 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::legacy::gl_framebuffer::framebuffer_bind_dispatch());
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::legacy::gl_framebuffer::framebuffer_restore_dispatch());
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());
const auto status = pp::renderer::gl::clear_opengl_render_target(
pp::renderer::gl::OpenGlDefaultClear {
.color = { color.r, color.g, color.b, color.a },
.mask = pp::renderer::gl::framebuffer_color_buffer_mask()
| pp::renderer::gl::framebuffer_depth_buffer_mask(),
},
pp::renderer::gl::OpenGlClearDispatch {
.clear_color = pp::legacy::ui_gl::set_opengl_clear_color,
.clear = pp::legacy::ui_gl::clear_opengl_buffer,
});
if (!status.ok())
LOG("RTT::clear() failed because: %s", status.message);
}
void RTT::clear_mask(glm::bool4 mask, glm::vec4 color)
{
assert(App::I->is_render_thread());
const auto status = pp::renderer::gl::clear_opengl_color_buffer_with_write_mask(
pp::renderer::gl::OpenGlColorMaskedClear {
.mask = {
.r = static_cast<std::uint8_t>(mask.r),
.g = static_cast<std::uint8_t>(mask.g),
.b = static_cast<std::uint8_t>(mask.b),
.a = static_cast<std::uint8_t>(mask.a),
},
.color = { color.r, color.g, color.b, color.a },
},
pp::renderer::gl::OpenGlColorMaskedClearDispatch {
.get_boolean = pp::legacy::ui_gl::get_opengl_boolean,
.color_mask = pp::legacy::ui_gl::set_opengl_color_mask,
.clear_color = pp::legacy::ui_gl::set_opengl_clear_color,
.clear = pp::legacy::ui_gl::clear_opengl_buffer,
});
if (!status.ok())
LOG("RTT::clear_mask() failed because: %s", status.message);
}
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 };
}
bool RTT::readPixelsRgba8(int x, int y, int width, int height, void* buffer) const noexcept
{
if (!valid() || buffer == nullptr)
return false;
bool ret = true;
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),
.x = x,
.y = y,
.width = width,
.height = height,
.format = readback,
.pixels = buffer,
},
pp::legacy::gl_framebuffer::framebuffer_readback_dispatch());
if (!status.ok()) {
LOG("RTT::readPixelsRgba8() failed because: %s", status.message);
ret = false;
}
});
return ret;
}
bool RTT::updateRgba8(int x, int y, int width, int height, const void* data) noexcept
{
if (!valid() || data == nullptr)
return false;
if (!pp::legacy::gl_texture::update_texture_2d(
pp::renderer::gl::OpenGlTexture2DUpdate {
.texture_id = texID,
.x = x,
.y = y,
.width = width,
.height = height,
.pixel_format = pp::renderer::gl::rgba_pixel_format(),
.component_type = pp::renderer::gl::unsigned_byte_component_type(),
.data = data,
},
"RTT::updateRgba8()")) {
return false;
}
unbindTexture();
return true;
}
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::legacy::gl_framebuffer::framebuffer_readback_dispatch());
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::legacy::gl_framebuffer::framebuffer_readback_dispatch());
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());
pp::legacy::gl_texture::bind_texture_2d(
static_cast<std::uint32_t>(texID),
"RTT::bindTexture()");
}
void RTT::unbindTexture()
{
assert(App::I->is_render_thread());
pp::legacy::gl_texture::bind_texture_2d(
0U,
"RTT::unbindTexture()");
}
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] {
const auto result = pp::renderer::gl::allocate_opengl_pixel_buffer(
pp::legacy::gl_pixel_buffer::pixel_buffer_allocation_dispatch());
if (!result.ok()) {
LOG("%s", result.status().message);
buffer_id = 0U;
return;
}
buffer_id = result.value();
});
return true;
}
bool PBO::create(RTT& rtt) noexcept
{
App::I->render_task([this, &rtt] {
width = rtt.getWidth();
height = rtt.getHeight();
rtt.bindFramebuffer();
const auto result = pp::renderer::gl::readback_opengl_framebuffer_to_pixel_buffer(
width,
height,
pp::renderer::gl::rgba8_readback_format(),
pp::legacy::gl_pixel_buffer::pixel_buffer_readback_dispatch());
rtt.unbindFramebuffer();
if (!result.ok()) {
LOG("%s", result.status().message);
buffer_id = 0U;
return;
}
buffer_id = result.value();
});
return true;
}
void PBO::destroy() noexcept
{
if (buffer_id)
{
App::I->render_task_async([id=buffer_id] {
const auto status = pp::renderer::gl::delete_opengl_pixel_buffer(
id,
pp::legacy::gl_pixel_buffer::pixel_buffer_delete_dispatch());
if (!status.ok()) {
LOG("%s", status.message);
}
});
buffer_id = 0;
bound_slot = 0;
width = 0;
height = 0;
mapped_ptr = nullptr;
}
}
glm::uint8_t* PBO::map() noexcept
{
App::I->render_task([this] {
const auto result = pp::renderer::gl::map_opengl_pixel_buffer(
buffer_id,
width,
height,
pp::renderer::gl::rgba8_readback_format(),
pp::legacy::gl_pixel_buffer::pixel_buffer_map_dispatch());
if (!result.ok()) {
LOG("%s", result.status().message);
mapped_ptr = nullptr;
return;
}
mapped_ptr = static_cast<glm::uint8_t*>(result.value());
});
return mapped_ptr;
}
void PBO::unmap() noexcept
{
App::I->render_task([this] {
const auto status = pp::renderer::gl::unmap_opengl_pixel_buffer(
buffer_id,
pp::legacy::gl_pixel_buffer::pixel_buffer_unmap_dispatch());
if (!status.ok()) {
LOG("%s", status.message);
}
});
}