Merge branch 'master' into LTO

This commit is contained in:
Elad 2025-03-02 07:22:50 +02:00 committed by GitHub
commit 08a7c8f017
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 1420 additions and 1254 deletions

View file

@ -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

View file

@ -4275,9 +4275,30 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<ppu_
}
}
if (!src && !Emu.klic.empty() && src.open(path))
{
src = decrypt_self(src, reinterpret_cast<u8*>(&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<std::string>& dir_queue, std::vector<ppu_
}
}
if (!src && !Emu.klic.empty() && src.open(path))
{
src = decrypt_self(src, reinterpret_cast<u8*>(&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;
}

View file

@ -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"

View file

@ -1,5 +1,5 @@
#include "device.h"
#include "instance.hpp"
#include "instance.h"
#include "util/logs.hpp"
#include "Emu/system_config.h"

View file

@ -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<PFN_vkCreateDebugReportCallbackEXT>(vkGetInstanceProcAddr(m_instance, "vkCreateDebugReportCallbackEXT"));
_vkDestroyDebugReportCallback = reinterpret_cast<PFN_vkDestroyDebugReportCallbackEXT>(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<const char*> extensions;
std::vector<const char*> 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<VkLayerSettingEXT> 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<uint32_t>(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<u32>(layers.size());
instance_info.ppEnabledLayerNames = layers.data();
instance_info.enabledExtensionCount = fast ? 0 : static_cast<u32>(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<physical_device>& 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<VkPhysicalDevice> 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<VkBool32> 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<VkSurfaceFormatKHR> 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);
}
}

View file

@ -0,0 +1,64 @@
#pragma once
#include "../VulkanAPI.h"
#include "swapchain.h"
#include <algorithm>
#include <vector>
#ifdef __APPLE__
#include <MoltenVK/mvk_vulkan.h>
#include <MoltenVK/mvk_private_api.h>
#endif
namespace vk
{
class supported_extensions
{
private:
std::vector<VkExtensionProperties> 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<physical_device> 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<physical_device>& enumerate_devices();
swapchain_base* create_swapchain(display_handle_t window_handle, vk::physical_device& dev);
};
}

View file

@ -1,457 +0,0 @@
#pragma once
#include "../VulkanAPI.h"
#include "swapchain.hpp"
#include <algorithm>
#include <vector>
#ifdef __APPLE__
#include <MoltenVK/mvk_vulkan.h>
#include <MoltenVK/mvk_private_api.h>
#endif
namespace vk
{
class supported_extensions
{
private:
std::vector<VkExtensionProperties> 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<physical_device> 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<PFN_vkCreateDebugReportCallbackEXT>(vkGetInstanceProcAddr(m_instance, "vkCreateDebugReportCallbackEXT"));
_vkDestroyDebugReportCallback = reinterpret_cast<PFN_vkDestroyDebugReportCallbackEXT>(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<const char*> extensions;
std::vector<const char*> 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<VkLayerSettingEXT> 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<uint32_t>(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<u32>(layers.size());
instance_info.ppEnabledLayerNames = layers.data();
instance_info.enabledExtensionCount = fast ? 0 : static_cast<u32>(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<physical_device>& 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<VkPhysicalDevice> 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<decltype(p)>;
#ifdef HAVE_X11
if constexpr (std::is_same_v<T, std::pair<Display*, Window>>)
{
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<T, std::pair<wl_display*, wl_surface*>>)
{
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<true, std::false_type, T>::value, "Unhandled window_handle type in std::variant");
}
}, window_handle);
#endif
u32 device_queues = dev.get_queue_count();
std::vector<VkBool32> 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<VkSurfaceFormatKHR> 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);
}
};
}

View file

@ -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<buffer>(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, &copyRegion);
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<swapchain_image_RPCS3>(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<VkImage> 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<PFN_vkCreateSwapchainKHR>(vkGetDeviceProcAddr(dev, "vkCreateSwapchainKHR"));
_vkDestroySwapchainKHR = reinterpret_cast<PFN_vkDestroySwapchainKHR>(vkGetDeviceProcAddr(dev, "vkDestroySwapchainKHR"));
_vkGetSwapchainImagesKHR = reinterpret_cast<PFN_vkGetSwapchainImagesKHR>(vkGetDeviceProcAddr(dev, "vkGetSwapchainImagesKHR"));
_vkAcquireNextImageKHR = reinterpret_cast<PFN_vkAcquireNextImageKHR>(vkGetDeviceProcAddr(dev, "vkAcquireNextImageKHR"));
_vkQueuePresentKHR = reinterpret_cast<PFN_vkQueuePresentKHR>(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<VkSurfaceCapabilitiesKHR, bool> 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<PFN_vkGetPhysicalDeviceSurfaceCapabilities2KHR>(
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<vk::physical_device&>(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<VkPresentModeKHR> 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<VkPresentModeKHR> 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<int>(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<int>(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 = &image;
if (semaphore != VK_NULL_HANDLE)
{
present.waitSemaphoreCount = 1;
present.pWaitSemaphores = &semaphore;
}
return _vkQueuePresentKHR(dev.get_present_queue(), &present);
}
}

View file

@ -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

View file

@ -1,786 +0,0 @@
#pragma once
#ifdef HAVE_X11
#include <X11/Xutil.h>
#endif
#include "../../display.h"
#include "../VulkanAPI.h"
#include "image.h"
#include <memory>
namespace vk
{
struct swapchain_image_WSI
{
VkImage value = VK_NULL_HANDLE;
};
class swapchain_image_RPCS3 : public image
{
std::unique_ptr<buffer> 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<buffer>(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, &copyRegion);
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<typename T>
class abstract_swapchain_impl : public swapchain_base
{
protected:
std::vector<T> 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<std::pair<bool, std::unique_ptr<swapchain_image_RPCS3>>>;
using WSI_swapchain_base = abstract_swapchain_impl<swapchain_image_WSI>;
#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<decltype(p)>;
if constexpr (std::is_same_v<T, std::pair<Display*, Window>>)
{
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<char*>(src.second->get_pixels());
XPutImage(display, window, gc, pixmap, 0, 0, 0, 0, m_width, m_height);
XFlush(display);
src.second->free_pixels();
}
//Release reference
src.first = false;
return VK_SUCCESS;
}
#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<swapchain_image_RPCS3>(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<VkImage> 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<PFN_vkCreateSwapchainKHR>(vkGetDeviceProcAddr(dev, "vkCreateSwapchainKHR"));
_vkDestroySwapchainKHR = reinterpret_cast<PFN_vkDestroySwapchainKHR>(vkGetDeviceProcAddr(dev, "vkDestroySwapchainKHR"));
_vkGetSwapchainImagesKHR = reinterpret_cast<PFN_vkGetSwapchainImagesKHR>(vkGetDeviceProcAddr(dev, "vkGetSwapchainImagesKHR"));
_vkAcquireNextImageKHR = reinterpret_cast<PFN_vkAcquireNextImageKHR>(vkGetDeviceProcAddr(dev, "vkAcquireNextImageKHR"));
_vkQueuePresentKHR = reinterpret_cast<PFN_vkQueuePresentKHR>(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<VkSurfaceCapabilitiesKHR, bool> 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<PFN_vkGetPhysicalDeviceSurfaceCapabilities2KHR>(
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<vk::physical_device&>(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<VkPresentModeKHR> 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<VkPresentModeKHR> 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<int>(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<int>(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 = &image;
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;
}
};
}

View file

@ -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
}

View file

@ -0,0 +1,227 @@
#pragma once
#ifdef HAVE_X11
#include <X11/Xutil.h>
#endif
#include "../../display.h"
#include "../VulkanAPI.h"
#include "image.h"
#include <memory>
namespace vk
{
struct swapchain_image_WSI
{
VkImage value = VK_NULL_HANDLE;
};
class swapchain_image_RPCS3 : public image
{
std::unique_ptr<buffer> 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<typename T>
class abstract_swapchain_impl : public swapchain_base
{
protected:
std::vector<T> 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<swapchain_image_WSI>;
class native_swapchain_base : public abstract_swapchain_impl<std::pair<bool, std::unique_ptr<swapchain_image_RPCS3>>>
{
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<VkSurfaceCapabilitiesKHR, bool> 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;
};
}

View file

@ -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
}

View file

@ -0,0 +1,172 @@
#pragma once
#include "swapchain_core.h"
#ifdef HAVE_X11
#include <X11/Xutil.h>
#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<decltype(p)>;
if constexpr (std::is_same_v<T, std::pair<Display*, Window>>)
{
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<char*>(src.second->get_pixels());
XPutImage(display, window, gc, pixmap, 0, 0, 0, 0, m_width, m_height);
XFlush(display);
src.second->free_pixels();
}
//Release reference
src.first = false;
return VK_SUCCESS;
}
};
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<decltype(p)>;
#ifdef HAVE_X11
if constexpr (std::is_same_v<T, std::pair<Display*, Window>>)
{
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<T, std::pair<wl_display*, wl_surface*>>)
{
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<true, std::false_type, T>::value, "Unhandled window_handle type in std::variant");
}
}, window_handle);
return ensure(result, "Failed to initialize Vulkan display surface");
}
}

View file

@ -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
}

View file

@ -49,7 +49,12 @@
<ClInclude Include="Emu\RSX\VK\vkutils\image.h" />
<ClInclude Include="Emu\RSX\VK\vkutils\image_helpers.h" />
<ClInclude Include="Emu\RSX\VK\vkutils\scratch.h" />
<ClInclude Include="Emu\RSX\VK\vkutils\swapchain.hpp" />
<ClInclude Include="Emu\RSX\VK\vkutils\swapchain.h" />
<ClInclude Include="Emu\RSX\VK\vkutils\swapchain_android.hpp" />
<ClInclude Include="Emu\RSX\VK\vkutils\swapchain_core.h" />
<ClInclude Include="Emu\RSX\VK\vkutils\swapchain_unix.hpp" />
<ClInclude Include="Emu\RSX\VK\vkutils\swapchain_macos.hpp" />
<ClInclude Include="Emu\RSX\VK\vkutils\swapchain_win32.hpp" />
<ClInclude Include="Emu\RSX\VK\vkutils\sync.h" />
<ClInclude Include="Emu\RSX\VK\vkutils\graphics_pipeline_state.hpp" />
<ClInclude Include="Emu\RSX\VK\vkutils\memory.h" />
@ -58,7 +63,7 @@
<ClInclude Include="Emu\RSX\VK\vkutils\query_pool.hpp" />
<ClInclude Include="Emu\RSX\VK\vkutils\sampler.h" />
<ClInclude Include="Emu\RSX\VK\vkutils\shared.h" />
<ClInclude Include="Emu\RSX\VK\vkutils\instance.hpp" />
<ClInclude Include="Emu\RSX\VK\vkutils\instance.h" />
<ClInclude Include="Emu\RSX\VK\VKVertexProgram.h" />
<ClInclude Include="Emu\RSX\VK\VulkanAPI.h" />
</ItemGroup>
@ -94,7 +99,9 @@
<ClCompile Include="Emu\RSX\VK\vkutils\data_heap.cpp" />
<ClCompile Include="Emu\RSX\VK\vkutils\image.cpp" />
<ClCompile Include="Emu\RSX\VK\vkutils\image_helpers.cpp" />
<ClCompile Include="Emu\RSX\VK\vkutils\instance.cpp" />
<ClCompile Include="Emu\RSX\VK\vkutils\scratch.cpp" />
<ClCompile Include="Emu\RSX\VK\vkutils\swapchain.cpp" />
<ClCompile Include="Emu\RSX\VK\vkutils\sync.cpp" />
<ClCompile Include="Emu\RSX\VK\vkutils\memory.cpp" />
<ClCompile Include="Emu\RSX\VK\vkutils\descriptors.cpp" />

View file

@ -73,6 +73,12 @@
<Filter>upscalers\fsr1</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\VK\VKCommonPipelineLayout.cpp" />
<ClCompile Include="Emu\RSX\VK\vkutils\instance.cpp">
<Filter>vkutils</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\VK\vkutils\swapchain.cpp">
<Filter>vkutils</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Emu\RSX\VK\VKCommonDecompiler.h" />
@ -124,7 +130,7 @@
<ClInclude Include="Emu\RSX\VK\vkutils\device.h">
<Filter>vkutils</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\vkutils\instance.hpp">
<ClInclude Include="Emu\RSX\VK\vkutils\instance.h">
<Filter>vkutils</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\vkutils\memory.h">
@ -139,7 +145,7 @@
<ClInclude Include="Emu\RSX\VK\vkutils\image.h">
<Filter>vkutils</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\vkutils\swapchain.hpp">
<ClInclude Include="Emu\RSX\VK\vkutils\swapchain.h">
<Filter>vkutils</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\vkutils\descriptors.h">
@ -175,6 +181,21 @@
<Filter>vkutils</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\VKCommonPipelineLayout.h" />
<ClInclude Include="Emu\RSX\VK\vkutils\swapchain_win32.hpp">
<Filter>vkutils</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\vkutils\swapchain_unix.hpp">
<Filter>vkutils</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\vkutils\swapchain_macos.hpp">
<Filter>vkutils</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\vkutils\swapchain_android.hpp">
<Filter>vkutils</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\VK\vkutils\swapchain_core.h">
<Filter>vkutils</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="vkutils">

View file

@ -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;
}

View file

@ -549,13 +549,12 @@ void main_window::show_boot_error(game_boot_result status)
}
const QString link = tr("<br /><br />For information on setting up the emulator and dumping your PS3 games, read the <a %0 href=\"https://rpcs3.net/quickstart\">quickstart guide</a>.").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();
}

View file

@ -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 <chrono>