2 * Copyright (C) 2011-2015 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 #define GLM_ENABLE_EXPERIMENTAL
17 #include "anbox/graphics/emugl/Renderer.h"
18 #include "anbox/graphics/emugl/DispatchTables.h"
19 #include "anbox/graphics/emugl/RenderThreadInfo.h"
20 #include "anbox/graphics/emugl/TimeUtils.h"
21 #include "anbox/graphics/gl_extensions.h"
22 #include "anbox/logger.h"
24 #include "external/android-emugl/host/include/OpenGLESDispatch/EGLDispatch.h"
26 // Generated with emugl at build time
27 #include "gles2_dec.h"
31 #include <glm/glm.hpp>
32 #include <glm/gtc/type_ptr.hpp>
33 #include <glm/gtx/transform.hpp>
37 // Helper class to call the bind_locked() / unbind_locked() properly.
40 // Constructor will call bind_locked() on |fb|.
41 // Use isValid() to check for errors.
42 ScopedBind(Renderer *fb) : mFb(fb) {
43 if (!fb->bind_locked()) {
48 // Returns true if contruction bound the framebuffer context properly.
49 bool isValid() const { return mFb != NULL; }
51 // Unbound the framebuffer explictly. This is also called by the
60 // Destructor will call release().
61 ~ScopedBind() { release(); }
67 // Implementation of a ColorBuffer::Helper instance that redirects calls
68 // to a FrameBuffer instance.
69 class ColorBufferHelper : public ColorBuffer::Helper {
71 ColorBufferHelper(Renderer *fb) : mFb(fb) {}
73 virtual bool setupContext() { return mFb->bind_locked(); }
75 virtual void teardownContext() { mFb->unbind_locked(); }
77 virtual TextureDraw *getTextureDraw() const { return mFb->getTextureDraw(); }
84 HandleType Renderer::s_nextHandle = 0;
86 void Renderer::finalize() {
87 m_colorbuffers.clear();
90 s_egl.eglMakeCurrent(m_eglDisplay, NULL, NULL, NULL);
91 s_egl.eglDestroyContext(m_eglDisplay, m_eglContext);
92 s_egl.eglDestroyContext(m_eglDisplay, m_pbufContext);
93 s_egl.eglDestroySurface(m_eglDisplay, m_pbufSurface);
96 bool Renderer::initialize(EGLNativeDisplayType nativeDisplay) {
97 m_eglDisplay = s_egl.eglGetDisplay(nativeDisplay);
98 if (m_eglDisplay == EGL_NO_DISPLAY) {
99 ERROR("Failed to Initialize backend EGL display");
103 if (!s_egl.eglInitialize(m_eglDisplay, &m_caps.eglMajor, &m_caps.eglMinor)) {
104 ERROR("Failed to initialize EGL");
108 anbox::graphics::GLExtensions egl_extensions{s_egl.eglQueryString(m_eglDisplay, EGL_EXTENSIONS)};
110 const auto surfaceless_supported = egl_extensions.support("EGL_KHR_surfaceless_context");
111 if (!surfaceless_supported)
112 DEBUG("EGL doesn't support surfaceless context");
114 s_egl.eglBindAPI(EGL_OPENGL_ES_API);
116 // Create EGL context for framebuffer post rendering.
117 GLint surfaceType = EGL_WINDOW_BIT | EGL_PBUFFER_BIT;
118 const GLint configAttribs[] = {EGL_RED_SIZE, 8,
121 EGL_SURFACE_TYPE, surfaceType,
122 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
126 if ((s_egl.eglChooseConfig(m_eglDisplay, configAttribs, &m_eglConfig,
127 1, &n) == EGL_FALSE) || n == 0) {
128 ERROR("Failed to select EGL configuration");
132 static const GLint glContextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2,
135 m_eglContext = s_egl.eglCreateContext(m_eglDisplay, m_eglConfig,
136 EGL_NO_CONTEXT, glContextAttribs);
137 if (m_eglContext == EGL_NO_CONTEXT) {
138 ERROR("Failed to create context: error=0x%x", s_egl.eglGetError());
142 // Create another context which shares with the eglContext to be used
143 // when we bind the pbuffer. That prevent switching drawable binding
144 // back and forth on framebuffer context.
145 // The main purpose of it is to solve a "blanking" behaviour we see on
146 // on Mac platform when switching binded drawable for a context however
147 // it is more efficient on other platforms as well.
148 m_pbufContext = s_egl.eglCreateContext(m_eglDisplay, m_eglConfig, m_eglContext, glContextAttribs);
149 if (m_pbufContext == EGL_NO_CONTEXT) {
150 ERROR("Failed to create pbuffer context: error=0x%x", s_egl.eglGetError());
154 if (!surfaceless_supported) {
155 // Create a 1x1 pbuffer surface which will be used for binding
156 // the FB context. The FB output will go to a subwindow, if one exist.
157 static const EGLint pbufAttribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE};
159 m_pbufSurface = s_egl.eglCreatePbufferSurface(m_eglDisplay, m_eglConfig, pbufAttribs);
160 if (m_pbufSurface == EGL_NO_SURFACE) {
161 ERROR("Failed to create pbuffer surface: error=0x%x", s_egl.eglGetError());
165 DEBUG("Using a surfaceless EGL context");
166 m_pbufSurface = EGL_NO_SURFACE;
169 // Make the context current
170 ScopedBind bind(this);
171 if (!bind.isValid()) {
172 ERROR("Failed to make current");
176 anbox::graphics::GLExtensions gl_extensions{reinterpret_cast<const char *>(s_gles2.glGetString(GL_EXTENSIONS))};
177 if (gl_extensions.support("GL_OES_EGL_image")) {
178 m_caps.has_eglimage_texture_2d = egl_extensions.support("EGL_KHR_gl_texture_2D_image");
179 m_caps.has_eglimage_renderbuffer = egl_extensions.support("EGL_KHR_gl_renderbuffer_image");
181 m_caps.has_eglimage_texture_2d = false;
182 m_caps.has_eglimage_renderbuffer = false;
185 // Fail initialization if not all of the following extensions
187 // EGL_KHR_gl_texture_2d_image
189 if (!m_caps.has_eglimage_texture_2d) {
190 ERROR("Failed: Missing egl_image related extension(s)");
195 // Initialize set of configs
196 m_configs = new RendererConfigList(m_eglDisplay);
197 if (m_configs->empty()) {
198 ERROR("Failed: Initialize set of configs");
203 // Check that we have config for each GLES and GLES2
204 size_t nConfigs = m_configs->size();
207 for (size_t i = 0; i < nConfigs; ++i) {
208 GLint rtype = m_configs->get(i)->getRenderableType();
209 if (0 != (rtype & EGL_OPENGL_ES_BIT)) {
212 if (0 != (rtype & EGL_OPENGL_ES2_BIT)) {
217 // Fail initialization if no GLES configs exist
218 if (nGLConfigs == 0) {
223 // If no GLES2 configs exist - not GLES2 capability
224 if (nGL2Configs == 0) {
225 ERROR("Failed: No GLES 2.x configs found!");
230 // Cache the GL strings so we don't have to think about threading or
231 // current-context when asked for them.
232 m_glVendor = reinterpret_cast<const char *>(s_gles2.glGetString(GL_VENDOR));
233 m_glRenderer = reinterpret_cast<const char *>(s_gles2.glGetString(GL_RENDERER));
234 m_glVersion = reinterpret_cast<const char *>(s_gles2.glGetString(GL_VERSION));
236 m_textureDraw = new TextureDraw(m_eglDisplay);
237 if (!m_textureDraw) {
238 ERROR("Failed: creation of TextureDraw instance");
243 m_defaultProgram = m_family.add_program(vshader, defaultFShader);
244 m_alphaProgram = m_family.add_program(vshader, alphaFShader);
248 DEBUG("Successfully initialized EGL");
253 Renderer::Program::Program(GLuint program_id) {
255 position_attr = s_gles2.glGetAttribLocation(id, "position");
256 texcoord_attr = s_gles2.glGetAttribLocation(id, "texcoord");
257 tex_uniform = s_gles2.glGetUniformLocation(id, "tex");
258 center_uniform = s_gles2.glGetUniformLocation(id, "center");
259 display_transform_uniform =
260 s_gles2.glGetUniformLocation(id, "display_transform");
261 transform_uniform = s_gles2.glGetUniformLocation(id, "transform");
262 screen_to_gl_coords_uniform =
263 s_gles2.glGetUniformLocation(id, "screen_to_gl_coords");
264 alpha_uniform = s_gles2.glGetUniformLocation(id, "alpha");
269 m_eglDisplay(EGL_NO_DISPLAY),
270 m_colorBufferHelper(new ColorBufferHelper(this)),
271 m_eglContext(EGL_NO_CONTEXT),
272 m_pbufContext(EGL_NO_CONTEXT),
273 m_prevContext(EGL_NO_CONTEXT),
274 m_prevReadSurf(EGL_NO_SURFACE),
275 m_prevDrawSurf(EGL_NO_SURFACE),
277 m_lastPostedColorBuffer(0),
279 m_statsStartTime(0LL),
283 m_fpsStats = getenv("SHOW_FPS_STATS") != NULL;
286 Renderer::~Renderer() {
287 delete m_textureDraw;
289 delete m_colorBufferHelper;
292 struct RendererWindow {
293 EGLNativeWindowType native_window = 0;
294 EGLSurface surface = EGL_NO_SURFACE;
295 anbox::graphics::Rect viewport;
296 glm::mat4 screen_to_gl_coords = glm::mat4(1.0f);
297 glm::mat4 display_transform = glm::mat4(1.0f);
300 RendererWindow *Renderer::createNativeWindow(
301 EGLNativeWindowType native_window) {
304 auto window = new RendererWindow;
305 window->native_window = native_window;
306 window->surface = s_egl.eglCreateWindowSurface(
307 m_eglDisplay, m_eglConfig, window->native_window, nullptr);
308 if (window->surface == EGL_NO_SURFACE) {
314 if (!bindWindow_locked(window)) {
315 s_egl.eglDestroySurface(m_eglDisplay, window->surface);
321 s_gles2.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT |
322 GL_STENCIL_BUFFER_BIT);
323 s_egl.eglSwapBuffers(m_eglDisplay, window->surface);
327 m_nativeWindows.insert({native_window, window});
334 void Renderer::destroyNativeWindow(EGLNativeWindowType native_window) {
335 auto w = m_nativeWindows.find(native_window);
336 if (w == m_nativeWindows.end()) return;
340 s_egl.eglMakeCurrent(m_eglDisplay, nullptr, nullptr, nullptr);
342 if (w->second->surface != EGL_NO_SURFACE)
343 s_egl.eglDestroySurface(m_eglDisplay, w->second->surface);
346 m_nativeWindows.erase(w);
351 HandleType Renderer::genHandle() {
355 } while (id == 0 || m_contexts.find(id) != m_contexts.end() ||
356 m_windows.find(id) != m_windows.end());
361 HandleType Renderer::createColorBuffer(int p_width, int p_height,
362 GLenum p_internalFormat) {
363 std::unique_lock<std::mutex> l(m_lock);
367 ColorBufferPtr cb(ColorBuffer::create(
368 getDisplay(), p_width, p_height, p_internalFormat,
369 getCaps().has_eglimage_texture_2d, m_colorBufferHelper));
372 m_colorbuffers[ret].cb = cb;
373 m_colorbuffers[ret].refcount = 1;
378 HandleType Renderer::createRenderContext(int p_config, HandleType p_share,
380 std::unique_lock<std::mutex> l(m_lock);
384 const RendererConfig *config = getConfigs()->get(p_config);
389 RenderContextPtr share(NULL);
391 RenderContextMap::iterator s(m_contexts.find(p_share));
392 if (s == m_contexts.end()) {
397 EGLContext sharedContext =
398 share ? share->getEGLContext() : EGL_NO_CONTEXT;
400 RenderContextPtr rctx(RenderContext::create(
401 m_eglDisplay, config->getEglConfig(), sharedContext, p_isGL2));
404 m_contexts[ret] = rctx;
405 RenderThreadInfo *tinfo = RenderThreadInfo::get();
406 tinfo->m_contextSet.insert(ret);
411 HandleType Renderer::createWindowSurface(int p_config, int p_width,
413 std::unique_lock<std::mutex> l(m_lock);
417 const RendererConfig *config = getConfigs()->get(p_config);
422 WindowSurfacePtr win(WindowSurface::create(
423 getDisplay(), config->getEglConfig(), p_width, p_height));
426 m_windows[ret] = std::pair<WindowSurfacePtr, HandleType>(win, 0);
427 RenderThreadInfo *tinfo = RenderThreadInfo::get();
428 tinfo->m_windowSet.insert(ret);
434 void Renderer::drainRenderContext() {
435 std::unique_lock<std::mutex> l(m_lock);
437 RenderThreadInfo *tinfo = RenderThreadInfo::get();
438 if (tinfo->m_contextSet.empty()) return;
439 for (std::set<HandleType>::iterator it = tinfo->m_contextSet.begin();
440 it != tinfo->m_contextSet.end(); ++it) {
441 HandleType contextHandle = *it;
442 m_contexts.erase(contextHandle);
444 tinfo->m_contextSet.clear();
447 void Renderer::drainWindowSurface() {
448 std::unique_lock<std::mutex> l(m_lock);
450 RenderThreadInfo *tinfo = RenderThreadInfo::get();
451 if (tinfo->m_windowSet.empty()) return;
452 for (std::set<HandleType>::iterator it = tinfo->m_windowSet.begin();
453 it != tinfo->m_windowSet.end(); ++it) {
454 HandleType windowHandle = *it;
455 if (m_windows.find(windowHandle) != m_windows.end()) {
456 HandleType oldColorBufferHandle = m_windows[windowHandle].second;
457 if (oldColorBufferHandle) {
458 ColorBufferMap::iterator cit(m_colorbuffers.find(oldColorBufferHandle));
459 if (cit != m_colorbuffers.end()) {
460 if (--(*cit).second.refcount == 0) {
461 m_colorbuffers.erase(cit);
465 m_windows.erase(windowHandle);
468 tinfo->m_windowSet.clear();
471 void Renderer::DestroyRenderContext(HandleType p_context) {
472 std::unique_lock<std::mutex> l(m_lock);
474 m_contexts.erase(p_context);
475 RenderThreadInfo *tinfo = RenderThreadInfo::get();
476 if (tinfo->m_contextSet.empty()) return;
477 tinfo->m_contextSet.erase(p_context);
480 void Renderer::DestroyWindowSurface(HandleType p_surface) {
481 std::unique_lock<std::mutex> l(m_lock);
483 if (m_windows.find(p_surface) != m_windows.end()) {
484 m_windows.erase(p_surface);
485 RenderThreadInfo *tinfo = RenderThreadInfo::get();
486 if (tinfo->m_windowSet.empty()) return;
487 tinfo->m_windowSet.erase(p_surface);
491 int Renderer::openColorBuffer(HandleType p_colorbuffer) {
492 std::unique_lock<std::mutex> l(m_lock);
494 ColorBufferMap::iterator c(m_colorbuffers.find(p_colorbuffer));
495 if (c == m_colorbuffers.end()) {
496 // bad colorbuffer handle
497 ERROR("FB: openColorBuffer cb handle %#x not found", p_colorbuffer);
500 (*c).second.refcount++;
504 void Renderer::closeColorBuffer(HandleType p_colorbuffer) {
505 std::unique_lock<std::mutex> l(m_lock);
507 ColorBufferMap::iterator c(m_colorbuffers.find(p_colorbuffer));
508 if (c == m_colorbuffers.end()) {
509 // This is harmless: it is normal for guest system to issue
510 // closeColorBuffer command when the color buffer is already
511 // garbage collected on the host. (we dont have a mechanism
512 // to give guest a notice yet)
515 if (--(*c).second.refcount == 0) {
516 m_colorbuffers.erase(c);
520 bool Renderer::flushWindowSurfaceColorBuffer(HandleType p_surface) {
521 std::unique_lock<std::mutex> l(m_lock);
523 WindowSurfaceMap::iterator w(m_windows.find(p_surface));
524 if (w == m_windows.end()) {
525 ERROR("FB::flushWindowSurfaceColorBuffer: window handle %#x not found",
527 // bad surface handle
531 auto surface = (*w).second.first;
535 surface->flushColorBuffer();
540 bool Renderer::setWindowSurfaceColorBuffer(HandleType p_surface,
541 HandleType p_colorbuffer) {
542 std::unique_lock<std::mutex> l(m_lock);
544 WindowSurfaceMap::iterator w(m_windows.find(p_surface));
545 if (w == m_windows.end()) {
546 // bad surface handle
547 ERROR("%s: bad window surface handle %#x", __FUNCTION__, p_surface);
551 ColorBufferMap::iterator c(m_colorbuffers.find(p_colorbuffer));
552 if (c == m_colorbuffers.end()) {
553 DEBUG("%s: bad color buffer handle %#x", __FUNCTION__, p_colorbuffer);
554 // bad colorbuffer handle
558 (*w).second.first->setColorBuffer((*c).second.cb);
559 (*w).second.second = p_colorbuffer;
563 void Renderer::readColorBuffer(HandleType p_colorbuffer, int x, int y,
564 int width, int height, GLenum format,
565 GLenum type, void *pixels) {
566 std::unique_lock<std::mutex> l(m_lock);
568 ColorBufferMap::iterator c(m_colorbuffers.find(p_colorbuffer));
569 if (c == m_colorbuffers.end()) {
570 // bad colorbuffer handle
574 (*c).second.cb->readPixels(x, y, width, height, format, type, pixels);
577 bool Renderer::updateColorBuffer(HandleType p_colorbuffer, int x, int y,
578 int width, int height, GLenum format,
579 GLenum type, void *pixels) {
580 std::unique_lock<std::mutex> l(m_lock);
582 ColorBufferMap::iterator c(m_colorbuffers.find(p_colorbuffer));
583 if (c == m_colorbuffers.end()) {
584 // bad colorbuffer handle
588 (*c).second.cb->subUpdate(x, y, width, height, format, type, pixels);
593 bool Renderer::bindColorBufferToTexture(HandleType p_colorbuffer) {
594 std::unique_lock<std::mutex> l(m_lock);
596 ColorBufferMap::iterator c(m_colorbuffers.find(p_colorbuffer));
597 if (c == m_colorbuffers.end()) {
598 // bad colorbuffer handle
602 return (*c).second.cb->bindToTexture();
605 bool Renderer::bindColorBufferToRenderbuffer(HandleType p_colorbuffer) {
606 std::unique_lock<std::mutex> l(m_lock);
608 ColorBufferMap::iterator c(m_colorbuffers.find(p_colorbuffer));
609 if (c == m_colorbuffers.end()) {
610 // bad colorbuffer handle
614 return (*c).second.cb->bindToRenderbuffer();
617 bool Renderer::bindContext(HandleType p_context, HandleType p_drawSurface,
618 HandleType p_readSurface) {
619 std::unique_lock<std::mutex> l(m_lock);
621 WindowSurfacePtr draw(NULL), read(NULL);
622 RenderContextPtr ctx(NULL);
625 // if this is not an unbind operation - make sure all handles are good
627 if (p_context || p_drawSurface || p_readSurface) {
628 RenderContextMap::iterator r(m_contexts.find(p_context));
629 if (r == m_contexts.end()) {
630 // bad context handle
635 WindowSurfaceMap::iterator w(m_windows.find(p_drawSurface));
636 if (w == m_windows.end()) {
637 // bad surface handle
640 draw = (*w).second.first;
642 if (p_readSurface != p_drawSurface) {
643 WindowSurfaceMap::iterator w(m_windows.find(p_readSurface));
644 if (w == m_windows.end()) {
645 // bad surface handle
648 read = (*w).second.first;
654 if (!s_egl.eglMakeCurrent(m_eglDisplay,
655 draw ? draw->getEGLSurface() : EGL_NO_SURFACE,
656 read ? read->getEGLSurface() : EGL_NO_SURFACE,
657 ctx ? ctx->getEGLContext() : EGL_NO_CONTEXT)) {
658 ERROR("eglMakeCurrent failed: 0x%04x", s_egl.eglGetError());
663 // Bind the surface(s) to the context
665 RenderThreadInfo *tinfo = RenderThreadInfo::get();
666 WindowSurfacePtr bindDraw, bindRead;
667 if (!draw && !read) {
668 // Unbind the current read and draw surfaces from the context
669 bindDraw = tinfo->currDrawSurf;
670 bindRead = tinfo->currReadSurf;
676 if (bindDraw && bindRead) {
677 if (bindDraw != bindRead) {
678 bindDraw->bind(ctx, WindowSurface::BIND_DRAW);
679 bindRead->bind(ctx, WindowSurface::BIND_READ);
681 bindDraw->bind(ctx, WindowSurface::BIND_READDRAW);
686 // update thread info with current bound context
688 tinfo->currContext = ctx;
689 tinfo->currDrawSurf = draw;
690 tinfo->currReadSurf = read;
693 tinfo->m_gl2Dec.setContextData(&ctx->decoderContextData());
695 tinfo->m_glDec.setContextData(&ctx->decoderContextData());
697 tinfo->m_glDec.setContextData(NULL);
698 tinfo->m_gl2Dec.setContextData(NULL);
703 HandleType Renderer::createClientImage(HandleType context, EGLenum target,
705 RenderContextPtr ctx(NULL);
708 RenderContextMap::iterator r(m_contexts.find(context));
709 if (r == m_contexts.end()) {
710 // bad context handle
717 EGLContext eglContext = ctx ? ctx->getEGLContext() : EGL_NO_CONTEXT;
719 s_egl.eglCreateImageKHR(m_eglDisplay, eglContext, target,
720 reinterpret_cast<EGLClientBuffer>(buffer), NULL);
722 return static_cast<HandleType>(reinterpret_cast<uintptr_t>(image));
725 EGLBoolean Renderer::destroyClientImage(HandleType image) {
726 return s_egl.eglDestroyImageKHR(m_eglDisplay,
727 reinterpret_cast<EGLImageKHR>(image));
731 // The framebuffer lock should be held when calling this function !
733 bool Renderer::bind_locked() {
734 EGLContext prevContext = s_egl.eglGetCurrentContext();
735 EGLSurface prevReadSurf = s_egl.eglGetCurrentSurface(EGL_READ);
736 EGLSurface prevDrawSurf = s_egl.eglGetCurrentSurface(EGL_DRAW);
738 if (!s_egl.eglMakeCurrent(m_eglDisplay, m_pbufSurface, m_pbufSurface,
740 ERROR("eglMakeCurrent failed: 0x%04x", s_egl.eglGetError());
744 m_prevContext = prevContext;
745 m_prevReadSurf = prevReadSurf;
746 m_prevDrawSurf = prevDrawSurf;
750 bool Renderer::bindWindow_locked(RendererWindow *window) {
751 EGLContext prevContext = s_egl.eglGetCurrentContext();
752 EGLSurface prevReadSurf = s_egl.eglGetCurrentSurface(EGL_READ);
753 EGLSurface prevDrawSurf = s_egl.eglGetCurrentSurface(EGL_DRAW);
755 if (!s_egl.eglMakeCurrent(m_eglDisplay, window->surface, window->surface,
757 ERROR("eglMakeCurrent failed");
761 m_prevContext = prevContext;
762 m_prevReadSurf = prevReadSurf;
763 m_prevDrawSurf = prevDrawSurf;
767 bool Renderer::unbind_locked() {
768 if (!s_egl.eglMakeCurrent(m_eglDisplay, m_prevDrawSurf, m_prevReadSurf,
773 m_prevContext = EGL_NO_CONTEXT;
774 m_prevReadSurf = EGL_NO_SURFACE;
775 m_prevDrawSurf = EGL_NO_SURFACE;
779 const GLchar *const Renderer::vshader = {
780 "attribute vec3 position;"
781 "attribute vec2 texcoord;"
782 "uniform mat4 screen_to_gl_coords;"
783 "uniform mat4 display_transform;"
784 "uniform mat4 transform;"
785 "uniform vec2 center;"
786 "varying vec2 v_texcoord;"
788 " vec4 mid = vec4(center, 0.0, 0.0);"
789 " vec4 transformed = (transform * (vec4(position, 1.0) - mid)) + mid;"
790 " gl_Position = display_transform * screen_to_gl_coords * transformed;"
791 " v_texcoord = texcoord;"
794 const GLchar *const Renderer::alphaFShader = {
795 "precision mediump float;"
796 "uniform sampler2D tex;"
797 "uniform float alpha;"
798 "varying vec2 v_texcoord;"
800 " vec4 frag = texture2D(tex, v_texcoord);"
801 " gl_FragColor = alpha*frag;"
804 const GLchar *const Renderer::defaultFShader =
805 { // This is the fastest fragment shader. Use it when you can.
806 "precision mediump float;"
807 "uniform sampler2D tex;"
808 "varying vec2 v_texcoord;"
810 " gl_FragColor = texture2D(tex, v_texcoord);"
813 void Renderer::setupViewport(RendererWindow *window,
814 const anbox::graphics::Rect &rect) {
816 * Here we provide a 3D perspective projection with a default 30 degrees
817 * vertical field of view. This projection matrix is carefully designed
818 * such that any vertices at depth z=0 will fit the screen coordinates. So
819 * client texels will fit screen pixels perfectly as long as the surface is
820 * at depth zero. But if you want to do anything fancy, you can also choose
821 * a different depth and it will appear to come out of or go into the
824 window->screen_to_gl_coords =
825 glm::translate(glm::mat4(1.0f), glm::vec3{-1.0f, 1.0f, 0.0f});
828 * Perspective division is one thing that can't be done in a matrix
829 * multiplication. It happens after the matrix multiplications. GL just
830 * scales {x,y} by 1/w. So modify the final part of the projection matrix
831 * to set w ([3]) to be the incoming z coordinate ([2]).
833 window->screen_to_gl_coords[2][3] = -1.0f;
835 float const vertical_fov_degrees = 30.0f;
836 float const near = (rect.height() / 2.0f) /
837 std::tan((vertical_fov_degrees * M_PI / 180.0f) / 2.0f);
838 float const far = -near;
840 window->screen_to_gl_coords =
841 glm::scale(window->screen_to_gl_coords,
842 glm::vec3{2.0f / rect.width(), -2.0f / rect.height(),
843 2.0f / (near - far)});
844 window->screen_to_gl_coords = glm::translate(
845 window->screen_to_gl_coords, glm::vec3{-rect.left(), -rect.top(), 0.0f});
847 window->viewport = rect;
850 void Renderer::tessellate(std::vector<anbox::graphics::Primitive> &primitives,
851 const anbox::graphics::Rect &buf_size,
852 const Renderable &renderable) {
853 auto rect = renderable.screen_position();
854 GLfloat left = rect.left();
855 GLfloat right = rect.right();
856 GLfloat top = rect.top();
857 GLfloat bottom = rect.bottom();
859 anbox::graphics::Primitive rectangle;
860 rectangle.tex_id = 0;
861 rectangle.type = GL_TRIANGLE_STRIP;
864 static_cast<GLfloat>(renderable.crop().left()) / buf_size.width();
866 static_cast<GLfloat>(renderable.crop().top()) / buf_size.height();
868 static_cast<GLfloat>(renderable.crop().right()) / buf_size.width();
870 static_cast<GLfloat>(renderable.crop().bottom()) / buf_size.height();
872 auto &vertices = rectangle.vertices;
873 vertices[0] = {{left, top, 0.0f}, {tex_left, tex_top}};
874 vertices[1] = {{left, bottom, 0.0f}, {tex_left, tex_bottom}};
875 vertices[2] = {{right, top, 0.0f}, {tex_right, tex_top}};
876 vertices[3] = {{right, bottom, 0.0f}, {tex_right, tex_bottom}};
878 primitives.resize(1);
879 primitives[0] = rectangle;
882 void Renderer::draw(RendererWindow *window, const Renderable &renderable,
883 const Program &prog) {
884 const auto &color_buffer = m_colorbuffers.find(renderable.buffer());
885 if (color_buffer == m_colorbuffers.end()) return;
887 const auto &cb = color_buffer->second.cb;
889 s_gles2.glUseProgram(prog.id);
890 s_gles2.glUniform1i(prog.tex_uniform, 0);
891 s_gles2.glUniformMatrix4fv(prog.display_transform_uniform, 1, GL_FALSE,
892 glm::value_ptr(window->display_transform));
893 s_gles2.glUniformMatrix4fv(prog.screen_to_gl_coords_uniform, 1, GL_FALSE,
894 glm::value_ptr(window->screen_to_gl_coords));
896 s_gles2.glActiveTexture(GL_TEXTURE0);
898 auto const &rect = renderable.screen_position();
899 GLfloat centerx = rect.left() + rect.width() / 2.0f;
900 GLfloat centery = rect.top() + rect.height() / 2.0f;
901 s_gles2.glUniform2f(prog.center_uniform, centerx, centery);
903 s_gles2.glUniformMatrix4fv(prog.transform_uniform, 1, GL_FALSE,
904 glm::value_ptr(renderable.transformation()));
906 if (prog.alpha_uniform >= 0)
907 s_gles2.glUniform1f(prog.alpha_uniform, renderable.alpha());
909 s_gles2.glEnableVertexAttribArray(prog.position_attr);
910 s_gles2.glEnableVertexAttribArray(prog.texcoord_attr);
912 m_primitives.clear();
913 tessellate(m_primitives, {
914 static_cast<int32_t>(cb->getWidth()),
915 static_cast<int32_t>(cb->getHeight())}, renderable);
917 for (auto const &p : m_primitives) {
920 s_gles2.glVertexAttribPointer(prog.position_attr, 3, GL_FLOAT, GL_FALSE,
921 sizeof(anbox::graphics::Vertex),
922 &p.vertices[0].position);
923 s_gles2.glVertexAttribPointer(prog.texcoord_attr, 2, GL_FLOAT, GL_FALSE,
924 sizeof(anbox::graphics::Vertex),
925 &p.vertices[0].texcoord);
927 s_gles2.glEnable(GL_BLEND);
928 s_gles2.glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE,
929 GL_ONE_MINUS_SRC_ALPHA);
931 s_gles2.glDrawArrays(p.type, 0, p.nvertices);
934 s_gles2.glDisableVertexAttribArray(prog.texcoord_attr);
935 s_gles2.glDisableVertexAttribArray(prog.position_attr);
938 bool Renderer::draw(EGLNativeWindowType native_window,
939 const anbox::graphics::Rect &window_frame,
940 const RenderableList &renderables) {
942 std::unique_lock<std::mutex> l(m_lock);
944 auto w = m_nativeWindows.find(native_window);
945 if (w == m_nativeWindows.end()) return false;
947 if (!bindWindow_locked(w->second))
950 setupViewport(w->second, window_frame);
951 s_gles2.glViewport(0, 0, window_frame.width(), window_frame.height());
952 s_gles2.glClearColor(0.0, 0.0, 0.0, 1.0);
953 s_gles2.glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
954 s_gles2.glClear(GL_COLOR_BUFFER_BIT);
956 for (const auto &r : renderables)
957 draw(w->second, r, r.alpha() < 1.0f ? m_alphaProgram : m_defaultProgram);
959 s_egl.eglSwapBuffers(m_eglDisplay, w->second->surface);