| 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 |