/** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #ifndef XENIA_BASE_PROFILING_H_ #define XENIA_BASE_PROFILING_H_ #include #include #include #include "xenia/base/platform.h" #include "xenia/base/string.h" #include "xenia/ui/ui_drawer.h" #include "xenia/ui/virtual_key.h" #include "xenia/ui/window_listener.h" #ifndef XE_OPTION_PROFILING #define XE_OPTION_PROFILING 0 #endif #ifndef XE_OPTION_PROFILING_UI #define XE_OPTION_PROFILING_UI 0 #endif #if XE_OPTION_PROFILING // Pollutes the global namespace. Yuck. #define MICROPROFILE_MAX_THREADS 256 #include #endif // XE_OPTION_PROFILING namespace xe { namespace ui { class ImmediateDrawer; class MicroprofileDrawer; class Presenter; class Window; } // namespace ui } // namespace xe namespace xe { #if XE_OPTION_PROFILING // Defines a profiling scope for CPU tasks. // Use `SCOPE_profile_cpu(name)` to activate the scope. #define DEFINE_profile_cpu(name, group_name, scope_name) \ MICROPROFILE_DEFINE(name, group_name, scope_name, \ xe::Profiler::GetColor(scope_name)) // Declares a previously defined profile scope. Use in a translation unit. #define DECLARE_profile_cpu(name) MICROPROFILE_DECLARE(name) // Defines a profiling scope for GPU tasks. // Use `COUNT_profile_gpu(name)` to activate the scope. #define DEFINE_profile_gpu(name, group_name, scope_name) \ MICROPROFILE_DEFINE_GPU(name, group_name, scope_name, \ xe::Profiler::GetColor(scope_name)) // Declares a previously defined profile scope. Use in a translation unit. #define DECLARE_profile_gpu(name) MICROPROFILE_DECLARE_GPU(name) // Enters a previously defined CPU profiling scope, active for the duration // of the containing block. #define SCOPE_profile_cpu(name) MICROPROFILE_SCOPE(name) // Enters a CPU profiling scope, active for the duration of the containing // block. No previous definition required. #define SCOPE_profile_cpu_i(group_name, scope_name) \ MICROPROFILE_SCOPEI(group_name, scope_name, \ xe::Profiler::GetColor(scope_name)) // Enters a CPU profiling scope by function name, active for the duration of // the containing block. No previous definition required. #define SCOPE_profile_cpu_f(group_name) \ MICROPROFILE_SCOPEI(group_name, __FUNCTION__, \ xe::Profiler::GetColor(__FUNCTION__)) // Enters a previously defined GPU profiling scope, active for the duration // of the containing block. #define SCOPE_profile_gpu(name) MICROPROFILE_SCOPEGPU(name) // Enters a GPU profiling scope, active for the duration of the containing // block. No previous definition required. #define SCOPE_profile_gpu_i(group_name, scope_name) \ MICROPROFILE_SCOPEGPUI(group_name, scope_name, \ xe::Profiler::GetColor(scope_name)) // Enters a GPU profiling scope by function name, active for the duration of // the containing block. No previous definition required. #define SCOPE_profile_gpu_f(group_name) \ MICROPROFILE_SCOPEGPUI(group_name, __FUNCTION__, \ xe::Profiler::GetColor(__FUNCTION__)) // Adds a number to a counter #define COUNT_profile_add(name, count) MICROPROFILE_COUNTER_ADD(name, count) // Subtracts a number to a counter #define COUNT_profile_sub(name, count) MICROPROFILE_COUNTER_SUB(name, count) // Sets a counter's value #define COUNT_profile_set(name, count) MICROPROFILE_COUNTER_SET(name, count) // Tracks a CPU value counter. #define COUNT_profile_cpu(name, count) MICROPROFILE_META_CPU(name, count) // Tracks a GPU value counter. #define COUNT_profile_gpu(name, count) MICROPROFILE_META_GPU(name, count) #else #define DEFINE_profile_cpu(name, group_name, scope_name) #define DEFINE_profile_gpu(name, group_name, scope_name) #define DECLARE_profile_cpu(name) #define DECLARE_profile_gpu(name) #define SCOPE_profile_cpu(name) \ do { \ } while (false) #define SCOPE_profile_cpu_f(name) \ do { \ } while (false) #define SCOPE_profile_cpu_i(group_name, scope_name) \ do { \ } while (false) #define SCOPE_profile_gpu(name) \ do { \ } while (false) #define SCOPE_profile_gpu_f(name) \ do { \ } while (false) #define SCOPE_profile_gpu_i(group_name, scope_name) \ do { \ } while (false) #define COUNT_profile_add(name, count) \ do { \ } while (false) #define COUNT_profile_sub(name, count) \ do { \ } while (false) #define COUNT_profile_set(name, count) \ do { \ } while (false) #define COUNT_profile_cpu(name, count) \ do { \ } while (false) #define COUNT_profile_gpu(name, count) \ do { \ } while (false) #ifndef MICROPROFILE_TEXT_WIDTH #define MICROPROFILE_TEXT_WIDTH 1 #define MICROPROFILE_TEXT_HEIGHT 1 #endif // !MICROPROFILE_TEXT_WIDTH #endif // XE_OPTION_PROFILING class Profiler { public: static bool is_enabled(); static bool is_visible(); // Initializes the profiler. Call at startup. static void Initialize(); // Dumps data to stdout. static void Dump(); // Cleans up profiling, releasing all memory. static void Shutdown(); // Computes a color from the given string. static uint32_t GetColor(const char* str); // Activates the calling thread for profiling. // This must be called immediately after launching a thread. static void ThreadEnter(const char* name = nullptr); // Deactivates the calling thread for profiling. static void ThreadExit(); static void ToggleDisplay(); static void TogglePause(); // Initializes input for the given window and drawing for the given presenter // and immediate drawer. static void SetUserIO(size_t z_order, ui::Window* window, ui::Presenter* presenter, ui::ImmediateDrawer* immediate_drawer); // Gets the current drawer, if any. static ui::MicroprofileDrawer* drawer() { #if XE_OPTION_PROFILING_UI return drawer_.get(); #else return nullptr; #endif } // Presents the profiler to the bound display, if any. static void Present(ui::UIDrawContext& ui_draw_context); // Starts a new frame on the profiler static void Flip(); private: #if XE_OPTION_PROFILING class ProfilerWindowInputListener final : public ui::WindowInputListener { public: void OnKeyDown(ui::KeyEvent& e) override; void OnKeyUp(ui::KeyEvent& e) override; #if XE_OPTION_PROFILING_UI void OnMouseDown(ui::MouseEvent& e) override; void OnMouseMove(ui::MouseEvent& e) override; void OnMouseUp(ui::MouseEvent& e) override; void OnMouseWheel(ui::MouseEvent& e) override; #endif // XE_OPTION_PROFILING_UI }; // For now, no need for OnDpiChanged in a WindowListener because redrawing is // done continuously. #if XE_OPTION_PROFILING_UI class ProfilerUIDrawer final : public ui::UIDrawer { public: void Draw(ui::UIDrawContext& context) override; }; #endif // XE_OPTION_PROFILING_UI #if XE_OPTION_PROFILING_UI static void SetMousePosition(int32_t x, int32_t y, int32_t wheel_delta); #endif // XE_OPTION_PROFILING_UI static void PostInputEvent(); static ProfilerWindowInputListener input_listener_; static size_t z_order_; static ui::Window* window_; #if XE_OPTION_PROFILING_UI static ProfilerUIDrawer ui_drawer_; static ui::Presenter* presenter_; static std::unique_ptr drawer_; static bool dpi_scaling_; #endif // XE_OPTION_PROFILING_UI #endif // XE_OPTION_PROFILING }; } // namespace xe #endif // XENIA_BASE_PROFILING_H_