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:
parent
987b607cb0
commit
3bfa564ef8
@ -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; };
|
||||||
};
|
};
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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:
|
||||||
|
Loading…
Reference in New Issue
Block a user