| // Copyright 2017 The Chromium Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "gpu/command_buffer/client/client_discardable_manager.h" | |
| #include "base/notreached.h" | |
| #include "testing/gtest/include/gtest/gtest.h" | |
| namespace gpu { | |
| namespace { | |
| class FakeCommandBuffer : public CommandBuffer { | |
| public: | |
| FakeCommandBuffer() = default; | |
| ~FakeCommandBuffer() override { EXPECT_TRUE(active_ids_.empty()); } | |
| // Overridden from CommandBuffer: | |
| State GetLastState() override { NOTREACHED(); } | |
| void Flush(int32_t put_offset) override { NOTREACHED(); } | |
| void OrderingBarrier(int32_t put_offset) override { NOTREACHED(); } | |
| State WaitForTokenInRange(int32_t start, int32_t end) override { | |
| NOTREACHED(); | |
| } | |
| State WaitForGetOffsetInRange(uint32_t set_get_buffer_count, | |
| int32_t start, | |
| int32_t end) override { | |
| NOTREACHED(); | |
| } | |
| void SetGetBuffer(int32_t transfer_buffer_id) override { NOTREACHED(); } | |
| scoped_refptr<gpu::Buffer> CreateTransferBuffer( | |
| uint32_t size, | |
| int32_t* id, | |
| uint32_t alignment = 0, | |
| TransferBufferAllocationOption option = | |
| TransferBufferAllocationOption::kLoseContextOnOOM) override { | |
| *id = next_id_++; | |
| active_ids_.insert(*id); | |
| return MakeMemoryBuffer(size, alignment); | |
| } | |
| void DestroyTransferBuffer(int32_t id) override { | |
| size_t erased_elements = active_ids_.erase(id); | |
| EXPECT_TRUE(erased_elements > 0); | |
| } | |
| void ForceLostContext(error::ContextLostReason reason) override { | |
| // No-op; doesn't need to be exercised here. | |
| } | |
| private: | |
| int32_t next_id_ = 1; | |
| std::set<int32_t> active_ids_; | |
| }; | |
| void UnlockClientHandleForTesting( | |
| const ClientDiscardableHandle& client_handle) { | |
| ServiceDiscardableHandle service_handle(client_handle.BufferForTesting(), | |
| client_handle.byte_offset(), | |
| client_handle.shm_id()); | |
| service_handle.Unlock(); | |
| } | |
| bool DeleteClientHandleForTesting( | |
| const ClientDiscardableHandle& client_handle) { | |
| ServiceDiscardableHandle service_handle(client_handle.BufferForTesting(), | |
| client_handle.byte_offset(), | |
| client_handle.shm_id()); | |
| return service_handle.Delete(); | |
| } | |
| void UnlockAndDeleteClientHandleForTesting( | |
| const ClientDiscardableHandle& client_handle) { | |
| UnlockClientHandleForTesting(client_handle); | |
| EXPECT_TRUE(DeleteClientHandleForTesting(client_handle)); | |
| } | |
| } // namespace | |
| TEST(ClientDiscardableManagerTest, BasicUsage) { | |
| FakeCommandBuffer command_buffer; | |
| ClientDiscardableManager manager; | |
| ClientDiscardableHandle::Id handle_id = manager.CreateHandle(&command_buffer); | |
| ClientDiscardableHandle handle = manager.GetHandle(handle_id); | |
| EXPECT_TRUE(handle.IsLockedForTesting()); | |
| EXPECT_EQ(handle.shm_id(), 1); | |
| EXPECT_FALSE(DeleteClientHandleForTesting(handle)); | |
| UnlockClientHandleForTesting(handle); | |
| manager.LockHandle(handle_id); | |
| EXPECT_FALSE(DeleteClientHandleForTesting(handle)); | |
| UnlockAndDeleteClientHandleForTesting(handle); | |
| manager.FreeHandle(handle_id); | |
| manager.CheckPendingForTesting(&command_buffer); | |
| } | |
| TEST(ClientDiscardableManagerTest, Reuse) { | |
| FakeCommandBuffer command_buffer; | |
| ClientDiscardableManager manager; | |
| manager.SetElementCountForTesting(1024); | |
| std::vector<ClientDiscardableHandle::Id> handle_ids; | |
| for (int i = 0; i < 1024; ++i) { | |
| ClientDiscardableHandle::Id handle_id = | |
| manager.CreateHandle(&command_buffer); | |
| ClientDiscardableHandle handle = manager.GetHandle(handle_id); | |
| EXPECT_TRUE(handle.IsLockedForTesting()); | |
| EXPECT_EQ(handle.shm_id(), 1); | |
| UnlockClientHandleForTesting(handle); | |
| handle_ids.push_back(handle_id); | |
| } | |
| // Delete every other entry. | |
| for (auto it = handle_ids.begin(); it != handle_ids.end();) { | |
| DeleteClientHandleForTesting(manager.GetHandle(*it)); | |
| manager.FreeHandle(*it); | |
| it = handle_ids.erase(it); | |
| ++it; | |
| } | |
| // Allocate 512 more entries, ensure we re-use the original buffer. | |
| for (int i = 0; i < 512; ++i) { | |
| ClientDiscardableHandle::Id handle_id = | |
| manager.CreateHandle(&command_buffer); | |
| ClientDiscardableHandle handle = manager.GetHandle(handle_id); | |
| EXPECT_TRUE(handle.IsLockedForTesting()); | |
| EXPECT_EQ(handle.shm_id(), 1); | |
| UnlockClientHandleForTesting(handle); | |
| handle_ids.push_back(handle_id); | |
| } | |
| // Delete all outstanding allocations | |
| for (const auto& handle_id : handle_ids) { | |
| DeleteClientHandleForTesting(manager.GetHandle(handle_id)); | |
| manager.FreeHandle(handle_id); | |
| } | |
| manager.CheckPendingForTesting(&command_buffer); | |
| } | |
| TEST(ClientDiscardableManagerTest, MultipleAllocations) { | |
| FakeCommandBuffer command_buffer; | |
| ClientDiscardableManager manager; | |
| manager.SetElementCountForTesting(1024); | |
| std::vector<ClientDiscardableHandle::Id> handle_ids; | |
| for (int i = 1; i <= 1024; ++i) { | |
| ClientDiscardableHandle::Id handle_id = | |
| manager.CreateHandle(&command_buffer); | |
| ClientDiscardableHandle handle = manager.GetHandle(handle_id); | |
| EXPECT_TRUE(handle.IsLockedForTesting()); | |
| EXPECT_EQ(handle.shm_id(), 1); | |
| UnlockClientHandleForTesting(handle); | |
| handle_ids.push_back(handle_id); | |
| } | |
| // Allocate and free one entry multiple times, this should cause the | |
| // allocation and release of a new shm_id each time. | |
| for (int i = 1; i < 10; ++i) { | |
| ClientDiscardableHandle::Id handle_id = | |
| manager.CreateHandle(&command_buffer); | |
| ClientDiscardableHandle handle = manager.GetHandle(handle_id); | |
| EXPECT_TRUE(handle.IsLockedForTesting()); | |
| EXPECT_EQ(handle.shm_id(), i + 1); | |
| UnlockAndDeleteClientHandleForTesting(handle); | |
| manager.FreeHandle(handle_id); | |
| } | |
| // Delete all outstanding allocations | |
| for (const auto& handle_id : handle_ids) { | |
| DeleteClientHandleForTesting(manager.GetHandle(handle_id)); | |
| manager.FreeHandle(handle_id); | |
| } | |
| manager.CheckPendingForTesting(&command_buffer); | |
| } | |
| TEST(ClientDiscardableManagerTest, FreeDeleted) { | |
| FakeCommandBuffer command_buffer; | |
| ClientDiscardableManager manager; | |
| manager.SetElementCountForTesting(4); | |
| // Track seen IDs, we should never see an ID again, even when re-using a | |
| // handle. | |
| std::set<ClientDiscardableHandle::Id> seen_ids; | |
| // Fill our allocation with unlocked handles. | |
| std::vector<ClientDiscardableHandle::Id> handle_ids; | |
| for (int i = 0; i < 4; ++i) { | |
| ClientDiscardableHandle::Id handle_id = | |
| manager.CreateHandle(&command_buffer); | |
| EXPECT_EQ(0u, seen_ids.count(handle_id)); | |
| seen_ids.insert(handle_id); | |
| ClientDiscardableHandle handle = manager.GetHandle(handle_id); | |
| EXPECT_TRUE(handle.IsLockedForTesting()); | |
| EXPECT_EQ(handle.shm_id(), 1); | |
| UnlockClientHandleForTesting(handle); | |
| handle_ids.push_back(handle_id); | |
| } | |
| // Allocate and free a new entry. It should get a new allocation. | |
| { | |
| ClientDiscardableHandle::Id handle_id = | |
| manager.CreateHandle(&command_buffer); | |
| EXPECT_EQ(0u, seen_ids.count(handle_id)); | |
| seen_ids.insert(handle_id); | |
| ClientDiscardableHandle handle = manager.GetHandle(handle_id); | |
| EXPECT_TRUE(handle.IsLockedForTesting()); | |
| EXPECT_EQ(handle.shm_id(), 2); | |
| UnlockAndDeleteClientHandleForTesting(handle); | |
| manager.FreeHandle(handle_id); | |
| } | |
| // Delete (but don't free) one of the above entries. | |
| DeleteClientHandleForTesting(manager.GetHandle(handle_ids[0])); | |
| // Allocate and free a new entry, it should re-use the first allocation. | |
| { | |
| ClientDiscardableHandle::Id handle_id = | |
| manager.CreateHandle(&command_buffer); | |
| EXPECT_EQ(0u, seen_ids.count(handle_id)); | |
| seen_ids.insert(handle_id); | |
| ClientDiscardableHandle handle = manager.GetHandle(handle_id); | |
| EXPECT_TRUE(handle.IsLockedForTesting()); | |
| EXPECT_EQ(handle.shm_id(), 1); | |
| UnlockAndDeleteClientHandleForTesting(handle); | |
| manager.FreeHandle(handle_id); | |
| } | |
| // Delete and free the remaining handles. | |
| for (int i = 1; i < 4; ++i) { | |
| DeleteClientHandleForTesting(manager.GetHandle(handle_ids[i])); | |
| manager.FreeHandle(handle_ids[i]); | |
| } | |
| manager.CheckPendingForTesting(&command_buffer); | |
| } | |
| } // namespace gpu |