diff --git a/src/dxvk/dxvk_adapter.cpp b/src/dxvk/dxvk_adapter.cpp index a735ad34f..f1a93db81 100644 --- a/src/dxvk/dxvk_adapter.cpp +++ b/src/dxvk/dxvk_adapter.cpp @@ -74,7 +74,7 @@ namespace dxvk { return i; } - throw DxvkError("DxvkAdapter::graphicsQueueFamily: No graphics queue found"); + throw DxvkError("DxvkAdapter: No graphics queue found"); } @@ -147,7 +147,14 @@ namespace dxvk { Rc DxvkAdapter::createDevice(const VkPhysicalDeviceFeatures& enabledFeatures) { - auto enabledExtensions = this->enableExtensions(); + const Rc extensions = new DxvkDeviceExtensions(); + extensions->enableExtensions(vk::NameSet::enumerateDeviceExtensions(*m_vki, m_handle)); + + if (!extensions->checkSupportStatus()) + throw DxvkError("DxvkAdapter: Failed to create device"); + + const vk::NameList enabledExtensions = + extensions->getEnabledExtensionNames(); Logger::info("Enabled device extensions:"); this->logNameList(enabledExtensions); @@ -188,8 +195,8 @@ namespace dxvk { VkDevice device = VK_NULL_HANDLE; if (m_vki->vkCreateDevice(m_handle, &info, nullptr, &device) != VK_SUCCESS) - throw DxvkError("DxvkDevice::createDevice: Failed to create device"); - return new DxvkDevice(this, new vk::DeviceFn(m_vki->instance(), device), enabledFeatures); + throw DxvkError("DxvkAdapter: Failed to create device"); + return new DxvkDevice(this, new vk::DeviceFn(m_vki->instance(), device), extensions, enabledFeatures); } @@ -198,36 +205,6 @@ namespace dxvk { } - vk::NameList DxvkAdapter::enableExtensions() { - std::vector extOptional = { - VK_KHR_MAINTENANCE2_EXTENSION_NAME, - }; - - std::vector extRequired = { - VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME, - VK_KHR_SWAPCHAIN_EXTENSION_NAME, - VK_KHR_MAINTENANCE1_EXTENSION_NAME, - }; - - const vk::NameSet extensionsAvailable - = vk::NameSet::enumerateDeviceExtensions(*m_vki, m_handle); - vk::NameList extensionsEnabled; - - for (auto e : extOptional) { - if (extensionsAvailable.supports(e)) - extensionsEnabled.add(e); - } - - for (auto e : extRequired) { - if (!extensionsAvailable.supports(e)) - throw DxvkError(str::format("DxvkDevice::getExtensions: Extension ", e, " not supported")); - extensionsEnabled.add(e); - } - - return extensionsEnabled; - } - - void DxvkAdapter::logNameList(const vk::NameList& names) { for (uint32_t i = 0; i < names.count(); i++) Logger::info(str::format(" ", names.name(i))); diff --git a/src/dxvk/dxvk_adapter.h b/src/dxvk/dxvk_adapter.h index 6907e295a..badbfbb64 100644 --- a/src/dxvk/dxvk_adapter.h +++ b/src/dxvk/dxvk_adapter.h @@ -155,8 +155,6 @@ namespace dxvk { std::vector m_queueFamilies; - vk::NameList enableExtensions(); - static void logNameList(const vk::NameList& names); }; diff --git a/src/dxvk/dxvk_device.cpp b/src/dxvk/dxvk_device.cpp index dede436dd..abfd64a87 100644 --- a/src/dxvk/dxvk_device.cpp +++ b/src/dxvk/dxvk_device.cpp @@ -6,9 +6,11 @@ namespace dxvk { DxvkDevice::DxvkDevice( const Rc& adapter, const Rc& vkd, + const Rc& extensions, const VkPhysicalDeviceFeatures& features) : m_adapter (adapter), m_vkd (vkd), + m_extensions (extensions), m_features (features), m_memory (new DxvkMemoryAllocator(adapter, vkd)), m_renderPassPool (new DxvkRenderPassPool (vkd)), diff --git a/src/dxvk/dxvk_device.h b/src/dxvk/dxvk_device.h index 13e6870c2..77d7948e6 100644 --- a/src/dxvk/dxvk_device.h +++ b/src/dxvk/dxvk_device.h @@ -5,6 +5,7 @@ #include "dxvk_compute.h" #include "dxvk_constant_state.h" #include "dxvk_context.h" +#include "dxvk_extensions.h" #include "dxvk_framebuffer.h" #include "dxvk_image.h" #include "dxvk_memory.h" @@ -40,6 +41,7 @@ namespace dxvk { DxvkDevice( const Rc& adapter, const Rc& vkd, + const Rc& extensions, const VkPhysicalDeviceFeatures& features); ~DxvkDevice(); @@ -71,6 +73,14 @@ namespace dxvk { return m_adapter; } + /** + * \brief Enabled device extensions + * \returns Enabled device extensions + */ + const DxvkDeviceExtensions& extensions() const { + return *m_extensions; + } + /** * \brief Enabled device features * \returns Enabled features @@ -300,6 +310,7 @@ namespace dxvk { Rc m_adapter; Rc m_vkd; + Rc m_extensions; VkPhysicalDeviceFeatures m_features; Rc m_memory; diff --git a/src/dxvk/dxvk_extensions.cpp b/src/dxvk/dxvk_extensions.cpp new file mode 100644 index 000000000..e1fd22e13 --- /dev/null +++ b/src/dxvk/dxvk_extensions.cpp @@ -0,0 +1,75 @@ +#include "dxvk_extensions.h" + +namespace dxvk { + + DxvkExtensionList:: DxvkExtensionList() { } + DxvkExtensionList::~DxvkExtensionList() { } + + + void DxvkExtensionList::enableExtensions(const vk::NameSet& extensions) { + for (auto ext : m_extensions) { + if (extensions.supports(ext->name())) + ext->setEnabled(true); + } + } + + + bool DxvkExtensionList::checkSupportStatus() { + bool requiredExtensionsEnabled = true; + + for (auto ext : m_extensions) { + if (!ext->enabled()) { + switch (ext->type()) { + case DxvkExtensionType::Optional: + // An optional extension should not have any impact on + // the functionality of an application, so inform the + // user only if verbose debug messages are enabled + Logger::debug(str::format("Optional Vulkan extension ", ext->name(), " not supported")); + break; + + case DxvkExtensionType::Desired: + // If a desired extension is not supported, applications + // should keep working but may exhibit unexpected behaviour, + // so we'll inform the user anyway + Logger::warn(str::format("Vulkan extension ", ext->name(), " not supported")); + break; + + case DxvkExtensionType::Required: + // Do not exit early so we can catch all unsupported extensions. + requiredExtensionsEnabled = false; + Logger::err(str::format("Required Vulkan extension ", ext->name(), " not supported")); + break; + } + } + } + + return requiredExtensionsEnabled; + } + + + vk::NameList DxvkExtensionList::getEnabledExtensionNames() const { + vk::NameList names; + + for (auto ext : m_extensions) { + if (ext->enabled()) + names.add(ext->name()); + } + + return names; + } + + + void DxvkExtensionList::registerExtension(DxvkExtension* extension) { + m_extensions.push_back(extension); + } + + + DxvkExtension::DxvkExtension( + DxvkExtensionList* parent, + const char* name, + DxvkExtensionType type) + : m_name(name), m_type(type), m_enabled(false) { + parent->registerExtension(this); + } + +} \ No newline at end of file diff --git a/src/dxvk/dxvk_extensions.h b/src/dxvk/dxvk_extensions.h new file mode 100644 index 000000000..e86d42715 --- /dev/null +++ b/src/dxvk/dxvk_extensions.h @@ -0,0 +1,138 @@ +#pragma once + +#include "dxvk_include.h" + +namespace dxvk { + + // Forward declarations + class DxvkExtension; + class DxvkExtensionList; + + /** + * \brief Extension type + */ + enum class DxvkExtensionType { + Optional, ///< Nothing will happen if not supported + Desired, ///< A warning will be issued if not supported + Required, ///< Device creation will fail if not supported + }; + + /** + * \brief Vulkan extension list + * + * Convenience class to manage a set of extensions + * which can be either required or optional. + */ + class DxvkExtensionList : public RcObject { + friend class DxvkExtension; + public: + + DxvkExtensionList(); + ~DxvkExtensionList(); + + DxvkExtensionList (const DxvkExtensionList&) = delete; + DxvkExtensionList& operator = (const DxvkExtensionList&) = delete; + + /** + * \brief Enables Vulkan extensions + * + * Marks all extension in the list as enabled. + * \param [in] extensions Supported extensions + */ + void enableExtensions( + const vk::NameSet& extensions); + + /** + * \brief Checks extension support status + * + * Checks whether all required extensions are present + * and logs the name of any unsupported extension. + * \returns \c true if required extensions are present + */ + bool checkSupportStatus(); + + /** + * \brief Creates a list of enabled extensions + * + * The resulting list can be fed into the Vulkan + * structs for device and instance creation. + * \returns Names of enabled Vulkan extensions + */ + vk::NameList getEnabledExtensionNames() const; + + private: + + std::vector m_extensions; + + void registerExtension(DxvkExtension* extension); + + }; + + /** + * \brief Extension class + * + * Stores the name, type and support + * status of a single Vulkan extension. + */ + class DxvkExtension { + friend class DxvkExtensionList; + public: + + DxvkExtension( + DxvkExtensionList* parent, + const char* name, + DxvkExtensionType type); + + DxvkExtension (const DxvkExtension&) = delete; + DxvkExtension& operator = (const DxvkExtension&) = delete; + + /** + * \brief Extension name + * \returns Extension name + */ + const char* name() const { + return m_name; + } + + /** + * \brief Extension type + * \returns Extension type + */ + DxvkExtensionType type() const { + return m_type; + } + + /** + * \brief Extension support status + * \returns \c true if supported + */ + bool enabled() const { + return m_enabled; + } + + private: + + const char* m_name; + DxvkExtensionType m_type; + bool m_enabled; + + void setEnabled(bool enabled) { + m_enabled = enabled; + } + + }; + + /** + * \brief Device extensions + * + * Lists all Vulkan extensions that are potentially + * used by DXVK if supported by the implementation. + */ + struct DxvkDeviceExtensions : public DxvkExtensionList { + DxvkExtension khrMaintenance1 = { this, VK_KHR_MAINTENANCE1_EXTENSION_NAME, DxvkExtensionType::Required }; + DxvkExtension khrMaintenance2 = { this, VK_KHR_MAINTENANCE2_EXTENSION_NAME, DxvkExtensionType::Desired }; + DxvkExtension khrShaderDrawParameters = { this, VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME, DxvkExtensionType::Required }; + DxvkExtension khrSwapchain = { this, VK_KHR_SWAPCHAIN_EXTENSION_NAME, DxvkExtensionType::Required }; + }; + +} \ No newline at end of file diff --git a/src/dxvk/meson.build b/src/dxvk/meson.build index 2694bf88f..687b7eb20 100644 --- a/src/dxvk/meson.build +++ b/src/dxvk/meson.build @@ -14,6 +14,7 @@ dxvk_src = files([ 'dxvk_data.cpp', 'dxvk_descriptor.cpp', 'dxvk_device.cpp', + 'dxvk_extensions.cpp', 'dxvk_format.cpp', 'dxvk_framebuffer.cpp', 'dxvk_graphics.cpp',