Chromium Code Reviews| 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 |