1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "cc/resources/resource_pool.h"
9 #include "base/format_macros.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/thread_task_runner_handle.h"
12 #include "base/trace_event/memory_dump_manager.h"
13 #include "cc/resources/resource_provider.h"
14 #include "cc/resources/resource_util.h"
15 #include "cc/resources/scoped_resource.h"
19 void ResourcePool::PoolResource::OnMemoryDump(
20 base::trace_event::ProcessMemoryDump
* pmd
,
22 // TODO(ericrk): Add per-compositor ID in name.
23 std::string parent_node
=
24 base::StringPrintf("cc/resource_memory/resource_%d", resource
->id());
25 std::string dump_name
=
26 base::StringPrintf("cc/tile_memory/resource_%d", resource
->id());
27 base::trace_event::MemoryAllocatorDump
* dump
=
28 pmd
->CreateAllocatorDump(dump_name
);
30 pmd
->AddSuballocation(dump
->guid(), parent_node
);
32 uint64_t total_bytes
= ResourceUtil::UncheckedSizeInBytesAligned
<size_t>(
33 resource
->size(), resource
->format());
34 dump
->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize
,
35 base::trace_event::MemoryAllocatorDump::kUnitsBytes
,
37 dump
->AddScalar("free_size",
38 base::trace_event::MemoryAllocatorDump::kUnitsBytes
,
39 is_free
? total_bytes
: 0);
42 ResourcePool::ResourcePool(ResourceProvider
* resource_provider
, GLenum target
)
43 : resource_provider_(resource_provider
),
45 max_memory_usage_bytes_(0),
46 max_unused_memory_usage_bytes_(0),
47 max_resource_count_(0),
48 memory_usage_bytes_(0),
49 unused_memory_usage_bytes_(0),
51 base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
52 this, base::ThreadTaskRunnerHandle::Get());
55 ResourcePool::~ResourcePool() {
56 base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
58 while (!busy_resources_
.empty()) {
59 auto const& front
= busy_resources_
.front();
60 DidFinishUsingResource(front
.resource
, front
.content_id
);
61 busy_resources_
.pop_front();
64 SetResourceUsageLimits(0, 0, 0);
65 DCHECK_EQ(0u, unused_resources_
.size());
66 DCHECK_EQ(0u, memory_usage_bytes_
);
67 DCHECK_EQ(0u, unused_memory_usage_bytes_
);
68 DCHECK_EQ(0u, resource_count_
);
71 scoped_ptr
<ScopedResource
> ResourcePool::AcquireResource(
72 const gfx::Size
& size
, ResourceFormat format
) {
73 for (ResourceList::iterator it
= unused_resources_
.begin();
74 it
!= unused_resources_
.end();
76 ScopedResource
* resource
= it
->resource
;
77 DCHECK(resource_provider_
->CanLockForWrite(resource
->id()));
79 if (resource
->format() != format
)
81 if (resource
->size() != size
)
84 unused_resources_
.erase(it
);
85 unused_memory_usage_bytes_
-=
86 ResourceUtil::UncheckedSizeInBytes
<size_t>(size
, format
);
87 return make_scoped_ptr(resource
);
90 scoped_ptr
<ScopedResource
> resource
=
91 ScopedResource::Create(resource_provider_
);
92 resource
->AllocateManaged(size
, target_
, format
);
94 DCHECK(ResourceUtil::VerifySizeInBytes
<size_t>(resource
->size(),
96 memory_usage_bytes_
+= ResourceUtil::UncheckedSizeInBytes
<size_t>(
97 resource
->size(), resource
->format());
99 return resource
.Pass();
102 scoped_ptr
<ScopedResource
> ResourcePool::TryAcquireResourceWithContentId(
103 uint64_t content_id
) {
106 auto it
= std::find_if(unused_resources_
.begin(), unused_resources_
.end(),
107 [content_id
](const PoolResource
& pool_resource
) {
108 return pool_resource
.content_id
== content_id
;
110 if (it
== unused_resources_
.end())
113 ScopedResource
* resource
= it
->resource
;
114 DCHECK(resource_provider_
->CanLockForWrite(resource
->id()));
116 unused_resources_
.erase(it
);
117 unused_memory_usage_bytes_
-= ResourceUtil::UncheckedSizeInBytes
<size_t>(
118 resource
->size(), resource
->format());
119 return make_scoped_ptr(resource
);
122 void ResourcePool::ReleaseResource(scoped_ptr
<ScopedResource
> resource
,
123 uint64_t content_id
) {
124 busy_resources_
.push_back(PoolResource(resource
.release(), content_id
));
127 void ResourcePool::SetResourceUsageLimits(size_t max_memory_usage_bytes
,
128 size_t max_unused_memory_usage_bytes
,
129 size_t max_resource_count
) {
130 max_memory_usage_bytes_
= max_memory_usage_bytes
;
131 max_unused_memory_usage_bytes_
= max_unused_memory_usage_bytes
;
132 max_resource_count_
= max_resource_count
;
134 ReduceResourceUsage();
137 void ResourcePool::ReduceResourceUsage() {
138 while (!unused_resources_
.empty()) {
139 if (!ResourceUsageTooHigh())
142 // LRU eviction pattern. Most recently used might be blocked by
143 // a read lock fence but it's still better to evict the least
144 // recently used as it prevents a resource that is hard to reuse
145 // because of unique size from being kept around. Resources that
146 // can't be locked for write might also not be truly free-able.
147 // We can free the resource here but it doesn't mean that the
148 // memory is necessarily returned to the OS.
149 ScopedResource
* resource
= unused_resources_
.front().resource
;
150 unused_resources_
.pop_front();
151 unused_memory_usage_bytes_
-= ResourceUtil::UncheckedSizeInBytes
<size_t>(
152 resource
->size(), resource
->format());
153 DeleteResource(resource
);
157 bool ResourcePool::ResourceUsageTooHigh() {
158 if (resource_count_
> max_resource_count_
)
160 if (memory_usage_bytes_
> max_memory_usage_bytes_
)
162 if (unused_memory_usage_bytes_
> max_unused_memory_usage_bytes_
)
167 void ResourcePool::DeleteResource(ScopedResource
* resource
) {
168 size_t resource_bytes
= ResourceUtil::UncheckedSizeInBytes
<size_t>(
169 resource
->size(), resource
->format());
170 memory_usage_bytes_
-= resource_bytes
;
175 void ResourcePool::CheckBusyResources(bool wait_if_needed
) {
176 ResourceList::iterator it
= busy_resources_
.begin();
178 while (it
!= busy_resources_
.end()) {
179 ScopedResource
* resource
= it
->resource
;
182 resource_provider_
->WaitReadLockIfNeeded(resource
->id());
184 if (resource_provider_
->CanLockForWrite(resource
->id())) {
185 DidFinishUsingResource(resource
, it
->content_id
);
186 it
= busy_resources_
.erase(it
);
187 } else if (resource_provider_
->IsLost(resource
->id())) {
188 // Remove lost resources from pool.
189 DeleteResource(resource
);
190 it
= busy_resources_
.erase(it
);
197 void ResourcePool::DidFinishUsingResource(ScopedResource
* resource
,
198 uint64_t content_id
) {
199 unused_memory_usage_bytes_
+= ResourceUtil::UncheckedSizeInBytes
<size_t>(
200 resource
->size(), resource
->format());
201 unused_resources_
.push_back(PoolResource(resource
, content_id
));
204 bool ResourcePool::OnMemoryDump(const base::trace_event::MemoryDumpArgs
& args
,
205 base::trace_event::ProcessMemoryDump
* pmd
) {
206 for (const auto& resource
: unused_resources_
) {
207 resource
.OnMemoryDump(pmd
, true /* is_free */);
209 for (const auto& resource
: busy_resources_
) {
210 resource
.OnMemoryDump(pmd
, false /* is_free */);
212 // TODO(ericrk): Dump vended out resources once that data is available.