yuzu: Drop SDL2 and Qt frontend Vulkan requirements
Create Vulkan instances and surfaces from the Vulkan backend.
This commit is contained in:
parent
bc1b4b85b0
commit
bf1d66b7c0
|
@ -12,6 +12,15 @@
|
||||||
|
|
||||||
namespace Core::Frontend {
|
namespace Core::Frontend {
|
||||||
|
|
||||||
|
/// Information for the Graphics Backends signifying what type of screen pointer is in
|
||||||
|
/// WindowInformation
|
||||||
|
enum class WindowSystemType {
|
||||||
|
Headless,
|
||||||
|
Windows,
|
||||||
|
X11,
|
||||||
|
Wayland,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a drawing context that supports graphics operations.
|
* Represents a drawing context that supports graphics operations.
|
||||||
*/
|
*/
|
||||||
|
@ -76,6 +85,23 @@ public:
|
||||||
std::pair<unsigned, unsigned> min_client_area_size;
|
std::pair<unsigned, unsigned> min_client_area_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Data describing host window system information
|
||||||
|
struct WindowSystemInfo {
|
||||||
|
// Window system type. Determines which GL context or Vulkan WSI is used.
|
||||||
|
WindowSystemType type = WindowSystemType::Headless;
|
||||||
|
|
||||||
|
// Connection to a display server. This is used on X11 and Wayland platforms.
|
||||||
|
void* display_connection = nullptr;
|
||||||
|
|
||||||
|
// Render surface. This is a pointer to the native window handle, which depends
|
||||||
|
// on the platform. e.g. HWND for Windows, Window for X11. If the surface is
|
||||||
|
// set to nullptr, the video backend will run in headless mode.
|
||||||
|
void* render_surface = nullptr;
|
||||||
|
|
||||||
|
// Scale of the render surface. For hidpi systems, this will be >1.
|
||||||
|
float render_surface_scale = 1.0f;
|
||||||
|
};
|
||||||
|
|
||||||
/// Polls window events
|
/// Polls window events
|
||||||
virtual void PollEvents() = 0;
|
virtual void PollEvents() = 0;
|
||||||
|
|
||||||
|
@ -87,10 +113,6 @@ public:
|
||||||
/// Returns if window is shown (not minimized)
|
/// Returns if window is shown (not minimized)
|
||||||
virtual bool IsShown() const = 0;
|
virtual bool IsShown() const = 0;
|
||||||
|
|
||||||
/// Retrieves Vulkan specific handlers from the window
|
|
||||||
virtual void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
|
||||||
void* surface) const = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signal that a touch pressed event has occurred (e.g. mouse click pressed)
|
* Signal that a touch pressed event has occurred (e.g. mouse click pressed)
|
||||||
* @param framebuffer_x Framebuffer x-coordinate that was pressed
|
* @param framebuffer_x Framebuffer x-coordinate that was pressed
|
||||||
|
@ -127,6 +149,13 @@ public:
|
||||||
config = val;
|
config = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns system information about the drawing area.
|
||||||
|
*/
|
||||||
|
const WindowSystemInfo& GetWindowInfo() const {
|
||||||
|
return window_info;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the framebuffer layout (width, height, and screen regions)
|
* Gets the framebuffer layout (width, height, and screen regions)
|
||||||
* @note This method is thread-safe
|
* @note This method is thread-safe
|
||||||
|
@ -142,7 +171,7 @@ public:
|
||||||
void UpdateCurrentFramebufferLayout(unsigned width, unsigned height);
|
void UpdateCurrentFramebufferLayout(unsigned width, unsigned height);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
EmuWindow();
|
explicit EmuWindow();
|
||||||
virtual ~EmuWindow();
|
virtual ~EmuWindow();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -179,6 +208,8 @@ protected:
|
||||||
client_area_height = size.second;
|
client_area_height = size.second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WindowSystemInfo window_info;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* Handler called when the minimal client area was requested to be changed via SetConfig.
|
* Handler called when the minimal client area was requested to be changed via SetConfig.
|
||||||
|
|
|
@ -51,6 +51,7 @@ using UniqueSampler = UniqueHandle<vk::Sampler>;
|
||||||
using UniqueSamplerYcbcrConversion = UniqueHandle<vk::SamplerYcbcrConversion>;
|
using UniqueSamplerYcbcrConversion = UniqueHandle<vk::SamplerYcbcrConversion>;
|
||||||
using UniqueSemaphore = UniqueHandle<vk::Semaphore>;
|
using UniqueSemaphore = UniqueHandle<vk::Semaphore>;
|
||||||
using UniqueShaderModule = UniqueHandle<vk::ShaderModule>;
|
using UniqueShaderModule = UniqueHandle<vk::ShaderModule>;
|
||||||
|
using UniqueSurfaceKHR = UniqueHandle<vk::SurfaceKHR>;
|
||||||
using UniqueSwapchainKHR = UniqueHandle<vk::SwapchainKHR>;
|
using UniqueSwapchainKHR = UniqueHandle<vk::SwapchainKHR>;
|
||||||
using UniqueValidationCacheEXT = UniqueHandle<vk::ValidationCacheEXT>;
|
using UniqueValidationCacheEXT = UniqueHandle<vk::ValidationCacheEXT>;
|
||||||
using UniqueDebugReportCallbackEXT = UniqueHandle<vk::DebugReportCallbackEXT>;
|
using UniqueDebugReportCallbackEXT = UniqueHandle<vk::DebugReportCallbackEXT>;
|
||||||
|
|
|
@ -2,8 +2,12 @@
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <cstring>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
@ -31,15 +35,30 @@
|
||||||
#include "video_core/renderer_vulkan/vk_state_tracker.h"
|
#include "video_core/renderer_vulkan/vk_state_tracker.h"
|
||||||
#include "video_core/renderer_vulkan/vk_swapchain.h"
|
#include "video_core/renderer_vulkan/vk_swapchain.h"
|
||||||
|
|
||||||
|
// Include these late to avoid changing Vulkan-Hpp's dynamic dispatcher size
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
// ensure include order
|
||||||
|
#include <vulkan/vulkan_win32.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <vulkan/vulkan_wayland.h>
|
||||||
|
#include <vulkan/vulkan_xlib.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
using Core::Frontend::WindowSystemType;
|
||||||
|
|
||||||
VkBool32 DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity_,
|
VkBool32 DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity_,
|
||||||
VkDebugUtilsMessageTypeFlagsEXT type,
|
VkDebugUtilsMessageTypeFlagsEXT type,
|
||||||
const VkDebugUtilsMessengerCallbackDataEXT* data,
|
const VkDebugUtilsMessengerCallbackDataEXT* data,
|
||||||
[[maybe_unused]] void* user_data) {
|
[[maybe_unused]] void* user_data) {
|
||||||
const vk::DebugUtilsMessageSeverityFlagBitsEXT severity{severity_};
|
const auto severity{static_cast<vk::DebugUtilsMessageSeverityFlagBitsEXT>(severity_)};
|
||||||
const char* message{data->pMessage};
|
const char* message{data->pMessage};
|
||||||
|
|
||||||
if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eError) {
|
if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eError) {
|
||||||
|
@ -75,21 +94,86 @@ Common::DynamicLibrary OpenVulkanLibrary() {
|
||||||
return library;
|
return library;
|
||||||
}
|
}
|
||||||
|
|
||||||
UniqueInstance CreateInstance(Common::DynamicLibrary& library, vk::DispatchLoaderDynamic& dld) {
|
UniqueInstance CreateInstance(Common::DynamicLibrary& library, vk::DispatchLoaderDynamic& dld,
|
||||||
|
WindowSystemType window_type = WindowSystemType::Headless,
|
||||||
|
bool enable_layers = false) {
|
||||||
|
if (!library.IsOpen()) {
|
||||||
|
LOG_ERROR(Render_Vulkan, "Vulkan library not available");
|
||||||
|
return UniqueInstance{};
|
||||||
|
}
|
||||||
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
|
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
|
||||||
if (!library.GetSymbol("vkGetInstanceProcAddr", &vkGetInstanceProcAddr)) {
|
if (!library.GetSymbol("vkGetInstanceProcAddr", &vkGetInstanceProcAddr)) {
|
||||||
|
LOG_ERROR(Render_Vulkan, "vkGetInstanceProcAddr not present in Vulkan");
|
||||||
return UniqueInstance{};
|
return UniqueInstance{};
|
||||||
}
|
}
|
||||||
dld.init(vkGetInstanceProcAddr);
|
dld.init(vkGetInstanceProcAddr);
|
||||||
|
|
||||||
const vk::ApplicationInfo application_info("yuzu", VK_MAKE_VERSION(0, 1, 0), "yuzu",
|
std::vector<const char*> extensions;
|
||||||
VK_MAKE_VERSION(0, 1, 0), VK_API_VERSION_1_1);
|
extensions.reserve(4);
|
||||||
const vk::InstanceCreateInfo instance_ci({}, &application_info, 0, nullptr, 0, nullptr);
|
switch (window_type) {
|
||||||
vk::Instance unsafe_instance;
|
case Core::Frontend::WindowSystemType::Headless:
|
||||||
if (vk::createInstance(&instance_ci, nullptr, &unsafe_instance, dld) != vk::Result::eSuccess) {
|
break;
|
||||||
|
#ifdef _WIN32
|
||||||
|
case Core::Frontend::WindowSystemType::Windows:
|
||||||
|
extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
#ifdef __linux__
|
||||||
|
case Core::Frontend::WindowSystemType::X11:
|
||||||
|
extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
|
||||||
|
break;
|
||||||
|
case Core::Frontend::WindowSystemType::Wayland:
|
||||||
|
extensions.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
LOG_ERROR(Render_Vulkan, "Presentation not supported on this platform");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (window_type != Core::Frontend::WindowSystemType::Headless) {
|
||||||
|
extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
|
||||||
|
}
|
||||||
|
if (enable_layers) {
|
||||||
|
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 num_properties;
|
||||||
|
if (vk::enumerateInstanceExtensionProperties(nullptr, &num_properties, nullptr, dld) !=
|
||||||
|
vk::Result::eSuccess) {
|
||||||
|
LOG_ERROR(Render_Vulkan, "Failed to query number of extension properties");
|
||||||
return UniqueInstance{};
|
return UniqueInstance{};
|
||||||
}
|
}
|
||||||
dld.init(unsafe_instance, vkGetInstanceProcAddr);
|
std::vector<vk::ExtensionProperties> properties(num_properties);
|
||||||
|
if (vk::enumerateInstanceExtensionProperties(nullptr, &num_properties, properties.data(),
|
||||||
|
dld) != vk::Result::eSuccess) {
|
||||||
|
LOG_ERROR(Render_Vulkan, "Failed to query extension properties");
|
||||||
|
return UniqueInstance{};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const char* extension : extensions) {
|
||||||
|
const auto it =
|
||||||
|
std::find_if(properties.begin(), properties.end(), [extension](const auto& prop) {
|
||||||
|
return !std::strcmp(extension, prop.extensionName);
|
||||||
|
});
|
||||||
|
if (it == properties.end()) {
|
||||||
|
LOG_ERROR(Render_Vulkan, "Required instance extension {} is not available", extension);
|
||||||
|
return UniqueInstance{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const vk::ApplicationInfo application_info("yuzu Emulator", VK_MAKE_VERSION(0, 1, 0),
|
||||||
|
"yuzu Emulator", VK_MAKE_VERSION(0, 1, 0),
|
||||||
|
VK_API_VERSION_1_1);
|
||||||
|
const std::array layers = {"VK_LAYER_LUNARG_standard_validation"};
|
||||||
|
const vk::InstanceCreateInfo instance_ci(
|
||||||
|
{}, &application_info, enable_layers ? static_cast<u32>(layers.size()) : 0, layers.data(),
|
||||||
|
static_cast<u32>(extensions.size()), extensions.data());
|
||||||
|
vk::Instance unsafe_instance;
|
||||||
|
if (vk::createInstance(&instance_ci, nullptr, &unsafe_instance, dld) != vk::Result::eSuccess) {
|
||||||
|
LOG_ERROR(Render_Vulkan, "Failed to create Vulkan instance");
|
||||||
|
return UniqueInstance{};
|
||||||
|
}
|
||||||
|
dld.init(unsafe_instance);
|
||||||
return UniqueInstance(unsafe_instance, {nullptr, dld});
|
return UniqueInstance(unsafe_instance, {nullptr, dld});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,27 +271,12 @@ bool RendererVulkan::TryPresent(int /*timeout_ms*/) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RendererVulkan::Init() {
|
bool RendererVulkan::Init() {
|
||||||
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{};
|
library = OpenVulkanLibrary();
|
||||||
render_window.RetrieveVulkanHandlers(&vkGetInstanceProcAddr, &instance, &surface);
|
instance = CreateInstance(library, dld, render_window.GetWindowInfo().type,
|
||||||
const vk::DispatchLoaderDynamic dldi(instance, vkGetInstanceProcAddr);
|
Settings::values.renderer_debug);
|
||||||
|
if (!instance || !CreateDebugCallback() || !CreateSurface() || !PickDevices()) {
|
||||||
std::optional<vk::DebugUtilsMessengerEXT> callback;
|
|
||||||
if (Settings::values.renderer_debug && dldi.vkCreateDebugUtilsMessengerEXT) {
|
|
||||||
callback = CreateDebugCallback(dldi);
|
|
||||||
if (!callback) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!PickDevices(dldi)) {
|
|
||||||
if (callback) {
|
|
||||||
instance.destroy(*callback, nullptr, dldi);
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
debug_callback = UniqueDebugUtilsMessengerEXT(
|
|
||||||
*callback, vk::ObjectDestroy<vk::Instance, vk::DispatchLoaderDynamic>(
|
|
||||||
instance, nullptr, device->GetDispatchLoader()));
|
|
||||||
|
|
||||||
Report();
|
Report();
|
||||||
|
|
||||||
|
@ -216,7 +285,7 @@ bool RendererVulkan::Init() {
|
||||||
resource_manager = std::make_unique<VKResourceManager>(*device);
|
resource_manager = std::make_unique<VKResourceManager>(*device);
|
||||||
|
|
||||||
const auto& framebuffer = render_window.GetFramebufferLayout();
|
const auto& framebuffer = render_window.GetFramebufferLayout();
|
||||||
swapchain = std::make_unique<VKSwapchain>(surface, *device);
|
swapchain = std::make_unique<VKSwapchain>(*surface, *device);
|
||||||
swapchain->Create(framebuffer.width, framebuffer.height, false);
|
swapchain->Create(framebuffer.width, framebuffer.height, false);
|
||||||
|
|
||||||
state_tracker = std::make_unique<StateTracker>(system);
|
state_tracker = std::make_unique<StateTracker>(system);
|
||||||
|
@ -253,8 +322,10 @@ void RendererVulkan::ShutDown() {
|
||||||
device.reset();
|
device.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<vk::DebugUtilsMessengerEXT> RendererVulkan::CreateDebugCallback(
|
bool RendererVulkan::CreateDebugCallback() {
|
||||||
const vk::DispatchLoaderDynamic& dldi) {
|
if (!Settings::values.renderer_debug) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
const vk::DebugUtilsMessengerCreateInfoEXT callback_ci(
|
const vk::DebugUtilsMessengerCreateInfoEXT callback_ci(
|
||||||
{},
|
{},
|
||||||
vk::DebugUtilsMessageSeverityFlagBitsEXT::eError |
|
vk::DebugUtilsMessageSeverityFlagBitsEXT::eError |
|
||||||
|
@ -265,32 +336,88 @@ std::optional<vk::DebugUtilsMessengerEXT> RendererVulkan::CreateDebugCallback(
|
||||||
vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation |
|
vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation |
|
||||||
vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance,
|
vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance,
|
||||||
&DebugCallback, nullptr);
|
&DebugCallback, nullptr);
|
||||||
vk::DebugUtilsMessengerEXT callback;
|
vk::DebugUtilsMessengerEXT unsafe_callback;
|
||||||
if (instance.createDebugUtilsMessengerEXT(&callback_ci, nullptr, &callback, dldi) !=
|
if (instance->createDebugUtilsMessengerEXT(&callback_ci, nullptr, &unsafe_callback, dld) !=
|
||||||
vk::Result::eSuccess) {
|
vk::Result::eSuccess) {
|
||||||
LOG_ERROR(Render_Vulkan, "Failed to create debug callback");
|
LOG_ERROR(Render_Vulkan, "Failed to create debug callback");
|
||||||
return {};
|
return false;
|
||||||
}
|
}
|
||||||
return callback;
|
debug_callback = UniqueDebugUtilsMessengerEXT(unsafe_callback, {*instance, nullptr, dld});
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RendererVulkan::PickDevices(const vk::DispatchLoaderDynamic& dldi) {
|
bool RendererVulkan::CreateSurface() {
|
||||||
const auto devices = instance.enumeratePhysicalDevices(dldi);
|
[[maybe_unused]] const auto& window_info = render_window.GetWindowInfo();
|
||||||
|
VkSurfaceKHR unsafe_surface = nullptr;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (window_info.type == Core::Frontend::WindowSystemType::Windows) {
|
||||||
|
const HWND hWnd = static_cast<HWND>(window_info.render_surface);
|
||||||
|
const VkWin32SurfaceCreateInfoKHR win32_ci{VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR,
|
||||||
|
nullptr, 0, nullptr, hWnd};
|
||||||
|
const auto vkCreateWin32SurfaceKHR = reinterpret_cast<PFN_vkCreateWin32SurfaceKHR>(
|
||||||
|
dld.vkGetInstanceProcAddr(*instance, "vkCreateWin32SurfaceKHR"));
|
||||||
|
if (!vkCreateWin32SurfaceKHR || vkCreateWin32SurfaceKHR(instance.get(), &win32_ci, nullptr,
|
||||||
|
&unsafe_surface) != VK_SUCCESS) {
|
||||||
|
LOG_ERROR(Render_Vulkan, "Failed to initialize Win32 surface");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef __linux__
|
||||||
|
if (window_info.type == Core::Frontend::WindowSystemType::X11) {
|
||||||
|
const VkXlibSurfaceCreateInfoKHR xlib_ci{
|
||||||
|
VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, nullptr, 0,
|
||||||
|
static_cast<Display*>(window_info.display_connection),
|
||||||
|
reinterpret_cast<Window>(window_info.render_surface)};
|
||||||
|
const auto vkCreateXlibSurfaceKHR = reinterpret_cast<PFN_vkCreateXlibSurfaceKHR>(
|
||||||
|
dld.vkGetInstanceProcAddr(*instance, "vkCreateXlibSurfaceKHR"));
|
||||||
|
if (!vkCreateXlibSurfaceKHR || vkCreateXlibSurfaceKHR(instance.get(), &xlib_ci, nullptr,
|
||||||
|
&unsafe_surface) != VK_SUCCESS) {
|
||||||
|
LOG_ERROR(Render_Vulkan, "Failed to initialize Xlib surface");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (window_info.type == Core::Frontend::WindowSystemType::Wayland) {
|
||||||
|
const VkWaylandSurfaceCreateInfoKHR wayland_ci{
|
||||||
|
VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR, nullptr, 0,
|
||||||
|
static_cast<wl_display*>(window_info.display_connection),
|
||||||
|
static_cast<wl_surface*>(window_info.render_surface)};
|
||||||
|
const auto vkCreateWaylandSurfaceKHR = reinterpret_cast<PFN_vkCreateWaylandSurfaceKHR>(
|
||||||
|
dld.vkGetInstanceProcAddr(*instance, "vkCreateWaylandSurfaceKHR"));
|
||||||
|
if (!vkCreateWaylandSurfaceKHR ||
|
||||||
|
vkCreateWaylandSurfaceKHR(instance.get(), &wayland_ci, nullptr, &unsafe_surface) !=
|
||||||
|
VK_SUCCESS) {
|
||||||
|
LOG_ERROR(Render_Vulkan, "Failed to initialize Wayland surface");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (!unsafe_surface) {
|
||||||
|
LOG_ERROR(Render_Vulkan, "Presentation not supported on this platform");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
surface = UniqueSurfaceKHR(unsafe_surface, {*instance, nullptr, dld});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RendererVulkan::PickDevices() {
|
||||||
|
const auto devices = instance->enumeratePhysicalDevices(dld);
|
||||||
|
|
||||||
// TODO(Rodrigo): Choose device from config file
|
|
||||||
const s32 device_index = Settings::values.vulkan_device;
|
const s32 device_index = Settings::values.vulkan_device;
|
||||||
if (device_index < 0 || device_index >= static_cast<s32>(devices.size())) {
|
if (device_index < 0 || device_index >= static_cast<s32>(devices.size())) {
|
||||||
LOG_ERROR(Render_Vulkan, "Invalid device index {}!", device_index);
|
LOG_ERROR(Render_Vulkan, "Invalid device index {}!", device_index);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const vk::PhysicalDevice physical_device = devices[device_index];
|
const vk::PhysicalDevice physical_device = devices[static_cast<std::size_t>(device_index)];
|
||||||
|
|
||||||
if (!VKDevice::IsSuitable(dldi, physical_device, surface)) {
|
if (!VKDevice::IsSuitable(physical_device, *surface, dld)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
device = std::make_unique<VKDevice>(dldi, physical_device, surface);
|
device = std::make_unique<VKDevice>(dld, physical_device, *surface);
|
||||||
return device->Create(dldi, instance);
|
return device->Create(*instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererVulkan::Report() const {
|
void RendererVulkan::Report() const {
|
||||||
|
@ -317,11 +444,11 @@ void RendererVulkan::Report() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> RendererVulkan::EnumerateDevices() {
|
std::vector<std::string> RendererVulkan::EnumerateDevices() {
|
||||||
|
// Avoid putting DispatchLoaderDynamic, it's too large
|
||||||
|
auto dld_memory = std::make_unique<vk::DispatchLoaderDynamic>();
|
||||||
|
auto& dld = *dld_memory;
|
||||||
|
|
||||||
Common::DynamicLibrary library = OpenVulkanLibrary();
|
Common::DynamicLibrary library = OpenVulkanLibrary();
|
||||||
if (!library.IsOpen()) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
vk::DispatchLoaderDynamic dld;
|
|
||||||
UniqueInstance instance = CreateInstance(library, dld);
|
UniqueInstance instance = CreateInstance(library, dld);
|
||||||
if (!instance) {
|
if (!instance) {
|
||||||
return {};
|
return {};
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "common/dynamic_library.h"
|
||||||
|
|
||||||
#include "video_core/renderer_base.h"
|
#include "video_core/renderer_base.h"
|
||||||
#include "video_core/renderer_vulkan/declarations.h"
|
#include "video_core/renderer_vulkan/declarations.h"
|
||||||
|
|
||||||
|
@ -48,17 +50,21 @@ public:
|
||||||
static std::vector<std::string> EnumerateDevices();
|
static std::vector<std::string> EnumerateDevices();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::optional<vk::DebugUtilsMessengerEXT> CreateDebugCallback(
|
bool CreateDebugCallback();
|
||||||
const vk::DispatchLoaderDynamic& dldi);
|
|
||||||
|
|
||||||
bool PickDevices(const vk::DispatchLoaderDynamic& dldi);
|
bool CreateSurface();
|
||||||
|
|
||||||
|
bool PickDevices();
|
||||||
|
|
||||||
void Report() const;
|
void Report() const;
|
||||||
|
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
|
|
||||||
vk::Instance instance;
|
Common::DynamicLibrary library;
|
||||||
vk::SurfaceKHR surface;
|
vk::DispatchLoaderDynamic dld;
|
||||||
|
|
||||||
|
UniqueInstance instance;
|
||||||
|
UniqueSurfaceKHR surface;
|
||||||
|
|
||||||
VKScreenInfo screen_info;
|
VKScreenInfo screen_info;
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
#include "video_core/renderer_vulkan/declarations.h"
|
#include "video_core/renderer_vulkan/declarations.h"
|
||||||
|
@ -35,20 +36,20 @@ void SetNext(void**& next, T& data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T GetFeatures(vk::PhysicalDevice physical, const vk::DispatchLoaderDynamic& dldi) {
|
T GetFeatures(vk::PhysicalDevice physical, const vk::DispatchLoaderDynamic& dld) {
|
||||||
vk::PhysicalDeviceFeatures2 features;
|
vk::PhysicalDeviceFeatures2 features;
|
||||||
T extension_features;
|
T extension_features;
|
||||||
features.pNext = &extension_features;
|
features.pNext = &extension_features;
|
||||||
physical.getFeatures2(&features, dldi);
|
physical.getFeatures2(&features, dld);
|
||||||
return extension_features;
|
return extension_features;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T GetProperties(vk::PhysicalDevice physical, const vk::DispatchLoaderDynamic& dldi) {
|
T GetProperties(vk::PhysicalDevice physical, const vk::DispatchLoaderDynamic& dld) {
|
||||||
vk::PhysicalDeviceProperties2 properties;
|
vk::PhysicalDeviceProperties2 properties;
|
||||||
T extension_properties;
|
T extension_properties;
|
||||||
properties.pNext = &extension_properties;
|
properties.pNext = &extension_properties;
|
||||||
physical.getProperties2(&properties, dldi);
|
physical.getProperties2(&properties, dld);
|
||||||
return extension_properties;
|
return extension_properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,19 +79,19 @@ vk::FormatFeatureFlags GetFormatFeatures(vk::FormatProperties properties, Format
|
||||||
|
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
VKDevice::VKDevice(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical,
|
VKDevice::VKDevice(const vk::DispatchLoaderDynamic& dld, vk::PhysicalDevice physical,
|
||||||
vk::SurfaceKHR surface)
|
vk::SurfaceKHR surface)
|
||||||
: physical{physical}, properties{physical.getProperties(dldi)},
|
: dld{dld}, physical{physical}, properties{physical.getProperties(dld)},
|
||||||
format_properties{GetFormatProperties(dldi, physical)} {
|
format_properties{GetFormatProperties(dld, physical)} {
|
||||||
SetupFamilies(dldi, surface);
|
SetupFamilies(surface);
|
||||||
SetupFeatures(dldi);
|
SetupFeatures();
|
||||||
}
|
}
|
||||||
|
|
||||||
VKDevice::~VKDevice() = default;
|
VKDevice::~VKDevice() = default;
|
||||||
|
|
||||||
bool VKDevice::Create(const vk::DispatchLoaderDynamic& dldi, vk::Instance instance) {
|
bool VKDevice::Create(vk::Instance instance) {
|
||||||
const auto queue_cis = GetDeviceQueueCreateInfos();
|
const auto queue_cis = GetDeviceQueueCreateInfos();
|
||||||
const std::vector extensions = LoadExtensions(dldi);
|
const std::vector extensions = LoadExtensions();
|
||||||
|
|
||||||
vk::PhysicalDeviceFeatures2 features2;
|
vk::PhysicalDeviceFeatures2 features2;
|
||||||
void** next = &features2.pNext;
|
void** next = &features2.pNext;
|
||||||
|
@ -165,15 +166,13 @@ bool VKDevice::Create(const vk::DispatchLoaderDynamic& dldi, vk::Instance instan
|
||||||
nullptr);
|
nullptr);
|
||||||
device_ci.pNext = &features2;
|
device_ci.pNext = &features2;
|
||||||
|
|
||||||
vk::Device dummy_logical;
|
vk::Device unsafe_logical;
|
||||||
if (physical.createDevice(&device_ci, nullptr, &dummy_logical, dldi) != vk::Result::eSuccess) {
|
if (physical.createDevice(&device_ci, nullptr, &unsafe_logical, dld) != vk::Result::eSuccess) {
|
||||||
LOG_CRITICAL(Render_Vulkan, "Logical device failed to be created!");
|
LOG_CRITICAL(Render_Vulkan, "Logical device failed to be created!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
dld.init(instance, dld.vkGetInstanceProcAddr, unsafe_logical);
|
||||||
dld.init(instance, dldi.vkGetInstanceProcAddr, dummy_logical, dldi.vkGetDeviceProcAddr);
|
logical = UniqueDevice(unsafe_logical, {nullptr, dld});
|
||||||
logical = UniqueDevice(
|
|
||||||
dummy_logical, vk::ObjectDestroy<vk::NoParent, vk::DispatchLoaderDynamic>(nullptr, dld));
|
|
||||||
|
|
||||||
CollectTelemetryParameters();
|
CollectTelemetryParameters();
|
||||||
|
|
||||||
|
@ -235,8 +234,8 @@ void VKDevice::ReportLoss() const {
|
||||||
// *(VKGraphicsPipeline*)data[0]
|
// *(VKGraphicsPipeline*)data[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VKDevice::IsOptimalAstcSupported(const vk::PhysicalDeviceFeatures& features,
|
bool VKDevice::IsOptimalAstcSupported(const vk::PhysicalDeviceFeatures& features) const {
|
||||||
const vk::DispatchLoaderDynamic& dldi) const {
|
// Disable for now to avoid converting ASTC twice.
|
||||||
static constexpr std::array astc_formats = {
|
static constexpr std::array astc_formats = {
|
||||||
vk::Format::eAstc4x4UnormBlock, vk::Format::eAstc4x4SrgbBlock,
|
vk::Format::eAstc4x4UnormBlock, vk::Format::eAstc4x4SrgbBlock,
|
||||||
vk::Format::eAstc5x4UnormBlock, vk::Format::eAstc5x4SrgbBlock,
|
vk::Format::eAstc5x4UnormBlock, vk::Format::eAstc5x4SrgbBlock,
|
||||||
|
@ -260,7 +259,7 @@ bool VKDevice::IsOptimalAstcSupported(const vk::PhysicalDeviceFeatures& features
|
||||||
vk::FormatFeatureFlagBits::eBlitDst | vk::FormatFeatureFlagBits::eTransferSrc |
|
vk::FormatFeatureFlagBits::eBlitDst | vk::FormatFeatureFlagBits::eTransferSrc |
|
||||||
vk::FormatFeatureFlagBits::eTransferDst};
|
vk::FormatFeatureFlagBits::eTransferDst};
|
||||||
for (const auto format : astc_formats) {
|
for (const auto format : astc_formats) {
|
||||||
const auto format_properties{physical.getFormatProperties(format, dldi)};
|
const auto format_properties{physical.getFormatProperties(format, dld)};
|
||||||
if (!(format_properties.optimalTilingFeatures & format_feature_usage)) {
|
if (!(format_properties.optimalTilingFeatures & format_feature_usage)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -279,11 +278,9 @@ bool VKDevice::IsFormatSupported(vk::Format wanted_format, vk::FormatFeatureFlag
|
||||||
return (supported_usage & wanted_usage) == wanted_usage;
|
return (supported_usage & wanted_usage) == wanted_usage;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical,
|
bool VKDevice::IsSuitable(vk::PhysicalDevice physical, vk::SurfaceKHR surface,
|
||||||
vk::SurfaceKHR surface) {
|
const vk::DispatchLoaderDynamic& dld) {
|
||||||
bool is_suitable = true;
|
static constexpr std::array required_extensions = {
|
||||||
|
|
||||||
constexpr std::array required_extensions = {
|
|
||||||
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
||||||
VK_KHR_16BIT_STORAGE_EXTENSION_NAME,
|
VK_KHR_16BIT_STORAGE_EXTENSION_NAME,
|
||||||
VK_KHR_8BIT_STORAGE_EXTENSION_NAME,
|
VK_KHR_8BIT_STORAGE_EXTENSION_NAME,
|
||||||
|
@ -293,9 +290,10 @@ bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDev
|
||||||
VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME,
|
VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME,
|
||||||
VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME,
|
VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME,
|
||||||
};
|
};
|
||||||
|
bool is_suitable = true;
|
||||||
std::bitset<required_extensions.size()> available_extensions{};
|
std::bitset<required_extensions.size()> available_extensions{};
|
||||||
|
|
||||||
for (const auto& prop : physical.enumerateDeviceExtensionProperties(nullptr, dldi)) {
|
for (const auto& prop : physical.enumerateDeviceExtensionProperties(nullptr, dld)) {
|
||||||
for (std::size_t i = 0; i < required_extensions.size(); ++i) {
|
for (std::size_t i = 0; i < required_extensions.size(); ++i) {
|
||||||
if (available_extensions[i]) {
|
if (available_extensions[i]) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -315,7 +313,7 @@ bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDev
|
||||||
}
|
}
|
||||||
|
|
||||||
bool has_graphics{}, has_present{};
|
bool has_graphics{}, has_present{};
|
||||||
const auto queue_family_properties = physical.getQueueFamilyProperties(dldi);
|
const auto queue_family_properties = physical.getQueueFamilyProperties(dld);
|
||||||
for (u32 i = 0; i < static_cast<u32>(queue_family_properties.size()); ++i) {
|
for (u32 i = 0; i < static_cast<u32>(queue_family_properties.size()); ++i) {
|
||||||
const auto& family = queue_family_properties[i];
|
const auto& family = queue_family_properties[i];
|
||||||
if (family.queueCount == 0) {
|
if (family.queueCount == 0) {
|
||||||
|
@ -323,7 +321,7 @@ bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDev
|
||||||
}
|
}
|
||||||
has_graphics |=
|
has_graphics |=
|
||||||
(family.queueFlags & vk::QueueFlagBits::eGraphics) != static_cast<vk::QueueFlagBits>(0);
|
(family.queueFlags & vk::QueueFlagBits::eGraphics) != static_cast<vk::QueueFlagBits>(0);
|
||||||
has_present |= physical.getSurfaceSupportKHR(i, surface, dldi) != 0;
|
has_present |= physical.getSurfaceSupportKHR(i, surface, dld) != 0;
|
||||||
}
|
}
|
||||||
if (!has_graphics || !has_present) {
|
if (!has_graphics || !has_present) {
|
||||||
LOG_ERROR(Render_Vulkan, "Device lacks a graphics and present queue");
|
LOG_ERROR(Render_Vulkan, "Device lacks a graphics and present queue");
|
||||||
|
@ -331,7 +329,7 @@ bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDev
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(Rodrigo): Check if the device matches all requeriments.
|
// TODO(Rodrigo): Check if the device matches all requeriments.
|
||||||
const auto properties{physical.getProperties(dldi)};
|
const auto properties{physical.getProperties(dld)};
|
||||||
const auto& limits{properties.limits};
|
const auto& limits{properties.limits};
|
||||||
|
|
||||||
constexpr u32 required_ubo_size = 65536;
|
constexpr u32 required_ubo_size = 65536;
|
||||||
|
@ -348,7 +346,7 @@ bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDev
|
||||||
is_suitable = false;
|
is_suitable = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto features{physical.getFeatures(dldi)};
|
const auto features{physical.getFeatures(dld)};
|
||||||
const std::array feature_report = {
|
const std::array feature_report = {
|
||||||
std::make_pair(features.vertexPipelineStoresAndAtomics, "vertexPipelineStoresAndAtomics"),
|
std::make_pair(features.vertexPipelineStoresAndAtomics, "vertexPipelineStoresAndAtomics"),
|
||||||
std::make_pair(features.independentBlend, "independentBlend"),
|
std::make_pair(features.independentBlend, "independentBlend"),
|
||||||
|
@ -380,7 +378,7 @@ bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDev
|
||||||
return is_suitable;
|
return is_suitable;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynamic& dldi) {
|
std::vector<const char*> VKDevice::LoadExtensions() {
|
||||||
std::vector<const char*> extensions;
|
std::vector<const char*> extensions;
|
||||||
const auto Test = [&](const vk::ExtensionProperties& extension,
|
const auto Test = [&](const vk::ExtensionProperties& extension,
|
||||||
std::optional<std::reference_wrapper<bool>> status, const char* name,
|
std::optional<std::reference_wrapper<bool>> status, const char* name,
|
||||||
|
@ -411,7 +409,7 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami
|
||||||
bool has_khr_shader_float16_int8{};
|
bool has_khr_shader_float16_int8{};
|
||||||
bool has_ext_subgroup_size_control{};
|
bool has_ext_subgroup_size_control{};
|
||||||
bool has_ext_transform_feedback{};
|
bool has_ext_transform_feedback{};
|
||||||
for (const auto& extension : physical.enumerateDeviceExtensionProperties(nullptr, dldi)) {
|
for (const auto& extension : physical.enumerateDeviceExtensionProperties(nullptr, dld)) {
|
||||||
Test(extension, khr_uniform_buffer_standard_layout,
|
Test(extension, khr_uniform_buffer_standard_layout,
|
||||||
VK_KHR_UNIFORM_BUFFER_STANDARD_LAYOUT_EXTENSION_NAME, true);
|
VK_KHR_UNIFORM_BUFFER_STANDARD_LAYOUT_EXTENSION_NAME, true);
|
||||||
Test(extension, has_khr_shader_float16_int8, VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME,
|
Test(extension, has_khr_shader_float16_int8, VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME,
|
||||||
|
@ -433,15 +431,15 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami
|
||||||
|
|
||||||
if (has_khr_shader_float16_int8) {
|
if (has_khr_shader_float16_int8) {
|
||||||
is_float16_supported =
|
is_float16_supported =
|
||||||
GetFeatures<vk::PhysicalDeviceFloat16Int8FeaturesKHR>(physical, dldi).shaderFloat16;
|
GetFeatures<vk::PhysicalDeviceFloat16Int8FeaturesKHR>(physical, dld).shaderFloat16;
|
||||||
extensions.push_back(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME);
|
extensions.push_back(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has_ext_subgroup_size_control) {
|
if (has_ext_subgroup_size_control) {
|
||||||
const auto features =
|
const auto features =
|
||||||
GetFeatures<vk::PhysicalDeviceSubgroupSizeControlFeaturesEXT>(physical, dldi);
|
GetFeatures<vk::PhysicalDeviceSubgroupSizeControlFeaturesEXT>(physical, dld);
|
||||||
const auto properties =
|
const auto properties =
|
||||||
GetProperties<vk::PhysicalDeviceSubgroupSizeControlPropertiesEXT>(physical, dldi);
|
GetProperties<vk::PhysicalDeviceSubgroupSizeControlPropertiesEXT>(physical, dld);
|
||||||
|
|
||||||
is_warp_potentially_bigger = properties.maxSubgroupSize > GuestWarpSize;
|
is_warp_potentially_bigger = properties.maxSubgroupSize > GuestWarpSize;
|
||||||
|
|
||||||
|
@ -456,9 +454,9 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami
|
||||||
|
|
||||||
if (has_ext_transform_feedback) {
|
if (has_ext_transform_feedback) {
|
||||||
const auto features =
|
const auto features =
|
||||||
GetFeatures<vk::PhysicalDeviceTransformFeedbackFeaturesEXT>(physical, dldi);
|
GetFeatures<vk::PhysicalDeviceTransformFeedbackFeaturesEXT>(physical, dld);
|
||||||
const auto properties =
|
const auto properties =
|
||||||
GetProperties<vk::PhysicalDeviceTransformFeedbackPropertiesEXT>(physical, dldi);
|
GetProperties<vk::PhysicalDeviceTransformFeedbackPropertiesEXT>(physical, dld);
|
||||||
|
|
||||||
if (features.transformFeedback && features.geometryStreams &&
|
if (features.transformFeedback && features.geometryStreams &&
|
||||||
properties.maxTransformFeedbackStreams >= 4 && properties.maxTransformFeedbackBuffers &&
|
properties.maxTransformFeedbackStreams >= 4 && properties.maxTransformFeedbackBuffers &&
|
||||||
|
@ -471,10 +469,10 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami
|
||||||
return extensions;
|
return extensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VKDevice::SetupFamilies(const vk::DispatchLoaderDynamic& dldi, vk::SurfaceKHR surface) {
|
void VKDevice::SetupFamilies(vk::SurfaceKHR surface) {
|
||||||
std::optional<u32> graphics_family_, present_family_;
|
std::optional<u32> graphics_family_, present_family_;
|
||||||
|
|
||||||
const auto queue_family_properties = physical.getQueueFamilyProperties(dldi);
|
const auto queue_family_properties = physical.getQueueFamilyProperties(dld);
|
||||||
for (u32 i = 0; i < static_cast<u32>(queue_family_properties.size()); ++i) {
|
for (u32 i = 0; i < static_cast<u32>(queue_family_properties.size()); ++i) {
|
||||||
if (graphics_family_ && present_family_)
|
if (graphics_family_ && present_family_)
|
||||||
break;
|
break;
|
||||||
|
@ -483,10 +481,12 @@ void VKDevice::SetupFamilies(const vk::DispatchLoaderDynamic& dldi, vk::SurfaceK
|
||||||
if (queue_family.queueCount == 0)
|
if (queue_family.queueCount == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (queue_family.queueFlags & vk::QueueFlagBits::eGraphics)
|
if (queue_family.queueFlags & vk::QueueFlagBits::eGraphics) {
|
||||||
graphics_family_ = i;
|
graphics_family_ = i;
|
||||||
if (physical.getSurfaceSupportKHR(i, surface, dldi))
|
}
|
||||||
|
if (physical.getSurfaceSupportKHR(i, surface, dld)) {
|
||||||
present_family_ = i;
|
present_family_ = i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ASSERT(graphics_family_ && present_family_);
|
ASSERT(graphics_family_ && present_family_);
|
||||||
|
|
||||||
|
@ -494,10 +494,10 @@ void VKDevice::SetupFamilies(const vk::DispatchLoaderDynamic& dldi, vk::SurfaceK
|
||||||
present_family = *present_family_;
|
present_family = *present_family_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VKDevice::SetupFeatures(const vk::DispatchLoaderDynamic& dldi) {
|
void VKDevice::SetupFeatures() {
|
||||||
const auto supported_features{physical.getFeatures(dldi)};
|
const auto supported_features{physical.getFeatures(dld)};
|
||||||
is_formatless_image_load_supported = supported_features.shaderStorageImageReadWithoutFormat;
|
is_formatless_image_load_supported = supported_features.shaderStorageImageReadWithoutFormat;
|
||||||
is_optimal_astc_supported = IsOptimalAstcSupported(supported_features, dldi);
|
is_optimal_astc_supported = IsOptimalAstcSupported(supported_features);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VKDevice::CollectTelemetryParameters() {
|
void VKDevice::CollectTelemetryParameters() {
|
||||||
|
@ -525,7 +525,7 @@ std::vector<vk::DeviceQueueCreateInfo> VKDevice::GetDeviceQueueCreateInfos() con
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unordered_map<vk::Format, vk::FormatProperties> VKDevice::GetFormatProperties(
|
std::unordered_map<vk::Format, vk::FormatProperties> VKDevice::GetFormatProperties(
|
||||||
const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical) {
|
const vk::DispatchLoaderDynamic& dld, vk::PhysicalDevice physical) {
|
||||||
static constexpr std::array formats{vk::Format::eA8B8G8R8UnormPack32,
|
static constexpr std::array formats{vk::Format::eA8B8G8R8UnormPack32,
|
||||||
vk::Format::eA8B8G8R8UintPack32,
|
vk::Format::eA8B8G8R8UintPack32,
|
||||||
vk::Format::eA8B8G8R8SnormPack32,
|
vk::Format::eA8B8G8R8SnormPack32,
|
||||||
|
@ -606,7 +606,7 @@ std::unordered_map<vk::Format, vk::FormatProperties> VKDevice::GetFormatProperti
|
||||||
vk::Format::eE5B9G9R9UfloatPack32};
|
vk::Format::eE5B9G9R9UfloatPack32};
|
||||||
std::unordered_map<vk::Format, vk::FormatProperties> format_properties;
|
std::unordered_map<vk::Format, vk::FormatProperties> format_properties;
|
||||||
for (const auto format : formats) {
|
for (const auto format : formats) {
|
||||||
format_properties.emplace(format, physical.getFormatProperties(format, dldi));
|
format_properties.emplace(format, physical.getFormatProperties(format, dld));
|
||||||
}
|
}
|
||||||
return format_properties;
|
return format_properties;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,12 +22,12 @@ const u32 GuestWarpSize = 32;
|
||||||
/// Handles data specific to a physical device.
|
/// Handles data specific to a physical device.
|
||||||
class VKDevice final {
|
class VKDevice final {
|
||||||
public:
|
public:
|
||||||
explicit VKDevice(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical,
|
explicit VKDevice(const vk::DispatchLoaderDynamic& dld, vk::PhysicalDevice physical,
|
||||||
vk::SurfaceKHR surface);
|
vk::SurfaceKHR surface);
|
||||||
~VKDevice();
|
~VKDevice();
|
||||||
|
|
||||||
/// Initializes the device. Returns true on success.
|
/// Initializes the device. Returns true on success.
|
||||||
bool Create(const vk::DispatchLoaderDynamic& dldi, vk::Instance instance);
|
bool Create(vk::Instance instance);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a format supported by the device for the passed requeriments.
|
* Returns a format supported by the device for the passed requeriments.
|
||||||
|
@ -188,18 +188,18 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if the physical device is suitable.
|
/// Checks if the physical device is suitable.
|
||||||
static bool IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical,
|
static bool IsSuitable(vk::PhysicalDevice physical, vk::SurfaceKHR surface,
|
||||||
vk::SurfaceKHR surface);
|
const vk::DispatchLoaderDynamic& dld);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Loads extensions into a vector and stores available ones in this object.
|
/// Loads extensions into a vector and stores available ones in this object.
|
||||||
std::vector<const char*> LoadExtensions(const vk::DispatchLoaderDynamic& dldi);
|
std::vector<const char*> LoadExtensions();
|
||||||
|
|
||||||
/// Sets up queue families.
|
/// Sets up queue families.
|
||||||
void SetupFamilies(const vk::DispatchLoaderDynamic& dldi, vk::SurfaceKHR surface);
|
void SetupFamilies(vk::SurfaceKHR surface);
|
||||||
|
|
||||||
/// Sets up device features.
|
/// Sets up device features.
|
||||||
void SetupFeatures(const vk::DispatchLoaderDynamic& dldi);
|
void SetupFeatures();
|
||||||
|
|
||||||
/// Collects telemetry information from the device.
|
/// Collects telemetry information from the device.
|
||||||
void CollectTelemetryParameters();
|
void CollectTelemetryParameters();
|
||||||
|
@ -208,8 +208,7 @@ private:
|
||||||
std::vector<vk::DeviceQueueCreateInfo> GetDeviceQueueCreateInfos() const;
|
std::vector<vk::DeviceQueueCreateInfo> GetDeviceQueueCreateInfos() const;
|
||||||
|
|
||||||
/// Returns true if ASTC textures are natively supported.
|
/// Returns true if ASTC textures are natively supported.
|
||||||
bool IsOptimalAstcSupported(const vk::PhysicalDeviceFeatures& features,
|
bool IsOptimalAstcSupported(const vk::PhysicalDeviceFeatures& features) const;
|
||||||
const vk::DispatchLoaderDynamic& dldi) const;
|
|
||||||
|
|
||||||
/// Returns true if a format is supported.
|
/// Returns true if a format is supported.
|
||||||
bool IsFormatSupported(vk::Format wanted_format, vk::FormatFeatureFlags wanted_usage,
|
bool IsFormatSupported(vk::Format wanted_format, vk::FormatFeatureFlags wanted_usage,
|
||||||
|
@ -217,10 +216,10 @@ private:
|
||||||
|
|
||||||
/// Returns the device properties for Vulkan formats.
|
/// Returns the device properties for Vulkan formats.
|
||||||
static std::unordered_map<vk::Format, vk::FormatProperties> GetFormatProperties(
|
static std::unordered_map<vk::Format, vk::FormatProperties> GetFormatProperties(
|
||||||
const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical);
|
const vk::DispatchLoaderDynamic& dld, vk::PhysicalDevice physical);
|
||||||
|
|
||||||
const vk::PhysicalDevice physical; ///< Physical device.
|
|
||||||
vk::DispatchLoaderDynamic dld; ///< Device function pointers.
|
vk::DispatchLoaderDynamic dld; ///< Device function pointers.
|
||||||
|
vk::PhysicalDevice physical; ///< Physical device.
|
||||||
vk::PhysicalDeviceProperties properties; ///< Device properties.
|
vk::PhysicalDeviceProperties properties; ///< Device properties.
|
||||||
UniqueDevice logical; ///< Logical device.
|
UniqueDevice logical; ///< Logical device.
|
||||||
vk::Queue graphics_queue; ///< Main graphics queue.
|
vk::Queue graphics_queue; ///< Main graphics queue.
|
||||||
|
|
|
@ -150,6 +150,10 @@ target_link_libraries(yuzu PRIVATE common core input_common video_core)
|
||||||
target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::OpenGL Qt5::Widgets)
|
target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::OpenGL Qt5::Widgets)
|
||||||
target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
|
target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
|
||||||
|
|
||||||
|
if (ENABLE_VULKAN AND NOT WIN32)
|
||||||
|
target_include_directories(yuzu PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
|
||||||
|
endif()
|
||||||
|
|
||||||
target_compile_definitions(yuzu PRIVATE
|
target_compile_definitions(yuzu PRIVATE
|
||||||
# Use QStringBuilder for string concatenation to reduce
|
# Use QStringBuilder for string concatenation to reduce
|
||||||
# the overall number of temporary strings created.
|
# the overall number of temporary strings created.
|
||||||
|
|
|
@ -14,8 +14,9 @@
|
||||||
#include <QScreen>
|
#include <QScreen>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QWindow>
|
#include <QWindow>
|
||||||
#ifdef HAS_VULKAN
|
|
||||||
#include <QVulkanWindow>
|
#if !defined(WIN32) && HAS_VULKAN
|
||||||
|
#include <qpa/qplatformnativeinterface.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
@ -238,16 +239,50 @@ private:
|
||||||
#ifdef HAS_VULKAN
|
#ifdef HAS_VULKAN
|
||||||
class VulkanRenderWidget : public RenderWidget {
|
class VulkanRenderWidget : public RenderWidget {
|
||||||
public:
|
public:
|
||||||
explicit VulkanRenderWidget(GRenderWindow* parent, QVulkanInstance* instance)
|
explicit VulkanRenderWidget(GRenderWindow* parent) : RenderWidget(parent) {
|
||||||
: RenderWidget(parent) {
|
|
||||||
windowHandle()->setSurfaceType(QWindow::VulkanSurface);
|
windowHandle()->setSurfaceType(QWindow::VulkanSurface);
|
||||||
windowHandle()->setVulkanInstance(instance);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
GRenderWindow::GRenderWindow(GMainWindow* parent_, EmuThread* emu_thread)
|
static Core::Frontend::WindowSystemType GetWindowSystemType() {
|
||||||
: QWidget(parent_), emu_thread(emu_thread) {
|
// Determine WSI type based on Qt platform.
|
||||||
|
QString platform_name = QGuiApplication::platformName();
|
||||||
|
if (platform_name == QStringLiteral("windows"))
|
||||||
|
return Core::Frontend::WindowSystemType::Windows;
|
||||||
|
else if (platform_name == QStringLiteral("xcb"))
|
||||||
|
return Core::Frontend::WindowSystemType::X11;
|
||||||
|
else if (platform_name == QStringLiteral("wayland"))
|
||||||
|
return Core::Frontend::WindowSystemType::Wayland;
|
||||||
|
|
||||||
|
LOG_CRITICAL(Frontend, "Unknown Qt platform!");
|
||||||
|
return Core::Frontend::WindowSystemType::Windows;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window) {
|
||||||
|
Core::Frontend::EmuWindow::WindowSystemInfo wsi;
|
||||||
|
wsi.type = GetWindowSystemType();
|
||||||
|
|
||||||
|
#ifdef HAS_VULKAN
|
||||||
|
// Our Win32 Qt external doesn't have the private API.
|
||||||
|
#if defined(WIN32) || defined(__APPLE__)
|
||||||
|
wsi.render_surface = window ? reinterpret_cast<void*>(window->winId()) : nullptr;
|
||||||
|
#else
|
||||||
|
QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface();
|
||||||
|
wsi.display_connection = pni->nativeResourceForWindow("display", window);
|
||||||
|
if (wsi.type == Core::Frontend::WindowSystemType::Wayland)
|
||||||
|
wsi.render_surface = window ? pni->nativeResourceForWindow("surface", window) : nullptr;
|
||||||
|
else
|
||||||
|
wsi.render_surface = window ? reinterpret_cast<void*>(window->winId()) : nullptr;
|
||||||
|
#endif
|
||||||
|
wsi.render_surface_scale = window ? static_cast<float>(window->devicePixelRatio()) : 1.0f;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return wsi;
|
||||||
|
}
|
||||||
|
|
||||||
|
GRenderWindow::GRenderWindow(GMainWindow* parent_, EmuThread* emu_thread_)
|
||||||
|
: QWidget(parent_), emu_thread(emu_thread_) {
|
||||||
setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
|
setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
|
||||||
.arg(QString::fromUtf8(Common::g_build_name),
|
.arg(QString::fromUtf8(Common::g_build_name),
|
||||||
QString::fromUtf8(Common::g_scm_branch),
|
QString::fromUtf8(Common::g_scm_branch),
|
||||||
|
@ -460,6 +495,9 @@ bool GRenderWindow::InitRenderTarget() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the Window System information with the new render target
|
||||||
|
window_info = GetWindowSystemInfo(child_widget->windowHandle());
|
||||||
|
|
||||||
child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
|
child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
|
||||||
layout()->addWidget(child_widget);
|
layout()->addWidget(child_widget);
|
||||||
// Reset minimum required size to avoid resizing issues on the main window after restarting.
|
// Reset minimum required size to avoid resizing issues on the main window after restarting.
|
||||||
|
@ -531,30 +569,7 @@ bool GRenderWindow::InitializeOpenGL() {
|
||||||
|
|
||||||
bool GRenderWindow::InitializeVulkan() {
|
bool GRenderWindow::InitializeVulkan() {
|
||||||
#ifdef HAS_VULKAN
|
#ifdef HAS_VULKAN
|
||||||
vk_instance = std::make_unique<QVulkanInstance>();
|
auto child = new VulkanRenderWidget(this);
|
||||||
vk_instance->setApiVersion(QVersionNumber(1, 1, 0));
|
|
||||||
vk_instance->setFlags(QVulkanInstance::Flag::NoDebugOutputRedirect);
|
|
||||||
if (Settings::values.renderer_debug) {
|
|
||||||
const auto supported_layers{vk_instance->supportedLayers()};
|
|
||||||
const bool found =
|
|
||||||
std::find_if(supported_layers.begin(), supported_layers.end(), [](const auto& layer) {
|
|
||||||
constexpr const char searched_layer[] = "VK_LAYER_LUNARG_standard_validation";
|
|
||||||
return layer.name == searched_layer;
|
|
||||||
});
|
|
||||||
if (found) {
|
|
||||||
vk_instance->setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation");
|
|
||||||
vk_instance->setExtensions(QByteArrayList() << VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!vk_instance->create()) {
|
|
||||||
QMessageBox::critical(
|
|
||||||
this, tr("Error while initializing Vulkan 1.1!"),
|
|
||||||
tr("Your OS doesn't seem to support Vulkan 1.1 instances, or you do not have the "
|
|
||||||
"latest graphics drivers."));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto child = new VulkanRenderWidget(this, vk_instance.get());
|
|
||||||
child_widget = child;
|
child_widget = child;
|
||||||
child_widget->windowHandle()->create();
|
child_widget->windowHandle()->create();
|
||||||
main_context = std::make_unique<DummyContext>();
|
main_context = std::make_unique<DummyContext>();
|
||||||
|
@ -567,21 +582,6 @@ bool GRenderWindow::InitializeVulkan() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
|
||||||
void* surface) const {
|
|
||||||
#ifdef HAS_VULKAN
|
|
||||||
const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr");
|
|
||||||
const VkInstance instance_copy = vk_instance->vkInstance();
|
|
||||||
const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child_widget->windowHandle());
|
|
||||||
|
|
||||||
std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr));
|
|
||||||
std::memcpy(instance, &instance_copy, sizeof(instance_copy));
|
|
||||||
std::memcpy(surface, &surface_copy, sizeof(surface_copy));
|
|
||||||
#else
|
|
||||||
UNREACHABLE_MSG("Executing Vulkan code without compiling Vulkan");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GRenderWindow::LoadOpenGL() {
|
bool GRenderWindow::LoadOpenGL() {
|
||||||
auto context = CreateSharedContext();
|
auto context = CreateSharedContext();
|
||||||
auto scope = context->Acquire();
|
auto scope = context->Acquire();
|
||||||
|
|
|
@ -22,9 +22,6 @@ class GMainWindow;
|
||||||
class QKeyEvent;
|
class QKeyEvent;
|
||||||
class QTouchEvent;
|
class QTouchEvent;
|
||||||
class QStringList;
|
class QStringList;
|
||||||
#ifdef HAS_VULKAN
|
|
||||||
class QVulkanInstance;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace VideoCore {
|
namespace VideoCore {
|
||||||
enum class LoadCallbackStage;
|
enum class LoadCallbackStage;
|
||||||
|
@ -122,8 +119,6 @@ public:
|
||||||
// EmuWindow implementation.
|
// EmuWindow implementation.
|
||||||
void PollEvents() override;
|
void PollEvents() override;
|
||||||
bool IsShown() const override;
|
bool IsShown() const override;
|
||||||
void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
|
||||||
void* surface) const override;
|
|
||||||
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
|
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
|
||||||
|
|
||||||
void BackupGeometry();
|
void BackupGeometry();
|
||||||
|
@ -186,10 +181,6 @@ private:
|
||||||
// should instead be shared from
|
// should instead be shared from
|
||||||
std::shared_ptr<Core::Frontend::GraphicsContext> main_context;
|
std::shared_ptr<Core::Frontend::GraphicsContext> main_context;
|
||||||
|
|
||||||
#ifdef HAS_VULKAN
|
|
||||||
std::unique_ptr<QVulkanInstance> vk_instance;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// Temporary storage of the screenshot taken
|
/// Temporary storage of the screenshot taken
|
||||||
QImage screenshot_image;
|
QImage screenshot_image;
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
#include "ui_configure_graphics.h"
|
#include "ui_configure_graphics.h"
|
||||||
#include "yuzu/configuration/configure_graphics.h"
|
#include "yuzu/configuration/configure_graphics.h"
|
||||||
|
|
||||||
#ifdef HAS_VULKAN
|
#ifdef HAS_VULKAN
|
||||||
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -156,12 +156,6 @@ EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() {
|
||||||
SDL_GL_DeleteContext(window_context);
|
SDL_GL_DeleteContext(window_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuWindow_SDL2_GL::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
|
||||||
void* surface) const {
|
|
||||||
// Should not have been called from OpenGL
|
|
||||||
UNREACHABLE();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const {
|
std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const {
|
||||||
return std::make_unique<SDLGLContext>();
|
return std::make_unique<SDLGLContext>();
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,10 +15,6 @@ public:
|
||||||
|
|
||||||
void Present() override;
|
void Present() override;
|
||||||
|
|
||||||
/// Ignored in OpenGL
|
|
||||||
void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
|
||||||
void* surface) const override;
|
|
||||||
|
|
||||||
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
|
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -2,102 +2,62 @@
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include <algorithm>
|
#include <cstdlib>
|
||||||
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
|
||||||
#include <SDL.h>
|
|
||||||
#include <SDL_vulkan.h>
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
#include <vulkan/vulkan.h>
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/scm_rev.h"
|
#include "common/scm_rev.h"
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
|
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
||||||
#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h"
|
#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h"
|
||||||
|
|
||||||
|
// Include these late to avoid polluting everything with Xlib macros
|
||||||
|
#include <SDL.h>
|
||||||
|
#include <SDL_syswm.h>
|
||||||
|
|
||||||
EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen)
|
EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen)
|
||||||
: EmuWindow_SDL2{system, fullscreen} {
|
: EmuWindow_SDL2{system, fullscreen} {
|
||||||
if (SDL_Vulkan_LoadLibrary(nullptr) != 0) {
|
|
||||||
LOG_CRITICAL(Frontend, "SDL failed to load the Vulkan library: {}", SDL_GetError());
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
vkGetInstanceProcAddr =
|
|
||||||
reinterpret_cast<PFN_vkGetInstanceProcAddr>(SDL_Vulkan_GetVkGetInstanceProcAddr());
|
|
||||||
if (vkGetInstanceProcAddr == nullptr) {
|
|
||||||
LOG_CRITICAL(Frontend, "Failed to retrieve Vulkan function pointer!");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name,
|
const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name,
|
||||||
Common::g_scm_branch, Common::g_scm_desc);
|
Common::g_scm_branch, Common::g_scm_desc);
|
||||||
render_window =
|
render_window =
|
||||||
SDL_CreateWindow(window_title.c_str(),
|
SDL_CreateWindow(window_title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
|
||||||
SDL_WINDOWPOS_UNDEFINED, // x position
|
|
||||||
SDL_WINDOWPOS_UNDEFINED, // y position
|
|
||||||
Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height,
|
Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height,
|
||||||
SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_VULKAN);
|
SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
|
||||||
|
|
||||||
const bool use_standard_layers = UseStandardLayers(vkGetInstanceProcAddr);
|
SDL_SysWMinfo wm;
|
||||||
|
if (SDL_GetWindowWMInfo(render_window, &wm) == SDL_FALSE) {
|
||||||
u32 extra_ext_count{};
|
LOG_CRITICAL(Frontend, "Failed to get information from the window manager");
|
||||||
if (!SDL_Vulkan_GetInstanceExtensions(render_window, &extra_ext_count, NULL)) {
|
std::exit(EXIT_FAILURE);
|
||||||
LOG_CRITICAL(Frontend, "Failed to query Vulkan extensions count from SDL! {}",
|
|
||||||
SDL_GetError());
|
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto extra_ext_names = std::make_unique<const char* []>(extra_ext_count);
|
switch (wm.subsystem) {
|
||||||
if (!SDL_Vulkan_GetInstanceExtensions(render_window, &extra_ext_count, extra_ext_names.get())) {
|
#ifdef SDL_VIDEO_DRIVER_WINDOWS
|
||||||
LOG_CRITICAL(Frontend, "Failed to query Vulkan extensions from SDL! {}", SDL_GetError());
|
case SDL_SYSWM_TYPE::SDL_SYSWM_WINDOWS:
|
||||||
exit(1);
|
window_info.type = Core::Frontend::WindowSystemType::Windows;
|
||||||
}
|
window_info.render_surface = reinterpret_cast<void*>(wm.info.win.window);
|
||||||
std::vector<const char*> enabled_extensions;
|
break;
|
||||||
enabled_extensions.insert(enabled_extensions.begin(), extra_ext_names.get(),
|
#endif
|
||||||
extra_ext_names.get() + extra_ext_count);
|
#ifdef SDL_VIDEO_DRIVER_X11
|
||||||
|
case SDL_SYSWM_TYPE::SDL_SYSWM_X11:
|
||||||
std::vector<const char*> enabled_layers;
|
window_info.type = Core::Frontend::WindowSystemType::X11;
|
||||||
if (use_standard_layers) {
|
window_info.display_connection = wm.info.x11.display;
|
||||||
enabled_extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
window_info.render_surface = reinterpret_cast<void*>(wm.info.x11.window);
|
||||||
enabled_layers.push_back("VK_LAYER_LUNARG_standard_validation");
|
break;
|
||||||
}
|
#endif
|
||||||
|
#ifdef SDL_VIDEO_DRIVER_WAYLAND
|
||||||
VkApplicationInfo app_info{};
|
case SDL_SYSWM_TYPE::SDL_SYSWM_WAYLAND:
|
||||||
app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
window_info.type = Core::Frontend::WindowSystemType::Wayland;
|
||||||
app_info.apiVersion = VK_API_VERSION_1_1;
|
window_info.display_connection = wm.info.wl.display;
|
||||||
app_info.applicationVersion = VK_MAKE_VERSION(0, 1, 0);
|
window_info.render_surface = wm.info.wl.surface;
|
||||||
app_info.pApplicationName = "yuzu-emu";
|
break;
|
||||||
app_info.engineVersion = VK_MAKE_VERSION(0, 1, 0);
|
#endif
|
||||||
app_info.pEngineName = "yuzu-emu";
|
default:
|
||||||
|
LOG_CRITICAL(Frontend, "Window manager subsystem not implemented");
|
||||||
VkInstanceCreateInfo instance_ci{};
|
std::exit(EXIT_FAILURE);
|
||||||
instance_ci.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
|
||||||
instance_ci.pApplicationInfo = &app_info;
|
|
||||||
instance_ci.enabledExtensionCount = static_cast<u32>(enabled_extensions.size());
|
|
||||||
instance_ci.ppEnabledExtensionNames = enabled_extensions.data();
|
|
||||||
if (Settings::values.renderer_debug) {
|
|
||||||
instance_ci.enabledLayerCount = static_cast<u32>(enabled_layers.size());
|
|
||||||
instance_ci.ppEnabledLayerNames = enabled_layers.data();
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto vkCreateInstance =
|
|
||||||
reinterpret_cast<PFN_vkCreateInstance>(vkGetInstanceProcAddr(nullptr, "vkCreateInstance"));
|
|
||||||
if (vkCreateInstance == nullptr ||
|
|
||||||
vkCreateInstance(&instance_ci, nullptr, &vk_instance) != VK_SUCCESS) {
|
|
||||||
LOG_CRITICAL(Frontend, "Failed to create Vulkan instance!");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
vkDestroyInstance = reinterpret_cast<PFN_vkDestroyInstance>(
|
|
||||||
vkGetInstanceProcAddr(vk_instance, "vkDestroyInstance"));
|
|
||||||
if (vkDestroyInstance == nullptr) {
|
|
||||||
LOG_CRITICAL(Frontend, "Failed to retrieve Vulkan function pointer!");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!SDL_Vulkan_CreateSurface(render_window, vk_instance, &vk_surface)) {
|
|
||||||
LOG_CRITICAL(Frontend, "Failed to create Vulkan surface! {}", SDL_GetError());
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OnResize();
|
OnResize();
|
||||||
|
@ -107,51 +67,12 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen)
|
||||||
Common::g_scm_branch, Common::g_scm_desc);
|
Common::g_scm_branch, Common::g_scm_desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() {
|
EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() = default;
|
||||||
vkDestroyInstance(vk_instance, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmuWindow_SDL2_VK::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
|
||||||
void* surface) const {
|
|
||||||
const auto instance_proc_addr = vkGetInstanceProcAddr;
|
|
||||||
std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr));
|
|
||||||
std::memcpy(instance, &vk_instance, sizeof(vk_instance));
|
|
||||||
std::memcpy(surface, &vk_surface, sizeof(vk_surface));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_VK::CreateSharedContext() const {
|
std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_VK::CreateSharedContext() const {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EmuWindow_SDL2_VK::UseStandardLayers(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr) const {
|
|
||||||
if (!Settings::values.renderer_debug) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto vkEnumerateInstanceLayerProperties =
|
|
||||||
reinterpret_cast<PFN_vkEnumerateInstanceLayerProperties>(
|
|
||||||
vkGetInstanceProcAddr(nullptr, "vkEnumerateInstanceLayerProperties"));
|
|
||||||
if (vkEnumerateInstanceLayerProperties == nullptr) {
|
|
||||||
LOG_CRITICAL(Frontend, "Failed to retrieve Vulkan function pointer!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 available_layers_count{};
|
|
||||||
if (vkEnumerateInstanceLayerProperties(&available_layers_count, nullptr) != VK_SUCCESS) {
|
|
||||||
LOG_CRITICAL(Frontend, "Failed to enumerate Vulkan validation layers!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
std::vector<VkLayerProperties> layers(available_layers_count);
|
|
||||||
if (vkEnumerateInstanceLayerProperties(&available_layers_count, layers.data()) != VK_SUCCESS) {
|
|
||||||
LOG_CRITICAL(Frontend, "Failed to enumerate Vulkan validation layers!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::find_if(layers.begin(), layers.end(), [&](const auto& layer) {
|
|
||||||
return layer.layerName == std::string("VK_LAYER_LUNARG_standard_validation");
|
|
||||||
}) != layers.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmuWindow_SDL2_VK::Present() {
|
void EmuWindow_SDL2_VK::Present() {
|
||||||
// TODO (bunnei): ImplementMe
|
// TODO (bunnei): ImplementMe
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,27 +4,21 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <vulkan/vulkan.h>
|
#include <memory>
|
||||||
|
|
||||||
#include "core/frontend/emu_window.h"
|
#include "core/frontend/emu_window.h"
|
||||||
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
|
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
class System;
|
||||||
|
}
|
||||||
|
|
||||||
class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 {
|
class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 {
|
||||||
public:
|
public:
|
||||||
explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen);
|
explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen);
|
||||||
~EmuWindow_SDL2_VK();
|
~EmuWindow_SDL2_VK();
|
||||||
|
|
||||||
void Present() override;
|
void Present() override;
|
||||||
void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
|
||||||
void* surface) const override;
|
|
||||||
|
|
||||||
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
|
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
|
||||||
|
|
||||||
private:
|
|
||||||
bool UseStandardLayers(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr) const;
|
|
||||||
|
|
||||||
VkInstance vk_instance{};
|
|
||||||
VkSurfaceKHR vk_surface{};
|
|
||||||
|
|
||||||
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{};
|
|
||||||
PFN_vkDestroyInstance vkDestroyInstance{};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -116,10 +116,6 @@ bool EmuWindow_SDL2_Hide::IsShown() const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuWindow_SDL2_Hide::RetrieveVulkanHandlers(void*, void*, void*) const {
|
|
||||||
UNREACHABLE();
|
|
||||||
}
|
|
||||||
|
|
||||||
class SDLGLContext : public Core::Frontend::GraphicsContext {
|
class SDLGLContext : public Core::Frontend::GraphicsContext {
|
||||||
public:
|
public:
|
||||||
explicit SDLGLContext() {
|
explicit SDLGLContext() {
|
||||||
|
|
|
@ -19,10 +19,6 @@ public:
|
||||||
/// Whether the screen is being shown or not.
|
/// Whether the screen is being shown or not.
|
||||||
bool IsShown() const override;
|
bool IsShown() const override;
|
||||||
|
|
||||||
/// Retrieves Vulkan specific handlers from the window
|
|
||||||
void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
|
||||||
void* surface) const override;
|
|
||||||
|
|
||||||
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
|
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
Loading…
Reference in New Issue