diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index 9a7c617e9e..562dd29476 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -600,6 +600,7 @@ if(TARGET 3rdparty_vulkan) RSX/VK/vkutils/instance.cpp RSX/VK/vkutils/scratch.cpp RSX/VK/vkutils/sync.cpp + RSX/VK/vkutils/swapchain.cpp RSX/VK/vkutils/memory.cpp RSX/VK/vkutils/device.cpp RSX/VK/vkutils/sampler.cpp diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.h b/rpcs3/Emu/RSX/VK/VKGSRender.h index f81b602cbf..eaf9cc4a44 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.h +++ b/rpcs3/Emu/RSX/VK/VKGSRender.h @@ -6,7 +6,7 @@ #include "vkutils/data_heap.h" #include "vkutils/instance.h" #include "vkutils/sync.h" -#include "vkutils/swapchain.hpp" +#include "vkutils/swapchain.h" #include "VKGSRenderTypes.hpp" #include "VKTextureCache.h" diff --git a/rpcs3/Emu/RSX/VK/vkutils/swapchain.cpp b/rpcs3/Emu/RSX/VK/vkutils/swapchain.cpp new file mode 100644 index 0000000000..a296e393e2 --- /dev/null +++ b/rpcs3/Emu/RSX/VK/vkutils/swapchain.cpp @@ -0,0 +1,354 @@ +#include "stdafx.h" +#include "swapchain.h" + +namespace vk +{ + // Swapchain image RPCS3 + swapchain_image_RPCS3::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, VMM_ALLOCATION_POOL_SWAPCHAIN) + { + 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, VMM_ALLOCATION_POOL_SWAPCHAIN); + } + + void swapchain_image_RPCS3::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 }; + + change_layout(cmd, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + vkCmdCopyImageToBuffer(cmd, value, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_dma_buffer->value, 1, ©Region); + change_layout(cmd, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + } + + u32 swapchain_image_RPCS3::get_required_memory_size() const + { + return m_width * m_height * 4; + } + + void* swapchain_image_RPCS3::get_pixels() + { + return m_dma_buffer->map(0, VK_WHOLE_SIZE); + } + + void swapchain_image_RPCS3::free_pixels() + { + m_dma_buffer->unmap(); + } + + // swapchain BASE + swapchain_base::swapchain_base(physical_device& gpu, u32 present_queue, u32 graphics_queue, u32 transfer_queue, VkFormat format) + { + dev.create(gpu, graphics_queue, present_queue, transfer_queue); + m_surface_format = format; + } + + // NATIVE swapchain base + VkResult native_swapchain_base::acquire_next_swapchain_image(VkSemaphore /*semaphore*/, u64 /*timeout*/, u32* result) + { + 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 native_swapchain_base::init_swapchain_images(render_device& dev, u32 preferred_count) + { + swapchain_images.resize(preferred_count); + for (auto& img : swapchain_images) + { + img.second = std::make_unique(dev, dev.get_memory_mapping(), m_width, m_height); + img.first = false; + } + } + + // WSI implementation + void swapchain_WSI::init_swapchain_images(render_device& dev, u32 /*preferred_count*/) + { + u32 nb_swap_images = 0; + _vkGetSwapchainImagesKHR(dev, m_vk_swapchain, &nb_swap_images, nullptr); + + if (!nb_swap_images) fmt::throw_exception("Driver returned 0 images for swapchain"); + + std::vector vk_images; + vk_images.resize(nb_swap_images); + _vkGetSwapchainImagesKHR(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].value = vk_images[i]; + } + } + + swapchain_WSI::swapchain_WSI(vk::physical_device& gpu, u32 present_queue, u32 graphics_queue, u32 transfer_queue, VkFormat format, VkSurfaceKHR surface, VkColorSpaceKHR color_space, bool force_wm_reporting_off) + : WSI_swapchain_base(gpu, present_queue, graphics_queue, transfer_queue, format) + { + _vkCreateSwapchainKHR = reinterpret_cast(vkGetDeviceProcAddr(dev, "vkCreateSwapchainKHR")); + _vkDestroySwapchainKHR = reinterpret_cast(vkGetDeviceProcAddr(dev, "vkDestroySwapchainKHR")); + _vkGetSwapchainImagesKHR = reinterpret_cast(vkGetDeviceProcAddr(dev, "vkGetSwapchainImagesKHR")); + _vkAcquireNextImageKHR = reinterpret_cast(vkGetDeviceProcAddr(dev, "vkAcquireNextImageKHR")); + _vkQueuePresentKHR = reinterpret_cast(vkGetDeviceProcAddr(dev, "vkQueuePresentKHR")); + + m_surface = surface; + m_color_space = color_space; + + if (!force_wm_reporting_off) + { + switch (gpu.get_driver_vendor()) + { + case driver_vendor::AMD: + case driver_vendor::INTEL: + case driver_vendor::RADV: + case driver_vendor::MVK: + break; + case driver_vendor::ANV: + case driver_vendor::NVIDIA: + m_wm_reports_flag = true; + break; + default: + break; + } + } + } + + void swapchain_WSI::destroy(bool) + { + if (VkDevice pdev = dev) + { + if (m_vk_swapchain) + { + _vkDestroySwapchainKHR(pdev, m_vk_swapchain, nullptr); + } + + dev.destroy(); + } + } + + std::pair swapchain_WSI::init_surface_capabilities() + { +#ifdef _WIN32 + if (g_cfg.video.vk.exclusive_fullscreen_mode != vk_exclusive_fs_mode::unspecified && dev.get_surface_capabilities_2_support()) + { + HMONITOR hmonitor = MonitorFromWindow(window_handle, MONITOR_DEFAULTTOPRIMARY); + if (hmonitor) + { + VkSurfaceCapabilities2KHR pSurfaceCapabilities = {}; + pSurfaceCapabilities.sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR; + + VkPhysicalDeviceSurfaceInfo2KHR pSurfaceInfo = {}; + pSurfaceInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR; + pSurfaceInfo.surface = m_surface; + + VkSurfaceCapabilitiesFullScreenExclusiveEXT full_screen_exclusive_capabilities = {}; + VkSurfaceFullScreenExclusiveWin32InfoEXT full_screen_exclusive_win32_info = {}; + full_screen_exclusive_capabilities.sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_FULL_SCREEN_EXCLUSIVE_EXT; + + pSurfaceCapabilities.pNext = &full_screen_exclusive_capabilities; + + full_screen_exclusive_win32_info.sType = VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_WIN32_INFO_EXT; + full_screen_exclusive_win32_info.hmonitor = hmonitor; + + pSurfaceInfo.pNext = &full_screen_exclusive_win32_info; + + auto getPhysicalDeviceSurfaceCapabilities2KHR = reinterpret_cast( + vkGetInstanceProcAddr(dev.gpu(), "vkGetPhysicalDeviceSurfaceCapabilities2KHR") + ); + ensure(getPhysicalDeviceSurfaceCapabilities2KHR); + CHECK_RESULT(getPhysicalDeviceSurfaceCapabilities2KHR(dev.gpu(), &pSurfaceInfo, &pSurfaceCapabilities)); + + return { pSurfaceCapabilities.surfaceCapabilities, !!full_screen_exclusive_capabilities.fullScreenExclusiveSupported }; + } + else + { + rsx_log.warning("Swapchain: failed to get monitor for the window"); + } + } +#endif + VkSurfaceCapabilitiesKHR surface_descriptors = {}; + CHECK_RESULT(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(dev.gpu(), m_surface, &surface_descriptors)); + return { surface_descriptors, false }; + } + + bool swapchain_WSI::init() + { + if (dev.get_present_queue() == VK_NULL_HANDLE) + { + rsx_log.error("Cannot create WSI swapchain without a present queue"); + return false; + } + + VkSwapchainKHR old_swapchain = m_vk_swapchain; + vk::physical_device& gpu = const_cast(dev.gpu()); + + auto [surface_descriptors, should_specify_exclusive_full_screen_mode] = init_surface_capabilities(); + + if (surface_descriptors.maxImageExtent.width < m_width || + surface_descriptors.maxImageExtent.height < m_height) + { + rsx_log.error("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; + } + + if (surface_descriptors.currentExtent.width != umax) + { + if (surface_descriptors.currentExtent.width == 0 || surface_descriptors.currentExtent.height == 0) + { + rsx_log.warning("Swapchain: Current surface extent is a null region. Is the window minimized?"); + return false; + } + + m_width = surface_descriptors.currentExtent.width; + m_height = surface_descriptors.currentExtent.height; + } + + u32 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; + + if (!g_cfg.video.vk.force_fifo) + { + // List of preferred modes in decreasing desirability + // NOTE: Always picks "triple-buffered vsync" types if possible + if (!g_cfg.video.vsync) + { + preferred_modes = { VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_FIFO_RELAXED_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; + } + + rsx_log.notice("Swapchain: present mode %d in use.", static_cast(swapchain_present_mode)); + + u32 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 = std::max(m_width, surface_descriptors.minImageExtent.width); + swap_info.imageExtent.height = std::max(m_height, surface_descriptors.minImageExtent.height); + +#ifdef _WIN32 + VkSurfaceFullScreenExclusiveInfoEXT full_screen_exclusive_info = {}; + if (should_specify_exclusive_full_screen_mode) + { + vk_exclusive_fs_mode fs_mode = g_cfg.video.vk.exclusive_fullscreen_mode; + ensure(fs_mode == vk_exclusive_fs_mode::enable || fs_mode == vk_exclusive_fs_mode::disable); + + full_screen_exclusive_info.sType = VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_INFO_EXT; + full_screen_exclusive_info.fullScreenExclusive = + fs_mode == vk_exclusive_fs_mode::enable ? VK_FULL_SCREEN_EXCLUSIVE_ALLOWED_EXT : VK_FULL_SCREEN_EXCLUSIVE_DISALLOWED_EXT; + + swap_info.pNext = &full_screen_exclusive_info; + } + + rsx_log.notice("Swapchain: requesting full screen exclusive mode %d.", static_cast(full_screen_exclusive_info.fullScreenExclusive)); +#endif + + _vkCreateSwapchainKHR(dev, &swap_info, nullptr, &m_vk_swapchain); + + if (old_swapchain) + { + if (!swapchain_images.empty()) + { + swapchain_images.clear(); + } + + _vkDestroySwapchainKHR(dev, old_swapchain, nullptr); + } + + init_swapchain_images(dev); + return true; + } + + VkResult swapchain_WSI::present(VkSemaphore semaphore, u32 image) + { + VkPresentInfoKHR present = {}; + present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + present.pNext = nullptr; + present.swapchainCount = 1; + present.pSwapchains = &m_vk_swapchain; + present.pImageIndices = ℑ + + if (semaphore != VK_NULL_HANDLE) + { + present.waitSemaphoreCount = 1; + present.pWaitSemaphores = &semaphore; + } + + return _vkQueuePresentKHR(dev.get_present_queue(), &present); + } +} diff --git a/rpcs3/Emu/RSX/VK/vkutils/swapchain.h b/rpcs3/Emu/RSX/VK/vkutils/swapchain.h new file mode 100644 index 0000000000..f5128fc487 --- /dev/null +++ b/rpcs3/Emu/RSX/VK/vkutils/swapchain.h @@ -0,0 +1,11 @@ +#pragma once + +#if defined (_WIN32) +#include "swapchain_win32.hpp" +#elif defined (ANDROID) +#include "swapchain_android.hpp" +#elif defined (__APPLE__) +#include "swapchain_macos.hpp" +#else // Both linux and BSD families +#include "swapchain_unix.hpp" +#endif diff --git a/rpcs3/Emu/RSX/VK/vkutils/swapchain.hpp b/rpcs3/Emu/RSX/VK/vkutils/swapchain.hpp deleted file mode 100644 index bca489d8d8..0000000000 --- a/rpcs3/Emu/RSX/VK/vkutils/swapchain.hpp +++ /dev/null @@ -1,786 +0,0 @@ -#pragma once - -#ifdef HAVE_X11 -#include -#endif - -#include "../../display.h" -#include "../VulkanAPI.h" -#include "image.h" - -#include - -namespace vk -{ - struct swapchain_image_WSI - { - VkImage value = VK_NULL_HANDLE; - }; - - 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, VMM_ALLOCATION_POOL_SWAPCHAIN) - { - 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, VMM_ALLOCATION_POOL_SWAPCHAIN); - } - - 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 }; - - change_layout(cmd, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - vkCmdCopyImageToBuffer(cmd, value, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_dma_buffer->value, 1, ©Region); - change_layout(cmd, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - } - - 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; - - 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, u32 present_queue, u32 graphics_queue, u32 transfer_queue, VkFormat format = VK_FORMAT_B8G8R8A8_UNORM) - { - dev.create(gpu, graphics_queue, present_queue, transfer_queue); - m_surface_format = format; - } - - virtual ~swapchain_base() = default; - - 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(VkSemaphore semaphore, u32 index) = 0; - virtual VkImageLayout get_optimal_present_layout() = 0; - - virtual bool supports_automatic_wm_reports() const - { - return false; - } - - bool init(u32 w, u32 h) - { - m_width = w; - m_height = h; - return init(); - } - - const vk::render_device& get_device() - { - return dev; - } - - VkFormat get_surface_format() - { - return m_surface_format; - } - - bool is_headless() const - { - return (dev.get_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, u32 present_queue, u32 graphics_queue, u32 transfer_queue, VkFormat format = VK_FORMAT_B8G8R8A8_UNORM) - : swapchain_base(gpu, present_queue, graphics_queue, transfer_queue, format) - {} - - ~abstract_swapchain_impl() override = default; - - u32 get_swap_image_count() const override - { - return ::size32(swapchain_images); - } - - 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, u32 present_queue, u32 graphics_queue, u32 transfer_queue, VkFormat format = VK_FORMAT_B8G8R8A8_UNORM) - : native_swapchain_base(gpu, present_queue, graphics_queue, transfer_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; - - if (m_width == 0 || m_height == 0) - { - rsx_log.error("Invalid window dimensions %d x %d", m_width, m_height); - return false; - } - - 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); - } - - void destroy(bool full = true) override - { - DeleteObject(hDIB); - DeleteDC(hSrcDC); - hDIB = NULL; - hSrcDC = NULL; - - swapchain_images.clear(); - - if (full) - { - ReleaseDC(window_handle, hDstDC); - hDstDC = NULL; - - dev.destroy(); - } - } - - VkResult present(VkSemaphore /*semaphore*/, u32 image) override - { - auto& src = swapchain_images[image]; - GdiFlush(); - - if (hSrcDC) - { - 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; - } - #elif defined(__APPLE__) - - class swapchain_MacOS : public native_swapchain_base - { - void* nsView = nullptr; - - public: - swapchain_MacOS(physical_device& gpu, u32 present_queue, u32 graphics_queue, u32 transfer_queue, VkFormat format = VK_FORMAT_B8G8R8A8_UNORM) - : native_swapchain_base(gpu, present_queue, graphics_queue, transfer_queue, format) - {} - - ~swapchain_MacOS() {} - - bool init() override - { - //TODO: get from `nsView` - m_width = 0; - m_height = 0; - - if (m_width == 0 || m_height == 0) - { - rsx_log.error("Invalid window dimensions %d x %d", m_width, m_height); - return false; - } - - init_swapchain_images(dev, 3); - return true; - } - - void create(display_handle_t& window_handle) override - { - nsView = window_handle; - } - - void destroy(bool full = true) override - { - swapchain_images.clear(); - - if (full) - dev.destroy(); - } - - VkResult present(VkSemaphore /*semaphore*/, u32 /*index*/) override - { - fmt::throw_exception("Native macOS swapchain is not implemented yet!"); - } - #elif defined(HAVE_X11) - - class swapchain_X11 : public native_swapchain_base - { - Display* display = nullptr; - Window window = 0; - XImage* pixmap = nullptr; - GC gc = nullptr; - int bit_depth = 24; - - public: - swapchain_X11(physical_device& gpu, u32 present_queue, u32 graphics_queue, u32 transfer_queue, VkFormat format = VK_FORMAT_B8G8R8A8_UNORM) - : native_swapchain_base(gpu, present_queue, graphics_queue, transfer_queue, format) - {} - - ~swapchain_X11() override = default; - - 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) - { - rsx_log.error("Invalid window dimensions %d x %d", m_width, m_height); - return false; - } - - XVisualInfo visual{}; - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wold-style-cast" - if (!XMatchVisualInfo(display, DefaultScreen(display), bit_depth, TrueColor, &visual)) - #pragma GCC diagnostic pop - { - rsx_log.error("Could not find matching visual info!"); - 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 - { - std::visit([&](auto&& p) - { - using T = std::decay_t; - if constexpr (std::is_same_v>) - { - display = p.first; - window = p.second; - } - }, window_handle); - - if (display == NULL) - { - rsx_log.fatal("Could not create virtual display on this window protocol (Wayland?)"); - return; - } - - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wold-style-cast" - gc = DefaultGC(display, DefaultScreen(display)); - #pragma GCC diagnostic pop - } - - void destroy(bool full = true) override - { - pixmap->data = nullptr; - XDestroyImage(pixmap); - pixmap = NULL; - - swapchain_images.clear(); - - if (full) - dev.destroy(); - } - - VkResult present(VkSemaphore /*semaphore*/, u32 index) override - { - auto& src = swapchain_images[index]; - if (pixmap) - { - pixmap->data = static_cast(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; - } - #else - - class swapchain_Wayland : public native_swapchain_base - { - - public: - swapchain_Wayland(physical_device& gpu, u32 present_queue, u32 graphics_queue, u32 transfer_queue, VkFormat format = VK_FORMAT_B8G8R8A8_UNORM) - : native_swapchain_base(gpu, present_queue, graphics_queue, transfer_queue, format) - {} - - ~swapchain_Wayland() {} - - bool init() override - { - fmt::throw_exception("Native Wayland swapchain is not implemented yet!"); - } - - void create(display_handle_t& window_handle) override - { - fmt::throw_exception("Native Wayland swapchain is not implemented yet!"); - } - - void destroy(bool full = true) override - { - fmt::throw_exception("Native Wayland swapchain is not implemented yet!"); - } - - VkResult present(VkSemaphore /*semaphore*/, u32 index) override - { - fmt::throw_exception("Native Wayland swapchain is not implemented yet!"); - } - #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) override - { - return swapchain_images[index].second->value; - } - - 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); - 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 _vkCreateSwapchainKHR = nullptr; - PFN_vkDestroySwapchainKHR _vkDestroySwapchainKHR = nullptr; - PFN_vkGetSwapchainImagesKHR _vkGetSwapchainImagesKHR = nullptr; - PFN_vkAcquireNextImageKHR _vkAcquireNextImageKHR = nullptr; - PFN_vkQueuePresentKHR _vkQueuePresentKHR = nullptr; - - bool m_wm_reports_flag = false; - - protected: - void init_swapchain_images(render_device& dev, u32 /*preferred_count*/ = 0) override - { - u32 nb_swap_images = 0; - _vkGetSwapchainImagesKHR(dev, m_vk_swapchain, &nb_swap_images, nullptr); - - if (!nb_swap_images) fmt::throw_exception("Driver returned 0 images for swapchain"); - - std::vector vk_images; - vk_images.resize(nb_swap_images); - _vkGetSwapchainImagesKHR(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].value = vk_images[i]; - } - } - - public: - swapchain_WSI(vk::physical_device& gpu, u32 present_queue, u32 graphics_queue, u32 transfer_queue, VkFormat format, VkSurfaceKHR surface, VkColorSpaceKHR color_space, bool force_wm_reporting_off) - : WSI_swapchain_base(gpu, present_queue, graphics_queue, transfer_queue, format) - { - _vkCreateSwapchainKHR = reinterpret_cast(vkGetDeviceProcAddr(dev, "vkCreateSwapchainKHR")); - _vkDestroySwapchainKHR = reinterpret_cast(vkGetDeviceProcAddr(dev, "vkDestroySwapchainKHR")); - _vkGetSwapchainImagesKHR = reinterpret_cast(vkGetDeviceProcAddr(dev, "vkGetSwapchainImagesKHR")); - _vkAcquireNextImageKHR = reinterpret_cast(vkGetDeviceProcAddr(dev, "vkAcquireNextImageKHR")); - _vkQueuePresentKHR = reinterpret_cast(vkGetDeviceProcAddr(dev, "vkQueuePresentKHR")); - - m_surface = surface; - m_color_space = color_space; - - if (!force_wm_reporting_off) - { - switch (gpu.get_driver_vendor()) - { - case driver_vendor::AMD: - case driver_vendor::INTEL: - case driver_vendor::RADV: - case driver_vendor::MVK: - break; - case driver_vendor::ANV: - case driver_vendor::NVIDIA: - m_wm_reports_flag = true; - break; - default: - break; - } - } - } - - ~swapchain_WSI() override = default; - - void create(display_handle_t&) override - {} - - void destroy(bool = true) override - { - if (VkDevice pdev = dev) - { - if (m_vk_swapchain) - { - _vkDestroySwapchainKHR(pdev, m_vk_swapchain, nullptr); - } - - dev.destroy(); - } - } - - std::pair init_surface_capabilities() - { -#ifdef _WIN32 - if (g_cfg.video.vk.exclusive_fullscreen_mode != vk_exclusive_fs_mode::unspecified && dev.get_surface_capabilities_2_support()) - { - HMONITOR hmonitor = MonitorFromWindow(window_handle, MONITOR_DEFAULTTOPRIMARY); - if (hmonitor) - { - VkSurfaceCapabilities2KHR pSurfaceCapabilities = {}; - pSurfaceCapabilities.sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR; - - VkPhysicalDeviceSurfaceInfo2KHR pSurfaceInfo = {}; - pSurfaceInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR; - pSurfaceInfo.surface = m_surface; - - VkSurfaceCapabilitiesFullScreenExclusiveEXT full_screen_exclusive_capabilities = {}; - VkSurfaceFullScreenExclusiveWin32InfoEXT full_screen_exclusive_win32_info = {}; - full_screen_exclusive_capabilities.sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_FULL_SCREEN_EXCLUSIVE_EXT; - - pSurfaceCapabilities.pNext = &full_screen_exclusive_capabilities; - - full_screen_exclusive_win32_info.sType = VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_WIN32_INFO_EXT; - full_screen_exclusive_win32_info.hmonitor = hmonitor; - - pSurfaceInfo.pNext = &full_screen_exclusive_win32_info; - - auto getPhysicalDeviceSurfaceCapabilities2KHR = reinterpret_cast( - vkGetInstanceProcAddr(dev.gpu(), "vkGetPhysicalDeviceSurfaceCapabilities2KHR") - ); - ensure(getPhysicalDeviceSurfaceCapabilities2KHR); - CHECK_RESULT(getPhysicalDeviceSurfaceCapabilities2KHR(dev.gpu(), &pSurfaceInfo, &pSurfaceCapabilities)); - - return { pSurfaceCapabilities.surfaceCapabilities, !!full_screen_exclusive_capabilities.fullScreenExclusiveSupported }; - } - else - { - rsx_log.warning("Swapchain: failed to get monitor for the window"); - } - } -#endif - VkSurfaceCapabilitiesKHR surface_descriptors = {}; - CHECK_RESULT(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(dev.gpu(), m_surface, &surface_descriptors)); - return { surface_descriptors, false }; - } - - using WSI_swapchain_base::init; - bool init() override - { - if (dev.get_present_queue() == VK_NULL_HANDLE) - { - rsx_log.error("Cannot create WSI swapchain without a present queue"); - return false; - } - - VkSwapchainKHR old_swapchain = m_vk_swapchain; - vk::physical_device& gpu = const_cast(dev.gpu()); - - auto [surface_descriptors, should_specify_exclusive_full_screen_mode] = init_surface_capabilities(); - - if (surface_descriptors.maxImageExtent.width < m_width || - surface_descriptors.maxImageExtent.height < m_height) - { - rsx_log.error("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; - } - - if (surface_descriptors.currentExtent.width != umax) - { - if (surface_descriptors.currentExtent.width == 0 || surface_descriptors.currentExtent.height == 0) - { - rsx_log.warning("Swapchain: Current surface extent is a null region. Is the window minimized?"); - return false; - } - - m_width = surface_descriptors.currentExtent.width; - m_height = surface_descriptors.currentExtent.height; - } - - u32 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; - - if (!g_cfg.video.vk.force_fifo) - { - // List of preferred modes in decreasing desirability - // NOTE: Always picks "triple-buffered vsync" types if possible - if (!g_cfg.video.vsync) - { - preferred_modes = { VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_FIFO_RELAXED_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; - } - - rsx_log.notice("Swapchain: present mode %d in use.", static_cast(swapchain_present_mode)); - - u32 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 = std::max(m_width, surface_descriptors.minImageExtent.width); - swap_info.imageExtent.height = std::max(m_height, surface_descriptors.minImageExtent.height); - - #ifdef _WIN32 - VkSurfaceFullScreenExclusiveInfoEXT full_screen_exclusive_info = {}; - if (should_specify_exclusive_full_screen_mode) - { - vk_exclusive_fs_mode fs_mode = g_cfg.video.vk.exclusive_fullscreen_mode; - ensure(fs_mode == vk_exclusive_fs_mode::enable || fs_mode == vk_exclusive_fs_mode::disable); - - full_screen_exclusive_info.sType = VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_INFO_EXT; - full_screen_exclusive_info.fullScreenExclusive = - fs_mode == vk_exclusive_fs_mode::enable ? VK_FULL_SCREEN_EXCLUSIVE_ALLOWED_EXT : VK_FULL_SCREEN_EXCLUSIVE_DISALLOWED_EXT; - - swap_info.pNext = &full_screen_exclusive_info; - } - - rsx_log.notice("Swapchain: requesting full screen exclusive mode %d.", static_cast(full_screen_exclusive_info.fullScreenExclusive)); - #endif - - _vkCreateSwapchainKHR(dev, &swap_info, nullptr, &m_vk_swapchain); - - if (old_swapchain) - { - if (!swapchain_images.empty()) - { - swapchain_images.clear(); - } - - _vkDestroySwapchainKHR(dev, old_swapchain, nullptr); - } - - init_swapchain_images(dev); - return true; - } - - bool supports_automatic_wm_reports() const override - { - return m_wm_reports_flag; - } - - 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(VkSemaphore semaphore, 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 = ℑ - - if (semaphore != VK_NULL_HANDLE) - { - present.waitSemaphoreCount = 1; - present.pWaitSemaphores = &semaphore; - } - - return _vkQueuePresentKHR(dev.get_present_queue(), &present); - } - - VkImage get_image(u32 index) override - { - return swapchain_images[index].value; - } - - VkImageLayout get_optimal_present_layout() override - { - return VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - } - }; -} diff --git a/rpcs3/Emu/RSX/VK/vkutils/swapchain_android.hpp b/rpcs3/Emu/RSX/VK/vkutils/swapchain_android.hpp new file mode 100644 index 0000000000..a4d8b613de --- /dev/null +++ b/rpcs3/Emu/RSX/VK/vkutils/swapchain_android.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include "swapchain_core.h" + +namespace vk +{ +#if defined(ANDROID) + class swapchain_ANDROID : public native_swapchain_base + { + void* surface_handle = nullptr; // FIXME: No idea what the android native surface is called + + public: + swapchain_ANDROID(physical_device& gpu, u32 present_queue, u32 graphics_queue, u32 transfer_queue, VkFormat format = VK_FORMAT_B8G8R8A8_UNORM) + : native_swapchain_base(gpu, present_queue, graphics_queue, transfer_queue, format) + {} + + ~swapchain_ANDROID() {} + + bool init() override + { + //TODO: get from `surface` + m_width = 0; + m_height = 0; + + if (m_width == 0 || m_height == 0) + { + rsx_log.error("Invalid window dimensions %d x %d", m_width, m_height); + return false; + } + + init_swapchain_images(dev, 3); + return true; + } + + void create(display_handle_t& window_handle) override + { + surface_handle = window_handle; + } + + void destroy(bool full = true) override + { + swapchain_images.clear(); + + if (full) + dev.destroy(); + } + + VkResult present(VkSemaphore /*semaphore*/, u32 /*index*/) override + { + fmt::throw_exception("Native macOS swapchain is not implemented yet!"); + } + }; + + using swapchain_NATIVE = swapchain_ANDROID; +#endif +} + diff --git a/rpcs3/Emu/RSX/VK/vkutils/swapchain_core.h b/rpcs3/Emu/RSX/VK/vkutils/swapchain_core.h new file mode 100644 index 0000000000..bb0f3db3c0 --- /dev/null +++ b/rpcs3/Emu/RSX/VK/vkutils/swapchain_core.h @@ -0,0 +1,200 @@ +#pragma once + +#ifdef HAVE_X11 +#include +#endif + +#include "../../display.h" +#include "../VulkanAPI.h" +#include "image.h" + +#include + +namespace vk +{ + struct swapchain_image_WSI + { + VkImage value = VK_NULL_HANDLE; + }; + + 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); + + void do_dma_transfer(command_buffer& cmd); + + u32 get_required_memory_size() const; + + void* get_pixels(); + + void free_pixels(); + }; + + class swapchain_base + { + protected: + render_device dev; + + 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, u32 present_queue, u32 graphics_queue, u32 transfer_queue, VkFormat format = VK_FORMAT_B8G8R8A8_UNORM); + + virtual ~swapchain_base() = default; + + 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(VkSemaphore semaphore, u32 index) = 0; + virtual VkImageLayout get_optimal_present_layout() const = 0; + + virtual bool supports_automatic_wm_reports() const + { + return false; + } + + bool init(u32 w, u32 h) + { + m_width = w; + m_height = h; + return init(); + } + + const vk::render_device& get_device() + { + return dev; + } + + VkFormat get_surface_format() const + { + return m_surface_format; + } + + bool is_headless() const + { + return (dev.get_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, u32 present_queue, u32 graphics_queue, u32 transfer_queue, VkFormat format = VK_FORMAT_B8G8R8A8_UNORM) + : swapchain_base(gpu, present_queue, graphics_queue, transfer_queue, format) + {} + + ~abstract_swapchain_impl() override = default; + + u32 get_swap_image_count() const override + { + return ::size32(swapchain_images); + } + + using swapchain_base::init; + }; + + using WSI_swapchain_base = abstract_swapchain_impl; + + class native_swapchain_base : public abstract_swapchain_impl>> + { + public: + using abstract_swapchain_impl::abstract_swapchain_impl; + + VkResult acquire_next_swapchain_image(VkSemaphore semaphore, u64 timeout, u32* result) override; + + void end_frame(command_buffer& cmd, u32 index) override + { + swapchain_images[index].second->do_dma_transfer(cmd); + } + + VkImage get_image(u32 index) override + { + return swapchain_images[index].second->value; + } + + VkImageLayout get_optimal_present_layout() const override + { + return VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + } + + protected: + void init_swapchain_images(render_device& dev, u32 preferred_count) override; + }; + + 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 _vkCreateSwapchainKHR = nullptr; + PFN_vkDestroySwapchainKHR _vkDestroySwapchainKHR = nullptr; + PFN_vkGetSwapchainImagesKHR _vkGetSwapchainImagesKHR = nullptr; + PFN_vkAcquireNextImageKHR _vkAcquireNextImageKHR = nullptr; + PFN_vkQueuePresentKHR _vkQueuePresentKHR = nullptr; + + bool m_wm_reports_flag = false; + + protected: + void init_swapchain_images(render_device& dev, u32 preferred_count = 0) override; + + public: + swapchain_WSI(vk::physical_device& gpu, u32 present_queue, u32 graphics_queue, u32 transfer_queue, VkFormat format, VkSurfaceKHR surface, VkColorSpaceKHR color_space, bool force_wm_reporting_off); + + ~swapchain_WSI() override = default; + + void create(display_handle_t&) override + {} + + void destroy(bool = true) override; + + std::pair init_surface_capabilities(); + + using WSI_swapchain_base::init; + bool init() override; + + bool supports_automatic_wm_reports() const override + { + return m_wm_reports_flag; + } + + 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(VkSemaphore semaphore, u32 image) override; + + VkImage get_image(u32 index) override + { + return swapchain_images[index].value; + } + + VkImageLayout get_optimal_present_layout() const override + { + return VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + } + }; +} diff --git a/rpcs3/Emu/RSX/VK/vkutils/swapchain_macos.hpp b/rpcs3/Emu/RSX/VK/vkutils/swapchain_macos.hpp new file mode 100644 index 0000000000..a9daf7718d --- /dev/null +++ b/rpcs3/Emu/RSX/VK/vkutils/swapchain_macos.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include "swapchain_core.h" + +namespace vk +{ +#if defined(__APPLE__) + class swapchain_MacOS : public native_swapchain_base + { + void* nsView = nullptr; + + public: + swapchain_MacOS(physical_device& gpu, u32 present_queue, u32 graphics_queue, u32 transfer_queue, VkFormat format = VK_FORMAT_B8G8R8A8_UNORM) + : native_swapchain_base(gpu, present_queue, graphics_queue, transfer_queue, format) + {} + + ~swapchain_MacOS() {} + + bool init() override + { + //TODO: get from `nsView` + m_width = 0; + m_height = 0; + + if (m_width == 0 || m_height == 0) + { + rsx_log.error("Invalid window dimensions %d x %d", m_width, m_height); + return false; + } + + init_swapchain_images(dev, 3); + return true; + } + + void create(display_handle_t& window_handle) override + { + nsView = window_handle; + } + + void destroy(bool full = true) override + { + swapchain_images.clear(); + + if (full) + dev.destroy(); + } + + VkResult present(VkSemaphore /*semaphore*/, u32 /*index*/) override + { + fmt::throw_exception("Native macOS swapchain is not implemented yet!"); + } + }; + + using swapchain_NATIVE = swapchain_MacOS; +#endif +} diff --git a/rpcs3/Emu/RSX/VK/vkutils/swapchain_unix.hpp b/rpcs3/Emu/RSX/VK/vkutils/swapchain_unix.hpp new file mode 100644 index 0000000000..c9d005045a --- /dev/null +++ b/rpcs3/Emu/RSX/VK/vkutils/swapchain_unix.hpp @@ -0,0 +1,161 @@ +#pragma once + +#include "swapchain_core.h" + +#ifdef HAVE_X11 +#include +#endif + +namespace vk +{ +#if defined(HAVE_X11) + + class swapchain_X11 : public native_swapchain_base + { + Display* display = nullptr; + Window window = 0; + XImage* pixmap = nullptr; + GC gc = nullptr; + int bit_depth = 24; + + public: + swapchain_X11(physical_device& gpu, u32 present_queue, u32 graphics_queue, u32 transfer_queue, VkFormat format = VK_FORMAT_B8G8R8A8_UNORM) + : native_swapchain_base(gpu, present_queue, graphics_queue, transfer_queue, format) + {} + + ~swapchain_X11() override = default; + + 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) + { + rsx_log.error("Invalid window dimensions %d x %d", m_width, m_height); + return false; + } + + XVisualInfo visual{}; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wold-style-cast" + if (!XMatchVisualInfo(display, DefaultScreen(display), bit_depth, TrueColor, &visual)) +#pragma GCC diagnostic pop + { + rsx_log.error("Could not find matching visual info!"); + 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 + { + std::visit([&](auto&& p) + { + using T = std::decay_t; + if constexpr (std::is_same_v>) + { + display = p.first; + window = p.second; + } + }, window_handle); + + if (display == NULL) + { + rsx_log.fatal("Could not create virtual display on this window protocol (Wayland?)"); + return; + } + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wold-style-cast" + gc = DefaultGC(display, DefaultScreen(display)); +#pragma GCC diagnostic pop + } + + void destroy(bool full = true) override + { + pixmap->data = nullptr; + XDestroyImage(pixmap); + pixmap = NULL; + + swapchain_images.clear(); + + if (full) + dev.destroy(); + } + + VkResult present(VkSemaphore /*semaphore*/, u32 index) override + { + auto& src = swapchain_images[index]; + if (pixmap) + { + pixmap->data = static_cast(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; + } + }; + + using swapchain_NATIVE = swapchain_X11; + +#endif + +#if defined(HAVE_WAYLAND) + + class swapchain_Wayland : public native_swapchain_base + { + public: + swapchain_Wayland(physical_device& gpu, u32 present_queue, u32 graphics_queue, u32 transfer_queue, VkFormat format = VK_FORMAT_B8G8R8A8_UNORM) + : native_swapchain_base(gpu, present_queue, graphics_queue, transfer_queue, format) + {} + + ~swapchain_Wayland() {} + + bool init() override + { + fmt::throw_exception("Native Wayland swapchain is not implemented yet!"); + } + + void create(display_handle_t& window_handle) override + { + fmt::throw_exception("Native Wayland swapchain is not implemented yet!"); + } + + void destroy(bool full = true) override + { + fmt::throw_exception("Native Wayland swapchain is not implemented yet!"); + } + + VkResult present(VkSemaphore /*semaphore*/, u32 index) override + { + fmt::throw_exception("Native Wayland swapchain is not implemented yet!"); + } + }; + +#ifndef HAVE_X11 + using swapchain_NATIVE = swapchain_Wayland; +#endif + +#endif +} diff --git a/rpcs3/Emu/RSX/VK/vkutils/swapchain_win32.hpp b/rpcs3/Emu/RSX/VK/vkutils/swapchain_win32.hpp new file mode 100644 index 0000000000..005a09d988 --- /dev/null +++ b/rpcs3/Emu/RSX/VK/vkutils/swapchain_win32.hpp @@ -0,0 +1,98 @@ +#pragma once + +#include "swapchain_core.h" + +namespace vk +{ +#if defined(_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, u32 present_queue, u32 graphics_queue, u32 transfer_queue, VkFormat format = VK_FORMAT_B8G8R8A8_UNORM) + : native_swapchain_base(gpu, present_queue, graphics_queue, transfer_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; + + if (m_width == 0 || m_height == 0) + { + rsx_log.error("Invalid window dimensions %d x %d", m_width, m_height); + return false; + } + + 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); + } + + void destroy(bool full = true) override + { + DeleteObject(hDIB); + DeleteDC(hSrcDC); + hDIB = NULL; + hSrcDC = NULL; + + swapchain_images.clear(); + + if (full) + { + ReleaseDC(window_handle, hDstDC); + hDstDC = NULL; + + dev.destroy(); + } + } + + VkResult present(VkSemaphore /*semaphore*/, u32 image) override + { + auto& src = swapchain_images[image]; + GdiFlush(); + + if (hSrcDC) + { + 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; + } + }; + + using swapchain_NATIVE = swapchain_WIN32; + + +#endif +} diff --git a/rpcs3/VKGSRender.vcxproj b/rpcs3/VKGSRender.vcxproj index 2c7d66e702..fb5c517adc 100644 --- a/rpcs3/VKGSRender.vcxproj +++ b/rpcs3/VKGSRender.vcxproj @@ -49,7 +49,12 @@ - + + + + + + @@ -96,6 +101,7 @@ + diff --git a/rpcs3/VKGSRender.vcxproj.filters b/rpcs3/VKGSRender.vcxproj.filters index 6af8e388bb..d9adc1939e 100644 --- a/rpcs3/VKGSRender.vcxproj.filters +++ b/rpcs3/VKGSRender.vcxproj.filters @@ -76,6 +76,9 @@ vkutils + + vkutils + @@ -142,7 +145,7 @@ vkutils - + vkutils @@ -178,6 +181,21 @@ vkutils + + vkutils + + + vkutils + + + vkutils + + + vkutils + + + vkutils +