From fa49d9ee093692ab318195d9bb3ecac776d15e8e Mon Sep 17 00:00:00 2001 From: omigamedev Date: Sat, 29 Apr 2017 21:30:40 +0100 Subject: [PATCH] save and restore layers image when context is lost in Android --- android/src/main/AndroidManifest.xml | 6 +- android/src/main/cpp/main.cpp | 29 ++++---- engine/app.cpp | 18 ++++- engine/app.h | 1 + engine/canvas.cpp | 103 ++++++++++++++++++++++++++- engine/canvas.h | 46 ++---------- engine/font.h | 1 + engine/layout.cpp | 12 ++++ engine/layout.h | 28 +++++++- engine/pch.cpp | 3 + engine/pch.h | 1 + engine/rtt.cpp | 14 ++-- engine/rtt.h | 1 + engine/shader.cpp | 7 +- engine/shader.h | 1 + engine/texture.cpp | 6 ++ engine/texture.h | 2 +- 17 files changed, 210 insertions(+), 69 deletions(-) diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index f8b0f92..165e683 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -3,8 +3,10 @@ package="com.omigamedev" android:versionCode="1" android:versionName="1.0"> - - + + + app->activity->assetManager; + App::I.data_path = engine->app->activity->externalDataPath; App::I.width = w; App::I.height = h; App::I.init(); @@ -357,7 +358,6 @@ static void engine_term_display(struct engine* engine) { engine->display = EGL_NO_DISPLAY; engine->context = EGL_NO_CONTEXT; engine->surface = EGL_NO_SURFACE; - App::I.terminate(); } /** @@ -366,7 +366,7 @@ static void engine_term_display(struct engine* engine) { static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) { struct engine* engine = (struct engine*)app->userData; int32_t eventType = AInputEvent_getType(event); - LOG("event type: %d", eventType); + //LOG("event type: %d", eventType); switch (eventType) { case AINPUT_EVENT_TYPE_MOTION: // switch (AInputEvent_getSource(event)) { @@ -384,7 +384,7 @@ static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) int ret = -1; for (int i = 0; i < count; i++) { - LOG("pointer %d id %d == %d", i, id, AMotionEvent_getPointerId(event, i)); + //LOG("pointer %d id %d == %d", i, id, AMotionEvent_getPointerId(event, i)); if (AMotionEvent_getPointerId(event, i) == id) ret = i; } @@ -400,7 +400,7 @@ static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) static Pointer p0, p1; static int tracked = 0; //LOG("event source: %d", AInputEvent_getSource(event)); - LOG("pointer id %d count %d", pointer_id, count); + //LOG("pointer id %d count %d", pointer_id, count); MouseEvent e; switch (action) { case AMOTION_EVENT_ACTION_DOWN: @@ -412,12 +412,12 @@ static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) p0.idx = index; App::I.mouse_down(0, x, y); tracked = 1; - LOG("first down"); + //LOG("first down"); return 1; } case AMOTION_EVENT_ACTION_POINTER_DOWN: { - LOG("pointer down index %d", index); + //LOG("pointer down index %d", index); if (count == 2) { float y = AMotionEvent_getY(event, 1); @@ -426,7 +426,7 @@ static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) p1.idx = index; p1.pos = {x, y}; tracked = 2; - LOG("second down"); + //LOG("second down"); App::I.mouse_cancel(0); App::I.gesture_start(p0.pos, p1.pos); } @@ -440,14 +440,14 @@ static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) p0.id = -1; p1.id = -1; App::I.mouse_up(0, x, y); - LOG("first up"); + //LOG("first up"); return 1; } case AMOTION_EVENT_ACTION_POINTER_UP: if (p1.id == AMotionEvent_getPointerId(event, 1)) { p1.id = -1; - LOG("second up"); + //LOG("second up"); App::I.gesture_end(); } return 1; @@ -456,7 +456,7 @@ static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) float y = AMotionEvent_getY(event, 0); float x = AMotionEvent_getX(event, 0); App::I.mouse_move(x, y); - LOG("single move"); + //LOG("single move"); return 1; } case AMOTION_EVENT_ACTION_MOVE: @@ -465,22 +465,22 @@ static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) float y = AMotionEvent_getY(event, 0); float x = AMotionEvent_getX(event, 0); App::I.mouse_move(x, y); - LOG("single move"); + //LOG("single move"); } else if (count == 2) { int idx = findPointer(pointer_id, event); - LOG("pointer move index %d", idx); + //LOG("pointer move index %d", idx); if (p0.idx == idx) { - LOG("first move"); + //LOG("first move"); float y = AMotionEvent_getY(event, 0); float x = AMotionEvent_getX(event, 0); p0.pos = {x, y}; } if (p1.idx == idx) { - LOG("second move"); + //LOG("second move"); float x = AMotionEvent_getX(event, 1); float y = AMotionEvent_getY(event, 1); p1.pos = {x, y}; @@ -528,6 +528,7 @@ static void engine_handle_cmd(struct android_app* app, int32_t cmd) { break; case APP_CMD_TERM_WINDOW: // The window is being hidden or closed, clean it up. + App::I.terminate(); engine_term_display(engine); //exit(0); break; diff --git a/engine/app.cpp b/engine/app.cpp index d87804d..3fa5bb8 100644 --- a/engine/app.cpp +++ b/engine/app.cpp @@ -298,6 +298,7 @@ void App::initLayout() sidebar = layout[main_id]->find("sidebar"); panels = layout[main_id]->find("panels"); canvas = layout[main_id]->find("paint-canvas"); + canvas->data_path = data_path; //brushes = layout[main_id]->find("panel-brush"); //layers = layout[main_id]->find("panel-layer"); @@ -413,6 +414,7 @@ void App::initLayout() //exit(0); if (canvas) { + canvas->m_canvas->snapshot_save(data_path); canvas->m_canvas->m_use_instanced = !canvas->m_canvas->m_use_instanced; //button->color_normal = canvas->m_canvas->m_use_instanced ? glm::vec4(1, 0, 0, 1) : glm::vec4(0, 1, 0, 1); button->m_text->set_text(canvas->m_canvas->m_use_instanced ? "INST" : "NORM"); @@ -500,7 +502,14 @@ void App::initLayout() }; LOG("initializing layout xml"); if (layout.m_loaded) - layout[main_id]->restore_context(); + { + LOG("restore layout"); + layout.restore_context(); + if (panels->get_child_index(brushes.get()) == -1) brushes->restore_context(); + if (panels->get_child_index(layers.get()) == -1) layers->restore_context(); + if (panels->get_child_index(color.get()) == -1) color->restore_context(); + if (panels->get_child_index(stroke.get()) == -1) stroke->restore_context(); + } else layout.load("data/layout.xml"); LOG("initializing layout completed"); @@ -722,5 +731,12 @@ bool App::key_char(char key) void App::terminate() { + LOG("App::terminate"); TextureManager::invalidate(); + ShaderManager::invalidate(); + layout.clear_context(); + brushes->clear_context(); + layers->clear_context(); + color->clear_context(); + stroke->clear_context(); } diff --git a/engine/app.h b/engine/app.h index 2a03ffd..332d910 100644 --- a/engine/app.h +++ b/engine/app.h @@ -11,6 +11,7 @@ class App { public: static App I; + std::string data_path; Sampler sampler; Texture2D tex; LayoutManager layout; diff --git a/engine/canvas.cpp b/engine/canvas.cpp index a5d17ba..0e1218a 100644 --- a/engine/canvas.cpp +++ b/engine/canvas.cpp @@ -362,15 +362,112 @@ bool ui::Canvas::create(int width, int height) m_plane.create<1>(1, 1); m_plane_brush.create<1>(1, 1); m_mesh.create(); + for (auto& l : m_layers) + { + l.create(width, height, ""); + } return true; } -void ui::Canvas::snapshot_save() +void ui::Canvas::snapshot_save(std::string data_path) { - + LOG("SAVE SNAPSHOT"); + m_layers_snapshot.clear(); + m_layers_snapshot.resize(m_layers.size()); + for (int i = 0; i < m_layers.size(); i++) + m_layers_snapshot[i] = m_layers[i].snapshot(data_path); } void ui::Canvas::snapshot_restore() { + LOG("RESTORE SNAPSHOT"); + for (int i = 0; i < m_layers.size(); i++) + m_layers[i].restore(m_layers_snapshot[i]); + m_layers_snapshot.clear(); + LOG("RESTORE SNAPSHOT complete"); +} -}; \ No newline at end of file +void ui::Canvas::clear_context() +{ + LOG("Canvas CLEAR CONTEXT"); + for (auto& layer : m_layers) + layer.destroy(); + for (int i = 0; i < 6; i++) + { + m_tmp[i].destroy(); + m_tex[i].destroy(); + m_tex2[i].destroy(); + } +}; + +/////////////////////////////////////////////////////////////////////////////////////////// + +void ui::Layer::destroy() +{ + for (int i = 0; i < 6; i++) + m_rtt[i].destroy(); +} + +void ui::Layer::restore(const Snapshot& snap) +{ + for (int i = 0; i < 6; i++) + { + if (!snap.image[i]) + continue; + m_rtt[i].create(512, 512); // TODO: this should not be recreated here! Sorry I messed up with this, just quick fix DON'T SHIP!! + m_rtt[i].bindTexture(); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_rtt[i].getWidth(), m_rtt[i].getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, snap.image[i].get()); + m_rtt[i].unbindTexture(); + LOG("restore face %d - %d bytes (%dx%d)", i, m_rtt[i].bytes(), m_rtt[i].getWidth(), m_rtt[i].getHeight()); + } +} + +ui::Layer::Snapshot ui::Layer::snapshot(std::string data_path) +{ + Snapshot snap; + static int counter = 0; + LOG("errno = %d", errno); + glBindTexture(GL_TEXTURE_2D, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + for (int i = 0; i < 6; i++) + { + snap.image[i] = std::make_unique(m_rtt[i].bytes()); + m_rtt[i].readTextureData(snap.image[i].get()); + LOG("snapshot face %d - %d bytes (%dx%d)", i, m_rtt[i].bytes(), m_rtt[i].getWidth(), m_rtt[i].getHeight()); + static char name[128]; + sprintf(name, "%s/Layer%d-%d.png", data_path.c_str(), counter, i); + //int ret = stbi_write_png(name, m_rtt[i].getWidth(), m_rtt[i].getHeight(), 4, snap.image[i].get(), m_rtt[i].stride()); + } + counter++; + return std::move(snap); +} + +void ui::Layer::clear(const glm::vec4& c) +{ + // push clear color state + GLfloat cc[4]; + glGetFloatv(GL_COLOR_CLEAR_VALUE, cc); + glClearColor(c.r, c.g, c.b, c.a); + + for (int i = 0; i < 6; i++) + { + m_rtt[i].bindFramebuffer(); + glClear(GL_COLOR_BUFFER_BIT); + m_rtt[i].unbindFramebuffer(); + } + + // restore clear color state + glClearColor(cc[0], cc[1], cc[2], cc[3]); +} + +bool ui::Layer::create(int width, int height, std::string name) +{ + for (int i = 0; i < 6; i++) + { + m_rtt[i].create(width, height); + m_rtt[i].bindFramebuffer(); + m_rtt[i].clear(); + m_rtt[i].unbindFramebuffer(); + } + return true; +} diff --git a/engine/canvas.h b/engine/canvas.h index 97811c0..b06d962 100644 --- a/engine/canvas.h +++ b/engine/canvas.h @@ -20,44 +20,11 @@ public: { std::unique_ptr image[6]; }; - bool create(int width, int height, std::string name) - { - for (int i = 0; i < 6; i++) - { - m_rtt[i].create(width, height); - m_rtt[i].bindFramebuffer(); - m_rtt[i].clear(); - m_rtt[i].unbindFramebuffer(); - } - return true; - } - void clear(const glm::vec4& c) - { - // push clear color state - GLfloat cc[4]; - glGetFloatv(GL_COLOR_CLEAR_VALUE, cc); - glClearColor(c.r, c.g, c.b, c.a); - - for (int i = 0; i < 6; i++) - { - m_rtt[i].bindFramebuffer(); - glClear(GL_COLOR_BUFFER_BIT); - m_rtt[i].unbindFramebuffer(); - } - - // restore clear color state - glClearColor(cc[0], cc[1], cc[2], cc[3]); - } - Snapshot snapshot() - { - Snapshot snap; - for (int i = 0; i < 6; i++) - { - snap.image[i] = std::make_unique(m_rtt[i].bytes()); - m_rtt[i].readTextureData(snap.image[i].get()); - } - return snap; - } + bool create(int width, int height, std::string name); + void clear(const glm::vec4& c); + Snapshot snapshot(std::string data_path); + void restore(const Snapshot& snap); + void destroy(); }; class Canvas @@ -105,8 +72,9 @@ public: void stroke_end(); void stroke_commit(); void clear(const glm::vec4& color = { 1, 1, 1, 1 }); - void snapshot_save(); + void snapshot_save(std::string data_path); void snapshot_restore(); + void clear_context(); }; diff --git a/engine/font.h b/engine/font.h index 23f5855..eaccdb6 100644 --- a/engine/font.h +++ b/engine/font.h @@ -31,6 +31,7 @@ public: static void init(); static bool load(kFont id, const char* ttf, int sz); static const Font& get(kFont id); + static void invalidate() { m_fonts.clear(); } }; class TextMesh diff --git a/engine/layout.cpp b/engine/layout.cpp index 9bb13cf..c054c2e 100644 --- a/engine/layout.cpp +++ b/engine/layout.cpp @@ -612,3 +612,15 @@ bool LayoutManager::reload() std::string path_copy = m_path; return load(path_copy.c_str()); } + +void LayoutManager::restore_context() +{ + for (auto& node : m_layouts) + node.second->restore_context(); +} + +void LayoutManager::clear_context() +{ + for (auto& node : m_layouts) + node.second->clear_context(); +} diff --git a/engine/layout.h b/engine/layout.h index 0b150f8..098168e 100644 --- a/engine/layout.h +++ b/engine/layout.h @@ -101,6 +101,8 @@ public: auto i = m_layouts.find(id); return i == m_layouts.end() ? nullptr : i->second.get(); } + void restore_context(); + void clear_context(); //Node& operator[](const char* ids) { return m_layouts[const_hash(ids)]; } }; @@ -222,10 +224,13 @@ public: virtual void restore_context() { for (auto& c : m_children) - { c->restore_context(); - } }; + virtual void clear_context() + { + for (auto& c : m_children) + c->clear_context(); + } void update(float width, float height, float zoom); void update(); void update_internal(const glm::vec2& origin, const glm::mat4& proj); @@ -482,6 +487,7 @@ public: { if (!m_path.empty() && TextureManager::load(m_path.c_str())) { + //LOG("load image node %s", m_path.c_str()); auto tex_sz = TextureManager::get(m_tex_id).size(); m_off = m_region.xy / tex_sz; m_sz = (m_region.zw - m_region.xy) / tex_sz; @@ -885,6 +891,7 @@ public: i += strlen(s) + 1; } file.close(); + TextureManager::load("data/spritesheet.png"); } virtual Node* clone_instantiate() const override { return new NodeIcon(); } virtual void clone_copy(Node* dest) const override @@ -1700,7 +1707,13 @@ public: { NodeBorder::restore_context(); init_controls(); - m_rtt.create(m_rtt.getWidth(), m_rtt.getHeight()); + if (m_size.x > 0 && m_size.y > 0) + m_rtt.create(m_size.x, m_size.y); + } + virtual void clear_context() override + { + NodeBorder::clear_context(); + m_rtt.destroy(); } void draw_stroke() { @@ -1856,6 +1869,7 @@ class NodeCanvas : public Node float m_zoom_start; bool method = true; public: + std::string data_path; std::unique_ptr m_canvas; ui::Brush m_brush; Sampler m_sampler; @@ -1876,7 +1890,15 @@ public: Node::restore_context(); m_canvas->create(512, 512); m_sampler.create(); + //m_canvas->clear(); m_face_plane.create<1>(2, 2); + m_canvas->snapshot_restore(); + } + virtual void clear_context() override + { + Node::clear_context(); + m_canvas->snapshot_save(data_path); + m_canvas->clear_context(); } virtual void draw() override { diff --git a/engine/pch.cpp b/engine/pch.cpp index 53b0580..21cf628 100644 --- a/engine/pch.cpp +++ b/engine/pch.cpp @@ -6,6 +6,9 @@ #define STB_IMAGE_IMPLEMENTATION #include +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include + #ifdef DEBUG #pragma comment (lib, "libcurl_debug.lib") #else diff --git a/engine/pch.h b/engine/pch.h index 7215e66..e8d2cd4 100644 --- a/engine/pch.h +++ b/engine/pch.h @@ -73,4 +73,5 @@ #include #include #include +#include #include diff --git a/engine/rtt.cpp b/engine/rtt.cpp index 18ed8bb..e998aa5 100644 --- a/engine/rtt.cpp +++ b/engine/rtt.cpp @@ -30,11 +30,12 @@ void RTT::destroy() { unbindFramebuffer(); glDeleteFramebuffers(1, &fboID); + LOG("RTT DESTROY %d", fboID); } fboID = 0; rboID = 0; - w = 0; - h = 0; +// w = 0; +// h = 0; } bool RTT::create(int width, int height, int tex/* = -1*/) @@ -74,6 +75,7 @@ bool RTT::create(int width, int height, int tex/* = -1*/) // Create a framebuffer object glGenFramebuffers(1, &fboID); glBindFramebuffer(GL_FRAMEBUFFER, fboID); + LOG("RTT CREATE %d - tex %d", fboID, texID); // Attach the texture to FBO color attachment point glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texID, 0); @@ -136,10 +138,12 @@ void RTT::clear(glm::vec4 color) void RTT::readTextureData(uint8_t* buffer) { - bindTexture(); + glReadBuffer(GL_BACK); + bindFramebuffer(); //glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, buffer); - glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, buffer); - unbindTexture(); + glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + unbindFramebuffer(); + glReadBuffer(GL_NONE); } uint8_t* RTT::createBuffer() diff --git a/engine/rtt.h b/engine/rtt.h index 9d9a869..1074cb8 100644 --- a/engine/rtt.h +++ b/engine/rtt.h @@ -27,5 +27,6 @@ public: int getWidth() { return w; } int getHeight() { return h; } int bytes() { return w * h * 4; } + int stride() { return w * 4; } GLuint getFBO() { return fboID; } }; diff --git a/engine/shader.cpp b/engine/shader.cpp index 3db4ae6..426e3d1 100644 --- a/engine/shader.cpp +++ b/engine/shader.cpp @@ -169,4 +169,9 @@ void ShaderManager::u_int(kShaderUniform id, int i) void ui::ShaderManager::u_float(kShaderUniform id, float f) { m_current->u_float(id, f); -} \ No newline at end of file +} + +void ShaderManager::invalidate() +{ + m_shaders.clear(); +} diff --git a/engine/shader.h b/engine/shader.h index a6cf823..f603566 100644 --- a/engine/shader.h +++ b/engine/shader.h @@ -60,6 +60,7 @@ public: static void u_mat4(kShaderUniform id, const glm::mat4& m); static void u_int(kShaderUniform id, int i); static void u_float(kShaderUniform id, float f); + static void invalidate(); }; } \ No newline at end of file diff --git a/engine/texture.cpp b/engine/texture.cpp index ba5e6df..29694b2 100644 --- a/engine/texture.cpp +++ b/engine/texture.cpp @@ -27,6 +27,10 @@ Texture2D& TextureManager::get(uint16_t id) void TextureManager::invalidate() { + for (auto& t : m_textures) + { + t.second.destroy(); + } m_textures.clear(); } @@ -37,6 +41,7 @@ bool Texture2D::create(int width, int height, GLint internal_format, GLint forma m_format = format; m_iformat = internal_format; glGenTextures(1, &m_tex); + LOG("genTex %d", m_tex); bind(); glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, format, GL_UNSIGNED_BYTE, data); unbind(); @@ -60,6 +65,7 @@ void Texture2D::assign(GLuint tex, int w/* = -1*/, int h/* = -1*/, GLuint intern bool Texture2D::load(std::string filename) { + LOG("load texture %s", filename.c_str()); ui::Image img; if (!img.load(filename)) return false; diff --git a/engine/texture.h b/engine/texture.h index df789cc..880fe26 100644 --- a/engine/texture.h +++ b/engine/texture.h @@ -13,7 +13,7 @@ public: bool create(const ui::Image& img); void assign(GLuint tex, int w = -1, int h = -1, GLuint internal_format = GL_RGBA8, GLuint format = GL_RGBA); bool load(std::string filename); - void destroy() { glDeleteTextures(1, &m_tex); } + void destroy() { LOG("TEX destroy %d", m_tex); glDeleteTextures(1, &m_tex); } void bind() const { glBindTexture(GL_TEXTURE_2D, m_tex); } void unbind() const { glBindTexture(GL_TEXTURE_2D, 0); } void update(const uint8_t* data);