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
,
21 const ResourceProvider
* resource_provider
,
23 // Resource IDs are not process-unique, so log with the ResourceProvider's
25 std::string parent_node
=
26 base::StringPrintf("cc/resource_memory/resource_provider_%d/resource_%d",
27 resource_provider
->tracing_id(), id());
29 std::string dump_name
=
30 base::StringPrintf("cc/tile_memory/resource_provider_%d/resource_%d",
31 resource_provider
->tracing_id(), id());
32 base::trace_event::MemoryAllocatorDump
* dump
=
33 pmd
->CreateAllocatorDump(dump_name
);
35 pmd
->AddSuballocation(dump
->guid(), parent_node
);
37 uint64_t total_bytes
=
38 ResourceUtil::UncheckedSizeInBytesAligned
<size_t>(size(), format());
39 dump
->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize
,
40 base::trace_event::MemoryAllocatorDump::kUnitsBytes
,
44 dump
->AddScalar("free_size",
45 base::trace_event::MemoryAllocatorDump::kUnitsBytes
,
49 ResourcePool::ResourcePool(ResourceProvider
* resource_provider
)
50 : resource_provider_(resource_provider
),
52 max_memory_usage_bytes_(0),
53 max_unused_memory_usage_bytes_(0),
54 max_resource_count_(0),
55 memory_usage_bytes_(0),
56 unused_memory_usage_bytes_(0),
58 base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
59 this, base::ThreadTaskRunnerHandle::Get());
62 ResourcePool::ResourcePool(ResourceProvider
* resource_provider
, GLenum target
)
63 : resource_provider_(resource_provider
),
65 max_memory_usage_bytes_(0),
66 max_unused_memory_usage_bytes_(0),
67 max_resource_count_(0),
68 memory_usage_bytes_(0),
69 unused_memory_usage_bytes_(0),
71 DCHECK_NE(0u, target
);
72 base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
73 this, base::ThreadTaskRunnerHandle::Get());
76 ResourcePool::~ResourcePool() {
77 base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
80 DCHECK_EQ(0u, in_use_resources_
.size());
82 while (!busy_resources_
.empty()) {
83 DidFinishUsingResource(busy_resources_
.take_front());
86 SetResourceUsageLimits(0, 0, 0);
87 DCHECK_EQ(0u, unused_resources_
.size());
88 DCHECK_EQ(0u, memory_usage_bytes_
);
89 DCHECK_EQ(0u, unused_memory_usage_bytes_
);
90 DCHECK_EQ(0u, resource_count_
);
93 Resource
* ResourcePool::AcquireResource(const gfx::Size
& size
,
94 ResourceFormat format
) {
95 for (ResourceDeque::iterator it
= unused_resources_
.begin();
96 it
!= unused_resources_
.end(); ++it
) {
97 ScopedResource
* resource
= *it
;
98 DCHECK(resource_provider_
->CanLockForWrite(resource
->id()));
100 if (resource
->format() != format
)
102 if (resource
->size() != size
)
105 // Transfer resource to |in_use_resources_|.
106 in_use_resources_
.set(resource
->id(), unused_resources_
.take(it
));
108 unused_memory_usage_bytes_
-=
109 ResourceUtil::UncheckedSizeInBytes
<size_t>(size
, format
);
113 scoped_ptr
<PoolResource
> pool_resource
=
114 PoolResource::Create(resource_provider_
);
116 target_
? target_
: resource_provider_
->GetImageTextureTarget(format
);
117 pool_resource
->AllocateManaged(size
, target
, format
);
119 DCHECK(ResourceUtil::VerifySizeInBytes
<size_t>(pool_resource
->size(),
120 pool_resource
->format()));
121 memory_usage_bytes_
+= ResourceUtil::UncheckedSizeInBytes
<size_t>(
122 pool_resource
->size(), pool_resource
->format());
125 Resource
* resource
= pool_resource
.get();
126 in_use_resources_
.set(resource
->id(), pool_resource
.Pass());
130 Resource
* ResourcePool::TryAcquireResourceWithContentId(uint64_t content_id
) {
133 auto it
= std::find_if(unused_resources_
.begin(), unused_resources_
.end(),
134 [content_id
](const PoolResource
* pool_resource
) {
135 return pool_resource
->content_id() == content_id
;
137 if (it
== unused_resources_
.end())
140 Resource
* resource
= *it
;
141 DCHECK(resource_provider_
->CanLockForWrite(resource
->id()));
143 // Transfer resource to |in_use_resources_|.
144 in_use_resources_
.set(resource
->id(), unused_resources_
.take(it
));
146 unused_memory_usage_bytes_
-= ResourceUtil::UncheckedSizeInBytes
<size_t>(
147 resource
->size(), resource
->format());
151 void ResourcePool::ReleaseResource(Resource
* resource
, uint64_t content_id
) {
152 auto it
= in_use_resources_
.find(resource
->id());
153 DCHECK(it
!= in_use_resources_
.end());
155 PoolResource
* pool_resource
= it
->second
;
156 pool_resource
->set_content_id(content_id
);
158 // Transfer resource to |busy_resources_|.
159 busy_resources_
.push_back(in_use_resources_
.take_and_erase(it
));
162 void ResourcePool::SetResourceUsageLimits(size_t max_memory_usage_bytes
,
163 size_t max_unused_memory_usage_bytes
,
164 size_t max_resource_count
) {
165 max_memory_usage_bytes_
= max_memory_usage_bytes
;
166 max_unused_memory_usage_bytes_
= max_unused_memory_usage_bytes
;
167 max_resource_count_
= max_resource_count
;
169 ReduceResourceUsage();
172 void ResourcePool::ReduceResourceUsage() {
173 while (!unused_resources_
.empty()) {
174 if (!ResourceUsageTooHigh())
177 // LRU eviction pattern. Most recently used might be blocked by
178 // a read lock fence but it's still better to evict the least
179 // recently used as it prevents a resource that is hard to reuse
180 // because of unique size from being kept around. Resources that
181 // can't be locked for write might also not be truly free-able.
182 // We can free the resource here but it doesn't mean that the
183 // memory is necessarily returned to the OS.
184 scoped_ptr
<PoolResource
> resource
= unused_resources_
.take_front();
185 unused_memory_usage_bytes_
-= ResourceUtil::UncheckedSizeInBytes
<size_t>(
186 resource
->size(), resource
->format());
187 DeleteResource(resource
.Pass());
191 bool ResourcePool::ResourceUsageTooHigh() {
192 if (resource_count_
> max_resource_count_
)
194 if (memory_usage_bytes_
> max_memory_usage_bytes_
)
196 if (unused_memory_usage_bytes_
> max_unused_memory_usage_bytes_
)
201 void ResourcePool::DeleteResource(scoped_ptr
<PoolResource
> resource
) {
202 size_t resource_bytes
= ResourceUtil::UncheckedSizeInBytes
<size_t>(
203 resource
->size(), resource
->format());
204 memory_usage_bytes_
-= resource_bytes
;
208 void ResourcePool::CheckBusyResources() {
209 for (size_t i
= 0; i
< busy_resources_
.size();) {
210 ResourceDeque::iterator
it(busy_resources_
.begin() + i
);
211 PoolResource
* resource
= *it
;
213 if (resource_provider_
->CanLockForWrite(resource
->id())) {
214 DidFinishUsingResource(busy_resources_
.take(it
));
215 } else if (resource_provider_
->IsLost(resource
->id())) {
216 // Remove lost resources from pool.
217 DeleteResource(busy_resources_
.take(it
));
224 void ResourcePool::DidFinishUsingResource(scoped_ptr
<PoolResource
> resource
) {
225 unused_memory_usage_bytes_
+= ResourceUtil::UncheckedSizeInBytes
<size_t>(
226 resource
->size(), resource
->format());
227 unused_resources_
.push_back(resource
.Pass());
230 bool ResourcePool::OnMemoryDump(const base::trace_event::MemoryDumpArgs
& args
,
231 base::trace_event::ProcessMemoryDump
* pmd
) {
232 for (const auto& resource
: unused_resources_
) {
233 resource
->OnMemoryDump(pmd
, resource_provider_
, true /* is_free */);
235 for (const auto& resource
: busy_resources_
) {
236 resource
->OnMemoryDump(pmd
, resource_provider_
, false /* is_free */);
238 for (const auto& entry
: in_use_resources_
) {
239 entry
.second
->OnMemoryDump(pmd
, resource_provider_
, false /* is_free */);