rsx/qt: Implement native window hooks for win32 windows to allow communication between WndProc thread and rsx::thread

- This communication is important in communicating window events. Helps properly synchronize swapchain management on vulkan and stops nvidia crashing
- Do not block the message queue lest the driver detect window as not responding
This commit is contained in:
kd-11 2017-10-11 01:09:04 +03:00
parent e8bde583ef
commit 58860614e3
8 changed files with 379 additions and 162 deletions

View File

@ -301,6 +301,12 @@ void D3D12GSRender::on_exit()
return GSRender::on_exit(); return GSRender::on_exit();
} }
void D3D12GSRender::do_local_task()
{
//TODO
m_frame->clear_wm_events();
}
bool D3D12GSRender::do_method(u32 cmd, u32 arg) bool D3D12GSRender::do_method(u32 cmd, u32 arg)
{ {
switch (cmd) switch (cmd)

View File

@ -173,6 +173,7 @@ private:
protected: protected:
virtual void on_init_thread() override; virtual void on_init_thread() override;
virtual void on_exit() override; virtual void on_exit() override;
virtual void do_local_task() override;
virtual bool do_method(u32 cmd, u32 arg) override; virtual bool do_method(u32 cmd, u32 arg) override;
virtual void end() override; virtual void end() override;
virtual void flip(int buffer) override; virtual void flip(int buffer) override;

View File

@ -1231,6 +1231,8 @@ void GLGSRender::on_notify_memory_unmapped(u32 address_base, u32 size)
void GLGSRender::do_local_task() void GLGSRender::do_local_task()
{ {
m_frame->clear_wm_events();
std::lock_guard<std::mutex> lock(queue_guard); std::lock_guard<std::mutex> lock(queue_guard);
work_queue.remove_if([](work_item &q) { return q.received; }); work_queue.remove_if([](work_item &q) { return q.received; });

View File

@ -18,6 +18,15 @@ 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_moved //window moved without resize
};
using RSXDebuggerPrograms = std::vector<RSXDebuggerProgram>; using RSXDebuggerPrograms = std::vector<RSXDebuggerProgram>;
using draw_context_t = std::shared_ptr<void>; using draw_context_t = std::shared_ptr<void>;
@ -45,6 +54,35 @@ public:
protected: protected:
virtual void delete_context(void* ctx) = 0; virtual void delete_context(void* ctx) = 0;
virtual void* make_context() = 0; virtual void* make_context() = 0;
//window manager event management
wm_event m_raised_event;
std::atomic_bool wm_event_raised = {};
public:
//synchronize native window access
std::mutex wm_event_lock;
virtual wm_event get_default_wm_event() const = 0;
void clear_wm_events()
{
m_raised_event = wm_event::none;
wm_event_raised.store(false);
}
wm_event get_wm_event()
{
if (wm_event_raised.load(std::memory_order_consume))
{
auto result = m_raised_event;
m_raised_event = wm_event::none;
wm_event_raised.store(false);
return result;
}
return get_default_wm_event();
}
}; };
class GSRender : public rsx::thread class GSRender : public rsx::thread

View File

@ -1584,13 +1584,27 @@ void VKGSRender::present(frame_context_t *ctx)
verify(HERE), ctx->present_image != UINT32_MAX; verify(HERE), ctx->present_image != UINT32_MAX;
VkSwapchainKHR swap_chain = (VkSwapchainKHR)(*m_swap_chain); VkSwapchainKHR swap_chain = (VkSwapchainKHR)(*m_swap_chain);
VkPresentInfoKHR present = {}; if (!present_surface_dirty_flag)
present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; {
present.pNext = nullptr; VkPresentInfoKHR present = {};
present.swapchainCount = 1; present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
present.pSwapchains = &swap_chain; present.pNext = nullptr;
present.pImageIndices = &ctx->present_image; present.swapchainCount = 1;
CHECK_RESULT(m_swap_chain->queuePresentKHR(m_swap_chain->get_present_queue(), &present)); present.pSwapchains = &swap_chain;
present.pImageIndices = &ctx->present_image;
switch (VkResult error = m_swap_chain->queuePresentKHR(m_swap_chain->get_present_queue(), &present))
{
case VK_SUCCESS:
case VK_SUBOPTIMAL_KHR:
break;
case VK_ERROR_OUT_OF_DATE_KHR:
present_surface_dirty_flag = true;
break;
default:
vk::die_with_error(HERE, error);
}
}
//Presentation image released; reset value //Presentation image released; reset value
ctx->present_image = UINT32_MAX; ctx->present_image = UINT32_MAX;
@ -1698,6 +1712,75 @@ void VKGSRender::do_local_task()
if (!cb->pending) if (!cb->pending)
m_last_flushable_cb = -1; m_last_flushable_cb = -1;
} }
#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::window_moved:
handled = true;
break;
case wm_event::geometry_change_in_progress:
timeout += 10; //extend timeout to wait for user to finish resizing
break;
}
if (handled)
break;
else
{
//wait for window manager event
std::this_thread::sleep_for(10ms);
timeout -= 10;
}
}
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);
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;
}
#endif
} }
bool VKGSRender::do_method(u32 cmd, u32 arg) bool VKGSRender::do_method(u32 cmd, u32 arg)
@ -2336,6 +2419,8 @@ void VKGSRender::reinitialize_swapchain()
m_current_command_buffer->reset(); m_current_command_buffer->reset();
open_command_buffer(); open_command_buffer();
present_surface_dirty_flag = false;
} }
void VKGSRender::flip(int buffer) void VKGSRender::flip(int buffer)
@ -2357,18 +2442,6 @@ void VKGSRender::flip(int buffer)
return; return;
} }
bool resize_screen = false;
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)
resize_screen = true;
}
std::chrono::time_point<steady_clock> flip_start = steady_clock::now(); std::chrono::time_point<steady_clock> flip_start = steady_clock::now();
close_render_pass(); close_render_pass();
@ -2399,171 +2472,169 @@ void VKGSRender::flip(int buffer)
process_swap_request(m_current_frame, true); process_swap_request(m_current_frame, true);
} }
if (!resize_screen) if (present_surface_dirty_flag)
{ {
u32 buffer_width = display_buffers[buffer].width; //Recreate swapchain and continue as usual
u32 buffer_height = display_buffers[buffer].height; reinitialize_swapchain();
u32 buffer_pitch = display_buffers[buffer].pitch; }
areai screen_area = coordi({}, { (int)buffer_width, (int)buffer_height }); u32 buffer_width = display_buffers[buffer].width;
u32 buffer_height = display_buffers[buffer].height;
u32 buffer_pitch = display_buffers[buffer].pitch;
coordi aspect_ratio; areai screen_area = coordi({}, { (int)buffer_width, (int)buffer_height });
sizei csize = { frame_width, frame_height }; coordi aspect_ratio;
sizei new_size = csize;
if (!g_cfg.video.stretch_to_display_area) sizei csize = { (s32)m_client_width, (s32)m_client_height };
sizei new_size = csize;
if (!g_cfg.video.stretch_to_display_area)
{
const double aq = (double)buffer_width / buffer_height;
const double rq = (double)new_size.width / new_size.height;
const double q = aq / rq;
if (q > 1.0)
{ {
const double aq = (double)buffer_width / buffer_height; new_size.height = int(new_size.height / q);
const double rq = (double)new_size.width / new_size.height; aspect_ratio.y = (csize.height - new_size.height) / 2;
const double q = aq / rq;
if (q > 1.0)
{
new_size.height = int(new_size.height / q);
aspect_ratio.y = (csize.height - new_size.height) / 2;
}
else if (q < 1.0)
{
new_size.width = int(new_size.width * q);
aspect_ratio.x = (csize.width - new_size.width) / 2;
}
} }
else if (q < 1.0)
aspect_ratio.size = new_size;
//Prepare surface for new frame. Set no timeout here so that we wait for the next image if need be
verify(HERE), m_current_frame->present_image == UINT32_MAX;
u64 timeout = m_swap_chain->get_swap_image_count() <= VK_MAX_ASYNC_FRAMES? 0ull: 100000000ull;
while (VkResult status = vkAcquireNextImageKHR((*m_device), (*m_swap_chain), timeout, m_current_frame->present_semaphore, VK_NULL_HANDLE, &m_current_frame->present_image))
{ {
switch (status) new_size.width = int(new_size.width * q);
{ aspect_ratio.x = (csize.width - new_size.width) / 2;
case VK_TIMEOUT: }
case VK_NOT_READY: }
{
//In some cases, after a fullscreen switch, the driver only allows N-1 images to be acquirable, where N = number of available swap images.
//This means that any acquired images have to be released
//before acquireNextImage can return successfully. This is despite the driver reporting 2 swap chain images available
//This makes fullscreen performance slower than windowed performance as throughput is lowered due to losing one presentable image
//Found on AMD Crimson 17.7.2
//Whatever returned from status, this is now a spin aspect_ratio.size = new_size;
timeout = 0ull;
for (auto &ctx : frame_context_storage) //Prepare surface for new frame. Set no timeout here so that we wait for the next image if need be
verify(HERE), m_current_frame->present_image == UINT32_MAX;
u64 timeout = m_swap_chain->get_swap_image_count() <= VK_MAX_ASYNC_FRAMES? 0ull: 100000000ull;
while (VkResult status = vkAcquireNextImageKHR((*m_device), (*m_swap_chain), timeout, m_current_frame->present_semaphore, VK_NULL_HANDLE, &m_current_frame->present_image))
{
switch (status)
{
case VK_TIMEOUT:
case VK_NOT_READY:
{
//In some cases, after a fullscreen switch, the driver only allows N-1 images to be acquirable, where N = number of available swap images.
//This means that any acquired images have to be released
//before acquireNextImage can return successfully. This is despite the driver reporting 2 swap chain images available
//This makes fullscreen performance slower than windowed performance as throughput is lowered due to losing one presentable image
//Found on AMD Crimson 17.7.2
//Whatever returned from status, this is now a spin
timeout = 0ull;
for (auto &ctx : frame_context_storage)
{
if (ctx.swap_command_buffer)
{ {
if (ctx.swap_command_buffer) ctx.swap_command_buffer->poke();
if (!ctx.swap_command_buffer->pending)
{ {
ctx.swap_command_buffer->poke(); //Release in case there is competition for frame resources
if (!ctx.swap_command_buffer->pending) process_swap_request(&ctx, true);
{
//Release in case there is competition for frame resources
process_swap_request(&ctx, true);
}
} }
} }
continue;
}
case VK_ERROR_OUT_OF_DATE_KHR:
LOG_ERROR(RSX, "vkAcquireNextImageKHR failed with VK_ERROR_OUT_OF_DATE_KHR. Flip request ignored until surface is recreated.");
reinitialize_swapchain();
return;
default:
vk::die_with_error(HERE, status);
}
}
//Confirm that the driver did not silently fail
verify(HERE), m_current_frame->present_image != UINT32_MAX;
//Blit contents to screen..
vk::image* image_to_flip = nullptr;
if (std::get<1>(m_rtts.m_bound_render_targets[0]) != nullptr)
image_to_flip = std::get<1>(m_rtts.m_bound_render_targets[0]);
else if (std::get<1>(m_rtts.m_bound_render_targets[1]) != nullptr)
image_to_flip = std::get<1>(m_rtts.m_bound_render_targets[1]);
VkImage target_image = m_swap_chain->get_swap_chain_image(m_current_frame->present_image);
if (image_to_flip)
{
vk::copy_scaled_image(*m_current_command_buffer, image_to_flip->value, target_image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
0, 0, image_to_flip->width(), image_to_flip->height(), aspect_ratio.x, aspect_ratio.y, aspect_ratio.width, aspect_ratio.height, 1, VK_IMAGE_ASPECT_COLOR_BIT);
}
else
{
//No draw call was issued!
VkImageSubresourceRange range = vk::get_image_subresource_range(0, 0, 1, 1, VK_IMAGE_ASPECT_COLOR_BIT);
VkClearColorValue clear_black = { 0 };
vk::change_image_layout(*m_current_command_buffer, m_swap_chain->get_swap_chain_image(m_current_frame->present_image), VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_GENERAL, range);
vkCmdClearColorImage(*m_current_command_buffer, m_swap_chain->get_swap_chain_image(m_current_frame->present_image), VK_IMAGE_LAYOUT_GENERAL, &clear_black, 1, &range);
vk::change_image_layout(*m_current_command_buffer, m_swap_chain->get_swap_chain_image(m_current_frame->present_image), VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, range);
}
std::unique_ptr<vk::framebuffer_holder> direct_fbo;
std::vector<std::unique_ptr<vk::image_view>> swap_image_view;
if (g_cfg.video.overlay)
{
//Change the image layout whilst setting up a dependency on waiting for the blit op to finish before we start writing
auto subres = vk::get_image_subresource_range(0, 0, 1, 1, VK_IMAGE_ASPECT_COLOR_BIT);
VkImageMemoryBarrier barrier = {};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
barrier.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
barrier.image = target_image;
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.subresourceRange = subres;
vkCmdPipelineBarrier(*m_current_command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_DEPENDENCY_BY_REGION_BIT, 0, nullptr, 0, nullptr, 1, &barrier);
size_t idx = vk::get_render_pass_location(m_swap_chain->get_surface_format(), VK_FORMAT_UNDEFINED, 1);
VkRenderPass single_target_pass = m_render_passes[idx];
for (auto It = m_framebuffers_to_clean.begin(); It != m_framebuffers_to_clean.end(); It++)
{
auto &fbo = *It;
if (fbo->attachments[0]->info.image == target_image)
{
direct_fbo.swap(fbo);
direct_fbo->reset_refs();
m_framebuffers_to_clean.erase(It);
break;
}
} }
if (!direct_fbo) continue;
{
swap_image_view.push_back(std::make_unique<vk::image_view>(*m_device, target_image, VK_IMAGE_VIEW_TYPE_2D, m_swap_chain->get_surface_format(), vk::default_component_map(), subres));
direct_fbo.reset(new vk::framebuffer_holder(*m_device, single_target_pass, m_client_width, m_client_height, std::move(swap_image_view)));
}
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 0, direct_fbo->width(), direct_fbo->height(), "draw calls: " + std::to_string(m_draw_calls));
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 18, direct_fbo->width(), direct_fbo->height(), "draw call setup: " + std::to_string(m_setup_time) + "us");
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 36, direct_fbo->width(), direct_fbo->height(), "vertex upload time: " + std::to_string(m_vertex_upload_time) + "us");
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 54, direct_fbo->width(), direct_fbo->height(), "texture upload time: " + std::to_string(m_textures_upload_time) + "us");
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 72, direct_fbo->width(), direct_fbo->height(), "draw call execution: " + std::to_string(m_draw_time) + "us");
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 90, direct_fbo->width(), direct_fbo->height(), "submit and flip: " + std::to_string(m_flip_time) + "us");
auto num_dirty_textures = m_texture_cache.get_unreleased_textures_count();
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 126, direct_fbo->width(), direct_fbo->height(), "Unreleased textures: " + std::to_string(num_dirty_textures));
vk::change_image_layout(*m_current_command_buffer, target_image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, subres);
m_framebuffers_to_clean.push_back(std::move(direct_fbo));
} }
case VK_ERROR_OUT_OF_DATE_KHR:
LOG_ERROR(RSX, "vkAcquireNextImageKHR failed with VK_ERROR_OUT_OF_DATE_KHR. Flip request ignored until surface is recreated.");
present_surface_dirty_flag = true;
reinitialize_swapchain();
return;
default:
vk::die_with_error(HERE, status);
}
}
queue_swap_request(); //Confirm that the driver did not silently fail
verify(HERE), m_current_frame->present_image != UINT32_MAX;
//Blit contents to screen..
vk::image* image_to_flip = nullptr;
if (std::get<1>(m_rtts.m_bound_render_targets[0]) != nullptr)
image_to_flip = std::get<1>(m_rtts.m_bound_render_targets[0]);
else if (std::get<1>(m_rtts.m_bound_render_targets[1]) != nullptr)
image_to_flip = std::get<1>(m_rtts.m_bound_render_targets[1]);
VkImage target_image = m_swap_chain->get_swap_chain_image(m_current_frame->present_image);
if (image_to_flip)
{
vk::copy_scaled_image(*m_current_command_buffer, image_to_flip->value, target_image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
0, 0, image_to_flip->width(), image_to_flip->height(), aspect_ratio.x, aspect_ratio.y, aspect_ratio.width, aspect_ratio.height, 1, VK_IMAGE_ASPECT_COLOR_BIT);
} }
else else
{ {
//Recreate the swapchain to resize it //No draw call was issued!
//NOTE: Nvidia driver does not invalidate the swapchain immediately on window size changes. The event will fire after a while and an OUT_OF_DATE error will be returned in subsequent rendering commands VkImageSubresourceRange range = vk::get_image_subresource_range(0, 0, 1, 1, VK_IMAGE_ASPECT_COLOR_BIT);
reinitialize_swapchain(); VkClearColorValue clear_black = { 0 };
vk::change_image_layout(*m_current_command_buffer, m_swap_chain->get_swap_chain_image(m_current_frame->present_image), VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_GENERAL, range);
vkCmdClearColorImage(*m_current_command_buffer, m_swap_chain->get_swap_chain_image(m_current_frame->present_image), VK_IMAGE_LAYOUT_GENERAL, &clear_black, 1, &range);
vk::change_image_layout(*m_current_command_buffer, m_swap_chain->get_swap_chain_image(m_current_frame->present_image), VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, range);
} }
std::unique_ptr<vk::framebuffer_holder> direct_fbo;
std::vector<std::unique_ptr<vk::image_view>> swap_image_view;
if (g_cfg.video.overlay)
{
//Change the image layout whilst setting up a dependency on waiting for the blit op to finish before we start writing
auto subres = vk::get_image_subresource_range(0, 0, 1, 1, VK_IMAGE_ASPECT_COLOR_BIT);
VkImageMemoryBarrier barrier = {};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
barrier.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
barrier.image = target_image;
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.subresourceRange = subres;
vkCmdPipelineBarrier(*m_current_command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_DEPENDENCY_BY_REGION_BIT, 0, nullptr, 0, nullptr, 1, &barrier);
size_t idx = vk::get_render_pass_location(m_swap_chain->get_surface_format(), VK_FORMAT_UNDEFINED, 1);
VkRenderPass single_target_pass = m_render_passes[idx];
for (auto It = m_framebuffers_to_clean.begin(); It != m_framebuffers_to_clean.end(); It++)
{
auto &fbo = *It;
if (fbo->attachments[0]->info.image == target_image)
{
direct_fbo.swap(fbo);
direct_fbo->reset_refs();
m_framebuffers_to_clean.erase(It);
break;
}
}
if (!direct_fbo)
{
swap_image_view.push_back(std::make_unique<vk::image_view>(*m_device, target_image, VK_IMAGE_VIEW_TYPE_2D, m_swap_chain->get_surface_format(), vk::default_component_map(), subres));
direct_fbo.reset(new vk::framebuffer_holder(*m_device, single_target_pass, m_client_width, m_client_height, std::move(swap_image_view)));
}
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 0, direct_fbo->width(), direct_fbo->height(), "draw calls: " + std::to_string(m_draw_calls));
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 18, direct_fbo->width(), direct_fbo->height(), "draw call setup: " + std::to_string(m_setup_time) + "us");
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 36, direct_fbo->width(), direct_fbo->height(), "vertex upload time: " + std::to_string(m_vertex_upload_time) + "us");
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 54, direct_fbo->width(), direct_fbo->height(), "texture upload time: " + std::to_string(m_textures_upload_time) + "us");
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 72, direct_fbo->width(), direct_fbo->height(), "draw call execution: " + std::to_string(m_draw_time) + "us");
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 90, direct_fbo->width(), direct_fbo->height(), "submit and flip: " + std::to_string(m_flip_time) + "us");
auto num_dirty_textures = m_texture_cache.get_unreleased_textures_count();
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 126, direct_fbo->width(), direct_fbo->height(), "Unreleased textures: " + std::to_string(num_dirty_textures));
vk::change_image_layout(*m_current_command_buffer, target_image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, subres);
m_framebuffers_to_clean.push_back(std::move(direct_fbo));
}
queue_swap_request();
std::chrono::time_point<steady_clock> flip_end = steady_clock::now(); std::chrono::time_point<steady_clock> flip_end = steady_clock::now();
m_flip_time = std::chrono::duration_cast<std::chrono::microseconds>(flip_end - flip_start).count(); m_flip_time = std::chrono::duration_cast<std::chrono::microseconds>(flip_end - flip_start).count();

