rsx/overlays: Putting it all together

- Migrate dialogs with input-only threads to use the unified input
  system
This commit is contained in:
kd-11 2023-02-13 21:57:38 +03:00 committed by kd-11
parent 12690de9de
commit ddc9e74aa8
22 changed files with 205 additions and 331 deletions

View File

@ -5,6 +5,8 @@
#include "Emu/Cell/lv2/sys_sync.h"
#include "Emu/Cell/timers.hpp"
#include "Emu/Io/interception.h"
#include "Emu/RSX/Overlays/overlay_manager.h"
#include "Emu/RSX/Overlays/overlay_message_dialog.h"
#include "cellSysutil.h"

View File

@ -4,9 +4,11 @@
#include "Emu/Cell/PPUModule.h"
#include "Emu/Io/interception.h"
#include "Emu/Io/Keyboard.h"
#include "Emu/RSX/Overlays/overlay_osk.h"
#include "Emu/IdManager.h"
#include "Emu/RSX/Overlays/overlay_manager.h"
#include "Emu/RSX/Overlays/overlay_osk.h"
#include "cellSysutil.h"
#include "cellOskDialog.h"
#include "cellMsgDialog.h"

View File

@ -4,6 +4,8 @@
#include "Emu/VFS.h"
#include "Emu/IdManager.h"
#include "Emu/Cell/PPUModule.h"
#include "Emu/RSX/Overlays/overlay_manager.h"
#include "Emu/RSX/Overlays/overlay_user_list_dialog.h"
#include "cellUserInfo.h"

View File

@ -1,6 +1,7 @@
#include "stdafx.h"
#include "GLGSRender.h"
#include "Emu/Cell/Modules/cellVideoOut.h"
#include "Emu/RSX/Overlays/overlay_manager.h"
#include "util/video_provider.h"
LOG_CHANNEL(screenshot_log, "SCREENSHOT");

View File

@ -1,5 +1,6 @@
#include "stdafx.h"
#include "overlay_home_menu.h"
#include "../overlay_manager.h"
#include "Emu/RSX/RSXThread.h"
#include "Utilities/date_time.h"
@ -165,31 +166,24 @@ namespace rsx
this->on_close = std::move(on_close);
visible = true;
auto& list_thread = g_fxo->get<named_thread<home_menu_dialog_thread>>();
const auto notify = std::make_shared<atomic_t<bool>>(false);
auto& overlayman = g_fxo->get<display_manager>();
list_thread([&, notify]()
{
const u64 tbit = alloc_thread_bit();
g_thread_bit = tbit;
*notify = true;
notify->notify_one();
auto ref = g_fxo->get<display_manager>().get(uid);
if (const auto error = run_input_loop())
overlayman.attach_thread_input(
uid, // Target
[](s32 error) // What to do with the result
{
if (error != selection_code::canceled)
if (error && error != selection_code::canceled)
{
rsx_log.error("Home menu dialog input loop exited with error code=%d", error);
}
},
[&notify]() // What to do before starting the loop
{
*notify = true;
notify->notify_one();
}
thread_bits &= ~tbit;
thread_bits.notify_all();
});
);
if (g_cfg.misc.pause_during_home_menu)
{
@ -199,7 +193,7 @@ namespace rsx
});
}
while (list_thread < thread_state::errored && !*notify)
while (!Emu.IsStopped() && !*notify)
{
notify->wait(false, atomic_wait_timeout{1'000'000});
}

View File

@ -1,5 +1,6 @@
#include "stdafx.h"
#include "shader_loading_dialog_native.h"
#include "../overlay_manager.h"
#include "../overlay_message_dialog.h"
#include "../../GSRender.h"
#include "Emu/Cell/ErrorCodes.h"

View File

@ -1,5 +1,7 @@
#include "stdafx.h"
#include "overlay_cursor.h"
#include "overlay_manager.h"
#include "Emu/RSX/RSXThread.h"
namespace rsx

View File

