Home | History | Annotate | Download | only in renderer_host
      1 // Copyright (c) 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/browser/renderer_host/compositing_iosurface_transformer_mac.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/basictypes.h"
     10 #include "base/debug/trace_event.h"
     11 #include "base/logging.h"
     12 #include "content/browser/renderer_host/compositing_iosurface_shader_programs_mac.h"
     13 #include "ui/gfx/rect.h"
     14 #include "ui/gfx/size.h"
     15 
     16 namespace content {
     17 
     18 namespace {
     19 
     20 const GLenum kColorAttachments[] = {
     21   GL_COLOR_ATTACHMENT0_EXT,
     22   GL_COLOR_ATTACHMENT1_EXT
     23 };
     24 
     25 // Set viewport and model/projection matrices for drawing to a framebuffer of
     26 // size dst_size, with coordinates starting at (0, 0).
     27 void SetTransformationsForOffScreenRendering(const gfx::Size& dst_size) {
     28   glViewport(0, 0, dst_size.width(), dst_size.height());
     29   glMatrixMode(GL_PROJECTION);
     30   glLoadIdentity();
     31   glOrtho(0, dst_size.width(), 0, dst_size.height(), -1, 1);
     32   glMatrixMode(GL_MODELVIEW);
     33   glLoadIdentity();
     34 }
     35 
     36 // Configure texture sampling parameters.
     37 void SetTextureParameters(GLenum target, GLint min_mag_filter, GLint wrap) {
     38   glTexParameteri(target, GL_TEXTURE_MIN_FILTER, min_mag_filter);
     39   glTexParameteri(target, GL_TEXTURE_MAG_FILTER, min_mag_filter);
     40   glTexParameteri(target, GL_TEXTURE_WRAP_S, wrap);
     41   glTexParameteri(target, GL_TEXTURE_WRAP_T, wrap);
     42 }
     43 
     44 // Draw the currently-bound texture.  The src region is applied to the entire
     45 // destination framebuffer of the given size.  Specify |flip_y| is the src
     46 // texture is upside-down relative to the destination.
     47 //
     48 // Assumption: The orthographic projection is set up as
     49 // (0,0)x(dst_width,dst_height).
     50 void DrawQuad(float src_x, float src_y, float src_width, float src_height,
     51               bool flip_y, float dst_width, float dst_height) {
     52   glEnableClientState(GL_VERTEX_ARRAY);
     53   glEnableClientState(GL_TEXTURE_COORD_ARRAY);
     54 
     55   float vertices[4][2] = {
     56     { 0.0f, dst_height },
     57     { 0.0f, 0.0f },
     58     { dst_width, 0.0f },
     59     { dst_width, dst_height }
     60   };
     61   glVertexPointer(arraysize(vertices[0]), GL_FLOAT, sizeof(vertices[0]),
     62                   vertices);
     63 
     64   float tex_coords[4][2] = {
     65     { src_x, src_y + src_height },
     66     { src_x, src_y },
     67     { src_x + src_width, src_y },
     68     { src_x + src_width, src_y + src_height }
     69   };
     70   if (flip_y) {
     71     std::swap(tex_coords[0][1], tex_coords[1][1]);
     72     std::swap(tex_coords[2][1], tex_coords[3][1]);
     73   }
     74   glTexCoordPointer(arraysize(tex_coords[0]), GL_FLOAT, sizeof(tex_coords[0]),
     75                     tex_coords);
     76 
     77   COMPILE_ASSERT(arraysize(vertices) == arraysize(tex_coords),
     78                  same_number_of_points_in_both);
     79   glDrawArrays(GL_QUADS, 0, arraysize(vertices));
     80 
     81   glDisableClientState(GL_VERTEX_ARRAY);
     82   glDisableClientState(GL_TEXTURE_COORD_ARRAY);
     83 }
     84 
     85 }  // namespace
     86 
     87 CompositingIOSurfaceTransformer::CompositingIOSurfaceTransformer(
     88     GLenum texture_target, bool src_texture_needs_y_flip,
     89     CompositingIOSurfaceShaderPrograms* shader_program_cache)
     90     : texture_target_(texture_target),
     91       src_texture_needs_y_flip_(src_texture_needs_y_flip),
     92       shader_program_cache_(shader_program_cache),
     93       frame_buffer_(0) {
     94   DCHECK(texture_target_ == GL_TEXTURE_RECTANGLE_ARB)
     95       << "Fragment shaders currently only support RECTANGLE textures.";
     96   DCHECK(shader_program_cache_);
     97 
     98   memset(textures_, 0, sizeof(textures_));
     99 
    100   // The RGB-to-YV12 transform requires that the driver/hardware supports
    101   // multiple draw buffers.
    102   GLint max_draw_buffers = 1;
    103   glGetIntegerv(GL_MAX_DRAW_BUFFERS, &max_draw_buffers);
    104   system_supports_multiple_draw_buffers_ = (max_draw_buffers >= 2);
    105 }
    106 
    107 CompositingIOSurfaceTransformer::~CompositingIOSurfaceTransformer() {
    108   for (int i = 0; i < NUM_CACHED_TEXTURES; ++i)
    109     DCHECK_EQ(textures_[i], 0u) << "Failed to call ReleaseCachedGLObjects().";
    110   DCHECK_EQ(frame_buffer_, 0u) << "Failed to call ReleaseCachedGLObjects().";
    111 }
    112 
    113 void CompositingIOSurfaceTransformer::ReleaseCachedGLObjects() {
    114   for (int i = 0; i < NUM_CACHED_TEXTURES; ++i) {
    115     if (textures_[i]) {
    116       glDeleteTextures(1, &textures_[i]);
    117       textures_[i] = 0;
    118       texture_sizes_[i] = gfx::Size();
    119     }
    120   }
    121   if (frame_buffer_) {
    122     glDeleteFramebuffersEXT(1, &frame_buffer_);
    123     frame_buffer_ = 0;
    124   }
    125 }
    126 
    127 bool CompositingIOSurfaceTransformer::ResizeBilinear(
    128     GLuint src_texture, const gfx::Rect& src_subrect, const gfx::Size& dst_size,
    129     GLuint* texture) {
    130   if (src_subrect.IsEmpty() || dst_size.IsEmpty())
    131     return false;
    132 
    133   glActiveTexture(GL_TEXTURE0);
    134   glDisable(GL_DEPTH_TEST);
    135   glDisable(GL_BLEND);
    136 
    137   PrepareTexture(RGBA_OUTPUT, dst_size);
    138   PrepareFramebuffer();
    139   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, frame_buffer_);
    140   glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
    141                             texture_target_, textures_[RGBA_OUTPUT], 0);
    142   DCHECK(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) ==
    143              GL_FRAMEBUFFER_COMPLETE_EXT);
    144 
    145   glBindTexture(texture_target_, src_texture);
    146   SetTextureParameters(
    147       texture_target_, src_subrect.size() == dst_size ? GL_NEAREST : GL_LINEAR,
    148       GL_CLAMP_TO_EDGE);
    149 
    150   const bool prepared = shader_program_cache_->UseBlitProgram();
    151   DCHECK(prepared);
    152   SetTransformationsForOffScreenRendering(dst_size);
    153   DrawQuad(src_subrect.x(), src_subrect.y(),
    154            src_subrect.width(), src_subrect.height(),
    155            src_texture_needs_y_flip_,
    156            dst_size.width(), dst_size.height());
    157   glUseProgram(0);
    158 
    159   glBindTexture(texture_target_, 0);
    160   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
    161 
    162   *texture = textures_[RGBA_OUTPUT];
    163   return true;
    164 }
    165 
    166 bool CompositingIOSurfaceTransformer::TransformRGBToYV12(
    167     GLuint src_texture,
    168     const gfx::Rect& src_subrect,
    169     const gfx::Size& dst_size,
    170     GLuint* texture_y,
    171     GLuint* texture_u,
    172     GLuint* texture_v,
    173     gfx::Size* packed_y_size,
    174     gfx::Size* packed_uv_size) {
    175   if (!system_supports_multiple_draw_buffers_)
    176     return false;
    177   if (src_subrect.IsEmpty() || dst_size.IsEmpty())
    178     return false;
    179 
    180   TRACE_EVENT0("gpu", "TransformRGBToYV12");
    181 
    182   glActiveTexture(GL_TEXTURE0);
    183   glDisable(GL_DEPTH_TEST);
    184   glDisable(GL_BLEND);
    185 
    186   // Resize output textures for each plane, and for the intermediate UUVV one
    187   // that becomes an input into pass #2.  |packed_y_size| is the size of the Y
    188   // output texture, where its width is 1/4 the number of Y pixels because 4 Y
    189   // pixels are packed into a single quad.  |packed_uv_size| is half the size of
    190   // Y in both dimensions, rounded up.
    191   *packed_y_size = gfx::Size((dst_size.width() + 3) / 4, dst_size.height());
    192   *packed_uv_size = gfx::Size((packed_y_size->width() + 1) / 2,
    193                               (packed_y_size->height() + 1) / 2);
    194   PrepareTexture(Y_PLANE_OUTPUT, *packed_y_size);
    195   PrepareTexture(UUVV_INTERMEDIATE, *packed_y_size);
    196   PrepareTexture(U_PLANE_OUTPUT, *packed_uv_size);
    197   PrepareTexture(V_PLANE_OUTPUT, *packed_uv_size);
    198 
    199   /////////////////////////////////////////
    200   // Pass 1: RGB --(scaled)--> YYYY + UUVV
    201   PrepareFramebuffer();
    202   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, frame_buffer_);
    203   glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
    204                             texture_target_, textures_[Y_PLANE_OUTPUT], 0);
    205   glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT,
    206                             texture_target_, textures_[UUVV_INTERMEDIATE], 0);
    207   DCHECK(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) ==
    208              GL_FRAMEBUFFER_COMPLETE_EXT);
    209   glDrawBuffers(2, kColorAttachments);
    210 
    211   // Read from |src_texture|.  Enable bilinear filtering only if scaling is
    212   // required.  The filtering will take place entirely in the first pass.
    213   glBindTexture(texture_target_, src_texture);
    214   SetTextureParameters(
    215       texture_target_, src_subrect.size() == dst_size ? GL_NEAREST : GL_LINEAR,
    216       GL_CLAMP_TO_EDGE);
    217 
    218   // Use the first-pass shader program and draw the scene.
    219   const bool prepared_pass_1 = shader_program_cache_->UseRGBToYV12Program(
    220       1,
    221       static_cast<float>(src_subrect.width()) / dst_size.width());
    222   DCHECK(prepared_pass_1);
    223   SetTransformationsForOffScreenRendering(*packed_y_size);
    224   DrawQuad(src_subrect.x(), src_subrect.y(),
    225            ((packed_y_size->width() * 4.0f) / dst_size.width()) *
    226                src_subrect.width(),
    227            src_subrect.height(),
    228            src_texture_needs_y_flip_,
    229            packed_y_size->width(), packed_y_size->height());
    230 
    231   /////////////////////////////////////////
    232   // Pass 2: UUVV -> UUUU + VVVV
    233   glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
    234                             texture_target_, textures_[U_PLANE_OUTPUT], 0);
    235   glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT,
    236                             texture_target_, textures_[V_PLANE_OUTPUT], 0);
    237   DCHECK(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) ==
    238              GL_FRAMEBUFFER_COMPLETE_EXT);
    239 
    240   // Read from the intermediate UUVV texture.  The second pass uses bilinear
    241   // minification to achieve vertical scaling, so enable it always.
    242   glBindTexture(texture_target_, textures_[UUVV_INTERMEDIATE]);
    243   SetTextureParameters(texture_target_, GL_LINEAR, GL_CLAMP_TO_EDGE);
    244 
    245   // Use the second-pass shader program and draw the scene.
    246   const bool prepared_pass_2 =
    247       shader_program_cache_->UseRGBToYV12Program(2, 1.0f);
    248   DCHECK(prepared_pass_2);
    249   SetTransformationsForOffScreenRendering(*packed_uv_size);
    250   DrawQuad(0.0f, 0.0f,
    251            packed_uv_size->width() * 2.0f,
    252            packed_uv_size->height() * 2.0f,
    253            false,
    254            packed_uv_size->width(), packed_uv_size->height());
    255   glUseProgram(0);
    256 
    257   // Before leaving, put back to drawing to a single rendering output.
    258   glDrawBuffers(1, kColorAttachments);
    259 
    260   glBindTexture(texture_target_, 0);
    261   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
    262 
    263   *texture_y = textures_[Y_PLANE_OUTPUT];
    264   *texture_u = textures_[U_PLANE_OUTPUT];
    265   *texture_v = textures_[V_PLANE_OUTPUT];
    266   return true;
    267 }
    268 
    269 void CompositingIOSurfaceTransformer::PrepareTexture(
    270     CachedTexture which, const gfx::Size& size) {
    271   DCHECK_GE(which, 0);
    272   DCHECK_LT(which, NUM_CACHED_TEXTURES);
    273   DCHECK(!size.IsEmpty());
    274 
    275   if (!textures_[which]) {
    276     glGenTextures(1, &textures_[which]);
    277     DCHECK_NE(textures_[which], 0u);
    278     texture_sizes_[which] = gfx::Size();
    279   }
    280 
    281   // Re-allocate the texture if its size has changed since last use.
    282   if (texture_sizes_[which] != size) {
    283     TRACE_EVENT2("gpu", "Resize Texture",
    284                  "which", which,
    285                  "new_size", size.ToString());
    286     glBindTexture(texture_target_, textures_[which]);
    287     glTexImage2D(texture_target_, 0, GL_RGBA, size.width(), size.height(), 0,
    288                  GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
    289     texture_sizes_[which] = size;
    290   }
    291 }
    292 
    293 void CompositingIOSurfaceTransformer::PrepareFramebuffer() {
    294   if (!frame_buffer_) {
    295     glGenFramebuffersEXT(1, &frame_buffer_);
    296     DCHECK_NE(frame_buffer_, 0u);
    297   }
    298 }
    299 
    300 }  // namespace content
    301