Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / common / gpu / image_transport_surface_fbo_mac.mm
blobe672f6d599d59c6d87281edd87aa588a66f55e8b
1 // Copyright 2014 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_fbo_mac.h"
7 #include "base/command_line.h"
8 #include "base/trace_event/trace_event.h"
9 #include "content/common/gpu/gpu_messages.h"
10 #include "content/common/gpu/image_transport_surface_calayer_mac.h"
11 #include "content/common/gpu/image_transport_surface_iosurface_mac.h"
12 #include "content/common/gpu/image_transport_surface_overlay_mac.h"
13 #include "ui/base/cocoa/remote_layer_api.h"
14 #include "ui/base/ui_base_switches.h"
15 #include "ui/gfx/native_widget_types.h"
16 #include "ui/gl/gl_context.h"
17 #include "ui/gl/gl_implementation.h"
18 #include "ui/gl/gl_surface_osmesa.h"
20 namespace content {
22 scoped_refptr<gfx::GLSurface> ImageTransportSurfaceCreateNativeSurface(
23     GpuChannelManager* manager,
24     GpuCommandBufferStub* stub,
25     gfx::PluginWindowHandle handle) {
26   // Overlays should be used unless the remote layer API isn't present (they
27   // depend on it) or it is disabled at the command line.
28   static bool overlays_disabled_at_command_line =
29       base::CommandLine::ForCurrentProcess()->HasSwitch(
30           switches::kDisableMacOverlays);
31   if (ui::RemoteLayerAPISupported() && !overlays_disabled_at_command_line)
32     return new ImageTransportSurfaceOverlayMac(manager, stub, handle);
33   else
34     return new ImageTransportSurfaceFBO(manager, stub, handle);
37 ImageTransportSurfaceFBO::ImageTransportSurfaceFBO(
38     GpuChannelManager* manager,
39     GpuCommandBufferStub* stub,
40     gfx::PluginWindowHandle handle)
41     : backbuffer_suggested_allocation_(true),
42       frontbuffer_suggested_allocation_(true),
43       fbo_id_(0),
44       texture_id_(0),
45       depth_stencil_renderbuffer_id_(0),
46       has_complete_framebuffer_(false),
47       context_(NULL),
48       scale_factor_(1.f),
49       made_current_(false),
50       is_swap_buffers_send_pending_(false) {
51   if (ui::RemoteLayerAPISupported())
52     storage_provider_.reset(new CALayerStorageProvider(this));
53   else
54     storage_provider_.reset(new IOSurfaceStorageProvider(this));
55   helper_.reset(new ImageTransportHelper(this, manager, stub, handle));
58 ImageTransportSurfaceFBO::~ImageTransportSurfaceFBO() {
61 bool ImageTransportSurfaceFBO::Initialize() {
62   // Only support IOSurfaces if the GL implementation is the native desktop GL.
63   // IO surfaces will not work with, for example, OSMesa software renderer
64   // GL contexts.
65   if (gfx::GetGLImplementation() !=
66       gfx::kGLImplementationDesktopGLCoreProfile &&
67       gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL &&
68       gfx::GetGLImplementation() != gfx::kGLImplementationAppleGL)
69     return false;
71   if (!helper_->Initialize())
72     return false;
74   helper_->stub()->AddDestructionObserver(this);
75   return true;
78 void ImageTransportSurfaceFBO::Destroy() {
79   DestroyFramebuffer();
82 bool ImageTransportSurfaceFBO::DeferDraws() {
83   storage_provider_->WillWriteToBackbuffer();
84   // We should not have a pending send when we are drawing the next frame.
85   DCHECK(!is_swap_buffers_send_pending_);
87   // The call to WillWriteToBackbuffer could potentially force a draw. Ensure
88   // that any changes made to the context's state are restored.
89   context_->RestoreStateIfDirtiedExternally();
90   return false;
93 bool ImageTransportSurfaceFBO::IsOffscreen() {
94   return false;
97 bool ImageTransportSurfaceFBO::OnMakeCurrent(gfx::GLContext* context) {
98   context_ = context;
100   if (made_current_)
101     return true;
103   AllocateOrResizeFramebuffer(gfx::Size(1, 1), 1.f);
105   made_current_ = true;
106   return true;
109 void ImageTransportSurfaceFBO::NotifyWasBound() {
110   // Sometimes calling glBindFramebuffer doesn't seem to be enough to get
111   // rendered contents to show up in the color attachment. It appears that doing
112   // a glBegin/End pair with program 0 is enough to tickle the driver into
113   // actually effecting the binding.
114   // http://crbug.com/435786
115   DCHECK(has_complete_framebuffer_);
117   // We will restore the current program after the dummy glBegin/End pair.
118   // Ensure that we will be able to restore this state before attempting to
119   // change it.
120   GLint old_program_signed = 0;
121   glGetIntegerv(GL_CURRENT_PROGRAM, &old_program_signed);
122   GLuint old_program = static_cast<GLuint>(old_program_signed);
123   if (old_program && glIsProgram(old_program)) {
124     // A deleted program cannot be re-bound.
125     GLint delete_status = GL_FALSE;
126     glGetProgramiv(old_program, GL_DELETE_STATUS, &delete_status);
127     if (delete_status == GL_TRUE)
128       return;
129     // A program which has had the most recent link fail cannot be re-bound.
130     GLint link_status = GL_FALSE;
131     glGetProgramiv(old_program, GL_LINK_STATUS, &link_status);
132     if (link_status != GL_TRUE)
133       return;
134   }
136   // Issue the dummy call and then restore the state.
137   glUseProgram(0);
138   GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
139   DCHECK(status == GL_FRAMEBUFFER_COMPLETE);
140   if (gfx::GetGLImplementation() == gfx::kGLImplementationDesktopGL) {
141     // These aren't present in the core profile.
142     // TODO(ccameron): verify this workaround isn't still needed with
143     // the core profile.
144     glBegin(GL_TRIANGLES);
145     glEnd();
146   }
147   glUseProgram(old_program);
150 unsigned int ImageTransportSurfaceFBO::GetBackingFrameBufferObject() {
151   return fbo_id_;
154 bool ImageTransportSurfaceFBO::SetBackbufferAllocation(bool allocation) {
155   if (backbuffer_suggested_allocation_ == allocation)
156     return true;
157   backbuffer_suggested_allocation_ = allocation;
158   AdjustBufferAllocation();
159   if (!allocation)
160     storage_provider_->DiscardBackbuffer();
161   return true;
164 void ImageTransportSurfaceFBO::SetFrontbufferAllocation(bool allocation) {
165   if (frontbuffer_suggested_allocation_ == allocation)
166     return;
167   frontbuffer_suggested_allocation_ = allocation;
168   AdjustBufferAllocation();
171 void ImageTransportSurfaceFBO::AdjustBufferAllocation() {
172   // On mac, the frontbuffer and backbuffer are the same buffer. The buffer is
173   // free'd when both the browser and gpu processes have Unref'd the IOSurface.
174   if (!backbuffer_suggested_allocation_ &&
175       !frontbuffer_suggested_allocation_ &&
176       has_complete_framebuffer_) {
177     DestroyFramebuffer();
178   } else if (backbuffer_suggested_allocation_ && !has_complete_framebuffer_) {
179     AllocateOrResizeFramebuffer(pixel_size_, scale_factor_);
180   }
183 gfx::SwapResult ImageTransportSurfaceFBO::SwapBuffers() {
184   TRACE_EVENT0("gpu", "ImageTransportSurfaceFBO::SwapBuffers");
185   pending_swap_pixel_damage_rect_ = gfx::Rect(pixel_size_);
186   return SwapBuffersInternal(gfx::Rect(pixel_size_)) ?
187       gfx::SwapResult::SWAP_ACK : gfx::SwapResult::SWAP_FAILED;
190 bool ImageTransportSurfaceFBO::SwapBuffersInternal(
191     const gfx::Rect& dirty_rect) {
192   DCHECK(backbuffer_suggested_allocation_);
193   if (!frontbuffer_suggested_allocation_)
194     return true;
196   {
197     TRACE_EVENT0("gpu", "ImageTransportSurfaceFBO::glFlush");
198     glFlush();
199   }
201   // It is the responsibility of the storage provider to send the swap IPC.
202   is_swap_buffers_send_pending_ = true;
203   storage_provider_->SwapBuffers(dirty_rect);
205   // The call to swapBuffers could potentially result in an immediate draw.
206   // Ensure that any changes made to the context's state are restored.
207   context_->RestoreStateIfDirtiedExternally();
208   return true;
211 void ImageTransportSurfaceFBO::SendSwapBuffers(uint64 surface_handle,
212                                                const gfx::Size pixel_size,
213                                                float scale_factor) {
214   TRACE_EVENT0("gpu", "ImageTransportSurfaceFBO::SendSwapBuffers");
215   GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params;
216   params.surface_handle = surface_handle;
217   params.size = pixel_size;
218   params.damage_rect = pending_swap_pixel_damage_rect_;
219   params.scale_factor = scale_factor;
220   params.latency_info.swap(latency_info_);
221   helper_->SendAcceleratedSurfaceBuffersSwapped(params);
222   is_swap_buffers_send_pending_ = false;
223   pending_swap_pixel_damage_rect_ = gfx::Rect();
226 void ImageTransportSurfaceFBO::SetRendererID(int renderer_id) {
227   if (renderer_id)
228     context_->share_group()->SetRendererID(renderer_id);
231 const gpu::gles2::FeatureInfo* ImageTransportSurfaceFBO::GetFeatureInfo()
232     const {
233   return helper_->stub()->GetFeatureInfo();
236 gfx::SwapResult ImageTransportSurfaceFBO::PostSubBuffer(int x,
237                                                         int y,
238                                                         int width,
239                                                         int height) {
240   TRACE_EVENT0("gpu", "ImageTransportSurfaceFBO::PostSubBuffer");
241   pending_swap_pixel_damage_rect_.Union(gfx::Rect(x, y, width, height));
242   return SwapBuffersInternal(gfx::Rect(x, y, width, height)) ?
243       gfx::SwapResult::SWAP_ACK : gfx::SwapResult::SWAP_FAILED;
246 bool ImageTransportSurfaceFBO::SupportsPostSubBuffer() {
247   return true;
250 gfx::Size ImageTransportSurfaceFBO::GetSize() {
251   return pixel_size_;
254 void* ImageTransportSurfaceFBO::GetHandle() {
255   return NULL;
258 void* ImageTransportSurfaceFBO::GetDisplay() {
259   return NULL;
262 void ImageTransportSurfaceFBO::OnBufferPresented(
263     const AcceleratedSurfaceMsg_BufferPresented_Params& params) {
264   TRACE_EVENT0("gpu", "ImageTransportSurfaceFBO::OnBufferPresented");
265   SetRendererID(params.renderer_id);
266   storage_provider_->SwapBuffersAckedByBrowser(params.disable_throttling);
269 void ImageTransportSurfaceFBO::OnResize(gfx::Size pixel_size,
270                                         float scale_factor) {
271   TRACE_EVENT2("gpu", "ImageTransportSurfaceFBO::OnResize",
272                "old_size", pixel_size_.ToString(),
273                "new_size", pixel_size.ToString());
274   // Caching |context_| from OnMakeCurrent. It should still be current.
275   DCHECK(context_->IsCurrent(this));
277   AllocateOrResizeFramebuffer(pixel_size, scale_factor);
280 void ImageTransportSurfaceFBO::SetLatencyInfo(
281     const std::vector<ui::LatencyInfo>& latency_info) {
282   for (size_t i = 0; i < latency_info.size(); i++)
283     latency_info_.push_back(latency_info[i]);
286 void ImageTransportSurfaceFBO::WakeUpGpu() {
287   NOTIMPLEMENTED();
290 void ImageTransportSurfaceFBO::OnWillDestroyStub() {
291   helper_->stub()->RemoveDestructionObserver(this);
292   Destroy();
295 void ImageTransportSurfaceFBO::DestroyFramebuffer() {
296   // If we have resources to destroy, then make sure that we have a current
297   // context which we can use to delete the resources.
298   if (context_ || fbo_id_ || texture_id_ || depth_stencil_renderbuffer_id_) {
299     DCHECK(gfx::GLContext::GetCurrent() == context_);
300     DCHECK(context_->IsCurrent(this));
301     DCHECK(CGLGetCurrentContext());
302   }
304   if (fbo_id_) {
305     glDeleteFramebuffersEXT(1, &fbo_id_);
306     fbo_id_ = 0;
307   }
309   if (texture_id_) {
310     glDeleteTextures(1, &texture_id_);
311     texture_id_ = 0;
312   }
314   if (depth_stencil_renderbuffer_id_) {
315     glDeleteRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_);
316     depth_stencil_renderbuffer_id_ = 0;
317   }
319   storage_provider_->FreeColorBufferStorage();
321   has_complete_framebuffer_ = false;
324 void ImageTransportSurfaceFBO::AllocateOrResizeFramebuffer(
325     const gfx::Size& new_pixel_size, float new_scale_factor) {
326   gfx::Size new_rounded_pixel_size =
327       storage_provider_->GetRoundedSize(new_pixel_size);
329   // Only recreate the surface's storage when the rounded up size has changed,
330   // or the scale factor has changed.
331   bool needs_new_storage =
332       !has_complete_framebuffer_ ||
333       new_rounded_pixel_size != rounded_pixel_size_ ||
334       new_scale_factor != scale_factor_;
336   // Save the new storage parameters.
337   pixel_size_ = new_pixel_size;
338   rounded_pixel_size_ = new_rounded_pixel_size;
339   scale_factor_ = new_scale_factor;
341   if (!needs_new_storage) {
342     storage_provider_->FrameSizeChanged(pixel_size_, scale_factor_);
343     return;
344   }
346   TRACE_EVENT2("gpu", "ImageTransportSurfaceFBO::AllocateOrResizeFramebuffer",
347                "width", new_rounded_pixel_size.width(),
348                "height", new_rounded_pixel_size.height());
350   // GL_TEXTURE_RECTANGLE_ARB is the best supported render target on
351   // Mac OS X and is required for IOSurface interoperability.
352   GLint previous_texture_id = 0;
354   GLenum texture_target = GL_TEXTURE_RECTANGLE_ARB;
355   GLenum texture_binding_target = GL_TEXTURE_BINDING_RECTANGLE_ARB;
356   // However, the remote core animation path on the core profile will
357   // be the preferred combination going forward.
358   if (gfx::GetGLImplementation() ==
359       gfx::kGLImplementationDesktopGLCoreProfile &&
360       ui::RemoteLayerAPISupported()) {
361     texture_target = GL_TEXTURE_2D;
362     texture_binding_target = GL_TEXTURE_BINDING_2D;
363   }
365   glGetIntegerv(texture_binding_target, &previous_texture_id);
367   // Free the old IO Surface first to reduce memory fragmentation.
368   DestroyFramebuffer();
370   glGenFramebuffersEXT(1, &fbo_id_);
371   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_id_);
373   glGenTextures(1, &texture_id_);
375   glBindTexture(texture_target, texture_id_);
376   glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
377   glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
378   glTexParameteri(texture_target,
379                   GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
380   glTexParameteri(texture_target,
381                   GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
383   glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
384                             GL_COLOR_ATTACHMENT0_EXT,
385                             texture_target,
386                             texture_id_,
387                             0);
389   // Search through the provided attributes; if the caller has
390   // requested a stencil buffer, try to get one.
392   int32 stencil_bits =
393       helper_->stub()->GetRequestedAttribute(EGL_STENCIL_SIZE);
394   if (stencil_bits > 0) {
395     // Create and bind the stencil buffer
396     bool has_packed_depth_stencil =
397          GLSurface::ExtensionsContain(
398              reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)),
399                                             "GL_EXT_packed_depth_stencil");
401     if (has_packed_depth_stencil) {
402       glGenRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_);
403       glBindRenderbufferEXT(GL_RENDERBUFFER_EXT,
404                             depth_stencil_renderbuffer_id_);
405       glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT,
406                               rounded_pixel_size_.width(),
407                               rounded_pixel_size_.height());
408       glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
409                                   GL_STENCIL_ATTACHMENT_EXT,
410                                   GL_RENDERBUFFER_EXT,
411                                   depth_stencil_renderbuffer_id_);
412     }
414     // If we asked for stencil but the extension isn't present,
415     // it's OK to silently fail; subsequent code will/must check
416     // for the presence of a stencil buffer before attempting to
417     // do stencil-based operations.
418   }
420   bool allocated_color_buffer = storage_provider_->AllocateColorBufferStorage(
421       static_cast<CGLContextObj>(
422           context_->GetHandle()),
423           context_->GetStateWasDirtiedExternallyCallback(),
424           texture_id_, rounded_pixel_size_, scale_factor_);
425   if (!allocated_color_buffer) {
426     DLOG(ERROR) << "Failed to allocate color buffer storage.";
427     DestroyFramebuffer();
428     return;
429   }
431   GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
432   if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
433     DLOG(ERROR) << "Framebuffer was incomplete: " << status;
434     DestroyFramebuffer();
435     return;
436   }
438   has_complete_framebuffer_ = true;
439   storage_provider_->FrameSizeChanged(pixel_size_, scale_factor_);
441   glBindTexture(texture_target, previous_texture_id);
442   // The FBO remains bound for this GL context.
445 }  // namespace content