renderer_vulkan: Isolate surface creation to vk_platform.cpp

* Also cleanup the init code somewhat
This commit is contained in:
GPUCode
2022-09-24 12:50:00 +03:00
parent c72a365d78
commit ec9f1902f5
6 changed files with 191 additions and 202 deletions

View File

@ -14,7 +14,7 @@ namespace Frontend {
/// Information for the Graphics Backends signifying what type of screen pointer is in /// Information for the Graphics Backends signifying what type of screen pointer is in
/// WindowInformation /// WindowInformation
enum class WindowSystemType { enum class WindowSystemType : u8 {
Headless, Headless,
Windows, Windows,
X11, X11,

View File

@ -93,6 +93,7 @@ add_library(video_core STATIC
renderer_vulkan/vk_instance.h renderer_vulkan/vk_instance.h
renderer_vulkan/vk_pipeline_cache.cpp renderer_vulkan/vk_pipeline_cache.cpp
renderer_vulkan/vk_pipeline_cache.h renderer_vulkan/vk_pipeline_cache.h
renderer_vulkan/vk_platform.cpp
renderer_vulkan/vk_platform.h renderer_vulkan/vk_platform.h
renderer_vulkan/vk_renderpass_cache.cpp renderer_vulkan/vk_renderpass_cache.cpp
renderer_vulkan/vk_renderpass_cache.h renderer_vulkan/vk_renderpass_cache.h

View File

@ -4,8 +4,8 @@
#define VULKAN_HPP_NO_CONSTRUCTORS #define VULKAN_HPP_NO_CONSTRUCTORS
#include <span> #include <span>
#include <array>
#include "common/assert.h" #include "common/assert.h"
#include "core/frontend/emu_window.h"
#include "video_core/renderer_vulkan/vk_platform.h" #include "video_core/renderer_vulkan/vk_platform.h"
#include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_instance.h"
@ -45,9 +45,7 @@ Instance::Instance(Frontend::EmuWindow& window) {
.ppEnabledExtensionNames = extensions.data() .ppEnabledExtensionNames = extensions.data()
}; };
// Create VkInstance
instance = vk::createInstance(instance_info); instance = vk::createInstance(instance_info);
VULKAN_HPP_DEFAULT_DISPATCHER.init(instance);
surface = CreateSurface(instance, window); surface = CreateSurface(instance, window);
// TODO: GPU select dialog // TODO: GPU select dialog
@ -55,8 +53,7 @@ Instance::Instance(Frontend::EmuWindow& window) {
physical_device = physical_devices[0]; physical_device = physical_devices[0];
device_properties = physical_device.getProperties(); device_properties = physical_device.getProperties();
// Create logical device CreateDevice();
CreateDevice(false);
} }
Instance::~Instance() { Instance::~Instance() {
@ -114,45 +111,16 @@ vk::Format Instance::GetFormatAlternative(vk::Format format) const {
} }
} }
bool Instance::CreateDevice(bool validation_enabled) { bool Instance::CreateDevice() {
// Determine required extensions and features
auto feature_chain = physical_device.getFeatures2<vk::PhysicalDeviceFeatures2, auto feature_chain = physical_device.getFeatures2<vk::PhysicalDeviceFeatures2,
vk::PhysicalDeviceDynamicRenderingFeaturesKHR, vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT>();
vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT,
vk::PhysicalDeviceExtendedDynamicState2FeaturesEXT>();
// 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; 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!"); 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<vk::PhysicalDeviceDynamicRenderingFeaturesKHR>(),
//feature_chain.get<vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT>(),
//feature_chain.get<vk::PhysicalDeviceExtendedDynamicState2FeaturesEXT>()
};
auto extension_list = physical_device.enumerateDeviceExtensionProperties(); auto extension_list = physical_device.enumerateDeviceExtensionProperties();
if (extension_list.empty()) { if (extension_list.empty()) {
LOG_CRITICAL(Render_Vulkan, "No extensions supported by device."); LOG_CRITICAL(Render_Vulkan, "No extensions supported by device.");
@ -163,7 +131,7 @@ bool Instance::CreateDevice(bool validation_enabled) {
std::array<const char*, 6> enabled_extensions; std::array<const char*, 6> enabled_extensions;
u32 enabled_extension_count = 0; 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) { auto result = std::find_if(extension_list.begin(), extension_list.end(), [&](const auto& prop) {
return name.compare(prop.extensionName.data()); return name.compare(prop.extensionName.data());
}); });
@ -174,20 +142,13 @@ bool Instance::CreateDevice(bool validation_enabled) {
return true; return true;
} }
if (required) { LOG_WARNING(Render_Vulkan, "Extension {} unavailable.", name);
LOG_ERROR(Render_Vulkan, "Unable to find required extension {}.", name);
}
return false; return false;
}; };
// Add required extensions extended_dynamic_state = AddExtension(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
AddExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME, true); push_descriptors = AddExtension(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
AddExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
// 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);
// Search queue families for graphics and present queues // Search queue families for graphics and present queues
auto family_properties = physical_device.getQueueFamilyProperties(); auto family_properties = physical_device.getQueueFamilyProperties();
@ -236,39 +197,50 @@ bool Instance::CreateDevice(bool validation_enabled) {
} }
}; };
vk::DeviceCreateInfo device_info = { const u32 queue_count = graphics_queue_family_index != present_queue_family_index ? 2u : 1u;
.pNext = &features, // TODO: Change this const vk::StructureChain device_chain = {
.queueCreateInfoCount = 1, vk::DeviceCreateInfo{
.queueCreateInfoCount = queue_count,
.pQueueCreateInfos = queue_infos.data(), .pQueueCreateInfos = queue_infos.data(),
.enabledExtensionCount = enabled_extension_count, .enabledExtensionCount = enabled_extension_count,
.ppEnabledExtensionNames = enabled_extensions.data(), .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<vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT>(),
}; };
if (graphics_queue_family_index != present_queue_family_index) {
device_info.queueCreateInfoCount = 2;
}
// Create logical device // Create logical device
device = physical_device.createDevice(device_info); device = physical_device.createDevice(device_chain.get());
VULKAN_HPP_DEFAULT_DISPATCHER.init(device); VULKAN_HPP_DEFAULT_DISPATCHER.init(device);
// Grab the graphics and present queues. // Grab the graphics and present queues.
graphics_queue = device.getQueue(graphics_queue_family_index, 0); graphics_queue = device.getQueue(graphics_queue_family_index, 0);
present_queue = device.getQueue(present_queue_family_index, 0); present_queue = device.getQueue(present_queue_family_index, 0);
// Create the VMA allocator
CreateAllocator(); CreateAllocator();
return true; return true;
} }
void Instance::CreateAllocator() { void Instance::CreateAllocator() {
VmaVulkanFunctions functions = { const VmaVulkanFunctions functions = {
.vkGetInstanceProcAddr = VULKAN_HPP_DEFAULT_DISPATCHER.vkGetInstanceProcAddr, .vkGetInstanceProcAddr = VULKAN_HPP_DEFAULT_DISPATCHER.vkGetInstanceProcAddr,
.vkGetDeviceProcAddr = VULKAN_HPP_DEFAULT_DISPATCHER.vkGetDeviceProcAddr .vkGetDeviceProcAddr = VULKAN_HPP_DEFAULT_DISPATCHER.vkGetDeviceProcAddr
}; };
VmaAllocatorCreateInfo allocator_info = { const VmaAllocatorCreateInfo allocator_info = {
.physicalDevice = physical_device, .physicalDevice = physical_device,
.device = device, .device = device,
.pVulkanFunctions = &functions, .pVulkanFunctions = &functions,
@ -276,7 +248,7 @@ void Instance::CreateAllocator() {
.vulkanApiVersion = VK_API_VERSION_1_1 .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); LOG_CRITICAL(Render_Vulkan, "Failed to initialize VMA with error {}", result);
UNREACHABLE(); UNREACHABLE();
} }

View File

@ -68,10 +68,6 @@ public:
} }
/// Feature support /// Feature support
bool IsDynamicRenderingSupported() const {
return dynamic_rendering;
}
bool IsExtendedDynamicStateSupported() const { bool IsExtendedDynamicStateSupported() const {
// TODO: Enable this when the pipeline builder is confirmed functional // TODO: Enable this when the pipeline builder is confirmed functional
return false; return false;
@ -102,27 +98,25 @@ public:
} }
private: private:
bool CreateDevice(bool validation_enabled); /// Creates the logical device opportunistically enabling extensions
bool CreateDevice();
/// Creates the VMA allocator handle
void CreateAllocator(); void CreateAllocator();
private: 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::Device device;
vk::PhysicalDevice physical_device; vk::PhysicalDevice physical_device;
vk::Instance instance; vk::Instance instance;
vk::SurfaceKHR surface; vk::SurfaceKHR surface;
vk::PhysicalDeviceProperties device_properties; vk::PhysicalDeviceProperties device_properties;
VmaAllocator allocator; VmaAllocator allocator;
vk::Queue present_queue;
// Features per vulkan version vk::Queue graphics_queue;
bool dynamic_rendering = false; u32 present_queue_family_index = 0;
u32 graphics_queue_family_index = 0;
bool extended_dynamic_state = false; bool extended_dynamic_state = false;
bool push_descriptors = false; bool push_descriptors = false;
}; };
} // namespace VideoCore::Vulkan } // namespace Vulkan

