Profiler skeleton.
This commit is contained in:
parent
f3f9d93017
commit
6486e0a48e
|
@ -18,6 +18,7 @@
|
||||||
#include <xenia/malloc.h>
|
#include <xenia/malloc.h>
|
||||||
#include <xenia/platform.h>
|
#include <xenia/platform.h>
|
||||||
#include <xenia/platform_includes.h>
|
#include <xenia/platform_includes.h>
|
||||||
|
#include <xenia/profiling.h>
|
||||||
#include <xenia/string.h>
|
#include <xenia/string.h>
|
||||||
#include <xenia/types.h>
|
#include <xenia/types.h>
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,8 @@
|
||||||
#define XE_OPTION_LOG_KERNEL 1
|
#define XE_OPTION_LOG_KERNEL 1
|
||||||
#define XE_OPTION_LOG_FS 1
|
#define XE_OPTION_LOG_FS 1
|
||||||
|
|
||||||
|
// Enable profiling.
|
||||||
|
#define XE_OPTION_PROFILING 1
|
||||||
|
|
||||||
// TODO(benvanik): make this a runtime option
|
// TODO(benvanik): make this a runtime option
|
||||||
#define XE_OPTION_OPTIMIZED 0
|
#define XE_OPTION_OPTIMIZED 0
|
||||||
|
|
|
@ -79,7 +79,9 @@ static uint32_t __stdcall xe_thread_callback_win32(void* param) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
xe::Profiler::ThreadEnter(thread->name);
|
||||||
thread->callback(thread->callback_param);
|
thread->callback(thread->callback_param);
|
||||||
|
xe::Profiler::ThreadExit();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#pragma warning(default : 6320; default : 6322)
|
#pragma warning(default : 6320; default : 6322)
|
||||||
|
@ -118,7 +120,9 @@ static void* xe_thread_callback_pthreads(void* param) {
|
||||||
#else
|
#else
|
||||||
pthread_setname_np(pthread_self(), thread->name);
|
pthread_setname_np(pthread_self(), thread->name);
|
||||||
#endif // OSX
|
#endif // OSX
|
||||||
|
xe::Profiler::ThreadEnter(thread->name);
|
||||||
thread->callback(thread->callback_param);
|
thread->callback(thread->callback_param);
|
||||||
|
xe::Profiler::ThreadExit();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -253,10 +253,12 @@ X_STATUS XThread::Exit(int exit_code) {
|
||||||
|
|
||||||
static uint32_t __stdcall XThreadStartCallbackWin32(void* param) {
|
static uint32_t __stdcall XThreadStartCallbackWin32(void* param) {
|
||||||
XThread* thread = reinterpret_cast<XThread*>(param);
|
XThread* thread = reinterpret_cast<XThread*>(param);
|
||||||
|
xe::Profiler::ThreadEnter(thread->name());
|
||||||
xeKeTlsSetValue(current_thread_tls, (uint64_t)thread);
|
xeKeTlsSetValue(current_thread_tls, (uint64_t)thread);
|
||||||
thread->Execute();
|
thread->Execute();
|
||||||
xeKeTlsSetValue(current_thread_tls, NULL);
|
xeKeTlsSetValue(current_thread_tls, NULL);
|
||||||
thread->Release();
|
thread->Release();
|
||||||
|
xe::Profiler::ThreadExit();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,10 +295,12 @@ X_STATUS XThread::PlatformExit(int exit_code) {
|
||||||
|
|
||||||
static void* XThreadStartCallbackPthreads(void* param) {
|
static void* XThreadStartCallbackPthreads(void* param) {
|
||||||
XThread* thread = reinterpret_cast<XThread*>(param);
|
XThread* thread = reinterpret_cast<XThread*>(param);
|
||||||
|
xe::Profiler::ThreadEnter(thread->name());
|
||||||
xeKeTlsSetValue(current_thread_tls, (uint64_t)thread);
|
xeKeTlsSetValue(current_thread_tls, (uint64_t)thread);
|
||||||
thread->Execute();
|
thread->Execute();
|
||||||
xeKeTlsSetValue(current_thread_tls, NULL);
|
xeKeTlsSetValue(current_thread_tls, NULL);
|
||||||
thread->Release();
|
thread->Release();
|
||||||
|
xe::Profiler::ThreadExit();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 <xenia/profiling.h>
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
|
||||||
|
std::unique_ptr<ProfilerDisplay> 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<ProfilerDisplay> 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<xe::ProfilerDisplay::BoxType>(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);
|
||||||
|
}
|
|
@ -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 <memory>
|
||||||
|
|
||||||
|
#include <xenia/config.h>
|
||||||
|
#include <xenia/platform.h>
|
||||||
|
#include <xenia/platform_includes.h>
|
||||||
|
#include <xenia/string.h>
|
||||||
|
#include <xenia/types.h>
|
||||||
|
|
||||||
|
#if XE_OPTION_PROFILING
|
||||||
|
// Pollutes the global namespace. Yuck.
|
||||||
|
#include <microprofile/microprofile.h>
|
||||||
|
#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<ProfilerDisplay> 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<ProfilerDisplay> display_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace xe
|
||||||
|
|
||||||
|
#endif // XENIA_PROFILING_H_
|
|
@ -18,6 +18,8 @@
|
||||||
'platform.cc',
|
'platform.cc',
|
||||||
'platform.h',
|
'platform.h',
|
||||||
'platform_includes.h',
|
'platform_includes.h',
|
||||||
|
'profiling.cc',
|
||||||
|
'profiling.h',
|
||||||
'string.cc',
|
'string.cc',
|
||||||
'string.h',
|
'string.h',
|
||||||
'types.h',
|
'types.h',
|
||||||
|
|
|
@ -24,6 +24,8 @@ using namespace xe::cpu;
|
||||||
|
|
||||||
|
|
||||||
int alloy_sandbox(int argc, xechar_t** argv) {
|
int alloy_sandbox(int argc, xechar_t** argv) {
|
||||||
|
xe::Profiler::ThreadEnter("main");
|
||||||
|
|
||||||
XenonMemory* memory = new XenonMemory();
|
XenonMemory* memory = new XenonMemory();
|
||||||
|
|
||||||
ExportResolver* export_resolver = new ExportResolver();
|
ExportResolver* export_resolver = new ExportResolver();
|
||||||
|
@ -57,6 +59,9 @@ int alloy_sandbox(int argc, xechar_t** argv) {
|
||||||
delete runtime;
|
delete runtime;
|
||||||
delete memory;
|
delete memory;
|
||||||
|
|
||||||
|
xe::Profiler::Dump();
|
||||||
|
xe::Profiler::ThreadExit();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
// ehhh
|
// ehhh
|
||||||
|
|
|
@ -22,6 +22,8 @@ DEFINE_string(target, "",
|
||||||
int xenia_run(int argc, xechar_t** argv) {
|
int xenia_run(int argc, xechar_t** argv) {
|
||||||
int result_code = 1;
|
int result_code = 1;
|
||||||
|
|
||||||
|
Profiler::ThreadEnter("main");
|
||||||
|
|
||||||
Emulator* emulator = NULL;
|
Emulator* emulator = NULL;
|
||||||
|
|
||||||
// Grab path from the flag or unnamed argument.
|
// Grab path from the flag or unnamed argument.
|
||||||
|
@ -89,6 +91,8 @@ XECLEANUP:
|
||||||
if (result_code) {
|
if (result_code) {
|
||||||
XEFATAL("Failed to launch emulator: %d", result_code);
|
XEFATAL("Failed to launch emulator: %d", result_code);
|
||||||
}
|
}
|
||||||
|
Profiler::Dump();
|
||||||
|
Profiler::Shutdown();
|
||||||
return result_code;
|
return result_code;
|
||||||
}
|
}
|
||||||
XE_MAIN_WINDOW_THUNK(xenia_run, XETEXT("xenia-run"), "xenia-run some.xex");
|
XE_MAIN_WINDOW_THUNK(xenia_run, XETEXT("xenia-run"), "xenia-run some.xex");
|
||||||
|
|
|
@ -39,6 +39,8 @@
|
||||||
'target_defaults': {
|
'target_defaults': {
|
||||||
'include_dirs': [
|
'include_dirs': [
|
||||||
'include/',
|
'include/',
|
||||||
|
'third_party/',
|
||||||
|
'.',
|
||||||
],
|
],
|
||||||
|
|
||||||
'defines': [
|
'defines': [
|
||||||
|
@ -242,6 +244,7 @@
|
||||||
'user32',
|
'user32',
|
||||||
'ole32',
|
'ole32',
|
||||||
'ntdll',
|
'ntdll',
|
||||||
|
'advapi32',
|
||||||
],
|
],
|
||||||
}],
|
}],
|
||||||
['OS == "mac"', {
|
['OS == "mac"', {
|
||||||
|
@ -318,6 +321,7 @@
|
||||||
'xinput',
|
'xinput',
|
||||||
'xaudio2',
|
'xaudio2',
|
||||||
'Shell32',
|
'Shell32',
|
||||||
|
'advapi32',
|
||||||
],
|
],
|
||||||
}],
|
}],
|
||||||
['OS == "mac"', {
|
['OS == "mac"', {
|
||||||
|
|
Loading…
Reference in New Issue