diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3d4130f..84d5209 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/anbox/cmds/run.cpp b/src/anbox/cmds/run.cpp index 26c7c9d..950fea8 100644 --- a/src/anbox/cmds/run.cpp +++ b/src/anbox/cmds/run.cpp @@ -95,7 +95,7 @@ anbox::cmds::Run::Run(const BusFactory& bus_factory) auto window_manager = std::make_shared(policy); - auto renderer = std::make_shared(); + auto renderer = std::make_shared(window_manager); renderer->start(); // Socket which will be used by the qemud service inside the Android diff --git a/src/anbox/graphics/emugl/ColorBuffer.cpp b/src/anbox/graphics/emugl/ColorBuffer.cpp index 270ccac..2537503 100644 --- a/src/anbox/graphics/emugl/ColorBuffer.cpp +++ b/src/anbox/graphics/emugl/ColorBuffer.cpp @@ -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); +} diff --git a/src/anbox/graphics/emugl/ColorBuffer.h b/src/anbox/graphics/emugl/ColorBuffer.h index 152b16b..6eb48a3 100644 --- a/src/anbox/graphics/emugl/ColorBuffer.h +++ b/src/anbox/graphics/emugl/ColorBuffer.h @@ -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. diff --git a/src/anbox/graphics/emugl/RenderControl.cpp b/src/anbox/graphics/emugl/RenderControl.cpp index 9c971cc..ef32831 100644 --- a/src/anbox/graphics/emugl/RenderControl.cpp +++ b/src/anbox/graphics/emugl/RenderControl.cpp @@ -24,10 +24,19 @@ #include "OpenGLESDispatch/EGLDispatch.h" +#include "anbox/graphics/layer_composer.h" +#include "anbox/logger.h" + #include #include static const GLint rendererVersion = 1; +static std::shared_ptr composer; + +void registerLayerComposer(const std::shared_ptr &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 frame_layers; + +bool is_layer_blacklisted(const std::string &name) { + static std::vector 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) diff --git a/src/anbox/graphics/emugl/RenderControl.h b/src/anbox/graphics/emugl/RenderControl.h index 87fe1e0..2867acc 100644 --- a/src/anbox/graphics/emugl/RenderControl.h +++ b/src/anbox/graphics/emugl/RenderControl.h @@ -18,6 +18,15 @@ #include "renderControl_dec.h" +#include + +namespace anbox { +namespace graphics { +class LayerComposer; +} // namespace graphics +} // namespace anbox + void initRenderControlContext(renderControl_decoder_context_t *dec); +void registerLayerComposer(const std::shared_ptr &c); #endif diff --git a/src/anbox/graphics/emugl/Renderable.cpp b/src/anbox/graphics/emugl/Renderable.cpp index b68b984..f11f7be 100644 --- a/src/anbox/graphics/emugl/Renderable.cpp +++ b/src/anbox/graphics/emugl/Renderable.cpp @@ -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_; +} diff --git a/src/anbox/graphics/emugl/Renderable.h b/src/anbox/graphics/emugl/Renderable.h index 164f59f..fd6874d 100644 --- a/src/anbox/graphics/emugl/Renderable.h +++ b/src/anbox/graphics/emugl/Renderable.h @@ -19,23 +19,35 @@ #include "anbox/graphics/rect.h" +#include #include #include +#include + 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 RenderableList; diff --git a/src/anbox/graphics/emugl/Renderer.cpp b/src/anbox/graphics/emugl/Renderer.cpp index f6c67a0..7c8eac2 100644 --- a/src/anbox/graphics/emugl/Renderer.cpp +++ b/src/anbox/graphics/emugl/Renderer.cpp @@ -25,8 +25,14 @@ #include "emugl/common/logging.h" +#include "anbox/logger.h" + #include +#include +#include +#include + 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& 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(rect.width()) / + buf_size.width(); + GLfloat tex_bottom = static_cast(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); diff --git a/src/anbox/graphics/emugl/Renderer.h b/src/anbox/graphics/emugl/Renderer.h index 4ef0851..525df05 100644 --- a/src/anbox/graphics/emugl/Renderer.h +++ b/src/anbox/graphics/emugl/Renderer.h @@ -27,6 +27,9 @@ #include "Renderable.h" +#include "anbox/graphics/program_family.h" +#include "anbox/graphics/primitives.h" + #include #include @@ -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& 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 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 m_primitives; + + static const GLchar* const vshader; + static const GLchar* const defaultFShader; + static const GLchar* const alphaFShader; }; #endif diff --git a/src/anbox/graphics/gl_renderer_server.cpp b/src/anbox/graphics/gl_renderer_server.cpp index 60d02cd..e770c79 100644 --- a/src/anbox/graphics/gl_renderer_server.cpp +++ b/src/anbox/graphics/gl_renderer_server.cpp @@ -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) : + wm_(wm), + composer_(std::make_shared(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() { diff --git a/src/anbox/graphics/gl_renderer_server.h b/src/anbox/graphics/gl_renderer_server.h index f6159cf..7bee80d 100644 --- a/src/anbox/graphics/gl_renderer_server.h +++ b/src/anbox/graphics/gl_renderer_server.h @@ -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); ~GLRendererServer(); void start(); @@ -38,6 +41,8 @@ public: private: std::string socket_path_; + std::shared_ptr wm_; + std::shared_ptr composer_; }; } // namespace graphics diff --git a/src/anbox/graphics/layer_composer.cpp b/src/anbox/graphics/layer_composer.cpp new file mode 100644 index 0000000..a0cb6c1 --- /dev/null +++ b/src/anbox/graphics/layer_composer.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * 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 . + * + */ + +#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) : + wm_(wm) +{ +} + +LayerComposer::~LayerComposer() +{ +} + +void LayerComposer::submit_layers(const RenderableList &renderables) +{ + std::map,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 diff --git a/src/anbox/graphics/layer_composer.h b/src/anbox/graphics/layer_composer.h new file mode 100644 index 0000000..9127b2d --- /dev/null +++ b/src/anbox/graphics/layer_composer.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * 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 . + * + */ + +#ifndef ANBOX_GRAPHICS_LAYER_COMPOSER_H_ +#define ANBOX_GRAPHICS_LAYER_COMPOSER_H_ + +#include "anbox/graphics/emugl/Renderable.h" + +#include + +namespace anbox { +namespace wm { +class Manager; +} // namespace wm +namespace graphics { +class LayerComposer { +public: + LayerComposer(const std::shared_ptr &wm); + ~LayerComposer(); + + void submit_layers(const RenderableList &renderables); + +private: + std::shared_ptr wm_; +}; +} // namespace graphics +} // namespace anbox + +#endif diff --git a/src/anbox/graphics/primitives.h b/src/anbox/graphics/primitives.h new file mode 100644 index 0000000..5529f95 --- /dev/null +++ b/src/anbox/graphics/primitives.h @@ -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 . + * + * Authored by: Daniel van Vugt + * Kevin DuBois + */ + + +#ifndef ANBOX_GRAPHICS_PRIMITIVES_H_ +#define ANBOX_GRAPHICS_PRIMITIVES_H_ + +#include + +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 diff --git a/src/anbox/graphics/program_family.cpp b/src/anbox/graphics/program_family.cpp new file mode 100644 index 0000000..c47e49e --- /dev/null +++ b/src/anbox/graphics/program_family.cpp @@ -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 . + * + * Authored by: Daniel van Vugt + */ + +#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 diff --git a/src/anbox/graphics/program_family.h b/src/anbox/graphics/program_family.h new file mode 100644 index 0000000..98e7f88 --- /dev/null +++ b/src/anbox/graphics/program_family.h @@ -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 . + * + * Authored by: Daniel van Vugt + */ + +#ifndef ANBOX_GRAPHICS_PROGRAM_FAMILY_H_ +#define ANBOX_GRAPHICS_PROGRAM_FAMILY_H_ + +#include +#include +#include + +#include + +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 ShaderMap; + ShaderMap vshader, fshader; + + typedef std::pair ShaderPair; + struct Program + { + GLuint id = 0; + }; + std::map program; +}; +} // namespace graphics +} // namespace anbox + +#endif // MIR_RENDERER_GL_PROGRAM_FAMILY_H_ diff --git a/src/anbox/wm/manager.cpp b/src/anbox/wm/manager.cpp index a6b9fa9..62a826a 100644 --- a/src/anbox/wm/manager.cpp +++ b/src/anbox/wm/manager.cpp @@ -19,6 +19,8 @@ #include "anbox/wm/platform_policy.h" #include "anbox/logger.h" +#include + namespace anbox { namespace wm { Manager::Manager(const std::shared_ptr &platform) : @@ -31,6 +33,8 @@ Manager::~Manager() { void Manager::apply_window_state_update(const WindowState::List &updated, const WindowState::List &removed) { + std::lock_guard 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 Manager::find_window_for_task(const Task::Id &task) +{ + std::lock_guard l(mutex_); + + for (const auto &w : windows_) + { + if (w.second->state().task() == task) + return w.second; + } + return nullptr; +} } // namespace wm } // namespace anbox diff --git a/src/anbox/wm/manager.h b/src/anbox/wm/manager.h index 69c66b0..aa4eede 100644 --- a/src/anbox/wm/manager.h +++ b/src/anbox/wm/manager.h @@ -23,6 +23,7 @@ #include #include +#include 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 find_window_for_task(const Task::Id &task); + private: + std::mutex mutex_; std::shared_ptr platform_; std::map> windows_; }; diff --git a/src/anbox/wm/window.cpp b/src/anbox/wm/window.cpp index 7b611e3..9bc5edc 100644 --- a/src/anbox/wm/window.cpp +++ b/src/anbox/wm/window.cpp @@ -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 diff --git a/src/anbox/wm/window.h b/src/anbox/wm/window.h index cda1b0e..32c323f 100644 --- a/src/anbox/wm/window.h +++ b/src/anbox/wm/window.h @@ -42,12 +42,16 @@ class Window public: typedef std::vector 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