diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index e3bd62d8b..e880ce85b 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h @@ -14,7 +14,7 @@ namespace Frontend { /// Information for the Graphics Backends signifying what type of screen pointer is in /// WindowInformation -enum class WindowSystemType { +enum class WindowSystemType : u8 { Headless, Windows, X11, diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 35e7126b8..6a4876aca 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -93,6 +93,7 @@ add_library(video_core STATIC renderer_vulkan/vk_instance.h renderer_vulkan/vk_pipeline_cache.cpp renderer_vulkan/vk_pipeline_cache.h + renderer_vulkan/vk_platform.cpp renderer_vulkan/vk_platform.h renderer_vulkan/vk_renderpass_cache.cpp renderer_vulkan/vk_renderpass_cache.h diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index ad76df4e1..6302ac709 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -4,8 +4,8 @@ #define VULKAN_HPP_NO_CONSTRUCTORS #include -#include #include "common/assert.h" +#include "core/frontend/emu_window.h" #include "video_core/renderer_vulkan/vk_platform.h" #include "video_core/renderer_vulkan/vk_instance.h" @@ -45,9 +45,7 @@ Instance::Instance(Frontend::EmuWindow& window) { .ppEnabledExtensionNames = extensions.data() }; - // Create VkInstance instance = vk::createInstance(instance_info); - VULKAN_HPP_DEFAULT_DISPATCHER.init(instance); surface = CreateSurface(instance, window); // TODO: GPU select dialog @@ -55,8 +53,7 @@ Instance::Instance(Frontend::EmuWindow& window) { physical_device = physical_devices[0]; device_properties = physical_device.getProperties(); - // Create logical device - CreateDevice(false); + CreateDevice(); } Instance::~Instance() { @@ -114,45 +111,16 @@ vk::Format Instance::GetFormatAlternative(vk::Format format) const { } } -bool Instance::CreateDevice(bool validation_enabled) { - // Determine required extensions and features +bool Instance::CreateDevice() { auto feature_chain = physical_device.getFeatures2(); + vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT>(); - // Not having geometry shaders or wide lines will cause issues with rendering. + // Not having geometry shaders will cause issues with accelerated rendering. const vk::PhysicalDeviceFeatures available = feature_chain.get().features; - if (!available.geometryShader && !available.wideLines) { + if (!available.geometryShader) { LOG_WARNING(Render_Vulkan, "Geometry shaders not availabe! Accelerated rendering not possible!"); } - // Enable some common features other emulators like Dolphin use - const vk::PhysicalDeviceFeatures2 features = { - .features = { - .robustBufferAccess = available.robustBufferAccess, - .geometryShader = available.geometryShader, - .sampleRateShading = available.sampleRateShading, - .dualSrcBlend = available.dualSrcBlend, - .logicOp = available.logicOp, - .depthClamp = available.depthClamp, - .largePoints = available.largePoints, - .samplerAnisotropy = available.samplerAnisotropy, - .occlusionQueryPrecise = available.occlusionQueryPrecise, - .fragmentStoresAndAtomics = available.fragmentStoresAndAtomics, - .shaderStorageImageMultisample = available.shaderStorageImageMultisample, - .shaderClipDistance = available.shaderClipDistance - } - }; - - // Enable newer Vulkan features - auto enabled_features = vk::StructureChain{ - features, - //feature_chain.get(), - //feature_chain.get(), - //feature_chain.get() - }; - auto extension_list = physical_device.enumerateDeviceExtensionProperties(); if (extension_list.empty()) { LOG_CRITICAL(Render_Vulkan, "No extensions supported by device."); @@ -163,7 +131,7 @@ bool Instance::CreateDevice(bool validation_enabled) { std::array enabled_extensions; u32 enabled_extension_count = 0; - auto AddExtension = [&](std::string_view name, bool required) -> bool { + auto AddExtension = [&](std::string_view name) -> bool { auto result = std::find_if(extension_list.begin(), extension_list.end(), [&](const auto& prop) { return name.compare(prop.extensionName.data()); }); @@ -174,20 +142,13 @@ bool Instance::CreateDevice(bool validation_enabled) { return true; } - if (required) { - LOG_ERROR(Render_Vulkan, "Unable to find required extension {}.", name); - } - + LOG_WARNING(Render_Vulkan, "Extension {} unavailable.", name); return false; }; - // Add required extensions - AddExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME, true); - - // Check for optional features - //dynamic_rendering = AddExtension(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME, false); - //extended_dynamic_state = AddExtension(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false); - //push_descriptors = AddExtension(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME, false); + extended_dynamic_state = AddExtension(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); + push_descriptors = AddExtension(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME); + AddExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME); // Search queue families for graphics and present queues auto family_properties = physical_device.getQueueFamilyProperties(); @@ -236,39 +197,50 @@ bool Instance::CreateDevice(bool validation_enabled) { } }; - vk::DeviceCreateInfo device_info = { - .pNext = &features, // TODO: Change this - .queueCreateInfoCount = 1, - .pQueueCreateInfos = queue_infos.data(), - .enabledExtensionCount = enabled_extension_count, - .ppEnabledExtensionNames = enabled_extensions.data(), + const u32 queue_count = graphics_queue_family_index != present_queue_family_index ? 2u : 1u; + const vk::StructureChain device_chain = { + vk::DeviceCreateInfo{ + .queueCreateInfoCount = queue_count, + .pQueueCreateInfos = queue_infos.data(), + .enabledExtensionCount = enabled_extension_count, + .ppEnabledExtensionNames = enabled_extensions.data(), + }, + vk::PhysicalDeviceFeatures2{ + .features = { + .robustBufferAccess = available.robustBufferAccess, + .geometryShader = available.geometryShader, + .dualSrcBlend = available.dualSrcBlend, + .logicOp = available.logicOp, + .depthClamp = available.depthClamp, + .largePoints = available.largePoints, + .samplerAnisotropy = available.samplerAnisotropy, + .fragmentStoresAndAtomics = available.fragmentStoresAndAtomics, + .shaderStorageImageMultisample = available.shaderStorageImageMultisample, + .shaderClipDistance = available.shaderClipDistance + } + }, + feature_chain.get(), }; - if (graphics_queue_family_index != present_queue_family_index) { - device_info.queueCreateInfoCount = 2; - } - // Create logical device - device = physical_device.createDevice(device_info); + device = physical_device.createDevice(device_chain.get()); VULKAN_HPP_DEFAULT_DISPATCHER.init(device); // Grab the graphics and present queues. graphics_queue = device.getQueue(graphics_queue_family_index, 0); present_queue = device.getQueue(present_queue_family_index, 0); - // Create the VMA allocator CreateAllocator(); - return true; } void Instance::CreateAllocator() { - VmaVulkanFunctions functions = { + const VmaVulkanFunctions functions = { .vkGetInstanceProcAddr = VULKAN_HPP_DEFAULT_DISPATCHER.vkGetInstanceProcAddr, .vkGetDeviceProcAddr = VULKAN_HPP_DEFAULT_DISPATCHER.vkGetDeviceProcAddr }; - VmaAllocatorCreateInfo allocator_info = { + const VmaAllocatorCreateInfo allocator_info = { .physicalDevice = physical_device, .device = device, .pVulkanFunctions = &functions, @@ -276,7 +248,7 @@ void Instance::CreateAllocator() { .vulkanApiVersion = VK_API_VERSION_1_1 }; - if (auto result = vmaCreateAllocator(&allocator_info, &allocator); result != VK_SUCCESS) { + if (VkResult result = vmaCreateAllocator(&allocator_info, &allocator); result != VK_SUCCESS) { LOG_CRITICAL(Render_Vulkan, "Failed to initialize VMA with error {}", result); UNREACHABLE(); } diff --git a/src/video_core/renderer_vulkan/vk_instance.h b/src/video_core/renderer_vulkan/vk_instance.h index a7ab34ed9..07753ce62 100644 --- a/src/video_core/renderer_vulkan/vk_instance.h +++ b/src/video_core/renderer_vulkan/vk_instance.h @@ -68,10 +68,6 @@ public: } /// Feature support - bool IsDynamicRenderingSupported() const { - return dynamic_rendering; - } - bool IsExtendedDynamicStateSupported() const { // TODO: Enable this when the pipeline builder is confirmed functional return false; @@ -102,27 +98,25 @@ public: } private: - bool CreateDevice(bool validation_enabled); + /// Creates the logical device opportunistically enabling extensions + bool CreateDevice(); + + /// Creates the VMA allocator handle void CreateAllocator(); private: - // Queue family indexes - u32 present_queue_family_index = 0; - u32 graphics_queue_family_index = 0; - vk::Queue present_queue, graphics_queue; - - // Core vulkan objects vk::Device device; vk::PhysicalDevice physical_device; vk::Instance instance; vk::SurfaceKHR surface; vk::PhysicalDeviceProperties device_properties; VmaAllocator allocator; - - // Features per vulkan version - bool dynamic_rendering = false; + vk::Queue present_queue; + vk::Queue graphics_queue; + u32 present_queue_family_index = 0; + u32 graphics_queue_family_index = 0; bool extended_dynamic_state = false; bool push_descriptors = false; }; -} // namespace VideoCore::Vulkan +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_platform.cpp b/src/video_core/renderer_vulkan/vk_platform.cpp new file mode 100644 index 000000000..2b5ba3aa5 --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_platform.cpp @@ -0,0 +1,134 @@ +// Copyright 2022 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +// Include the vulkan platform specific header +#if defined(ANDROID) || defined (__ANDROID__) + #define VK_USE_PLATFORM_ANDROID_KHR +#elif defined(_WIN32) + #define VK_USE_PLATFORM_WIN32_KHR +#elif defined(__APPLE__) + #define VK_USE_PLATFORM_MACOS_MVK + #define VK_USE_PLATFORM_METAL_EXT +#else + #ifdef WAYLAND_DISPLAY + #define VK_USE_PLATFORM_WAYLAND_KHR + #else // wayland + #define VK_USE_PLATFORM_XLIB_KHR + #endif +#endif + +#define VULKAN_HPP_NO_CONSTRUCTORS +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/frontend/emu_window.h" +#include "video_core/renderer_vulkan/vk_platform.h" + +namespace Vulkan { + +vk::SurfaceKHR CreateSurface(vk::Instance instance, const Frontend::EmuWindow& emu_window) { + const auto& window_info = emu_window.GetWindowInfo(); + vk::SurfaceKHR surface{}; + + // Perform instance function loading here, to also load window system functions + VULKAN_HPP_DEFAULT_DISPATCHER.init(instance); + +#if defined(VK_USE_PLATFORM_WIN32_KHR) + if (window_info.type == Frontend::WindowSystemType::Windows) { + const vk::Win32SurfaceCreateInfoKHR win32_ci = { + .hinstance = nullptr, + .hwnd = static_cast(window_info.render_surface) + }; + + if (instance.createWin32SurfaceKHR(&win32_ci, nullptr, &surface) != vk::Result::eSuccess) { + LOG_CRITICAL(Render_Vulkan, "Failed to initialize Win32 surface"); + UNREACHABLE(); + } + } +#elif defined(VK_USE_PLATFORM_XLIB_KHR) + if (window_info.type == Frontend::WindowSystemType::X11) { + const vk::XlibSurfaceCreateInfoKHR xlib_ci = { + .dpy = static_cast(window_info.display_connection), + .window = reinterpret_cast(window_info.render_surface) + }; + + if (instance.createXlibSurfaceKHR(&xlib_ci, nullptr, &surface) != vk::Result::eSuccess) { + LOG_ERROR(Render_Vulkan, "Failed to initialize Xlib surface"); + UNREACHABLE(); + } + } + +#elif defined(VK_USE_PLATFORM_WAYLAND_KHR) + if (window_info.type == Frontend::WindowSystemType::Wayland) { + const vk::WaylandSurfaceCreateInfoKHR wayland_ci{{}, + static_cast(window_info.display_connection), + static_cast(window_info.render_surface)}; + if (instance.createWaylandSurfaceKHR(&wayland_ci, nullptr, &surface) != vk::Result::eSuccess) { + LOG_ERROR(Render_Vulkan, "Failed to initialize Wayland surface"); + UNREACHABLE(); + } + } +#endif + + if (!surface) { + LOG_CRITICAL(Render_Vulkan, "Presentation not supported on this platform"); + } + + return surface; +} + +std::vector GetInstanceExtensions(Frontend::WindowSystemType window_type, bool enable_debug_utils) { + const auto properties = vk::enumerateInstanceExtensionProperties(); + if (properties.empty()) { + LOG_ERROR(Render_Vulkan, "Failed to query extension properties"); + return std::vector{}; + } + + // Add the windowing system specific extension + std::vector extensions; + extensions.reserve(6); + + switch (window_type) { + case Frontend::WindowSystemType::Headless: + break; +#if defined(VK_USE_PLATFORM_WIN32_KHR) + case Frontend::WindowSystemType::Windows: + extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); + break; +#elif defined(VK_USE_PLATFORM_XLIB_KHR) + case Frontend::WindowSystemType::X11: + extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); + break; +#elif defined(VK_USE_PLATFORM_WAYLAND_KHR) + case 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 != Frontend::WindowSystemType::Headless) { + extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME); + } + + if (enable_debug_utils) { + extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + } + + for (const char* extension : extensions) { + const auto iter = std::ranges::find_if(properties, [extension](const auto& prop) { + return std::strcmp(extension, prop.extensionName) == 0; + }); + + if (iter == properties.end()) { + LOG_ERROR(Render_Vulkan, "Required instance extension {} is not available", extension); + return std::vector{}; + } + } + + return extensions; +} + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_platform.h b/src/video_core/renderer_vulkan/vk_platform.h index 2624ce876..c588909f6 100644 --- a/src/video_core/renderer_vulkan/vk_platform.h +++ b/src/video_core/renderer_vulkan/vk_platform.h @@ -4,131 +4,19 @@ #pragma once -// Include the vulkan platform specific header -#if defined(ANDROID) || defined (__ANDROID__) - #define VK_USE_PLATFORM_ANDROID_KHR 1 -#elif defined(_WIN32) - #define VK_USE_PLATFORM_WIN32_KHR 1 -#elif defined(__APPLE__) - #define VK_USE_PLATFORM_MACOS_MVK 1 - #define VK_USE_PLATFORM_METAL_EXT 1 -#else - #ifdef WAYLAND_DISPLAY - #define VK_USE_PLATFORM_WAYLAND_KHR 1 - #else // wayland - #define VK_USE_PLATFORM_XLIB_KHR 1 - #endif -#endif - -#define VULKAN_HPP_NO_CONSTRUCTORS #include -#include "common/assert.h" -#include "common/logging/log.h" -#include "core/frontend/emu_window.h" +#include "common/common_types.h" #include "video_core/renderer_vulkan/vk_common.h" +namespace Frontend { +class EmuWindow; +enum class WindowSystemType : u8; +} + namespace Vulkan { -inline vk::SurfaceKHR CreateSurface(const vk::Instance& instance, const Frontend::EmuWindow& emu_window) { - const auto& window_info = emu_window.GetWindowInfo(); - vk::SurfaceKHR surface; +std::vector GetInstanceExtensions(Frontend::WindowSystemType window_type, bool enable_debug_utils); -#if VK_USE_PLATFORM_WIN32_KHR - if (window_info.type == Frontend::WindowSystemType::Windows) { - const vk::Win32SurfaceCreateInfoKHR win32_ci = { - .hinstance = nullptr, - .hwnd = static_cast(window_info.render_surface) - }; - - if (instance.createWin32SurfaceKHR(&win32_ci, nullptr, &surface) != vk::Result::eSuccess) { - LOG_CRITICAL(Render_Vulkan, "Failed to initialize Win32 surface"); - UNREACHABLE(); - } - } -#elif VK_USE_PLATFORM_XLIB_KHR - if (window_info.type == Frontend::WindowSystemType::X11) { - const vk::XlibSurfaceCreateInfoKHR xlib_ci = { - .dpy = static_cast(window_info.display_connection), - .window = reinterpret_cast(window_info.render_surface) - }; - - if (instance.createXlibSurfaceKHR(&xlib_ci, nullptr, &surface) != vk::Result::eSuccess) { - LOG_ERROR(Render_Vulkan, "Failed to initialize Xlib surface"); - UNREACHABLE(); - } - } - -#elif VK_USE_PLATFORM_WAYLAND_KHR - if (window_info.type == Frontend::WindowSystemType::Wayland) { - const vk::WaylandSurfaceCreateInfoKHR wayland_ci{{}, - static_cast(window_info.display_connection), - static_cast(window_info.render_surface)}; - if (instance.createWaylandSurfaceKHR(&wayland_ci, nullptr, &surface) != vk::Result::eSuccess) { - LOG_ERROR(Render_Vulkan, "Failed to initialize Wayland surface"); - UNREACHABLE(); - } - } -#endif - - if (!surface) { - LOG_CRITICAL(Render_Vulkan, "Presentation not supported on this platform"); - } - - return surface; -} - -inline auto GetInstanceExtensions(Frontend::WindowSystemType window_type, bool enable_debug_utils) { - const auto properties = vk::enumerateInstanceExtensionProperties(); - if (properties.empty()) { - LOG_ERROR(Render_Vulkan, "Failed to query extension properties"); - return std::vector{}; - } - - // Add the windowing system specific extension - std::vector extensions; - extensions.reserve(6); - - switch (window_type) { - case Frontend::WindowSystemType::Headless: - break; -#if VK_USE_PLATFORM_WIN32_KHR - case Frontend::WindowSystemType::Windows: - extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); - break; -#elif VK_USE_PLATFORM_XLIB_KHR - case Frontend::WindowSystemType::X11: - extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); - break; -#elif VK_USE_PLATFORM_WAYLAND_KHR - case 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 != Frontend::WindowSystemType::Headless) { - extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME); - } - - if (enable_debug_utils) { - extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); - } - - for (const char* extension : extensions) { - const auto iter = std::ranges::find_if(properties, [extension](const auto& prop) { - return std::strcmp(extension, prop.extensionName) == 0; - }); - - if (iter == properties.end()) { - LOG_ERROR(Render_Vulkan, "Required instance extension {} is not available", extension); - return std::vector{}; - } - } - - return extensions; -} +vk::SurfaceKHR CreateSurface(vk::Instance instance, const Frontend::EmuWindow& emu_window); } // namespace Vulkan