@ -1,10 +1,23 @@
#include "stdafx.h"
#include "overlay_manager.h"
#include "Emu/System.h"
namespace rsx
{
namespace overlays
{
display_manager::~display_manager()
{
if (m_input_thread)
{
m_input_thread_abort.store(true);
while (*m_input_thread <= thread_state::aborting)
{
_mm_pause();
}
}
}
void display_manager::lock()
{
m_list_mutex.lock_shared();
@ -123,8 +136,29 @@ namespace rsx
{
if (auto iface = std::dynamic_pointer_cast<user_interface>(item))
{
// Kick input thread if not enabled. Expect the interface to attach shortly
std::lock_guard lock(m_input_thread_lock);
m_input_token_stack.emplace_front(std::move(iface));
if (!m_input_thread)
{
m_input_thread = std::make_shared<named_thread<overlay_input_thread>>();
(*m_input_thread)([this]()
{
input_thread_loop();
});
}
}
}
void display_manager::attach_thread_input(
u32 uid,
std::function<void(s32)> on_input_loop_exit,
std::function<void()> on_input_loop_enter)
{
if (auto iface = std::dynamic_pointer_cast<user_interface>(get(uid)))
{
std::lock_guard lock(m_input_thread_lock);
m_input_token_stack.emplace_front(std::move(iface), on_input_loop_enter, on_input_loop_exit);
}
}
@ -153,5 +187,40 @@ namespace rsx
m_input_token_stack.pop_front();
}
}
void display_manager::input_thread_loop()
{
while (!m_input_thread_abort)
{
input_thread_context_t input_context;
{
reader_lock lock(m_input_thread_lock);
if (!m_input_token_stack.empty())
{
input_context = m_input_token_stack.front();
m_input_token_stack.pop_front();
}
}
if (input_context.target)
{
if (input_context.input_loop_prologue)
{
input_context.input_loop_prologue();
}
const auto result = input_context.target->run_input_loop();
if (input_context.input_loop_epilogue)
{
input_context.input_loop_epilogue(result);
}
}
else
{
thread_ctrl::wait_for(1000);
}
}
}
}
}

View File

@ -4,6 +4,7 @@
#include "Emu/IdManager.h"
#include "Utilities/mutex.h"
#include "Utilities/Thread.h"
#include "Utilities/Timer.h"
#include <deque>
@ -41,6 +42,8 @@ namespace rsx
explicit display_manager(int) noexcept
{}
~display_manager();
// Adds an object to the internal list. Optionally removes other objects of the same type.
// Original handle loses ownership but a usable pointer is returned
template <typename T>
@ -152,19 +155,31 @@ namespace rsx
// Release read-only lock (BasicLockable). May perform internal cleanup before returning
void unlock();
// Enable input thread attach to the specified interface
void attach_thread_input(
u32 uid, // The input target
std::function<void(s32)> on_input_loop_exit = nullptr, // [optional] What to do with the result if any
std::function<void()> on_input_loop_enter = nullptr); // [optional] What to do before running the input routine
private:
struct overlay_input_thread
{
static constexpr auto thread_name = "Overlay Input Thread"sv;
};
struct input_thread_access_token
struct input_thread_context_t
{
std::shared_ptr<user_interface> target;
std::function<void()> input_loop_prologue = nullptr;
std::function<void(s32)> input_loop_epilogue = nullptr;
};
std::deque<input_thread_access_token> m_input_token_stack;
std::deque<input_thread_context_t> m_input_token_stack;
shared_mutex m_input_thread_lock;
atomic_t<bool> m_input_thread_abort = false;
std::shared_ptr<named_thread<overlay_input_thread>> m_input_thread;
void input_thread_loop();
};
}
}

View File