View File

@ -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<HWND>(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<Display*>(window_info.display_connection),
.window = reinterpret_cast<Window>(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<wl_display*>(window_info.display_connection),
static_cast<wl_surface*>(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<const char*> 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<const char*>{};
}
// Add the windowing system specific extension
std::vector<const char*> 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<const char*>{};
}
}
return extensions;
}
} // namespace Vulkan

View File

@ -4,131 +4,19 @@
#pragma once #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 <vector> #include <vector>
#include "common/assert.h" #include "common/common_types.h"
#include "common/logging/log.h"
#include "core/frontend/emu_window.h"
#include "video_core/renderer_vulkan/vk_common.h" #include "video_core/renderer_vulkan/vk_common.h"
namespace Frontend {
class EmuWindow;
enum class WindowSystemType : u8;
}
namespace Vulkan { namespace Vulkan {
inline vk::SurfaceKHR CreateSurface(const vk::Instance& instance, const Frontend::EmuWindow& emu_window) { std::vector<const char*> GetInstanceExtensions(Frontend::WindowSystemType window_type, bool enable_debug_utils);
const auto& window_info = emu_window.GetWindowInfo();
vk::SurfaceKHR surface;
#if VK_USE_PLATFORM_WIN32_KHR vk::SurfaceKHR CreateSurface(vk::Instance instance, const Frontend::EmuWindow& emu_window);
if (window_info.type == Frontend::WindowSystemType::Windows) {
const vk::Win32SurfaceCreateInfoKHR win32_ci = {
.hinstance = nullptr,
.hwnd = static_cast<HWND>(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<Display*>(window_info.display_connection),
.window = reinterpret_cast<Window>(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<wl_display*>(window_info.display_connection),
static_cast<wl_surface*>(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<const char*>{};
}
// Add the windowing system specific extension
std::vector<const char*> 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<const char*>{};
}
}
return extensions;
}
} // namespace Vulkan } // namespace Vulkan