[UI] Image post-processing and full presentation/window rework
[GPU] Add FXAA post-processing [UI] Add FidelityFX FSR and CAS post-processing [UI] Add blue noise dithering from 10bpc to 8bpc [GPU] Apply the DC PWL gamma ramp closer to the spec, supporting fully white color [UI] Allow the GPU CP thread to present on the host directly, bypassing the UI thread OS paint event [UI] Allow variable refresh rate (or tearing) [UI] Present the newest frame (restart) on DXGI [UI] Replace GraphicsContext with a far more advanced Presenter with more coherent surface connection and UI overlay state management [UI] Connect presentation to windows via the Surface class, not native window handles [Vulkan] Switch to simpler Vulkan setup with no instance/device separation due to interdependencies and to pass fewer objects around [Vulkan] Lower the minimum required Vulkan version to 1.0 [UI/GPU] Various cleanup, mainly ComPtr usage [UI] Support per-monitor DPI awareness v2 on Windows [UI] DPI-scale Dear ImGui [UI] Replace the remaining non-detachable window delegates with unified window event and input listeners [UI] Allow listeners to safely destroy or close the window, and to register/unregister listeners without use-after-free and the ABA problem [UI] Explicit Z ordering of input listeners and UI overlays, top-down for input, bottom-up for drawing [UI] Add explicit window lifecycle phases [UI] Replace Window virtual functions with explicit desired state, its application, actual state, its feedback [UI] GTK: Apply the initial size to the drawing area [UI] Limit internal UI frame rate to that of the monitor [UI] Hide the cursor using a timer instead of polling due to no repeated UI thread paints with GPU CP thread presentation, and only within the window
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -86,6 +86,10 @@ class ConfigVar : public CommandVar<T>, virtual public IConfigVar {
|
||||
void LoadGameConfigValue(std::shared_ptr<cpptoml::base> result) override;
|
||||
void SetConfigValue(T val);
|
||||
void SetGameConfigValue(T val);
|
||||
// Changes the actual value used to the one specified, and also makes it the
|
||||
// one that will be stored when the global config is written next time. After
|
||||
// overriding, however, the next game config loaded may still change it.
|
||||
void OverrideConfigValue(T val);
|
||||
|
||||
private:
|
||||
std::string category_;
|
||||
@@ -260,6 +264,16 @@ void ConfigVar<T>::SetGameConfigValue(T val) {
|
||||
UpdateValue();
|
||||
}
|
||||
template <class T>
|
||||
void ConfigVar<T>::OverrideConfigValue(T val) {
|
||||
config_value_ = std::make_unique<T>(val);
|
||||
// The user explicitly changes the value at runtime and wants it to take
|
||||
// effect immediately. Drop everything with a higher priority. The next game
|
||||
// config load, however, may still change it.
|
||||
game_config_value_.reset();
|
||||
this->commandline_value_.reset();
|
||||
UpdateValue();
|
||||
}
|
||||
template <class T>
|
||||
void ConfigVar<T>::ResetConfigValueToDefault() {
|
||||
SetConfigValue(this->default_value_);
|
||||
}
|
||||
@@ -373,6 +387,28 @@ ICommandVar* define_cmdvar(const char* name, T* default_value,
|
||||
extern type name; \
|
||||
}
|
||||
|
||||
#define ACCESS_CVar(name) (*cv::cv_##name)
|
||||
|
||||
// dynamic_cast is needed because of virtual inheritance.
|
||||
#define OVERRIDE_CVar(name, type, value) \
|
||||
dynamic_cast<cvar::ConfigVar<type>*>(&ACCESS_CVar(name)) \
|
||||
->OverrideConfigValue(value);
|
||||
|
||||
#define OVERRIDE_bool(name, value) OVERRIDE_CVar(name, bool, value)
|
||||
|
||||
#define OVERRIDE_int32(name, value) OVERRIDE_CVar(name, int32_t, value)
|
||||
|
||||
#define OVERRIDE_uint32(name, value) OVERRIDE_CVar(name, uint32_t, value)
|
||||
|
||||
#define OVERRIDE_uint64(name, value) OVERRIDE_CVar(name, uint64_t, value)
|
||||
|
||||
#define OVERRIDE_double(name, value) OVERRIDE_CVar(name, double, value)
|
||||
|
||||
#define OVERRIDE_string(name, value) OVERRIDE_CVar(name, std::string, value)
|
||||
|
||||
#define OVERRIDE_path(name, value) \
|
||||
OVERRIDE_CVar(name, std::filesystem::path, value)
|
||||
|
||||
// Interface for changing the default value of a variable with auto-upgrading of
|
||||
// users' configs (to distinguish between a leftover old default and an explicit
|
||||
// override), without having to rename the variable.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -41,8 +41,8 @@ std::filesystem::path GetUserFolder();
|
||||
// attempting to create it.
|
||||
bool CreateParentFolder(const std::filesystem::path& path);
|
||||
|
||||
// Creates an empty file at the given path.
|
||||
bool CreateFile(const std::filesystem::path& path);
|
||||
// Creates an empty file at the given path, overwriting if it exists.
|
||||
bool CreateEmptyFile(const std::filesystem::path& path);
|
||||
|
||||
// Opens the file at the given path with the specified mode.
|
||||
// This behaves like fopen and the returned handle can be used with stdio.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -122,7 +122,7 @@ static uint64_t convertUnixtimeToWinFiletime(time_t unixtime) {
|
||||
return filetime;
|
||||
}
|
||||
|
||||
bool CreateFile(const std::filesystem::path& path) {
|
||||
bool CreateEmptyFile(const std::filesystem::path& path) {
|
||||
int file = creat(path.c_str(), 0774);
|
||||
if (file >= 0) {
|
||||
close(file);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -60,7 +60,7 @@ std::filesystem::path GetUserFolder() {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CreateFile(const std::filesystem::path& path) {
|
||||
bool CreateEmptyFile(const std::filesystem::path& path) {
|
||||
auto handle = CreateFileW(path.c_str(), 0, 0, nullptr, CREATE_ALWAYS,
|
||||
FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (handle == INVALID_HANDLE_VALUE) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2021 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -19,37 +19,64 @@
|
||||
// Autogenerated by `xb premake`.
|
||||
#include "build/version.h"
|
||||
|
||||
// For RequestHighPerformance.
|
||||
// For RequestWin32MMCSS.
|
||||
#include <dwmapi.h>
|
||||
// For RequestWin32HighResolutionTimer.
|
||||
#include <winternl.h>
|
||||
|
||||
DEFINE_bool(win32_high_freq, true,
|
||||
"Requests high performance from the NT kernel", "Kernel");
|
||||
DEFINE_bool(win32_high_resolution_timer, true,
|
||||
"Requests high-resolution timer from the NT kernel", "Win32");
|
||||
DEFINE_bool(
|
||||
win32_mmcss, true,
|
||||
"Opt in the Multimedia Class Scheduler Service (MMCSS) scheduling for "
|
||||
"prioritized access to CPU resources",
|
||||
"Win32");
|
||||
|
||||
namespace xe {
|
||||
|
||||
static void RequestHighPerformance() {
|
||||
#if XE_PLATFORM_WIN32
|
||||
NTSTATUS(*NtQueryTimerResolution)
|
||||
(OUT PULONG MinimumResolution, OUT PULONG MaximumResolution,
|
||||
OUT PULONG CurrentResolution);
|
||||
static void RequestWin32HighResolutionTimer() {
|
||||
HMODULE ntdll_module = GetModuleHandleW(L"ntdll.dll");
|
||||
if (!ntdll_module) {
|
||||
return;
|
||||
}
|
||||
|
||||
NTSTATUS(*NtSetTimerResolution)
|
||||
(IN ULONG DesiredResolution, IN BOOLEAN SetResolution,
|
||||
OUT PULONG CurrentResolution);
|
||||
|
||||
NtQueryTimerResolution = (decltype(NtQueryTimerResolution))GetProcAddress(
|
||||
GetModuleHandleW(L"ntdll.dll"), "NtQueryTimerResolution");
|
||||
NtSetTimerResolution = (decltype(NtSetTimerResolution))GetProcAddress(
|
||||
GetModuleHandleW(L"ntdll.dll"), "NtSetTimerResolution");
|
||||
if (!NtQueryTimerResolution || !NtSetTimerResolution) {
|
||||
// clang-format off
|
||||
NTSTATUS (NTAPI* nt_query_timer_resolution)(OUT PULONG MinimumResolution,
|
||||
OUT PULONG MaximumResolution,
|
||||
OUT PULONG CurrentResolution);
|
||||
NTSTATUS (NTAPI* nt_set_timer_resolution)(IN ULONG DesiredResolution,
|
||||
IN BOOLEAN SetResolution,
|
||||
OUT PULONG CurrentResolution);
|
||||
// clang-format on
|
||||
nt_query_timer_resolution =
|
||||
reinterpret_cast<decltype(nt_query_timer_resolution)>(
|
||||
GetProcAddress(ntdll_module, "NtQueryTimerResolution"));
|
||||
nt_set_timer_resolution = reinterpret_cast<decltype(nt_set_timer_resolution)>(
|
||||
GetProcAddress(ntdll_module, "NtSetTimerResolution"));
|
||||
if (!nt_query_timer_resolution || !nt_set_timer_resolution) {
|
||||
return;
|
||||
}
|
||||
|
||||
ULONG minimum_resolution, maximum_resolution, current_resolution;
|
||||
NtQueryTimerResolution(&minimum_resolution, &maximum_resolution,
|
||||
¤t_resolution);
|
||||
NtSetTimerResolution(maximum_resolution, TRUE, ¤t_resolution);
|
||||
#endif
|
||||
nt_query_timer_resolution(&minimum_resolution, &maximum_resolution,
|
||||
¤t_resolution);
|
||||
nt_set_timer_resolution(maximum_resolution, TRUE, ¤t_resolution);
|
||||
}
|
||||
|
||||
static void RequestWin32MMCSS() {
|
||||
HMODULE dwmapi_module = LoadLibraryW(L"dwmapi.dll");
|
||||
if (!dwmapi_module) {
|
||||
return;
|
||||
}
|
||||
// clang-format off
|
||||
HRESULT (STDAPICALLTYPE* dwm_enable_mmcss)(BOOL fEnableMMCSS);
|
||||
// clang-format on
|
||||
dwm_enable_mmcss = reinterpret_cast<decltype(dwm_enable_mmcss)>(
|
||||
GetProcAddress(dwmapi_module, "DwmEnableMMCSS"));
|
||||
if (dwm_enable_mmcss) {
|
||||
dwm_enable_mmcss(TRUE);
|
||||
}
|
||||
FreeLibrary(dwmapi_module);
|
||||
}
|
||||
|
||||
bool ParseWin32LaunchArguments(
|
||||
@@ -103,9 +130,12 @@ int InitializeWin32App(const std::string_view app_name) {
|
||||
#endif
|
||||
XE_BUILD_BRANCH "@" XE_BUILD_COMMIT_SHORT " on " XE_BUILD_DATE);
|
||||
|
||||
// Request high performance timing.
|
||||
if (cvars::win32_high_freq) {
|
||||
RequestHighPerformance();
|
||||
// Request high-performance timing and scheduling.
|
||||
if (cvars::win32_high_resolution_timer) {
|
||||
RequestWin32HighResolutionTimer();
|
||||
}
|
||||
if (cvars::win32_mmcss) {
|
||||
RequestWin32MMCSS();
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -53,8 +53,11 @@ constexpr T align(T value, T alignment) {
|
||||
|
||||
// Rounds the given number up to the next highest multiple.
|
||||
template <typename T, typename V>
|
||||
constexpr T round_up(T value, V multiple) {
|
||||
return value ? (((value + multiple - 1) / multiple) * multiple) : multiple;
|
||||
constexpr T round_up(T value, V multiple, bool force_non_zero = true) {
|
||||
if (force_non_zero && !value) {
|
||||
return multiple;
|
||||
}
|
||||
return (value + multiple - 1) / multiple * multiple;
|
||||
}
|
||||
|
||||
// Using the same conventions as in shading languages, returning 0 for NaN.
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2014 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
// NOTE: this must be included before microprofile as macro expansion needs
|
||||
@@ -36,6 +37,7 @@
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/cvar.h"
|
||||
#include "xenia/base/profiling.h"
|
||||
#include "xenia/ui/ui_event.h"
|
||||
#include "xenia/ui/virtual_key.h"
|
||||
#include "xenia/ui/window.h"
|
||||
|
||||
@@ -44,21 +46,27 @@
|
||||
#endif // XE_OPTION_PROFILING
|
||||
|
||||
#if XE_OPTION_PROFILING_UI
|
||||
#undef DrawText
|
||||
#include "xenia/ui/microprofile_drawer.h"
|
||||
#endif // XE_OPTION_PROFILING_UI
|
||||
|
||||
DEFINE_bool(profiler_dpi_scaling, false,
|
||||
"Apply window DPI scaling to the profiler.", "UI");
|
||||
DEFINE_bool(show_profiler, false, "Show profiling UI by default.", "UI");
|
||||
|
||||
namespace xe {
|
||||
|
||||
#if XE_OPTION_PROFILING_UI
|
||||
ui::Window* Profiler::window_ = nullptr;
|
||||
std::unique_ptr<ui::MicroprofileDrawer> Profiler::drawer_ = nullptr;
|
||||
#endif // XE_OPTION_PROFILING_UI
|
||||
|
||||
#if XE_OPTION_PROFILING
|
||||
|
||||
Profiler::ProfilerWindowInputListener Profiler::input_listener_;
|
||||
size_t Profiler::z_order_ = 0;
|
||||
ui::Window* Profiler::window_ = nullptr;
|
||||
#if XE_OPTION_PROFILING_UI
|
||||
Profiler::ProfilerUIDrawer Profiler::ui_drawer_;
|
||||
ui::Presenter* Profiler::presenter_ = nullptr;
|
||||
std::unique_ptr<ui::MicroprofileDrawer> Profiler::drawer_;
|
||||
bool Profiler::dpi_scaling_ = false;
|
||||
#endif // XE_OPTION_PROFILING_UI
|
||||
|
||||
bool Profiler::is_enabled() { return true; }
|
||||
|
||||
bool Profiler::is_visible() { return is_enabled() && MicroProfileIsDrawing(); }
|
||||
@@ -79,6 +87,7 @@ void Profiler::Initialize() {
|
||||
g_MicroProfile.nActiveBars |= 0x1 | 0x2;
|
||||
|
||||
#if XE_OPTION_PROFILING_UI
|
||||
dpi_scaling_ = cvars::profiler_dpi_scaling;
|
||||
MicroProfileInitUI();
|
||||
g_MicroProfileUI.bShowSpikes = true;
|
||||
g_MicroProfileUI.nOpacityBackground = 0x40u << 24;
|
||||
@@ -102,7 +111,7 @@ void Profiler::Dump() {
|
||||
}
|
||||
|
||||
void Profiler::Shutdown() {
|
||||
drawer_.reset();
|
||||
SetUserIO(0, nullptr, nullptr, nullptr);
|
||||
window_ = nullptr;
|
||||
MicroProfileShutdown();
|
||||
}
|
||||
@@ -119,144 +128,208 @@ void Profiler::ThreadEnter(const char* name) {
|
||||
|
||||
void Profiler::ThreadExit() { MicroProfileOnThreadExit(); }
|
||||
|
||||
bool Profiler::OnKeyDown(ui::VirtualKey virtual_key) {
|
||||
void Profiler::ProfilerWindowInputListener::OnKeyDown(ui::KeyEvent& e) {
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
|
||||
switch (virtual_key) {
|
||||
bool handled = true;
|
||||
switch (e.virtual_key()) {
|
||||
case ui::VirtualKey::kOem3: // `
|
||||
MicroProfileTogglePause();
|
||||
return true;
|
||||
break;
|
||||
#if XE_OPTION_PROFILING_UI
|
||||
case ui::VirtualKey::kTab:
|
||||
MicroProfileToggleDisplayMode();
|
||||
return true;
|
||||
ToggleDisplay();
|
||||
break;
|
||||
case ui::VirtualKey::k1:
|
||||
MicroProfileModKey(1);
|
||||
return true;
|
||||
break;
|
||||
#endif // XE_OPTION_PROFILING_UI
|
||||
default:
|
||||
handled = false;
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
if (handled) {
|
||||
e.set_handled(true);
|
||||
}
|
||||
PostInputEvent();
|
||||
}
|
||||
|
||||
bool Profiler::OnKeyUp(ui::VirtualKey virtual_key) {
|
||||
switch (virtual_key) {
|
||||
void Profiler::ProfilerWindowInputListener::OnKeyUp(ui::KeyEvent& e) {
|
||||
bool handled = true;
|
||||
switch (e.virtual_key()) {
|
||||
#if XE_OPTION_PROFILING_UI
|
||||
case ui::VirtualKey::k1:
|
||||
MicroProfileModKey(0);
|
||||
return true;
|
||||
break;
|
||||
#endif // XE_OPTION_PROFILING_UI
|
||||
default:
|
||||
handled = false;
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
if (handled) {
|
||||
e.set_handled(true);
|
||||
}
|
||||
PostInputEvent();
|
||||
}
|
||||
|
||||
#if XE_OPTION_PROFILING_UI
|
||||
|
||||
void Profiler::OnMouseDown(bool left_button, bool right_button) {
|
||||
MicroProfileMouseButton(left_button, right_button);
|
||||
void Profiler::ProfilerWindowInputListener::OnMouseDown(ui::MouseEvent& e) {
|
||||
Profiler::SetMousePosition(e.x(), e.y(), 0);
|
||||
MicroProfileMouseButton(e.button() == ui::MouseEvent::Button::kLeft,
|
||||
e.button() == ui::MouseEvent::Button::kRight);
|
||||
e.set_handled(true);
|
||||
PostInputEvent();
|
||||
}
|
||||
|
||||
void Profiler::OnMouseUp() { MicroProfileMouseButton(0, 0); }
|
||||
|
||||
void Profiler::OnMouseMove(int x, int y) { MicroProfileMousePosition(x, y, 0); }
|
||||
|
||||
void Profiler::OnMouseWheel(int x, int y, int dy) {
|
||||
MicroProfileMousePosition(x, y, dy);
|
||||
void Profiler::ProfilerWindowInputListener::OnMouseUp(ui::MouseEvent& e) {
|
||||
Profiler::SetMousePosition(e.x(), e.y(), 0);
|
||||
MicroProfileMouseButton(0, 0);
|
||||
e.set_handled(true);
|
||||
PostInputEvent();
|
||||
}
|
||||
|
||||
void Profiler::ToggleDisplay() { MicroProfileToggleDisplayMode(); }
|
||||
void Profiler::ProfilerWindowInputListener::OnMouseMove(ui::MouseEvent& e) {
|
||||
Profiler::SetMousePosition(e.x(), e.y(), 0);
|
||||
e.set_handled(true);
|
||||
PostInputEvent();
|
||||
}
|
||||
|
||||
void Profiler::ProfilerWindowInputListener::OnMouseWheel(ui::MouseEvent& e) {
|
||||
Profiler::SetMousePosition(e.x(), e.y(), e.scroll_y());
|
||||
e.set_handled(true);
|
||||
PostInputEvent();
|
||||
}
|
||||
|
||||
void Profiler::TogglePause() { MicroProfileTogglePause(); }
|
||||
|
||||
#else
|
||||
|
||||
void Profiler::OnMouseDown(bool left_button, bool right_button) {}
|
||||
|
||||
void Profiler::OnMouseUp() {}
|
||||
|
||||
void Profiler::OnMouseMove(int x, int y) {}
|
||||
|
||||
void Profiler::OnMouseWheel(int x, int y, int dy) {}
|
||||
|
||||
void Profiler::ToggleDisplay() {}
|
||||
|
||||
void Profiler::TogglePause() {}
|
||||
|
||||
#endif // XE_OPTION_PROFILING_UI
|
||||
|
||||
void Profiler::set_window(ui::Window* window) {
|
||||
assert_null(window_);
|
||||
void Profiler::ToggleDisplay() {
|
||||
bool was_visible = is_visible();
|
||||
MicroProfileToggleDisplayMode();
|
||||
if (is_visible() != was_visible) {
|
||||
if (window_) {
|
||||
if (was_visible) {
|
||||
window_->RemoveInputListener(&input_listener_);
|
||||
} else {
|
||||
window_->AddInputListener(&input_listener_, z_order_);
|
||||
}
|
||||
}
|
||||
#if XE_OPTION_PROFILING_UI
|
||||
if (presenter_) {
|
||||
if (was_visible) {
|
||||
presenter_->RemoveUIDrawerFromUIThread(&ui_drawer_);
|
||||
} else {
|
||||
presenter_->AddUIDrawerFromUIThread(&ui_drawer_, z_order_);
|
||||
}
|
||||
}
|
||||
#endif // XE_OPTION_PROFILING_UI
|
||||
}
|
||||
}
|
||||
|
||||
void Profiler::SetUserIO(size_t z_order, ui::Window* window,
|
||||
ui::Presenter* presenter,
|
||||
ui::ImmediateDrawer* immediate_drawer) {
|
||||
#if XE_OPTION_PROFILING_UI
|
||||
if (presenter_ && is_visible()) {
|
||||
presenter_->RemoveUIDrawerFromUIThread(&ui_drawer_);
|
||||
}
|
||||
drawer_.reset();
|
||||
presenter_ = nullptr;
|
||||
#endif // XE_OPTION_PROFILING_UI
|
||||
|
||||
if (window_) {
|
||||
if (is_visible()) {
|
||||
window_->RemoveInputListener(&input_listener_);
|
||||
}
|
||||
window_ = nullptr;
|
||||
}
|
||||
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
|
||||
z_order_ = z_order;
|
||||
window_ = window;
|
||||
drawer_ = std::make_unique<ui::MicroprofileDrawer>(window);
|
||||
|
||||
window_->on_painted.AddListener([](ui::UIEvent* e) { Profiler::Present(); });
|
||||
#if XE_OPTION_PROFILING_UI
|
||||
if (presenter && immediate_drawer) {
|
||||
presenter_ = presenter;
|
||||
drawer_ = std::make_unique<ui::MicroprofileDrawer>(immediate_drawer);
|
||||
}
|
||||
#endif // XE_OPTION_PROFILING_UI
|
||||
|
||||
// Pass through mouse events.
|
||||
window_->on_mouse_down.AddListener([](ui::MouseEvent* e) {
|
||||
if (Profiler::is_visible()) {
|
||||
Profiler::OnMouseDown(e->button() == ui::MouseEvent::Button::kLeft,
|
||||
e->button() == ui::MouseEvent::Button::kRight);
|
||||
e->set_handled(true);
|
||||
window_->Invalidate();
|
||||
if (is_visible()) {
|
||||
window_->AddInputListener(&input_listener_, z_order_);
|
||||
#if XE_OPTION_PROFILING_UI
|
||||
if (presenter_) {
|
||||
presenter_->AddUIDrawerFromUIThread(&ui_drawer_, z_order_);
|
||||
}
|
||||
});
|
||||
window_->on_mouse_up.AddListener([](ui::MouseEvent* e) {
|
||||
if (Profiler::is_visible()) {
|
||||
Profiler::OnMouseUp();
|
||||
e->set_handled(true);
|
||||
window_->Invalidate();
|
||||
}
|
||||
});
|
||||
window_->on_mouse_move.AddListener([](ui::MouseEvent* e) {
|
||||
if (Profiler::is_visible()) {
|
||||
Profiler::OnMouseMove(e->x(), e->y());
|
||||
e->set_handled(true);
|
||||
window_->Invalidate();
|
||||
}
|
||||
});
|
||||
window_->on_mouse_wheel.AddListener([](ui::MouseEvent* e) {
|
||||
if (Profiler::is_visible()) {
|
||||
Profiler::OnMouseWheel(e->x(), e->y(), -e->dy());
|
||||
e->set_handled(true);
|
||||
window_->Invalidate();
|
||||
}
|
||||
});
|
||||
|
||||
// Watch for toggle/mode keys and such.
|
||||
window_->on_key_down.AddListener([](ui::KeyEvent* e) {
|
||||
if (Profiler::is_visible()) {
|
||||
Profiler::OnKeyDown(e->virtual_key());
|
||||
e->set_handled(true);
|
||||
window_->Invalidate();
|
||||
}
|
||||
});
|
||||
window_->on_key_up.AddListener([](ui::KeyEvent* e) {
|
||||
if (Profiler::is_visible()) {
|
||||
Profiler::OnKeyUp(e->virtual_key());
|
||||
e->set_handled(true);
|
||||
window_->Invalidate();
|
||||
}
|
||||
});
|
||||
#endif // XE_OPTION_PROFILING_UI
|
||||
}
|
||||
}
|
||||
|
||||
void Profiler::Flip() {
|
||||
MicroProfileFlip();
|
||||
// This can be called from non-UI threads, so not trying to access the drawer
|
||||
// to trigger redraw here as it's owned and managed exclusively by the UI
|
||||
// thread. Relying on continuous painting currently.
|
||||
}
|
||||
|
||||
void Profiler::Present() {
|
||||
SCOPE_profile_cpu_f("internal");
|
||||
#if XE_OPTION_PROFILING_UI
|
||||
if (!window_ || !drawer_) {
|
||||
void Profiler::ProfilerUIDrawer::Draw(ui::UIDrawContext& ui_draw_context) {
|
||||
if (!window_ || !presenter_ || !drawer_) {
|
||||
return;
|
||||
}
|
||||
drawer_->Begin();
|
||||
MicroProfileDraw(window_->scaled_width(), window_->scaled_height());
|
||||
SCOPE_profile_cpu_f("internal");
|
||||
uint32_t coordinate_space_width = dpi_scaling_
|
||||
? window_->GetActualLogicalWidth()
|
||||
: window_->GetActualPhysicalWidth();
|
||||
uint32_t coordinate_space_height = dpi_scaling_
|
||||
? window_->GetActualLogicalHeight()
|
||||
: window_->GetActualPhysicalHeight();
|
||||
drawer_->Begin(ui_draw_context, coordinate_space_width,
|
||||
coordinate_space_height);
|
||||
MicroProfileDraw(coordinate_space_width, coordinate_space_height);
|
||||
drawer_->End();
|
||||
#endif // XE_OPTION_PROFILING_UI
|
||||
// Continuous repaint.
|
||||
if (is_visible()) {
|
||||
presenter_->RequestUIPaintFromUIThread();
|
||||
}
|
||||
}
|
||||
#endif // XE_OPTION_PROFILING_UI
|
||||
|
||||
void Profiler::Flip() { MicroProfileFlip(); }
|
||||
#if XE_OPTION_PROFILING_UI
|
||||
void Profiler::SetMousePosition(int32_t x, int32_t y, int32_t wheel_delta) {
|
||||
if (!window_) {
|
||||
return;
|
||||
}
|
||||
if (dpi_scaling_) {
|
||||
x = window_->PositionToLogical(x);
|
||||
y = window_->PositionToLogical(y);
|
||||
}
|
||||
MicroProfileMousePosition(uint32_t(std::max(int32_t(0), x)),
|
||||
uint32_t(std::max(int32_t(0), y)), wheel_delta);
|
||||
}
|
||||
#endif // XE_OPTION_PROFILING_UI
|
||||
|
||||
void Profiler::PostInputEvent() {
|
||||
// The profiler can be hidden from within the profiler (Mode > Off).
|
||||
if (!is_visible()) {
|
||||
window_->RemoveInputListener(&input_listener_);
|
||||
#if XE_OPTION_PROFILING_UI
|
||||
if (presenter_) {
|
||||
presenter_->RemoveUIDrawerFromUIThread(&ui_drawer_);
|
||||
}
|
||||
#endif // XE_OPTION_PROFILING_UI
|
||||
return;
|
||||
}
|
||||
// Relying on continuous painting currently, no need to request drawing.
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
@@ -268,16 +341,11 @@ void Profiler::Shutdown() {}
|
||||
uint32_t Profiler::GetColor(const char* str) { return 0; }
|
||||
void Profiler::ThreadEnter(const char* name) {}
|
||||
void Profiler::ThreadExit() {}
|
||||
bool Profiler::OnKeyDown(ui::VirtualKey virtual_key) { return false; }
|
||||
bool Profiler::OnKeyUp(ui::VirtualKey virtual_key) { return false; }
|
||||
void Profiler::OnMouseDown(bool left_button, bool right_button) {}
|
||||
void Profiler::OnMouseUp() {}
|
||||
void Profiler::OnMouseMove(int x, int y) {}
|
||||
void Profiler::OnMouseWheel(int x, int y, int dy) {}
|
||||
void Profiler::ToggleDisplay() {}
|
||||
void Profiler::TogglePause() {}
|
||||
void Profiler::set_window(ui::Window* window) {}
|
||||
void Profiler::Present() {}
|
||||
void Profiler::SetUserIO(size_t z_order, ui::Window* window,
|
||||
ui::Presenter* presenter,
|
||||
ui::ImmediateDrawer* immediate_drawer) {}
|
||||
void Profiler::Flip() {}
|
||||
|
||||
#endif // XE_OPTION_PROFILING
|
||||
@@ -316,7 +384,7 @@ void MicroProfileDrawText(int nX, int nY, uint32_t nColor, const char* pText,
|
||||
if (!drawer) {
|
||||
return;
|
||||
}
|
||||
drawer->DrawText(nX, nY, nColor, pText, nLen);
|
||||
drawer->DrawTextString(nX, nY, nColor, pText, nLen);
|
||||
}
|
||||
|
||||
#endif // XE_OPTION_PROFILING_UI
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2014 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -10,10 +10,15 @@
|
||||
#ifndef XENIA_BASE_PROFILING_H_
|
||||
#define XENIA_BASE_PROFILING_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#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"
|
||||
|
||||
#if XE_PLATFORM_WIN32
|
||||
#define XE_OPTION_PROFILING 1
|
||||
@@ -30,7 +35,9 @@
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
class ImmediateDrawer;
|
||||
class MicroprofileDrawer;
|
||||
class Presenter;
|
||||
class Window;
|
||||
} // namespace ui
|
||||
} // namespace xe
|
||||
@@ -172,27 +179,65 @@ class Profiler {
|
||||
// Deactivates the calling thread for profiling.
|
||||
static void ThreadExit();
|
||||
|
||||
static bool OnKeyDown(ui::VirtualKey virtual_key);
|
||||
static bool OnKeyUp(ui::VirtualKey virtual_key);
|
||||
static void OnMouseDown(bool left_button, bool right_button);
|
||||
static void OnMouseUp();
|
||||
static void OnMouseMove(int x, int y);
|
||||
static void OnMouseWheel(int x, int y, int dy);
|
||||
static void ToggleDisplay();
|
||||
static void TogglePause();
|
||||
|
||||
// Initializes input and drawing with the given display.
|
||||
static void set_window(ui::Window* window);
|
||||
// Gets the current display, if any.
|
||||
static ui::MicroprofileDrawer* drawer() { return drawer_.get(); }
|
||||
// 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();
|
||||
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<ui::MicroprofileDrawer> drawer_;
|
||||
static bool dpi_scaling_;
|
||||
#endif // XE_OPTION_PROFILING_UI
|
||||
#endif // XE_OPTION_PROFILING
|
||||
};
|
||||
|
||||
} // namespace xe
|
||||
|
||||
Reference in New Issue
Block a user