Add a FrameHostMsg_BeginNavigation IPC
[chromium-blink-merge.git] / content / browser / renderer_host / compositing_iosurface_transformer_mac.cc
blob2e0401ff476afacb2556bd5ba7571ae730469857
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.
5 #include "content/browser/renderer_host/compositing_iosurface_transformer_mac.h"
7 #include <algorithm>
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"
16 namespace content {
18 namespace {
20 const GLenum kColorAttachments[] = {
21 GL_COLOR_ATTACHMENT0_EXT,
22 GL_COLOR_ATTACHMENT1_EXT
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();
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);
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.
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);
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 }
61 glVertexPointer(arraysize(vertices[0]), GL_FLOAT, sizeof(vertices[0]),
62 vertices);
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 }
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]);
74 glTexCoordPointer(arraysize(tex_coords[0]), GL_FLOAT, sizeof(tex_coords[0]),
75 tex_coords);
77 COMPILE_ASSERT(arraysize(vertices) == arraysize(tex_coords),
78 same_number_of_points_in_both);
79 glDrawArrays(GL_QUADS, 0, arraysize(vertices));
81 glDisableClientState(GL_VERTEX_ARRAY);
82 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
85 } // namespace
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_);
98 memset(textures_, 0, sizeof(textures_));
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);
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().";
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();
121 if (frame_buffer_) {
122 glDeleteFramebuffersEXT(1, &frame_buffer_);
123 frame_buffer_ = 0;
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;
133 glActiveTexture(GL_TEXTURE0);
134 glDisable(GL_DEPTH_TEST);
135 glDisable(GL_BLEND);
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);
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);
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);
159 glBindTexture(texture_target_, 0);
160 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
162 *texture = textures_[RGBA_OUTPUT];
163 return true;
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;
180 TRACE_EVENT0("gpu", "TransformRGBToYV12");
182 glActiveTexture(GL_TEXTURE0);
183 glDisable(GL_DEPTH_TEST);
184 glDisable(GL_BLEND);
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);
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);
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);
218 // Use the first-pass shader program and draw the scene.
219 const bool prepared_pass_1 = shader_program_cache_->UseRGBToYV12Program(
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());
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);
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);
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);
257 // Before leaving, put back to drawing to a single rendering output.
258 glDrawBuffers(1, kColorAttachments);
260 glBindTexture(texture_target_, 0);
261 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
263 *texture_y = textures_[Y_PLANE_OUTPUT];
264 *texture_u = textures_[U_PLANE_OUTPUT];
265 *texture_v = textures_[V_PLANE_OUTPUT];
266 return true;
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());
275 if (!textures_[which]) {
276 glGenTextures(1, &textures_[which]);
277 DCHECK_NE(textures_[which], 0u);
278 texture_sizes_[which] = gfx::Size();
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;
293 void CompositingIOSurfaceTransformer::PrepareFramebuffer() {
294 if (!frame_buffer_) {
295 glGenFramebuffersEXT(1, &frame_buffer_);
296 DCHECK_NE(frame_buffer_, 0u);
300 } // namespace content