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"
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"
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
);
31 glOrtho(0, dst_size
.width(), 0, dst_size
.height(), -1, 1);
32 glMatrixMode(GL_MODELVIEW
);
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] = {
59 { dst_width
, dst_height
}
61 glVertexPointer(arraysize(vertices
[0]), GL_FLOAT
, sizeof(vertices
[0]),
64 float tex_coords
[4][2] = {
65 { src_x
, src_y
+ src_height
},
67 { src_x
+ src_width
, src_y
},
68 { src_x
+ src_width
, src_y
+ src_height
}
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]),
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
);
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
),
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
) {
116 glDeleteTextures(1, &textures_
[i
]);
118 texture_sizes_
[i
] = gfx::Size();
122 glDeleteFramebuffersEXT(1, &frame_buffer_
);
127 bool CompositingIOSurfaceTransformer::ResizeBilinear(
128 GLuint src_texture
, const gfx::Rect
& src_subrect
, const gfx::Size
& dst_size
,
130 if (src_subrect
.IsEmpty() || dst_size
.IsEmpty())
133 glActiveTexture(GL_TEXTURE0
);
134 glDisable(GL_DEPTH_TEST
);
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
,
150 const bool prepared
= shader_program_cache_
->UseBlitProgram();
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());
159 glBindTexture(texture_target_
, 0);
160 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, 0);
162 *texture
= textures_
[RGBA_OUTPUT
];
166 bool CompositingIOSurfaceTransformer::TransformRGBToYV12(
168 const gfx::Rect
& src_subrect
,
169 const gfx::Size
& dst_size
,
173 gfx::Size
* packed_y_size
,
174 gfx::Size
* packed_uv_size
) {
175 if (!system_supports_multiple_draw_buffers_
)
177 if (src_subrect
.IsEmpty() || dst_size
.IsEmpty())
180 TRACE_EVENT0("gpu", "TransformRGBToYV12");
182 glActiveTexture(GL_TEXTURE0
);
183 glDisable(GL_DEPTH_TEST
);
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
,
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()) *
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
);
251 packed_uv_size
->width() * 2.0f
,
252 packed_uv_size
->height() * 2.0f
,
254 packed_uv_size
->width(), packed_uv_size
->height());
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
];
269 void CompositingIOSurfaceTransformer::PrepareTexture(
270 CachedTexture which
, const gfx::Size
& size
) {
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",
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