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