1 // Copyright (c) 2012 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 #include "content/common/gpu/image_transport_surface.h"
7 #include "base/mac/scoped_cftyperef.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "content/common/gpu/gpu_command_buffer_stub.h"
10 #include "content/common/gpu/gpu_messages.h"
11 #include "ui/gfx/native_widget_types.h"
12 #include "ui/gl/gl_bindings.h"
13 #include "ui/gl/gl_context.h"
14 #include "ui/gl/gl_implementation.h"
15 #include "ui/gl/gl_surface_cgl.h"
16 #include "ui/gl/gl_surface_osmesa.h"
17 #include "ui/gl/io_surface_support_mac.h"
22 // IOSurface dimensions will be rounded up to a multiple of this value in order
23 // to reduce memory thrashing during resize. This must be a power of 2.
24 const uint32 kIOSurfaceDimensionRoundup
= 64;
26 int RoundUpSurfaceDimension(int number
) {
28 // Cast into unsigned space for portable bitwise ops.
29 uint32 unsigned_number
= static_cast<uint32
>(number
);
30 uint32 roundup_sub_1
= kIOSurfaceDimensionRoundup
- 1;
31 unsigned_number
= (unsigned_number
+ roundup_sub_1
) & ~roundup_sub_1
;
32 return static_cast<int>(unsigned_number
);
35 // We are backed by an offscreen surface for the purposes of creating
36 // a context, but use FBOs to render to texture backed IOSurface
37 class IOSurfaceImageTransportSurface
38 : public gfx::NoOpGLSurfaceCGL
,
39 public ImageTransportSurface
,
40 public GpuCommandBufferStub::DestructionObserver
{
42 IOSurfaceImageTransportSurface(GpuChannelManager
* manager
,
43 GpuCommandBufferStub
* stub
,
44 gfx::PluginWindowHandle handle
);
46 // GLSurface implementation
47 virtual bool Initialize() OVERRIDE
;
48 virtual void Destroy() OVERRIDE
;
49 virtual bool DeferDraws() OVERRIDE
;
50 virtual bool IsOffscreen() OVERRIDE
;
51 virtual bool SwapBuffers() OVERRIDE
;
52 virtual bool PostSubBuffer(int x
, int y
, int width
, int height
) OVERRIDE
;
53 virtual bool SupportsPostSubBuffer() OVERRIDE
;
54 virtual gfx::Size
GetSize() OVERRIDE
;
55 virtual bool OnMakeCurrent(gfx::GLContext
* context
) OVERRIDE
;
56 virtual unsigned int GetBackingFrameBufferObject() OVERRIDE
;
57 virtual bool SetBackbufferAllocation(bool allocated
) OVERRIDE
;
58 virtual void SetFrontbufferAllocation(bool allocated
) OVERRIDE
;
61 // ImageTransportSurface implementation
62 virtual void OnBufferPresented(
63 const AcceleratedSurfaceMsg_BufferPresented_Params
& params
) OVERRIDE
;
64 virtual void OnResize(gfx::Size size
, float scale_factor
) OVERRIDE
;
65 virtual void SetLatencyInfo(
66 const std::vector
<ui::LatencyInfo
>&) OVERRIDE
;
67 virtual void WakeUpGpu() OVERRIDE
;
69 // GpuCommandBufferStub::DestructionObserver implementation.
70 virtual void OnWillDestroyStub() OVERRIDE
;
73 virtual ~IOSurfaceImageTransportSurface() OVERRIDE
;
75 void AdjustBufferAllocation();
76 void UnrefIOSurface();
77 void CreateIOSurface();
79 // Tracks the current buffer allocation state.
80 bool backbuffer_suggested_allocation_
;
81 bool frontbuffer_suggested_allocation_
;
85 GLuint depth_stencil_renderbuffer_id_
;
87 base::ScopedCFTypeRef
<CFTypeRef
> io_surface_
;
89 // The id of |io_surface_| or 0 if that's NULL.
90 uint64 io_surface_handle_
;
92 // Weak pointer to the context that this was last made current to.
93 gfx::GLContext
* context_
;
96 gfx::Size rounded_size_
;
99 // Whether or not we've successfully made the surface current once.
102 // Whether a SwapBuffers is pending.
103 bool is_swap_buffers_pending_
;
105 // Whether we unscheduled command buffer because of pending SwapBuffers.
106 bool did_unschedule_
;
108 std::vector
<ui::LatencyInfo
> latency_info_
;
110 scoped_ptr
<ImageTransportHelper
> helper_
;
112 DISALLOW_COPY_AND_ASSIGN(IOSurfaceImageTransportSurface
);
115 void AddBooleanValue(CFMutableDictionaryRef dictionary
,
116 const CFStringRef key
,
118 CFDictionaryAddValue(dictionary
, key
,
119 (value
? kCFBooleanTrue
: kCFBooleanFalse
));
122 void AddIntegerValue(CFMutableDictionaryRef dictionary
,
123 const CFStringRef key
,
125 base::ScopedCFTypeRef
<CFNumberRef
> number(
126 CFNumberCreate(NULL
, kCFNumberSInt32Type
, &value
));
127 CFDictionaryAddValue(dictionary
, key
, number
.get());
130 IOSurfaceImageTransportSurface::IOSurfaceImageTransportSurface(
131 GpuChannelManager
* manager
,
132 GpuCommandBufferStub
* stub
,
133 gfx::PluginWindowHandle handle
)
134 : gfx::NoOpGLSurfaceCGL(gfx::Size(1, 1)),
135 backbuffer_suggested_allocation_(true),
136 frontbuffer_suggested_allocation_(true),
139 depth_stencil_renderbuffer_id_(0),
140 io_surface_handle_(0),
143 made_current_(false),
144 is_swap_buffers_pending_(false),
145 did_unschedule_(false) {
146 helper_
.reset(new ImageTransportHelper(this, manager
, stub
, handle
));
149 IOSurfaceImageTransportSurface::~IOSurfaceImageTransportSurface() {
152 bool IOSurfaceImageTransportSurface::Initialize() {
153 // Only support IOSurfaces if the GL implementation is the native desktop GL.
154 // IO surfaces will not work with, for example, OSMesa software renderer
156 if (gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL
&&
157 gfx::GetGLImplementation() != gfx::kGLImplementationAppleGL
)
160 if (!helper_
->Initialize())
163 if (!NoOpGLSurfaceCGL::Initialize()) {
168 helper_
->stub()->AddDestructionObserver(this);
172 void IOSurfaceImageTransportSurface::Destroy() {
176 NoOpGLSurfaceCGL::Destroy();
179 bool IOSurfaceImageTransportSurface::DeferDraws() {
180 // The command buffer hit a draw/clear command that could clobber the
181 // IOSurface in use by an earlier SwapBuffers. If a Swap is pending, abort
182 // processing of the command by returning true and unschedule until the Swap
185 return true; // Still unscheduled, so just return true.
186 if (is_swap_buffers_pending_
) {
187 did_unschedule_
= true;
188 helper_
->SetScheduled(false);
194 bool IOSurfaceImageTransportSurface::IsOffscreen() {
198 bool IOSurfaceImageTransportSurface::OnMakeCurrent(gfx::GLContext
* context
) {
204 OnResize(gfx::Size(1, 1), 1.f
);
206 made_current_
= true;
210 unsigned int IOSurfaceImageTransportSurface::GetBackingFrameBufferObject() {
214 bool IOSurfaceImageTransportSurface::SetBackbufferAllocation(bool allocation
) {
215 if (backbuffer_suggested_allocation_
== allocation
)
217 backbuffer_suggested_allocation_
= allocation
;
218 AdjustBufferAllocation();
222 void IOSurfaceImageTransportSurface::SetFrontbufferAllocation(bool allocation
) {
223 if (frontbuffer_suggested_allocation_
== allocation
)
225 frontbuffer_suggested_allocation_
= allocation
;
226 AdjustBufferAllocation();
229 void IOSurfaceImageTransportSurface::AdjustBufferAllocation() {
230 // On mac, the frontbuffer and backbuffer are the same buffer. The buffer is
231 // free'd when both the browser and gpu processes have Unref'd the IOSurface.
232 if (!backbuffer_suggested_allocation_
&&
233 !frontbuffer_suggested_allocation_
&&
237 } else if (backbuffer_suggested_allocation_
&& !io_surface_
) {
242 bool IOSurfaceImageTransportSurface::SwapBuffers() {
243 DCHECK(backbuffer_suggested_allocation_
);
244 if (!frontbuffer_suggested_allocation_
)
248 GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params
;
249 params
.surface_handle
= io_surface_handle_
;
250 params
.size
= GetSize();
251 params
.scale_factor
= scale_factor_
;
252 params
.latency_info
.swap(latency_info_
);
253 helper_
->SendAcceleratedSurfaceBuffersSwapped(params
);
255 DCHECK(!is_swap_buffers_pending_
);
256 is_swap_buffers_pending_
= true;
260 bool IOSurfaceImageTransportSurface::PostSubBuffer(
261 int x
, int y
, int width
, int height
) {
262 DCHECK(backbuffer_suggested_allocation_
);
263 if (!frontbuffer_suggested_allocation_
)
267 GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params params
;
268 params
.surface_handle
= io_surface_handle_
;
271 params
.width
= width
;
272 params
.height
= height
;
273 params
.surface_size
= GetSize();
274 params
.surface_scale_factor
= scale_factor_
;
275 params
.latency_info
.swap(latency_info_
);
276 helper_
->SendAcceleratedSurfacePostSubBuffer(params
);
278 DCHECK(!is_swap_buffers_pending_
);
279 is_swap_buffers_pending_
= true;
283 bool IOSurfaceImageTransportSurface::SupportsPostSubBuffer() {
287 gfx::Size
IOSurfaceImageTransportSurface::GetSize() {
291 void IOSurfaceImageTransportSurface::OnBufferPresented(
292 const AcceleratedSurfaceMsg_BufferPresented_Params
& params
) {
293 DCHECK(is_swap_buffers_pending_
);
295 context_
->share_group()->SetRendererID(params
.renderer_id
);
296 is_swap_buffers_pending_
= false;
297 if (did_unschedule_
) {
298 did_unschedule_
= false;
299 helper_
->SetScheduled(true);
303 void IOSurfaceImageTransportSurface::OnResize(gfx::Size size
,
304 float scale_factor
) {
305 // This trace event is used in gpu_feature_browsertest.cc - the test will need
306 // to be updated if this event is changed or moved.
307 TRACE_EVENT2("gpu", "IOSurfaceImageTransportSurface::OnResize",
308 "old_width", size_
.width(), "new_width", size
.width());
309 // Caching |context_| from OnMakeCurrent. It should still be current.
310 DCHECK(context_
->IsCurrent(this));
313 scale_factor_
= scale_factor
;
318 void IOSurfaceImageTransportSurface::SetLatencyInfo(
319 const std::vector
<ui::LatencyInfo
>& latency_info
) {
320 for (size_t i
= 0; i
< latency_info
.size(); i
++)
321 latency_info_
.push_back(latency_info
[i
]);
324 void IOSurfaceImageTransportSurface::WakeUpGpu() {
328 void IOSurfaceImageTransportSurface::OnWillDestroyStub() {
329 helper_
->stub()->RemoveDestructionObserver(this);
333 void IOSurfaceImageTransportSurface::UnrefIOSurface() {
334 // If we have resources to destroy, then make sure that we have a current
335 // context which we can use to delete the resources.
336 if (context_
|| fbo_id_
|| texture_id_
|| depth_stencil_renderbuffer_id_
) {
337 DCHECK(gfx::GLContext::GetCurrent() == context_
);
338 DCHECK(context_
->IsCurrent(this));
339 DCHECK(CGLGetCurrentContext());
343 glDeleteFramebuffersEXT(1, &fbo_id_
);
348 glDeleteTextures(1, &texture_id_
);
352 if (depth_stencil_renderbuffer_id_
) {
353 glDeleteRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_
);
354 depth_stencil_renderbuffer_id_
= 0;
358 io_surface_handle_
= 0;
361 void IOSurfaceImageTransportSurface::CreateIOSurface() {
362 gfx::Size
new_rounded_size(RoundUpSurfaceDimension(size_
.width()),
363 RoundUpSurfaceDimension(size_
.height()));
365 // Only recreate surface when the rounded up size has changed.
366 if (io_surface_
.get() && new_rounded_size
== rounded_size_
)
369 // This trace event is used in gpu_feature_browsertest.cc - the test will need
370 // to be updated if this event is changed or moved.
371 TRACE_EVENT2("gpu", "IOSurfaceImageTransportSurface::CreateIOSurface",
372 "width", new_rounded_size
.width(),
373 "height", new_rounded_size
.height());
375 rounded_size_
= new_rounded_size
;
377 GLint previous_texture_id
= 0;
378 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB
, &previous_texture_id
);
380 // Free the old IO Surface first to reduce memory fragmentation.
383 glGenFramebuffersEXT(1, &fbo_id_
);
384 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, fbo_id_
);
386 IOSurfaceSupport
* io_surface_support
= IOSurfaceSupport::Initialize();
388 glGenTextures(1, &texture_id_
);
390 // GL_TEXTURE_RECTANGLE_ARB is the best supported render target on
391 // Mac OS X and is required for IOSurface interoperability.
392 GLenum target
= GL_TEXTURE_RECTANGLE_ARB
;
393 glBindTexture(target
, texture_id_
);
394 glTexParameteri(target
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
395 glTexParameteri(target
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
396 glTexParameteri(target
, GL_TEXTURE_WRAP_S
, GL_CLAMP_TO_EDGE
);
397 glTexParameteri(target
, GL_TEXTURE_WRAP_T
, GL_CLAMP_TO_EDGE
);
399 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT
,
400 GL_COLOR_ATTACHMENT0_EXT
,
406 // Search through the provided attributes; if the caller has
407 // requested a stencil buffer, try to get one.
410 helper_
->stub()->GetRequestedAttribute(EGL_STENCIL_SIZE
);
411 if (stencil_bits
> 0) {
412 // Create and bind the stencil buffer
413 bool has_packed_depth_stencil
=
414 GLSurface::ExtensionsContain(
415 reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS
)),
416 "GL_EXT_packed_depth_stencil");
418 if (has_packed_depth_stencil
) {
419 glGenRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_
);
420 glBindRenderbufferEXT(GL_RENDERBUFFER_EXT
,
421 depth_stencil_renderbuffer_id_
);
422 glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT
, GL_DEPTH24_STENCIL8_EXT
,
423 rounded_size_
.width(), rounded_size_
.height());
424 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT
,
425 GL_STENCIL_ATTACHMENT_EXT
,
427 depth_stencil_renderbuffer_id_
);
430 // If we asked for stencil but the extension isn't present,
431 // it's OK to silently fail; subsequent code will/must check
432 // for the presence of a stencil buffer before attempting to
433 // do stencil-based operations.
436 // Allocate a new IOSurface, which is the GPU resource that can be
437 // shared across processes.
438 base::ScopedCFTypeRef
<CFMutableDictionaryRef
> properties
;
439 properties
.reset(CFDictionaryCreateMutable(kCFAllocatorDefault
,
441 &kCFTypeDictionaryKeyCallBacks
,
442 &kCFTypeDictionaryValueCallBacks
));
443 AddIntegerValue(properties
,
444 io_surface_support
->GetKIOSurfaceWidth(),
445 rounded_size_
.width());
446 AddIntegerValue(properties
,
447 io_surface_support
->GetKIOSurfaceHeight(),
448 rounded_size_
.height());
449 AddIntegerValue(properties
,
450 io_surface_support
->GetKIOSurfaceBytesPerElement(), 4);
451 AddBooleanValue(properties
,
452 io_surface_support
->GetKIOSurfaceIsGlobal(), true);
453 // I believe we should be able to unreference the IOSurfaces without
454 // synchronizing with the browser process because they are
455 // ultimately reference counted by the operating system.
456 io_surface_
.reset(io_surface_support
->IOSurfaceCreate(properties
));
457 io_surface_handle_
= io_surface_support
->IOSurfaceGetID(io_surface_
);
459 // Don't think we need to identify a plane.
462 io_surface_support
->CGLTexImageIOSurface2D(
463 static_cast<CGLContextObj
>(context_
->GetHandle()),
466 rounded_size_
.width(),
467 rounded_size_
.height(),
469 GL_UNSIGNED_INT_8_8_8_8_REV
,
472 if (cglerror
!= kCGLNoError
) {
479 GLenum status
= glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT
);
480 if (status
!= GL_FRAMEBUFFER_COMPLETE_EXT
) {
481 DLOG(ERROR
) << "Framebuffer was incomplete: " << status
;
486 glBindTexture(target
, previous_texture_id
);
487 // The FBO remains bound for this GL context.
490 // A subclass of GLSurfaceOSMesa that doesn't print an error message when
491 // SwapBuffers() is called.
492 class DRTSurfaceOSMesa
: public gfx::GLSurfaceOSMesa
{
494 // Size doesn't matter, the surface is resized to the right size later.
495 DRTSurfaceOSMesa() : GLSurfaceOSMesa(GL_RGBA
, gfx::Size(1, 1)) {}
497 // Implement a subset of GLSurface.
498 virtual bool SwapBuffers() OVERRIDE
;
501 virtual ~DRTSurfaceOSMesa() {}
502 DISALLOW_COPY_AND_ASSIGN(DRTSurfaceOSMesa
);
505 bool DRTSurfaceOSMesa::SwapBuffers() {
509 bool g_allow_os_mesa
= false;
514 scoped_refptr
<gfx::GLSurface
> ImageTransportSurface::CreateNativeSurface(
515 GpuChannelManager
* manager
,
516 GpuCommandBufferStub
* stub
,
517 const gfx::GLSurfaceHandle
& surface_handle
) {
518 DCHECK(surface_handle
.transport_type
== gfx::NATIVE_TRANSPORT
);
519 IOSurfaceSupport
* io_surface_support
= IOSurfaceSupport::Initialize();
521 switch (gfx::GetGLImplementation()) {
522 case gfx::kGLImplementationDesktopGL
:
523 case gfx::kGLImplementationAppleGL
:
524 if (!io_surface_support
) {
525 DLOG(WARNING
) << "No IOSurface support";
526 return scoped_refptr
<gfx::GLSurface
>();
528 return scoped_refptr
<gfx::GLSurface
>(new IOSurfaceImageTransportSurface(
529 manager
, stub
, surface_handle
.handle
));
532 // Content shell in DRT mode spins up a gpu process which needs an
533 // image transport surface, but that surface isn't used to read pixel
534 // baselines. So this is mostly a dummy surface.
535 if (!g_allow_os_mesa
) {
537 return scoped_refptr
<gfx::GLSurface
>();
539 scoped_refptr
<gfx::GLSurface
> surface(new DRTSurfaceOSMesa());
540 if (!surface
.get() || !surface
->Initialize())
542 return scoped_refptr
<gfx::GLSurface
>(new PassThroughImageTransportSurface(
543 manager
, stub
, surface
.get()));
548 void ImageTransportSurface::SetAllowOSMesaForTesting(bool allow
) {
549 g_allow_os_mesa
= allow
;
552 } // namespace content