Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / content / common / gpu / image_transport_surface_mac.cc
blobdbd2182ee120332f6f1750c59e8e6ab17bade4ef
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"
19 namespace content {
20 namespace {
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) {
27 DCHECK(number >= 0);
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 {
41 public:
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;
60 protected:
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;
72 private:
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_;
83 uint32 fbo_id_;
84 GLuint texture_id_;
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_;
95 gfx::Size size_;
96 gfx::Size rounded_size_;
97 float scale_factor_;
99 // Whether or not we've successfully made the surface current once.
100 bool made_current_;
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,
117 bool value) {
118 CFDictionaryAddValue(dictionary, key,
119 (value ? kCFBooleanTrue : kCFBooleanFalse));
122 void AddIntegerValue(CFMutableDictionaryRef dictionary,
123 const CFStringRef key,
124 int32 value) {
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),
137 fbo_id_(0),
138 texture_id_(0),
139 depth_stencil_renderbuffer_id_(0),
140 io_surface_handle_(0),
141 context_(NULL),
142 scale_factor_(1.f),
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
155 // GL contexts.
156 if (gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL &&
157 gfx::GetGLImplementation() != gfx::kGLImplementationAppleGL)
158 return false;
160 if (!helper_->Initialize())
161 return false;
163 if (!NoOpGLSurfaceCGL::Initialize()) {
164 helper_->Destroy();
165 return false;
168 helper_->stub()->AddDestructionObserver(this);
169 return true;
172 void IOSurfaceImageTransportSurface::Destroy() {
173 UnrefIOSurface();
175 helper_->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
183 // Ack arrives.
184 if(did_unschedule_)
185 return true; // Still unscheduled, so just return true.
186 if (is_swap_buffers_pending_) {
187 did_unschedule_ = true;
188 helper_->SetScheduled(false);
189 return true;
191 return false;
194 bool IOSurfaceImageTransportSurface::IsOffscreen() {
195 return false;
198 bool IOSurfaceImageTransportSurface::OnMakeCurrent(gfx::GLContext* context) {
199 context_ = context;
201 if (made_current_)
202 return true;
204 OnResize(gfx::Size(1, 1), 1.f);
206 made_current_ = true;
207 return true;
210 unsigned int IOSurfaceImageTransportSurface::GetBackingFrameBufferObject() {
211 return fbo_id_;
214 bool IOSurfaceImageTransportSurface::SetBackbufferAllocation(bool allocation) {
215 if (backbuffer_suggested_allocation_ == allocation)
216 return true;
217 backbuffer_suggested_allocation_ = allocation;
218 AdjustBufferAllocation();
219 return true;
222 void IOSurfaceImageTransportSurface::SetFrontbufferAllocation(bool allocation) {
223 if (frontbuffer_suggested_allocation_ == allocation)
224 return;
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_ &&
234 io_surface_.get()) {
235 UnrefIOSurface();
236 helper_->Suspend();
237 } else if (backbuffer_suggested_allocation_ && !io_surface_) {
238 CreateIOSurface();
242 bool IOSurfaceImageTransportSurface::SwapBuffers() {
243 DCHECK(backbuffer_suggested_allocation_);
244 if (!frontbuffer_suggested_allocation_)
245 return true;
246 glFlush();
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;
257 return true;
260 bool IOSurfaceImageTransportSurface::PostSubBuffer(
261 int x, int y, int width, int height) {
262 DCHECK(backbuffer_suggested_allocation_);
263 if (!frontbuffer_suggested_allocation_)
264 return true;
265 glFlush();
267 GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params params;
268 params.surface_handle = io_surface_handle_;
269 params.x = x;
270 params.y = y;
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;
280 return true;
283 bool IOSurfaceImageTransportSurface::SupportsPostSubBuffer() {
284 return true;
287 gfx::Size IOSurfaceImageTransportSurface::GetSize() {
288 return size_;
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));
312 size_ = size;
313 scale_factor_ = scale_factor;
315 CreateIOSurface();
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() {
325 NOTIMPLEMENTED();
328 void IOSurfaceImageTransportSurface::OnWillDestroyStub() {
329 helper_->stub()->RemoveDestructionObserver(this);
330 Destroy();
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());
342 if (fbo_id_) {
343 glDeleteFramebuffersEXT(1, &fbo_id_);
344 fbo_id_ = 0;
347 if (texture_id_) {
348 glDeleteTextures(1, &texture_id_);
349 texture_id_ = 0;
352 if (depth_stencil_renderbuffer_id_) {
353 glDeleteRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_);
354 depth_stencil_renderbuffer_id_ = 0;
357 io_surface_.reset();
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_)
367 return;
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.
381 UnrefIOSurface();
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,
401 target,
402 texture_id_,
406 // Search through the provided attributes; if the caller has
407 // requested a stencil buffer, try to get one.
409 int32 stencil_bits =
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,
426 GL_RENDERBUFFER_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.
460 GLuint plane = 0;
461 CGLError cglerror =
462 io_surface_support->CGLTexImageIOSurface2D(
463 static_cast<CGLContextObj>(context_->GetHandle()),
464 target,
465 GL_RGBA,
466 rounded_size_.width(),
467 rounded_size_.height(),
468 GL_BGRA,
469 GL_UNSIGNED_INT_8_8_8_8_REV,
470 io_surface_.get(),
471 plane);
472 if (cglerror != kCGLNoError) {
473 UnrefIOSurface();
474 return;
477 glFlush();
479 GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
480 if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
481 DLOG(ERROR) << "Framebuffer was incomplete: " << status;
482 UnrefIOSurface();
483 return;
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 {
493 public:
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;
500 private:
501 virtual ~DRTSurfaceOSMesa() {}
502 DISALLOW_COPY_AND_ASSIGN(DRTSurfaceOSMesa);
505 bool DRTSurfaceOSMesa::SwapBuffers() {
506 return true;
509 bool g_allow_os_mesa = false;
511 } // namespace
513 // static
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));
531 default:
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) {
536 NOTREACHED();
537 return scoped_refptr<gfx::GLSurface>();
539 scoped_refptr<gfx::GLSurface> surface(new DRTSurfaceOSMesa());
540 if (!surface.get() || !surface->Initialize())
541 return surface;
542 return scoped_refptr<gfx::GLSurface>(new PassThroughImageTransportSurface(
543 manager, stub, surface.get()));
547 // static
548 void ImageTransportSurface::SetAllowOSMesaForTesting(bool allow) {
549 g_allow_os_mesa = allow;
552 } // namespace content