From b18f09a5097cab393215668a129553adbf13a13f Mon Sep 17 00:00:00 2001 From: Alexandros Frantzis Date: Wed, 12 Jun 2019 10:29:32 +0300 Subject: [PATCH] virgl: Introduce virgl_resource_cache Introduce a resource cache implementation that can be used by any virgl winsys backend. Signed-off-by: Alexandros Frantzis Reviewed-by: Chia-I Wu --- src/gallium/meson.build | 1 + src/gallium/winsys/virgl/common/Android.mk | 35 ++++ .../winsys/virgl/common/Makefile.sources | 3 + src/gallium/winsys/virgl/common/meson.build | 31 ++++ .../virgl/common/virgl_resource_cache.c | 161 ++++++++++++++++++ .../virgl/common/virgl_resource_cache.h | 100 +++++++++++ 6 files changed, 331 insertions(+) create mode 100644 src/gallium/winsys/virgl/common/Android.mk create mode 100644 src/gallium/winsys/virgl/common/Makefile.sources create mode 100644 src/gallium/winsys/virgl/common/meson.build create mode 100644 src/gallium/winsys/virgl/common/virgl_resource_cache.c create mode 100644 src/gallium/winsys/virgl/common/virgl_resource_cache.h diff --git a/src/gallium/meson.build b/src/gallium/meson.build index a9efb6296b6..2344bf3e88b 100644 --- a/src/gallium/meson.build +++ b/src/gallium/meson.build @@ -137,6 +137,7 @@ else driver_svga = declare_dependency() endif if with_gallium_virgl + subdir('winsys/virgl/common') subdir('winsys/virgl/drm') subdir('winsys/virgl/vtest') subdir('drivers/virgl') diff --git a/src/gallium/winsys/virgl/common/Android.mk b/src/gallium/winsys/virgl/common/Android.mk new file mode 100644 index 00000000000..0b8eda17589 --- /dev/null +++ b/src/gallium/winsys/virgl/common/Android.mk @@ -0,0 +1,35 @@ +# Copyright (C) 2019 Collabora Ltd. +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. + +LOCAL_PATH := $(call my-dir) + +# get C_SOURCES +include $(LOCAL_PATH)/Makefile.sources + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(C_SOURCES) + +LOCAL_MODULE := libmesa_winsys_virgl_common + +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) + +include $(GALLIUM_COMMON_MK) +include $(BUILD_STATIC_LIBRARY) diff --git a/src/gallium/winsys/virgl/common/Makefile.sources b/src/gallium/winsys/virgl/common/Makefile.sources new file mode 100644 index 00000000000..09a9ba46d21 --- /dev/null +++ b/src/gallium/winsys/virgl/common/Makefile.sources @@ -0,0 +1,3 @@ +C_SOURCES := \ + virgl_resource_cache.h \ + virgl_resource_cache.c diff --git a/src/gallium/winsys/virgl/common/meson.build b/src/gallium/winsys/virgl/common/meson.build new file mode 100644 index 00000000000..9182b79a675 --- /dev/null +++ b/src/gallium/winsys/virgl/common/meson.build @@ -0,0 +1,31 @@ +# Copyright © 2019 Collabora Ltd + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +libvirglcommon = static_library( + 'virglcommon', + 'virgl_resource_cache.c', + c_args : c_vis_args, + include_directories : [inc_common, inc_gallium_drivers], +) + +dep_libvirglcommon = declare_dependency( + link_with : libvirglcommon, + include_directories : include_directories('.'), +) diff --git a/src/gallium/winsys/virgl/common/virgl_resource_cache.c b/src/gallium/winsys/virgl/common/virgl_resource_cache.c new file mode 100644 index 00000000000..2ad4906cc3b --- /dev/null +++ b/src/gallium/winsys/virgl/common/virgl_resource_cache.c @@ -0,0 +1,161 @@ +/* + * Copyright 2019 Collabora Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * on the rights to use, copy, modify, merge, publish, distribute, sub + * license, and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "virgl_resource_cache.h" +#include "util/os_time.h" + +/* Checks whether the resource represented by a cache entry is able to hold + * data of the specified size, bind and format. + */ +static bool +virgl_resource_cache_entry_is_compatible(struct virgl_resource_cache_entry *entry, + uint32_t size, uint32_t bind, + uint32_t format) +{ + return (entry->bind == bind && + entry->format == format && + entry->size >= size && + /* We don't want to waste space, so don't reuse resource storage to + * hold much smaller (< 50%) sizes. + */ + entry->size <= size * 2); +} + +static void +virgl_resource_cache_entry_release(struct virgl_resource_cache *cache, + struct virgl_resource_cache_entry *entry) +{ + LIST_DEL(&entry->head); + cache->entry_release_func(entry, cache->user_data); +} + +static void +virgl_resource_cache_destroy_expired(struct virgl_resource_cache *cache, int64_t now) +{ + list_for_each_entry_safe(struct virgl_resource_cache_entry, + entry, &cache->resources, head) { + /* Entries are in non-decreasing timeout order, so we can stop + * at the first entry which hasn't expired. + */ + if (!os_time_timeout(entry->timeout_start, entry->timeout_end, now)) + break; + virgl_resource_cache_entry_release(cache, entry); + } +} + +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) +{ + LIST_INITHEAD(&cache->resources); + cache->timeout_usecs = timeout_usecs; + cache->entry_is_busy_func = is_busy_func; + cache->entry_release_func = destroy_func; + cache->user_data = user_data; +} + +void +virgl_resource_cache_add(struct virgl_resource_cache *cache, + struct virgl_resource_cache_entry *entry) +{ + const int64_t now = os_time_get(); + + /* Entry should not already be in the cache. */ + assert(entry->head.next == NULL); + assert(entry->head.prev == NULL); + + virgl_resource_cache_destroy_expired(cache, now); + + entry->timeout_start = now; + entry->timeout_end = entry->timeout_start + cache->timeout_usecs; + LIST_ADDTAIL(&entry->head, &cache->resources); +} + +struct virgl_resource_cache_entry * +virgl_resource_cache_remove_compatible(struct virgl_resource_cache *cache, + uint32_t size, uint32_t bind, uint32_t format) +{ + const int64_t now = os_time_get(); + struct virgl_resource_cache_entry *compat_entry = NULL; + bool check_expired = true; + + /* Iterate through the cache to find a compatible resource, while also + * destroying any expired resources. + */ + list_for_each_entry_safe(struct virgl_resource_cache_entry, + entry, &cache->resources, head) { + /* If we haven't yet found a compatible resource, try this one. */ + if (!compat_entry) { + const bool compatible = + virgl_resource_cache_entry_is_compatible(entry, size, bind, format); + + if (compatible) { + const bool busy = cache->entry_is_busy_func(entry, cache->user_data); + if (!busy) { + compat_entry = entry; + continue; + } else { + /* If the resource is busy, there is no point checking further, + * since any resources later in the cache list will also be + * busy. + */ + break; + } + } + } + + /* If we aren't using this resource, check to see if it has expired. + * Once we have found the first non-expired resource, we can stop checking + * since the cache holds resources in non-decreasing timeout order. + */ + if (check_expired) { + if (os_time_timeout(entry->timeout_start, entry->timeout_end, now)) + virgl_resource_cache_entry_release(cache, entry); + else + check_expired = false; + } + + /* If we have found a compatible entry and there are no remaining expired + * resources we can stop. + */ + if (compat_entry && !check_expired) + break; + } + + if (compat_entry) + LIST_DEL(&compat_entry->head); + + return compat_entry; +} + +void +virgl_resource_cache_flush(struct virgl_resource_cache *cache) +{ + list_for_each_entry_safe(struct virgl_resource_cache_entry, + entry, &cache->resources, head) { + virgl_resource_cache_entry_release(cache, entry); + } +} diff --git a/src/gallium/winsys/virgl/common/virgl_resource_cache.h b/src/gallium/winsys/virgl/common/virgl_resource_cache.h new file mode 100644 index 00000000000..f57d4af94b5 --- /dev/null +++ b/src/gallium/winsys/virgl/common/virgl_resource_cache.h @@ -0,0 +1,100 @@ +/* + * Copyright 2019 Collabora Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * on the rights to use, copy, modify, merge, publish, distribute, sub + * license, and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef VIRGL_RESOURCE_CACHE_H +#define VIRGL_RESOURCE_CACHE_H + +#include + +#include "util/list.h" + +struct virgl_resource_cache_entry { + struct list_head head; + int64_t timeout_start; + int64_t timeout_end; + uint32_t size; + uint32_t bind; + uint32_t format; +}; + +/* Pointer to a function that returns whether the resource represented by + * the specified cache entry is busy. + */ +typedef bool (*virgl_resource_cache_entry_is_busy_func) ( + struct virgl_resource_cache_entry *entry, void *user_data); + +/* Pointer to a function that destroys the resource represented by + * the specified cache entry. + */ +typedef void (*virgl_resource_cache_entry_release_func) ( + struct virgl_resource_cache_entry *entry, void *user_data); + +struct virgl_resource_cache { + struct list_head resources; + unsigned timeout_usecs; + virgl_resource_cache_entry_is_busy_func entry_is_busy_func; + virgl_resource_cache_entry_release_func entry_release_func; + void *user_data; +}; + +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); + +/** Adds a resource to the cache. + * + * Adding a resource that's already present in the cache leads to undefined + * behavior. + */ +void +virgl_resource_cache_add(struct virgl_resource_cache *cache, + struct virgl_resource_cache_entry *entry); + +/** Finds and removes a cached resource compatible with size, bind and format. + * + * Returns a pointer to the cache entry of the compatible resource, or NULL if + * no such resource was found. + */ +struct virgl_resource_cache_entry * +virgl_resource_cache_remove_compatible(struct virgl_resource_cache *cache, + uint32_t size, uint32_t bind, + uint32_t format); + +/** Empties the resource cache. */ +void +virgl_resource_cache_flush(struct virgl_resource_cache *cache); + +static inline void +virgl_resource_cache_entry_init(struct virgl_resource_cache_entry *entry, + uint32_t size, uint32_t bind, + uint32_t format) +{ + entry->size = size; + entry->bind = bind; + entry->format = format; +} + +#endif