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 |