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/browser/compositor/buffer_queue.h" |
| 6 |
| 7 #include "base/containers/adapters.h" |
| 8 #include "base/memory/ptr_util.h" |
| 9 #include "build/build_config.h" |
| 10 #include "cc/output/context_provider.h" |
| 11 #include "content/browser/compositor/gl_helper.h" |
| 12 #include "gpu/GLES2/gl2extchromium.h" |
| 13 #include "gpu/command_buffer/client/gles2_interface.h" |
| 14 #include "gpu/command_buffer/client/gpu_memory_buffer_manager.h" |
| 15 #include "gpu/command_buffer/common/gpu_memory_buffer_support.h" |
| 16 #include "third_party/skia/include/core/SkRect.h" |
| 17 #include "third_party/skia/include/core/SkRegion.h" |
| 18 #include "ui/gfx/gpu_memory_buffer.h" |
| 19 #include "ui/gfx/skia_util.h" |
| 20 |
| 21 namespace content { |
| 22 |
| 23 BufferQueue::BufferQueue(scoped_refptr<cc::ContextProvider> context_provider, |
| 24 unsigned int texture_target, |
| 25 unsigned int internalformat, |
| 26 GLHelper* gl_helper, |
| 27 gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager, |
| 28 int surface_id) |
| 29 : context_provider_(context_provider), |
| 30 fbo_(0), |
| 31 allocated_count_(0), |
| 32 texture_target_(texture_target), |
| 33 internal_format_(internalformat), |
| 34 gl_helper_(gl_helper), |
| 35 gpu_memory_buffer_manager_(gpu_memory_buffer_manager), |
| 36 surface_id_(surface_id) {} |
| 37 |
| 38 BufferQueue::~BufferQueue() { |
| 39 FreeAllSurfaces(); |
| 40 |
| 41 gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL(); |
| 42 if (fbo_) |
| 43 gl->DeleteFramebuffers(1, &fbo_); |
| 44 } |
| 45 |
| 46 void BufferQueue::Initialize() { |
| 47 gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL(); |
| 48 gl->GenFramebuffers(1, &fbo_); |
| 49 } |
| 50 |
| 51 void BufferQueue::BindFramebuffer() { |
| 52 gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL(); |
| 53 gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_); |
| 54 |
| 55 if (!current_surface_) |
| 56 current_surface_ = GetNextSurface(); |
| 57 |
| 58 if (current_surface_) { |
| 59 gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, |
| 60 texture_target_, current_surface_->texture, 0); |
| 61 } |
| 62 } |
| 63 |
| 64 void BufferQueue::CopyBufferDamage(int texture, |
| 65 int source_texture, |
| 66 const gfx::Rect& new_damage, |
| 67 const gfx::Rect& old_damage) { |
| 68 gl_helper_->CopySubBufferDamage( |
| 69 texture_target_, texture, source_texture, |
| 70 SkRegion(gfx::RectToSkIRect(new_damage)), |
| 71 SkRegion(gfx::RectToSkIRect(old_damage))); |
| 72 } |
| 73 |
| 74 void BufferQueue::UpdateBufferDamage(const gfx::Rect& damage) { |
| 75 if (displayed_surface_) |
| 76 displayed_surface_->damage.Union(damage); |
| 77 for (auto& surface : available_surfaces_) |
| 78 surface->damage.Union(damage); |
| 79 for (auto& surface : in_flight_surfaces_) { |
| 80 if (surface) |
| 81 surface->damage.Union(damage); |
| 82 } |
| 83 } |
| 84 |
| 85 void BufferQueue::SwapBuffers(const gfx::Rect& damage) { |
| 86 if (current_surface_) { |
| 87 if (damage != gfx::Rect(size_)) { |
| 88 // Copy damage from the most recently swapped buffer. In the event that |
| 89 // the buffer was destroyed and failed to recreate, pick from the most |
| 90 // recently available buffer. |
| 91 unsigned int texture_id = 0; |
| 92 for (auto& surface : base::Reversed(in_flight_surfaces_)) { |
| 93 if (surface) { |
| 94 texture_id = surface->texture; |
| 95 break; |
| 96 } |
| 97 } |
| 98 if (!texture_id && displayed_surface_) |
| 99 texture_id = displayed_surface_->texture; |
| 100 |
| 101 if (texture_id) { |
| 102 CopyBufferDamage(current_surface_->texture, texture_id, damage, |
| 103 current_surface_->damage); |
| 104 } |
| 105 } |
| 106 current_surface_->damage = gfx::Rect(); |
| 107 } |
| 108 UpdateBufferDamage(damage); |
| 109 in_flight_surfaces_.push_back(std::move(current_surface_)); |
| 110 // Some things reset the framebuffer (CopySubBufferDamage, some GLRenderer |
| 111 // paths), so ensure we restore it here. |
| 112 context_provider_->ContextGL()->BindFramebuffer(GL_FRAMEBUFFER, fbo_); |
| 113 } |
| 114 |
| 115 void BufferQueue::Reshape(const gfx::Size& size, float scale_factor) { |
| 116 if (size == size_) |
| 117 return; |
| 118 // TODO(ccameron): This assert is being hit on Mac try jobs. Determine if that |
| 119 // is cause for concern or if it is benign. |
| 120 // http://crbug.com/524624 |
| 121 #if !defined(OS_MACOSX) |
| 122 DCHECK(!current_surface_); |
| 123 #endif |
| 124 size_ = size; |
| 125 |
| 126 // TODO: add stencil buffer when needed. |
| 127 gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL(); |
| 128 gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_); |
| 129 gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, |
| 130 texture_target_, 0, 0); |
| 131 |
| 132 FreeAllSurfaces(); |
| 133 } |
| 134 |
| 135 void BufferQueue::RecreateBuffers() { |
| 136 // We need to recreate the buffers, for whatever reason the old ones are not |
| 137 // presentable on the device anymore. |
| 138 // Unused buffers can be freed directly, they will be re-allocated as needed. |
| 139 // Any in flight, current or displayed surface must be replaced. |
| 140 available_surfaces_.clear(); |
| 141 |
| 142 // Recreate all in-flight surfaces and put the recreated copies in the queue. |
| 143 for (auto& surface : in_flight_surfaces_) |
| 144 surface = RecreateBuffer(std::move(surface)); |
| 145 |
| 146 current_surface_ = RecreateBuffer(std::move(current_surface_)); |
| 147 displayed_surface_ = RecreateBuffer(std::move(displayed_surface_)); |
| 148 |
| 149 if (current_surface_) { |
| 150 // If we have a texture bound, we will need to re-bind it. |
| 151 gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL(); |
| 152 gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_); |
| 153 gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, |
| 154 texture_target_, current_surface_->texture, 0); |
| 155 } |
| 156 } |
| 157 |
| 158 std::unique_ptr<BufferQueue::AllocatedSurface> BufferQueue::RecreateBuffer( |
| 159 std::unique_ptr<AllocatedSurface> surface) { |
| 160 if (!surface) |
| 161 return nullptr; |
| 162 |
| 163 std::unique_ptr<AllocatedSurface> new_surface(GetNextSurface()); |
| 164 if (!new_surface) |
| 165 return nullptr; |
| 166 |
| 167 new_surface->damage = surface->damage; |
| 168 |
| 169 // Copy the entire texture. |
| 170 CopyBufferDamage(new_surface->texture, surface->texture, gfx::Rect(), |
| 171 gfx::Rect(size_)); |
| 172 return new_surface; |
| 173 } |
| 174 |
| 175 void BufferQueue::PageFlipComplete() { |
| 176 DCHECK(!in_flight_surfaces_.empty()); |
| 177 if (displayed_surface_) |
| 178 available_surfaces_.push_back(std::move(displayed_surface_)); |
| 179 displayed_surface_ = std::move(in_flight_surfaces_.front()); |
| 180 in_flight_surfaces_.pop_front(); |
| 181 } |
| 182 |
| 183 void BufferQueue::FreeAllSurfaces() { |
| 184 displayed_surface_.reset(); |
| 185 current_surface_.reset(); |
| 186 // This is intentionally not emptied since the swap buffers acks are still |
| 187 // expected to arrive. |
| 188 for (auto& surface : in_flight_surfaces_) |
| 189 surface = nullptr; |
| 190 available_surfaces_.clear(); |
| 191 } |
| 192 |
| 193 void BufferQueue::FreeSurfaceResources(AllocatedSurface* surface) { |
| 194 if (!surface->texture) |
| 195 return; |
| 196 |
| 197 gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL(); |
| 198 gl->BindTexture(texture_target_, surface->texture); |
| 199 gl->ReleaseTexImage2DCHROMIUM(texture_target_, surface->image); |
| 200 gl->DeleteTextures(1, &surface->texture); |
| 201 gl->DestroyImageCHROMIUM(surface->image); |
| 202 surface->buffer.reset(); |
| 203 allocated_count_--; |
| 204 } |
| 205 |
| 206 std::unique_ptr<BufferQueue::AllocatedSurface> BufferQueue::GetNextSurface() { |
| 207 if (!available_surfaces_.empty()) { |
| 208 std::unique_ptr<AllocatedSurface> surface = |
| 209 std::move(available_surfaces_.back()); |
| 210 available_surfaces_.pop_back(); |
| 211 return surface; |
| 212 } |
| 213 |
| 214 unsigned int texture = 0; |
| 215 gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL(); |
| 216 gl->GenTextures(1, &texture); |
| 217 if (!texture) |
| 218 return nullptr; |
| 219 |
| 220 // We don't want to allow anything more than triple buffering. |
| 221 DCHECK_LT(allocated_count_, 4U); |
| 222 |
| 223 std::unique_ptr<gfx::GpuMemoryBuffer> buffer( |
| 224 gpu_memory_buffer_manager_->AllocateGpuMemoryBuffer( |
| 225 size_, gpu::DefaultBufferFormatForImageFormat(internal_format_), |
| 226 gfx::BufferUsage::SCANOUT, surface_id_)); |
| 227 if (!buffer.get()) { |
| 228 gl->DeleteTextures(1, &texture); |
| 229 DLOG(ERROR) << "Failed to allocate GPU memory buffer"; |
| 230 return nullptr; |
| 231 } |
| 232 |
| 233 unsigned int id = gl->CreateImageCHROMIUM( |
| 234 buffer->AsClientBuffer(), size_.width(), size_.height(), |
| 235 internal_format_); |
| 236 if (!id) { |
| 237 LOG(ERROR) << "Failed to allocate backing image surface"; |
| 238 gl->DeleteTextures(1, &texture); |
| 239 return nullptr; |
| 240 } |
| 241 |
| 242 allocated_count_++; |
| 243 gl->BindTexture(texture_target_, texture); |
| 244 gl->BindTexImage2DCHROMIUM(texture_target_, id); |
| 245 return base::WrapUnique(new AllocatedSurface(this, std::move(buffer), texture, |
| 246 id, gfx::Rect(size_))); |
| 247 } |
| 248 |
| 249 BufferQueue::AllocatedSurface::AllocatedSurface( |
| 250 BufferQueue* buffer_queue, |
| 251 std::unique_ptr<gfx::GpuMemoryBuffer> buffer, |
| 252 unsigned int texture, |
| 253 unsigned int image, |
| 254 const gfx::Rect& rect) |
| 255 : buffer_queue(buffer_queue), |
| 256 buffer(buffer.release()), |
| 257 texture(texture), |
| 258 image(image), |
| 259 damage(rect) {} |
| 260 |
| 261 BufferQueue::AllocatedSurface::~AllocatedSurface() { |
| 262 buffer_queue->FreeSurfaceResources(this); |
| 263 } |
| 264 |
| 265 } // namespace content |
OLD | NEW |