diff --git a/src/vulkan/runtime/vk_device.c b/src/vulkan/runtime/vk_device.c index 7768cdc29c3..0e9c4c48320 100644 --- a/src/vulkan/runtime/vk_device.c +++ b/src/vulkan/runtime/vk_device.c @@ -227,6 +227,8 @@ vk_device_finish(struct vk_device *device) /* Drivers should tear down their own queues */ assert(list_is_empty(&device->queues)); + vk_memory_trace_finish(device); + #ifdef ANDROID if (device->swapchain_private) { hash_table_foreach(device->swapchain_private, entry) diff --git a/src/vulkan/runtime/vk_device.h b/src/vulkan/runtime/vk_device.h index afbb4af8917..4044f615bde 100644 --- a/src/vulkan/runtime/vk_device.h +++ b/src/vulkan/runtime/vk_device.h @@ -23,6 +23,7 @@ #ifndef VK_DEVICE_H #define VK_DEVICE_H +#include "rmv/vk_rmv_common.h" #include "vk_dispatch_table.h" #include "vk_extensions.h" #include "vk_object.h" @@ -242,6 +243,8 @@ struct vk_device { */ enum vk_queue_submit_mode submit_mode; + struct vk_memory_trace_data memory_trace_data; + #ifdef ANDROID mtx_t swapchain_private_mtx; struct hash_table *swapchain_private; diff --git a/src/vulkan/util/meson.build b/src/vulkan/util/meson.build index 44f8bc8e645..3bcb501baf8 100644 --- a/src/vulkan/util/meson.build +++ b/src/vulkan/util/meson.build @@ -54,6 +54,8 @@ vk_cmd_queue_gen = files('vk_cmd_queue_gen.py') vk_dispatch_trampolines_gen = files('vk_dispatch_trampolines_gen.py') files_vulkan_util = files( + 'rmv/vk_rmv_common.c', + 'rmv/vk_rmv_common.h', 'rmv/vk_rmv_tokens.h', 'vk_alloc.c', 'vk_alloc.h', diff --git a/src/vulkan/util/rmv/vk_rmv_common.c b/src/vulkan/util/rmv/vk_rmv_common.c new file mode 100644 index 00000000000..467d49228a0 --- /dev/null +++ b/src/vulkan/util/rmv/vk_rmv_common.c @@ -0,0 +1,175 @@ +/* + * Copyright © 2022 Friedrich Vock + * + * 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 (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 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. + */ + +#include "vk_rmv_common.h" +#include "vulkan/runtime/vk_buffer.h" +#include "vulkan/runtime/vk_device.h" + +void +vk_memory_trace_init(struct vk_device *device, const struct vk_rmv_device_info *device_info) +{ + device->memory_trace_data.device_info = *device_info; + device->memory_trace_data.is_enabled = true; + util_dynarray_init(&device->memory_trace_data.tokens, NULL); + simple_mtx_init(&device->memory_trace_data.token_mtx, mtx_plain); + + device->memory_trace_data.trace_frame_idx = vk_memory_trace_frame(); + device->memory_trace_data.trigger_file_name = vk_memory_trace_trigger_file(); + + device->memory_trace_data.cur_frame_idx = 0; + device->memory_trace_data.next_resource_id = 1; + device->memory_trace_data.handle_table = _mesa_hash_table_u64_create(NULL); +} + +void +vk_memory_trace_finish(struct vk_device *device) +{ + if (!device->memory_trace_data.is_enabled) + return; + util_dynarray_foreach (&device->memory_trace_data.tokens, struct vk_rmv_token, token) { + switch (token->type) { + case VK_RMV_TOKEN_TYPE_RESOURCE_CREATE: { + struct vk_rmv_resource_create_token *create_token = &token->data.resource_create; + if (create_token->type == VK_RMV_RESOURCE_TYPE_DESCRIPTOR_POOL) { + free(create_token->descriptor_pool.pool_sizes); + } + break; + } + case VK_RMV_TOKEN_TYPE_USERDATA: + free(token->data.userdata.name); + break; + default: + break; + } + } + util_dynarray_fini(&device->memory_trace_data.tokens); + if (_mesa_hash_table_num_entries(device->memory_trace_data.handle_table->table)) + fprintf(stderr, + "mesa: Unfreed resources detected at device destroy, there may be memory leaks!\n"); + _mesa_hash_table_u64_destroy(device->memory_trace_data.handle_table); + device->memory_trace_data.is_enabled = false; +} + +void +vk_rmv_handle_present_locked(struct vk_device *device) +{ + struct vk_memory_trace_data *trace_data = &device->memory_trace_data; + + if (!trace_data->is_enabled) + return; + bool frame_trigger = false; + bool file_trigger = false; + +#ifndef _WIN32 + if (trace_data->trigger_file_name && access(trace_data->trigger_file_name, W_OK) == 0) { + if (unlink(trace_data->trigger_file_name) == 0) + file_trigger = true; + else + /* Do not enable tracing if we cannot remove the file, + * because by then we'll trace every frame ... */ + fprintf(stderr, "mesa: could not remove memory trace trigger " + "file, ignoring\n"); + } +#endif + + int32_t new_frame_index = p_atomic_add_return(&trace_data->cur_frame_idx, 1); + frame_trigger = + (new_frame_index % trace_data->trace_frame_idx == 0) && trace_data->trace_frame_idx != -1; +} + +void +vk_rmv_emit_token(struct vk_memory_trace_data *data, enum vk_rmv_token_type type, void *token_data) +{ + struct vk_rmv_token token; + token.type = type; + token.timestamp = (uint64_t)os_time_get_nano(); + memcpy(&token.data, token_data, vk_rmv_token_size_from_type(type)); + util_dynarray_append(&data->tokens, struct vk_rmv_token, token); +} + +uint32_t +vk_rmv_get_resource_id_locked(struct vk_device *device, uint64_t handle) +{ + void *entry = _mesa_hash_table_u64_search(device->memory_trace_data.handle_table, handle); + if (!entry) { + uint32_t id = device->memory_trace_data.next_resource_id++; + _mesa_hash_table_u64_insert(device->memory_trace_data.handle_table, handle, + (void *)(uintptr_t)id); + return id; + } + return (uint32_t)(uintptr_t)entry; +} + +void +vk_rmv_destroy_resource_id_locked(struct vk_device *device, uint64_t handle) +{ + _mesa_hash_table_u64_remove(device->memory_trace_data.handle_table, handle); +} + +void +vk_rmv_log_buffer_create(struct vk_device *device, bool is_internal, VkBuffer _buffer) +{ + if (!device->memory_trace_data.is_enabled) + return; + + VK_FROM_HANDLE(vk_buffer, buffer, _buffer); + simple_mtx_lock(&device->memory_trace_data.token_mtx); + struct vk_rmv_resource_create_token token = {0}; + token.is_driver_internal = is_internal; + token.resource_id = vk_rmv_get_resource_id_locked(device, (uint64_t)_buffer); + token.type = VK_RMV_RESOURCE_TYPE_BUFFER; + token.buffer.create_flags = buffer->create_flags; + token.buffer.size = buffer->size; + token.buffer.usage_flags = buffer->usage; + + vk_rmv_emit_token(&device->memory_trace_data, VK_RMV_TOKEN_TYPE_RESOURCE_CREATE, &token); + simple_mtx_unlock(&device->memory_trace_data.token_mtx); +} + +void +vk_rmv_log_cpu_map(struct vk_device *device, uint64_t va, bool is_unmap) +{ + if (!device->memory_trace_data.is_enabled) + return; + + struct vk_rmv_cpu_map_token map_token; + map_token.address = va; + map_token.unmapped = is_unmap; + + simple_mtx_lock(&device->memory_trace_data.token_mtx); + vk_rmv_emit_token(&device->memory_trace_data, VK_RMV_TOKEN_TYPE_CPU_MAP, &map_token); + simple_mtx_unlock(&device->memory_trace_data.token_mtx); +} + +void +vk_rmv_log_misc_token(struct vk_device *device, enum vk_rmv_misc_event_type type) +{ + if (!device->memory_trace_data.is_enabled) + return; + + simple_mtx_lock(&device->memory_trace_data.token_mtx); + struct vk_rmv_misc_token token; + token.type = type; + vk_rmv_emit_token(&device->memory_trace_data, VK_RMV_TOKEN_TYPE_MISC, &token); + simple_mtx_unlock(&device->memory_trace_data.token_mtx); +} diff --git a/src/vulkan/util/rmv/vk_rmv_common.h b/src/vulkan/util/rmv/vk_rmv_common.h new file mode 100644 index 00000000000..f12bb3259f1 --- /dev/null +++ b/src/vulkan/util/rmv/vk_rmv_common.h @@ -0,0 +1,163 @@ +/* + * Copyright © 2022 Friedrich Vock + * + * 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 (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 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. + */ + +#ifndef VK_RMV_COMMON_H +#define VK_RMV_COMMON_H + +#include +#include "util/hash_table.h" +#include "util/simple_mtx.h" +#include "util/u_debug.h" +#include "util/u_dynarray.h" +#include +#include "vk_rmv_tokens.h" + +struct vk_memory_trace_data; + +/* + * The different memory domains RMV supports. + */ +enum vk_rmv_memory_location { + /* DEVICE_LOCAL | HOST_VISIBLE */ + VK_RMV_MEMORY_LOCATION_DEVICE, + /* DEVICE_LOCAL */ + VK_RMV_MEMORY_LOCATION_DEVICE_INVISIBLE, + /* HOST_VISIBLE | HOST_COHERENT */ + VK_RMV_MEMORY_LOCATION_HOST, + + /* add above here */ + VK_RMV_MEMORY_LOCATION_COUNT +}; + +/* + * Information about a memory domain. + */ +struct vk_rmv_memory_info { + uint64_t size; + uint64_t physical_base_address; +}; + +enum vk_rmv_memory_type { + VK_RMV_MEMORY_TYPE_UNKNOWN, + VK_RMV_MEMORY_TYPE_DDR2, + VK_RMV_MEMORY_TYPE_DDR3, + VK_RMV_MEMORY_TYPE_DDR4, + VK_RMV_MEMORY_TYPE_GDDR5, + VK_RMV_MEMORY_TYPE_GDDR6, + VK_RMV_MEMORY_TYPE_HBM, + VK_RMV_MEMORY_TYPE_HBM2, + VK_RMV_MEMORY_TYPE_HBM3, + VK_RMV_MEMORY_TYPE_LPDDR4, + VK_RMV_MEMORY_TYPE_LPDDR5, + VK_RMV_MEMORY_TYPE_DDR5 +}; + +/* + * Device information for RMV traces. + */ +struct vk_rmv_device_info { + struct vk_rmv_memory_info memory_infos[VK_RMV_MEMORY_LOCATION_COUNT]; + + /* The memory type of dedicated VRAM. */ + enum vk_rmv_memory_type vram_type; + + char device_name[128]; + + uint32_t pcie_family_id; + uint32_t pcie_revision_id; + uint32_t pcie_device_id; + /* The minimum shader clock, in MHz. */ + uint32_t minimum_shader_clock; + /* The maximum shader clock, in MHz. */ + uint32_t maximum_shader_clock; + uint32_t vram_operations_per_clock; + uint32_t vram_bus_width; + /* The VRAM bandwidth, in GB/s (1 GB/s = 1000 MB/s). */ + uint32_t vram_bandwidth; + /* The minimum memory clock, in MHz. */ + uint32_t minimum_memory_clock; + /* The maximum memory clock, in MHz. */ + uint32_t maximum_memory_clock; +}; + +struct vk_device; + +struct vk_memory_trace_data { + struct util_dynarray tokens; + simple_mtx_t token_mtx; + + int32_t cur_frame_idx; + int32_t trace_frame_idx; + const char *trigger_file_name; + + bool is_enabled; + + struct vk_rmv_device_info device_info; + + struct hash_table_u64 *handle_table; + uint32_t next_resource_id; +}; + +struct vk_device; + +static inline int +vk_memory_trace_frame() +{ + return (int)debug_get_num_option("MESA_VK_MEMORY_TRACE", -1); +} + +static inline const char * +vk_memory_trace_trigger_file() +{ + return getenv("MESA_VK_MEMORY_TRACE_TRIGGER"); +} + +static inline bool +vk_memory_trace_enabled() +{ + return vk_memory_trace_frame() != -1 || vk_memory_trace_trigger_file(); +} + +void vk_memory_trace_init(struct vk_device *device, const struct vk_rmv_device_info *device_info); + +void vk_memory_trace_finish(struct vk_device *device); + +/* The memory trace mutex should be locked when entering this function. */ +void vk_rmv_handle_present_locked(struct vk_device *device); + +void vk_rmv_emit_token(struct vk_memory_trace_data *data, enum vk_rmv_token_type type, + void *token_data); +void vk_rmv_log_buffer_create(struct vk_device *device, bool is_internal, VkBuffer _buffer); +void vk_rmv_log_cpu_map(struct vk_device *device, uint64_t va, bool is_unmap); +void vk_rmv_log_misc_token(struct vk_device *device, enum vk_rmv_misc_event_type type); + +/* Retrieves the unique resource id for the resource specified by handle. + * Allocates a new id if none exists already. + * The memory trace mutex should be locked when entering this function. */ +uint32_t vk_rmv_get_resource_id_locked(struct vk_device *device, uint64_t handle); +/* Destroys a resource id. If the same handle is allocated again, a new resource + * id is given to it. + * The memory trace mutex should be locked when entering this function. */ +void vk_rmv_destroy_resource_id_locked(struct vk_device *device, uint64_t handle); + +#endif