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