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