From 8e4f77333ea4f4df48e5a4409303baf0906c2647 Mon Sep 17 00:00:00 2001 From: omigamedev Date: Tue, 22 Oct 2019 19:37:55 +0200 Subject: [PATCH] implement load/unload frames --- src/app_layout.cpp | 2 +- src/canvas.cpp | 20 +-- src/canvas.h | 2 +- src/canvas_actions.h | 4 +- src/canvas_layer.cpp | 235 +++++++++++++++++++++++------------ src/canvas_layer.h | 44 ++++--- src/canvas_modes.cpp | 4 +- src/node_canvas.cpp | 2 +- src/node_panel_animation.cpp | 10 +- src/node_panel_layer.cpp | 2 +- src/node_panel_layer.h | 2 +- 11 files changed, 211 insertions(+), 116 deletions(-) diff --git a/src/app_layout.cpp b/src/app_layout.cpp index 38a0509..3fd4aad 100644 --- a/src/app_layout.cpp +++ b/src/app_layout.cpp @@ -1349,7 +1349,7 @@ void App::initLayout() { auto& l = Canvas::I->layer(); l.m_frame_index = (int)glm::clamp( - floor(value * l.m_frames.size()), 0, (int)l.m_frames.size() - 1); + floor(value * l.frames_count()), 0, l.frames_count() - 1); /* auto& c = *Canvas::I; for (int i = 0; i < c.m_layers.size(); i++) diff --git a/src/canvas.cpp b/src/canvas.cpp index 3e5d673..de63d2b 100644 --- a/src/canvas.cpp +++ b/src/canvas.cpp @@ -118,7 +118,7 @@ void Canvas::clear(const glm::vec4& c/*={0,0,0,1}*/) auto a = new ActionLayerClear; a->m_layer = m_layers[m_current_layer_idx]; a->m_frame = layer().m_frame_index; - a->m_snap = std::make_shared(a->m_layer->snapshot()); + a->m_snap = std::make_shared(a->m_layer->snapshot()); a->m_color = c; ActionManager::add(a); @@ -1703,7 +1703,7 @@ void Canvas::import_equirectangular_thread(std::string file_path, std::shared_pt auto a = new ActionImportEquirect; a->m_layer = layer; a->m_frame = frame; - a->m_snap = std::make_shared(layer->snapshot(frame)); + a->m_snap = std::make_shared(layer->snapshot(frame)); a->m_path = file_path; ActionManager::add(a); @@ -2137,7 +2137,7 @@ bool Canvas::project_save_thread(std::string file_path, bool show_progress) fwrite(&n_layers, sizeof(int), 1, fp); int n_frames = std::accumulate(m_layers.begin(), m_layers.end(), 0, - [](int tot, auto& l) { return tot + l->m_frames.size(); }); + [](int tot, auto& l) { return tot + l->frames_count(); }); if (ppi_header.doc_version.minor >= 3) fwrite(&n_frames, sizeof(int), 1, fp); @@ -2166,14 +2166,17 @@ bool Canvas::project_save_thread(std::string file_path, bool show_progress) int frames = 1; if (ppi_header.doc_version.minor >= 3) { - frames = (int)m_layers[i]->m_frames.size(); + frames = (int)m_layers[i]->frames_count(); fwrite(&frames, sizeof(int), 1, fp); } for (int fi = 0; fi < frames; fi++) { if (ppi_header.doc_version.minor >= 3) - fwrite(&m_layers[i]->m_frames[fi].m_duration, sizeof(int), 1, fp); + { + int duration = m_layers[i]->frame_duration(fi); + fwrite(&duration, sizeof(int), 1, fp); + } m_layers[i]->optimize(fi); auto snap = m_layers[i]->snapshot(fi); @@ -2315,7 +2318,7 @@ bool Canvas::project_open_thread(std::string file_path) fread(&n_frames, sizeof(int), 1, fp); const int bytes = m_width * m_height * 4; - Layer::Snapshot snap; + LayerFrame::Snapshot snap; snap.create(m_width, m_height); // allocate single data, no box should be bigger int progress = 0; @@ -2365,7 +2368,10 @@ bool Canvas::project_open_thread(std::string file_path) if (fi > 0) layer->add_frame(); if (ppi_header.doc_version.minor >= 3) - fread(&layer->m_frames[fi].m_duration, sizeof(int), 1, fp); + { + int duration = layer->frame_duration(fi); + fread(&duration, sizeof(int), 1, fp); + } snap.clear(); for (int plane_index = 0; plane_index < 6; plane_index++) { diff --git a/src/canvas.h b/src/canvas.h index 091eefa..e68d338 100644 --- a/src/canvas.h +++ b/src/canvas.h @@ -173,7 +173,7 @@ public: I->on_mode_changed(prev, mode); } - std::vector m_layers_snapshot; + std::vector m_layers_snapshot; Canvas() { I = this; } ~Canvas() { destroy(); } diff --git a/src/canvas_actions.h b/src/canvas_actions.h index d973fe8..b7c3070 100644 --- a/src/canvas_actions.h +++ b/src/canvas_actions.h @@ -22,7 +22,7 @@ public: struct ActionLayerClear : public Action { - std::shared_ptr m_snap; + std::shared_ptr m_snap; std::shared_ptr m_layer; int m_frame = 0; glm::vec4 m_color; @@ -34,7 +34,7 @@ struct ActionLayerClear : public Action struct ActionImportEquirect : public Action { - std::shared_ptr m_snap; + std::shared_ptr m_snap; std::shared_ptr m_layer; int m_frame = 0; std::string m_path; diff --git a/src/canvas_layer.cpp b/src/canvas_layer.cpp index 8b93861..9f66b77 100644 --- a/src/canvas_layer.cpp +++ b/src/canvas_layer.cpp @@ -9,6 +9,7 @@ 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]; } @@ -145,71 +146,22 @@ void Layer::optimize(int frame /*= -1*/) LOG("optimized %d bytes", saved_bytes); } -void Layer::restore(const Snapshot& snap, int frame /*= -1*/) +void Layer::restore(const LayerFrame::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(); + auto loaded = m_frames[frame].gpu_load(); + m_frames[frame].restore(snap); + if (!loaded) m_frames[frame].gpu_unload(); } -Layer::Snapshot Layer::snapshot(int frame /*= -1*/, std::array* dirty_box /*= nullptr*/, std::array* dirty_face /*= nullptr*/) +LayerFrame::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(); + 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; } @@ -229,24 +181,45 @@ bool Layer::create(int width, int height, std::string name) m_frames.clear(); m_frames.emplace_back(); m_frames.back().create(width, height); + frames_gpu_update(); return true; } bool Layer::add_frame() { m_frames.emplace_back(); - return m_frames.back().create(w, h); + m_frames.back().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) { 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::total_duration() const noexcept @@ -263,6 +236,8 @@ void Layer::goto_frame(int frame) noexcept 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) @@ -275,7 +250,7 @@ void Layer::resize(int width, int height) /////////////////////////////////////////////////////////////////////////////////////////// -void Layer::Snapshot::create(int w, int h) +void LayerFrame::Snapshot::create(int w, int h) { width = w; height = h; @@ -288,7 +263,7 @@ void Layer::Snapshot::create(int w, int h) } } -void Layer::Snapshot::clear() +void LayerFrame::Snapshot::clear() { for (int i = 0; i < 6; i++) { @@ -298,7 +273,7 @@ void Layer::Snapshot::clear() } } -void Layer::Snapshot::optimize() +void LayerFrame::Snapshot::optimize() { for (int i = 0; i < 6; i++) { @@ -322,7 +297,7 @@ void Layer::Snapshot::optimize() } } -int Layer::Snapshot::memsize() const +int LayerFrame::Snapshot::memsize() const { int ret = 0; for (int i = 0; i < 6; i++) @@ -372,22 +347,28 @@ LayerFrame& LayerFrame::operator=(LayerFrame&& other) bool LayerFrame::create(int width, int height, int duration /*= 1*/) { bool success = true; - App::I->render_task([&] + //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++) { - 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; - } - }); + 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; @@ -396,6 +377,7 @@ bool LayerFrame::create(int width, int height, int duration /*= 1*/) 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++) { @@ -405,6 +387,7 @@ bool LayerFrame::resize(int width, int height) } w = width; h = height; + if (!loaded) gpu_unload(); return true; } @@ -442,17 +425,113 @@ void LayerFrame::clear(const glm::vec4& c) }); } -LayerFrame LayerFrame::clone() const noexcept +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(GL_TEXTURE_2D, 0, + m_dirty_box[i].x, m_dirty_box[i].y, + box_sz.x, box_sz.y, GL_RGBA, GL_UNSIGNED_BYTE, + 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(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()); + 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; +} diff --git a/src/canvas_layer.h b/src/canvas_layer.h index 527cdf1..a9bb5e9 100644 --- a/src/canvas_layer.h +++ b/src/canvas_layer.h @@ -5,9 +5,23 @@ struct LayerFrame { + struct Snapshot + { + std::unique_ptr image[6] = SIXPLETTE(0); + std::array m_dirty_box = SIXPLETTE(glm::vec4(0)); + std::array m_dirty_face = SIXPLETTE(false); + int width = 0; + int height = 0; + void create(int w, int h); + void clear(); + void optimize(); + int memsize() const; + }; + std::array m_rtt; std::array m_dirty_box = SIXPLETTE(glm::vec4(0)); std::array m_dirty_face = SIXPLETTE(false); + std::unique_ptr m_gpu_data; int m_duration = 1; int w = 0, h = 0; @@ -23,17 +37,23 @@ struct LayerFrame bool create(int width, int height, int duration = 1); bool resize(int width, int height); void clear(const glm::vec4& c); - LayerFrame clone() const noexcept; + LayerFrame clone() noexcept; + // return previous state + bool gpu_load() noexcept; + // return if succesfully loaded on gpu + bool gpu_unload() noexcept; + Snapshot snapshot(std::array* dirty_box = nullptr, std::array* dirty_face = nullptr); + void restore(const Snapshot& snap); }; class Layer { static uint32_t s_count; + std::vector m_frames; public: Layer() { id = s_count++; } Layer(const Layer&) = delete; uint32_t id; - std::vector m_frames; int m_frame_index = 0; bool m_visible = true; bool m_alpha_locked = false; @@ -43,18 +63,6 @@ public: std::string m_name; int w = 0; int h = 0; - struct Snapshot - { - std::unique_ptr image[6] = SIXPLETTE(0); - std::array m_dirty_box = SIXPLETTE(glm::vec4(0)); - std::array m_dirty_face = SIXPLETTE(false); - int width = 0; - int height = 0; - void create(int w, int h); - void clear(); - void optimize(); - int memsize() const; - }; RTT& rtt(int i, int frame = -1); glm::vec4& box(int i, int frame = -1); bool& face(int i, int frame = -1); @@ -64,13 +72,17 @@ public: bool add_frame(); void remove_frame(int frame); void duplicate_frame(int frame); + void frames_gpu_update(); + int frames_count() const noexcept { return m_frames.size(); } + int frame_duration(int frame) const noexcept { return m_frames[frame].m_duration; } + void set_frame_duration(int frame, int duration) noexcept { m_frames[frame].m_duration = duration; } int total_duration() const noexcept; void goto_frame(int frame) noexcept; void clear(const glm::vec4& c, int frame = -1); - Snapshot snapshot(int frame = -1, std::array* dirty_box = nullptr, std::array* dirty_face = nullptr); + LayerFrame::Snapshot snapshot(int frame = -1, std::array* dirty_box = nullptr, std::array* dirty_face = nullptr); TextureCube gen_cube(); Texture2D gen_equirect(); - void restore(const Snapshot& snap, int frame = -1); + void restore(const LayerFrame::Snapshot& snap, int frame = -1); void destroy(); void optimize(int frame = -1); }; diff --git a/src/canvas_modes.cpp b/src/canvas_modes.cpp index 26912b6..1a0fa72 100644 --- a/src/canvas_modes.cpp +++ b/src/canvas_modes.cpp @@ -1611,7 +1611,7 @@ void CanvasModeFloodFill::on_MouseEvent(MouseEvent* me, glm::vec2& loc) struct ActionFloodFill : public Action { std::shared_ptr m_layer; - std::shared_ptr m_snap; + std::shared_ptr m_snap; std::array m_dirty_face = SIXPLETTE(false); std::array m_dirty_box = SIXPLETTE(glm::vec4(0)); glm::ivec2 m_pos; @@ -1670,7 +1670,7 @@ void CanvasModeFloodFill::on_MouseEvent(MouseEvent* me, glm::vec2& loc) auto a = new ActionFloodFill; a->m_direction = Action::Direction::Undo; a->m_layer = plane_data.layer; - a->m_snap = std::make_shared(plane_data.layer->snapshot()); + a->m_snap = std::make_shared(plane_data.layer->snapshot()); a->m_pos = (glm::ivec2)pos; a->m_color = Canvas::I->m_current_brush->m_tip_color; a->m_layer_index = Canvas::I->m_current_layer_idx; diff --git a/src/node_canvas.cpp b/src/node_canvas.cpp index 0741cef..8a92ed3 100644 --- a/src/node_canvas.cpp +++ b/src/node_canvas.cpp @@ -217,7 +217,7 @@ void NodeCanvas::draw() int onion_size = App::I->animation->get_onion_size(); int frame_current = m_canvas->m_layers[layer_index]->m_frame_index; int frame_start = glm::max(frame_current - onion_size, 0); - int frame_end = glm::min(frame_current + onion_size, m_canvas->m_layers[layer_index]->m_frames.size() - 1); + int frame_end = glm::min(frame_current + onion_size, m_canvas->m_layers[layer_index]->frames_count() - 1); bool faces = false; for (int frame = frame_start; frame <= frame_end; frame++) faces |= m_canvas->m_layers[layer_index]->face(plane_index, frame); diff --git a/src/node_panel_animation.cpp b/src/node_panel_animation.cpp index 111bff4..16eb7aa 100644 --- a/src/node_panel_animation.cpp +++ b/src/node_panel_animation.cpp @@ -55,14 +55,12 @@ void NodePanelAnimation::init_controls() }; btn_up->on_click = [this](Node*) { if (auto layer = Canvas::I->layer_with_id(m_selected_frame_layer_id)) - layer->m_frames[m_selected_frame_index].m_duration = - glm::max(layer->m_frames[m_selected_frame_index].m_duration + 1, 1); + layer->set_frame_duration(m_selected_frame_index, glm::max(layer->frame_duration(m_selected_frame_index) + 1, 1)); load_layers(); }; btn_down->on_click = [this](Node*) { if (auto layer = Canvas::I->layer_with_id(m_selected_frame_layer_id)) - layer->m_frames[m_selected_frame_index].m_duration = - glm::max(layer->m_frames[m_selected_frame_index].m_duration - 1, 1); + layer->set_frame_duration(m_selected_frame_index, glm::max(layer->frame_duration(m_selected_frame_index) - 1, 1)); load_layers(); }; @@ -120,9 +118,9 @@ void NodePanelAnimation::load_layers() l->set_selected(Canvas::I->m_current_layer_idx == i); l->set_chekcbox(layers[i]->m_visible); auto film = m_frames_container->add_child_ref(); - for (int fi = 0; fi < layers[i]->m_frames.size(); fi++) + for (int fi = 0; fi < layers[i]->frames_count(); fi++) { - auto b = film->add_frame(layers[i]->m_frames[fi].m_duration); + auto b = film->add_frame(layers[i]->frame_duration(fi)); if (m_selected_frame_layer_id == layers[i]->id && m_selected_frame_index == fi) { diff --git a/src/node_panel_layer.cpp b/src/node_panel_layer.cpp index a8c484f..12a6989 100644 --- a/src/node_panel_layer.cpp +++ b/src/node_panel_layer.cpp @@ -408,7 +408,7 @@ void NodePanelLayer::merge(int src_index, int dst_index, bool create_history) a->m_dirty_box[i] = Canvas::I->m_layers[dst_index]->box(i); a->m_dirty_face[i] = Canvas::I->m_layers[dst_index]->face(i); } - a->m_snap = std::make_shared(); + a->m_snap = std::make_shared(); *a->m_snap = Canvas::I->m_layers[dst_index]->snapshot(-1, &Canvas::I->m_layers[src_index]->frame().m_dirty_box, &Canvas::I->m_layers[src_index]->frame().m_dirty_face); a->m_layer = Canvas::I->m_layers[src_index]; diff --git a/src/node_panel_layer.h b/src/node_panel_layer.h index 41de78b..3708069 100644 --- a/src/node_panel_layer.h +++ b/src/node_panel_layer.h @@ -136,7 +136,7 @@ public: struct ActionLayerMerge : public Action { - std::shared_ptr m_snap; + std::shared_ptr m_snap; std::shared_ptr m_layer; std::shared_ptr m_layer_node; std::shared_ptr m_panel;