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 unsigned int ImageTransportSurfaceFBO::GetBackingFrameBufferObject() {
88 bool ImageTransportSurfaceFBO::SetBackbufferAllocation(bool allocation) {
89 if (backbuffer_suggested_allocation_ == allocation)
91 backbuffer_suggested_allocation_ = allocation;
92 AdjustBufferAllocation();
94 storage_provider_->DiscardBackbuffer();
98 void ImageTransportSurfaceFBO::SetFrontbufferAllocation(bool allocation) {
99 if (frontbuffer_suggested_allocation_ == allocation)
101 frontbuffer_suggested_allocation_ = allocation;
102 AdjustBufferAllocation();
105 void ImageTransportSurfaceFBO::AdjustBufferAllocation() {
106 // On mac, the frontbuffer and backbuffer are the same buffer. The buffer is
107 // free'd when both the browser and gpu processes have Unref'd the IOSurface.
108 if (!backbuffer_suggested_allocation_ &&
109 !frontbuffer_suggested_allocation_ &&
110 has_complete_framebuffer_) {
111 DestroyFramebuffer();
112 } else if (backbuffer_suggested_allocation_ && !has_complete_framebuffer_) {
113 AllocateOrResizeFramebuffer(pixel_size_, scale_factor_);
117 bool ImageTransportSurfaceFBO::SwapBuffers() {
118 DCHECK(backbuffer_suggested_allocation_);
119 if (!frontbuffer_suggested_allocation_)
123 // It is the responsibility of the storage provider to send the swap IPC.
124 is_swap_buffers_send_pending_ = true;
125 storage_provider_->SwapBuffers(pixel_size_, scale_factor_);
129 void ImageTransportSurfaceFBO::SendSwapBuffers(uint64 surface_handle,
130 const gfx::Size pixel_size,
131 float scale_factor) {
132 GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params;
133 params.surface_handle = surface_handle;
134 params.size = pixel_size;
135 params.scale_factor = scale_factor;
136 params.latency_info.swap(latency_info_);
137 helper_->SendAcceleratedSurfaceBuffersSwapped(params);
138 is_swap_buffers_send_pending_ = false;
141 void ImageTransportSurfaceFBO::SetRendererID(int renderer_id) {
143 context_->share_group()->SetRendererID(renderer_id);
146 bool ImageTransportSurfaceFBO::PostSubBuffer(
147 int x, int y, int width, int height) {
148 // Mac does not support sub-buffer swaps.
153 bool ImageTransportSurfaceFBO::SupportsPostSubBuffer() {
157 gfx::Size ImageTransportSurfaceFBO::GetSize() {
161 void* ImageTransportSurfaceFBO::GetHandle() {
165 void* ImageTransportSurfaceFBO::GetDisplay() {
169 void ImageTransportSurfaceFBO::OnBufferPresented(
170 const AcceleratedSurfaceMsg_BufferPresented_Params& params) {
171 SetRendererID(params.renderer_id);
172 storage_provider_->SwapBuffersAckedByBrowser(params.disable_throttling);
175 void ImageTransportSurfaceFBO::OnResize(gfx::Size pixel_size,
176 float scale_factor) {
177 TRACE_EVENT2("gpu", "ImageTransportSurfaceFBO::OnResize",
178 "old_size", pixel_size_.ToString(),
179 "new_size", pixel_size.ToString());
180 // Caching |context_| from OnMakeCurrent. It should still be current.
181 DCHECK(context_->IsCurrent(this));
183 AllocateOrResizeFramebuffer(pixel_size, scale_factor);
186 void ImageTransportSurfaceFBO::SetLatencyInfo(
187 const std::vector<ui::LatencyInfo>& latency_info) {
188 for (size_t i = 0; i < latency_info.size(); i++)
189 latency_info_.push_back(latency_info[i]);
192 void ImageTransportSurfaceFBO::WakeUpGpu() {
196 void ImageTransportSurfaceFBO::OnWillDestroyStub() {
197 helper_->stub()->RemoveDestructionObserver(this);
201 void ImageTransportSurfaceFBO::DestroyFramebuffer() {
202 // If we have resources to destroy, then make sure that we have a current
203 // context which we can use to delete the resources.
204 if (context_ || fbo_id_ || texture_id_ || depth_stencil_renderbuffer_id_) {
205 DCHECK(gfx::GLContext::GetCurrent() == context_);
206 DCHECK(context_->IsCurrent(this));
207 DCHECK(CGLGetCurrentContext());
211 glDeleteFramebuffersEXT(1, &fbo_id_);
216 glDeleteTextures(1, &texture_id_);
220 if (depth_stencil_renderbuffer_id_) {
221 glDeleteRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_);
222 depth_stencil_renderbuffer_id_ = 0;
225 storage_provider_->FreeColorBufferStorage();
227 has_complete_framebuffer_ = false;
230 void ImageTransportSurfaceFBO::AllocateOrResizeFramebuffer(
231 const gfx::Size& new_pixel_size, float new_scale_factor) {
232 gfx::Size new_rounded_pixel_size =
233 storage_provider_->GetRoundedSize(new_pixel_size);
235 // Only recreate the surface's storage when the rounded up size has changed,
236 // or the scale factor has changed.
237 bool needs_new_storage =
238 !has_complete_framebuffer_ ||
239 new_rounded_pixel_size != rounded_pixel_size_ ||
240 new_scale_factor != scale_factor_;
242 // Save the new storage parameters.
243 pixel_size_ = new_pixel_size;
244 rounded_pixel_size_ = new_rounded_pixel_size;
245 scale_factor_ = new_scale_factor;
247 if (!needs_new_storage)
250 TRACE_EVENT2("gpu", "ImageTransportSurfaceFBO::AllocateOrResizeFramebuffer",
251 "width", new_rounded_pixel_size.width(),
252 "height", new_rounded_pixel_size.height());
254 // GL_TEXTURE_RECTANGLE_ARB is the best supported render target on
255 // Mac OS X and is required for IOSurface interoperability.
256 GLint previous_texture_id = 0;
257 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &previous_texture_id);
259 // Free the old IO Surface first to reduce memory fragmentation.
260 DestroyFramebuffer();
262 glGenFramebuffersEXT(1, &fbo_id_);
263 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_id_);
265 glGenTextures(1, &texture_id_);
267 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_id_);
268 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
269 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
270 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,
271 GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
272 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,
273 GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
275 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
276 GL_COLOR_ATTACHMENT0_EXT,
277 GL_TEXTURE_RECTANGLE_ARB,
281 // Search through the provided attributes; if the caller has
282 // requested a stencil buffer, try to get one.
285 helper_->stub()->GetRequestedAttribute(EGL_STENCIL_SIZE);
286 if (stencil_bits > 0) {
287 // Create and bind the stencil buffer
288 bool has_packed_depth_stencil =
289 GLSurface::ExtensionsContain(
290 reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)),
291 "GL_EXT_packed_depth_stencil");
293 if (has_packed_depth_stencil) {
294 glGenRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_);
295 glBindRenderbufferEXT(GL_RENDERBUFFER_EXT,
296 depth_stencil_renderbuffer_id_);
297 glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT,
298 rounded_pixel_size_.width(),
299 rounded_pixel_size_.height());
300 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
301 GL_STENCIL_ATTACHMENT_EXT,
303 depth_stencil_renderbuffer_id_);
306 // If we asked for stencil but the extension isn't present,
307 // it's OK to silently fail; subsequent code will/must check
308 // for the presence of a stencil buffer before attempting to
309 // do stencil-based operations.
312 bool allocated_color_buffer = storage_provider_->AllocateColorBufferStorage(
313 static_cast<CGLContextObj>(context_->GetHandle()), texture_id_,
314 rounded_pixel_size_, scale_factor_);
315 if (!allocated_color_buffer) {
316 DLOG(ERROR) << "Failed to allocate color buffer storage.";
317 DestroyFramebuffer();
321 GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
322 if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
323 DLOG(ERROR) << "Framebuffer was incomplete: " << status;
324 DestroyFramebuffer();
328 has_complete_framebuffer_ = true;
330 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, previous_texture_id);
331 // The FBO remains bound for this GL context.
334 } // namespace content