Home | History | Annotate | Download | only in media
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "content/common/gpu/media/rendering_helper.h"
      6 
      7 #include <algorithm>
      8 #include <numeric>
      9 #include <vector>
     10 
     11 #include "base/bind.h"
     12 #include "base/mac/scoped_nsautorelease_pool.h"
     13 #include "base/message_loop/message_loop.h"
     14 #include "base/strings/stringize_macros.h"
     15 #include "base/synchronization/waitable_event.h"
     16 #include "ui/gl/gl_context.h"
     17 #include "ui/gl/gl_context_stub_with_extensions.h"
     18 #include "ui/gl/gl_implementation.h"
     19 #include "ui/gl/gl_surface.h"
     20 
     21 #if defined(OS_WIN)
     22 #include <windows.h>
     23 #endif
     24 
     25 #if defined(USE_X11)
     26 #include "ui/gfx/x/x11_types.h"
     27 #endif
     28 
     29 #ifdef GL_VARIANT_GLX
     30 struct XFreeDeleter {
     31   void operator()(void* x) const { ::XFree(x); }
     32 };
     33 #endif
     34 
     35 // Helper for Shader creation.
     36 static void CreateShader(GLuint program,
     37                          GLenum type,
     38                          const char* source,
     39                          int size) {
     40   GLuint shader = glCreateShader(type);
     41   glShaderSource(shader, 1, &source, &size);
     42   glCompileShader(shader);
     43   int result = GL_FALSE;
     44   glGetShaderiv(shader, GL_COMPILE_STATUS, &result);
     45   if (!result) {
     46     char log[4096];
     47     glGetShaderInfoLog(shader, arraysize(log), NULL, log);
     48     LOG(FATAL) << log;
     49   }
     50   glAttachShader(program, shader);
     51   glDeleteShader(shader);
     52   CHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR);
     53 }
     54 
     55 namespace content {
     56 
     57 RenderingHelperParams::RenderingHelperParams() {}
     58 
     59 RenderingHelperParams::~RenderingHelperParams() {}
     60 
     61 static const gfx::GLImplementation kGLImplementation =
     62 #if defined(GL_VARIANT_GLX)
     63     gfx::kGLImplementationDesktopGL;
     64 #elif defined(GL_VARIANT_EGL)
     65     gfx::kGLImplementationEGLGLES2;
     66 #else
     67     -1;
     68 #error "Unknown GL implementation."
     69 #endif
     70 
     71 RenderingHelper::RenderingHelper() {
     72 #if defined(GL_VARIANT_EGL)
     73   gl_surface_ = EGL_NO_SURFACE;
     74 #endif
     75 
     76 #if defined(OS_WIN)
     77   window_ = NULL;
     78 #else
     79   x_window_ = (Window)0;
     80 #endif
     81 
     82   Clear();
     83 }
     84 
     85 RenderingHelper::~RenderingHelper() {
     86   CHECK_EQ(clients_.size(), 0U) << "Must call UnInitialize before dtor.";
     87   Clear();
     88 }
     89 
     90 void RenderingHelper::Initialize(const RenderingHelperParams& params,
     91                                  base::WaitableEvent* done) {
     92   // Use cients_.size() != 0 as a proxy for the class having already been
     93   // Initialize()'d, and UnInitialize() before continuing.
     94   if (clients_.size()) {
     95     base::WaitableEvent done(false, false);
     96     UnInitialize(&done);
     97     done.Wait();
     98   }
     99 
    100   frame_duration_ = params.rendering_fps > 0
    101                         ? base::TimeDelta::FromSeconds(1) / params.rendering_fps
    102                         : base::TimeDelta();
    103 
    104   gfx::InitializeStaticGLBindings(kGLImplementation);
    105   scoped_refptr<gfx::GLContextStubWithExtensions> stub_context(
    106       new gfx::GLContextStubWithExtensions());
    107 
    108   render_as_thumbnails_ = params.render_as_thumbnails;
    109   message_loop_ = base::MessageLoop::current();
    110 
    111 #if GL_VARIANT_GLX
    112   x_display_ = gfx::GetXDisplay();
    113   CHECK(x_display_);
    114   CHECK(glXQueryVersion(x_display_, NULL, NULL));
    115   const int fbconfig_attr[] = {
    116     GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
    117     GLX_RENDER_TYPE, GLX_RGBA_BIT,
    118     GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT,
    119     GLX_BIND_TO_TEXTURE_RGB_EXT, GL_TRUE,
    120     GLX_DOUBLEBUFFER, True,
    121     GL_NONE,
    122   };
    123   int num_fbconfigs;
    124   scoped_ptr<GLXFBConfig, XFreeDeleter> glx_fb_configs(
    125       glXChooseFBConfig(x_display_, DefaultScreen(x_display_), fbconfig_attr,
    126                         &num_fbconfigs));
    127   CHECK(glx_fb_configs.get());
    128   CHECK_GT(num_fbconfigs, 0);
    129   x_visual_ = glXGetVisualFromFBConfig(x_display_, glx_fb_configs.get()[0]);
    130   CHECK(x_visual_);
    131   gl_context_ = glXCreateContext(x_display_, x_visual_, 0, true);
    132   CHECK(gl_context_);
    133   stub_context->AddExtensionsString(
    134       reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)));
    135   stub_context->SetGLVersionString(
    136       reinterpret_cast<const char*>(glGetString(GL_VERSION)));
    137 
    138   Screen* screen = DefaultScreenOfDisplay(x_display_);
    139   screen_size_ = gfx::Size(XWidthOfScreen(screen), XHeightOfScreen(screen));
    140 #else // EGL
    141   EGLNativeDisplayType native_display;
    142 
    143 #if defined(OS_WIN)
    144   native_display = EGL_DEFAULT_DISPLAY;
    145   screen_size_ =
    146       gfx::Size(GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
    147 #else
    148   x_display_ = gfx::GetXDisplay();
    149   CHECK(x_display_);
    150   native_display = x_display_;
    151 
    152   Screen* screen = DefaultScreenOfDisplay(x_display_);
    153   screen_size_ = gfx::Size(XWidthOfScreen(screen), XHeightOfScreen(screen));
    154 #endif
    155   gl_display_ = eglGetDisplay(native_display);
    156   CHECK(gl_display_);
    157   CHECK(eglInitialize(gl_display_, NULL, NULL)) << eglGetError();
    158 
    159   static EGLint rgba8888[] = {
    160     EGL_RED_SIZE, 8,
    161     EGL_GREEN_SIZE, 8,
    162     EGL_BLUE_SIZE, 8,
    163     EGL_ALPHA_SIZE, 8,
    164     EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
    165     EGL_NONE,
    166   };
    167   EGLConfig egl_config;
    168   int num_configs;
    169   CHECK(eglChooseConfig(gl_display_, rgba8888, &egl_config, 1, &num_configs))
    170       << eglGetError();
    171   CHECK_GE(num_configs, 1);
    172   static EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
    173   gl_context_ = eglCreateContext(
    174       gl_display_, egl_config, EGL_NO_CONTEXT, context_attribs);
    175   CHECK_NE(gl_context_, EGL_NO_CONTEXT) << eglGetError();
    176   stub_context->AddExtensionsString(
    177       reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)));
    178   stub_context->AddExtensionsString(
    179       eglQueryString(gl_display_, EGL_EXTENSIONS));
    180   stub_context->SetGLVersionString(
    181       reinterpret_cast<const char*>(glGetString(GL_VERSION)));
    182 #endif
    183   clients_ = params.clients;
    184   CHECK_GT(clients_.size(), 0U);
    185   LayoutRenderingAreas();
    186 
    187 #if defined(OS_WIN)
    188   window_ = CreateWindowEx(0,
    189                            L"Static",
    190                            L"VideoDecodeAcceleratorTest",
    191                            WS_OVERLAPPEDWINDOW | WS_VISIBLE,
    192                            0,
    193                            0,
    194                            screen_size_.width(),
    195                            screen_size_.height(),
    196                            NULL,
    197                            NULL,
    198                            NULL,
    199                            NULL);
    200   CHECK(window_ != NULL);
    201 #else
    202   int depth = DefaultDepth(x_display_, DefaultScreen(x_display_));
    203 
    204 #if defined(GL_VARIANT_GLX)
    205   CHECK_EQ(depth, x_visual_->depth);
    206 #endif
    207 
    208   XSetWindowAttributes window_attributes;
    209   window_attributes.background_pixel =
    210       BlackPixel(x_display_, DefaultScreen(x_display_));
    211   window_attributes.override_redirect = true;
    212 
    213   x_window_ = XCreateWindow(x_display_,
    214                             DefaultRootWindow(x_display_),
    215                             0,
    216                             0,
    217                             screen_size_.width(),
    218                             screen_size_.height(),
    219                             0 /* border width */,
    220                             depth,
    221                             CopyFromParent /* class */,
    222                             CopyFromParent /* visual */,
    223                             (CWBackPixel | CWOverrideRedirect),
    224                             &window_attributes);
    225   XStoreName(x_display_, x_window_, "VideoDecodeAcceleratorTest");
    226   XSelectInput(x_display_, x_window_, ExposureMask);
    227   XMapWindow(x_display_, x_window_);
    228 #endif
    229 
    230 #if GL_VARIANT_EGL
    231 #if defined(OS_WIN)
    232   gl_surface_ =
    233       eglCreateWindowSurface(gl_display_, egl_config, window_, NULL);
    234 #else
    235   gl_surface_ =
    236       eglCreateWindowSurface(gl_display_, egl_config, x_window_, NULL);
    237 #endif
    238   CHECK_NE(gl_surface_, EGL_NO_SURFACE);
    239 #endif
    240 
    241 #if GL_VARIANT_GLX
    242   CHECK(glXMakeContextCurrent(x_display_, x_window_, x_window_, gl_context_));
    243 #else  // EGL
    244   CHECK(eglMakeCurrent(gl_display_, gl_surface_, gl_surface_, gl_context_))
    245       << eglGetError();
    246 #endif
    247 
    248   // Must be done after a context is made current.
    249   gfx::InitializeDynamicGLBindings(kGLImplementation, stub_context.get());
    250 
    251   if (render_as_thumbnails_) {
    252     CHECK_EQ(clients_.size(), 1U);
    253 
    254     GLint max_texture_size;
    255     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
    256     CHECK_GE(max_texture_size, params.thumbnails_page_size.width());
    257     CHECK_GE(max_texture_size, params.thumbnails_page_size.height());
    258 
    259     thumbnails_fbo_size_ = params.thumbnails_page_size;
    260     thumbnail_size_ = params.thumbnail_size;
    261 
    262     glGenFramebuffersEXT(1, &thumbnails_fbo_id_);
    263     glGenTextures(1, &thumbnails_texture_id_);
    264     glBindTexture(GL_TEXTURE_2D, thumbnails_texture_id_);
    265     glTexImage2D(GL_TEXTURE_2D,
    266                  0,
    267                  GL_RGB,
    268                  thumbnails_fbo_size_.width(),
    269                  thumbnails_fbo_size_.height(),
    270                  0,
    271                  GL_RGB,
    272                  GL_UNSIGNED_SHORT_5_6_5,
    273                  NULL);
    274     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    275     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    276     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    277     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    278 
    279     glBindFramebufferEXT(GL_FRAMEBUFFER, thumbnails_fbo_id_);
    280     glFramebufferTexture2DEXT(GL_FRAMEBUFFER,
    281                               GL_COLOR_ATTACHMENT0,
    282                               GL_TEXTURE_2D,
    283                               thumbnails_texture_id_,
    284                               0);
    285 
    286     GLenum fb_status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER);
    287     CHECK(fb_status == GL_FRAMEBUFFER_COMPLETE) << fb_status;
    288     glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    289     glClear(GL_COLOR_BUFFER_BIT);
    290     glBindFramebufferEXT(GL_FRAMEBUFFER, 0);
    291   }
    292 
    293   // These vertices and texture coords. map (0,0) in the texture to the
    294   // bottom left of the viewport.  Since we get the video frames with the
    295   // the top left at (0,0) we need to flip the texture y coordinate
    296   // in the vertex shader for this to be rendered the right way up.
    297   // In the case of thumbnail rendering we use the same vertex shader
    298   // to render the FBO the screen, where we do not want this flipping.
    299   static const float kVertices[] =
    300       { -1.f, 1.f, -1.f, -1.f, 1.f, 1.f, 1.f, -1.f, };
    301   static const float kTextureCoords[] = { 0, 1, 0, 0, 1, 1, 1, 0, };
    302   static const char kVertexShader[] = STRINGIZE(
    303       varying vec2 interp_tc;
    304       attribute vec4 in_pos;
    305       attribute vec2 in_tc;
    306       uniform bool tex_flip;
    307       void main() {
    308         if (tex_flip)
    309           interp_tc = vec2(in_tc.x, 1.0 - in_tc.y);
    310         else
    311           interp_tc = in_tc;
    312         gl_Position = in_pos;
    313       });
    314 
    315 #if GL_VARIANT_EGL
    316   static const char kFragmentShader[] =
    317       "#extension GL_OES_EGL_image_external : enable\n"
    318       "precision mediump float;\n"
    319       "varying vec2 interp_tc;\n"
    320       "uniform sampler2D tex;\n"
    321       "#ifdef GL_OES_EGL_image_external\n"
    322       "uniform samplerExternalOES tex_external;\n"
    323       "#endif\n"
    324       "void main() {\n"
    325       "  vec4 color = texture2D(tex, interp_tc);\n"
    326       "#ifdef GL_OES_EGL_image_external\n"
    327       "  color += texture2D(tex_external, interp_tc);\n"
    328       "#endif\n"
    329       "  gl_FragColor = color;\n"
    330       "}\n";
    331 #else
    332   static const char kFragmentShader[] = STRINGIZE(
    333       varying vec2 interp_tc;
    334       uniform sampler2D tex;
    335       void main() {
    336         gl_FragColor = texture2D(tex, interp_tc);
    337       });
    338 #endif
    339   program_ = glCreateProgram();
    340   CreateShader(
    341       program_, GL_VERTEX_SHADER, kVertexShader, arraysize(kVertexShader));
    342   CreateShader(program_,
    343                GL_FRAGMENT_SHADER,
    344                kFragmentShader,
    345                arraysize(kFragmentShader));
    346   glLinkProgram(program_);
    347   int result = GL_FALSE;
    348   glGetProgramiv(program_, GL_LINK_STATUS, &result);
    349   if (!result) {
    350     char log[4096];
    351     glGetShaderInfoLog(program_, arraysize(log), NULL, log);
    352     LOG(FATAL) << log;
    353   }
    354   glUseProgram(program_);
    355   glDeleteProgram(program_);
    356 
    357   glUniform1i(glGetUniformLocation(program_, "tex_flip"), 0);
    358   glUniform1i(glGetUniformLocation(program_, "tex"), 0);
    359   GLint tex_external = glGetUniformLocation(program_, "tex_external");
    360   if (tex_external != -1) {
    361     glUniform1i(tex_external, 1);
    362   }
    363   int pos_location = glGetAttribLocation(program_, "in_pos");
    364   glEnableVertexAttribArray(pos_location);
    365   glVertexAttribPointer(pos_location, 2, GL_FLOAT, GL_FALSE, 0, kVertices);
    366   int tc_location = glGetAttribLocation(program_, "in_tc");
    367   glEnableVertexAttribArray(tc_location);
    368   glVertexAttribPointer(tc_location, 2, GL_FLOAT, GL_FALSE, 0, kTextureCoords);
    369 
    370   if (frame_duration_ != base::TimeDelta()) {
    371     render_timer_.Start(
    372         FROM_HERE, frame_duration_, this, &RenderingHelper::RenderContent);
    373   }
    374   done->Signal();
    375 }
    376 
    377 void RenderingHelper::UnInitialize(base::WaitableEvent* done) {
    378   CHECK_EQ(base::MessageLoop::current(), message_loop_);
    379   render_timer_.Stop();
    380   if (render_as_thumbnails_) {
    381     glDeleteTextures(1, &thumbnails_texture_id_);
    382     glDeleteFramebuffersEXT(1, &thumbnails_fbo_id_);
    383   }
    384 #if GL_VARIANT_GLX
    385 
    386   glXDestroyContext(x_display_, gl_context_);
    387 #else // EGL
    388   CHECK(eglMakeCurrent(
    389       gl_display_, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT))
    390       << eglGetError();
    391   CHECK(eglDestroyContext(gl_display_, gl_context_));
    392   CHECK(eglDestroySurface(gl_display_, gl_surface_));
    393   CHECK(eglTerminate(gl_display_));
    394 #endif
    395   gfx::ClearGLBindings();
    396   Clear();
    397   done->Signal();
    398 }
    399 
    400 void RenderingHelper::CreateTexture(uint32 texture_target,
    401                                     uint32* texture_id,
    402                                     const gfx::Size& size,
    403                                     base::WaitableEvent* done) {
    404   if (base::MessageLoop::current() != message_loop_) {
    405     message_loop_->PostTask(FROM_HERE,
    406                             base::Bind(&RenderingHelper::CreateTexture,
    407                                        base::Unretained(this),
    408                                        texture_target,
    409                                        texture_id,
    410                                        size,
    411                                        done));
    412     return;
    413   }
    414   glGenTextures(1, texture_id);
    415   glBindTexture(texture_target, *texture_id);
    416   if (texture_target == GL_TEXTURE_2D) {
    417     glTexImage2D(GL_TEXTURE_2D,
    418                  0,
    419                  GL_RGBA,
    420                  size.width(),
    421                  size.height(),
    422                  0,
    423                  GL_RGBA,
    424                  GL_UNSIGNED_BYTE,
    425                  NULL);
    426   }
    427   glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    428   glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    429   // OpenGLES2.0.25 section 3.8.2 requires CLAMP_TO_EDGE for NPOT textures.
    430   glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    431   glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    432   CHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR);
    433   done->Signal();
    434 }
    435 
    436 // Helper function to set GL viewport.
    437 static inline void GLSetViewPort(const gfx::Rect& area) {
    438   glViewport(area.x(), area.y(), area.width(), area.height());
    439   glScissor(area.x(), area.y(), area.width(), area.height());
    440 }
    441 
    442 void RenderingHelper::RenderThumbnail(uint32 texture_target,
    443                                       uint32 texture_id) {
    444   CHECK_EQ(base::MessageLoop::current(), message_loop_);
    445   const int width = thumbnail_size_.width();
    446   const int height = thumbnail_size_.height();
    447   const int thumbnails_in_row = thumbnails_fbo_size_.width() / width;
    448   const int thumbnails_in_column = thumbnails_fbo_size_.height() / height;
    449   const int row = (frame_count_ / thumbnails_in_row) % thumbnails_in_column;
    450   const int col = frame_count_ % thumbnails_in_row;
    451 
    452   gfx::Rect area(col * width, row * height, width, height);
    453 
    454   glUniform1i(glGetUniformLocation(program_, "tex_flip"), 0);
    455   glBindFramebufferEXT(GL_FRAMEBUFFER, thumbnails_fbo_id_);
    456   GLSetViewPort(area);
    457   RenderTexture(texture_target, texture_id);
    458   glBindFramebufferEXT(GL_FRAMEBUFFER, 0);
    459   ++frame_count_;
    460 }
    461 
    462 void RenderingHelper::RenderTexture(uint32 texture_target, uint32 texture_id) {
    463   // The ExternalOES sampler is bound to GL_TEXTURE1 and the Texture2D sampler
    464   // is bound to GL_TEXTURE0.
    465   if (texture_target == GL_TEXTURE_2D) {
    466     glActiveTexture(GL_TEXTURE0 + 0);
    467   } else if (texture_target == GL_TEXTURE_EXTERNAL_OES) {
    468     glActiveTexture(GL_TEXTURE0 + 1);
    469   }
    470   glBindTexture(texture_target, texture_id);
    471   glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    472   glBindTexture(texture_target, 0);
    473   CHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR);
    474 }
    475 
    476 void RenderingHelper::DeleteTexture(uint32 texture_id) {
    477   glDeleteTextures(1, &texture_id);
    478   CHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR);
    479 }
    480 
    481 NativeContextType RenderingHelper::GetGLContext() { return gl_context_; }
    482 
    483 void* RenderingHelper::GetGLDisplay() {
    484 #if GL_VARIANT_GLX
    485   return x_display_;
    486 #else  // EGL
    487   return gl_display_;
    488 #endif
    489 }
    490 
    491 void RenderingHelper::Clear() {
    492   clients_.clear();
    493   message_loop_ = NULL;
    494   gl_context_ = NULL;
    495 #if GL_VARIANT_EGL
    496   gl_display_ = EGL_NO_DISPLAY;
    497   gl_surface_ = EGL_NO_SURFACE;
    498 #endif
    499   render_as_thumbnails_ = false;
    500   frame_count_ = 0;
    501   thumbnails_fbo_id_ = 0;
    502   thumbnails_texture_id_ = 0;
    503 
    504 #if defined(OS_WIN)
    505   if (window_) {
    506     DestroyWindow(window_);
    507     window_ = NULL;
    508   }
    509 #else
    510   // Destroy resources acquired in Initialize, in reverse-acquisition order.
    511   if (x_window_) {
    512     CHECK(XUnmapWindow(x_display_, x_window_));
    513     CHECK(XDestroyWindow(x_display_, x_window_));
    514     x_window_ = (Window)0;
    515   }
    516   // Mimic newly created object.
    517   x_display_ = NULL;
    518 #endif
    519 }
    520 
    521 void RenderingHelper::GetThumbnailsAsRGB(std::vector<unsigned char>* rgb,
    522                                          bool* alpha_solid,
    523                                          base::WaitableEvent* done) {
    524   CHECK(render_as_thumbnails_);
    525 
    526   const size_t num_pixels = thumbnails_fbo_size_.GetArea();
    527   std::vector<unsigned char> rgba;
    528   rgba.resize(num_pixels * 4);
    529   glBindFramebufferEXT(GL_FRAMEBUFFER, thumbnails_fbo_id_);
    530   glPixelStorei(GL_PACK_ALIGNMENT, 1);
    531   // We can only count on GL_RGBA/GL_UNSIGNED_BYTE support.
    532   glReadPixels(0,
    533                0,
    534                thumbnails_fbo_size_.width(),
    535                thumbnails_fbo_size_.height(),
    536                GL_RGBA,
    537                GL_UNSIGNED_BYTE,
    538                &rgba[0]);
    539   glBindFramebufferEXT(GL_FRAMEBUFFER, 0);
    540   rgb->resize(num_pixels * 3);
    541   // Drop the alpha channel, but check as we go that it is all 0xff.
    542   bool solid = true;
    543   unsigned char* rgb_ptr = &((*rgb)[0]);
    544   unsigned char* rgba_ptr = &rgba[0];
    545   for (size_t i = 0; i < num_pixels; ++i) {
    546     *rgb_ptr++ = *rgba_ptr++;
    547     *rgb_ptr++ = *rgba_ptr++;
    548     *rgb_ptr++ = *rgba_ptr++;
    549     solid = solid && (*rgba_ptr == 0xff);
    550     rgba_ptr++;
    551   }
    552   *alpha_solid = solid;
    553 
    554   done->Signal();
    555 }
    556 
    557 void RenderingHelper::RenderContent() {
    558   glUniform1i(glGetUniformLocation(program_, "tex_flip"), 1);
    559 
    560   if (render_as_thumbnails_) {
    561     // In render_as_thumbnails_ mode, we render the FBO content on the
    562     // screen instead of the decoded textures.
    563     GLSetViewPort(render_areas_[0]);
    564     RenderTexture(GL_TEXTURE_2D, thumbnails_texture_id_);
    565   } else {
    566     for (size_t i = 0; i < clients_.size(); ++i) {
    567       if (clients_[i]) {
    568         GLSetViewPort(render_areas_[i]);
    569         clients_[i]->RenderContent(this);
    570       }
    571     }
    572   }
    573 
    574 #if GL_VARIANT_GLX
    575   glXSwapBuffers(x_display_, x_window_);
    576 #else  // EGL
    577   eglSwapBuffers(gl_display_, gl_surface_);
    578   CHECK_EQ(static_cast<int>(eglGetError()), EGL_SUCCESS);
    579 #endif
    580 }
    581 
    582 // Helper function for the LayoutRenderingAreas(). The |lengths| are the
    583 // heights(widths) of the rows(columns). It scales the elements in
    584 // |lengths| proportionally so that the sum of them equal to |total_length|.
    585 // It also outputs the coordinates of the rows(columns) to |offsets|.
    586 static void ScaleAndCalculateOffsets(std::vector<int>* lengths,
    587                                      std::vector<int>* offsets,
    588                                      int total_length) {
    589   int sum = std::accumulate(lengths->begin(), lengths->end(), 0);
    590   for (size_t i = 0; i < lengths->size(); ++i) {
    591     lengths->at(i) = lengths->at(i) * total_length / sum;
    592     offsets->at(i) = (i == 0) ? 0 : offsets->at(i - 1) + lengths->at(i - 1);
    593   }
    594 }
    595 
    596 void RenderingHelper::LayoutRenderingAreas() {
    597   // Find the number of colums and rows.
    598   // The smallest n * n or n * (n + 1) > number of clients.
    599   size_t cols = sqrt(clients_.size() - 1) + 1;
    600   size_t rows = (clients_.size() + cols - 1) / cols;
    601 
    602   // Find the widths and heights of the grid.
    603   std::vector<int> widths(cols);
    604   std::vector<int> heights(rows);
    605   std::vector<int> offset_x(cols);
    606   std::vector<int> offset_y(rows);
    607 
    608   for (size_t i = 0; i < clients_.size(); ++i) {
    609     const gfx::Size& window_size = clients_[i]->GetWindowSize();
    610     widths[i % cols] = std::max(widths[i % cols], window_size.width());
    611     heights[i / cols] = std::max(heights[i / cols], window_size.height());
    612   }
    613 
    614   ScaleAndCalculateOffsets(&widths, &offset_x, screen_size_.width());
    615   ScaleAndCalculateOffsets(&heights, &offset_y, screen_size_.height());
    616 
    617   // Put each render_area_ in the center of each cell.
    618   render_areas_.clear();
    619   for (size_t i = 0; i < clients_.size(); ++i) {
    620     const gfx::Size& window_size = clients_[i]->GetWindowSize();
    621     float scale =
    622         std::min(static_cast<float>(widths[i % cols]) / window_size.width(),
    623                  static_cast<float>(heights[i / cols]) / window_size.height());
    624 
    625     // Don't scale up the texture.
    626     scale = std::min(1.0f, scale);
    627 
    628     size_t w = scale * window_size.width();
    629     size_t h = scale * window_size.height();
    630     size_t x = offset_x[i % cols] + (widths[i % cols] - w) / 2;
    631     size_t y = offset_y[i / cols] + (heights[i / cols] - h) / 2;
    632     render_areas_.push_back(gfx::Rect(x, y, w, h));
    633   }
    634 }
    635 }  // namespace content
    636