renderer_vulkan: Create Vulkan instance and surface in the backend

Drop SDL2 and Qt Vulkan support requirements, use the native handles
instead.
This commit is contained in:
ReinUsesLisp
2020-03-17 16:26:50 -03:00
parent b8ff26132e
commit f441a90a8e
18 changed files with 461 additions and 385 deletions

View File

@@ -44,10 +44,6 @@ private:
};
EmuWindow::EmuWindow() {
// TODO: Find a better place to set this.
config.min_client_area_size =
std::make_pair(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
active_config = config;
touch_state = std::make_shared<TouchState>();
Input::RegisterFactory<Input::TouchDevice>("emu_window", touch_state);
}

View File

@@ -12,6 +12,15 @@
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 graphics context that can be used for background computation or drawing. If the
* graphics backend doesn't require the context, then the implementation of these methods can be
@@ -49,11 +58,20 @@ public:
class EmuWindow : public GraphicsContext {
public:
/// Data structure to store emuwindow configuration
struct WindowConfig {
bool fullscreen = false;
int res_width = 0;
int res_height = 0;
std::pair<unsigned, unsigned> min_client_area_size;
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
@@ -75,10 +93,6 @@ public:
/// Returns if window is shown (not minimized)
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)
* @param framebuffer_x Framebuffer x-coordinate that was pressed
@@ -97,22 +111,10 @@ public:
void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y);
/**
* Returns currently active configuration.
* @note Accesses to the returned object need not be consistent because it may be modified in
* another thread
* Returns system information about the drawing area.
*/
const WindowConfig& GetActiveConfig() const {
return active_config;
}
/**
* Requests the internal configuration to be replaced by the specified argument at some point in
* the future.
* @note This method is thread-safe, because it delays configuration changes to the GUI event
* loop. Hence there is no guarantee on when the requested configuration will be active.
*/
void SetConfig(const WindowConfig& val) {
config = val;
const WindowSystemInfo& GetWindowInfo() const {
return window_info;
}
/**
@@ -130,26 +132,9 @@ public:
void UpdateCurrentFramebufferLayout(unsigned width, unsigned height);
protected:
EmuWindow();
explicit EmuWindow();
virtual ~EmuWindow();
/**
* Processes any pending configuration changes from the last SetConfig call.
* This method invokes OnMinimalClientAreaChangeRequest if the corresponding configuration
* field changed.
* @note Implementations will usually want to call this from the GUI thread.
* @todo Actually call this in existing implementations.
*/
void ProcessConfigurationChanges() {
// TODO: For proper thread safety, we should eventually implement a proper
// multiple-writer/single-reader queue...
if (config.min_client_area_size != active_config.min_client_area_size) {
OnMinimalClientAreaChangeRequest(config.min_client_area_size);
config.min_client_area_size = active_config.min_client_area_size;
}
}
/**
* Update framebuffer layout with the given parameter.
* @note EmuWindow implementations will usually use this in window resize event handlers.
@@ -167,6 +152,8 @@ protected:
client_area_height = size.second;
}
WindowSystemInfo window_info;
private:
/**
* Handler called when the minimal client area was requested to be changed via SetConfig.
@@ -182,10 +169,6 @@ private:
unsigned client_area_width; ///< Current client width, should be set by window impl.
unsigned client_area_height; ///< Current client height, should be set by window impl.
WindowConfig config; ///< Internal configuration (changes pending for being applied in
/// ProcessConfigurationChanges)
WindowConfig active_config; ///< Internal active configuration
class TouchState;
std::shared_ptr<TouchState> touch_state;

View File

@@ -51,6 +51,7 @@ using UniqueSampler = UniqueHandle<vk::Sampler>;
using UniqueSamplerYcbcrConversion = UniqueHandle<vk::SamplerYcbcrConversion>;
using UniqueSemaphore = UniqueHandle<vk::Semaphore>;
using UniqueShaderModule = UniqueHandle<vk::ShaderModule>;
using UniqueSurfaceKHR = UniqueHandle<vk::SurfaceKHR>;
using UniqueSwapchainKHR = UniqueHandle<vk::SwapchainKHR>;
using UniqueValidationCacheEXT = UniqueHandle<vk::ValidationCacheEXT>;
using UniqueDebugReportCallbackEXT = UniqueHandle<vk::DebugReportCallbackEXT>;

View File

@@ -2,8 +2,12 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <array>
#include <cstring>
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include <fmt/format.h>
@@ -31,15 +35,29 @@
#include "video_core/renderer_vulkan/vk_state_tracker.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>
#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 {
using Core::Frontend::WindowSystemType;
VkBool32 DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity_,
VkDebugUtilsMessageTypeFlagsEXT type,
const VkDebugUtilsMessengerCallbackDataEXT* data,
[[maybe_unused]] void* user_data) {
const vk::DebugUtilsMessageSeverityFlagBitsEXT severity{severity_};
const auto severity{static_cast<vk::DebugUtilsMessageSeverityFlagBitsEXT>(severity_)};
const char* message{data->pMessage};
if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eError) {
@@ -75,21 +93,86 @@ Common::DynamicLibrary OpenVulkanLibrary() {
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;
if (!library.GetSymbol("vkGetInstanceProcAddr", &vkGetInstanceProcAddr)) {
LOG_ERROR(Render_Vulkan, "vkGetInstanceProcAddr not present in Vulkan");
return UniqueInstance{};
}
dld.init(vkGetInstanceProcAddr);
const vk::ApplicationInfo application_info("yuzu", VK_MAKE_VERSION(0, 1, 0), "yuzu",
VK_MAKE_VERSION(0, 1, 0), VK_API_VERSION_1_1);
const vk::InstanceCreateInfo instance_ci({}, &application_info, 0, nullptr, 0, nullptr);
vk::Instance unsafe_instance;
if (vk::createInstance(&instance_ci, nullptr, &unsafe_instance, dld) != vk::Result::eSuccess) {
std::vector<const char*> extensions;
extensions.reserve(4);
switch (window_type) {
case Core::Frontend::WindowSystemType::Headless:
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{};
}
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");
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});
}
@@ -186,27 +269,12 @@ void RendererVulkan::TryPresent(int /*timeout_ms*/) {
}
bool RendererVulkan::Init() {
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{};
render_window.RetrieveVulkanHandlers(&vkGetInstanceProcAddr, &instance, &surface);
const vk::DispatchLoaderDynamic dldi(instance, vkGetInstanceProcAddr);
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);
}
library = OpenVulkanLibrary();
instance = CreateInstance(library, dld, render_window.GetWindowInfo().type,
Settings::values.renderer_debug);
if (!instance || !CreateDebugCallback() || !CreateSurface() || !PickDevices()) {
return false;
}
debug_callback = UniqueDebugUtilsMessengerEXT(
*callback, vk::ObjectDestroy<vk::Instance, vk::DispatchLoaderDynamic>(
instance, nullptr, device->GetDispatchLoader()));
Report();
@@ -215,7 +283,7 @@ bool RendererVulkan::Init() {
resource_manager = std::make_unique<VKResourceManager>(*device);
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);
state_tracker = std::make_unique<StateTracker>(system);
@@ -252,8 +320,10 @@ void RendererVulkan::ShutDown() {
device.reset();
}
std::optional<vk::DebugUtilsMessengerEXT> RendererVulkan::CreateDebugCallback(
const vk::DispatchLoaderDynamic& dldi) {
bool RendererVulkan::CreateDebugCallback() {
if (!Settings::values.renderer_debug) {
return true;
}
const vk::DebugUtilsMessengerCreateInfoEXT callback_ci(
{},
vk::DebugUtilsMessageSeverityFlagBitsEXT::eError |
@@ -264,32 +334,88 @@ std::optional<vk::DebugUtilsMessengerEXT> RendererVulkan::CreateDebugCallback(
vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation |
vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance,
&DebugCallback, nullptr);
vk::DebugUtilsMessengerEXT callback;
if (instance.createDebugUtilsMessengerEXT(&callback_ci, nullptr, &callback, dldi) !=
vk::DebugUtilsMessengerEXT unsafe_callback;
if (instance->createDebugUtilsMessengerEXT(&callback_ci, nullptr, &unsafe_callback, dld) !=
vk::Result::eSuccess) {
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) {
const auto devices = instance.enumeratePhysicalDevices(dldi);
bool RendererVulkan::CreateSurface() {
[[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;
if (device_index < 0 || device_index >= static_cast<s32>(devices.size())) {
LOG_ERROR(Render_Vulkan, "Invalid device index {}!", device_index);
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;
}
device = std::make_unique<VKDevice>(dldi, physical_device, surface);
return device->Create(dldi, instance);
device = std::make_unique<VKDevice>(dld, physical_device, *surface);
return device->Create(*instance);
}
void RendererVulkan::Report() const {
@@ -316,11 +442,11 @@ void RendererVulkan::Report() const {
}
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();
if (!library.IsOpen()) {
return {};
}
vk::DispatchLoaderDynamic dld;
UniqueInstance instance = CreateInstance(library, dld);
if (!instance) {
return {};

View File

@@ -9,6 +9,8 @@
#include <string>
#include <vector>
#include "common/dynamic_library.h"
#include "video_core/renderer_base.h"
#include "video_core/renderer_vulkan/declarations.h"
@@ -48,17 +50,21 @@ public:
static std::vector<std::string> EnumerateDevices();
private:
std::optional<vk::DebugUtilsMessengerEXT> CreateDebugCallback(
const vk::DispatchLoaderDynamic& dldi);
bool CreateDebugCallback();
bool PickDevices(const vk::DispatchLoaderDynamic& dldi);
bool CreateSurface();
bool PickDevices();
void Report() const;
Core::System& system;
vk::Instance instance;
vk::SurfaceKHR surface;
Common::DynamicLibrary library;
vk::DispatchLoaderDynamic dld;
UniqueInstance instance;
UniqueSurfaceKHR surface;
VKScreenInfo screen_info;

View File

@@ -10,6 +10,7 @@
#include <string_view>
#include <thread>
#include <vector>
#include "common/assert.h"
#include "core/settings.h"
#include "video_core/renderer_vulkan/declarations.h"
@@ -35,20 +36,20 @@ void SetNext(void**& next, T& data) {
}
template <typename T>
T GetFeatures(vk::PhysicalDevice physical, const vk::DispatchLoaderDynamic& dldi) {
T GetFeatures(vk::PhysicalDevice physical, const vk::DispatchLoaderDynamic& dld) {
vk::PhysicalDeviceFeatures2 features;
T extension_features;
features.pNext = &extension_features;
physical.getFeatures2(&features, dldi);
physical.getFeatures2(&features, dld);
return extension_features;
}
template <typename T>
T GetProperties(vk::PhysicalDevice physical, const vk::DispatchLoaderDynamic& dldi) {
T GetProperties(vk::PhysicalDevice physical, const vk::DispatchLoaderDynamic& dld) {
vk::PhysicalDeviceProperties2 properties;
T extension_properties;
properties.pNext = &extension_properties;
physical.getProperties2(&properties, dldi);
physical.getProperties2(&properties, dld);
return extension_properties;
}
@@ -78,19 +79,19 @@ vk::FormatFeatureFlags GetFormatFeatures(vk::FormatProperties properties, Format
} // Anonymous namespace
VKDevice::VKDevice(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical,
VKDevice::VKDevice(const vk::DispatchLoaderDynamic& dld, vk::PhysicalDevice physical,
vk::SurfaceKHR surface)
: physical{physical}, properties{physical.getProperties(dldi)},
format_properties{GetFormatProperties(dldi, physical)} {
SetupFamilies(dldi, surface);
SetupFeatures(dldi);
: dld{dld}, physical{physical}, properties{physical.getProperties(dld)},
format_properties{GetFormatProperties(dld, physical)} {
SetupFamilies(surface);
SetupFeatures();
}
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 std::vector extensions = LoadExtensions(dldi);
const std::vector extensions = LoadExtensions();
vk::PhysicalDeviceFeatures2 features2;
void** next = &features2.pNext;
@@ -165,15 +166,13 @@ bool VKDevice::Create(const vk::DispatchLoaderDynamic& dldi, vk::Instance instan
nullptr);
device_ci.pNext = &features2;
vk::Device dummy_logical;
if (physical.createDevice(&device_ci, nullptr, &dummy_logical, dldi) != vk::Result::eSuccess) {
vk::Device unsafe_logical;
if (physical.createDevice(&device_ci, nullptr, &unsafe_logical, dld) != vk::Result::eSuccess) {
LOG_CRITICAL(Render_Vulkan, "Logical device failed to be created!");
return false;
}
dld.init(instance, dldi.vkGetInstanceProcAddr, dummy_logical, dldi.vkGetDeviceProcAddr);
logical = UniqueDevice(
dummy_logical, vk::ObjectDestroy<vk::NoParent, vk::DispatchLoaderDynamic>(nullptr, dld));
dld.init(instance, dld.vkGetInstanceProcAddr, unsafe_logical);
logical = UniqueDevice(unsafe_logical, {nullptr, dld});
CollectTelemetryParameters();
@@ -235,8 +234,7 @@ void VKDevice::ReportLoss() const {
// *(VKGraphicsPipeline*)data[0]
}
bool VKDevice::IsOptimalAstcSupported(const vk::PhysicalDeviceFeatures& features,
const vk::DispatchLoaderDynamic& dldi) const {
bool VKDevice::IsOptimalAstcSupported(const vk::PhysicalDeviceFeatures& features) const {
// Disable for now to avoid converting ASTC twice.
return false;
static constexpr std::array astc_formats = {
@@ -257,7 +255,7 @@ bool VKDevice::IsOptimalAstcSupported(const vk::PhysicalDeviceFeatures& features
vk::FormatFeatureFlagBits::eBlitDst | vk::FormatFeatureFlagBits::eTransferSrc |
vk::FormatFeatureFlagBits::eTransferDst};
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)) {
return false;
}
@@ -276,11 +274,9 @@ bool VKDevice::IsFormatSupported(vk::Format wanted_format, vk::FormatFeatureFlag
return (supported_usage & wanted_usage) == wanted_usage;
}
bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical,
vk::SurfaceKHR surface) {
bool is_suitable = true;
constexpr std::array required_extensions = {
bool VKDevice::IsSuitable(vk::PhysicalDevice physical, vk::SurfaceKHR surface,
const vk::DispatchLoaderDynamic& dld) {
static constexpr std::array required_extensions = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
VK_KHR_16BIT_STORAGE_EXTENSION_NAME,
VK_KHR_8BIT_STORAGE_EXTENSION_NAME,
@@ -290,9 +286,10 @@ bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDev
VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME,
VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME,
};
bool is_suitable = true;
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) {
if (available_extensions[i]) {
continue;
@@ -312,7 +309,7 @@ bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDev
}
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) {
const auto& family = queue_family_properties[i];
if (family.queueCount == 0) {
@@ -320,7 +317,7 @@ bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDev
}
has_graphics |=
(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) {
LOG_ERROR(Render_Vulkan, "Device lacks a graphics and present queue");
@@ -328,7 +325,7 @@ bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDev
}
// 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};
constexpr u32 required_ubo_size = 65536;
@@ -345,7 +342,7 @@ bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDev
is_suitable = false;
}
const auto features{physical.getFeatures(dldi)};
const auto features{physical.getFeatures(dld)};
const std::array feature_report = {
std::make_pair(features.vertexPipelineStoresAndAtomics, "vertexPipelineStoresAndAtomics"),
std::make_pair(features.independentBlend, "independentBlend"),
@@ -377,7 +374,7 @@ bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDev
return is_suitable;
}
std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynamic& dldi) {
std::vector<const char*> VKDevice::LoadExtensions() {
std::vector<const char*> extensions;
const auto Test = [&](const vk::ExtensionProperties& extension,
std::optional<std::reference_wrapper<bool>> status, const char* name,
@@ -408,7 +405,7 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami
bool has_khr_shader_float16_int8{};
bool has_ext_subgroup_size_control{};
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,
VK_KHR_UNIFORM_BUFFER_STANDARD_LAYOUT_EXTENSION_NAME, true);
Test(extension, has_khr_shader_float16_int8, VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME,
@@ -430,15 +427,15 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami
if (has_khr_shader_float16_int8) {
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);
}
if (has_ext_subgroup_size_control) {
const auto features =
GetFeatures<vk::PhysicalDeviceSubgroupSizeControlFeaturesEXT>(physical, dldi);
GetFeatures<vk::PhysicalDeviceSubgroupSizeControlFeaturesEXT>(physical, dld);
const auto properties =
GetProperties<vk::PhysicalDeviceSubgroupSizeControlPropertiesEXT>(physical, dldi);
GetProperties<vk::PhysicalDeviceSubgroupSizeControlPropertiesEXT>(physical, dld);
is_warp_potentially_bigger = properties.maxSubgroupSize > GuestWarpSize;
@@ -453,9 +450,9 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami
if (has_ext_transform_feedback) {
const auto features =
GetFeatures<vk::PhysicalDeviceTransformFeedbackFeaturesEXT>(physical, dldi);
GetFeatures<vk::PhysicalDeviceTransformFeedbackFeaturesEXT>(physical, dld);
const auto properties =
GetProperties<vk::PhysicalDeviceTransformFeedbackPropertiesEXT>(physical, dldi);
GetProperties<vk::PhysicalDeviceTransformFeedbackPropertiesEXT>(physical, dld);
if (features.transformFeedback && features.geometryStreams &&
properties.maxTransformFeedbackStreams >= 4 && properties.maxTransformFeedbackBuffers &&
@@ -468,10 +465,10 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami
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_;
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) {
if (graphics_family_ && present_family_)
break;
@@ -480,10 +477,12 @@ void VKDevice::SetupFamilies(const vk::DispatchLoaderDynamic& dldi, vk::SurfaceK
if (queue_family.queueCount == 0)
continue;
if (queue_family.queueFlags & vk::QueueFlagBits::eGraphics)
if (queue_family.queueFlags & vk::QueueFlagBits::eGraphics) {
graphics_family_ = i;
if (physical.getSurfaceSupportKHR(i, surface, dldi))
}
if (physical.getSurfaceSupportKHR(i, surface, dld)) {
present_family_ = i;
}
}
ASSERT(graphics_family_ && present_family_);
@@ -491,10 +490,10 @@ void VKDevice::SetupFamilies(const vk::DispatchLoaderDynamic& dldi, vk::SurfaceK
present_family = *present_family_;
}
void VKDevice::SetupFeatures(const vk::DispatchLoaderDynamic& dldi) {
const auto supported_features{physical.getFeatures(dldi)};
void VKDevice::SetupFeatures() {
const auto supported_features{physical.getFeatures(dld)};
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() {
@@ -522,7 +521,7 @@ std::vector<vk::DeviceQueueCreateInfo> VKDevice::GetDeviceQueueCreateInfos() con
}
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,
vk::Format::eA8B8G8R8UintPack32,
vk::Format::eA8B8G8R8SnormPack32,
@@ -593,7 +592,7 @@ std::unordered_map<vk::Format, vk::FormatProperties> VKDevice::GetFormatProperti
vk::Format::eE5B9G9R9UfloatPack32};
std::unordered_map<vk::Format, vk::FormatProperties> format_properties;
for (const auto format : formats) {
format_properties.emplace(format, physical.getFormatProperties(format, dldi));
format_properties.emplace(format, physical.getFormatProperties(format, dld));
}
return format_properties;
}

View File

@@ -22,12 +22,12 @@ const u32 GuestWarpSize = 32;
/// Handles data specific to a physical device.
class VKDevice final {
public:
explicit VKDevice(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical,
explicit VKDevice(const vk::DispatchLoaderDynamic& dld, vk::PhysicalDevice physical,
vk::SurfaceKHR surface);
~VKDevice();
/// 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.
@@ -188,18 +188,18 @@ public:
}
/// Checks if the physical device is suitable.
static bool IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical,
vk::SurfaceKHR surface);
static bool IsSuitable(vk::PhysicalDevice physical, vk::SurfaceKHR surface,
const vk::DispatchLoaderDynamic& dld);
private:
/// 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.
void SetupFamilies(const vk::DispatchLoaderDynamic& dldi, vk::SurfaceKHR surface);
void SetupFamilies(vk::SurfaceKHR surface);
/// Sets up device features.
void SetupFeatures(const vk::DispatchLoaderDynamic& dldi);
void SetupFeatures();
/// Collects telemetry information from the device.
void CollectTelemetryParameters();
@@ -208,8 +208,7 @@ private:
std::vector<vk::DeviceQueueCreateInfo> GetDeviceQueueCreateInfos() const;
/// Returns true if ASTC textures are natively supported.
bool IsOptimalAstcSupported(const vk::PhysicalDeviceFeatures& features,
const vk::DispatchLoaderDynamic& dldi) const;
bool IsOptimalAstcSupported(const vk::PhysicalDeviceFeatures& features) const;
/// Returns true if a format is supported.
bool IsFormatSupported(vk::Format wanted_format, vk::FormatFeatureFlags wanted_usage,
@@ -217,10 +216,10 @@ private:
/// Returns the device properties for Vulkan formats.
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::PhysicalDevice physical; ///< Physical device.
vk::PhysicalDeviceProperties properties; ///< Device properties.
UniqueDevice logical; ///< Logical device.
vk::Queue graphics_queue; ///< Main graphics queue.

View File

@@ -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 ${PLATFORM_LIBRARIES} Threads::Threads)
if (NOT WIN32)
target_include_directories(yuzu PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
endif()
target_compile_definitions(yuzu PRIVATE
# Use QStringBuilder for string concatenation to reduce
# the overall number of temporary strings created.

View File

@@ -17,8 +17,9 @@
#include <QScreen>
#include <QStringList>
#include <QWindow>
#ifdef HAS_VULKAN
#include <QVulkanWindow>
#ifndef WIN32
#include <qpa/qplatformnativeinterface.h>
#endif
#include <fmt/format.h>
@@ -111,9 +112,9 @@ void EmuThread::run() {
#endif
}
class GGLContext : public Core::Frontend::GraphicsContext {
class OpenGLContext : public Core::Frontend::GraphicsContext {
public:
explicit GGLContext(QOpenGLContext* shared_context)
explicit OpenGLContext(QOpenGLContext* shared_context)
: context(new QOpenGLContext(shared_context->parent())),
surface(new QOffscreenSurface(nullptr)) {
@@ -203,6 +204,80 @@ private:
QWidget* event_handler{};
};
class RenderWidget : public QWidget {
public:
RenderWidget(GRenderWindow* parent) : QWidget(parent), parent(parent) {
setAttribute(Qt::WA_NativeWindow);
SetFillBackground(true);
}
virtual ~RenderWidget() = default;
void resizeEvent(QResizeEvent* ev) override {
parent->resize(ev->size());
parent->OnFramebufferSizeChanged();
}
void keyPressEvent(QKeyEvent* event) override {
InputCommon::GetKeyboard()->PressKey(event->key());
}
void keyReleaseEvent(QKeyEvent* event) override {
InputCommon::GetKeyboard()->ReleaseKey(event->key());
}
void mousePressEvent(QMouseEvent* event) override {
if (event->source() == Qt::MouseEventSynthesizedBySystem)
return; // touch input is handled in TouchBeginEvent
const auto pos{event->pos()};
if (event->button() == Qt::LeftButton) {
const auto [x, y] = parent->ScaleTouch(pos);
parent->TouchPressed(x, y);
} else if (event->button() == Qt::RightButton) {
InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y());
}
}
void mouseMoveEvent(QMouseEvent* event) override {
if (event->source() == Qt::MouseEventSynthesizedBySystem)
return; // touch input is handled in TouchUpdateEvent
const auto pos{event->pos()};
const auto [x, y] = parent->ScaleTouch(pos);
parent->TouchMoved(x, y);
InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y());
}
void mouseReleaseEvent(QMouseEvent* event) override {
if (event->source() == Qt::MouseEventSynthesizedBySystem)
return; // touch input is handled in TouchEndEvent
if (event->button() == Qt::LeftButton)
parent->TouchReleased();
else if (event->button() == Qt::RightButton)
InputCommon::GetMotionEmu()->EndTilt();
}
std::pair<unsigned, unsigned> GetSize() const {
return std::make_pair(width(), height());
}
void SetFillBackground(bool fill) {
setAutoFillBackground(fill);
setAttribute(Qt::WA_OpaquePaintEvent, !fill);
setAttribute(Qt::WA_NoSystemBackground, !fill);
setAttribute(Qt::WA_PaintOnScreen, !fill);
}
QPaintEngine* paintEngine() const override {
return autoFillBackground() ? QWidget::paintEngine() : nullptr;
}
private:
GRenderWindow* parent;
};
class OpenGLWindow final : public ChildRenderWindow {
public:
OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context)
@@ -262,10 +337,44 @@ public:
}
private:
QWidget* event_handler{};
QWidget* event_handler = nullptr;
};
#endif
static Core::Frontend::WindowSystemType GetWindowSystemType() {
// 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();
// 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;
return wsi;
}
GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread)
: QWidget(parent_), emu_thread(emu_thread) {
setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
@@ -309,21 +418,6 @@ bool GRenderWindow::IsShown() const {
return !isMinimized();
}
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_window);
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
}
// On Qt 5.0+, this correctly gets the size of the framebuffer (pixels).
//
// Older versions get the window size (density independent pixels),
@@ -474,16 +568,26 @@ void GRenderWindow::resizeEvent(QResizeEvent* event) {
std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const {
if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) {
return std::make_unique<GGLContext>(QOpenGLContext::globalShareContext());
return std::make_unique<OpenGLContext>(QOpenGLContext::globalShareContext());
}
return {};
}
bool GRenderWindow::InitRenderTarget() {
ReleaseRenderTarget();
bool GRenderWindow::ReloadRenderTarget() {
core_context.reset();
delete child;
delete layout();
first_frame = false;
child = new RenderWidget(this);
// Update the Window System information with the new render target
window_info = GetWindowSystemInfo(child->windowHandle());
QBoxLayout* layout = new QHBoxLayout(this);
layout->setMargin(0);
setLayout(layout);
switch (Settings::values.renderer_backend) {
case Settings::RendererBackend::OpenGL:
if (!InitializeOpenGL()) {
@@ -506,9 +610,10 @@ bool GRenderWindow::InitRenderTarget() {
hide();
resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
child->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
OnFramebufferSizeChanged();
BackupGeometry();
if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) {
@@ -550,10 +655,6 @@ void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_p
layout);
}
void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) {
setMinimumSize(minimal_size.first, minimal_size.second);
}
bool GRenderWindow::InitializeOpenGL() {
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
// WA_DontShowOnScreen, WA_DeleteOnClose
@@ -575,43 +676,12 @@ bool GRenderWindow::InitializeOpenGL() {
layout()->addWidget(child_widget);
core_context = CreateSharedContext();
return true;
}
bool GRenderWindow::InitializeVulkan() {
#ifdef HAS_VULKAN
vk_instance = std::make_unique<QVulkanInstance>();
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;
}
GMainWindow* parent = GetMainWindow();
QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr;
child_window = new VulkanWindow(parent_win_handle, this, vk_instance.get());
child_window->create();
child_widget = createWindowContainer(child_window, this);
child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
layout()->addWidget(child_widget);
layout()->addWidget(child);
return true;
#else
QMessageBox::critical(this, tr("Vulkan not available!"),
@@ -673,10 +743,12 @@ QStringList GRenderWindow::GetUnsupportedGLExtensions() const {
void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) {
this->emu_thread = emu_thread;
child->SetFillBackground(false);
}
void GRenderWindow::OnEmulationStopping() {
emu_thread = nullptr;
child->SetFillBackground(true);
}
void GRenderWindow::showEvent(QShowEvent* event) {

View File

@@ -19,14 +19,14 @@
class GRenderWindow;
class QKeyEvent;
class QOpenGLContext;
class QScreen;
class QTouchEvent;
class QStringList;
class QSurface;
class QOpenGLContext;
#ifdef HAS_VULKAN
class QVulkanInstance;
#endif
class RenderWidget;
class GMainWindow;
class GRenderWindow;
namespace VideoCore {
enum class LoadCallbackStage;
@@ -132,8 +132,6 @@ public:
void DoneCurrent() override;
void PollEvents() 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;
void BackupGeometry();
@@ -142,6 +140,7 @@ public:
QByteArray saveGeometry(); // overridden
qreal windowPixelRatio() const;
std::pair<u32, u32> ScaleTouch(QPointF pos) const;
void closeEvent(QCloseEvent* event) override;
@@ -158,7 +157,7 @@ public:
void focusOutEvent(QFocusEvent* event) override;
bool InitRenderTarget();
bool ReloadRenderTarget();
/// Destroy the previous run's child_widget which should also destroy the child_window
void ReleaseRenderTarget();
@@ -176,26 +175,21 @@ signals:
void FirstFrameDisplayed();
private:
std::pair<u32, u32> ScaleTouch(QPointF pos) const;
void TouchBeginEvent(const QTouchEvent* event);
void TouchUpdateEvent(const QTouchEvent* event);
void TouchEndEvent();
void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;
bool InitializeOpenGL();
bool InitializeVulkan();
bool LoadOpenGL();
QStringList GetUnsupportedGLExtensions() const;
RenderWidget* child = nullptr;
EmuThread* emu_thread;
std::unique_ptr<GraphicsContext> core_context;
#ifdef HAS_VULKAN
std::unique_ptr<QVulkanInstance> vk_instance;
#endif
/// Temporary storage of the screenshot taken
QImage screenshot_image;

View File

@@ -14,6 +14,7 @@
#include "core/settings.h"
#include "ui_configure_graphics.h"
#include "yuzu/configuration/configure_graphics.h"
#ifdef HAS_VULKAN
#include "video_core/renderer_vulkan/renderer_vulkan.h"
#endif

View File

@@ -875,7 +875,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
if (emu_thread != nullptr)
ShutdownGame();
if (!render_window->InitRenderTarget()) {
if (!render_window->ReloadRenderTarget()) {
return false;
}

View File

@@ -136,7 +136,6 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(Core::System& system, bool fullscreen)
}
OnResize();
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
SDL_PumpEvents();
LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch,
Common::g_scm_desc);
@@ -156,12 +155,6 @@ void EmuWindow_SDL2_GL::DoneCurrent() {
core_context->DoneCurrent();
}
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 {
return std::make_unique<SDLGLContext>();
}

View File

@@ -17,10 +17,6 @@ public:
void DoneCurrent() 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;
private:

View File

@@ -2,114 +2,71 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <cstdlib>
#include <memory>
#include <string>
#include <vector>
#include <SDL.h>
#include <SDL_vulkan.h>
#include <fmt/format.h>
#include <vulkan/vulkan.h>
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/scm_rev.h"
#include "core/settings.h"
#include "video_core/renderer_vulkan/renderer_vulkan.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{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,
Common::g_scm_branch, Common::g_scm_desc);
render_window =
SDL_CreateWindow(window_title.c_str(),
SDL_WINDOWPOS_UNDEFINED, // x position
SDL_WINDOWPOS_UNDEFINED, // y position
SDL_CreateWindow(window_title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
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);
u32 extra_ext_count{};
if (!SDL_Vulkan_GetInstanceExtensions(render_window, &extra_ext_count, NULL)) {
LOG_CRITICAL(Frontend, "Failed to query Vulkan extensions count from SDL! {}",
SDL_GetError());
exit(1);
SDL_SysWMinfo wm;
if (SDL_GetWindowWMInfo(render_window, &wm) == SDL_FALSE) {
LOG_CRITICAL(Frontend, "Failed to get information from the window manager");
std::exit(EXIT_FAILURE);
}
auto extra_ext_names = std::make_unique<const char* []>(extra_ext_count);
if (!SDL_Vulkan_GetInstanceExtensions(render_window, &extra_ext_count, extra_ext_names.get())) {
LOG_CRITICAL(Frontend, "Failed to query Vulkan extensions from SDL! {}", SDL_GetError());
exit(1);
}
std::vector<const char*> enabled_extensions;
enabled_extensions.insert(enabled_extensions.begin(), extra_ext_names.get(),
extra_ext_names.get() + extra_ext_count);
std::vector<const char*> enabled_layers;
if (use_standard_layers) {
enabled_extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
enabled_layers.push_back("VK_LAYER_LUNARG_standard_validation");
}
VkApplicationInfo app_info{};
app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
app_info.apiVersion = VK_API_VERSION_1_1;
app_info.applicationVersion = VK_MAKE_VERSION(0, 1, 0);
app_info.pApplicationName = "yuzu-emu";
app_info.engineVersion = VK_MAKE_VERSION(0, 1, 0);
app_info.pEngineName = "yuzu-emu";
VkInstanceCreateInfo instance_ci{};
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);
switch (wm.subsystem) {
#ifdef SDL_VIDEO_DRIVER_WINDOWS
case SDL_SYSWM_TYPE::SDL_SYSWM_WINDOWS:
window_info.type = Core::Frontend::WindowSystemType::Windows;
window_info.render_surface = reinterpret_cast<void*>(wm.info.win.window);
break;
#endif
#ifdef SDL_VIDEO_DRIVER_X11
case SDL_SYSWM_TYPE::SDL_SYSWM_X11:
window_info.type = Core::Frontend::WindowSystemType::X11;
window_info.display_connection = wm.info.x11.display;
window_info.render_surface = reinterpret_cast<void*>(wm.info.x11.window);
break;
#endif
#ifdef SDL_VIDEO_DRIVER_WAYLAND
case SDL_SYSWM_TYPE::SDL_SYSWM_WAYLAND:
window_info.type = Core::Frontend::WindowSystemType::Wayland;
window_info.display_connection = wm.info.wl.display;
window_info.render_surface = wm.info.wl.surface;
break;
#endif
default:
LOG_CRITICAL(Frontend, "Window manager subsystem not implemented");
std::exit(EXIT_FAILURE);
}
OnResize();
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
SDL_PumpEvents();
LOG_INFO(Frontend, "yuzu Version: {} | {}-{} (Vulkan)", Common::g_build_name,
Common::g_scm_branch, Common::g_scm_desc);
}
EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() {
vkDestroyInstance(vk_instance, nullptr);
}
EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() = default;
void EmuWindow_SDL2_VK::MakeCurrent() {
// Unused on Vulkan
@@ -119,47 +76,10 @@ void EmuWindow_SDL2_VK::DoneCurrent() {
// Unused on Vulkan
}
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 {
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() {
// TODO (bunnei): ImplementMe
}

View File

@@ -4,10 +4,15 @@
#pragma once
#include <vulkan/vulkan.h>
#include <memory>
#include "core/frontend/emu_window.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
namespace Core {
class System;
}
class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 {
public:
explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen);
@@ -16,17 +21,6 @@ public:
void MakeCurrent() override;
void DoneCurrent() 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;
private:
bool UseStandardLayers(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr) const;
VkInstance vk_instance{};
VkSurfaceKHR vk_surface{};
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{};
PFN_vkDestroyInstance vkDestroyInstance{};
};

View File

@@ -125,7 +125,3 @@ void EmuWindow_SDL2_Hide::DoneCurrent() {
bool EmuWindow_SDL2_Hide::IsShown() const {
return false;
}
void EmuWindow_SDL2_Hide::RetrieveVulkanHandlers(void*, void*, void*) const {
UNREACHABLE();
}

View File

@@ -25,10 +25,6 @@ public:
/// Whether the screen is being shown or not.
bool IsShown() const override;
/// Retrieves Vulkan specific handlers from the window
void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
void* surface) const override;
/// Whether the window is still open, and a close request hasn't yet been sent
bool IsOpen() const;