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 std::string
GetExtensions() 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 OnResizeViewACK() OVERRIDE
;
65 virtual void OnResize(gfx::Size size
, float scale_factor
) OVERRIDE
;
66 virtual void SetLatencyInfo(const ui::LatencyInfo
&) OVERRIDE
;
68 // GpuCommandBufferStub::DestructionObserver implementation.
69 virtual void OnWillDestroyStub() OVERRIDE
;
72 virtual ~IOSurfaceImageTransportSurface() OVERRIDE
;
74 void AdjustBufferAllocation();
75 void UnrefIOSurface();
76 void CreateIOSurface();
78 // Tracks the current buffer allocation state.
79 bool backbuffer_suggested_allocation_
;
80 bool frontbuffer_suggested_allocation_
;
85 base::ScopedCFTypeRef
<CFTypeRef
> io_surface_
;
87 // The id of |io_surface_| or 0 if that's NULL.
88 uint64 io_surface_handle_
;
90 // Weak pointer to the context that this was last made current to.
91 gfx::GLContext
* context_
;
94 gfx::Size rounded_size_
;
97 // Whether or not we've successfully made the surface current once.
100 // Whether a SwapBuffers is pending.
101 bool is_swap_buffers_pending_
;
103 // Whether we unscheduled command buffer because of pending SwapBuffers.
104 bool did_unschedule_
;
106 ui::LatencyInfo latency_info_
;
108 scoped_ptr
<ImageTransportHelper
> helper_
;
110 DISALLOW_COPY_AND_ASSIGN(IOSurfaceImageTransportSurface
);
113 void AddBooleanValue(CFMutableDictionaryRef dictionary
,
114 const CFStringRef key
,
116 CFDictionaryAddValue(dictionary
, key
,
117 (value
? kCFBooleanTrue
: kCFBooleanFalse
));
120 void AddIntegerValue(CFMutableDictionaryRef dictionary
,
121 const CFStringRef key
,
123 base::ScopedCFTypeRef
<CFNumberRef
> number(
124 CFNumberCreate(NULL
, kCFNumberSInt32Type
, &value
));
125 CFDictionaryAddValue(dictionary
, key
, number
.get());
128 IOSurfaceImageTransportSurface::IOSurfaceImageTransportSurface(
129 GpuChannelManager
* manager
,
130 GpuCommandBufferStub
* stub
,
131 gfx::PluginWindowHandle handle
)
132 : gfx::NoOpGLSurfaceCGL(gfx::Size(1, 1)),
133 backbuffer_suggested_allocation_(true),
134 frontbuffer_suggested_allocation_(true),
137 io_surface_handle_(0),
140 made_current_(false),
141 is_swap_buffers_pending_(false),
142 did_unschedule_(false) {
143 helper_
.reset(new ImageTransportHelper(this, manager
, stub
, handle
));
146 IOSurfaceImageTransportSurface::~IOSurfaceImageTransportSurface() {
149 bool IOSurfaceImageTransportSurface::Initialize() {
150 // Only support IOSurfaces if the GL implementation is the native desktop GL.
151 // IO surfaces will not work with, for example, OSMesa software renderer
153 if (gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL
&&
154 gfx::GetGLImplementation() != gfx::kGLImplementationAppleGL
)
157 if (!helper_
->Initialize())
160 if (!NoOpGLSurfaceCGL::Initialize()) {
165 helper_
->stub()->AddDestructionObserver(this);
169 void IOSurfaceImageTransportSurface::Destroy() {
173 NoOpGLSurfaceCGL::Destroy();
176 bool IOSurfaceImageTransportSurface::DeferDraws() {
177 // The command buffer hit a draw/clear command that could clobber the
178 // IOSurface in use by an earlier SwapBuffers. If a Swap is pending, abort
179 // processing of the command by returning true and unschedule until the Swap
182 return true; // Still unscheduled, so just return true.
183 if (is_swap_buffers_pending_
) {
184 did_unschedule_
= true;
185 helper_
->SetScheduled(false);
191 bool IOSurfaceImageTransportSurface::IsOffscreen() {
195 bool IOSurfaceImageTransportSurface::OnMakeCurrent(gfx::GLContext
* context
) {
201 OnResize(gfx::Size(1, 1), 1.f
);
203 made_current_
= true;
207 unsigned int IOSurfaceImageTransportSurface::GetBackingFrameBufferObject() {
211 bool IOSurfaceImageTransportSurface::SetBackbufferAllocation(bool allocation
) {
212 if (backbuffer_suggested_allocation_
== allocation
)
214 backbuffer_suggested_allocation_
= allocation
;
215 AdjustBufferAllocation();
219 void IOSurfaceImageTransportSurface::SetFrontbufferAllocation(bool allocation
) {
220 if (frontbuffer_suggested_allocation_
== allocation
)
222 frontbuffer_suggested_allocation_
= allocation
;
223 AdjustBufferAllocation();
226 void IOSurfaceImageTransportSurface::AdjustBufferAllocation() {
227 // On mac, the frontbuffer and backbuffer are the same buffer. The buffer is
228 // free'd when both the browser and gpu processes have Unref'd the IOSurface.
229 if (!backbuffer_suggested_allocation_
&&
230 !frontbuffer_suggested_allocation_
&&
234 } else if (backbuffer_suggested_allocation_
&& !io_surface_
) {
239 bool IOSurfaceImageTransportSurface::SwapBuffers() {
240 DCHECK(backbuffer_suggested_allocation_
);
241 if (!frontbuffer_suggested_allocation_
)
245 GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params
;
246 params
.surface_handle
= io_surface_handle_
;
247 params
.size
= GetSize();
248 params
.scale_factor
= scale_factor_
;
249 params
.latency_info
= latency_info_
;
250 helper_
->SendAcceleratedSurfaceBuffersSwapped(params
);
252 DCHECK(!is_swap_buffers_pending_
);
253 is_swap_buffers_pending_
= true;
257 bool IOSurfaceImageTransportSurface::PostSubBuffer(
258 int x
, int y
, int width
, int height
) {
259 DCHECK(backbuffer_suggested_allocation_
);
260 if (!frontbuffer_suggested_allocation_
)
264 GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params params
;
265 params
.surface_handle
= io_surface_handle_
;
268 params
.width
= width
;
269 params
.height
= height
;
270 params
.surface_size
= GetSize();
271 params
.surface_scale_factor
= scale_factor_
;
272 params
.latency_info
= latency_info_
;
273 helper_
->SendAcceleratedSurfacePostSubBuffer(params
);
275 DCHECK(!is_swap_buffers_pending_
);
276 is_swap_buffers_pending_
= true;
280 std::string
IOSurfaceImageTransportSurface::GetExtensions() {
281 std::string extensions
= gfx::GLSurface::GetExtensions();
282 extensions
+= extensions
.empty() ? "" : " ";
283 extensions
+= "GL_CHROMIUM_front_buffer_cached ";
284 extensions
+= "GL_CHROMIUM_post_sub_buffer";
288 gfx::Size
IOSurfaceImageTransportSurface::GetSize() {
292 void IOSurfaceImageTransportSurface::OnBufferPresented(
293 const AcceleratedSurfaceMsg_BufferPresented_Params
& params
) {
294 DCHECK(is_swap_buffers_pending_
);
296 context_
->share_group()->SetRendererID(params
.renderer_id
);
297 is_swap_buffers_pending_
= false;
298 if (did_unschedule_
) {
299 did_unschedule_
= false;
300 helper_
->SetScheduled(true);
304 void IOSurfaceImageTransportSurface::OnResizeViewACK() {
308 void IOSurfaceImageTransportSurface::OnResize(gfx::Size size
,
309 float scale_factor
) {
310 // This trace event is used in gpu_feature_browsertest.cc - the test will need
311 // to be updated if this event is changed or moved.
312 TRACE_EVENT2("gpu", "IOSurfaceImageTransportSurface::OnResize",
313 "old_width", size_
.width(), "new_width", size
.width());
314 // Caching |context_| from OnMakeCurrent. It should still be current.
315 DCHECK(context_
->IsCurrent(this));
318 scale_factor_
= scale_factor
;
323 void IOSurfaceImageTransportSurface::SetLatencyInfo(
324 const ui::LatencyInfo
& latency_info
) {
325 latency_info_
= latency_info
;
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_
) {
337 DCHECK(gfx::GLContext::GetCurrent() == context_
);
338 DCHECK(context_
->IsCurrent(this));
339 DCHECK(CGLGetCurrentContext());
343 glDeleteFramebuffersEXT(1, &fbo_id_
);
348 glDeleteTextures(1, &texture_id_
);
353 io_surface_handle_
= 0;
356 void IOSurfaceImageTransportSurface::CreateIOSurface() {
357 gfx::Size
new_rounded_size(RoundUpSurfaceDimension(size_
.width()),
358 RoundUpSurfaceDimension(size_
.height()));
360 // Only recreate surface when the rounded up size has changed.
361 if (io_surface_
.get() && new_rounded_size
== rounded_size_
)
364 // This trace event is used in gpu_feature_browsertest.cc - the test will need
365 // to be updated if this event is changed or moved.
366 TRACE_EVENT2("gpu", "IOSurfaceImageTransportSurface::CreateIOSurface",
367 "width", new_rounded_size
.width(),
368 "height", new_rounded_size
.height());
370 rounded_size_
= new_rounded_size
;
372 GLint previous_texture_id
= 0;
373 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB
, &previous_texture_id
);
375 // Free the old IO Surface first to reduce memory fragmentation.
378 glGenFramebuffersEXT(1, &fbo_id_
);
379 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, fbo_id_
);
381 IOSurfaceSupport
* io_surface_support
= IOSurfaceSupport::Initialize();
383 glGenTextures(1, &texture_id_
);
385 // GL_TEXTURE_RECTANGLE_ARB is the best supported render target on
386 // Mac OS X and is required for IOSurface interoperability.
387 GLenum target
= GL_TEXTURE_RECTANGLE_ARB
;
388 glBindTexture(target
, texture_id_
);
389 glTexParameteri(target
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
390 glTexParameteri(target
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
391 glTexParameteri(target
, GL_TEXTURE_WRAP_S
, GL_CLAMP_TO_EDGE
);
392 glTexParameteri(target
, GL_TEXTURE_WRAP_T
, GL_CLAMP_TO_EDGE
);
394 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT
,
395 GL_COLOR_ATTACHMENT0_EXT
,
400 // Allocate a new IOSurface, which is the GPU resource that can be
401 // shared across processes.
402 base::ScopedCFTypeRef
<CFMutableDictionaryRef
> properties
;
403 properties
.reset(CFDictionaryCreateMutable(kCFAllocatorDefault
,
405 &kCFTypeDictionaryKeyCallBacks
,
406 &kCFTypeDictionaryValueCallBacks
));
407 AddIntegerValue(properties
,
408 io_surface_support
->GetKIOSurfaceWidth(),
409 rounded_size_
.width());
410 AddIntegerValue(properties
,
411 io_surface_support
->GetKIOSurfaceHeight(),
412 rounded_size_
.height());
413 AddIntegerValue(properties
,
414 io_surface_support
->GetKIOSurfaceBytesPerElement(), 4);
415 AddBooleanValue(properties
,
416 io_surface_support
->GetKIOSurfaceIsGlobal(), true);
417 // I believe we should be able to unreference the IOSurfaces without
418 // synchronizing with the browser process because they are
419 // ultimately reference counted by the operating system.
420 io_surface_
.reset(io_surface_support
->IOSurfaceCreate(properties
));
421 io_surface_handle_
= io_surface_support
->IOSurfaceGetID(io_surface_
);
423 // Don't think we need to identify a plane.
426 io_surface_support
->CGLTexImageIOSurface2D(
427 static_cast<CGLContextObj
>(context_
->GetHandle()),
430 rounded_size_
.width(),
431 rounded_size_
.height(),
433 GL_UNSIGNED_INT_8_8_8_8_REV
,
436 if (cglerror
!= kCGLNoError
) {
437 DLOG(ERROR
) << "CGLTexImageIOSurface2D: " << cglerror
;
444 glBindTexture(target
, previous_texture_id
);
445 // The FBO remains bound for this GL context.
448 // A subclass of GLSurfaceOSMesa that doesn't print an error message when
449 // SwapBuffers() is called.
450 class DRTSurfaceOSMesa
: public gfx::GLSurfaceOSMesa
{
452 // Size doesn't matter, the surface is resized to the right size later.
453 DRTSurfaceOSMesa() : GLSurfaceOSMesa(GL_RGBA
, gfx::Size(1, 1)) {}
455 // Implement a subset of GLSurface.
456 virtual bool SwapBuffers() OVERRIDE
;
459 virtual ~DRTSurfaceOSMesa() {}
460 DISALLOW_COPY_AND_ASSIGN(DRTSurfaceOSMesa
);
463 bool DRTSurfaceOSMesa::SwapBuffers() {
467 bool g_allow_os_mesa
= false;
472 scoped_refptr
<gfx::GLSurface
> ImageTransportSurface::CreateNativeSurface(
473 GpuChannelManager
* manager
,
474 GpuCommandBufferStub
* stub
,
475 const gfx::GLSurfaceHandle
& surface_handle
) {
476 DCHECK(surface_handle
.transport_type
== gfx::NATIVE_TRANSPORT
);
477 IOSurfaceSupport
* io_surface_support
= IOSurfaceSupport::Initialize();
479 switch (gfx::GetGLImplementation()) {
480 case gfx::kGLImplementationDesktopGL
:
481 case gfx::kGLImplementationAppleGL
:
482 if (!io_surface_support
) {
483 DLOG(WARNING
) << "No IOSurface support";
484 return scoped_refptr
<gfx::GLSurface
>();
486 return scoped_refptr
<gfx::GLSurface
>(new IOSurfaceImageTransportSurface(
487 manager
, stub
, surface_handle
.handle
));
490 // Content shell in DRT mode spins up a gpu process which needs an
491 // image transport surface, but that surface isn't used to read pixel
492 // baselines. So this is mostly a dummy surface.
493 if (!g_allow_os_mesa
) {
495 return scoped_refptr
<gfx::GLSurface
>();
497 scoped_refptr
<gfx::GLSurface
> surface(new DRTSurfaceOSMesa());
498 if (!surface
.get() || !surface
->Initialize())
500 return scoped_refptr
<gfx::GLSurface
>(new PassThroughImageTransportSurface(
501 manager
, stub
, surface
.get(), false));
506 void ImageTransportSurface::SetAllowOSMesaForTesting(bool allow
) {
507 g_allow_os_mesa
= allow
;
510 } // namespace content