1 // Copyright (c) 2018 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.
4 
5 #include "gpu/command_buffer/service/passthrough_discardable_manager.h"
6 
7 #include "gpu/command_buffer/service/context_group.h"
8 #include "gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h"
9 #include "gpu/command_buffer/service/service_discardable_manager.h"
10 #include "gpu/config/gpu_preferences.h"
11 
12 namespace gpu {
13 
14 PassthroughDiscardableManager::DiscardableCacheValue::DiscardableCacheValue() =
15     default;
16 PassthroughDiscardableManager::DiscardableCacheValue::DiscardableCacheValue(
17     const DiscardableCacheValue& other) = default;
18 PassthroughDiscardableManager::DiscardableCacheValue::DiscardableCacheValue(
19     DiscardableCacheValue&& other) = default;
20 PassthroughDiscardableManager::DiscardableCacheValue&
21 PassthroughDiscardableManager::DiscardableCacheValue::operator=(
22     const DiscardableCacheValue& other) = default;
23 PassthroughDiscardableManager::DiscardableCacheValue&
24 PassthroughDiscardableManager::DiscardableCacheValue::operator=(
25     DiscardableCacheValue&& other) = default;
26 PassthroughDiscardableManager::DiscardableCacheValue::~DiscardableCacheValue() =
27     default;
28 
PassthroughDiscardableManager(const GpuPreferences & preferences)29 PassthroughDiscardableManager::PassthroughDiscardableManager(
30     const GpuPreferences& preferences)
31     : cache_(DiscardableCache::NO_AUTO_EVICT),
32       cache_size_limit_(preferences.force_gpu_mem_discardable_limit_bytes
33                             ? preferences.force_gpu_mem_discardable_limit_bytes
34                             : DiscardableCacheSizeLimit()) {}
35 
~PassthroughDiscardableManager()36 PassthroughDiscardableManager::~PassthroughDiscardableManager() {
37   DCHECK(cache_.empty());
38 }
39 
InitializeTexture(uint32_t client_id,const gles2::ContextGroup * context_group,size_t texture_size,ServiceDiscardableHandle handle)40 void PassthroughDiscardableManager::InitializeTexture(
41     uint32_t client_id,
42     const gles2::ContextGroup* context_group,
43     size_t texture_size,
44     ServiceDiscardableHandle handle) {
45   DCHECK(cache_.Get({client_id, context_group}) == cache_.end());
46 
47   total_size_ += texture_size;
48 
49   DiscardableCacheValue entry;
50   entry.handle = std::move(handle);
51   entry.size = texture_size;
52 
53   cache_.Put({client_id, context_group}, std::move(entry));
54   EnforceCacheSizeLimit(cache_size_limit_);
55 }
UnlockTexture(uint32_t client_id,const gles2::ContextGroup * context_group,gles2::TexturePassthrough ** texture_to_unbind)56 bool PassthroughDiscardableManager::UnlockTexture(
57     uint32_t client_id,
58     const gles2::ContextGroup* context_group,
59     gles2::TexturePassthrough** texture_to_unbind) {
60   *texture_to_unbind = nullptr;
61 
62   auto iter = cache_.Get({client_id, context_group});
63   if (iter == cache_.end())
64     return false;
65 
66   iter->second.handle.Unlock();
67   DCHECK(iter->second.lock_count > 0);
68   if (--iter->second.lock_count == 0) {
69     // Get the texture from this context group and store it.  Remove it from the
70     // ID maps and tell the decoder to unbind it.
71     gles2::PassthroughResources* resources =
72         context_group->passthrough_resources();
73     resources->texture_object_map.GetServiceID(client_id,
74                                                &iter->second.unlocked_texture);
75     resources->texture_id_map.RemoveClientID(client_id);
76     resources->texture_object_map.RemoveClientID(client_id);
77     *texture_to_unbind = iter->second.unlocked_texture.get();
78   }
79 
80   return true;
81 }
82 
LockTexture(uint32_t client_id,const gles2::ContextGroup * context_group)83 bool PassthroughDiscardableManager::LockTexture(
84     uint32_t client_id,
85     const gles2::ContextGroup* context_group) {
86   auto iter = cache_.Get({client_id, context_group});
87   if (iter == cache_.end())
88     return false;
89 
90   ++iter->second.lock_count;
91   if (iter->second.unlocked_texture != nullptr) {
92     scoped_refptr<gles2::TexturePassthrough> texture =
93         std::move(iter->second.unlocked_texture);
94 
95     // Re-insert the texture back into the context group's ID maps.
96     // If we've generated a replacement texture due to "bind generates
97     // resource", behavior, just delete the resource being returned.
98     gles2::PassthroughResources* resources =
99         context_group->passthrough_resources();
100     GLuint service_id = 0;
101     if (!resources->texture_id_map.GetServiceID(client_id, &service_id)) {
102       resources->texture_id_map.SetIDMapping(client_id, texture->service_id());
103       resources->texture_object_map.SetIDMapping(client_id, std::move(texture));
104     }
105   }
106 
107   return true;
108 }
109 
DeleteContextGroup(const gles2::ContextGroup * context_group,bool has_context)110 void PassthroughDiscardableManager::DeleteContextGroup(
111     const gles2::ContextGroup* context_group,
112     bool has_context) {
113   DCHECK(context_group);
114 
115   DeleteTextures(context_group, has_context);
116 }
117 
OnContextLost()118 void PassthroughDiscardableManager::OnContextLost() {
119   DeleteTextures(nullptr, false);
120 }
121 
DeleteTextures(const gles2::ContextGroup * context_group,bool has_context)122 void PassthroughDiscardableManager::DeleteTextures(
123     const gles2::ContextGroup* context_group,
124     bool has_context) {
125   auto iter = cache_.begin();
126   while (iter != cache_.end()) {
127     if (iter->first.second == context_group || !context_group) {
128       iter->second.handle.ForceDelete();
129       total_size_ -= iter->second.size;
130       if (!has_context && iter->second.unlocked_texture)
131         iter->second.unlocked_texture->MarkContextLost();
132       iter = cache_.Erase(iter);
133     } else {
134       iter++;
135     }
136   }
137 }
138 
DeleteTexture(uint32_t client_id,const gles2::ContextGroup * context_group)139 void PassthroughDiscardableManager::DeleteTexture(
140     uint32_t client_id,
141     const gles2::ContextGroup* context_group) {
142   auto iter = cache_.Get({client_id, context_group});
143   if (iter == cache_.end())
144     return;
145 
146   iter->second.handle.ForceDelete();
147   total_size_ -= iter->second.size;
148   cache_.Erase(iter);
149 }
150 
UpdateTextureSize(uint32_t client_id,const gles2::ContextGroup * context_group,size_t new_size)151 void PassthroughDiscardableManager::UpdateTextureSize(
152     uint32_t client_id,
153     const gles2::ContextGroup* context_group,
154     size_t new_size) {
155   auto iter = cache_.Get({client_id, context_group});
156   if (iter == cache_.end())
157     return;
158 
159   total_size_ -= iter->second.size;
160   iter->second.size = new_size;
161   total_size_ += iter->second.size;
162 
163   EnforceCacheSizeLimit(cache_size_limit_);
164 }
165 
HandleMemoryPressure(base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level)166 void PassthroughDiscardableManager::HandleMemoryPressure(
167     base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
168   size_t limit = DiscardableCacheSizeLimitForPressure(cache_size_limit_,
169                                                       memory_pressure_level);
170   EnforceCacheSizeLimit(limit);
171 }
172 
IsEntryLockedForTesting(uint32_t client_id,const gles2::ContextGroup * context_group) const173 bool PassthroughDiscardableManager::IsEntryLockedForTesting(
174     uint32_t client_id,
175     const gles2::ContextGroup* context_group) const {
176   auto iter = cache_.Peek({client_id, context_group});
177   DCHECK(iter != cache_.end());
178   return iter->second.unlocked_texture == nullptr;
179 }
180 
IsEntryTrackedForTesting(uint32_t client_id,const gles2::ContextGroup * context_group) const181 bool PassthroughDiscardableManager::IsEntryTrackedForTesting(
182     uint32_t client_id,
183     const gles2::ContextGroup* context_group) const {
184   return cache_.Peek({client_id, context_group}) != cache_.end();
185 }
186 
187 scoped_refptr<gles2::TexturePassthrough>
UnlockedTextureForTesting(uint32_t client_id,const gles2::ContextGroup * context_group) const188 PassthroughDiscardableManager::UnlockedTextureForTesting(
189     uint32_t client_id,
190     const gles2::ContextGroup* context_group) const {
191   auto iter = cache_.Peek({client_id, context_group});
192   DCHECK(iter != cache_.end());
193   return iter->second.unlocked_texture;
194 }
195 
EnforceCacheSizeLimit(size_t limit)196 void PassthroughDiscardableManager::EnforceCacheSizeLimit(size_t limit) {
197   for (auto it = cache_.rbegin(); it != cache_.rend();) {
198     if (total_size_ <= limit) {
199       return;
200     }
201     if (!it->second.handle.Delete()) {
202       ++it;
203       continue;
204     }
205 
206     total_size_ -= it->second.size;
207 
208     GLuint client_id = it->first.first;
209     const gles2::ContextGroup* context_group = it->first.second;
210     gles2::PassthroughResources* resources =
211         context_group->passthrough_resources();
212 
213     resources->texture_id_map.RemoveClientID(client_id);
214     resources->texture_object_map.RemoveClientID(client_id);
215 
216     // Erase before calling texture_manager->RemoveTexture, to avoid attempting
217     // to remove the texture from entries_ twice.
218     it = cache_.Erase(it);
219   }
220 }
221 
222 }  // namespace gpu
223