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.
4
5#include "content/common/gpu/image_transport_surface_fbo_mac.h"
6
7#include "content/common/gpu/gpu_messages.h"
8#include "content/common/gpu/image_transport_surface_iosurface_mac.h"
9#include "ui/gfx/native_widget_types.h"
10#include "ui/gl/gl_context.h"
11#include "ui/gl/gl_implementation.h"
12#include "ui/gl/gl_surface_osmesa.h"
13
14namespace content {
15
16ImageTransportSurfaceFBO::ImageTransportSurfaceFBO(
17    StorageProvider* storage_provider,
18    GpuChannelManager* manager,
19    GpuCommandBufferStub* stub,
20    gfx::PluginWindowHandle handle)
21    : storage_provider_(storage_provider),
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_pending_(false),
32      did_unschedule_(false) {
33  helper_.reset(new ImageTransportHelper(this, manager, stub, handle));
34}
35
36ImageTransportSurfaceFBO::~ImageTransportSurfaceFBO() {
37}
38
39bool ImageTransportSurfaceFBO::Initialize() {
40  // Only support IOSurfaces if the GL implementation is the native desktop GL.
41  // IO surfaces will not work with, for example, OSMesa software renderer
42  // GL contexts.
43  if (gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL &&
44      gfx::GetGLImplementation() != gfx::kGLImplementationAppleGL)
45    return false;
46
47  if (!helper_->Initialize())
48    return false;
49
50  helper_->stub()->AddDestructionObserver(this);
51  return true;
52}
53
54void ImageTransportSurfaceFBO::Destroy() {
55  DestroyFramebuffer();
56
57  helper_->Destroy();
58}
59
60bool ImageTransportSurfaceFBO::DeferDraws() {
61  // The command buffer hit a draw/clear command that could clobber the
62  // IOSurface in use by an earlier SwapBuffers. If a Swap is pending, abort
63  // processing of the command by returning true and unschedule until the Swap
64  // Ack arrives.
65  if(did_unschedule_)
66    return true;  // Still unscheduled, so just return true.
67  if (is_swap_buffers_pending_) {
68    did_unschedule_ = true;
69    helper_->SetScheduled(false);
70    return true;
71  }
72  return false;
73}
74
75bool ImageTransportSurfaceFBO::IsOffscreen() {
76  return false;
77}
78
79bool ImageTransportSurfaceFBO::OnMakeCurrent(gfx::GLContext* context) {
80  context_ = context;
81
82  if (made_current_)
83    return true;
84
85  OnResize(gfx::Size(1, 1), 1.f);
86
87  made_current_ = true;
88  return true;
89}
90
91unsigned int ImageTransportSurfaceFBO::GetBackingFrameBufferObject() {
92  return fbo_id_;
93}
94
95bool ImageTransportSurfaceFBO::SetBackbufferAllocation(bool allocation) {
96  if (backbuffer_suggested_allocation_ == allocation)
97    return true;
98  backbuffer_suggested_allocation_ = allocation;
99  AdjustBufferAllocation();
100  return true;
101}
102
103void ImageTransportSurfaceFBO::SetFrontbufferAllocation(bool allocation) {
104  if (frontbuffer_suggested_allocation_ == allocation)
105    return;
106  frontbuffer_suggested_allocation_ = allocation;
107  AdjustBufferAllocation();
108}
109
110void ImageTransportSurfaceFBO::AdjustBufferAllocation() {
111  // On mac, the frontbuffer and backbuffer are the same buffer. The buffer is
112  // free'd when both the browser and gpu processes have Unref'd the IOSurface.
113  if (!backbuffer_suggested_allocation_ &&
114      !frontbuffer_suggested_allocation_ &&
115      has_complete_framebuffer_) {
116    DestroyFramebuffer();
117    helper_->Suspend();
118  } else if (backbuffer_suggested_allocation_ && !has_complete_framebuffer_) {
119    CreateFramebuffer();
120  }
121}
122
123bool ImageTransportSurfaceFBO::SwapBuffers() {
124  DCHECK(backbuffer_suggested_allocation_);
125  if (!frontbuffer_suggested_allocation_)
126    return true;
127  glFlush();
128
129  GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params;
130  params.surface_handle = storage_provider_->GetSurfaceHandle();
131  params.size = GetSize();
132  params.scale_factor = scale_factor_;
133  params.latency_info.swap(latency_info_);
134  helper_->SendAcceleratedSurfaceBuffersSwapped(params);
135
136  DCHECK(!is_swap_buffers_pending_);
137  is_swap_buffers_pending_ = true;
138  return true;
139}
140
141bool ImageTransportSurfaceFBO::PostSubBuffer(
142    int x, int y, int width, int height) {
143  DCHECK(backbuffer_suggested_allocation_);
144  if (!frontbuffer_suggested_allocation_)
145    return true;
146  glFlush();
147
148  GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params params;
149  params.surface_handle = storage_provider_->GetSurfaceHandle();
150  params.x = x;
151  params.y = y;
152  params.width = width;
153  params.height = height;
154  params.surface_size = GetSize();
155  params.surface_scale_factor = scale_factor_;
156  params.latency_info.swap(latency_info_);
157  helper_->SendAcceleratedSurfacePostSubBuffer(params);
158
159  DCHECK(!is_swap_buffers_pending_);
160  is_swap_buffers_pending_ = true;
161  return true;
162}
163
164bool ImageTransportSurfaceFBO::SupportsPostSubBuffer() {
165  return true;
166}
167
168gfx::Size ImageTransportSurfaceFBO::GetSize() {
169  return size_;
170}
171
172void* ImageTransportSurfaceFBO::GetHandle() {
173  return NULL;
174}
175
176void* ImageTransportSurfaceFBO::GetDisplay() {
177  return NULL;
178}
179
180void ImageTransportSurfaceFBO::OnBufferPresented(
181    const AcceleratedSurfaceMsg_BufferPresented_Params& params) {
182  DCHECK(is_swap_buffers_pending_);
183
184  context_->share_group()->SetRendererID(params.renderer_id);
185  is_swap_buffers_pending_ = false;
186  if (did_unschedule_) {
187    did_unschedule_ = false;
188    helper_->SetScheduled(true);
189  }
190}
191
192void ImageTransportSurfaceFBO::OnResize(gfx::Size size,
193                                        float scale_factor) {
194  // This trace event is used in gpu_feature_browsertest.cc - the test will need
195  // to be updated if this event is changed or moved.
196  TRACE_EVENT2("gpu", "ImageTransportSurfaceFBO::OnResize",
197               "old_width", size_.width(), "new_width", size.width());
198  // Caching |context_| from OnMakeCurrent. It should still be current.
199  DCHECK(context_->IsCurrent(this));
200
201  size_ = size;
202  scale_factor_ = scale_factor;
203
204  CreateFramebuffer();
205}
206
207void ImageTransportSurfaceFBO::SetLatencyInfo(
208    const std::vector<ui::LatencyInfo>& latency_info) {
209  for (size_t i = 0; i < latency_info.size(); i++)
210    latency_info_.push_back(latency_info[i]);
211}
212
213void ImageTransportSurfaceFBO::WakeUpGpu() {
214  NOTIMPLEMENTED();
215}
216
217void ImageTransportSurfaceFBO::OnWillDestroyStub() {
218  helper_->stub()->RemoveDestructionObserver(this);
219  Destroy();
220}
221
222void ImageTransportSurfaceFBO::DestroyFramebuffer() {
223  // If we have resources to destroy, then make sure that we have a current
224  // context which we can use to delete the resources.
225  if (context_ || fbo_id_ || texture_id_ || depth_stencil_renderbuffer_id_) {
226    DCHECK(gfx::GLContext::GetCurrent() == context_);
227    DCHECK(context_->IsCurrent(this));
228    DCHECK(CGLGetCurrentContext());
229  }
230
231  if (fbo_id_) {
232    glDeleteFramebuffersEXT(1, &fbo_id_);
233    fbo_id_ = 0;
234  }
235
236  if (texture_id_) {
237    glDeleteTextures(1, &texture_id_);
238    texture_id_ = 0;
239  }
240
241  if (depth_stencil_renderbuffer_id_) {
242    glDeleteRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_);
243    depth_stencil_renderbuffer_id_ = 0;
244  }
245
246  storage_provider_->FreeColorBufferStorage();
247
248  has_complete_framebuffer_ = false;
249}
250
251void ImageTransportSurfaceFBO::CreateFramebuffer() {
252  gfx::Size new_rounded_size = storage_provider_->GetRoundedSize(size_);
253
254  // Only recreate surface when the rounded up size has changed.
255  if (has_complete_framebuffer_ && new_rounded_size == rounded_size_)
256    return;
257
258  // This trace event is used in gpu_feature_browsertest.cc - the test will need
259  // to be updated if this event is changed or moved.
260  TRACE_EVENT2("gpu", "ImageTransportSurfaceFBO::CreateFramebuffer",
261               "width", new_rounded_size.width(),
262               "height", new_rounded_size.height());
263
264  rounded_size_ = new_rounded_size;
265
266  // GL_TEXTURE_RECTANGLE_ARB is the best supported render target on
267  // Mac OS X and is required for IOSurface interoperability.
268  GLint previous_texture_id = 0;
269  glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &previous_texture_id);
270
271  // Free the old IO Surface first to reduce memory fragmentation.
272  DestroyFramebuffer();
273
274  glGenFramebuffersEXT(1, &fbo_id_);
275  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_id_);
276
277  glGenTextures(1, &texture_id_);
278
279  glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_id_);
280  glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
281  glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
282  glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,
283                  GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
284  glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,
285                  GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
286
287  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
288                            GL_COLOR_ATTACHMENT0_EXT,
289                            GL_TEXTURE_RECTANGLE_ARB,
290                            texture_id_,
291                            0);
292
293  // Search through the provided attributes; if the caller has
294  // requested a stencil buffer, try to get one.
295
296  int32 stencil_bits =
297      helper_->stub()->GetRequestedAttribute(EGL_STENCIL_SIZE);
298  if (stencil_bits > 0) {
299    // Create and bind the stencil buffer
300    bool has_packed_depth_stencil =
301         GLSurface::ExtensionsContain(
302             reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)),
303                                            "GL_EXT_packed_depth_stencil");
304
305    if (has_packed_depth_stencil) {
306      glGenRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_);
307      glBindRenderbufferEXT(GL_RENDERBUFFER_EXT,
308                            depth_stencil_renderbuffer_id_);
309      glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT,
310                              rounded_size_.width(), rounded_size_.height());
311      glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
312                                  GL_STENCIL_ATTACHMENT_EXT,
313                                  GL_RENDERBUFFER_EXT,
314                                  depth_stencil_renderbuffer_id_);
315    }
316
317    // If we asked for stencil but the extension isn't present,
318    // it's OK to silently fail; subsequent code will/must check
319    // for the presence of a stencil buffer before attempting to
320    // do stencil-based operations.
321  }
322
323  bool allocated_color_buffer = storage_provider_->AllocateColorBufferStorage(
324      static_cast<CGLContextObj>(context_->GetHandle()),
325      rounded_size_);
326  if (!allocated_color_buffer) {
327    DLOG(ERROR) << "Failed to allocate color buffer storage.";
328    DestroyFramebuffer();
329    return;
330  }
331
332  GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
333  if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
334    DLOG(ERROR) << "Framebuffer was incomplete: " << status;
335    DestroyFramebuffer();
336    return;
337  }
338
339  has_complete_framebuffer_ = true;
340
341  glBindTexture(GL_TEXTURE_RECTANGLE_ARB, previous_texture_id);
342  // The FBO remains bound for this GL context.
343}
344
345}  // namespace content
346