|  | // Copyright 2018 The Fuchsia 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 "src/ui/lib/escher/renderer/buffer_cache.h" | 
|  |  | 
|  | #include <gtest/gtest.h> | 
|  |  | 
|  | #include "src/ui/lib/escher/test/common/gtest_escher.h" | 
|  |  | 
|  | namespace escher { | 
|  |  | 
|  | VK_TEST(BufferCache, CreateDestroyCache) { | 
|  | auto escher = test::GetEscher()->GetWeakPtr(); | 
|  |  | 
|  | fxl::WeakPtr<BufferCache> weak_cache; | 
|  | { | 
|  | BufferCache cache(escher); | 
|  | weak_cache = cache.GetWeakPtr(); | 
|  | } | 
|  |  | 
|  | EXPECT_FALSE(weak_cache); | 
|  | } | 
|  |  | 
|  | VK_TEST(BufferCache, CreateBuffer) { | 
|  | auto escher = test::GetEscher()->GetWeakPtr(); | 
|  | BufferCache buffer_cache(escher); | 
|  |  | 
|  | vk::DeviceSize requested_size = 256; | 
|  | BufferPtr buffer = buffer_cache.NewHostBuffer(requested_size); | 
|  |  | 
|  | EXPECT_EQ(requested_size, buffer->size()); | 
|  | } | 
|  |  | 
|  | VK_TEST(BufferCache, RecycleBuffer) { | 
|  | auto escher = test::GetEscher()->GetWeakPtr(); | 
|  | BufferCache buffer_cache(escher); | 
|  | vk::DeviceSize requested_size = 512; | 
|  | BufferPtr buffer = buffer_cache.NewHostBuffer(requested_size); | 
|  | uint64_t buffer_id = buffer->uid(); | 
|  |  | 
|  | // Recycle the buffer and request a new buffer of equal or smaller size. | 
|  | buffer_cache.RecycleResource(std::unique_ptr<Resource>(static_cast<Resource*>(buffer.get()))); | 
|  | vk::DeviceSize requested_size2 = 256; | 
|  | BufferPtr buffer2 = buffer_cache.NewHostBuffer(requested_size2); | 
|  |  | 
|  | // The first buffer should have been recycled by the cache. | 
|  | EXPECT_EQ(buffer_id, buffer2->uid()); | 
|  | } | 
|  |  | 
|  | VK_TEST(BufferCache, SmallerBufferShouldNotRecycle) { | 
|  | auto escher = test::GetEscher()->GetWeakPtr(); | 
|  |  | 
|  | // We create a buffer of size 127 and a buffer of size 128. | 
|  | // | 
|  | // In most cases for the first request we will generate a buffer of | 
|  | // size > 127; however after recycling this buffer, we still cannot use that | 
|  | // for buffers of size 128, as the requested buffer size should be less than | 
|  | // or equal to the Vulkan buffer size. | 
|  | BufferCache buffer_cache(escher); | 
|  | vk::DeviceSize requested_size_1 = 127; | 
|  | vk::DeviceSize requested_size_2 = 128; | 
|  |  | 
|  | BufferPtr buffer1 = buffer_cache.NewHostBuffer(requested_size_1); | 
|  | uint64_t buffer1_id = buffer1->uid(); | 
|  | EXPECT_EQ(buffer1->size(), requested_size_1); | 
|  |  | 
|  | // Recycle buffer 1. | 
|  | buffer1.reset(); | 
|  |  | 
|  | BufferPtr buffer2 = buffer_cache.NewHostBuffer(requested_size_2); | 
|  | uint64_t buffer2_id = buffer2->uid(); | 
|  | EXPECT_EQ(buffer2->size(), requested_size_2); | 
|  |  | 
|  | EXPECT_TRUE(buffer1_id != buffer2_id); | 
|  | } | 
|  |  | 
|  | VK_TEST(BufferCache, SlightlyLargerBufferShouldRecycle) { | 
|  | auto escher = test::GetEscher()->GetWeakPtr(); | 
|  |  | 
|  | // We create a buffer of size 256 and then recycle it. Then when we create a | 
|  | // buffer of size 254 we will reuse the previous buffer. | 
|  | BufferCache buffer_cache(escher); | 
|  | vk::DeviceSize requested_size_1 = 256; | 
|  | vk::DeviceSize requested_size_2 = 254; | 
|  |  | 
|  | BufferPtr buffer1 = buffer_cache.NewHostBuffer(requested_size_1); | 
|  | uint64_t buffer1_id = buffer1->uid(); | 
|  | vk::DeviceSize buffer1_size = buffer1->size(); | 
|  |  | 
|  | // Recycle buffer 1. | 
|  | buffer1.reset(); | 
|  |  | 
|  | BufferPtr buffer2 = buffer_cache.NewHostBuffer(requested_size_2); | 
|  | uint64_t buffer2_id = buffer2->uid(); | 
|  |  | 
|  | EXPECT_EQ(buffer1_size, buffer2->size()); | 
|  | EXPECT_TRUE(buffer1_id == buffer2_id); | 
|  | } | 
|  |  | 
|  | VK_TEST(BufferCache, DontRecycleLargeBuffer) { | 
|  | auto escher = test::GetEscher()->GetWeakPtr(); | 
|  | BufferCache buffer_cache(escher); | 
|  | vk::DeviceSize requested_size = 512; | 
|  | BufferPtr buffer = buffer_cache.NewHostBuffer(requested_size); | 
|  | uint64_t buffer_id = buffer->uid(); | 
|  |  | 
|  | // Recycle the buffer and request a new buffer of less than half the size. | 
|  | EXPECT_EQ(0U, buffer_cache.free_buffer_count()); | 
|  | buffer = nullptr; | 
|  | EXPECT_EQ(1U, buffer_cache.free_buffer_count()); | 
|  | vk::DeviceSize requested_size2 = requested_size / 4; | 
|  | BufferPtr buffer2 = buffer_cache.NewHostBuffer(requested_size2); | 
|  |  | 
|  | // The first buffer should not have been recycled by the cache. | 
|  | EXPECT_NE(buffer_id, buffer2->uid()); | 
|  | } | 
|  |  | 
|  | VK_TEST(BufferCache, RecycleMany) { | 
|  | auto escher = test::GetEscher()->GetWeakPtr(); | 
|  | BufferCache buffer_cache(escher); | 
|  | vk::DeviceSize requested_size = 1024 * 512; | 
|  | BufferPtr big_buffer = buffer_cache.NewHostBuffer(requested_size); | 
|  | BufferPtr big_buffer2 = buffer_cache.NewHostBuffer(requested_size); | 
|  | BufferPtr big_buffer3 = buffer_cache.NewHostBuffer(requested_size * 2); | 
|  | uint64_t big_buffer_id = big_buffer->uid(); | 
|  | uint64_t big_buffer2_id = big_buffer2->uid(); | 
|  | uint64_t big_buffer3_id = big_buffer3->uid(); | 
|  | if (big_buffer_id == big_buffer2_id || big_buffer_id == big_buffer3_id || | 
|  | big_buffer2_id == big_buffer3_id) { | 
|  | // TODO(fxbug.dev/23753) It seems that the allocator is allocating garbage | 
|  | // memory, and then later filling multiple BufferPtrs with the same | 
|  | // buffer, allocated later. Allocating buffers with the same ID will | 
|  | // crash the BufferCache (as the invariant that buffer IDs are unique) | 
|  | // must be held. When this case is hit, error out early of the test. | 
|  | // Remove this early return when fxbug.dev/23753 is resolved. | 
|  | FX_LOGS(ERROR) << "Error allocating memory, aborting test!"; | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Recycle the buffers. The third buffer should flush the cache. | 
|  | EXPECT_EQ(0U, buffer_cache.free_buffer_count()); | 
|  | big_buffer = nullptr; | 
|  | big_buffer2 = nullptr; | 
|  | EXPECT_EQ(2U, buffer_cache.free_buffer_count()); | 
|  | big_buffer3 = nullptr; | 
|  |  | 
|  | // Requesting a buffer should create a new buffer since the cache should | 
|  | // have been flushed by the third buffer, and it is too big to use for this | 
|  | // fourth buffer. | 
|  | BufferPtr big_buffer4 = buffer_cache.NewHostBuffer(requested_size / 2); | 
|  | EXPECT_NE(big_buffer3_id, big_buffer4->uid()); | 
|  | EXPECT_NE(big_buffer2_id, big_buffer4->uid()); | 
|  | EXPECT_NE(big_buffer_id, big_buffer4->uid()); | 
|  |  | 
|  | // Request a buffer that should use the recycled big_buffer3. | 
|  | BufferPtr big_buffer5 = buffer_cache.NewHostBuffer(requested_size); | 
|  | EXPECT_EQ(big_buffer3_id, big_buffer5->uid()); | 
|  | } | 
|  |  | 
|  | }  // namespace escher |