/* * Copyright 2019 Google LLC * SPDX-License-Identifier: MIT * * based in part on anv and radv which are: * Copyright © 2015 Intel Corporation * Copyright © 2016 Red Hat. * Copyright © 2016 Bas Nieuwenhuizen */ #ifndef VN_COMMON_H #define VN_COMMON_H #include #include #include #include #include #include #include #include #include #include #include #include "c11/threads.h" #include "drm-uapi/drm_fourcc.h" #include "util/bitscan.h" #include "util/bitset.h" #include "util/compiler.h" #include "util/detect_os.h" #include "util/libsync.h" #include "util/list.h" #include "util/macros.h" #include "util/os_time.h" #include "util/perf/cpu_trace.h" #include "util/simple_mtx.h" #include "util/u_atomic.h" #include "util/u_math.h" #include "util/xmlconfig.h" #include "vk_alloc.h" #include "vk_debug_report.h" #include "vk_device.h" #include "vk_device_memory.h" #include "vk_image.h" #include "vk_instance.h" #include "vk_object.h" #include "vk_physical_device.h" #include "vk_queue.h" #include "vk_util.h" #include "vn_entrypoints.h" #define VN_DEFAULT_ALIGN 8 #define VN_WATCHDOG_REPORT_PERIOD_US 3000000 #define VN_DEBUG(category) (unlikely(vn_env.debug & VN_DEBUG_##category)) #define VN_PERF(category) (unlikely(vn_env.perf & VN_PERF_##category)) #define vn_error(instance, error) \ (VN_DEBUG(RESULT) ? vn_log_result((instance), (error), __func__) : (error)) #define vn_result(instance, result) \ ((result) >= VK_SUCCESS ? (result) : vn_error((instance), (result))) #define VN_TRACE_SCOPE(name) MESA_TRACE_SCOPE(name) #define VN_TRACE_FUNC() MESA_TRACE_SCOPE(__func__) struct vn_instance; struct vn_physical_device; struct vn_device; struct vn_queue; struct vn_fence; struct vn_semaphore; struct vn_device_memory; struct vn_buffer; struct vn_buffer_view; struct vn_image; struct vn_image_view; struct vn_sampler; struct vn_sampler_ycbcr_conversion; struct vn_descriptor_set_layout; struct vn_descriptor_pool; struct vn_descriptor_set; struct vn_descriptor_update_template; struct vn_render_pass; struct vn_framebuffer; struct vn_event; struct vn_query_pool; struct vn_shader_module; struct vn_pipeline_layout; struct vn_pipeline_cache; struct vn_pipeline; struct vn_command_pool; struct vn_command_buffer; struct vn_cs_encoder; struct vn_cs_decoder; struct vn_ring; struct vn_renderer; struct vn_renderer_shmem; struct vn_renderer_bo; struct vn_renderer_sync; enum vn_debug { VN_DEBUG_INIT = 1ull << 0, VN_DEBUG_RESULT = 1ull << 1, VN_DEBUG_VTEST = 1ull << 2, VN_DEBUG_WSI = 1ull << 3, VN_DEBUG_NO_ABORT = 1ull << 4, VN_DEBUG_LOG_CTX_INFO = 1ull << 5, VN_DEBUG_CACHE = 1ull << 6, VN_DEBUG_NO_SPARSE = 1ull << 7, VN_DEBUG_NO_GPL = 1ull << 8, }; enum vn_perf { VN_PERF_NO_ASYNC_SET_ALLOC = 1ull << 0, VN_PERF_NO_ASYNC_BUFFER_CREATE = 1ull << 1, VN_PERF_NO_ASYNC_QUEUE_SUBMIT = 1ull << 2, VN_PERF_NO_EVENT_FEEDBACK = 1ull << 3, VN_PERF_NO_FENCE_FEEDBACK = 1ull << 4, VN_PERF_NO_CMD_BATCHING = 1ull << 6, VN_PERF_NO_SEMAPHORE_FEEDBACK = 1ull << 7, VN_PERF_NO_QUERY_FEEDBACK = 1ull << 8, VN_PERF_NO_ASYNC_MEM_ALLOC = 1ull << 9, VN_PERF_NO_TILED_WSI_IMAGE = 1ull << 10, VN_PERF_NO_MULTI_RING = 1ull << 11, VN_PERF_NO_ASYNC_IMAGE_CREATE = 1ull << 12, VN_PERF_NO_ASYNC_IMAGE_FORMAT = 1ull << 13, }; typedef uint64_t vn_object_id; /* base class of vn_instance */ struct vn_instance_base { struct vk_instance base; vn_object_id id; }; /* base class of vn_physical_device */ struct vn_physical_device_base { struct vk_physical_device base; vn_object_id id; }; /* base class of vn_device */ struct vn_device_base { struct vk_device base; vn_object_id id; }; /* base class of vn_queue */ struct vn_queue_base { struct vk_queue base; vn_object_id id; }; /* base class of vn_device_memory */ struct vn_device_memory_base { struct vk_device_memory base; vn_object_id id; }; /* base class of vn_image */ struct vn_image_base { struct vk_image base; vn_object_id id; }; /* base class of other driver objects */ struct vn_object_base { struct vk_object_base base; vn_object_id id; }; struct vn_refcount { atomic_int count; }; struct vn_env { uint64_t debug; uint64_t perf; }; extern struct vn_env vn_env; /* Only one "waiting" thread may fulfill the "watchdog" role at a time. Every * VN_WATCHDOG_REPORT_PERIOD_US or longer, the watchdog tests the ring's ALIVE * status, updates the "alive" atomic, and resets the ALIVE status for the * next cycle. Other waiting threads just check the "alive" atomic. The * watchdog role may be released and acquired by another waiting thread * dynamically. * * Examples of "waiting" are to wait for: * - ring to reach a seqno * - ring space to be released * - sync primitives to signal * - query result being available */ struct vn_watchdog { mtx_t mutex; atomic_int tid; atomic_bool alive; }; enum vn_relax_reason { VN_RELAX_REASON_RING_SEQNO, VN_RELAX_REASON_TLS_RING_SEQNO, VN_RELAX_REASON_RING_SPACE, VN_RELAX_REASON_FENCE, VN_RELAX_REASON_SEMAPHORE, VN_RELAX_REASON_QUERY, }; /* vn_relax_profile defines the driver side polling behavior * * - base_sleep_us: * - the minimum polling interval after initial busy waits * * - busy_wait_order: * - initial 2 ^ busy_wait_order times thrd_yield() * * - warn_order: * - number of polls at order N: * - fn_cnt(N) = 2 ^ N * - interval of poll at order N: * - fn_step(N) = base_sleep_us * (2 ^ (N - busy_wait_order)) * - warn occasionally if we have slept at least: * - for (i = busy_wait_order; i < warn_order; i++) * total_sleep += fn_cnt(i) * fn_step(i) * * - abort_order: * - similar to warn_order, but would abort() instead */ struct vn_relax_profile { uint32_t base_sleep_us; uint32_t busy_wait_order; uint32_t warn_order; uint32_t abort_order; }; struct vn_relax_state { struct vn_instance *instance; uint32_t iter; const struct vn_relax_profile profile; const char *reason_str; }; /* TLS ring * - co-owned by TLS and VkInstance * - initialized in TLS upon requested * - teardown happens upon thread exit or instance destroy * - teardown is split into 2 stages: * 1. one owner locks and destroys the ring and mark destroyed * 2. the other owner locks and frees up the tls ring storage */ struct vn_tls_ring { mtx_t mutex; struct vn_ring *ring; struct vn_instance *instance; struct list_head tls_head; struct list_head vk_head; }; struct vn_tls { /* Track the threads on which swapchain and command pool creations occur. * Pipeline create on those threads are forced async via the primary ring. */ bool async_pipeline_create; /* Track TLS rings owned across instances. */ struct list_head tls_rings; }; /* A cached storage for object internal usages with below constraints: * - It belongs to the object and shares the lifetime. * - The storage reuse is protected by external synchronization. * - The returned storage is not zero-initialized. * - It never shrinks unless being purged via fini. * * The current users are: * - VkCommandPool * - VkQueue */ struct vn_cached_storage { const VkAllocationCallbacks *alloc; size_t size; void *data; }; void vn_env_init(void); void vn_trace_init(void); void vn_log(struct vn_instance *instance, const char *format, ...) PRINTFLIKE(2, 3); VkResult vn_log_result(struct vn_instance *instance, VkResult result, const char *where); #define VN_REFCOUNT_INIT(val) \ (struct vn_refcount) \ { \ .count = (val), \ } static inline int vn_refcount_load_relaxed(const struct vn_refcount *ref) { return atomic_load_explicit(&ref->count, memory_order_relaxed); } static inline int vn_refcount_fetch_add_relaxed(struct vn_refcount *ref, int val) { return atomic_fetch_add_explicit(&ref->count, val, memory_order_relaxed); } static inline int vn_refcount_fetch_sub_release(struct vn_refcount *ref, int val) { return atomic_fetch_sub_explicit(&ref->count, val, memory_order_release); } static inline bool vn_refcount_is_valid(const struct vn_refcount *ref) { return vn_refcount_load_relaxed(ref) > 0; } static inline void vn_refcount_inc(struct vn_refcount *ref) { /* no ordering imposed */ ASSERTED const int old = vn_refcount_fetch_add_relaxed(ref, 1); assert(old >= 1); } static inline bool vn_refcount_dec(struct vn_refcount *ref) { /* prior reads/writes cannot be reordered after this */ const int old = vn_refcount_fetch_sub_release(ref, 1); assert(old >= 1); /* subsequent free cannot be reordered before this */ if (old == 1) atomic_thread_fence(memory_order_acquire); return old == 1; } extern uint64_t vn_next_obj_id; static inline uint64_t vn_get_next_obj_id(void) { return p_atomic_fetch_add(&vn_next_obj_id, 1); } uint32_t vn_extension_get_spec_version(const char *name); static inline void vn_watchdog_init(struct vn_watchdog *watchdog) { #ifndef NDEBUG /* ensure minimum check period is greater than maximum renderer * reporting period (with margin of safety to ensure no false * positives). * * first_warn_time is pre-calculated based on parameters in vn_relax * and must update together. */ static const uint32_t first_warn_time = 3481600; static const uint32_t safety_margin = 250000; assert(first_warn_time - safety_margin >= VN_WATCHDOG_REPORT_PERIOD_US); #endif mtx_init(&watchdog->mutex, mtx_plain); watchdog->tid = 0; /* initialized to be alive to avoid vn_watchdog_timout false alarm */ watchdog->alive = true; } static inline void vn_watchdog_fini(struct vn_watchdog *watchdog) { mtx_destroy(&watchdog->mutex); } struct vn_relax_state vn_relax_init(struct vn_instance *instance, enum vn_relax_reason reason); void vn_relax(struct vn_relax_state *state); void vn_relax_fini(struct vn_relax_state *state); static_assert(sizeof(vn_object_id) >= sizeof(uintptr_t), ""); static inline VkResult vn_instance_base_init( struct vn_instance_base *instance, const struct vk_instance_extension_table *supported_extensions, const struct vk_instance_dispatch_table *dispatch_table, const VkInstanceCreateInfo *info, const VkAllocationCallbacks *alloc) { VkResult result = vk_instance_init(&instance->base, supported_extensions, dispatch_table, info, alloc); instance->id = vn_get_next_obj_id(); return result; } static inline void vn_instance_base_fini(struct vn_instance_base *instance) { vk_instance_finish(&instance->base); } static inline VkResult vn_physical_device_base_init( struct vn_physical_device_base *physical_dev, struct vn_instance_base *instance, const struct vk_device_extension_table *supported_extensions, const struct vk_physical_device_dispatch_table *dispatch_table) { VkResult result = vk_physical_device_init( &physical_dev->base, &instance->base, supported_extensions, NULL, NULL, dispatch_table); physical_dev->id = vn_get_next_obj_id(); return result; } static inline void vn_physical_device_base_fini(struct vn_physical_device_base *physical_dev) { vk_physical_device_finish(&physical_dev->base); } static inline VkResult vn_device_base_init(struct vn_device_base *dev, struct vn_physical_device_base *physical_dev, const struct vk_device_dispatch_table *dispatch_table, const VkDeviceCreateInfo *info, const VkAllocationCallbacks *alloc) { VkResult result = vk_device_init(&dev->base, &physical_dev->base, dispatch_table, info, alloc); dev->id = vn_get_next_obj_id(); return result; } static inline void vn_device_base_fini(struct vn_device_base *dev) { vk_device_finish(&dev->base); } static inline VkResult vn_queue_base_init(struct vn_queue_base *queue, struct vn_device_base *dev, const VkDeviceQueueCreateInfo *queue_info, uint32_t queue_index) { VkResult result = vk_queue_init(&queue->base, &dev->base, queue_info, queue_index); queue->id = vn_get_next_obj_id(); return result; } static inline void vn_queue_base_fini(struct vn_queue_base *queue) { vk_queue_finish(&queue->base); } static inline void vn_object_base_init(struct vn_object_base *obj, VkObjectType type, struct vn_device_base *dev) { vk_object_base_init(&dev->base, &obj->base, type); obj->id = vn_get_next_obj_id(); } static inline void vn_object_base_fini(struct vn_object_base *obj) { vk_object_base_finish(&obj->base); } static inline void vn_object_set_id(void *obj, vn_object_id id, VkObjectType type) { assert(((const struct vk_object_base *)obj)->type == type); switch (type) { case VK_OBJECT_TYPE_INSTANCE: ((struct vn_instance_base *)obj)->id = id; break; case VK_OBJECT_TYPE_PHYSICAL_DEVICE: ((struct vn_physical_device_base *)obj)->id = id; break; case VK_OBJECT_TYPE_DEVICE: ((struct vn_device_base *)obj)->id = id; break; case VK_OBJECT_TYPE_QUEUE: ((struct vn_queue_base *)obj)->id = id; break; case VK_OBJECT_TYPE_DEVICE_MEMORY: ((struct vn_device_memory_base *)obj)->id = id; break; case VK_OBJECT_TYPE_IMAGE: ((struct vn_image_base *)obj)->id = id; break; default: ((struct vn_object_base *)obj)->id = id; break; } } static inline vn_object_id vn_object_get_id(const void *obj, VkObjectType type) { assert(((const struct vk_object_base *)obj)->type == type); switch (type) { case VK_OBJECT_TYPE_INSTANCE: return ((struct vn_instance_base *)obj)->id; case VK_OBJECT_TYPE_PHYSICAL_DEVICE: return ((struct vn_physical_device_base *)obj)->id; case VK_OBJECT_TYPE_DEVICE: return ((struct vn_device_base *)obj)->id; case VK_OBJECT_TYPE_QUEUE: return ((struct vn_queue_base *)obj)->id; case VK_OBJECT_TYPE_DEVICE_MEMORY: return ((struct vn_device_memory_base *)obj)->id; case VK_OBJECT_TYPE_IMAGE: return ((struct vn_image_base *)obj)->id; default: return ((struct vn_object_base *)obj)->id; } } static inline pid_t vn_gettid(void) { #if DETECT_OS_ANDROID return gettid(); #else return syscall(SYS_gettid); #endif } struct vn_tls * vn_tls_get(void); static inline void vn_tls_set_async_pipeline_create(void) { struct vn_tls *tls = vn_tls_get(); if (likely(tls)) tls->async_pipeline_create = true; } static inline bool vn_tls_get_async_pipeline_create(void) { const struct vn_tls *tls = vn_tls_get(); if (likely(tls)) return tls->async_pipeline_create; return true; } struct vn_ring * vn_tls_get_ring(struct vn_instance *instance); void vn_tls_destroy_ring(struct vn_tls_ring *tls_ring); static inline uint32_t vn_cache_key_hash_function(const void *key) { return _mesa_hash_data(key, SHA1_DIGEST_LENGTH); } static inline bool vn_cache_key_equal_function(const void *key1, const void *key2) { return memcmp(key1, key2, SHA1_DIGEST_LENGTH) == 0; } static inline void vn_cached_storage_init(struct vn_cached_storage *storage, const VkAllocationCallbacks *alloc) { storage->alloc = alloc; storage->size = 0; storage->data = NULL; } static inline void * vn_cached_storage_get(struct vn_cached_storage *storage, size_t size) { if (size > storage->size) { void *data = vk_realloc(storage->alloc, storage->data, size, VN_DEFAULT_ALIGN, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); if (!data) return NULL; storage->size = size; storage->data = data; } return storage->data; } static inline void vn_cached_storage_fini(struct vn_cached_storage *storage) { vk_free(storage->alloc, storage->data); } #endif /* VN_COMMON_H */