| OLD | NEW |
| (Empty) |
| 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 | |
| 21 using gpu::gles2::ContextGroup; | |
| 22 using gpu::gles2::GLES2Decoder; | |
| 23 using gpu::gles2::MailboxManager; | |
| 24 using gpu::gles2::Texture; | |
| 25 using gpu::gles2::TextureManager; | |
| 26 using gpu::gles2::TextureRef; | |
| 27 using gpu::Mailbox; | |
| 28 | |
| 29 namespace content { | |
| 30 namespace { | |
| 31 | |
| 32 bool IsContextValid(ImageTransportHelper* helper) { | |
| 33 return helper->stub()->decoder()->GetGLContext()->IsCurrent(NULL); | |
| 34 } | |
| 35 | |
| 36 } // namespace | |
| 37 | |
| 38 TextureImageTransportSurface::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 | |
| 57 TextureImageTransportSurface::~TextureImageTransportSurface() { | |
| 58 DCHECK(stub_destroyed_); | |
| 59 Destroy(); | |
| 60 } | |
| 61 | |
| 62 bool 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 | |
| 85 void TextureImageTransportSurface::Destroy() { | |
| 86 if (surface_.get()) | |
| 87 surface_ = NULL; | |
| 88 | |
| 89 helper_->Destroy(); | |
| 90 } | |
| 91 | |
| 92 bool 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 | |
| 106 bool TextureImageTransportSurface::IsOffscreen() { | |
| 107 return true; | |
| 108 } | |
| 109 | |
| 110 unsigned 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 | |
| 122 bool 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 | |
| 138 void 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 | |
| 155 void* TextureImageTransportSurface::GetShareHandle() { | |
| 156 return GetHandle(); | |
| 157 } | |
| 158 | |
| 159 void* TextureImageTransportSurface::GetDisplay() { | |
| 160 return surface_.get() ? surface_->GetDisplay() : NULL; | |
| 161 } | |
| 162 | |
| 163 void* TextureImageTransportSurface::GetConfig() { | |
| 164 return surface_.get() ? surface_->GetConfig() : NULL; | |
| 165 } | |
| 166 | |
| 167 void 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 | |
| 177 void 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 | |
| 202 void 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 | |
| 208 void TextureImageTransportSurface::WakeUpGpu() { | |
| 209 NOTIMPLEMENTED(); | |
| 210 } | |
| 211 | |
| 212 bool 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 | |
| 240 bool 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 | |
| 278 bool TextureImageTransportSurface::SupportsPostSubBuffer() { | |
| 279 return true; | |
| 280 } | |
| 281 | |
| 282 gfx::Size TextureImageTransportSurface::GetSize() { | |
| 283 return current_size_; | |
| 284 } | |
| 285 | |
| 286 void* TextureImageTransportSurface::GetHandle() { | |
| 287 return surface_.get() ? surface_->GetHandle() : NULL; | |
| 288 } | |
| 289 | |
| 290 unsigned TextureImageTransportSurface::GetFormat() { | |
| 291 return surface_.get() ? surface_->GetFormat() : 0; | |
| 292 } | |
| 293 | |
| 294 void 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 | |
| 307 void 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 | |
| 355 void TextureImageTransportSurface::ReleaseBackTexture() { | |
| 356 DCHECK(IsContextValid(helper_.get())); | |
| 357 backbuffer_ = NULL; | |
| 358 back_mailbox_ = Mailbox(); | |
| 359 glFlush(); | |
| 360 CHECK_GL_ERROR(); | |
| 361 } | |
| 362 | |
| 363 void 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 | |
| 372 void 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 | |
| 441 void 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 | |
| OLD | NEW |