Browse Source

Vk: implement queue submit.

pull/494/head
Vladimír Vondruš 5 years ago
parent
commit
46ed065bb2
  1. 4
      doc/vulkan-mapping.dox
  2. 88
      src/Magnum/Vk/Queue.cpp
  3. 106
      src/Magnum/Vk/Queue.h
  4. 2
      src/Magnum/Vk/Test/CMakeLists.txt
  5. 81
      src/Magnum/Vk/Test/QueueTest.cpp
  6. 102
      src/Magnum/Vk/Test/QueueVkTest.cpp
  7. 1
      src/Magnum/Vk/Vk.h

4
doc/vulkan-mapping.dox

@ -268,7 +268,7 @@ Vulkan function | Matching API
@fn_vk{QueueBeginDebugUtilsLabelEXT} @m_class{m-label m-flat m-warning} **EXT**, \n @fn_vk{QueueEndDebugUtilsLabelEXT} @m_class{m-label m-flat m-warning} **EXT** | |
@fn_vk{QueueBindSparse} | |
@fn_vk{QueueInsertDebugUtilsLabelEXT} @m_class{m-label m-flat m-warning} **EXT** | |
@fn_vk{QueueSubmit} | |
@fn_vk{QueueSubmit} | @ref Queue::submit()
@fn_vk{QueueWaitIdle} | |
@subsection vulkan-mapping-functions-r R
@ -667,7 +667,7 @@ Vulkan structure | Matching API
@type_vk{SpecializationMapEntry} | |
@type_vk{StencilOpState} | |
@type_vk{StridedDeviceAddressRegionKHR} @m_class{m-label m-flat m-warning} **KHR** | |
@type_vk{SubmitInfo} | |
@type_vk{SubmitInfo} | @ref SubmitInfo
@type_vk{SubpassBeginInfo} @m_class{m-label m-flat m-success} **KHR, 1.2** | @ref SubpassBeginInfo
@type_vk{SubpassEndInfo} @m_class{m-label m-flat m-success} **KHR, 1.2** | @ref SubpassEndInfo
@type_vk{SubpassDependency}, \n @type_vk{SubpassDependency2} @m_class{m-label m-flat m-success} **KHR, 1.2** | @ref SubpassDependency

88
src/Magnum/Vk/Queue.cpp

