2 * Copyright 2019 Collabora Ltd.
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:
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
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.
24 #include "virgl_resource_cache.h"
25 #include "util/os_time.h"
27 /* Checks whether the resource represented by a cache entry is able to hold
28 * data of the specified size, bind and format.
31 virgl_resource_cache_entry_is_compatible(struct virgl_resource_cache_entry
*entry
,
32 uint32_t size
, uint32_t bind
,
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.
41 entry
->size
<= size
* 2);
45 virgl_resource_cache_entry_release(struct virgl_resource_cache
*cache
,
46 struct virgl_resource_cache_entry
*entry
)
48 LIST_DEL(&entry
->head
);
49 cache
->entry_release_func(entry
, cache
->user_data
);
53 virgl_resource_cache_destroy_expired(struct virgl_resource_cache
*cache
, int64_t now
)
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.
60 if (!os_time_timeout(entry
->timeout_start
, entry
->timeout_end
, now
))
62 virgl_resource_cache_entry_release(cache
, entry
);
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
,
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
;
81 virgl_resource_cache_add(struct virgl_resource_cache
*cache
,
82 struct virgl_resource_cache_entry
*entry
)
84 const int64_t now
= os_time_get();
86 /* Entry should not already be in the cache. */
87 assert(entry
->head
.next
== NULL
);
88 assert(entry
->head
.prev
== NULL
);
90 virgl_resource_cache_destroy_expired(cache
, now
);
92 entry
->timeout_start
= now
;
93 entry
->timeout_end
= entry
->timeout_start
+ cache
->timeout_usecs
;
94 LIST_ADDTAIL(&entry
->head
, &cache
->resources
);
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
)
101 const int64_t now
= os_time_get();
102 struct virgl_resource_cache_entry
*compat_entry
= NULL
;
103 bool check_expired
= true;
105 /* Iterate through the cache to find a compatible resource, while also
106 * destroying any expired resources we come across.
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
);
114 if (!cache
->entry_is_busy_func(entry
, cache
->user_data
))
115 compat_entry
= entry
;
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
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.
130 if (os_time_timeout(entry
->timeout_start
, entry
->timeout_end
, now
))
131 virgl_resource_cache_entry_release(cache
, entry
);
133 check_expired
= false;
138 LIST_DEL(&compat_entry
->head
);
144 virgl_resource_cache_flush(struct virgl_resource_cache
*cache
)
146 list_for_each_entry_safe(struct virgl_resource_cache_entry
,
147 entry
, &cache
->resources
, head
) {
148 virgl_resource_cache_entry_release(cache
, entry
);