From afb8af98539b2d6b6d82e698182a193f587b9805 Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Tue, 12 Feb 2019 17:57:53 -0300 Subject: [PATCH 1/5] gitmodules: Add Vulkan headers dependency --- .gitmodules | 3 +++ externals/Vulkan-Headers | 1 + 2 files changed, 4 insertions(+) create mode 160000 externals/Vulkan-Headers diff --git a/.gitmodules b/.gitmodules index a33a041672..2558a5ebc1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -37,3 +37,6 @@ [submodule "discord-rpc"] path = externals/discord-rpc url = https://github.com/discordapp/discord-rpc.git +[submodule "Vulkan-Headers"] + path = externals/Vulkan-Headers + url = https://github.com/KhronosGroup/Vulkan-Headers.git diff --git a/externals/Vulkan-Headers b/externals/Vulkan-Headers new file mode 160000 index 0000000000..7f02d9bb81 --- /dev/null +++ b/externals/Vulkan-Headers @@ -0,0 +1 @@ +Subproject commit 7f02d9bb810f371de0fe833c80004c34f7ff8c57 From cc94a6d1018630c28492604fdd4de11390c1258e Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Tue, 12 Feb 2019 17:58:11 -0300 Subject: [PATCH 2/5] cmake: Add Vulkan option --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 97d888762d..32cfa85802 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,8 @@ option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OF option(ENABLE_CUBEB "Enables the cubeb audio backend" ON) +option(ENABLE_VULKAN "Enables Vulkan backend" ON) + option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF) if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/hooks/pre-commit) From b12ab4d805be81c2a9bdefaea77f640439ab1633 Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Tue, 12 Feb 2019 17:59:04 -0300 Subject: [PATCH 3/5] logging: Add Vulkan backend logging class type --- src/common/logging/backend.cpp | 1 + src/common/logging/log.h | 1 + 2 files changed, 2 insertions(+) diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 12f6d01144..a5e031189b 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -232,6 +232,7 @@ void DebuggerBackend::Write(const Entry& entry) { CLS(Render) \ SUB(Render, Software) \ SUB(Render, OpenGL) \ + SUB(Render, Vulkan) \ CLS(Audio) \ SUB(Audio, DSP) \ SUB(Audio, Sink) \ diff --git a/src/common/logging/log.h b/src/common/logging/log.h index d4ec31ec35..8ed6d50502 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h @@ -112,6 +112,7 @@ enum class Class : ClassType { Render, ///< Emulator video output and hardware acceleration Render_Software, ///< Software renderer backend Render_OpenGL, ///< OpenGL backend + Render_Vulkan, ///< Vulkan backend Audio, ///< Audio emulation Audio_DSP, ///< The HLE implementation of the DSP Audio_Sink, ///< Emulator audio output backend From 18fe910957b2edb7e89eefc6f5c984b791d85341 Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Tue, 12 Feb 2019 18:01:40 -0300 Subject: [PATCH 4/5] renderer_vulkan: Add declarations file This file is intended to be included instead of vulkan/vulkan.hpp. It includes declarations of unique handlers using a dynamic dispatcher instead of a static one (which would require linking to a Vulkan library). --- src/video_core/CMakeLists.txt | 7 +++ src/video_core/renderer_vulkan/declarations.h | 45 +++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 src/video_core/renderer_vulkan/declarations.h diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 33e507e69e..a12680d350 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -101,6 +101,13 @@ add_library(video_core STATIC video_core.h ) +if (ENABLE_VULKAN) + target_sources(video_core PRIVATE renderer_vulkan/declarations.h) + + target_include_directories(video_core PRIVATE ../../externals/Vulkan-Headers/include) + target_compile_definitions(video_core PRIVATE HAS_VULKAN) +endif() + create_target_directory_groups(video_core) target_link_libraries(video_core PUBLIC common core) diff --git a/src/video_core/renderer_vulkan/declarations.h b/src/video_core/renderer_vulkan/declarations.h new file mode 100644 index 0000000000..ba25b5bc70 --- /dev/null +++ b/src/video_core/renderer_vulkan/declarations.h @@ -0,0 +1,45 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include + +namespace Vulkan { + +// vulkan.hpp unique handlers use DispatchLoaderStatic +template +using UniqueHandle = vk::UniqueHandle; + +using UniqueAccelerationStructureNV = UniqueHandle; +using UniqueBuffer = UniqueHandle; +using UniqueBufferView = UniqueHandle; +using UniqueCommandBuffer = UniqueHandle; +using UniqueCommandPool = UniqueHandle; +using UniqueDescriptorPool = UniqueHandle; +using UniqueDescriptorSet = UniqueHandle; +using UniqueDescriptorSetLayout = UniqueHandle; +using UniqueDescriptorUpdateTemplate = UniqueHandle; +using UniqueDevice = UniqueHandle; +using UniqueDeviceMemory = UniqueHandle; +using UniqueEvent = UniqueHandle; +using UniqueFence = UniqueHandle; +using UniqueFramebuffer = UniqueHandle; +using UniqueImage = UniqueHandle; +using UniqueImageView = UniqueHandle; +using UniqueIndirectCommandsLayoutNVX = UniqueHandle; +using UniqueObjectTableNVX = UniqueHandle; +using UniquePipeline = UniqueHandle; +using UniquePipelineCache = UniqueHandle; +using UniquePipelineLayout = UniqueHandle; +using UniqueQueryPool = UniqueHandle; +using UniqueRenderPass = UniqueHandle; +using UniqueSampler = UniqueHandle; +using UniqueSamplerYcbcrConversion = UniqueHandle; +using UniqueSemaphore = UniqueHandle; +using UniqueShaderModule = UniqueHandle; +using UniqueSwapchainKHR = UniqueHandle; +using UniqueValidationCacheEXT = UniqueHandle; + +} // namespace Vulkan From 8beca060d1585f530eaa36972edf7bc92f42bd7e Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Tue, 12 Feb 2019 18:28:05 -0300 Subject: [PATCH 5/5] vk_device: Abstract device handling into a class VKDevice contains all the data required to manage and initialize a physical device. Its intention is to be passed across Vulkan objects to query device-specific data (for example the logical device and the dispatch loader). --- src/video_core/CMakeLists.txt | 5 +- src/video_core/renderer_vulkan/vk_device.cpp | 231 +++++++++++++++++++ src/video_core/renderer_vulkan/vk_device.h | 116 ++++++++++ 3 files changed, 351 insertions(+), 1 deletion(-) create mode 100644 src/video_core/renderer_vulkan/vk_device.cpp create mode 100644 src/video_core/renderer_vulkan/vk_device.h diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index a12680d350..a0cb6ba5f8 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -102,7 +102,10 @@ add_library(video_core STATIC ) if (ENABLE_VULKAN) - target_sources(video_core PRIVATE renderer_vulkan/declarations.h) + target_sources(video_core PRIVATE + renderer_vulkan/declarations.h + renderer_vulkan/vk_device.cpp + renderer_vulkan/vk_device.h) target_include_directories(video_core PRIVATE ../../externals/Vulkan-Headers/include) target_compile_definitions(video_core PRIVATE HAS_VULKAN) diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp new file mode 100644 index 0000000000..78a4e5f0e0 --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_device.cpp @@ -0,0 +1,231 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include +#include "common/assert.h" +#include "video_core/renderer_vulkan/declarations.h" +#include "video_core/renderer_vulkan/vk_device.h" + +namespace Vulkan { + +namespace Alternatives { + +constexpr std::array Depth24UnormS8Uint = { + vk::Format::eD32SfloatS8Uint, vk::Format::eD16UnormS8Uint, {}}; +constexpr std::array Depth16UnormS8Uint = { + vk::Format::eD24UnormS8Uint, vk::Format::eD32SfloatS8Uint, {}}; + +} // namespace Alternatives + +constexpr const vk::Format* GetFormatAlternatives(vk::Format format) { + switch (format) { + case vk::Format::eD24UnormS8Uint: + return Alternatives::Depth24UnormS8Uint.data(); + case vk::Format::eD16UnormS8Uint: + return Alternatives::Depth16UnormS8Uint.data(); + default: + return nullptr; + } +} + +constexpr vk::FormatFeatureFlags GetFormatFeatures(vk::FormatProperties properties, + FormatType format_type) { + switch (format_type) { + case FormatType::Linear: + return properties.linearTilingFeatures; + case FormatType::Optimal: + return properties.optimalTilingFeatures; + case FormatType::Buffer: + return properties.bufferFeatures; + default: + return {}; + } +} + +VKDevice::VKDevice(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical, + vk::SurfaceKHR surface) + : physical{physical}, format_properties{GetFormatProperties(dldi, physical)} { + SetupFamilies(dldi, surface); + SetupProperties(dldi); +} + +VKDevice::~VKDevice() = default; + +bool VKDevice::Create(const vk::DispatchLoaderDynamic& dldi, vk::Instance instance) { + const auto queue_cis = GetDeviceQueueCreateInfos(); + vk::PhysicalDeviceFeatures device_features{}; + + const std::vector extensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME}; + const vk::DeviceCreateInfo device_ci({}, static_cast(queue_cis.size()), queue_cis.data(), + 0, nullptr, static_cast(extensions.size()), + extensions.data(), &device_features); + vk::Device dummy_logical; + if (physical.createDevice(&device_ci, nullptr, &dummy_logical, dldi) != 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(nullptr, dld)); + + graphics_queue = logical->getQueue(graphics_family, 0, dld); + present_queue = logical->getQueue(present_family, 0, dld); + return true; +} + +vk::Format VKDevice::GetSupportedFormat(vk::Format wanted_format, + vk::FormatFeatureFlags wanted_usage, + FormatType format_type) const { + if (IsFormatSupported(wanted_format, wanted_usage, format_type)) { + return wanted_format; + } + // The wanted format is not supported by hardware, search for alternatives + const vk::Format* alternatives = GetFormatAlternatives(wanted_format); + if (alternatives == nullptr) { + LOG_CRITICAL(Render_Vulkan, + "Format={} with usage={} and type={} has no defined alternatives and host " + "hardware does not support it", + static_cast(wanted_format), static_cast(wanted_usage), + static_cast(format_type)); + UNREACHABLE(); + return wanted_format; + } + + std::size_t i = 0; + for (vk::Format alternative = alternatives[0]; alternative != vk::Format{}; + alternative = alternatives[++i]) { + if (!IsFormatSupported(alternative, wanted_usage, format_type)) + continue; + LOG_WARNING(Render_Vulkan, + "Emulating format={} with alternative format={} with usage={} and type={}", + static_cast(wanted_format), static_cast(alternative), + static_cast(wanted_usage), static_cast(format_type)); + return alternative; + } + + // No alternatives found, panic + LOG_CRITICAL(Render_Vulkan, + "Format={} with usage={} and type={} is not supported by the host hardware and " + "doesn't support any of the alternatives", + static_cast(wanted_format), static_cast(wanted_usage), + static_cast(format_type)); + UNREACHABLE(); + return wanted_format; +} + +bool VKDevice::IsFormatSupported(vk::Format wanted_format, vk::FormatFeatureFlags wanted_usage, + FormatType format_type) const { + const auto it = format_properties.find(wanted_format); + if (it == format_properties.end()) { + LOG_CRITICAL(Render_Vulkan, "Unimplemented format query={}", + static_cast(wanted_format)); + UNREACHABLE(); + return true; + } + const vk::FormatFeatureFlags supported_usage = GetFormatFeatures(it->second, format_type); + return (supported_usage & wanted_usage) == wanted_usage; +} + +bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical, + vk::SurfaceKHR surface) { + const std::string swapchain_extension = VK_KHR_SWAPCHAIN_EXTENSION_NAME; + + bool has_swapchain{}; + for (const auto& prop : physical.enumerateDeviceExtensionProperties(nullptr, dldi)) { + has_swapchain |= prop.extensionName == swapchain_extension; + } + if (!has_swapchain) { + // The device doesn't support creating swapchains. + return false; + } + + bool has_graphics{}, has_present{}; + const auto queue_family_properties = physical.getQueueFamilyProperties(dldi); + for (u32 i = 0; i < static_cast(queue_family_properties.size()); ++i) { + const auto& family = queue_family_properties[i]; + if (family.queueCount == 0) + continue; + + has_graphics |= + (family.queueFlags & vk::QueueFlagBits::eGraphics) != static_cast(0); + has_present |= physical.getSurfaceSupportKHR(i, surface, dldi) != 0; + } + if (!has_graphics || !has_present) { + // The device doesn't have a graphics and present queue. + return false; + } + + // TODO(Rodrigo): Check if the device matches all requeriments. + const vk::PhysicalDeviceProperties props = physical.getProperties(dldi); + if (props.limits.maxUniformBufferRange < 65536) { + return false; + } + + // Device is suitable. + return true; +} + +void VKDevice::SetupFamilies(const vk::DispatchLoaderDynamic& dldi, vk::SurfaceKHR surface) { + std::optional graphics_family_, present_family_; + + const auto queue_family_properties = physical.getQueueFamilyProperties(dldi); + for (u32 i = 0; i < static_cast(queue_family_properties.size()); ++i) { + if (graphics_family_ && present_family_) + break; + + const auto& queue_family = queue_family_properties[i]; + if (queue_family.queueCount == 0) + continue; + + if (queue_family.queueFlags & vk::QueueFlagBits::eGraphics) + graphics_family_ = i; + if (physical.getSurfaceSupportKHR(i, surface, dldi)) + present_family_ = i; + } + ASSERT(graphics_family_ && present_family_); + + graphics_family = *graphics_family_; + present_family = *present_family_; +} + +void VKDevice::SetupProperties(const vk::DispatchLoaderDynamic& dldi) { + const vk::PhysicalDeviceProperties props = physical.getProperties(dldi); + device_type = props.deviceType; + uniform_buffer_alignment = static_cast(props.limits.minUniformBufferOffsetAlignment); +} + +std::vector VKDevice::GetDeviceQueueCreateInfos() const { + static const float QUEUE_PRIORITY = 1.f; + + std::set unique_queue_families = {graphics_family, present_family}; + std::vector queue_cis; + + for (u32 queue_family : unique_queue_families) + queue_cis.push_back({{}, queue_family, 1, &QUEUE_PRIORITY}); + + return queue_cis; +} + +std::map VKDevice::GetFormatProperties( + const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical) { + std::map format_properties; + + const auto AddFormatQuery = [&format_properties, &dldi, physical](vk::Format format) { + format_properties.emplace(format, physical.getFormatProperties(format, dldi)); + }; + AddFormatQuery(vk::Format::eA8B8G8R8UnormPack32); + AddFormatQuery(vk::Format::eR5G6B5UnormPack16); + AddFormatQuery(vk::Format::eD32Sfloat); + AddFormatQuery(vk::Format::eD16UnormS8Uint); + AddFormatQuery(vk::Format::eD24UnormS8Uint); + AddFormatQuery(vk::Format::eD32SfloatS8Uint); + + return format_properties; +} + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_device.h b/src/video_core/renderer_vulkan/vk_device.h new file mode 100644 index 0000000000..e87c7a5085 --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_device.h @@ -0,0 +1,116 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include "common/common_types.h" +#include "video_core/renderer_vulkan/declarations.h" + +namespace Vulkan { + +/// Format usage descriptor +enum class FormatType { Linear, Optimal, Buffer }; + +/// Handles data specific to a physical device. +class VKDevice final { +public: + explicit VKDevice(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical, + vk::SurfaceKHR surface); + ~VKDevice(); + + /// Initializes the device. Returns true on success. + bool Create(const vk::DispatchLoaderDynamic& dldi, vk::Instance instance); + + /** + * Returns a format supported by the device for the passed requeriments. + * @param wanted_format The ideal format to be returned. It may not be the returned format. + * @param wanted_usage The usage that must be fulfilled even if the format is not supported. + * @param format_type Format type usage. + * @returns A format supported by the device. + */ + vk::Format GetSupportedFormat(vk::Format wanted_format, vk::FormatFeatureFlags wanted_usage, + FormatType format_type) const; + + /// Returns the dispatch loader with direct function pointers of the device + const vk::DispatchLoaderDynamic& GetDispatchLoader() const { + return dld; + } + + /// Returns the logical device + vk::Device GetLogical() const { + return logical.get(); + } + + /// Returns the physical device. + vk::PhysicalDevice GetPhysical() const { + return physical; + } + + /// Returns the main graphics queue. + vk::Queue GetGraphicsQueue() const { + return graphics_queue; + } + + /// Returns the main present queue. + vk::Queue GetPresentQueue() const { + return present_queue; + } + + /// Returns main graphics queue family index. + u32 GetGraphicsFamily() const { + return graphics_family; + } + + /// Returns main present queue family index. + u32 GetPresentFamily() const { + return present_family; + } + + /// Returns if the device is integrated with the host CPU + bool IsIntegrated() const { + return device_type == vk::PhysicalDeviceType::eIntegratedGpu; + } + + /// Returns uniform buffer alignment requeriment + u64 GetUniformBufferAlignment() const { + return uniform_buffer_alignment; + } + + /// Checks if the physical device is suitable. + static bool IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical, + vk::SurfaceKHR surface); + +private: + /// Sets up queue families. + void SetupFamilies(const vk::DispatchLoaderDynamic& dldi, vk::SurfaceKHR surface); + + /// Sets up device properties. + void SetupProperties(const vk::DispatchLoaderDynamic& dldi); + + /// Returns a list of queue initialization descriptors. + std::vector GetDeviceQueueCreateInfos() const; + + /// Returns true if a format is supported. + bool IsFormatSupported(vk::Format wanted_format, vk::FormatFeatureFlags wanted_usage, + FormatType format_type) const; + + /// Returns the device properties for Vulkan formats. + static std::map GetFormatProperties( + const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical); + + const vk::PhysicalDevice physical; ///< Physical device + vk::DispatchLoaderDynamic dld; ///< Device function pointers + UniqueDevice logical; ///< Logical device + vk::Queue graphics_queue; ///< Main graphics queue + vk::Queue present_queue; ///< Main present queue + u32 graphics_family{}; ///< Main graphics queue family index + u32 present_family{}; ///< Main present queue family index + vk::PhysicalDeviceType device_type; ///< Physical device type + u64 uniform_buffer_alignment{}; ///< Uniform buffer alignment requeriment + std::map format_properties; ///< Format properties dictionary +}; + +} // namespace Vulkan