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 "gpu/command_buffer/service/mailbox_manager.h" 19#include "ui/gl/scoped_binders.h" 20 21using gpu::gles2::ContextGroup; 22using gpu::gles2::GLES2Decoder; 23using gpu::gles2::MailboxManager; 24using gpu::gles2::Texture; 25using gpu::gles2::TextureManager; 26using gpu::gles2::TextureRef; 27using gpu::Mailbox; 28 29namespace content { 30namespace { 31 32bool IsContextValid(ImageTransportHelper* helper) { 33 return helper->stub()->decoder()->GetGLContext()->IsCurrent(NULL); 34} 35 36} // namespace 37 38TextureImageTransportSurface::TextureImageTransportSurface( 39 GpuChannelManager* manager, 40 GpuCommandBufferStub* stub, 41 const gfx::GLSurfaceHandle& handle) 42 : fbo_id_(0), 43 current_size_(1, 1), 44 scale_factor_(1.f), 45 stub_destroyed_(false), 46 backbuffer_suggested_allocation_(true), 47 frontbuffer_suggested_allocation_(true), 48 handle_(handle), 49 is_swap_buffers_pending_(false), 50 did_unschedule_(false) { 51 helper_.reset(new ImageTransportHelper(this, 52 manager, 53 stub, 54 gfx::kNullPluginWindow)); 55} 56 57TextureImageTransportSurface::~TextureImageTransportSurface() { 58 DCHECK(stub_destroyed_); 59 Destroy(); 60} 61 62bool TextureImageTransportSurface::Initialize() { 63 mailbox_manager_ = 64 helper_->stub()->decoder()->GetContextGroup()->mailbox_manager(); 65 66 GpuChannelManager* manager = helper_->manager(); 67 surface_ = manager->GetDefaultOffscreenSurface(); 68 if (!surface_.get()) 69 return false; 70 71 if (!helper_->Initialize()) 72 return false; 73 74 GpuChannel* parent_channel = manager->LookupChannel(handle_.parent_client_id); 75 if (parent_channel) { 76 const base::CommandLine* command_line = 77 base::CommandLine::ForCurrentProcess(); 78 if (command_line->HasSwitch(switches::kUIPrioritizeInGpuProcess)) 79 helper_->SetPreemptByFlag(parent_channel->GetPreemptionFlag()); 80 } 81 82 return true; 83} 84 85void TextureImageTransportSurface::Destroy() { 86 if (surface_.get()) 87 surface_ = NULL; 88 89 helper_->Destroy(); 90} 91 92bool TextureImageTransportSurface::DeferDraws() { 93 // The command buffer hit a draw/clear command that could clobber the 94 // texture in use by the UI compositor. If a Swap is pending, abort 95 // processing of the command by returning true and unschedule until the Swap 96 // Ack arrives. 97 DCHECK(!did_unschedule_); 98 if (is_swap_buffers_pending_) { 99 did_unschedule_ = true; 100 helper_->SetScheduled(false); 101 return true; 102 } 103 return false; 104} 105 106bool TextureImageTransportSurface::IsOffscreen() { 107 return true; 108} 109 110unsigned int TextureImageTransportSurface::GetBackingFrameBufferObject() { 111 DCHECK(IsContextValid(helper_.get())); 112 if (!fbo_id_) { 113 glGenFramebuffersEXT(1, &fbo_id_); 114 glBindFramebufferEXT(GL_FRAMEBUFFER, fbo_id_); 115 helper_->stub()->AddDestructionObserver(this); 116 CreateBackTexture(); 117 } 118 119 return fbo_id_; 120} 121 122bool TextureImageTransportSurface::SetBackbufferAllocation(bool allocation) { 123 DCHECK(!is_swap_buffers_pending_); 124 if (backbuffer_suggested_allocation_ == allocation) 125 return true; 126 backbuffer_suggested_allocation_ = allocation; 127 128 if (backbuffer_suggested_allocation_) { 129 DCHECK(!backbuffer_.get()); 130 CreateBackTexture(); 131 } else { 132 ReleaseBackTexture(); 133 } 134 135 return true; 136} 137 138void TextureImageTransportSurface::SetFrontbufferAllocation(bool allocation) { 139 if (frontbuffer_suggested_allocation_ == allocation) 140 return; 141 frontbuffer_suggested_allocation_ = allocation; 142 143 // If a swapbuffers is in flight, wait for the ack before releasing the front 144 // buffer: 145 // - we don't know yet which texture the browser will want to keep 146 // - we want to ensure we don't destroy a texture that is in flight before the 147 // browser got a reference on it. 148 if (!frontbuffer_suggested_allocation_ && 149 !is_swap_buffers_pending_ && 150 helper_->MakeCurrent()) { 151 ReleaseFrontTexture(); 152 } 153} 154 155void* TextureImageTransportSurface::GetShareHandle() { 156 return GetHandle(); 157} 158 159void* TextureImageTransportSurface::GetDisplay() { 160 return surface_.get() ? surface_->GetDisplay() : NULL; 161} 162 163void* TextureImageTransportSurface::GetConfig() { 164 return surface_.get() ? surface_->GetConfig() : NULL; 165} 166 167void TextureImageTransportSurface::OnResize(gfx::Size size, 168 float scale_factor) { 169 DCHECK_GE(size.width(), 1); 170 DCHECK_GE(size.height(), 1); 171 current_size_ = size; 172 scale_factor_ = scale_factor; 173 if (backbuffer_suggested_allocation_) 174 CreateBackTexture(); 175} 176 177void TextureImageTransportSurface::OnWillDestroyStub() { 178 bool have_context = IsContextValid(helper_.get()); 179 helper_->stub()->RemoveDestructionObserver(this); 180 181 // We are losing the stub owning us, this is our last chance to clean up the 182 // resources we allocated in the stub's context. 183 if (have_context) { 184 ReleaseBackTexture(); 185 ReleaseFrontTexture(); 186 } else { 187 backbuffer_ = NULL; 188 back_mailbox_ = Mailbox(); 189 frontbuffer_ = NULL; 190 front_mailbox_ = Mailbox(); 191 } 192 193 if (fbo_id_ && have_context) { 194 glDeleteFramebuffersEXT(1, &fbo_id_); 195 CHECK_GL_ERROR(); 196 } 197 fbo_id_ = 0; 198 199 stub_destroyed_ = true; 200} 201 202void TextureImageTransportSurface::SetLatencyInfo( 203 const std::vector<ui::LatencyInfo>& latency_info) { 204 for (size_t i = 0; i < latency_info.size(); i++) 205 latency_info_.push_back(latency_info[i]); 206} 207 208void TextureImageTransportSurface::WakeUpGpu() { 209 NOTIMPLEMENTED(); 210} 211 212bool TextureImageTransportSurface::SwapBuffers() { 213 DCHECK(IsContextValid(helper_.get())); 214 DCHECK(backbuffer_suggested_allocation_); 215 216 if (!frontbuffer_suggested_allocation_) 217 return true; 218 219 if (!backbuffer_.get()) { 220 LOG(ERROR) << "Swap without valid backing."; 221 return true; 222 } 223 224 DCHECK(backbuffer_size() == current_size_); 225 GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params; 226 params.size = backbuffer_size(); 227 params.scale_factor = scale_factor_; 228 params.mailbox = back_mailbox_; 229 230 glFlush(); 231 232 params.latency_info.swap(latency_info_); 233 helper_->SendAcceleratedSurfaceBuffersSwapped(params); 234 235 DCHECK(!is_swap_buffers_pending_); 236 is_swap_buffers_pending_ = true; 237 return true; 238} 239 240bool TextureImageTransportSurface::PostSubBuffer( 241 int x, int y, int width, int height) { 242 DCHECK(IsContextValid(helper_.get())); 243 DCHECK(backbuffer_suggested_allocation_); 244 if (!frontbuffer_suggested_allocation_) 245 return true; 246 const gfx::Rect new_damage_rect(x, y, width, height); 247 DCHECK(gfx::Rect(gfx::Point(), current_size_).Contains(new_damage_rect)); 248 249 // An empty damage rect is a successful no-op. 250 if (new_damage_rect.IsEmpty()) 251 return true; 252 253 if (!backbuffer_.get()) { 254 LOG(ERROR) << "Swap without valid backing."; 255 return true; 256 } 257 258 DCHECK(current_size_ == backbuffer_size()); 259 GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params params; 260 params.surface_size = backbuffer_size(); 261 params.surface_scale_factor = scale_factor_; 262 params.x = x; 263 params.y = y; 264 params.width = width; 265 params.height = height; 266 params.mailbox = back_mailbox_; 267 268 glFlush(); 269 270 params.latency_info.swap(latency_info_); 271 helper_->SendAcceleratedSurfacePostSubBuffer(params); 272 273 DCHECK(!is_swap_buffers_pending_); 274 is_swap_buffers_pending_ = true; 275 return true; 276} 277 278bool TextureImageTransportSurface::SupportsPostSubBuffer() { 279 return true; 280} 281 282gfx::Size TextureImageTransportSurface::GetSize() { 283 return current_size_; 284} 285 286void* TextureImageTransportSurface::GetHandle() { 287 return surface_.get() ? surface_->GetHandle() : NULL; 288} 289 290unsigned TextureImageTransportSurface::GetFormat() { 291 return surface_.get() ? surface_->GetFormat() : 0; 292} 293 294void TextureImageTransportSurface::OnBufferPresented( 295 const AcceleratedSurfaceMsg_BufferPresented_Params& params) { 296 if (params.sync_point == 0) { 297 BufferPresentedImpl(params.mailbox); 298 } else { 299 helper_->manager()->sync_point_manager()->AddSyncPointCallback( 300 params.sync_point, 301 base::Bind(&TextureImageTransportSurface::BufferPresentedImpl, 302 this, 303 params.mailbox)); 304 } 305} 306 307void TextureImageTransportSurface::BufferPresentedImpl(const Mailbox& mailbox) { 308 DCHECK(is_swap_buffers_pending_); 309 is_swap_buffers_pending_ = false; 310 311 // When we wait for a sync point, we may get called back after the stub is 312 // destroyed. In that case there's no need to do anything with the returned 313 // mailbox. 314 if (stub_destroyed_) 315 return; 316 317 // We should not have allowed the backbuffer to be discarded while the ack 318 // was pending. 319 DCHECK(backbuffer_suggested_allocation_); 320 DCHECK(backbuffer_.get()); 321 322 bool swap = true; 323 if (!mailbox.IsZero()) { 324 if (mailbox == back_mailbox_) { 325 // The browser has skipped the frame to unblock the GPU process, waiting 326 // for one of the right size, and returned the back buffer, so don't swap. 327 swap = false; 328 } 329 } 330 if (swap) { 331 std::swap(backbuffer_, frontbuffer_); 332 std::swap(back_mailbox_, front_mailbox_); 333 } 334 335 // We're relying on the fact that the parent context is 336 // finished with its context when it inserts the sync point that 337 // triggers this callback. 338 if (helper_->MakeCurrent()) { 339 if (frontbuffer_.get() && !frontbuffer_suggested_allocation_) 340 ReleaseFrontTexture(); 341 if (!backbuffer_.get() || backbuffer_size() != current_size_) 342 CreateBackTexture(); 343 else 344 AttachBackTextureToFBO(); 345 } 346 347 // Even if MakeCurrent fails, schedule anyway, to trigger the lost context 348 // logic. 349 if (did_unschedule_) { 350 did_unschedule_ = false; 351 helper_->SetScheduled(true); 352 } 353} 354 355void TextureImageTransportSurface::ReleaseBackTexture() { 356 DCHECK(IsContextValid(helper_.get())); 357 backbuffer_ = NULL; 358 back_mailbox_ = Mailbox(); 359 glFlush(); 360 CHECK_GL_ERROR(); 361} 362 363void TextureImageTransportSurface::ReleaseFrontTexture() { 364 DCHECK(IsContextValid(helper_.get())); 365 frontbuffer_ = NULL; 366 front_mailbox_ = Mailbox(); 367 glFlush(); 368 CHECK_GL_ERROR(); 369 helper_->SendAcceleratedSurfaceRelease(); 370} 371 372void TextureImageTransportSurface::CreateBackTexture() { 373 DCHECK(IsContextValid(helper_.get())); 374 // If |is_swap_buffers_pending| we are waiting for our backbuffer 375 // in the mailbox, so we shouldn't be reallocating it now. 376 DCHECK(!is_swap_buffers_pending_); 377 378 if (backbuffer_.get() && backbuffer_size() == current_size_) 379 return; 380 381 VLOG(1) << "Allocating new backbuffer texture"; 382 383 GLES2Decoder* decoder = helper_->stub()->decoder(); 384 TextureManager* texture_manager = 385 decoder->GetContextGroup()->texture_manager(); 386 if (!backbuffer_.get()) { 387 back_mailbox_ = gpu::Mailbox::Generate(); 388 GLuint service_id; 389 glGenTextures(1, &service_id); 390 backbuffer_ = TextureRef::Create(texture_manager, 0, service_id); 391 texture_manager->SetTarget(backbuffer_.get(), GL_TEXTURE_2D); 392 Texture* texture = texture_manager->Produce(backbuffer_.get()); 393 mailbox_manager_->ProduceTexture(GL_TEXTURE_2D, back_mailbox_, texture); 394 } 395 396 { 397 gfx::ScopedTextureBinder texture_binder(GL_TEXTURE_2D, 398 backbuffer_->service_id()); 399 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 400 current_size_.width(), current_size_.height(), 0, 401 GL_RGBA, GL_UNSIGNED_BYTE, NULL); 402 gpu::gles2::ErrorState* error_state = decoder->GetErrorState(); 403 texture_manager->SetParameteri("Backbuffer", 404 error_state, 405 backbuffer_.get(), 406 GL_TEXTURE_MIN_FILTER, 407 GL_LINEAR); 408 texture_manager->SetParameteri("Backbuffer", 409 error_state, 410 backbuffer_.get(), 411 GL_TEXTURE_MAG_FILTER, 412 GL_LINEAR); 413 texture_manager->SetParameteri("Backbuffer", 414 error_state, 415 backbuffer_.get(), 416 GL_TEXTURE_WRAP_S, 417 GL_CLAMP_TO_EDGE); 418 texture_manager->SetParameteri("Backbuffer", 419 error_state, 420 backbuffer_.get(), 421 GL_TEXTURE_WRAP_T, 422 GL_CLAMP_TO_EDGE); 423 texture_manager->SetLevelInfo(backbuffer_.get(), 424 GL_TEXTURE_2D, 425 0, 426 GL_RGBA, 427 current_size_.width(), 428 current_size_.height(), 429 1, 430 0, 431 GL_RGBA, 432 GL_UNSIGNED_BYTE, 433 true); 434 DCHECK(texture_manager->CanRender(backbuffer_.get())); 435 CHECK_GL_ERROR(); 436 } 437 438 AttachBackTextureToFBO(); 439} 440 441void TextureImageTransportSurface::AttachBackTextureToFBO() { 442 DCHECK(IsContextValid(helper_.get())); 443 DCHECK(backbuffer_.get()); 444 gfx::ScopedFrameBufferBinder fbo_binder(fbo_id_); 445 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, 446 GL_COLOR_ATTACHMENT0, 447 GL_TEXTURE_2D, 448 backbuffer_->service_id(), 449 0); 450 CHECK_GL_ERROR(); 451 452#ifndef NDEBUG 453 GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER); 454 if (status != GL_FRAMEBUFFER_COMPLETE) { 455 DLOG(FATAL) << "Framebuffer incomplete: " << status; 456 } 457#endif 458} 459 460} // namespace content 461