OLD | NEW |
| (Empty) |
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_ma
c.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 | |
OLD | NEW |