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() {
63 bool ImageTransportSurfaceFBO::DeferDraws() {
64 storage_provider_->WillWriteToBackbuffer();
65 // We should not have a pending send when we are drawing the next frame.
66 DCHECK(!is_swap_buffers_send_pending_);
70 bool ImageTransportSurfaceFBO::IsOffscreen() {
74 bool ImageTransportSurfaceFBO::OnMakeCurrent(gfx::GLContext* context) {
80 OnResize(gfx::Size(1, 1), 1.f);
86 unsigned int ImageTransportSurfaceFBO::GetBackingFrameBufferObject() {
90 bool ImageTransportSurfaceFBO::SetBackbufferAllocation(bool allocation) {
91 if (backbuffer_suggested_allocation_ == allocation)
93 backbuffer_suggested_allocation_ = allocation;
94 AdjustBufferAllocation();
96 storage_provider_->DiscardBackbuffer();
100 void ImageTransportSurfaceFBO::SetFrontbufferAllocation(bool allocation) {
101 if (frontbuffer_suggested_allocation_ == allocation)
103 frontbuffer_suggested_allocation_ = allocation;
104 AdjustBufferAllocation();
107 void ImageTransportSurfaceFBO::AdjustBufferAllocation() {
108 // On mac, the frontbuffer and backbuffer are the same buffer. The buffer is
109 // free'd when both the browser and gpu processes have Unref'd the IOSurface.
110 if (!backbuffer_suggested_allocation_ &&
111 !frontbuffer_suggested_allocation_ &&
112 has_complete_framebuffer_) {
113 DestroyFramebuffer();
115 } else if (backbuffer_suggested_allocation_ && !has_complete_framebuffer_) {
120 bool ImageTransportSurfaceFBO::SwapBuffers() {
121 DCHECK(backbuffer_suggested_allocation_);
122 if (!frontbuffer_suggested_allocation_)
126 // It is the responsibility of the storage provider to send the swap IPC.
127 is_swap_buffers_send_pending_ = true;
128 storage_provider_->SwapBuffers(size_, scale_factor_);
132 void ImageTransportSurfaceFBO::SendSwapBuffers(uint64 surface_handle,
133 const gfx::Size pixel_size,
134 float scale_factor) {
135 GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params;
136 params.surface_handle = surface_handle;
137 params.size = pixel_size;
138 params.scale_factor = scale_factor;
139 params.latency_info.swap(latency_info_);
140 helper_->SendAcceleratedSurfaceBuffersSwapped(params);
141 is_swap_buffers_send_pending_ = false;
144 bool ImageTransportSurfaceFBO::PostSubBuffer(
145 int x, int y, int width, int height) {
146 // Mac does not support sub-buffer swaps.
151 bool ImageTransportSurfaceFBO::SupportsPostSubBuffer() {
155 gfx::Size ImageTransportSurfaceFBO::GetSize() {
159 void* ImageTransportSurfaceFBO::GetHandle() {
163 void* ImageTransportSurfaceFBO::GetDisplay() {
167 void ImageTransportSurfaceFBO::OnBufferPresented(
168 const AcceleratedSurfaceMsg_BufferPresented_Params& params) {
169 context_->share_group()->SetRendererID(params.renderer_id);
170 storage_provider_->SwapBuffersAckedByBrowser();
173 void ImageTransportSurfaceFBO::OnResize(gfx::Size size,
174 float scale_factor) {
175 TRACE_EVENT2("gpu", "ImageTransportSurfaceFBO::OnResize",
176 "old_width", size_.width(), "new_width", size.width());
177 // Caching |context_| from OnMakeCurrent. It should still be current.
178 DCHECK(context_->IsCurrent(this));
181 scale_factor_ = 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::CreateFramebuffer() {
231 gfx::Size new_rounded_size = storage_provider_->GetRoundedSize(size_);
233 // Only recreate surface when the rounded up size has changed.
234 if (has_complete_framebuffer_ && new_rounded_size == rounded_size_)
237 TRACE_EVENT2("gpu", "ImageTransportSurfaceFBO::CreateFramebuffer",
238 "width", new_rounded_size.width(),
239 "height", new_rounded_size.height());
241 rounded_size_ = new_rounded_size;
243 // GL_TEXTURE_RECTANGLE_ARB is the best supported render target on
244 // Mac OS X and is required for IOSurface interoperability.
245 GLint previous_texture_id = 0;
246 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &previous_texture_id);
248 // Free the old IO Surface first to reduce memory fragmentation.
249 DestroyFramebuffer();
251 glGenFramebuffersEXT(1, &fbo_id_);
252 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_id_);
254 glGenTextures(1, &texture_id_);
256 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_id_);
257 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
258 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
259 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,
260 GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
261 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,
262 GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
264 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
265 GL_COLOR_ATTACHMENT0_EXT,
266 GL_TEXTURE_RECTANGLE_ARB,
270 // Search through the provided attributes; if the caller has
271 // requested a stencil buffer, try to get one.
274 helper_->stub()->GetRequestedAttribute(EGL_STENCIL_SIZE);
275 if (stencil_bits > 0) {
276 // Create and bind the stencil buffer
277 bool has_packed_depth_stencil =
278 GLSurface::ExtensionsContain(
279 reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)),
280 "GL_EXT_packed_depth_stencil");
282 if (has_packed_depth_stencil) {
283 glGenRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_);
284 glBindRenderbufferEXT(GL_RENDERBUFFER_EXT,
285 depth_stencil_renderbuffer_id_);
286 glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT,
287 rounded_size_.width(), rounded_size_.height());
288 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
289 GL_STENCIL_ATTACHMENT_EXT,
291 depth_stencil_renderbuffer_id_);
294 // If we asked for stencil but the extension isn't present,
295 // it's OK to silently fail; subsequent code will/must check
296 // for the presence of a stencil buffer before attempting to
297 // do stencil-based operations.
300 bool allocated_color_buffer = storage_provider_->AllocateColorBufferStorage(
301 static_cast<CGLContextObj>(context_->GetHandle()), texture_id_,
302 rounded_size_, scale_factor_);
303 if (!allocated_color_buffer) {
304 DLOG(ERROR) << "Failed to allocate color buffer storage.";
305 DestroyFramebuffer();
309 GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
310 if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
311 DLOG(ERROR) << "Framebuffer was incomplete: " << status;
312 DestroyFramebuffer();
316 has_complete_framebuffer_ = true;
318 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, previous_texture_id);
319 // The FBO remains bound for this GL context.
322 } // namespace content