View File

@ -159,6 +159,8 @@ private:
std::unique_ptr<vk::framebuffer_holder> m_draw_fbo; std::unique_ptr<vk::framebuffer_holder> m_draw_fbo;
bool present_surface_dirty_flag = false;
u64 m_last_heap_sync_time = 0; u64 m_last_heap_sync_time = 0;
vk::vk_data_heap m_attrib_ring_info; vk::vk_data_heap m_attrib_ring_info;
vk::vk_data_heap m_uniform_buffer_ring_info; vk::vk_data_heap m_uniform_buffer_ring_info;

View File

@ -12,6 +12,10 @@
#include "rpcs3_version.h" #include "rpcs3_version.h"
#ifdef _WIN32
#include <windows.h>
#endif
constexpr auto qstr = QString::fromStdString; constexpr auto qstr = QString::fromStdString;
gs_frame::gs_frame(const QString& title, int w, int h, QIcon appIcon, bool disableMouse) gs_frame::gs_frame(const QString& title, int w, int h, QIcon appIcon, bool disableMouse)
@ -238,3 +242,88 @@ 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
//Wait for consumer to clear notification pending flag
while (wm_event_raised.load(std::memory_order_consume));
{
std::lock_guard<std::mutex> lock(wm_event_lock);
MSG* msg = static_cast<MSG*>(message);
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;
if (flags & SWP_NOSIZE)
m_raised_event = wm_event::window_moved;
else
m_raised_event = wm_event::window_resized;
//Just finished resizing using maximize or SWP
m_in_sizing_event = false;
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));
#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;
}

View File

@ -14,8 +14,14 @@ class gs_frame : public QWindow, public GSFrameBase
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
public: public:
gs_frame(const QString& title, int w, int h, QIcon appIcon, bool disableMouse); gs_frame(const QString& title, int w, int h, QIcon appIcon, bool disableMouse);
wm_event get_default_wm_event() const override;
protected: protected:
virtual void paintEvent(QPaintEvent *event); virtual void paintEvent(QPaintEvent *event);
@ -38,6 +44,8 @@ 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:
void HandleCursor(QWindow::Visibility visibility); void HandleCursor(QWindow::Visibility visibility);