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_;
}
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;
float alpha() const;
void set_screen_position(const anbox::graphics::Rect &screen_position);
private:
std::string name_;
std::uint32_t buffer_;

View file

@ -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()));

View file

@ -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;

View file

@ -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

View file

@ -17,9 +17,19 @@
#include "anbox/graphics/rect.h"
#include <algorithm>
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

View file

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

View file

@ -29,8 +29,10 @@
namespace anbox {
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),
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<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 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});
return w;
}

View file

@ -42,7 +42,7 @@ public:
PlatformPolicy(const std::shared_ptr<input::Manager> &input_manager);
~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;

View file

@ -41,19 +41,20 @@ public:
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();
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> observer_;

View file

@ -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<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("");
return std::make_shared<::NullWindow>(state);
return std::make_shared<::NullWindow>(task, frame);
}
} // namespace wm
} // namespace anbox

View file

@ -25,7 +25,7 @@ namespace wm {
class DefaultPlatformPolicy : public PlatformPolicy {
public:
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 anbox

View file

@ -35,40 +35,63 @@ void Manager::apply_window_state_update(const WindowState::List &updated,
{
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
// 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::Id, WindowState::List> 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<Window> Manager::find_window_for_task(const Task::Id &task)
{
std::lock_guard<std::mutex> l(mutex_);
for (const auto &w : windows_)
{
if (w.second->state().task() == task)
if (w.second->task() == task)
return w.second;
}
return nullptr;

View file

@ -30,7 +30,7 @@ class PlatformPolicy {
public:
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 anbox

View file

@ -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

View file

@ -42,24 +42,26 @@ class Window
public:
typedef std::vector<Window> 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