blob: e7b82b14ebf2352cee23b5097e55f34e12bbd577 [file] [log] [blame]
// 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