From 53c2fac5d4f25e311cc0455666797a87397898d9 Mon Sep 17 00:00:00 2001 From: Simon Fels Date: Fri, 25 Nov 2016 07:14:20 +0100 Subject: [PATCH] Improve window/layer handling for less flickering and better window movement --- src/anbox/graphics/emugl/Renderable.cpp | 5 +++ src/anbox/graphics/emugl/Renderable.h | 2 + src/anbox/graphics/emugl/Renderer.cpp | 12 +++--- src/anbox/graphics/emugl/Renderer.h | 2 +- src/anbox/graphics/layer_composer.cpp | 40 ++++++++++++++++++- src/anbox/graphics/rect.cpp | 10 +++++ src/anbox/graphics/rect.h | 2 + src/anbox/ubuntu/platform_policy.cpp | 8 ++-- src/anbox/ubuntu/platform_policy.h | 2 +- src/anbox/ubuntu/window.h | 9 +++-- src/anbox/wm/default_platform_policy.cpp | 17 ++++++-- src/anbox/wm/default_platform_policy.h | 2 +- src/anbox/wm/manager.cpp | 50 +++++++++++++++++------- src/anbox/wm/platform_policy.h | 2 +- src/anbox/wm/window.cpp | 49 +++++++++++------------ src/anbox/wm/window.h | 20 +++++----- 16 files changed, 163 insertions(+), 69 deletions(-) diff --git a/src/anbox/graphics/emugl/Renderable.cpp b/src/anbox/graphics/emugl/Renderable.cpp index f11f7be..232f3a1 100644 --- a/src/anbox/graphics/emugl/Renderable.cpp +++ b/src/anbox/graphics/emugl/Renderable.cpp @@ -57,3 +57,8 @@ float Renderable::alpha() const { return alpha_; } + +void Renderable::set_screen_position(const anbox::graphics::Rect &screen_position) +{ + screen_position_ = screen_position; +} diff --git a/src/anbox/graphics/emugl/Renderable.h b/src/anbox/graphics/emugl/Renderable.h index fd6874d..819f1b7 100644 --- a/src/anbox/graphics/emugl/Renderable.h +++ b/src/anbox/graphics/emugl/Renderable.h @@ -42,6 +42,8 @@ public: glm::mat4 transformation() const; float alpha() const; + void set_screen_position(const anbox::graphics::Rect &screen_position); + private: std::string name_; std::uint32_t buffer_; diff --git a/src/anbox/graphics/emugl/Renderer.cpp b/src/anbox/graphics/emugl/Renderer.cpp index 8f09af1..3e26991 100644 --- a/src/anbox/graphics/emugl/Renderer.cpp +++ b/src/anbox/graphics/emugl/Renderer.cpp @@ -441,7 +441,7 @@ Renderer::Program::Program(GLuint program_id) position_attr = s_gles2.glGetAttribLocation(id, "position"); texcoord_attr = s_gles2.glGetAttribLocation(id, "texcoord"); tex_uniform = s_gles2.glGetUniformLocation(id, "tex"); - centre_uniform = s_gles2.glGetUniformLocation(id, "centre"); + center_uniform = s_gles2.glGetUniformLocation(id, "centre"); display_transform_uniform = s_gles2.glGetUniformLocation(id, "display_transform"); transform_uniform = s_gles2.glGetUniformLocation(id, "transform"); screen_to_gl_coords_uniform = s_gles2.glGetUniformLocation(id, "screen_to_gl_coords"); @@ -985,10 +985,10 @@ const GLchar* const Renderer::vshader = "uniform mat4 screen_to_gl_coords;\n" "uniform mat4 display_transform;\n" "uniform mat4 transform;\n" - "uniform vec2 centre;\n" + "uniform vec2 center;\n" "varying vec2 v_texcoord;\n" "void main() {\n" - " vec4 mid = vec4(centre, 0.0, 0.0);\n" + " vec4 mid = vec4(center, 0.0, 0.0);\n" " vec4 transformed = (transform * (vec4(position, 1.0) - mid)) + mid;\n" " gl_Position = display_transform * screen_to_gl_coords * transformed;\n" " v_texcoord = texcoord;\n" @@ -1103,11 +1103,11 @@ void Renderer::draw(RendererWindow *window, const Renderable &renderable, const s_gles2.glActiveTexture(GL_TEXTURE0); auto const& rect = renderable.screen_position(); - GLfloat centrex = rect.left() + + GLfloat centerx = rect.left() + rect.width() / 2.0f; - GLfloat centrey = rect.top() + + GLfloat centery = rect.top() + rect.height() / 2.0f; - s_gles2.glUniform2f(prog.centre_uniform, centrex, centrey); + s_gles2.glUniform2f(prog.center_uniform, centerx, centery); s_gles2.glUniformMatrix4fv(prog.transform_uniform, 1, GL_FALSE, glm::value_ptr(renderable.transformation())); diff --git a/src/anbox/graphics/emugl/Renderer.h b/src/anbox/graphics/emugl/Renderer.h index 525df05..b0bc2c2 100644 --- a/src/anbox/graphics/emugl/Renderer.h +++ b/src/anbox/graphics/emugl/Renderer.h @@ -303,7 +303,7 @@ private: GLint tex_uniform = -1; GLint position_attr = -1; GLint texcoord_attr = -1; - GLint centre_uniform = -1; + GLint center_uniform = -1; GLint display_transform_uniform = -1; GLint transform_uniform = -1; GLint screen_to_gl_coords_uniform = -1; diff --git a/src/anbox/graphics/layer_composer.cpp b/src/anbox/graphics/layer_composer.cpp index a0cb6c1..9d31c3f 100644 --- a/src/anbox/graphics/layer_composer.cpp +++ b/src/anbox/graphics/layer_composer.cpp @@ -57,7 +57,45 @@ void LayerComposer::submit_layers(const RenderableList &renderables) } for (const auto &w : win_layers) - Renderer::get()->draw(w.first->native_handle(), w.first->state().frame(), w.second); + { + const auto &window = w.first; + const auto &renderables = w.second; + RenderableList final_renderables; + auto new_window_frame = Rect::Invalid; + + // As we get absolute display coordinates from the Android hwcomposer we + // need to recalculate all layer coordinates into relatives ones to the + // window they are drawn into. + for (auto &r : renderables) + { + if (new_window_frame == Rect::Invalid) + new_window_frame = r.screen_position(); + else + new_window_frame.merge(r.screen_position()); + } + + for (auto &r : renderables) + { + auto left = r.screen_position().left() - new_window_frame.left(); + auto top = r.screen_position().top() - new_window_frame.top(); + + auto rect = Rect{ + left, top, + r.screen_position().width() + left, + r.screen_position().height() + top, + }; + + auto new_renderable = r; + new_renderable.set_screen_position(rect); + final_renderables.push_back(new_renderable); + } + + w.first->update_frame(new_window_frame); + + Renderer::get()->draw(window->native_handle(), + Rect{0, 0, window->frame().width(), window->frame().height()}, + final_renderables); + } } } // namespace graphics } // namespace anbox diff --git a/src/anbox/graphics/rect.cpp b/src/anbox/graphics/rect.cpp index 62d3e75..e8cbb13 100644 --- a/src/anbox/graphics/rect.cpp +++ b/src/anbox/graphics/rect.cpp @@ -17,9 +17,19 @@ #include "anbox/graphics/rect.h" +#include + namespace anbox { namespace graphics { const Rect Rect::Invalid{-1,-1,-1,-1}; const Rect Rect::Empty{0,0,0,0}; + +void Rect::merge(const Rect &rhs) +{ + left_ = std::min(left_, rhs.left()); + top_ = std::min(top_, rhs.top()); + right_ = std::max(right_, rhs.right()); + bottom_ = std::max(bottom_, rhs.bottom()); +} } // namespace graphics } // namespace anbox diff --git a/src/anbox/graphics/rect.h b/src/anbox/graphics/rect.h index a9c994e..15949c2 100644 --- a/src/anbox/graphics/rect.h +++ b/src/anbox/graphics/rect.h @@ -89,6 +89,8 @@ public: return !operator == (rhs); } + void merge(const Rect &rhs); + private: std::int32_t left_; std::int32_t top_; diff --git a/src/anbox/ubuntu/platform_policy.cpp b/src/anbox/ubuntu/platform_policy.cpp index b415a03..0045b61 100644 --- a/src/anbox/ubuntu/platform_policy.cpp +++ b/src/anbox/ubuntu/platform_policy.cpp @@ -29,8 +29,10 @@ namespace anbox { namespace ubuntu { -PlatformPolicy::PlatformPolicy(const std::shared_ptr &input_manager) : +PlatformPolicy::PlatformPolicy(const std::shared_ptr &input_manager, + const std::shared_ptr &android_api) : input_manager_(input_manager), + android_api_(android_api), event_thread_running_(false) { if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0) @@ -181,9 +183,9 @@ Window::Id PlatformPolicy::next_window_id() { return next_id++; } -std::shared_ptr PlatformPolicy::create_window(const wm::WindowState &state) { +std::shared_ptr PlatformPolicy::create_window(const anbox::wm::Task::Id &task, const anbox::graphics::Rect &frame) { auto id = next_window_id(); - auto w = std::make_shared(id, shared_from_this(), state); + auto w = std::make_shared(id, task, shared_from_this(), frame); windows_.insert({id, w}); return w; } diff --git a/src/anbox/ubuntu/platform_policy.h b/src/anbox/ubuntu/platform_policy.h index dd1083c..242c5c8 100644 --- a/src/anbox/ubuntu/platform_policy.h +++ b/src/anbox/ubuntu/platform_policy.h @@ -42,7 +42,7 @@ public: PlatformPolicy(const std::shared_ptr &input_manager); ~PlatformPolicy(); - std::shared_ptr create_window(const wm::WindowState &state) override; + std::shared_ptr create_window(const anbox::wm::Task::Id &task, const anbox::graphics::Rect &frame) override; void window_deleted(const Window::Id &id) override; diff --git a/src/anbox/ubuntu/window.h b/src/anbox/ubuntu/window.h index ceb3bcb..a92ea85 100644 --- a/src/anbox/ubuntu/window.h +++ b/src/anbox/ubuntu/window.h @@ -41,19 +41,20 @@ public: virtual void window_deleted(const Id &id) = 0; }; - Window(const Id &id, const std::shared_ptr &observer, const wm::WindowState &state); + Window(const Id &id, const wm::Task::Id &task, const std::shared_ptr &observer, const graphics::Rect &frame); Window(int x, int y, int width, int height); ~Window(); - void resize(int width, int height); - int update_position(int x, int y); - void process_event(const SDL_Event &event); EGLNativeWindowType native_handle() const override; Id id() const; std::uint32_t window_id() const; +protected: + void resize(int width, int height) override; + void update_position(int x, int y) override; + private: Id id_; std::shared_ptr observer_; diff --git a/src/anbox/wm/default_platform_policy.cpp b/src/anbox/wm/default_platform_policy.cpp index f37e1c2..0dce9e8 100644 --- a/src/anbox/wm/default_platform_policy.cpp +++ b/src/anbox/wm/default_platform_policy.cpp @@ -22,8 +22,17 @@ namespace { class NullWindow : public anbox::wm::Window { public: - NullWindow(const anbox::wm::WindowState &state) : - anbox::wm::Window(state) { + NullWindow(const anbox::wm::Task::Id &task, const anbox::graphics::Rect &frame) : + anbox::wm::Window(0, frame) { + } + +protected: + void resize(int width, int height) override { + WARNING("Not implemented"); + } + + void update_position(int x, int y) override { + WARNING("Not implemented"); } }; } @@ -33,10 +42,10 @@ namespace wm { DefaultPlatformPolicy::DefaultPlatformPolicy() { } -std::shared_ptr DefaultPlatformPolicy::create_window(const WindowState &state) +std::shared_ptr DefaultPlatformPolicy::create_window(const anbox::wm::Task::Id &task, const anbox::graphics::Rect &frame) { DEBUG(""); - return std::make_shared<::NullWindow>(state); + return std::make_shared<::NullWindow>(task, frame); } } // namespace wm } // namespace anbox diff --git a/src/anbox/wm/default_platform_policy.h b/src/anbox/wm/default_platform_policy.h index e9af49c..9476050 100644 --- a/src/anbox/wm/default_platform_policy.h +++ b/src/anbox/wm/default_platform_policy.h @@ -25,7 +25,7 @@ namespace wm { class DefaultPlatformPolicy : public PlatformPolicy { public: DefaultPlatformPolicy(); - std::shared_ptr create_window(const WindowState &state) override; + std::shared_ptr create_window(const anbox::wm::Task::Id &task, const anbox::graphics::Rect &frame) override; }; } // namespace wm } // namespace anbox diff --git a/src/anbox/wm/manager.cpp b/src/anbox/wm/manager.cpp index 83e9702..67c7a24 100644 --- a/src/anbox/wm/manager.cpp +++ b/src/anbox/wm/manager.cpp @@ -35,40 +35,63 @@ void Manager::apply_window_state_update(const WindowState::List &updated, { std::lock_guard l(mutex_); - DEBUG("updated %d removed %d", updated.size(), removed.size()); - // Base on the update we get from the Android WindowManagerService we will create // different window instances with the properties supplied. Incoming layer updates // from SurfaceFlinger will be mapped later into those windows and eventually // composited there via GLES (e.g. for popups, ..) + std::map task_updates; + for (const auto &window : updated) { + // Ignore all windows which are not part of the freeform task stack + if (window.stack() != Stack::Freeform) + continue; + + // And also those which don't have a surface mapped at the moment + if (!window.has_surface()) + continue; + + // If we know that task already we first collect all window updates + // for it so we can apply all of them together. auto w = windows_.find(window.task()); if (w != windows_.end()) { - w->second->update_state(window); + auto t = task_updates.find(window.task()); + if (t == task_updates.end()) + task_updates.insert({window.task(), {window}}); + else + task_updates[window.task()].push_back(window); continue; } - // HACK: We ignore the first task here which will be always the home - // stack of Android. This needs to be solved differently so that the - // Android side doesn't create this stack at all. - if (window.task() == 0) - continue; - auto platform_window = platform_->create_window(window); - platform_window->ref(); + + auto platform_window = platform_->create_window(window.task(), window.frame()); platform_window->attach(); windows_.insert({window.task(), platform_window}); } + // Send updates we collected per task down to the corresponding window + // so that they can update themself. + for (const auto &u : task_updates) + { + auto w = windows_.find(u.first); + if (w == windows_.end()) + continue; + + w->second->update_state(u.second); + } + + // As final step we process all windows we need to remove as they + // got killed on the other side. We need to respect here that we + // also get removals for windows which are part of a task which is + // still in use by other windows. for (const auto &window : removed) { auto w = windows_.find(window.task()); if (w == windows_.end()) continue; - w->second->unref(); - if (!w->second->still_used()) + if (task_updates.find(window.task()) == task_updates.end()) { auto platform_window = w->second; platform_window->release(); @@ -80,10 +103,9 @@ void Manager::apply_window_state_update(const WindowState::List &updated, std::shared_ptr Manager::find_window_for_task(const Task::Id &task) { std::lock_guard l(mutex_); - for (const auto &w : windows_) { - if (w.second->state().task() == task) + if (w.second->task() == task) return w.second; } return nullptr; diff --git a/src/anbox/wm/platform_policy.h b/src/anbox/wm/platform_policy.h index 7270ae0..f9261e3 100644 --- a/src/anbox/wm/platform_policy.h +++ b/src/anbox/wm/platform_policy.h @@ -30,7 +30,7 @@ class PlatformPolicy { public: virtual ~PlatformPolicy(); - virtual std::shared_ptr create_window(const WindowState &state) = 0; + virtual std::shared_ptr create_window(const anbox::wm::Task::Id &task, const anbox::graphics::Rect &frame) = 0; }; } // namespace wm } // namespace anbox diff --git a/src/anbox/wm/window.cpp b/src/anbox/wm/window.cpp index 9bc5edc..03968fb 100644 --- a/src/anbox/wm/window.cpp +++ b/src/anbox/wm/window.cpp @@ -21,20 +21,38 @@ namespace anbox { namespace wm { -Window::Window(const WindowState &state) : - state_(state), - refcount_(0) { +Window::Window(const Task::Id &task, const graphics::Rect &frame) : + task_(task), + frame_(frame) { } Window::~Window() { } -void Window::update_state(const WindowState &new_state) { - state_ = new_state; +void Window::update_state(const WindowState::List &states) +{ } -WindowState Window::state() const { - return state_; +void Window::update_frame(const graphics::Rect &frame) +{ + if (frame == frame_) + return; + + if (frame.width() != frame_.width() || frame.height() != frame_.height()) + resize(frame.width(), frame.height()); + + if (frame.top() != frame_.top() || frame.left() != frame_.left()) + update_position(frame.left(), frame.top()); + + frame_ = frame; +} + +Task::Id Window::task() const { + return task_; +} + +graphics::Rect Window::frame() const { + return frame_; } EGLNativeWindowType Window::native_handle() const { @@ -48,23 +66,6 @@ bool Window::attach() { void Window::release() { Renderer::get()->destroyNativeWindow(native_handle()); } - -void Window::ref() { - refcount_++; -} - -void Window::unref() { - if (refcount_ == 0) { - WARNING("reference count is out of sync"); - return; - } - - refcount_--; -} - -bool Window::still_used() const { - return refcount_ > 0; -} } // namespace wm } // namespace anbox diff --git a/src/anbox/wm/window.h b/src/anbox/wm/window.h index 32c323f..687e90d 100644 --- a/src/anbox/wm/window.h +++ b/src/anbox/wm/window.h @@ -42,24 +42,26 @@ class Window public: typedef std::vector List; - Window(const WindowState &new_state); + Window(const Task::Id &task, const graphics::Rect &frame); virtual ~Window(); bool attach(); void release(); - void ref(); - void unref(); - bool still_used() const; - - void update_state(const WindowState &state); + void update_state(const WindowState::List &states); + void update_frame(const graphics::Rect &frame); virtual EGLNativeWindowType native_handle() const; - WindowState state() const; + graphics::Rect frame() const; + Task::Id task() const; + +protected: + virtual void resize(int width, int height) = 0; + virtual void update_position(int x, int y) = 0; private: - WindowState state_; - std::uint32_t refcount_; + Task::Id task_; + graphics::Rect frame_; }; } // namespace wm } // namespace anbox