diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index a6ae618ea6..562dd29476 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -597,8 +597,10 @@ if(TARGET 3rdparty_vulkan) RSX/VK/vkutils/descriptors.cpp RSX/VK/vkutils/image.cpp RSX/VK/vkutils/image_helpers.cpp + 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/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 63835db35f..90241016e6 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -4275,9 +4275,30 @@ extern void ppu_precompile(std::vector& dir_queue, std::vector(&Emu.klic[0])); + + if (src) + { + ppu_log.error("Possible missed KLIC for precompilation of '%s', please report to developers.", path); + + // Ignore executables larger than 500KB to prevent a long pause on exitspawn + if (src.size() >= 500000) + { + g_progr_ftotal_bits -= file_size; + + continue; + } + } + } + if (!src) { ppu_log.notice("Failed to decrypt '%s'", path); + + g_progr_ftotal_bits -= file_size; + continue; } @@ -4459,9 +4480,22 @@ extern void ppu_precompile(std::vector& dir_queue, std::vector(&Emu.klic[0])); + + if (src) + { + ppu_log.error("Possible missed KLIC for precompilation of '%s', please report to developers.", path); + } + } + if (!src) { ppu_log.notice("Failed to decrypt '%s'", path); + + g_progr_ftotal_bits -= file_size; + continue; } diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.h b/rpcs3/Emu/RSX/VK/VKGSRender.h index 028aa6b178..eaf9cc4a44 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.h +++ b/rpcs3/Emu/RSX/VK/VKGSRender.h @@ -4,9 +4,9 @@ #include "vkutils/descriptors.h" #include "vkutils/data_heap.h" -#include "vkutils/instance.hpp" +#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/device.cpp b/rpcs3/Emu/RSX/VK/vkutils/device.cpp index 207c1155c3..5391e1308e 100644 --- a/rpcs3/Emu/RSX/VK/vkutils/device.cpp +++ b/rpcs3/Emu/RSX/VK/vkutils/device.cpp @@ -1,5 +1,5 @@ #include "device.h" -#include "instance.hpp" +#include "instance.h" #include "util/logs.hpp" #include "Emu/system_config.h" diff --git a/rpcs3/Emu/RSX/VK/vkutils/instance.cpp b/rpcs3/Emu/RSX/VK/vkutils/instance.cpp new file mode 100644 index 0000000000..0527376e08 --- /dev/null +++ b/rpcs3/Emu/RSX/VK/vkutils/instance.cpp @@ -0,0 +1,366 @@ +#include "stdafx.h" +#include "instance.h" + +namespace vk +{ + // Supported extensions + supported_extensions::supported_extensions(enumeration_class _class, const char* layer_name, VkPhysicalDevice pdev) + { + u32 count; + if (_class == enumeration_class::instance) + { + if (vkEnumerateInstanceExtensionProperties(layer_name, &count, nullptr) != VK_SUCCESS) + return; + } + else + { + ensure(pdev); + if (vkEnumerateDeviceExtensionProperties(pdev, layer_name, &count, nullptr) != VK_SUCCESS) + return; + } + + m_vk_exts.resize(count); + if (_class == enumeration_class::instance) + { + vkEnumerateInstanceExtensionProperties(layer_name, &count, m_vk_exts.data()); + } + else + { + vkEnumerateDeviceExtensionProperties(pdev, layer_name, &count, m_vk_exts.data()); + } + } + + bool supported_extensions::is_supported(std::string_view ext) const + { + return std::any_of(m_vk_exts.cbegin(), m_vk_exts.cend(), [&](const VkExtensionProperties& p) { return p.extensionName == ext; }); + } + + // Instance + instance::~instance() + { + if (m_instance) + { + destroy(); + } + } + + void instance::destroy() + { + if (!m_instance) return; + + if (m_debugger) + { + _vkDestroyDebugReportCallback(m_instance, m_debugger, nullptr); + m_debugger = nullptr; + } + + if (m_surface) + { + vkDestroySurfaceKHR(m_instance, m_surface, nullptr); + m_surface = VK_NULL_HANDLE; + } + + vkDestroyInstance(m_instance, nullptr); + m_instance = VK_NULL_HANDLE; + } + + void instance::enable_debugging() + { + if (!g_cfg.video.debug_output) return; + + PFN_vkDebugReportCallbackEXT callback = vk::dbgFunc; + + _vkCreateDebugReportCallback = reinterpret_cast(vkGetInstanceProcAddr(m_instance, "vkCreateDebugReportCallbackEXT")); + _vkDestroyDebugReportCallback = reinterpret_cast(vkGetInstanceProcAddr(m_instance, "vkDestroyDebugReportCallbackEXT")); + + VkDebugReportCallbackCreateInfoEXT dbgCreateInfo = {}; + dbgCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT; + dbgCreateInfo.pfnCallback = callback; + dbgCreateInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; + + CHECK_RESULT(_vkCreateDebugReportCallback(m_instance, &dbgCreateInfo, NULL, &m_debugger)); + } + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wold-style-cast" +#endif + bool instance::create(const char* app_name, bool fast) + { + // Initialize a vulkan instance + VkApplicationInfo app = {}; + + app.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + app.pApplicationName = app_name; + app.applicationVersion = 0; + app.pEngineName = app_name; + app.engineVersion = 0; + app.apiVersion = VK_API_VERSION_1_0; + + // Set up instance information + + std::vector extensions; + std::vector layers; + const void* next_info = nullptr; + +#ifdef __APPLE__ + // Declare MVK variables here to ensure the lifetime within the entire scope + const VkBool32 setting_true = VK_TRUE; + const int32_t setting_fast_math = g_cfg.video.disable_msl_fast_math.get() ? MVK_CONFIG_FAST_MATH_NEVER : MVK_CONFIG_FAST_MATH_ON_DEMAND; + + std::vector mvk_settings; + VkLayerSettingsCreateInfoEXT mvk_layer_settings_create_info{}; +#endif + + if (!fast) + { + extensions_loaded = true; + supported_extensions support(supported_extensions::instance); + + extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME); + if (support.is_supported(VK_EXT_DEBUG_REPORT_EXTENSION_NAME)) + { + extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); + } + + if (support.is_supported(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) + { + extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + } + +#ifdef __APPLE__ + if (support.is_supported(VK_EXT_LAYER_SETTINGS_EXTENSION_NAME)) + { + extensions.push_back(VK_EXT_LAYER_SETTINGS_EXTENSION_NAME); + layers.push_back(kMVKMoltenVKDriverLayerName); + + mvk_settings.push_back(VkLayerSettingEXT{ kMVKMoltenVKDriverLayerName, "MVK_CONFIG_RESUME_LOST_DEVICE", VK_LAYER_SETTING_TYPE_BOOL32_EXT, 1, &setting_true }); + mvk_settings.push_back(VkLayerSettingEXT{ kMVKMoltenVKDriverLayerName, "MVK_CONFIG_FAST_MATH_ENABLED", VK_LAYER_SETTING_TYPE_INT32_EXT, 1, &setting_fast_math }); + + mvk_layer_settings_create_info.sType = VK_STRUCTURE_TYPE_LAYER_SETTINGS_CREATE_INFO_EXT; + mvk_layer_settings_create_info.pNext = next_info; + mvk_layer_settings_create_info.settingCount = static_cast(mvk_settings.size()); + mvk_layer_settings_create_info.pSettings = mvk_settings.data(); + + next_info = &mvk_layer_settings_create_info; + } +#endif + + if (support.is_supported(VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME)) + { + extensions.push_back(VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME); + } + + if (support.is_supported(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME)) + { + extensions.push_back(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME); + } + + if (g_cfg.video.renderdoc_compatiblity && support.is_supported(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) + { + extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + } + +#ifdef _WIN32 + extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); +#elif defined(__APPLE__) + extensions.push_back(VK_MVK_MACOS_SURFACE_EXTENSION_NAME); +#else + bool found_surface_ext = false; +#ifdef HAVE_X11 + if (support.is_supported(VK_KHR_XLIB_SURFACE_EXTENSION_NAME)) + { + extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); + found_surface_ext = true; + } +#endif +#ifdef VK_USE_PLATFORM_WAYLAND_KHR + if (support.is_supported(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME)) + { + extensions.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME); + found_surface_ext = true; + } +#endif //(WAYLAND) + if (!found_surface_ext) + { + rsx_log.error("Could not find a supported Vulkan surface extension"); + return 0; + } +#endif //(WIN32, __APPLE__) + if (g_cfg.video.debug_output) + layers.push_back("VK_LAYER_KHRONOS_validation"); + } + + VkInstanceCreateInfo instance_info = {}; + instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + instance_info.pApplicationInfo = &app; + instance_info.enabledLayerCount = static_cast(layers.size()); + instance_info.ppEnabledLayerNames = layers.data(); + instance_info.enabledExtensionCount = fast ? 0 : static_cast(extensions.size()); + instance_info.ppEnabledExtensionNames = fast ? nullptr : extensions.data(); + instance_info.pNext = next_info; + + if (VkResult result = vkCreateInstance(&instance_info, nullptr, &m_instance); result != VK_SUCCESS) + { + if (result == VK_ERROR_LAYER_NOT_PRESENT) + { + rsx_log.fatal("Could not initialize layer VK_LAYER_KHRONOS_validation"); + } + + return false; + } + + return true; + } +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + void instance::bind() + { + // Register some global states + if (m_debugger) + { + _vkDestroyDebugReportCallback(m_instance, m_debugger, nullptr); + m_debugger = nullptr; + } + + enable_debugging(); + } + + std::vector& instance::enumerate_devices() + { + u32 num_gpus; + // This may fail on unsupported drivers, so just assume no devices + if (vkEnumeratePhysicalDevices(m_instance, &num_gpus, nullptr) != VK_SUCCESS) + return gpus; + + if (gpus.size() != num_gpus) + { + std::vector pdevs(num_gpus); + gpus.resize(num_gpus); + + CHECK_RESULT(vkEnumeratePhysicalDevices(m_instance, &num_gpus, pdevs.data())); + + for (u32 i = 0; i < num_gpus; ++i) + gpus[i].create(m_instance, pdevs[i], extensions_loaded); + } + + return gpus; + } + + swapchain_base* instance::create_swapchain(display_handle_t window_handle, vk::physical_device& dev) + { + WSI_config surface_config + { + .supports_automatic_wm_reports = true + }; + m_surface = make_WSI_surface(m_instance, window_handle, &surface_config); + + u32 device_queues = dev.get_queue_count(); + std::vector supports_present(device_queues, VK_FALSE); + bool present_possible = true; + + for (u32 index = 0; index < device_queues; index++) + { + vkGetPhysicalDeviceSurfaceSupportKHR(dev, index, m_surface, &supports_present[index]); + } + + u32 graphics_queue_idx = -1; + u32 present_queue_idx = -1; + u32 transfer_queue_idx = -1; + + auto test_queue_family = [&](u32 index, u32 desired_flags) + { + if (const auto flags = dev.get_queue_properties(index).queueFlags; + (flags & desired_flags) == desired_flags) + { + return true; + } + + return false; + }; + + for (u32 i = 0; i < device_queues; ++i) + { + // 1. Test for a present queue possibly one that also supports present + if (present_queue_idx == umax && supports_present[i]) + { + present_queue_idx = i; + if (test_queue_family(i, VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT)) + { + graphics_queue_idx = i; + } + } + // 2. Check for graphics support + else if (graphics_queue_idx == umax && test_queue_family(i, VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT)) + { + graphics_queue_idx = i; + if (supports_present[i]) + { + present_queue_idx = i; + } + } + // 3. Check if transfer + compute is available + else if (transfer_queue_idx == umax && test_queue_family(i, VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT)) + { + transfer_queue_idx = i; + } + } + + if (graphics_queue_idx == umax) + { + rsx_log.fatal("Failed to find a suitable graphics queue"); + return nullptr; + } + + if (graphics_queue_idx != present_queue_idx) + { + // Separate graphics and present, use headless fallback + present_possible = false; + } + + if (!present_possible) + { + //Native(sw) swapchain + rsx_log.error("It is not possible for the currently selected GPU to present to the window (Likely caused by NVIDIA driver running the current display)"); + rsx_log.warning("Falling back to software present support (native windowing API)"); + auto swapchain = new swapchain_NATIVE(dev, -1, graphics_queue_idx, transfer_queue_idx); + swapchain->create(window_handle); + return swapchain; + } + + // Get the list of VkFormat's that are supported: + u32 formatCount; + CHECK_RESULT(vkGetPhysicalDeviceSurfaceFormatsKHR(dev, m_surface, &formatCount, nullptr)); + + std::vector surfFormats(formatCount); + CHECK_RESULT(vkGetPhysicalDeviceSurfaceFormatsKHR(dev, m_surface, &formatCount, surfFormats.data())); + + VkFormat format; + VkColorSpaceKHR color_space; + + if (formatCount == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED) + { + format = VK_FORMAT_B8G8R8A8_UNORM; + } + else + { + if (!formatCount) fmt::throw_exception("Format count is zero!"); + format = surfFormats[0].format; + + //Prefer BGRA8_UNORM to avoid sRGB compression (RADV) + for (auto& surface_format : surfFormats) + { + if (surface_format.format == VK_FORMAT_B8G8R8A8_UNORM) + { + format = VK_FORMAT_B8G8R8A8_UNORM; + break; + } + } + } + + color_space = surfFormats[0].colorSpace; + + return new swapchain_WSI(dev, present_queue_idx, graphics_queue_idx, transfer_queue_idx, format, m_surface, color_space, !surface_config.supports_automatic_wm_reports); + } +} diff --git a/rpcs3/Emu/RSX/VK/vkutils/instance.h b/rpcs3/Emu/RSX/VK/vkutils/instance.h new file mode 100644 index 0000000000..b4b87b1a76 --- /dev/null +++ b/rpcs3/Emu/RSX/VK/vkutils/instance.h @@ -0,0 +1,64 @@ +#pragma once + +#include "../VulkanAPI.h" +#include "swapchain.h" + +#include +#include + +#ifdef __APPLE__ +#include +#include +#endif + +namespace vk +{ + class supported_extensions + { + private: + std::vector m_vk_exts; + + public: + enum enumeration_class + { + instance = 0, + device = 1 + }; + + supported_extensions(enumeration_class _class, const char* layer_name = nullptr, VkPhysicalDevice pdev = VK_NULL_HANDLE); + + bool is_supported(std::string_view ext) const; + }; + + class instance + { + private: + std::vector gpus; + VkInstance m_instance = VK_NULL_HANDLE; + VkSurfaceKHR m_surface = VK_NULL_HANDLE; + + PFN_vkDestroyDebugReportCallbackEXT _vkDestroyDebugReportCallback = nullptr; + PFN_vkCreateDebugReportCallbackEXT _vkCreateDebugReportCallback = nullptr; + VkDebugReportCallbackEXT m_debugger = nullptr; + + bool extensions_loaded = false; + + public: + + instance() = default; + + ~instance(); + + void destroy(); + + void enable_debugging(); + + bool create(const char* app_name, bool fast = false); + + void bind(); + + std::vector& enumerate_devices(); + + swapchain_base* create_swapchain(display_handle_t window_handle, vk::physical_device& dev); + }; +} diff --git a/rpcs3/Emu/RSX/VK/vkutils/instance.hpp b/rpcs3/Emu/RSX/VK/vkutils/instance.hpp deleted file mode 100644 index 19ae72f9e0..0000000000 --- a/rpcs3/Emu/RSX/VK/vkutils/instance.hpp +++ /dev/null @@ -1,457 +0,0 @@ -#pragma once - -#include "../VulkanAPI.h" -#include "swapchain.hpp" - -#include -#include - -#ifdef __APPLE__ -#include -#include -#endif - -namespace vk -{ - class supported_extensions - { - private: - std::vector m_vk_exts; - - public: - enum enumeration_class - { - instance = 0, - device = 1 - }; - - supported_extensions(enumeration_class _class, const char* layer_name = nullptr, VkPhysicalDevice pdev = VK_NULL_HANDLE) - { - u32 count; - if (_class == enumeration_class::instance) - { - if (vkEnumerateInstanceExtensionProperties(layer_name, &count, nullptr) != VK_SUCCESS) - return; - } - else - { - ensure(pdev); - if (vkEnumerateDeviceExtensionProperties(pdev, layer_name, &count, nullptr) != VK_SUCCESS) - return; - } - - m_vk_exts.resize(count); - if (_class == enumeration_class::instance) - { - vkEnumerateInstanceExtensionProperties(layer_name, &count, m_vk_exts.data()); - } - else - { - vkEnumerateDeviceExtensionProperties(pdev, layer_name, &count, m_vk_exts.data()); - } - } - - bool is_supported(std::string_view ext) - { - return std::any_of(m_vk_exts.cbegin(), m_vk_exts.cend(), [&](const VkExtensionProperties& p) { return p.extensionName == ext; }); - } - }; - - class instance - { - private: - std::vector gpus; - VkInstance m_instance = VK_NULL_HANDLE; - VkSurfaceKHR m_surface = VK_NULL_HANDLE; - - PFN_vkDestroyDebugReportCallbackEXT _vkDestroyDebugReportCallback = nullptr; - PFN_vkCreateDebugReportCallbackEXT _vkCreateDebugReportCallback = nullptr; - VkDebugReportCallbackEXT m_debugger = nullptr; - - bool extensions_loaded = false; - - public: - - instance() = default; - - ~instance() - { - if (m_instance) - { - destroy(); - } - } - - void destroy() - { - if (!m_instance) return; - - if (m_debugger) - { - _vkDestroyDebugReportCallback(m_instance, m_debugger, nullptr); - m_debugger = nullptr; - } - - if (m_surface) - { - vkDestroySurfaceKHR(m_instance, m_surface, nullptr); - m_surface = VK_NULL_HANDLE; - } - - vkDestroyInstance(m_instance, nullptr); - m_instance = VK_NULL_HANDLE; - } - - void enable_debugging() - { - if (!g_cfg.video.debug_output) return; - - PFN_vkDebugReportCallbackEXT callback = vk::dbgFunc; - - _vkCreateDebugReportCallback = reinterpret_cast(vkGetInstanceProcAddr(m_instance, "vkCreateDebugReportCallbackEXT")); - _vkDestroyDebugReportCallback = reinterpret_cast(vkGetInstanceProcAddr(m_instance, "vkDestroyDebugReportCallbackEXT")); - - VkDebugReportCallbackCreateInfoEXT dbgCreateInfo = {}; - dbgCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT; - dbgCreateInfo.pfnCallback = callback; - dbgCreateInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; - - CHECK_RESULT(_vkCreateDebugReportCallback(m_instance, &dbgCreateInfo, NULL, &m_debugger)); - } -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wold-style-cast" -#endif - bool create(const char* app_name, bool fast = false) - { - // Initialize a vulkan instance - VkApplicationInfo app = {}; - - app.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; - app.pApplicationName = app_name; - app.applicationVersion = 0; - app.pEngineName = app_name; - app.engineVersion = 0; - app.apiVersion = VK_API_VERSION_1_0; - - // Set up instance information - - std::vector extensions; - std::vector layers; - const void* next_info = nullptr; - -#ifdef __APPLE__ - // Declare MVK variables here to ensure the lifetime within the entire scope - const VkBool32 setting_true = VK_TRUE; - const int32_t setting_fast_math = g_cfg.video.disable_msl_fast_math.get() ? MVK_CONFIG_FAST_MATH_NEVER : MVK_CONFIG_FAST_MATH_ON_DEMAND; - - std::vector mvk_settings; - VkLayerSettingsCreateInfoEXT mvk_layer_settings_create_info{}; -#endif - - if (!fast) - { - extensions_loaded = true; - supported_extensions support(supported_extensions::instance); - - extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME); - if (support.is_supported(VK_EXT_DEBUG_REPORT_EXTENSION_NAME)) - { - extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); - } - - if (support.is_supported(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) - { - extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); - } - -#ifdef __APPLE__ - if (support.is_supported(VK_EXT_LAYER_SETTINGS_EXTENSION_NAME)) - { - extensions.push_back(VK_EXT_LAYER_SETTINGS_EXTENSION_NAME); - layers.push_back(kMVKMoltenVKDriverLayerName); - - mvk_settings.push_back(VkLayerSettingEXT{ kMVKMoltenVKDriverLayerName, "MVK_CONFIG_RESUME_LOST_DEVICE", VK_LAYER_SETTING_TYPE_BOOL32_EXT, 1, &setting_true }); - mvk_settings.push_back(VkLayerSettingEXT{ kMVKMoltenVKDriverLayerName, "MVK_CONFIG_FAST_MATH_ENABLED", VK_LAYER_SETTING_TYPE_INT32_EXT, 1, &setting_fast_math }); - - mvk_layer_settings_create_info.sType = VK_STRUCTURE_TYPE_LAYER_SETTINGS_CREATE_INFO_EXT; - mvk_layer_settings_create_info.pNext = next_info; - mvk_layer_settings_create_info.settingCount = static_cast(mvk_settings.size()); - mvk_layer_settings_create_info.pSettings = mvk_settings.data(); - - next_info = &mvk_layer_settings_create_info; - } -#endif - - if (support.is_supported(VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME)) - { - extensions.push_back(VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME); - } - - if (support.is_supported(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME)) - { - extensions.push_back(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME); - } - - if (g_cfg.video.renderdoc_compatiblity && support.is_supported(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) - { - extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); - } - -#ifdef _WIN32 - extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); -#elif defined(__APPLE__) - extensions.push_back(VK_MVK_MACOS_SURFACE_EXTENSION_NAME); -#else - bool found_surface_ext = false; -#ifdef HAVE_X11 - if (support.is_supported(VK_KHR_XLIB_SURFACE_EXTENSION_NAME)) - { - extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); - found_surface_ext = true; - } -#endif -#ifdef VK_USE_PLATFORM_WAYLAND_KHR - if (support.is_supported(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME)) - { - extensions.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME); - found_surface_ext = true; - } -#endif //(WAYLAND) - if (!found_surface_ext) - { - rsx_log.error("Could not find a supported Vulkan surface extension"); - return 0; - } -#endif //(WIN32, __APPLE__) - if (g_cfg.video.debug_output) - layers.push_back("VK_LAYER_KHRONOS_validation"); - } - - VkInstanceCreateInfo instance_info = {}; - instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - instance_info.pApplicationInfo = &app; - instance_info.enabledLayerCount = static_cast(layers.size()); - instance_info.ppEnabledLayerNames = layers.data(); - instance_info.enabledExtensionCount = fast ? 0 : static_cast(extensions.size()); - instance_info.ppEnabledExtensionNames = fast ? nullptr : extensions.data(); - instance_info.pNext = next_info; - - if (VkResult result = vkCreateInstance(&instance_info, nullptr, &m_instance); result != VK_SUCCESS) - { - if (result == VK_ERROR_LAYER_NOT_PRESENT) - { - rsx_log.fatal("Could not initialize layer VK_LAYER_KHRONOS_validation"); - } - - return false; - } - - return true; - } -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - void bind() - { - // Register some global states - if (m_debugger) - { - _vkDestroyDebugReportCallback(m_instance, m_debugger, nullptr); - m_debugger = nullptr; - } - - enable_debugging(); - } - - std::vector& enumerate_devices() - { - u32 num_gpus; - // This may fail on unsupported drivers, so just assume no devices - if (vkEnumeratePhysicalDevices(m_instance, &num_gpus, nullptr) != VK_SUCCESS) - return gpus; - - if (gpus.size() != num_gpus) - { - std::vector pdevs(num_gpus); - gpus.resize(num_gpus); - - CHECK_RESULT(vkEnumeratePhysicalDevices(m_instance, &num_gpus, pdevs.data())); - - for (u32 i = 0; i < num_gpus; ++i) - gpus[i].create(m_instance, pdevs[i], extensions_loaded); - } - - return gpus; - } - - swapchain_base* create_swapchain(display_handle_t window_handle, vk::physical_device& dev) - { - bool force_wm_reporting_off = false; -#ifdef _WIN32 - using swapchain_NATIVE = swapchain_WIN32; - HINSTANCE hInstance = NULL; - - VkWin32SurfaceCreateInfoKHR createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; - createInfo.hinstance = hInstance; - createInfo.hwnd = window_handle; - - CHECK_RESULT(vkCreateWin32SurfaceKHR(m_instance, &createInfo, NULL, &m_surface)); - -#elif defined(__APPLE__) - using swapchain_NATIVE = swapchain_MacOS; - VkMacOSSurfaceCreateInfoMVK createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK; - createInfo.pView = window_handle; - - CHECK_RESULT(vkCreateMacOSSurfaceMVK(m_instance, &createInfo, NULL, &m_surface)); -#else -#ifdef HAVE_X11 - using swapchain_NATIVE = swapchain_X11; -#else - using swapchain_NATIVE = swapchain_Wayland; -#endif - - std::visit([&](auto&& p) - { - using T = std::decay_t; - -#ifdef HAVE_X11 - if constexpr (std::is_same_v>) - { - VkXlibSurfaceCreateInfoKHR createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; - createInfo.dpy = p.first; - createInfo.window = p.second; - CHECK_RESULT(vkCreateXlibSurfaceKHR(this->m_instance, &createInfo, nullptr, &m_surface)); - } - else -#endif -#ifdef HAVE_WAYLAND - if constexpr (std::is_same_v>) - { - VkWaylandSurfaceCreateInfoKHR createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; - createInfo.display = p.first; - createInfo.surface = p.second; - CHECK_RESULT(vkCreateWaylandSurfaceKHR(this->m_instance, &createInfo, nullptr, &m_surface)); - force_wm_reporting_off = true; - } - else -#endif - { - static_assert(std::conditional_t::value, "Unhandled window_handle type in std::variant"); - } - }, window_handle); -#endif - - u32 device_queues = dev.get_queue_count(); - std::vector supports_present(device_queues, VK_FALSE); - bool present_possible = true; - - for (u32 index = 0; index < device_queues; index++) - { - vkGetPhysicalDeviceSurfaceSupportKHR(dev, index, m_surface, &supports_present[index]); - } - - u32 graphics_queue_idx = -1; - u32 present_queue_idx = -1; - u32 transfer_queue_idx = -1; - - auto test_queue_family = [&](u32 index, u32 desired_flags) - { - if (const auto flags = dev.get_queue_properties(index).queueFlags; - (flags & desired_flags) == desired_flags) - { - return true; - } - - return false; - }; - - for (u32 i = 0; i < device_queues; ++i) - { - // 1. Test for a present queue possibly one that also supports present - if (present_queue_idx == umax && supports_present[i]) - { - present_queue_idx = i; - if (test_queue_family(i, VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT)) - { - graphics_queue_idx = i; - } - } - // 2. Check for graphics support - else if (graphics_queue_idx == umax && test_queue_family(i, VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT)) - { - graphics_queue_idx = i; - if (supports_present[i]) - { - present_queue_idx = i; - } - } - // 3. Check if transfer + compute is available - else if (transfer_queue_idx == umax && test_queue_family(i, VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT)) - { - transfer_queue_idx = i; - } - } - - if (graphics_queue_idx == umax) - { - rsx_log.fatal("Failed to find a suitable graphics queue"); - return nullptr; - } - - if (graphics_queue_idx != present_queue_idx) - { - // Separate graphics and present, use headless fallback - present_possible = false; - } - - if (!present_possible) - { - //Native(sw) swapchain - rsx_log.error("It is not possible for the currently selected GPU to present to the window (Likely caused by NVIDIA driver running the current display)"); - rsx_log.warning("Falling back to software present support (native windowing API)"); - auto swapchain = new swapchain_NATIVE(dev, -1, graphics_queue_idx, transfer_queue_idx); - swapchain->create(window_handle); - return swapchain; - } - - // Get the list of VkFormat's that are supported: - u32 formatCount; - CHECK_RESULT(vkGetPhysicalDeviceSurfaceFormatsKHR(dev, m_surface, &formatCount, nullptr)); - - std::vector surfFormats(formatCount); - CHECK_RESULT(vkGetPhysicalDeviceSurfaceFormatsKHR(dev, m_surface, &formatCount, surfFormats.data())); - - VkFormat format; - VkColorSpaceKHR color_space; - - if (formatCount == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED) - { - format = VK_FORMAT_B8G8R8A8_UNORM; - } - else - { - if (!formatCount) fmt::throw_exception("Format count is zero!"); - format = surfFormats[0].format; - - //Prefer BGRA8_UNORM to avoid sRGB compression (RADV) - for (auto& surface_format : surfFormats) - { - if (surface_format.format == VK_FORMAT_B8G8R8A8_UNORM) - { - format = VK_FORMAT_B8G8R8A8_UNORM; - break; - } - } - } - - color_space = surfFormats[0].colorSpace; - - return new swapchain_WSI(dev, present_queue_idx, graphics_queue_idx, transfer_queue_idx, format, m_surface, color_space, force_wm_reporting_off); - } - }; -} 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..ccec22a383 --- /dev/null +++ b/rpcs3/Emu/RSX/VK/vkutils/swapchain_android.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include "swapchain_core.h" + +namespace vk +{ +#if defined(ANDROID) + using swapchain_ANDROID = native_swapchain_base; + using swapchain_NATIVE = swapchain_ANDROID; + + // TODO: Implement this + [[maybe_unused]] static + VkSurfaceKHR make_WSI_surface(VkInstance vk_instance, display_handle_t window_handle, WSI_config* /*config*/) + { + return VK_NULL_HANDLE; + } +#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..aaf8426b9a --- /dev/null +++ b/rpcs3/Emu/RSX/VK/vkutils/swapchain_core.h @@ -0,0 +1,227 @@ +#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; + + // Clients must implement these methods to render without WSI support + bool init() override + { + fmt::throw_exception("Native swapchain is not implemented yet!"); + } + + void create(display_handle_t& /*window_handle*/) override + { + fmt::throw_exception("Native swapchain is not implemented yet!"); + } + + void destroy(bool /*full*/ = true) override + { + fmt::throw_exception("Native swapchain is not implemented yet!"); + } + + VkResult present(VkSemaphore /*semaphore*/, u32 /*index*/) override + { + fmt::throw_exception("Native swapchain is not implemented yet!"); + } + + // Generic accessors + 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; + } + }; + + struct WSI_config + { + bool supports_automatic_wm_reports = true; + }; +} 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..9e4217692d --- /dev/null +++ b/rpcs3/Emu/RSX/VK/vkutils/swapchain_macos.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include "swapchain_core.h" + +namespace vk +{ +#if defined(__APPLE__) + using swapchain_MacOS = native_swapchain_base; + using swapchain_NATIVE = swapchain_MacOS; + + [[maybe_unused]] static + VkSurfaceKHR make_WSI_surface(VkInstance vk_instance, display_handle_t window_handle, WSI_config* /*config*/) + { + VkSurfaceKHR result = VK_NULL_HANDLE; + VkMacOSSurfaceCreateInfoMVK createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK; + createInfo.pView = window_handle; + + CHECK_RESULT(vkCreateMacOSSurfaceMVK(vk_instance, &createInfo, NULL, &result)); + return result; + } +#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..ec893da8e8 --- /dev/null +++ b/rpcs3/Emu/RSX/VK/vkutils/swapchain_unix.hpp @@ -0,0 +1,172 @@ +#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) + using swapchain_Wayland = native_swapchain_base; + +#ifndef HAVE_X11 + using swapchain_NATIVE = swapchain_Wayland; +#endif + +#endif + + [[maybe_unused]] static + VkSurfaceKHR make_WSI_surface(VkInstance vk_instance, display_handle_t window_handle, WSI_config* config) + { + VkSurfaceKHR result = VK_NULL_HANDLE; + + std::visit([&](auto&& p) + { + using T = std::decay_t; + +#ifdef HAVE_X11 + if constexpr (std::is_same_v>) + { + VkXlibSurfaceCreateInfoKHR createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; + createInfo.dpy = p.first; + createInfo.window = p.second; + CHECK_RESULT(vkCreateXlibSurfaceKHR(vk_instance, &createInfo, nullptr, &result)); + } + else +#endif +#ifdef HAVE_WAYLAND + if constexpr (std::is_same_v>) + { + VkWaylandSurfaceCreateInfoKHR createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; + createInfo.display = p.first; + createInfo.surface = p.second; + CHECK_RESULT(vkCreateWaylandSurfaceKHR(vk_instance, &createInfo, nullptr, &result)); + config->supports_automatic_wm_reports = false; + } + else +#endif + { + static_assert(std::conditional_t::value, "Unhandled window_handle type in std::variant"); + } + }, window_handle); + + return ensure(result, "Failed to initialize Vulkan display surface"); + } +} 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..c509d7cffa --- /dev/null +++ b/rpcs3/Emu/RSX/VK/vkutils/swapchain_win32.hpp @@ -0,0 +1,110 @@ +#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; + + [[maybe_unused]] static + VkSurfaceKHR make_WSI_surface(VkInstance vk_instance, display_handle_t window_handle, WSI_config* /*config*/) + { + HINSTANCE hInstance = NULL; + VkSurfaceKHR result = VK_NULL_HANDLE; + + VkWin32SurfaceCreateInfoKHR createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + createInfo.hinstance = hInstance; + createInfo.hwnd = window_handle; + CHECK_RESULT(vkCreateWin32SurfaceKHR(vk_instance, &createInfo, NULL, &result)); + return result; + } +#endif +} diff --git a/rpcs3/VKGSRender.vcxproj b/rpcs3/VKGSRender.vcxproj index 868ff9ad66..fb5c517adc 100644 --- a/rpcs3/VKGSRender.vcxproj +++ b/rpcs3/VKGSRender.vcxproj @@ -49,7 +49,12 @@ - + + + + + + @@ -58,7 +63,7 @@ - + @@ -94,7 +99,9 @@ + + diff --git a/rpcs3/VKGSRender.vcxproj.filters b/rpcs3/VKGSRender.vcxproj.filters index 153a21a9b0..d9adc1939e 100644 --- a/rpcs3/VKGSRender.vcxproj.filters +++ b/rpcs3/VKGSRender.vcxproj.filters @@ -73,6 +73,12 @@ upscalers\fsr1 + + vkutils + + + vkutils + @@ -124,7 +130,7 @@ vkutils - + vkutils @@ -139,7 +145,7 @@ vkutils - + vkutils @@ -175,6 +181,21 @@ vkutils + + vkutils + + + vkutils + + + vkutils + + + vkutils + + + vkutils + diff --git a/rpcs3/rpcs3_version.cpp b/rpcs3/rpcs3_version.cpp index cafa870e2a..57bac63f22 100644 --- a/rpcs3/rpcs3_version.cpp +++ b/rpcs3/rpcs3_version.cpp @@ -28,7 +28,7 @@ namespace rpcs3 // Currently accessible by Windows and Linux build scripts, see implementations when doing MACOSX const utils::version& get_version() { - static constexpr utils::version version{ 0, 0, 34, utils::version_type::alpha, 1, RPCS3_GIT_VERSION }; + static constexpr utils::version version{ 0, 0, 35, utils::version_type::alpha, 1, RPCS3_GIT_VERSION }; return version; } diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index a0f94331fd..eaa72807f3 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -549,13 +549,12 @@ void main_window::show_boot_error(game_boot_result status) } const QString link = tr("

For information on setting up the emulator and dumping your PS3 games, read the quickstart guide.").arg(gui::utils::get_link_style()); - QMessageBox* msg = new QMessageBox(); + QMessageBox* msg = new QMessageBox(this); msg->setWindowTitle(tr("Boot Failed")); msg->setIcon(QMessageBox::Critical); msg->setTextFormat(Qt::RichText); msg->setStandardButtons(QMessageBox::Ok); msg->setText(tr("Booting failed: %1 %2").arg(message).arg(link)); - msg->setParent(this); msg->setAttribute(Qt::WA_DeleteOnClose); msg->open(); } diff --git a/rpcs3/rpcs3qt/render_creator.cpp b/rpcs3/rpcs3qt/render_creator.cpp index b85336bf8b..ef425366b3 100644 --- a/rpcs3/rpcs3qt/render_creator.cpp +++ b/rpcs3/rpcs3qt/render_creator.cpp @@ -5,7 +5,7 @@ #include "Utilities/Thread.h" #if defined(HAVE_VULKAN) -#include "Emu/RSX/VK/vkutils/instance.hpp" +#include "Emu/RSX/VK/vkutils/instance.h" #endif #include