Add unit tests for LayerComposer
This commit is contained in:
parent
dbbb8ab795
commit
128cc2422d
11 changed files with 298 additions and 19 deletions
|
|
@ -118,6 +118,7 @@ set(SOURCES
|
|||
anbox/graphics/layer_composer.cpp
|
||||
anbox/graphics/program_family.cpp
|
||||
anbox/graphics/primitives.h
|
||||
anbox/graphics/renderer.h
|
||||
|
||||
anbox/graphics/emugl/ColorBuffer.cpp
|
||||
anbox/graphics/emugl/DisplayManager.cpp
|
||||
|
|
|
|||
|
|
@ -47,3 +47,9 @@ void Renderable::set_screen_position(
|
|||
const anbox::graphics::Rect &screen_position) {
|
||||
screen_position_ = screen_position;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const Renderable &r) {
|
||||
return out << "{ name " << r.name() << " buffer " << r.buffer()
|
||||
<< " screen position " << r.screen_position() << " crop "
|
||||
<< r.crop() << " alpha " << r.alpha();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,16 @@ class Renderable {
|
|||
|
||||
void set_screen_position(const anbox::graphics::Rect &screen_position);
|
||||
|
||||
inline bool operator==(const Renderable &rhs) const {
|
||||
return (name_ == rhs.name() && buffer_ == rhs.buffer() &&
|
||||
screen_position_ == rhs.screen_position() && crop_ == rhs.crop() &&
|
||||
transformation_ == rhs.transformation() && alpha_ == rhs.alpha());
|
||||
}
|
||||
|
||||
inline bool operator!=(const Renderable &rhs) const {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
std::uint32_t buffer_;
|
||||
|
|
@ -52,6 +62,8 @@ class Renderable {
|
|||
float alpha_;
|
||||
};
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const Renderable &r);
|
||||
|
||||
typedef std::vector<Renderable> RenderableList;
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
#include "anbox/graphics/primitives.h"
|
||||
#include "anbox/graphics/program_family.h"
|
||||
#include "anbox/graphics/renderer.h"
|
||||
|
||||
#include <EGL/egl.h>
|
||||
|
||||
|
|
@ -71,10 +72,10 @@ struct RendererWindow;
|
|||
// There is only one global instance, that can be retrieved with getFB(),
|
||||
// and which must be previously setup by calling initialize().
|
||||
//
|
||||
class Renderer {
|
||||
class Renderer : public anbox::graphics::Renderer {
|
||||
public:
|
||||
Renderer();
|
||||
~Renderer();
|
||||
virtual ~Renderer();
|
||||
|
||||
// Initialize the global instance.
|
||||
// |width| and |height| are the dimensions of the emulator GPU display
|
||||
|
|
@ -223,7 +224,7 @@ class Renderer {
|
|||
|
||||
bool draw(EGLNativeWindowType native_window,
|
||||
const anbox::graphics::Rect& window_frame,
|
||||
const RenderableList& renderables);
|
||||
const RenderableList& renderables) override;
|
||||
|
||||
// Return the host EGLDisplay used by this instance.
|
||||
EGLDisplay getDisplay() const { return m_eglDisplay; }
|
||||
|
|
|
|||
|
|
@ -43,10 +43,9 @@ void logger_write(const char *format, ...) {
|
|||
namespace anbox {
|
||||
namespace graphics {
|
||||
GLRendererServer::GLRendererServer(const std::shared_ptr<wm::Manager> &wm)
|
||||
: renderer_(std::make_shared<Renderer>()),
|
||||
: renderer_(std::make_shared<::Renderer>()),
|
||||
wm_(wm),
|
||||
composer_(std::make_shared<LayerComposer>(renderer_, wm)) {
|
||||
|
||||
if (utils::is_env_set("USE_HOST_GLES")) {
|
||||
// Force the host EGL/GLES libraries as translator implementation
|
||||
::setenv("ANDROID_EGL_LIB", "libEGL.so.1", 0);
|
||||
|
|
@ -83,8 +82,6 @@ GLRendererServer::GLRendererServer(const std::shared_ptr<wm::Manager> &wm)
|
|||
registerLayerComposer(composer_);
|
||||
}
|
||||
|
||||
GLRendererServer::~GLRendererServer() {
|
||||
renderer_->finalize();
|
||||
}
|
||||
GLRendererServer::~GLRendererServer() { renderer_->finalize(); }
|
||||
} // namespace graphics
|
||||
} // namespace anbox
|
||||
|
|
|
|||
|
|
@ -22,7 +22,8 @@
|
|||
|
||||
namespace anbox {
|
||||
namespace graphics {
|
||||
LayerComposer::LayerComposer(const std::shared_ptr<Renderer> renderer, const std::shared_ptr<wm::Manager> &wm)
|
||||
LayerComposer::LayerComposer(const std::shared_ptr<Renderer> renderer,
|
||||
const std::shared_ptr<wm::Manager> &wm)
|
||||
: renderer_(renderer), wm_(wm) {}
|
||||
|
||||
LayerComposer::~LayerComposer() {}
|
||||
|
|
@ -85,10 +86,9 @@ void LayerComposer::submit_layers(const RenderableList &renderables) {
|
|||
final_renderables.push_back(new_renderable);
|
||||
}
|
||||
|
||||
renderer_->draw(
|
||||
window->native_handle(),
|
||||
Rect{0, 0, window->frame().width(), window->frame().height()},
|
||||
final_renderables);
|
||||
renderer_->draw(window->native_handle(), Rect{0, 0, window->frame().width(),
|
||||
window->frame().height()},
|
||||
final_renderables);
|
||||
}
|
||||
}
|
||||
} // namespace graphics
|
||||
|
|
|
|||
|
|
@ -18,12 +18,10 @@
|
|||
#ifndef ANBOX_GRAPHICS_LAYER_COMPOSER_H_
|
||||
#define ANBOX_GRAPHICS_LAYER_COMPOSER_H_
|
||||
|
||||
#include "anbox/graphics/emugl/Renderable.h"
|
||||
#include "anbox/graphics/renderer.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
class Renderer;
|
||||
|
||||
namespace anbox {
|
||||
namespace wm {
|
||||
class Manager;
|
||||
|
|
@ -31,7 +29,8 @@ class Manager;
|
|||
namespace graphics {
|
||||
class LayerComposer {
|
||||
public:
|
||||
LayerComposer(const std::shared_ptr<Renderer> renderer, const std::shared_ptr<wm::Manager> &wm);
|
||||
LayerComposer(const std::shared_ptr<Renderer> renderer,
|
||||
const std::shared_ptr<wm::Manager> &wm);
|
||||
~LayerComposer();
|
||||
|
||||
void submit_layers(const RenderableList &renderables);
|
||||
|
|
|
|||
38
src/anbox/graphics/renderer.h
Normal file
38
src/anbox/graphics/renderer.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 3, as published
|
||||
* by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranties of
|
||||
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ANBOX_GRAPHICS_RENDERER_H_
|
||||
#define ANBOX_GRAPHICS_RENDERER_H_
|
||||
|
||||
#include "anbox/graphics/emugl/Renderable.h"
|
||||
|
||||
#include <EGL/egl.h>
|
||||
|
||||
namespace anbox {
|
||||
namespace graphics {
|
||||
class Renderer {
|
||||
public:
|
||||
virtual ~Renderer() {}
|
||||
|
||||
virtual bool draw(EGLNativeWindowType native_window,
|
||||
const anbox::graphics::Rect& window_frame,
|
||||
const RenderableList& renderables) = 0;
|
||||
};
|
||||
} // namespace graphics
|
||||
} // namespace anbox
|
||||
|
||||
#endif
|
||||
|
|
@ -24,7 +24,7 @@ class NullWindow : public anbox::wm::Window {
|
|||
public:
|
||||
NullWindow(const anbox::wm::Task::Id &task,
|
||||
const anbox::graphics::Rect &frame)
|
||||
: anbox::wm::Window(nullptr, 0, frame) {}
|
||||
: anbox::wm::Window(nullptr, task, frame) {}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -34,7 +34,6 @@ DefaultPlatformPolicy::DefaultPlatformPolicy() {}
|
|||
|
||||
std::shared_ptr<Window> DefaultPlatformPolicy::create_window(
|
||||
const anbox::wm::Task::Id &task, const anbox::graphics::Rect &frame) {
|
||||
DEBUG("");
|
||||
return std::make_shared<::NullWindow>(task, frame);
|
||||
}
|
||||
} // namespace wm
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
ANBOX_ADD_TEST(buffer_queue_tests buffer_queue_tests.cpp)
|
||||
ANBOX_ADD_TEST(buffered_io_stream_tests buffered_io_stream_tests.cpp)
|
||||
ANBOX_ADD_TEST(layer_composer_tests layer_composer_tests.cpp)
|
||||
|
|
|
|||
225
tests/anbox/graphics/layer_composer_tests.cpp
Normal file
225
tests/anbox/graphics/layer_composer_tests.cpp
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
* Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 3, as published
|
||||
* by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranties of
|
||||
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Both includes need to go first as otherwise they can conflict with EGL.h
|
||||
// being included by the following includes.
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "anbox/wm/default_platform_policy.h"
|
||||
#include "anbox/wm/manager.h"
|
||||
#include "anbox/wm/window_state.h"
|
||||
|
||||
#include "anbox/graphics/layer_composer.h"
|
||||
|
||||
using namespace ::testing;
|
||||
|
||||
namespace {
|
||||
class MockRenderer : public anbox::graphics::Renderer {
|
||||
public:
|
||||
MOCK_METHOD3(draw, bool(EGLNativeWindowType, const anbox::graphics::Rect&,
|
||||
const RenderableList&));
|
||||
};
|
||||
}
|
||||
|
||||
namespace anbox {
|
||||
namespace graphics {
|
||||
TEST(LayerComposer, FindsNoSuitableWindowForLayer) {
|
||||
auto renderer = std::make_shared<MockRenderer>();
|
||||
|
||||
// The default policy will create a dumb window instance when requested
|
||||
// from the manager.
|
||||
auto platform_policy = std::make_shared<wm::DefaultPlatformPolicy>();
|
||||
auto wm = std::make_shared<wm::Manager>(platform_policy);
|
||||
|
||||
auto single_window = wm::WindowState{
|
||||
wm::Display::Id{1},
|
||||
true,
|
||||
graphics::Rect{0, 0, 1024, 768},
|
||||
"org.anbox.test.1",
|
||||
wm::Task::Id{1},
|
||||
wm::Stack::Freeform,
|
||||
};
|
||||
|
||||
wm->apply_window_state_update({single_window}, {});
|
||||
|
||||
LayerComposer composer(renderer, wm);
|
||||
|
||||
// A single renderable which has a different task id then the window we know
|
||||
// about
|
||||
RenderableList renderables = {
|
||||
{"org.anbox.surface.2", 0, {0, 0, 1024, 768}, {0, 0, 1024, 768}},
|
||||
};
|
||||
|
||||
// The renderer should not be called for a layer which doesn't exist
|
||||
EXPECT_CALL(*renderer, draw(_, _, _)).Times(0);
|
||||
|
||||
composer.submit_layers(renderables);
|
||||
}
|
||||
|
||||
TEST(LayerComposer, MapsLayersToWindows) {
|
||||
auto renderer = std::make_shared<MockRenderer>();
|
||||
|
||||
// The default policy will create a dumb window instance when requested
|
||||
// from the manager.
|
||||
auto platform_policy = std::make_shared<wm::DefaultPlatformPolicy>();
|
||||
auto wm = std::make_shared<wm::Manager>(platform_policy);
|
||||
|
||||
auto first_window = wm::WindowState{
|
||||
wm::Display::Id{1},
|
||||
true,
|
||||
graphics::Rect{0, 0, 1024, 768},
|
||||
"org.anbox.foo",
|
||||
wm::Task::Id{1},
|
||||
wm::Stack::Freeform,
|
||||
};
|
||||
|
||||
auto second_window = wm::WindowState{
|
||||
wm::Display::Id{1},
|
||||
true,
|
||||
graphics::Rect{300, 400, 1324, 1168},
|
||||
"org.anbox.bar",
|
||||
wm::Task::Id{2},
|
||||
wm::Stack::Freeform,
|
||||
};
|
||||
|
||||
wm->apply_window_state_update({first_window, second_window}, {});
|
||||
|
||||
LayerComposer composer(renderer, wm);
|
||||
|
||||
// A single renderable which has a different task id then the window we know
|
||||
// about
|
||||
RenderableList renderables = {
|
||||
{"org.anbox.surface.1", 0, {0, 0, 1024, 768}, {0, 0, 1024, 768}},
|
||||
{"org.anbox.surface.2", 1, {0, 0, 1024, 768}, {0, 0, 1024, 768}},
|
||||
};
|
||||
|
||||
RenderableList first_window_renderables{
|
||||
{"org.anbox.surface.1", 0, {0, 0, 1024, 768}, {0, 0, 1024, 768}},
|
||||
};
|
||||
|
||||
RenderableList second_window_renderables{
|
||||
{"org.anbox.surface.2", 1, {0, 0, 1024, 768}, {0, 0, 1024, 768}},
|
||||
};
|
||||
|
||||
EXPECT_CALL(*renderer, draw(_, Rect{0, 0, first_window.frame().width(),
|
||||
first_window.frame().height()},
|
||||
first_window_renderables))
|
||||
.Times(1)
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(*renderer, draw(_, Rect{0, 0, second_window.frame().width(),
|
||||
second_window.frame().height()},
|
||||
second_window_renderables))
|
||||
.Times(1)
|
||||
.WillOnce(Return(true));
|
||||
|
||||
composer.submit_layers(renderables);
|
||||
}
|
||||
|
||||
TEST(LayerComposer, WindowPartiallyOffscreen) {
|
||||
auto renderer = std::make_shared<MockRenderer>();
|
||||
|
||||
// The default policy will create a dumb window instance when requested
|
||||
// from the manager.
|
||||
auto platform_policy = std::make_shared<wm::DefaultPlatformPolicy>();
|
||||
auto wm = std::make_shared<wm::Manager>(platform_policy);
|
||||
|
||||
auto window = wm::WindowState{
|
||||
wm::Display::Id{1},
|
||||
true,
|
||||
graphics::Rect{-100, -100, 924, 668},
|
||||
"org.anbox.foo",
|
||||
wm::Task::Id{1},
|
||||
wm::Stack::Freeform,
|
||||
};
|
||||
|
||||
wm->apply_window_state_update({window}, {});
|
||||
|
||||
LayerComposer composer(renderer, wm);
|
||||
|
||||
// Window is build out of two layers where one is placed inside the other
|
||||
// but the layer covering the whole window is placed with its top left
|
||||
// origin outside of the visible display area.
|
||||
RenderableList renderables = {
|
||||
{"org.anbox.surface.1", 0, {-100, -100, 924, 668}, {0, 0, 1024, 768}},
|
||||
{"org.anbox.surface.1", 1, {0, 0, 100, 200}, {0, 0, 100, 200}},
|
||||
};
|
||||
|
||||
RenderableList expected_renderables{
|
||||
{"org.anbox.surface.1", 0, {0, 0, 1024, 768}, {0, 0, 1024, 768}},
|
||||
{"org.anbox.surface.1", 1, {100, 100, 200, 300}, {0, 0, 100, 200}},
|
||||
};
|
||||
|
||||
EXPECT_CALL(*renderer, draw(_, Rect{0, 0,
|
||||
window.frame().width(),
|
||||
window.frame().height()},
|
||||
expected_renderables))
|
||||
.Times(1)
|
||||
.WillOnce(Return(true));
|
||||
|
||||
composer.submit_layers(renderables);
|
||||
}
|
||||
|
||||
TEST(LayerComposer, PopupShouldNotCauseWindowLayerOffset) {
|
||||
auto renderer = std::make_shared<MockRenderer>();
|
||||
|
||||
// The default policy will create a dumb window instance when requested
|
||||
// from the manager.
|
||||
auto platform_policy = std::make_shared<wm::DefaultPlatformPolicy>();
|
||||
auto wm = std::make_shared<wm::Manager>(platform_policy);
|
||||
|
||||
auto window = wm::WindowState{
|
||||
wm::Display::Id{1},
|
||||
true,
|
||||
graphics::Rect{1120, 270, 2144, 1038},
|
||||
"org.anbox.foo",
|
||||
wm::Task::Id{3},
|
||||
wm::Stack::Freeform,
|
||||
};
|
||||
|
||||
wm->apply_window_state_update({window}, {});
|
||||
|
||||
LayerComposer composer(renderer, wm);
|
||||
|
||||
// Having two renderables where the second smaller one overlaps the bigger
|
||||
// one and goes a bit offscreen. This should be still placed correctly and
|
||||
// the small layer should go offscreen as it is supposed to. This typically
|
||||
// happens when a popup window appears which has a shadow attached which goes
|
||||
// out of the window area. In our case this is not possible as the area the
|
||||
// window has available is static.
|
||||
RenderableList renderables = {
|
||||
{"org.anbox.surface.3", 0, {1120,270,2144,1038}, {0, 0, 1024, 768}},
|
||||
{"org.anbox.surface.3", 1, {1904, 246, 2164, 406}, {0, 0, 260, 160}},
|
||||
};
|
||||
|
||||
RenderableList expected_renderables{
|
||||
{"org.anbox.surface.3", 0, {0, 0, 1024, 768}, {0, 0, 1024, 768}},
|
||||
{"org.anbox.surface.3", 1, {784, -24, 1044, 136}, {0, 0, 260, 160}},
|
||||
};
|
||||
|
||||
EXPECT_CALL(*renderer, draw(_, Rect{0, 0,
|
||||
window.frame().width(),
|
||||
window.frame().height()},
|
||||
expected_renderables))
|
||||
.Times(1)
|
||||
.WillOnce(Return(true));
|
||||
|
||||
composer.submit_layers(renderables);
|
||||
}
|
||||
|
||||
} // namespace graphics
|
||||
} // namespace anbox
|
||||
Loading…
Add table
Add a link
Reference in a new issue