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/browser/compositor/buffer_queue.h" 6 7 #include "content/browser/compositor/image_transport_factory.h" 8 #include "content/common/gpu/client/context_provider_command_buffer.h" 9 #include "content/common/gpu/client/gl_helper.h" 10 #include "gpu/GLES2/gl2extchromium.h" 11 #include "gpu/command_buffer/client/gles2_interface.h" 12 #include "third_party/skia/include/core/SkRect.h" 13 #include "third_party/skia/include/core/SkRegion.h" 14 15 namespace content { 16 17 BufferQueue::BufferQueue(scoped_refptr<cc::ContextProvider> context_provider, 18 unsigned int internalformat) 19 : context_provider_(context_provider), 20 fbo_(0), 21 allocated_count_(0), 22 internalformat_(internalformat) { 23 } 24 25 BufferQueue::~BufferQueue() { 26 FreeAllSurfaces(); 27 28 gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL(); 29 if (fbo_) 30 gl->DeleteFramebuffers(1, &fbo_); 31 } 32 33 bool BufferQueue::Initialize() { 34 gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL(); 35 gl->GenFramebuffers(1, &fbo_); 36 return fbo_ != 0; 37 } 38 39 void BufferQueue::BindFramebuffer() { 40 gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL(); 41 gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_); 42 43 if (!current_surface_.texture) { 44 current_surface_ = GetNextSurface(); 45 gl->FramebufferTexture2D(GL_FRAMEBUFFER, 46 GL_COLOR_ATTACHMENT0, 47 GL_TEXTURE_2D, 48 current_surface_.texture, 49 0); 50 } 51 } 52 53 void BufferQueue::CopyBufferDamage(int texture, 54 int source_texture, 55 const gfx::Rect& new_damage, 56 const gfx::Rect& old_damage) { 57 ImageTransportFactory::GetInstance()->GetGLHelper()->CopySubBufferDamage( 58 texture, 59 source_texture, 60 SkRegion(SkIRect::MakeXYWH(new_damage.x(), 61 new_damage.y(), 62 new_damage.width(), 63 new_damage.height())), 64 SkRegion(SkIRect::MakeXYWH(old_damage.x(), 65 old_damage.y(), 66 old_damage.width(), 67 old_damage.height()))); 68 } 69 70 void BufferQueue::UpdateBufferDamage(const gfx::Rect& damage) { 71 for (size_t i = 0; i < available_surfaces_.size(); i++) 72 available_surfaces_[i].damage.Union(damage); 73 for (std::deque<AllocatedSurface>::iterator it = 74 in_flight_surfaces_.begin(); 75 it != in_flight_surfaces_.end(); 76 ++it) 77 it->damage.Union(damage); 78 } 79 80 void BufferQueue::SwapBuffers(const gfx::Rect& damage) { 81 if (damage != gfx::Rect(size_)) { 82 // We must have a frame available to copy from. 83 DCHECK(!in_flight_surfaces_.empty()); 84 CopyBufferDamage(current_surface_.texture, 85 in_flight_surfaces_.back().texture, 86 damage, 87 current_surface_.damage); 88 } 89 UpdateBufferDamage(damage); 90 current_surface_.damage = gfx::Rect(); 91 in_flight_surfaces_.push_back(current_surface_); 92 current_surface_.texture = 0; 93 current_surface_.image = 0; 94 } 95 96 void BufferQueue::Reshape(const gfx::Size& size, float scale_factor) { 97 DCHECK(!current_surface_.texture); 98 if (size == size_) 99 return; 100 size_ = size; 101 102 // TODO: add stencil buffer when needed. 103 gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL(); 104 gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_); 105 gl->FramebufferTexture2D( 106 GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); 107 108 FreeAllSurfaces(); 109 } 110 111 void BufferQueue::PageFlipComplete() { 112 if (in_flight_surfaces_.size() > 1) { 113 available_surfaces_.push_back(in_flight_surfaces_.front()); 114 in_flight_surfaces_.pop_front(); 115 } 116 } 117 118 void BufferQueue::FreeAllSurfaces() { 119 FreeSurface(¤t_surface_); 120 while (!in_flight_surfaces_.empty()) { 121 FreeSurface(&in_flight_surfaces_.front()); 122 in_flight_surfaces_.pop_front(); 123 } 124 for (size_t i = 0; i < available_surfaces_.size(); i++) 125 FreeSurface(&available_surfaces_[i]); 126 available_surfaces_.clear(); 127 } 128 129 void BufferQueue::FreeSurface(AllocatedSurface* surface) { 130 if (surface->texture) { 131 gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL(); 132 gl->BindTexture(GL_TEXTURE_2D, surface->texture); 133 gl->ReleaseTexImage2DCHROMIUM(GL_TEXTURE_2D, surface->image); 134 gl->DeleteTextures(1, &surface->texture); 135 gl->DestroyImageCHROMIUM(surface->image); 136 surface->image = 0; 137 surface->texture = 0; 138 allocated_count_--; 139 } 140 } 141 142 BufferQueue::AllocatedSurface BufferQueue::GetNextSurface() { 143 if (!available_surfaces_.empty()) { 144 AllocatedSurface surface = available_surfaces_.back(); 145 available_surfaces_.pop_back(); 146 return surface; 147 } 148 149 unsigned int texture = 0; 150 gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL(); 151 gl->GenTextures(1, &texture); 152 if (!texture) 153 return AllocatedSurface(); 154 155 // We don't want to allow anything more than triple buffering. 156 DCHECK_LT(allocated_count_, 4U); 157 158 unsigned int id = gl->CreateImageCHROMIUM( 159 size_.width(), 160 size_.height(), 161 internalformat_, 162 GL_IMAGE_SCANOUT_CHROMIUM); 163 if (!id) { 164 LOG(ERROR) << "Failed to allocate backing CreateImageCHROMIUM surface"; 165 gl->DeleteTextures(1, &texture); 166 return AllocatedSurface(); 167 } 168 allocated_count_++; 169 gl->BindTexture(GL_TEXTURE_2D, texture); 170 gl->BindTexImage2DCHROMIUM(GL_TEXTURE_2D, id); 171 return AllocatedSurface(texture, id, gfx::Rect(size_)); 172 } 173 174 } // namespace content 175