1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2024-11-23 03:02:53 +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:
kd-11 2019-01-07 01:03:56 +03:00 committed by kd-11
parent 987b607cb0
commit 3bfa564ef8
7 changed files with 420 additions and 314 deletions

View File

@ -1,4 +1,4 @@
#pragma once #pragma once
#include "Emu/RSX/RSXThread.h" #include "Emu/RSX/RSXThread.h"
#include <memory> #include <memory>
@ -35,13 +35,14 @@ struct RSXDebuggerProgram
enum wm_event enum wm_event
{ {
none, //nothing none, // nothing
geometry_change_notice, //about to start resizing and/or moving the window toggle_fullscreen, // user is requesting a fullscreen switch
geometry_change_in_progress, //window being resized and/or moved geometry_change_notice, // about to start resizing and/or moving the window
window_resized, //window was resized geometry_change_in_progress, // window being resized and/or moved
window_minimized, //window was minimized window_resized, // window was resized
window_restored, //window was restored from a minimized state window_minimized, // window was minimized
window_moved, //window moved without resize window_restored, // window was restored from a minimized state
window_moved, // window moved without resize
window_visibility_changed window_visibility_changed
}; };
@ -62,9 +63,9 @@ using draw_context_t = void*;
>; >;
#endif #endif
class GSFrameBase class GSFrameBase
{ {
public: public:
GSFrameBase() = default; GSFrameBase() = default;
GSFrameBase(const GSFrameBase&) = delete; GSFrameBase(const GSFrameBase&) = delete;
virtual ~GSFrameBase() {} virtual ~GSFrameBase() {}
@ -73,46 +74,67 @@ public:
virtual bool shown() = 0; virtual bool shown() = 0;
virtual void hide() = 0; virtual void hide() = 0;
virtual void show() = 0; virtual void show() = 0;
virtual void toggle_fullscreen() = 0;
virtual void delete_context(draw_context_t ctx) = 0; virtual void delete_context(draw_context_t ctx) = 0;
virtual draw_context_t make_context() = 0; virtual draw_context_t make_context() = 0;
virtual void set_current(draw_context_t ctx) = 0; virtual void set_current(draw_context_t ctx) = 0;
virtual void flip(draw_context_t ctx, bool skip_frame=false) = 0; virtual void flip(draw_context_t ctx, bool skip_frame = false) = 0;
virtual int client_width() = 0; virtual int client_width() = 0;
virtual int client_height() = 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 // window manager event management
wm_event m_raised_event; std::deque<wm_event> m_raised_events;
std::atomic_bool wm_event_raised = {};
std::atomic_bool wm_event_queue_enabled = {}; std::atomic_bool wm_event_queue_enabled = {};
std::atomic_bool wm_allow_fullscreen = { true };
public: public:
//synchronize native window access // synchronize native window access
std::mutex wm_event_lock; 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() void clear_wm_events()
{ {
m_raised_event = wm_event::none; if (!m_raised_events.empty())
wm_event_raised.store(false); {
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() wm_event get_wm_event()
{ {
if (wm_event_raised.load(std::memory_order_consume)) if (m_raised_events.empty())
{ {
auto result = m_raised_event; return wm_event::none;
m_raised_event = wm_event::none;
wm_event_raised.store(false);
return result;
} }
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() void disable_wm_event_queue()
@ -124,6 +146,16 @@ public:
{ {
wm_event_queue_enabled.store(true); 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 class GSRender : public rsx::thread
@ -141,4 +173,6 @@ public:
void on_exit() override; void on_exit() override;
void flip(int buffer) override; void flip(int buffer) override;
GSFrameBase* get_frame() { return m_frame; };
}; };

View File

@ -918,7 +918,7 @@ bool VKGSRender::on_access_violation(u32 address, bool is_writing)
if (target_cb) 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() VkDescriptorSet VKGSRender::allocate_descriptor_set()
{ {
verify(HERE), m_current_frame->used_descriptors < DESCRIPTOR_MAX_DRAW_CALLS; 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_frame->disable_wm_event_queue();
m_shaders_cache->load(&helper, *m_device, pipeline_layout); m_shaders_cache->load(&helper, *m_device, pipeline_layout);
m_frame->enable_wm_event_queue(); 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) if (ctx->swap_command_buffer->pending)
{ {
//Perform hard swap here // Perform hard swap here
ctx->swap_command_buffer->wait(); 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; free_resources = true;
} }
@ -2347,84 +2451,7 @@ void VKGSRender::do_local_task(rsx::FIFO_state state)
return; return;
} }
#ifdef _WIN32 check_window_status();
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
if (m_overlay_manager) if (m_overlay_manager)
{ {
@ -3052,8 +3079,8 @@ void VKGSRender::reinitialize_swapchain()
if (ctx.present_image == UINT32_MAX) if (ctx.present_image == UINT32_MAX)
continue; continue;
//Release present image by presenting it // Release present image by presenting it
ctx.swap_command_buffer->wait(); ctx.swap_command_buffer->wait(FRAME_PRESENT_TIMEOUT);
ctx.swap_command_buffer = nullptr; ctx.swap_command_buffer = nullptr;
present(&ctx); present(&ctx);
} }
@ -3558,7 +3585,7 @@ void VKGSRender::get_occlusion_query_result(rsx::reports::occlusion_query_info*
} }
if (data.command_buffer_to_wait->pending) if (data.command_buffer_to_wait->pending)
data.command_buffer_to_wait->wait(); data.command_buffer_to_wait->wait(GENERAL_WAIT_TIMEOUT);
//Gather data //Gather data
for (const auto occlusion_id : data.indices) for (const auto occlusion_id : data.indices)

View File

@ -96,7 +96,7 @@ struct command_buffer_chunk: public vk::command_buffer
poke(); poke();
if (pending) if (pending)
wait(); wait(FRAME_PRESENT_TIMEOUT);
CHECK_RESULT(vkResetCommandBuffer(commands, 0)); CHECK_RESULT(vkResetCommandBuffer(commands, 0));
num_draws = 0; num_draws = 0;
@ -124,14 +124,14 @@ struct command_buffer_chunk: public vk::command_buffer
return !pending; return !pending;
} }
void wait() VkResult wait(u64 timeout = 0ull)
{ {
reader_lock lock(guard_mutex); reader_lock lock(guard_mutex);
if (!pending) if (!pending)
return; return VK_SUCCESS;
vk::wait_for_fence(submit_fence); const auto ret = vk::wait_for_fence(submit_fence, timeout);
lock.upgrade(); lock.upgrade();
@ -140,6 +140,8 @@ struct command_buffer_chunk: public vk::command_buffer
vk::reset_fence(&submit_fence); vk::reset_fence(&submit_fence);
pending = false; pending = false;
} }
return ret;
} }
}; };
@ -441,6 +443,8 @@ public:
void set_scissor(); void set_scissor();
void bind_viewport(); void bind_viewport();
void check_window_status();
void sync_hint(rsx::FIFO_hint hint) override; void sync_hint(rsx::FIFO_hint hint) override;
void begin_occlusion_query(rsx::reports::occlusion_query_info* query) override; void begin_occlusion_query(rsx::reports::occlusion_query_info* query) override;

