From f75cc9330c363c088526c5530251dbfb216908ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 15 Jul 2020 18:51:49 +0200 Subject: [PATCH] Vk: a non-error-prone way to retrieve queues after device creation. I wonder why this couldn't be done this way in the Vulkan API directly. --- doc/vulkan-mapping.dox | 2 +- src/Magnum/Vk/CMakeLists.txt | 1 + src/Magnum/Vk/Device.cpp | 60 +++++++- src/Magnum/Vk/Device.h | 23 +++- src/Magnum/Vk/Implementation/DeviceState.cpp | 11 +- src/Magnum/Vk/Implementation/DeviceState.h | 3 +- src/Magnum/Vk/Queue.h | 88 ++++++++++++ src/Magnum/Vk/Test/DeviceVkTest.cpp | 136 +++++++++++++++++-- src/Magnum/Vk/Vk.h | 1 + 9 files changed, 299 insertions(+), 26 deletions(-) create mode 100644 src/Magnum/Vk/Queue.h diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index eaea4a38d..a59e50116 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -194,7 +194,7 @@ Vulkan function | Matching API @fn_vk{GetDeviceGroupPeerMemoryFeatures} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @fn_vk{GetDeviceMemoryCommitment} | | @fn_vk{GetDeviceProcAddr} | @ref Device constructor -@fn_vk{GetDeviceQueue}, \n @fn_vk{GetDeviceQueue2} @m_class{m-label m-flat m-success} **1.1** | | +@fn_vk{GetDeviceQueue}, \n @fn_vk{GetDeviceQueue2} @m_class{m-label m-flat m-success} **1.1** | @ref Device constructor @fn_vk{GetEventStatus} | | @fn_vk{GetFenceStatus} | | @fn_vk{GetImageMemoryRequirements}, \n @fn_vk{GetImageMemoryRequirements2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | diff --git a/src/Magnum/Vk/CMakeLists.txt b/src/Magnum/Vk/CMakeLists.txt index 2ea9e44e4..1b1d4cc74 100644 --- a/src/Magnum/Vk/CMakeLists.txt +++ b/src/Magnum/Vk/CMakeLists.txt @@ -54,6 +54,7 @@ set(MagnumVk_HEADERS Instance.h Integration.h LayerProperties.h + Queue.h Result.h TypeTraits.h Version.h diff --git a/src/Magnum/Vk/Device.cpp b/src/Magnum/Vk/Device.cpp index a98411139..dcfad66a1 100644 --- a/src/Magnum/Vk/Device.cpp +++ b/src/Magnum/Vk/Device.cpp @@ -40,6 +40,7 @@ #include "Magnum/Vk/DeviceProperties.h" #include "Magnum/Vk/Extensions.h" #include "Magnum/Vk/ExtensionProperties.h" +#include "Magnum/Vk/Queue.h" #include "Magnum/Vk/Result.h" #include "Magnum/Vk/Version.h" #include "Magnum/Vk/Implementation/Arguments.h" @@ -57,6 +58,7 @@ struct DeviceCreateInfo::State { Containers::Array disabledExtensions; Containers::Array queues; Containers::StaticArray<32, Float> queuePriorities; + Containers::StaticArray<32, Queue*> queueOutput; std::size_t nextQueuePriority = 0; bool quietLog = false; @@ -182,8 +184,9 @@ DeviceCreateInfo& DeviceCreateInfo::addEnabledExtensions(const std::initializer_ return addEnabledExtensions(Containers::arrayView(extensions)); } -DeviceCreateInfo& DeviceCreateInfo::addQueues(UnsignedInt family, Containers::ArrayView priorities) { +DeviceCreateInfo& DeviceCreateInfo::addQueues(const UnsignedInt family, const Containers::ArrayView priorities, const Containers::ArrayView> output) { CORRADE_ASSERT(!priorities.empty(), "Vk::DeviceCreateInfo::addQueues(): at least one queue priority has to be specified", *this); + CORRADE_ASSERT(output.size() == priorities.size(), "Vk::DeviceCreateInfo::addQueues(): expected" << priorities.size() << "outuput queue references but got" << output.size(), *this); /* This can happen in case we used the NoInit or VkDeviceCreateInfo constructor */ @@ -195,19 +198,22 @@ DeviceCreateInfo& DeviceCreateInfo::addQueues(UnsignedInt family, Containers::Ar info.queueCount = priorities.size(); info.pQueuePriorities = _state->queuePriorities + _state->nextQueuePriority; - /* Copy the passed queue priorities to an internal storage that never - reallocates. If this blows up, see the definition of queuePriorities for - details. We can't easily reallocate if this grows too big as all - pointers would need to be patched, so there's a static limit. */ + /* Copy the passed queue priorities and output queue references to an + internal storage that never reallocates. If this blows up, see the + definition of queuePriorities for details. We can't easily reallocate if + this grows too big as all pointers would need to be patched, so there's + a static limit. */ CORRADE_INTERNAL_ASSERT(_state->nextQueuePriority + priorities.size() <= _state->queuePriorities.size()); Utility::copy(priorities, _state->queuePriorities.suffix(_state->nextQueuePriority).prefix(priorities.size())); + for(std::size_t i = 0; i != priorities.size(); ++i) + _state->queueOutput[_state->nextQueuePriority + i] = &*output[i]; _state->nextQueuePriority += priorities.size(); return addQueues(info); } -DeviceCreateInfo& DeviceCreateInfo::addQueues(UnsignedInt family, std::initializer_list priorities) { - return addQueues(family, Containers::arrayView(priorities)); +DeviceCreateInfo& DeviceCreateInfo::addQueues(const UnsignedInt family, const std::initializer_list priorities, const std::initializer_list> output) { + return addQueues(family, Containers::arrayView(priorities), Containers::arrayView(output)); } DeviceCreateInfo& DeviceCreateInfo::addQueues(const VkDeviceQueueCreateInfo& info) { @@ -266,6 +272,38 @@ Device::Device(Instance& instance, const DeviceCreateInfo& info): initializeExtensions({info->ppEnabledExtensionNames, info->enabledExtensionCount}); initialize(instance, version); + + /* Extension-dependent state is initialized, now we can retrieve the queues + from the device and save them to the outputs specified in addQueues(). + Each of those calls added one or more entries into _state->queueOutput, + maintain an offset into it. */ + UnsignedInt queueOutputIndex = 0; + for(const VkDeviceQueueCreateInfo& createInfo: info._state->queues) { + /* If the info structure doesn't point into our priority array, it + means it was added with the addQueues(VkDeviceQueueCreateInfo) + overload. For that we didn't remember any output, thus skip it */ + if(createInfo.pQueuePriorities < info._state->queuePriorities.begin() || + createInfo.pQueuePriorities >= info._state->queuePriorities.end()) + continue; + + for(UnsignedInt i = 0; i != createInfo.queueCount; ++i) { + VkDeviceQueueInfo2 requestInfo{}; + requestInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2; + requestInfo.queueFamilyIndex = createInfo.queueFamilyIndex; + /* According to the spec we can request each family only once, + which means here we don't need to remember the per-family index + across multiple VkDeviceQueueCreateInfos, making the + implementation a bit simpler. */ + requestInfo.queueIndex = i; + + /* Retrieve the queue handle, create a new Queue object in desired + output location, and increment the output location for the next + queue */ + VkQueue queue; + _state->getDeviceQueueImplementation(*this, requestInfo, queue); + *info._state->queueOutput[queueOutputIndex++] = Queue::wrap(*this, queue); + } + } } Device::Device(NoCreateT): _handle{}, _functionPointers{} {} @@ -332,4 +370,12 @@ void Device::populateGlobalFunctionPointers() { flextVkDevice = _functionPointers; } +void Device::getQueueImplementation11(Device& self, const VkDeviceQueueInfo2& info, VkQueue& queue) { + return self->GetDeviceQueue2(self, &info, &queue); +} + +void Device::getQueueImplementationDefault(Device& self, const VkDeviceQueueInfo2& info, VkQueue& queue) { + return self->GetDeviceQueue(self, info.queueFamilyIndex, info.queueIndex, &queue); +} + }} diff --git a/src/Magnum/Vk/Device.h b/src/Magnum/Vk/Device.h index 69a14742d..98ddb292c 100644 --- a/src/Magnum/Vk/Device.h +++ b/src/Magnum/Vk/Device.h @@ -32,6 +32,7 @@ #include #include +#include #include "Magnum/Tags.h" #include "Magnum/Math/BoolVector.h" @@ -169,18 +170,20 @@ class MAGNUM_VK_EXPORT DeviceCreateInfo { /** * @brief Add queues - * @param family Family index, smaller than + * @param[in] family Family index, smaller than * @ref DeviceProperties::queueFamilyCount() - * @param priorities Queue priorities. Size of the array implies how - * many queues to add and has to be at least one. + * @param[in] priorities Queue priorities. Size of the array implies + * how many queues to add and has to be at least one. + * @param[out] output Where to save resulting queues once the + * device is created. Has to have the same sizes as @p priorities. * @return Reference to self (for method chaining) * * At least one queue has to be added. * @see @ref DeviceProperties::pickQueueFamily() */ - DeviceCreateInfo& addQueues(UnsignedInt family, Containers::ArrayView priorities); + DeviceCreateInfo& addQueues(UnsignedInt family, Containers::ArrayView priorities, Containers::ArrayView> output); /** @overload */ - DeviceCreateInfo& addQueues(UnsignedInt family, std::initializer_list priorities); + DeviceCreateInfo& addQueues(UnsignedInt family, std::initializer_list priorities, std::initializer_list> output); /** * @brief Add queues using raw info @@ -255,7 +258,10 @@ class MAGNUM_VK_EXPORT Device { * @param instance Vulkan instance to create the device on * @param info Device creation info * - * @see @fn_vk_keyword{CreateDevice} + * After creating the device requests device queues added via + * @ref DeviceCreateInfo::addQueues(UnsignedInt, Containers::ArrayView, Containers::ArrayView>), populating the @ref Queue references. + * @see @fn_vk_keyword{CreateDevice}, @fn_vk_keyword{GetDeviceQueue2}, + * @fn_vk_keyword{GetDeviceQueue} */ explicit Device(Instance& instance, const DeviceCreateInfo& info); @@ -374,9 +380,14 @@ class MAGNUM_VK_EXPORT Device { Implementation::DeviceState& state() { return *_state; } private: + friend Implementation::DeviceState; + template MAGNUM_VK_LOCAL void initializeExtensions(Containers::ArrayView enabledExtensions); MAGNUM_VK_LOCAL void initialize(Instance& instance, Version version); + MAGNUM_VK_LOCAL static void getQueueImplementationDefault(Device& self, const VkDeviceQueueInfo2& info, VkQueue& queue); + MAGNUM_VK_LOCAL static void getQueueImplementation11(Device& self, const VkDeviceQueueInfo2& info, VkQueue& queue); + VkDevice _handle; HandleFlags _flags; Version _version; diff --git a/src/Magnum/Vk/Implementation/DeviceState.cpp b/src/Magnum/Vk/Implementation/DeviceState.cpp index 7475ec1bd..79d481606 100644 --- a/src/Magnum/Vk/Implementation/DeviceState.cpp +++ b/src/Magnum/Vk/Implementation/DeviceState.cpp @@ -25,8 +25,17 @@ #include "DeviceState.h" +#include "Magnum/Vk/Device.h" +#include "Magnum/Vk/Version.h" + namespace Magnum { namespace Vk { namespace Implementation { -DeviceState::DeviceState(Device&) {} +DeviceState::DeviceState(Device& device) { + if(device.isVersionSupported(Version::Vk11)) { + getDeviceQueueImplementation = &Device::getQueueImplementation11; + } else { + getDeviceQueueImplementation = &Device::getQueueImplementationDefault; + } +} }}} diff --git a/src/Magnum/Vk/Implementation/DeviceState.h b/src/Magnum/Vk/Implementation/DeviceState.h index 1d09020b6..e35075626 100644 --- a/src/Magnum/Vk/Implementation/DeviceState.h +++ b/src/Magnum/Vk/Implementation/DeviceState.h @@ -32,9 +32,10 @@ namespace Magnum { namespace Vk { namespace Implementation { struct DeviceState { explicit DeviceState(Device& instance); + + void(*getDeviceQueueImplementation)(Device&, const VkDeviceQueueInfo2&, VkQueue&); }; }}} #endif - diff --git a/src/Magnum/Vk/Queue.h b/src/Magnum/Vk/Queue.h new file mode 100644 index 000000000..b53a03add --- /dev/null +++ b/src/Magnum/Vk/Queue.h @@ -0,0 +1,88 @@ +#ifndef Magnum_Vk_Queue_h +#define Magnum_Vk_Queue_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + 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. +*/ + +/** @file + * @brief Class @ref Magnum::Vk::Queue + * @m_since_latest + */ + +#include "Magnum/Tags.h" +#include "Magnum/Vk/Vk.h" +#include "Magnum/Vk/Vulkan.h" +#include "Magnum/Vk/visibility.h" + +namespace Magnum { namespace Vk { + +/** +@brief Queue +@m_since_latest + +Wraps a @type_vk_keyword{Queue}. +@see @ref DeviceCreateInfo::addQueues() +*/ +class MAGNUM_VK_EXPORT Queue { + public: + /** + * @brief Wrap existing Vulkan queue + * @param device Vulkan device + * @param handle The @type_vk{Queue} handle + * + * The @p handle is expected to be originating from @p device. Unlike + * with other handle types, the @type_vk{Queue} handles don't have to + * be destroyed at the end, so there's no equivalent of e.g. + * @ref Device::release() or @ref Device::handleFlags(). + */ + static Queue wrap(Device& device, VkQueue handle) { + Queue out{NoCreate}; + out._device = &device; + out._handle = handle; + return out; + } + + /** + * @brief Construct without creating the instance + * + * This is the expected way to create a queue that's later populated + * on @ref Device creation through @ref DeviceCreateInfo::addQueues(). + */ + explicit Queue(NoCreateT): _device{}, _handle{} {} + + /** @brief Underlying @type_vk{Queue} handle */ + VkQueue handle() { return _handle; } + /** @overload */ + operator VkQueue() { return _handle; } + + private: + /* Can't be a reference because of the NoCreate constructor */ + Device* _device; + + VkQueue _handle; +}; + +}} + +#endif diff --git a/src/Magnum/Vk/Test/DeviceVkTest.cpp b/src/Magnum/Vk/Test/DeviceVkTest.cpp index f1c95f052..3656f447a 100644 --- a/src/Magnum/Vk/Test/DeviceVkTest.cpp +++ b/src/Magnum/Vk/Test/DeviceVkTest.cpp @@ -37,6 +37,7 @@ #include "Magnum/Vk/Handle.h" #include "Magnum/Vk/Instance.h" #include "Magnum/Vk/LayerProperties.h" +#include "Magnum/Vk/Queue.h" #include "Magnum/Vk/Result.h" #include "Magnum/Vk/Version.h" @@ -53,11 +54,14 @@ struct DeviceVkTest: TestSuite::Tester { void createInfoExtensions(); void createInfoCopiedStrings(); void createInfoNoQueuePriorities(); + void createInfoWrongQueueOutputCount(); void construct(); void constructExtensions(); void constructExtensionsCommandLineDisable(); void constructExtensionsCommandLineEnable(); + void constructMultipleQueues(); + void constructRawQueue(); void constructMove(); void constructUnknownExtension(); void constructNoQueue(); @@ -125,6 +129,7 @@ DeviceVkTest::DeviceVkTest(): _instance{InstanceCreateInfo{arguments().first, ar &DeviceVkTest::createInfoExtensions, &DeviceVkTest::createInfoCopiedStrings, &DeviceVkTest::createInfoNoQueuePriorities, + &DeviceVkTest::createInfoWrongQueueOutputCount, &DeviceVkTest::construct, &DeviceVkTest::constructExtensions}); @@ -133,7 +138,10 @@ DeviceVkTest::DeviceVkTest(): _instance{InstanceCreateInfo{arguments().first, ar &DeviceVkTest::constructExtensionsCommandLineEnable}, Containers::arraySize(ConstructCommandLineData)); - addTests({&DeviceVkTest::constructMove, + addTests({&DeviceVkTest::constructMultipleQueues, + &DeviceVkTest::constructRawQueue, + + &DeviceVkTest::constructMove, &DeviceVkTest::constructUnknownExtension, &DeviceVkTest::constructNoQueue, @@ -220,18 +228,31 @@ void DeviceVkTest::createInfoNoQueuePriorities() { std::ostringstream out; Error redirectError{&out}; - DeviceCreateInfo{_instance}.addQueues(0, {}); + DeviceCreateInfo{_instance}.addQueues(0, {}, {}); CORRADE_COMPARE(out.str(), "Vk::DeviceCreateInfo::addQueues(): at least one queue priority has to be specified\n"); } +void DeviceVkTest::createInfoWrongQueueOutputCount() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + std::ostringstream out; + Error redirectError{&out}; + Queue a{NoCreate}, b{NoCreate}; + DeviceCreateInfo{_instance}.addQueues(0, {0.0f, 1.0f, 0.3f}, {a, b}); + CORRADE_COMPARE(out.str(), "Vk::DeviceCreateInfo::addQueues(): expected 3 outuput queue references but got 2\n"); +} + void DeviceVkTest::construct() { if(std::getenv("MAGNUM_VULKAN_VERSION")) CORRADE_SKIP("Can't test with the MAGNUM_VULKAN_VERSION environment variable set"); { DeviceProperties deviceProperties = pickDevice(_instance); + Queue queue{NoCreate}; Device device{_instance, DeviceCreateInfo{deviceProperties} - .addQueues(0, {0.0f}) + .addQueues(0, {0.0f}, {queue}) }; CORRADE_VERIFY(device.handle()); /* Device function pointers should be populated */ @@ -245,6 +266,9 @@ void DeviceVkTest::construct() { CORRADE_VERIFY(!device.isExtensionEnabled()); /* ... and thus also no function pointers loaded */ CORRADE_VERIFY(!device->CmdDebugMarkerInsertEXT); + + /* The queue should be also filled in */ + CORRADE_VERIFY(queue.handle()); } /* Shouldn't crash or anything */ @@ -273,8 +297,9 @@ void DeviceVkTest::constructExtensions() { if(!extensions.isSupported()) CORRADE_SKIP("VK_KHR_maintenance1 not supported, can't test"); + Queue queue{NoCreate}; Device device{instance, DeviceCreateInfo{deviceProperties} - .addQueues(0, {0.0f}) + .addQueues(0, {0.0f}, {queue}) .addEnabledExtensions({ Extensions::EXT::debug_marker::string(), "VK_KHR_maintenance1"_s @@ -320,8 +345,9 @@ void DeviceVkTest::constructExtensionsCommandLineDisable() { std::ostringstream out; Debug redirectOutput{&out}; + Queue queue{NoCreate}; Device device{instance, DeviceCreateInfo{deviceProperties, DeviceCreateInfo::Flag::NoImplicitExtensions} - .addQueues(0, {0.0f}) + .addQueues(0, {0.0f}, {queue}) .addEnabledExtensions< Extensions::EXT::debug_marker, Extensions::KHR::maintenance1 @@ -375,8 +401,9 @@ void DeviceVkTest::constructExtensionsCommandLineEnable() { std::ostringstream out; Debug redirectOutput{&out}; + Queue queue{NoCreate}; Device device{instance, DeviceCreateInfo{instance, DeviceCreateInfo::Flag::NoImplicitExtensions} - .addQueues(0, {0.0f}) + .addQueues(0, {0.0f}, {queue}) /* Nothing enabled by the application */ }; CORRADE_VERIFY(device.handle()); @@ -397,14 +424,97 @@ void DeviceVkTest::constructExtensionsCommandLineEnable() { CORRADE_COMPARE(!!device->TrimCommandPoolKHR, data.maintenance1Enabled); } +void DeviceVkTest::constructMultipleQueues() { + /* Find a GPU that has at least two queue families and at least four + queues in one family */ + Containers::Array deviceProperties = enumerateDevices(_instance); + + DeviceProperties* deviceWithMultipleQueues = nullptr; + UnsignedInt largeFamily = ~UnsignedInt{}; + for(DeviceProperties& i: deviceProperties) { + if(i.queueFamilyCount() < 2) continue; + for(UnsignedInt family = 0; family != i.queueFamilyCount(); ++family) { + if(i.queueFamilySize(family) < 4) continue; + largeFamily = family; + break; + } + + deviceWithMultipleQueues = &i; + break; + } + + if(!deviceWithMultipleQueues || largeFamily == ~UnsignedInt{}) + CORRADE_SKIP("No device with at least two queue families and at least four queues in one family found, can't test"); + + const UnsignedInt otherFamily = largeFamily == 0 ? 1 : 0; + + constexpr Float zero = 0.0f; + VkDeviceQueueCreateInfo rawQueueInfo{}; + rawQueueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + rawQueueInfo.pQueuePriorities = &zero; + rawQueueInfo.queueFamilyIndex = otherFamily; + rawQueueInfo.queueCount = 1; + + Queue a{NoCreate}, b{NoCreate}, c{NoCreate}; + Device device{_instance, DeviceCreateInfo{*deviceWithMultipleQueues} + /* Request a raw queue in the middle of it all to test we skip it when + populating the outputs, and correctly offset the next IDs. According + to the spec we can request each family only once, which makes the + implementation and testing slightly simpler. */ + .addQueues(rawQueueInfo) + /* Request multiple queues in a single family to test we correctly loop + over these as well */ + .addQueues(largeFamily, {0.5f, 0.75f, 1.0f}, {a, b, c})}; + + /* All queues should be found and different */ + CORRADE_VERIFY(a.handle()); + CORRADE_VERIFY(b.handle()); + CORRADE_VERIFY(c.handle()); + CORRADE_VERIFY(a.handle() != b.handle()); + CORRADE_VERIFY(a.handle() != c.handle()); + CORRADE_VERIFY(b.handle() != c.handle()); + + /* Fetching the same queue again should give the same handle */ + VkQueue aAgain; + device->GetDeviceQueue(device, largeFamily, 0, &aAgain); + CORRADE_COMPARE(aAgain, a.handle()); + + /* Fetch the raw queue, should be different from everything else as well */ + VkQueue rawQueue; + device->GetDeviceQueue(device, otherFamily, 0, &rawQueue); + CORRADE_VERIFY(rawQueue); + CORRADE_VERIFY(rawQueue != a.handle()); + CORRADE_VERIFY(rawQueue != b.handle()); + CORRADE_VERIFY(rawQueue != c.handle()); +} + +void DeviceVkTest::constructRawQueue() { + /* Testing a subset of constructQueues() again because not all drivers + have multiple queues and we want to have the coverage */ + constexpr Float zero = 0.0f; + VkDeviceQueueCreateInfo rawQueueInfo{}; + rawQueueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + rawQueueInfo.pQueuePriorities = &zero; + rawQueueInfo.queueFamilyIndex = 0; + rawQueueInfo.queueCount = 1; + Device device{_instance, DeviceCreateInfo{_instance} + .addQueues(rawQueueInfo)}; + + /* Fetch the raw queue */ + VkQueue rawQueue; + device->GetDeviceQueue(device, 0, 0, &rawQueue); + CORRADE_VERIFY(rawQueue); +} + void DeviceVkTest::constructMove() { DeviceProperties deviceProperties = pickDevice(_instance); ExtensionProperties extensions = deviceProperties.enumerateExtensionProperties(); if(!extensions.isSupported()) CORRADE_SKIP("VK_KHR_maintenance1 not supported, can't test"); + Queue queue{NoCreate}; Device a{_instance, DeviceCreateInfo{deviceProperties} - .addQueues(0, {0.0f}) + .addQueues(0, {0.0f}, {queue}) .addEnabledExtensions() }; VkDevice handle = a.handle(); @@ -444,8 +554,9 @@ void DeviceVkTest::constructUnknownExtension() { std::ostringstream out; Error redirectError{&out}; + Queue queue{NoCreate}; Device device{_instance, DeviceCreateInfo{_instance} - .addQueues(0, {0.0f}) + .addQueues(0, {0.0f}, {queue}) .addEnabledExtensions({"VK_this_doesnt_exist"_s})}; CORRADE_COMPARE(out.str(), "TODO"); } @@ -487,15 +598,19 @@ void DeviceVkTest::wrap() { CORRADE_SKIP("VK_KHR_maintenance1 not supported, can't test"); VkDevice device; + Queue queue{NoCreate}; CORRADE_COMPARE(Result(instance->CreateDevice(deviceProperties, DeviceCreateInfo{instance} - .addQueues(0, {0.0f}) + .addQueues(0, {0.0f}, {queue}) .addEnabledExtensions< Extensions::EXT::debug_marker, Extensions::KHR::maintenance1 >(), nullptr, &device)), Result::Success); CORRADE_VERIFY(device); + /* Populating the queue handle is done only from Device itself, so it won't + happen here -- would need to call vkGetDeviceQueue[2] directly */ + CORRADE_VERIFY(!queue.handle()); { /* Wrapping should load the basic function pointers */ @@ -532,8 +647,9 @@ void DeviceVkTest::wrap() { void DeviceVkTest::populateGlobalFunctionPointers() { vkDestroyDevice = nullptr; + Queue queue{NoCreate}; Device device{_instance, DeviceCreateInfo{_instance} - .addQueues(0, {0.0f}) + .addQueues(0, {0.0f}, {queue}) }; CORRADE_VERIFY(!vkDestroyDevice); device.populateGlobalFunctionPointers(); diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index 89f2af7a4..3cb353a7a 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -49,6 +49,7 @@ class InstanceCreateInfo; class InstanceExtension; class InstanceExtensionProperties; class LayerProperties; +class Queue; enum class QueueFlag: UnsignedInt; typedef Containers::EnumSet QueueFlags; enum class Result: Int;