Improve window/layer handling for less flickering and better window movement

This commit is contained in:
Simon Fels 2016-11-25 07:14:20 +01:00
commit 53c2fac5d4
16 changed files with 163 additions and 69 deletions

View file

@ -57,3 +57,8 @@ float Renderable::alpha() const
{ {
return alpha_; return alpha_;
} }
void Renderable::set_screen_position(const anbox::graphics::Rect &screen_position)
{
screen_position_ = screen_position;
}

View file

@ -42,6 +42,8 @@ public:
glm::mat4 transformation() const; glm::mat4 transformation() const;
float alpha() const; float alpha() const;
void set_screen_position(const anbox::graphics::Rect &screen_position);
private: private:
std::string name_; std::string name_;
std::uint32_t buffer_; std::uint32_t buffer_;

View file

@ -441,7 +441,7 @@ Renderer::Program::Program(GLuint program_id)
position_attr = s_gles2.glGetAttribLocation(id, "position"); position_attr = s_gles2.glGetAttribLocation(id, "position");
texcoord_attr = s_gles2.glGetAttribLocation(id, "texcoord"); texcoord_attr = s_gles2.glGetAttribLocation(id, "texcoord");
tex_uniform = s_gles2.glGetUniformLocation(id, "tex"); 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"); display_transform_uniform = s_gles2.glGetUniformLocation(id, "display_transform");
transform_uniform = s_gles2.glGetUniformLocation(id, "transform"); transform_uniform = s_gles2.glGetUniformLocation(id, "transform");
screen_to_gl_coords_uniform = s_gles2.glGetUniformLocation(id, "screen_to_gl_coords"); 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 screen_to_gl_coords;\n"
"uniform mat4 display_transform;\n" "uniform mat4 display_transform;\n"
"uniform mat4 transform;\n" "uniform mat4 transform;\n"
"uniform vec2 centre;\n" "uniform vec2 center;\n"
"varying vec2 v_texcoord;\n" "varying vec2 v_texcoord;\n"
"void main() {\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" " vec4 transformed = (transform * (vec4(position, 1.0) - mid)) + mid;\n"
" gl_Position = display_transform * screen_to_gl_coords * transformed;\n" " gl_Position = display_transform * screen_to_gl_coords * transformed;\n"
" v_texcoord = texcoord;\n" " v_texcoord = texcoord;\n"
@ -1103,11 +1103,11 @@ void Renderer::draw(RendererWindow *window, const Renderable &renderable, const
s_gles2.glActiveTexture(GL_TEXTURE0); s_gles2.glActiveTexture(GL_TEXTURE0);
auto const& rect = renderable.screen_position(); auto const& rect = renderable.screen_position();
GLfloat centrex = rect.left() + GLfloat centerx = rect.left() +
rect.width() / 2.0f; rect.width() / 2.0f;
GLfloat centrey = rect.top() + GLfloat centery = rect.top() +
rect.height() / 2.0f; 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, s_gles2.glUniformMatrix4fv(prog.transform_uniform, 1, GL_FALSE,
glm::value_ptr(renderable.transformation())); glm::value_ptr(renderable.transformation()));

View file

@ -303,7 +303,7 @@ private:
GLint tex_uniform = -1; GLint tex_uniform = -1;
GLint position_attr = -1; GLint position_attr = -1;
GLint texcoord_attr = -1; GLint texcoord_attr = -1;
GLint centre_uniform = -1; GLint center_uniform = -1;
GLint display_transform_uniform = -1; GLint display_transform_uniform = -1;
GLint transform_uniform = -1; GLint transform_uniform = -1;
GLint screen_to_gl_coords_uniform = -1; GLint screen_to_gl_coords_uniform = -1;

View file

@ -57,7 +57,45 @@ void LayerComposer::submit_layers(const RenderableList &renderables)
} }
for (const auto &w : win_layers) 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 graphics
} // namespace anbox } // namespace anbox

View file

