1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
media / gpu / android / pooled_shared_image_video_provider.cc [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/gpu/android/pooled_shared_image_video_provider.h"
#include "base/memory/ptr_util.h"
#include "base/task/bind_post_task.h"
#include "base/task/single_thread_task_runner.h"
#include "gpu/command_buffer/common/sync_token.h"
#include "mojo/public/cpp/bindings/callback_helpers.h"
namespace media {
// static
std::unique_ptr<PooledSharedImageVideoProvider>
PooledSharedImageVideoProvider::Create(
scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
GetStubCB get_stub_cb,
std::unique_ptr<SharedImageVideoProvider> provider,
scoped_refptr<gpu::RefCountedLock> drdc_lock) {
return base::WrapUnique(new PooledSharedImageVideoProvider(
base::SequenceBound<GpuHelperImpl>(std::move(gpu_task_runner),
std::move(get_stub_cb)),
std::move(provider), std::move(drdc_lock)));
}
PooledSharedImageVideoProvider::PooledImage::PooledImage(const ImageSpec& spec,
ImageRecord record)
: spec(spec), record(std::move(record)) {}
PooledSharedImageVideoProvider::PooledImage::~PooledImage() = default;
PooledSharedImageVideoProvider::PendingRequest::PendingRequest(
const ImageSpec& spec,
ImageReadyCB cb)
: spec(spec), cb(std::move(cb)) {}
PooledSharedImageVideoProvider::PendingRequest::~PendingRequest() = default;
PooledSharedImageVideoProvider::PooledSharedImageVideoProvider(
base::SequenceBound<GpuHelper> gpu_helper,
std::unique_ptr<SharedImageVideoProvider> provider,
scoped_refptr<gpu::RefCountedLock> drdc_lock)
: gpu::RefCountedLockHelperDrDc(std::move(drdc_lock)),
provider_(std::move(provider)),
gpu_helper_(std::move(gpu_helper)),
weak_factory_(this) {}
// Note that this will drop everything in |pool_|, which will call all the
// release callbacks for the underlying byffer.
PooledSharedImageVideoProvider::~PooledSharedImageVideoProvider() = default;
// SharedImageVideoProvider
void PooledSharedImageVideoProvider::Initialize(GpuInitCB gpu_init_cb) {
provider_->Initialize(std::move(gpu_init_cb));
}
void PooledSharedImageVideoProvider::RequestImage(ImageReadyCB cb,
const ImageSpec& spec) {
// See if the pool matches the requested spec.
if (pool_spec_ != spec) {
// Nope -- mark any outstanding images for destruction and start a new pool.
// Note that this calls all the release callbacks.
pool_.clear();
// Any images added to the pool should match |spec|.
pool_spec_ = spec;
}
// Push this onto the pending requests.
// IMPORTANT BUT SUBTLE NOTE: |spec| doesn't mention the TextureOwner, but it
// is sent to the provider so it must also match the one that was used with
// |spec|. We assume that the generation id will be updated by our caller
// whenever the TextureOwner changes. While this is fragile, it's also just
// a temporary thing. Keeping a strong ref to |texture_owner| would probably
// work, but it's not good to keep refs to those around longer than needed.
// It might be okay to do that directly, since the request (if any) that's
// pending for it would have the strong ref, so maybe we could just add it
// here too.
pending_requests_.emplace_back(spec, std::move(cb));
// Are there any free images in the pool? If so, then pop one and use it to
// process the request we just pushed, assuming that it's the most recent. We
// could optimize this call out if |pending_requensts_| wasn't empty before,
// since we know it doesn't match the pool spec if the pool's not empty. As
// it is, it will just pop and re-push the pooled buffer in the (rare) case
// that the pool doesn't match.
if (!pool_.empty()) {
auto front = std::move(pool_.front());
pool_.pop_front();
ProcessFreePooledImage(front);
// TODO(liberato): See if skipping the return if |pool_| is now empty is
// helpful, especially during start-up. Alternatively, just request some
// constant number of images (~5) when the pool spec changes, then add them
// one at a time if needed.
return;
}
// Request a new image, since we don't have enough. There might be some
// outstanding that will be returned, but we'd like to have enough not to wait
// on them. This has the nice property that everything in |pending_requests_|
// will have an image delivered in order for it. Note that we might not
// exactly match up returned (new) images to the requests; there might be
// intervening returns of existing images from the client that happen to match
// if we switch from spec A => spec B => spec A, but that's okay. We can be
// sure that there are at least as many that will arrive as we need.
auto ready_cb =
base::BindOnce(&PooledSharedImageVideoProvider::OnImageCreated,
weak_factory_.GetWeakPtr(), spec);
provider_->RequestImage(std::move(ready_cb), spec);
}
void PooledSharedImageVideoProvider::OnImageCreated(ImageSpec spec,
ImageRecord record) {
// Wrap |record| up for the pool, and process it.
scoped_refptr<PooledImage> pooled_image =
base::MakeRefCounted<PooledImage>(std::move(spec), std::move(record));
ProcessFreePooledImage(pooled_image);
}
void PooledSharedImageVideoProvider::OnImageReturned(
scoped_refptr<PooledImage> pooled_image,
const gpu::SyncToken& sync_token) {
// An image has been returned to us. Wait for |sync_token| and then send it
// to ProcessFreePooledImage to re-use / pool / delete.
gpu_helper_.AsyncCall(&GpuHelper::OnImageReturned)
.WithArgs(sync_token, pooled_image->record.codec_image_holder,
base::BindPostTaskToCurrentDefault(base::BindOnce(
&PooledSharedImageVideoProvider::ProcessFreePooledImage,
weak_factory_.GetWeakPtr(), pooled_image)),
GetDrDcLock());
}
void PooledSharedImageVideoProvider::ProcessFreePooledImage(
scoped_refptr<PooledImage> pooled_image) {
// Are there any requests pending?
if (pending_requests_.size()) {
// See if |record| matches the top request. If so, fulfill it, else drop
// |record| since we don't need it. Note that it's possible to have pending
// requests that don't match the pool spec; the pool spec is the most recent
// request. There might be other ones that were made before that which we
// didn't fulfill yet.
auto& front = pending_requests_.front();
if (pooled_image->spec == front.spec) {
// Construct a record that notifies us when the image is released.
// TODO(liberato): Don't copy fields this way.
ImageRecord record;
record.shared_image = pooled_image->record.shared_image;
record.is_vulkan = pooled_image->record.is_vulkan;
record.codec_image_holder = pooled_image->record.codec_image_holder;
// The release CB notifies us instead of |provider_|.
record.release_cb = mojo::WrapCallbackWithDefaultInvokeIfNotRun(
base::BindOnce(&PooledSharedImageVideoProvider::OnImageReturned,
weak_factory_.GetWeakPtr(), std::move(pooled_image)),
gpu::SyncToken());
// Save the callback and remove the request, in case |cb| calls us back.
auto cb = std::move(front.cb);
pending_requests_.pop_front();
std::move(cb).Run(std::move(record));
return;
}
// Can't fulfill the topmost request. Discard |pooled_image|, even if it
// matches the pool. The reason is that any pending requests will have
// images created for them, which we'll use when they arrive. It would be
// okay to store |pooled_image| in the pool if it matches, but then we'd
// have more pooled images than we expect.
return;
}
// There are no outstanding image requests, or the top one doesn't match
// |pooled_image|. If this image is compatible with the pool, then pool it.
// Otherwise, discard it.
// See if |record| matches |pool_spec_|. If not, then drop it. Otherwise,
// pool it for later. Note that we don't explicitly call the release cb,
// since dropping the image is sufficient to notify |provider_|. Note that
// we've already waited for any sync token at this point, so it's okay if we
// don't provide one to the underlying release cb.
if (pool_spec_ != pooled_image->spec)
return;
// Add it to the pool.
pool_.push_front(std::move(pooled_image));
}
PooledSharedImageVideoProvider::GpuHelperImpl::GpuHelperImpl(
GetStubCB get_stub_cb)
: weak_factory_(this) {
gpu::CommandBufferStub* stub = get_stub_cb.Run();
if (stub) {
command_buffer_helper_ = CommandBufferHelper::Create(stub);
}
}
PooledSharedImageVideoProvider::GpuHelperImpl::~GpuHelperImpl() = default;
void PooledSharedImageVideoProvider::GpuHelperImpl::OnImageReturned(
const gpu::SyncToken& sync_token,
scoped_refptr<CodecImageHolder> codec_image_holder,
base::OnceClosure cb,
scoped_refptr<gpu::RefCountedLock> drdc_lock) {
auto on_sync_token_cleared_cb = base::BindOnce(
&GpuHelperImpl::OnSyncTokenCleared, weak_factory_.GetWeakPtr(),
std::move(codec_image_holder), std::move(cb), std::move(drdc_lock));
command_buffer_helper_->WaitForSyncToken(sync_token,
std::move(on_sync_token_cleared_cb));
}
void PooledSharedImageVideoProvider::GpuHelperImpl::OnSyncTokenCleared(
scoped_refptr<CodecImageHolder> codec_image_holder,
base::OnceClosure cb,
scoped_refptr<gpu::RefCountedLock> drdc_lock) {
{
base::AutoLockMaybe auto_lock(drdc_lock ? drdc_lock->GetDrDcLockPtr()
: nullptr);
codec_image_holder->codec_image_raw()->NotifyUnused();
}
// Do this last, since |cb| might post to some other thread.
std::move(cb).Run();
}
} // namespace media