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"
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),
34 depth_stencil_renderbuffer_id_(0),
35 has_complete_framebuffer_(false),
39 is_swap_buffers_send_pending_(false) {
40 if (ui::RemoteLayerAPISupported())
41 storage_provider_.reset(new CALayerStorageProvider(this));
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
54 if (gfx::GetGLImplementation() !=
55 gfx::kGLImplementationDesktopGLCoreProfile &&
56 gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL &&
57 gfx::GetGLImplementation() != gfx::kGLImplementationAppleGL)
60 if (!helper_->Initialize())
63 helper_->stub()->AddDestructionObserver(this);
67 void ImageTransportSurfaceFBO::Destroy() {
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();
82 bool ImageTransportSurfaceFBO::IsOffscreen() {
86 bool ImageTransportSurfaceFBO::OnMakeCurrent(gfx::GLContext* context) {
92 AllocateOrResizeFramebuffer(gfx::Size(1, 1), 1.f);
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
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)
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)
125 // Issue the dummy call and then restore the state.
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
133 glBegin(GL_TRIANGLES);
136 glUseProgram(old_program);
139 unsigned int ImageTransportSurfaceFBO::GetBackingFrameBufferObject() {
143 bool ImageTransportSurfaceFBO::SetBackbufferAllocation(bool allocation) {
144 if (backbuffer_suggested_allocation_ == allocation)
146 backbuffer_suggested_allocation_ = allocation;
147 AdjustBufferAllocation();
149 storage_provider_->DiscardBackbuffer();
153 void ImageTransportSurfaceFBO::SetFrontbufferAllocation(bool allocation) {
154 if (frontbuffer_suggested_allocation_ == allocation)
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_);
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_)
186 TRACE_EVENT0("gpu", "ImageTransportSurfaceFBO::glFlush");
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();
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) {
217 context_->share_group()->SetRendererID(renderer_id);
220 const gpu::gles2::FeatureInfo* ImageTransportSurfaceFBO::GetFeatureInfo()
222 return helper_->stub()->GetFeatureInfo();
225 gfx::SwapResult ImageTransportSurfaceFBO::PostSubBuffer(int x,
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() {
239 gfx::Size ImageTransportSurfaceFBO::GetSize() {
243 void* ImageTransportSurfaceFBO::GetHandle() {
247 void* ImageTransportSurfaceFBO::GetDisplay() {
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() {
279 void ImageTransportSurfaceFBO::OnWillDestroyStub() {
280 helper_->stub()->RemoveDestructionObserver(this);
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());
294 glDeleteFramebuffersEXT(1, &fbo_id_);
299 glDeleteTextures(1, &texture_id_);
303 if (depth_stencil_renderbuffer_id_) {
304 glDeleteRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_);
305 depth_stencil_renderbuffer_id_ = 0;
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_);
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;
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,
378 // Search through the provided attributes; if the caller has
379 // requested a stencil buffer, try to get one.
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,
400 depth_stencil_renderbuffer_id_);
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.
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();
420 GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
421 if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
422 DLOG(ERROR) << "Framebuffer was incomplete: " << status;
423 DestroyFramebuffer();
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