diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index 7fade0567..803c00736 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -220,7 +220,7 @@ Vulkan function | Matching API @fn_vk{GetDeviceProcAddr} | @ref Device constructor @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{GetFenceStatus} | @ref Fence::status() @fn_vk{GetImageMemoryRequirements}, \n @fn_vk{GetImageMemoryRequirements2} @m_class{m-label m-flat m-success} **KHR, 1.1** | @ref Image::memoryRequirements() @fn_vk{GetImageSparseMemoryRequirements}, \n @fn_vk{GetImageSparseMemoryRequirements2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @fn_vk{GetImageSubresourceLayout} | | @@ -281,7 +281,7 @@ Vulkan function | Matching API @fn_vk{ResetCommandBuffer} | @ref CommandBuffer::reset() @fn_vk{ResetCommandPool} | @ref CommandPool::reset() @fn_vk{ResetDescriptorPool} | | -@fn_vk{ResetFences} | | +@fn_vk{ResetFences} | @ref Fence::reset() @fn_vk{ResetQueryPool} @m_class{m-label m-flat m-success} **EXT, 1.2** | | @subsection vulkan-mapping-functions-s S @@ -319,7 +319,7 @@ Vulkan function | Matching API Vulkan function | Matching API --------------------------------------- | ------------ -@fn_vk{WaitForFences} | | +@fn_vk{WaitForFences} | @ref Fence::wait() @fn_vk{WriteAccelerationStructuresPropertiesKHR} @m_class{m-label m-flat m-warning} **KHR** | | @section vulkan-mapping-structures Structures diff --git a/src/Magnum/Vk/Fence.cpp b/src/Magnum/Vk/Fence.cpp index d5a844173..8f25a17a9 100644 --- a/src/Magnum/Vk/Fence.cpp +++ b/src/Magnum/Vk/Fence.cpp @@ -28,6 +28,7 @@ #include "Magnum/Vk/Assert.h" #include "Magnum/Vk/Device.h" +#include "Magnum/Vk/Result.h" namespace Magnum { namespace Vk { @@ -76,6 +77,22 @@ Fence& Fence::operator=(Fence&& other) noexcept { return *this; } +bool Fence::status() { + return MAGNUM_VK_INTERNAL_ASSERT_SUCCESS_OR(NotReady, (**_device).GetFenceStatus(*_device, _handle)) == Result::Success; +} + +void Fence::reset() { + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS((**_device).ResetFences(*_device, 1, &_handle)); +} + +bool Fence::wait(const std::chrono::nanoseconds timeout) { + return MAGNUM_VK_INTERNAL_ASSERT_SUCCESS_OR(Timeout, (**_device).WaitForFences(*_device, 1, &_handle, true, timeout.count())) == Result::Success; +} + +void Fence::wait() { + CORRADE_INTERNAL_ASSERT_OUTPUT(wait(std::chrono::nanoseconds{~std::uint64_t{}})); +} + VkFence Fence::release() { const VkFence handle = _handle; _handle = {}; diff --git a/src/Magnum/Vk/Fence.h b/src/Magnum/Vk/Fence.h index 324acbf46..400945357 100644 --- a/src/Magnum/Vk/Fence.h +++ b/src/Magnum/Vk/Fence.h @@ -30,6 +30,8 @@ * @m_since_latest */ +#include + #include "Magnum/Magnum.h" #include "Magnum/Tags.h" #include "Magnum/Vk/Handle.h" @@ -53,6 +55,13 @@ the @p info parameter at its default. If you want to pass additional parameters to it, include the @ref FenceCreateInfo class as usual: @snippet MagnumVk.cpp Fence-creation + +@section Vk-Fence-usage Basic usage + +By default a fence is created unsignaled. It can be created in a signaled state +using @ref FenceCreateInfo::Flag::Signaled and its signaled state reset back +via @ref reset(). Fence status can be queried immediately via @ref status() and +waited on using @ref wait(). */ class MAGNUM_VK_EXPORT Fence { public: @@ -122,6 +131,50 @@ class MAGNUM_VK_EXPORT Fence { /** @brief Handle flags */ HandleFlags handleFlags() const { return _flags; } + /** + * @brief Reset the fence + * + * Sets the state of a fence to unsignaled from the host. + * @see @fn_vk_keyword{ResetFences} + * @todo batch version (needs `DynamicArray` to avoid an + * allocation) + */ + void reset(); + + /** + * @brief Fence status + * + * Returns @cpp true @ce if the fence is signaled and @cpp false @ce + * if unsignaled. + * @see @fn_vk_keyword{GetFenceStatus} + */ + bool status(); + + /** + * @brief Wait for the fence to become signaled + * + * Blocks until the fence becomes signaled or @p timeout is elapsed, + * whichever happens sooner, returning @cpp true @ce is the fence + * became signaled. If the fence is already signaled, the function + * returns immediately, if the timeout happens before the fence + * becomes signaled, @cpp false @ce is returned. + * + * Calling this function with zero @p timeout is equivalent to calling + * @ref status(). + * @see @fn_vk_keyword{WaitForFences} + * @todo batch version (needs DynamicArray to avoid an + * allocation), expose `waitAll` + */ + bool wait(std::chrono::nanoseconds timeout); + + /** + * @brief Wait indefinitely for the fence to become signaled + * + * Equivalent to calling @ref wait(std::chrono::nanoseconds) with the + * largest representable 64-bit value. + */ + void wait(); + /** * @brief Release the underlying Vulkan fence * diff --git a/src/Magnum/Vk/FenceCreateInfo.h b/src/Magnum/Vk/FenceCreateInfo.h index 2e3d55df6..57f797931 100644 --- a/src/Magnum/Vk/FenceCreateInfo.h +++ b/src/Magnum/Vk/FenceCreateInfo.h @@ -58,7 +58,11 @@ class MAGNUM_VK_EXPORT FenceCreateInfo { * @m_enum_values_as_keywords */ enum class Flag: UnsignedInt { - /** Create the fence in a signaled state */ + /** + * Create the fence in a signaled state. + * + * @ref Fence::status(), @ref Fence::reset() + */ Signaled = VK_FENCE_CREATE_SIGNALED_BIT }; diff --git a/src/Magnum/Vk/Test/FenceVkTest.cpp b/src/Magnum/Vk/Test/FenceVkTest.cpp index 241342d66..41f18b390 100644 --- a/src/Magnum/Vk/Test/FenceVkTest.cpp +++ b/src/Magnum/Vk/Test/FenceVkTest.cpp @@ -38,13 +38,25 @@ struct FenceVkTest: VulkanTester { void constructMove(); void wrap(); + + void status(); + void reset(); + void wait100ms(); + void wait(); }; FenceVkTest::FenceVkTest() { addTests({&FenceVkTest::construct, &FenceVkTest::constructMove, - &FenceVkTest::wrap}); + &FenceVkTest::wrap, + + &FenceVkTest::status, + &FenceVkTest::reset}); + + addBenchmarks({&FenceVkTest::wait100ms}, 1); + + addTests({&FenceVkTest::wait}); } void FenceVkTest::construct() { @@ -93,6 +105,41 @@ void FenceVkTest::wrap() { device()->DestroyFence(device(), fence, nullptr); } +void FenceVkTest::status() { + Fence a{device()}; + CORRADE_VERIFY(!a.status()); + + Fence b{device(), FenceCreateInfo{FenceCreateInfo::Flag::Signaled}}; + CORRADE_VERIFY(b.status()); +} + +void FenceVkTest::reset() { + Fence a{device(), FenceCreateInfo{FenceCreateInfo::Flag::Signaled}}; + CORRADE_VERIFY(a.status()); + + a.reset(); + CORRADE_VERIFY(!a.status()); +} + +void FenceVkTest::wait100ms() { + Fence a{device()}; + CORRADE_VERIFY(!a.status()); + + /* A benchmark so we have at least some verification we're not terribly off + with the units */ + CORRADE_BENCHMARK(1) + CORRADE_VERIFY(!a.wait(std::chrono::milliseconds{100})); + + CORRADE_VERIFY(!a.status()); +} + +void FenceVkTest::wait() { + Fence a{device(), FenceCreateInfo{FenceCreateInfo::Flag::Signaled}}; + CORRADE_VERIFY(a.status()); + a.wait(); + CORRADE_VERIFY(a.status()); +} + }}}} CORRADE_TEST_MAIN(Magnum::Vk::Test::FenceVkTest)