base: Change DCHECK_IS_ON to a macro DCHECK_IS_ON().
[chromium-blink-merge.git] / content / common / gpu / image_transport_surface_fbo_mac.mm
blobe081633de6879a808f53295024a1328cac7a184c
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 ImageTransportSurfaceFBO::ImageTransportSurfaceFBO(
19     GpuChannelManager* manager,
20     GpuCommandBufferStub* stub,
21     gfx::PluginWindowHandle handle)
22     : backbuffer_suggested_allocation_(true),
23       frontbuffer_suggested_allocation_(true),
24       fbo_id_(0),
25       texture_id_(0),
26       depth_stencil_renderbuffer_id_(0),
27       has_complete_framebuffer_(false),
28       context_(NULL),
29       scale_factor_(1.f),
30       made_current_(false),
31       is_swap_buffers_send_pending_(false) {
32   if (ui::RemoteLayerAPISupported())
33     storage_provider_.reset(new CALayerStorageProvider(this));
34   else
35     storage_provider_.reset(new IOSurfaceStorageProvider(this));
36   helper_.reset(new ImageTransportHelper(this, manager, stub, handle));
39 ImageTransportSurfaceFBO::~ImageTransportSurfaceFBO() {
42 bool ImageTransportSurfaceFBO::Initialize() {
43   // Only support IOSurfaces if the GL implementation is the native desktop GL.
44   // IO surfaces will not work with, for example, OSMesa software renderer
45   // GL contexts.
46   if (gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL &&
47       gfx::GetGLImplementation() != gfx::kGLImplementationAppleGL)
48     return false;
50   if (!helper_->Initialize())
51     return false;
53   helper_->stub()->AddDestructionObserver(this);
54   return true;
57 void ImageTransportSurfaceFBO::Destroy() {
58   DestroyFramebuffer();
61 bool ImageTransportSurfaceFBO::DeferDraws() {
62   storage_provider_->WillWriteToBackbuffer();
63   // We should not have a pending send when we are drawing the next frame.
64   DCHECK(!is_swap_buffers_send_pending_);
65   return false;
68 bool ImageTransportSurfaceFBO::IsOffscreen() {
69   return false;
72 bool ImageTransportSurfaceFBO::OnMakeCurrent(gfx::GLContext* context) {
73   context_ = context;
75   if (made_current_)
76     return true;
78   AllocateOrResizeFramebuffer(gfx::Size(1, 1), 1.f);
80   made_current_ = true;
81   return true;
84 void ImageTransportSurfaceFBO::NotifyWasBound() {
85   // Sometimes calling glBindFramebuffer doesn't seem to be enough to get
86   // rendered contents to show up in the color attachment. It appears that doing
87   // a glBegin/End pair with program 0 is enough to tickle the driver into
88   // actually effecting the binding.
89   // http://crbug.com/435786
90   DCHECK(has_complete_framebuffer_);
92   // We will restore the current program after the dummy glBegin/End pair.
93   // Ensure that we will be able to restore this state before attempting to
94   // change it.
95   GLint old_program_signed = 0;
96   glGetIntegerv(GL_CURRENT_PROGRAM, &old_program_signed);
97   GLuint old_program = static_cast<GLuint>(old_program_signed);
98   if (old_program && glIsProgram(old_program)) {
99     // A deleted program cannot be re-bound.
100     GLint delete_status = GL_FALSE;
101     glGetProgramiv(old_program, GL_DELETE_STATUS, &delete_status);
102     if (delete_status == GL_TRUE)
103       return;
104     // A program which has had the most recent link fail cannot be re-bound.
105     GLint link_status = GL_FALSE;
106     glGetProgramiv(old_program, GL_LINK_STATUS, &link_status);
107     if (link_status != GL_TRUE)
108       return;
109   }
111   // Issue the dummy call and then restore the state.
112   glUseProgram(0);
113   GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
114   DCHECK(status == GL_FRAMEBUFFER_COMPLETE);
115   glBegin(GL_TRIANGLES);
116   glEnd();
117   glUseProgram(old_program);
120 unsigned int ImageTransportSurfaceFBO::GetBackingFrameBufferObject() {
121   return fbo_id_;
124 bool ImageTransportSurfaceFBO::SetBackbufferAllocation(bool allocation) {
125   if (backbuffer_suggested_allocation_ == allocation)
126     return true;
127   backbuffer_suggested_allocation_ = allocation;
128   AdjustBufferAllocation();
129   if (!allocation)
130     storage_provider_->DiscardBackbuffer();
131   return true;
134 void ImageTransportSurfaceFBO::SetFrontbufferAllocation(bool allocation) {
135   if (frontbuffer_suggested_allocation_ == allocation)
136     return;
137   frontbuffer_suggested_allocation_ = allocation;
138   AdjustBufferAllocation();
141 void ImageTransportSurfaceFBO::AdjustBufferAllocation() {
142   // On mac, the frontbuffer and backbuffer are the same buffer. The buffer is
143   // free'd when both the browser and gpu processes have Unref'd the IOSurface.
144   if (!backbuffer_suggested_allocation_ &&
145       !frontbuffer_suggested_allocation_ &&
146       has_complete_framebuffer_) {
147     DestroyFramebuffer();
148   } else if (backbuffer_suggested_allocation_ && !has_complete_framebuffer_) {
149     AllocateOrResizeFramebuffer(pixel_size_, scale_factor_);
150   }
153 bool ImageTransportSurfaceFBO::SwapBuffers() {
154   DCHECK(backbuffer_suggested_allocation_);
155   if (!frontbuffer_suggested_allocation_)
156     return true;
157   glFlush();
159   // It is the responsibility of the storage provider to send the swap IPC.
160   is_swap_buffers_send_pending_ = true;
161   storage_provider_->SwapBuffers(pixel_size_, scale_factor_);
162   return true;
165 void ImageTransportSurfaceFBO::SendSwapBuffers(uint64 surface_handle,
166                                                const gfx::Size pixel_size,
167                                                float scale_factor) {
168   GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params;
169   params.surface_handle = surface_handle;
170   params.size = pixel_size;
171   params.scale_factor = scale_factor;
172   params.latency_info.swap(latency_info_);
173   helper_->SendAcceleratedSurfaceBuffersSwapped(params);
174   is_swap_buffers_send_pending_ = false;
177 void ImageTransportSurfaceFBO::SetRendererID(int renderer_id) {
178   if (renderer_id)
179     context_->share_group()->SetRendererID(renderer_id);
182 bool ImageTransportSurfaceFBO::PostSubBuffer(
183     int x, int y, int width, int height) {
184   // Mac does not support sub-buffer swaps.
185   NOTREACHED();
186   return false;
189 bool ImageTransportSurfaceFBO::SupportsPostSubBuffer() {
190   return true;
193 gfx::Size ImageTransportSurfaceFBO::GetSize() {
194   return pixel_size_;
197 void* ImageTransportSurfaceFBO::GetHandle() {
198   return NULL;
201 void* ImageTransportSurfaceFBO::GetDisplay() {
202   return NULL;
205 void ImageTransportSurfaceFBO::OnBufferPresented(
206     const AcceleratedSurfaceMsg_BufferPresented_Params& params) {
207   SetRendererID(params.renderer_id);
208   storage_provider_->SwapBuffersAckedByBrowser(params.disable_throttling);
211 void ImageTransportSurfaceFBO::OnResize(gfx::Size pixel_size,
212                                         float scale_factor) {
213   TRACE_EVENT2("gpu", "ImageTransportSurfaceFBO::OnResize",
214                "old_size", pixel_size_.ToString(),
215                "new_size", pixel_size.ToString());
216   // Caching |context_| from OnMakeCurrent. It should still be current.
217   DCHECK(context_->IsCurrent(this));
219   AllocateOrResizeFramebuffer(pixel_size, scale_factor);
222 void ImageTransportSurfaceFBO::SetLatencyInfo(
223     const std::vector<ui::LatencyInfo>& latency_info) {
224   for (size_t i = 0; i < latency_info.size(); i++)
225     latency_info_.push_back(latency_info[i]);
228 void ImageTransportSurfaceFBO::WakeUpGpu() {
229   NOTIMPLEMENTED();
232 void ImageTransportSurfaceFBO::OnWillDestroyStub() {
233   helper_->stub()->RemoveDestructionObserver(this);
234   Destroy();
237 void ImageTransportSurfaceFBO::DestroyFramebuffer() {
238   // If we have resources to destroy, then make sure that we have a current
239   // context which we can use to delete the resources.
240   if (context_ || fbo_id_ || texture_id_ || depth_stencil_renderbuffer_id_) {
241     DCHECK(gfx::GLContext::GetCurrent() == context_);
242     DCHECK(context_->IsCurrent(this));
243     DCHECK(CGLGetCurrentContext());
244   }
246   if (fbo_id_) {
247     glDeleteFramebuffersEXT(1, &fbo_id_);
248     fbo_id_ = 0;
249   }
251   if (texture_id_) {
252     glDeleteTextures(1, &texture_id_);
253     texture_id_ = 0;
254   }
256   if (depth_stencil_renderbuffer_id_) {
257     glDeleteRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_);
258     depth_stencil_renderbuffer_id_ = 0;
259   }
261   storage_provider_->FreeColorBufferStorage();
263   has_complete_framebuffer_ = false;
266 void ImageTransportSurfaceFBO::AllocateOrResizeFramebuffer(
267     const gfx::Size& new_pixel_size, float new_scale_factor) {
268   gfx::Size new_rounded_pixel_size =
269       storage_provider_->GetRoundedSize(new_pixel_size);
271   // Only recreate the surface's storage when the rounded up size has changed,
272   // or the scale factor has changed.
273   bool needs_new_storage =
274       !has_complete_framebuffer_ ||
275       new_rounded_pixel_size != rounded_pixel_size_ ||
276       new_scale_factor != scale_factor_;
278   // Save the new storage parameters.
279   pixel_size_ = new_pixel_size;
280   rounded_pixel_size_ = new_rounded_pixel_size;
281   scale_factor_ = new_scale_factor;
283   if (!needs_new_storage)
284     return;
286   TRACE_EVENT2("gpu", "ImageTransportSurfaceFBO::AllocateOrResizeFramebuffer",
287                "width", new_rounded_pixel_size.width(),
288                "height", new_rounded_pixel_size.height());
290   // GL_TEXTURE_RECTANGLE_ARB is the best supported render target on
291   // Mac OS X and is required for IOSurface interoperability.
292   GLint previous_texture_id = 0;
293   glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &previous_texture_id);
295   // Free the old IO Surface first to reduce memory fragmentation.
296   DestroyFramebuffer();
298   glGenFramebuffersEXT(1, &fbo_id_);
299   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_id_);
301   glGenTextures(1, &texture_id_);
303   glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_id_);
304   glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
305   glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
306   glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,
307                   GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
308   glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,
309                   GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
311   glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
312                             GL_COLOR_ATTACHMENT0_EXT,
313                             GL_TEXTURE_RECTANGLE_ARB,
314                             texture_id_,
315                             0);
317   // Search through the provided attributes; if the caller has
318   // requested a stencil buffer, try to get one.
320   int32 stencil_bits =
321       helper_->stub()->GetRequestedAttribute(EGL_STENCIL_SIZE);
322   if (stencil_bits > 0) {
323     // Create and bind the stencil buffer
324     bool has_packed_depth_stencil =
325          GLSurface::ExtensionsContain(
326              reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)),
327                                             "GL_EXT_packed_depth_stencil");
329     if (has_packed_depth_stencil) {
330       glGenRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_);
331       glBindRenderbufferEXT(GL_RENDERBUFFER_EXT,
332                             depth_stencil_renderbuffer_id_);
333       glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT,
334                               rounded_pixel_size_.width(),
335                               rounded_pixel_size_.height());
336       glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
337                                   GL_STENCIL_ATTACHMENT_EXT,
338                                   GL_RENDERBUFFER_EXT,
339                                   depth_stencil_renderbuffer_id_);
340     }
342     // If we asked for stencil but the extension isn't present,
343     // it's OK to silently fail; subsequent code will/must check
344     // for the presence of a stencil buffer before attempting to
345     // do stencil-based operations.
346   }
348   bool allocated_color_buffer = storage_provider_->AllocateColorBufferStorage(
349       static_cast<CGLContextObj>(context_->GetHandle()), texture_id_,
350       rounded_pixel_size_, scale_factor_);
351   if (!allocated_color_buffer) {
352     DLOG(ERROR) << "Failed to allocate color buffer storage.";
353     DestroyFramebuffer();
354     return;
355   }
357   GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
358   if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
359     DLOG(ERROR) << "Framebuffer was incomplete: " << status;
360     DestroyFramebuffer();
361     return;
362   }
364   has_complete_framebuffer_ = true;
366   glBindTexture(GL_TEXTURE_RECTANGLE_ARB, previous_texture_id);
367   // The FBO remains bound for this GL context.
370 }  // namespace content