Improve window/layer handling for less flickering and better window movement
This commit is contained in:
parent
06aacfe4df
commit
53c2fac5d4
16 changed files with 163 additions and 69 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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_;
|
||||
|
|
|
|||
|
|
@ -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()));
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -89,6 +89,8 @@ public:
|
|||
return !operator == (rhs);
|
||||
}
|
||||
|
||||
void merge(const Rect &rhs);
|
||||
|
||||
private:
|
||||
std::int32_t left_;
|
||||
std::int32_t top_;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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_;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue