diff --git a/rpcs3/CMakeLists.txt b/rpcs3/CMakeLists.txt index acf359a69a0b..7b32e8690769 100644 --- a/rpcs3/CMakeLists.txt +++ b/rpcs3/CMakeLists.txt @@ -394,7 +394,7 @@ if(NOT MSVC) endif() if(WIN32) - target_link_libraries(rpcs3 ws2_32.lib Winmm.lib Psapi.lib VKstatic.1 glslang OSDependent OGLCompiler SPIRV HLSL setupapi.lib hidapi-hid Shlwapi.lib) + target_link_libraries(rpcs3 ws2_32.lib Winmm.lib Psapi.lib gdi32.lib VKstatic.1 glslang OSDependent OGLCompiler SPIRV HLSL setupapi.lib hidapi-hid Shlwapi.lib) if(NOT MSVC) target_link_libraries(rpcs3 ${OPENGL_LIBRARIES} opengl32.lib glu32.lib libpthread) else() diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index 9198068267b8..90c820f98e3f 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -472,8 +472,6 @@ namespace VKGSRender::VKGSRender() : GSRender() { - //shaders_cache.load(rsx::old_shaders_cache::shader_language::glsl); - u32 instance_handle = m_thread_context.createInstance("RPCS3"); if (instance_handle > 0) @@ -505,15 +503,15 @@ VKGSRender::VKGSRender() : GSRender() display_handle_t display = m_frame->handle(); -#ifdef _WIN32 - - HINSTANCE hInstance = NULL; +#ifndef _WIN32 + display.match([this](std::pair p) { m_display_handle = p.first; XFlush(m_display_handle); }, [](auto _) {}); +#endif for (auto &gpu : gpus) { if (gpu.name() == adapter_name) { - m_swap_chain = m_thread_context.createSwapChain(hInstance, display, gpu); + m_swapchain.reset(m_thread_context.createSwapChain(display, gpu)); gpu_found = true; break; } @@ -521,44 +519,28 @@ VKGSRender::VKGSRender() : GSRender() if (!gpu_found || adapter_name.empty()) { - m_swap_chain = m_thread_context.createSwapChain(hInstance, display, gpus[0]); + m_swapchain.reset(m_thread_context.createSwapChain(display, gpus[0])); } -#elif HAVE_VULKAN - - display.match([](std::pair p) { XFlush(p.first); }, [](auto _) {}); - - for (auto &gpu : gpus) + if (!m_swapchain) { - if (gpu.name() == adapter_name) - { - m_swap_chain = m_thread_context.createSwapChain(display, gpu); - gpu_found = true; - break; - } + m_device = VK_NULL_HANDLE; + LOG_FATAL(RSX, "Could not successfully initialize a swapchain"); + return; } - if (!gpu_found || adapter_name.empty()) - { - m_swap_chain = m_thread_context.createSwapChain(display, gpus[0]); - } + m_device = (vk::render_device*)(&m_swapchain->get_device()); - display.match([&](std::pair p) { m_display_handle = p.first; }, [](auto _) {}); - -#endif - - m_device = (vk::render_device *)(&m_swap_chain->get_device()); - - m_memory_type_mapping = get_memory_mapping(m_device->gpu()); + m_memory_type_mapping = m_device->get_memory_mapping(); m_optimal_tiling_supported_formats = vk::get_optimal_tiling_supported_formats(m_device->gpu()); vk::set_current_thread_ctx(m_thread_context); - vk::set_current_renderer(m_swap_chain->get_device()); + vk::set_current_renderer(m_swapchain->get_device()); m_client_width = m_frame->client_width(); m_client_height = m_frame->client_height(); - if (!m_swap_chain->init_swapchain(m_client_width, m_client_height)) + if (!m_swapchain->init(m_client_width, m_client_height)) present_surface_dirty_flag = true; //create command buffer... @@ -619,7 +601,7 @@ VKGSRender::VKGSRender() : GSRender() if (g_cfg.video.overlay) { - size_t idx = vk::get_render_pass_location( m_swap_chain->get_surface_format(), VK_FORMAT_UNDEFINED, 1); + size_t idx = vk::get_render_pass_location( m_swapchain->get_surface_format(), VK_FORMAT_UNDEFINED, 1); m_text_writer.reset(new vk::text_writer()); m_text_writer->init(*m_device, m_memory_type_mapping, m_render_passes[idx]); } @@ -641,25 +623,22 @@ VKGSRender::VKGSRender() : GSRender() open_command_buffer(); - for (u32 i = 0; i < m_swap_chain->get_swap_image_count(); ++i) + for (u32 i = 0; i < m_swapchain->get_swap_image_count(); ++i) { + const auto target_layout = m_swapchain->get_optimal_present_layout(); + const auto target_image = m_swapchain->get_image(i); VkClearColorValue clear_color{}; VkImageSubresourceRange range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; - vk::change_image_layout(*m_current_command_buffer, m_swap_chain->get_swap_chain_image(i), - VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, - range); - - vkCmdClearColorImage(*m_current_command_buffer, m_swap_chain->get_swap_chain_image(i), VK_IMAGE_LAYOUT_GENERAL, &clear_color, 1, &range); - vk::change_image_layout(*m_current_command_buffer, m_swap_chain->get_swap_chain_image(i), - VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - range); + vk::change_image_layout(*m_current_command_buffer, target_image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, range); + vkCmdClearColorImage(*m_current_command_buffer, target_image, VK_IMAGE_LAYOUT_GENERAL, &clear_color, 1, &range); + vk::change_image_layout(*m_current_command_buffer, target_image, VK_IMAGE_LAYOUT_GENERAL, target_layout, range); } m_current_frame = &frame_context_storage[0]; - m_texture_cache.initialize((*m_device), m_memory_type_mapping, m_optimal_tiling_supported_formats, m_swap_chain->get_present_queue(), + m_texture_cache.initialize((*m_device), m_memory_type_mapping, m_optimal_tiling_supported_formats, m_swapchain->get_graphics_queue(), m_texture_upload_buffer_ring_info); m_ui_renderer.reset(new vk::ui_overlay_renderer()); @@ -774,10 +753,8 @@ VKGSRender::~VKGSRender() m_secondary_command_buffer_pool.destroy(); //Device handles/contexts - m_swap_chain->destroy(); + m_swapchain->destroy(); m_thread_context.close(); - - delete m_swap_chain; #if !defined(_WIN32) && defined(HAVE_VULKAN) if (m_display_handle) @@ -790,7 +767,7 @@ bool VKGSRender::on_access_violation(u32 address, bool is_writing) vk::texture_cache::thrashed_set result; { std::lock_guard lock(m_secondary_cb_guard); - result = std::move(m_texture_cache.invalidate_address(address, is_writing, false, m_secondary_command_buffer, m_memory_type_mapping, m_swap_chain->get_present_queue())); + result = std::move(m_texture_cache.invalidate_address(address, is_writing, false, m_secondary_command_buffer, m_memory_type_mapping, m_swapchain->get_graphics_queue())); } if (!result.violation_handled) @@ -852,7 +829,7 @@ bool VKGSRender::on_access_violation(u32 address, bool is_writing) m_flush_requests.producer_wait(); } - m_texture_cache.flush_all(result, m_secondary_command_buffer, m_memory_type_mapping, m_swap_chain->get_present_queue()); + m_texture_cache.flush_all(result, m_secondary_command_buffer, m_memory_type_mapping, m_swapchain->get_graphics_queue()); if (has_queue_ref) { @@ -868,7 +845,7 @@ void VKGSRender::on_notify_memory_unmapped(u32 address_base, u32 size) { std::lock_guard lock(m_secondary_cb_guard); if (m_texture_cache.invalidate_range(address_base, size, true, true, false, - m_secondary_command_buffer, m_memory_type_mapping, m_swap_chain->get_present_queue()).violation_handled) + m_secondary_command_buffer, m_memory_type_mapping, m_swapchain->get_graphics_queue()).violation_handled) { m_texture_cache.purge_dirty(); { @@ -1809,7 +1786,7 @@ void VKGSRender::copy_render_targets_to_dma_location() continue; m_texture_cache.flush_memory_to_cache(m_surface_info[index].address, m_surface_info[index].pitch * m_surface_info[index].height, true, - *m_current_command_buffer, m_memory_type_mapping, m_swap_chain->get_present_queue()); + *m_current_command_buffer, m_memory_type_mapping, m_swapchain->get_graphics_queue()); } } @@ -1818,7 +1795,7 @@ void VKGSRender::copy_render_targets_to_dma_location() if (m_depth_surface_info.pitch) { m_texture_cache.flush_memory_to_cache(m_depth_surface_info.address, m_depth_surface_info.pitch * m_depth_surface_info.height, true, - *m_current_command_buffer, m_memory_type_mapping, m_swap_chain->get_present_queue()); + *m_current_command_buffer, m_memory_type_mapping, m_swapchain->get_graphics_queue()); } } @@ -1927,18 +1904,10 @@ void VKGSRender::advance_queued_frames() void VKGSRender::present(frame_context_t *ctx) { verify(HERE), ctx->present_image != UINT32_MAX; - VkSwapchainKHR swap_chain = (VkSwapchainKHR)(*m_swap_chain); 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)) + switch (VkResult error = m_swapchain->present(ctx->present_image)) { case VK_SUCCESS: case VK_SUBOPTIMAL_KHR: @@ -1968,7 +1937,17 @@ void VKGSRender::queue_swap_request() } m_current_frame->swap_command_buffer = m_current_command_buffer; - close_and_submit_command_buffer({ m_current_frame->present_semaphore }, m_current_command_buffer->submit_fence); + + if (m_swapchain->is_headless()) + { + m_swapchain->end_frame(*m_current_command_buffer, m_current_frame->present_image); + close_and_submit_command_buffer({}, m_current_command_buffer->submit_fence); + } + else + { + close_and_submit_command_buffer({ m_current_frame->present_semaphore }, m_current_command_buffer->submit_fence); + } + m_current_frame->swap_command_buffer->pending = true; //Grab next cb in line and make it usable @@ -2462,7 +2441,7 @@ void VKGSRender::close_and_submit_command_buffer(const std::vector { m_current_command_buffer->end(); m_current_command_buffer->tag(); - m_current_command_buffer->submit(m_swap_chain->get_present_queue(), semaphores, fence, pipeline_stage_flags); + m_current_command_buffer->submit(m_swapchain->get_graphics_queue(), semaphores, fence, pipeline_stage_flags); } void VKGSRender::open_command_buffer() @@ -2654,7 +2633,7 @@ void VKGSRender::prepare_rtts(rsx::framebuffer_creation_context context) m_texture_cache.set_memory_read_flags(m_surface_info[i].address, m_surface_info[i].pitch * m_surface_info[i].height, rsx::memory_read_flags::flush_once); m_texture_cache.flush_if_cache_miss_likely(old_format, m_surface_info[i].address, m_surface_info[i].pitch * m_surface_info[i].height, - *m_current_command_buffer, m_memory_type_mapping, m_swap_chain->get_present_queue()); + *m_current_command_buffer, m_memory_type_mapping, m_swapchain->get_graphics_queue()); } m_surface_info[i].address = m_surface_info[i].pitch = 0; @@ -2670,7 +2649,7 @@ void VKGSRender::prepare_rtts(rsx::framebuffer_creation_context context) auto old_format = vk::get_compatible_depth_surface_format(m_optimal_tiling_supported_formats, m_depth_surface_info.depth_format); m_texture_cache.set_memory_read_flags(m_depth_surface_info.address, m_depth_surface_info.pitch * m_depth_surface_info.height, rsx::memory_read_flags::flush_once); m_texture_cache.flush_if_cache_miss_likely(old_format, m_depth_surface_info.address, m_depth_surface_info.pitch * m_depth_surface_info.height, - *m_current_command_buffer, m_memory_type_mapping, m_swap_chain->get_present_queue()); + *m_current_command_buffer, m_memory_type_mapping, m_swapchain->get_graphics_queue()); } m_depth_surface_info.address = m_depth_surface_info.pitch = 0; @@ -2847,14 +2826,14 @@ void VKGSRender::reinitialize_swapchain() present(&ctx); } - vkQueueWaitIdle(m_swap_chain->get_present_queue()); + //Wait for completion vkDeviceWaitIdle(*m_device); //Remove any old refs to the old images as they are about to be destroyed m_framebuffers_to_clean.clear(); //Rebuild swapchain. Old swapchain destruction is handled by the init_swapchain call - if (!m_swap_chain->init_swapchain(new_width, new_height)) + if (!m_swapchain->init(new_width, new_height)) { LOG_WARNING(RSX, "Swapchain initialization failed. Request ignored [%dx%d]", new_width, new_height); present_surface_dirty_flag = true; @@ -2869,18 +2848,16 @@ void VKGSRender::reinitialize_swapchain() //Prepare new swapchain images for use open_command_buffer(); - for (u32 i = 0; i < m_swap_chain->get_swap_image_count(); ++i) + for (u32 i = 0; i < m_swapchain->get_swap_image_count(); ++i) { - vk::change_image_layout(*m_current_command_buffer, m_swap_chain->get_swap_chain_image(i), - VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, - { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }); - + const auto target_layout = m_swapchain->get_optimal_present_layout(); + const auto target_image = m_swapchain->get_image(i); VkClearColorValue clear_color{}; - auto range = vk::get_image_subresource_range(0, 0, 1, 1, VK_IMAGE_ASPECT_COLOR_BIT); - vkCmdClearColorImage(*m_current_command_buffer, m_swap_chain->get_swap_chain_image(i), VK_IMAGE_LAYOUT_GENERAL, &clear_color, 1, &range); - vk::change_image_layout(*m_current_command_buffer, m_swap_chain->get_swap_chain_image(i), - VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }); + VkImageSubresourceRange range = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; + + vk::change_image_layout(*m_current_command_buffer, target_image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, range); + vkCmdClearColorImage(*m_current_command_buffer, target_image, VK_IMAGE_LAYOUT_GENERAL, &clear_color, 1, &range); + vk::change_image_layout(*m_current_command_buffer, target_image, VK_IMAGE_LAYOUT_GENERAL, target_layout, range); } //Will have to block until rendering is completed @@ -2989,8 +2966,9 @@ void VKGSRender::flip(int buffer) //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)) + + u64 timeout = m_swapchain->get_swap_image_count() <= VK_MAX_ASYNC_FRAMES? 0ull: 100000000ull; + while (VkResult status = m_swapchain->acquire_next_swapchain_image(m_current_frame->present_semaphore, timeout, &m_current_frame->present_image)) { switch (status) { @@ -3053,16 +3031,18 @@ void VKGSRender::flip(int buffer) } } - VkImage target_image = m_swap_chain->get_swap_chain_image(m_current_frame->present_image); + VkImage target_image = m_swapchain->get_image(m_current_frame->present_image); + const auto present_layout = m_swapchain->get_optimal_present_layout(); + if (image_to_flip) { - VkImageLayout target_layout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + VkImageLayout target_layout = present_layout; VkImageSubresourceRange range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; if (aspect_ratio.x || aspect_ratio.y) { VkClearColorValue clear_black {}; - vk::change_image_layout(*m_current_command_buffer, target_image, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, range); + vk::change_image_layout(*m_current_command_buffer, target_image, present_layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, range); vkCmdClearColorImage(*m_current_command_buffer, target_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_black, 1, &range); target_layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; @@ -3071,9 +3051,9 @@ void VKGSRender::flip(int buffer) vk::copy_scaled_image(*m_current_command_buffer, image_to_flip->value, target_image, image_to_flip->current_layout, target_layout, 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, false); - if (target_layout != VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) + if (target_layout != present_layout) { - vk::change_image_layout(*m_current_command_buffer, target_image, target_layout, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, range); + vk::change_image_layout(*m_current_command_buffer, target_image, target_layout, present_layout, range); } } else @@ -3082,9 +3062,9 @@ void VKGSRender::flip(int buffer) //TODO: Upload raw bytes from cpu for rendering VkImageSubresourceRange range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; VkClearColorValue clear_black {}; - 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); + vk::change_image_layout(*m_current_command_buffer, m_swapchain->get_image(m_current_frame->present_image), present_layout, VK_IMAGE_LAYOUT_GENERAL, range); + vkCmdClearColorImage(*m_current_command_buffer, m_swapchain->get_image(m_current_frame->present_image), VK_IMAGE_LAYOUT_GENERAL, &clear_black, 1, &range); + vk::change_image_layout(*m_current_command_buffer, m_swapchain->get_image(m_current_frame->present_image), VK_IMAGE_LAYOUT_GENERAL, present_layout, range); } std::unique_ptr direct_fbo; @@ -3096,7 +3076,7 @@ void VKGSRender::flip(int buffer) 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.oldLayout = present_layout; barrier.image = target_image; barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; @@ -3106,7 +3086,7 @@ void VKGSRender::flip(int buffer) 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); + size_t idx = vk::get_render_pass_location(m_swapchain->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++) @@ -3123,7 +3103,7 @@ void VKGSRender::flip(int buffer) 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)); + swap_image_view.push_back(std::make_unique(*m_device, target_image, VK_IMAGE_VIEW_TYPE_2D, m_swapchain->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))); } @@ -3152,7 +3132,7 @@ void VKGSRender::flip(int buffer) m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 180, direct_fbo->width(), direct_fbo->height(), "Flush requests: " + std::to_string(num_flushes) + " (" + std::to_string(cache_miss_ratio) + "% hard faults)"); } - vk::change_image_layout(*m_current_command_buffer, target_image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, subres); + vk::change_image_layout(*m_current_command_buffer, target_image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, present_layout, subres); m_framebuffers_to_clean.push_back(std::move(direct_fbo)); } @@ -3217,7 +3197,7 @@ bool VKGSRender::scaled_image_from_memory(rsx::blit_src_info& src, rsx::blit_dst if (result.dst_image) { if (m_texture_cache.flush_if_cache_miss_likely(result.dst_image->info.format, result.real_dst_address, result.real_dst_size, - *m_current_command_buffer, m_memory_type_mapping, m_swap_chain->get_present_queue())) + *m_current_command_buffer, m_memory_type_mapping, m_swapchain->get_graphics_queue())) require_flush = true; } diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.h b/rpcs3/Emu/RSX/VK/VKGSRender.h index c7207c6ae2bf..66c64c02ee15 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.h +++ b/rpcs3/Emu/RSX/VK/VKGSRender.h @@ -238,9 +238,7 @@ class VKGSRender : public GSRender private: VKFragmentProgram m_fragment_prog; VKVertexProgram m_vertex_prog; - vk::glsl::program *m_program; - vk::context m_thread_context; vk::texture_cache m_texture_cache; rsx::vk_render_targets m_rtts; @@ -275,8 +273,9 @@ class VKGSRender : public GSRender private: std::unique_ptr m_prog_buffer; + std::unique_ptr m_swapchain; + vk::context m_thread_context; vk::render_device *m_device; - vk::swap_chain* m_swap_chain; //Vulkan internals vk::command_pool m_command_buffer_pool; diff --git a/rpcs3/Emu/RSX/VK/VKHelpers.cpp b/rpcs3/Emu/RSX/VK/VKHelpers.cpp index 55ccd6475f2f..902b19891d42 100644 --- a/rpcs3/Emu/RSX/VK/VKHelpers.cpp +++ b/rpcs3/Emu/RSX/VK/VKHelpers.cpp @@ -56,10 +56,10 @@ namespace vk #endif } - memory_type_mapping get_memory_mapping(VkPhysicalDevice pdev) + memory_type_mapping get_memory_mapping(const vk::physical_device& dev) { VkPhysicalDeviceMemoryProperties memory_properties; - vkGetPhysicalDeviceMemoryProperties(pdev, &memory_properties); + vkGetPhysicalDeviceMemoryProperties((VkPhysicalDevice&)dev, &memory_properties); memory_type_mapping result; result.device_local = VK_MAX_MEMORY_TYPES; diff --git a/rpcs3/Emu/RSX/VK/VKHelpers.h b/rpcs3/Emu/RSX/VK/VKHelpers.h index c707ed6b1bb5..a3a7030802b2 100644 --- a/rpcs3/Emu/RSX/VK/VKHelpers.h +++ b/rpcs3/Emu/RSX/VK/VKHelpers.h @@ -19,6 +19,10 @@ #include "../Common/GLSLCommon.h" #include "../rsx_cache.h" +#ifndef _WIN32 +#include +#endif + #define DESCRIPTOR_MAX_DRAW_CALLS 4096 #define VERTEX_BUFFERS_FIRST_BIND_SLOT 3 @@ -114,7 +118,7 @@ namespace vk uint32_t device_local; }; - memory_type_mapping get_memory_mapping(VkPhysicalDevice pdev); + memory_type_mapping get_memory_mapping(const physical_device& dev); class physical_device { @@ -135,12 +139,12 @@ namespace vk vkGetPhysicalDeviceMemoryProperties(pdev, &memory_properties); } - std::string name() + std::string name() const { return props.deviceName; } - uint32_t get_queue_count() + uint32_t get_queue_count() const { if (queue_props.size()) return (u32)queue_props.size(); @@ -166,12 +170,12 @@ namespace vk return queue_props[queue]; } - VkPhysicalDeviceMemoryProperties get_memory_properties() + VkPhysicalDeviceMemoryProperties get_memory_properties() const { return memory_properties; } - operator VkPhysicalDevice() + operator VkPhysicalDevice() const { return dev; } @@ -179,16 +183,13 @@ namespace vk class render_device { - vk::physical_device *pgpu; - VkDevice dev; + physical_device *pgpu = nullptr; + memory_type_mapping memory_map{}; + VkDevice dev = VK_NULL_HANDLE; public: - render_device() - { - dev = nullptr; - pgpu = nullptr; - } + {} render_device(vk::physical_device &pdev, uint32_t graphics_queue_idx) { @@ -235,6 +236,7 @@ namespace vk device.pEnabledFeatures = &available_features; CHECK_RESULT(vkCreateDevice(*pgpu, &device, nullptr, &dev)); + memory_map = vk::get_memory_mapping(pdev); } ~render_device() @@ -271,12 +273,17 @@ namespace vk return false; } - vk::physical_device& gpu() + const physical_device& gpu() const { return *pgpu; } - operator VkDevice() + const memory_type_mapping& get_memory_mapping() const + { + return memory_map; + } + + operator VkDevice&() { return dev; } @@ -381,7 +388,7 @@ namespace vk struct image { - VkImage value; + VkImage value = VK_NULL_HANDLE; VkComponentMapping native_component_map = {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A}; VkImageLayout current_layout = VK_IMAGE_LAYOUT_UNDEFINED; VkImageCreateInfo info = {}; @@ -573,6 +580,11 @@ namespace vk vkUnmapMemory(m_device, memory->memory); } + u32 size() const + { + return (u32)info.size; + } + buffer(const buffer&) = delete; buffer(buffer&&) = delete; @@ -739,301 +751,6 @@ namespace vk VkDevice m_device; }; - class swap_chain_image - { - VkImageView view = nullptr; - VkImage image = nullptr; - VkFormat internal_format; - vk::render_device *owner = nullptr; - - public: - swap_chain_image() {} - - void create(vk::render_device &dev, VkImage &swap_image, VkFormat format) - { - VkImageViewCreateInfo color_image_view = {}; - - color_image_view.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - color_image_view.format = format; - - color_image_view.components.r = VK_COMPONENT_SWIZZLE_R; - color_image_view.components.g = VK_COMPONENT_SWIZZLE_G; - color_image_view.components.b = VK_COMPONENT_SWIZZLE_B; - color_image_view.components.a = VK_COMPONENT_SWIZZLE_A; - - color_image_view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - color_image_view.subresourceRange.baseMipLevel = 0; - color_image_view.subresourceRange.levelCount = 1; - color_image_view.subresourceRange.baseArrayLayer = 0; - color_image_view.subresourceRange.layerCount = 1; - - color_image_view.viewType = VK_IMAGE_VIEW_TYPE_2D; - - color_image_view.image = swap_image; - vkCreateImageView(dev, &color_image_view, nullptr, &view); - - image = swap_image; - internal_format = format; - owner = &dev; - } - - void discard(vk::render_device &dev) - { - vkDestroyImageView(dev, view, nullptr); - } - - operator VkImage() - { - return image; - } - - operator VkImageView() - { - return view; - } - }; - - class swap_chain - { - vk::render_device dev; - - uint32_t m_present_queue = 0xFFFF; - uint32_t m_graphics_queue = 0xFFFF; - - VkQueue vk_graphics_queue = nullptr; - VkQueue vk_present_queue = nullptr; - - /* WSI surface information */ - VkSurfaceKHR m_surface = nullptr; - VkFormat m_surface_format; - VkColorSpaceKHR m_color_space; - - VkSwapchainKHR m_vk_swapchain = nullptr; - std::vector m_swap_images; - - public: - - PFN_vkCreateSwapchainKHR createSwapchainKHR; - PFN_vkDestroySwapchainKHR destroySwapchainKHR; - PFN_vkGetSwapchainImagesKHR getSwapchainImagesKHR; - PFN_vkAcquireNextImageKHR acquireNextImageKHR; - PFN_vkQueuePresentKHR queuePresentKHR; - - swap_chain(vk::physical_device &gpu, uint32_t _present_queue, uint32_t _graphics_queue, VkFormat format, VkSurfaceKHR surface, VkColorSpaceKHR color_space) - { - dev = render_device(gpu, _graphics_queue); - - createSwapchainKHR = (PFN_vkCreateSwapchainKHR)vkGetDeviceProcAddr(dev, "vkCreateSwapchainKHR"); - destroySwapchainKHR = (PFN_vkDestroySwapchainKHR)vkGetDeviceProcAddr(dev, "vkDestroySwapchainKHR"); - getSwapchainImagesKHR = (PFN_vkGetSwapchainImagesKHR)vkGetDeviceProcAddr(dev, "vkGetSwapchainImagesKHR"); - acquireNextImageKHR = (PFN_vkAcquireNextImageKHR)vkGetDeviceProcAddr(dev, "vkAcquireNextImageKHR"); - queuePresentKHR = (PFN_vkQueuePresentKHR)vkGetDeviceProcAddr(dev, "vkQueuePresentKHR"); - - vkGetDeviceQueue(dev, _graphics_queue, 0, &vk_graphics_queue); - vkGetDeviceQueue(dev, _present_queue, 0, &vk_present_queue); - - m_present_queue = _present_queue; - m_graphics_queue = _graphics_queue; - m_surface = surface; - m_color_space = color_space; - m_surface_format = format; - } - - ~swap_chain() - { - } - - void destroy() - { - if (VkDevice pdev = (VkDevice)dev) - { - if (m_vk_swapchain) - { - if (m_swap_images.size()) - { - for (vk::swap_chain_image &img : m_swap_images) - img.discard(dev); - } - - destroySwapchainKHR(pdev, m_vk_swapchain, nullptr); - } - - dev.destroy(); - } - } - - bool init_swapchain(u32 width, u32 height) - { - VkSwapchainKHR old_swapchain = m_vk_swapchain; - vk::physical_device& gpu = const_cast(dev.gpu()); - - VkSurfaceCapabilitiesKHR surface_descriptors = {}; - CHECK_RESULT(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(gpu, m_surface, &surface_descriptors)); - - if (surface_descriptors.maxImageExtent.width < width || - surface_descriptors.maxImageExtent.height < height) - { - LOG_ERROR(RSX, "Swapchain: Swapchain creation failed because dimensions cannot fit. Max = %d, %d, Requested = %d, %d", - surface_descriptors.maxImageExtent.width, surface_descriptors.maxImageExtent.height, width, height); - - return false; - } - - VkExtent2D swapchainExtent; - if (surface_descriptors.currentExtent.width == (uint32_t)-1) - { - swapchainExtent.width = width; - swapchainExtent.height = height; - } - else - { - if (surface_descriptors.currentExtent.width == 0 || surface_descriptors.currentExtent.height == 0) - { - LOG_WARNING(RSX, "Swapchain: Current surface extent is a null region. Is the window minimized?"); - return false; - } - - swapchainExtent = surface_descriptors.currentExtent; - width = surface_descriptors.currentExtent.width; - height = surface_descriptors.currentExtent.height; - } - - uint32_t nb_available_modes = 0; - CHECK_RESULT(vkGetPhysicalDeviceSurfacePresentModesKHR(gpu, m_surface, &nb_available_modes, nullptr)); - - std::vector present_modes(nb_available_modes); - CHECK_RESULT(vkGetPhysicalDeviceSurfacePresentModesKHR(gpu, m_surface, &nb_available_modes, present_modes.data())); - - VkPresentModeKHR swapchain_present_mode = VK_PRESENT_MODE_FIFO_KHR; - std::vector preferred_modes; - - //List of preferred modes in decreasing desirability - if (g_cfg.video.vsync) - preferred_modes = { VK_PRESENT_MODE_MAILBOX_KHR }; - else if (!g_cfg.video.vk.force_fifo) - preferred_modes = { VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_RELAXED_KHR, VK_PRESENT_MODE_MAILBOX_KHR }; - - bool mode_found = false; - for (VkPresentModeKHR preferred_mode : preferred_modes) - { - //Search for this mode in supported modes - for (VkPresentModeKHR mode : present_modes) - { - if (mode == preferred_mode) - { - swapchain_present_mode = mode; - mode_found = true; - break; - } - } - - if (mode_found) - break; - } - - LOG_NOTICE(RSX, "Swapchain: present mode %d in use.", (s32&)swapchain_present_mode); - - uint32_t nb_swap_images = surface_descriptors.minImageCount + 1; - if (surface_descriptors.maxImageCount > 0) - { - //Try to negotiate for a triple buffer setup - //In cases where the front-buffer isnt available for present, its better to have a spare surface - nb_swap_images = std::max(surface_descriptors.minImageCount + 2u, 3u); - - if (nb_swap_images > surface_descriptors.maxImageCount) - { - // Application must settle for fewer images than desired: - nb_swap_images = surface_descriptors.maxImageCount; - } - } - - VkSurfaceTransformFlagBitsKHR pre_transform = surface_descriptors.currentTransform; - if (surface_descriptors.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) - pre_transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; - - VkSwapchainCreateInfoKHR swap_info = {}; - swap_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; - swap_info.surface = m_surface; - swap_info.minImageCount = nb_swap_images; - swap_info.imageFormat = m_surface_format; - swap_info.imageColorSpace = m_color_space; - - swap_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; - swap_info.preTransform = pre_transform; - swap_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - swap_info.imageArrayLayers = 1; - swap_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; - swap_info.presentMode = swapchain_present_mode; - swap_info.oldSwapchain = old_swapchain; - swap_info.clipped = true; - - swap_info.imageExtent.width = width; - swap_info.imageExtent.height = height; - - createSwapchainKHR(dev, &swap_info, nullptr, &m_vk_swapchain); - - if (old_swapchain) - { - if (m_swap_images.size()) - { - for (auto &img : m_swap_images) - img.discard(dev); - - m_swap_images.resize(0); - } - - destroySwapchainKHR(dev, old_swapchain, nullptr); - } - - nb_swap_images = 0; - getSwapchainImagesKHR(dev, m_vk_swapchain, &nb_swap_images, nullptr); - - if (!nb_swap_images) fmt::throw_exception("Driver returned 0 images for swapchain" HERE); - - std::vector swap_images; - swap_images.resize(nb_swap_images); - getSwapchainImagesKHR(dev, m_vk_swapchain, &nb_swap_images, swap_images.data()); - - m_swap_images.resize(nb_swap_images); - for (u32 i = 0; i < nb_swap_images; ++i) - { - m_swap_images[i].create(dev, swap_images[i], m_surface_format); - } - - return true; - } - - u32 get_swap_image_count() - { - return (u32)m_swap_images.size(); - } - - vk::swap_chain_image& get_swap_chain_image(const int index) - { - return m_swap_images[index]; - } - - const vk::render_device& get_device() - { - return dev; - } - - const VkQueue& get_present_queue() - { - return vk_graphics_queue; - } - - const VkFormat get_surface_format() - { - return m_surface_format; - } - - operator const VkSwapchainKHR() - { - return m_vk_swapchain; - } - }; - class command_pool { vk::render_device *owner = nullptr; @@ -1171,13 +888,667 @@ namespace vk } }; - class supported_extensions + class swapchain_image_WSI { - private: - std::vector m_vk_exts; + VkImageView view = nullptr; + VkImage image = nullptr; + VkFormat internal_format; + vk::render_device *owner = nullptr; + + public: + swapchain_image_WSI() {} + + void create(vk::render_device &dev, VkImage &swap_image, VkFormat format) + { + VkImageViewCreateInfo color_image_view = {}; + + color_image_view.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + color_image_view.format = format; + + color_image_view.components.r = VK_COMPONENT_SWIZZLE_R; + color_image_view.components.g = VK_COMPONENT_SWIZZLE_G; + color_image_view.components.b = VK_COMPONENT_SWIZZLE_B; + color_image_view.components.a = VK_COMPONENT_SWIZZLE_A; + + color_image_view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + color_image_view.subresourceRange.baseMipLevel = 0; + color_image_view.subresourceRange.levelCount = 1; + color_image_view.subresourceRange.baseArrayLayer = 0; + color_image_view.subresourceRange.layerCount = 1; + + color_image_view.viewType = VK_IMAGE_VIEW_TYPE_2D; + + color_image_view.image = swap_image; + vkCreateImageView(dev, &color_image_view, nullptr, &view); + + image = swap_image; + internal_format = format; + owner = &dev; + } + + void discard(vk::render_device &dev) + { + vkDestroyImageView(dev, view, nullptr); + } + + operator VkImage&() + { + return image; + } + + operator VkImageView&() + { + return view; + } + }; + + class swapchain_image_RPCS3 : public image + { + std::unique_ptr m_dma_buffer; + u32 m_width = 0; + u32 m_height = 0; + +public: + swapchain_image_RPCS3(render_device &dev, const memory_type_mapping& memory_map, u32 width, u32 height) + :image(dev, memory_map.device_local, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, VK_IMAGE_TYPE_2D, VK_FORMAT_B8G8R8A8_UNORM, width, height, 1, 1, 1, + VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 0) + { + m_width = width; + m_height = height; + current_layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + + m_dma_buffer = std::make_unique(dev, m_width * m_height * 4, memory_map.host_visible_coherent, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, VK_BUFFER_USAGE_TRANSFER_DST_BIT, 0); + } + + void do_dma_transfer(command_buffer& cmd) + { + VkBufferImageCopy copyRegion = {}; + copyRegion.bufferOffset = 0; + copyRegion.bufferRowLength = m_width; + copyRegion.bufferImageHeight = m_height; + copyRegion.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; + copyRegion.imageOffset = {}; + copyRegion.imageExtent = {m_width, m_height, 1}; + + VkImageSubresourceRange subresource_range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; + change_image_layout(cmd, this, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, subresource_range); + vkCmdCopyImageToBuffer(cmd, value, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_dma_buffer->value, 1, ©Region); + change_image_layout(cmd, this, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, subresource_range); + } + + u32 get_required_memory_size() const + { + return m_width * m_height * 4; + } + + void* get_pixels() + { + return m_dma_buffer->map(0, VK_WHOLE_SIZE); + } + + void free_pixels() + { + m_dma_buffer->unmap(); + } + }; + + class swapchain_base + { + protected: + render_device dev; + + uint32_t m_present_queue = UINT32_MAX; + uint32_t m_graphics_queue = UINT32_MAX; + VkQueue vk_graphics_queue = VK_NULL_HANDLE; + VkQueue vk_present_queue = VK_NULL_HANDLE; + + display_handle_t window_handle{}; + u32 m_width = 0; + u32 m_height = 0; + VkFormat m_surface_format = VK_FORMAT_B8G8R8A8_UNORM; + + virtual void init_swapchain_images(render_device& dev, u32 count) = 0; + + public: + swapchain_base(physical_device &gpu, uint32_t _present_queue, uint32_t _graphics_queue, VkFormat format = VK_FORMAT_B8G8R8A8_UNORM) + { + dev = render_device(gpu, _graphics_queue); + + if (_graphics_queue < UINT32_MAX) vkGetDeviceQueue(dev, _graphics_queue, 0, &vk_graphics_queue); + if (_present_queue < UINT32_MAX) vkGetDeviceQueue(dev, _present_queue, 0, &vk_present_queue); + + m_present_queue = _present_queue; + m_graphics_queue = _graphics_queue; + m_surface_format = format; + } + + ~swapchain_base(){} + + virtual void create(display_handle_t& handle) = 0; + virtual void destroy(bool full = true) = 0; + virtual bool init() = 0; + + virtual u32 get_swap_image_count() const = 0; + virtual VkImage& get_image(u32 index) = 0; + virtual VkResult acquire_next_swapchain_image(VkSemaphore semaphore, u64 timeout, u32* result) = 0; + virtual void end_frame(command_buffer& cmd, u32 index) = 0; + virtual VkResult present(u32 index) = 0; + virtual VkImageLayout get_optimal_present_layout() = 0; + + virtual bool init(u32 w, u32 h) + { + m_width = w; + m_height = h; + return init(); + } + + const vk::render_device& get_device() + { + return dev; + } + + const VkQueue& get_present_queue() + { + return vk_present_queue; + } + + const VkQueue& get_graphics_queue() + { + return vk_graphics_queue; + } + + const VkFormat get_surface_format() + { + return m_surface_format; + } + + const bool is_headless() const + { + return (vk_present_queue == VK_NULL_HANDLE); + } + }; + + template + class abstract_swapchain_impl : public swapchain_base + { + protected: + std::vector swapchain_images; + + public: + abstract_swapchain_impl(physical_device &gpu, uint32_t _present_queue, uint32_t _graphics_queue, VkFormat format = VK_FORMAT_B8G8R8A8_UNORM) + : swapchain_base(gpu, _present_queue, _graphics_queue, format) + {} + + ~abstract_swapchain_impl() + {} + + u32 get_swap_image_count() const override + { + return (u32)swapchain_images.size(); + } + + using swapchain_base::init; + }; + + using native_swapchain_base = abstract_swapchain_impl>>; + using WSI_swapchain_base = abstract_swapchain_impl; + +#ifdef _WIN32 + + class swapchain_WIN32 : public native_swapchain_base + { + HDC hDstDC = NULL; + HDC hSrcDC = NULL; + HBITMAP hDIB = NULL; + LPVOID hPtr = NULL; + + public: + swapchain_WIN32(physical_device &gpu, uint32_t _present_queue, uint32_t _graphics_queue, VkFormat format = VK_FORMAT_B8G8R8A8_UNORM) + : native_swapchain_base(gpu, _present_queue, _graphics_queue, format) + {} + + ~swapchain_WIN32(){} + + bool init() override + { + if (hDIB || hSrcDC) + destroy(false); + + RECT rect; + GetClientRect(window_handle, &rect); + m_width = rect.right - rect.left; + m_height = rect.bottom - rect.top; + + BITMAPINFO bitmap = {}; + bitmap.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bitmap.bmiHeader.biWidth = m_width; + bitmap.bmiHeader.biHeight = m_height * -1; + bitmap.bmiHeader.biPlanes = 1; + bitmap.bmiHeader.biBitCount = 32; + bitmap.bmiHeader.biCompression = BI_RGB; + + hSrcDC = CreateCompatibleDC(hDstDC); + hDIB = CreateDIBSection(hSrcDC, &bitmap, DIB_RGB_COLORS, &hPtr, NULL, 0); + SelectObject(hSrcDC, hDIB); + init_swapchain_images(dev, 3); + return true; + } + + void create(display_handle_t& handle) override + { + window_handle = handle; + hDstDC = GetDC(handle); + init(); + } + + void destroy(bool full=true) override + { + DeleteObject(hDIB); + DeleteDC(hSrcDC); + hDIB = NULL; + hSrcDC = NULL; + + swapchain_images.clear(); + + if (full) + dev.destroy(); + } + + VkResult present(u32 image) override + { + auto& src = swapchain_images[image]; + GdiFlush(); + + memcpy(hPtr, src.second->get_pixels(), src.second->get_required_memory_size()); + BitBlt(hDstDC, 0, 0, m_width, m_height, hSrcDC, 0, 0, SRCCOPY); + + src.second->free_pixels(); + src.first = false; + return VK_SUCCESS; + } +#else + + class swapchain_X11 : public native_swapchain_base + { + Display *display = NULL; + Window window = (Window)NULL; + XImage* pixmap = NULL; + GC gc = NULL; + int bit_depth = 24; + + public: + swapchain_X11(physical_device &gpu, uint32_t _present_queue, uint32_t _graphics_queue, VkFormat format = VK_FORMAT_B8G8R8A8_UNORM) + : native_swapchain_base(gpu, _present_queue, _graphics_queue, format) + {} + + ~swapchain_X11(){} + + bool init() override + { + if (pixmap) + destroy(false); + + Window root; + int x, y; + u32 w = 0, h = 0, border, depth; + + if (XGetGeometry(display, window, &root, &x, &y, &w, & h, &border, &depth)) + { + m_width = w; + m_height = h; + bit_depth = depth; + } + + if (m_width == 0 || m_height == 0) + { + LOG_ERROR(RSX, "Invalid window dimensions %d x %d", m_width, m_height); + return false; + } + + XVisualInfo visual{}; + if (!XMatchVisualInfo(display, DefaultScreen(display), bit_depth, TrueColor, &visual)) + { + LOG_ERROR(RSX, "Could not find matching visual info!" HERE); + return false; + } + + pixmap = XCreateImage(display, visual.visual, visual.depth, ZPixmap, 0, nullptr, m_width, m_height, 32, 0); + init_swapchain_images(dev, 3); + return true; + } + + void create(display_handle_t& window_handle) override + { + window_handle.match([&](std::pair p) { display = p.first; window = p.second; }, [](auto _) {}); + + if (display == NULL) + { + LOG_FATAL(RSX, "Could not create virtual display on this window protocol (Wayland?)"); + return; + } + + gc = DefaultGC(display, DefaultScreen(display)); + init(); + } + + void destroy(bool full=true) override + { + pixmap->data = nullptr; + XDestroyImage(pixmap); + pixmap = NULL; + + swapchain_images.clear(); + + if (full) + dev.destroy(); + } + + VkResult present(u32 index) override + { + auto& src = swapchain_images[index]; + if (pixmap) + { + pixmap->data = (char*)src.second->get_pixels(); + + XPutImage(display, window, gc, pixmap, 0, 0, 0, 0, m_width, m_height); + XFlush(display); + + src.second->free_pixels(); + } + + //Release reference + src.first = false; + return VK_SUCCESS; + } +#endif + + VkResult acquire_next_swapchain_image(VkSemaphore semaphore, u64 timeout, u32* result) override + { + u32 index = 0; + for (auto &p : swapchain_images) + { + if (!p.first) + { + p.first = true; + *result = index; + return VK_SUCCESS; + } + + ++index; + } + + return VK_NOT_READY; + } + + void end_frame(command_buffer& cmd, u32 index) override + { + swapchain_images[index].second->do_dma_transfer(cmd); + } + + VkImage& get_image(u32 index) + { + return (VkImage&)(*swapchain_images[index].second.get()); + } + + VkImageLayout get_optimal_present_layout() override + { + return VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + } + + protected: + void init_swapchain_images(render_device& dev, u32 preferred_count) override + { + swapchain_images.resize(preferred_count); + printf("Preparing %d images with sizes %d x %d\n", preferred_count, m_width, m_height); + for (auto &img : swapchain_images) + { + img.second = std::make_unique(dev, dev.get_memory_mapping(), m_width, m_height); + img.first = false; + } + } + }; + + class swapchain_WSI : public WSI_swapchain_base + { + VkSurfaceKHR m_surface = VK_NULL_HANDLE; + VkColorSpaceKHR m_color_space = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; + VkSwapchainKHR m_vk_swapchain = nullptr; + + PFN_vkCreateSwapchainKHR createSwapchainKHR = nullptr; + PFN_vkDestroySwapchainKHR destroySwapchainKHR = nullptr; + PFN_vkGetSwapchainImagesKHR getSwapchainImagesKHR = nullptr; + PFN_vkAcquireNextImageKHR acquireNextImageKHR = nullptr; + PFN_vkQueuePresentKHR queuePresentKHR = nullptr; + + protected: + void init_swapchain_images(render_device& dev, u32 preferred_count) override + { + u32 nb_swap_images = 0; + getSwapchainImagesKHR(dev, m_vk_swapchain, &nb_swap_images, nullptr); + + if (!nb_swap_images) fmt::throw_exception("Driver returned 0 images for swapchain" HERE); + + std::vector vk_images; + vk_images.resize(nb_swap_images); + getSwapchainImagesKHR(dev, m_vk_swapchain, &nb_swap_images, vk_images.data()); + + swapchain_images.resize(nb_swap_images); + for (u32 i = 0; i < nb_swap_images; ++i) + { + swapchain_images[i].create(dev, vk_images[i], m_surface_format); + } + } public: + swapchain_WSI(vk::physical_device &gpu, uint32_t _present_queue, uint32_t _graphics_queue, VkFormat format, VkSurfaceKHR surface, VkColorSpaceKHR color_space) + : WSI_swapchain_base(gpu, _present_queue, _graphics_queue, format) + { + createSwapchainKHR = (PFN_vkCreateSwapchainKHR)vkGetDeviceProcAddr(dev, "vkCreateSwapchainKHR"); + destroySwapchainKHR = (PFN_vkDestroySwapchainKHR)vkGetDeviceProcAddr(dev, "vkDestroySwapchainKHR"); + getSwapchainImagesKHR = (PFN_vkGetSwapchainImagesKHR)vkGetDeviceProcAddr(dev, "vkGetSwapchainImagesKHR"); + acquireNextImageKHR = (PFN_vkAcquireNextImageKHR)vkGetDeviceProcAddr(dev, "vkAcquireNextImageKHR"); + queuePresentKHR = (PFN_vkQueuePresentKHR)vkGetDeviceProcAddr(dev, "vkQueuePresentKHR"); + + m_surface = surface; + m_color_space = color_space; + } + + ~swapchain_WSI() + {} + void create(display_handle_t&) override + {} + + void destroy(bool=true) override + { + if (VkDevice pdev = (VkDevice)dev) + { + if (m_vk_swapchain) + { + for (auto &img : swapchain_images) + img.discard(dev); + + destroySwapchainKHR(pdev, m_vk_swapchain, nullptr); + } + + dev.destroy(); + } + } + + using WSI_swapchain_base::init; + bool init() override + { + if (vk_present_queue == VK_NULL_HANDLE) + { + LOG_ERROR(RSX, "Cannot create WSI swapchain without a present queue"); + return false; + } + + VkSwapchainKHR old_swapchain = m_vk_swapchain; + vk::physical_device& gpu = const_cast(dev.gpu()); + + VkSurfaceCapabilitiesKHR surface_descriptors = {}; + CHECK_RESULT(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(gpu, m_surface, &surface_descriptors)); + + if (surface_descriptors.maxImageExtent.width < m_width || + surface_descriptors.maxImageExtent.height < m_height) + { + LOG_ERROR(RSX, "Swapchain: Swapchain creation failed because dimensions cannot fit. Max = %d, %d, Requested = %d, %d", + surface_descriptors.maxImageExtent.width, surface_descriptors.maxImageExtent.height, m_width, m_height); + + return false; + } + + VkExtent2D swapchainExtent; + if (surface_descriptors.currentExtent.width == (uint32_t)-1) + { + swapchainExtent.width = m_width; + swapchainExtent.height = m_height; + } + else + { + if (surface_descriptors.currentExtent.width == 0 || surface_descriptors.currentExtent.height == 0) + { + LOG_WARNING(RSX, "Swapchain: Current surface extent is a null region. Is the window minimized?"); + return false; + } + + swapchainExtent = surface_descriptors.currentExtent; + m_width = surface_descriptors.currentExtent.width; + m_height = surface_descriptors.currentExtent.height; + } + + uint32_t nb_available_modes = 0; + CHECK_RESULT(vkGetPhysicalDeviceSurfacePresentModesKHR(gpu, m_surface, &nb_available_modes, nullptr)); + + std::vector present_modes(nb_available_modes); + CHECK_RESULT(vkGetPhysicalDeviceSurfacePresentModesKHR(gpu, m_surface, &nb_available_modes, present_modes.data())); + + VkPresentModeKHR swapchain_present_mode = VK_PRESENT_MODE_FIFO_KHR; + std::vector preferred_modes; + + //List of preferred modes in decreasing desirability + if (g_cfg.video.vsync) + preferred_modes = { VK_PRESENT_MODE_MAILBOX_KHR }; + else if (!g_cfg.video.vk.force_fifo) + preferred_modes = { VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_RELAXED_KHR, VK_PRESENT_MODE_MAILBOX_KHR }; + + bool mode_found = false; + for (VkPresentModeKHR preferred_mode : preferred_modes) + { + //Search for this mode in supported modes + for (VkPresentModeKHR mode : present_modes) + { + if (mode == preferred_mode) + { + swapchain_present_mode = mode; + mode_found = true; + break; + } + } + + if (mode_found) + break; + } + + LOG_NOTICE(RSX, "Swapchain: present mode %d in use.", (s32&)swapchain_present_mode); + + uint32_t nb_swap_images = surface_descriptors.minImageCount + 1; + if (surface_descriptors.maxImageCount > 0) + { + //Try to negotiate for a triple buffer setup + //In cases where the front-buffer isnt available for present, its better to have a spare surface + nb_swap_images = std::max(surface_descriptors.minImageCount + 2u, 3u); + + if (nb_swap_images > surface_descriptors.maxImageCount) + { + // Application must settle for fewer images than desired: + nb_swap_images = surface_descriptors.maxImageCount; + } + } + + VkSurfaceTransformFlagBitsKHR pre_transform = surface_descriptors.currentTransform; + if (surface_descriptors.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) + pre_transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + + VkSwapchainCreateInfoKHR swap_info = {}; + swap_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + swap_info.surface = m_surface; + swap_info.minImageCount = nb_swap_images; + swap_info.imageFormat = m_surface_format; + swap_info.imageColorSpace = m_color_space; + + swap_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + swap_info.preTransform = pre_transform; + swap_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + swap_info.imageArrayLayers = 1; + swap_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + swap_info.presentMode = swapchain_present_mode; + swap_info.oldSwapchain = old_swapchain; + swap_info.clipped = true; + + swap_info.imageExtent.width = m_width; + swap_info.imageExtent.height = m_height; + + createSwapchainKHR(dev, &swap_info, nullptr, &m_vk_swapchain); + + if (old_swapchain) + { + if (swapchain_images.size()) + { + for (auto &img : swapchain_images) + img.discard(dev); + + swapchain_images.resize(0); + } + + destroySwapchainKHR(dev, old_swapchain, nullptr); + } + + init_swapchain_images(dev, nb_swap_images); + return true; + } + + VkResult acquire_next_swapchain_image(VkSemaphore semaphore, u64 timeout, u32* result) override + { + return vkAcquireNextImageKHR(dev, m_vk_swapchain, timeout, semaphore, VK_NULL_HANDLE, result); + } + + void end_frame(command_buffer &cmd, u32 index) override + { + } + + VkResult present(u32 image) override + { + VkPresentInfoKHR present = {}; + present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + present.pNext = nullptr; + present.swapchainCount = 1; + present.pSwapchains = &m_vk_swapchain; + present.pImageIndices = ℑ + + return queuePresentKHR(vk_present_queue, &present); + } + + VkImage& get_image(u32 index) + { + return (VkImage&)swapchain_images[index]; + } + + VkImageLayout get_optimal_present_layout() override + { + return VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + } + }; + + class supported_extensions + { + private: + std::vector m_vk_exts; + + public: supported_extensions() { uint32_t count; @@ -1378,24 +1749,25 @@ namespace vk return gpus; } + swapchain_base* createSwapChain(display_handle_t window_handle, vk::physical_device &dev) + { #ifdef _WIN32 + using swapchain_NATIVE = swapchain_WIN32; + HINSTANCE hInstance = NULL; - vk::swap_chain* createSwapChain(HINSTANCE hInstance, display_handle_t hWnd, vk::physical_device &dev) - { VkWin32SurfaceCreateInfoKHR createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; createInfo.hinstance = hInstance; - createInfo.hwnd = hWnd; + createInfo.hwnd = window_handle; VkSurfaceKHR surface; CHECK_RESULT(vkCreateWin32SurfaceKHR(m_instance, &createInfo, NULL, &surface)); -#elif HAVE_VULKAN - vk::swap_chain* createSwapChain(display_handle_t ctx, vk::physical_device &dev) - { +#else + using swapchain_NATIVE = swapchain_X11; VkSurfaceKHR surface; - ctx.match( + window_handle.match( [&](std::pair p) { VkXlibSurfaceCreateInfoKHR createInfo = {}; @@ -1425,6 +1797,21 @@ namespace vk vkGetPhysicalDeviceSurfaceSupportKHR(dev, index, surface, &supportsPresent[index]); } + bool present_possible = false; + for (const auto &value : supportsPresent) + { + if (value) + { + present_possible = true; + break; + } + } + + if (!present_possible) + { + LOG_ERROR(RSX, "It is not possible for the currently selected GPU to present to the window (Likely caused by NVIDIA driver running the current display)"); + } + // Search for a graphics and a present queue in the array of queue // families, try to find one that supports both uint32_t graphicsQueueNodeIndex = UINT32_MAX; @@ -1461,12 +1848,26 @@ namespace vk } } - // Generate error if could not find both a graphics and a present queue - if (graphicsQueueNodeIndex == UINT32_MAX || presentQueueNodeIndex == UINT32_MAX) - fmt::throw_exception("Failed to find a suitable graphics/compute queue" HERE); + if (graphicsQueueNodeIndex == UINT32_MAX) + { + LOG_FATAL(RSX, "Failed to find a suitable graphics queue" HERE); + return nullptr; + } if (graphicsQueueNodeIndex != presentQueueNodeIndex) - fmt::throw_exception("Separate graphics and present queues not supported" HERE); + { + //Separate graphics and present, use headless fallback + present_possible = false; + } + + if (!present_possible) + { + //Native(sw) swapchain + LOG_WARNING(RSX, "Falling back to software present support (native windowing API)"); + auto swapchain = new swapchain_NATIVE(dev, UINT32_MAX, graphicsQueueNodeIndex); + swapchain->create(window_handle); + return swapchain; + } // Get the list of VkFormat's that are supported: uint32_t formatCount; @@ -1500,7 +1901,7 @@ namespace vk color_space = surfFormats[0].colorSpace; - return new swap_chain(dev, presentQueueNodeIndex, graphicsQueueNodeIndex, format, surface, color_space); + return new swapchain_WSI(dev, presentQueueNodeIndex, graphicsQueueNodeIndex, format, surface, color_space); } }; diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index 53b60f7166a1..fce2913970e4 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -195,7 +195,7 @@ Level3 - ..\hidapi.lib;winmm.lib;OpenAL.lib;XAudio.lib;D3D12GSRender.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;VKstatic.1.lib;glslang.lib;OSDependent.lib;OGLCompiler.lib;SPIRV.lib;HLSL.lib;Advapi32.lib;user32.lib;zlib.lib;..\libpng.lib;asmjit.lib;yaml-cpp.lib;emucore.lib;dxgi.lib;$(QTDIR)\lib\qtmain.lib;shell32.lib;opengl32.lib;$(QTDIR)\lib\Qt5OpenGL.lib;$(QTDIR)\lib\Qt5Widgets.lib;$(QTDIR)\lib\Qt5Quick.lib;$(QTDIR)\lib\Qt5Gui.lib;$(QTDIR)\lib\Qt5Qml.lib;$(QTDIR)\lib\Qt5Network.lib;$(QTDIR)\lib\Qt5Core.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;Qt5WinExtras.lib;%(AdditionalDependencies) + gdi32.lib;..\hidapi.lib;winmm.lib;OpenAL.lib;XAudio.lib;D3D12GSRender.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;VKstatic.1.lib;glslang.lib;OSDependent.lib;OGLCompiler.lib;SPIRV.lib;HLSL.lib;Advapi32.lib;user32.lib;zlib.lib;..\libpng.lib;asmjit.lib;yaml-cpp.lib;emucore.lib;dxgi.lib;$(QTDIR)\lib\qtmain.lib;shell32.lib;opengl32.lib;$(QTDIR)\lib\Qt5OpenGL.lib;$(QTDIR)\lib\Qt5Widgets.lib;$(QTDIR)\lib\Qt5Quick.lib;$(QTDIR)\lib\Qt5Gui.lib;$(QTDIR)\lib\Qt5Qml.lib;$(QTDIR)\lib\Qt5Network.lib;$(QTDIR)\lib\Qt5Core.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;Qt5WinExtras.lib;%(AdditionalDependencies) ..\3rdparty\OpenAL\libs\Win64;..\Vulkan\glslang-build\hlsl\Release;..\Vulkan\glslang-build\SPIRV\Release;..\Vulkan\glslang-build\OGLCompilersDLL\Release;..\Vulkan\glslang-build\glslang\OSDependent\Windows\Release;..\Vulkan\Vulkan-build\loader\Release;..\Vulkan\glslang-build\glslang\Release;..\lib\$(CONFIGURATION)-$(PLATFORM);..\3rdparty\minidx12\Lib;$(QTDIR)\lib;%(AdditionalLibraryDirectories) "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) true