diff options
| author | bunnei <bunneidev@gmail.com> | 2019-02-14 10:06:48 -0500 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-02-14 10:06:48 -0500 | 
| commit | fcc3aa0bbf4a1343f90dfc7a93afc31e770c3a70 (patch) | |
| tree | 05acdede20385a9d738666c59fb63aa9ca055a6a | |
| parent | 8490e7746a46df9c5feed50a2af342f0890270ff (diff) | |
| parent | 8beca060d1585f530eaa36972edf7bc92f42bd7e (diff) | |
Merge pull request #2113 from ReinUsesLisp/vulkan-base
vulkan: Add dependencies and device abstraction
| -rw-r--r-- | .gitmodules | 3 | ||||
| -rw-r--r-- | CMakeLists.txt | 2 | ||||
| m--------- | externals/Vulkan-Headers | 0 | ||||
| -rw-r--r-- | src/common/logging/backend.cpp | 1 | ||||
| -rw-r--r-- | src/common/logging/log.h | 1 | ||||
| -rw-r--r-- | src/video_core/CMakeLists.txt | 10 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/declarations.h | 45 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_device.cpp | 231 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_device.h | 116 | 
9 files changed, 409 insertions, 0 deletions
| diff --git a/.gitmodules b/.gitmodules index a33a04167..2558a5ebc 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/CMakeLists.txt b/CMakeLists.txt index 97d888762..32cfa8580 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) diff --git a/externals/Vulkan-Headers b/externals/Vulkan-Headers new file mode 160000 +Subproject 7f02d9bb810f371de0fe833c80004c34f7ff8c5 diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 12f6d0114..a5e031189 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 d4ec31ec3..8ed6d5050 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 diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 1db0d031d..d35a738d5 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -101,6 +101,16 @@ add_library(video_core STATIC      video_core.h  ) +if (ENABLE_VULKAN) +    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) +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 000000000..ba25b5bc7 --- /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 <vulkan/vulkan.hpp> + +namespace Vulkan { + +// vulkan.hpp unique handlers use DispatchLoaderStatic +template <typename T> +using UniqueHandle = vk::UniqueHandle<T, vk::DispatchLoaderDynamic>; + +using UniqueAccelerationStructureNV = UniqueHandle<vk::AccelerationStructureNV>; +using UniqueBuffer = UniqueHandle<vk::Buffer>; +using UniqueBufferView = UniqueHandle<vk::BufferView>; +using UniqueCommandBuffer = UniqueHandle<vk::CommandBuffer>; +using UniqueCommandPool = UniqueHandle<vk::CommandPool>; +using UniqueDescriptorPool = UniqueHandle<vk::DescriptorPool>; +using UniqueDescriptorSet = UniqueHandle<vk::DescriptorSet>; +using UniqueDescriptorSetLayout = UniqueHandle<vk::DescriptorSetLayout>; +using UniqueDescriptorUpdateTemplate = UniqueHandle<vk::DescriptorUpdateTemplate>; +using UniqueDevice = UniqueHandle<vk::Device>; +using UniqueDeviceMemory = UniqueHandle<vk::DeviceMemory>; +using UniqueEvent = UniqueHandle<vk::Event>; +using UniqueFence = UniqueHandle<vk::Fence>; +using UniqueFramebuffer = UniqueHandle<vk::Framebuffer>; +using UniqueImage = UniqueHandle<vk::Image>; +using UniqueImageView = UniqueHandle<vk::ImageView>; +using UniqueIndirectCommandsLayoutNVX = UniqueHandle<vk::IndirectCommandsLayoutNVX>; +using UniqueObjectTableNVX = UniqueHandle<vk::ObjectTableNVX>; +using UniquePipeline = UniqueHandle<vk::Pipeline>; +using UniquePipelineCache = UniqueHandle<vk::PipelineCache>; +using UniquePipelineLayout = UniqueHandle<vk::PipelineLayout>; +using UniqueQueryPool = UniqueHandle<vk::QueryPool>; +using UniqueRenderPass = UniqueHandle<vk::RenderPass>; +using UniqueSampler = UniqueHandle<vk::Sampler>; +using UniqueSamplerYcbcrConversion = UniqueHandle<vk::SamplerYcbcrConversion>; +using UniqueSemaphore = UniqueHandle<vk::Semaphore>; +using UniqueShaderModule = UniqueHandle<vk::ShaderModule>; +using UniqueSwapchainKHR = UniqueHandle<vk::SwapchainKHR>; +using UniqueValidationCacheEXT = UniqueHandle<vk::ValidationCacheEXT>; + +} // namespace 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 000000000..78a4e5f0e --- /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 <map> +#include <optional> +#include <set> +#include <vector> +#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<vk::Format, 3> Depth24UnormS8Uint = { +    vk::Format::eD32SfloatS8Uint, vk::Format::eD16UnormS8Uint, {}}; +constexpr std::array<vk::Format, 3> 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<const char*> extensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME}; +    const vk::DeviceCreateInfo device_ci({}, static_cast<u32>(queue_cis.size()), queue_cis.data(), +                                         0, nullptr, static_cast<u32>(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<vk::NoParent, vk::DispatchLoaderDynamic>(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<u32>(wanted_format), static_cast<u32>(wanted_usage), +                     static_cast<u32>(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<u32>(wanted_format), static_cast<u32>(alternative), +                    static_cast<u32>(wanted_usage), static_cast<u32>(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<u32>(wanted_format), static_cast<u32>(wanted_usage), +                 static_cast<u32>(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<u32>(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<u32>(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<vk::QueueFlagBits>(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<u32> graphics_family_, present_family_; + +    const auto queue_family_properties = physical.getQueueFamilyProperties(dldi); +    for (u32 i = 0; i < static_cast<u32>(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<u64>(props.limits.minUniformBufferOffsetAlignment); +} + +std::vector<vk::DeviceQueueCreateInfo> VKDevice::GetDeviceQueueCreateInfos() const { +    static const float QUEUE_PRIORITY = 1.f; + +    std::set<u32> unique_queue_families = {graphics_family, present_family}; +    std::vector<vk::DeviceQueueCreateInfo> queue_cis; + +    for (u32 queue_family : unique_queue_families) +        queue_cis.push_back({{}, queue_family, 1, &QUEUE_PRIORITY}); + +    return queue_cis; +} + +std::map<vk::Format, vk::FormatProperties> VKDevice::GetFormatProperties( +    const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical) { +    std::map<vk::Format, vk::FormatProperties> 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 000000000..e87c7a508 --- /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 <map> +#include <vector> +#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<vk::DeviceQueueCreateInfo> 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<vk::Format, vk::FormatProperties> 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<vk::Format, vk::FormatProperties> format_properties; ///< Format properties dictionary +}; + +} // namespace Vulkan | 
