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 ImageTransportSurfaceFBO::ImageTransportSurfaceFBO(
19 GpuChannelManager* manager,
20 GpuCommandBufferStub* stub,
21 gfx::PluginWindowHandle handle)
22 : backbuffer_suggested_allocation_(true),
23 frontbuffer_suggested_allocation_(true),
26 depth_stencil_renderbuffer_id_(0),
27 has_complete_framebuffer_(false),
31 is_swap_buffers_send_pending_(false) {
32 if (ui::RemoteLayerAPISupported())
33 storage_provider_.reset(new CALayerStorageProvider(this));
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
46 if (gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL &&
47 gfx::GetGLImplementation() != gfx::kGLImplementationAppleGL)
50 if (!helper_->Initialize())
53 helper_->stub()->AddDestructionObserver(this);
57 void ImageTransportSurfaceFBO::Destroy() {
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_);
68 bool ImageTransportSurfaceFBO::IsOffscreen() {
72 bool ImageTransportSurfaceFBO::OnMakeCurrent(gfx::GLContext* context) {
78 AllocateOrResizeFramebuffer(gfx::Size(1, 1), 1.f);
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
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)
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)
111 // Issue the dummy call and then restore the state.
113 GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
114 DCHECK(status == GL_FRAMEBUFFER_COMPLETE);
115 glBegin(GL_TRIANGLES);
117 glUseProgram(old_program);
120 unsigned int ImageTransportSurfaceFBO::GetBackingFrameBufferObject() {
124 bool ImageTransportSurfaceFBO::SetBackbufferAllocation(bool allocation) {
125 if (backbuffer_suggested_allocation_ == allocation)
127 backbuffer_suggested_allocation_ = allocation;
128 AdjustBufferAllocation();
130 storage_provider_->DiscardBackbuffer();
134 void ImageTransportSurfaceFBO::SetFrontbufferAllocation(bool allocation) {
135 if (frontbuffer_suggested_allocation_ == allocation)
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_);
153 bool ImageTransportSurfaceFBO::SwapBuffers() {
154 DCHECK(backbuffer_suggested_allocation_);
155 if (!frontbuffer_suggested_allocation_)
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_);
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) {
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.
189 bool ImageTransportSurfaceFBO::SupportsPostSubBuffer() {
193 gfx::Size ImageTransportSurfaceFBO::GetSize() {
197 void* ImageTransportSurfaceFBO::GetHandle() {
201 void* ImageTransportSurfaceFBO::GetDisplay() {
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() {
232 void ImageTransportSurfaceFBO::OnWillDestroyStub() {
233 helper_->stub()->RemoveDestructionObserver(this);
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());
247 glDeleteFramebuffersEXT(1, &fbo_id_);
252 glDeleteTextures(1, &texture_id_);
256 if (depth_stencil_renderbuffer_id_) {
257 glDeleteRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_);
258 depth_stencil_renderbuffer_id_ = 0;
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)
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,
317 // Search through the provided attributes; if the caller has
318 // requested a stencil buffer, try to get one.
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,
339 depth_stencil_renderbuffer_id_);
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.
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();
357 GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
358 if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
359 DLOG(ERROR) << "Framebuffer was incomplete: " << status;
360 DestroyFramebuffer();
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