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(), resource
->id());
28 std::string dump_name
=
29 base::StringPrintf("cc/tile_memory/resource_provider_%d/resource_%d",
30 resource_provider
->tracing_id(), resource
->id());
31 base::trace_event::MemoryAllocatorDump
* dump
=
32 pmd
->CreateAllocatorDump(dump_name
);
34 pmd
->AddSuballocation(dump
->guid(), parent_node
);
36 uint64_t total_bytes
= ResourceUtil::UncheckedSizeInBytesAligned
<size_t>(
37 resource
->size(), resource
->format());
38 dump
->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize
,
39 base::trace_event::MemoryAllocatorDump::kUnitsBytes
,
41 dump
->AddScalar("free_size",
42 base::trace_event::MemoryAllocatorDump::kUnitsBytes
,
43 is_free
? total_bytes
: 0);
45 ResourcePool::ResourcePool(ResourceProvider
* resource_provider
)
46 : resource_provider_(resource_provider
),
48 max_memory_usage_bytes_(0),
49 max_unused_memory_usage_bytes_(0),
50 max_resource_count_(0),
51 memory_usage_bytes_(0),
52 unused_memory_usage_bytes_(0),
54 base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
55 this, base::ThreadTaskRunnerHandle::Get());
58 ResourcePool::ResourcePool(ResourceProvider
* resource_provider
, GLenum target
)
59 : resource_provider_(resource_provider
),
61 max_memory_usage_bytes_(0),
62 max_unused_memory_usage_bytes_(0),
63 max_resource_count_(0),
64 memory_usage_bytes_(0),
65 unused_memory_usage_bytes_(0),
67 DCHECK_NE(0u, target
);
68 base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
69 this, base::ThreadTaskRunnerHandle::Get());
72 ResourcePool::~ResourcePool() {
73 base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
75 while (!busy_resources_
.empty()) {
76 auto const& front
= busy_resources_
.front();
77 DidFinishUsingResource(front
.resource
, front
.content_id
);
78 busy_resources_
.pop_front();
81 SetResourceUsageLimits(0, 0, 0);
82 DCHECK_EQ(0u, unused_resources_
.size());
83 DCHECK_EQ(0u, memory_usage_bytes_
);
84 DCHECK_EQ(0u, unused_memory_usage_bytes_
);
85 DCHECK_EQ(0u, resource_count_
);
88 scoped_ptr
<ScopedResource
> ResourcePool::AcquireResource(
89 const gfx::Size
& size
, ResourceFormat format
) {
90 for (ResourceList::iterator it
= unused_resources_
.begin();
91 it
!= unused_resources_
.end();
93 ScopedResource
* resource
= it
->resource
;
94 DCHECK(resource_provider_
->CanLockForWrite(resource
->id()));
96 if (resource
->format() != format
)
98 if (resource
->size() != size
)
101 unused_resources_
.erase(it
);
102 unused_memory_usage_bytes_
-=
103 ResourceUtil::UncheckedSizeInBytes
<size_t>(size
, format
);
104 return make_scoped_ptr(resource
);
107 scoped_ptr
<ScopedResource
> resource
=
108 ScopedResource::Create(resource_provider_
);
110 target_
? target_
: resource_provider_
->GetImageTextureTarget(format
);
111 resource
->AllocateManaged(size
, target
, format
);
113 DCHECK(ResourceUtil::VerifySizeInBytes
<size_t>(resource
->size(),
114 resource
->format()));
115 memory_usage_bytes_
+= ResourceUtil::UncheckedSizeInBytes
<size_t>(
116 resource
->size(), resource
->format());
118 return resource
.Pass();
121 scoped_ptr
<ScopedResource
> ResourcePool::TryAcquireResourceWithContentId(
122 uint64_t content_id
) {
125 auto it
= std::find_if(unused_resources_
.begin(), unused_resources_
.end(),
126 [content_id
](const PoolResource
& pool_resource
) {
127 return pool_resource
.content_id
== content_id
;
129 if (it
== unused_resources_
.end())
132 ScopedResource
* resource
= it
->resource
;
133 DCHECK(resource_provider_
->CanLockForWrite(resource
->id()));
135 unused_resources_
.erase(it
);
136 unused_memory_usage_bytes_
-= ResourceUtil::UncheckedSizeInBytes
<size_t>(
137 resource
->size(), resource
->format());
138 return make_scoped_ptr(resource
);
141 void ResourcePool::ReleaseResource(scoped_ptr
<ScopedResource
> resource
,
142 uint64_t content_id
) {
143 busy_resources_
.push_back(PoolResource(resource
.release(), content_id
));
146 void ResourcePool::SetResourceUsageLimits(size_t max_memory_usage_bytes
,
147 size_t max_unused_memory_usage_bytes
,
148 size_t max_resource_count
) {
149 max_memory_usage_bytes_
= max_memory_usage_bytes
;
150 max_unused_memory_usage_bytes_
= max_unused_memory_usage_bytes
;
151 max_resource_count_
= max_resource_count
;
153 ReduceResourceUsage();
156 void ResourcePool::ReduceResourceUsage() {
157 while (!unused_resources_
.empty()) {
158 if (!ResourceUsageTooHigh())
161 // LRU eviction pattern. Most recently used might be blocked by
162 // a read lock fence but it's still better to evict the least
163 // recently used as it prevents a resource that is hard to reuse
164 // because of unique size from being kept around. Resources that
165 // can't be locked for write might also not be truly free-able.
166 // We can free the resource here but it doesn't mean that the
167 // memory is necessarily returned to the OS.
168 ScopedResource
* resource
= unused_resources_
.front().resource
;
169 unused_resources_
.pop_front();
170 unused_memory_usage_bytes_
-= ResourceUtil::UncheckedSizeInBytes
<size_t>(
171 resource
->size(), resource
->format());
172 DeleteResource(resource
);
176 bool ResourcePool::ResourceUsageTooHigh() {
177 if (resource_count_
> max_resource_count_
)
179 if (memory_usage_bytes_
> max_memory_usage_bytes_
)
181 if (unused_memory_usage_bytes_
> max_unused_memory_usage_bytes_
)
186 void ResourcePool::DeleteResource(ScopedResource
* resource
) {
187 size_t resource_bytes
= ResourceUtil::UncheckedSizeInBytes
<size_t>(
188 resource
->size(), resource
->format());
189 memory_usage_bytes_
-= resource_bytes
;
194 void ResourcePool::CheckBusyResources(bool wait_if_needed
) {
195 ResourceList::iterator it
= busy_resources_
.begin();
197 while (it
!= busy_resources_
.end()) {
198 ScopedResource
* resource
= it
->resource
;
201 resource_provider_
->WaitReadLockIfNeeded(resource
->id());
203 if (resource_provider_
->CanLockForWrite(resource
->id())) {
204 DidFinishUsingResource(resource
, it
->content_id
);
205 it
= busy_resources_
.erase(it
);
206 } else if (resource_provider_
->IsLost(resource
->id())) {
207 // Remove lost resources from pool.
208 DeleteResource(resource
);
209 it
= busy_resources_
.erase(it
);
216 void ResourcePool::DidFinishUsingResource(ScopedResource
* resource
,
217 uint64_t content_id
) {
218 unused_memory_usage_bytes_
+= ResourceUtil::UncheckedSizeInBytes
<size_t>(
219 resource
->size(), resource
->format());
220 unused_resources_
.push_back(PoolResource(resource
, content_id
));
223 bool ResourcePool::OnMemoryDump(const base::trace_event::MemoryDumpArgs
& args
,
224 base::trace_event::ProcessMemoryDump
* pmd
) {
225 for (const auto& resource
: unused_resources_
) {
226 resource
.OnMemoryDump(pmd
, resource_provider_
, true /* is_free */);
228 for (const auto& resource
: busy_resources_
) {
229 resource
.OnMemoryDump(pmd
, resource_provider_
, false /* is_free */);
231 // TODO(ericrk): Dump vended out resources once that data is available.