• 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,struct virgl_resource_params params)31 virgl_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, &params, sizeof(params)) == 0;
46    }
47 }
48 
49 static void
virgl_resource_cache_entry_release(struct virgl_resource_cache * cache,struct virgl_resource_cache_entry * entry)50 virgl_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 
57 static void
virgl_resource_cache_destroy_expired(struct virgl_resource_cache * cache,int64_t now)58 virgl_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 
71 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)72 virgl_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 
85 void
virgl_resource_cache_add(struct virgl_resource_cache * cache,struct virgl_resource_cache_entry * entry)86 virgl_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 
102 struct virgl_resource_cache_entry *
virgl_resource_cache_remove_compatible(struct virgl_resource_cache * cache,struct virgl_resource_params params)103 virgl_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 
148 void
virgl_resource_cache_flush(struct virgl_resource_cache * cache)149 virgl_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