- add sources.
[platform/framework/web/crosswalk.git] / src / content / common / gpu / image_transport_surface_mac.cc
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.
4
5 #include "content/common/gpu/image_transport_surface.h"
6
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"
18
19 namespace content {
20 namespace {
21
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;
25
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);
33 }
34
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);
45
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;
59
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;
67
68   // GpuCommandBufferStub::DestructionObserver implementation.
69   virtual void OnWillDestroyStub() OVERRIDE;
70
71  private:
72   virtual ~IOSurfaceImageTransportSurface() OVERRIDE;
73
74   void AdjustBufferAllocation();
75   void UnrefIOSurface();
76   void CreateIOSurface();
77
78   // Tracks the current buffer allocation state.
79   bool backbuffer_suggested_allocation_;
80   bool frontbuffer_suggested_allocation_;
81
82   uint32 fbo_id_;
83   GLuint texture_id_;
84
85   base::ScopedCFTypeRef<CFTypeRef> io_surface_;
86
87   // The id of |io_surface_| or 0 if that's NULL.
88   uint64 io_surface_handle_;
89
90   // Weak pointer to the context that this was last made current to.
91   gfx::GLContext* context_;
92
93   gfx::Size size_;
94   gfx::Size rounded_size_;
95   float scale_factor_;
96
97   // Whether or not we've successfully made the surface current once.
98   bool made_current_;
99
100   // Whether a SwapBuffers is pending.
101   bool is_swap_buffers_pending_;
102
103   // Whether we unscheduled command buffer because of pending SwapBuffers.
104   bool did_unschedule_;
105
106   ui::LatencyInfo latency_info_;
107
108   scoped_ptr<ImageTransportHelper> helper_;
109
110   DISALLOW_COPY_AND_ASSIGN(IOSurfaceImageTransportSurface);
111 };
112
113 void AddBooleanValue(CFMutableDictionaryRef dictionary,
114                      const CFStringRef key,
115                      bool value) {
116   CFDictionaryAddValue(dictionary, key,
117                        (value ? kCFBooleanTrue : kCFBooleanFalse));
118 }
119
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());
126 }
127
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));
144 }
145
146 IOSurfaceImageTransportSurface::~IOSurfaceImageTransportSurface() {
147 }
148
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;
156
157   if (!helper_->Initialize())
158     return false;
159
160   if (!NoOpGLSurfaceCGL::Initialize()) {
161     helper_->Destroy();
162     return false;
163   }
164
165   helper_->stub()->AddDestructionObserver(this);
166   return true;
167 }
168
169 void IOSurfaceImageTransportSurface::Destroy() {
170   UnrefIOSurface();
171
172   helper_->Destroy();
173   NoOpGLSurfaceCGL::Destroy();
174 }
175
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;
187   }
188   return false;
189 }
190
191 bool IOSurfaceImageTransportSurface::IsOffscreen() {
192   return false;
193 }
194
195 bool IOSurfaceImageTransportSurface::OnMakeCurrent(gfx::GLContext* context) {
196   context_ = context;
197
198   if (made_current_)
199     return true;
200
201   OnResize(gfx::Size(1, 1), 1.f);
202
203   made_current_ = true;
204   return true;
205 }
206
207 unsigned int IOSurfaceImageTransportSurface::GetBackingFrameBufferObject() {
208   return fbo_id_;
209 }
210
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;
217 }
218
219 void IOSurfaceImageTransportSurface::SetFrontbufferAllocation(bool allocation) {
220   if (frontbuffer_suggested_allocation_ == allocation)
221     return;
222   frontbuffer_suggested_allocation_ = allocation;
223   AdjustBufferAllocation();
224 }
225
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();
236   }
237 }
238
239 bool IOSurfaceImageTransportSurface::SwapBuffers() {
240   DCHECK(backbuffer_suggested_allocation_);
241   if (!frontbuffer_suggested_allocation_)
242     return true;
243   glFlush();
244
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);
251
252   DCHECK(!is_swap_buffers_pending_);
253   is_swap_buffers_pending_ = true;
254   return true;
255 }
256
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();
263
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);
274
275   DCHECK(!is_swap_buffers_pending_);
276   is_swap_buffers_pending_ = true;
277   return true;
278 }
279
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;
286 }
287
288 gfx::Size IOSurfaceImageTransportSurface::GetSize() {
289   return size_;
290 }
291
292 void IOSurfaceImageTransportSurface::OnBufferPresented(
293     const AcceleratedSurfaceMsg_BufferPresented_Params& params) {
294   DCHECK(is_swap_buffers_pending_);
295
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);
301   }
302 }
303
304 void IOSurfaceImageTransportSurface::OnResizeViewACK() {
305   NOTREACHED();
306 }
307
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));
316
317   size_ = size;
318   scale_factor_ = scale_factor;
319
320   CreateIOSurface();
321 }
322
323 void IOSurfaceImageTransportSurface::SetLatencyInfo(
324     const ui::LatencyInfo& latency_info) {
325   latency_info_ = latency_info;
326 }
327
328 void IOSurfaceImageTransportSurface::OnWillDestroyStub() {
329   helper_->stub()->RemoveDestructionObserver(this);
330   Destroy();
331 }
332
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());
340   }
341
342   if (fbo_id_) {
343     glDeleteFramebuffersEXT(1, &fbo_id_);
344     fbo_id_ = 0;
345   }
346
347   if (texture_id_) {
348     glDeleteTextures(1, &texture_id_);
349     texture_id_ = 0;
350   }
351
352   io_surface_.reset();
353   io_surface_handle_ = 0;
354 }
355
356 void IOSurfaceImageTransportSurface::CreateIOSurface() {
357   gfx::Size new_rounded_size(RoundUpSurfaceDimension(size_.width()),
358                              RoundUpSurfaceDimension(size_.height()));
359
360   // Only recreate surface when the rounded up size has changed.
361   if (io_surface_.get() && new_rounded_size == rounded_size_)
362     return;
363
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());
369
370   rounded_size_ = new_rounded_size;
371
372   GLint previous_texture_id = 0;
373   glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &previous_texture_id);
374
375   // Free the old IO Surface first to reduce memory fragmentation.
376   UnrefIOSurface();
377
378   glGenFramebuffersEXT(1, &fbo_id_);
379   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_id_);
380
381   IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize();
382
383   glGenTextures(1, &texture_id_);
384
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);
393
394   glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
395                             GL_COLOR_ATTACHMENT0_EXT,
396                             target,
397                             texture_id_,
398                             0);
399
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,
404                                              0,
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_);
422
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;
440   }
441
442   glFlush();
443
444   glBindTexture(target, previous_texture_id);
445   // The FBO remains bound for this GL context.
446 }
447
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)) {}
454
455   // Implement a subset of GLSurface.
456   virtual bool SwapBuffers() OVERRIDE;
457
458  private:
459   virtual ~DRTSurfaceOSMesa() {}
460   DISALLOW_COPY_AND_ASSIGN(DRTSurfaceOSMesa);
461 };
462
463 bool DRTSurfaceOSMesa::SwapBuffers() {
464   return true;
465 }
466
467 bool g_allow_os_mesa = false;
468
469 }  // namespace
470
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();
478
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>();
485       }
486       return scoped_refptr<gfx::GLSurface>(new IOSurfaceImageTransportSurface(
487           manager, stub, surface_handle.handle));
488
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>();
496       }
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));
502   }
503 }
504
505 // static
506 void ImageTransportSurface::SetAllowOSMesaForTesting(bool allow) {
507   g_allow_os_mesa = allow;
508 }
509
510 }  // namespace content