mirror of https://github.com/mosra/magnum.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
390 lines
16 KiB
390 lines
16 KiB
/* |
|
This file is part of Magnum. |
|
|
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
|
2020, 2021 Vladimír Vondruš <mosra@centrum.cz> |
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a |
|
copy of this software and associated documentation files (the "Software"), |
|
to deal in the Software without restriction, including without limitation |
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
|
and/or sell copies of the Software, and to permit persons to whom the |
|
Software is furnished to do so, subject to the following conditions: |
|
|
|
The above copyright notice and this permission notice shall be included |
|
in all copies or substantial portions of the Software. |
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
|
DEALINGS IN THE SOFTWARE. |
|
*/ |
|
|
|
#include "Instance.h" |
|
#include "InstanceCreateInfo.h" |
|
|
|
#include <algorithm> |
|
#include <Corrade/Containers/Optional.h> |
|
#include <Corrade/Containers/GrowableArray.h> |
|
#include <Corrade/Containers/String.h> |
|
#include <Corrade/Containers/StringView.h> |
|
#include <Corrade/Utility/Arguments.h> |
|
|
|
#include "Magnum/Vk/Assert.h" |
|
#include "Magnum/Vk/Extensions.h" |
|
#include "Magnum/Vk/ExtensionProperties.h" |
|
#include "Magnum/Vk/Handle.h" |
|
#include "Magnum/Vk/Result.h" |
|
#include "Magnum/Vk/Version.h" |
|
#include "Magnum/Vk/Implementation/Arguments.h" |
|
#include "Magnum/Vk/Implementation/InstanceState.h" |
|
#include "MagnumExternal/Vulkan/flextVkGlobal.h" |
|
|
|
namespace Magnum { namespace Vk { |
|
|
|
struct InstanceCreateInfo::State { |
|
Containers::String applicationName; |
|
Containers::Array<Containers::String> ownedStrings; |
|
Containers::Array<const char*> layers; |
|
Containers::Array<const char*> extensions; |
|
|
|
Containers::String disabledLayersStorage, disabledExtensionsStorage; |
|
Containers::Array<Containers::StringView> disabledLayers, disabledExtensions; |
|
bool quietLog = false; |
|
Version version = Version::None; |
|
Int argc; |
|
const char** argv; |
|
}; |
|
|
|
InstanceCreateInfo::InstanceCreateInfo(const Int argc, const char** const argv, const LayerProperties* const layerProperties, const InstanceExtensionProperties* extensionProperties, const Flags flags): _info{}, _applicationInfo{} { |
|
Utility::Arguments args = Implementation::arguments(); |
|
args.parse(argc, argv); |
|
|
|
if(args.value("log") == "quiet") |
|
_state.emplace().quietLog = true; |
|
if(argc && argv) { |
|
if(!_state) _state.emplace(); |
|
_state->argc = argc; |
|
_state->argv = argv; |
|
} |
|
|
|
_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; |
|
_info.flags = VkInstanceCreateFlags(flags & ~Flag::NoImplicitExtensions); |
|
_info.pApplicationInfo = &_applicationInfo; |
|
_applicationInfo.pEngineName = "Magnum"; |
|
/** @todo magnum version? 2020 can't fit into Vulkan's version |
|
representation, sigh */ |
|
|
|
/* If there's a forced Vulkan version, use that, otherwise use the reported |
|
instance version */ |
|
Containers::StringView version = args.value<Containers::StringView>("vulkan-version"); |
|
if(!version.isEmpty()) { |
|
if(!_state) _state.emplace(); |
|
|
|
if((_state->version = args.value<Version>("vulkan-version")) == Version::None) |
|
Warning{} << "Invalid --magnum-vulkan-version" << args.value<Containers::StringView>("vulkan-version") << Debug::nospace << ", ignoring"; |
|
} |
|
if(_state && _state->version == Version::None) |
|
_state->version = enumerateInstanceVersion(); |
|
_applicationInfo.apiVersion = UnsignedInt(_state ? _state->version : enumerateInstanceVersion()); |
|
|
|
/** @todo handle disabled workarounds once we need them on the instance |
|
level as well -- also ensure that warnings about unknown workarounds are |
|
printed just once (silence the output on one side), or not? since we're |
|
printing two lists anyway */ |
|
|
|
/* If there are any disabled layers or extensions, sort them and save for |
|
later -- we'll use them to filter the ones added by the app */ |
|
Containers::String disabledLayers = args.value<Containers::String>("disable-layers"); |
|
Containers::String disabledExtensions = args.value<Containers::String>("disable-extensions"); |
|
if(!disabledLayers.isEmpty()) { |
|
if(!_state) _state.emplace(); |
|
|
|
_state->disabledLayersStorage = std::move(disabledLayers); |
|
_state->disabledLayers = Containers::StringView{_state->disabledLayersStorage}.splitWithoutEmptyParts(); |
|
std::sort(_state->disabledLayers.begin(), _state->disabledLayers.end()); |
|
} |
|
if(!disabledExtensions.isEmpty()) { |
|
if(!_state) _state.emplace(); |
|
|
|
_state->disabledExtensionsStorage = std::move(disabledExtensions); |
|
_state->disabledExtensions = Containers::StringView{_state->disabledExtensionsStorage}.splitWithoutEmptyParts(); |
|
std::sort(_state->disabledExtensions.begin(), _state->disabledExtensions.end()); |
|
} |
|
|
|
/* Add all layers and extensions enabled on command-line. The blacklist is |
|
applied on those as well. */ |
|
/** @todo use a generator split() so we can avoid the growing allocation |
|
of the output array */ |
|
/** @todo unfortunately even though the split and value retrieval is mostly |
|
allocation-free, the strings will be turned into owning copies because |
|
none of them is null-terminated or global -- could be a better idea to |
|
just grow one giant string internally (once we have growable strings) */ |
|
addEnabledLayers(args.value<Containers::StringView>("enable-layers").splitWithoutEmptyParts()); |
|
addEnabledExtensions(args.value<Containers::StringView>("enable-instance-extensions").splitWithoutEmptyParts()); |
|
|
|
/** @todo use this (enabling debug layers etc.) */ |
|
static_cast<void>(layerProperties); |
|
|
|
/* Enable implicit extensions unless that's forbidden */ |
|
/** @todo move this somewhere else as this will grow significantly? */ |
|
if(!(flags & Flag::NoImplicitExtensions)) { |
|
if(!_state) _state.emplace(); |
|
|
|
/* Fetch searchable extension properties if not already */ |
|
Containers::Optional<InstanceExtensionProperties> extensionPropertiesStorage; |
|
if(!extensionProperties) { |
|
extensionPropertiesStorage = enumerateInstanceExtensionProperties(); |
|
extensionProperties = &*extensionPropertiesStorage; |
|
} |
|
|
|
/* Only if we don't have Vulkan 1.1, on which this is core */ |
|
if(_state->version < Version::Vk11 && extensionProperties->isSupported<Extensions::KHR::get_physical_device_properties2>()) |
|
addEnabledExtensions<Extensions::KHR::get_physical_device_properties2>(); |
|
} |
|
} |
|
|
|
InstanceCreateInfo::InstanceCreateInfo(NoInitT) noexcept {} |
|
|
|
InstanceCreateInfo::InstanceCreateInfo(const VkInstanceCreateInfo& info): |
|
/* Can't use {} with GCC 4.8 here because it tries to initialize the first |
|
member instead of doing a copy */ |
|
_info(info) {} |
|
|
|
InstanceCreateInfo::~InstanceCreateInfo() = default; |
|
|
|
InstanceCreateInfo& InstanceCreateInfo::setApplicationInfo(const Containers::StringView name, const Version version) { |
|
/* Keep an owned copy of the name if it's not global / null-terminated; |
|
Use nullptr if the view is empty */ |
|
if(!name.isEmpty()) { |
|
if(!_state) _state.emplace(); |
|
|
|
_state->applicationName = Containers::String::nullTerminatedGlobalView(name); |
|
_applicationInfo.pApplicationName = _state->applicationName.data(); |
|
} else { |
|
if(_state) _state->applicationName = nullptr; |
|
_applicationInfo.pApplicationName = nullptr; |
|
} |
|
|
|
_applicationInfo.applicationVersion = UnsignedInt(version); |
|
return *this; |
|
} |
|
|
|
InstanceCreateInfo& InstanceCreateInfo::addEnabledLayers(const Containers::ArrayView<const Containers::StringView> layers) { |
|
if(layers.empty()) return *this; |
|
if(!_state) _state.emplace(); |
|
|
|
/* Add null-terminated strings to the layer array */ |
|
arrayReserve(_state->layers, _state->layers.size() + layers.size()); |
|
for(const Containers::StringView layer: layers) { |
|
/* If the layer is blacklisted, skip it */ |
|
if(std::binary_search(_state->disabledLayers.begin(), _state->disabledLayers.end(), layer)) continue; |
|
|
|
/* Keep an owned *allocated* copy of the string if it's not global or |
|
null-terminated -- ideally, if people use string view literals, |
|
those will be, so this won't allocate. Allocated so the pointers |
|
don't get invalidated when the array gets reallocated. */ |
|
const char* data; |
|
if(!(layer.flags() >= (Containers::StringViewFlag::NullTerminated|Containers::StringViewFlag::Global))) |
|
data = arrayAppend(_state->ownedStrings, Containers::InPlaceInit, |
|
Containers::AllocatedInit, layer).data(); |
|
else data = layer.data(); |
|
|
|
arrayAppend(_state->layers, data); |
|
} |
|
|
|
/* Update the layer count, re-route the pointer to the layers array in case |
|
it got reallocated */ |
|
_info.enabledLayerCount = _state->layers.size(); |
|
_info.ppEnabledLayerNames = _state->layers.data(); |
|
return *this; |
|
} |
|
|
|
InstanceCreateInfo& InstanceCreateInfo::addEnabledLayers(const std::initializer_list<Containers::StringView> layers) { |
|
return addEnabledLayers(Containers::arrayView(layers)); |
|
} |
|
|
|
InstanceCreateInfo& InstanceCreateInfo::addEnabledExtensions(const Containers::ArrayView<const Containers::StringView> extensions) { |
|
if(extensions.empty()) return *this; |
|
if(!_state) _state.emplace(); |
|
|
|
/* Add null-terminated strings to the extension array */ |
|
arrayReserve(_state->extensions, _state->extensions.size() + extensions.size()); |
|
for(const Containers::StringView extension: extensions) { |
|
/* If the extension is blacklisted, skip it */ |
|
if(std::binary_search(_state->disabledExtensions.begin(), _state->disabledExtensions.end(), extension)) continue; |
|
|
|
/* Keep an owned *allocated* copy of the string if it's not global or |
|
null-terminated -- ideally, if people use string view literals, |
|
those will be, so this won't allocate. Allocated so the pointers |
|
don't get invalidated when the array gets reallocated. */ |
|
const char* data; |
|
if(!(extension.flags() >= (Containers::StringViewFlag::NullTerminated|Containers::StringViewFlag::Global))) |
|
data = arrayAppend(_state->ownedStrings, Containers::InPlaceInit, |
|
Containers::AllocatedInit, extension).data(); |
|
else data = extension.data(); |
|
|
|
arrayAppend(_state->extensions, data); |
|
} |
|
|
|
/* Update the extension count, re-route the pointer to the layers array in |
|
case it got reallocated */ |
|
_info.enabledExtensionCount = _state->extensions.size(); |
|
_info.ppEnabledExtensionNames = _state->extensions.data(); |
|
return *this; |
|
} |
|
|
|
InstanceCreateInfo& InstanceCreateInfo::addEnabledExtensions(const std::initializer_list<Containers::StringView> extensions) { |
|
return addEnabledExtensions(Containers::arrayView(extensions)); |
|
} |
|
|
|
InstanceCreateInfo& InstanceCreateInfo::addEnabledExtensions(const Containers::ArrayView<const InstanceExtension> extensions) { |
|
if(extensions.empty()) return *this; |
|
if(!_state) _state.emplace(); |
|
|
|
arrayReserve(_state->extensions, _state->extensions.size() + extensions.size()); |
|
for(const InstanceExtension& extension: extensions) { |
|
/* If the extension is blacklisted, skip it */ |
|
if(std::binary_search(_state->disabledExtensions.begin(), _state->disabledExtensions.end(), extension.string())) continue; |
|
|
|
arrayAppend(_state->extensions, extension.string().data()); |
|
} |
|
|
|
/* Update the extension count, re-route the pointer to the layers array in |
|
case it got reallocated */ |
|
_info.enabledExtensionCount = _state->extensions.size(); |
|
_info.ppEnabledExtensionNames = _state->extensions.data(); |
|
return *this; |
|
} |
|
|
|
InstanceCreateInfo& InstanceCreateInfo::addEnabledExtensions(const std::initializer_list<InstanceExtension> extensions) { |
|
return addEnabledExtensions(Containers::arrayView(extensions)); |
|
} |
|
|
|
void Instance::wrap(const VkInstance handle, const Version version, const Containers::ArrayView<const Containers::StringView> enabledExtensions, const HandleFlags flags) { |
|
CORRADE_ASSERT(!_handle, |
|
"Vk::Instance::wrap(): instance already created", ); |
|
|
|
/* Compared to the constructor nothing is printed here as it would be just |
|
repeating what was passed to the constructor */ |
|
_handle = handle; |
|
_flags = flags; |
|
initializeExtensions(enabledExtensions); |
|
initialize(version, 0, nullptr); |
|
} |
|
|
|
void Instance::wrap(const VkInstance handle, const Version version, const std::initializer_list<Containers::StringView> enabledExtensions, const HandleFlags flags) { |
|
wrap(handle, version, Containers::arrayView(enabledExtensions), flags); |
|
} |
|
|
|
Instance::Instance(const InstanceCreateInfo& info): Instance{NoCreate} { |
|
create(info); |
|
} |
|
|
|
Instance::Instance(): Instance{NoCreate} { |
|
create(); |
|
} |
|
|
|
Instance::Instance(NoCreateT): _handle{}, _functionPointers{} {} |
|
|
|
Instance::~Instance() { |
|
if(_handle && (_flags & HandleFlag::DestroyOnDestruction)) |
|
_functionPointers.DestroyInstance(_handle, nullptr); |
|
} |
|
|
|
void Instance::create(const InstanceCreateInfo& info) { |
|
if(tryCreate(info) != Result::Success) std::exit(1); |
|
} |
|
|
|
void Instance::create() { |
|
if(tryCreate() != Result::Success) std::exit(1); |
|
} |
|
|
|
Result Instance::tryCreate(const InstanceCreateInfo& info) { |
|
CORRADE_ASSERT(!_handle, |
|
"Vk::Instance::tryCreate(): instance already created", {}); |
|
|
|
_flags = HandleFlag::DestroyOnDestruction; |
|
|
|
const Version version = info._state && info._state->version != Version::None ? info._state->version : enumerateInstanceVersion(); |
|
|
|
/* Print all enabled layers and extensions if we're not told to be quiet */ |
|
if(!info._state || !info._state->quietLog) { |
|
Debug{} << "Instance version:" << version; |
|
|
|
if(info->enabledLayerCount) { |
|
Debug{} << "Enabled layers:"; |
|
for(std::size_t i = 0, max = info->enabledLayerCount; i != max; ++i) |
|
Debug{} << " " << info->ppEnabledLayerNames[i]; |
|
} |
|
|
|
if(info->enabledExtensionCount) { |
|
Debug{} << "Enabled instance extensions:"; |
|
for(std::size_t i = 0, max = info->enabledExtensionCount; i != max; ++i) |
|
Debug{} << " " << info->ppEnabledExtensionNames[i]; |
|
} |
|
} |
|
|
|
if(const VkResult result = vkCreateInstance(info, nullptr, &_handle)) { |
|
Error{} << "Vk::Instance::tryCreate(): instance creation failed:" << Result(result); |
|
return Result(result); |
|
} |
|
|
|
initializeExtensions<const char*>({info->ppEnabledExtensionNames, info->enabledExtensionCount}); |
|
if(info._state) |
|
initialize(version, info._state->argc, info._state->argv); |
|
else |
|
initialize(version, 0, nullptr); |
|
|
|
return Result::Success; |
|
} |
|
|
|
Result Instance::tryCreate() { |
|
return tryCreate(InstanceCreateInfo{}); |
|
} |
|
|
|
template<class T> void Instance::initializeExtensions(const Containers::ArrayView<const T> enabledExtensions) { |
|
/* Mark all known extensions as enabled */ |
|
for(const T extension: enabledExtensions) { |
|
for(Containers::ArrayView<const InstanceExtension> knownExtensions: { |
|
InstanceExtension::extensions(Version::None), |
|
/*InstanceExtension::extensions(Version::Vk10), is empty */ |
|
InstanceExtension::extensions(Version::Vk11), |
|
/*InstanceExtension::extensions(Version::Vk12) is empty */ |
|
}) { |
|
const auto found = std::lower_bound(knownExtensions.begin(), knownExtensions.end(), extension, [](const InstanceExtension& a, const T& b) { |
|
return a.string() < static_cast<const Containers::StringView&>(b); |
|
}); |
|
if(found->string() != extension) continue; |
|
_extensionStatus.set(found->index(), true); |
|
} |
|
} |
|
} |
|
|
|
void Instance::initialize(const Version version, const Int argc, const char** const argv) { |
|
/* Init version, function pointers */ |
|
_version = version; |
|
flextVkInitInstance(_handle, &_functionPointers); |
|
|
|
/* Set up extension-dependent functionality */ |
|
_state.emplace(*this, argc, argv); |
|
} |
|
|
|
VkInstance Instance::release() { |
|
const VkInstance handle = _handle; |
|
_handle = nullptr; |
|
return handle; |
|
} |
|
|
|
bool Instance::isExtensionEnabled(const InstanceExtension& extension) const { |
|
return _extensionStatus[extension.index()]; |
|
} |
|
|
|
void Instance::populateGlobalFunctionPointers() { |
|
flextVkInstance = _functionPointers; |
|
} |
|
|
|
}}
|
|
|