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. 4 5#include "content/common/gpu/image_transport_surface_fbo_mac.h" 6 7#include "content/common/gpu/gpu_messages.h" 8#include "content/common/gpu/image_transport_surface_iosurface_mac.h" 9#include "ui/gfx/native_widget_types.h" 10#include "ui/gl/gl_context.h" 11#include "ui/gl/gl_implementation.h" 12#include "ui/gl/gl_surface_osmesa.h" 13 14namespace content { 15 16ImageTransportSurfaceFBO::ImageTransportSurfaceFBO( 17 StorageProvider* storage_provider, 18 GpuChannelManager* manager, 19 GpuCommandBufferStub* stub, 20 gfx::PluginWindowHandle handle) 21 : storage_provider_(storage_provider), 22 backbuffer_suggested_allocation_(true), 23 frontbuffer_suggested_allocation_(true), 24 fbo_id_(0), 25 texture_id_(0), 26 depth_stencil_renderbuffer_id_(0), 27 has_complete_framebuffer_(false), 28 context_(NULL), 29 scale_factor_(1.f), 30 made_current_(false), 31 is_swap_buffers_pending_(false), 32 did_unschedule_(false) { 33 helper_.reset(new ImageTransportHelper(this, manager, stub, handle)); 34} 35 36ImageTransportSurfaceFBO::~ImageTransportSurfaceFBO() { 37} 38 39bool ImageTransportSurfaceFBO::Initialize() { 40 // Only support IOSurfaces if the GL implementation is the native desktop GL. 41 // IO surfaces will not work with, for example, OSMesa software renderer 42 // GL contexts. 43 if (gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL && 44 gfx::GetGLImplementation() != gfx::kGLImplementationAppleGL) 45 return false; 46 47 if (!helper_->Initialize()) 48 return false; 49 50 helper_->stub()->AddDestructionObserver(this); 51 return true; 52} 53 54void ImageTransportSurfaceFBO::Destroy() { 55 DestroyFramebuffer(); 56 57 helper_->Destroy(); 58} 59 60bool ImageTransportSurfaceFBO::DeferDraws() { 61 // The command buffer hit a draw/clear command that could clobber the 62 // IOSurface in use by an earlier SwapBuffers. If a Swap is pending, abort 63 // processing of the command by returning true and unschedule until the Swap 64 // Ack arrives. 65 if(did_unschedule_) 66 return true; // Still unscheduled, so just return true. 67 if (is_swap_buffers_pending_) { 68 did_unschedule_ = true; 69 helper_->SetScheduled(false); 70 return true; 71 } 72 return false; 73} 74 75bool ImageTransportSurfaceFBO::IsOffscreen() { 76 return false; 77} 78 79bool ImageTransportSurfaceFBO::OnMakeCurrent(gfx::GLContext* context) { 80 context_ = context; 81 82 if (made_current_) 83 return true; 84 85 OnResize(gfx::Size(1, 1), 1.f); 86 87 made_current_ = true; 88 return true; 89} 90 91unsigned int ImageTransportSurfaceFBO::GetBackingFrameBufferObject() { 92 return fbo_id_; 93} 94 95bool ImageTransportSurfaceFBO::SetBackbufferAllocation(bool allocation) { 96 if (backbuffer_suggested_allocation_ == allocation) 97 return true; 98 backbuffer_suggested_allocation_ = allocation; 99 AdjustBufferAllocation(); 100 return true; 101} 102 103void ImageTransportSurfaceFBO::SetFrontbufferAllocation(bool allocation) { 104 if (frontbuffer_suggested_allocation_ == allocation) 105 return; 106 frontbuffer_suggested_allocation_ = allocation; 107 AdjustBufferAllocation(); 108} 109 110void ImageTransportSurfaceFBO::AdjustBufferAllocation() { 111 // On mac, the frontbuffer and backbuffer are the same buffer. The buffer is 112 // free'd when both the browser and gpu processes have Unref'd the IOSurface. 113 if (!backbuffer_suggested_allocation_ && 114 !frontbuffer_suggested_allocation_ && 115 has_complete_framebuffer_) { 116 DestroyFramebuffer(); 117 helper_->Suspend(); 118 } else if (backbuffer_suggested_allocation_ && !has_complete_framebuffer_) { 119 CreateFramebuffer(); 120 } 121} 122 123bool ImageTransportSurfaceFBO::SwapBuffers() { 124 DCHECK(backbuffer_suggested_allocation_); 125 if (!frontbuffer_suggested_allocation_) 126 return true; 127 glFlush(); 128 129 GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params; 130 params.surface_handle = storage_provider_->GetSurfaceHandle(); 131 params.size = GetSize(); 132 params.scale_factor = scale_factor_; 133 params.latency_info.swap(latency_info_); 134 helper_->SendAcceleratedSurfaceBuffersSwapped(params); 135 136 DCHECK(!is_swap_buffers_pending_); 137 is_swap_buffers_pending_ = true; 138 return true; 139} 140 141bool ImageTransportSurfaceFBO::PostSubBuffer( 142 int x, int y, int width, int height) { 143 DCHECK(backbuffer_suggested_allocation_); 144 if (!frontbuffer_suggested_allocation_) 145 return true; 146 glFlush(); 147 148 GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params params; 149 params.surface_handle = storage_provider_->GetSurfaceHandle(); 150 params.x = x; 151 params.y = y; 152 params.width = width; 153 params.height = height; 154 params.surface_size = GetSize(); 155 params.surface_scale_factor = scale_factor_; 156 params.latency_info.swap(latency_info_); 157 helper_->SendAcceleratedSurfacePostSubBuffer(params); 158 159 DCHECK(!is_swap_buffers_pending_); 160 is_swap_buffers_pending_ = true; 161 return true; 162} 163 164bool ImageTransportSurfaceFBO::SupportsPostSubBuffer() { 165 return true; 166} 167 168gfx::Size ImageTransportSurfaceFBO::GetSize() { 169 return size_; 170} 171 172void* ImageTransportSurfaceFBO::GetHandle() { 173 return NULL; 174} 175 176void* ImageTransportSurfaceFBO::GetDisplay() { 177 return NULL; 178} 179 180void ImageTransportSurfaceFBO::OnBufferPresented( 181 const AcceleratedSurfaceMsg_BufferPresented_Params& params) { 182 DCHECK(is_swap_buffers_pending_); 183 184 context_->share_group()->SetRendererID(params.renderer_id); 185 is_swap_buffers_pending_ = false; 186 if (did_unschedule_) { 187 did_unschedule_ = false; 188 helper_->SetScheduled(true); 189 } 190} 191 192void ImageTransportSurfaceFBO::OnResize(gfx::Size size, 193 float scale_factor) { 194 // This trace event is used in gpu_feature_browsertest.cc - the test will need 195 // to be updated if this event is changed or moved. 196 TRACE_EVENT2("gpu", "ImageTransportSurfaceFBO::OnResize", 197 "old_width", size_.width(), "new_width", size.width()); 198 // Caching |context_| from OnMakeCurrent. It should still be current. 199 DCHECK(context_->IsCurrent(this)); 200 201 size_ = size; 202 scale_factor_ = scale_factor; 203 204 CreateFramebuffer(); 205} 206 207void ImageTransportSurfaceFBO::SetLatencyInfo( 208 const std::vector<ui::LatencyInfo>& latency_info) { 209 for (size_t i = 0; i < latency_info.size(); i++) 210 latency_info_.push_back(latency_info[i]); 211} 212 213void ImageTransportSurfaceFBO::WakeUpGpu() { 214 NOTIMPLEMENTED(); 215} 216 217void ImageTransportSurfaceFBO::OnWillDestroyStub() { 218 helper_->stub()->RemoveDestructionObserver(this); 219 Destroy(); 220} 221 222void ImageTransportSurfaceFBO::DestroyFramebuffer() { 223 // If we have resources to destroy, then make sure that we have a current 224 // context which we can use to delete the resources. 225 if (context_ || fbo_id_ || texture_id_ || depth_stencil_renderbuffer_id_) { 226 DCHECK(gfx::GLContext::GetCurrent() == context_); 227 DCHECK(context_->IsCurrent(this)); 228 DCHECK(CGLGetCurrentContext()); 229 } 230 231 if (fbo_id_) { 232 glDeleteFramebuffersEXT(1, &fbo_id_); 233 fbo_id_ = 0; 234 } 235 236 if (texture_id_) { 237 glDeleteTextures(1, &texture_id_); 238 texture_id_ = 0; 239 } 240 241 if (depth_stencil_renderbuffer_id_) { 242 glDeleteRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_); 243 depth_stencil_renderbuffer_id_ = 0; 244 } 245 246 storage_provider_->FreeColorBufferStorage(); 247 248 has_complete_framebuffer_ = false; 249} 250 251void ImageTransportSurfaceFBO::CreateFramebuffer() { 252 gfx::Size new_rounded_size = storage_provider_->GetRoundedSize(size_); 253 254 // Only recreate surface when the rounded up size has changed. 255 if (has_complete_framebuffer_ && new_rounded_size == rounded_size_) 256 return; 257 258 // This trace event is used in gpu_feature_browsertest.cc - the test will need 259 // to be updated if this event is changed or moved. 260 TRACE_EVENT2("gpu", "ImageTransportSurfaceFBO::CreateFramebuffer", 261 "width", new_rounded_size.width(), 262 "height", new_rounded_size.height()); 263 264 rounded_size_ = new_rounded_size; 265 266 // GL_TEXTURE_RECTANGLE_ARB is the best supported render target on 267 // Mac OS X and is required for IOSurface interoperability. 268 GLint previous_texture_id = 0; 269 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &previous_texture_id); 270 271 // Free the old IO Surface first to reduce memory fragmentation. 272 DestroyFramebuffer(); 273 274 glGenFramebuffersEXT(1, &fbo_id_); 275 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_id_); 276 277 glGenTextures(1, &texture_id_); 278 279 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_id_); 280 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 281 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 282 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, 283 GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 284 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, 285 GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 286 287 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, 288 GL_COLOR_ATTACHMENT0_EXT, 289 GL_TEXTURE_RECTANGLE_ARB, 290 texture_id_, 291 0); 292 293 // Search through the provided attributes; if the caller has 294 // requested a stencil buffer, try to get one. 295 296 int32 stencil_bits = 297 helper_->stub()->GetRequestedAttribute(EGL_STENCIL_SIZE); 298 if (stencil_bits > 0) { 299 // Create and bind the stencil buffer 300 bool has_packed_depth_stencil = 301 GLSurface::ExtensionsContain( 302 reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)), 303 "GL_EXT_packed_depth_stencil"); 304 305 if (has_packed_depth_stencil) { 306 glGenRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_); 307 glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 308 depth_stencil_renderbuffer_id_); 309 glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, 310 rounded_size_.width(), rounded_size_.height()); 311 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, 312 GL_STENCIL_ATTACHMENT_EXT, 313 GL_RENDERBUFFER_EXT, 314 depth_stencil_renderbuffer_id_); 315 } 316 317 // If we asked for stencil but the extension isn't present, 318 // it's OK to silently fail; subsequent code will/must check 319 // for the presence of a stencil buffer before attempting to 320 // do stencil-based operations. 321 } 322 323 bool allocated_color_buffer = storage_provider_->AllocateColorBufferStorage( 324 static_cast<CGLContextObj>(context_->GetHandle()), 325 rounded_size_); 326 if (!allocated_color_buffer) { 327 DLOG(ERROR) << "Failed to allocate color buffer storage."; 328 DestroyFramebuffer(); 329 return; 330 } 331 332 GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); 333 if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { 334 DLOG(ERROR) << "Framebuffer was incomplete: " << status; 335 DestroyFramebuffer(); 336 return; 337 } 338 339 has_complete_framebuffer_ = true; 340 341 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, previous_texture_id); 342 // The FBO remains bound for this GL context. 343} 344 345} // namespace content 346