#include "pch.h" #include "canvas_layer.h" #include "app.h" #include "rtt.h" uint32_t Layer::s_count = 0; RTT& Layer::rtt(int i, int frame /*= -1*/) { if (frame == -1) frame = m_frame_index; return m_frames[frame].m_rtt[i]; } glm::vec4& Layer::box(int i, int frame /*= -1*/) { if (frame == -1) frame = m_frame_index; return m_frames[frame].m_dirty_box[i]; } bool& Layer::face(int i, int frame /*= -1*/) { if (frame == -1) frame = m_frame_index; return m_frames[frame].m_dirty_face[i]; } LayerFrame& Layer::frame() { return m_frames[m_frame_index]; } TextureCube Layer::gen_cube() { TextureCube ret; App::I->render_task([&] { ret.create(w); ret.bind(); for (int i = 0; i < 6; i++) { rtt(i).bindFramebuffer(); glCopyTexSubImage2D(TextureCube::m_faces_map[i], 0, 0, 0, 0, 0, w, w); rtt(i).unbindFramebuffer(); } }); return ret; } Texture2D Layer::gen_equirect() { Texture2D ret; App::I->render_task([&] { gl_state gl; gl.save(); TextureCube cube; RTT latlong; cube = gen_cube(); latlong.create(w * 4, h * 2); ret.create(w * 4, h * 2); glDisable(GL_BLEND); latlong.bindFramebuffer(); latlong.clear({ 0, 1, 1, 1 }); glViewport(0, 0, latlong.getWidth(), latlong.getHeight()); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_CUBE_MAP, cube.m_cubetex_id); ShaderManager::use(kShader::Equirect); ShaderManager::u_mat4(kShaderUniform::MVP, glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f)); ShaderManager::u_int(kShaderUniform::Tex, 0); Canvas::I->m_sampler.bind(0); Canvas::I->m_plane.draw_fill(); ret.bind(); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, latlong.getWidth(), latlong.getHeight()); latlong.unbindFramebuffer(); latlong.destroy(); cube.destroy(); gl.restore(); }); return ret; } void Layer::destroy() { for (int i = 0; i < 6; i++) rtt(i).destroy(); } void Layer::optimize(int frame /*= -1*/) { if (frame == -1) frame = m_frame_index; int saved_bytes = 0; for (int i = 0; i < 6; i++) { if (!face(i, frame)) continue; auto data = std::unique_ptr(reinterpret_cast(rtt(i, frame).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 }); } } } glm::vec2 bbsz = bbmax - bbmin; glm::vec2 old_size = zw(box(i, frame)) - xy(box(i, frame)); glm::vec2 diff; if (bbsz.x <= 0 || bbmax.y <= 0) { face(i, frame) = false; box(i, frame) = glm::vec4(0); diff = old_size; } else { box(i, frame) = { bbmin, bbmax }; diff = old_size - bbsz; } saved_bytes += (int)(diff.x * diff.y * 4); } LOG("optimized %d bytes", saved_bytes); } void Layer::restore(const Snapshot& snap, int frame /*= -1*/) { if (frame == -1) frame = m_frame_index; clear({ 0, 0, 0, 0 }, frame); for (int i = 0; i < 6; i++) { if (snap.image[i] == nullptr || snap.m_dirty_face[i] == false || box_area(snap.m_dirty_box[i]) <= 0) { box(i, frame) = glm::vec4(snap.width, snap.height, 0, 0); face(i, frame) = false; continue; } box(i, frame) = snap.m_dirty_box[i]; face(i, frame) = snap.m_dirty_face[i]; // TODO: this should not be recreated here! // Sorry I messed up with this, // it's just a quick fix DON'T SHIP!! //m_rtt[i].recreate(); App::I->render_task_async([this, i, &snap, frame] { rtt(i, frame).bindTexture(); glm::vec2 box_sz = zw(box(i, frame)) - xy(box(i, frame)); glTexSubImage2D(GL_TEXTURE_2D, 0, box(i, frame).x, box(i, frame).y, box_sz.x, box_sz.y, GL_RGBA, GL_UNSIGNED_BYTE, snap.image[i].get()); rtt(i, frame).unbindTexture(); LOG("restore frame %d face %d - %d bytes (%dx%d)", frame, i, (int)box_sz.x * (int)box_sz.y * 4, (int)box_sz.x, (int)box_sz.y); }); } App::I->render_sync(); } Layer::Snapshot Layer::snapshot(int frame /*= -1*/, std::array* dirty_box /*= nullptr*/, std::array* dirty_face /*= nullptr*/) { if (frame == -1) frame = m_frame_index; Snapshot snap; snap.width = w; snap.height = h; for (int i = 0; i < 6; i++) { snap.m_dirty_box[i] = dirty_box ? dirty_box->at(i) : box(i, frame); snap.m_dirty_face[i] = dirty_face ? dirty_face->at(i) : face(i, frame); if (!snap.m_dirty_face[i]) continue; snap.image[i] = std::make_unique(rtt(i, frame).bytes()); App::I->render_task_async([this, i, &snap, frame] { rtt(i, frame).bindFramebuffer(); glm::vec2 box_sz = zw(snap.m_dirty_box[i]) - xy(snap.m_dirty_box[i]); glReadPixels(snap.m_dirty_box[i].x, snap.m_dirty_box[i].y, box_sz.x, box_sz.y, GL_RGBA, GL_UNSIGNED_BYTE, snap.image[i].get()); rtt(i, frame).unbindFramebuffer(); }); } App::I->render_sync(); return snap; } void Layer::clear(const glm::vec4& c, int frame /*= -1*/) { if (frame == -1) frame = m_frame_index; m_frames[frame].clear(c); } bool Layer::create(int width, int height, std::string name) { m_name = name; w = width; h = height; m_frame_index = 0; m_frames.clear(); m_frames.emplace_back(); m_frames.back().create(width, height); return true; } bool Layer::add_frame() { m_frames.emplace_back(); return m_frames.back().create(w, h); } int Layer::total_duration() const noexcept { int duration = 0; for (auto& f : m_frames) duration += f.m_duration; return duration; } void Layer::goto_frame(int frame) noexcept { int i = 0; for (i = 0; i < m_frames.size() && frame >= 0; i++) frame -= m_frames[i].m_duration; m_frame_index = i - 1; } void Layer::resize(int width, int height) { w = width; h = height; for (auto& frame : m_frames) frame.resize(width, height); } /////////////////////////////////////////////////////////////////////////////////////////// void Layer::Snapshot::create(int w, int h) { width = w; height = h; for (int i = 0; i < 6; i++) { m_dirty_face[i] = false; m_dirty_box[i] = glm::vec4(width, height, 0, 0); image[i] = std::make_unique(w * h * 4); std::fill_n(image[i].get(), w * h * 4, 0); } } void Layer::Snapshot::clear() { for (int i = 0; i < 6; i++) { m_dirty_face[i] = false; m_dirty_box[i] = glm::vec4(width, height, 0, 0); std::fill_n(image[i].get(), width * height * 4, 0); } } void Layer::Snapshot::optimize() { for (int i = 0; i < 6; i++) { if (!m_dirty_face[i] || !image[i]) continue; auto data = reinterpret_cast(image[i].get()); glm::ivec2 bbmin(width, height); glm::ivec2 bbmax(0); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (data[x + y * width].a > 0) { bbmin = glm::min(bbmin, { x, y }); bbmax = glm::max(bbmax, { x + 1, y + 1 }); } } } //glm::vec2 bbsz = bbmax - bbmin; } } int Layer::Snapshot::memsize() const { int ret = 0; for (int i = 0; i < 6; i++) { if (m_dirty_face[i]) { glm::vec2 sz = zw(m_dirty_box[i]) - xy(m_dirty_box[i]); ret += sz.x * sz.y * 4; } } return ret; } /////////////////////////////////////////////////////////////////////////////////////////// /* LayerFrame::LayerFrame(LayerFrame&& other) { LOG("LayerFrame move-ctor"); for (int i = 0; i < 6; i++) { m_rtt[i] = std::move(other.m_rtt[i]); m_dirty_box[i] = other.m_dirty_box[i]; m_dirty_face[i] = other.m_dirty_face[i]; } m_duration = other.m_duration; w = other.w; h = other.h; } LayerFrame& LayerFrame::operator=(LayerFrame&& other) { LOG("LayerFrame move-assignment"); for (int i = 0; i < 6; i++) { m_rtt[i] = std::move(other.m_rtt[i]); m_dirty_box[i] = other.m_dirty_box[i]; m_dirty_face[i] = other.m_dirty_face[i]; } m_duration = other.m_duration; w = other.w; h = other.h; return *this; } */ bool LayerFrame::create(int width, int height, int duration /*= 1*/) { App::I->render_task([&] { for (int i = 0; i < 6; i++) { if (!m_rtt[i].create(width, height)) return false; m_rtt[i].bindFramebuffer(); m_rtt[i].clear(); m_rtt[i].unbindFramebuffer(); m_dirty_box[i] = glm::vec4(width, height, 0, 0); // reset bounding box m_dirty_face[i] = false; } }); m_duration = duration; w = width; h = height; return true; } bool LayerFrame::resize(int width, int height) { glm::vec2 ratio = glm::vec2(width, height) / glm::vec2(w, h); for (int i = 0; i < 6; i++) { if (!m_rtt[i].resize(width, height)) return false; m_dirty_box[i] = m_dirty_box[i] * glm::vec4(ratio, ratio); } w = width; h = height; return true; } void LayerFrame::clear(const glm::vec4& c) { App::I->render_task([&] { // push clear color state GLfloat cc[4]; glGetFloatv(GL_COLOR_CLEAR_VALUE, cc); glClearColor(c.r, c.g, c.b, c.a); bool erase = (c.a == 0.f); for (int i = 0; i < 6; i++) { m_rtt[i].bindFramebuffer(); glClear(GL_COLOR_BUFFER_BIT); m_rtt[i].unbindFramebuffer(); if (erase) { m_dirty_box[i] = glm::vec4(w, h, 0, 0); // reset bounding box m_dirty_face[i] = false; } else { m_dirty_box[i] = glm::vec4(0, 0, w, h); // reset bounding box m_dirty_face[i] = true; } } // restore clear color state glClearColor(cc[0], cc[1], cc[2], cc[3]); }); }