View File

@ -592,7 +592,13 @@ namespace vk
} }
} }
void wait_for_fence(VkFence fence) VkResult wait_for_fence(VkFence fence, u64 timeout)
{
if (timeout)
{
return vkWaitForFences(*g_current_renderer, 1, &fence, VK_FALSE, timeout * 1000ull);
}
else
{ {
while (auto status = vkGetFenceStatus(*g_current_renderer, fence)) while (auto status = vkGetFenceStatus(*g_current_renderer, fence))
{ {
@ -602,9 +608,12 @@ namespace vk
continue; continue;
default: default:
die_with_error(HERE, status); die_with_error(HERE, status);
return; return status;
} }
} }
return VK_SUCCESS;
}
} }
void die_with_error(const char* faulting_addr, VkResult error_code) void die_with_error(const char* faulting_addr, VkResult error_code)

View File

@ -46,6 +46,9 @@
#define VK_NUM_DESCRIPTOR_BINDINGS (VERTEX_TEXTURES_FIRST_BIND_SLOT + 4) #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 namespace rsx
{ {
class fragment_texture; class fragment_texture;
@ -175,9 +178,9 @@ namespace vk
const u64 get_current_frame_id(); const u64 get_current_frame_id();
const u64 get_last_completed_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 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); void die_with_error(const char* faulting_addr, VkResult error_code);

View File

@ -27,13 +27,162 @@
constexpr auto qstr = QString::fromStdString; 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) 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) : QWindow(), m_windowTitle(title), m_gui_settings(gui_settings)
{ {
m_disable_mouse = gui_settings->GetValue(gui::gs_disableMouse).toBool(); m_disable_mouse = gui_settings->GetValue(gui::gs_disableMouse).toBool();
// Workaround for a Qt bug affecting 5.11.1 binaries // 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 //Get version by substringing VersionNumber-buildnumber-commithash to get just the part before the dash
std::string version = rpcs3::version.to_string(); 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 = m_tb_button->progress();
m_tb_progress->setRange(0, m_gauge_max); m_tb_progress->setRange(0, m_gauge_max);
m_tb_progress->setVisible(false); m_tb_progress->setVisible(false);
win32::_ReleaseHook();
#elif HAVE_QTDBUS #elif HAVE_QTDBUS
UpdateProgress(0); UpdateProgress(0);
m_progress_value = 0; m_progress_value = 0;
@ -126,18 +277,16 @@ void gs_frame::showEvent(QShowEvent *event)
void gs_frame::keyPressEvent(QKeyEvent *keyEvent) void gs_frame::keyPressEvent(QKeyEvent *keyEvent)
{ {
auto l_handleKeyEvent = [this ,keyEvent]()
{
switch (keyEvent->key()) switch (keyEvent->key())
{ {
case Qt::Key_L: case Qt::Key_L:
if (keyEvent->modifiers() == Qt::AltModifier) { static int count = 0; LOG_SUCCESS(GENERAL, "Made forced mark %d in log", ++count); } if (keyEvent->modifiers() == Qt::AltModifier) { static int count = 0; LOG_SUCCESS(GENERAL, "Made forced mark %d in log", ++count); }
break; break;
case Qt::Key_Return: case Qt::Key_Return:
if (keyEvent->modifiers() == Qt::AltModifier) { OnFullScreen(); return; } if (keyEvent->modifiers() == Qt::AltModifier) { toggle_fullscreen(); return; }
break; break;
case Qt::Key_Escape: case Qt::Key_Escape:
if (visibility() == FullScreen) { setVisibility(Windowed); return; } if (visibility() == FullScreen) { toggle_fullscreen(); return; }
break; break;
case Qt::Key_P: case Qt::Key_P:
if (keyEvent->modifiers() == Qt::ControlModifier && Emu.IsRunning()) { Emu.Pause(); return; } if (keyEvent->modifiers() == Qt::ControlModifier && Emu.IsRunning()) { Emu.Pause(); return; }
@ -156,13 +305,13 @@ void gs_frame::keyPressEvent(QKeyEvent *keyEvent)
} }
break; break;
} }
};
Emu.CallAfter(l_handleKeyEvent);
} }
void gs_frame::OnFullScreen() void gs_frame::toggle_fullscreen()
{ {
auto l_setFullScreenVis = [=]() if (wm_allow_fullscreen)
{
auto l_setFullScreenVis = [&]()
{ {
if (visibility() == FullScreen) if (visibility() == FullScreen)
{ {
@ -173,7 +322,15 @@ void gs_frame::OnFullScreen()
setVisibility(FullScreen); setVisibility(FullScreen);
} }
}; };
Emu.CallAfter(l_setFullScreenVis); 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() void gs_frame::close()
@ -248,6 +405,10 @@ draw_context_t gs_frame::make_context()
void gs_frame::set_current(draw_context_t ctx) void gs_frame::set_current(draw_context_t ctx)
{ {
Q_UNUSED(ctx); Q_UNUSED(ctx);
#ifdef _WIN32
win32::_InstallHook();
#endif
} }
void gs_frame::delete_context(draw_context_t ctx) void gs_frame::delete_context(draw_context_t ctx)
@ -310,7 +471,7 @@ void gs_frame::mouseDoubleClickEvent(QMouseEvent* ev)
if (ev->button() == Qt::LeftButton) if (ev->button() == Qt::LeftButton)
{ {
OnFullScreen(); toggle_fullscreen();
} }
} }
@ -351,127 +512,6 @@ bool gs_frame::event(QEvent* ev)
return QWindow::event(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) void gs_frame::progress_reset(bool reset_limit)
{ {
#ifdef _WIN32 #ifdef _WIN32

View File

@ -40,13 +40,6 @@ private:
bool m_show_fps; bool m_show_fps;
bool m_disable_mouse; 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: public:
gs_frame(const QString& title, const QRect& geometry, const QIcon& appIcon, const std::shared_ptr<gui_settings>& gui_settings); gs_frame(const QString& title, const QRect& geometry, const QIcon& appIcon, const std::shared_ptr<gui_settings>& gui_settings);
~gs_frame(); ~gs_frame();
@ -54,8 +47,7 @@ public:
draw_context_t make_context() override; draw_context_t make_context() override;
void set_current(draw_context_t context) override; void set_current(draw_context_t context) override;
void delete_context(draw_context_t context) override; void delete_context(draw_context_t context) override;
void toggle_fullscreen() override;
wm_event get_default_wm_event() const override;
// taskbar progress // taskbar progress
void progress_reset(bool reset_limit = false); void progress_reset(bool reset_limit = false);
@ -67,7 +59,6 @@ protected:
virtual void showEvent(QShowEvent *event) override; virtual void showEvent(QShowEvent *event) override;
void keyPressEvent(QKeyEvent *keyEvent) override; void keyPressEvent(QKeyEvent *keyEvent) override;
void OnFullScreen();
void close() override; void close() override;
@ -82,8 +73,6 @@ protected:
int client_width() override; int client_width() override;
int client_height() override; int client_height() override;
bool nativeEvent(const QByteArray &eventType, void *message, long *result) override;
bool event(QEvent* ev) override; bool event(QEvent* ev) override;
private Q_SLOTS: private Q_SLOTS: