From 6486e0a48ee5ad5a7f74be048e1952a630ece55c Mon Sep 17 00:00:00 2001 From: Ben Vanik Date: Tue, 27 May 2014 22:54:40 -0700 Subject: [PATCH] Profiler skeleton. --- src/xenia/common.h | 1 + src/xenia/config.h | 2 + src/xenia/core/thread.cc | 4 + src/xenia/kernel/objects/xthread.cc | 4 + src/xenia/profiling.cc | 88 +++++++++++++++++ src/xenia/profiling.h | 135 +++++++++++++++++++++++++++ src/xenia/sources.gypi | 2 + tools/alloy-sandbox/alloy-sandbox.cc | 5 + tools/xenia-run/xenia-run.cc | 4 + xenia.gyp | 4 + 10 files changed, 249 insertions(+) create mode 100644 src/xenia/profiling.cc create mode 100644 src/xenia/profiling.h diff --git a/src/xenia/common.h b/src/xenia/common.h index ff16b03c3..68d9d2eb7 100644 --- a/src/xenia/common.h +++ b/src/xenia/common.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include diff --git a/src/xenia/config.h b/src/xenia/config.h index b83aa8715..0804277bf 100644 --- a/src/xenia/config.h +++ b/src/xenia/config.h @@ -27,6 +27,8 @@ #define XE_OPTION_LOG_KERNEL 1 #define XE_OPTION_LOG_FS 1 +// Enable profiling. +#define XE_OPTION_PROFILING 1 // TODO(benvanik): make this a runtime option #define XE_OPTION_OPTIMIZED 0 diff --git a/src/xenia/core/thread.cc b/src/xenia/core/thread.cc index 8a48d8267..8aace9ee4 100644 --- a/src/xenia/core/thread.cc +++ b/src/xenia/core/thread.cc @@ -79,7 +79,9 @@ static uint32_t __stdcall xe_thread_callback_win32(void* param) { } } + xe::Profiler::ThreadEnter(thread->name); thread->callback(thread->callback_param); + xe::Profiler::ThreadExit(); return 0; } #pragma warning(default : 6320; default : 6322) @@ -118,7 +120,9 @@ static void* xe_thread_callback_pthreads(void* param) { #else pthread_setname_np(pthread_self(), thread->name); #endif // OSX + xe::Profiler::ThreadEnter(thread->name); thread->callback(thread->callback_param); + xe::Profiler::ThreadExit(); return 0; } diff --git a/src/xenia/kernel/objects/xthread.cc b/src/xenia/kernel/objects/xthread.cc index 33f5aa378..8acce8b27 100644 --- a/src/xenia/kernel/objects/xthread.cc +++ b/src/xenia/kernel/objects/xthread.cc @@ -253,10 +253,12 @@ X_STATUS XThread::Exit(int exit_code) { static uint32_t __stdcall XThreadStartCallbackWin32(void* param) { XThread* thread = reinterpret_cast(param); + xe::Profiler::ThreadEnter(thread->name()); xeKeTlsSetValue(current_thread_tls, (uint64_t)thread); thread->Execute(); xeKeTlsSetValue(current_thread_tls, NULL); thread->Release(); + xe::Profiler::ThreadExit(); return 0; } @@ -293,10 +295,12 @@ X_STATUS XThread::PlatformExit(int exit_code) { static void* XThreadStartCallbackPthreads(void* param) { XThread* thread = reinterpret_cast(param); + xe::Profiler::ThreadEnter(thread->name()); xeKeTlsSetValue(current_thread_tls, (uint64_t)thread); thread->Execute(); xeKeTlsSetValue(current_thread_tls, NULL); thread->Release(); + xe::Profiler::ThreadExit(); return 0; } diff --git a/src/xenia/profiling.cc b/src/xenia/profiling.cc new file mode 100644 index 000000000..c7b5f2eb2 --- /dev/null +++ b/src/xenia/profiling.cc @@ -0,0 +1,88 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2014 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#define MICRO_PROFILE_IMPL +#include + +namespace xe { + +std::unique_ptr Profiler::display_ = nullptr; + +void Profiler::Dump() { + MicroProfileDumpTimers(); +} + +void Profiler::Shutdown() { + display_.reset(); + MicroProfileShutdown(); +} + +void Profiler::ThreadEnter(const char* name) { + MicroProfileOnThreadCreate(name); +} + +void Profiler::ThreadExit() { + MicroProfileOnThreadExit(); +} + +void Profiler::set_display(std::unique_ptr display) { + display_ = std::move(display); +} + +void Profiler::Present() { + MicroProfileFlip(); + if (!display_) { + return; + } + + display_->Begin(); + MicroProfileDraw(display_->width(), display_->height()); + display_->End(); +} + +} // namespace xe + +uint32_t MicroProfileGpuInsertTimeStamp() { + return 0; +} + +uint64_t MicroProfileGpuGetTimeStamp(uint32_t nKey) { + return 0; +} + +uint64_t MicroProfileTicksPerSecondGpu() { + return 0; +} + +void MicroProfileDrawBox(int nX, int nY, int nX1, int nY1, uint32_t nColor, MicroProfileBoxType type) { + auto display = xe::Profiler::display(); + if (!display) { + return; + } + display->DrawBox( + nX, nY, nX1, nY1, + nColor, + static_cast(type)); +} + +void MicroProfileDrawLine2D(uint32_t nVertices, float* pVertices, uint32_t nColor) { + auto display = xe::Profiler::display(); + if (!display) { + return; + } + display->DrawLine2D(nVertices, pVertices, nColor); +} + +void MicroProfileDrawText(int nX, int nY, uint32_t nColor, const char* pText, uint32_t nLen) { + auto display = xe::Profiler::display(); + if (!display) { + return; + } + display->DrawText(nX, nY, nColor, pText, nLen); +} diff --git a/src/xenia/profiling.h b/src/xenia/profiling.h new file mode 100644 index 000000000..8ab3d3169 --- /dev/null +++ b/src/xenia/profiling.h @@ -0,0 +1,135 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2014 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_PROFILING_H_ +#define XENIA_PROFILING_H_ + +#include + +#include +#include +#include +#include +#include + +#if XE_OPTION_PROFILING +// Pollutes the global namespace. Yuck. +#include +#endif // XE_OPTION_PROFILING + +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, color) \ + MICROPROFILE_DEFINE(name, group_name, scope_name, color) + +// 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, color) \ + MICROPROFILE_DEFINE_GPU(name, group_name, scope_name, color) + +// 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, color) \ + MICROPROFILE_SCOPEI(group_name, scope_name, color) + +// 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, color) \ + MICROPROFILE_SCOPEGPUI(group_name, scope_name, color) + +// 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, color) +#define DEFINE_profile_gpu(name, group_name, scope_name, color) +#define DECLARE_profile_cpu(name) +#define DECLARE_profile_gpu(name) +#define SCOPE_profile_cpu(name) do {} while (false) +#define SCOPE_profile_cpu_i(group_name, scope_name, color) do {} while (false) +#define SCOPE_profile_gpu(name) do {} while (false) +#define SCOPE_profile_gpu_i(group_name, scope_name, color) do {} while (false) +#define COUNT_profile_cpu(name, count) do {} while (false) +#define COUNT_profile_gpu(name, count) do {} while (false) + +#endif // XE_OPTION_PROFILING + +class ProfilerDisplay { +public: + enum BoxType { + BOX_TYPE_BAR = MicroProfileBoxTypeBar, + BOX_TYPE_FLAT = MicroProfileBoxTypeFlat, + }; + + virtual uint32_t width() const = 0; + virtual uint32_t height() const = 0; + + // TODO(benvanik): GPU timestamping. + + virtual void Begin() = 0; + virtual void End() = 0; + virtual void DrawBox(int x, int y, int x1, int y1, uint32_t color, BoxType type) = 0; + virtual void DrawLine2D(uint32_t count, float* vertices, uint32_t color) = 0; + virtual void DrawText(int x, int y, uint32_t color, const char* text, size_t text_length) = 0; +}; + +class Profiler { +public: + // Dumps data to stdout. + static void Dump(); + // Cleans up profiling, releasing all memory. + static void Shutdown(); + + // 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(); + + // Gets the current display, if any. + static ProfilerDisplay* display() { return display_.get(); } + // Initializes drawing with the given display. + static void set_display(std::unique_ptr display); + // Presents the profiler to the bound display, if any. + static void Present(); + + // TODO(benvanik): display mode/pause/etc? + // TODO(benvanik): mouse, keys + +private: + static std::unique_ptr display_; +}; + +} // namespace xe + +#endif // XENIA_PROFILING_H_ diff --git a/src/xenia/sources.gypi b/src/xenia/sources.gypi index 5d2c066b0..0d20898e0 100644 --- a/src/xenia/sources.gypi +++ b/src/xenia/sources.gypi @@ -18,6 +18,8 @@ 'platform.cc', 'platform.h', 'platform_includes.h', + 'profiling.cc', + 'profiling.h', 'string.cc', 'string.h', 'types.h', diff --git a/tools/alloy-sandbox/alloy-sandbox.cc b/tools/alloy-sandbox/alloy-sandbox.cc index e7f6bb2d6..e37a1d3d2 100644 --- a/tools/alloy-sandbox/alloy-sandbox.cc +++ b/tools/alloy-sandbox/alloy-sandbox.cc @@ -24,6 +24,8 @@ using namespace xe::cpu; int alloy_sandbox(int argc, xechar_t** argv) { + xe::Profiler::ThreadEnter("main"); + XenonMemory* memory = new XenonMemory(); ExportResolver* export_resolver = new ExportResolver(); @@ -57,6 +59,9 @@ int alloy_sandbox(int argc, xechar_t** argv) { delete runtime; delete memory; + xe::Profiler::Dump(); + xe::Profiler::ThreadExit(); + return 0; } // ehhh diff --git a/tools/xenia-run/xenia-run.cc b/tools/xenia-run/xenia-run.cc index 59bc70a24..909e26dc8 100644 --- a/tools/xenia-run/xenia-run.cc +++ b/tools/xenia-run/xenia-run.cc @@ -22,6 +22,8 @@ DEFINE_string(target, "", int xenia_run(int argc, xechar_t** argv) { int result_code = 1; + Profiler::ThreadEnter("main"); + Emulator* emulator = NULL; // Grab path from the flag or unnamed argument. @@ -89,6 +91,8 @@ XECLEANUP: if (result_code) { XEFATAL("Failed to launch emulator: %d", result_code); } + Profiler::Dump(); + Profiler::Shutdown(); return result_code; } XE_MAIN_WINDOW_THUNK(xenia_run, XETEXT("xenia-run"), "xenia-run some.xex"); diff --git a/xenia.gyp b/xenia.gyp index a765e5c00..8933eb750 100644 --- a/xenia.gyp +++ b/xenia.gyp @@ -39,6 +39,8 @@ 'target_defaults': { 'include_dirs': [ 'include/', + 'third_party/', + '.', ], 'defines': [ @@ -242,6 +244,7 @@ 'user32', 'ole32', 'ntdll', + 'advapi32', ], }], ['OS == "mac"', { @@ -318,6 +321,7 @@ 'xinput', 'xaudio2', 'Shell32', + 'advapi32', ], }], ['OS == "mac"', {