diff --git a/doc/snippets/MagnumVk.cpp b/doc/snippets/MagnumVk.cpp index 1f88b1846..bb157c1eb 100644 --- a/doc/snippets/MagnumVk.cpp +++ b/doc/snippets/MagnumVk.cpp @@ -38,6 +38,7 @@ #include "Magnum/Vk/DeviceProperties.h" #include "Magnum/Vk/Extensions.h" #include "Magnum/Vk/ExtensionProperties.h" +#include "Magnum/Vk/FramebufferCreateInfo.h" #include "Magnum/Vk/InstanceCreateInfo.h" #include "Magnum/Vk/Integration.h" #include "Magnum/Vk/ImageCreateInfo.h" @@ -291,6 +292,39 @@ if(device.isExtensionEnabled()) { /* [Device-isExtensionEnabled] */ } +{ +Vk::Device device{DOXYGEN_IGNORE(NoCreate)}; +Vector2i size; +/* The include should be a no-op here since it was already included above */ +/* [Framebuffer-creation] */ +#include + +DOXYGEN_IGNORE() + +Vk::Image color{device, Vk::ImageCreateInfo2D{ /* created before */ + Vk::ImageUsage::ColorAttachment, + VK_FORMAT_R8G8B8A8_UNORM, size, 1}, DOXYGEN_IGNORE(NoAllocate)}; +Vk::Image depth{device, Vk::ImageCreateInfo2D{ + Vk::ImageUsage::DepthStencilAttachment, + VK_FORMAT_D24_UNORM_S8_UINT, size, 1}, DOXYGEN_IGNORE(NoAllocate)}; +Vk::ImageView colorView{device, Vk::ImageViewCreateInfo2D{color}}; +Vk::ImageView depthView{device, Vk::ImageViewCreateInfo2D{depth}}; + +Vk::RenderPass renderPass{device, Vk::RenderPassCreateInfo{} /* created before */ + .setAttachments({ + color.format(), + depth.format() + }) + DOXYGEN_IGNORE() +}; + +Vk::Framebuffer framebuffer{device, Vk::FramebufferCreateInfo{renderPass, { + colorView, + depthView +}, size}}; +/* [Framebuffer-creation] */ +} + { Vk::Device device{NoCreate}; /* The include should be a no-op here since it was already included above */ diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index d8f439e50..86167bbdf 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -150,7 +150,7 @@ Vulkan function | Matching API @fn_vk{CreateDevice}, \n @fn_vk{DestroyDevice} | @ref Device constructor and destructor @fn_vk{CreateEvent}, \n @fn_vk{DestroyEvent} | | @fn_vk{CreateFence}, \n @fn_vk{DestroyFence} | | -@fn_vk{CreateFramebuffer}, \n @fn_vk{DestroyFramebuffer} | | +@fn_vk{CreateFramebuffer}, \n @fn_vk{DestroyFramebuffer} | @ref Framebuffer constructor and destructor @fn_vk{CreateImage}, \n @fn_vk{DestroyImage} | @ref Image constructor and destructor @fn_vk{CreateImageView}, \n @fn_vk{DestroyImageView} | @ref ImageView constructor and destructor @fn_vk{CreateInstance}, \n @fn_vk{DestroyInstance} | @ref Instance constructor and destructor @@ -471,7 +471,7 @@ Vulkan structure | Matching API @type_vk{FormatProperties}, \n @type_vk{FormatProperties2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @type_vk{FramebufferAttachmentsCreateInfo} @m_class{m-label m-flat m-success} **KHR, 1.2** | | @type_vk{FramebufferAttachmentImageInfo} @m_class{m-label m-flat m-success} **KHR, 1.2** | | -@type_vk{FramebufferCreateInfo} | | +@type_vk{FramebufferCreateInfo} | @ref FramebufferCreateInfo @subsection vulkan-mapping-structures-g G diff --git a/src/Magnum/Vk/CMakeLists.txt b/src/Magnum/Vk/CMakeLists.txt index 03061c353..3fc9551f8 100644 --- a/src/Magnum/Vk/CMakeLists.txt +++ b/src/Magnum/Vk/CMakeLists.txt @@ -30,6 +30,7 @@ set(MagnumVk_SRCS CommandBuffer.cpp CommandPool.cpp Extensions.cpp + Framebuffer.cpp Handle.cpp Instance.cpp Result.cpp @@ -67,6 +68,8 @@ set(MagnumVk_HEADERS Enums.h Extensions.h ExtensionProperties.h + Framebuffer.h + FramebufferCreateInfo.h Handle.h Image.h ImageCreateInfo.h diff --git a/src/Magnum/Vk/Framebuffer.cpp b/src/Magnum/Vk/Framebuffer.cpp new file mode 100644 index 000000000..798755c7d --- /dev/null +++ b/src/Magnum/Vk/Framebuffer.cpp @@ -0,0 +1,132 @@ +/* + 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. +*/ + +#include "Framebuffer.h" +#include "FramebufferCreateInfo.h" + +#include +#include + +#include "Magnum/Math/Vector3.h" +#include "Magnum/Vk/Assert.h" +#include "Magnum/Vk/Device.h" +#include "Magnum/Vk/ImageView.h" + +namespace Magnum { namespace Vk { + +struct FramebufferCreateInfo::State { + Containers::Array attachments; +}; + +FramebufferCreateInfo::FramebufferCreateInfo(const VkRenderPass renderPass, const Containers::ArrayView> attachments, const Vector3i& size, const Flags flags): _info{}, _state{Containers::InPlaceInit} { + _info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + _info.flags = VkFramebufferCreateFlags(flags); + _info.renderPass = renderPass; + _info.width = size.x(); + _info.height = size.y(); + _info.layers = size.z(); + + /* Allocate an array with VkImageView handles */ + /** @todo combine the state and array allocations into one (could use + ArrayTuple, however that one includes ArrayView so not much better than + including Array directly in the header which I don't want to do) */ + _state->attachments = Containers::Array{Containers::NoInit, attachments.size()}; + for(std::size_t i = 0; i != attachments.size(); ++i) + _state->attachments[i] = *attachments[i]; + _info.attachmentCount = _state->attachments.size(); + _info.pAttachments = _state->attachments; +} + +FramebufferCreateInfo::FramebufferCreateInfo(const VkRenderPass renderPass, const std::initializer_list> attachments, const Vector3i& size, const Flags flags): FramebufferCreateInfo{renderPass, Containers::arrayView(attachments), size, flags} {} + +FramebufferCreateInfo::FramebufferCreateInfo(const VkRenderPass renderPass, const Containers::ArrayView> attachments, const Vector2i& size, const Flags flags): FramebufferCreateInfo{renderPass, attachments, {size, 1}, flags} {} + +FramebufferCreateInfo::FramebufferCreateInfo(const VkRenderPass renderPass, const std::initializer_list> attachments, const Vector2i& size, const Flags flags): FramebufferCreateInfo{renderPass, Containers::arrayView(attachments), size, flags} {} + +FramebufferCreateInfo::FramebufferCreateInfo(NoInitT) noexcept {} + +FramebufferCreateInfo::FramebufferCreateInfo(const VkFramebufferCreateInfo& info): + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _info(info) {} + +FramebufferCreateInfo::FramebufferCreateInfo(FramebufferCreateInfo&& 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 */ + other._info.attachmentCount = 0; + other._info.pAttachments = nullptr; +} + +FramebufferCreateInfo::~FramebufferCreateInfo() = default; + +FramebufferCreateInfo& FramebufferCreateInfo::operator=(FramebufferCreateInfo&& other) noexcept { + using std::swap; + swap(other._info, _info); + swap(other._state, _state); + return *this; +} + +Framebuffer Framebuffer::wrap(Device& device, const VkFramebuffer handle, const HandleFlags flags) { + Framebuffer out{NoCreate}; + out._device = &device; + out._handle = handle; + out._flags = flags; + return out; +} + +Framebuffer::Framebuffer(Device& device, const FramebufferCreateInfo& info): _device{&device}, _flags{HandleFlag::DestroyOnDestruction} { + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(device->CreateFramebuffer(device, info, nullptr, &_handle)); +} + +Framebuffer::Framebuffer(NoCreateT): _device{}, _handle{} {} + +Framebuffer::Framebuffer(Framebuffer&& other) noexcept: _device{other._device}, _handle{other._handle}, _flags{other._flags} { + other._handle = {}; +} + +Framebuffer::~Framebuffer() { + if(_handle && (_flags & HandleFlag::DestroyOnDestruction)) + (**_device).DestroyFramebuffer(*_device, _handle, nullptr); +} + +Framebuffer& Framebuffer::operator=(Framebuffer&& other) noexcept { + using std::swap; + swap(other._device, _device); + swap(other._handle, _handle); + swap(other._flags, _flags); + return *this; +} + +VkFramebuffer Framebuffer::release() { + const VkFramebuffer handle = _handle; + _handle = {}; + return handle; +} + +}} diff --git a/src/Magnum/Vk/Framebuffer.h b/src/Magnum/Vk/Framebuffer.h new file mode 100644 index 000000000..fc71f4427 --- /dev/null +++ b/src/Magnum/Vk/Framebuffer.h @@ -0,0 +1,141 @@ +#ifndef Magnum_Vk_Framebuffer_h +#define Magnum_Vk_Framebuffer_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::Framebuffer + * @m_since_latest + */ + +#include "Magnum/Magnum.h" +#include "Magnum/Tags.h" +#include "Magnum/Vk/Handle.h" +#include "Magnum/Vk/Vk.h" +#include "Magnum/Vk/Vulkan.h" +#include "Magnum/Vk/visibility.h" + +namespace Magnum { namespace Vk { + +/** +@brief Framebuffer +@m_since_latest + +Wraps a @type_vk_keyword{Framebuffer}, which connects a @ref RenderPass +together with concrete @ref ImageView "ImageViews" for attachments. + +@section Vk-Framebuffer-creation Framebuffer creation + +A framebuffer is created using @ref FramebufferCreateInfo that takes a +previously-created @ref RenderPass together with @ref ImageView "ImageViews" +onto @ref Image "Images" of desired sizes and compatible formats for all its attachments: + +@snippet MagnumVk.cpp Framebuffer-creation +*/ +class MAGNUM_VK_EXPORT Framebuffer { + public: + /** + * @brief Wrap existing Vulkan handle + * @param device Vulkan device the framebuffer is created on + * @param handle The @type_vk{Framebuffer} handle + * @param flags Handle flags + * + * The @p handle is expected to be originating from @p device. Unlike + * a framebuffer created using a constructor, the Vulkan framebuffer is + * by default not deleted on destruction, use @p flags for different + * behavior. + * @see @ref release() + */ + static Framebuffer wrap(Device& device, VkFramebuffer handle, HandleFlags flags = {}); + + /** + * @brief Constructor + * @param device Vulkan device to create the image on + * @param info Image creation info + * + * @see @fn_vk_keyword{CreateFramebuffer} + */ + explicit Framebuffer(Device& device, const FramebufferCreateInfo& info); + + /** + * @brief Construct without creating the image + * + * The constructed instance is equivalent to moved-from state. Useful + * in cases where you will overwrite the instance later anyway. Move + * another object over it to make it useful. + */ + explicit Framebuffer(NoCreateT); + + /** @brief Copying is not allowed */ + Framebuffer(const Framebuffer&) = delete; + + /** @brief Move constructor */ + Framebuffer(Framebuffer&& other) noexcept; + + /** + * @brief Destructor + * + * Destroys associated @type_vk{Framebuffer} handle, unless the + * instance was created using @ref wrap() without + * @ref HandleFlag::DestroyOnDestruction specified. + * @see @fn_vk_keyword{DestroyFramebuffer}, @ref release() + */ + ~Framebuffer(); + + /** @brief Copying is not allowed */ + Framebuffer& operator=(const Framebuffer&) = delete; + + /** @brief Move assignment */ + Framebuffer& operator=(Framebuffer&& other) noexcept; + + /** @brief Underlying @type_vk{Framebuffer} handle */ + VkFramebuffer handle() { return _handle; } + /** @overload */ + operator VkFramebuffer() { return _handle; } + + /** @brief Handle flags */ + HandleFlags handleFlags() const { return _flags; } + + /** + * @brief Release the underlying Vulkan framebuffer + * + * Releases ownership of the Vulkan framebuffer and returns its handle + * so @fn_vk{DestroyFramebuffer} is not called on destruction. The + * internal state is then equivalent to moved-from state. + * @see @ref wrap() + */ + VkFramebuffer release(); + + private: + /* Can't be a reference because of the NoCreate constructor */ + Device* _device; + + VkFramebuffer _handle; + HandleFlags _flags; +}; + +}} + +#endif diff --git a/src/Magnum/Vk/FramebufferCreateInfo.h b/src/Magnum/Vk/FramebufferCreateInfo.h new file mode 100644 index 000000000..2a4d1d2d9 --- /dev/null +++ b/src/Magnum/Vk/FramebufferCreateInfo.h @@ -0,0 +1,163 @@ +#ifndef Magnum_Vk_FramebufferCreateInfo_h +#define Magnum_Vk_FramebufferCreateInfo_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::FramebufferCreateInfo + * @m_since_latest + */ + +#include +#include + +#include "Magnum/Magnum.h" +#include "Magnum/Tags.h" +#include "Magnum/Vk/visibility.h" +#include "Magnum/Vk/Vk.h" +#include "Magnum/Vk/Vulkan.h" + +namespace Magnum { namespace Vk { + +/** +@brief Framebuffer creation info +@m_since_latest + +Wraps a @type_vk{FramebufferCreateInfo}. See +@ref Vk-Framebuffer-creation "Framebuffer creation" for usage information. +*/ +class MAGNUM_VK_EXPORT FramebufferCreateInfo { + public: + /** + * @brief Framebuffer creation flag + * + * Wraps @type_vk_keyword{FramebufferCreateFlagBits}. + * @see @ref Flags, @ref FramebufferCreateInfo(VkRenderPass, Containers::ArrayView>, const Vector3i&, Flags) + * @m_enum_values_as_keywords + */ + enum class Flag: UnsignedInt { + /** @todo VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT, 1.2, plus a + dedicated attachment-less constructor enabling that implicitly */ + }; + + /** + * @brief Framebuffer creation flags + * + * Type-safe wrapper for @type_vk_keyword{FramebufferCreateFlags}. + * @see @ref FramebufferCreateInfo(VkRenderPass, Containers::ArrayView>, const Vector3i&, Flags) + */ + typedef Containers::EnumSet Flags; + + /** + * @brief Construct a multi-layer framebuffer + * @param renderPass A @ref RenderPass or a raw Vulkan render pass + * handle the framebuffer is compatible with + * @param attachments Image views corresponding to all attachments + * listed in @ref RenderPassCreateInfo::setAttachments() + * @param size Width, height and layer count of the + * framebuffer + * @param flags Framebuffer creation flags + * + * The following @type_vk{FramebufferCreateInfo} fields are pre-filled + * in addition to `sType`, everything else is zero-filled: + * + * - `flags` + * - `renderPass` + * - `attachmentCount` and `pAttachments` to @p attachments + * - `width`, `height` and `layers` to @p size + */ + explicit FramebufferCreateInfo(VkRenderPass renderPass, Containers::ArrayView> attachments, const Vector3i& size, Flags flags = {}); + + /** @overload */ + explicit FramebufferCreateInfo(VkRenderPass renderPass, std::initializer_list> attachments, const Vector3i& size, Flags flags = {}); + + /** + * @brief Construct a single-layer framebuffer + * + * Equivalent to calling @ref FramebufferCreateInfo(VkRenderPass, Containers::ArrayView>, const Vector3i&, Flags) + * with the last dimension set to @cpp 1 @ce. + */ + explicit FramebufferCreateInfo(VkRenderPass renderPass, Containers::ArrayView> attachments, const Vector2i& size, Flags flags = {}); + + /** @overload */ + explicit FramebufferCreateInfo(VkRenderPass renderPass, std::initializer_list> attachments, const Vector2i& size, Flags flags = {}); + + /** + * @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 FramebufferCreateInfo(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 FramebufferCreateInfo(const VkFramebufferCreateInfo& info); + + /** @brief Copying is not allowed */ + FramebufferCreateInfo(const FramebufferCreateInfo&) = delete; + + /** @brief Move constructor */ + FramebufferCreateInfo(FramebufferCreateInfo&& other) noexcept; + + ~FramebufferCreateInfo(); + + /** @brief Copying is not allowed */ + FramebufferCreateInfo& operator=(const FramebufferCreateInfo&) = delete; + + /** @brief Move assignment */ + FramebufferCreateInfo& operator=(FramebufferCreateInfo&& other) noexcept; + + /** @brief Underlying @type_vk{FramebufferCreateInfo} structure */ + VkFramebufferCreateInfo& operator*() { return _info; } + /** @overload */ + const VkFramebufferCreateInfo& operator*() const { return _info; } + /** @overload */ + VkFramebufferCreateInfo* operator->() { return &_info; } + /** @overload */ + const VkFramebufferCreateInfo* operator->() const { return &_info; } + /** @overload */ + operator const VkFramebufferCreateInfo*() const { return &_info; } + + private: + VkFramebufferCreateInfo _info; + struct State; + Containers::Pointer _state; +}; + +CORRADE_ENUMSET_OPERATORS(FramebufferCreateInfo::Flags) + +}} + +/* Make the definition complete -- it doesn't make sense to have a CreateInfo + without the corresponding object anyway. */ +#include "Magnum/Vk/Framebuffer.h" + +#endif diff --git a/src/Magnum/Vk/RenderPass.h b/src/Magnum/Vk/RenderPass.h index 2bab9f65f..d5b34d8ce 100644 --- a/src/Magnum/Vk/RenderPass.h +++ b/src/Magnum/Vk/RenderPass.h @@ -47,7 +47,7 @@ namespace Implementation { struct DeviceState; } Wraps a @type_vk_keyword{RenderPass}, represents a collection of attachment descriptions, subpasses and their dependencies. The render pass description is independent of any specific image views used for attachments, these two are -connected together in a framebuffer. +connected together in a @ref Framebuffer. @section Vk-RenderPass-creation Render pass creation diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index 0a371ce63..fd3dc678a 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -33,6 +33,7 @@ corrade_add_test(VkDeviceFeaturesTest DeviceFeaturesTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkEnumsTest EnumsTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkExtensionsTest ExtensionsTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkExtensionPropertiesTest ExtensionPropertiesTest.cpp LIBRARIES MagnumVk) +corrade_add_test(VkFramebufferTest FramebufferTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkHandleTest HandleTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkImageTest ImageTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkImageViewTest ImageViewTest.cpp LIBRARIES MagnumVkTestLib) @@ -122,6 +123,7 @@ set_target_properties( VkEnumsTest VkExtensionsTest VkExtensionPropertiesTest + VkFramebufferTest VkHandleTest VkImageTest VkImageViewTest @@ -152,6 +154,7 @@ if(BUILD_VK_TESTS) corrade_add_test(VkDeviceVkTest DeviceVkTest.cpp LIBRARIES MagnumVkTestLib MagnumVulkanTester) corrade_add_test(VkDevicePropertiesVkTest DevicePropertiesVkTest.cpp LIBRARIES MagnumVkTestLib MagnumVulkanTester) corrade_add_test(VkExtensionPropertiesVkTest ExtensionPropertiesVkTest.cpp LIBRARIES MagnumVkTestLib) + corrade_add_test(VkFramebufferVkTest FramebufferVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester) corrade_add_test(VkLayerPropertiesVkTest LayerPropertiesVkTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkImageVkTest ImageVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester) corrade_add_test(VkImageViewVkTest ImageViewVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester) @@ -169,6 +172,7 @@ if(BUILD_VK_TESTS) VkDeviceVkTest VkDevicePropertiesVkTest VkExtensionPropertiesVkTest + VkFramebufferVkTest VkLayerPropertiesVkTest VkImageVkTest VkImageViewVkTest diff --git a/src/Magnum/Vk/Test/FramebufferTest.cpp b/src/Magnum/Vk/Test/FramebufferTest.cpp new file mode 100644 index 000000000..4e17efd3b --- /dev/null +++ b/src/Magnum/Vk/Test/FramebufferTest.cpp @@ -0,0 +1,175 @@ +/* + 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. +*/ + +#include +#include +#include + +#include "Magnum/Math/Vector3.h" +#include "Magnum/Vk/Device.h" +#include "Magnum/Vk/FramebufferCreateInfo.h" +#include "Magnum/Vk/ImageView.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct FramebufferTest: TestSuite::Tester { + explicit FramebufferTest(); + + void createInfoConstruct(); + void createInfoConstructLayered(); + void createInfoConstructNoInit(); + void createInfoConstructFromVk(); + void createInfoConstructCopy(); + void createInfoConstructMove(); + + void constructNoCreate(); + void constructCopy(); +}; + +FramebufferTest::FramebufferTest() { + addTests({&FramebufferTest::createInfoConstruct, + &FramebufferTest::createInfoConstructLayered, + &FramebufferTest::createInfoConstructNoInit, + &FramebufferTest::createInfoConstructFromVk, + &FramebufferTest::createInfoConstructCopy, + &FramebufferTest::createInfoConstructMove, + + &FramebufferTest::constructNoCreate, + &FramebufferTest::constructCopy}); +} + +void FramebufferTest::createInfoConstruct() { + Device device{NoCreate}; + auto renderPass = reinterpret_cast(0xdeadbeef); + auto a = ImageView::wrap(device, reinterpret_cast(0xcafe1), {}); + auto b = ImageView::wrap(device, reinterpret_cast(0xcafe2), {}); + auto c = ImageView::wrap(device, reinterpret_cast(0xcafe3), {}); + + /** @todo use a real flag once it exists */ + FramebufferCreateInfo info{renderPass, {a, b, c}, {256, 512}, FramebufferCreateInfo::Flag(0xbadda9)}; + CORRADE_COMPARE(info->flags, 0xbadda9); + CORRADE_COMPARE(info->renderPass, reinterpret_cast(0xdeadbeef)); + CORRADE_COMPARE(info->attachmentCount, 3); + CORRADE_VERIFY(info->pAttachments); + CORRADE_COMPARE(info->pAttachments[0], reinterpret_cast(0xcafe1)); + CORRADE_COMPARE(info->pAttachments[1], reinterpret_cast(0xcafe2)); + CORRADE_COMPARE(info->pAttachments[2], reinterpret_cast(0xcafe3)); + CORRADE_COMPARE(info->width, 256); + CORRADE_COMPARE(info->height, 512); + CORRADE_COMPARE(info->layers, 1); +} + +void FramebufferTest::createInfoConstructLayered() { + Device device{NoCreate}; + auto renderPass = reinterpret_cast(0xdeadbeef); + auto a = ImageView::wrap(device, reinterpret_cast(0xcafe1), {}); + auto b = ImageView::wrap(device, reinterpret_cast(0xcafe2), {}); + auto c = ImageView::wrap(device, reinterpret_cast(0xcafe3), {}); + + /** @todo use a real flag once it exists */ + FramebufferCreateInfo info{renderPass, {a, b, c}, {256, 512, 5}, FramebufferCreateInfo::Flag(0xbadda9)}; + CORRADE_COMPARE(info->flags, 0xbadda9); + CORRADE_COMPARE(info->renderPass, reinterpret_cast(0xdeadbeef)); + CORRADE_COMPARE(info->attachmentCount, 3); + CORRADE_VERIFY(info->pAttachments); + CORRADE_COMPARE(info->pAttachments[0], reinterpret_cast(0xcafe1)); + CORRADE_COMPARE(info->pAttachments[1], reinterpret_cast(0xcafe2)); + CORRADE_COMPARE(info->pAttachments[2], reinterpret_cast(0xcafe3)); + CORRADE_COMPARE(info->width, 256); + CORRADE_COMPARE(info->height, 512); + CORRADE_COMPARE(info->layers, 5); +} + +void FramebufferTest::createInfoConstructNoInit() { + FramebufferCreateInfo info{NoInit}; + info->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + new(&info) FramebufferCreateInfo{NoInit}; + CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); + + CORRADE_VERIFY((std::is_nothrow_constructible::value)); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void FramebufferTest::createInfoConstructFromVk() { + VkFramebufferCreateInfo vkInfo; + vkInfo.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + + FramebufferCreateInfo info{vkInfo}; + CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); +} + +void FramebufferTest::createInfoConstructCopy() { + CORRADE_VERIFY(!(std::is_copy_constructible{})); + CORRADE_VERIFY(!(std::is_copy_assignable{})); +} + +void FramebufferTest::createInfoConstructMove() { + Device device{NoCreate}; + auto renderPass = reinterpret_cast(0xdeadbeef); + auto view = ImageView::wrap(device, reinterpret_cast(0xcafe), {}); + + FramebufferCreateInfo a{renderPass, {view}, {256, 512}}; + + FramebufferCreateInfo b = std::move(a); + CORRADE_COMPARE(a->attachmentCount, 0); + CORRADE_VERIFY(!a->pAttachments); + CORRADE_COMPARE(b->renderPass, reinterpret_cast(0xdeadbeef)); + CORRADE_COMPARE(b->attachmentCount, 1); + CORRADE_VERIFY(b->pAttachments); + CORRADE_COMPARE(b->pAttachments[0], reinterpret_cast(0xcafe)); + + FramebufferCreateInfo c{VkFramebufferCreateInfo{}}; + c = std::move(b); + CORRADE_COMPARE(b->attachmentCount, 0); + CORRADE_VERIFY(!b->pAttachments); + CORRADE_COMPARE(c->renderPass, reinterpret_cast(0xdeadbeef)); + CORRADE_COMPARE(c->attachmentCount, 1); + CORRADE_VERIFY(c->pAttachments); + CORRADE_COMPARE(c->pAttachments[0], reinterpret_cast(0xcafe)); + + CORRADE_VERIFY(std::is_nothrow_move_constructible::value); + CORRADE_VERIFY(std::is_nothrow_move_assignable::value); +} + +void FramebufferTest::constructNoCreate() { + { + Framebuffer view{NoCreate}; + CORRADE_VERIFY(!view.handle()); + } + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void FramebufferTest::constructCopy() { + CORRADE_VERIFY(!std::is_copy_constructible{}); + CORRADE_VERIFY(!std::is_copy_assignable{}); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::FramebufferTest) diff --git a/src/Magnum/Vk/Test/FramebufferVkTest.cpp b/src/Magnum/Vk/Test/FramebufferVkTest.cpp new file mode 100644 index 000000000..83725ff34 --- /dev/null +++ b/src/Magnum/Vk/Test/FramebufferVkTest.cpp @@ -0,0 +1,142 @@ +/* + 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. +*/ + +#include + +#include "Magnum/Vk/FramebufferCreateInfo.h" +#include "Magnum/Vk/ImageCreateInfo.h" +#include "Magnum/Vk/ImageViewCreateInfo.h" +#include "Magnum/Vk/RenderPassCreateInfo.h" +#include "Magnum/Vk/Result.h" +#include "Magnum/Vk/VulkanTester.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct FramebufferVkTest: VulkanTester { + explicit FramebufferVkTest(); + + void construct(); + void constructMove(); + + void wrap(); +}; + +FramebufferVkTest::FramebufferVkTest() { + addTests({&FramebufferVkTest::construct, + &FramebufferVkTest::constructMove, + + &FramebufferVkTest::wrap}); +} + +void FramebufferVkTest::construct() { + /* Using a depth attachment as well even though not strictly necessary to + catch potential unexpected bugs */ + Image color{device(), ImageCreateInfo2D{ImageUsage::ColorAttachment, + VK_FORMAT_R8G8B8A8_UNORM, {256, 256}, 1}, MemoryFlag::DeviceLocal}; + Image depth{device(), ImageCreateInfo2D{ImageUsage::DepthStencilAttachment, + VK_FORMAT_D24_UNORM_S8_UINT, {256, 256}, 1}, MemoryFlag::DeviceLocal}; + ImageView colorView{device(), ImageViewCreateInfo2D{color}}; + ImageView depthView{device(), ImageViewCreateInfo2D{depth}}; + + RenderPass renderPass{device(), RenderPassCreateInfo{} + .setAttachments({ + color.format(), + depth.format() + }) + .addSubpass(SubpassDescription{} + .setColorAttachments({0}) + .setDepthStencilAttachment(1) + ) + }; + + { + Framebuffer framebuffer{device(), FramebufferCreateInfo{renderPass, { + colorView, + depthView + }, {256, 256}}}; + CORRADE_VERIFY(framebuffer.handle()); + CORRADE_COMPARE(framebuffer.handleFlags(), HandleFlag::DestroyOnDestruction); + } + + /* Shouldn't crash or anything */ + CORRADE_VERIFY(true); +} + +void FramebufferVkTest::constructMove() { + Image color{device(), ImageCreateInfo2D{ImageUsage::ColorAttachment, + VK_FORMAT_R8G8B8A8_UNORM, {256, 256}, 1}, MemoryFlag::DeviceLocal}; + ImageView colorView{device(), ImageViewCreateInfo2D{color}}; + RenderPass renderPass{device(), RenderPassCreateInfo{} + .setAttachments({color.format()}) + .addSubpass(SubpassDescription{}.setColorAttachments({0})) + }; + + Framebuffer a{device(), FramebufferCreateInfo{renderPass, { + colorView + }, {256, 256}}}; + VkFramebuffer handle = a.handle(); + + Framebuffer b = std::move(a); + CORRADE_VERIFY(!a.handle()); + CORRADE_COMPARE(b.handle(), handle); + CORRADE_COMPARE(b.handleFlags(), HandleFlag::DestroyOnDestruction); + + Framebuffer c{NoCreate}; + c = std::move(b); + CORRADE_VERIFY(!b.handle()); + CORRADE_COMPARE(b.handleFlags(), HandleFlags{}); + CORRADE_COMPARE(c.handle(), handle); + CORRADE_COMPARE(c.handleFlags(), HandleFlag::DestroyOnDestruction); + + CORRADE_VERIFY(std::is_nothrow_move_constructible::value); + CORRADE_VERIFY(std::is_nothrow_move_assignable::value); +} + +void FramebufferVkTest::wrap() { + Image color{device(), ImageCreateInfo2D{ImageUsage::ColorAttachment, + VK_FORMAT_R8G8B8A8_UNORM, {256, 256}, 1}, MemoryFlag::DeviceLocal}; + ImageView colorView{device(), ImageViewCreateInfo2D{color}}; + RenderPass renderPass{device(), RenderPassCreateInfo{} + .setAttachments({color.format()}) + .addSubpass(SubpassDescription{}.setColorAttachments({0})) + }; + + VkFramebuffer framebuffer{}; + CORRADE_COMPARE(Result(device()->CreateFramebuffer(device(), + FramebufferCreateInfo{renderPass, {colorView}, {256, 256}}, + nullptr, &framebuffer)), Result::Success); + + auto wrapped = Framebuffer::wrap(device(), framebuffer, HandleFlag::DestroyOnDestruction); + CORRADE_COMPARE(wrapped.handle(), framebuffer); + + /* Release the handle again, destroy by hand */ + CORRADE_COMPARE(wrapped.release(), framebuffer); + CORRADE_VERIFY(!wrapped.handle()); + device()->DestroyFramebuffer(device(), framebuffer, nullptr); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::FramebufferVkTest) diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index c4ad1548d..9eb00963e 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -49,6 +49,8 @@ class DeviceProperties; enum class DeviceType: Int; class Extension; class ExtensionProperties; +class Framebuffer; +class FramebufferCreateInfo; enum class HandleFlag: UnsignedByte; typedef Containers::EnumSet HandleFlags; class Image;