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
3 // found in the LICENSE file.
5 #if defined(ENABLE_GPU)
7 #include "content/common/gpu/image_transport_surface.h"
9 #include "base/mac/scoped_cftyperef.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "content/common/gpu/gpu_messages.h"
12 #include "ui/gfx/gl/gl_context.h"
13 #include "ui/gfx/gl/gl_bindings.h"
14 #include "ui/gfx/gl/gl_implementation.h"
15 #include "ui/gfx/gl/gl_surface_cgl.h"
16 #include "ui/gfx/native_widget_types.h"
17 #include "ui/gfx/surface/io_surface_support_mac.h"
21 // We are backed by an offscreen surface for the purposes of creating
22 // a context, but use FBOs to render to texture backed IOSurface
23 class IOSurfaceImageTransportSurface
: public gfx::NoOpGLSurfaceCGL
,
24 public ImageTransportSurface
{
26 IOSurfaceImageTransportSurface(GpuChannelManager
* manager
,
29 int32 command_buffer_id
,
30 gfx::PluginWindowHandle handle
);
32 // GLSurface implementation
33 virtual bool Initialize() OVERRIDE
;
34 virtual void Destroy() OVERRIDE
;
35 virtual bool IsOffscreen() OVERRIDE
;
36 virtual bool SwapBuffers() OVERRIDE
;
37 virtual bool PostSubBuffer(int x
, int y
, int width
, int height
) OVERRIDE
;
38 virtual std::string
GetExtensions() OVERRIDE
;
39 virtual gfx::Size
GetSize() OVERRIDE
;
40 virtual bool OnMakeCurrent(gfx::GLContext
* context
) OVERRIDE
;
41 virtual unsigned int GetBackingFrameBufferObject() OVERRIDE
;
44 // ImageTransportSurface implementation
45 virtual void OnNewSurfaceACK(uint64 surface_id
,
46 TransportDIB::Handle shm_handle
) OVERRIDE
;
47 virtual void OnBuffersSwappedACK() OVERRIDE
;
48 virtual void OnPostSubBufferACK() OVERRIDE
;
49 virtual void OnResizeViewACK() OVERRIDE
;
50 virtual void OnResize(gfx::Size size
) OVERRIDE
;
53 virtual ~IOSurfaceImageTransportSurface() OVERRIDE
;
58 base::mac::ScopedCFTypeRef
<CFTypeRef
> io_surface_
;
60 // The id of |io_surface_| or 0 if that's NULL.
61 uint64 io_surface_id_
;
63 // Weak pointer to the context that this was last made current to.
64 gfx::GLContext
* context_
;
68 // Whether or not we've successfully made the surface current once.
71 scoped_ptr
<ImageTransportHelper
> helper_
;
73 DISALLOW_COPY_AND_ASSIGN(IOSurfaceImageTransportSurface
);
76 // We are backed by an offscreen surface for the purposes of creating
77 // a context, but use FBOs to render to texture backed IOSurface
78 class TransportDIBImageTransportSurface
: public gfx::NoOpGLSurfaceCGL
,
79 public ImageTransportSurface
{
81 TransportDIBImageTransportSurface(GpuChannelManager
* manager
,
84 int32 command_buffer_id
,
85 gfx::PluginWindowHandle handle
);
87 // GLSurface implementation
88 virtual bool Initialize() OVERRIDE
;
89 virtual void Destroy() OVERRIDE
;
90 virtual bool IsOffscreen() OVERRIDE
;
91 virtual bool SwapBuffers() OVERRIDE
;
92 virtual bool PostSubBuffer(int x
, int y
, int width
, int height
) OVERRIDE
;
93 virtual std::string
GetExtensions() OVERRIDE
;
94 virtual gfx::Size
GetSize() OVERRIDE
;
95 virtual bool OnMakeCurrent(gfx::GLContext
* context
) OVERRIDE
;
96 virtual unsigned int GetBackingFrameBufferObject() OVERRIDE
;
99 // ImageTransportSurface implementation
100 virtual void OnBuffersSwappedACK() OVERRIDE
;
101 virtual void OnPostSubBufferACK() OVERRIDE
;
102 virtual void OnNewSurfaceACK(uint64 surface_id
,
103 TransportDIB::Handle shm_handle
) OVERRIDE
;
104 virtual void OnResizeViewACK() OVERRIDE
;
105 virtual void OnResize(gfx::Size size
) OVERRIDE
;
108 virtual ~TransportDIBImageTransportSurface() OVERRIDE
;
111 GLuint render_buffer_id_
;
113 scoped_ptr
<TransportDIB
> shared_mem_
;
117 static uint32 next_id_
;
119 // Whether or not we've successfully made the surface current once.
122 scoped_ptr
<ImageTransportHelper
> helper_
;
124 DISALLOW_COPY_AND_ASSIGN(TransportDIBImageTransportSurface
);
127 uint32
TransportDIBImageTransportSurface::next_id_
= 1;
129 void AddBooleanValue(CFMutableDictionaryRef dictionary
,
130 const CFStringRef key
,
132 CFDictionaryAddValue(dictionary
, key
,
133 (value
? kCFBooleanTrue
: kCFBooleanFalse
));
136 void AddIntegerValue(CFMutableDictionaryRef dictionary
,
137 const CFStringRef key
,
139 base::mac::ScopedCFTypeRef
<CFNumberRef
> number(
140 CFNumberCreate(NULL
, kCFNumberSInt32Type
, &value
));
141 CFDictionaryAddValue(dictionary
, key
, number
.get());
144 IOSurfaceImageTransportSurface::IOSurfaceImageTransportSurface(
145 GpuChannelManager
* manager
,
146 int32 render_view_id
,
148 int32 command_buffer_id
,
149 gfx::PluginWindowHandle handle
)
150 : gfx::NoOpGLSurfaceCGL(gfx::Size(1, 1)),
155 made_current_(false) {
156 helper_
.reset(new ImageTransportHelper(this,
165 IOSurfaceImageTransportSurface::~IOSurfaceImageTransportSurface() {
169 bool IOSurfaceImageTransportSurface::Initialize() {
170 // Only support IOSurfaces if the GL implementation is the native desktop GL.
171 // IO surfaces will not work with, for example, OSMesa software renderer
173 if (gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL
&&
174 gfx::GetGLImplementation() != gfx::kGLImplementationAppleGL
)
177 if (!helper_
->Initialize())
179 return NoOpGLSurfaceCGL::Initialize();
182 void IOSurfaceImageTransportSurface::Destroy() {
184 glDeleteFramebuffersEXT(1, &fbo_id_
);
189 glDeleteTextures(1, &texture_id_
);
194 NoOpGLSurfaceCGL::Destroy();
197 bool IOSurfaceImageTransportSurface::IsOffscreen() {
201 bool IOSurfaceImageTransportSurface::OnMakeCurrent(gfx::GLContext
* context
) {
207 glGenFramebuffersEXT(1, &fbo_id_
);
208 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, fbo_id_
);
209 OnResize(gfx::Size(1, 1));
211 GLenum status
= glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT
);
212 if (status
!= GL_FRAMEBUFFER_COMPLETE
) {
213 DLOG(ERROR
) << "Framebuffer incomplete.";
217 made_current_
= true;
221 unsigned int IOSurfaceImageTransportSurface::GetBackingFrameBufferObject() {
225 bool IOSurfaceImageTransportSurface::SwapBuffers() {
228 GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params
;
229 params
.surface_id
= io_surface_id_
;
230 helper_
->SendAcceleratedSurfaceBuffersSwapped(params
);
232 helper_
->SetScheduled(false);
236 bool IOSurfaceImageTransportSurface::PostSubBuffer(
237 int x
, int y
, int width
, int height
) {
240 GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params params
;
241 params
.surface_id
= io_surface_id_
;
244 params
.width
= width
;
245 params
.height
= height
;
246 helper_
->SendAcceleratedSurfacePostSubBuffer(params
);
248 helper_
->SetScheduled(false);
252 std::string
IOSurfaceImageTransportSurface::GetExtensions() {
253 std::string extensions
= gfx::GLSurface::GetExtensions();
254 extensions
+= extensions
.empty() ? "" : " ";
255 extensions
+= "GL_CHROMIUM_front_buffer_cached ";
256 extensions
+= "GL_CHROMIUM_post_sub_buffer";
260 gfx::Size
IOSurfaceImageTransportSurface::GetSize() {
264 void IOSurfaceImageTransportSurface::OnBuffersSwappedACK() {
265 helper_
->SetScheduled(true);
268 void IOSurfaceImageTransportSurface::OnPostSubBufferACK() {
269 helper_
->SetScheduled(true);
272 void IOSurfaceImageTransportSurface::OnNewSurfaceACK(
274 TransportDIB::Handle
/* shm_handle */) {
275 DCHECK_EQ(io_surface_id_
, surface_id
);
276 helper_
->SetScheduled(true);
279 void IOSurfaceImageTransportSurface::OnResizeViewACK() {
283 void IOSurfaceImageTransportSurface::OnResize(gfx::Size size
) {
284 IOSurfaceSupport
* io_surface_support
= IOSurfaceSupport::Initialize();
286 // Caching |context_| from OnMakeCurrent. It should still be current.
287 DCHECK(context_
->IsCurrent(this));
292 glDeleteTextures(1, &texture_id_
);
296 glGenTextures(1, &texture_id_
);
298 GLint previous_texture_id
= 0;
299 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB
, &previous_texture_id
);
301 // GL_TEXTURE_RECTANGLE_ARB is the best supported render target on
302 // Mac OS X and is required for IOSurface interoperability.
303 GLenum target
= GL_TEXTURE_RECTANGLE_ARB
;
304 glBindTexture(target
, texture_id_
);
305 glTexParameteri(target
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
306 glTexParameteri(target
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
307 glTexParameteri(target
, GL_TEXTURE_WRAP_S
, GL_CLAMP_TO_EDGE
);
308 glTexParameteri(target
, GL_TEXTURE_WRAP_T
, GL_CLAMP_TO_EDGE
);
310 GLint previous_fbo_id
= 0;
311 glGetIntegerv(GL_FRAMEBUFFER_BINDING
, &previous_fbo_id
);
313 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, fbo_id_
);
315 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT
,
316 GL_COLOR_ATTACHMENT0_EXT
,
321 // Allocate a new IOSurface, which is the GPU resource that can be
322 // shared across processes.
323 base::mac::ScopedCFTypeRef
<CFMutableDictionaryRef
> properties
;
324 properties
.reset(CFDictionaryCreateMutable(kCFAllocatorDefault
,
326 &kCFTypeDictionaryKeyCallBacks
,
327 &kCFTypeDictionaryValueCallBacks
));
328 AddIntegerValue(properties
,
329 io_surface_support
->GetKIOSurfaceWidth(),
331 AddIntegerValue(properties
,
332 io_surface_support
->GetKIOSurfaceHeight(),
334 AddIntegerValue(properties
,
335 io_surface_support
->GetKIOSurfaceBytesPerElement(), 4);
336 AddBooleanValue(properties
,
337 io_surface_support
->GetKIOSurfaceIsGlobal(), true);
338 // I believe we should be able to unreference the IOSurfaces without
339 // synchronizing with the browser process because they are
340 // ultimately reference counted by the operating system.
341 io_surface_
.reset(io_surface_support
->IOSurfaceCreate(properties
));
343 // Don't think we need to identify a plane.
345 io_surface_support
->CGLTexImageIOSurface2D(
346 static_cast<CGLContextObj
>(context_
->GetHandle()),
352 GL_UNSIGNED_INT_8_8_8_8_REV
,
356 io_surface_id_
= io_surface_support
->IOSurfaceGetID(io_surface_
);
359 glBindTexture(target
, previous_texture_id
);
360 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, previous_fbo_id
);
362 GpuHostMsg_AcceleratedSurfaceNew_Params params
;
363 params
.width
= size_
.width();
364 params
.height
= size_
.height();
365 params
.surface_id
= io_surface_id_
;
366 params
.create_transport_dib
= false;
367 helper_
->SendAcceleratedSurfaceNew(params
);
369 helper_
->SetScheduled(false);
372 TransportDIBImageTransportSurface::TransportDIBImageTransportSurface(
373 GpuChannelManager
* manager
,
374 int32 render_view_id
,
376 int32 command_buffer_id
,
377 gfx::PluginWindowHandle handle
)
378 : gfx::NoOpGLSurfaceCGL(gfx::Size(1, 1)),
380 render_buffer_id_(0),
381 made_current_(false) {
382 helper_
.reset(new ImageTransportHelper(this,
391 TransportDIBImageTransportSurface::~TransportDIBImageTransportSurface() {
395 bool TransportDIBImageTransportSurface::Initialize() {
396 if (!helper_
->Initialize())
398 return NoOpGLSurfaceCGL::Initialize();
401 void TransportDIBImageTransportSurface::Destroy() {
403 glDeleteFramebuffersEXT(1, &fbo_id_
);
407 if (render_buffer_id_
) {
408 glDeleteRenderbuffersEXT(1, &render_buffer_id_
);
409 render_buffer_id_
= 0;
413 NoOpGLSurfaceCGL::Destroy();
416 bool TransportDIBImageTransportSurface::IsOffscreen() {
420 bool TransportDIBImageTransportSurface::OnMakeCurrent(gfx::GLContext
* context
) {
424 glGenFramebuffersEXT(1, &fbo_id_
);
425 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, fbo_id_
);
426 OnResize(gfx::Size(1, 1));
428 GLenum status
= glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT
);
429 if (status
!= GL_FRAMEBUFFER_COMPLETE
) {
430 DLOG(ERROR
) << "Framebuffer incomplete.";
434 made_current_
= true;
438 unsigned int TransportDIBImageTransportSurface::GetBackingFrameBufferObject() {
442 bool TransportDIBImageTransportSurface::SwapBuffers() {
443 DCHECK_NE(shared_mem_
.get(), static_cast<void*>(NULL
));
445 GLint previous_fbo_id
= 0;
446 glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT
, &previous_fbo_id
);
448 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, fbo_id_
);
450 GLint current_alignment
= 0;
451 glGetIntegerv(GL_PACK_ALIGNMENT
, ¤t_alignment
);
452 glPixelStorei(GL_PACK_ALIGNMENT
, 4);
454 size_
.width(), size_
.height(),
455 GL_BGRA
, // This pixel format should have no conversion.
456 GL_UNSIGNED_INT_8_8_8_8_REV
,
457 shared_mem_
->memory());
458 glPixelStorei(GL_PACK_ALIGNMENT
, current_alignment
);
460 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, previous_fbo_id
);
462 GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params
;
463 params
.surface_id
= next_id_
;
464 helper_
->SendAcceleratedSurfaceBuffersSwapped(params
);
466 helper_
->SetScheduled(false);
470 bool TransportDIBImageTransportSurface::PostSubBuffer(
471 int x
, int y
, int width
, int height
) {
472 DCHECK_NE(shared_mem_
.get(), static_cast<void*>(NULL
));
474 GLint previous_fbo_id
= 0;
475 glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT
, &previous_fbo_id
);
477 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, fbo_id_
);
479 GLint current_alignment
= 0, current_pack_row_length
= 0;
480 glGetIntegerv(GL_PACK_ALIGNMENT
, ¤t_alignment
);
481 glGetIntegerv(GL_PACK_ROW_LENGTH
, ¤t_pack_row_length
);
483 glPixelStorei(GL_PACK_ALIGNMENT
, 4);
484 glPixelStorei(GL_PACK_ROW_LENGTH
, size_
.width());
486 unsigned char* buffer
=
487 static_cast<unsigned char*>(shared_mem_
->memory());
490 GL_BGRA
, // This pixel format should have no conversion.
491 GL_UNSIGNED_INT_8_8_8_8_REV
,
492 &buffer
[(x
+ y
* size_
.width()) * 4]);
494 glPixelStorei(GL_PACK_ALIGNMENT
, current_alignment
);
495 glPixelStorei(GL_PACK_ROW_LENGTH
, current_pack_row_length
);
497 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, previous_fbo_id
);
499 GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params params
;
500 params
.surface_id
= next_id_
;
503 params
.width
= width
;
504 params
.height
= height
;
505 helper_
->SendAcceleratedSurfacePostSubBuffer(params
);
507 helper_
->SetScheduled(false);
511 std::string
TransportDIBImageTransportSurface::GetExtensions() {
512 std::string extensions
= gfx::GLSurface::GetExtensions();
513 extensions
+= extensions
.empty() ? "" : " ";
514 extensions
+= "GL_CHROMIUM_front_buffer_cached ";
515 extensions
+= "GL_CHROMIUM_post_sub_buffer";
519 gfx::Size
TransportDIBImageTransportSurface::GetSize() {
523 void TransportDIBImageTransportSurface::OnBuffersSwappedACK() {
524 helper_
->SetScheduled(true);
527 void TransportDIBImageTransportSurface::OnPostSubBufferACK() {
528 helper_
->SetScheduled(true);
531 void TransportDIBImageTransportSurface::OnNewSurfaceACK(
533 TransportDIB::Handle shm_handle
) {
534 helper_
->SetScheduled(true);
536 shared_mem_
.reset(TransportDIB::Map(shm_handle
));
537 DCHECK_NE(shared_mem_
.get(), static_cast<void*>(NULL
));
540 void TransportDIBImageTransportSurface::OnResizeViewACK() {
544 void TransportDIBImageTransportSurface::OnResize(gfx::Size size
) {
547 if (!render_buffer_id_
)
548 glGenRenderbuffersEXT(1, &render_buffer_id_
);
550 GLint previous_fbo_id
= 0;
551 glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT
, &previous_fbo_id
);
553 GLint previous_renderbuffer_id
= 0;
554 glGetIntegerv(GL_RENDERBUFFER_BINDING_EXT
, &previous_renderbuffer_id
);
556 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, fbo_id_
);
557 glBindRenderbufferEXT(GL_RENDERBUFFER_EXT
, render_buffer_id_
);
559 glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT
,
561 size_
.width(), size_
.height());
562 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT
,
563 GL_COLOR_ATTACHMENT0_EXT
,
567 glBindRenderbufferEXT(GL_RENDERBUFFER_EXT
, previous_renderbuffer_id
);
568 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, previous_fbo_id
);
570 GpuHostMsg_AcceleratedSurfaceNew_Params params
;
571 params
.width
= size_
.width();
572 params
.height
= size_
.height();
573 params
.surface_id
= next_id_
++;
574 params
.create_transport_dib
= true;
575 helper_
->SendAcceleratedSurfaceNew(params
);
577 helper_
->SetScheduled(false);
583 scoped_refptr
<gfx::GLSurface
> ImageTransportSurface::CreateSurface(
584 GpuChannelManager
* manager
,
585 int32 render_view_id
,
587 int32 command_buffer_id
,
588 gfx::PluginWindowHandle handle
) {
589 scoped_refptr
<gfx::GLSurface
> surface
;
590 IOSurfaceSupport
* io_surface_support
= IOSurfaceSupport::Initialize();
592 switch (gfx::GetGLImplementation()) {
593 case gfx::kGLImplementationDesktopGL
:
594 case gfx::kGLImplementationAppleGL
:
595 if (!io_surface_support
) {
596 surface
= new TransportDIBImageTransportSurface(manager
,
602 surface
= new IOSurfaceImageTransportSurface(manager
,
613 if (surface
->Initialize())
619 #endif // defined(USE_GPU)