@ -1,10 +1,12 @@
#include "stdafx.h"
#include "overlays.h"
#include "overlay_manager.h"
#include "overlay_media_list_dialog.h"
#include "Emu/Cell/Modules/cellMusic.h"
#include "Emu/VFS.h"
#include "Utilities/StrUtil.h"
#include "Utilities/Thread.h"
#include "overlays.h"
#include "Emu/VFS.h"
#include "Emu/Cell/Modules/cellMusic.h"
namespace rsx
{

View File

@ -1,6 +1,8 @@
#pragma once
#include "overlays.h"
#include "overlay_manager.h"
#include <deque>
namespace rsx

View File

@ -1,4 +1,5 @@
#include "stdafx.h"
#include "overlay_manager.h"
#include "overlay_message_dialog.h"
#include "Emu/System.h"
#include "Emu/system_config.h"
@ -299,52 +300,42 @@ namespace rsx
{
if (!m_stop_input_loop)
{
auto& dlg_thread = g_fxo->get<named_thread<msg_dialog_thread>>();
const auto notify = std::make_shared<atomic_t<bool>>(false);
auto& overlayman = g_fxo->get<display_manager>();
dlg_thread([&, notify]()
overlayman.attach_thread_input(
uid,
[](s32 error)
{
if (error && error != selection_code::canceled)
{
rsx_log.error("Message dialog input loop exited with error code=%d", error);
}
},
[&notify]()
{
*notify = true;
notify->notify_one();
}
);
#if 0
while (!m_stop_input_loop && thread_ctrl::state() != thread_state::aborting)
{
const u64 tbit = alloc_thread_bit();
g_thread_bit = tbit;
refresh();
*notify = true;
notify->notify_one();
// Only update the screen at about 60fps since updating it everytime slows down the process
std::this_thread::sleep_for(16ms);
if (interactive)
if (!g_fxo->is_init<display_manager>())
{
auto ref = g_fxo->get<display_manager>().get(uid);
if (const auto error = run_input_loop())
{
if (error != selection_code::canceled)
{
rsx_log.error("Message dialog input loop exited with error code=%d", error);
}
}
rsx_log.fatal("display_manager was improperly destroyed");
break;
}
else
{
while (!m_stop_input_loop && thread_ctrl::state() != thread_state::aborting)
{
refresh();
}
#endif
// Only update the screen at about 60fps since updating it everytime slows down the process
std::this_thread::sleep_for(16ms);
if (!g_fxo->is_init<display_manager>())
{
rsx_log.fatal("display_manager was improperly destroyed");
break;
}
}
}
thread_bits &= ~tbit;
thread_bits.notify_all();
});
while (dlg_thread < thread_state::errored && !*notify)
while (!Emu.IsStopped() && !*notify)
{
notify->wait(false, atomic_wait_timeout{1'000'000});
}

View File

@ -1,4 +1,5 @@
#include "stdafx.h"
#include "overlay_manager.h"
#include "overlay_osk.h"
#include "Emu/RSX/RSXThread.h"
#include "Emu/Cell/Modules/cellSysutil.h"
@ -1624,31 +1625,26 @@ namespace rsx
update_panel();
auto& osk_thread = g_fxo->get<named_thread<osk_dialog_thread>>();
const auto notify = std::make_shared<atomic_t<bool>>(false);
auto& overlayman = g_fxo->get<display_manager>();
osk_thread([&, notify]()
{
const u64 tbit = alloc_thread_bit();
g_thread_bit = tbit;
*notify = true;
notify->notify_one();
if (const auto error = run_input_loop())
overlayman.attach_thread_input(
uid,
[](s32 error)
{
if (error != selection_code::canceled)
if (error && error != selection_code::canceled)
{
rsx_log.error("Osk input loop exited with error code=%d", error);
}
},
[&notify]()
{
*notify = true;
notify->notify_one();
}
);
thread_bits &= ~tbit;
thread_bits.notify_all();
});
while (osk_thread < thread_state::errored && !*notify)
while (!Emu.IsStopped() && !*notify)
{
notify->wait(false, atomic_wait_timeout{1'000'000});
}

View File

@ -1,4 +1,5 @@
#include "stdafx.h"
#include "overlay_manager.h"
#include "overlay_perf_metrics.h"
#include "Emu/RSX/RSXThread.h"
#include "Emu/Cell/SPUThread.h"

View File

@ -1,4 +1,5 @@
#include "stdafx.h"
#include "overlay_manager.h"
#include "overlay_user_list_dialog.h"
#include "Emu/vfs_config.h"
#include "Emu/system_utils.hpp"
@ -243,33 +244,26 @@ namespace rsx
this->on_close = std::move(on_close);
visible = true;
auto& list_thread = g_fxo->get<named_thread<user_list_dialog_thread>>();
const auto notify = std::make_shared<atomic_t<bool>>(false);
auto& overlayman = g_fxo->get<display_manager>();
list_thread([&, notify]()
{
const u64 tbit = alloc_thread_bit();
g_thread_bit = tbit;
*notify = true;
notify->notify_one();
auto ref = g_fxo->get<display_manager>().get(uid);
if (const auto error = run_input_loop())
overlayman.attach_thread_input(
uid,
[](s32 error)
{
if (error != selection_code::canceled)
if (error && error != selection_code::canceled)
{
rsx_log.error("User list dialog input loop exited with error code=%d", error);
}
},
[&notify]()
{
*notify = true;
notify->notify_one();
}
);
thread_bits &= ~tbit;
thread_bits.notify_all();
});
while (list_thread < thread_state::errored && !*notify)
while (!Emu.IsStopped() && !*notify)
{
notify->wait(false, atomic_wait_timeout{1'000'000});
}

View File

@ -1,5 +1,6 @@
#include "stdafx.h"
#include "overlays.h"
#include "overlay_manager.h"
#include "overlay_message_dialog.h"
#include "Input/pad_thread.h"
#include "Emu/Io/interception.h"
@ -47,6 +48,8 @@ namespace rsx
s32 user_interface::run_input_loop()
{
user_interface::thread_bits_allocator thread_bits_alloc(this);
m_interactive = true;
std::array<steady_clock::time_point, CELL_PAD_MAX_PORT_NUM> timestamp;

View File

@ -123,6 +123,26 @@ namespace rsx
std::function<void(s32 status)> on_close = nullptr;
class thread_bits_allocator
{
public:
thread_bits_allocator(user_interface* parent)
: m_parent(parent)
{
m_thread_bit = m_parent->alloc_thread_bit();
g_thread_bit = m_thread_bit;
}
~thread_bits_allocator()
{
m_parent->thread_bits &= ~m_thread_bit;
m_parent->thread_bits.notify_all();
}
private:
user_interface* m_parent;
u64 m_thread_bit;
};
public:
s32 return_code = 0; // CELL_OK
@ -137,236 +157,5 @@ namespace rsx
s32 run_input_loop();
};
class display_manager
{
private:
atomic_t<u32> m_uid_ctr = 0;
std::vector<std::shared_ptr<overlay>> m_iface_list;
std::vector<std::shared_ptr<overlay>> m_dirty_list;
shared_mutex m_list_mutex;
std::vector<u32> m_uids_to_remove;
std::vector<u32> m_type_ids_to_remove;
bool remove_type(u32 type_id)
{
bool found = false;
for (auto It = m_iface_list.begin(); It != m_iface_list.end();)
{
if (It->get()->type_index == type_id)
{
m_dirty_list.push_back(std::move(*It));
It = m_iface_list.erase(It);
found = true;
}
else
{
++It;
}
}
return found;
}
bool remove_uid(u32 uid)
{
for (auto It = m_iface_list.begin(); It != m_iface_list.end(); It++)
{
const auto e = It->get();
if (e->uid == uid)
{
m_dirty_list.push_back(std::move(*It));
m_iface_list.erase(It);
return true;
}
}
return false;
}
void cleanup_internal()
{
for (const auto &uid : m_uids_to_remove)
{
remove_uid(uid);
}
for (const auto &type_id : m_type_ids_to_remove)
{
remove_type(type_id);
}
m_uids_to_remove.clear();
m_type_ids_to_remove.clear();
}
public:
// Disable default construction to make it conditionally available in g_fxo
explicit display_manager(int) noexcept
{
}
// Adds an object to the internal list. Optionally removes other objects of the same type.
// Original handle loses ownership but a usable pointer is returned
template <typename T>
std::shared_ptr<T> add(std::shared_ptr<T>& entry, bool remove_existing = true)
{
std::lock_guard lock(m_list_mutex);
entry->uid = m_uid_ctr.fetch_add(1);
entry->type_index = id_manager::typeinfo::get_index<T>();
if (remove_existing)
{
for (auto It = m_iface_list.begin(); It != m_iface_list.end(); It++)
{
if (It->get()->type_index == entry->type_index)
{
// Replace
m_dirty_list.push_back(std::move(*It));
*It = std::move(entry);
return std::static_pointer_cast<T>(*It);
}
}
}
m_iface_list.push_back(std::move(entry));
return std::static_pointer_cast<T>(m_iface_list.back());
}
// Allocates object and adds to internal list. Returns pointer to created object
template <typename T, typename ...Args>
std::shared_ptr<T> create(Args&&... args)
{
auto object = std::make_shared<T>(std::forward<Args>(args)...);
return add(object);
}
// Removes item from list if it matches the uid
void remove(u32 uid)
{
if (m_list_mutex.try_lock())
{
remove_uid(uid);
m_list_mutex.unlock();
}
else
{
m_uids_to_remove.push_back(uid);
}
}
// Removes all objects of this type from the list
template <typename T>
void remove()
{
const auto type_id = id_manager::typeinfo::get_index<T>();
if (m_list_mutex.try_lock())
{
remove_type(type_id);
m_list_mutex.unlock();
}
else
{
m_type_ids_to_remove.push_back(type_id);
}
}
// True if any visible elements to draw exist
bool has_visible() const
{
return !m_iface_list.empty();
}
// True if any elements have been deleted but their resources may not have been cleaned up
bool has_dirty() const
{
return !m_dirty_list.empty();
}
// Returns current list for reading. Caller must ensure synchronization by first locking the list
const std::vector<std::shared_ptr<overlay>>& get_views() const
{
return m_iface_list;
}
// Returns current list of removed objects not yet deallocated for reading.
// Caller must ensure synchronization by first locking the list
const std::vector<std::shared_ptr<overlay>>& get_dirty() const
{
return m_dirty_list;
}
// Deallocate object. Object must first be removed via the remove() functions
void dispose(const std::vector<u32>& uids)
{
std::lock_guard lock(m_list_mutex);
if (!m_uids_to_remove.empty() || !m_type_ids_to_remove.empty())
{
cleanup_internal();
}
m_dirty_list.erase
(
std::remove_if(m_dirty_list.begin(), m_dirty_list.end(), [&uids](std::shared_ptr<overlay>& e)
{
return std::find(uids.begin(), uids.end(), e->uid) != uids.end();
}),
m_dirty_list.end()
);
}
// Returns pointer to the object matching the given uid
std::shared_ptr<overlay> get(u32 uid)
{
reader_lock lock(m_list_mutex);
for (const auto& iface : m_iface_list)
{
if (iface->uid == uid)
return iface;
}
return {};
}
// Returns pointer to the first object matching the given type
template <typename T>
std::shared_ptr<T> get()
{
reader_lock lock(m_list_mutex);
const auto type_id = id_manager::typeinfo::get_index<T>();
for (const auto& iface : m_iface_list)
{
if (iface->type_index == type_id)
{
return std::static_pointer_cast<T>(iface);
}
}
return {};
}
// Lock for read-only access (BasicLockable)
void lock()
{
m_list_mutex.lock_shared();
}
// Release read-only lock (BasicLockable). May perform internal cleanup before returning
void unlock()
{
m_list_mutex.unlock_shared();
if (!m_uids_to_remove.empty() || !m_type_ids_to_remove.empty())
{
std::lock_guard lock(m_list_mutex);
cleanup_internal();
}
}
};
}
}

