Implement renderer instance which composes multiple layers into a window

This commit is contained in:
Simon Fels 2016-11-23 12:16:20 +01:00
commit 8018942323
21 changed files with 723 additions and 118 deletions

View file

@ -105,6 +105,9 @@ set(SOURCES
anbox/graphics/gl_renderer_server.cpp
anbox/graphics/density.h
anbox/graphics/rect.cpp
anbox/graphics/layer_composer.cpp
anbox/graphics/program_family.cpp
anbox/graphics/primitives.h
anbox/graphics/emugl/ColorBuffer.cpp
anbox/graphics/emugl/DisplayManager.cpp

View file

@ -95,7 +95,7 @@ anbox::cmds::Run::Run(const BusFactory& bus_factory)
auto window_manager = std::make_shared<wm::Manager>(policy);
auto renderer = std::make_shared<graphics::GLRendererServer>();
auto renderer = std::make_shared<graphics::GLRendererServer>(window_manager);
renderer->start();
// Socket which will be used by the qemud service inside the Android

View file

@ -376,11 +376,6 @@ bool ColorBuffer::bindToRenderbuffer() {
return true;
}
bool ColorBuffer::post(float rotation, float dx, float dy) {
// NOTE: Do not call m_helper->setupContext() here!
return m_helper->getTextureDraw()->draw(m_resizer->update(m_tex));
}
void ColorBuffer::readback(unsigned char* img) {
ScopedHelperContext context(m_helper);
if (!context.isOk()) {
@ -392,3 +387,8 @@ void ColorBuffer::readback(unsigned char* img) {
unbindFbo();
}
}
void ColorBuffer::bind() {
const auto id = m_resizer->update(m_tex);
s_gles2.glBindTexture(GL_TEXTURE_2D, id);
}

View file

@ -108,15 +108,6 @@ public:
GLenum p_type,
void *pixels);
// Draw a ColorBuffer instance, i.e. blit it to the current guest
// framebuffer object / window surface. This doesn't display anything.
bool draw();
// Post this ColorBuffer to the host native sub-window.
// |rotation| is the rotation angle in degrees, clockwise in the GL
// coordinate space.
bool post(float rotation, float dx, float dy);
// Bind the current context's EGL_TEXTURE_2D texture to this ColorBuffer's
// EGLImage. This is intended to implement glEGLImageTargetTexture2DOES()
// for all GLES versions.
@ -136,6 +127,7 @@ public:
// |img| must be a buffer large enough (i.e. width * height * 4).
void readback(unsigned char* img);
void bind();
private:
ColorBuffer(); // no default constructor.

View file

@ -24,10 +24,19 @@
#include "OpenGLESDispatch/EGLDispatch.h"
#include "anbox/graphics/layer_composer.h"
#include "anbox/logger.h"
#include <map>
#include <string>
static const GLint rendererVersion = 1;
static std::shared_ptr<anbox::graphics::LayerComposer> composer;
void registerLayerComposer(const std::shared_ptr<anbox::graphics::LayerComposer> &c)
{
composer = c;
}
static GLint rcGetRendererVersion()
{
@ -305,12 +314,7 @@ static EGLint rcMakeCurrent(uint32_t context,
static void rcFBPost(uint32_t colorBuffer)
{
Renderer *fb = Renderer::get();
if (!fb) {
return;
}
fb->post(nullptr, colorBuffer);
WARNING("Not implemented");
}
static void rcFBSetSwapInterval(EGLint interval)
@ -425,15 +429,31 @@ int rcGetDisplayVsyncPeriod(uint32_t display_id) {
return 1;
}
static std::vector<Renderable> frame_layers;
bool is_layer_blacklisted(const std::string &name) {
static std::vector<std::string> blacklist = {
"Sprite",
};
return std::find(blacklist.begin(), blacklist.end(), name) != blacklist.end();
}
void rcPostLayer(const char *name, uint32_t color_buffer,
int32_t sourceCropLeft, int32_t sourceCropTop,
int32_t sourceCropRight, int32_t sourceCropBottom,
int32_t displayFrameLeft, int32_t displayFrameTop,
int32_t displayFrameRight, int32_t displayFrameBottom) {
int32_t displayFrameRight, int32_t displayFrameBottom)
{
Renderable r{name, color_buffer, {displayFrameLeft, displayFrameTop, displayFrameRight, displayFrameBottom}};
frame_layers.push_back(r);
}
void rcPostAllLayersDone() {
void rcPostAllLayersDone()
{
if (composer)
composer->submit_layers(frame_layers);
frame_layers.clear();
}
void initRenderControlContext(renderControl_decoder_context_t *dec)

View file

@ -18,6 +18,15 @@
#include "renderControl_dec.h"
#include <memory>
namespace anbox {
namespace graphics {
class LayerComposer;
} // namespace graphics
} // namespace anbox
void initRenderControlContext(renderControl_decoder_context_t *dec);
void registerLayerComposer(const std::shared_ptr<anbox::graphics::LayerComposer> &c);
#endif

View file

@ -16,10 +16,16 @@
#include "Renderable.h"
Renderable::Renderable(const std::uint32_t &buffer,
const anbox::graphics::Rect &screen_position) :
Renderable::Renderable(const std::string &name,
const std::uint32_t &buffer,
const anbox::graphics::Rect &screen_position,
const glm::mat4 &transformation,
const float &alpha) :
name_(name),
buffer_(buffer),
screen_position_(screen_position)
screen_position_(screen_position),
transformation_(transformation),
alpha_(alpha)
{
}
@ -27,6 +33,11 @@ Renderable::~Renderable()
{
}
std::string Renderable::name() const
{
return name_;
}
std::uint32_t Renderable::buffer() const
{
return buffer_;
@ -36,3 +47,13 @@ anbox::graphics::Rect Renderable::screen_position() const
{
return screen_position_;
}
glm::mat4 Renderable::transformation() const
{
return transformation_;
}
float Renderable::alpha() const
{
return alpha_;
}

View file

@ -19,23 +19,35 @@
#include "anbox/graphics/rect.h"
#include <string>
#include <vector>
#include <cstdint>
#include <glm/glm.hpp>
class Renderable
{
public:
Renderable(const std::uint32_t &buffer,
const anbox::graphics::Rect &screen_position);
Renderable(const std::string &name,
const std::uint32_t &buffer,
const anbox::graphics::Rect &screen_position,
const glm::mat4 &transformation = {},
const float &alpha = 1.0f);
~Renderable();
std::string name() const;
std::uint32_t buffer() const;
anbox::graphics::Rect screen_position() const;
glm::mat4 transformation() const;
float alpha() const;
private:
std::string name_;
std::uint32_t buffer_;
anbox::graphics::Rect screen_position_;
glm::mat4 transformation_;
float alpha_;
};
typedef std::vector<Renderable> RenderableList;

View file

@ -25,8 +25,14 @@
#include "emugl/common/logging.h"
#include "anbox/logger.h"
#include <stdio.h>
#include <glm/glm.hpp>
#include <glm/gtx/transform.hpp>
#include <glm/gtc/type_ptr.hpp>
namespace {
// Helper class to call the bind_locked() / unbind_locked() properly.
@ -415,6 +421,9 @@ bool Renderer::initialize(EGLNativeDisplayType nativeDisplay)
return false;
}
fb->m_defaultProgram = fb->m_family.add_program(vshader, defaultFShader);
fb->m_alphaProgram = fb->m_family.add_program(vshader, alphaFShader);
// release the FB context
bind.release();
@ -426,6 +435,19 @@ bool Renderer::initialize(EGLNativeDisplayType nativeDisplay)
return true;
}
Renderer::Program::Program(GLuint program_id)
{
id = 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");
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");
alpha_uniform = s_gles2.glGetUniformLocation(id, "alpha");
}
Renderer::Renderer() :
m_configs(NULL),
m_eglDisplay(EGL_NO_DISPLAY),
@ -455,6 +477,9 @@ Renderer::~Renderer() {
struct RendererWindow {
EGLNativeWindowType native_window = 0;
EGLSurface surface = EGL_NO_SURFACE;
anbox::graphics::Rect viewport;
glm::mat4 screen_to_gl_coords;
glm::mat4 display_transform;
};
RendererWindow* Renderer::createNativeWindow(EGLNativeWindowType native_window)
@ -480,7 +505,6 @@ RendererWindow* Renderer::createNativeWindow(EGLNativeWindowType native_window)
return nullptr;
}
// s_gles2.glViewport(0, 0, width, height);
s_gles2.glClear(GL_COLOR_BUFFER_BIT |
GL_DEPTH_BUFFER_BIT |
GL_STENCIL_BUFFER_BIT);
@ -954,68 +978,172 @@ bool Renderer::unbind_locked()
return true;
}
bool Renderer::post(RendererWindow *window, HandleType p_colorbuffer, bool needLock)
const GLchar* const Renderer::vshader =
{
if (!window)
return false;
"attribute vec3 position;\n"
"attribute vec2 texcoord;\n"
"uniform mat4 screen_to_gl_coords;\n"
"uniform mat4 display_transform;\n"
"uniform mat4 transform;\n"
"uniform vec2 centre;\n"
"varying vec2 v_texcoord;\n"
"void main() {\n"
" vec4 mid = vec4(centre, 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"
"}\n"
};
if (needLock)
m_lock.lock();
const GLchar* const Renderer::alphaFShader =
{
"precision mediump float;\n"
"uniform sampler2D tex;\n"
"uniform float alpha;\n"
"varying vec2 v_texcoord;\n"
"void main() {\n"
" vec4 frag = texture2D(tex, v_texcoord);\n"
" gl_FragColor = alpha*frag;\n"
"}\n"
};
bool ret;
const GLchar* const Renderer::defaultFShader =
{ // This is the fastest fragment shader. Use it when you can.
"precision mediump float;\n"
"uniform sampler2D tex;\n"
"varying vec2 v_texcoord;\n"
"void main() {\n"
" gl_FragColor = texture2D(tex, v_texcoord);\n"
"}\n"
};
ColorBufferMap::iterator c( m_colorbuffers.find(p_colorbuffer) );
if (c == m_colorbuffers.end())
goto EXIT;
void Renderer::setupViewport(RendererWindow *window, const anbox::graphics::Rect &rect)
{
/*
* Here we provide a 3D perspective projection with a default 30 degrees
* vertical field of view. This projection matrix is carefully designed
* such that any vertices at depth z=0 will fit the screen coordinates. So
* client texels will fit screen pixels perfectly as long as the surface is
* at depth zero. But if you want to do anything fancy, you can also choose
* a different depth and it will appear to come out of or go into the
* screen.
*/
window->screen_to_gl_coords = glm::translate(glm::mat4(1.0f), glm::vec3{-1.0f, 1.0f, 0.0f});
m_lastPostedColorBuffer = p_colorbuffer;
/*
* Perspective division is one thing that can't be done in a matrix
* multiplication. It happens after the matrix multiplications. GL just
* scales {x,y} by 1/w. So modify the final part of the projection matrix
* to set w ([3]) to be the incoming z coordinate ([2]).
*/
window->screen_to_gl_coords[2][3] = -1.0f;
if (!bindWindow_locked(window))
goto EXIT;
float const vertical_fov_degrees = 30.0f;
float const near =
(rect.height() / 2.0f) /
std::tan((vertical_fov_degrees * M_PI / 180.0f) / 2.0f);
float const far = -near;
#if 0
if (window->needViewportUpdate) {
s_gles2.glViewport(0, 0, window->width, window->height);
window->needViewportUpdate = false;
}
#endif
window->screen_to_gl_coords = glm::scale(window->screen_to_gl_coords,
glm::vec3{2.0f / rect.width(),
-2.0f / rect.height(),
2.0f / (near - far)});
window->screen_to_gl_coords = glm::translate(window->screen_to_gl_coords,
glm::vec3{-rect.left(),
-rect.top(),
0.0f});
s_gles2.glClearColor(0.0, 0.0, 1.0, 0.0);
s_gles2.glClear(GL_COLOR_BUFFER_BIT);
ret = (*c).second.cb->post(0.0f, 0, 0);
if (ret) {
s_egl.eglSwapBuffers(m_eglDisplay, window->surface);
}
// restore previous binding
unbind_locked();
//
// output FPS statistics
//
if (m_fpsStats) {
long long currTime = GetCurrentTimeMS();
m_statsNumFrames++;
if (currTime - m_statsStartTime >= 1000) {
float dt = (float)(currTime - m_statsStartTime) / 1000.0f;
printf("FPS: %5.3f\n", (float)m_statsNumFrames / dt);
m_statsStartTime = currTime;
m_statsNumFrames = 0;
}
}
EXIT:
if (!ret)
printf("post: FAILED\n");
if (needLock) {
m_lock.unlock();
}
return ret;
window->viewport = rect;
}
bool Renderer::draw(EGLNativeWindowType native_window, const RenderableList &renderables)
void Renderer::tessellate(std::vector<anbox::graphics::Primitive>& primitives,
const anbox::graphics::Rect &buf_size,
const Renderable &renderable)
{
auto rect = renderable.screen_position();
GLfloat left = rect.left();
GLfloat right = rect.right();
GLfloat top = rect.top();
GLfloat bottom = rect.bottom();
anbox::graphics::Primitive rectangle;
rectangle.tex_id = 0;
rectangle.type = GL_TRIANGLE_STRIP;
GLfloat tex_right = static_cast<GLfloat>(rect.width()) /
buf_size.width();
GLfloat tex_bottom = static_cast<GLfloat>(rect.height()) /
buf_size.height();
auto& vertices = rectangle.vertices;
vertices[0] = {{left, top, 0.0f}, {0.0f, 0.0f}};
vertices[1] = {{left, bottom, 0.0f}, {0.0f, tex_bottom}};
vertices[2] = {{right, top, 0.0f}, {tex_right, 0.0f}};
vertices[3] = {{right, bottom, 0.0f}, {tex_right, tex_bottom}};
primitives.resize(1);
primitives[0] = rectangle;
}
void Renderer::draw(RendererWindow *window, const Renderable &renderable, const Program &prog)
{
const auto &color_buffer = m_colorbuffers.find(renderable.buffer());
if (color_buffer == m_colorbuffers.end())
return;
const auto &cb = color_buffer->second.cb;
s_gles2.glUseProgram(prog.id);
s_gles2.glUniform1i(prog.tex_uniform, 0);
s_gles2.glUniformMatrix4fv(prog.display_transform_uniform, 1, GL_FALSE,
glm::value_ptr(window->display_transform));
s_gles2.glUniformMatrix4fv(prog.screen_to_gl_coords_uniform, 1, GL_FALSE,
glm::value_ptr(window->screen_to_gl_coords));
s_gles2.glActiveTexture(GL_TEXTURE0);
auto const& rect = renderable.screen_position();
GLfloat centrex = rect.left() +
rect.width() / 2.0f;
GLfloat centrey = rect.top() +
rect.height() / 2.0f;
s_gles2.glUniform2f(prog.centre_uniform, centrex, centrey);
s_gles2.glUniformMatrix4fv(prog.transform_uniform, 1, GL_FALSE,
glm::value_ptr(renderable.transformation()));
if (prog.alpha_uniform >= 0)
s_gles2.glUniform1f(prog.alpha_uniform, renderable.alpha());
s_gles2.glEnableVertexAttribArray(prog.position_attr);
s_gles2.glEnableVertexAttribArray(prog.texcoord_attr);
m_primitives.clear();
tessellate(m_primitives, {cb->getWidth(), cb->getHeight()}, renderable);
for (auto const& p : m_primitives)
{
cb->bind();
s_gles2.glVertexAttribPointer(prog.position_attr, 3, GL_FLOAT,
GL_FALSE, sizeof(anbox::graphics::Vertex),
&p.vertices[0].position);
s_gles2.glVertexAttribPointer(prog.texcoord_attr, 2, GL_FLOAT,
GL_FALSE, sizeof(anbox::graphics::Vertex),
&p.vertices[0].texcoord);
s_gles2.glEnable(GL_BLEND);
s_gles2.glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA,
GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
s_gles2.glDrawArrays(p.type, 0, p.nvertices);
}
s_gles2.glDisableVertexAttribArray(prog.texcoord_attr);
s_gles2.glDisableVertexAttribArray(prog.position_attr);
}
bool Renderer::draw(EGLNativeWindowType native_window, const anbox::graphics::Rect &window_frame, const RenderableList &renderables)
{
auto w = m_nativeWindows.find(native_window);
if (w == m_nativeWindows.end())
@ -1027,18 +1155,13 @@ bool Renderer::draw(EGLNativeWindowType native_window, const RenderableList &ren
return false;
}
s_gles2.glClearColor(0.0, 0.0, 1.0, 0.0);
setupViewport(w->second, window_frame);
s_gles2.glClearColor(0.0, 0.0, 0.0, 1.0);
s_gles2.glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
s_gles2.glClear(GL_COLOR_BUFFER_BIT);
for (const auto &renderable : renderables)
{
const auto &color_buffer = m_colorbuffers.find(renderable.buffer());
if (color_buffer == m_colorbuffers.end())
continue;
color_buffer->second.cb->post(0.0f,
renderable.screen_position().left(), renderable.screen_position().top());
}
for (const auto &r : renderables)
draw(w->second, r, r.alpha() < 1.0f ? m_alphaProgram : m_defaultProgram);
s_egl.eglSwapBuffers(m_eglDisplay, w->second->surface);

View file

@ -27,6 +27,9 @@
#include "Renderable.h"
#include "anbox/graphics/program_family.h"
#include "anbox/graphics/primitives.h"
#include <EGL/egl.h>
#include <map>
@ -229,14 +232,7 @@ public:
int x, int y, int width, int height,
GLenum format, GLenum type, void *pixels);
// Display the content of a given ColorBuffer into the framebuffer's
// sub-window. |p_colorbuffer| is a handle value.
// |needLock| is used to indicate whether the operation requires
// acquiring/releasing the FrameBuffer instance's lock. It should be
// false only when called internally.
bool post(RendererWindow *window, HandleType p_colorbuffer, bool needLock = true);
bool draw(EGLNativeWindowType native_window, const RenderableList &renderables);
bool draw(EGLNativeWindowType native_window, const anbox::graphics::Rect &window_frame, const RenderableList &renderables);
// Return the host EGLDisplay used by this instance.
EGLDisplay getDisplay() const { return m_eglDisplay; }
@ -259,6 +255,13 @@ private:
bool bindWindow_locked(RendererWindow *window);
void setupViewport(RendererWindow *window, const anbox::graphics::Rect &rect);
struct Program;
void draw(RendererWindow *window, const Renderable &renderable, const Program &prog);
void tessellate(std::vector<anbox::graphics::Primitive>& primitives,
const anbox::graphics::Rect &buf_size,
const Renderable &renderable);
private:
static Renderer *s_renderer;
static HandleType s_nextHandle;
@ -292,5 +295,30 @@ private:
const char* m_glVersion;
std::map<EGLNativeWindowType,RendererWindow*> m_nativeWindows;
anbox::graphics::ProgramFamily m_family;
struct Program
{
GLuint id = 0;
GLint tex_uniform = -1;
GLint position_attr = -1;
GLint texcoord_attr = -1;
GLint centre_uniform = -1;
GLint display_transform_uniform = -1;
GLint transform_uniform = -1;
GLint screen_to_gl_coords_uniform = -1;
GLint alpha_uniform = -1;
mutable long long last_used_frameno = 0;
Program(GLuint program_id);
Program() {}
};
Program m_defaultProgram, m_alphaProgram;
std::vector<anbox::graphics::Primitive> m_primitives;
static const GLchar* const vshader;
static const GLchar* const defaultFShader;
static const GLchar* const alphaFShader;
};
#endif

View file

@ -17,6 +17,9 @@
#include "anbox/logger.h"
#include "anbox/graphics/gl_renderer_server.h"
#include "anbox/graphics/layer_composer.h"
#include "anbox/graphics/emugl/RenderControl.h"
#include "anbox/wm/manager.h"
#include "OpenglRender/render_api.h"
@ -26,7 +29,9 @@
namespace anbox {
namespace graphics {
GLRendererServer::GLRendererServer()
GLRendererServer::GLRendererServer(const std::shared_ptr<wm::Manager> &wm) :
wm_(wm),
composer_(std::make_shared<LayerComposer>(wm))
{
if (utils::is_env_set("USE_HOST_GLES")) {
@ -43,6 +48,8 @@ GLRendererServer::GLRendererServer()
if (!initLibrary())
BOOST_THROW_EXCEPTION(std::runtime_error("Failed to initialize OpenGL renderer"));
registerLayerComposer(composer_);
}
GLRendererServer::~GLRendererServer() {

View file

@ -25,11 +25,14 @@ namespace anbox {
namespace input {
class Manager;
} // namespace input
namespace wm {
class Manager;
} // namespace wm
namespace graphics {
class WindowCreator;
class LayerComposer;
class GLRendererServer {
public:
GLRendererServer();
GLRendererServer(const std::shared_ptr<wm::Manager> &wm);
~GLRendererServer();
void start();
@ -38,6 +41,8 @@ public:
private:
std::string socket_path_;
std::shared_ptr<wm::Manager> wm_;
std::shared_ptr<LayerComposer> composer_;
};
} // namespace graphics

View file

@ -0,0 +1,63 @@
/*
* 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/>.
*
*/
#include "anbox/graphics/layer_composer.h"
#include "anbox/graphics/emugl/Renderer.h"
#include "anbox/wm/manager.h"
#include "anbox/logger.h"
namespace anbox {
namespace graphics {
LayerComposer::LayerComposer(const std::shared_ptr<wm::Manager> &wm) :
wm_(wm)
{
}
LayerComposer::~LayerComposer()
{
}
void LayerComposer::submit_layers(const RenderableList &renderables)
{
std::map<std::shared_ptr<wm::Window>,RenderableList> win_layers;
for (const auto &renderable : renderables)
{
// Ignore all surfaces which are not meant for a task
if (!utils::string_starts_with(renderable.name(), "org.anbox.surface."))
continue;
wm::Task::Id task_id = 0;
if (sscanf(renderable.name().c_str(), "org.anbox.surface.%d", &task_id) != 1 || !task_id)
continue;
auto w = wm_->find_window_for_task(task_id);
if (!w)
continue;
if (win_layers.find(w) == win_layers.end()) {
win_layers.insert({w, {renderable}});
continue;
}
win_layers[w].push_back(renderable);
}
for (const auto &w : win_layers)
Renderer::get()->draw(w.first->native_handle(), w.first->state().frame(), w.second);
}
} // namespace graphics
} // namespace anbox

View file

@ -0,0 +1,43 @@
/*
* 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_LAYER_COMPOSER_H_
#define ANBOX_GRAPHICS_LAYER_COMPOSER_H_
#include "anbox/graphics/emugl/Renderable.h"
#include <memory>
namespace anbox {
namespace wm {
class Manager;
} // namespace wm
namespace graphics {
class LayerComposer {
public:
LayerComposer(const std::shared_ptr<wm::Manager> &wm);
~LayerComposer();
void submit_layers(const RenderableList &renderables);
private:
std::shared_ptr<wm::Manager> wm_;
};
} // namespace graphics
} // namespace anbox
#endif

View file

@ -0,0 +1,52 @@
/*
* Copyright © 2014 Canonical Ltd.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser 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 warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Authored by: Daniel van Vugt <daniel.van.vugt@canonical.com>
* Kevin DuBois <kevin.dubois@canonical.com>
*/
#ifndef ANBOX_GRAPHICS_PRIMITIVES_H_
#define ANBOX_GRAPHICS_PRIMITIVES_H_
#include <GLES2/gl2.h>
namespace anbox {
namespace graphics {
struct Vertex
{
GLfloat position[3];
GLfloat texcoord[2];
};
struct Primitive
{
enum {max_vertices = 4};
Primitive()
: type(GL_TRIANGLE_FAN), nvertices(4)
{
// Default is a quad. Just need to assign vertices[] and tex_id.
}
GLenum type; // GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_TRIANGLES etc
GLuint tex_id; // GL texture ID (or 0 to represent the surface itself)
int nvertices;
Vertex vertices[max_vertices];
};
} // namespace graphics
} // namespace anbox
#endif

View file

@ -0,0 +1,107 @@
/*
* Copyright © 2015 Canonical Ltd.
*
* 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 warranty of
* MERCHANTABILITY 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/>.
*
* Authored by: Daniel van Vugt <daniel.van.vugt@canonical.com>
*/
#include "anbox/graphics/program_family.h"
#include "anbox/graphics/emugl/DispatchTables.h"
namespace anbox {
namespace graphics {
void ProgramFamily::Shader::init(GLenum type, const GLchar* src)
{
if (!id)
{
id = s_gles2.glCreateShader(type);
if (id)
{
s_gles2.glShaderSource(id, 1, &src, NULL);
s_gles2.glCompileShader(id);
GLint ok;
s_gles2.glGetShaderiv(id, GL_COMPILE_STATUS, &ok);
if (!ok)
{
GLchar log[1024];
s_gles2.glGetShaderInfoLog(id, sizeof log - 1, NULL, log);
log[sizeof log - 1] = '\0';
s_gles2.glDeleteShader(id);
id = 0;
throw std::runtime_error(std::string("Compile failed: ")+
log + " for:\n" + src);
}
}
}
}
ProgramFamily::~ProgramFamily() noexcept
{
// shader and program lifetimes are managed manually, so that we don't
// need any reference counting or to worry about how many copy constructions
// might have been followed by destructor calls during container resizes.
for (auto& p : program)
{
if (p.second.id)
s_gles2.glDeleteProgram(p.second.id);
}
for (auto& v : vshader)
{
if (v.second.id)
s_gles2.glDeleteShader(v.second.id);
}
for (auto& f : fshader)
{
if (f.second.id)
s_gles2.glDeleteShader(f.second.id);
}
}
GLuint ProgramFamily::add_program(const GLchar* const vshader_src,
const GLchar* const fshader_src)
{
auto& v = vshader[vshader_src];
if (!v.id) v.init(GL_VERTEX_SHADER, vshader_src);
auto& f = fshader[fshader_src];
if (!f.id) f.init(GL_FRAGMENT_SHADER, fshader_src);
auto& p = program[{v.id, f.id}];
if (!p.id)
{
p.id = s_gles2.glCreateProgram();
s_gles2.glAttachShader(p.id, v.id);
s_gles2.glAttachShader(p.id, f.id);
s_gles2.glLinkProgram(p.id);
GLint ok;
s_gles2.glGetProgramiv(p.id, GL_LINK_STATUS, &ok);
if (!ok)
{
GLchar log[1024];
s_gles2.glGetProgramInfoLog(p.id, sizeof log - 1, NULL, log);
log[sizeof log - 1] = '\0';
s_gles2.glDeleteShader(p.id);
p.id = 0;
throw std::runtime_error(std::string("Link failed: ")+log);
}
}
return p.id;
}
} // namespace graphics
} // namespace anbox

View file

@ -0,0 +1,68 @@
/*
* Copyright © 2015 Canonical Ltd.
*
* 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 warranty of
* MERCHANTABILITY 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/>.
*
* Authored by: Daniel van Vugt <daniel.van.vugt@canonical.com>
*/
#ifndef ANBOX_GRAPHICS_PROGRAM_FAMILY_H_
#define ANBOX_GRAPHICS_PROGRAM_FAMILY_H_
#include <utility>
#include <map>
#include <unordered_map>
#include <GLES2/gl2.h>
namespace anbox {
namespace graphics {
/**
* ProgramFamily represents a set of GLSL programs that are closely
* related. Programs which point to the same shader source strings will be
* made to share the same compiled shader objects.
* A secondary intention is that this class may be extended to allow the
* different programs within the family to share common patterns of uniform
* usage too.
*/
class ProgramFamily
{
public:
ProgramFamily() = default;
ProgramFamily(ProgramFamily const&) = delete;
ProgramFamily& operator=(ProgramFamily const&) = delete;
~ProgramFamily() noexcept;
GLuint add_program(const GLchar* const static_vshader_src,
const GLchar* const static_fshader_src);
private:
struct Shader
{
GLuint id = 0;
void init(GLenum type, const GLchar* src);
};
typedef std::unordered_map<const GLchar*, Shader> ShaderMap;
ShaderMap vshader, fshader;
typedef std::pair<GLuint, GLuint> ShaderPair;
struct Program
{
GLuint id = 0;
};
std::map<ShaderPair, Program> program;
};
} // namespace graphics
} // namespace anbox
#endif // MIR_RENDERER_GL_PROGRAM_FAMILY_H_

View file

@ -19,6 +19,8 @@
#include "anbox/wm/platform_policy.h"
#include "anbox/logger.h"
#include <algorithm>
namespace anbox {
namespace wm {
Manager::Manager(const std::shared_ptr<PlatformPolicy> &platform) :
@ -31,6 +33,8 @@ Manager::~Manager() {
void Manager::apply_window_state_update(const WindowState::List &updated,
const WindowState::List &removed)
{
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
@ -38,30 +42,46 @@ void Manager::apply_window_state_update(const WindowState::List &updated,
// from SurfaceFlinger will be mapped later into those windows and eventually
// composited there via GLES (e.g. for popups, ..)
for (const auto &window : updated) {
for (const auto &window : updated)
{
auto w = windows_.find(window.task());
if (w != windows_.end()) {
DEBUG("Found existing window for task %d", window.task());
if (w != windows_.end())
{
w->second->update_state(window);
continue;
}
DEBUG("Found new window for task %d", window.task());
auto platform_window = platform_->create_window(window);
platform_window->ref();
platform_window->attach();
windows_.insert({window.task(), platform_window});
}
for (const auto &window : removed) {
for (const auto &window : removed)
{
auto w = windows_.find(window.task());
if (w == windows_.end()) {
WARNING("Got remove request for window we don't know about (task id %d)", window.task());
if (w == windows_.end())
continue;
w->second->unref();
if (!w->second->still_used())
{
auto platform_window = w->second;
platform_window->release();
windows_.erase(w);
}
DEBUG("Removing window for task %d", window.task());
auto platform_window = w->second;
platform_window->release();
windows_.erase(w);
}
}
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)
return w.second;
}
return nullptr;
}
} // namespace wm
} // namespace anbox

View file

@ -23,6 +23,7 @@
#include <memory>
#include <map>
#include <mutex>
namespace anbox {
namespace wm {
@ -35,7 +36,10 @@ public:
void apply_window_state_update(const WindowState::List &updated,
const WindowState::List &removed);
std::shared_ptr<Window> find_window_for_task(const Task::Id &task);
private:
std::mutex mutex_;
std::shared_ptr<PlatformPolicy> platform_;
std::map<Task::Id, std::shared_ptr<Window>> windows_;
};

View file

@ -17,18 +17,24 @@
#include "anbox/wm/window.h"
#include "anbox/graphics/emugl/Renderer.h"
#include "anbox/logger.h"
namespace anbox {
namespace wm {
Window::Window(const WindowState &state) :
state_(state) {
state_(state),
refcount_(0) {
}
Window::~Window() {
}
void Window::update_state(const WindowState &state) {
state_ = state;
void Window::update_state(const WindowState &new_state) {
state_ = new_state;
}
WindowState Window::state() const {
return state_;
}
EGLNativeWindowType Window::native_handle() const {
@ -42,6 +48,23 @@ 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,12 +42,16 @@ class Window
public:
typedef std::vector<Window> List;
Window(const WindowState &state);
Window(const WindowState &new_state);
virtual ~Window();
bool attach();
void release();
void ref();
void unref();
bool still_used() const;
void update_state(const WindowState &state);
virtual EGLNativeWindowType native_handle() const;
@ -55,6 +59,7 @@ public:
private:
WindowState state_;
std::uint32_t refcount_;
};
} // namespace wm
} // namespace anbox