1/* 2 * Copyright 2019 Collabora Ltd. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * on the rights to use, copy, modify, merge, publish, distribute, sub 8 * license, and/or sell copies of the Software, and to permit persons to whom 9 * the Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, 19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 21 * USE OR OTHER DEALINGS IN THE SOFTWARE. 22 */ 23 24#include "virgl_resource_cache.h" 25#include "util/os_time.h" 26 27/* Checks whether the resource represented by a cache entry is able to hold 28 * data of the specified size, bind and format. 29 */ 30static bool 31virgl_resource_cache_entry_is_compatible(struct virgl_resource_cache_entry *entry, struct virgl_resource_params params) 32{ 33 if (entry->params.target == PIPE_BUFFER) { 34 return (entry->params.bind == params.bind && 35 entry->params.format == params.format && 36 entry->params.size >= params.size && 37 entry->params.flags == params.flags && 38 /* We don't want to waste space, so don't reuse resource storage to 39 * hold much smaller (< 50%) sizes. 40 */ 41 entry->params.size <= params.size * 2 && 42 entry->params.width >= params.width && 43 entry->params.target == params.target); 44 } else { 45 return memcmp(&entry->params, ¶ms, sizeof(params)) == 0; 46 } 47} 48 49static void 50virgl_resource_cache_entry_release(struct virgl_resource_cache *cache, 51 struct virgl_resource_cache_entry *entry) 52{ 53 list_del(&entry->head); 54 cache->entry_release_func(entry, cache->user_data); 55} 56 57static void 58virgl_resource_cache_destroy_expired(struct virgl_resource_cache *cache, int64_t now) 59{ 60 list_for_each_entry_safe(struct virgl_resource_cache_entry, 61 entry, &cache->resources, head) { 62 /* Entries are in non-decreasing timeout order, so we can stop 63 * at the first entry which hasn't expired. 64 */ 65 if (!os_time_timeout(entry->timeout_start, entry->timeout_end, now)) 66 break; 67 virgl_resource_cache_entry_release(cache, entry); 68 } 69} 70 71void 72virgl_resource_cache_init(struct virgl_resource_cache *cache, 73 unsigned timeout_usecs, 74 virgl_resource_cache_entry_is_busy_func is_busy_func, 75 virgl_resource_cache_entry_release_func destroy_func, 76 void *user_data) 77{ 78 list_inithead(&cache->resources); 79 cache->timeout_usecs = timeout_usecs; 80 cache->entry_is_busy_func = is_busy_func; 81 cache->entry_release_func = destroy_func; 82 cache->user_data = user_data; 83} 84 85void 86virgl_resource_cache_add(struct virgl_resource_cache *cache, 87 struct virgl_resource_cache_entry *entry) 88{ 89 const int64_t now = os_time_get(); 90 91 /* Entry should not already be in the cache. */ 92 assert(entry->head.next == NULL); 93 assert(entry->head.prev == NULL); 94 95 virgl_resource_cache_destroy_expired(cache, now); 96 97 entry->timeout_start = now; 98 entry->timeout_end = entry->timeout_start + cache->timeout_usecs; 99 list_addtail(&entry->head, &cache->resources); 100} 101 102struct virgl_resource_cache_entry * 103virgl_resource_cache_remove_compatible(struct virgl_resource_cache *cache, 104 struct virgl_resource_params params) 105{ 106 const int64_t now = os_time_get(); 107 struct virgl_resource_cache_entry *compat_entry = NULL; 108 bool check_expired = true; 109 110 /* Iterate through the cache to find a compatible resource, while also 111 * destroying any expired resources we come across. 112 */ 113 list_for_each_entry_safe(struct virgl_resource_cache_entry, 114 entry, &cache->resources, head) { 115 const bool compatible = 116 virgl_resource_cache_entry_is_compatible(entry, params); 117 118 if (compatible) { 119 if (!cache->entry_is_busy_func(entry, cache->user_data)) 120 compat_entry = entry; 121 122 /* We either have found a compatible resource, in which case we are 123 * done, or the resource is busy, which means resources later in 124 * the cache list will also be busy, so there is no point in 125 * searching further. 126 */ 127 break; 128 } 129 130 /* If we aren't using this resource, check to see if it has expired. 131 * Once we have found the first non-expired resource, we can stop checking 132 * since the cache holds resources in non-decreasing timeout order. 133 */ 134 if (check_expired) { 135 if (os_time_timeout(entry->timeout_start, entry->timeout_end, now)) 136 virgl_resource_cache_entry_release(cache, entry); 137 else 138 check_expired = false; 139 } 140 } 141 142 if (compat_entry) 143 list_del(&compat_entry->head); 144 145 return compat_entry; 146} 147 148void 149virgl_resource_cache_flush(struct virgl_resource_cache *cache) 150{ 151 list_for_each_entry_safe(struct virgl_resource_cache_entry, 152 entry, &cache->resources, head) { 153 virgl_resource_cache_entry_release(cache, entry); 154 } 155} 156