diff --git a/src/Magnum/Vk/DeviceProperties.cpp b/src/Magnum/Vk/DeviceProperties.cpp index 7bbef6ea0..54ebb33fd 100644 --- a/src/Magnum/Vk/DeviceProperties.cpp +++ b/src/Magnum/Vk/DeviceProperties.cpp @@ -26,13 +26,16 @@ #include "DeviceProperties.h" #include +#include #include +#include #include #include "Magnum/Vk/Instance.h" #include "Magnum/Vk/ExtensionProperties.h" #include "Magnum/Vk/LayerProperties.h" #include "Magnum/Vk/Result.h" +#include "Magnum/Vk/Implementation/Arguments.h" #include "Magnum/Vk/Implementation/InstanceState.h" namespace Magnum { namespace Vk { @@ -114,6 +117,59 @@ Containers::Array enumerateDevices(Instance& instance) { return out; } +Containers::Optional tryPickDevice(Instance& instance) { + Utility::Arguments args = Implementation::arguments(); + args.parse(instance.state().argc, instance.state().argv); + + Containers::Array devices = enumerateDevices(instance); + + /* Pick the first by default */ + if(args.value("device").empty()) { + if(devices.empty()) { + Error{} << "Vk::tryPickDevice(): no Vulkan devices found"; + return {}; + } + return std::move(devices.front()); + } + + /* Pick by ID */ + if(args.value("device")[0] >= '0' && args.value("device")[0] <= '9') { + UnsignedInt id = args.value("device"); + if(id >= devices.size()) { + Error{} << "Vk::tryPickDevice(): index" << id << "out of bounds for" << devices.size() << "Vulkan devices"; + return {}; + } + return std::move(devices[id]); + } + + /* Pick by type */ + DeviceType type; + if(args.value("device") == "integrated") + type = DeviceType::IntegratedGpu; + else if(args.value("device") == "discrete") + type = DeviceType::DiscreteGpu; + else if(args.value("device") == "virtual") + type = DeviceType::VirtualGpu; + else if(args.value("device") == "cpu") + type = DeviceType::Cpu; + else { + Error{} << "Vk::tryPickDevice(): unknown Vulkan device type" << args.value("device"); + return {}; + } + + for(DeviceProperties& device: devices) + if(device.type() == type) return std::move(device); + + Error{} << "Vk::tryPickDevice(): no" << type << "found among" << devices.size() << "Vulkan devices"; + return {}; +} + +DeviceProperties pickDevice(Instance& instance) { + Containers::Optional device = tryPickDevice(instance); + if(device) return *std::move(device); + std::exit(1); /* LCOV_EXCL_LINE */ +} + Debug& operator<<(Debug& debug, const DeviceType value) { debug << "Vk::DeviceType" << Debug::nospace; diff --git a/src/Magnum/Vk/DeviceProperties.h b/src/Magnum/Vk/DeviceProperties.h index 27523e128..9ab5caf6d 100644 --- a/src/Magnum/Vk/DeviceProperties.h +++ b/src/Magnum/Vk/DeviceProperties.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class @ref Magnum::Vk::DeviceProperties, enum @ref Magnum::Vk::DeviceType, function @ref Magnum::Vk::enumerateDevices() + * @brief Class @ref Magnum::Vk::DeviceProperties, enum @ref Magnum::Vk::DeviceType, function @ref Magnum::Vk::enumerateDevices(), @ref Magnum::Vk::pickDevice(), @ref Magnum::Vk::tryPickDevice() * @m_since_latest */ @@ -86,7 +86,7 @@ MAGNUM_VK_EXPORT Debug& operator<<(Debug& debug, DeviceType value); Wraps a @type_vk_keyword{PhysicalDevice} along with its (lazy-populated) properties. -@see @ref enumeratePhysicalDevices() +@see @ref enumerateDevices() */ class MAGNUM_VK_EXPORT DeviceProperties { public: @@ -228,6 +228,26 @@ class MAGNUM_VK_EXPORT DeviceProperties { */ MAGNUM_VK_EXPORT Containers::Array enumerateDevices(Instance& instance); +/** +@brief Pick a physical device +@m_since_latest + +Calls @ref enumerateDevices() and selects a device based on preferences +specified through command-line parameters or the environment. If a device is +not found, exits. See @ref tryPickDevice() for an alternative that doesn't exit +on failure. +*/ +MAGNUM_VK_EXPORT DeviceProperties pickDevice(Instance& instance); + +/** +@brief Try to pick a physical device +@m_since_latest + +Compared to @ref pickDevice() the function returns @ref Containers::NullOpt if +a device isn't found instead of exiting. +*/ +MAGNUM_VK_EXPORT Containers::Optional tryPickDevice(Instance& instance); + }} #endif diff --git a/src/Magnum/Vk/Implementation/Arguments.cpp b/src/Magnum/Vk/Implementation/Arguments.cpp index 1b64c25f0..a9fae5c73 100644 --- a/src/Magnum/Vk/Implementation/Arguments.cpp +++ b/src/Magnum/Vk/Implementation/Arguments.cpp @@ -37,12 +37,14 @@ Utility::Arguments arguments() { .addOption("enable-instance-extensions").setHelp("enable-instance-extensions", "Vulkan instance extensions to enable in addition to the defaults and what the application requests", "LIST") .addOption("vulkan-version").setHelp("vulkan-version", "force Vulkan version", "X.Y") .addOption("log", "default").setHelp("log", "console logging", "default|quiet|verbose") + .addOption("device").setHelp("device", "device to use", "ID|integrated|discrete|virtual|cpu") .setFromEnvironment("disable-layers") .setFromEnvironment("disable-extensions") .setFromEnvironment("enable-layers") .setFromEnvironment("enable-instance-extensions") .setFromEnvironment("vulkan-version") - .setFromEnvironment("log"); + .setFromEnvironment("log") + .setFromEnvironment("device"); return args; } diff --git a/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp b/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp index f11848af8..2a6d6f765 100644 --- a/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp +++ b/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp @@ -25,11 +25,13 @@ #include #include +#include #include #include #include #include #include +#include #include "Magnum/Vk/DeviceProperties.h" #include "Magnum/Vk/Extensions.h" @@ -58,9 +60,27 @@ struct DevicePropertiesVkTest: TestSuite::Tester { void extensionIsSupported(); void extensionNamedRevision(); + void pickDevice(); + void pickDeviceIndex(); + void pickDeviceType(); + void pickDeviceError(); + Instance _instance; }; +const struct { + const char* name; + Containers::Array args; + const char* message; +} PickDeviceErrorData[] { + {"nothing for type found", Containers::array({"", "--magnum-device", "virtual"}), + "Vk::tryPickDevice(): no Vk::DeviceType::VirtualGpu found among {} Vulkan devices\n"}, + {"index out of bounds", Containers::array({"", "--magnum-device", "666"}), + "Vk::tryPickDevice(): index 666 out of bounds for {} Vulkan devices\n"}, + {"unknown type", Containers::array({"", "--magnum-device", "FAST"}), + "Vk::tryPickDevice(): unknown Vulkan device type FAST\n"} +}; + DevicePropertiesVkTest::DevicePropertiesVkTest(): _instance{InstanceCreateInfo{arguments().first, arguments().second}} { addTests({&DevicePropertiesVkTest::enumerate, &DevicePropertiesVkTest::constructMove, @@ -72,7 +92,14 @@ DevicePropertiesVkTest::DevicePropertiesVkTest(): _instance{InstanceCreateInfo{a &DevicePropertiesVkTest::extensionConstructMove, &DevicePropertiesVkTest::extensionIsSupported, - &DevicePropertiesVkTest::extensionNamedRevision}); + &DevicePropertiesVkTest::extensionNamedRevision, + + &DevicePropertiesVkTest::pickDevice, + &DevicePropertiesVkTest::pickDeviceIndex, + &DevicePropertiesVkTest::pickDeviceType}); + + addInstancedTests({&DevicePropertiesVkTest::pickDeviceError}, + Containers::arraySize(PickDeviceErrorData)); } void DevicePropertiesVkTest::enumerate() { @@ -227,6 +254,53 @@ void DevicePropertiesVkTest::extensionNamedRevision() { TestSuite::Compare::GreaterOrEqual); } +void DevicePropertiesVkTest::pickDevice() { + /* Default behavior */ + Containers::Optional device = tryPickDevice(_instance); + CORRADE_VERIFY(device); +} + +void DevicePropertiesVkTest::pickDeviceIndex() { + Containers::Array devices = enumerateDevices(_instance); + CORRADE_VERIFY(!devices.empty()); + + /* Pick the last one */ + CORRADE_COMPARE_AS(devices.size(), 10, TestSuite::Compare::Less); + const char id[] {char('0' + devices.size() - 1), '\0'}; + const char* argv[] {"", "--magnum-device", id}; + + /* Creating another dedicated instance so we can pass custom args */ + Instance instance{InstanceCreateInfo{Int(Containers::arraySize(argv)), argv}}; + + Containers::Optional device = tryPickDevice(instance); + CORRADE_VERIFY(device); +} + +void DevicePropertiesVkTest::pickDeviceType() { + const char* argv[] {"", "--magnum-device", "cpu"}; + + /* Creating a dedicated instance so we can pass custom args */ + Instance instance{InstanceCreateInfo{Int(Containers::arraySize(argv)), argv}}; + + Containers::Optional device = tryPickDevice(instance); + if(!device) CORRADE_SKIP("No CPU device found."); + + CORRADE_VERIFY(device->type() == DeviceType::Cpu); +} + +void DevicePropertiesVkTest::pickDeviceError() { + auto&& data = PickDeviceErrorData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + /* Creating a dedicated instance so we can pass custom args */ + Instance instance{InstanceCreateInfo{Int(data.args.size()), const_cast(data.args.data())}}; + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!tryPickDevice(instance)); + CORRADE_COMPARE(out.str(), Utility::formatString(data.message, enumerateDevices(_instance).size())); +} + }}}} CORRADE_TEST_MAIN(Magnum::Vk::Test::DevicePropertiesVkTest)