| /* | |
| * Copyright 2020 Hans-Kristian Arntzen for Valve Corporation | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| */ | |
| #ifdef VKD3D_ENABLE_PROFILING | |
| #define VKD3D_DBG_CHANNEL VKD3D_DBG_CHANNEL_API | |
| #include "vkd3d_profiling.h" | |
| #include "vkd3d_platform.h" | |
| #include "vkd3d_threads.h" | |
| #include "vkd3d_debug.h" | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <stdio.h> | |
| #ifdef _WIN32 | |
| #define WIN32_LEAN_AND_MEAN | |
| #include <windows.h> | |
| #else | |
| #include <sys/types.h> | |
| #include <sys/mman.h> | |
| #include <fcntl.h> | |
| #include <unistd.h> | |
| #endif | |
| static pthread_once_t profiling_block_once = PTHREAD_ONCE_INIT; | |
| static unsigned int profiling_region_count; | |
| static spinlock_t profiling_lock; | |
| struct vkd3d_profiling_block | |
| { | |
| uint64_t ticks_total; | |
| uint64_t iteration_total; | |
| char name[64 - 2 * sizeof(uint64_t)]; | |
| }; | |
| static struct vkd3d_profiling_block *mapped_blocks; | |
| #define VKD3D_MAX_PROFILING_REGIONS 256 | |
| static spinlock_t region_locks[VKD3D_MAX_PROFILING_REGIONS]; | |
| #ifdef _WIN32 | |
| static void vkd3d_init_profiling_path(const char *path) | |
| { | |
| HANDLE profiling_fd; | |
| HANDLE file_view; | |
| char path_pid[_MAX_PATH]; | |
| snprintf(path_pid, sizeof(path_pid), "%s.%u", path, GetCurrentProcessId()); | |
| profiling_fd = CreateFileA(path_pid, GENERIC_READ | GENERIC_WRITE, | |
| FILE_SHARE_READ, NULL, CREATE_ALWAYS, | |
| FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE); | |
| if (profiling_fd == INVALID_HANDLE_VALUE) | |
| { | |
| ERR("Failed to open profiling FD.\n"); | |
| return; | |
| } | |
| file_view = CreateFileMappingA(profiling_fd, NULL, PAGE_READWRITE, 0, | |
| VKD3D_MAX_PROFILING_REGIONS * sizeof(*mapped_blocks), NULL); | |
| if (file_view == INVALID_HANDLE_VALUE) | |
| { | |
| ERR("Failed to create profiling file view.\n"); | |
| CloseHandle(profiling_fd); | |
| return; | |
| } | |
| mapped_blocks = MapViewOfFile(file_view, FILE_MAP_ALL_ACCESS, 0, 0, | |
| VKD3D_MAX_PROFILING_REGIONS * sizeof(*mapped_blocks)); | |
| if (!mapped_blocks) | |
| ERR("Failed to map view of file.\n"); | |
| CloseHandle(file_view); | |
| CloseHandle(profiling_fd); | |
| } | |
| #else | |
| static void vkd3d_init_profiling_path(const char *path) | |
| { | |
| int profiling_fd; | |
| char path_pid[PATH_MAX]; | |
| snprintf(path_pid, sizeof(path_pid), "%s.%u", path, getpid()); | |
| profiling_fd = open(path_pid, O_RDWR | O_CREAT, 0644); | |
| if (profiling_fd >= 0) | |
| { | |
| if (ftruncate(profiling_fd, VKD3D_MAX_PROFILING_REGIONS * sizeof(*mapped_blocks)) < 0) | |
| { | |
| ERR("Failed to resize profiling FD.\n"); | |
| close(profiling_fd); | |
| return; | |
| } | |
| mapped_blocks = mmap(NULL, VKD3D_MAX_PROFILING_REGIONS * sizeof(*mapped_blocks), | |
| PROT_READ | PROT_WRITE, MAP_SHARED, profiling_fd, 0); | |
| if (!mapped_blocks) | |
| { | |
| ERR("Failed to map block.\n"); | |
| close(profiling_fd); | |
| return; | |
| } | |
| memset(mapped_blocks, 0, VKD3D_MAX_PROFILING_REGIONS * sizeof(*mapped_blocks)); | |
| close(profiling_fd); | |
| } | |
| else | |
| { | |
| ERR("Failed to open profiling FD.\n"); | |
| } | |
| } | |
| #endif | |
| static void vkd3d_init_profiling_once(void) | |
| { | |
| char path[VKD3D_PATH_MAX]; | |
| vkd3d_get_env_var("VKD3D_PROFILE_PATH", path, sizeof(path)); | |
| if (strlen(path) > 0) | |
| vkd3d_init_profiling_path(path); | |
| } | |
| void vkd3d_init_profiling(void) | |
| { | |
| pthread_once(&profiling_block_once, vkd3d_init_profiling_once); | |
| } | |
| bool vkd3d_uses_profiling(void) | |
| { | |
| return mapped_blocks != NULL; | |
| } | |
| unsigned int vkd3d_profiling_register_region(const char *name, spinlock_t *lock, uint32_t *latch) | |
| { | |
| unsigned int index; | |
| if (!mapped_blocks) | |
| return 0; | |
| spinlock_acquire(lock); | |
| if (*latch == 0) | |
| { | |
| spinlock_acquire(&profiling_lock); | |
| /* Begin at 1, 0 is reserved as a sentinel. */ | |
| index = ++profiling_region_count; | |
| if (index <= VKD3D_MAX_PROFILING_REGIONS) | |
| { | |
| strncpy(mapped_blocks[index - 1].name, name, sizeof(mapped_blocks[index - 1].name) - 1); | |
| /* Important to store with release semantics after we've initialized the block. */ | |
| vkd3d_atomic_uint32_store_explicit(latch, index, vkd3d_memory_order_release); | |
| } | |
| else | |
| { | |
| ERR("Too many profiling regions!\n"); | |
| index = 0; | |
| } | |
| spinlock_release(&profiling_lock); | |
| } | |
| else | |
| index = *latch; | |
| spinlock_release(lock); | |
| return index; | |
| } | |
| void vkd3d_profiling_notify_work(unsigned int index, | |
| uint64_t start_ticks, uint64_t end_ticks, | |
| unsigned int iteration_count) | |
| { | |
| struct vkd3d_profiling_block *block; | |
| spinlock_t *lock; | |
| if (index == 0 || index > VKD3D_MAX_PROFILING_REGIONS || !mapped_blocks) | |
| return; | |
| index--; | |
| lock = ®ion_locks[index]; | |
| block = &mapped_blocks[index]; | |
| spinlock_acquire(lock); | |
| block->iteration_total += iteration_count; | |
| block->ticks_total += end_ticks - start_ticks; | |
| spinlock_release(lock); | |
| } | |
| #endif /* VKD3D_ENABLE_PROFILING */ |