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
/// WindowInformation
enum class WindowSystemType {
enum class WindowSystemType : u8 {
Headless,
Windows,
X11,

View File

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

View File

@ -4,8 +4,8 @@
#define VULKAN_HPP_NO_CONSTRUCTORS
#include <span>
#include <array>
#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::PhysicalDeviceFeatures2,
vk::PhysicalDeviceDynamicRenderingFeaturesKHR,
vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT,
vk::PhysicalDeviceExtendedDynamicState2FeaturesEXT>();
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<vk::PhysicalDeviceDynamicRenderingFeaturesKHR>(),
//feature_chain.get<vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT>(),
//feature_chain.get<vk::PhysicalDeviceExtendedDynamicState2FeaturesEXT>()
};
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<const char*, 6> 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<vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT>(),
};
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();
}

View File

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

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
// 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 "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<const char*> 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<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;
}
vk::SurfaceKHR CreateSurface(vk::Instance instance, const Frontend::EmuWindow& emu_window);
} // namespace Vulkan