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