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