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