util: remove LIST_INITHEAD macro
[mesa.git] / src / gallium / winsys / virgl / common / virgl_resource_cache.c
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 */
30 static bool
31 virgl_resource_cache_entry_is_compatible(struct virgl_resource_cache_entry *entry,
32 uint32_t size, uint32_t bind,
33 uint32_t format)
34 {
35 return (entry->bind == bind &&
36 entry->format == format &&
37 entry->size >= size &&
38 /* We don't want to waste space, so don't reuse resource storage to
39 * hold much smaller (< 50%) sizes.
40 */
41 entry->size <= size * 2);
42 }
43
44 static void
45 virgl_resource_cache_entry_release(struct virgl_resource_cache *cache,
46 struct virgl_resource_cache_entry *entry)
47 {
48 LIST_DEL(&entry->head);
49 cache->entry_release_func(entry, cache->user_data);
50 }
51
52 static void
53 virgl_resource_cache_destroy_expired(struct virgl_resource_cache *cache, int64_t now)
54 {
55 list_for_each_entry_safe(struct virgl_resource_cache_entry,
56 entry, &cache->resources, head) {
57 /* Entries are in non-decreasing timeout order, so we can stop
58 * at the first entry which hasn't expired.
59 */
60 if (!os_time_timeout(entry->timeout_start, entry->timeout_end, now))
61 break;
62 virgl_resource_cache_entry_release(cache, entry);
63 }
64 }
65
66 void
67 virgl_resource_cache_init(struct virgl_resource_cache *cache,
68 unsigned timeout_usecs,
69 virgl_resource_cache_entry_is_busy_func is_busy_func,
70 virgl_resource_cache_entry_release_func destroy_func,
71 void *user_data)
72 {
73 list_inithead(&cache->resources);
74 cache->timeout_usecs = timeout_usecs;
75 cache->entry_is_busy_func = is_busy_func;
76 cache->entry_release_func = destroy_func;
77 cache->user_data = user_data;
78 }
79
80 void
81 virgl_resource_cache_add(struct virgl_resource_cache *cache,
82 struct virgl_resource_cache_entry *entry)
83 {
84 const int64_t now = os_time_get();
85
86 /* Entry should not already be in the cache. */
87 assert(entry->head.next == NULL);
88 assert(entry->head.prev == NULL);
89
90 virgl_resource_cache_destroy_expired(cache, now);
91
92 entry->timeout_start = now;
93 entry->timeout_end = entry->timeout_start + cache->timeout_usecs;
94 LIST_ADDTAIL(&entry->head, &cache->resources);
95 }
96
97 struct virgl_resource_cache_entry *
98 virgl_resource_cache_remove_compatible(struct virgl_resource_cache *cache,
99 uint32_t size, uint32_t bind, uint32_t format)
100 {
101 const int64_t now = os_time_get();
102 struct virgl_resource_cache_entry *compat_entry = NULL;
103 bool check_expired = true;
104
105 /* Iterate through the cache to find a compatible resource, while also
106 * destroying any expired resources we come across.
107 */
108 list_for_each_entry_safe(struct virgl_resource_cache_entry,
109 entry, &cache->resources, head) {
110 const bool compatible =
111 virgl_resource_cache_entry_is_compatible(entry, size, bind, format);
112
113 if (compatible) {
114 if (!cache->entry_is_busy_func(entry, cache->user_data))
115 compat_entry = entry;
116
117 /* We either have found a compatible resource, in which case we are
118 * done, or the resource is busy, which means resources later in
119 * the cache list will also be busy, so there is no point in
120 * searching further.
121 */
122 break;
123 }
124
125 /* If we aren't using this resource, check to see if it has expired.
126 * Once we have found the first non-expired resource, we can stop checking
127 * since the cache holds resources in non-decreasing timeout order.
128 */
129 if (check_expired) {
130 if (os_time_timeout(entry->timeout_start, entry->timeout_end, now))
131 virgl_resource_cache_entry_release(cache, entry);
132 else
133 check_expired = false;
134 }
135 }
136
137 if (compat_entry)
138 LIST_DEL(&compat_entry->head);
139
140 return compat_entry;
141 }
142
143 void
144 virgl_resource_cache_flush(struct virgl_resource_cache *cache)
145 {
146 list_for_each_entry_safe(struct virgl_resource_cache_entry,
147 entry, &cache->resources, head) {
148 virgl_resource_cache_entry_release(cache, entry);
149 }
150 }