Supervised user whitelists: Cleanup
[chromium-blink-merge.git] / content / common / gpu / image_transport_surface_fbo_mac.mm
blobfcb40245b4147c847dc445822f85058da87ec71e
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 "content/common/gpu/gpu_messages.h"
8 #include "content/common/gpu/image_transport_surface_calayer_mac.h"
9 #include "content/common/gpu/image_transport_surface_iosurface_mac.h"
10 #include "ui/base/cocoa/remote_layer_api.h"
11 #include "ui/gfx/native_widget_types.h"
12 #include "ui/gl/gl_context.h"
13 #include "ui/gl/gl_implementation.h"
14 #include "ui/gl/gl_surface_osmesa.h"
16 namespace content {
18 scoped_refptr<gfx::GLSurface> ImageTransportSurfaceCreateNativeSurface(
19     GpuChannelManager* manager,
20     GpuCommandBufferStub* stub,
21     gfx::PluginWindowHandle handle) {
22   return new ImageTransportSurfaceFBO(manager, stub, handle);
25 ImageTransportSurfaceFBO::ImageTransportSurfaceFBO(
26     GpuChannelManager* manager,
27     GpuCommandBufferStub* stub,
28     gfx::PluginWindowHandle handle)
29     : backbuffer_suggested_allocation_(true),
30       frontbuffer_suggested_allocation_(true),
31       fbo_id_(0),
32       texture_id_(0),
33       depth_stencil_renderbuffer_id_(0),
34       has_complete_framebuffer_(false),
35       context_(NULL),
36       scale_factor_(1.f),
37       made_current_(false),
38       is_swap_buffers_send_pending_(false) {
39   if (ui::RemoteLayerAPISupported())
40     storage_provider_.reset(new CALayerStorageProvider(this));
41   else
42     storage_provider_.reset(new IOSurfaceStorageProvider(this));
43   helper_.reset(new ImageTransportHelper(this, manager, stub, handle));
46 ImageTransportSurfaceFBO::~ImageTransportSurfaceFBO() {
49 bool ImageTransportSurfaceFBO::Initialize() {
50   // Only support IOSurfaces if the GL implementation is the native desktop GL.
51   // IO surfaces will not work with, for example, OSMesa software renderer
52   // GL contexts.
53   if (gfx::GetGLImplementation() !=
54       gfx::kGLImplementationDesktopGLCoreProfile &&
55       gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL &&
56       gfx::GetGLImplementation() != gfx::kGLImplementationAppleGL)
57     return false;
59   if (!helper_->Initialize())
60     return false;
62   helper_->stub()->AddDestructionObserver(this);
63   return true;
66 void ImageTransportSurfaceFBO::Destroy() {
67   DestroyFramebuffer();
70 bool ImageTransportSurfaceFBO::DeferDraws() {
71   storage_provider_->WillWriteToBackbuffer();
72   // We should not have a pending send when we are drawing the next frame.
73   DCHECK(!is_swap_buffers_send_pending_);
75   // The call to WillWriteToBackbuffer could potentially force a draw. Ensure
76   // that any changes made to the context's state are restored.
77   context_->RestoreStateIfDirtiedExternally();
78   return false;
81 bool ImageTransportSurfaceFBO::IsOffscreen() {
82   return false;
85 bool ImageTransportSurfaceFBO::OnMakeCurrent(gfx::GLContext* context) {
86   context_ = context;
88   if (made_current_)
89     return true;
91   AllocateOrResizeFramebuffer(gfx::Size(1, 1), 1.f);
93   made_current_ = true;
94   return true;
97 void ImageTransportSurfaceFBO::NotifyWasBound() {
98   // Sometimes calling glBindFramebuffer doesn't seem to be enough to get
99   // rendered contents to show up in the color attachment. It appears that doing
100   // a glBegin/End pair with program 0 is enough to tickle the driver into
101   // actually effecting the binding.
102   // http://crbug.com/435786
103   DCHECK(has_complete_framebuffer_);
105   // We will restore the current program after the dummy glBegin/End pair.
106   // Ensure that we will be able to restore this state before attempting to
107   // change it.
108   GLint old_program_signed = 0;
109   glGetIntegerv(GL_CURRENT_PROGRAM, &old_program_signed);
110   GLuint old_program = static_cast<GLuint>(old_program_signed);
111   if (old_program && glIsProgram(old_program)) {
112     // A deleted program cannot be re-bound.
113     GLint delete_status = GL_FALSE;
114     glGetProgramiv(old_program, GL_DELETE_STATUS, &delete_status);
115     if (delete_status == GL_TRUE)
116       return;
117     // A program which has had the most recent link fail cannot be re-bound.
118     GLint link_status = GL_FALSE;
119     glGetProgramiv(old_program, GL_LINK_STATUS, &link_status);
120     if (link_status != GL_TRUE)
121       return;
122   }
124   // Issue the dummy call and then restore the state.
125   glUseProgram(0);
126   GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
127   DCHECK(status == GL_FRAMEBUFFER_COMPLETE);
128   if (gfx::GetGLImplementation() == gfx::kGLImplementationDesktopGL) {
129     // These aren't present in the core profile.
130     // TODO(ccameron): verify this workaround isn't still needed with
131     // the core profile.
132     glBegin(GL_TRIANGLES);
133     glEnd();
134   }
135   glUseProgram(old_program);
138 unsigned int ImageTransportSurfaceFBO::GetBackingFrameBufferObject() {
139   return fbo_id_;
142 bool ImageTransportSurfaceFBO::SetBackbufferAllocation(bool allocation) {
143   if (backbuffer_suggested_allocation_ == allocation)
144     return true;
145   backbuffer_suggested_allocation_ = allocation;
146   AdjustBufferAllocation();
147   if (!allocation)
148     storage_provider_->DiscardBackbuffer();
149   return true;
152 void ImageTransportSurfaceFBO::SetFrontbufferAllocation(bool allocation) {
153   if (frontbuffer_suggested_allocation_ == allocation)
154     return;
155   frontbuffer_suggested_allocation_ = allocation;
156   AdjustBufferAllocation();
159 void ImageTransportSurfaceFBO::AdjustBufferAllocation() {
160   // On mac, the frontbuffer and backbuffer are the same buffer. The buffer is
161   // free'd when both the browser and gpu processes have Unref'd the IOSurface.
162   if (!backbuffer_suggested_allocation_ &&
163       !frontbuffer_suggested_allocation_ &&
164       has_complete_framebuffer_) {
165     DestroyFramebuffer();
166   } else if (backbuffer_suggested_allocation_ && !has_complete_framebuffer_) {
167     AllocateOrResizeFramebuffer(pixel_size_, scale_factor_);
168   }
171 bool ImageTransportSurfaceFBO::SwapBuffers() {
172   return SwapBuffersInternal();
175 bool ImageTransportSurfaceFBO::SwapBuffersInternal() {
176   DCHECK(backbuffer_suggested_allocation_);
177   if (!frontbuffer_suggested_allocation_)
178     return true;
179   glFlush();
181   // It is the responsibility of the storage provider to send the swap IPC.
182   is_swap_buffers_send_pending_ = true;
183   storage_provider_->SwapBuffers();
185   // The call to swapBuffers could potentially result in an immediate draw.
186   // Ensure that any changes made to the context's state are restored.
187   context_->RestoreStateIfDirtiedExternally();
188   return true;
191 void ImageTransportSurfaceFBO::SendSwapBuffers(uint64 surface_handle,
192                                                const gfx::Size pixel_size,
193                                                float scale_factor) {
194   GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params;
195   params.surface_handle = surface_handle;
196   params.size = pixel_size;
197   params.scale_factor = scale_factor;
198   params.latency_info.swap(latency_info_);
199   helper_->SendAcceleratedSurfaceBuffersSwapped(params);
200   is_swap_buffers_send_pending_ = false;
203 void ImageTransportSurfaceFBO::SetRendererID(int renderer_id) {
204   if (renderer_id)
205     context_->share_group()->SetRendererID(renderer_id);
208 bool ImageTransportSurfaceFBO::PostSubBuffer(
209     int x, int y, int width, int height) {
210   return SwapBuffersInternal();
213 bool ImageTransportSurfaceFBO::SupportsPostSubBuffer() {
214   return true;
217 gfx::Size ImageTransportSurfaceFBO::GetSize() {
218   return pixel_size_;
221 void* ImageTransportSurfaceFBO::GetHandle() {
222   return NULL;
225 void* ImageTransportSurfaceFBO::GetDisplay() {
226   return NULL;
229 void ImageTransportSurfaceFBO::OnBufferPresented(
230     const AcceleratedSurfaceMsg_BufferPresented_Params& params) {
231   SetRendererID(params.renderer_id);
232   storage_provider_->SwapBuffersAckedByBrowser(params.disable_throttling);
235 void ImageTransportSurfaceFBO::OnResize(gfx::Size pixel_size,
236                                         float scale_factor) {
237   TRACE_EVENT2("gpu", "ImageTransportSurfaceFBO::OnResize",
238                "old_size", pixel_size_.ToString(),
239                "new_size", pixel_size.ToString());
240   // Caching |context_| from OnMakeCurrent. It should still be current.
241   DCHECK(context_->IsCurrent(this));
243   AllocateOrResizeFramebuffer(pixel_size, scale_factor);
246 void ImageTransportSurfaceFBO::SetLatencyInfo(
247     const std::vector<ui::LatencyInfo>& latency_info) {
248   for (size_t i = 0; i < latency_info.size(); i++)
249     latency_info_.push_back(latency_info[i]);
252 void ImageTransportSurfaceFBO::WakeUpGpu() {
253   NOTIMPLEMENTED();
256 void ImageTransportSurfaceFBO::OnWillDestroyStub() {
257   helper_->stub()->RemoveDestructionObserver(this);
258   Destroy();
261 void ImageTransportSurfaceFBO::DestroyFramebuffer() {
262   // If we have resources to destroy, then make sure that we have a current
263   // context which we can use to delete the resources.
264   if (context_ || fbo_id_ || texture_id_ || depth_stencil_renderbuffer_id_) {
265     DCHECK(gfx::GLContext::GetCurrent() == context_);
266     DCHECK(context_->IsCurrent(this));
267     DCHECK(CGLGetCurrentContext());
268   }
270   if (fbo_id_) {
271     glDeleteFramebuffersEXT(1, &fbo_id_);
272     fbo_id_ = 0;
273   }
275   if (texture_id_) {
276     glDeleteTextures(1, &texture_id_);
277     texture_id_ = 0;
278   }
280   if (depth_stencil_renderbuffer_id_) {
281     glDeleteRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_);
282     depth_stencil_renderbuffer_id_ = 0;
283   }
285   storage_provider_->FreeColorBufferStorage();
287   has_complete_framebuffer_ = false;
290 void ImageTransportSurfaceFBO::AllocateOrResizeFramebuffer(
291     const gfx::Size& new_pixel_size, float new_scale_factor) {
292   gfx::Size new_rounded_pixel_size =
293       storage_provider_->GetRoundedSize(new_pixel_size);
295   // Only recreate the surface's storage when the rounded up size has changed,
296   // or the scale factor has changed.
297   bool needs_new_storage =
298       !has_complete_framebuffer_ ||
299       new_rounded_pixel_size != rounded_pixel_size_ ||
300       new_scale_factor != scale_factor_;
302   // Save the new storage parameters.
303   pixel_size_ = new_pixel_size;
304   rounded_pixel_size_ = new_rounded_pixel_size;
305   scale_factor_ = new_scale_factor;
307   if (!needs_new_storage) {
308     storage_provider_->FrameSizeChanged(pixel_size_, scale_factor_);
309     return;
310   }
312   TRACE_EVENT2("gpu", "ImageTransportSurfaceFBO::AllocateOrResizeFramebuffer",
313                "width", new_rounded_pixel_size.width(),
314                "height", new_rounded_pixel_size.height());
316   // GL_TEXTURE_RECTANGLE_ARB is the best supported render target on
317   // Mac OS X and is required for IOSurface interoperability.
318   GLint previous_texture_id = 0;
320   GLenum texture_target = GL_TEXTURE_RECTANGLE_ARB;
321   GLenum texture_binding_target = GL_TEXTURE_BINDING_RECTANGLE_ARB;
322   // However, the remote core animation path on the core profile will
323   // be the preferred combination going forward.
324   if (gfx::GetGLImplementation() ==
325       gfx::kGLImplementationDesktopGLCoreProfile &&
326       ui::RemoteLayerAPISupported()) {
327     texture_target = GL_TEXTURE_2D;
328     texture_binding_target = GL_TEXTURE_BINDING_2D;
329   }
331   glGetIntegerv(texture_binding_target, &previous_texture_id);
333   // Free the old IO Surface first to reduce memory fragmentation.
334   DestroyFramebuffer();
336   glGenFramebuffersEXT(1, &fbo_id_);
337   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_id_);
339   glGenTextures(1, &texture_id_);
341   glBindTexture(texture_target, texture_id_);
342   glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
343   glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
344   glTexParameteri(texture_target,
345                   GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
346   glTexParameteri(texture_target,
347                   GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
349   glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
350                             GL_COLOR_ATTACHMENT0_EXT,
351                             texture_target,
352                             texture_id_,
353                             0);
355   // Search through the provided attributes; if the caller has
356   // requested a stencil buffer, try to get one.
358   int32 stencil_bits =
359       helper_->stub()->GetRequestedAttribute(EGL_STENCIL_SIZE);
360   if (stencil_bits > 0) {
361     // Create and bind the stencil buffer
362     bool has_packed_depth_stencil =
363          GLSurface::ExtensionsContain(
364              reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)),
365                                             "GL_EXT_packed_depth_stencil");
367     if (has_packed_depth_stencil) {
368       glGenRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_);
369       glBindRenderbufferEXT(GL_RENDERBUFFER_EXT,
370                             depth_stencil_renderbuffer_id_);
371       glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT,
372                               rounded_pixel_size_.width(),
373                               rounded_pixel_size_.height());
374       glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
375                                   GL_STENCIL_ATTACHMENT_EXT,
376                                   GL_RENDERBUFFER_EXT,
377                                   depth_stencil_renderbuffer_id_);
378     }
380     // If we asked for stencil but the extension isn't present,
381     // it's OK to silently fail; subsequent code will/must check
382     // for the presence of a stencil buffer before attempting to
383     // do stencil-based operations.
384   }
386   bool allocated_color_buffer = storage_provider_->AllocateColorBufferStorage(
387       static_cast<CGLContextObj>(
388           context_->GetHandle()),
389           context_->GetStateWasDirtiedExternallyCallback(),
390           texture_id_, rounded_pixel_size_, scale_factor_);
391   if (!allocated_color_buffer) {
392     DLOG(ERROR) << "Failed to allocate color buffer storage.";
393     DestroyFramebuffer();
394     return;
395   }
397   GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
398   if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
399     DLOG(ERROR) << "Framebuffer was incomplete: " << status;
400     DestroyFramebuffer();
401     return;
402   }
404   has_complete_framebuffer_ = true;
405   storage_provider_->FrameSizeChanged(pixel_size_, scale_factor_);
407   glBindTexture(texture_target, previous_texture_id);
408   // The FBO remains bound for this GL context.
411 }  // namespace content