@ -17,9 +17,19 @@
#include "anbox/graphics/rect.h" #include "anbox/graphics/rect.h"
#include <algorithm>
namespace anbox { namespace anbox {
namespace graphics { namespace graphics {
const Rect Rect::Invalid{-1,-1,-1,-1}; const Rect Rect::Invalid{-1,-1,-1,-1};
const Rect Rect::Empty{0,0,0,0}; 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 graphics
} // namespace anbox } // namespace anbox

View file

@ -89,6 +89,8 @@ public:
return !operator == (rhs); return !operator == (rhs);
} }
void merge(const Rect &rhs);
private: private:
std::int32_t left_; std::int32_t left_;
std::int32_t top_; std::int32_t top_;

View file

@ -29,8 +29,10 @@
namespace anbox { namespace anbox {
namespace ubuntu { namespace ubuntu {
PlatformPolicy::PlatformPolicy(const std::shared_ptr<input::Manager> &input_manager) : PlatformPolicy::PlatformPolicy(const std::shared_ptr<input::Manager> &input_manager,
const std::shared_ptr<bridge::AndroidApiStub> &android_api) :
input_manager_(input_manager), input_manager_(input_manager),
android_api_(android_api),
event_thread_running_(false) { event_thread_running_(false) {
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0) if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0)
@ -181,9 +183,9 @@ Window::Id PlatformPolicy::next_window_id() {
return next_id++; return next_id++;
} }
std::shared_ptr<wm::Window> PlatformPolicy::create_window(const wm::WindowState &state) { std::shared_ptr<wm::Window> PlatformPolicy::create_window(const anbox::wm::Task::Id &task, const anbox::graphics::Rect &frame) {
auto id = next_window_id(); auto id = next_window_id();
auto w = std::make_shared<Window>(id, shared_from_this(), state); auto w = std::make_shared<Window>(id, task, shared_from_this(), frame);
windows_.insert({id, w}); windows_.insert({id, w});
return w; return w;
} }

View file

@ -42,7 +42,7 @@ public:
PlatformPolicy(const std::shared_ptr<input::Manager> &input_manager); PlatformPolicy(const std::shared_ptr<input::Manager> &input_manager);
~PlatformPolicy(); ~PlatformPolicy();
std::shared_ptr<wm::Window> create_window(const wm::WindowState &state) override; std::shared_ptr<wm::Window> create_window(const anbox::wm::Task::Id &task, const anbox::graphics::Rect &frame) override;
void window_deleted(const Window::Id &id) override; void window_deleted(const Window::Id &id) override;

View file

@ -41,19 +41,20 @@ public:
virtual void window_deleted(const Id &id) = 0; virtual void window_deleted(const Id &id) = 0;
}; };
Window(const Id &id, const std::shared_ptr<Observer> &observer, const wm::WindowState &state); Window(const Id &id, const wm::Task::Id &task, const std::shared_ptr<Observer> &observer, const graphics::Rect &frame);
Window(int x, int y, int width, int height); Window(int x, int y, int width, int height);
~Window(); ~Window();
void resize(int width, int height);
int update_position(int x, int y);
void process_event(const SDL_Event &event); void process_event(const SDL_Event &event);
EGLNativeWindowType native_handle() const override; EGLNativeWindowType native_handle() const override;
Id id() const; Id id() const;
std::uint32_t window_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: private:
Id id_; Id id_;
std::shared_ptr<Observer> observer_; std::shared_ptr<Observer> observer_;

View file

@ -22,8 +22,17 @@
namespace { namespace {
class NullWindow : public anbox::wm::Window { class NullWindow : public anbox::wm::Window {
public: public:
NullWindow(const anbox::wm::WindowState &state) : NullWindow(const anbox::wm::Task::Id &task, const anbox::graphics::Rect &frame) :
anbox::wm::Window(state) { 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() { DefaultPlatformPolicy::DefaultPlatformPolicy() {
} }
std::shared_ptr<Window> DefaultPlatformPolicy::create_window(const WindowState &state) std::shared_ptr<Window> DefaultPlatformPolicy::create_window(const anbox::wm::Task::Id &task, const anbox::graphics::Rect &frame)
{ {
DEBUG(""); DEBUG("");
return std::make_shared<::NullWindow>(state); return std::make_shared<::NullWindow>(task, frame);
} }
} // namespace wm } // namespace wm
} // namespace anbox } // namespace anbox

View file

@ -25,7 +25,7 @@ namespace wm {
class DefaultPlatformPolicy : public PlatformPolicy { class DefaultPlatformPolicy : public PlatformPolicy {
public: public:
DefaultPlatformPolicy(); DefaultPlatformPolicy();
std::shared_ptr<Window> create_window(const WindowState &state) override; std::shared_ptr<Window> create_window(const anbox::wm::Task::Id &task, const anbox::graphics::Rect &frame) override;
}; };
} // namespace wm } // namespace wm
} // namespace anbox } // namespace anbox

View file

@ -35,40 +35,63 @@ void Manager::apply_window_state_update(const WindowState::List &updated,
{ {
std::lock_guard<std::mutex> l(mutex_); std::lock_guard<std::mutex> l(mutex_);
DEBUG("updated %d removed %d", updated.size(), removed.size());
// Base on the update we get from the Android WindowManagerService we will create // Base on the update we get from the Android WindowManagerService we will create
// different window instances with the properties supplied. Incoming layer updates // different window instances with the properties supplied. Incoming layer updates
// from SurfaceFlinger will be mapped later into those windows and eventually // from SurfaceFlinger will be mapped later into those windows and eventually
// composited there via GLES (e.g. for popups, ..) // composited there via GLES (e.g. for popups, ..)
std::map<Task::Id, WindowState::List> task_updates;
for (const auto &window : updated) 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()); auto w = windows_.find(window.task());
if (w != windows_.end()) 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; 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 auto platform_window = platform_->create_window(window.task(), window.frame());
// Android side doesn't create this stack at all.
if (window.task() == 0)
continue;
auto platform_window = platform_->create_window(window);
platform_window->ref();
platform_window->attach(); platform_window->attach();
windows_.insert({window.task(), platform_window}); 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) for (const auto &window : removed)
{ {
auto w = windows_.find(window.task()); auto w = windows_.find(window.task());
if (w == windows_.end()) if (w == windows_.end())
continue; continue;
w->second->unref(); if (task_updates.find(window.task()) == task_updates.end())
if (!w->second->still_used())
{ {
auto platform_window = w->second; auto platform_window = w->second;
platform_window->release(); platform_window->release();
@ -80,10 +103,9 @@ void Manager::apply_window_state_update(const WindowState::List &updated,
std::shared_ptr<Window> Manager::find_window_for_task(const Task::Id &task) std::shared_ptr<Window> Manager::find_window_for_task(const Task::Id &task)
{ {
std::lock_guard<std::mutex> l(mutex_); std::lock_guard<std::mutex> l(mutex_);
for (const auto &w : windows_) for (const auto &w : windows_)
{ {
if (w.second->state().task() == task) if (w.second->task() == task)
return w.second; return w.second;
} }
return nullptr; return nullptr;

View file

@ -30,7 +30,7 @@ class PlatformPolicy {
public: public:
virtual ~PlatformPolicy(); virtual ~PlatformPolicy();
virtual std::shared_ptr<Window> create_window(const WindowState &state) = 0; virtual std::shared_ptr<Window> create_window(const anbox::wm::Task::Id &task, const anbox::graphics::Rect &frame) = 0;
}; };
} // namespace wm } // namespace wm
} // namespace anbox } // namespace anbox

View file

@ -21,20 +21,38 @@
namespace anbox { namespace anbox {
namespace wm { namespace wm {
Window::Window(const WindowState &state) : Window::Window(const Task::Id &task, const graphics::Rect &frame) :
state_(state), task_(task),
refcount_(0) { frame_(frame) {
} }
Window::~Window() { Window::~Window() {
} }
void Window::update_state(const WindowState &new_state) { void Window::update_state(const WindowState::List &states)
state_ = new_state; {
} }
WindowState Window::state() const { void Window::update_frame(const graphics::Rect &frame)
return state_; {
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 { EGLNativeWindowType Window::native_handle() const {
@ -48,23 +66,6 @@ bool Window::attach() {
void Window::release() { void Window::release() {
Renderer::get()->destroyNativeWindow(native_handle()); 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 wm
} // namespace anbox } // namespace anbox

View file

@ -42,24 +42,26 @@ class Window
public: public:
typedef std::vector<Window> List; typedef std::vector<Window> List;
Window(const WindowState &new_state); Window(const Task::Id &task, const graphics::Rect &frame);
virtual ~Window(); virtual ~Window();
bool attach(); bool attach();
void release(); void release();
void ref(); void update_state(const WindowState::List &states);
void unref(); void update_frame(const graphics::Rect &frame);
bool still_used() const;
void update_state(const WindowState &state);
virtual EGLNativeWindowType native_handle() const; 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: private:
WindowState state_; Task::Id task_;
std::uint32_t refcount_; graphics::Rect frame_;
}; };
} // namespace wm } // namespace wm
} // namespace anbox } // namespace anbox