Chromium Code Reviews| Index: gpu/command_buffer/service/service_discardable_manager_unittest.cc |
| diff --git a/gpu/command_buffer/service/service_discardable_manager_unittest.cc b/gpu/command_buffer/service/service_discardable_manager_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..ee143c148dd43a59d032a016c313f987d67728bc |
| --- /dev/null |
| +++ b/gpu/command_buffer/service/service_discardable_manager_unittest.cc |
| @@ -0,0 +1,384 @@ |
| +// Copyright (c) 2017 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "gpu/command_buffer/service/service_discardable_manager.h" |
| + |
| +#include "gpu/command_buffer/service/gles2_cmd_decoder_mock.h" |
| +#include "gpu/command_buffer/service/gpu_service_test.h" |
| +#include "gpu/command_buffer/service/mailbox_manager.h" |
| +#include "gpu/command_buffer/service/memory_tracking.h" |
| +#include "gpu/command_buffer/service/mocks.h" |
| +#include "gpu/command_buffer/service/test_helper.h" |
| +#include "gpu/command_buffer/service/texture_manager.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| +#include "ui/gl/gl_image_stub.h" |
| +#include "ui/gl/gl_mock.h" |
| +#include "ui/gl/gl_switches.h" |
| + |
| +using ::testing::Pointee; |
| +using ::testing::_; |
| +using ::testing::Invoke; |
| +using ::testing::Mock; |
| +using ::testing::InSequence; |
| + |
| +namespace gpu { |
| +namespace gles2 { |
| +namespace { |
| + |
| +void CreateLockedHandlesForTesting( |
| + std::unique_ptr<ServiceDiscardableHandle>* service_handle, |
| + std::unique_ptr<ClientDiscardableHandle>* client_handle) { |
| + std::unique_ptr<base::SharedMemory> shared_mem(new base::SharedMemory); |
| + shared_mem->CreateAndMapAnonymous(sizeof(uint32_t)); |
| + scoped_refptr<gpu::Buffer> buffer = |
| + MakeBufferFromSharedMemory(std::move(shared_mem), sizeof(uint32_t)); |
| + |
| + client_handle->reset(new ClientDiscardableHandle(buffer, 0, 0)); |
| + service_handle->reset(new ServiceDiscardableHandle(buffer, 0, 0)); |
| +} |
| + |
| +ServiceDiscardableHandle CreateLockedServiceHandleForTesting() { |
| + std::unique_ptr<ServiceDiscardableHandle> service_handle; |
| + std::unique_ptr<ClientDiscardableHandle> client_handle; |
| + CreateLockedHandlesForTesting(&service_handle, &client_handle); |
| + return *service_handle; |
| +} |
| + |
| +class MockDestructionObserver : public TextureManager::DestructionObserver { |
| + public: |
| + MOCK_METHOD1(OnTextureManagerDestroying, void(TextureManager* manager)); |
| + MOCK_METHOD1(OnTextureRefDestroying, void(TextureRef* ref)); |
| +}; |
| + |
| +} // namespace |
| + |
| +class ServiceDiscardableManagerTest : public GpuServiceTest { |
| + public: |
| + ServiceDiscardableManagerTest() {} |
| + ~ServiceDiscardableManagerTest() override {} |
| + |
| + protected: |
| + void SetUp() override { |
| + GpuServiceTest::SetUp(); |
| + decoder_.reset(new MockGLES2Decoder()); |
| + feature_info_ = new FeatureInfo(); |
| + context_group_ = scoped_refptr<ContextGroup>(new ContextGroup( |
| + gpu_preferences_, nullptr, nullptr, nullptr, nullptr, feature_info_, |
| + false, nullptr, nullptr, GpuFeatureInfo(), &discardable_manager_)); |
| + TestHelper::SetupContextGroupInitExpectations( |
| + gl_.get(), DisallowedFeatures(), "", "", CONTEXT_TYPE_OPENGLES2, false); |
| + context_group_->Initialize(decoder_.get(), CONTEXT_TYPE_OPENGLES2, |
| + DisallowedFeatures()); |
| + texture_manager_ = context_group_->texture_manager(); |
| + texture_manager_->AddObserver(&destruction_observer_); |
| + } |
| + |
| + void TearDown() override { |
| + EXPECT_CALL(destruction_observer_, OnTextureManagerDestroying(_)) |
| + .RetiresOnSaturation(); |
| + // 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.
|
| + EXPECT_CALL(*gl_, DeleteTextures(6, _)); |
| + |
| + context_group_->Destroy(decoder_.get(), true); |
| + context_group_ = nullptr; |
| + EXPECT_EQ(0u, discardable_manager_.NumCacheEntriesForTesting()); |
| + GpuServiceTest::TearDown(); |
| + } |
| + |
| + void ExpectUnlockedTextureDeletion(uint32_t client_id) { |
| + TextureRef* ref = discardable_manager_.UnlockedTextureRefForTesting( |
| + client_id, texture_manager_); |
| + ExpectTextureRefDeletion(ref); |
| + } |
| + |
| + void ExpectTextureDeletion(uint32_t client_id) { |
| + TextureRef* ref = texture_manager_->GetTexture(client_id); |
| + ExpectTextureRefDeletion(ref); |
| + } |
| + |
| + void ExpectTextureRefDeletion(TextureRef* ref) { |
| + EXPECT_NE(nullptr, ref); |
| + ref->AddObserver(); |
| + EXPECT_CALL(destruction_observer_, OnTextureRefDestroying(ref)) |
| + .WillOnce(Invoke([](TextureRef* ref) { ref->RemoveObserver(); })); |
| + EXPECT_CALL(*gl_, DeleteTextures(1, Pointee(ref->service_id()))) |
| + .RetiresOnSaturation(); |
| + } |
| + |
| + ServiceDiscardableManager discardable_manager_; |
| + GpuPreferences gpu_preferences_; |
| + scoped_refptr<FeatureInfo> feature_info_; |
| + MockDestructionObserver destruction_observer_; |
| + TextureManager* texture_manager_; |
| + std::unique_ptr<MockGLES2Decoder> decoder_; |
| + scoped_refptr<gles2::ContextGroup> context_group_; |
| +}; |
| + |
| +TEST_F(ServiceDiscardableManagerTest, BasicUsage) { |
| + const GLuint kClientId = 1; |
| + const GLuint kServiceId = 2; |
| + const size_t texture_size = 4 * 1024 * 1024; |
| + |
| + // Create and insert a new texture. |
| + texture_manager_->CreateTexture(kClientId, kServiceId); |
| + auto handle = CreateLockedServiceHandleForTesting(); |
| + discardable_manager_.InsertLockedTexture(kClientId, texture_size, |
| + texture_manager_, handle); |
| + EXPECT_EQ(1u, discardable_manager_.NumCacheEntriesForTesting()); |
| + EXPECT_TRUE(discardable_manager_.IsEntryLockedForTesting(kClientId, |
| + texture_manager_)); |
| + EXPECT_NE(nullptr, texture_manager_->GetTexture(kClientId)); |
| + |
| + // Unlock the texture, ServiceDiscardableManager should take ownership of the |
| + // TextureRef. |
| + gles2::TextureRef* texture_to_unbind; |
| + EXPECT_TRUE(discardable_manager_.UnlockTexture(kClientId, texture_manager_, |
| + &texture_to_unbind)); |
| + EXPECT_NE(nullptr, texture_to_unbind); |
| + EXPECT_FALSE(discardable_manager_.IsEntryLockedForTesting(kClientId, |
| + texture_manager_)); |
| + EXPECT_EQ(nullptr, texture_manager_->GetTexture(kClientId)); |
| + |
| + // Re-lock the texture, the TextureManager should now resume ownership of |
| + // the TextureRef. |
| + discardable_manager_.LockTexture(kClientId, texture_manager_); |
| + EXPECT_NE(nullptr, texture_manager_->GetTexture(kClientId)); |
| + |
| + // Delete the texture from the TextureManager, it should also be removed from |
| + // the ServiceDiscardableManager. |
| + ExpectTextureDeletion(kClientId); |
| + texture_manager_->RemoveTexture(kClientId); |
| + EXPECT_EQ(0u, discardable_manager_.NumCacheEntriesForTesting()); |
| +} |
| + |
| +TEST_F(ServiceDiscardableManagerTest, DeleteAtShutdown) { |
| + const size_t texture_size = 4 * 16 * 16; |
| + |
| + // Create 8 small textures (which will not hit memory limits), leaving every |
| + // other one unlocked. |
| + for (int i = 1; i <= 8; ++i) { |
| + texture_manager_->CreateTexture(i, i); |
| + auto handle = CreateLockedServiceHandleForTesting(); |
| + discardable_manager_.InsertLockedTexture(i, texture_size, texture_manager_, |
| + handle); |
| + if (i % 2) { |
| + TextureRef* texture_to_unbind; |
| + EXPECT_TRUE(discardable_manager_.UnlockTexture(i, texture_manager_, |
| + &texture_to_unbind)); |
| + EXPECT_NE(nullptr, texture_to_unbind); |
| + } |
| + } |
| + |
| + // Expect that all 8 will be deleted at shutdown, regardless of |
| + // locked/unlocked state. |
| + for (int i = 1; i <= 8; ++i) { |
| + if (i % 2) { |
| + ExpectUnlockedTextureDeletion(i); |
| + } else { |
| + ExpectTextureDeletion(i); |
| + } |
| + } |
| + |
| + // Let the test shut down, the expectations should be fulfilled. |
| +} |
| + |
| +TEST_F(ServiceDiscardableManagerTest, UnlockInvalid) { |
| + const GLuint kClientId = 1; |
| + gles2::TextureRef* texture_to_unbind; |
| + EXPECT_FALSE(discardable_manager_.UnlockTexture(kClientId, texture_manager_, |
| + &texture_to_unbind)); |
| + EXPECT_EQ(nullptr, texture_to_unbind); |
| +} |
| + |
| +TEST_F(ServiceDiscardableManagerTest, Limits) { |
| + const size_t texture_size = 64 * 1024 * 1024; |
| + 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.
|
| + |
| + // Create 4 textures, this should fill up the discardable cache. |
| + for (int i = 1; i < 5; ++i) { |
| + texture_manager_->CreateTexture(i, i); |
| + auto handle = CreateLockedServiceHandleForTesting(); |
| + discardable_manager_.InsertLockedTexture(i, texture_size, texture_manager_, |
| + handle); |
| + } |
| + |
| + gles2::TextureRef* texture_to_unbind; |
| + EXPECT_TRUE(discardable_manager_.UnlockTexture(3, texture_manager_, |
| + &texture_to_unbind)); |
| + EXPECT_NE(nullptr, texture_to_unbind); |
| + EXPECT_TRUE(discardable_manager_.UnlockTexture(1, texture_manager_, |
| + &texture_to_unbind)); |
| + EXPECT_NE(nullptr, texture_to_unbind); |
| + EXPECT_TRUE(discardable_manager_.UnlockTexture(2, texture_manager_, |
| + &texture_to_unbind)); |
| + EXPECT_NE(nullptr, texture_to_unbind); |
| + EXPECT_TRUE(discardable_manager_.UnlockTexture(4, texture_manager_, |
| + &texture_to_unbind)); |
| + EXPECT_NE(nullptr, texture_to_unbind); |
| + |
| + // Allocate four more textures - the previous 4 should be evicted / deleted in |
| + // LRU order. |
| + { |
| + InSequence s; |
| + ExpectUnlockedTextureDeletion(3); |
| + ExpectUnlockedTextureDeletion(1); |
| + ExpectUnlockedTextureDeletion(2); |
| + ExpectUnlockedTextureDeletion(4); |
| + } |
| + |
| + for (int i = 5; i < 9; ++i) { |
| + texture_manager_->CreateTexture(i, i); |
| + auto handle = CreateLockedServiceHandleForTesting(); |
| + discardable_manager_.InsertLockedTexture(i, texture_size, texture_manager_, |
| + handle); |
| + } |
| + |
| + // Ensure that the above expectations are handled by this point. |
| + Mock::VerifyAndClearExpectations(gl_.get()); |
| + Mock::VerifyAndClearExpectations(&destruction_observer_); |
| + |
| + // Unlock the next four textures: |
| + EXPECT_TRUE(discardable_manager_.UnlockTexture(5, texture_manager_, |
| + &texture_to_unbind)); |
| + EXPECT_NE(nullptr, texture_to_unbind); |
| + EXPECT_TRUE(discardable_manager_.UnlockTexture(6, texture_manager_, |
| + &texture_to_unbind)); |
| + EXPECT_NE(nullptr, texture_to_unbind); |
| + EXPECT_TRUE(discardable_manager_.UnlockTexture(8, texture_manager_, |
| + &texture_to_unbind)); |
| + EXPECT_NE(nullptr, texture_to_unbind); |
| + EXPECT_TRUE(discardable_manager_.UnlockTexture(7, texture_manager_, |
| + &texture_to_unbind)); |
| + EXPECT_NE(nullptr, texture_to_unbind); |
| + |
| + // Allocate one more *large* texture, it should evict the LRU 3 textures. |
| + { |
| + InSequence s; |
| + ExpectUnlockedTextureDeletion(5); |
| + ExpectUnlockedTextureDeletion(6); |
| + ExpectUnlockedTextureDeletion(8); |
| + } |
| + |
| + texture_manager_->CreateTexture(9, 9); |
| + auto handle = CreateLockedServiceHandleForTesting(); |
| + discardable_manager_.InsertLockedTexture(9, large_texture_size, |
| + texture_manager_, handle); |
| + |
| + // Expect the two remaining textures to clean up. |
| + ExpectTextureDeletion(9); |
| + ExpectUnlockedTextureDeletion(7); |
| +} |
| + |
| +TEST_F(ServiceDiscardableManagerTest, TextureSizeChanged) { |
| + const GLuint kClientId = 1; |
| + const GLuint kServiceId = 2; |
| + const size_t texture_size = 4 * 1024 * 1024; |
| + |
| + texture_manager_->CreateTexture(kClientId, kServiceId); |
| + TextureRef* texture_ref = texture_manager_->GetTexture(kClientId); |
| + auto handle = CreateLockedServiceHandleForTesting(); |
| + discardable_manager_.InsertLockedTexture(kClientId, 0, texture_manager_, |
| + handle); |
| + EXPECT_EQ(0u, discardable_manager_.TotalSizeForTesting()); |
| + texture_manager_->SetTarget(texture_ref, GL_TEXTURE_2D); |
| + texture_manager_->SetLevelInfo(texture_ref, GL_TEXTURE_2D, 0, GL_RGBA, 1024, |
| + 1024, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| + gfx::Rect(1024, 1024)); |
| + EXPECT_EQ(texture_size, discardable_manager_.TotalSizeForTesting()); |
| + |
| + ExpectTextureDeletion(kClientId); |
| +} |
| + |
| +TEST_F(ServiceDiscardableManagerTest, OwnershipOnUnlock) { |
| + const GLuint kClientId = 1; |
| + const GLuint kServiceId = 2; |
| + const size_t texture_size = 4 * 1024 * 1024; |
| + |
| + std::unique_ptr<ServiceDiscardableHandle> service_handle; |
| + std::unique_ptr<ClientDiscardableHandle> client_handle; |
| + CreateLockedHandlesForTesting(&service_handle, &client_handle); |
| + texture_manager_->CreateTexture(kClientId, kServiceId); |
| + discardable_manager_.InsertLockedTexture(kClientId, texture_size, |
| + texture_manager_, *service_handle); |
| + |
| + // Ensure that the service ref count is used to determine ownership changes. |
| + client_handle->Lock(); |
| + TextureRef* texture_to_unbind; |
| + discardable_manager_.UnlockTexture(kClientId, texture_manager_, |
| + &texture_to_unbind); |
| + EXPECT_NE(nullptr, texture_to_unbind); |
| + EXPECT_TRUE(discardable_manager_.IsEntryLockedForTesting(kClientId, |
| + texture_manager_)); |
| + |
| + // Get the counts back in sync. |
| + discardable_manager_.LockTexture(kClientId, texture_manager_); |
| + discardable_manager_.UnlockTexture(kClientId, texture_manager_, |
| + &texture_to_unbind); |
| + EXPECT_NE(nullptr, texture_to_unbind); |
| + EXPECT_FALSE(discardable_manager_.IsEntryLockedForTesting(kClientId, |
| + texture_manager_)); |
| + |
| + // Re-lock the texture twice. |
| + client_handle->Lock(); |
| + discardable_manager_.LockTexture(kClientId, texture_manager_); |
| + client_handle->Lock(); |
| + discardable_manager_.LockTexture(kClientId, texture_manager_); |
| + |
| + // Ensure that unlocking once doesn't cause us to unbind the texture. |
| + discardable_manager_.UnlockTexture(kClientId, texture_manager_, |
| + &texture_to_unbind); |
| + EXPECT_EQ(nullptr, texture_to_unbind); |
| + EXPECT_TRUE(discardable_manager_.IsEntryLockedForTesting(kClientId, |
| + texture_manager_)); |
| + |
| + // The second unlock should unbind/unlock the texture. |
| + discardable_manager_.UnlockTexture(kClientId, texture_manager_, |
| + &texture_to_unbind); |
| + EXPECT_NE(nullptr, texture_to_unbind); |
| + EXPECT_FALSE(discardable_manager_.IsEntryLockedForTesting(kClientId, |
| + texture_manager_)); |
| + |
| + ExpectUnlockedTextureDeletion(kClientId); |
| +} |
| + |
| +TEST_F(ServiceDiscardableManagerTest, BindGeneratedTextureCollision) { |
| + const GLuint kClientId = 1; |
| + const GLuint kServiceId = 2; |
| + const GLuint kGeneratedServiceId = 3; |
| + const size_t texture_size = 4 * 1024 * 1024; |
| + |
| + // Create and insert a new texture. |
| + texture_manager_->CreateTexture(kClientId, kServiceId); |
| + auto handle = CreateLockedServiceHandleForTesting(); |
| + discardable_manager_.InsertLockedTexture(kClientId, texture_size, |
| + texture_manager_, handle); |
| + |
| + // Unlock the texture, ServiceDiscardableManager should take ownership of the |
| + // TextureRef. |
| + gles2::TextureRef* texture_to_unbind; |
| + EXPECT_TRUE(discardable_manager_.UnlockTexture(kClientId, texture_manager_, |
| + &texture_to_unbind)); |
| + EXPECT_NE(nullptr, texture_to_unbind); |
| + EXPECT_EQ(nullptr, texture_manager_->GetTexture(kClientId)); |
| + |
| + // Generate a new texture for the given client id, similar to "bind generates |
| + // resource" behavior. |
| + texture_manager_->CreateTexture(kClientId, kGeneratedServiceId); |
| + TextureRef* generated_texture_ref = texture_manager_->GetTexture(kClientId); |
| + |
| + // Re-lock the texture, the TextureManager should delete the returned |
| + // texture and keep the generated one. |
| + ExpectUnlockedTextureDeletion(kClientId); |
| + discardable_manager_.LockTexture(kClientId, texture_manager_); |
| + EXPECT_EQ(generated_texture_ref, texture_manager_->GetTexture(kClientId)); |
| + |
| + // Delete the texture from the TextureManager, it should also be removed from |
| + // the ServiceDiscardableManager. |
| + ExpectTextureDeletion(kClientId); |
| + texture_manager_->RemoveTexture(kClientId); |
| + 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
|
| +} |
| + |
| +} // namespace gles2 |
| +} // namespace gpu |