blob: 0c587833bce1dd51a7cfe0edcd6e1c3b79821f32 [file] [log] [blame]
// 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 "src/ui/lib/escher/util/trace_macros.h"
#include "src/ui/lib/escher/vk/gpu_allocator.h"
namespace escher {
// The maximum ratio that the allocated buffer can exceed the requested size.
// Note: If there are large discrepancies between requested size and cached
// buffer size, it would make sense to sub-allocate a buffer and release a
// smaller buffer portion. For now, the cache will reuse a buffer that is at
// most 2x the size requested.
constexpr int8_t kMaxBufferAllocationRequestRatio = 2;
BufferCache::BufferCache(EscherWeakPtr escher)
: ResourceRecycler(escher),
weak_factory_(this) {
BufferCache::~BufferCache() {
cache_size_ = 0;
BufferPtr BufferCache::NewHostBuffer(vk::DeviceSize vk_size) {
TRACE_DURATION("gfx", "escher::BufferCache::NewHostBuffer");
BufferPtr buffer;
// See if there's a buffer of the right size. Or, find the smallest buffer
// that is big enough to handle the size request.
auto size_itr = free_buffers_.lower_bound(vk_size);
if (size_itr != free_buffers_.end() &&
size_itr->first > kMaxBufferAllocationRequestRatio * vk_size) {
// Next largest buffer is too big, just create a new one.
size_itr = free_buffers_.end();
if (size_itr != free_buffers_.end()) {
// There is a free buffer in the cache that can be recycled.
auto& buffer_list_at_size = size_itr->second;
auto buf = std::move(buffer_list_at_size.front());
buffer = BufferPtr(buf.release());
// Remove the element from the free map, and prune the map if necessary.
if (buffer_list_at_size.empty()) {
// Remove the buffer from the cache.
cache_size_ -= buffer->size();
auto info_itr = free_buffers_by_id_.find(buffer->uid());
FX_DCHECK(info_itr != free_buffers_by_id_.end());
auto time_key = info_itr->second.allocation_time;
} else {
// Construct a new buffer of the requested size.
buffer = gpu_allocator_->AllocateBuffer(this, vk_size, kUsageFlags, kMemoryPropertyFlags);
return buffer;
void BufferCache::RecycleResource(std::unique_ptr<Resource> resource) {
std::unique_ptr<Buffer> buffer(static_cast<Buffer*>(resource.release()));
CacheInfo cache_info; = buffer->uid();
cache_info.allocation_time = std::chrono::steady_clock::now();
// TODO( Now buffer->size() is the size of VkBuffer, so it can only
// reclaim buffers of size greater than or equal to the requested size. For
// buffers with size less than the requested size, but with memory enough to
// hold the requested buffer, it doesn't work.
cache_info.size = buffer->size();
// Ensure this buffer is not already tracked.
FX_DCHECK(free_buffers_by_id_.find( == free_buffers_by_id_.end())
<< "uid: " << buffer->uid();
// Add to the map.
// Add to the cache.
cache_size_ += cache_info.size;
free_buffer_cache_[cache_info.allocation_time] = cache_info;
free_buffers_by_id_[] = cache_info;
// Prune if the cache has grown too much.
while (cache_size_ > kMaxMemoryCached && !free_buffer_cache_.empty()) {
// Find the oldest buffer in the cache.
auto cache_itr = free_buffer_cache_.lower_bound(std::chrono::steady_clock::time_point::min());
FX_DCHECK(cache_itr != free_buffer_cache_.end());
uint64_t id_to_free = cache_itr->;
vk::DeviceSize size_freed = cache_itr->second.size;
// Drop the entries from the cache.
cache_size_ -= size_freed;
FX_DCHECK(cache_size_ >= 0);
// Remove the buffer from the free map, releasing the buffer.
auto find_map_for_size_itr = free_buffers_.find(size_freed);
FX_DCHECK(find_map_for_size_itr != free_buffers_.end());
auto& buffer_list_at_size = find_map_for_size_itr->second;
auto find_buf_itr = std::find_if(buffer_list_at_size.begin(), buffer_list_at_size.end(),
[id_to_free](const std::unique_ptr<Buffer>& buffer) {
return buffer->uid() == id_to_free;
FX_DCHECK(find_buf_itr != buffer_list_at_size.end());
// Release the buffer.
// If there's no other buffers of this size, release size map from the
// free_buffers_ map.
if (buffer_list_at_size.empty()) {
} // namespace escher