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