View File

@ -1,4 +1,7 @@
#include "stdafx.h"
#include "../Overlays/overlay_animated_icon.h"
#include "../Overlays/overlay_dock.h"
#include "../Overlays/overlay_manager.h"
#include "../Overlays/overlay_shader_compile_notification.h"
#include "../Overlays/Shaders/shader_loading_dialog_native.h"

View File

@ -1,6 +1,7 @@
#include "stdafx.h"
#include "VKGSRender.h"
#include "vkutils/buffer_object.h"
#include "Emu/RSX/Overlays/overlay_manager.h"
#include "Emu/RSX/Overlays/overlays.h"
#include "Emu/Cell/Modules/cellVideoOut.h"

View File

@ -2,6 +2,7 @@
#include "system_progress.hpp"
#include "Emu/Cell/Modules/cellMsgDialog.h"
#include "Emu/RSX/RSXThread.h"
#include "Emu/RSX/Overlays/overlay_manager.h"
#include "Emu/RSX/Overlays/overlay_message_dialog.h"
#include "Emu/System.h"

View File

@ -4,6 +4,7 @@
#include "Emu/System.h"
#include "Emu/IdManager.h"
#include "Emu/Io/interception.h"
#include "../Emu/RSX/Overlays/overlay_manager.h"
#include "Emu/RSX/Overlays/overlay_save_dialog.h"
#include "Emu/Cell/Modules/cellSysutil.h"

View File

@ -3,6 +3,8 @@
#include "../Emu/IdManager.h"
#include "../Emu/System.h"
#include "../Emu/RSX/Overlays/overlay_manager.h"
#include "../Emu/RSX/Overlays/overlay_trophy_notification.h"
#include "Utilities/File.h"