Path: blob/21.2-virgl/src/gallium/auxiliary/pipebuffer/pb_cache.c
4565 views
/**************************************************************************1*2* Copyright 2007-2008 VMware, Inc.3* Copyright 2015 Advanced Micro Devices, Inc.4* All Rights Reserved.5*6* Permission is hereby granted, free of charge, to any person obtaining a7* copy of this software and associated documentation files (the8* "Software"), to deal in the Software without restriction, including9* without limitation the rights to use, copy, modify, merge, publish,10* distribute, sub license, and/or sell copies of the Software, and to11* permit persons to whom the Software is furnished to do so, subject to12* the following conditions:13*14* The above copyright notice and this permission notice (including the15* next paragraph) shall be included in all copies or substantial portions16* of the Software.17*18* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS19* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF20* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.21* IN NO EVENT SHALL AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR22* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,23* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE24* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.25*26**************************************************************************/2728#include "pb_cache.h"29#include "util/u_memory.h"30#include "util/os_time.h"313233/**34* Actually destroy the buffer.35*/36static void37destroy_buffer_locked(struct pb_cache_entry *entry)38{39struct pb_cache *mgr = entry->mgr;40struct pb_buffer *buf = entry->buffer;4142assert(!pipe_is_referenced(&buf->reference));43if (list_is_linked(&entry->head)) {44list_del(&entry->head);45assert(mgr->num_buffers);46--mgr->num_buffers;47mgr->cache_size -= buf->size;48}49mgr->destroy_buffer(mgr->winsys, buf);50}5152/**53* Free as many cache buffers from the list head as possible.54*/55static void56release_expired_buffers_locked(struct list_head *cache,57int64_t current_time)58{59struct list_head *curr, *next;60struct pb_cache_entry *entry;6162curr = cache->next;63next = curr->next;64while (curr != cache) {65entry = LIST_ENTRY(struct pb_cache_entry, curr, head);6667if (!os_time_timeout(entry->start, entry->end, current_time))68break;6970destroy_buffer_locked(entry);7172curr = next;73next = curr->next;74}75}7677/**78* Add a buffer to the cache. This is typically done when the buffer is79* being released.80*/81void82pb_cache_add_buffer(struct pb_cache_entry *entry)83{84struct pb_cache *mgr = entry->mgr;85struct list_head *cache = &mgr->buckets[entry->bucket_index];86struct pb_buffer *buf = entry->buffer;87unsigned i;8889mtx_lock(&mgr->mutex);90assert(!pipe_is_referenced(&buf->reference));9192int64_t current_time = os_time_get();9394for (i = 0; i < mgr->num_heaps; i++)95release_expired_buffers_locked(&mgr->buckets[i], current_time);9697/* Directly release any buffer that exceeds the limit. */98if (mgr->cache_size + buf->size > mgr->max_cache_size) {99mgr->destroy_buffer(mgr->winsys, buf);100mtx_unlock(&mgr->mutex);101return;102}103104entry->start = os_time_get();105entry->end = entry->start + mgr->usecs;106list_addtail(&entry->head, cache);107++mgr->num_buffers;108mgr->cache_size += buf->size;109mtx_unlock(&mgr->mutex);110}111112/**113* \return 1 if compatible and can be reclaimed114* 0 if incompatible115* -1 if compatible and can't be reclaimed116*/117static int118pb_cache_is_buffer_compat(struct pb_cache_entry *entry,119pb_size size, unsigned alignment, unsigned usage)120{121struct pb_cache *mgr = entry->mgr;122struct pb_buffer *buf = entry->buffer;123124if (!pb_check_usage(usage, buf->usage))125return 0;126127/* be lenient with size */128if (buf->size < size ||129buf->size > (unsigned) (mgr->size_factor * size))130return 0;131132if (usage & mgr->bypass_usage)133return 0;134135if (!pb_check_alignment(alignment, 1u << buf->alignment_log2))136return 0;137138return mgr->can_reclaim(mgr->winsys, buf) ? 1 : -1;139}140141/**142* Find a compatible buffer in the cache, return it, and remove it143* from the cache.144*/145struct pb_buffer *146pb_cache_reclaim_buffer(struct pb_cache *mgr, pb_size size,147unsigned alignment, unsigned usage,148unsigned bucket_index)149{150struct pb_cache_entry *entry;151struct pb_cache_entry *cur_entry;152struct list_head *cur, *next;153int64_t now;154int ret = 0;155156assert(bucket_index < mgr->num_heaps);157struct list_head *cache = &mgr->buckets[bucket_index];158159mtx_lock(&mgr->mutex);160161entry = NULL;162cur = cache->next;163next = cur->next;164165/* search in the expired buffers, freeing them in the process */166now = os_time_get();167while (cur != cache) {168cur_entry = LIST_ENTRY(struct pb_cache_entry, cur, head);169170if (!entry && (ret = pb_cache_is_buffer_compat(cur_entry, size,171alignment, usage)) > 0)172entry = cur_entry;173else if (os_time_timeout(cur_entry->start, cur_entry->end, now))174destroy_buffer_locked(cur_entry);175else176/* This buffer (and all hereafter) are still hot in cache */177break;178179/* the buffer is busy (and probably all remaining ones too) */180if (ret == -1)181break;182183cur = next;184next = cur->next;185}186187/* keep searching in the hot buffers */188if (!entry && ret != -1) {189while (cur != cache) {190cur_entry = LIST_ENTRY(struct pb_cache_entry, cur, head);191ret = pb_cache_is_buffer_compat(cur_entry, size, alignment, usage);192193if (ret > 0) {194entry = cur_entry;195break;196}197if (ret == -1)198break;199/* no need to check the timeout here */200cur = next;201next = cur->next;202}203}204205/* found a compatible buffer, return it */206if (entry) {207struct pb_buffer *buf = entry->buffer;208209mgr->cache_size -= buf->size;210list_del(&entry->head);211--mgr->num_buffers;212mtx_unlock(&mgr->mutex);213/* Increase refcount */214pipe_reference_init(&buf->reference, 1);215return buf;216}217218mtx_unlock(&mgr->mutex);219return NULL;220}221222/**223* Empty the cache. Useful when there is not enough memory.224*/225void226pb_cache_release_all_buffers(struct pb_cache *mgr)227{228struct list_head *curr, *next;229struct pb_cache_entry *buf;230unsigned i;231232mtx_lock(&mgr->mutex);233for (i = 0; i < mgr->num_heaps; i++) {234struct list_head *cache = &mgr->buckets[i];235236curr = cache->next;237next = curr->next;238while (curr != cache) {239buf = LIST_ENTRY(struct pb_cache_entry, curr, head);240destroy_buffer_locked(buf);241curr = next;242next = curr->next;243}244}245mtx_unlock(&mgr->mutex);246}247248void249pb_cache_init_entry(struct pb_cache *mgr, struct pb_cache_entry *entry,250struct pb_buffer *buf, unsigned bucket_index)251{252assert(bucket_index < mgr->num_heaps);253254memset(entry, 0, sizeof(*entry));255entry->buffer = buf;256entry->mgr = mgr;257entry->bucket_index = bucket_index;258}259260/**261* Initialize a caching buffer manager.262*263* @param mgr The cache buffer manager264* @param num_heaps Number of separate caches/buckets indexed by bucket_index265* for faster buffer matching (alternative to slower266* "usage"-based matching).267* @param usecs Unused buffers may be released from the cache after this268* time269* @param size_factor Declare buffers that are size_factor times bigger than270* the requested size as cache hits.271* @param bypass_usage Bitmask. If (requested usage & bypass_usage) != 0,272* buffer allocation requests are rejected.273* @param maximum_cache_size Maximum size of all unused buffers the cache can274* hold.275* @param destroy_buffer Function that destroys a buffer for good.276* @param can_reclaim Whether a buffer can be reclaimed (e.g. is not busy)277*/278void279pb_cache_init(struct pb_cache *mgr, uint num_heaps,280uint usecs, float size_factor,281unsigned bypass_usage, uint64_t maximum_cache_size,282void *winsys,283void (*destroy_buffer)(void *winsys, struct pb_buffer *buf),284bool (*can_reclaim)(void *winsys, struct pb_buffer *buf))285{286unsigned i;287288mgr->buckets = CALLOC(num_heaps, sizeof(struct list_head));289if (!mgr->buckets)290return;291292for (i = 0; i < num_heaps; i++)293list_inithead(&mgr->buckets[i]);294295(void) mtx_init(&mgr->mutex, mtx_plain);296mgr->winsys = winsys;297mgr->cache_size = 0;298mgr->max_cache_size = maximum_cache_size;299mgr->num_heaps = num_heaps;300mgr->usecs = usecs;301mgr->num_buffers = 0;302mgr->bypass_usage = bypass_usage;303mgr->size_factor = size_factor;304mgr->destroy_buffer = destroy_buffer;305mgr->can_reclaim = can_reclaim;306}307308/**309* Deinitialize the manager completely.310*/311void312pb_cache_deinit(struct pb_cache *mgr)313{314pb_cache_release_all_buffers(mgr);315mtx_destroy(&mgr->mutex);316FREE(mgr->buckets);317mgr->buckets = NULL;318}319320321