texture_image_transport_surface.cc revision a93a17c8d99d686bd4a1511e5504e5e6cc9fcadf
1// Copyright (c) 2012 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/texture_image_transport_surface.h" 6 7#include <string> 8#include <vector> 9 10#include "base/command_line.h" 11#include "content/common/gpu/gpu_channel.h" 12#include "content/common/gpu/gpu_channel_manager.h" 13#include "content/common/gpu/gpu_messages.h" 14#include "content/common/gpu/sync_point_manager.h" 15#include "content/public/common/content_switches.h" 16#include "gpu/command_buffer/service/context_group.h" 17#include "gpu/command_buffer/service/gpu_scheduler.h" 18#include "ui/gl/scoped_binders.h" 19 20using gpu::gles2::ContextGroup; 21using gpu::gles2::MailboxManager; 22using gpu::gles2::MailboxName; 23using gpu::gles2::TextureDefinition; 24using gpu::gles2::TextureManager; 25 26namespace content { 27 28TextureImageTransportSurface::TextureImageTransportSurface( 29 GpuChannelManager* manager, 30 GpuCommandBufferStub* stub, 31 const gfx::GLSurfaceHandle& handle) 32 : fbo_id_(0), 33 backbuffer_(CreateTextureDefinition(gfx::Size(), 0)), 34 stub_destroyed_(false), 35 backbuffer_suggested_allocation_(true), 36 frontbuffer_suggested_allocation_(true), 37 handle_(handle), 38 is_swap_buffers_pending_(false), 39 did_unschedule_(false) { 40 helper_.reset(new ImageTransportHelper(this, 41 manager, 42 stub, 43 gfx::kNullPluginWindow)); 44} 45 46TextureImageTransportSurface::~TextureImageTransportSurface() { 47 DCHECK(stub_destroyed_); 48 Destroy(); 49} 50 51bool TextureImageTransportSurface::Initialize() { 52 mailbox_manager_ = 53 helper_->stub()->decoder()->GetContextGroup()->mailbox_manager(); 54 55 GpuChannelManager* manager = helper_->manager(); 56 surface_ = manager->GetDefaultOffscreenSurface(); 57 if (!surface_) 58 return false; 59 60 if (!helper_->Initialize()) 61 return false; 62 63 GpuChannel* parent_channel = manager->LookupChannel(handle_.parent_client_id); 64 if (parent_channel) { 65 const CommandLine* command_line = CommandLine::ForCurrentProcess(); 66 if (command_line->HasSwitch(switches::kUIPrioritizeInGpuProcess)) 67 helper_->SetPreemptByFlag(parent_channel->GetPreemptionFlag()); 68 } 69 70 return true; 71} 72 73void TextureImageTransportSurface::Destroy() { 74 if (surface_) 75 surface_ = NULL; 76 77 helper_->Destroy(); 78} 79 80bool TextureImageTransportSurface::DeferDraws() { 81 // The command buffer hit a draw/clear command that could clobber the 82 // texture in use by the UI compositor. If a Swap is pending, abort 83 // processing of the command by returning true and unschedule until the Swap 84 // Ack arrives. 85 DCHECK(!did_unschedule_); 86 if (is_swap_buffers_pending_) { 87 did_unschedule_ = true; 88 helper_->SetScheduled(false); 89 return true; 90 } 91 return false; 92} 93 94bool TextureImageTransportSurface::Resize(const gfx::Size&) { 95 return true; 96} 97 98bool TextureImageTransportSurface::IsOffscreen() { 99 return true; 100} 101 102bool TextureImageTransportSurface::OnMakeCurrent(gfx::GLContext* context) { 103 if (stub_destroyed_) { 104 // Early-exit so that we don't recreate the fbo. We still want to return 105 // true, so that the context is made current and the GLES2DecoderImpl can 106 // release its own resources. 107 return true; 108 } 109 110 context_ = context; 111 112 if (!fbo_id_) { 113 glGenFramebuffersEXT(1, &fbo_id_); 114 glBindFramebufferEXT(GL_FRAMEBUFFER, fbo_id_); 115 current_size_ = gfx::Size(1, 1); 116 helper_->stub()->AddDestructionObserver(this); 117 } 118 119 // We could be receiving non-deferred GL commands, that is anything that does 120 // not need a framebuffer. 121 if (!backbuffer_->service_id() && !is_swap_buffers_pending_ && 122 backbuffer_suggested_allocation_) { 123 CreateBackTexture(); 124 } 125 return true; 126} 127 128unsigned int TextureImageTransportSurface::GetBackingFrameBufferObject() { 129 return fbo_id_; 130} 131 132bool TextureImageTransportSurface::SetBackbufferAllocation(bool allocation) { 133 DCHECK(!is_swap_buffers_pending_); 134 if (backbuffer_suggested_allocation_ == allocation) 135 return true; 136 backbuffer_suggested_allocation_ = allocation; 137 138 if (backbuffer_suggested_allocation_) { 139 DCHECK(!backbuffer_->service_id()); 140 CreateBackTexture(); 141 } else { 142 ReleaseBackTexture(); 143 } 144 145 return true; 146} 147 148void TextureImageTransportSurface::SetFrontbufferAllocation(bool allocation) { 149 if (frontbuffer_suggested_allocation_ == allocation) 150 return; 151 frontbuffer_suggested_allocation_ = allocation; 152 153 if (!frontbuffer_suggested_allocation_) { 154 GpuHostMsg_AcceleratedSurfaceRelease_Params params; 155 helper_->SendAcceleratedSurfaceRelease(params); 156 } 157} 158 159void* TextureImageTransportSurface::GetShareHandle() { 160 return GetHandle(); 161} 162 163void* TextureImageTransportSurface::GetDisplay() { 164 return surface_.get() ? surface_->GetDisplay() : NULL; 165} 166 167void* TextureImageTransportSurface::GetConfig() { 168 return surface_.get() ? surface_->GetConfig() : NULL; 169} 170 171void TextureImageTransportSurface::OnResize(gfx::Size size) { 172 current_size_ = size; 173 CreateBackTexture(); 174} 175 176void TextureImageTransportSurface::OnWillDestroyStub() { 177 helper_->stub()->RemoveDestructionObserver(this); 178 179 GpuHostMsg_AcceleratedSurfaceRelease_Params params; 180 helper_->SendAcceleratedSurfaceRelease(params); 181 182 ReleaseBackTexture(); 183 184 // We are losing the stub owning us, this is our last chance to clean up the 185 // resources we allocated in the stub's context. 186 if (fbo_id_) { 187 glDeleteFramebuffersEXT(1, &fbo_id_); 188 CHECK_GL_ERROR(); 189 fbo_id_ = 0; 190 } 191 192 stub_destroyed_ = true; 193} 194 195void TextureImageTransportSurface::SetLatencyInfo( 196 const cc::LatencyInfo& latency_info) { 197 latency_info_ = latency_info; 198} 199 200bool TextureImageTransportSurface::SwapBuffers() { 201 DCHECK(backbuffer_suggested_allocation_); 202 203 if (!frontbuffer_suggested_allocation_) 204 return true; 205 206 if (!backbuffer_->service_id()) { 207 LOG(ERROR) << "Swap without valid backing."; 208 return true; 209 } 210 211 DCHECK(backbuffer_size() == current_size_); 212 GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params; 213 params.size = backbuffer_size(); 214 params.mailbox_name.assign( 215 reinterpret_cast<const char*>(&mailbox_name_), sizeof(mailbox_name_)); 216 217 glFlush(); 218 ProduceTexture(); 219 220 // Do not allow destruction while we are still waiting for a swap ACK, 221 // so we do not leak a texture in the mailbox. 222 AddRef(); 223 224 params.latency_info = latency_info_; 225 helper_->SendAcceleratedSurfaceBuffersSwapped(params); 226 227 DCHECK(!is_swap_buffers_pending_); 228 is_swap_buffers_pending_ = true; 229 return true; 230} 231 232bool TextureImageTransportSurface::PostSubBuffer( 233 int x, int y, int width, int height) { 234 DCHECK(backbuffer_suggested_allocation_); 235 if (!frontbuffer_suggested_allocation_) 236 return true; 237 const gfx::Rect new_damage_rect(x, y, width, height); 238 DCHECK(gfx::Rect(gfx::Point(), current_size_).Contains(new_damage_rect)); 239 240 // An empty damage rect is a successful no-op. 241 if (new_damage_rect.IsEmpty()) 242 return true; 243 244 if (!backbuffer_->service_id()) { 245 LOG(ERROR) << "Swap without valid backing."; 246 return true; 247 } 248 249 DCHECK(current_size_ == backbuffer_size()); 250 GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params params; 251 params.surface_size = backbuffer_size(); 252 params.x = x; 253 params.y = y; 254 params.width = width; 255 params.height = height; 256 params.mailbox_name.assign( 257 reinterpret_cast<const char*>(&mailbox_name_), sizeof(mailbox_name_)); 258 259 glFlush(); 260 ProduceTexture(); 261 262 // Do not allow destruction while we are still waiting for a swap ACK, 263 // so we do not leak a texture in the mailbox. 264 AddRef(); 265 266 params.latency_info = latency_info_; 267 helper_->SendAcceleratedSurfacePostSubBuffer(params); 268 269 DCHECK(!is_swap_buffers_pending_); 270 is_swap_buffers_pending_ = true; 271 return true; 272} 273 274std::string TextureImageTransportSurface::GetExtensions() { 275 std::string extensions = gfx::GLSurface::GetExtensions(); 276 extensions += extensions.empty() ? "" : " "; 277 extensions += "GL_CHROMIUM_front_buffer_cached "; 278 extensions += "GL_CHROMIUM_post_sub_buffer"; 279 return extensions; 280} 281 282gfx::Size TextureImageTransportSurface::GetSize() { 283 gfx::Size size = current_size_; 284 285 // OSMesa expects a non-zero size. 286 return gfx::Size(size.width() == 0 ? 1 : size.width(), 287 size.height() == 0 ? 1 : size.height()); 288} 289 290void* TextureImageTransportSurface::GetHandle() { 291 return surface_.get() ? surface_->GetHandle() : NULL; 292} 293 294unsigned TextureImageTransportSurface::GetFormat() { 295 return surface_.get() ? surface_->GetFormat() : 0; 296} 297 298void TextureImageTransportSurface::OnBufferPresented( 299 const AcceleratedSurfaceMsg_BufferPresented_Params& params) { 300 if (params.sync_point == 0) { 301 BufferPresentedImpl(params.mailbox_name); 302 } else { 303 helper_->manager()->sync_point_manager()->AddSyncPointCallback( 304 params.sync_point, 305 base::Bind(&TextureImageTransportSurface::BufferPresentedImpl, 306 this, 307 params.mailbox_name)); 308 } 309 310 // Careful, we might get deleted now if we were only waiting for 311 // a final swap ACK. 312 Release(); 313} 314 315void TextureImageTransportSurface::BufferPresentedImpl( 316 const std::string& mailbox_name) { 317 DCHECK(!backbuffer_->service_id()); 318 if (!mailbox_name.empty()) { 319 DCHECK(mailbox_name.length() == GL_MAILBOX_SIZE_CHROMIUM); 320 mailbox_name.copy(reinterpret_cast<char *>(&mailbox_name_), 321 sizeof(MailboxName)); 322 ConsumeTexture(); 323 } 324 325 if (stub_destroyed_ && backbuffer_->service_id()) { 326 // TODO(sievers): Remove this after changes to the mailbox to take ownership 327 // of the service ids. 328 DCHECK(context_.get() && surface_.get()); 329 uint32 service_id = backbuffer_->ReleaseServiceId(); 330 if (context_->MakeCurrent(surface_)) 331 glDeleteTextures(1, &service_id); 332 333 return; 334 } 335 336 DCHECK(is_swap_buffers_pending_); 337 is_swap_buffers_pending_ = false; 338 339 // We should not have allowed the backbuffer to be discarded while the ack 340 // was pending. 341 DCHECK(backbuffer_suggested_allocation_); 342 343 // We're relying on the fact that the parent context is 344 // finished with it's context when it inserts the sync point that 345 // triggers this callback. 346 if (helper_->MakeCurrent()) { 347 if (backbuffer_size() != current_size_ || !backbuffer_->service_id()) 348 CreateBackTexture(); 349 else 350 AttachBackTextureToFBO(); 351 } 352 353 // Even if MakeCurrent fails, schedule anyway, to trigger the lost context 354 // logic. 355 if (did_unschedule_) { 356 did_unschedule_ = false; 357 helper_->SetScheduled(true); 358 } 359} 360 361void TextureImageTransportSurface::OnResizeViewACK() { 362 NOTREACHED(); 363} 364 365void TextureImageTransportSurface::ReleaseBackTexture() { 366 if (!backbuffer_->service_id()) 367 return; 368 369 uint32 service_id = backbuffer_->ReleaseServiceId(); 370 glDeleteTextures(1, &service_id); 371 backbuffer_.reset(CreateTextureDefinition(gfx::Size(), 0)); 372 mailbox_name_ = MailboxName(); 373 glFlush(); 374 CHECK_GL_ERROR(); 375} 376 377void TextureImageTransportSurface::CreateBackTexture() { 378 // If |is_swap_buffers_pending| we are waiting for our backbuffer 379 // in the mailbox, so we shouldn't be reallocating it now. 380 DCHECK(!is_swap_buffers_pending_); 381 382 if (backbuffer_->service_id() && backbuffer_size() == current_size_) 383 return; 384 385 uint32 service_id = backbuffer_->ReleaseServiceId(); 386 387 VLOG(1) << "Allocating new backbuffer texture"; 388 389 // On Qualcomm we couldn't resize an FBO texture past a certain 390 // size, after we allocated it as 1x1. So here we simply delete 391 // the previous texture on resize, to insure we don't 'run out of 392 // memory'. 393 if (service_id && 394 helper_->stub() 395 ->decoder() 396 ->GetContextGroup() 397 ->feature_info() 398 ->workarounds() 399 .delete_instead_of_resize_fbo) { 400 glDeleteTextures(1, &service_id); 401 service_id = 0; 402 mailbox_name_ = MailboxName(); 403 } 404 405 if (!service_id) { 406 MailboxName new_mailbox_name; 407 MailboxName& name = mailbox_name_; 408 // This slot should be uninitialized. 409 DCHECK(!memcmp(&name, &new_mailbox_name, sizeof(MailboxName))); 410 mailbox_manager_->GenerateMailboxName(&new_mailbox_name); 411 name = new_mailbox_name; 412 glGenTextures(1, &service_id); 413 } 414 415 backbuffer_.reset( 416 CreateTextureDefinition(current_size_, service_id)); 417 418 { 419 gfx::ScopedTextureBinder texture_binder(GL_TEXTURE_2D, service_id); 420 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 421 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 422 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 423 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 424 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 425 current_size_.width(), current_size_.height(), 0, 426 GL_RGBA, GL_UNSIGNED_BYTE, NULL); 427 CHECK_GL_ERROR(); 428 } 429 430 AttachBackTextureToFBO(); 431} 432 433void TextureImageTransportSurface::AttachBackTextureToFBO() { 434 DCHECK(backbuffer_->service_id()); 435 gfx::ScopedFrameBufferBinder fbo_binder(fbo_id_); 436 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, 437 GL_COLOR_ATTACHMENT0, 438 GL_TEXTURE_2D, 439 backbuffer_->service_id(), 440 0); 441 CHECK_GL_ERROR(); 442 443#ifndef NDEBUG 444 GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER); 445 if (status != GL_FRAMEBUFFER_COMPLETE) { 446 DLOG(FATAL) << "Framebuffer incomplete: " << status; 447 } 448#endif 449} 450 451TextureDefinition* TextureImageTransportSurface::CreateTextureDefinition( 452 gfx::Size size, int service_id) { 453 TextureDefinition::LevelInfo info( 454 GL_TEXTURE_2D, GL_RGBA, size.width(), size.height(), 1, 455 0, GL_RGBA, GL_UNSIGNED_BYTE, true); 456 457 TextureDefinition::LevelInfos level_infos; 458 level_infos.resize(1); 459 level_infos[0].resize(1); 460 level_infos[0][0] = info; 461 return new TextureDefinition( 462 GL_TEXTURE_2D, 463 service_id, 464 GL_LINEAR, 465 GL_LINEAR, 466 GL_CLAMP_TO_EDGE, 467 GL_CLAMP_TO_EDGE, 468 GL_NONE, 469 true, 470 false, 471 level_infos); 472} 473 474void TextureImageTransportSurface::ConsumeTexture() { 475 DCHECK(!backbuffer_->service_id()); 476 477 backbuffer_.reset(mailbox_manager_->ConsumeTexture( 478 GL_TEXTURE_2D, mailbox_name_)); 479 if (!backbuffer_) { 480 mailbox_name_ = MailboxName(); 481 backbuffer_.reset(CreateTextureDefinition(gfx::Size(), 0)); 482 } 483} 484 485void TextureImageTransportSurface::ProduceTexture() { 486 DCHECK(backbuffer_->service_id()); 487 DCHECK(!backbuffer_size().IsEmpty()); 488 489 // Pass NULL as |owner| here to avoid errors from glConsumeTextureCHROMIUM() 490 // when the renderer context group goes away before the RWHV handles a pending 491 // ACK. We avoid leaking a texture in the mailbox by waiting for the final ACK 492 // at which point we consume the correct texture back. 493 bool success = mailbox_manager_->ProduceTexture( 494 GL_TEXTURE_2D, 495 mailbox_name_, 496 backbuffer_.release(), 497 NULL); 498 DCHECK(success); 499 mailbox_name_ = MailboxName(); 500 backbuffer_.reset(CreateTextureDefinition(gfx::Size(), 0)); 501} 502 503} // namespace content 504