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
|
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "gpu/command_buffer/client/shared_image_pool.h"
#include "gpu/command_buffer/client/shared_image_interface.h"
namespace {
gpu::ImageInfo GetImageInfo(scoped_refptr<gpu::ClientImage> image) {
auto shared_image = image->GetSharedImage();
return gpu::ImageInfo(
shared_image->size(), shared_image->format(), shared_image->usage(),
shared_image->color_space(), shared_image->surface_origin(),
shared_image->alpha_type(), shared_image->buffer_usage());
}
} // namespace
namespace gpu {
// Implementation of the ClientImage class.
ClientImage::ClientImage(scoped_refptr<ClientSharedImage> shared_image)
: shared_image_(std::move(shared_image)) {
CHECK(shared_image_);
sync_token_ = shared_image_->creation_sync_token();
}
ClientImage::~ClientImage() {
CHECK(shared_image_);
shared_image_->UpdateDestructionSyncToken(std::move(sync_token_));
}
const scoped_refptr<ClientSharedImage>& ClientImage::GetSharedImage() const {
return shared_image_;
}
const SyncToken& ClientImage::GetSyncToken() const {
return sync_token_;
}
void ClientImage::SetReleaseSyncToken(SyncToken release_sync_token) {
sync_token_ = std::move(release_sync_token);
}
const SharedImagePoolId& ClientImage::GetPoolIdForTesting() const {
return pool_id_;
}
SharedImagePoolBase::SharedImagePoolBase(
const SharedImagePoolId& pool_id,
const ImageInfo& image_info,
std::string_view debug_label,
const scoped_refptr<SharedImageInterface> sii,
std::optional<uint8_t> max_pool_size,
std::optional<base::TimeDelta> unused_resource_expiration_time)
: pool_id_(pool_id),
image_info_(image_info),
debug_label_(debug_label),
sii_(std::move(sii)),
max_pool_size_(std::move(max_pool_size)),
unused_resource_expiration_time_(
std::move(unused_resource_expiration_time)) {}
SharedImagePoolBase::~SharedImagePoolBase() {
ClearInternal();
}
size_t SharedImagePoolBase::GetPoolSizeForTesting() const {
return image_pool_.size();
}
bool SharedImagePoolBase::IsReclaimTimerRunningForTesting() const {
return unused_resources_reclaim_timer_.IsRunning();
}
scoped_refptr<ClientSharedImage>
SharedImagePoolBase::CreateSharedImageInternal() {
CHECK(sii_);
if (image_info_.buffer_usage.has_value()) {
// Creates a Mappable shared image. Note that eventually when shared image
// usage is merged with buffer usage, there will be only one method to
// create both mappable and non-mappable shared image. These 2 paths will be
// merged after that.
return sii_->CreateSharedImage(
{image_info_.format, image_info_.size, image_info_.color_space,
image_info_.surface_origin, image_info_.alpha_type, image_info_.usage,
debug_label_ + "Mappable"},
gpu::kNullSurfaceHandle, image_info_.buffer_usage.value());
} else {
return sii_->CreateSharedImage(
{image_info_.format, image_info_.size, image_info_.color_space,
image_info_.surface_origin, image_info_.alpha_type, image_info_.usage,
debug_label_},
gpu::kNullSurfaceHandle);
}
}
scoped_refptr<ClientImage> SharedImagePoolBase::GetImageFromPoolInternal() {
if (!image_pool_.empty()) {
auto image = image_pool_.back();
image_pool_.pop_back();
return image;
}
return nullptr;
}
void SharedImagePoolBase::ReleaseImageInternal(
scoped_refptr<ClientImage> image) {
if (!image || (GetImageInfo(image) != image_info_)) {
return;
}
// Ensure that the |image| belongs to |this| pool.
CHECK_EQ(image->pool_id_.ToString(), pool_id_.ToString());
// Ensure that there is only one reference which the current |image| and
// clients are not accidentally keeping more references alive while releasing
// this |image|.
CHECK(image->HasOneRef());
// Recycle the image into the pool only if the pool is not full or if
// |max_pool_size_| is not specified. Otherwise the last ref of |image| here
// will get destroyed automatically.
if (!max_pool_size_.has_value() ||
image_pool_.size() < max_pool_size_.value()) {
// Update the last used time.
image->last_used_time_ = base::TimeTicks::Now();
image_pool_.push_back(std::move(image));
MaybePostUnusedResourcesReclaimTask();
}
}
void SharedImagePoolBase::ClearInternal() {
image_pool_.clear();
CHECK(sii_);
// ClientSharedImage destructor calls DestroySharedImage which in turn ensures
// that the deferred destroy request is flushed. Thus, clients don't need to
// call SharedImageInterface::Flush explicitly.
}
void SharedImagePoolBase::ReconfigureInternal(const ImageInfo& image_info) {
if (image_info_ == image_info) {
return;
}
// If ImageInfo does not matches, we clear the existing images.
ClearInternal();
image_info_ = image_info;
}
void SharedImagePoolBase::MaybePostUnusedResourcesReclaimTask() {
if (!unused_resources_reclaim_timer_.IsRunning() && !image_pool_.empty() &&
unused_resource_expiration_time_.has_value()) {
unused_resources_reclaim_timer_.Start(
FROM_HERE, unused_resource_expiration_time_.value(),
base::BindOnce(&SharedImagePoolBase::ClearOldUnusedResources,
base::Unretained(this)));
}
}
void SharedImagePoolBase::ClearOldUnusedResources() {
CHECK(unused_resource_expiration_time_.has_value());
// Get the current time.
auto now = base::TimeTicks::Now();
// Clear the resources that have expired.
// Remove elements that satisfy the predicate by using std::remove_if and
// erase.
auto new_end =
std::remove_if(image_pool_.begin(), image_pool_.end(),
[this, now](const scoped_refptr<ClientImage>& resource) {
return now - resource->last_used_time_ >=
unused_resource_expiration_time_.value();
});
// Erase the "removed" elements from the vector.
image_pool_.erase(new_end, image_pool_.end());
// ClientSharedImage destructor calls DestroySharedImage which in turn ensures
// that the deferred destroy request is flushed. Thus, clients don't need to
// call SharedImageInterface::Flush explicitly.
// Reclaim unused resource again.
MaybePostUnusedResourcesReclaimTask();
}
} // namespace gpu
|