blob: 3aedcd750357c3510014b8620c71741c28d1ac46 [file] [log] [blame]
// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#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 "base/unguessable_token.h"
#include "cc/cc_export.h"
#include "components/viz/common/resources/resource_format.h"
#include "components/viz/common/resources/resource_id.h"
#include "components/viz/common/resources/resource_sizes.h"
#include "components/viz/common/resources/shared_bitmap.h"
#include "gpu/command_buffer/common/sync_token.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "ui/gfx/color_space.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/gpu_memory_buffer.h"
namespace base {
class SingleThreadTaskRunner;
}
namespace gpu {
struct Capabilities;
}
namespace viz {
class ClientResourceProvider;
class ContextProvider;
}
namespace cc {
class CC_EXPORT ResourcePool : public base::trace_event::MemoryDumpProvider {
class PoolResource;
public:
// Delay before a resource is considered expired.
static constexpr base::TimeDelta kDefaultExpirationDelay = base::Seconds(5);
// Max delay before an evicted resource is flushed.
static constexpr base::TimeDelta kDefaultMaxFlushDelay = base::Seconds(1);
// A base class to hold ownership of gpu backed PoolResources. Allows the
// client to define destruction semantics.
class GpuBacking {
public:
virtual ~GpuBacking() = default;
// Dumps information about the memory backing the GpuBacking to |pmd|.
// The memory usage is attributed to |buffer_dump_guid|.
// |tracing_process_id| uniquely identifies the process owning the memory.
// |importance| is relevant only for the cases of co-ownership, the memory
// gets attributed to the owner with the highest importance.
// Called on the compositor thread.
virtual void OnMemoryDump(
base::trace_event::ProcessMemoryDump* pmd,
const base::trace_event::MemoryAllocatorDumpGuid& buffer_dump_guid,
uint64_t tracing_process_id,
int importance) const = 0;
void InitOverlayCandidateAndTextureTarget(
const viz::ResourceFormat format,
const gpu::Capabilities& caps,
bool use_gpu_memory_buffer_resources);
gpu::Mailbox mailbox;
gpu::SyncToken mailbox_sync_token;
GLenum texture_target = 0;
bool overlay_candidate = false;
// For resources that are modified directly on the gpu, outside the command
// stream, a fence must be used to know when the backing is not in use and
// may be returned to and reused by the pool.
bool wait_on_fence_required = false;
// Set by the ResourcePool when a resource is returned from the display
// compositor, or when the resource texture and mailbox are created for the
// first time, if the resource is shared with another context. The client of
// ResourcePool needs to wait on this token if it exists, before using a
// resource handed out by the ResourcePool.
gpu::SyncToken returned_sync_token;
// True if the backing is using raw draw.
bool is_using_raw_draw = false;
};
// A base class to hold ownership of software backed PoolResources. Allows the
// client to define destruction semantics.
class SoftwareBacking {
public:
virtual ~SoftwareBacking() = default;
// Dumps information about the memory backing the SoftwareBacking to |pmd|.
// The memory usage is attributed to |buffer_dump_guid|.
// |tracing_process_id| uniquely identifies the process owning the memory.
// |importance| is relevant only for the cases of co-ownership, the memory
// gets attributed to the owner with the highest importance.
// Called on the compositor thread.
virtual void OnMemoryDump(
base::trace_event::ProcessMemoryDump* pmd,
const base::trace_event::MemoryAllocatorDumpGuid& buffer_dump_guid,
uint64_t tracing_process_id,
int importance) const = 0;
viz::SharedBitmapId shared_bitmap_id;
};
// Scoped move-only object returned when getting a resource from the pool.
// Ownership must be given back to the pool to release the resource.
class InUsePoolResource {
public:
InUsePoolResource() = default;
~InUsePoolResource() {
DCHECK(!resource_) << "Must be returned to ResourcePool to be freed.";
}
InUsePoolResource(InUsePoolResource&& other) {
is_gpu_ = other.is_gpu_;
resource_ = other.resource_;
other.resource_ = nullptr;
}
InUsePoolResource& operator=(InUsePoolResource&& other) {
is_gpu_ = other.is_gpu_;
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::ResourceFormat& format() const { return resource_->format(); }
const gfx::ColorSpace& color_space() const {
return resource_->color_space();
}
// The ResourceId when the backing is given to the ResourceProvider for
// export to the display compositor.
const viz::ResourceId& resource_id_for_export() const {
// The ResourceId should not be accessed before it is created!
DCHECK(resource_->resource_id());
return resource_->resource_id();
}
// Only valid when the ResourcePool is vending texture-backed resources.
GpuBacking* gpu_backing() const {
DCHECK(is_gpu_);
return resource_->gpu_backing();
}
void set_gpu_backing(std::unique_ptr<GpuBacking> gpu) const {
DCHECK(is_gpu_);
return resource_->set_gpu_backing(std::move(gpu));
}
// Only valid when the ResourcePool is vending software-backed resources.
SoftwareBacking* software_backing() const {
DCHECK(!is_gpu_);
return resource_->software_backing();
}
void set_software_backing(std::unique_ptr<SoftwareBacking> software) const {
DCHECK(!is_gpu_);
resource_->set_software_backing(std::move(software));
}
size_t memory_usage() const {
DCHECK(resource_);
return resource_->memory_usage();
}
// Production code should not be built around these ids, but tests use them
// to check for identity.
size_t unique_id_for_testing() const { return resource_->unique_id(); }
private:
friend ResourcePool;
explicit InUsePoolResource(PoolResource* resource, bool is_gpu)
: is_gpu_(is_gpu), resource_(resource) {
DCHECK_EQ(resource->state(), PoolResource::kInUse);
}
void SetWasFreedByResourcePool() { resource_ = nullptr; }
bool is_gpu_ = false;
// `resource_` is not a raw_ptr<...> for performance reasons (based on
// analysis of sampling profiler data and tab_search:top100:2020).
PoolResource* resource_ = nullptr;
};
// When holding gpu resources, the |context_provider| should be non-null,
// and when holding software resources, it should be null. It is used for
// consistency checking as well as for correctness.
ResourcePool(viz::ClientResourceProvider* resource_provider,
viz::ContextProvider* 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;
// Tries to reuse a resource. If none are available, makes a new one.
InUsePoolResource AcquireResource(
const gfx::Size& size,
viz::ResourceFormat format,
const gfx::ColorSpace& color_space,
const std::string& debug_name = std::string());
// Tries to acquire the resource with |previous_content_id| for us in partial
// raster. If successful, this function will retun the invalidated rect which
// must be re-rastered in |total_invalidated_rect|.
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());
// Gives the InUsePoolResource a |resource_id_for_export()| in order to allow
// exporting of the resource to the display compositor. This must be called
// with a resource only after it has a backing allocated for it. Initially an
// acquired InUsePoolResource will be only metadata, and the backing is given
// to it by code which is aware of the expected backing type - currently by
// RasterBufferProvider::AcquireBufferForRaster().
// Returns false if the backing does not contain valid data, in particular
// a zero mailbox for GpuBacking, in which case the resource is not exported,
// and true otherwise.
bool PrepareForExport(const InUsePoolResource& resource);
// Marks any resources in the pool as invalid, preventing their reuse. Call if
// previous resources were allocated in one way, but future resources should
// be allocated in a different way.
void InvalidateResources();
// Called when a resource's content has been fully replaced (and is completely
// valid). Updates the resource's content ID to its new value.
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 in_use_memory_usage_bytes_; }
size_t resource_count() const { return in_use_resources_.size(); }
// Overridden from base::trace_event::MemoryDumpProvider:
bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
base::trace_event::ProcessMemoryDump* pmd) override;
void OnMemoryPressure(
base::MemoryPressureListener::MemoryPressureLevel level);
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_;
}
// Overrides internal clock for testing purposes.
void SetClockForTesting(const base::TickClock* clock) { clock_ = clock; }
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::ResourceFormat format,
const gfx::ColorSpace& color_space);
~PoolResource();
size_t unique_id() const { return unique_id_; }
const gfx::Size& size() const { return size_; }
const viz::ResourceFormat& 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; }
GpuBacking* gpu_backing() const { return gpu_backing_.get(); }
void set_gpu_backing(std::unique_ptr<GpuBacking> gpu) {
DCHECK(gpu);
DCHECK(!gpu_backing_);
DCHECK(!software_backing_);
gpu_backing_ = std::move(gpu);
resource_pool_->OnBackingAllocated(this);
}
SoftwareBacking* software_backing() const {
return software_backing_.get();
}
void set_software_backing(std::unique_ptr<SoftwareBacking> software) {
DCHECK(software);
DCHECK(!gpu_backing_);
DCHECK(!software_backing_);
software_backing_ = std::move(software);
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) 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 means the resource is free for reusing or releasing.
// A new created resource is in kUnused as well.
kUnused,
// kInUse means the resource is being used viz InUsePoolResource.
// The InUsePoolResource can be released by calling
// ResourcePool::ReleaseResource(), after that, the state will be changed
// to kBusy or kUnused depends on if the resource is exported.
kInUse,
// The resource has been expored (sent) to viz process for compositing.
// When the resource is returned from the viz, the state will be changed
// to kUnused.
kBusy,
};
State state() const { return state_; }
void set_state(State state) { state_ = state; }
size_t memory_usage() const {
if (!gpu_backing_ && !software_backing_)
return 0;
size_t memory_usage =
viz::ResourceSizes::UncheckedSizeInBytes<size_t>(size(), format());
// Early research found with raw draw, GPU memory usage is reduced to
// 50%, so we consider a raw draw backing uses 50% of a normal backing
// in average.
// TODO(crbug.com/1295443): use accurate size for raw draw backings.
if (gpu_backing_ && gpu_backing_->is_using_raw_draw) {
memory_usage = memory_usage / 2;
}
return memory_usage;
}
private:
ResourcePool* const resource_pool_;
const size_t unique_id_;
const gfx::Size size_;
const viz::ResourceFormat format_;
const gfx::ColorSpace color_space_;
uint64_t content_id_ = 0;
base::TimeTicks last_usage_;
gfx::Rect invalidated_rect_;
// Set to true for resources that should be destroyed instead of returned to
// the pool for reuse.
bool avoid_reuse_ = false;
// An id used to name the backing for transfer to the display compositor.
viz::ResourceId resource_id_ = viz::kInvalidResourceId;
// The backing for gpu resources. Initially null for resources given
// out by ResourcePool, to be filled in by the client. Is destroyed on the
// compositor thread.
std::unique_ptr<GpuBacking> gpu_backing_;
// The backing for software resources. Initially null for resources given
// out by ResourcePool, to be filled in by the client. Is destroyed on the
// compositor thread.
std::unique_ptr<SoftwareBacking> software_backing_;
// Used for debugging and tracing.
std::string debug_name_;
// The current resource state. See enum State for detail.
State state_ = kUnused;
};
// Called when backing is set for the PoolResource.
void OnBackingAllocated(PoolResource* resource);
// Callback from the ResourceProvider to notify when an exported PoolResource
// is not busy and may be reused.
void OnResourceReleased(size_t unique_id,
const gpu::SyncToken& sync_token,
bool lost);
// Tries to reuse a resource. Returns |nullptr| if none are available.
PoolResource* ReuseResource(const gfx::Size& size,
viz::ResourceFormat format,
const gfx::ColorSpace& color_space);
// Creates a new resource without trying to reuse an old one.
PoolResource* CreateResource(const gfx::Size& size,
viz::ResourceFormat 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);
// Functions which manage periodic eviction of expired resources.
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::ContextProvider> 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 in_use_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;
// Holds most recently used resources at the front of the queue.
base::circular_deque<std::unique_ptr<PoolResource>> unused_resources_;
base::circular_deque<std::unique_ptr<PoolResource>> busy_resources_;
// Map from the PoolResource |unique_id| to the PoolResource.
std::map<size_t, std::unique_ptr<PoolResource>> in_use_resources_;
std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_;
base::TimeTicks flush_evicted_resources_deadline_;
raw_ptr<const base::TickClock> clock_;
base::WeakPtrFactory<ResourcePool> weak_ptr_factory_{this};
};
} // namespace cc
#endif // CC_RESOURCES_RESOURCE_POOL_H_