OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2017 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/service_discardable_manager.h" | |
6 | |
7 #include "gpu/command_buffer/service/gles2_cmd_decoder_mock.h" | |
8 #include "gpu/command_buffer/service/gpu_service_test.h" | |
9 #include "gpu/command_buffer/service/mailbox_manager.h" | |
10 #include "gpu/command_buffer/service/memory_tracking.h" | |
11 #include "gpu/command_buffer/service/mocks.h" | |
12 #include "gpu/command_buffer/service/test_helper.h" | |
13 #include "gpu/command_buffer/service/texture_manager.h" | |
14 #include "testing/gtest/include/gtest/gtest.h" | |
15 #include "ui/gl/gl_image_stub.h" | |
16 #include "ui/gl/gl_mock.h" | |
17 #include "ui/gl/gl_switches.h" | |
18 | |
19 using ::testing::Pointee; | |
20 using ::testing::_; | |
21 using ::testing::Invoke; | |
22 using ::testing::Mock; | |
23 using ::testing::InSequence; | |
24 | |
25 namespace gpu { | |
26 namespace gles2 { | |
27 namespace { | |
28 | |
29 void CreateLockedHandlesForTesting( | |
30 std::unique_ptr<ServiceDiscardableHandle>* service_handle, | |
31 std::unique_ptr<ClientDiscardableHandle>* client_handle) { | |
32 std::unique_ptr<base::SharedMemory> shared_mem(new base::SharedMemory); | |
33 shared_mem->CreateAndMapAnonymous(sizeof(uint32_t)); | |
34 scoped_refptr<gpu::Buffer> buffer = | |
35 MakeBufferFromSharedMemory(std::move(shared_mem), sizeof(uint32_t)); | |
36 | |
37 client_handle->reset(new ClientDiscardableHandle(buffer, 0, 0)); | |
38 service_handle->reset(new ServiceDiscardableHandle(buffer, 0, 0)); | |
39 } | |
40 | |
41 ServiceDiscardableHandle CreateLockedServiceHandleForTesting() { | |
42 std::unique_ptr<ServiceDiscardableHandle> service_handle; | |
43 std::unique_ptr<ClientDiscardableHandle> client_handle; | |
44 CreateLockedHandlesForTesting(&service_handle, &client_handle); | |
45 return *service_handle; | |
46 } | |
47 | |
48 class MockDestructionObserver : public TextureManager::DestructionObserver { | |
49 public: | |
50 MOCK_METHOD1(OnTextureManagerDestroying, void(TextureManager* manager)); | |
51 MOCK_METHOD1(OnTextureRefDestroying, void(TextureRef* ref)); | |
52 }; | |
53 | |
54 } // namespace | |
55 | |
56 class ServiceDiscardableManagerTest : public GpuServiceTest { | |
57 public: | |
58 ServiceDiscardableManagerTest() {} | |
59 ~ServiceDiscardableManagerTest() override {} | |
60 | |
61 protected: | |
62 void SetUp() override { | |
63 GpuServiceTest::SetUp(); | |
64 decoder_.reset(new MockGLES2Decoder()); | |
65 feature_info_ = new FeatureInfo(); | |
66 context_group_ = scoped_refptr<ContextGroup>(new ContextGroup( | |
67 gpu_preferences_, nullptr, nullptr, nullptr, nullptr, feature_info_, | |
68 false, nullptr, nullptr, GpuFeatureInfo(), &discardable_manager_)); | |
69 TestHelper::SetupContextGroupInitExpectations( | |
70 gl_.get(), DisallowedFeatures(), "", "", CONTEXT_TYPE_OPENGLES2, false); | |
71 context_group_->Initialize(decoder_.get(), CONTEXT_TYPE_OPENGLES2, | |
72 DisallowedFeatures()); | |
73 texture_manager_ = context_group_->texture_manager(); | |
74 texture_manager_->AddObserver(&destruction_observer_); | |
75 } | |
76 | |
77 void TearDown() override { | |
78 EXPECT_CALL(destruction_observer_, OnTextureManagerDestroying(_)) | |
79 .RetiresOnSaturation(); | |
80 // Texture manager will destory the 6 black/default textures. | |
piman
2017/05/12 18:01:27
nit: typo destory->destroy
piman
2017/05/12 18:01:27
nit: TextureManager::kNumDefaultTextures instead o
ericrk
2017/05/12 20:24:00
Done.
ericrk
2017/05/12 20:24:00
need to get a spell checker for my current editor.
| |
81 EXPECT_CALL(*gl_, DeleteTextures(6, _)); | |
82 | |
83 context_group_->Destroy(decoder_.get(), true); | |
84 context_group_ = nullptr; | |
85 EXPECT_EQ(0u, discardable_manager_.NumCacheEntriesForTesting()); | |
86 GpuServiceTest::TearDown(); | |
87 } | |
88 | |
89 void ExpectUnlockedTextureDeletion(uint32_t client_id) { | |
90 TextureRef* ref = discardable_manager_.UnlockedTextureRefForTesting( | |
91 client_id, texture_manager_); | |
92 ExpectTextureRefDeletion(ref); | |
93 } | |
94 | |
95 void ExpectTextureDeletion(uint32_t client_id) { | |
96 TextureRef* ref = texture_manager_->GetTexture(client_id); | |
97 ExpectTextureRefDeletion(ref); | |
98 } | |
99 | |
100 void ExpectTextureRefDeletion(TextureRef* ref) { | |
101 EXPECT_NE(nullptr, ref); | |
102 ref->AddObserver(); | |
103 EXPECT_CALL(destruction_observer_, OnTextureRefDestroying(ref)) | |
104 .WillOnce(Invoke([](TextureRef* ref) { ref->RemoveObserver(); })); | |
105 EXPECT_CALL(*gl_, DeleteTextures(1, Pointee(ref->service_id()))) | |
106 .RetiresOnSaturation(); | |
107 } | |
108 | |
109 ServiceDiscardableManager discardable_manager_; | |
110 GpuPreferences gpu_preferences_; | |
111 scoped_refptr<FeatureInfo> feature_info_; | |
112 MockDestructionObserver destruction_observer_; | |
113 TextureManager* texture_manager_; | |
114 std::unique_ptr<MockGLES2Decoder> decoder_; | |
115 scoped_refptr<gles2::ContextGroup> context_group_; | |
116 }; | |
117 | |
118 TEST_F(ServiceDiscardableManagerTest, BasicUsage) { | |
119 const GLuint kClientId = 1; | |
120 const GLuint kServiceId = 2; | |
121 const size_t texture_size = 4 * 1024 * 1024; | |
122 | |
123 // Create and insert a new texture. | |
124 texture_manager_->CreateTexture(kClientId, kServiceId); | |
125 auto handle = CreateLockedServiceHandleForTesting(); | |
126 discardable_manager_.InsertLockedTexture(kClientId, texture_size, | |
127 texture_manager_, handle); | |
128 EXPECT_EQ(1u, discardable_manager_.NumCacheEntriesForTesting()); | |
129 EXPECT_TRUE(discardable_manager_.IsEntryLockedForTesting(kClientId, | |
130 texture_manager_)); | |
131 EXPECT_NE(nullptr, texture_manager_->GetTexture(kClientId)); | |
132 | |
133 // Unlock the texture, ServiceDiscardableManager should take ownership of the | |
134 // TextureRef. | |
135 gles2::TextureRef* texture_to_unbind; | |
136 EXPECT_TRUE(discardable_manager_.UnlockTexture(kClientId, texture_manager_, | |
137 &texture_to_unbind)); | |
138 EXPECT_NE(nullptr, texture_to_unbind); | |
139 EXPECT_FALSE(discardable_manager_.IsEntryLockedForTesting(kClientId, | |
140 texture_manager_)); | |
141 EXPECT_EQ(nullptr, texture_manager_->GetTexture(kClientId)); | |
142 | |
143 // Re-lock the texture, the TextureManager should now resume ownership of | |
144 // the TextureRef. | |
145 discardable_manager_.LockTexture(kClientId, texture_manager_); | |
146 EXPECT_NE(nullptr, texture_manager_->GetTexture(kClientId)); | |
147 | |
148 // Delete the texture from the TextureManager, it should also be removed from | |
149 // the ServiceDiscardableManager. | |
150 ExpectTextureDeletion(kClientId); | |
151 texture_manager_->RemoveTexture(kClientId); | |
152 EXPECT_EQ(0u, discardable_manager_.NumCacheEntriesForTesting()); | |
153 } | |
154 | |
155 TEST_F(ServiceDiscardableManagerTest, DeleteAtShutdown) { | |
156 const size_t texture_size = 4 * 16 * 16; | |
157 | |
158 // Create 8 small textures (which will not hit memory limits), leaving every | |
159 // other one unlocked. | |
160 for (int i = 1; i <= 8; ++i) { | |
161 texture_manager_->CreateTexture(i, i); | |
162 auto handle = CreateLockedServiceHandleForTesting(); | |
163 discardable_manager_.InsertLockedTexture(i, texture_size, texture_manager_, | |
164 handle); | |
165 if (i % 2) { | |
166 TextureRef* texture_to_unbind; | |
167 EXPECT_TRUE(discardable_manager_.UnlockTexture(i, texture_manager_, | |
168 &texture_to_unbind)); | |
169 EXPECT_NE(nullptr, texture_to_unbind); | |
170 } | |
171 } | |
172 | |
173 // Expect that all 8 will be deleted at shutdown, regardless of | |
174 // locked/unlocked state. | |
175 for (int i = 1; i <= 8; ++i) { | |
176 if (i % 2) { | |
177 ExpectUnlockedTextureDeletion(i); | |
178 } else { | |
179 ExpectTextureDeletion(i); | |
180 } | |
181 } | |
182 | |
183 // Let the test shut down, the expectations should be fulfilled. | |
184 } | |
185 | |
186 TEST_F(ServiceDiscardableManagerTest, UnlockInvalid) { | |
187 const GLuint kClientId = 1; | |
188 gles2::TextureRef* texture_to_unbind; | |
189 EXPECT_FALSE(discardable_manager_.UnlockTexture(kClientId, texture_manager_, | |
190 &texture_to_unbind)); | |
191 EXPECT_EQ(nullptr, texture_to_unbind); | |
192 } | |
193 | |
194 TEST_F(ServiceDiscardableManagerTest, Limits) { | |
195 const size_t texture_size = 64 * 1024 * 1024; | |
196 const size_t large_texture_size = 3 * 64 * 1024 * 1024; | |
piman
2017/05/12 18:01:27
nit: could we base these constants on ServiceDisca
ericrk
2017/05/12 20:24:00
Done.
| |
197 | |
198 // Create 4 textures, this should fill up the discardable cache. | |
199 for (int i = 1; i < 5; ++i) { | |
200 texture_manager_->CreateTexture(i, i); | |
201 auto handle = CreateLockedServiceHandleForTesting(); | |
202 discardable_manager_.InsertLockedTexture(i, texture_size, texture_manager_, | |
203 handle); | |
204 } | |
205 | |
206 gles2::TextureRef* texture_to_unbind; | |
207 EXPECT_TRUE(discardable_manager_.UnlockTexture(3, texture_manager_, | |
208 &texture_to_unbind)); | |
209 EXPECT_NE(nullptr, texture_to_unbind); | |
210 EXPECT_TRUE(discardable_manager_.UnlockTexture(1, texture_manager_, | |
211 &texture_to_unbind)); | |
212 EXPECT_NE(nullptr, texture_to_unbind); | |
213 EXPECT_TRUE(discardable_manager_.UnlockTexture(2, texture_manager_, | |
214 &texture_to_unbind)); | |
215 EXPECT_NE(nullptr, texture_to_unbind); | |
216 EXPECT_TRUE(discardable_manager_.UnlockTexture(4, texture_manager_, | |
217 &texture_to_unbind)); | |
218 EXPECT_NE(nullptr, texture_to_unbind); | |
219 | |
220 // Allocate four more textures - the previous 4 should be evicted / deleted in | |
221 // LRU order. | |
222 { | |
223 InSequence s; | |
224 ExpectUnlockedTextureDeletion(3); | |
225 ExpectUnlockedTextureDeletion(1); | |
226 ExpectUnlockedTextureDeletion(2); | |
227 ExpectUnlockedTextureDeletion(4); | |
228 } | |
229 | |
230 for (int i = 5; i < 9; ++i) { | |
231 texture_manager_->CreateTexture(i, i); | |
232 auto handle = CreateLockedServiceHandleForTesting(); | |
233 discardable_manager_.InsertLockedTexture(i, texture_size, texture_manager_, | |
234 handle); | |
235 } | |
236 | |
237 // Ensure that the above expectations are handled by this point. | |
238 Mock::VerifyAndClearExpectations(gl_.get()); | |
239 Mock::VerifyAndClearExpectations(&destruction_observer_); | |
240 | |
241 // Unlock the next four textures: | |
242 EXPECT_TRUE(discardable_manager_.UnlockTexture(5, texture_manager_, | |
243 &texture_to_unbind)); | |
244 EXPECT_NE(nullptr, texture_to_unbind); | |
245 EXPECT_TRUE(discardable_manager_.UnlockTexture(6, texture_manager_, | |
246 &texture_to_unbind)); | |
247 EXPECT_NE(nullptr, texture_to_unbind); | |
248 EXPECT_TRUE(discardable_manager_.UnlockTexture(8, texture_manager_, | |
249 &texture_to_unbind)); | |
250 EXPECT_NE(nullptr, texture_to_unbind); | |
251 EXPECT_TRUE(discardable_manager_.UnlockTexture(7, texture_manager_, | |
252 &texture_to_unbind)); | |
253 EXPECT_NE(nullptr, texture_to_unbind); | |
254 | |
255 // Allocate one more *large* texture, it should evict the LRU 3 textures. | |
256 { | |
257 InSequence s; | |
258 ExpectUnlockedTextureDeletion(5); | |
259 ExpectUnlockedTextureDeletion(6); | |
260 ExpectUnlockedTextureDeletion(8); | |
261 } | |
262 | |
263 texture_manager_->CreateTexture(9, 9); | |
264 auto handle = CreateLockedServiceHandleForTesting(); | |
265 discardable_manager_.InsertLockedTexture(9, large_texture_size, | |
266 texture_manager_, handle); | |
267 | |
268 // Expect the two remaining textures to clean up. | |
269 ExpectTextureDeletion(9); | |
270 ExpectUnlockedTextureDeletion(7); | |
271 } | |
272 | |
273 TEST_F(ServiceDiscardableManagerTest, TextureSizeChanged) { | |
274 const GLuint kClientId = 1; | |
275 const GLuint kServiceId = 2; | |
276 const size_t texture_size = 4 * 1024 * 1024; | |
277 | |
278 texture_manager_->CreateTexture(kClientId, kServiceId); | |
279 TextureRef* texture_ref = texture_manager_->GetTexture(kClientId); | |
280 auto handle = CreateLockedServiceHandleForTesting(); | |
281 discardable_manager_.InsertLockedTexture(kClientId, 0, texture_manager_, | |
282 handle); | |
283 EXPECT_EQ(0u, discardable_manager_.TotalSizeForTesting()); | |
284 texture_manager_->SetTarget(texture_ref, GL_TEXTURE_2D); | |
285 texture_manager_->SetLevelInfo(texture_ref, GL_TEXTURE_2D, 0, GL_RGBA, 1024, | |
286 1024, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, | |
287 gfx::Rect(1024, 1024)); | |
288 EXPECT_EQ(texture_size, discardable_manager_.TotalSizeForTesting()); | |
289 | |
290 ExpectTextureDeletion(kClientId); | |
291 } | |
292 | |
293 TEST_F(ServiceDiscardableManagerTest, OwnershipOnUnlock) { | |
294 const GLuint kClientId = 1; | |
295 const GLuint kServiceId = 2; | |
296 const size_t texture_size = 4 * 1024 * 1024; | |
297 | |
298 std::unique_ptr<ServiceDiscardableHandle> service_handle; | |
299 std::unique_ptr<ClientDiscardableHandle> client_handle; | |
300 CreateLockedHandlesForTesting(&service_handle, &client_handle); | |
301 texture_manager_->CreateTexture(kClientId, kServiceId); | |
302 discardable_manager_.InsertLockedTexture(kClientId, texture_size, | |
303 texture_manager_, *service_handle); | |
304 | |
305 // Ensure that the service ref count is used to determine ownership changes. | |
306 client_handle->Lock(); | |
307 TextureRef* texture_to_unbind; | |
308 discardable_manager_.UnlockTexture(kClientId, texture_manager_, | |
309 &texture_to_unbind); | |
310 EXPECT_NE(nullptr, texture_to_unbind); | |
311 EXPECT_TRUE(discardable_manager_.IsEntryLockedForTesting(kClientId, | |
312 texture_manager_)); | |
313 | |
314 // Get the counts back in sync. | |
315 discardable_manager_.LockTexture(kClientId, texture_manager_); | |
316 discardable_manager_.UnlockTexture(kClientId, texture_manager_, | |
317 &texture_to_unbind); | |
318 EXPECT_NE(nullptr, texture_to_unbind); | |
319 EXPECT_FALSE(discardable_manager_.IsEntryLockedForTesting(kClientId, | |
320 texture_manager_)); | |
321 | |
322 // Re-lock the texture twice. | |
323 client_handle->Lock(); | |
324 discardable_manager_.LockTexture(kClientId, texture_manager_); | |
325 client_handle->Lock(); | |
326 discardable_manager_.LockTexture(kClientId, texture_manager_); | |
327 | |
328 // Ensure that unlocking once doesn't cause us to unbind the texture. | |
329 discardable_manager_.UnlockTexture(kClientId, texture_manager_, | |
330 &texture_to_unbind); | |
331 EXPECT_EQ(nullptr, texture_to_unbind); | |
332 EXPECT_TRUE(discardable_manager_.IsEntryLockedForTesting(kClientId, | |
333 texture_manager_)); | |
334 | |
335 // The second unlock should unbind/unlock the texture. | |
336 discardable_manager_.UnlockTexture(kClientId, texture_manager_, | |
337 &texture_to_unbind); | |
338 EXPECT_NE(nullptr, texture_to_unbind); | |
339 EXPECT_FALSE(discardable_manager_.IsEntryLockedForTesting(kClientId, | |
340 texture_manager_)); | |
341 | |
342 ExpectUnlockedTextureDeletion(kClientId); | |
343 } | |
344 | |
345 TEST_F(ServiceDiscardableManagerTest, BindGeneratedTextureCollision) { | |
346 const GLuint kClientId = 1; | |
347 const GLuint kServiceId = 2; | |
348 const GLuint kGeneratedServiceId = 3; | |
349 const size_t texture_size = 4 * 1024 * 1024; | |
350 | |
351 // Create and insert a new texture. | |
352 texture_manager_->CreateTexture(kClientId, kServiceId); | |
353 auto handle = CreateLockedServiceHandleForTesting(); | |
354 discardable_manager_.InsertLockedTexture(kClientId, texture_size, | |
355 texture_manager_, handle); | |
356 | |
357 // Unlock the texture, ServiceDiscardableManager should take ownership of the | |
358 // TextureRef. | |
359 gles2::TextureRef* texture_to_unbind; | |
360 EXPECT_TRUE(discardable_manager_.UnlockTexture(kClientId, texture_manager_, | |
361 &texture_to_unbind)); | |
362 EXPECT_NE(nullptr, texture_to_unbind); | |
363 EXPECT_EQ(nullptr, texture_manager_->GetTexture(kClientId)); | |
364 | |
365 // Generate a new texture for the given client id, similar to "bind generates | |
366 // resource" behavior. | |
367 texture_manager_->CreateTexture(kClientId, kGeneratedServiceId); | |
368 TextureRef* generated_texture_ref = texture_manager_->GetTexture(kClientId); | |
369 | |
370 // Re-lock the texture, the TextureManager should delete the returned | |
371 // texture and keep the generated one. | |
372 ExpectUnlockedTextureDeletion(kClientId); | |
373 discardable_manager_.LockTexture(kClientId, texture_manager_); | |
374 EXPECT_EQ(generated_texture_ref, texture_manager_->GetTexture(kClientId)); | |
375 | |
376 // Delete the texture from the TextureManager, it should also be removed from | |
377 // the ServiceDiscardableManager. | |
378 ExpectTextureDeletion(kClientId); | |
379 texture_manager_->RemoveTexture(kClientId); | |
380 EXPECT_EQ(0u, discardable_manager_.NumCacheEntriesForTesting()); | |
piman
2017/05/12 18:01:27
Another edge case worth testing:
glGenTextures ->
ericrk
2017/05/12 20:24:00
This shouldn't be allowed by the client gles2_impl
| |
381 } | |
382 | |
383 } // namespace gles2 | |
384 } // namespace gpu | |
OLD | NEW |