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