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"
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),
33 depth_stencil_renderbuffer_id_(0),
34 has_complete_framebuffer_(false),
38 is_swap_buffers_send_pending_(false) {
39 if (ui::RemoteLayerAPISupported())
40 storage_provider_.reset(new CALayerStorageProvider(this));
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
53 if (gfx::GetGLImplementation() !=
54 gfx::kGLImplementationDesktopGLCoreProfile &&
55 gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL &&
56 gfx::GetGLImplementation() != gfx::kGLImplementationAppleGL)
59 if (!helper_->Initialize())
62 helper_->stub()->AddDestructionObserver(this);
66 void ImageTransportSurfaceFBO::Destroy() {
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();
81 bool ImageTransportSurfaceFBO::IsOffscreen() {
85 bool ImageTransportSurfaceFBO::OnMakeCurrent(gfx::GLContext* context) {
91 AllocateOrResizeFramebuffer(gfx::Size(1, 1), 1.f);
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
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)
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)
124 // Issue the dummy call and then restore the state.
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
132 glBegin(GL_TRIANGLES);
135 glUseProgram(old_program);
138 unsigned int ImageTransportSurfaceFBO::GetBackingFrameBufferObject() {
142 bool ImageTransportSurfaceFBO::SetBackbufferAllocation(bool allocation) {
143 if (backbuffer_suggested_allocation_ == allocation)
145 backbuffer_suggested_allocation_ = allocation;
146 AdjustBufferAllocation();
148 storage_provider_->DiscardBackbuffer();
152 void ImageTransportSurfaceFBO::SetFrontbufferAllocation(bool allocation) {
153 if (frontbuffer_suggested_allocation_ == allocation)
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_);
171 bool ImageTransportSurfaceFBO::SwapBuffers() {
172 DCHECK(backbuffer_suggested_allocation_);
173 if (!frontbuffer_suggested_allocation_)
177 // It is the responsibility of the storage provider to send the swap IPC.
178 is_swap_buffers_send_pending_ = true;
179 storage_provider_->SwapBuffers(pixel_size_, scale_factor_);
181 // The call to swapBuffers could potentially result in an immediate draw.
182 // Ensure that any changes made to the context's state are restored.
183 context_->RestoreStateIfDirtiedExternally();
187 void ImageTransportSurfaceFBO::SendSwapBuffers(uint64 surface_handle,
188 const gfx::Size pixel_size,
189 float scale_factor) {
190 GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params;
191 params.surface_handle = surface_handle;
192 params.size = pixel_size;
193 params.scale_factor = scale_factor;
194 params.latency_info.swap(latency_info_);
195 helper_->SendAcceleratedSurfaceBuffersSwapped(params);
196 is_swap_buffers_send_pending_ = false;
199 void ImageTransportSurfaceFBO::SetRendererID(int renderer_id) {
201 context_->share_group()->SetRendererID(renderer_id);
204 bool ImageTransportSurfaceFBO::PostSubBuffer(
205 int x, int y, int width, int height) {
206 // Mac does not support sub-buffer swaps.
211 bool ImageTransportSurfaceFBO::SupportsPostSubBuffer() {
215 gfx::Size ImageTransportSurfaceFBO::GetSize() {
219 void* ImageTransportSurfaceFBO::GetHandle() {
223 void* ImageTransportSurfaceFBO::GetDisplay() {
227 void ImageTransportSurfaceFBO::OnBufferPresented(
228 const AcceleratedSurfaceMsg_BufferPresented_Params& params) {
229 SetRendererID(params.renderer_id);
230 storage_provider_->SwapBuffersAckedByBrowser(params.disable_throttling);
233 void ImageTransportSurfaceFBO::OnResize(gfx::Size pixel_size,
234 float scale_factor) {
235 TRACE_EVENT2("gpu", "ImageTransportSurfaceFBO::OnResize",
236 "old_size", pixel_size_.ToString(),
237 "new_size", pixel_size.ToString());
238 // Caching |context_| from OnMakeCurrent. It should still be current.
239 DCHECK(context_->IsCurrent(this));
241 AllocateOrResizeFramebuffer(pixel_size, scale_factor);
244 void ImageTransportSurfaceFBO::SetLatencyInfo(
245 const std::vector<ui::LatencyInfo>& latency_info) {
246 for (size_t i = 0; i < latency_info.size(); i++)
247 latency_info_.push_back(latency_info[i]);
250 void ImageTransportSurfaceFBO::WakeUpGpu() {
254 void ImageTransportSurfaceFBO::OnWillDestroyStub() {
255 helper_->stub()->RemoveDestructionObserver(this);
259 void ImageTransportSurfaceFBO::DestroyFramebuffer() {
260 // If we have resources to destroy, then make sure that we have a current
261 // context which we can use to delete the resources.
262 if (context_ || fbo_id_ || texture_id_ || depth_stencil_renderbuffer_id_) {
263 DCHECK(gfx::GLContext::GetCurrent() == context_);
264 DCHECK(context_->IsCurrent(this));
265 DCHECK(CGLGetCurrentContext());
269 glDeleteFramebuffersEXT(1, &fbo_id_);
274 glDeleteTextures(1, &texture_id_);
278 if (depth_stencil_renderbuffer_id_) {
279 glDeleteRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_);
280 depth_stencil_renderbuffer_id_ = 0;
283 storage_provider_->FreeColorBufferStorage();
285 has_complete_framebuffer_ = false;
288 void ImageTransportSurfaceFBO::AllocateOrResizeFramebuffer(
289 const gfx::Size& new_pixel_size, float new_scale_factor) {
290 gfx::Size new_rounded_pixel_size =
291 storage_provider_->GetRoundedSize(new_pixel_size);
293 // Only recreate the surface's storage when the rounded up size has changed,
294 // or the scale factor has changed.
295 bool needs_new_storage =
296 !has_complete_framebuffer_ ||
297 new_rounded_pixel_size != rounded_pixel_size_ ||
298 new_scale_factor != scale_factor_;
300 // Save the new storage parameters.
301 pixel_size_ = new_pixel_size;
302 rounded_pixel_size_ = new_rounded_pixel_size;
303 scale_factor_ = new_scale_factor;
305 if (!needs_new_storage)
308 TRACE_EVENT2("gpu", "ImageTransportSurfaceFBO::AllocateOrResizeFramebuffer",
309 "width", new_rounded_pixel_size.width(),
310 "height", new_rounded_pixel_size.height());
312 // GL_TEXTURE_RECTANGLE_ARB is the best supported render target on
313 // Mac OS X and is required for IOSurface interoperability.
314 GLint previous_texture_id = 0;
316 GLenum texture_target = GL_TEXTURE_RECTANGLE_ARB;
317 GLenum texture_binding_target = GL_TEXTURE_BINDING_RECTANGLE_ARB;
318 // However, the remote core animation path on the core profile will
319 // be the preferred combination going forward.
320 if (gfx::GetGLImplementation() ==
321 gfx::kGLImplementationDesktopGLCoreProfile &&
322 ui::RemoteLayerAPISupported()) {
323 texture_target = GL_TEXTURE_2D;
324 texture_binding_target = GL_TEXTURE_BINDING_2D;
327 glGetIntegerv(texture_binding_target, &previous_texture_id);
329 // Free the old IO Surface first to reduce memory fragmentation.
330 DestroyFramebuffer();
332 glGenFramebuffersEXT(1, &fbo_id_);
333 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_id_);
335 glGenTextures(1, &texture_id_);
337 glBindTexture(texture_target, texture_id_);
338 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
339 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
340 glTexParameteri(texture_target,
341 GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
342 glTexParameteri(texture_target,
343 GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
345 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
346 GL_COLOR_ATTACHMENT0_EXT,
351 // Search through the provided attributes; if the caller has
352 // requested a stencil buffer, try to get one.
355 helper_->stub()->GetRequestedAttribute(EGL_STENCIL_SIZE);
356 if (stencil_bits > 0) {
357 // Create and bind the stencil buffer
358 bool has_packed_depth_stencil =
359 GLSurface::ExtensionsContain(
360 reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)),
361 "GL_EXT_packed_depth_stencil");
363 if (has_packed_depth_stencil) {
364 glGenRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_);
365 glBindRenderbufferEXT(GL_RENDERBUFFER_EXT,
366 depth_stencil_renderbuffer_id_);
367 glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT,
368 rounded_pixel_size_.width(),
369 rounded_pixel_size_.height());
370 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
371 GL_STENCIL_ATTACHMENT_EXT,
373 depth_stencil_renderbuffer_id_);
376 // If we asked for stencil but the extension isn't present,
377 // it's OK to silently fail; subsequent code will/must check
378 // for the presence of a stencil buffer before attempting to
379 // do stencil-based operations.
382 bool allocated_color_buffer = storage_provider_->AllocateColorBufferStorage(
383 static_cast<CGLContextObj>(
384 context_->GetHandle()),
385 context_->GetStateWasDirtiedExternallyCallback(),
386 texture_id_, rounded_pixel_size_, scale_factor_);
387 if (!allocated_color_buffer) {
388 DLOG(ERROR) << "Failed to allocate color buffer storage.";
389 DestroyFramebuffer();
393 GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
394 if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
395 DLOG(ERROR) << "Framebuffer was incomplete: " << status;
396 DestroyFramebuffer();
400 has_complete_framebuffer_ = true;
402 glBindTexture(texture_target, previous_texture_id);
403 // The FBO remains bound for this GL context.
406 } // namespace content