@ -26,8 +26,12 @@
#include "Queue.h"
#include <Corrade/Containers/Array.h>
#include <Corrade/Containers/Reference.h>
#include <Corrade/Utility/Algorithms.h>
#include "Magnum/Vk/Assert.h"
#include "Magnum/Vk/Device.h"
namespace Magnum { namespace Vk {
Queue Queue::wrap(Device& device, VkQueue handle) {
@ -52,4 +56,88 @@ Queue& Queue::operator=(Queue&& other) noexcept {
return *this;
}
void Queue::submit(const Containers::ArrayView<const Containers::Reference<const SubmitInfo>> infos, const VkFence fence) {
/** @todo use DynamicArray here. I also thought about taking an ArrayView
of VkSubmitInfo structures directly and thus avoiding this whole hell,
but that feels kinda inconsistent in the public API (as everywhere else
we take only the structure wrappers, as opposed to handles which *are*
taken raw); plus once the SubmitInfo gets more complex it may not be
so easy to verify we don't use unsupported functionality / backport on
the raw structure (like done in RenderPassCreateInfo, e.g.) */
/* If we have just one item, we don't need to allocate. This will become
obsolete once DynamicArray can handle both cases efficiently */
if(infos.size() == 1) {
MAGNUM_VK_INTERNAL_ASSERT_SUCCESS((**_device).QueueSubmit(_handle, 1, *infos[0], fence));
return;
}
Containers::Array<VkSubmitInfo> vkInfos{Containers::NoInit, infos.size()};
for(std::size_t i = 0; i != infos.size(); ++i)
vkInfos[i] = *infos[i];
MAGNUM_VK_INTERNAL_ASSERT_SUCCESS((**_device).QueueSubmit(_handle, vkInfos.size(), vkInfos, fence));
}
void Queue::submit(std::initializer_list<Containers::Reference<const SubmitInfo>> infos, VkFence fence) {
return submit(Containers::arrayView(infos), fence);
}
struct SubmitInfo::State {
Containers::Array<VkCommandBuffer> commandBuffers;
};
SubmitInfo::SubmitInfo(): _info{} {
_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
}
SubmitInfo::SubmitInfo(NoInitT) noexcept {}
SubmitInfo::SubmitInfo(const VkSubmitInfo& info):
/* Can't use {} with GCC 4.8 here because it tries to initialize the first
member instead of doing a copy */
_info(info) {}
SubmitInfo::SubmitInfo(SubmitInfo&& other) noexcept:
/* Can't use {} with GCC 4.8 here because it tries to initialize the first
member instead of doing a copy */
_info(other._info),
_state{std::move(other._state)}
{
/* Ensure the previous instance doesn't reference state that's now ours */
/** @todo this is now more like a destructible move, do it more selectively
and clear only what's really ours and not external? */
other._info.pNext = nullptr;
other._info.waitSemaphoreCount = 0;
other._info.pWaitSemaphores = nullptr;
other._info.pWaitDstStageMask = nullptr;
other._info.commandBufferCount = 0;
other._info.pCommandBuffers = nullptr;
other._info.signalSemaphoreCount = 0;
other._info.pSignalSemaphores = nullptr;
}
SubmitInfo::~SubmitInfo() = default;
SubmitInfo& SubmitInfo::operator=(SubmitInfo&& other) noexcept {
using std::swap;
swap(other._info, _info);
swap(other._state, _state);
return *this;
}
SubmitInfo& SubmitInfo::setCommandBuffers(Containers::ArrayView<const VkCommandBuffer> buffers) {
if(!_state) _state.emplace();
_state->commandBuffers = Containers::Array<VkCommandBuffer>{Containers::NoInit, buffers.size()};
Utility::copy(buffers, _state->commandBuffers);
_info.commandBufferCount = _state->commandBuffers.size();
_info.pCommandBuffers = _state->commandBuffers;
return *this;
}
SubmitInfo& SubmitInfo::setCommandBuffers(std::initializer_list<VkCommandBuffer> buffers) {
return setCommandBuffers(Containers::arrayView(buffers));
}
}}

106
src/Magnum/Vk/Queue.h

@ -30,6 +30,8 @@
* @m_since_latest
*/
#include <Corrade/Containers/Pointer.h>
#include "Magnum/Tags.h"
#include "Magnum/Vk/Vk.h"
#include "Magnum/Vk/Vulkan.h"
@ -43,7 +45,7 @@ namespace Magnum { namespace Vk {
Wraps a @type_vk_keyword{Queue}. See @ref Device class docs for an introduction
on how to create a queue.
@see @ref DeviceCreateInfo::addQueues()
@see @ref DeviceCreateInfo::addQueues(), @ref submit()
*/
class MAGNUM_VK_EXPORT Queue {
public:
@ -98,6 +100,22 @@ class MAGNUM_VK_EXPORT Queue {
/** @overload */
operator VkQueue() { return _handle; }
/**
* @brief Submit a sequence of semaphores or command buffers to a queue
* @param infos Submit info structures, each specifying a command
* buffer submission batch
* @param fence A @ref Fence or a raw Vulkan fence handle to be
* signaled once all submitted command buffers have completed
* execution. Pass a @cpp {} @ce or a
* @ref Fence::Fence(NoCreateT) "NoCreate"'d @ref Fence to not
* signal anything.
*
* @see @fn_vk_keyword{QueueSubmit}
*/
void submit(Containers::ArrayView<const Containers::Reference<const SubmitInfo>> infos, VkFence fence);
/** @overload */
void submit(std::initializer_list<Containers::Reference<const SubmitInfo>> infos, VkFence fence);
private:
/* Can't be a reference because of the NoCreate constructor */
Device* _device;
@ -105,6 +123,92 @@ class MAGNUM_VK_EXPORT Queue {
VkQueue _handle;
};
/**
@brief Queue submit info
@m_since_latest
Wraps a @type_vk_keyword{SubmitInfo}.
*/
class MAGNUM_VK_EXPORT SubmitInfo {
public:
/**
* @brief Constructor
*
* The following @type_vk{SubmitInfo} fields are pre-filled in
* addition to `sType`, everything else is zero-filled:
*
* - *(none)*
*
* @see @ref setCommandBuffers()
*/
explicit SubmitInfo();
/**
* @brief Construct without initializing the contents
*
* Note that not even the `sType` field is set --- the structure has to
* be fully initialized afterwards in order to be usable.
*/
explicit SubmitInfo(NoInitT) noexcept;
/**
* @brief Construct from existing data
*
* Copies the existing values verbatim, pointers are kept unchanged
* without taking over the ownership. Modifying the newly created
* instance will not modify the original data nor the pointed-to data.
*/
explicit SubmitInfo(const VkSubmitInfo& info);
/** @brief Copying is not allowed */
SubmitInfo(const SubmitInfo&) = delete;
/** @brief Move constructor */
SubmitInfo(SubmitInfo&& other) noexcept;
~SubmitInfo();
/** @brief Copying is not allowed */
SubmitInfo& operator=(const SubmitInfo&) = delete;
/** @brief Move assignment */
SubmitInfo& operator=(SubmitInfo&& other) noexcept;
/**
* @brief Set command buffers to execute in the batch
* @return Reference to self (for method chaining)
*/
SubmitInfo& setCommandBuffers(Containers::ArrayView<const VkCommandBuffer> buffers);
/** @overload */
SubmitInfo& setCommandBuffers(std::initializer_list<VkCommandBuffer> buffers);
/** @brief Underlying @type_vk{SubmitInfo} structure */
VkSubmitInfo& operator*() { return _info; }
/** @overload */
const VkSubmitInfo& operator*() const { return _info; }
/** @overload */
VkSubmitInfo* operator->() { return &_info; }
/** @overload */
const VkSubmitInfo* operator->() const { return &_info; }
/** @overload */
operator const VkSubmitInfo*() const { return &_info; }
/**
* @overload
*
* The class is implicitly convertible to a reference in addition to
* a pointer because the type is commonly used in arrays as well, which
* would be annoying to do with a pointer conversion.
*/
operator const VkSubmitInfo&() const { return _info; }
private:
VkSubmitInfo _info;
struct State;
Containers::Pointer<State> _state;
};
}}
#endif

2
src/Magnum/Vk/Test/CMakeLists.txt

@ -165,6 +165,7 @@ if(BUILD_VK_TESTS)
corrade_add_test(VkImageViewVkTest ImageViewVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester)
corrade_add_test(VkInstanceVkTest InstanceVkTest.cpp LIBRARIES MagnumVkTestLib)
corrade_add_test(VkMemoryVkTest MemoryVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester)
corrade_add_test(VkQueueVkTest QueueVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester)
corrade_add_test(VkRenderPassVkTest RenderPassVkTest.cpp LIBRARIES MagnumVkTestLib MagnumVulkanTester)
corrade_add_test(VkShaderVkTest ShaderVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester)
target_include_directories(VkShaderVkTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
@ -184,6 +185,7 @@ if(BUILD_VK_TESTS)
VkImageViewVkTest
VkInstanceVkTest
VkMemoryVkTest
VkQueueVkTest
VkRenderPassVkTest
VkShaderVkTest
VkVersionVkTest

81
src/Magnum/Vk/Test/QueueTest.cpp

@ -23,6 +23,7 @@
DEALINGS IN THE SOFTWARE.
*/
#include <new>
#include <Corrade/TestSuite/Tester.h>
#include "Magnum/Vk/Device.h"
@ -38,6 +39,13 @@ struct QueueTest: TestSuite::Tester {
void constructMove();
void wrap();
void submitInfoConstruct();
void submitInfoConstructNoInit();
void submitInfoConstructCommandBuffers();
void submitInfoConstructFromVk();
void submitInfoConstructCopy();
void submitInfoConstructMove();
};
QueueTest::QueueTest() {
@ -45,7 +53,14 @@ QueueTest::QueueTest() {
&QueueTest::constructCopy,
&QueueTest::constructMove,
&QueueTest::wrap});
&QueueTest::wrap,
&QueueTest::submitInfoConstruct,
&QueueTest::submitInfoConstructNoInit,
&QueueTest::submitInfoConstructCommandBuffers,
&QueueTest::submitInfoConstructFromVk,
&QueueTest::submitInfoConstructCopy,
&QueueTest::submitInfoConstructMove});
}
void QueueTest::constructNoCreate() {
@ -93,6 +108,70 @@ void QueueTest::wrap() {
CORRADE_COMPARE(queue.handle(), vkQueue);
}
void QueueTest::submitInfoConstruct() {
SubmitInfo info;
CORRADE_COMPARE(info->commandBufferCount, 0);
CORRADE_VERIFY(!info->pCommandBuffers);
}
void QueueTest::submitInfoConstructNoInit() {
SubmitInfo info{NoInit};
info->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2;
new(&info) SubmitInfo{NoInit};
CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2);
CORRADE_VERIFY((std::is_nothrow_constructible<SubmitInfo, NoInitT>::value));
/* Implicit construction is not allowed */
CORRADE_VERIFY(!(std::is_convertible<NoInitT, SubmitInfo>::value));
}
void QueueTest::submitInfoConstructCommandBuffers() {
SubmitInfo info;
info.setCommandBuffers({
reinterpret_cast<VkCommandBuffer>(0xbadbeef),
reinterpret_cast<VkCommandBuffer>(0xcafecafe)
});
CORRADE_COMPARE(info->commandBufferCount, 2);
CORRADE_VERIFY(info->pCommandBuffers);
CORRADE_COMPARE(info->pCommandBuffers[0], reinterpret_cast<VkCommandBuffer>(0xbadbeef));
CORRADE_COMPARE(info->pCommandBuffers[1], reinterpret_cast<VkCommandBuffer>(0xcafecafe));
}
void QueueTest::submitInfoConstructFromVk() {
VkSubmitInfo vkInfo;
vkInfo.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2;
SubmitInfo info{vkInfo};
CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2);
}
void QueueTest::submitInfoConstructCopy() {
CORRADE_VERIFY(!(std::is_copy_constructible<SubmitInfo>{}));
CORRADE_VERIFY(!(std::is_copy_assignable<SubmitInfo>{}));
}
void QueueTest::submitInfoConstructMove() {
SubmitInfo a;
a.setCommandBuffers({{}, reinterpret_cast<VkCommandBuffer>(0xcafecafe)});
SubmitInfo b = std::move(a);
CORRADE_COMPARE(a->commandBufferCount, 0);
CORRADE_VERIFY(!a->pCommandBuffers);
CORRADE_COMPARE(b->commandBufferCount, 2);
CORRADE_VERIFY(b->pCommandBuffers);
CORRADE_COMPARE(b->pCommandBuffers[1], reinterpret_cast<VkCommandBuffer>(0xcafecafe));
SubmitInfo c{VkSubmitInfo{}};
c = std::move(b);
CORRADE_COMPARE(b->commandBufferCount, 0);
CORRADE_VERIFY(!b->pCommandBuffers);
CORRADE_COMPARE(c->commandBufferCount, 2);
CORRADE_VERIFY(c->pCommandBuffers);
CORRADE_COMPARE(c->pCommandBuffers[1], reinterpret_cast<VkCommandBuffer>(0xcafecafe));
}
}}}}
CORRADE_TEST_MAIN(Magnum::Vk::Test::QueueTest)

