Respond with QuotaExceededError when IndexedDB has no disk space on open.
[chromium-blink-merge.git] / content / common / gpu / image_transport_surface_mac.cc
blob6e6c5804fd50e72567e240b5ab1a82183cf70d76
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 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;
60 protected:
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;
71 private:
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_;
82 uint32 fbo_id_;
83 GLuint texture_id_;
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_;
93 gfx::Size size_;
94 gfx::Size rounded_size_;
95 float scale_factor_;
97 // Whether or not we've successfully made the surface current once.
98 bool made_current_;
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,
115 bool value) {
116 CFDictionaryAddValue(dictionary, key,
117 (value ? kCFBooleanTrue : kCFBooleanFalse));
120 void AddIntegerValue(CFMutableDictionaryRef dictionary,
121 const CFStringRef key,
122 int32 value) {
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),
135 fbo_id_(0),
136 texture_id_(0),
137 io_surface_handle_(0),
138 context_(NULL),
139 scale_factor_(1.f),
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
152 // GL contexts.
153 if (gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL &&
154 gfx::GetGLImplementation() != gfx::kGLImplementationAppleGL)
155 return false;
157 if (!helper_->Initialize())
158 return false;
160 if (!NoOpGLSurfaceCGL::Initialize()) {
161 helper_->Destroy();
162 return false;
165 helper_->stub()->AddDestructionObserver(this);
166 return true;
169 void IOSurfaceImageTransportSurface::Destroy() {
170 UnrefIOSurface();
172 helper_->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
180 // Ack arrives.
181 if(did_unschedule_)
182 return true; // Still unscheduled, so just return true.
183 if (is_swap_buffers_pending_) {
184 did_unschedule_ = true;
185 helper_->SetScheduled(false);
186 return true;
188 return false;
191 bool IOSurfaceImageTransportSurface::IsOffscreen() {
192 return false;
195 bool IOSurfaceImageTransportSurface::OnMakeCurrent(gfx::GLContext* context) {
196 context_ = context;
198 if (made_current_)
199 return true;
201 OnResize(gfx::Size(1, 1), 1.f);
203 made_current_ = true;
204 return true;
207 unsigned int IOSurfaceImageTransportSurface::GetBackingFrameBufferObject() {
208 return fbo_id_;
211 bool IOSurfaceImageTransportSurface::SetBackbufferAllocation(bool allocation) {
212 if (backbuffer_suggested_allocation_ == allocation)
213 return true;
214 backbuffer_suggested_allocation_ = allocation;
215 AdjustBufferAllocation();
216 return true;
219 void IOSurfaceImageTransportSurface::SetFrontbufferAllocation(bool allocation) {
220 if (frontbuffer_suggested_allocation_ == allocation)
221 return;
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_ &&
231 io_surface_.get()) {
232 UnrefIOSurface();
233 helper_->Suspend();
234 } else if (backbuffer_suggested_allocation_ && !io_surface_) {
235 CreateIOSurface();
239 bool IOSurfaceImageTransportSurface::SwapBuffers() {
240 DCHECK(backbuffer_suggested_allocation_);
241 if (!frontbuffer_suggested_allocation_)
242 return true;
243 glFlush();
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;
254 return true;
257 bool IOSurfaceImageTransportSurface::PostSubBuffer(
258 int x, int y, int width, int height) {
259 DCHECK(backbuffer_suggested_allocation_);
260 if (!frontbuffer_suggested_allocation_)
261 return true;
262 glFlush();
264 GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params params;
265 params.surface_handle = io_surface_handle_;
266 params.x = x;
267 params.y = y;
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;
277 return 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";
285 return extensions;
288 gfx::Size IOSurfaceImageTransportSurface::GetSize() {
289 return size_;
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() {
305 NOTREACHED();
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));
317 size_ = size;
318 scale_factor_ = scale_factor;
320 CreateIOSurface();
323 void IOSurfaceImageTransportSurface::SetLatencyInfo(
324 const ui::LatencyInfo& latency_info) {
325 latency_info_ = latency_info;
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_) {
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 io_surface_.reset();
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_)
362 return;
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.
376 UnrefIOSurface();
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,
396 target,
397 texture_id_,
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.
424 GLuint plane = 0;
425 CGLError cglerror =
426 io_surface_support->CGLTexImageIOSurface2D(
427 static_cast<CGLContextObj>(context_->GetHandle()),
428 target,
429 GL_RGBA,
430 rounded_size_.width(),
431 rounded_size_.height(),
432 GL_BGRA,
433 GL_UNSIGNED_INT_8_8_8_8_REV,
434 io_surface_.get(),
435 plane);
436 if (cglerror != kCGLNoError) {
437 DLOG(ERROR) << "CGLTexImageIOSurface2D: " << cglerror;
438 UnrefIOSurface();
439 return;
442 glFlush();
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 {
451 public:
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;
458 private:
459 virtual ~DRTSurfaceOSMesa() {}
460 DISALLOW_COPY_AND_ASSIGN(DRTSurfaceOSMesa);
463 bool DRTSurfaceOSMesa::SwapBuffers() {
464 return true;
467 bool g_allow_os_mesa = false;
469 } // namespace
471 // static
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));
489 default:
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) {
494 NOTREACHED();
495 return scoped_refptr<gfx::GLSurface>();
497 scoped_refptr<gfx::GLSurface> surface(new DRTSurfaceOSMesa());
498 if (!surface.get() || !surface->Initialize())
499 return surface;
500 return scoped_refptr<gfx::GLSurface>(new PassThroughImageTransportSurface(
501 manager, stub, surface.get(), false));
505 // static
506 void ImageTransportSurface::SetAllowOSMesaForTesting(bool allow) {
507 g_allow_os_mesa = allow;
510 } // namespace content