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/viz/service/display_embedder/buffer_queue.h"
6 
7 #include <utility>
8 
9 #include "base/containers/adapters.h"
10 #include "build/build_config.h"
11 #include "components/viz/common/resources/resource_format_utils.h"
12 #include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
13 #include "gpu/command_buffer/client/shared_image_interface.h"
14 #include "gpu/command_buffer/common/shared_image_usage.h"
15 #include "gpu/command_buffer/common/sync_token.h"
16 #include "ui/gfx/gpu_memory_buffer.h"
17 
18 namespace viz {
19 
BufferQueue(gpu::SharedImageInterface * sii,gpu::SurfaceHandle surface_handle)20 BufferQueue::BufferQueue(gpu::SharedImageInterface* sii,
21                          gpu::SurfaceHandle surface_handle)
22     : sii_(sii),
23       allocated_count_(0),
24       surface_handle_(surface_handle) {}
25 
~BufferQueue()26 BufferQueue::~BufferQueue() {
27   FreeAllSurfaces();
28 }
29 
SetSyncTokenProvider(SyncTokenProvider * sync_token_provider)30 void BufferQueue::SetSyncTokenProvider(SyncTokenProvider* sync_token_provider) {
31   DCHECK(!sync_token_provider_);
32   sync_token_provider_ = sync_token_provider;
33 }
34 
GetCurrentBuffer(gpu::SyncToken * creation_sync_token)35 gpu::Mailbox BufferQueue::GetCurrentBuffer(
36     gpu::SyncToken* creation_sync_token) {
37   DCHECK(creation_sync_token);
38   if (!current_surface_)
39     current_surface_ = GetNextSurface(creation_sync_token);
40   return current_surface_ ? current_surface_->mailbox : gpu::Mailbox();
41 }
42 
UpdateBufferDamage(const gfx::Rect & damage)43 void BufferQueue::UpdateBufferDamage(const gfx::Rect& damage) {
44   if (displayed_surface_)
45     displayed_surface_->damage.Union(damage);
46   for (auto& surface : available_surfaces_)
47     surface->damage.Union(damage);
48   for (auto& surface : in_flight_surfaces_) {
49     if (surface)
50       surface->damage.Union(damage);
51   }
52 }
53 
CurrentBufferDamage() const54 gfx::Rect BufferQueue::CurrentBufferDamage() const {
55   DCHECK(current_surface_);
56   return current_surface_->damage;
57 }
58 
SwapBuffers(const gfx::Rect & damage)59 void BufferQueue::SwapBuffers(const gfx::Rect& damage) {
60   UpdateBufferDamage(damage);
61   if (current_surface_)
62     current_surface_->damage = gfx::Rect();
63   in_flight_surfaces_.push_back(std::move(current_surface_));
64 }
65 
Reshape(const gfx::Size & size,const gfx::ColorSpace & color_space,gfx::BufferFormat format)66 bool BufferQueue::Reshape(const gfx::Size& size,
67                           const gfx::ColorSpace& color_space,
68                           gfx::BufferFormat format) {
69   if (size == size_ && color_space == color_space_ && format == format_)
70     return false;
71 
72 #if !defined(OS_MACOSX)
73   // TODO(ccameron): This assert is being hit on Mac try jobs. Determine if that
74   // is cause for concern or if it is benign.
75   // http://crbug.com/524624
76   DCHECK(!current_surface_);
77 #endif
78   size_ = size;
79   color_space_ = color_space;
80   format_ = format;
81 
82   FreeAllSurfaces();
83   return true;
84 }
85 
SetMaxBuffers(size_t max)86 void BufferQueue::SetMaxBuffers(size_t max) {
87   max_buffers_ = max;
88 }
89 
PageFlipComplete()90 void BufferQueue::PageFlipComplete() {
91   DCHECK(!in_flight_surfaces_.empty());
92   if (in_flight_surfaces_.front()) {
93     if (displayed_surface_)
94       available_surfaces_.push_back(std::move(displayed_surface_));
95     displayed_surface_ = std::move(in_flight_surfaces_.front());
96   }
97 
98   in_flight_surfaces_.pop_front();
99 }
100 
FreeAllSurfaces()101 void BufferQueue::FreeAllSurfaces() {
102   DCHECK(sync_token_provider_);
103   const gpu::SyncToken destruction_sync_token =
104       sync_token_provider_->GenSyncToken();
105   FreeSurface(std::move(displayed_surface_), destruction_sync_token);
106   FreeSurface(std::move(current_surface_), destruction_sync_token);
107 
108   // This is intentionally not emptied since the swap buffers acks are still
109   // expected to arrive.
110   for (auto& surface : in_flight_surfaces_) {
111     FreeSurface(std::move(surface), destruction_sync_token);
112   }
113 
114   for (auto& surface : available_surfaces_) {
115     FreeSurface(std::move(surface), destruction_sync_token);
116   }
117   available_surfaces_.clear();
118 }
119 
FreeSurface(std::unique_ptr<AllocatedSurface> surface,const gpu::SyncToken & sync_token)120 void BufferQueue::FreeSurface(std::unique_ptr<AllocatedSurface> surface,
121                               const gpu::SyncToken& sync_token) {
122   if (!surface)
123     return;
124   DCHECK(!surface->mailbox.IsZero());
125   sii_->DestroySharedImage(sync_token, surface->mailbox);
126   allocated_count_--;
127 }
128 
GetNextSurface(gpu::SyncToken * creation_sync_token)129 std::unique_ptr<BufferQueue::AllocatedSurface> BufferQueue::GetNextSurface(
130     gpu::SyncToken* creation_sync_token) {
131   DCHECK(creation_sync_token);
132   if (!available_surfaces_.empty()) {
133     std::unique_ptr<AllocatedSurface> surface =
134         std::move(available_surfaces_.back());
135     available_surfaces_.pop_back();
136     return surface;
137   }
138 
139   // We don't want to allow anything more than triple buffering.
140   DCHECK_LT(allocated_count_, max_buffers_);
141 
142   DCHECK(format_);
143   const ResourceFormat format = GetResourceFormat(format_.value());
144   const gpu::Mailbox mailbox = sii_->CreateSharedImage(
145       format, size_, color_space_,
146       gpu::SHARED_IMAGE_USAGE_SCANOUT |
147           gpu::SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT,
148       surface_handle_);
149 
150   if (mailbox.IsZero()) {
151     LOG(ERROR) << "Failed to create SharedImage";
152     return nullptr;
153   }
154 
155   allocated_count_++;
156   *creation_sync_token = sii_->GenUnverifiedSyncToken();
157   return std::make_unique<AllocatedSurface>(mailbox, gfx::Rect(size_));
158 }
159 
AllocatedSurface(const gpu::Mailbox & mailbox,const gfx::Rect & rect)160 BufferQueue::AllocatedSurface::AllocatedSurface(const gpu::Mailbox& mailbox,
161                                                 const gfx::Rect& rect)
162     : mailbox(mailbox), damage(rect) {}
163 
164 BufferQueue::AllocatedSurface::~AllocatedSurface() = default;
165 
166 }  // namespace viz
167