| OLD | NEW |
| (Empty) |
| 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 | |
| 14 namespace content { | |
| 15 | |
| 16 ImageTransportSurfaceFBO::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 | |
| 36 ImageTransportSurfaceFBO::~ImageTransportSurfaceFBO() { | |
| 37 } | |
| 38 | |
| 39 bool 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 | |
| 54 void ImageTransportSurfaceFBO::Destroy() { | |
| 55 DestroyFramebuffer(); | |
| 56 | |
| 57 helper_->Destroy(); | |
| 58 } | |
| 59 | |
| 60 bool 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 | |
| 75 bool ImageTransportSurfaceFBO::IsOffscreen() { | |
| 76 return false; | |
| 77 } | |
| 78 | |
| 79 bool 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 | |
| 91 unsigned int ImageTransportSurfaceFBO::GetBackingFrameBufferObject() { | |
| 92 return fbo_id_; | |
| 93 } | |
| 94 | |
| 95 bool 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 | |
| 103 void ImageTransportSurfaceFBO::SetFrontbufferAllocation(bool allocation) { | |
| 104 if (frontbuffer_suggested_allocation_ == allocation) | |
| 105 return; | |
| 106 frontbuffer_suggested_allocation_ = allocation; | |
| 107 AdjustBufferAllocation(); | |
| 108 } | |
| 109 | |
| 110 void 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 | |
| 123 bool 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 | |
| 139 storage_provider_->WillSwapBuffers(); | |
| 140 return true; | |
| 141 } | |
| 142 | |
| 143 bool ImageTransportSurfaceFBO::PostSubBuffer( | |
| 144 int x, int y, int width, int height) { | |
| 145 DCHECK(backbuffer_suggested_allocation_); | |
| 146 if (!frontbuffer_suggested_allocation_) | |
| 147 return true; | |
| 148 glFlush(); | |
| 149 | |
| 150 GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params params; | |
| 151 params.surface_handle = storage_provider_->GetSurfaceHandle(); | |
| 152 params.x = x; | |
| 153 params.y = y; | |
| 154 params.width = width; | |
| 155 params.height = height; | |
| 156 params.surface_size = GetSize(); | |
| 157 params.surface_scale_factor = scale_factor_; | |
| 158 params.latency_info.swap(latency_info_); | |
| 159 helper_->SendAcceleratedSurfacePostSubBuffer(params); | |
| 160 | |
| 161 DCHECK(!is_swap_buffers_pending_); | |
| 162 is_swap_buffers_pending_ = true; | |
| 163 | |
| 164 storage_provider_->WillSwapBuffers(); | |
| 165 return true; | |
| 166 } | |
| 167 | |
| 168 bool ImageTransportSurfaceFBO::SupportsPostSubBuffer() { | |
| 169 return true; | |
| 170 } | |
| 171 | |
| 172 gfx::Size ImageTransportSurfaceFBO::GetSize() { | |
| 173 return size_; | |
| 174 } | |
| 175 | |
| 176 void* ImageTransportSurfaceFBO::GetHandle() { | |
| 177 return NULL; | |
| 178 } | |
| 179 | |
| 180 void* ImageTransportSurfaceFBO::GetDisplay() { | |
| 181 return NULL; | |
| 182 } | |
| 183 | |
| 184 void ImageTransportSurfaceFBO::OnBufferPresented( | |
| 185 const AcceleratedSurfaceMsg_BufferPresented_Params& params) { | |
| 186 DCHECK(is_swap_buffers_pending_); | |
| 187 | |
| 188 context_->share_group()->SetRendererID(params.renderer_id); | |
| 189 is_swap_buffers_pending_ = false; | |
| 190 if (did_unschedule_) { | |
| 191 did_unschedule_ = false; | |
| 192 helper_->SetScheduled(true); | |
| 193 } | |
| 194 } | |
| 195 | |
| 196 void ImageTransportSurfaceFBO::OnResize(gfx::Size size, | |
| 197 float scale_factor) { | |
| 198 // This trace event is used in gpu_feature_browsertest.cc - the test will need | |
| 199 // to be updated if this event is changed or moved. | |
| 200 TRACE_EVENT2("gpu", "ImageTransportSurfaceFBO::OnResize", | |
| 201 "old_width", size_.width(), "new_width", size.width()); | |
| 202 // Caching |context_| from OnMakeCurrent. It should still be current. | |
| 203 DCHECK(context_->IsCurrent(this)); | |
| 204 | |
| 205 size_ = size; | |
| 206 scale_factor_ = scale_factor; | |
| 207 | |
| 208 CreateFramebuffer(); | |
| 209 } | |
| 210 | |
| 211 void ImageTransportSurfaceFBO::SetLatencyInfo( | |
| 212 const std::vector<ui::LatencyInfo>& latency_info) { | |
| 213 for (size_t i = 0; i < latency_info.size(); i++) | |
| 214 latency_info_.push_back(latency_info[i]); | |
| 215 } | |
| 216 | |
| 217 void ImageTransportSurfaceFBO::WakeUpGpu() { | |
| 218 NOTIMPLEMENTED(); | |
| 219 } | |
| 220 | |
| 221 void ImageTransportSurfaceFBO::OnWillDestroyStub() { | |
| 222 helper_->stub()->RemoveDestructionObserver(this); | |
| 223 Destroy(); | |
| 224 } | |
| 225 | |
| 226 void ImageTransportSurfaceFBO::DestroyFramebuffer() { | |
| 227 // If we have resources to destroy, then make sure that we have a current | |
| 228 // context which we can use to delete the resources. | |
| 229 if (context_ || fbo_id_ || texture_id_ || depth_stencil_renderbuffer_id_) { | |
| 230 DCHECK(gfx::GLContext::GetCurrent() == context_); | |
| 231 DCHECK(context_->IsCurrent(this)); | |
| 232 DCHECK(CGLGetCurrentContext()); | |
| 233 } | |
| 234 | |
| 235 if (fbo_id_) { | |
| 236 glDeleteFramebuffersEXT(1, &fbo_id_); | |
| 237 fbo_id_ = 0; | |
| 238 } | |
| 239 | |
| 240 if (texture_id_) { | |
| 241 glDeleteTextures(1, &texture_id_); | |
| 242 texture_id_ = 0; | |
| 243 } | |
| 244 | |
| 245 if (depth_stencil_renderbuffer_id_) { | |
| 246 glDeleteRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_); | |
| 247 depth_stencil_renderbuffer_id_ = 0; | |
| 248 } | |
| 249 | |
| 250 storage_provider_->FreeColorBufferStorage(); | |
| 251 | |
| 252 has_complete_framebuffer_ = false; | |
| 253 } | |
| 254 | |
| 255 void ImageTransportSurfaceFBO::CreateFramebuffer() { | |
| 256 gfx::Size new_rounded_size = storage_provider_->GetRoundedSize(size_); | |
| 257 | |
| 258 // Only recreate surface when the rounded up size has changed. | |
| 259 if (has_complete_framebuffer_ && new_rounded_size == rounded_size_) | |
| 260 return; | |
| 261 | |
| 262 // This trace event is used in gpu_feature_browsertest.cc - the test will need | |
| 263 // to be updated if this event is changed or moved. | |
| 264 TRACE_EVENT2("gpu", "ImageTransportSurfaceFBO::CreateFramebuffer", | |
| 265 "width", new_rounded_size.width(), | |
| 266 "height", new_rounded_size.height()); | |
| 267 | |
| 268 rounded_size_ = new_rounded_size; | |
| 269 | |
| 270 // GL_TEXTURE_RECTANGLE_ARB is the best supported render target on | |
| 271 // Mac OS X and is required for IOSurface interoperability. | |
| 272 GLint previous_texture_id = 0; | |
| 273 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &previous_texture_id); | |
| 274 | |
| 275 // Free the old IO Surface first to reduce memory fragmentation. | |
| 276 DestroyFramebuffer(); | |
| 277 | |
| 278 glGenFramebuffersEXT(1, &fbo_id_); | |
| 279 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_id_); | |
| 280 | |
| 281 glGenTextures(1, &texture_id_); | |
| 282 | |
| 283 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_id_); | |
| 284 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
| 285 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
| 286 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, | |
| 287 GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
| 288 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, | |
| 289 GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
| 290 | |
| 291 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, | |
| 292 GL_COLOR_ATTACHMENT0_EXT, | |
| 293 GL_TEXTURE_RECTANGLE_ARB, | |
| 294 texture_id_, | |
| 295 0); | |
| 296 | |
| 297 // Search through the provided attributes; if the caller has | |
| 298 // requested a stencil buffer, try to get one. | |
| 299 | |
| 300 int32 stencil_bits = | |
| 301 helper_->stub()->GetRequestedAttribute(EGL_STENCIL_SIZE); | |
| 302 if (stencil_bits > 0) { | |
| 303 // Create and bind the stencil buffer | |
| 304 bool has_packed_depth_stencil = | |
| 305 GLSurface::ExtensionsContain( | |
| 306 reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)), | |
| 307 "GL_EXT_packed_depth_stencil"); | |
| 308 | |
| 309 if (has_packed_depth_stencil) { | |
| 310 glGenRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_); | |
| 311 glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, | |
| 312 depth_stencil_renderbuffer_id_); | |
| 313 glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, | |
| 314 rounded_size_.width(), rounded_size_.height()); | |
| 315 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, | |
| 316 GL_STENCIL_ATTACHMENT_EXT, | |
| 317 GL_RENDERBUFFER_EXT, | |
| 318 depth_stencil_renderbuffer_id_); | |
| 319 } | |
| 320 | |
| 321 // If we asked for stencil but the extension isn't present, | |
| 322 // it's OK to silently fail; subsequent code will/must check | |
| 323 // for the presence of a stencil buffer before attempting to | |
| 324 // do stencil-based operations. | |
| 325 } | |
| 326 | |
| 327 bool allocated_color_buffer = storage_provider_->AllocateColorBufferStorage( | |
| 328 static_cast<CGLContextObj>(context_->GetHandle()), texture_id_, | |
| 329 rounded_size_, scale_factor_); | |
| 330 if (!allocated_color_buffer) { | |
| 331 DLOG(ERROR) << "Failed to allocate color buffer storage."; | |
| 332 DestroyFramebuffer(); | |
| 333 return; | |
| 334 } | |
| 335 | |
| 336 GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); | |
| 337 if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { | |
| 338 DLOG(ERROR) << "Framebuffer was incomplete: " << status; | |
| 339 DestroyFramebuffer(); | |
| 340 return; | |
| 341 } | |
| 342 | |
| 343 has_complete_framebuffer_ = true; | |
| 344 | |
| 345 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, previous_texture_id); | |
| 346 // The FBO remains bound for this GL context. | |
| 347 } | |
| 348 | |
| 349 } // namespace content | |
| OLD | NEW |