[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:
Triang3l
2022-01-29 13:22:03 +03:00
parent 372bdd3ec9
commit fe3f0f26e4
428 changed files with 75942 additions and 18360 deletions

View File

@@ -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