OLD | NEW |
| (Empty) |
1 // Copyright (c) 2010 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 "chrome/gpu/gpu_video_layer_glx.h" | |
6 | |
7 #include "app/gfx/gl/gl_bindings.h" | |
8 #include "chrome/common/gpu_messages.h" | |
9 #include "chrome/gpu/gpu_thread.h" | |
10 #include "chrome/gpu/gpu_view_x.h" | |
11 | |
12 // Handy constants for addressing YV12 data. | |
13 static const int kYUVPlanes = 3; | |
14 static const int kYPlane = 0; | |
15 static const int kUPlane = 1; | |
16 static const int kVPlane = 2; | |
17 | |
18 // Buffer size for shader compile errors. | |
19 static const unsigned int kErrorSize = 4096; | |
20 | |
21 // Matrix used for the YUV to RGB conversion. | |
22 static const float kYUV2RGB[9] = { | |
23 1.f, 0.f, 1.403f, | |
24 1.f, -.344f, -.714f, | |
25 1.f, 1.772f, 0.f, | |
26 }; | |
27 | |
28 // Texture coordinates mapping the entire texture. | |
29 static const float kTextureCoords[8] = { | |
30 0, 0, | |
31 0, 1, | |
32 1, 0, | |
33 1, 1, | |
34 }; | |
35 | |
36 #define I915_WORKAROUND | |
37 | |
38 // Pass-through vertex shader. | |
39 static const char kVertexShader[] = | |
40 "varying vec2 interp_tc;\n" | |
41 "\n" | |
42 "attribute vec4 in_pos;\n" | |
43 "attribute vec2 in_tc;\n" | |
44 "\n" | |
45 "void main() {\n" | |
46 #if defined(I915_WORKAROUND) | |
47 " gl_TexCoord[0].st = in_tc;\n" | |
48 #else | |
49 " interp_tc = in_tc;\n" | |
50 #endif | |
51 " gl_Position = in_pos;\n" | |
52 "}\n"; | |
53 | |
54 // YUV to RGB pixel shader. Loads a pixel from each plane and pass through the | |
55 // matrix. | |
56 static const char kFragmentShader[] = | |
57 "varying vec2 interp_tc;\n" | |
58 "\n" | |
59 "uniform sampler2D y_tex;\n" | |
60 "uniform sampler2D u_tex;\n" | |
61 "uniform sampler2D v_tex;\n" | |
62 "uniform mat3 yuv2rgb;\n" | |
63 "\n" | |
64 "void main() {\n" | |
65 #if defined(I915_WORKAROUND) | |
66 " float y = texture2D(y_tex, gl_TexCoord[0].st).x;\n" | |
67 " float u = texture2D(u_tex, gl_TexCoord[0].st).r - .5;\n" | |
68 " float v = texture2D(v_tex, gl_TexCoord[0].st).r - .5;\n" | |
69 " float r = y + v * 1.403;\n" | |
70 " float g = y - u * 0.344 - v * 0.714;\n" | |
71 " float b = y + u * 1.772;\n" | |
72 " gl_FragColor = vec4(r, g, b, 1);\n" | |
73 #else | |
74 " float y = texture2D(y_tex, interp_tc).x;\n" | |
75 " float u = texture2D(u_tex, interp_tc).r - .5;\n" | |
76 " float v = texture2D(v_tex, interp_tc).r - .5;\n" | |
77 " vec3 rgb = yuv2rgb * vec3(y, u, v);\n" | |
78 " gl_FragColor = vec4(rgb, 1);\n" | |
79 #endif | |
80 "}\n"; | |
81 | |
82 | |
83 // Assume that somewhere along the line, someone will do width * height * 4 | |
84 // with signed numbers. If the maximum value is 2**31, then 2**31 / 4 = | |
85 // 2**29 and floor(sqrt(2**29)) = 23170. | |
86 | |
87 // Max height and width for layers | |
88 static const int kMaxVideoLayerSize = 23170; | |
89 | |
90 GpuVideoLayerGLX::GpuVideoLayerGLX(GpuViewX* view, | |
91 GpuThread* gpu_thread, | |
92 int32 routing_id, | |
93 const gfx::Size& size) | |
94 : view_(view), | |
95 gpu_thread_(gpu_thread), | |
96 routing_id_(routing_id), | |
97 native_size_(size), | |
98 program_(0) { | |
99 memset(textures_, 0, sizeof(textures_)); | |
100 | |
101 // Load identity vertices. | |
102 gfx::Rect identity(0, 0, 1, 1); | |
103 CalculateVertices(identity.size(), identity, target_vertices_); | |
104 | |
105 gpu_thread_->AddRoute(routing_id_, this); | |
106 | |
107 view_->BindContext(); // Must do this before issuing OpenGl. | |
108 | |
109 // TODO(apatrick): These functions are not available in GLES2. | |
110 // glMatrixMode(GL_MODELVIEW); | |
111 | |
112 // Create 3 textures, one for each plane, and bind them to different | |
113 // texture units. | |
114 glGenTextures(kYUVPlanes, textures_); | |
115 | |
116 glBindTexture(GL_TEXTURE_2D, textures_[kYPlane]); | |
117 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
118 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
119 | |
120 glBindTexture(GL_TEXTURE_2D, textures_[kUPlane]); | |
121 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
122 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
123 | |
124 glBindTexture(GL_TEXTURE_2D, textures_[kVPlane]); | |
125 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
126 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
127 | |
128 // Create our YUV->RGB shader. | |
129 program_ = glCreateProgram(); | |
130 GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER); | |
131 const char* vs_source = kVertexShader; | |
132 int vs_size = sizeof(kVertexShader); | |
133 glShaderSource(vertex_shader, 1, &vs_source, &vs_size); | |
134 glCompileShader(vertex_shader); | |
135 int result = GL_FALSE; | |
136 glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &result); | |
137 if (!result) { | |
138 char log[kErrorSize]; | |
139 int len; | |
140 glGetShaderInfoLog(vertex_shader, kErrorSize - 1, &len, log); | |
141 log[kErrorSize - 1] = 0; | |
142 LOG(FATAL) << log; | |
143 } | |
144 glAttachShader(program_, vertex_shader); | |
145 glDeleteShader(vertex_shader); | |
146 | |
147 GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); | |
148 const char* ps_source = kFragmentShader; | |
149 int ps_size = sizeof(kFragmentShader); | |
150 glShaderSource(fragment_shader, 1, &ps_source, &ps_size); | |
151 glCompileShader(fragment_shader); | |
152 result = GL_FALSE; | |
153 glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &result); | |
154 if (!result) { | |
155 char log[kErrorSize]; | |
156 int len; | |
157 glGetShaderInfoLog(fragment_shader, kErrorSize - 1, &len, log); | |
158 log[kErrorSize - 1] = 0; | |
159 LOG(FATAL) << log; | |
160 } | |
161 glAttachShader(program_, fragment_shader); | |
162 glDeleteShader(fragment_shader); | |
163 | |
164 glLinkProgram(program_); | |
165 result = GL_FALSE; | |
166 glGetProgramiv(program_, GL_LINK_STATUS, &result); | |
167 if (!result) { | |
168 char log[kErrorSize]; | |
169 int len; | |
170 glGetProgramInfoLog(program_, kErrorSize - 1, &len, log); | |
171 log[kErrorSize - 1] = 0; | |
172 LOG(FATAL) << log; | |
173 } | |
174 } | |
175 | |
176 GpuVideoLayerGLX::~GpuVideoLayerGLX() { | |
177 // TODO(scherkus): this seems like a bad idea.. we might be better off with | |
178 // separate Initialize()/Teardown() calls instead. | |
179 view_->BindContext(); | |
180 if (program_) { | |
181 glDeleteProgram(program_); | |
182 } | |
183 | |
184 gpu_thread_->RemoveRoute(routing_id_); | |
185 } | |
186 | |
187 void GpuVideoLayerGLX::Render(const gfx::Size& viewport_size) { | |
188 // Nothing to do if we're not visible or have no YUV data. | |
189 if (target_rect_.IsEmpty()) { | |
190 return; | |
191 } | |
192 | |
193 // Calculate the position of our quad. | |
194 CalculateVertices(viewport_size, target_rect_, target_vertices_); | |
195 | |
196 // Bind Y, U and V textures to texture units. | |
197 glActiveTexture(GL_TEXTURE0); | |
198 glBindTexture(GL_TEXTURE_2D, textures_[kYPlane]); | |
199 glActiveTexture(GL_TEXTURE1); | |
200 glBindTexture(GL_TEXTURE_2D, textures_[kUPlane]); | |
201 glActiveTexture(GL_TEXTURE2); | |
202 glBindTexture(GL_TEXTURE_2D, textures_[kVPlane]); | |
203 | |
204 // Bind vertex/fragment shader program. | |
205 glUseProgram(program_); | |
206 | |
207 // Bind parameters. | |
208 glUniform1i(glGetUniformLocation(program_, "y_tex"), 0); | |
209 glUniform1i(glGetUniformLocation(program_, "u_tex"), 1); | |
210 glUniform1i(glGetUniformLocation(program_, "v_tex"), 2); | |
211 | |
212 #if !defined(I915_WORKAROUND) | |
213 int yuv2rgb_location = glGetUniformLocation(program_, "yuv2rgb"); | |
214 glUniformMatrix3fv(yuv2rgb_location, 1, GL_TRUE, kYUV2RGB); | |
215 #endif | |
216 | |
217 // TODO(scherkus): instead of calculating and loading a geometry each time, | |
218 // we should store a constant geometry in a VBO and use a vertex shader. | |
219 int pos_location = glGetAttribLocation(program_, "in_pos"); | |
220 glEnableVertexAttribArray(pos_location); | |
221 glVertexAttribPointer(pos_location, 2, GL_FLOAT, GL_FALSE, 0, | |
222 target_vertices_); | |
223 | |
224 int tc_location = glGetAttribLocation(program_, "in_tc"); | |
225 glEnableVertexAttribArray(tc_location); | |
226 glVertexAttribPointer(tc_location, 2, GL_FLOAT, GL_FALSE, 0, | |
227 kTextureCoords); | |
228 | |
229 // Render! | |
230 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | |
231 | |
232 // Reset back to original state. | |
233 glDisableVertexAttribArray(pos_location); | |
234 glDisableVertexAttribArray(tc_location); | |
235 glActiveTexture(GL_TEXTURE0); | |
236 glUseProgram(0); | |
237 } | |
238 | |
239 void GpuVideoLayerGLX::OnMessageReceived(const IPC::Message& msg) { | |
240 IPC_BEGIN_MESSAGE_MAP(GpuVideoLayerGLX, msg) | |
241 IPC_MESSAGE_HANDLER(GpuMsg_PaintToVideoLayer, OnPaintToVideoLayer) | |
242 IPC_END_MESSAGE_MAP_EX() | |
243 } | |
244 | |
245 void GpuVideoLayerGLX::OnChannelConnected(int32 peer_pid) { | |
246 } | |
247 | |
248 void GpuVideoLayerGLX::OnChannelError() { | |
249 // FIXME(brettw) does this mean we aren't getting any more messages and we | |
250 // should delete outselves? | |
251 NOTIMPLEMENTED(); | |
252 } | |
253 | |
254 void GpuVideoLayerGLX::OnPaintToVideoLayer(base::ProcessId source_process_id, | |
255 TransportDIB::Id id, | |
256 const gfx::Rect& bitmap_rect) { | |
257 // TODO(scherkus): |native_size_| is set in constructor, so perhaps this check | |
258 // should be a DCHECK(). | |
259 const int width = native_size_.width(); | |
260 const int height = native_size_.height(); | |
261 const int stride = width; | |
262 | |
263 if (width <= 0 || width > kMaxVideoLayerSize || | |
264 height <= 0 || height > kMaxVideoLayerSize) | |
265 return; | |
266 | |
267 TransportDIB* dib = TransportDIB::Map(id); | |
268 if (!dib) | |
269 return; | |
270 | |
271 // Everything looks good, update our target position and size. | |
272 target_rect_ = bitmap_rect; | |
273 | |
274 // Perform colour space conversion. | |
275 uint8* planes[kYUVPlanes]; | |
276 planes[kYPlane] = reinterpret_cast<uint8*>(dib->memory()); | |
277 planes[kUPlane] = planes[kYPlane] + width * height; | |
278 planes[kVPlane] = planes[kUPlane] + ((width * height) >> 2); | |
279 | |
280 view_->BindContext(); // Must do this before issuing OpenGl. | |
281 | |
282 // Assume YV12 format. | |
283 for (int i = 0; i < kYUVPlanes; ++i) { | |
284 int plane_width = (i == kYPlane ? width : width / 2); | |
285 int plane_height = (i == kYPlane ? height : height / 2); | |
286 int plane_stride = (i == kYPlane ? stride : stride / 2); | |
287 | |
288 // Ensure that we will not read outside the shared mem region. | |
289 if (planes[i] >= planes[kYPlane] && | |
290 (dib->size() - (planes[kYPlane] - planes[i])) >= | |
291 static_cast<unsigned int>(plane_width * plane_height)) { | |
292 glActiveTexture(GL_TEXTURE0 + i); | |
293 glBindTexture(GL_TEXTURE_2D, textures_[i]); | |
294 glPixelStorei(GL_UNPACK_ROW_LENGTH, plane_stride); | |
295 glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, plane_width, plane_height, 0, | |
296 GL_LUMINANCE, GL_UNSIGNED_BYTE, planes[i]); | |
297 } | |
298 } | |
299 | |
300 // Reset back to original state. | |
301 glActiveTexture(GL_TEXTURE0); | |
302 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); | |
303 glFlush(); | |
304 | |
305 // TODO(scherkus): we may not need to ACK video layer updates at all. | |
306 gpu_thread_->Send(new GpuHostMsg_PaintToVideoLayer_ACK(routing_id_)); | |
307 } | |
308 | |
309 // static | |
310 void GpuVideoLayerGLX::CalculateVertices(const gfx::Size& world, | |
311 const gfx::Rect& object, | |
312 float* vertices) { | |
313 // Don't forget GL has a flipped Y-axis! | |
314 float width = world.width(); | |
315 float height = world.height(); | |
316 | |
317 // Top left. | |
318 vertices[0] = 2.0f * (object.x() / width) - 1.0f; | |
319 vertices[1] = -2.0f * (object.y() / height) + 1.0f; | |
320 | |
321 // Bottom left. | |
322 vertices[2] = 2.0f * (object.x() / width) - 1.0f; | |
323 vertices[3] = -2.0f * (object.bottom() / height) + 1.0f; | |
324 | |
325 // Top right. | |
326 vertices[4] = 2.0f * (object.right() / width) - 1.0f; | |
327 vertices[5] = -2.0f * (object.y() / height) + 1.0f; | |
328 | |
329 // Bottom right. | |
330 vertices[6] = 2.0f * (object.right() / width) - 1.0f; | |
331 vertices[7] = -2.0f * (object.bottom() / height) + 1.0f; | |
332 } | |
OLD | NEW |