[Android WebView] Fix webview perf bot switchover to use org.chromium.webview_shell...
[chromium-blink-merge.git] / content / common / gpu / image_transport_surface_fbo_mac.mm
blobe6fcc408f357557d0661a6e4d94c302e57fb5220
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/trace_event/trace_event.h"
8 #include "content/common/gpu/gpu_messages.h"
9 #include "content/common/gpu/image_transport_surface_calayer_mac.h"
10 #include "content/common/gpu/image_transport_surface_iosurface_mac.h"
11 #include "ui/base/cocoa/remote_layer_api.h"
12 #include "ui/gfx/native_widget_types.h"
13 #include "ui/gl/gl_context.h"
14 #include "ui/gl/gl_implementation.h"
15 #include "ui/gl/gl_surface_osmesa.h"
17 namespace content {
19 scoped_refptr<gfx::GLSurface> ImageTransportSurfaceCreateNativeSurface(
20     GpuChannelManager* manager,
21     GpuCommandBufferStub* stub,
22     gfx::PluginWindowHandle handle) {
23   return new ImageTransportSurfaceFBO(manager, stub, handle);
26 ImageTransportSurfaceFBO::ImageTransportSurfaceFBO(
27     GpuChannelManager* manager,
28     GpuCommandBufferStub* stub,
29     gfx::PluginWindowHandle handle)
30     : backbuffer_suggested_allocation_(true),
31       frontbuffer_suggested_allocation_(true),
32       fbo_id_(0),
33       texture_id_(0),
34       depth_stencil_renderbuffer_id_(0),
35       has_complete_framebuffer_(false),
36       context_(NULL),
37       scale_factor_(1.f),
38       made_current_(false),
39       is_swap_buffers_send_pending_(false) {
40   if (ui::RemoteLayerAPISupported())
41     storage_provider_.reset(new CALayerStorageProvider(this));
42   else
43     storage_provider_.reset(new IOSurfaceStorageProvider(this));
44   helper_.reset(new ImageTransportHelper(this, manager, stub, handle));
47 ImageTransportSurfaceFBO::~ImageTransportSurfaceFBO() {
50 bool ImageTransportSurfaceFBO::Initialize() {
51   // Only support IOSurfaces if the GL implementation is the native desktop GL.
52   // IO surfaces will not work with, for example, OSMesa software renderer
53   // GL contexts.
54   if (gfx::GetGLImplementation() !=
55       gfx::kGLImplementationDesktopGLCoreProfile &&
56       gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL &&
57       gfx::GetGLImplementation() != gfx::kGLImplementationAppleGL)
58     return false;
60   if (!helper_->Initialize())
61     return false;
63   helper_->stub()->AddDestructionObserver(this);
64   return true;
67 void ImageTransportSurfaceFBO::Destroy() {
68   DestroyFramebuffer();
71 bool ImageTransportSurfaceFBO::DeferDraws() {
72   storage_provider_->WillWriteToBackbuffer();
73   // We should not have a pending send when we are drawing the next frame.
74   DCHECK(!is_swap_buffers_send_pending_);
76   // The call to WillWriteToBackbuffer could potentially force a draw. Ensure
77   // that any changes made to the context's state are restored.
78   context_->RestoreStateIfDirtiedExternally();
79   return false;
82 bool ImageTransportSurfaceFBO::IsOffscreen() {
83   return false;
86 bool ImageTransportSurfaceFBO::OnMakeCurrent(gfx::GLContext* context) {
87   context_ = context;
89   if (made_current_)
90     return true;
92   AllocateOrResizeFramebuffer(gfx::Size(1, 1), 1.f);
94   made_current_ = true;
95   return true;
98 void ImageTransportSurfaceFBO::NotifyWasBound() {
99   // Sometimes calling glBindFramebuffer doesn't seem to be enough to get
100   // rendered contents to show up in the color attachment. It appears that doing
101   // a glBegin/End pair with program 0 is enough to tickle the driver into
102   // actually effecting the binding.
103   // http://crbug.com/435786
104   DCHECK(has_complete_framebuffer_);
106   // We will restore the current program after the dummy glBegin/End pair.
107   // Ensure that we will be able to restore this state before attempting to
108   // change it.
109   GLint old_program_signed = 0;
110   glGetIntegerv(GL_CURRENT_PROGRAM, &old_program_signed);
111   GLuint old_program = static_cast<GLuint>(old_program_signed);
112   if (old_program && glIsProgram(old_program)) {
113     // A deleted program cannot be re-bound.
114     GLint delete_status = GL_FALSE;
115     glGetProgramiv(old_program, GL_DELETE_STATUS, &delete_status);
116     if (delete_status == GL_TRUE)
117       return;
118     // A program which has had the most recent link fail cannot be re-bound.
119     GLint link_status = GL_FALSE;
120     glGetProgramiv(old_program, GL_LINK_STATUS, &link_status);
121     if (link_status != GL_TRUE)
122       return;
123   }
125   // Issue the dummy call and then restore the state.
126   glUseProgram(0);
127   GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
128   DCHECK(status == GL_FRAMEBUFFER_COMPLETE);
129   if (gfx::GetGLImplementation() == gfx::kGLImplementationDesktopGL) {
130     // These aren't present in the core profile.
131     // TODO(ccameron): verify this workaround isn't still needed with
132     // the core profile.
133     glBegin(GL_TRIANGLES);
134     glEnd();
135   }
136   glUseProgram(old_program);
139 unsigned int ImageTransportSurfaceFBO::GetBackingFrameBufferObject() {
140   return fbo_id_;
143 bool ImageTransportSurfaceFBO::SetBackbufferAllocation(bool allocation) {
144   if (backbuffer_suggested_allocation_ == allocation)
145     return true;
146   backbuffer_suggested_allocation_ = allocation;
147   AdjustBufferAllocation();
148   if (!allocation)
149     storage_provider_->DiscardBackbuffer();
150   return true;
153 void ImageTransportSurfaceFBO::SetFrontbufferAllocation(bool allocation) {
154   if (frontbuffer_suggested_allocation_ == allocation)
155     return;
156   frontbuffer_suggested_allocation_ = allocation;
157   AdjustBufferAllocation();
160 void ImageTransportSurfaceFBO::AdjustBufferAllocation() {
161   // On mac, the frontbuffer and backbuffer are the same buffer. The buffer is
162   // free'd when both the browser and gpu processes have Unref'd the IOSurface.
163   if (!backbuffer_suggested_allocation_ &&
164       !frontbuffer_suggested_allocation_ &&
165       has_complete_framebuffer_) {
166     DestroyFramebuffer();
167   } else if (backbuffer_suggested_allocation_ && !has_complete_framebuffer_) {
168     AllocateOrResizeFramebuffer(pixel_size_, scale_factor_);
169   }
172 gfx::SwapResult ImageTransportSurfaceFBO::SwapBuffers() {
173   TRACE_EVENT0("gpu", "ImageTransportSurfaceFBO::SwapBuffers");
174   pending_swap_pixel_damage_rect_ = gfx::Rect(pixel_size_);
175   return SwapBuffersInternal(gfx::Rect(pixel_size_)) ?
176       gfx::SwapResult::SWAP_ACK : gfx::SwapResult::SWAP_FAILED;
179 bool ImageTransportSurfaceFBO::SwapBuffersInternal(
180     const gfx::Rect& dirty_rect) {
181   DCHECK(backbuffer_suggested_allocation_);
182   if (!frontbuffer_suggested_allocation_)
183     return true;
185   {
186     TRACE_EVENT0("gpu", "ImageTransportSurfaceFBO::glFlush");
187     glFlush();
188   }
190   // It is the responsibility of the storage provider to send the swap IPC.
191   is_swap_buffers_send_pending_ = true;
192   storage_provider_->SwapBuffers(dirty_rect);
194   // The call to swapBuffers could potentially result in an immediate draw.
195   // Ensure that any changes made to the context's state are restored.
196   context_->RestoreStateIfDirtiedExternally();
197   return true;
200 void ImageTransportSurfaceFBO::SendSwapBuffers(uint64 surface_handle,
201                                                const gfx::Size pixel_size,
202                                                float scale_factor) {
203   TRACE_EVENT0("gpu", "ImageTransportSurfaceFBO::SendSwapBuffers");
204   GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params;
205   params.surface_handle = surface_handle;
206   params.size = pixel_size;
207   params.damage_rect = pending_swap_pixel_damage_rect_;
208   params.scale_factor = scale_factor;
209   params.latency_info.swap(latency_info_);
210   helper_->SendAcceleratedSurfaceBuffersSwapped(params);
211   is_swap_buffers_send_pending_ = false;
212   pending_swap_pixel_damage_rect_ = gfx::Rect();
215 void ImageTransportSurfaceFBO::SetRendererID(int renderer_id) {
216   if (renderer_id)
217     context_->share_group()->SetRendererID(renderer_id);
220 const gpu::gles2::FeatureInfo* ImageTransportSurfaceFBO::GetFeatureInfo()
221     const {
222   return helper_->stub()->GetFeatureInfo();
225 gfx::SwapResult ImageTransportSurfaceFBO::PostSubBuffer(int x,
226                                                         int y,
227                                                         int width,
228                                                         int height) {
229   TRACE_EVENT0("gpu", "ImageTransportSurfaceFBO::PostSubBuffer");
230   pending_swap_pixel_damage_rect_.Union(gfx::Rect(x, y, width, height));
231   return SwapBuffersInternal(gfx::Rect(x, y, width, height)) ?
232       gfx::SwapResult::SWAP_ACK : gfx::SwapResult::SWAP_FAILED;
235 bool ImageTransportSurfaceFBO::SupportsPostSubBuffer() {
236   return true;
239 gfx::Size ImageTransportSurfaceFBO::GetSize() {
240   return pixel_size_;
243 void* ImageTransportSurfaceFBO::GetHandle() {
244   return NULL;
247 void* ImageTransportSurfaceFBO::GetDisplay() {
248   return NULL;
251 void ImageTransportSurfaceFBO::OnBufferPresented(
252     const AcceleratedSurfaceMsg_BufferPresented_Params& params) {
253   TRACE_EVENT0("gpu", "ImageTransportSurfaceFBO::OnBufferPresented");
254   SetRendererID(params.renderer_id);
255   storage_provider_->SwapBuffersAckedByBrowser(params.disable_throttling);
258 void ImageTransportSurfaceFBO::OnResize(gfx::Size pixel_size,
259                                         float scale_factor) {
260   TRACE_EVENT2("gpu", "ImageTransportSurfaceFBO::OnResize",
261                "old_size", pixel_size_.ToString(),
262                "new_size", pixel_size.ToString());
263   // Caching |context_| from OnMakeCurrent. It should still be current.
264   DCHECK(context_->IsCurrent(this));
266   AllocateOrResizeFramebuffer(pixel_size, scale_factor);
269 void ImageTransportSurfaceFBO::SetLatencyInfo(
270     const std::vector<ui::LatencyInfo>& latency_info) {
271   for (size_t i = 0; i < latency_info.size(); i++)
272     latency_info_.push_back(latency_info[i]);
275 void ImageTransportSurfaceFBO::WakeUpGpu() {
276   NOTIMPLEMENTED();
279 void ImageTransportSurfaceFBO::OnWillDestroyStub() {
280   helper_->stub()->RemoveDestructionObserver(this);
281   Destroy();
284 void ImageTransportSurfaceFBO::DestroyFramebuffer() {
285   // If we have resources to destroy, then make sure that we have a current
286   // context which we can use to delete the resources.
287   if (context_ || fbo_id_ || texture_id_ || depth_stencil_renderbuffer_id_) {
288     DCHECK(gfx::GLContext::GetCurrent() == context_);
289     DCHECK(context_->IsCurrent(this));
290     DCHECK(CGLGetCurrentContext());
291   }
293   if (fbo_id_) {
294     glDeleteFramebuffersEXT(1, &fbo_id_);
295     fbo_id_ = 0;
296   }
298   if (texture_id_) {
299     glDeleteTextures(1, &texture_id_);
300     texture_id_ = 0;
301   }
303   if (depth_stencil_renderbuffer_id_) {
304     glDeleteRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_);
305     depth_stencil_renderbuffer_id_ = 0;
306   }
308   storage_provider_->FreeColorBufferStorage();
310   has_complete_framebuffer_ = false;
313 void ImageTransportSurfaceFBO::AllocateOrResizeFramebuffer(
314     const gfx::Size& new_pixel_size, float new_scale_factor) {
315   gfx::Size new_rounded_pixel_size =
316       storage_provider_->GetRoundedSize(new_pixel_size);
318   // Only recreate the surface's storage when the rounded up size has changed,
319   // or the scale factor has changed.
320   bool needs_new_storage =
321       !has_complete_framebuffer_ ||
322       new_rounded_pixel_size != rounded_pixel_size_ ||
323       new_scale_factor != scale_factor_;
325   // Save the new storage parameters.
326   pixel_size_ = new_pixel_size;
327   rounded_pixel_size_ = new_rounded_pixel_size;
328   scale_factor_ = new_scale_factor;
330   if (!needs_new_storage) {
331     storage_provider_->FrameSizeChanged(pixel_size_, scale_factor_);
332     return;
333   }
335   TRACE_EVENT2("gpu", "ImageTransportSurfaceFBO::AllocateOrResizeFramebuffer",
336                "width", new_rounded_pixel_size.width(),
337                "height", new_rounded_pixel_size.height());
339   // GL_TEXTURE_RECTANGLE_ARB is the best supported render target on
340   // Mac OS X and is required for IOSurface interoperability.
341   GLint previous_texture_id = 0;
343   GLenum texture_target = GL_TEXTURE_RECTANGLE_ARB;
344   GLenum texture_binding_target = GL_TEXTURE_BINDING_RECTANGLE_ARB;
345   // However, the remote core animation path on the core profile will
346   // be the preferred combination going forward.
347   if (gfx::GetGLImplementation() ==
348       gfx::kGLImplementationDesktopGLCoreProfile &&
349       ui::RemoteLayerAPISupported()) {
350     texture_target = GL_TEXTURE_2D;
351     texture_binding_target = GL_TEXTURE_BINDING_2D;
352   }
354   glGetIntegerv(texture_binding_target, &previous_texture_id);
356   // Free the old IO Surface first to reduce memory fragmentation.
357   DestroyFramebuffer();
359   glGenFramebuffersEXT(1, &fbo_id_);
360   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_id_);
362   glGenTextures(1, &texture_id_);
364   glBindTexture(texture_target, texture_id_);
365   glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
366   glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
367   glTexParameteri(texture_target,
368                   GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
369   glTexParameteri(texture_target,
370                   GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
372   glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
373                             GL_COLOR_ATTACHMENT0_EXT,
374                             texture_target,
375                             texture_id_,
376                             0);
378   // Search through the provided attributes; if the caller has
379   // requested a stencil buffer, try to get one.
381   int32 stencil_bits =
382       helper_->stub()->GetRequestedAttribute(EGL_STENCIL_SIZE);
383   if (stencil_bits > 0) {
384     // Create and bind the stencil buffer
385     bool has_packed_depth_stencil =
386          GLSurface::ExtensionsContain(
387              reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)),
388                                             "GL_EXT_packed_depth_stencil");
390     if (has_packed_depth_stencil) {
391       glGenRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_);
392       glBindRenderbufferEXT(GL_RENDERBUFFER_EXT,
393                             depth_stencil_renderbuffer_id_);
394       glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT,
395                               rounded_pixel_size_.width(),
396                               rounded_pixel_size_.height());
397       glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
398                                   GL_STENCIL_ATTACHMENT_EXT,
399                                   GL_RENDERBUFFER_EXT,
400                                   depth_stencil_renderbuffer_id_);
401     }
403     // If we asked for stencil but the extension isn't present,
404     // it's OK to silently fail; subsequent code will/must check
405     // for the presence of a stencil buffer before attempting to
406     // do stencil-based operations.
407   }
409   bool allocated_color_buffer = storage_provider_->AllocateColorBufferStorage(
410       static_cast<CGLContextObj>(
411           context_->GetHandle()),
412           context_->GetStateWasDirtiedExternallyCallback(),
413           texture_id_, rounded_pixel_size_, scale_factor_);
414   if (!allocated_color_buffer) {
415     DLOG(ERROR) << "Failed to allocate color buffer storage.";
416     DestroyFramebuffer();
417     return;
418   }
420   GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
421   if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
422     DLOG(ERROR) << "Framebuffer was incomplete: " << status;
423     DestroyFramebuffer();
424     return;
425   }
427   has_complete_framebuffer_ = true;
428   storage_provider_->FrameSizeChanged(pixel_size_, scale_factor_);
430   glBindTexture(texture_target, previous_texture_id);
431   // The FBO remains bound for this GL context.
434 }  // namespace content