102
src/Magnum/Vk/Test/QueueVkTest.cpp

@ -0,0 +1,102 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020 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 "Magnum/Vk/CommandBuffer.h"
#include "Magnum/Vk/CommandPoolCreateInfo.h"
#include "Magnum/Vk/DeviceProperties.h"
#include "Magnum/Vk/Fence.h"
#include "Magnum/Vk/VulkanTester.h"
namespace Magnum { namespace Vk { namespace Test { namespace {
struct QueueVkTest: VulkanTester {
explicit QueueVkTest();
/* construction tested in DeviceVkTest as it's done implicitly on Device
creation */
/* move and wrap tested in QueueTest as there's no vkDestroyQueue() and
thus we don't need a Vulkan-enabled test for that */
void submit();
void submitOne();
};
QueueVkTest::QueueVkTest() {
addTests({&QueueVkTest::submit,
&QueueVkTest::submitOne});
}
void QueueVkTest::submit() {
CommandPool pool{device(), CommandPoolCreateInfo{
device().properties().pickQueueFamily(QueueFlag::Graphics)}};
CommandBuffer a = pool.allocate();
a.begin()
.end();
CommandBuffer b = pool.allocate();
b.begin()
.end();
CommandBuffer c = pool.allocate();
c.begin()
.end();
Fence fence{device()};
CORRADE_VERIFY(!fence.status());
queue().submit({
SubmitInfo{}.setCommandBuffers({a, b}),
SubmitInfo{}.setCommandBuffers({c})
}, fence);
CORRADE_VERIFY(fence.wait(std::chrono::milliseconds{1000}));
}
void QueueVkTest::submitOne() {
/* Until DynamicArray is a thing, submit() has a special case for a single
item that doesn't allocate but instead just point to it */
/** @todo drop once DynamicArray handles that */
CommandPool pool{device(), CommandPoolCreateInfo{
device().properties().pickQueueFamily(QueueFlag::Graphics)}};
CommandBuffer a = pool.allocate();
a.begin()
.end();
Fence fence{device()};
CORRADE_VERIFY(!fence.status());
queue().submit({
SubmitInfo{}.setCommandBuffers({a})
}, fence);
CORRADE_VERIFY(fence.wait(std::chrono::milliseconds{1000}));
}
}}}}
CORRADE_TEST_MAIN(Magnum::Vk::Test::QueueVkTest)

1
src/Magnum/Vk/Vk.h

@ -88,6 +88,7 @@ class RenderPassCreateInfo;
enum class Result: Int;
class Shader;
class ShaderCreateInfo;
class SubmitInfo;
class SubpassBeginInfo;
class SubpassEndInfo;
enum class Version: UnsignedInt;

Loading…
Cancel
Save