diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index 12d5a52f4..5686fd3c4 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -134,7 +134,7 @@ Vulkan function | Matching API @fn_vk{CreateEvent}, \n @fn_vk{DestroyEvent} | | @fn_vk{CreateFence}, \n @fn_vk{DestroyFence} | | @fn_vk{CreateFramebuffer}, \n @fn_vk{DestroyFramebuffer} | | -@fn_vk{CreateImage}, \n @fn_vk{DestroyImage} | | +@fn_vk{CreateImage}, \n @fn_vk{DestroyImage} | @ref Image constructor and destructor @fn_vk{CreateImageView}, \n @fn_vk{DestroyImageView} | | @fn_vk{CreateInstance}, \n @fn_vk{DestroyInstance} | @ref Instance constructor and destructor @fn_vk{CreatePipeline}, \n @fn_vk{DestroyPipeline} | | @@ -328,6 +328,7 @@ Vulkan structure | Matching API Vulkan structure | Matching API --------------------------------------- | ------------ +@type_vk{ImageCreateInfo} | @ref ImageCreateInfo @type_vk{InstanceCreateInfo} | @ref InstanceCreateInfo */ diff --git a/src/Magnum/Vk/CMakeLists.txt b/src/Magnum/Vk/CMakeLists.txt index 3f8906b93..afc2038da 100644 --- a/src/Magnum/Vk/CMakeLists.txt +++ b/src/Magnum/Vk/CMakeLists.txt @@ -31,6 +31,7 @@ set(MagnumVk_SRCS CommandPool.cpp Extensions.cpp Handle.cpp + Image.cpp Instance.cpp Memory.cpp Result.cpp @@ -56,6 +57,7 @@ set(MagnumVk_HEADERS Extensions.h ExtensionProperties.h Handle.h + Image.h Instance.h Integration.h LayerProperties.h diff --git a/src/Magnum/Vk/Image.cpp b/src/Magnum/Vk/Image.cpp new file mode 100644 index 000000000..cd9a81934 --- /dev/null +++ b/src/Magnum/Vk/Image.cpp @@ -0,0 +1,105 @@ +/* + 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 "Image.h" + +#include "Magnum/Vk/Device.h" +#include "Magnum/Vk/Handle.h" +#include "Magnum/Vk/Integration.h" +#include "Magnum/Vk/Result.h" + +namespace Magnum { namespace Vk { + +ImageCreateInfo::ImageCreateInfo(const VkImageType type, const ImageUsages usages, const VkFormat format, const Vector3i& size, const Int layers, const Int levels, const Int samples, const Flags flags): _info{} { + _info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + _info.flags = VkImageCreateFlags(flags); + _info.imageType = type; + _info.format = format; + _info.extent = VkExtent3D(size); + _info.mipLevels = levels; + _info.arrayLayers = layers; + _info.samples = VkSampleCountFlagBits(samples); + _info.tiling = VK_IMAGE_TILING_OPTIMAL; + _info.usage = VkImageUsageFlags(usages); + /* _info.sharingMode is implicitly VK_SHARING_MODE_EXCLUSIVE; + _info.queueFamilyIndexCount and _info.pQueueFamilyIndices should be + filled only for VK_SHARING_MODE_CONCURRENT */ + /* _info.initialLayout is implicitly VK_IMAGE_LAYOUT_UNDEFINED. The only + other possible value is VK_IMAGE_LAYOUT_PREDEFINED, which however also + needs VK_IMAGE_TILING_LINEAR, one sample and possibly other restrictions + -- https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkImageCreateInfo.html#_description + + Such images need to be allocated from host-visible memory which on + discrete GPUs is not fast for device access and thus is recommended to + go through a staging buffer (not image) instead. This is however still + useful for iGPUs, as the the memory is shared and this avoid an + expensive extra copy. */ +} + +ImageCreateInfo::ImageCreateInfo(NoInitT) noexcept {} + +ImageCreateInfo::ImageCreateInfo(const VkImageCreateInfo& info): + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _info(info) {} + +Image Image::wrap(Device& device, const VkImage handle, const HandleFlags flags) { + Image out{NoCreate}; + out._device = &device; + out._handle = handle; + out._flags = flags; + return out; +} + +Image::Image(Device& device, const ImageCreateInfo& info, NoAllocateT): _device{&device}, _flags{HandleFlag::DestroyOnDestruction} { + MAGNUM_VK_INTERNAL_ASSERT_RESULT(device->CreateImage(device, info, nullptr, &_handle)); +} + +Image::Image(NoCreateT): _device{}, _handle{} {} + +Image::Image(Image&& other) noexcept: _device{other._device}, _handle{other._handle}, _flags{other._flags} { + other._handle = {}; +} + +Image::~Image() { + if(_handle && (_flags & HandleFlag::DestroyOnDestruction)) + (**_device).DestroyImage(*_device, _handle, nullptr); +} + +Image& Image::operator=(Image&& other) noexcept { + using std::swap; + swap(other._device, _device); + swap(other._handle, _handle); + swap(other._flags, _flags); + return *this; +} + +VkImage Image::release() { + const VkImage handle = _handle; + _handle = {}; + return handle; +} + +}} diff --git a/src/Magnum/Vk/Image.h b/src/Magnum/Vk/Image.h new file mode 100644 index 000000000..cbc314887 --- /dev/null +++ b/src/Magnum/Vk/Image.h @@ -0,0 +1,418 @@ +#ifndef Magnum_Vk_Image_h +#define Magnum_Vk_Image_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::ImageCreateInfo, @ref Magnum::Vk::ImageCreateInfo1D, @ref Magnum::Vk::ImageCreateInfo2D, @ref Magnum::Vk::ImageCreateInfo3D, @ref Magnum::Vk::ImageCreateInfo1DArray, @ref Magnum::Vk::ImageCreateInfo2DArray, @ref Magnum::Vk::ImageCreateInfoCubeMap, @ref Magnum::Vk::ImageCreateInfoCubeMapArray, @ref Magnum::Vk::Image, enum @ref Magnum::Vk::ImageUsage, enum set @ref Magnum::Vk::ImageUsages + * @m_since_latest + */ + +#include + +#include "Magnum/DimensionTraits.h" +#include "Magnum/Magnum.h" +#include "Magnum/Math/Vector3.h" +#include "Magnum/Vk/Vk.h" +#include "Magnum/Vk/Vulkan.h" +#include "Magnum/Vk/visibility.h" + +namespace Magnum { namespace Vk { + +/** +@brief Image usage +@m_since_latest + +Wraps a @type_vk_keyword{ImageUsageFlagBits}. +@see @ref ImageUsages, @ref ImageCreateInfo +@m_enum_values_as_keywords +*/ +enum class ImageUsage: UnsignedInt { + /** Source of a transfer command */ + TransferSource = VK_IMAGE_USAGE_TRANSFER_SRC_BIT, + + /** Destination of a transfer command */ + TransferDestination = VK_IMAGE_USAGE_TRANSFER_DST_BIT, + + /** Sampled by a shader */ + Sampled = VK_IMAGE_USAGE_SAMPLED_BIT, + + /** Shader storage */ + Storage = VK_IMAGE_USAGE_STORAGE_BIT, + + /** Color attachment */ + ColorAttachment = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + + /** Depth/stencil attachment */ + DepthStencilAttachment = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, + + /** Transient attachment */ + TransientAttachment = VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT, + + /** Input attachment in a shader or framebuffer */ + InputAttachment = VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT +}; + +/** +@brief Image usages +@m_since_latest + +Type-safe wrapper for @type_vk_keyword{ImageUsageFlags}. +@see @ref ImageCreateInfo +*/ +typedef Containers::EnumSet ImageUsages; + +CORRADE_ENUMSET_OPERATORS(ImageUsages) + +/** +@brief Image creation info +@m_since_latest + +Wraps a @type_vk_keyword{ImageCreateInfo}. See @ref Image for usage +information. +@see @ref ImageCreateInfo1D, @ref ImageCreateInfo2D, @ref ImageCreateInfo3D, + @ref ImageCreateInfo1DArray, @ref ImageCreateInfo2DArray, + @ref ImageCreateInfoCubeMap, @ref ImageCreateInfoCubeMapArray +*/ +class MAGNUM_VK_EXPORT ImageCreateInfo { + public: + /** + * @brief Image creation flag + * + * Wraps @type_vk_keyword{ImageCreateFlagBits}. + * @see @ref Flags, @ref ImageCreateInfo(VkImageType, ImageUsages, VkFormat, const Vector3i&, Int, Int, Int, Flags) + * @m_enum_values_as_keywords + */ + enum class Flag: UnsignedInt { + /** @todo sparse binding/residency/aliased */ + + /** Allow creating a view of different format */ + MutableFormat = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT, + + /** Allow creating a cube map view */ + CubeCompatible = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT, + + /** @todo alias, 2D array compatible ... (Vulkan 1.1+) */ + }; + + /** + * @brief Image creation flags + * + * Type-safe wrapper for @type_vk_keyword{ImageCreateFlags}. + * @see @ref ImageCreateInfo(VkImageType, ImageUsages, VkFormat, const Vector3i&, Int, Int, Int, Flags), + * @ref ImageCreateInfo1D, @ref ImageCreateInfo2D, + * @ref ImageCreateInfo3D, @ref ImageCreateInfo1DArray, + * @ref ImageCreateInfo2DArray, @ref ImageCreateInfoCubeMap, + * @ref ImageCreateInfoCubeMapArray + */ + typedef Containers::EnumSet Flags; + + /** + * @brief Constructor + * @param type Image type + * @param usages Desired image usage. At least one flag is required. + * @param format Image format + * @param size Image size + * @param layers Array layer count + * @param levels Mip level count + * @param samples Sample count + * @param flags Image creation flags + * + * The following @type_vk{ImageCreateInfo} fields are pre-filled in + * addition to `sType`, everything else is zero-filled: + * + * - `flags` + * - `imageType` to @p type + * - `format` + * - `extent` to @p size + * - `mipLevels` to @p levels + * - `arrayLayers` to @p layers + * - `samples` + * - `tiling` to @val_vk{IMAGE_TILING_OPTIMAL,ImageTiling} + * - `usage` to @p usages + * - `sharingMode` to @val_vk{SHARING_MODE_EXCLUSIVE,SharingMode} + * - `initialLayout` to @val_vk{IMAGE_LAYOUT_UNDEFINED,ImageLayout} + * + * There are various restrictions on @p size, @p layers, @p levels for + * a particular @p type --- for common image types you're encouraged to + * make use of @ref ImageCreateInfo1D, @ref ImageCreateInfo2D, + * @ref ImageCreateInfo3D, @ref ImageCreateInfo1DArray, + * @ref ImageCreateInfo2DArray, @ref ImageCreateInfoCubeMap and + * @ref ImageCreateInfoCubeMapArray convenience classes instead of + * this constructor. + */ + explicit ImageCreateInfo(VkImageType type, ImageUsages usages, VkFormat format, const Vector3i& size, Int layers, Int levels, Int samples = 1, 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 ImageCreateInfo(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 ImageCreateInfo(const VkImageCreateInfo& info); + + /** @brief Underlying @type_vk{ImageCreateInfo} structure */ + VkImageCreateInfo& operator*() { return _info; } + /** @overload */ + const VkImageCreateInfo& operator*() const { return _info; } + /** @overload */ + VkImageCreateInfo* operator->() { return &_info; } + /** @overload */ + const VkImageCreateInfo* operator->() const { return &_info; } + /** @overload */ + operator const VkImageCreateInfo*() const { return &_info; } + + private: + VkImageCreateInfo _info; +}; + +CORRADE_ENUMSET_OPERATORS(ImageCreateInfo::Flags) + +/** +@brief Convenience constructor for 1D images +@m_since_latest + +Compared to the base @ref ImageCreateInfo constructor creates an image of type +@val_vk{IMAGE_TYPE_1D,ImageType} with the last two `size` components and +`layers` set to @cpp 1 @ce. + +Note that same as with the +@ref ImageCreateInfo::ImageCreateInfo(VkImageType, ImageUsages, VkFormat, const Vector3i&, Int, Int, Int, Flags) +constructor, at least one @ref ImageUsage value is required. +*/ +class ImageCreateInfo1D: public ImageCreateInfo { + public: + /** @brief Constructor */ + explicit ImageCreateInfo1D(ImageUsages usages, VkFormat format, Int size, Int levels, Int samples = 1, Flags flags = {}): ImageCreateInfo{VK_IMAGE_TYPE_1D, usages, format, {size, 1, 1}, 1, levels, samples, flags} {} +}; + +/** +@brief Convenience constructor for 2D images +@m_since_latest + +Compared to the base @ref ImageCreateInfo constructor creates an image of type +@val_vk{IMAGE_TYPE_2D,ImageType} with the last `size` component and `layers` +set to @cpp 1 @ce. + +Note that same as with the +@ref ImageCreateInfo::ImageCreateInfo(VkImageType, ImageUsages, VkFormat, const Vector3i&, Int, Int, Int, Flags) +constructor, at least one @ref ImageUsage value is required. +*/ +class ImageCreateInfo2D: public ImageCreateInfo { + public: + /** @brief Constructor */ + explicit ImageCreateInfo2D(ImageUsages usages, VkFormat format, const Vector2i& size, Int levels, Int samples = 1, Flags flags = {}): ImageCreateInfo{VK_IMAGE_TYPE_2D, usages, format, {size, 1}, 1, levels, samples, flags} {} +}; + +/** +@brief Convenience constructor for 3D images +@m_since_latest + +Compared to the base @ref ImageCreateInfo constructor creates an image of type +@val_vk{IMAGE_TYPE_3D,ImageType} with `layers` set to @cpp 1 @ce. + +Note that same as with the +@ref ImageCreateInfo::ImageCreateInfo(VkImageType, ImageUsages, VkFormat, const Vector3i&, Int, Int, Int, Flags) +constructor, at least one @ref ImageUsage value is required. +*/ +class ImageCreateInfo3D: public ImageCreateInfo { + public: + /** @brief Constructor */ + explicit ImageCreateInfo3D(ImageUsages usages, VkFormat format, const Vector3i& size, Int levels, Int samples = 1, Flags flags = {}): ImageCreateInfo{VK_IMAGE_TYPE_3D, usages, format, size, 1, levels, samples, flags} {} +}; + +/** +@brief Convenience constructor for 1D array images +@m_since_latest + +Compared to the base @ref ImageCreateInfo constructor creates an image of type +@val_vk{IMAGE_TYPE_1D,ImageType} with the last two `size` components set to +@cpp 1 @ce and `layers` set to @cpp size.y() @ce. + +Note that same as with the +@ref ImageCreateInfo::ImageCreateInfo(VkImageType, ImageUsages, VkFormat, const Vector3i&, Int, Int, Int, Flags) +constructor, at least one @ref ImageUsage value is required. +*/ +class ImageCreateInfo1DArray: public ImageCreateInfo { + public: + /** @brief Constructor */ + explicit ImageCreateInfo1DArray(ImageUsages usages, VkFormat format, const Vector2i& size, Int levels, Int samples = 1, Flags flags = {}): ImageCreateInfo{VK_IMAGE_TYPE_1D, usages, format, {size.x(), 1, 1}, size.y(), levels, samples, flags} {} +}; + +/** +@brief Convenience constructor for 2D array images +@m_since_latest + +Compared to the base @ref ImageCreateInfo constructor creates an image of type +@val_vk{IMAGE_TYPE_2D,ImageType} with the last `size` component set to +@cpp 1 @ce and `layers` set to @cpp size.z() @ce. + +Note that same as with the +@ref ImageCreateInfo::ImageCreateInfo(VkImageType, ImageUsages, VkFormat, const Vector3i&, Int, Int, Int, Flags) +constructor, at least one @ref ImageUsage value is required. +*/ +class ImageCreateInfo2DArray: public ImageCreateInfo { + public: + /** @brief Constructor */ + explicit ImageCreateInfo2DArray(ImageUsages usages, VkFormat format, const Vector3i& size, Int levels, Int samples = 1, Flags flags = {}): ImageCreateInfo{VK_IMAGE_TYPE_2D, usages, format, {size.xy(), 1}, size.z(), levels, samples, flags} {} +}; + +/** +@brief Convenience constructor for cube map images +@m_since_latest + +Compared to the base @ref ImageCreateInfo constructor creates an image of type +@val_vk{IMAGE_TYPE_2D,ImageType} with the last `size` component set to +@cpp 1 @ce, `layers` set to @cpp 6 @ce and `flags` additionally having +@ref Flag::CubeCompatible. + +Note that same as with the +@ref ImageCreateInfo::ImageCreateInfo(VkImageType, ImageUsages, VkFormat, const Vector3i&, Int, Int, Int, Flags) +constructor, at least one @ref ImageUsage value is required. +*/ +class ImageCreateInfoCubeMap: public ImageCreateInfo { + public: + /** @brief Constructor */ + explicit ImageCreateInfoCubeMap(ImageUsages usages, VkFormat format, const Vector2i& size, Int levels, Int samples = 1, Flags flags = {}): ImageCreateInfo{VK_IMAGE_TYPE_2D, usages, format, {size, 1}, 6, levels, samples, flags|Flag::CubeCompatible} {} +}; + +/** +@brief Convenience constructor for cube map array images +@m_since_latest + +Compared to the base @ref ImageCreateInfo constructor creates an image of type +@val_vk{IMAGE_TYPE_2D,ImageType} with the last `size` component set to +@cpp 1 @ce, `layers` set to @cpp size.y() @ce and `flags` additionally having +@ref Flag::CubeCompatible. + +Note that same as with the +@ref ImageCreateInfo::ImageCreateInfo(VkImageType, ImageUsages, VkFormat, const Vector3i&, Int, Int, Int, Flags) +constructor, at least one @ref ImageUsage value is required. +*/ +class ImageCreateInfoCubeMapArray: public ImageCreateInfo { + public: + /** @brief Constructor */ + explicit ImageCreateInfoCubeMapArray(ImageUsages usages, VkFormat format, const Vector3i& size, Int levels, Int samples = 1, Flags flags = {}): ImageCreateInfo{VK_IMAGE_TYPE_2D, usages, format, {size.xy(), 1}, size.z(), levels, samples, flags|Flag::CubeCompatible} {} +}; + +/** +@brief Image +@m_since_latest + +Wraps a @type_vk_keyword{Image}. +*/ +class MAGNUM_VK_EXPORT Image { + public: + /** + * @brief Wrap existing Vulkan handle + * @param device Vulkan device the image is created on + * @param handle The @type_vk{Image} handle + * @param flags Handle flags + * + * The @p handle is expected to be originating from @p device. Unlike + * an image created using a constructor, the Vulkan image is by default + * not deleted on destruction, use @p flags for different behavior. + * @see @ref release() + */ + static Image wrap(Device& device, VkImage handle, HandleFlags flags = {}); + + /** + * @brief Construct an image without allocating + * @param device Vulkan device to create the image on + * @param info Image creation info + * + * @see @fn_vk_keyword{CreateImage} + */ + explicit Image(Device& device, const ImageCreateInfo& info, NoAllocateT); + + /** + * @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 Image(NoCreateT); + + /** @brief Copying is not allowed */ + Image(const Image&) = delete; + + /** @brief Move constructor */ + Image(Image&& other) noexcept; + + /** + * @brief Destructor + * + * Destroys associated @type_vk{Image} handle, unless the instance + * was created using @ref wrap() without @ref HandleFlag::DestroyOnDestruction + * specified. + * @see @fn_vk_keyword{DestroyImage}, @ref release() + */ + ~Image(); + + /** @brief Copying is not allowed */ + Image& operator=(const Image&) = delete; + + /** @brief Move assignment */ + Image& operator=(Image&& other) noexcept; + + /** @brief Underlying @type_vk{Image} handle */ + VkImage handle() { return _handle; } + /** @overload */ + operator VkImage() { return _handle; } + + /** @brief Handle flags */ + HandleFlags handleFlags() const { return _flags; } + + /** + * @brief Release the underlying Vulkan image + * + * Releases ownership of the Vulkan image and returns its handle so + * @fn_vk{DestroyImage} is not called on destruction. The internal + * state is then equivalent to moved-from state. + * @see @ref wrap() + */ + VkImage release(); + + private: + /* Can't be a reference because of the NoCreate constructor */ + Device* _device; + + VkImage _handle; + HandleFlags _flags; +}; + +}} + +#endif diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index 06eee16fe..40d6f89c2 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -32,6 +32,7 @@ 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(VkHandleTest HandleTest.cpp LIBRARIES MagnumVk) +corrade_add_test(VkImageTest ImageTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkInstanceTest InstanceTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkIntegrationTest IntegrationTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkLayerPropertiesTest LayerPropertiesTest.cpp LIBRARIES MagnumVk) @@ -46,6 +47,7 @@ if(BUILD_VK_TESTS) corrade_add_test(VkDevicePropertiesVkTest DevicePropertiesVkTest.cpp LIBRARIES MagnumVkTestLib MagnumVulkanTester) corrade_add_test(VkExtensionPropertiesVkTest ExtensionPropertiesVkTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkLayerPropertiesVkTest LayerPropertiesVkTest.cpp LIBRARIES MagnumVkTestLib) + corrade_add_test(VkImageVkTest ImageVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester) corrade_add_test(VkInstanceVkTest InstanceVkTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkVersionVkTest VersionVkTest.cpp LIBRARIES MagnumVk) endif() diff --git a/src/Magnum/Vk/Test/ImageTest.cpp b/src/Magnum/Vk/Test/ImageTest.cpp new file mode 100644 index 000000000..ec69b2b86 --- /dev/null +++ b/src/Magnum/Vk/Test/ImageTest.cpp @@ -0,0 +1,204 @@ +/* + 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 "Magnum/Vk/Image.h" +#include "Magnum/Vk/Integration.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct ImageTest: TestSuite::Tester { + explicit ImageTest(); + + void createInfoConstruct(); + void createInfoConstruct1D(); + void createInfoConstruct2D(); + void createInfoConstruct3D(); + void createInfoConstruct1DArray(); + void createInfoConstruct2DArray(); + void createInfoConstructCubeMap(); + void createInfoConstructCubeMapArray(); + void createInfoConstructNoInit(); + void createInfoConstructFromVk(); + + void constructNoCreate(); + void constructCopy(); +}; + +ImageTest::ImageTest() { + addTests({&ImageTest::createInfoConstruct, + &ImageTest::createInfoConstruct1D, + &ImageTest::createInfoConstruct2D, + &ImageTest::createInfoConstruct3D, + &ImageTest::createInfoConstruct1DArray, + &ImageTest::createInfoConstruct2DArray, + &ImageTest::createInfoConstructCubeMap, + &ImageTest::createInfoConstructCubeMapArray, + &ImageTest::createInfoConstructNoInit, + &ImageTest::createInfoConstructFromVk, + + &ImageTest::constructNoCreate, + &ImageTest::constructCopy}); +} + +void ImageTest::createInfoConstruct() { + ImageCreateInfo info{VK_IMAGE_TYPE_2D, ImageUsage::Sampled, VK_FORMAT_R8G8B8A8_UNORM, {256, 128, 1}, 6, 8, 16, ImageCreateInfo::Flag::CubeCompatible}; + CORRADE_COMPARE(info->flags, VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT); + CORRADE_COMPARE(info->imageType, VK_IMAGE_TYPE_2D); + CORRADE_COMPARE(info->format, VK_FORMAT_R8G8B8A8_UNORM); + CORRADE_COMPARE(Vector3i(info->extent), (Vector3i{256, 128, 1})); + CORRADE_COMPARE(info->mipLevels, 8); + CORRADE_COMPARE(info->arrayLayers, 6); + CORRADE_COMPARE(info->samples, VK_SAMPLE_COUNT_16_BIT); + CORRADE_COMPARE(info->usage, VK_IMAGE_USAGE_SAMPLED_BIT); + CORRADE_COMPARE(info->tiling, VK_IMAGE_TILING_OPTIMAL); + CORRADE_COMPARE(info->sharingMode, VK_SHARING_MODE_EXCLUSIVE); + CORRADE_COMPARE(info->initialLayout, VK_IMAGE_LAYOUT_UNDEFINED); +} + +void ImageTest::createInfoConstruct1D() { + ImageCreateInfo1D info{ImageUsage::Storage, VK_FORMAT_R8G8B8A8_UNORM, 256, 8, 16, ImageCreateInfo::Flag::MutableFormat}; + CORRADE_COMPARE(info->flags, VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT); + CORRADE_COMPARE(info->imageType, VK_IMAGE_TYPE_1D); + CORRADE_COMPARE(info->format, VK_FORMAT_R8G8B8A8_UNORM); + CORRADE_COMPARE(Vector3i(info->extent), (Vector3i{256, 1, 1})); + CORRADE_COMPARE(info->mipLevels, 8); + CORRADE_COMPARE(info->arrayLayers, 1); + CORRADE_COMPARE(info->samples, VK_SAMPLE_COUNT_16_BIT); + CORRADE_COMPARE(info->usage, VK_IMAGE_USAGE_STORAGE_BIT); +} + +void ImageTest::createInfoConstruct2D() { + ImageCreateInfo2D info{ImageUsage::TransferDestination, VK_FORMAT_R8G8B8A8_UNORM, {256, 64}, 8, 16, ImageCreateInfo::Flag::MutableFormat}; + CORRADE_COMPARE(info->flags, VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT); + CORRADE_COMPARE(info->imageType, VK_IMAGE_TYPE_2D); + CORRADE_COMPARE(info->format, VK_FORMAT_R8G8B8A8_UNORM); + CORRADE_COMPARE(Vector3i(info->extent), (Vector3i{256, 64, 1})); + CORRADE_COMPARE(info->mipLevels, 8); + CORRADE_COMPARE(info->arrayLayers, 1); + CORRADE_COMPARE(info->samples, VK_SAMPLE_COUNT_16_BIT); + CORRADE_COMPARE(info->usage, VK_IMAGE_USAGE_TRANSFER_DST_BIT); +} + +void ImageTest::createInfoConstruct3D() { + ImageCreateInfo3D info{ImageUsage::InputAttachment, VK_FORMAT_R8G8B8A8_UNORM, {256, 64, 32}, 8, 16, ImageCreateInfo::Flag::MutableFormat}; + CORRADE_COMPARE(info->flags, VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT); + CORRADE_COMPARE(info->imageType, VK_IMAGE_TYPE_3D); + CORRADE_COMPARE(info->format, VK_FORMAT_R8G8B8A8_UNORM); + CORRADE_COMPARE(Vector3i(info->extent), (Vector3i{256, 64, 32})); + CORRADE_COMPARE(info->mipLevels, 8); + CORRADE_COMPARE(info->arrayLayers, 1); + CORRADE_COMPARE(info->samples, VK_SAMPLE_COUNT_16_BIT); + CORRADE_COMPARE(info->usage, VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT); +} + +void ImageTest::createInfoConstruct1DArray() { + ImageCreateInfo1DArray info{ImageUsage::TransferDestination, VK_FORMAT_R8G8B8A8_UNORM, {256, 64}, 8, 16, ImageCreateInfo::Flag::MutableFormat}; + CORRADE_COMPARE(info->flags, VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT); + CORRADE_COMPARE(info->imageType, VK_IMAGE_TYPE_1D); + CORRADE_COMPARE(info->format, VK_FORMAT_R8G8B8A8_UNORM); + CORRADE_COMPARE(Vector3i(info->extent), (Vector3i{256, 1, 1})); + CORRADE_COMPARE(info->mipLevels, 8); + CORRADE_COMPARE(info->arrayLayers, 64); + CORRADE_COMPARE(info->samples, VK_SAMPLE_COUNT_16_BIT); + CORRADE_COMPARE(info->usage, VK_IMAGE_USAGE_TRANSFER_DST_BIT); +} + +void ImageTest::createInfoConstruct2DArray() { + ImageCreateInfo2DArray info{ImageUsage::TransferDestination, VK_FORMAT_R8G8B8A8_UNORM, {256, 64, 32}, 8, 16, ImageCreateInfo::Flag::MutableFormat}; + CORRADE_COMPARE(info->flags, VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT); + CORRADE_COMPARE(info->imageType, VK_IMAGE_TYPE_2D); + CORRADE_COMPARE(info->format, VK_FORMAT_R8G8B8A8_UNORM); + CORRADE_COMPARE(Vector3i(info->extent), (Vector3i{256, 64, 1})); + CORRADE_COMPARE(info->mipLevels, 8); + CORRADE_COMPARE(info->arrayLayers, 32); + CORRADE_COMPARE(info->samples, VK_SAMPLE_COUNT_16_BIT); + CORRADE_COMPARE(info->usage, VK_IMAGE_USAGE_TRANSFER_DST_BIT); +} + +void ImageTest::createInfoConstructCubeMap() { + ImageCreateInfoCubeMap info{ImageUsage::TransferDestination, VK_FORMAT_R8G8B8A8_UNORM, {256, 256}, 8, 16, ImageCreateInfo::Flag::MutableFormat}; + CORRADE_COMPARE(info->flags, VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT|VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT); + CORRADE_COMPARE(info->imageType, VK_IMAGE_TYPE_2D); + CORRADE_COMPARE(info->format, VK_FORMAT_R8G8B8A8_UNORM); + CORRADE_COMPARE(Vector3i(info->extent), (Vector3i{256, 256, 1})); + CORRADE_COMPARE(info->mipLevels, 8); + CORRADE_COMPARE(info->arrayLayers, 6); + CORRADE_COMPARE(info->samples, VK_SAMPLE_COUNT_16_BIT); + CORRADE_COMPARE(info->usage, VK_IMAGE_USAGE_TRANSFER_DST_BIT); +} + +void ImageTest::createInfoConstructCubeMapArray() { + ImageCreateInfoCubeMapArray info{ImageUsage::TransferDestination, VK_FORMAT_R8G8B8A8_UNORM, {256, 256, 36}, 8, 16, ImageCreateInfo::Flag::MutableFormat}; + CORRADE_COMPARE(info->flags, VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT|VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT); + CORRADE_COMPARE(info->imageType, VK_IMAGE_TYPE_2D); + CORRADE_COMPARE(info->format, VK_FORMAT_R8G8B8A8_UNORM); + CORRADE_COMPARE(Vector3i(info->extent), (Vector3i{256, 256, 1})); + CORRADE_COMPARE(info->mipLevels, 8); + CORRADE_COMPARE(info->arrayLayers, 36); + CORRADE_COMPARE(info->samples, VK_SAMPLE_COUNT_16_BIT); + CORRADE_COMPARE(info->usage, VK_IMAGE_USAGE_TRANSFER_DST_BIT); +} + +void ImageTest::createInfoConstructNoInit() { + ImageCreateInfo info{NoInit}; + info->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + new(&info) ImageCreateInfo{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 ImageTest::createInfoConstructFromVk() { + VkImageCreateInfo vkInfo; + vkInfo.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + + ImageCreateInfo info{vkInfo}; + CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); +} + +void ImageTest::constructNoCreate() { + { + Image image{NoCreate}; + CORRADE_VERIFY(!image.handle()); + } + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void ImageTest::constructCopy() { + CORRADE_VERIFY(!(std::is_constructible{})); + CORRADE_VERIFY(!(std::is_assignable{})); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::ImageTest) diff --git a/src/Magnum/Vk/Test/ImageVkTest.cpp b/src/Magnum/Vk/Test/ImageVkTest.cpp new file mode 100644 index 000000000..30b2f3e95 --- /dev/null +++ b/src/Magnum/Vk/Test/ImageVkTest.cpp @@ -0,0 +1,185 @@ +/* + 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 "Magnum/Vk/Image.h" +#include "Magnum/Vk/Handle.h" +#include "Magnum/Vk/Result.h" +#include "Magnum/Vk/VulkanTester.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct ImageVkTest: VulkanTester { + explicit ImageVkTest(); + + void construct1D(); + void construct2D(); + void construct3D(); + void construct1DArray(); + void construct2DArray(); + void constructCubeMap(); + void constructCubeMapArray(); + void constructMove(); + + void wrap(); +}; + +ImageVkTest::ImageVkTest() { + addTests({&ImageVkTest::construct1D, + &ImageVkTest::construct2D, + &ImageVkTest::construct3D, + &ImageVkTest::construct1DArray, + &ImageVkTest::construct2DArray, + &ImageVkTest::constructCubeMap, + &ImageVkTest::constructCubeMapArray, + &ImageVkTest::constructMove, + + &ImageVkTest::wrap}); +} + +void ImageVkTest::construct1D() { + { + Image image{device(), ImageCreateInfo1D{ImageUsage::Sampled, + VK_FORMAT_R8G8B8A8_UNORM, 256, 8}, NoAllocate}; + CORRADE_VERIFY(image.handle()); + CORRADE_COMPARE(image.handleFlags(), HandleFlag::DestroyOnDestruction); + } + + /* Shouldn't crash or anything */ + CORRADE_VERIFY(true); +} + +void ImageVkTest::construct2D() { + { + Image image{device(), ImageCreateInfo2D{ImageUsage::Sampled, + VK_FORMAT_R8G8B8A8_UNORM, {256, 256}, 8}, NoAllocate}; + CORRADE_VERIFY(image.handle()); + CORRADE_COMPARE(image.handleFlags(), HandleFlag::DestroyOnDestruction); + } + + /* Shouldn't crash or anything */ + CORRADE_VERIFY(true); +} + +void ImageVkTest::construct3D() { + { + Image image{device(), ImageCreateInfo3D{ImageUsage::Sampled, + VK_FORMAT_R8G8B8A8_UNORM, {256, 256, 64}, 8}, NoAllocate}; + CORRADE_VERIFY(image.handle()); + CORRADE_COMPARE(image.handleFlags(), HandleFlag::DestroyOnDestruction); + } + + /* Shouldn't crash or anything */ + CORRADE_VERIFY(true); +} + +void ImageVkTest::construct1DArray() { + { + Image image{device(), ImageCreateInfo1DArray{ImageUsage::Sampled, + VK_FORMAT_R8G8B8A8_UNORM, {256, 64}, 8}, NoAllocate}; + CORRADE_VERIFY(image.handle()); + CORRADE_COMPARE(image.handleFlags(), HandleFlag::DestroyOnDestruction); + } + + /* Shouldn't crash or anything */ + CORRADE_VERIFY(true); +} + +void ImageVkTest::construct2DArray() { + { + Image image{device(), ImageCreateInfo2DArray{ImageUsage::Sampled, + VK_FORMAT_R8G8B8A8_UNORM, {256, 256, 64}, 8}, NoAllocate}; + CORRADE_VERIFY(image.handle()); + CORRADE_COMPARE(image.handleFlags(), HandleFlag::DestroyOnDestruction); + } + + /* Shouldn't crash or anything */ + CORRADE_VERIFY(true); +} + +void ImageVkTest::constructCubeMap() { + { + Image image{device(), ImageCreateInfoCubeMap{ImageUsage::Sampled, + VK_FORMAT_R8G8B8A8_UNORM, {256, 256}, 8}, NoAllocate}; + CORRADE_VERIFY(image.handle()); + CORRADE_COMPARE(image.handleFlags(), HandleFlag::DestroyOnDestruction); + } + + /* Shouldn't crash or anything */ + CORRADE_VERIFY(true); +} + +void ImageVkTest::constructCubeMapArray() { + { + Image image{device(), ImageCreateInfoCubeMapArray{ImageUsage::Sampled, + VK_FORMAT_R8G8B8A8_UNORM, {256, 256, 36}, 8}, NoAllocate}; + CORRADE_VERIFY(image.handle()); + CORRADE_COMPARE(image.handleFlags(), HandleFlag::DestroyOnDestruction); + } + + /* Shouldn't crash or anything */ + CORRADE_VERIFY(true); +} + +void ImageVkTest::constructMove() { + Image a{device(), ImageCreateInfo2D{ImageUsage::ColorAttachment, + VK_FORMAT_R8G8B8A8_UNORM, {256, 256}, 1}, NoAllocate}; + VkImage handle = a.handle(); + + Image b = std::move(a); + CORRADE_VERIFY(!a.handle()); + CORRADE_COMPARE(b.handle(), handle); + CORRADE_COMPARE(b.handleFlags(), HandleFlag::DestroyOnDestruction); + + Image 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 ImageVkTest::wrap() { + VkImage image{}; + CORRADE_COMPARE(Result(device()->CreateImage(device(), + ImageCreateInfo2D{ImageUsage::Sampled, + VK_FORMAT_R8G8B8A8_UNORM, {256, 256}, 8}, + nullptr, &image)), Result::Success); + CORRADE_VERIFY(image); + + auto wrapped = Image::wrap(device(), image, HandleFlag::DestroyOnDestruction); + CORRADE_COMPARE(wrapped.handle(), image); + + /* Release the handle again, destroy by hand */ + CORRADE_COMPARE(wrapped.release(), image); + CORRADE_VERIFY(!wrapped.handle()); + device()->DestroyImage(device(), image, nullptr); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::ImageVkTest) diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index 51afdd2a2..b3bfe970c 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -46,6 +46,7 @@ class Extension; class ExtensionProperties; enum class HandleFlag: UnsignedByte; typedef Containers::EnumSet HandleFlags; +class Image; class Instance; class InstanceCreateInfo; class InstanceExtension;