From 58860614e361bb3f90a1c65b166e82dc46fcfe1a Mon Sep 17 00:00:00 2001 From: kd-11 Date: Wed, 11 Oct 2017 01:09:04 +0300 Subject: [PATCH] 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 --- rpcs3/Emu/RSX/D3D12/D3D12GSRender.cpp | 6 + rpcs3/Emu/RSX/D3D12/D3D12GSRender.h | 1 + rpcs3/Emu/RSX/GL/GLGSRender.cpp | 2 + rpcs3/Emu/RSX/GSRender.h | 38 +++ rpcs3/Emu/RSX/VK/VKGSRender.cpp | 395 +++++++++++++++----------- rpcs3/Emu/RSX/VK/VKGSRender.h | 2 + rpcs3/rpcs3qt/gs_frame.cpp | 89 ++++++ rpcs3/rpcs3qt/gs_frame.h | 8 + 8 files changed, 379 insertions(+), 162 deletions(-) diff --git a/rpcs3/Emu/RSX/D3D12/D3D12GSRender.cpp b/rpcs3/Emu/RSX/D3D12/D3D12GSRender.cpp index 8e988602af..275e611cd5 100644 --- a/rpcs3/Emu/RSX/D3D12/D3D12GSRender.cpp +++ b/rpcs3/Emu/RSX/D3D12/D3D12GSRender.cpp @@ -301,6 +301,12 @@ void D3D12GSRender::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) { switch (cmd) diff --git a/rpcs3/Emu/RSX/D3D12/D3D12GSRender.h b/rpcs3/Emu/RSX/D3D12/D3D12GSRender.h index 80c86f3f07..066eabd467 100644 --- a/rpcs3/Emu/RSX/D3D12/D3D12GSRender.h +++ b/rpcs3/Emu/RSX/D3D12/D3D12GSRender.h @@ -173,6 +173,7 @@ private: protected: virtual void on_init_thread() override; virtual void on_exit() override; + virtual void do_local_task() override; virtual bool do_method(u32 cmd, u32 arg) override; virtual void end() override; virtual void flip(int buffer) override; diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.cpp b/rpcs3/Emu/RSX/GL/GLGSRender.cpp index 350228b41f..5e3c5c1bed 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.cpp +++ b/rpcs3/Emu/RSX/GL/GLGSRender.cpp @@ -1231,6 +1231,8 @@ void GLGSRender::on_notify_memory_unmapped(u32 address_base, u32 size) void GLGSRender::do_local_task() { + m_frame->clear_wm_events(); + std::lock_guard lock(queue_guard); work_queue.remove_if([](work_item &q) { return q.received; }); diff --git a/rpcs3/Emu/RSX/GSRender.h b/rpcs3/Emu/RSX/GSRender.h index 7973281f93..f6f5693c09 100644 --- a/rpcs3/Emu/RSX/GSRender.h +++ b/rpcs3/Emu/RSX/GSRender.h @@ -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; using draw_context_t = std::shared_ptr; @@ -45,6 +54,35 @@ public: protected: virtual void delete_context(void* ctx) = 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 diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index 11c042852f..3b79cef44d 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -1584,13 +1584,27 @@ void VKGSRender::present(frame_context_t *ctx) verify(HERE), ctx->present_image != UINT32_MAX; VkSwapchainKHR swap_chain = (VkSwapchainKHR)(*m_swap_chain); - VkPresentInfoKHR present = {}; - present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; - present.pNext = nullptr; - present.swapchainCount = 1; - present.pSwapchains = &swap_chain; - present.pImageIndices = &ctx->present_image; - CHECK_RESULT(m_swap_chain->queuePresentKHR(m_swap_chain->get_present_queue(), &present)); + if (!present_surface_dirty_flag) + { + VkPresentInfoKHR present = {}; + present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + present.pNext = nullptr; + present.swapchainCount = 1; + 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 ctx->present_image = UINT32_MAX; @@ -1698,6 +1712,75 @@ void VKGSRender::do_local_task() if (!cb->pending) 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) @@ -2336,6 +2419,8 @@ void VKGSRender::reinitialize_swapchain() m_current_command_buffer->reset(); open_command_buffer(); + + present_surface_dirty_flag = false; } void VKGSRender::flip(int buffer) @@ -2357,18 +2442,6 @@ void VKGSRender::flip(int buffer) 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 flip_start = steady_clock::now(); close_render_pass(); @@ -2399,171 +2472,169 @@ void VKGSRender::flip(int buffer) process_swap_request(m_current_frame, true); } - if (!resize_screen) + if (present_surface_dirty_flag) { - u32 buffer_width = display_buffers[buffer].width; - u32 buffer_height = display_buffers[buffer].height; - u32 buffer_pitch = display_buffers[buffer].pitch; + //Recreate swapchain and continue as usual + reinitialize_swapchain(); + } - 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 }; - sizei new_size = csize; + coordi aspect_ratio; - 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; - const double rq = (double)new_size.width / new_size.height; - 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; - } + new_size.height = int(new_size.height / q); + aspect_ratio.y = (csize.height - new_size.height) / 2; } - - 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)) + else if (q < 1.0) { - 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 + new_size.width = int(new_size.width * q); + aspect_ratio.x = (csize.width - new_size.width) / 2; + } + } - //Whatever returned from status, this is now a spin - timeout = 0ull; - for (auto &ctx : frame_context_storage) + 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) + { + 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(); - if (!ctx.swap_command_buffer->pending) - { - //Release in case there is competition for frame resources - 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 direct_fbo; - std::vector> 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(*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)); + 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."); + 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 { - //Recreate the swapchain to resize it - //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 - reinitialize_swapchain(); + //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 direct_fbo; + std::vector> 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(*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 flip_end = steady_clock::now(); m_flip_time = std::chrono::duration_cast(flip_end - flip_start).count(); diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.h b/rpcs3/Emu/RSX/VK/VKGSRender.h index a8b5e62924..cacb64b997 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.h +++ b/rpcs3/Emu/RSX/VK/VKGSRender.h @@ -159,6 +159,8 @@ private: std::unique_ptr m_draw_fbo; + bool present_surface_dirty_flag = false; + u64 m_last_heap_sync_time = 0; vk::vk_data_heap m_attrib_ring_info; vk::vk_data_heap m_uniform_buffer_ring_info; diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index 3c86e4c7cf..de075d906a 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -12,6 +12,10 @@ #include "rpcs3_version.h" +#ifdef _WIN32 +#include +#endif + constexpr auto qstr = QString::fromStdString; 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); } + +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 lock(wm_event_lock); + MSG* msg = static_cast(message); + switch (msg->message) + { + case WM_WINDOWPOSCHANGING: + { + const auto flags = reinterpret_cast(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(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; +} diff --git a/rpcs3/rpcs3qt/gs_frame.h b/rpcs3/rpcs3qt/gs_frame.h index 32094b5d14..b29c523a52 100644 --- a/rpcs3/rpcs3qt/gs_frame.h +++ b/rpcs3/rpcs3qt/gs_frame.h @@ -14,8 +14,14 @@ class gs_frame : public QWindow, public GSFrameBase 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 + public: gs_frame(const QString& title, int w, int h, QIcon appIcon, bool disableMouse); + + wm_event get_default_wm_event() const override; protected: virtual void paintEvent(QPaintEvent *event); @@ -38,6 +44,8 @@ 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: void HandleCursor(QWindow::Visibility visibility);