#include "pch.h" #include "canvas_layer.h" #include "app.h" #include "renderer_gl/opengl_capabilities.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; assert(m_frames[frame].m_rtt[i].valid()); 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(int frame /*= -1*/) { if (frame == -1) frame = m_frame_index; return m_frames[frame]; } TextureCube Layer::gen_cube() { TextureCube ret; ret.create(w); for (int i = 0; i < 6; i++) { App::I->render_task([&] { ret.bind(); rtt(i).bindFramebuffer(); glCopyTexSubImage2D(pp::renderer::gl::cube_face_texture_target(i), 0, 0, 0, 0, 0, w, w); rtt(i).unbindFramebuffer(); }); } return ret; } Texture2D Layer::gen_equirect(glm::ivec2 size /*= { 0, 0 }*/) { Texture2D ret; App::I->render_task([&] { gl_state gl; gl.save(); TextureCube cube; RTT latlong; if (size.x == 0 || size.y == 0) size = { w, h }; cube = gen_cube(); latlong.create(size.x * 4, size.y * 2); ret.create(size.x * 4, size.y * 2); glDisable(pp::renderer::gl::blend_state()); latlong.bindFramebuffer(); //latlong.clear({ 0, 1, 1, 1 }); glViewport(0, 0, latlong.getWidth(), latlong.getHeight()); glActiveTexture(pp::renderer::gl::active_texture_unit(0U)); glBindTexture(pp::renderer::gl::texture_cube_map_target(), 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(pp::renderer::gl::texture_2d_target(), 0, 0, 0, 0, 0, latlong.getWidth(), latlong.getHeight()); latlong.unbindFramebuffer(); latlong.destroy(); cube.destroy(); gl.restore(); }); return ret; } PBO Layer::gen_equirect_pbo(glm::ivec2 size /*= { 0, 0 }*/) { if (size.x == 0 || size.y == 0) size = { w, h }; PBO pbo; TextureCube cube = gen_cube(); std::this_thread::yield(); RTT latlong; latlong.create(size.x, size.y); std::this_thread::yield(); App::I->render_task([&] { glDisable(pp::renderer::gl::blend_state()); latlong.bindFramebuffer(); glViewport(0, 0, latlong.getWidth(), latlong.getHeight()); glActiveTexture(pp::renderer::gl::active_texture_unit(0U)); glBindTexture(pp::renderer::gl::texture_cube_map_target(), 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(); latlong.unbindFramebuffer(); }); std::this_thread::yield(); pbo.create(latlong); std::this_thread::yield(); latlong.destroy(); cube.destroy(); return pbo; } 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 LayerFrame::Snapshot& snap, int frame /*= -1*/) { if (frame == -1) frame = m_frame_index; auto loaded = m_frames[frame].gpu_load(); m_frames[frame].restore(snap); if (!loaded) m_frames[frame].gpu_unload(); } LayerFrame::Snapshot Layer::snapshot(int frame /*= -1*/, std::array* dirty_box /*= nullptr*/, std::array* dirty_face /*= nullptr*/) { if (frame == -1) frame = m_frame_index; auto loaded = m_frames[frame].gpu_load(); auto snap = m_frames[frame].snapshot(dirty_box, dirty_face); if (!loaded) m_frames[frame].gpu_unload(); 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); frames_gpu_update(); return true; } bool Layer::add_frame(int index /*= -1*/) { auto pos = index == -1 ? m_frames.end() : m_frames.begin() + index; auto it = m_frames.emplace(pos); it->create(w, h); frames_gpu_update(); return true; } void Layer::remove_frame(int frame) { m_frames.erase(m_frames.begin() + frame); m_frame_index = glm::clamp(m_frame_index, 0, (int)m_frames.size() - 1); frames_gpu_update(); } void Layer::duplicate_frame(int frame) { if (frame == -1) frame = m_frame_index; m_frames.insert(m_frames.begin() + frame + 1, m_frames[frame].clone()); frames_gpu_update(); } void Layer::frames_gpu_update() { App::I->render_task_async([=] { for (int j = 0; j < m_frames.size(); j++) { int dist = glm::abs(j - m_frame_index); int onion = App::I->animation ? App::I->animation->get_onion_size() : 1; if (dist <= onion) m_frames[j].gpu_load(); else m_frames[j].gpu_unload(); } }); } int Layer::move_frame_offset(int frame, int offset) noexcept { int new_pos = glm::clamp(frame + offset, 0, (int)m_frames.size() - 1); auto from = m_frames.begin() + frame; auto to = m_frames.begin() + new_pos; if (new_pos < frame) std::rotate(to, from, from + 1); if (new_pos > frame) std::rotate(from, from + 1, to + 1); frames_gpu_update(); return new_pos; } 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; m_frames[m_frame_index].gpu_load(); frames_gpu_update(); } void Layer::resize(int width, int height) { w = width; h = height; for (auto& frame : m_frames) frame.resize(width, height); } /////////////////////////////////////////////////////////////////////////////////////////// void LayerFrame::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 LayerFrame::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 LayerFrame::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 LayerFrame::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*/) { bool success = true; //App::I->render_task([&] //{ // for (int i = 0; i < 6; i++) // { // if (!m_rtt[i].create(width, height)) // { // success = false; // return; // } // 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; // } //}); for (int i = 0; i < 6; i++) { m_dirty_box[i] = glm::vec4(width, height, 0, 0); // reset bounding box m_dirty_face[i] = false; } m_gpu_data = nullptr; m_duration = duration; w = width; h = height; return success; } bool LayerFrame::resize(int width, int height) { bool loaded = gpu_load(); 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; if (!loaded) gpu_unload(); return true; } void LayerFrame::clear(const glm::vec4& c) { App::I->render_task([&] { // push clear color state GLfloat cc[4]; glGetFloatv(pp::renderer::gl::color_clear_value_query(), 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(pp::renderer::gl::framebuffer_color_buffer_mask()); 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]); }); } LayerFrame LayerFrame::clone() noexcept { LayerFrame dup; dup.m_duration = m_duration; dup.w = w; dup.h = h; bool loaded = gpu_load(); for (int i = 0; i < 6; i++) { dup.m_rtt[i] = m_rtt[i].clone(); dup.m_dirty_box[i] = m_dirty_box[i]; dup.m_dirty_face[i] = m_dirty_face[i]; } if (!loaded) gpu_unload(); return dup; } void LayerFrame::restore(const Snapshot& snap) { App::I->render_task([this, &snap] { clear({ 0, 0, 0, 0 }); for (int i = 0; i < 6; i++) { if (!m_rtt[i].valid()) m_rtt[i].create(w, h); if (snap.image[i] == nullptr || snap.m_dirty_face[i] == false || box_area(snap.m_dirty_box[i]) <= 0) { m_dirty_box[i] = glm::vec4(snap.width, snap.height, 0, 0); m_dirty_face[i] = false; continue; } m_dirty_box[i] = snap.m_dirty_box[i]; m_dirty_face[i] = 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(); m_rtt[i].bindTexture(); glm::vec2 box_sz = zw(m_dirty_box[i]) - xy(m_dirty_box[i]); glTexSubImage2D(pp::renderer::gl::texture_2d_target(), 0, static_cast(m_dirty_box[i].x), static_cast(m_dirty_box[i].y), static_cast(box_sz.x), static_cast(box_sz.y), pp::renderer::gl::rgba_pixel_format(), pp::renderer::gl::unsigned_byte_component_type(), snap.image[i].get()); m_rtt[i].unbindTexture(); LOG("restore face %d - %d bytes (%dx%d)", i, (int)box_sz.x * (int)box_sz.y * 4, (int)box_sz.x, (int)box_sz.y); } }); } LayerFrame::Snapshot LayerFrame::snapshot(std::array* dirty_box /*= nullptr*/, std::array* dirty_face /*= nullptr*/) { Snapshot snap; snap.width = w; snap.height = h; App::I->render_task([this, &snap, dirty_face, dirty_box] { for (int i = 0; i < 6; i++) { snap.m_dirty_box[i] = dirty_box ? dirty_box->at(i) : m_dirty_box[i]; snap.m_dirty_face[i] = dirty_face ? dirty_face->at(i) : m_dirty_face[i]; if (!snap.m_dirty_face[i] || !m_rtt[i].valid()) continue; snap.image[i] = std::make_unique(m_rtt[i].bytes()); m_rtt[i].bindFramebuffer(); glm::vec2 box_sz = zw(snap.m_dirty_box[i]) - xy(snap.m_dirty_box[i]); glReadPixels(static_cast(snap.m_dirty_box[i].x), static_cast(snap.m_dirty_box[i].y), static_cast(box_sz.x), static_cast(box_sz.y), pp::renderer::gl::rgba_pixel_format(), pp::renderer::gl::unsigned_byte_component_type(), snap.image[i].get()); m_rtt[i].unbindFramebuffer(); } }); return snap; } bool LayerFrame::gpu_load() noexcept { if (m_gpu_data) { restore(*m_gpu_data); } else { for (auto& rtt : m_rtt) if (!rtt.valid()) rtt.create(w, h); } m_gpu_data.reset(); return true; } bool LayerFrame::gpu_unload() noexcept { bool ret = false; // already unloaded from gpu if (!m_gpu_data) { m_gpu_data = std::make_unique(snapshot()); ret = true; // previous state was loaded on gpu } for (auto& rtt : m_rtt) rtt.destroy(); return ret; }