OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ui/gfx/surface/accelerated_surface_mac.h" | 5 #include "ui/gfx/surface/accelerated_surface_mac.h" |
6 | 6 |
7 #include "base/logging.h" | 7 #include "base/logging.h" |
8 #include "base/mac/scoped_cftyperef.h" | 8 #include "base/mac/scoped_cftyperef.h" |
9 #include "ui/gfx/gl/gl_bindings.h" | 9 #include "ui/gfx/gl/gl_bindings.h" |
10 #include "ui/gfx/gl/gl_implementation.h" | 10 #include "ui/gfx/gl/gl_implementation.h" |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
87 // rendering results consumed by the AcceleratedSurface. | 87 // rendering results consumed by the AcceleratedSurface. |
88 // Need to save and restore OpenGL state around this call. | 88 // Need to save and restore OpenGL state around this call. |
89 GLint current_texture = 0; | 89 GLint current_texture = 0; |
90 GLenum target_binding = GL_TEXTURE_BINDING_RECTANGLE_ARB; | 90 GLenum target_binding = GL_TEXTURE_BINDING_RECTANGLE_ARB; |
91 GLenum target = GL_TEXTURE_RECTANGLE_ARB; | 91 GLenum target = GL_TEXTURE_RECTANGLE_ARB; |
92 glGetIntegerv(target_binding, ¤t_texture); | 92 glGetIntegerv(target_binding, ¤t_texture); |
93 glBindTexture(target, texture_); | 93 glBindTexture(target, texture_); |
94 glCopyTexSubImage2D(target, 0, | 94 glCopyTexSubImage2D(target, 0, |
95 0, 0, | 95 0, 0, |
96 0, 0, | 96 0, 0, |
97 surface_size_.width(), surface_size_.height()); | 97 real_surface_size_.width(), |
| 98 real_surface_size_.height()); |
98 glBindTexture(target, current_texture); | 99 glBindTexture(target, current_texture); |
99 // This flush is absolutely essential -- it guarantees that the | 100 // This flush is absolutely essential -- it guarantees that the |
100 // rendering results are seen by the other process. | 101 // rendering results are seen by the other process. |
101 glFlush(); | 102 glFlush(); |
102 } | 103 } |
103 } else if (transport_dib_.get() != NULL) { | 104 } else if (transport_dib_.get() != NULL) { |
104 // Pre-Mac OS X 10.6, fetch the rendered image from the current frame | 105 // Pre-Mac OS X 10.6, fetch the rendered image from the current frame |
105 // buffer and copy it into the TransportDIB. | 106 // buffer and copy it into the TransportDIB. |
106 // TODO(dspringer): There are a couple of options that can speed this up. | 107 // TODO(dspringer): There are a couple of options that can speed this up. |
107 // First is to use async reads into a PBO, second is to use SPI that | 108 // First is to use async reads into a PBO, second is to use SPI that |
108 // allows many tasks to access the same CGSSurface. | 109 // allows many tasks to access the same CGSSurface. |
109 void* pixel_memory = transport_dib_->memory(); | 110 void* pixel_memory = transport_dib_->memory(); |
110 if (pixel_memory) { | 111 if (pixel_memory) { |
111 // Note that glReadPixels does an implicit glFlush(). | 112 // Note that glReadPixels does an implicit glFlush(). |
112 glReadPixels(0, | 113 glReadPixels(0, |
113 0, | 114 0, |
114 surface_size_.width(), | 115 real_surface_size_.width(), |
115 surface_size_.height(), | 116 real_surface_size_.height(), |
116 GL_BGRA, // This pixel format should have no conversion. | 117 GL_BGRA, // This pixel format should have no conversion. |
117 GL_UNSIGNED_INT_8_8_8_8_REV, | 118 GL_UNSIGNED_INT_8_8_8_8_REV, |
118 pixel_memory); | 119 pixel_memory); |
119 } | 120 } |
120 } | 121 } |
121 } | 122 } |
122 | 123 |
123 static void AddBooleanValue(CFMutableDictionaryRef dictionary, | 124 static void AddBooleanValue(CFMutableDictionaryRef dictionary, |
124 const CFStringRef key, | 125 const CFStringRef key, |
125 bool value) { | 126 bool value) { |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
187 if (fbo_status == GL_FRAMEBUFFER_COMPLETE_EXT) { | 188 if (fbo_status == GL_FRAMEBUFFER_COMPLETE_EXT) { |
188 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, | 189 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, |
189 0x8D20, // GL_STENCIL_ATTACHMENT, | 190 0x8D20, // GL_STENCIL_ATTACHMENT, |
190 GL_RENDERBUFFER_EXT, | 191 GL_RENDERBUFFER_EXT, |
191 depth_stencil_renderbuffer_); | 192 depth_stencil_renderbuffer_); |
192 fbo_status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); | 193 fbo_status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); |
193 } | 194 } |
194 return fbo_status == GL_FRAMEBUFFER_COMPLETE_EXT; | 195 return fbo_status == GL_FRAMEBUFFER_COMPLETE_EXT; |
195 } | 196 } |
196 | 197 |
| 198 gfx::Size AcceleratedSurface::ClampToValidDimensions(const gfx::Size& size) { |
| 199 return gfx::Size(std::max(size.width(), 1), std::max(size.height(), 1)); |
| 200 } |
| 201 |
197 bool AcceleratedSurface::MakeCurrent() { | 202 bool AcceleratedSurface::MakeCurrent() { |
198 if (!gl_context_.get()) | 203 if (!gl_context_.get()) |
199 return false; | 204 return false; |
200 return gl_context_->MakeCurrent(gl_surface_.get()); | 205 return gl_context_->MakeCurrent(gl_surface_.get()); |
201 } | 206 } |
202 | 207 |
203 void AcceleratedSurface::Clear(const gfx::Rect& rect) { | 208 void AcceleratedSurface::Clear(const gfx::Rect& rect) { |
204 DCHECK(gl_context_->IsCurrent(gl_surface_.get())); | 209 DCHECK(gl_context_->IsCurrent(gl_surface_.get())); |
205 glClearColor(0, 0, 0, 0); | 210 glClearColor(0, 0, 0, 0); |
206 glViewport(0, 0, rect.width(), rect.height()); | 211 glViewport(0, 0, rect.width(), rect.height()); |
(...skipping 16 matching lines...) Expand all Loading... |
223 if (gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL) | 228 if (gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL) |
224 return 0; | 229 return 0; |
225 | 230 |
226 IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize(); | 231 IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize(); |
227 if (!io_surface_support) | 232 if (!io_surface_support) |
228 return 0; // Caller can try using SetWindowSizeForTransportDIB(). | 233 return 0; // Caller can try using SetWindowSizeForTransportDIB(). |
229 | 234 |
230 if (!MakeCurrent()) | 235 if (!MakeCurrent()) |
231 return 0; | 236 return 0; |
232 | 237 |
| 238 gfx::Size clamped_size = ClampToValidDimensions(size); |
| 239 |
233 // GL_TEXTURE_RECTANGLE_ARB is the best supported render target on | 240 // GL_TEXTURE_RECTANGLE_ARB is the best supported render target on |
234 // Mac OS X and is required for IOSurface interoperability. | 241 // Mac OS X and is required for IOSurface interoperability. |
235 GLenum target = GL_TEXTURE_RECTANGLE_ARB; | 242 GLenum target = GL_TEXTURE_RECTANGLE_ARB; |
236 if (allocate_fbo_) { | 243 if (allocate_fbo_) { |
237 AllocateRenderBuffers(target, size); | 244 AllocateRenderBuffers(target, clamped_size); |
238 } else if (!texture_) { | 245 } else if (!texture_) { |
239 // Generate the texture object. | 246 // Generate the texture object. |
240 glGenTextures(1, &texture_); | 247 glGenTextures(1, &texture_); |
241 glBindTexture(target, texture_); | 248 glBindTexture(target, texture_); |
242 glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | 249 glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
243 glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | 250 glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
244 glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | 251 glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
245 glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | 252 glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
246 } | 253 } |
247 | 254 |
248 // Allocate a new IOSurface, which is the GPU resource that can be | 255 // Allocate a new IOSurface, which is the GPU resource that can be |
249 // shared across processes. | 256 // shared across processes. |
250 base::mac::ScopedCFTypeRef<CFMutableDictionaryRef> properties; | 257 base::mac::ScopedCFTypeRef<CFMutableDictionaryRef> properties; |
251 properties.reset(CFDictionaryCreateMutable(kCFAllocatorDefault, | 258 properties.reset(CFDictionaryCreateMutable(kCFAllocatorDefault, |
252 0, | 259 0, |
253 &kCFTypeDictionaryKeyCallBacks, | 260 &kCFTypeDictionaryKeyCallBacks, |
254 &kCFTypeDictionaryValueCallBacks)); | 261 &kCFTypeDictionaryValueCallBacks)); |
255 AddIntegerValue(properties, | 262 AddIntegerValue(properties, |
256 io_surface_support->GetKIOSurfaceWidth(), size.width()); | 263 io_surface_support->GetKIOSurfaceWidth(), |
| 264 clamped_size.width()); |
257 AddIntegerValue(properties, | 265 AddIntegerValue(properties, |
258 io_surface_support->GetKIOSurfaceHeight(), size.height()); | 266 io_surface_support->GetKIOSurfaceHeight(), |
| 267 clamped_size.height()); |
259 AddIntegerValue(properties, | 268 AddIntegerValue(properties, |
260 io_surface_support->GetKIOSurfaceBytesPerElement(), 4); | 269 io_surface_support->GetKIOSurfaceBytesPerElement(), 4); |
261 AddBooleanValue(properties, | 270 AddBooleanValue(properties, |
262 io_surface_support->GetKIOSurfaceIsGlobal(), true); | 271 io_surface_support->GetKIOSurfaceIsGlobal(), true); |
263 // I believe we should be able to unreference the IOSurfaces without | 272 // I believe we should be able to unreference the IOSurfaces without |
264 // synchronizing with the browser process because they are | 273 // synchronizing with the browser process because they are |
265 // ultimately reference counted by the operating system. | 274 // ultimately reference counted by the operating system. |
266 io_surface_.reset(io_surface_support->IOSurfaceCreate(properties)); | 275 io_surface_.reset(io_surface_support->IOSurfaceCreate(properties)); |
267 | 276 |
268 // Don't think we need to identify a plane. | 277 // Don't think we need to identify a plane. |
269 GLuint plane = 0; | 278 GLuint plane = 0; |
270 io_surface_support->CGLTexImageIOSurface2D( | 279 io_surface_support->CGLTexImageIOSurface2D( |
271 static_cast<CGLContextObj>(gl_context_->GetHandle()), | 280 static_cast<CGLContextObj>(gl_context_->GetHandle()), |
272 target, | 281 target, |
273 GL_RGBA, | 282 GL_RGBA, |
274 size.width(), | 283 clamped_size.width(), |
275 size.height(), | 284 clamped_size.height(), |
276 GL_BGRA, | 285 GL_BGRA, |
277 GL_UNSIGNED_INT_8_8_8_8_REV, | 286 GL_UNSIGNED_INT_8_8_8_8_REV, |
278 io_surface_.get(), | 287 io_surface_.get(), |
279 plane); | 288 plane); |
280 if (allocate_fbo_) { | 289 if (allocate_fbo_) { |
281 // Set up the frame buffer object. | 290 // Set up the frame buffer object. |
282 SetupFrameBufferObject(target); | 291 SetupFrameBufferObject(target); |
283 } | 292 } |
284 surface_size_ = size; | 293 surface_size_ = size; |
| 294 real_surface_size_ = clamped_size; |
285 | 295 |
286 // Now send back an identifier for the IOSurface. We originally | 296 // Now send back an identifier for the IOSurface. We originally |
287 // intended to send back a mach port from IOSurfaceCreateMachPort | 297 // intended to send back a mach port from IOSurfaceCreateMachPort |
288 // but it looks like Chrome IPC would need to be modified to | 298 // but it looks like Chrome IPC would need to be modified to |
289 // properly send mach ports between processes. For the time being we | 299 // properly send mach ports between processes. For the time being we |
290 // make our IOSurfaces global and send back their identifiers. On | 300 // make our IOSurfaces global and send back their identifiers. On |
291 // the browser process side the identifier is reconstituted into an | 301 // the browser process side the identifier is reconstituted into an |
292 // IOSurface for on-screen rendering. | 302 // IOSurface for on-screen rendering. |
293 io_surface_id_ = io_surface_support->IOSurfaceGetID(io_surface_); | 303 io_surface_id_ = io_surface_support->IOSurfaceGetID(io_surface_); |
294 return io_surface_id_; | 304 return io_surface_id_; |
295 } | 305 } |
296 | 306 |
297 uint64 AcceleratedSurface::GetSurfaceId() { | 307 uint64 AcceleratedSurface::GetSurfaceId() { |
298 return io_surface_id_; | 308 return io_surface_id_; |
299 } | 309 } |
300 | 310 |
301 TransportDIB::Handle AcceleratedSurface::SetTransportDIBSize( | 311 TransportDIB::Handle AcceleratedSurface::SetTransportDIBSize( |
302 const gfx::Size& size) { | 312 const gfx::Size& size) { |
303 if (surface_size_ == size) { | 313 if (surface_size_ == size) { |
304 // Return an invalid handle to indicate to the caller that no new backing | 314 // Return an invalid handle to indicate to the caller that no new backing |
305 // store allocation occurred. | 315 // store allocation occurred. |
306 return TransportDIB::DefaultHandleValue(); | 316 return TransportDIB::DefaultHandleValue(); |
307 } | 317 } |
308 surface_size_ = size; | 318 surface_size_ = size; |
| 319 gfx::Size clamped_size = ClampToValidDimensions(size); |
| 320 real_surface_size_ = clamped_size; |
309 | 321 |
310 // Release the old TransportDIB in the browser. | 322 // Release the old TransportDIB in the browser. |
311 if (dib_free_callback_.get() && transport_dib_.get()) { | 323 if (dib_free_callback_.get() && transport_dib_.get()) { |
312 dib_free_callback_->Run(transport_dib_->id()); | 324 dib_free_callback_->Run(transport_dib_->id()); |
313 } | 325 } |
314 transport_dib_.reset(); | 326 transport_dib_.reset(); |
315 | 327 |
316 // Ask the renderer to create a TransportDIB. | 328 // Ask the renderer to create a TransportDIB. |
317 size_t dib_size = size.width() * 4 * size.height(); // 4 bytes per pixel. | 329 size_t dib_size = |
| 330 clamped_size.width() * 4 * clamped_size.height(); // 4 bytes per pixel. |
318 TransportDIB::Handle dib_handle; | 331 TransportDIB::Handle dib_handle; |
319 if (dib_alloc_callback_.get()) { | 332 if (dib_alloc_callback_.get()) { |
320 dib_alloc_callback_->Run(dib_size, &dib_handle); | 333 dib_alloc_callback_->Run(dib_size, &dib_handle); |
321 } | 334 } |
322 if (!TransportDIB::is_valid_handle(dib_handle)) { | 335 if (!TransportDIB::is_valid_handle(dib_handle)) { |
323 // If the allocator fails, it means the DIB was not created in the browser, | 336 // If the allocator fails, it means the DIB was not created in the browser, |
324 // so there is no need to run the deallocator here. | 337 // so there is no need to run the deallocator here. |
325 return TransportDIB::DefaultHandleValue(); | 338 return TransportDIB::DefaultHandleValue(); |
326 } | 339 } |
327 transport_dib_.reset(TransportDIB::Map(dib_handle)); | 340 transport_dib_.reset(TransportDIB::Map(dib_handle)); |
328 if (transport_dib_.get() == NULL) { | 341 if (transport_dib_.get() == NULL) { |
329 // TODO(dspringer): if the Map() fails, should the deallocator be run so | 342 // TODO(dspringer): if the Map() fails, should the deallocator be run so |
330 // that the DIB is deallocated in the browser? | 343 // that the DIB is deallocated in the browser? |
331 return TransportDIB::DefaultHandleValue(); | 344 return TransportDIB::DefaultHandleValue(); |
332 } | 345 } |
333 | 346 |
334 if (allocate_fbo_) { | 347 if (allocate_fbo_) { |
335 DCHECK(gl_context_->IsCurrent(gl_surface_.get())); | 348 DCHECK(gl_context_->IsCurrent(gl_surface_.get())); |
336 // Set up the render buffers and reserve enough space on the card for the | 349 // Set up the render buffers and reserve enough space on the card for the |
337 // framebuffer texture. | 350 // framebuffer texture. |
338 GLenum target = GL_TEXTURE_RECTANGLE_ARB; | 351 GLenum target = GL_TEXTURE_RECTANGLE_ARB; |
339 AllocateRenderBuffers(target, size); | 352 AllocateRenderBuffers(target, clamped_size); |
340 glTexImage2D(target, | 353 glTexImage2D(target, |
341 0, // mipmap level 0 | 354 0, // mipmap level 0 |
342 GL_RGBA8, // internal pixel format | 355 GL_RGBA8, // internal pixel format |
343 size.width(), | 356 clamped_size.width(), |
344 size.height(), | 357 clamped_size.height(), |
345 0, // 0 border | 358 0, // 0 border |
346 GL_BGRA, // Used for consistency | 359 GL_BGRA, // Used for consistency |
347 GL_UNSIGNED_INT_8_8_8_8_REV, | 360 GL_UNSIGNED_INT_8_8_8_8_REV, |
348 NULL); // No data, just reserve room on the card. | 361 NULL); // No data, just reserve room on the card. |
349 SetupFrameBufferObject(target); | 362 SetupFrameBufferObject(target); |
350 } | 363 } |
351 return transport_dib_->handle(); | 364 return transport_dib_->handle(); |
352 } | 365 } |
353 | 366 |
354 void AcceleratedSurface::SetTransportDIBAllocAndFree( | 367 void AcceleratedSurface::SetTransportDIBAllocAndFree( |
355 Callback2<size_t, TransportDIB::Handle*>::Type* allocator, | 368 Callback2<size_t, TransportDIB::Handle*>::Type* allocator, |
356 Callback1<TransportDIB::Id>::Type* deallocator) { | 369 Callback1<TransportDIB::Id>::Type* deallocator) { |
357 dib_alloc_callback_.reset(allocator); | 370 dib_alloc_callback_.reset(allocator); |
358 dib_free_callback_.reset(deallocator); | 371 dib_free_callback_.reset(deallocator); |
359 } | 372 } |
OLD | NEW |