#ifndef CC_RESOURCES_RESOURCE_POOL_H_
#define CC_RESOURCES_RESOURCE_POOL_H_
#include <stddef.h>
#include <stdint.h>
#include <map>
#include <memory>
#include <string>
#include <utility>
#include "base/containers/circular_deque.h"
#include "base/gtest_prod_util.h"
#include "base/memory/memory_pressure_listener.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/tick_clock.h"
#include "base/time/time.h"
#include "base/trace_event/memory_allocator_dump_guid.h"
#include "base/trace_event/memory_dump_provider.h"
#include "cc/cc_export.h"
#include "components/viz/common/resources/resource_id.h"
#include "components/viz/common/resources/shared_image_format.h"
#include "components/viz/common/resources/transferable_resource.h"
#include "gpu/command_buffer/client/client_shared_image.h"
#include "gpu/command_buffer/client/shared_image_interface.h"
#include "gpu/command_buffer/common/sync_token.h"
#include "ui/gfx/color_space.h"
#include "ui/gfx/geometry/rect.h"
namespace base {
class SingleThreadTaskRunner;
}
namespace viz {
class ClientResourceProvider;
class RasterContextProvider;
}
namespace cc {
class CC_EXPORT ResourcePool : public base::trace_event::MemoryDumpProvider,
public base::MemoryPressureListener {
class PoolResource;
public:
static constexpr base::TimeDelta kDefaultExpirationDelay = base::Seconds(5);
static constexpr base::TimeDelta kDefaultMaxFlushDelay = base::Seconds(1);
#if BUILDFLAG(IS_ARKWEB_EXT)
static constexpr base::TimeDelta kDefaultMaxExpirationDelay = base::Seconds(60);
static constexpr size_t kUnusedResourcesToKeep = 12;
#endif
class CC_EXPORT Backing {
public:
Backing(const gfx::Size& size,
viz::SharedImageFormat format,
const gfx::ColorSpace& color_space);
virtual ~Backing();
void CreateSharedImage(gpu::SharedImageInterface* sii,
const gpu::SharedImageUsageSet& usage,
std::string_view debug_label);
void CreateSharedImageForSoftwareCompositor(gpu::SharedImageInterface* sii,
std::string_view debug_label);
bool CreateSharedImage(gpu::SharedImageInterface* sii,
const gpu::SharedImageUsageSet& usage,
std::string_view debug_label,
gfx::BufferUsage buffer_usage);
void CreateSharedImageForTesting();
void CreateSharedImageForTesting(uint32_t texture_target);
void clear_shared_image() { shared_image_.reset(); }
scoped_refptr<gpu::ClientSharedImage> shared_image() {
return shared_image_;
}
const gfx::Size& size() const { return size_; }
const viz::SharedImageFormat& format() const { return format_; }
const gfx::ColorSpace& color_space() const { return color_space_; }
bool can_access_shared_image_on_compositor_thread = true;
gpu::SyncToken mailbox_sync_token;
bool wait_on_fence_required = false;
gpu::SyncToken returned_sync_token;
bool is_using_raw_draw = false;
private:
scoped_refptr<gpu::ClientSharedImage> shared_image_;
const gfx::Size size_;
const viz::SharedImageFormat format_;
const gfx::ColorSpace color_space_;
};
class CC_EXPORT InUsePoolResource {
public:
InUsePoolResource() = default;
~InUsePoolResource() {
DCHECK(!resource_) << "Must be returned to ResourcePool to be freed.";
}
InUsePoolResource(InUsePoolResource&& other) {
resource_ = other.resource_;
other.resource_ = nullptr;
}
InUsePoolResource& operator=(InUsePoolResource&& other) {
resource_ = other.resource_;
other.resource_ = nullptr;
return *this;
}
InUsePoolResource(const InUsePoolResource&) = delete;
InUsePoolResource& operator=(const InUsePoolResource&) = delete;
explicit operator bool() const { return !!resource_; }
const gfx::Size& size() const { return resource_->size(); }
const viz::SharedImageFormat& format() const { return resource_->format(); }
const gfx::ColorSpace& color_space() const {
return resource_->color_space();
}
const viz::ResourceId& resource_id_for_export() const {
DCHECK(resource_->resource_id());
return resource_->resource_id();
}
Backing* backing() const { return resource_->backing(); }
void set_backing(std::unique_ptr<Backing> backing) const {
return resource_->set_backing(std::move(backing));
}
void InstallGpuBacking(gpu::SharedImageInterface* sii,
bool is_overlay_candidate,
std::string_view debug_label) const;
void InstallSoftwareBacking(scoped_refptr<gpu::SharedImageInterface> sii,
std::string_view debug_label) const;
size_t memory_usage() const {
DCHECK(resource_);
return resource_->memory_usage();
}
size_t unique_id_for_testing() const { return resource_->unique_id(); }
private:
friend ResourcePool;
explicit InUsePoolResource(PoolResource* resource) : resource_(resource) {
DCHECK_EQ(resource->state(), PoolResource::kInUse);
}
void SetWasFreedByResourcePool() { resource_ = nullptr; }
RAW_PTR_EXCLUSION PoolResource* resource_ = nullptr;
};
ResourcePool(viz::ClientResourceProvider* resource_provider,
viz::RasterContextProvider* context_provider,
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
const base::TimeDelta& expiration_delay,
bool disallow_non_exact_reuse);
ResourcePool(const ResourcePool&) = delete;
~ResourcePool() override;
ResourcePool& operator=(const ResourcePool&) = delete;
InUsePoolResource AcquireResource(
const gfx::Size& size,
viz::SharedImageFormat format,
const gfx::ColorSpace& color_space,
const std::string& debug_name = std::string());
InUsePoolResource TryAcquireResourceForPartialRaster(
uint64_t new_content_id,
const gfx::Rect& new_invalidated_rect,
uint64_t previous_content_id,
gfx::Rect* total_invalidated_rect,
const gfx::ColorSpace& raster_color_space,
const std::string& debug_name = std::string());
bool PrepareForExport(
const InUsePoolResource& resource,
viz::TransferableResource::ResourceSource resource_source);
void InvalidateResources();
void OnContentReplaced(const InUsePoolResource& in_use_resource,
uint64_t content_id);
void ReleaseResource(InUsePoolResource resource);
void SetResourceUsageLimits(size_t max_memory_usage_bytes,
size_t max_resource_count);
void ReduceResourceUsage();
bool ResourceUsageTooHigh();
size_t memory_usage_bytes() const {
return total_memory_usage_bytes_ - unused_memory_usage_bytes_;
}
size_t resource_count() const { return in_use_resources_.size(); }
bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
base::trace_event::ProcessMemoryDump* pmd) override;
void OnMemoryPressure(base::MemoryPressureLevel level) override;
size_t GetTotalMemoryUsageForTesting() const {
return total_memory_usage_bytes_;
}
size_t GetTotalResourceCountForTesting() const {
return total_resource_count_;
}
size_t GetBusyResourceCountForTesting() const {
return busy_resources_.size();
}
bool AllowsNonExactReUseForTesting() const {
return !disallow_non_exact_reuse_;
}
void SetClockForTesting(const base::TickClock* clock) { clock_ = clock; }
int tracing_id() const { return tracing_id_; }
#if BUILDFLAG(IS_ARKWEB_EXT)
void EnableDeleteUnusedResourcesDelay(bool enable);
#endif
private:
FRIEND_TEST_ALL_PREFIXES(ResourcePoolTest, ReuseResource);
FRIEND_TEST_ALL_PREFIXES(ResourcePoolTest, ExactRequestsRespected);
class PoolResource {
public:
PoolResource(ResourcePool* resource_pool,
size_t unique_id,
const gfx::Size& size,
viz::SharedImageFormat format,
const gfx::ColorSpace& color_space);
~PoolResource();
size_t unique_id() const { return unique_id_; }
const gfx::Size& size() const { return size_; }
const viz::SharedImageFormat& format() const { return format_; }
const gfx::ColorSpace& color_space() const { return color_space_; }
const viz::ResourceId& resource_id() const { return resource_id_; }
void set_resource_id(viz::ResourceId id) { resource_id_ = id; }
Backing* backing() const { return backing_.get(); }
void set_backing(std::unique_ptr<Backing> backing) {
DCHECK(backing);
DCHECK(!backing_);
backing_ = std::move(backing);
resource_pool_->OnBackingAllocated(this);
}
uint64_t content_id() const { return content_id_; }
void set_content_id(uint64_t content_id) { content_id_ = content_id; }
base::TimeTicks last_usage() const { return last_usage_; }
void set_last_usage(base::TimeTicks time) { last_usage_ = time; }
const gfx::Rect& invalidated_rect() const { return invalidated_rect_; }
void set_invalidated_rect(const gfx::Rect& invalidated_rect) {
invalidated_rect_ = invalidated_rect;
}
bool avoid_reuse() const { return avoid_reuse_; }
void mark_avoid_reuse() { avoid_reuse_ = true; }
void OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd,
int tracing_id,
const viz::ClientResourceProvider* resource_provider,
bool is_free,
bool is_busy) const;
void set_debug_name(const std::string& name) { debug_name_ = name; }
const std::string& debug_name() const { return debug_name_; }
ResourcePool* resource_pool() const { return resource_pool_; }
enum State {
kUnused,
kInUse,
kBusy,
};
State state() const { return state_; }
void set_state(State state) { state_ = state; }
size_t memory_usage() const {
if (!backing_) {
return 0;
}
size_t memory_usage = format().EstimatedSizeInBytes(size());
if (backing_->is_using_raw_draw) {
memory_usage = memory_usage / 2;
}
return memory_usage;
}
private:
const raw_ptr<ResourcePool> resource_pool_;
const size_t unique_id_;
const gfx::Size size_;
const viz::SharedImageFormat format_;
const gfx::ColorSpace color_space_;
uint64_t content_id_ = 0;
base::TimeTicks last_usage_;
gfx::Rect invalidated_rect_;
bool avoid_reuse_ = false;
viz::ResourceId resource_id_ = viz::kInvalidResourceId;
std::unique_ptr<Backing> backing_;
std::string debug_name_;
State state_ = kUnused;
};
void OnBackingAllocated(PoolResource* resource);
void OnResourceReleased(size_t unique_id,
const gpu::SyncToken& sync_token,
bool lost);
PoolResource* ReuseResource(const gfx::Size& size,
viz::SharedImageFormat format,
const gfx::ColorSpace& color_space);
PoolResource* CreateResource(const gfx::Size& size,
viz::SharedImageFormat format,
const gfx::ColorSpace& color_space);
void DidFinishUsingResource(std::unique_ptr<PoolResource> resource);
void DeleteResource(std::unique_ptr<PoolResource> resource);
static void UpdateResourceContentIdAndInvalidation(
PoolResource* resource,
uint64_t new_content_id,
const gfx::Rect& new_invalidated_rect);
void ScheduleEvictExpiredResourcesIn(base::TimeDelta time_from_now);
void EvictExpiredResources();
void EvictResourcesNotUsedSince(base::TimeTicks time_limit);
bool HasEvictableResources() const;
base::TimeTicks GetUsageTimeForLRUResource() const;
void FlushEvictedResources();
const raw_ptr<viz::ClientResourceProvider> resource_provider_;
const raw_ptr<viz::RasterContextProvider> context_provider_;
const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
const base::TimeDelta resource_expiration_delay_;
const bool disallow_non_exact_reuse_ = false;
const int tracing_id_;
size_t next_resource_unique_id_ = 1;
size_t max_memory_usage_bytes_ = 0;
size_t max_resource_count_ = 0;
size_t unused_memory_usage_bytes_ = 0;
size_t total_memory_usage_bytes_ = 0;
size_t total_resource_count_ = 0;
bool evict_expired_resources_pending_ = false;
bool evict_busy_resources_when_unused_ = false;
base::circular_deque<std::unique_ptr<PoolResource>> unused_resources_;
base::circular_deque<std::unique_ptr<PoolResource>> busy_resources_;
std::map<size_t, std::unique_ptr<PoolResource>> in_use_resources_;
std::unique_ptr<base::AsyncMemoryPressureListenerRegistration>
memory_pressure_listener_registration_;
base::TimeTicks flush_evicted_resources_deadline_;
raw_ptr<const base::TickClock> clock_;
#if BUILDFLAG(IS_ARKWEB_EXT)
bool delete_unused_resources_delay_enabled_ = false;
#endif
base::WeakPtrFactory<ResourcePool> weak_ptr_factory_{this};
};
}
#endif