diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ab08739f7..5501343e38 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -219,6 +219,8 @@ cmake_dependent_option(YUZU_CMD "Compile the eden-cli executable" ON "NOT ANDROI cmake_dependent_option(YUZU_CRASH_DUMPS "Compile crash dump (Minidump) support" OFF "WIN32 OR PLATFORM_LINUX" OFF) +option(ENABLE_TRACY "Enable Tracy profiler support" OFF) + option(YUZU_DOWNLOAD_TIME_ZONE_DATA "Always download time zone binaries" ON) set(YUZU_TZDB_PATH "" CACHE STRING "Path to a pre-downloaded timezone database") @@ -662,6 +664,29 @@ if (WIN32 AND YUZU_CRASH_DUMPS) # target_include_directories(libbreakpad_client INTERFACE "${BREAKPAD_CLIENT_INCLUDE_DIR}") endif() +if (ENABLE_TRACY) + AddJsonPackage(tracy) + + if (tracy_ADDED) + include_directories(${tracy_SOURCE_DIR}/public) + # build tracy client as its own static library even if overkill + add_library(tracy_client STATIC ${tracy_SOURCE_DIR}/public/TracyClient.cpp) + target_include_directories(tracy_client PRIVATE src) + + # Avoid tracy errors (This sets it to W0 instead of W3 though? TODO: fix this) + if (MSVC AND CXX_CLANG) + target_compile_options(tracy_client PRIVATE /W3) + endif() + + add_compile_definitions(TRACY_ENABLE) + add_compile_definitions(TRACY_SAMPLING_PROFILER_MANUAL_START) # See yuzu\main.cpp + add_compile_definitions(TRACY_ON_DEMAND) # Use on demand mode to avoid profiling emulator start up and game load screens + add_compile_definitions(TRACY_FIBERS) + + add_library(Tracy::client ALIAS tracy_client) + endif() +endif() + # Include source code # =================== diff --git a/cpmfile.json b/cpmfile.json index c938e67e88..f2cf069c8e 100644 --- a/cpmfile.json +++ b/cpmfile.json @@ -114,5 +114,12 @@ "QUAZIP_INSTALL OFF", "QUAZIP_ENABLE_QTEXTCODEC OFF" ] + }, + "tracy": { + "repo": "wolfpld/tracy", + "sha": "05cceee", + "hash": "fdf8f3eb0f44c17760e9e559ece6907606580da20568b7900e97e40f3ea773b9e6dbac7f0b04ef32e363d779ec013af63c85adbe2a3807db9205ec48887a546c", + "version": "0.13.1", + "download_only": true } } diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index 69eca732eb..3483cd74e1 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -13,6 +13,9 @@ #include +//#include "common/tracy_instrumentation.h" +//#include + namespace Common { #ifdef __OPENORBIS__ @@ -42,6 +45,13 @@ struct Fiber::FiberImpl { u8* rewind_stack_limit = nullptr; bool is_thread_fiber = false; bool released = false; + +// Support tracy fibers, show them using incrementing counter in profiler +// TODO: Commented out because I could not properly test because CPU threads have messed up ghost zones due to jit code failing to support stack traces +//#if TRACY_ENABLE +// std::string tracy_fiber_name; +// static inline int tracy_fiber_next_id = 1; +//#endif }; void Fiber::SetRewindPoint(std::function&& rewind_func) { @@ -49,12 +59,20 @@ void Fiber::SetRewindPoint(std::function&& rewind_func) { } Fiber::Fiber(std::function&& entry_point_func) : impl{std::make_unique()} { +//#if TRACY_ENABLE +// std::ostringstream ss; +// ss << "fiber #" << impl->tracy_fiber_next_id++; +// impl->tracy_fiber_name = std::move(ss).str(); +//#endif impl->entry_point = std::move(entry_point_func); impl->stack_limit = impl->stack.data(); impl->rewind_stack_limit = impl->rewind_stack.data(); u8* stack_base = impl->stack_limit + DEFAULT_STACK_SIZE; impl->context = boost::context::detail::make_fcontext(stack_base, impl->stack.size(), [](boost::context::detail::transfer_t transfer) -> void { auto* fiber = static_cast(transfer.data); + //#if TRACY_ENABLE + // TracyFiberEnter(fiber->impl->tracy_fiber_name.c_str()); + //#endif ASSERT(fiber && fiber->impl && fiber->impl->previous_fiber && fiber->impl->previous_fiber->impl); ASSERT(fiber->impl->canary_1 == CANARY_VALUE); ASSERT(fiber->impl->canary_2 == CANARY_VALUE); @@ -91,7 +109,16 @@ void Fiber::YieldTo(std::weak_ptr weak_from, Fiber& to) { to.impl->guard.lock(); to.impl->previous_fiber = weak_from.lock(); +//#if TRACY_ENABLE +// // Is this correct? +// TracyFiberLeave; +//#endif + auto transfer = boost::context::detail::jump_fcontext(to.impl->context, &to); +//#if TRACY_ENABLE +// // Switched to executing "to" fiber, inform tracy +// TracyFiberEnter(to.impl->tracy_fiber_name.c_str()); +//#endif // "from" might no longer be valid if the thread was killed if (auto from = weak_from.lock()) { if (from->impl->previous_fiber == nullptr) { diff --git a/src/common/tracy_instrumentation.h b/src/common/tracy_instrumentation.h new file mode 100644 index 0000000000..1bbdd20522 --- /dev/null +++ b/src/common/tracy_instrumentation.h @@ -0,0 +1,22 @@ +#pragma once + +// Add our own tracy instrumentation macros which compile to nothing without TRACY_ENABLE +// tracys macros alread do that but do require you to always include the full tracy code even without TRACY_ENABLE +// Doing this means any instrumentation in the project keeps compiling even with ENABLE_TRACY=0 in cmake, meaning most devs can compile without even pulling tracy at all +// Additionally These macros may be more readable than the badly named "ZoneScoped", and we could easily add levels or categories of instrumentation that can be made selectable +// Unless there is already a tracing framework that is configurable...? +#if TRACY_ENABLE +#include + +#define TRACY_ZONE_SCOPED ZoneScoped; +#define TRACY_ZONE_SCOPEDN(name) ZoneScopedN(name); +#define TRACY_ZONE_SCOPEDVN(var, name) ZoneScopedVN(var, name); +#define TRACY_ZONE_NAMEDN(var, name) ZoneNamedN(var, name, true); +#define TRACY_MSG_C(text, col) TracyMessageC(text, strlen(text), col); +#else +#define TRACY_ZONE_SCOPED +#define TRACY_ZONE_SCOPEDN(name) +#define TRACY_ZONE_SCOPEDVN(var, name) +#define TRACY_ZONE_NAMEDN(var, name) +#define TRACY_MSG_C(text, col) +#endif diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index 5d55cf551b..d3e9a9ccce 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -19,6 +19,8 @@ #include "video_core/vulkan_common/vulkan_wrapper.h" #include "vulkan/vulkan_core.h" +#include "common/tracy_instrumentation.h" + namespace Vulkan { namespace { @@ -226,6 +228,10 @@ void Swapchain::Present(VkSemaphore render_semaphore) { if (frame_index >= image_count) { frame_index = 0; } + + #if TRACY_ENABLE + FrameMark; + #endif } void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities) { diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 982c0eb196..f23e525f3c 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -401,6 +401,10 @@ target_link_libraries(yuzu PRIVATE common core input_common frontend_common netw target_link_libraries(yuzu PRIVATE Boost::headers glad Qt6::Widgets Qt6::Charts Qt6::Concurrent) target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) +if (ENABLE_TRACY) + target_link_libraries(yuzu PRIVATE Tracy::client) +endif() + if (UNIX AND NOT APPLE) target_link_libraries(yuzu PRIVATE Qt6::DBus) endif() diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index f8528d9671..c135e8c28d 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -20,6 +20,8 @@ #ifdef _WIN32 #include +#include "common/tracy_instrumentation.h" + static void OverrideWindowsFont() { // Qt5 chooses these fonts on Windows and they have fairly ugly alphanumeric/cyrillic characters // Asking to use "MS Shell Dlg 2" gives better other chars while leaving the Chinese Characters. @@ -91,6 +93,17 @@ int main(int argc, char* argv[]) { return 0; } +#if TRACY_ENABLE + // Avoid cmake static library for tracy being optimized away by "helpful" linker if when not referenced (no manual instrumentation, but still want sample-based mode) + //TracyNoop; // This would be needed if not for tracy::BeginSamplingProfiling + + // have to use TRACY_SAMPLING_PROFILER_MANUAL_START because program is started a second time as child process for vulkan check + // this messes with tracy sample profiling as windows event tracing in child process stops it for parent process + if (!is_child) { + tracy::BeginSamplingProfiling(); + } +#endif + if (StartupChecks(argv[0], &has_broken_vulkan, Settings::values.perform_vulkan_check.GetValue())) { return 0; @@ -177,5 +190,11 @@ int main(int argc, char* argv[]) { int result = app.exec(); detached_tasks.WaitForAllTasks(); + +#if TRACY_ENABLE + if (!is_child) { + tracy::EndSamplingProfiling(); + } +#endif return result; } diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt index a1f16be75c..ddac4171d0 100644 --- a/src/yuzu_cmd/CMakeLists.txt +++ b/src/yuzu_cmd/CMakeLists.txt @@ -55,6 +55,10 @@ if(WIN32) endif() endif() +if (ENABLE_TRACY) + target_link_libraries(yuzu-cmd PRIVATE Tracy::client) +endif() + create_target_directory_groups(yuzu-cmd) # needed for vma diff --git a/src/yuzu_room_standalone/CMakeLists.txt b/src/yuzu_room_standalone/CMakeLists.txt index 125c7c911d..c29e97dffe 100644 --- a/src/yuzu_room_standalone/CMakeLists.txt +++ b/src/yuzu_room_standalone/CMakeLists.txt @@ -11,6 +11,10 @@ if(UNIX AND NOT APPLE) install(TARGETS yuzu_room_standalone RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") endif() +if (ENABLE_TRACY) + target_link_libraries(yuzu_room_standalone PRIVATE Tracy::client) +endif() + if (YUZU_STATIC_ROOM) target_link_options(yuzu_room_standalone PRIVATE "-static") endif()