mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-22 18:53:28 +01:00
vk/windows: Try to keep msq thread from ever stopping
- NVIDIA drivers hook into the msq before our nativeEvent handler. This means NV is aware of events before rpcs3 is aware of them and sometimes stops until a new event is triggered. If rpcs3 is inside a driver call at this time, the system will deadlock since the driver waits for msq which waits for the renderer which waits for the driver. - Use explicit hook management to control window events - Add fence timeout to attempt detection of surface loss events
This commit is contained in:
parent
987b607cb0
commit
3bfa564ef8
@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "Emu/RSX/RSXThread.h"
|
||||
#include <memory>
|
||||
@ -35,13 +35,14 @@ struct RSXDebuggerProgram
|
||||
|
||||
enum wm_event
|
||||
{
|
||||
none, //nothing
|
||||
geometry_change_notice, //about to start resizing and/or moving the window
|
||||
geometry_change_in_progress, //window being resized and/or moved
|
||||
window_resized, //window was resized
|
||||
window_minimized, //window was minimized
|
||||
window_restored, //window was restored from a minimized state
|
||||
window_moved, //window moved without resize
|
||||
none, // nothing
|
||||
toggle_fullscreen, // user is requesting a fullscreen switch
|
||||
geometry_change_notice, // about to start resizing and/or moving the window
|
||||
geometry_change_in_progress, // window being resized and/or moved
|
||||
window_resized, // window was resized
|
||||
window_minimized, // window was minimized
|
||||
window_restored, // window was restored from a minimized state
|
||||
window_moved, // window moved without resize
|
||||
window_visibility_changed
|
||||
};
|
||||
|
||||
@ -62,57 +63,78 @@ using draw_context_t = void*;
|
||||
>;
|
||||
#endif
|
||||
|
||||
class GSFrameBase
|
||||
{
|
||||
public:
|
||||
GSFrameBase() = default;
|
||||
GSFrameBase(const GSFrameBase&) = delete;
|
||||
virtual ~GSFrameBase() {}
|
||||
class GSFrameBase
|
||||
{
|
||||
public:
|
||||
GSFrameBase() = default;
|
||||
GSFrameBase(const GSFrameBase&) = delete;
|
||||
virtual ~GSFrameBase() {}
|
||||
|
||||
virtual void close() = 0;
|
||||
virtual bool shown() = 0;
|
||||
virtual void hide() = 0;
|
||||
virtual void show() = 0;
|
||||
virtual void close() = 0;
|
||||
virtual bool shown() = 0;
|
||||
virtual void hide() = 0;
|
||||
virtual void show() = 0;
|
||||
virtual void toggle_fullscreen() = 0;
|
||||
|
||||
virtual void delete_context(draw_context_t ctx) = 0;
|
||||
virtual draw_context_t make_context() = 0;
|
||||
virtual void set_current(draw_context_t ctx) = 0;
|
||||
virtual void flip(draw_context_t ctx, bool skip_frame=false) = 0;
|
||||
virtual int client_width() = 0;
|
||||
virtual int client_height() = 0;
|
||||
virtual void delete_context(draw_context_t ctx) = 0;
|
||||
virtual draw_context_t make_context() = 0;
|
||||
virtual void set_current(draw_context_t ctx) = 0;
|
||||
virtual void flip(draw_context_t ctx, bool skip_frame = false) = 0;
|
||||
virtual int client_width() = 0;
|
||||
virtual int client_height() = 0;
|
||||
|
||||
virtual display_handle_t handle() const = 0;
|
||||
virtual display_handle_t handle() const = 0;
|
||||
|
||||
protected:
|
||||
protected:
|
||||
|
||||
//window manager event management
|
||||
wm_event m_raised_event;
|
||||
std::atomic_bool wm_event_raised = {};
|
||||
std::atomic_bool wm_event_queue_enabled = {};
|
||||
// window manager event management
|
||||
std::deque<wm_event> m_raised_events;
|
||||
std::atomic_bool wm_event_queue_enabled = {};
|
||||
std::atomic_bool wm_allow_fullscreen = { true };
|
||||
|
||||
public:
|
||||
//synchronize native window access
|
||||
std::mutex wm_event_lock;
|
||||
// synchronize native window access
|
||||
shared_mutex wm_event_lock;
|
||||
|
||||
virtual wm_event get_default_wm_event() const = 0;
|
||||
void wm_wait() const
|
||||
{
|
||||
while (!m_raised_events.empty() && !Emu.IsStopped()) _mm_pause();
|
||||
}
|
||||
|
||||
bool has_wm_events() const
|
||||
{
|
||||
return !m_raised_events.empty();
|
||||
}
|
||||
|
||||
void clear_wm_events()
|
||||
{
|
||||
m_raised_event = wm_event::none;
|
||||
wm_event_raised.store(false);
|
||||
if (!m_raised_events.empty())
|
||||
{
|
||||
std::lock_guard lock(wm_event_lock);
|
||||
m_raised_events.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void push_wm_event(wm_event&& _event)
|
||||
{
|
||||
std::lock_guard lock(wm_event_lock);
|
||||
m_raised_events.push_back(_event);
|
||||
}
|
||||
|
||||
wm_event get_wm_event()
|
||||
{
|
||||
if (wm_event_raised.load(std::memory_order_consume))
|
||||
if (m_raised_events.empty())
|
||||
{
|
||||
auto result = m_raised_event;
|
||||
m_raised_event = wm_event::none;
|
||||
wm_event_raised.store(false);
|
||||
return result;
|
||||
return wm_event::none;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::lock_guard lock(wm_event_lock);
|
||||
|
||||
return get_default_wm_event();
|
||||
const auto _event = m_raised_events.front();
|
||||
m_raised_events.pop_front();
|
||||
return _event;
|
||||
}
|
||||
}
|
||||
|
||||
void disable_wm_event_queue()
|
||||
@ -124,6 +146,16 @@ public:
|
||||
{
|
||||
wm_event_queue_enabled.store(true);
|
||||
}
|
||||
|
||||
void disable_wm_fullscreen()
|
||||
{
|
||||
wm_allow_fullscreen.store(false);
|
||||
}
|
||||
|
||||
void enable_wm_fullscreen()
|
||||
{
|
||||
wm_allow_fullscreen.store(true);
|
||||
}
|
||||
};
|
||||
|
||||
class GSRender : public rsx::thread
|
||||
@ -141,4 +173,6 @@ public:
|
||||
void on_exit() override;
|
||||
|
||||
void flip(int buffer) override;
|
||||
|
||||
GSFrameBase* get_frame() { return m_frame; };
|
||||
};
|
||||
|
@ -918,7 +918,7 @@ bool VKGSRender::on_access_violation(u32 address, bool is_writing)
|
||||
|
||||
if (target_cb)
|
||||
{
|
||||
target_cb->wait();
|
||||
target_cb->wait(GENERAL_WAIT_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1046,6 +1046,103 @@ void VKGSRender::check_descriptors()
|
||||
}
|
||||
}
|
||||
|
||||
void VKGSRender::check_window_status()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
|
||||
if (LIKELY(!m_frame->has_wm_events()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while (const auto _event = m_frame->get_wm_event())
|
||||
{
|
||||
switch (_event)
|
||||
{
|
||||
case wm_event::toggle_fullscreen:
|
||||
{
|
||||
renderer_unavailable = true;
|
||||
m_frame->enable_wm_fullscreen();
|
||||
m_frame->toggle_fullscreen();
|
||||
m_frame->disable_wm_fullscreen();
|
||||
break;
|
||||
}
|
||||
case wm_event::geometry_change_notice:
|
||||
{
|
||||
// Stall until finish notification is received. Also, raise surface dirty flag
|
||||
u32 timeout = 1000;
|
||||
bool handled = false;
|
||||
|
||||
while (timeout)
|
||||
{
|
||||
switch (m_frame->get_wm_event())
|
||||
{
|
||||
default:
|
||||
break;
|
||||
case wm_event::window_resized:
|
||||
handled = true;
|
||||
present_surface_dirty_flag = true;
|
||||
break;
|
||||
case wm_event::geometry_change_in_progress:
|
||||
timeout += 10; // Extend timeout to wait for user to finish resizing
|
||||
break;
|
||||
case wm_event::window_restored:
|
||||
case wm_event::window_visibility_changed:
|
||||
case wm_event::window_minimized:
|
||||
case wm_event::window_moved:
|
||||
handled = true; // Ignore these events as they do not alter client area
|
||||
break;
|
||||
}
|
||||
|
||||
if (handled)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Wait for window manager event
|
||||
std::this_thread::sleep_for(1ms);
|
||||
timeout --;
|
||||
}
|
||||
}
|
||||
|
||||
if (!timeout)
|
||||
{
|
||||
LOG_ERROR(RSX, "wm event handler timed out");
|
||||
}
|
||||
|
||||
// Reset renderer availability if something has changed about the window
|
||||
renderer_unavailable = false;
|
||||
break;
|
||||
}
|
||||
case wm_event::window_resized:
|
||||
{
|
||||
LOG_ERROR(RSX, "wm_event::window_resized received without corresponding wm_event::geometry_change_notice!");
|
||||
std::this_thread::sleep_for(100ms);
|
||||
renderer_unavailable = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
const auto frame_width = m_frame->client_width();
|
||||
const auto frame_height = m_frame->client_height();
|
||||
|
||||
if (m_client_height != frame_height ||
|
||||
m_client_width != frame_width)
|
||||
{
|
||||
if (!!frame_width && !!frame_height)
|
||||
{
|
||||
present_surface_dirty_flag = true;
|
||||
renderer_unavailable = false;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
VkDescriptorSet VKGSRender::allocate_descriptor_set()
|
||||
{
|
||||
verify(HERE), m_current_frame->used_descriptors < DESCRIPTOR_MAX_DRAW_CALLS;
|
||||
@ -1868,6 +1965,7 @@ void VKGSRender::on_init_thread()
|
||||
m_frame->disable_wm_event_queue();
|
||||
m_shaders_cache->load(&helper, *m_device, pipeline_layout);
|
||||
m_frame->enable_wm_event_queue();
|
||||
m_frame->disable_wm_fullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2242,8 +2340,14 @@ void VKGSRender::process_swap_request(frame_context_t *ctx, bool free_resources)
|
||||
|
||||
if (ctx->swap_command_buffer->pending)
|
||||
{
|
||||
//Perform hard swap here
|
||||
ctx->swap_command_buffer->wait();
|
||||
// Perform hard swap here
|
||||
if (ctx->swap_command_buffer->wait(FRAME_PRESENT_TIMEOUT) != VK_SUCCESS)
|
||||
{
|
||||
// Lost surface, release renderer
|
||||
present_surface_dirty_flag = true;
|
||||
renderer_unavailable = true;
|
||||
}
|
||||
|
||||
free_resources = true;
|
||||
}
|
||||
|
||||
@ -2347,84 +2451,7 @@ void VKGSRender::do_local_task(rsx::FIFO_state state)
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
switch (m_frame->get_wm_event())
|
||||
{
|
||||
case wm_event::none:
|
||||
break;
|
||||
case wm_event::geometry_change_notice:
|
||||
{
|
||||
//Stall until finish notification is received. Also, raise surface dirty flag
|
||||
u32 timeout = 1000;
|
||||
bool handled = false;
|
||||
|
||||
while (timeout)
|
||||
{
|
||||
switch (m_frame->get_wm_event())
|
||||
{
|
||||
default:
|
||||
break;
|
||||
case wm_event::window_resized:
|
||||
handled = true;
|
||||
present_surface_dirty_flag = true;
|
||||
break;
|
||||
case wm_event::geometry_change_in_progress:
|
||||
timeout += 10; //extend timeout to wait for user to finish resizing
|
||||
break;
|
||||
case wm_event::window_restored:
|
||||
case wm_event::window_visibility_changed:
|
||||
case wm_event::window_minimized:
|
||||
case wm_event::window_moved:
|
||||
handled = true; //ignore these events as they do not alter client area
|
||||
break;
|
||||
}
|
||||
|
||||
if (handled)
|
||||
break;
|
||||
else
|
||||
{
|
||||
//wait for window manager event
|
||||
std::this_thread::sleep_for(10ms);
|
||||
timeout -= 10;
|
||||
}
|
||||
|
||||
//reset renderer availability if something has changed about the window
|
||||
renderer_unavailable = false;
|
||||
}
|
||||
|
||||
if (!timeout)
|
||||
{
|
||||
LOG_ERROR(RSX, "wm event handler timed out");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case wm_event::window_resized:
|
||||
{
|
||||
LOG_ERROR(RSX, "wm_event::window_resized received without corresponding wm_event::geometry_change_notice!");
|
||||
std::this_thread::sleep_for(100ms);
|
||||
renderer_unavailable = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
const auto frame_width = m_frame->client_width();
|
||||
const auto frame_height = m_frame->client_height();
|
||||
|
||||
if (m_client_height != frame_height ||
|
||||
m_client_width != frame_width)
|
||||
{
|
||||
if (!!frame_width && !!frame_height)
|
||||
{
|
||||
present_surface_dirty_flag = true;
|
||||
renderer_unavailable = false;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
check_window_status();
|
||||
|
||||
if (m_overlay_manager)
|
||||
{
|
||||
@ -3052,8 +3079,8 @@ void VKGSRender::reinitialize_swapchain()
|
||||
if (ctx.present_image == UINT32_MAX)
|
||||
continue;
|
||||
|
||||
//Release present image by presenting it
|
||||
ctx.swap_command_buffer->wait();
|
||||
// Release present image by presenting it
|
||||
ctx.swap_command_buffer->wait(FRAME_PRESENT_TIMEOUT);
|
||||
ctx.swap_command_buffer = nullptr;
|
||||
present(&ctx);
|
||||
}
|
||||
@ -3558,7 +3585,7 @@ void VKGSRender::get_occlusion_query_result(rsx::reports::occlusion_query_info*
|
||||
}
|
||||
|
||||
if (data.command_buffer_to_wait->pending)
|
||||
data.command_buffer_to_wait->wait();
|
||||
data.command_buffer_to_wait->wait(GENERAL_WAIT_TIMEOUT);
|
||||
|
||||
//Gather data
|
||||
for (const auto occlusion_id : data.indices)
|
||||
|
@ -96,7 +96,7 @@ struct command_buffer_chunk: public vk::command_buffer
|
||||
poke();
|
||||
|
||||
if (pending)
|
||||
wait();
|
||||
wait(FRAME_PRESENT_TIMEOUT);
|
||||
|
||||
CHECK_RESULT(vkResetCommandBuffer(commands, 0));
|
||||
num_draws = 0;
|
||||
@ -124,14 +124,14 @@ struct command_buffer_chunk: public vk::command_buffer
|
||||
return !pending;
|
||||
}
|
||||
|
||||
void wait()
|
||||
VkResult wait(u64 timeout = 0ull)
|
||||
{
|
||||
reader_lock lock(guard_mutex);
|
||||
|
||||
if (!pending)
|
||||
return;
|
||||
return VK_SUCCESS;
|
||||
|
||||
vk::wait_for_fence(submit_fence);
|
||||
const auto ret = vk::wait_for_fence(submit_fence, timeout);
|
||||
|
||||
lock.upgrade();
|
||||
|
||||
@ -140,6 +140,8 @@ struct command_buffer_chunk: public vk::command_buffer
|
||||
vk::reset_fence(&submit_fence);
|
||||
pending = false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
@ -441,6 +443,8 @@ public:
|
||||
void set_scissor();
|
||||
void bind_viewport();
|
||||
|
||||
void check_window_status();
|
||||
|
||||
void sync_hint(rsx::FIFO_hint hint) override;
|
||||
|
||||
void begin_occlusion_query(rsx::reports::occlusion_query_info* query) override;
|
||||
|
@ -592,18 +592,27 @@ namespace vk
|
||||
}
|
||||
}
|
||||
|
||||
void wait_for_fence(VkFence fence)
|
||||
VkResult wait_for_fence(VkFence fence, u64 timeout)
|
||||
{
|
||||
while (auto status = vkGetFenceStatus(*g_current_renderer, fence))
|
||||
if (timeout)
|
||||
{
|
||||
switch (status)
|
||||
return vkWaitForFences(*g_current_renderer, 1, &fence, VK_FALSE, timeout * 1000ull);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (auto status = vkGetFenceStatus(*g_current_renderer, fence))
|
||||
{
|
||||
case VK_NOT_READY:
|
||||
continue;
|
||||
default:
|
||||
die_with_error(HERE, status);
|
||||
return;
|
||||
switch (status)
|
||||
{
|
||||
case VK_NOT_READY:
|
||||
continue;
|
||||
default:
|
||||
die_with_error(HERE, status);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,6 +46,9 @@
|
||||
|
||||
#define VK_NUM_DESCRIPTOR_BINDINGS (VERTEX_TEXTURES_FIRST_BIND_SLOT + 4)
|
||||
|
||||
#define FRAME_PRESENT_TIMEOUT 1000000ull // 1 second
|
||||
#define GENERAL_WAIT_TIMEOUT 100000ull // 100ms
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
class fragment_texture;
|
||||
@ -175,9 +178,9 @@ namespace vk
|
||||
const u64 get_current_frame_id();
|
||||
const u64 get_last_completed_frame_id();
|
||||
|
||||
//Fence reset with driver workarounds in place
|
||||
// Fence reset with driver workarounds in place
|
||||
void reset_fence(VkFence *pFence);
|
||||
void wait_for_fence(VkFence pFence);
|
||||
VkResult wait_for_fence(VkFence pFence, u64 timeout = 0ull);
|
||||
|
||||
void die_with_error(const char* faulting_addr, VkResult error_code);
|
||||
|
||||
|
@ -27,13 +27,162 @@
|
||||
|
||||
constexpr auto qstr = QString::fromStdString;
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
namespace win32
|
||||
{
|
||||
HHOOK _hook = NULL;
|
||||
bool _in_sizing_event = false;
|
||||
bool _interactive_resize = false;
|
||||
bool _user_interaction_active = false;
|
||||
bool _minimized = false;
|
||||
|
||||
void _ReleaseHook();
|
||||
|
||||
LRESULT CALLBACK __HookCallback(INT nCode, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
std::shared_ptr<GSRender> renderer;
|
||||
|
||||
if (Emu.IsStopped())
|
||||
{
|
||||
// Stop receiving events
|
||||
_ReleaseHook();
|
||||
}
|
||||
else
|
||||
{
|
||||
renderer = fxm::get<GSRender>();
|
||||
}
|
||||
|
||||
if (nCode >= 0 && renderer)
|
||||
{
|
||||
auto frame_wnd = renderer->get_frame();
|
||||
auto msg = (CWPSTRUCT*)lParam;
|
||||
|
||||
switch ((msg->hwnd == frame_wnd->handle()) ? msg->message : 0)
|
||||
{
|
||||
case WM_WINDOWPOSCHANGING:
|
||||
{
|
||||
const auto flags = reinterpret_cast<LPWINDOWPOS>(msg->lParam)->flags & SWP_NOSIZE;
|
||||
if (_in_sizing_event || flags != 0)
|
||||
break;
|
||||
|
||||
// About to resize
|
||||
_in_sizing_event = true;
|
||||
_interactive_resize = false;
|
||||
frame_wnd->push_wm_event(wm_event::geometry_change_notice);
|
||||
break;
|
||||
}
|
||||
case WM_WINDOWPOSCHANGED:
|
||||
{
|
||||
if (_user_interaction_active)
|
||||
{
|
||||
// Window dragged or resized by user causing position to change, but user is not yet done
|
||||
frame_wnd->push_wm_event(wm_event::geometry_change_in_progress);
|
||||
break;
|
||||
}
|
||||
|
||||
const auto flags = reinterpret_cast<LPWINDOWPOS>(msg->lParam)->flags & (SWP_NOSIZE | SWP_NOMOVE);
|
||||
if (!_in_sizing_event || flags == (SWP_NOSIZE | SWP_NOMOVE))
|
||||
break;
|
||||
|
||||
_in_sizing_event = false;
|
||||
|
||||
if (flags & SWP_NOSIZE)
|
||||
{
|
||||
frame_wnd->push_wm_event(wm_event::window_moved);
|
||||
}
|
||||
else
|
||||
{
|
||||
LPWINDOWPOS wpos = reinterpret_cast<LPWINDOWPOS>(msg->lParam);
|
||||
if (wpos->cx <= GetSystemMetrics(SM_CXMINIMIZED) || wpos->cy <= GetSystemMetrics(SM_CYMINIMIZED))
|
||||
{
|
||||
// Minimize event
|
||||
_minimized = true;
|
||||
frame_wnd->push_wm_event(wm_event::window_minimized);
|
||||
}
|
||||
else if (_minimized)
|
||||
{
|
||||
_minimized = false;
|
||||
frame_wnd->push_wm_event(wm_event::window_restored);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Handle the resize in WM_SIZE message
|
||||
_in_sizing_event = true;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case WM_ENTERSIZEMOVE:
|
||||
_user_interaction_active = true;
|
||||
break;
|
||||
case WM_EXITSIZEMOVE:
|
||||
_user_interaction_active = false;
|
||||
if (_in_sizing_event && !_user_interaction_active)
|
||||
{
|
||||
// Just finished resizing using manual interaction. The corresponding WM_SIZE is consumed before this event fires
|
||||
frame_wnd->push_wm_event(_interactive_resize ? wm_event::window_resized : wm_event::window_moved);
|
||||
_in_sizing_event = false;
|
||||
}
|
||||
break;
|
||||
case WM_SIZE:
|
||||
{
|
||||
if (_user_interaction_active)
|
||||
{
|
||||
// Interaction is a resize not a move
|
||||
_interactive_resize = true;
|
||||
frame_wnd->push_wm_event(wm_event::geometry_change_in_progress);
|
||||
}
|
||||
else if (_in_sizing_event)
|
||||
{
|
||||
// Any other unexpected resize mode will give an unconsumed WM_SIZE event
|
||||
frame_wnd->push_wm_event(wm_event::window_resized);
|
||||
_in_sizing_event = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return CallNextHookEx(_hook, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
void _InstallHook()
|
||||
{
|
||||
if (_hook)
|
||||
{
|
||||
_ReleaseHook();
|
||||
}
|
||||
|
||||
Emu.CallAfter([&]()
|
||||
{
|
||||
if (_hook = SetWindowsHookEx(WH_CALLWNDPROC, __HookCallback, NULL, GetCurrentThreadId()); !_hook)
|
||||
{
|
||||
LOG_ERROR(RSX, "Failed to install window hook!");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _ReleaseHook()
|
||||
{
|
||||
if (_hook)
|
||||
{
|
||||
UnhookWindowsHookEx(_hook);
|
||||
_hook = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
gs_frame::gs_frame(const QString& title, const QRect& geometry, const QIcon& appIcon, const std::shared_ptr<gui_settings>& gui_settings)
|
||||
: QWindow(), m_windowTitle(title), m_gui_settings(gui_settings)
|
||||
{
|
||||
m_disable_mouse = gui_settings->GetValue(gui::gs_disableMouse).toBool();
|
||||
|
||||
// Workaround for a Qt bug affecting 5.11.1 binaries
|
||||
m_use_5_11_1_workaround = QLibraryInfo::version() == QVersionNumber(5, 11, 1);
|
||||
//m_use_5_11_1_workaround = QLibraryInfo::version() == QVersionNumber(5, 11, 1);
|
||||
|
||||
//Get version by substringing VersionNumber-buildnumber-commithash to get just the part before the dash
|
||||
std::string version = rpcs3::version.to_string();
|
||||
@ -85,6 +234,8 @@ gs_frame::gs_frame(const QString& title, const QRect& geometry, const QIcon& app
|
||||
m_tb_progress = m_tb_button->progress();
|
||||
m_tb_progress->setRange(0, m_gauge_max);
|
||||
m_tb_progress->setVisible(false);
|
||||
|
||||
win32::_ReleaseHook();
|
||||
#elif HAVE_QTDBUS
|
||||
UpdateProgress(0);
|
||||
m_progress_value = 0;
|
||||
@ -126,54 +277,60 @@ void gs_frame::showEvent(QShowEvent *event)
|
||||
|
||||
void gs_frame::keyPressEvent(QKeyEvent *keyEvent)
|
||||
{
|
||||
auto l_handleKeyEvent = [this ,keyEvent]()
|
||||
switch (keyEvent->key())
|
||||
{
|
||||
switch (keyEvent->key())
|
||||
case Qt::Key_L:
|
||||
if (keyEvent->modifiers() == Qt::AltModifier) { static int count = 0; LOG_SUCCESS(GENERAL, "Made forced mark %d in log", ++count); }
|
||||
break;
|
||||
case Qt::Key_Return:
|
||||
if (keyEvent->modifiers() == Qt::AltModifier) { toggle_fullscreen(); return; }
|
||||
break;
|
||||
case Qt::Key_Escape:
|
||||
if (visibility() == FullScreen) { toggle_fullscreen(); return; }
|
||||
break;
|
||||
case Qt::Key_P:
|
||||
if (keyEvent->modifiers() == Qt::ControlModifier && Emu.IsRunning()) { Emu.Pause(); return; }
|
||||
break;
|
||||
case Qt::Key_S:
|
||||
if (keyEvent->modifiers() == Qt::ControlModifier && (!Emu.IsStopped())) { Emu.Stop(); return; }
|
||||
break;
|
||||
case Qt::Key_R:
|
||||
if (keyEvent->modifiers() == Qt::ControlModifier && (!Emu.GetBoot().empty())) { Emu.Restart(); return; }
|
||||
break;
|
||||
case Qt::Key_E:
|
||||
if (keyEvent->modifiers() == Qt::ControlModifier)
|
||||
{
|
||||
case Qt::Key_L:
|
||||
if (keyEvent->modifiers() == Qt::AltModifier) { static int count = 0; LOG_SUCCESS(GENERAL, "Made forced mark %d in log", ++count); }
|
||||
break;
|
||||
case Qt::Key_Return:
|
||||
if (keyEvent->modifiers() == Qt::AltModifier) { OnFullScreen(); return; }
|
||||
break;
|
||||
case Qt::Key_Escape:
|
||||
if (visibility() == FullScreen) { setVisibility(Windowed); return; }
|
||||
break;
|
||||
case Qt::Key_P:
|
||||
if (keyEvent->modifiers() == Qt::ControlModifier && Emu.IsRunning()) { Emu.Pause(); return; }
|
||||
break;
|
||||
case Qt::Key_S:
|
||||
if (keyEvent->modifiers() == Qt::ControlModifier && (!Emu.IsStopped())) { Emu.Stop(); return; }
|
||||
break;
|
||||
case Qt::Key_R:
|
||||
if (keyEvent->modifiers() == Qt::ControlModifier && (!Emu.GetBoot().empty())) { Emu.Restart(); return; }
|
||||
break;
|
||||
case Qt::Key_E:
|
||||
if (keyEvent->modifiers() == Qt::ControlModifier)
|
||||
{
|
||||
if (Emu.IsReady()) { Emu.Run(); return; }
|
||||
else if (Emu.IsPaused()) { Emu.Resume(); return; }
|
||||
}
|
||||
break;
|
||||
if (Emu.IsReady()) { Emu.Run(); return; }
|
||||
else if (Emu.IsPaused()) { Emu.Resume(); return; }
|
||||
}
|
||||
};
|
||||
Emu.CallAfter(l_handleKeyEvent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void gs_frame::OnFullScreen()
|
||||
void gs_frame::toggle_fullscreen()
|
||||
{
|
||||
auto l_setFullScreenVis = [=]()
|
||||
if (wm_allow_fullscreen)
|
||||
{
|
||||
if (visibility() == FullScreen)
|
||||
auto l_setFullScreenVis = [&]()
|
||||
{
|
||||
setVisibility(Windowed);
|
||||
}
|
||||
else
|
||||
{
|
||||
setVisibility(FullScreen);
|
||||
}
|
||||
};
|
||||
Emu.CallAfter(l_setFullScreenVis);
|
||||
if (visibility() == FullScreen)
|
||||
{
|
||||
setVisibility(Windowed);
|
||||
}
|
||||
else
|
||||
{
|
||||
setVisibility(FullScreen);
|
||||
}
|
||||
};
|
||||
|
||||
Emu.CallAfter(l_setFullScreenVis);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Forward the request to the backend
|
||||
push_wm_event(wm_event::toggle_fullscreen);
|
||||
std::this_thread::sleep_for(1s);
|
||||
}
|
||||
}
|
||||
|
||||
void gs_frame::close()
|
||||
@ -248,6 +405,10 @@ draw_context_t gs_frame::make_context()
|
||||
void gs_frame::set_current(draw_context_t ctx)
|
||||
{
|
||||
Q_UNUSED(ctx);
|
||||
|
||||
#ifdef _WIN32
|
||||
win32::_InstallHook();
|
||||
#endif
|
||||
}
|
||||
|
||||
void gs_frame::delete_context(draw_context_t ctx)
|
||||
@ -310,7 +471,7 @@ void gs_frame::mouseDoubleClickEvent(QMouseEvent* ev)
|
||||
|
||||
if (ev->button() == Qt::LeftButton)
|
||||
{
|
||||
OnFullScreen();
|
||||
toggle_fullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
@ -351,127 +512,6 @@ bool gs_frame::event(QEvent* ev)
|
||||
return QWindow::event(ev);
|
||||
}
|
||||
|
||||
bool gs_frame::nativeEvent(const QByteArray &eventType, void *message, long *result)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (wm_event_queue_enabled.load(std::memory_order_consume) && !Emu.IsStopped())
|
||||
{
|
||||
//Wait for consumer to clear notification pending flag
|
||||
while (wm_event_raised.load(std::memory_order_consume) && !Emu.IsStopped());
|
||||
|
||||
{
|
||||
MSG* msg;
|
||||
if (m_use_5_11_1_workaround)
|
||||
{
|
||||
// https://bugreports.qt.io/browse/QTBUG-69074?focusedCommentId=409797&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-409797
|
||||
msg = *reinterpret_cast<MSG**>(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = reinterpret_cast<MSG*>(message);
|
||||
}
|
||||
|
||||
std::lock_guard lock(wm_event_lock);
|
||||
|
||||
switch (msg->message)
|
||||
{
|
||||
case WM_WINDOWPOSCHANGING:
|
||||
{
|
||||
const auto flags = reinterpret_cast<LPWINDOWPOS>(msg->lParam)->flags & SWP_NOSIZE;
|
||||
if (m_in_sizing_event || flags != 0)
|
||||
break;
|
||||
|
||||
//About to resize
|
||||
m_in_sizing_event = true;
|
||||
m_interactive_resize = false;
|
||||
m_raised_event = wm_event::geometry_change_notice;
|
||||
wm_event_raised.store(true);
|
||||
break;
|
||||
}
|
||||
case WM_WINDOWPOSCHANGED:
|
||||
{
|
||||
const auto flags = reinterpret_cast<LPWINDOWPOS>(msg->lParam)->flags & (SWP_NOSIZE | SWP_NOMOVE);
|
||||
if (!m_in_sizing_event || m_user_interaction_active || flags == (SWP_NOSIZE | SWP_NOMOVE))
|
||||
break;
|
||||
|
||||
m_in_sizing_event = false;
|
||||
|
||||
if (flags & SWP_NOSIZE)
|
||||
{
|
||||
m_raised_event = wm_event::window_moved;
|
||||
}
|
||||
else
|
||||
{
|
||||
LPWINDOWPOS wpos = reinterpret_cast<LPWINDOWPOS>(msg->lParam);
|
||||
if (wpos->cx <= GetSystemMetrics(SM_CXMINIMIZED) || wpos->cy <= GetSystemMetrics(SM_CYMINIMIZED))
|
||||
{
|
||||
//Minimize event
|
||||
m_minimized = true;
|
||||
m_raised_event = wm_event::window_minimized;
|
||||
}
|
||||
else if (m_minimized)
|
||||
{
|
||||
m_minimized = false;
|
||||
m_raised_event = wm_event::window_restored;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Handle the resize in WM_SIZE message
|
||||
m_in_sizing_event = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Possibly finished resizing using maximize or SWP
|
||||
wm_event_raised.store(true);
|
||||
break;
|
||||
}
|
||||
case WM_ENTERSIZEMOVE:
|
||||
m_user_interaction_active = true;
|
||||
break;
|
||||
case WM_EXITSIZEMOVE:
|
||||
m_user_interaction_active = false;
|
||||
if (m_in_sizing_event && !m_user_interaction_active)
|
||||
{
|
||||
//Just finished resizing using manual interaction. The corresponding WM_SIZE is consumed before this event fires
|
||||
m_raised_event = m_interactive_resize ? wm_event::window_resized : wm_event::window_moved;
|
||||
m_in_sizing_event = false;
|
||||
wm_event_raised.store(true);
|
||||
}
|
||||
break;
|
||||
case WM_SIZE:
|
||||
{
|
||||
if (m_user_interaction_active)
|
||||
{
|
||||
//Interaction is a resize not a move
|
||||
m_interactive_resize = true;
|
||||
}
|
||||
else if (m_in_sizing_event)
|
||||
{
|
||||
//Any other unexpected resize mode will give an unconsumed WM_SIZE event
|
||||
m_raised_event = wm_event::window_resized;
|
||||
m_in_sizing_event = false;
|
||||
wm_event_raised.store(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Do not enter DefWndProc until the consumer has consumed the message
|
||||
while (wm_event_raised.load(std::memory_order_consume) && !Emu.IsStopped());
|
||||
}
|
||||
#endif
|
||||
|
||||
//Let the default handler deal with this. Only the notification is required
|
||||
return false;
|
||||
}
|
||||
|
||||
wm_event gs_frame::get_default_wm_event() const
|
||||
{
|
||||
return (m_user_interaction_active) ? wm_event::geometry_change_in_progress : wm_event::none;
|
||||
}
|
||||
|
||||
void gs_frame::progress_reset(bool reset_limit)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
|
@ -40,13 +40,6 @@ private:
|
||||
bool m_show_fps;
|
||||
bool m_disable_mouse;
|
||||
|
||||
bool m_in_sizing_event = false; // a signal that the window is about to be resized was received
|
||||
bool m_user_interaction_active = false; // a signal indicating the window is being manually moved/resized was received
|
||||
bool m_interactive_resize = false; // resize signal received while dragging window
|
||||
bool m_minimized = false;
|
||||
|
||||
bool m_use_5_11_1_workaround = false; // QT ABI bug workaround
|
||||
|
||||
public:
|
||||
gs_frame(const QString& title, const QRect& geometry, const QIcon& appIcon, const std::shared_ptr<gui_settings>& gui_settings);
|
||||
~gs_frame();
|
||||
@ -54,8 +47,7 @@ public:
|
||||
draw_context_t make_context() override;
|
||||
void set_current(draw_context_t context) override;
|
||||
void delete_context(draw_context_t context) override;
|
||||
|
||||
wm_event get_default_wm_event() const override;
|
||||
void toggle_fullscreen() override;
|
||||
|
||||
// taskbar progress
|
||||
void progress_reset(bool reset_limit = false);
|
||||
@ -67,7 +59,6 @@ protected:
|
||||
virtual void showEvent(QShowEvent *event) override;
|
||||
|
||||
void keyPressEvent(QKeyEvent *keyEvent) override;
|
||||
void OnFullScreen();
|
||||
|
||||
void close() override;
|
||||
|
||||
@ -82,8 +73,6 @@ protected:
|
||||
int client_width() override;
|
||||
int client_height() override;
|
||||
|
||||
bool nativeEvent(const QByteArray &eventType, void *message, long *result) override;
|
||||
|
||||
bool event(QEvent* ev) override;
|
||||
|
||||
private Q_SLOTS:
|
||||
|
Loading…
Reference in New Issue
Block a user