From 47add2f7dd1b1d10c9d54611c30ed9570fae4f5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 23 Jan 2021 15:43:05 +0100 Subject: [PATCH] Vk: implement image clears, buffer, image and buffer/image copies. Since depth/stencil images can't be linear, I needed buffer/image copies to test those, and conversely to test buffer/image copies I needed image clears. A pretty big chunk of work, and it led to a discovery of a SwiftShader bug, which I will work around next. First Vulkan driver workaround, so the whole scaffolding needs to get added as well. --- doc/snippets/MagnumVk.cpp | 115 ++ doc/vulkan-mapping.dox | 26 +- doc/vulkan-support.dox | 2 +- src/Magnum/Vk/Buffer.cpp | 98 ++ src/Magnum/Vk/Buffer.h | 214 ++++ src/Magnum/Vk/CommandBuffer.h | 268 ++++- src/Magnum/Vk/Device.cpp | 5 + src/Magnum/Vk/Image.cpp | 441 ++++++++ src/Magnum/Vk/Image.h | 1056 ++++++++++++++++++ src/Magnum/Vk/Implementation/DeviceState.cpp | 12 + src/Magnum/Vk/Implementation/DeviceState.h | 5 + src/Magnum/Vk/Pipeline.h | 10 +- src/Magnum/Vk/RenderPass.h | 14 +- src/Magnum/Vk/Test/BufferTest.cpp | 157 ++- src/Magnum/Vk/Test/BufferVkTest.cpp | 72 +- src/Magnum/Vk/Test/CMakeLists.txt | 4 +- src/Magnum/Vk/Test/ImageTest.cpp | 533 +++++++++ src/Magnum/Vk/Test/ImageVkTest.cpp | 955 +++++++++++++++- src/Magnum/Vk/Vk.h | 9 + 19 files changed, 3959 insertions(+), 37 deletions(-) diff --git a/doc/snippets/MagnumVk.cpp b/doc/snippets/MagnumVk.cpp index f81587c49..be9096a6f 100644 --- a/doc/snippets/MagnumVk.cpp +++ b/doc/snippets/MagnumVk.cpp @@ -241,6 +241,30 @@ cmd.fillBuffer(buffer, 0x00000000); /* [Buffer-usage-fill] */ } +{ +Vk::Device device{NoCreate}; +Vk::CommandBuffer cmd{NoCreate}; +UnsignedLong size{}; +/* [Buffer-usage-copy] */ +Vk::Buffer input{device, Vk::BufferCreateInfo{ + Vk::BufferUsage::TransferSource, size +}, Vk::MemoryFlag::HostVisible}; +Vk::Buffer vertices{device, Vk::BufferCreateInfo{ + Vk::BufferUsage::TransferDestination|Vk::BufferUsage::VertexBuffer, size +}, Vk::MemoryFlag::DeviceLocal}; + +DOXYGEN_IGNORE() + +cmd.copyBuffer({input, vertices, { + {0, 0, size} /* Copy the whole buffer */ + }}) + .pipelineBarrier(Vk::PipelineStage::Transfer, Vk::PipelineStage::VertexInput, { + /* Make the buffer memory available for vertex input */ + {Vk::Access::TransferWrite, Vk::Access::VertexAttributeRead, vertices} + }); +/* [Buffer-usage-copy] */ +} + { /* The include should be a no-op here since it was already included above */ /* [CommandPool-creation] */ @@ -483,6 +507,97 @@ image.bindMemory(memory, 0); /* [Image-creation-custom-allocation] */ } +{ +Vk::Device device{NoCreate}; +Vk::CommandBuffer cmd{NoCreate}; +/* [Image-usage-clear] */ +Vk::Image image{device, Vk::ImageCreateInfo2D{ + Vk::ImageUsage::TransferDestination|DOXYGEN_IGNORE(Vk::ImageUsage{}), Vk::PixelFormat::RGBA8Srgb, DOXYGEN_IGNORE({}, 1) +}, DOXYGEN_IGNORE(Vk::MemoryFlag{})}; + +DOXYGEN_IGNORE() + +cmd.pipelineBarrier(Vk::PipelineStage::TopOfPipe, Vk::PipelineStage::Transfer, { + /* Transition the image to a layout required by the clear operation */ + {Vk::Accesses{}, Vk::Access::TransferWrite, + Vk::ImageLayout::Undefined, Vk::ImageLayout::TransferDestination, image} + }) + .clearColorImage(image, Vk::ImageLayout::TransferDestination, 0x1f1f1f_srgbf); +/* [Image-usage-clear] */ +} + +{ +Vk::Device device{NoCreate}; +Vk::CommandBuffer cmd{NoCreate}; +/* [Image-usage-copy-from-buffer] */ +Vk::Buffer input{device, Vk::BufferCreateInfo{ + Vk::BufferUsage::TransferSource, 256*256*4 DOXYGEN_IGNORE() +}, Vk::MemoryFlag::HostVisible}; +Vk::Image texture{device, Vk::ImageCreateInfo2D{ + Vk::ImageUsage::TransferDestination|Vk::ImageUsage::Sampled, + Vk::PixelFormat::RGBA8Srgb, {256, 256}, DOXYGEN_IGNORE(1) +}, Vk::MemoryFlag::DeviceLocal}; + +DOXYGEN_IGNORE() + +cmd.pipelineBarrier(Vk::PipelineStage::TopOfPipe, Vk::PipelineStage::Transfer, { + /* Transition the image to a layout required by the copy operation */ + {Vk::Accesses{}, Vk::Access::TransferWrite, + Vk::ImageLayout::Undefined, Vk::ImageLayout::TransferDestination, texture} + }) + .copyBufferToImage({input, texture, Vk::ImageLayout::TransferDestination, { + /* Copy the whole buffer to the first level of the image */ + Vk::BufferImageCopy2D{0, Vk::ImageAspect::Color, 0, {{}, {256, 256}}} + }}) + .pipelineBarrier(Vk::PipelineStage::Transfer, Vk::PipelineStage::FragmentShader, { + /* Make the image memory available for fragment shader sampling */ + {Vk::Access::TransferWrite, Vk::Access::ShaderRead, + Vk::ImageLayout::TransferDestination, Vk::ImageLayout::ShaderReadOnly, texture} + }); +/* [Image-usage-copy-from-buffer] */ + +/* [Image-usage-copy-from-buffer-multiple] */ +cmd.copyBufferToImage(Vk::CopyBufferToImageInfo2D{ + input, texture, Vk::ImageLayout::Undefined, { + /* Assuming mip levels are tightly packed after each other */ + { 0, Vk::ImageAspect::Color, 0, {{}, {256, 256}}}, + {262144, Vk::ImageAspect::Color, 1, {{}, {128, 128}}}, + {327680, Vk::ImageAspect::Color, 2, {{}, { 64, 64}}}, + DOXYGEN_IGNORE() + } +}); +/* [Image-usage-copy-from-buffer-multiple] */ +} + +{ +Vk::Device device{NoCreate}; +Vk::CommandBuffer cmd{NoCreate}; +/* [Image-usage-copy-from-image] */ +Vk::Image a{device, Vk::ImageCreateInfo2D{ + Vk::ImageUsage::TransferSource|DOXYGEN_IGNORE(Vk::ImageUsage{}), + Vk::PixelFormat::RGBA8Srgb, {256, 256}, DOXYGEN_IGNORE(1) +}, DOXYGEN_IGNORE({})}; +Vk::Image b{device, Vk::ImageCreateInfo2D{ + Vk::ImageUsage::TransferDestination|DOXYGEN_IGNORE(Vk::ImageUsage{}), Vk::PixelFormat::RGBA8Srgb, {256, 256}, DOXYGEN_IGNORE(1) +}, DOXYGEN_IGNORE({})}; + +DOXYGEN_IGNORE() + +cmd.pipelineBarrier(Vk::PipelineStage::TopOfPipe, Vk::PipelineStage::Transfer, { + /* Transfer both images to a layout required by the copy operation */ + {Vk::Accesses{}, Vk::Access::TransferRead, + Vk::ImageLayout::DOXYGEN_IGNORE(Undefined), Vk::ImageLayout::TransferSource, a}, + {Vk::Accesses{}, Vk::Access::TransferWrite, + Vk::ImageLayout::Undefined, Vk::ImageLayout::TransferDestination, b} + }) + .copyImage({a, Vk::ImageLayout::TransferSource, + b, Vk::ImageLayout::TransferDestination, { + /* Copy the whole first layer/level between the images */ + {Vk::ImageAspect::Color, 0, 0, 1, {}, 0, 0, 1, {}, {256, 256, 1}} + }}); +/* [Image-usage-copy-from-image] */ +} + { 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 cdbe2c084..de1d9cf5f 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -120,14 +120,14 @@ Vulkan function | Matching API @fn_vk{CmdBuildAccelerationStructuresIndirectKHR} @m_class{m-label m-flat m-warning} **KHR** | | @fn_vk{CmdBuildAccelerationStructuresKHR} @m_class{m-label m-flat m-warning} **KHR** | | @fn_vk{CmdClearAttachments} | | -@fn_vk{CmdClearColorImage} | | -@fn_vk{CmdClearDepthStencilImage} | | +@fn_vk{CmdClearColorImage} | @ref CommandBuffer::clearColorImage() +@fn_vk{CmdClearDepthStencilImage} | @ref CommandBuffer::clearDepthStencilImage(), \n @ref CommandBuffer::clearDepthImage(), \n @ref CommandBuffer::clearStencilImage() @fn_vk{CmdCopyAccelerationStructureKHR} @m_class{m-label m-flat m-warning} **KHR** | | @fn_vk{CmdCopyAccelerationStructureToMemoryKHR} @m_class{m-label m-flat m-warning} **KHR** | | -@fn_vk{CmdCopyBuffer}, \n @fn_vk{CmdCopyBuffer2KHR} @m_class{m-label m-flat m-warning} **KHR** | | -@fn_vk{CmdCopyBufferToImage}, \n @fn_vk{CmdCopyBufferToImage2KHR} @m_class{m-label m-flat m-warning} **KHR** | | -@fn_vk{CmdCopyImage}, \n @fn_vk{CmdCopyImage2KHR} @m_class{m-label m-flat m-warning} **KHR** | | -@fn_vk{CmdCopyImageToBuffer}, \n @fn_vk{CmdCopyImageToBuffer2KHR} @m_class{m-label m-flat m-warning} **KHR** | | +@fn_vk{CmdCopyBuffer}, \n @fn_vk{CmdCopyBuffer2KHR} @m_class{m-label m-flat m-warning} **KHR** | @ref CommandBuffer::copyBuffer() +@fn_vk{CmdCopyBufferToImage}, \n @fn_vk{CmdCopyBufferToImage2KHR} @m_class{m-label m-flat m-warning} **KHR** | @ref CommandBuffer::copyBufferToImage() +@fn_vk{CmdCopyImage}, \n @fn_vk{CmdCopyImage2KHR} @m_class{m-label m-flat m-warning} **KHR** | @ref CommandBuffer::copyImage() +@fn_vk{CmdCopyImageToBuffer}, \n @fn_vk{CmdCopyImageToBuffer2KHR} @m_class{m-label m-flat m-warning} **KHR** | @ref CommandBuffer::copyImageToBuffer() @fn_vk{CmdCopyMemoryToAccelerationStructureKHR} @m_class{m-label m-flat m-warning} **KHR** | | @fn_vk{CmdCopyQueryPoolResults} | | @fn_vk{CmdDebugMarkerBeginEXT} @m_class{m-label m-danger} **deprecated** @m_class{m-label m-flat m-warning} **EXT**, \n @fn_vk{CmdDebugMarkerEndEXT} @m_class{m-label m-danger} **deprecated** @m_class{m-label m-flat m-warning} **EXT** | | @@ -400,10 +400,10 @@ Vulkan structure | Matching API @type_vk{BaseOutStructure} | | @type_vk{BindSparseInfo} | | @type_vk{BlitImageInfo2KHR} @m_class{m-label m-flat m-warning} **KHR** | | -@type_vk{BufferCopy}, \n @type_vk{BufferCopy2KHR} @m_class{m-label m-flat m-warning} **KHR** | | +@type_vk{BufferCopy}, \n @type_vk{BufferCopy2KHR} @m_class{m-label m-flat m-warning} **KHR** | @ref BufferCopy @type_vk{BufferCreateInfo} | @ref BufferCreateInfo @type_vk{BufferDeviceAddressInfo} @m_class{m-label m-flat m-success} **KHR, 1.2** | | -@type_vk{BufferImageCopy}, \n @type_vk{BufferImageCopy2KHR} @m_class{m-label m-flat m-warning} **KHR** | | +@type_vk{BufferImageCopy}, \n @type_vk{BufferImageCopy2KHR} @m_class{m-label m-flat m-warning} **KHR** | @ref BufferImageCopy @type_vk{BufferMemoryBarrier} | @ref BufferMemoryBarrier @type_vk{BufferMemoryRequirementsInfo}, \n @type_vk{BufferMemoryRequirementsInfo2} @m_class{m-label m-flat m-success} **KHR, 1.1** | not exposed, internal to @ref Buffer::memoryRequirements() @type_vk{BufferOpaqueCaptureAddressCreateInfo} @m_class{m-label m-flat m-success} **KHR, 1.2** | | @@ -427,10 +427,10 @@ Vulkan structure | Matching API @type_vk{ComponentMapping} | | @type_vk{ComputePipelineCreateInfo} | | @type_vk{ConformanceVersion} | | -@type_vk{CopyBufferInfo2KHR} @m_class{m-label m-flat m-warning} **KHR** | | -@type_vk{CopyBufferToImageInfo2KHR} @m_class{m-label m-flat m-warning} **KHR** | | -@type_vk{CopyImageInfo2KHR} @m_class{m-label m-flat m-warning} **KHR** | | -@type_vk{CopyImageToBuffer2KHR} @m_class{m-label m-flat m-warning} **KHR** | | +@type_vk{CopyBufferInfo2KHR} @m_class{m-label m-flat m-warning} **KHR** | @ref CopyBufferInfo +@type_vk{CopyBufferToImageInfo2KHR} @m_class{m-label m-flat m-warning} **KHR** | @ref CopyBufferToImageInfo +@type_vk{CopyImageInfo2KHR} @m_class{m-label m-flat m-warning} **KHR** | @ref CopyImageInfo +@type_vk{CopyImageToBuffer2KHR} @m_class{m-label m-flat m-warning} **KHR** | @ref CopyImageToBufferInfo @type_vk{CopyAccelerationStructureInfoKHR} @m_class{m-label m-flat m-warning} **KHR** | | @type_vk{CopyAccelerationStructureToMemoryInfoKHR} @m_class{m-label m-flat m-warning} **KHR** | | @type_vk{CopyMemoryToAccelerationStructureInfoKHR} @m_class{m-label m-flat m-warning} **KHR** | | @@ -528,7 +528,7 @@ Vulkan structure | Matching API Vulkan structure | Matching API --------------------------------------- | ------------ @type_vk{ImageBlit}, \n @type_vk{ImageBlit2KHR} @m_class{m-label m-flat m-warning} **KHR** | | -@type_vk{ImageCopy}, \n @type_vk{ImageCopy2KHR} @m_class{m-label m-flat m-warning} **KHR** | | +@type_vk{ImageCopy}, \n @type_vk{ImageCopy2KHR} @m_class{m-label m-flat m-warning} **KHR** | @ref ImageCopy @type_vk{ImageCreateInfo} | @ref ImageCreateInfo @type_vk{ImageFormatListCreateInfo} @m_class{m-label m-flat m-success} **KHR, 1.2** | | @type_vk{ImageFormatProperties}, \n @type_vk{ImageFormatProperties2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | diff --git a/doc/vulkan-support.dox b/doc/vulkan-support.dox index 2a9457cf3..62dcee1c7 100644 --- a/doc/vulkan-support.dox +++ b/doc/vulkan-support.dox @@ -126,7 +126,7 @@ Extension | Status @vk_extension{KHR,portability_subset} | done except properties @vk_extension{KHR,deferred_host_operations} | | @vk_extension{KHR,pipeline_library} | | -@vk_extension{KHR,copy_commands2} | | +@vk_extension{KHR,copy_commands2} | done except blit and resolve @vk_extension{KHR,ray_tracing_pipeline} | | @vk_extension{KHR,ray_query} | | @vk_extension{IMG,format_pvrtc} | done diff --git a/src/Magnum/Vk/Buffer.cpp b/src/Magnum/Vk/Buffer.cpp index 465a876fd..3c19f5d69 100644 --- a/src/Magnum/Vk/Buffer.cpp +++ b/src/Magnum/Vk/Buffer.cpp @@ -27,6 +27,8 @@ #include "BufferCreateInfo.h" #include "CommandBuffer.h" +#include + #include "Magnum/Vk/Assert.h" #include "Magnum/Vk/Device.h" #include "Magnum/Vk/DeviceProperties.h" @@ -160,9 +162,105 @@ VkResult Buffer::bindMemoryImplementation11(Device& device, UnsignedInt count, c return device->BindBufferMemory2(device, count, infos); } +BufferCopy::BufferCopy(const UnsignedLong sourceOffset, const UnsignedLong destinationOffset, const UnsignedLong size): _copy{} { + _copy.sType = VK_STRUCTURE_TYPE_BUFFER_COPY_2_KHR; + _copy.srcOffset = sourceOffset; + _copy.dstOffset = destinationOffset; + _copy.size = size; +} + +BufferCopy::BufferCopy(NoInitT) noexcept {} + +BufferCopy::BufferCopy(const VkBufferCopy2KHR& copy): + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _copy(copy) {} + +BufferCopy::BufferCopy(const VkBufferCopy& copy): _copy{ + VK_STRUCTURE_TYPE_BUFFER_COPY_2_KHR, + nullptr, + copy.srcOffset, + copy.dstOffset, + copy.size +} {} + +namespace { + +/* Used by CopyBufferInfo::vkBufferCopies() as well */ +VkBufferCopy vkBufferCopy(const VkBufferCopy2KHR& copy) { + CORRADE_ASSERT(!copy.pNext, + "Vk::BufferCopy: disallowing conversion to VkBufferCopy with non-empty pNext to prevent information loss", {}); + return { + copy.srcOffset, + copy.dstOffset, + copy.size + }; +} + +} + +VkBufferCopy BufferCopy::vkBufferCopy() const { + return Vk::vkBufferCopy(_copy); +} + +CopyBufferInfo::CopyBufferInfo(const VkBuffer source, const VkBuffer destination, const Containers::ArrayView regions): _info{} { + _info.sType = VK_STRUCTURE_TYPE_COPY_BUFFER_INFO_2_KHR; + _info.srcBuffer = source; + _info.dstBuffer = destination; + + /* Vulkan 1.2.166 doesn't allow anything in VkBufferCopy2KHR::pNext yet so + there's no point in storing the original BufferCopy wrapper */ + static_assert(sizeof(VkBufferCopy2KHR) == sizeof(BufferCopy), + "expecting BufferCopy to have no extra members referenced from pNext"); + Containers::ArrayView vkBufferCopies2; + _data = Containers::ArrayTuple{ + {NoInit, regions.size(), vkBufferCopies2} + }; + + for(std::size_t i = 0; i != regions.size(); ++i) { + /* Can't use {} with GCC 4.8 here because it tries to initialize the + first member instead of doing a copy */ + new(vkBufferCopies2 + i) VkBufferCopy2KHR(BufferCopy{regions[i]}); + } + + _info.regionCount = regions.size(); + _info.pRegions = vkBufferCopies2; +} + +CopyBufferInfo::CopyBufferInfo(const VkBuffer source, const VkBuffer destination, const std::initializer_list regions): CopyBufferInfo{source, destination, Containers::arrayView(regions)} {} + +CopyBufferInfo::CopyBufferInfo(NoInitT) noexcept {} + +CopyBufferInfo::CopyBufferInfo(const VkCopyBufferInfo2KHR& info): + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _info(info) {} + +Containers::Array CopyBufferInfo::vkBufferCopies() const { + Containers::Array out{NoInit, _info.regionCount}; + for(std::size_t i = 0; i != _info.regionCount; ++i) + new(out + i) VkBufferCopy(vkBufferCopy(_info.pRegions[i])); + return out; +} + CommandBuffer& CommandBuffer::fillBuffer(const VkBuffer buffer, const UnsignedLong offset, const UnsignedLong size, UnsignedInt value) { (**_device).CmdFillBuffer(_handle, buffer, offset, size, value); return *this; } +CommandBuffer& CommandBuffer::copyBuffer(const CopyBufferInfo& info) { + _device->state().cmdCopyBufferImplementation(*this, info); + return *this; +} + +void CommandBuffer::copyBufferImplementationDefault(CommandBuffer& self, const CopyBufferInfo& info) { + CORRADE_ASSERT(!info->pNext, + "Vk::CommandBuffer::copyBuffer(): disallowing extraction of CopyBufferInfo with non-empty pNext to prevent information loss", ); + return (**self._device).CmdCopyBuffer(self, info->srcBuffer, info->dstBuffer, info->regionCount, info.vkBufferCopies()); +} + +void CommandBuffer::copyBufferImplementationKHR(CommandBuffer& self, const CopyBufferInfo& info) { + return (**self._device).CmdCopyBuffer2KHR(self, info); +} + }} diff --git a/src/Magnum/Vk/Buffer.h b/src/Magnum/Vk/Buffer.h index cace8fd8d..ca29d0618 100644 --- a/src/Magnum/Vk/Buffer.h +++ b/src/Magnum/Vk/Buffer.h @@ -30,6 +30,8 @@ * @m_since_latest */ +#include +#include #include #include "Magnum/Magnum.h" @@ -85,6 +87,23 @@ The following snippet shows zero-filling the whole buffer using @snippet MagnumVk.cpp Buffer-usage-fill +@subsection Vk-Buffer-usage-copy Copying buffer data + +Most common buffer copy operation is uploading vertex data from a host-visible +to device-local memory. This is the preferred workflow for static data over +using a host-visible memory directly, since it usually isn't the fastest for +device access. + +The copy is done using @ref CommandBuffer::copyBuffer(). In most cases you'll +want to combine it with a @ref CommandBuffer::pipelineBarrier(PipelineStages, PipelineStages, Containers::ArrayView, DependencyFlags) "pipelineBarrier()" +after to make the memory visible for subsequent operations. The following +snippet shows populating a device-local vertex buffer from host-visible memory: + +@snippet MagnumVk.cpp Buffer-usage-copy + +It's also possible to copy data between buffers and images, see +@ref Vk-Image-usage-copy for examples. + @see @ref Image */ class MAGNUM_VK_EXPORT Buffer { @@ -241,6 +260,201 @@ class MAGNUM_VK_EXPORT Buffer { Memory _dedicatedMemory; }; +/** +@brief Buffer copy region +@m_since_latest + +Wraps a @type_vk_keyword{BufferCopy2KHR}. This class is subsequently passed to +a @ref CopyBufferInfo and then used in @ref CommandBuffer::copyBuffer(). See +@ref Vk-Buffer-usage-copy for usage information and examples. + +@section Vk-BufferCopy-compatibility Compatibility with VkBufferCopy + +While the class operates on the @type_vk{BufferCopy2KHR} structure that's +provided by the @vk_extension{KHR,copy_commands2} extension, conversion from +and to @type_vk{BufferCopy} is provided to some extent --- you can create a +@ref BufferCopy from it, call various methods on the instance and then get a +@type_vk{BufferCopy} back again using @ref vkBufferCopy(). + +For direct editing of the Vulkan structure, it's recommended to edit the +@type_vk{BufferCopy2KHR} fields and then perform the conversion instead of +editing the resulting @type_vk{BufferCopy}, as additional safety checks may be +done during the conversion to ensure no information is lost. + +@see @ref ImageCopy, @ref BufferImageCopy +*/ +class MAGNUM_VK_EXPORT BufferCopy { + public: + /** + * @brief Constructor + * @param sourceOffset Source buffer offset in bytes + * @param destinationOffset Destination buffer offset in bytes + * @param size Size to copy in bytes + * + * The following @type_vk{BufferCopy2KHR} fields are pre-filled in + * addition to `sType`, everything else is zero-filled: + * + * - `srcOffset` to @p sourceOffset + * - `dstOffset` to @p destinationOffset + * - `size` + */ + /*implicit*/ BufferCopy(UnsignedLong sourceOffset, UnsignedLong destinationOffset, UnsignedLong size); + + /** + * @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 BufferCopy(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 BufferCopy(const VkBufferCopy2KHR& copy); + + /** + * @brief Construct from a @type_vk{BufferCopy} + * + * Compared to the above, fills the common subset of + * @type_vk{BufferCopy2KHR}, sets `sType` and zero-fills `pNext`. + * @see @ref vkBufferCopy() + */ + explicit BufferCopy(const VkBufferCopy& copy); + + /** + * @brief Corresponding @type_vk{BufferCopy} structure + * + * Provided for compatibility with Vulkan implementations that don't + * support the @vk_extension{KHR,copy_commands2} extensions. See + * @ref Vk-BufferCopy-compatibility for more information. + * @see @ref BufferCopy(const VkBufferCopy&), + * @ref CopyBufferInfo::vkBufferCopies(), + * @ref CopyImageInfo::vkImageCopies(), + * @ref ImageCopy::vkImageCopy(), + * @ref CopyImageToBufferInfo::vkBufferImageCopies(), + * @ref CopyBufferToImageInfo::vkBufferImageCopies(), + * @ref BufferImageCopy::vkBufferImageCopy() + */ + VkBufferCopy vkBufferCopy() const; + + /** @brief Underlying @type_vk{BufferCopy2KHR} structure */ + VkBufferCopy2KHR& operator*() { return _copy; } + /** @overload */ + const VkBufferCopy2KHR& operator*() const { return _copy; } + /** @overload */ + VkBufferCopy2KHR* operator->() { return &_copy; } + /** @overload */ + const VkBufferCopy2KHR* operator->() const { return &_copy; } + /** @overload */ + operator const VkBufferCopy2KHR*() const { return &_copy; } + + /** + * @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 VkBufferCopy2KHR&() const { return _copy; } + + private: + VkBufferCopy2KHR _copy; +}; + +/** +@brief Buffer copy command +@m_since_latest + +Wraps a @type_vk_keyword{CopyBufferInfo2KHR}. This class is subsequently used +in @ref CommandBuffer::copyBuffer(). See @ref Vk-Buffer-usage-copy for more +information. + +@section Vk-CopyBufferInfo-compatibility Compatibility with vkCmdCopyBuffer() + +While the class operates on the @type_vk{CopyBufferInfo2KHR} structure that's +provided by the @vk_extension{KHR,copy_commands2} extension, conversion from +and to the set of parameters accepted by @fn_vk{CmdCopyBuffer} is provided to +some extent --- you can create @ref BufferCopy instances out of +@type_vk{BufferCopy} structures, pass them together with the rest to +@ref CopyBufferInfo and then get a @type_vk{BufferCopy} list back again using +@ref vkBufferCopies(). + +For direct editing of the Vulkan structure, it's recommended to edit the +@type_vk{CopyBufferInfo2KHR} fields and then perform the conversion instead of +editing the resulting @type_vk{BufferCopy} list, as additional safety checks +may be done during the conversion to ensure no information is lost. + +@see @ref CopyImageInfo, @ref CopyBufferToImageInfo, @ref CopyImageToBufferInfo +*/ +class MAGNUM_VK_EXPORT CopyBufferInfo { + public: + /** + * @brief Constructor + * @param source Source @ref Buffer or a raw Vulkan buffer + * handle. Expected to have been created with + * @ref BufferUsage::TransferSource. + * @param destination Destination @ref Buffer or a raw Vulkan buffer + * handle. Expected to have been created with + * @ref BufferUsage::TransferDestination. + * @param regions Regions to copy. There has to be at least one. + */ + /*implicit*/ CopyBufferInfo(VkBuffer source, VkBuffer destination, Containers::ArrayView regions); + /** @overload */ + /*implicit*/ CopyBufferInfo(VkBuffer source, VkBuffer destination, std::initializer_list regions); + + /** + * @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 CopyBufferInfo(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 CopyBufferInfo(const VkCopyBufferInfo2KHR& info); + + /** + * @brief Corresponding @type_vk{BufferCopy} structures + * + * Provided for compatibility with Vulkan implementations that don't + * support the @vk_extension{KHR,copy_commands2} extension. See + * @ref Vk-CopyBufferInfo-compatibility for more information. + * @see @ref BufferImageCopy::vkBufferImageCopy(), + * @ref BufferCopy::vkBufferCopy(), + * @ref CopyImageInfo::vkImageCopies(), + * @ref ImageCopy::vkImageCopy(), + * @ref CopyImageToBufferInfo::vkBufferImageCopies(), + * @ref BufferImageCopy::vkBufferImageCopy() + */ + Containers::Array vkBufferCopies() const; + + /** @brief Underlying @type_vk{CopyBufferInfo2KHR} structure */ + VkCopyBufferInfo2KHR& operator*() { return _info; } + /** @overload */ + const VkCopyBufferInfo2KHR& operator*() const { return _info; } + /** @overload */ + VkCopyBufferInfo2KHR* operator->() { return &_info; } + /** @overload */ + const VkCopyBufferInfo2KHR* operator->() const { return &_info; } + /** @overload */ + operator const VkCopyBufferInfo2KHR*() const { return &_info; } + + private: + VkCopyBufferInfo2KHR _info; + Containers::ArrayTuple _data; +}; + }} #endif diff --git a/src/Magnum/Vk/CommandBuffer.h b/src/Magnum/Vk/CommandBuffer.h index f2461cbac..2cd4005f6 100644 --- a/src/Magnum/Vk/CommandBuffer.h +++ b/src/Magnum/Vk/CommandBuffer.h @@ -387,6 +387,8 @@ class MAGNUM_VK_EXPORT CommandBuffer { * - and old and new @ref ImageLayout in @p imageMemoryBarriers have * to be equal * + * See @ref Vk-Buffer-usage-copy, @ref Vk-Image-usage-clear and + * @ref Vk-Image-usage-copy for usage examples. * @see @fn_vk_keyword{CmdPipelineBarrier} */ CommandBuffer& pipelineBarrier(PipelineStages sourceStages, PipelineStages destinationStages, Containers::ArrayView memoryBarriers, Containers::ArrayView bufferMemoryBarriers, Containers::ArrayView imageMemoryBarriers, DependencyFlags dependencyFlags = {}); @@ -409,7 +411,8 @@ class MAGNUM_VK_EXPORT CommandBuffer { * @return Reference to self (for method chaining) * * Equivalent to calling @ref pipelineBarrier(PipelineStages, PipelineStages, Containers::ArrayView, Containers::ArrayView, Containers::ArrayView, DependencyFlags) - * with empty @p memoryBarriers and @p imageBarriers. + * with empty @p memoryBarriers and @p imageBarriers. See + * @ref Vk-Buffer-usage-copy for usage examples. */ CommandBuffer& pipelineBarrier(PipelineStages sourceStages, PipelineStages destinationStages, Containers::ArrayView bufferMemoryBarriers, DependencyFlags dependencyFlags = {}); /** @overload */ @@ -420,7 +423,9 @@ class MAGNUM_VK_EXPORT CommandBuffer { * @return Reference to self (for method chaining) * * Equivalent to calling @ref pipelineBarrier(PipelineStages, PipelineStages, Containers::ArrayView, Containers::ArrayView, Containers::ArrayView, DependencyFlags) - * with empty @p memoryBarriers and @p bufferBarriers. + * with empty @p memoryBarriers and @p bufferBarriers. See + * @ref Vk-Image-usage-clear and @ref Vk-Image-usage-copy for usage + * examples. */ CommandBuffer& pipelineBarrier(PipelineStages sourceStages, PipelineStages destinationStages, Containers::ArrayView imageMemoryBarriers, DependencyFlags dependencyFlags = {}); /** @overload */ @@ -440,7 +445,9 @@ class MAGNUM_VK_EXPORT CommandBuffer { * * Allowed only outside of a render pass. See @ref Vk-Buffer-usage-fill * for a usage example. - * @see @fn_vk_keyword{CmdFillBuffer} + * @see @ref clearColorImage(), @ref clearDepthStencilImage(), + * @ref clearDepthImage(), @ref clearStencilImage(), + * @fn_vk_keyword{CmdFillBuffer} */ CommandBuffer& fillBuffer(VkBuffer buffer, UnsignedLong offset, UnsignedLong size, UnsignedInt value); @@ -456,6 +463,249 @@ class MAGNUM_VK_EXPORT CommandBuffer { return fillBuffer(buffer, 0, VK_WHOLE_SIZE, value); } + /** + * @brief Clear a floating-point or normalized color image + * @param image An @ref Image or a raw Vulkan image handle to + * clear. Expected to have been created with + * @ref ImageUsage::TransferDestination and a floating-point or + * normalized non-compressed @ref PixelFormat usable for transfer + * destination. + * @param layout Image layout. Can be either + * @ref ImageLayout::General or + * @ref ImageLayout::TransferDestination. + * @param color Color to clear the image with + * @return Reference to self (for method chaining) + * + * Allowed only outside of a render pass. For clearing inside a render + * pass you can either specify @ref AttachmentLoadOperation::Clear for + * a particular attachment, which will clear it on render pass begin + * (see @ref Vk-RenderPass-usage for an example), or use + * @fn_vk{CmdClearAttachments} which allows you to clear at any time + * inside a render pass. See @ref Vk-Image-usage-clear for a usage + * example. + * + * A single @type_vk{ImageSubresourceRange} is passed to the command, + * with the following fields are set: + * + * - `aspectMask` to @val_vk{IMAGE_ASPECT_COLOR,ImageAspectFlagBits} + * - `baseMipLevel` to @cpp 0 @ce + * - `levelCount` to @def_vk{REMAINING_MIP_LEVELS} + * - `baseArrayLayer` to @cpp 0 @ce + * - `layerCount` to @def_vk{REMAINING_ARRAY_LAYERS} + * + * @attention This function currently clears all layers and mip levels + * of the image, with no ability to specify particular layer/level + * ranges. For that please use the Vulkan API directly. + * + * @see @fn_vk_keyword{CmdClearColorImage} + * + * @todoc mention ImageLayout::SharedPresent being allowed once the + * extension is exposed + */ + CommandBuffer& clearColorImage(VkImage image, ImageLayout layout, const Color4& color); + + /** + * @brief Clear a signed integral color image + * @return Reference to self (for method chaining) + * + * Allowed only outside of a render pass. Behaves like + * @ref clearColorImage(VkImage, ImageLayout, const Color4&), except + * that it's meant for images with a signed integral non-compressed + * @ref PixelFormat. + */ + CommandBuffer& clearColorImage(VkImage image, ImageLayout layout, const Vector4i& color); + + /** + * @brief Clear an unsigned integral color image + * @return Reference to self (for method chaining) + * + * Allowed only outside of a render pass. Behaves like + * @ref clearColorImage(VkImage, ImageLayout, const Color4&), except + * that it's meant for images with an unsigned integral non-compressed + * @ref PixelFormat. + */ + CommandBuffer& clearColorImage(VkImage image, ImageLayout layout, const Vector4ui& color); + + /** + * @brief Clear a combined depth/stencil image + * @param image An @ref Image or a raw Vulkan image handle to + * clear. Expected to have been be created with + * @ref ImageUsage::TransferDestination and a combined + * depth/stencil @ref PixelFormat usable for transfer destination. + * @param layout Image layout. Can be either + * @ref ImageLayout::General or + * @ref ImageLayout::TransferDestination. + * @param depth Depth value to clear with + * @param stencil Stencil value to clear with + * @return Reference to self (for method chaining) + * + * Allowed only outside of a render pass. For clearing inside a render + * pass you can either specify @ref AttachmentLoadOperation::Clear for + * a particular attachment, which will clear it on render pass begin + * (see @ref Vk-RenderPass-usage for an example), or use + * @fn_vk{CmdClearAttachments} which allows you to clear at any point + * inside a render pass. For clearing a depth-only or a stencil-only + * image (or just a depth orstencil buffer of a combined depth/stencil + * image) use @ref clearDepthImage() or @ref clearStencilImage() + * instead. See @ref Vk-Image-usage-clear for a usage example. + * + * A single @type_vk{ImageSubresourceRange} is passed to the command, + * with the following fields are set: + * + * - `aspectMask` to @val_vk{IMAGE_ASPECT_DEPTH,ImageAspectFlagBits} + * and @val_vk{IMAGE_ASPECT_STENCIL,ImageAspectFlagBits} + * - `baseMipLevel` to @cpp 0 @ce + * - `levelCount` to @def_vk{REMAINING_MIP_LEVELS} + * - `baseArrayLayer` to @cpp 0 @ce + * - `layerCount` to @def_vk{REMAINING_ARRAY_LAYERS} + * + * @attention This function currently clears all layers and mip levels + * of the image, with no ability to specify particular layer/level + * ranges. For that please use the Vulkan API directly. + * + * @see @fn_vk_keyword{CmdClearDepthStencilImage} + */ + CommandBuffer& clearDepthStencilImage(VkImage image, ImageLayout layout, Float depth, UnsignedInt stencil); + + /** + * @brief Clear a depth-only image + * @param image An @ref Image or a raw Vulkan image handle to + * clear. Expected to have been be created with + * @ref ImageUsage::TransferDestination and a depth-only + * @ref PixelFormat usable for transfer destination. + * @param layout Image layout. Can be either + * @ref ImageLayout::General or + * @ref ImageLayout::TransferDestination. + * @param depth Depth value to clear with + * @return Reference to self (for method chaining) + * + * Allowed only outside of a render pass. For clearing inside a render + * pass you can either specify @ref AttachmentLoadOperation::Clear for + * a particular attachment, which will clear it on render pass begin + * (see @ref Vk-RenderPass-usage for an example), or use + * @fn_vk{CmdClearAttachments} which allows you to clear at any point + * inside a render pass. For clearing a combined depth/stencil image + * use @ref clearDepthStencilImage(), for clearing a stencil-only image + * or just the stencil buffer of a combined depth/stencil image use + * @ref clearStencilImage() instead. See @ref Vk-Image-usage-clear for + * a usage example. + * + * A single @type_vk{ImageSubresourceRange} is passed to the command, + * with the following fields are set: + * + * - `aspectMask` to @val_vk{IMAGE_ASPECT_DEPTH,ImageAspectFlagBits} + * - `baseMipLevel` to @cpp 0 @ce + * - `levelCount` to @def_vk{REMAINING_MIP_LEVELS} + * - `baseArrayLayer` to @cpp 0 @ce + * - `layerCount` to @def_vk{REMAINING_ARRAY_LAYERS} + * + * @attention This function currently clears all layers and mip levels + * of the image, with no ability to specify particular layer/level + * ranges. For that please use the Vulkan API directly. + * + * @see @fn_vk_keyword{CmdClearDepthStencilImage} + */ + CommandBuffer& clearDepthImage(VkImage image, ImageLayout layout, Float depth); + + /** + * @brief Clear a stencil-only image + * @param image An @ref Image or a raw Vulkan image handle to + * clear. Expected to have been be created with + * @ref ImageUsage::TransferDestination and a stencil-only + * @ref PixelFormat usable for transfer destination. + * @param layout Image layout. Can be either + * @ref ImageLayout::General or + * @ref ImageLayout::TransferDestination. + * @param stencil Stencil value to clear with + * @return Reference to self (for method chaining) + * + * Allowed only outside of a render pass. For clearing inside a render + * pass you can either specify @ref AttachmentLoadOperation::Clear for + * a particular attachment, which will clear it on render pass begin + * (see @ref Vk-RenderPass-usage for an example), or use + * @fn_vk{CmdClearAttachments} which allows you to clear at any point + * inside a render pass. For clearing a combined depth/stencil image + * use @ref clearDepthStencilImage(), for clearing a depth-only image + * or just the depth buffer of a combined depth/stencil image use + * @ref clearDepthImage() instead. See @ref Vk-Image-usage-clear for a + * usage example. + * + * A single @type_vk{ImageSubresourceRange} is passed to the command, + * with the following fields set: + * + * - `aspectMask` to @val_vk{IMAGE_ASPECT_STENCIL,ImageAspectFlagBits} + * - `baseMipLevel` to @cpp 0 @ce + * - `levelCount` to @def_vk{REMAINING_MIP_LEVELS} + * - `baseArrayLayer` to @cpp 0 @ce + * - `layerCount` to @def_vk{REMAINING_ARRAY_LAYERS} + * + * @attention This function currently clears all layers and mip levels + * of the image, with no ability to specify particular layer/level + * ranges. For that please use the Vulkan API directly. + * + * @see @fn_vk_keyword{CmdClearDepthStencilImage} + */ + CommandBuffer& clearStencilImage(VkImage image, ImageLayout layout, UnsignedInt stencil); + + /** @todo clearAttachments(), I'm too lazy to expose all the needed + structures right now */ + + /** + * @brief Copy data between buffer regions + * @return Reference to self (for method chaining) + * + * Allowed only outside of a render pass. If the + * @vk_extension{KHR,copy_commands2} extension is not supported and + * enabled on the device, the `pNext` chain in @p info and its + * substructures has to be empty. See @ref Vk-Buffer-usage-copy for + * details and usage examples. + * @see @fn_vk_keyword{CmdCopyBuffer2KHR}, + * @fn_vk_keyword{CmdCopyBuffer} + */ + CommandBuffer& copyBuffer(const CopyBufferInfo& info); + + /** + * @brief Copy data between images + * @return Reference to self (for method chaining) + * + * Allowed only outside of a render pass. If the + * @vk_extension{KHR,copy_commands2} extension is not supported + * and enabled on the device, the `pNext` chain in @p info and its + * substructures has to be empty. See @ref Vk-Image-usage-copy for + * details and usage examples. + * @see @fn_vk_keyword{CmdCopyImage2KHR}, + * @fn_vk_keyword{CmdCopyImage} + */ + CommandBuffer& copyImage(const CopyImageInfo& info); + + /** + * @brief Copy data from a buffer into an image + * @return Reference to self (for method chaining) + * + * Allowed only outside of a render pass. If the + * @vk_extension{KHR,copy_commands2} extension is not supported and + * enabled on the device, the `pNext` chain in @p info and its + * substructures has to be empty. See @ref Vk-Image-usage-copy for + * details and usage examples. + * @see @fn_vk_keyword{CmdCopyBufferToImage2KHR}, + * @fn_vk_keyword{CmdCopyBufferToImage} + */ + CommandBuffer& copyBufferToImage(const CopyBufferToImageInfo& info); + + /** + * @brief Copy image data into a buffer + * @return Reference to self (for method chaining) + * + * Allowed only outside of a render pass. If the + * @vk_extension{KHR,copy_commands2} extension is not supported and + * enabled on the device, the `pNext` chain in @p info and its + * substructures has to be empty. See @ref Vk-Image-usage-copy for + * details and usage examples. + * @see @fn_vk_keyword{CmdCopyImageToBuffer2KHR}, + * @fn_vk_keyword{CmdCopyImageToBuffer} + */ + CommandBuffer& copyImageToBuffer(const CopyImageToBufferInfo& info); + private: friend CommandPool; friend Implementation::DeviceState; @@ -472,6 +722,18 @@ class MAGNUM_VK_EXPORT CommandBuffer { MAGNUM_VK_LOCAL static void endRenderPassImplementationKHR(CommandBuffer& self, const VkSubpassEndInfo& endInfo); MAGNUM_VK_LOCAL static void endRenderPassImplementation12(CommandBuffer& self, const VkSubpassEndInfo& endInfo); + MAGNUM_VK_LOCAL static void copyBufferImplementationDefault(CommandBuffer& self, const CopyBufferInfo& info); + MAGNUM_VK_LOCAL static void copyBufferImplementationKHR(CommandBuffer& self, const CopyBufferInfo& info); + + MAGNUM_VK_LOCAL static void copyImageImplementationDefault(CommandBuffer& self, const CopyImageInfo& info); + MAGNUM_VK_LOCAL static void copyImageImplementationKHR(CommandBuffer& self, const CopyImageInfo& info); + + MAGNUM_VK_LOCAL static void copyBufferToImageImplementationDefault(CommandBuffer& self, const CopyBufferToImageInfo& info); + MAGNUM_VK_LOCAL static void copyBufferToImageImplementationKHR(CommandBuffer& self, const CopyBufferToImageInfo& info); + + MAGNUM_VK_LOCAL static void copyImageToBufferImplementationDefault(CommandBuffer& self, const CopyImageToBufferInfo& info); + MAGNUM_VK_LOCAL static void copyImageToBufferImplementationKHR(CommandBuffer& self, const CopyImageToBufferInfo& info); + /* Can't be a reference because of the NoCreate constructor */ Device* _device; diff --git a/src/Magnum/Vk/Device.cpp b/src/Magnum/Vk/Device.cpp index 719355613..9e33cc0ec 100644 --- a/src/Magnum/Vk/Device.cpp +++ b/src/Magnum/Vk/Device.cpp @@ -175,6 +175,11 @@ DeviceCreateInfo::DeviceCreateInfo(DeviceProperties& deviceProperties, const Ext addEnabledExtensions(); } + /* Enable the KHR_copy_commands2 extension. Not in any Vulkan version + yet. */ + if(extensionProperties->isSupported()) + addEnabledExtensions(); + /* Enable the KHR_portability_subset extension, which *has to be* enabled when available. Not enabling any of its features though, that responsibility lies on the user. */ diff --git a/src/Magnum/Vk/Image.cpp b/src/Magnum/Vk/Image.cpp index b4c2d1aa5..40a810409 100644 --- a/src/Magnum/Vk/Image.cpp +++ b/src/Magnum/Vk/Image.cpp @@ -25,9 +25,12 @@ #include "Image.h" #include "ImageCreateInfo.h" +#include "CommandBuffer.h" #include +#include +#include "Magnum/Math/Color.h" #include "Magnum/Vk/Assert.h" #include "Magnum/Vk/Device.h" #include "Magnum/Vk/DeviceProperties.h" @@ -233,4 +236,442 @@ VkResult Image::bindMemoryImplementation11(Device& device, UnsignedInt count, co return device->BindImageMemory2(device, count, infos); } +ImageCopy::ImageCopy(const ImageAspects aspects, const Int sourceLevel, const Int sourceLayerOffset, const Int sourceLayerCount, const Vector3i& sourceOffset, const Int destinationLevel, const Int destinationLayerOffset, const Int destinationLayerCount, const Vector3i& destinationOffset, const Vector3i& size): _copy{} { + _copy.sType = VK_STRUCTURE_TYPE_IMAGE_COPY_2_KHR; + _copy.srcSubresource.aspectMask = VkImageAspectFlags(aspects); + _copy.srcSubresource.mipLevel = sourceLevel; + _copy.srcSubresource.baseArrayLayer = sourceLayerOffset; + _copy.srcSubresource.layerCount = sourceLayerCount; + _copy.srcOffset = VkOffset3D(sourceOffset); + _copy.dstSubresource.aspectMask = VkImageAspectFlags(aspects); + _copy.dstSubresource.mipLevel = destinationLevel; + _copy.dstSubresource.baseArrayLayer = destinationLayerOffset; + _copy.dstSubresource.layerCount = destinationLayerCount; + _copy.dstOffset = VkOffset3D(destinationOffset); + _copy.extent = VkExtent3D(size); +} + +ImageCopy::ImageCopy(NoInitT) noexcept {} + +ImageCopy::ImageCopy(const VkImageCopy2KHR& copy): + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _copy(copy) {} + +ImageCopy::ImageCopy(const VkImageCopy& copy): _copy{ + VK_STRUCTURE_TYPE_IMAGE_COPY_2_KHR, + nullptr, + copy.srcSubresource, + copy.srcOffset, + copy.dstSubresource, + copy.dstOffset, + copy.extent +} {} + +namespace { + +/* Used by CopyImageInfo::vkCopyImageInfo() as well */ +VkImageCopy vkImageCopy(const VkImageCopy2KHR& copy) { + CORRADE_ASSERT(!copy.pNext, + "Vk::ImageCopy: disallowing conversion to VkImageCopy with non-empty pNext to prevent information loss", {}); + return { + copy.srcSubresource, + copy.srcOffset, + copy.dstSubresource, + copy.dstOffset, + copy.extent + }; +} + +} + +VkImageCopy ImageCopy::vkImageCopy() const { + return Vk::vkImageCopy(_copy); +} + +CopyImageInfo::CopyImageInfo(const VkImage source, const ImageLayout sourceLayout, const VkImage destination, const ImageLayout destinationLayout, const Containers::ArrayView regions): _info{} { + _info.sType = VK_STRUCTURE_TYPE_COPY_IMAGE_INFO_2_KHR; + _info.srcImage = source; + _info.srcImageLayout = VkImageLayout(sourceLayout); + _info.dstImage = destination; + _info.dstImageLayout = VkImageLayout(destinationLayout); + + /* Vulkan 1.2.166 doesn't allow anything in VkImageCopy2KHR::pNext yet + so there's no point in storing the original ImageCopy wrapper */ + static_assert(sizeof(VkImageCopy2KHR) == sizeof(ImageCopy), + "expecting ImageCopy to have no extra members referenced from pNext"); + Containers::ArrayView vkImageCopies2; + _data = Containers::ArrayTuple{ + {NoInit, regions.size(), vkImageCopies2} + }; + + for(std::size_t i = 0; i != regions.size(); ++i) { + /* Can't use {} with GCC 4.8 here because it tries to initialize the + first member instead of doing a copy */ + new(vkImageCopies2 + i) VkImageCopy2KHR(ImageCopy{regions[i]}); + } + + _info.regionCount = regions.size(); + _info.pRegions = vkImageCopies2; +} + +CopyImageInfo::CopyImageInfo(const VkImage source, const ImageLayout sourceLayout, const VkImage destination, const ImageLayout destinationLayout, const std::initializer_list regions): CopyImageInfo{source, sourceLayout, destination, destinationLayout, Containers::arrayView(regions)} {} + +CopyImageInfo::CopyImageInfo(NoInitT) noexcept {} + +CopyImageInfo::CopyImageInfo(const VkCopyImageInfo2KHR& info): + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _info(info) {} + +Containers::Array CopyImageInfo::vkImageCopies() const { + Containers::Array out{NoInit, _info.regionCount}; + for(std::size_t i = 0; i != _info.regionCount; ++i) + new(out + i) VkImageCopy(vkImageCopy(_info.pRegions[i])); + return out; +} + +BufferImageCopy::BufferImageCopy(const UnsignedLong bufferOffset, const UnsignedInt bufferRowLength, const UnsignedInt bufferImageHeight, const ImageAspect imageAspect, const Int imageLevel, const Int imageLayerOffset, const Int imageLayerCount, const Range3Di& imageRange): _copy{} { + _copy.sType = VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2_KHR; + _copy.bufferOffset = bufferOffset; + _copy.bufferRowLength = bufferRowLength; + _copy.bufferImageHeight = bufferImageHeight; + _copy.imageSubresource.aspectMask = VkImageAspectFlags(imageAspect); + _copy.imageSubresource.mipLevel = imageLevel; + _copy.imageSubresource.baseArrayLayer = imageLayerOffset; + _copy.imageSubresource.layerCount = imageLayerCount; + _copy.imageOffset = VkOffset3D(imageRange.min()); + _copy.imageExtent = VkExtent3D(imageRange.size()); +} + +BufferImageCopy::BufferImageCopy(NoInitT) noexcept {} + +BufferImageCopy::BufferImageCopy(const VkBufferImageCopy2KHR& copy): + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _copy(copy) {} + +BufferImageCopy::BufferImageCopy(const VkBufferImageCopy& copy): _copy{ + VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2_KHR, + nullptr, + copy.bufferOffset, + copy.bufferRowLength, + copy.bufferImageHeight, + copy.imageSubresource, + copy.imageOffset, + copy.imageExtent +} {} + +namespace { + +/* Used by CopyImageToBufferInfo::vkImageCopies() and + CopyImageToBufferInfo::vkImageCopies() as well */ +VkBufferImageCopy vkBufferImageCopy(const VkBufferImageCopy2KHR& copy) { + CORRADE_ASSERT(!copy.pNext, + "Vk::BufferImageCopy: disallowing conversion to VkBufferImageCopy with non-empty pNext to prevent information loss", {}); + return { + copy.bufferOffset, + copy.bufferRowLength, + copy.bufferImageHeight, + copy.imageSubresource, + copy.imageOffset, + copy.imageExtent + }; +} + +} + +VkBufferImageCopy BufferImageCopy::vkBufferImageCopy() const { + return Vk::vkBufferImageCopy(_copy); +} + +BufferImageCopy1D::BufferImageCopy1D(const UnsignedLong bufferOffset, const ImageAspect aspect, const Int level, const Range1Di& range): BufferImageCopy{bufferOffset, 0, 0, aspect, level, 0, 1, {{range.min(), 0, 0}, {range.max(), 1, 1}}} {} + +BufferImageCopy2D::BufferImageCopy2D(const UnsignedLong bufferOffset, const UnsignedInt bufferRowLength, const ImageAspect aspect, const Int level, const Range2Di& range): BufferImageCopy{bufferOffset, bufferRowLength, 0, aspect, level, 0, 1, {{range.min(), 0}, {range.max(), 1}}} {} + +BufferImageCopy3D::BufferImageCopy3D(const UnsignedLong bufferOffset, const UnsignedInt bufferRowLength, const UnsignedInt bufferImageHeight, const ImageAspect aspect, const Int level, const Range3Di& range): BufferImageCopy{bufferOffset, bufferRowLength, bufferImageHeight, aspect, level, 0, 1, range} {} + +BufferImageCopy1DArray::BufferImageCopy1DArray(const UnsignedLong bufferOffset, const UnsignedInt bufferRowLength, const ImageAspect aspect, const Int level, const Range2Di& range): BufferImageCopy{bufferOffset, bufferRowLength, 0, aspect, level, range.min().y(), range.sizeY(), {{range.min().x(), 0, 0}, {range.max().x(), 1, 1}}} {} + +BufferImageCopy2DArray::BufferImageCopy2DArray(const UnsignedLong bufferOffset, const UnsignedInt bufferRowLength, const UnsignedInt bufferImageHeight, const ImageAspect aspect, const Int level, const Range3Di& range): BufferImageCopy{bufferOffset, bufferRowLength, bufferImageHeight, aspect, level, range.min().z(), range.sizeZ(), {{range.min().xy(), 0}, {range.max().xy(), 1}}} {} + +BufferImageCopyCubeMap::BufferImageCopyCubeMap(const UnsignedLong bufferOffset, const UnsignedInt bufferRowLength, const UnsignedInt bufferImageHeight, const ImageAspect aspect, const Int level, const Range2Di& range): BufferImageCopy{bufferOffset, bufferRowLength, bufferImageHeight, aspect, level, 0, 6, {{range.min(), 0}, {range.max(), 1}}} {} + +BufferImageCopyCubeMapArray::BufferImageCopyCubeMapArray(const UnsignedLong bufferOffset, const UnsignedInt bufferRowLength, const UnsignedInt bufferImageHeight, const ImageAspect aspect, const Int level, const Range3Di& range): BufferImageCopy{bufferOffset, bufferRowLength, bufferImageHeight, aspect, level, range.min().z(), range.sizeZ(), {{range.min().xy(), 0}, {range.max().xy(), 1}}} {} + +CopyBufferToImageInfo::CopyBufferToImageInfo(const VkBuffer source, const VkImage destination, const ImageLayout destinationLayout, const Containers::ArrayView regions): _info{} { + _info.sType = VK_STRUCTURE_TYPE_COPY_BUFFER_TO_IMAGE_INFO_2_KHR; + _info.srcBuffer = source; + _info.dstImage = destination; + _info.dstImageLayout = VkImageLayout(destinationLayout); + + /* While not strictly needed right now, storing the original + BufferImageCopy instances as well to prepare for a future case where + VkBufferImageCopy2KHR::pNext will reference something stored there (such + as copy transformation) */ + Containers::ArrayView wrappers; + Containers::ArrayView vkBufferImageCopies2; + _data = Containers::ArrayTuple{ + {NoInit, regions.size(), wrappers}, + {NoInit, regions.size(), vkBufferImageCopies2} + }; + + for(std::size_t i = 0; i != regions.size(); ++i) { + new(wrappers + i) BufferImageCopy{regions[i]}; + /* Can't use {} with GCC 4.8 here because it tries to initialize the + first member instead of doing a copy */ + new(vkBufferImageCopies2 + i) VkBufferImageCopy2KHR(wrappers[i]); + } + + _info.regionCount = regions.size(); + _info.pRegions = vkBufferImageCopies2; +} + +CopyBufferToImageInfo::CopyBufferToImageInfo(NoInitT) noexcept {} + +CopyBufferToImageInfo::CopyBufferToImageInfo(const VkCopyBufferToImageInfo2KHR& info): + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _info(info) {} + +CopyBufferToImageInfo::CopyBufferToImageInfo(const VkBuffer source, const VkImage destination, const ImageLayout destinationLayout, const std::initializer_list regions): CopyBufferToImageInfo{source, destination, destinationLayout, Containers::arrayView(regions)} {} + +Containers::Array CopyBufferToImageInfo::vkBufferImageCopies() const { + Containers::Array out{NoInit, _info.regionCount}; + for(std::size_t i = 0; i != _info.regionCount; ++i) + new(out + i) VkBufferImageCopy(vkBufferImageCopy(_info.pRegions[i])); + return out; +} + +CopyBufferToImageInfo1D::CopyBufferToImageInfo1D(const VkBuffer source, const VkImage destination, const ImageLayout destinationLayout, const Containers::ArrayView regions): CopyBufferToImageInfo{source, destination, destinationLayout, Containers::arrayCast(regions)} {} + +CopyBufferToImageInfo1D::CopyBufferToImageInfo1D(const VkBuffer source, const VkImage destination, const ImageLayout destinationLayout, const std::initializer_list regions): CopyBufferToImageInfo1D{source, destination, destinationLayout, Containers::arrayView(regions)} {} + +CopyBufferToImageInfo2D::CopyBufferToImageInfo2D(const VkBuffer source, const VkImage destination, const ImageLayout destinationLayout, const Containers::ArrayView regions): CopyBufferToImageInfo{source, destination, destinationLayout, Containers::arrayCast(regions)} {} + +CopyBufferToImageInfo2D::CopyBufferToImageInfo2D(const VkBuffer source, const VkImage destination, const ImageLayout destinationLayout, const std::initializer_list regions): CopyBufferToImageInfo2D{source, destination, destinationLayout, Containers::arrayView(regions)} {} + +CopyBufferToImageInfo3D::CopyBufferToImageInfo3D(const VkBuffer source, const VkImage destination, const ImageLayout destinationLayout, const Containers::ArrayView regions): CopyBufferToImageInfo{source, destination, destinationLayout, Containers::arrayCast(regions)} {} + +CopyBufferToImageInfo3D::CopyBufferToImageInfo3D(const VkBuffer source, const VkImage destination, const ImageLayout destinationLayout, const std::initializer_list regions): CopyBufferToImageInfo3D{source, destination, destinationLayout, Containers::arrayView(regions)} {} + +CopyBufferToImageInfo1DArray::CopyBufferToImageInfo1DArray(const VkBuffer source, const VkImage destination, const ImageLayout destinationLayout, const Containers::ArrayView regions): CopyBufferToImageInfo{source, destination, destinationLayout, Containers::arrayCast(regions)} {} + +CopyBufferToImageInfo1DArray::CopyBufferToImageInfo1DArray(const VkBuffer source, const VkImage destination, const ImageLayout destinationLayout, const std::initializer_list regions): CopyBufferToImageInfo1DArray{source, destination, destinationLayout, Containers::arrayView(regions)} {} + +CopyBufferToImageInfo2DArray::CopyBufferToImageInfo2DArray(const VkBuffer source, const VkImage destination, const ImageLayout destinationLayout, const Containers::ArrayView regions): CopyBufferToImageInfo{source, destination, destinationLayout, Containers::arrayCast(regions)} {} + +CopyBufferToImageInfo2DArray::CopyBufferToImageInfo2DArray(const VkBuffer source, const VkImage destination, const ImageLayout destinationLayout, const std::initializer_list regions): CopyBufferToImageInfo2DArray{source, destination, destinationLayout, Containers::arrayView(regions)} {} + +CopyBufferToImageInfoCubeMap::CopyBufferToImageInfoCubeMap(const VkBuffer source, const VkImage destination, const ImageLayout destinationLayout, const Containers::ArrayView regions): CopyBufferToImageInfo{source, destination, destinationLayout, Containers::arrayCast(regions)} {} + +CopyBufferToImageInfoCubeMap::CopyBufferToImageInfoCubeMap(const VkBuffer source, const VkImage destination, const ImageLayout destinationLayout, const std::initializer_list regions): CopyBufferToImageInfoCubeMap{source, destination, destinationLayout, Containers::arrayView(regions)} {} + +CopyBufferToImageInfoCubeMapArray::CopyBufferToImageInfoCubeMapArray(const VkBuffer source, const VkImage destination, const ImageLayout destinationLayout, const Containers::ArrayView regions): CopyBufferToImageInfo{source, destination, destinationLayout, Containers::arrayCast(regions)} {} + +CopyBufferToImageInfoCubeMapArray::CopyBufferToImageInfoCubeMapArray(const VkBuffer source, const VkImage destination, const ImageLayout destinationLayout, const std::initializer_list regions): CopyBufferToImageInfoCubeMapArray{source, destination, destinationLayout, Containers::arrayView(regions)} {} + +CopyImageToBufferInfo::CopyImageToBufferInfo(const VkImage source, const ImageLayout sourceLayout, const VkBuffer destination, const Containers::ArrayView regions): _info{} { + _info.sType = VK_STRUCTURE_TYPE_COPY_IMAGE_TO_BUFFER_INFO_2_KHR; + _info.srcImage = source; + _info.srcImageLayout = VkImageLayout(sourceLayout); + _info.dstBuffer = destination; + + /* While not strictly needed right now, storing the original + BufferImageCopy instances as well to prepare for a future case where + VkBufferImageCopy2KHR::pNext will reference something stored there (such + as copy transformation) */ + Containers::ArrayView wrappers; + Containers::ArrayView vkBufferImageCopies2; + _data = Containers::ArrayTuple{ + {NoInit, regions.size(), wrappers}, + {NoInit, regions.size(), vkBufferImageCopies2} + }; + + for(std::size_t i = 0; i != regions.size(); ++i) { + new(wrappers + i) BufferImageCopy{regions[i]}; + /* Can't use {} with GCC 4.8 here because it tries to initialize the + first member instead of doing a copy */ + new(vkBufferImageCopies2 + i) VkBufferImageCopy2KHR(wrappers[i]); + } + + _info.regionCount = regions.size(); + _info.pRegions = vkBufferImageCopies2; +} + +CopyImageToBufferInfo::CopyImageToBufferInfo(const VkImage source, const ImageLayout sourceLayout, const VkBuffer destination, const std::initializer_list regions): CopyImageToBufferInfo{source, sourceLayout, destination, Containers::arrayView(regions)} {} + +CopyImageToBufferInfo::CopyImageToBufferInfo(NoInitT) noexcept {} + +CopyImageToBufferInfo::CopyImageToBufferInfo(const VkCopyImageToBufferInfo2KHR& info): + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _info(info) {} + +Containers::Array CopyImageToBufferInfo::vkBufferImageCopies() const { + Containers::Array out{NoInit, _info.regionCount}; + for(std::size_t i = 0; i != _info.regionCount; ++i) + new(out + i) VkBufferImageCopy(vkBufferImageCopy(_info.pRegions[i])); + return out; +} + +CopyImageToBufferInfo1D::CopyImageToBufferInfo1D(const VkImage source, const ImageLayout sourceLayout, const VkBuffer destination, const Containers::ArrayView regions): CopyImageToBufferInfo{source, sourceLayout, destination, Containers::arrayCast(regions)} {} + +CopyImageToBufferInfo1D::CopyImageToBufferInfo1D(const VkImage source, const ImageLayout sourceLayout, const VkBuffer destination, const std::initializer_list regions): CopyImageToBufferInfo1D{source, sourceLayout, destination, Containers::arrayView(regions)} {} + +CopyImageToBufferInfo2D::CopyImageToBufferInfo2D(const VkImage source, const ImageLayout sourceLayout, const VkBuffer destination, const Containers::ArrayView regions): CopyImageToBufferInfo{source, sourceLayout, destination, Containers::arrayCast(regions)} {} + +CopyImageToBufferInfo2D::CopyImageToBufferInfo2D(const VkImage source, const ImageLayout sourceLayout, const VkBuffer destination, const std::initializer_list regions): CopyImageToBufferInfo2D{source, sourceLayout, destination, Containers::arrayView(regions)} {} + +CopyImageToBufferInfo3D::CopyImageToBufferInfo3D(const VkImage source, const ImageLayout sourceLayout, const VkBuffer destination, const Containers::ArrayView regions): CopyImageToBufferInfo{source, sourceLayout, destination, Containers::arrayCast(regions)} {} + +CopyImageToBufferInfo3D::CopyImageToBufferInfo3D(const VkImage source, const ImageLayout sourceLayout, const VkBuffer destination, const std::initializer_list regions): CopyImageToBufferInfo3D{source, sourceLayout, destination, Containers::arrayView(regions)} {} + +CopyImageToBufferInfo1DArray::CopyImageToBufferInfo1DArray(const VkImage source, const ImageLayout sourceLayout, const VkBuffer destination, const Containers::ArrayView regions): CopyImageToBufferInfo{source, sourceLayout, destination, Containers::arrayCast(regions)} {} + +CopyImageToBufferInfo1DArray::CopyImageToBufferInfo1DArray(const VkImage source, const ImageLayout sourceLayout, const VkBuffer destination, const std::initializer_list regions): CopyImageToBufferInfo1DArray{source, sourceLayout, destination, Containers::arrayView(regions)} {} + +CopyImageToBufferInfo2DArray::CopyImageToBufferInfo2DArray(const VkImage source, const ImageLayout sourceLayout, const VkBuffer destination, const Containers::ArrayView regions): CopyImageToBufferInfo{source, sourceLayout, destination, Containers::arrayCast(regions)} {} + +CopyImageToBufferInfo2DArray::CopyImageToBufferInfo2DArray(const VkImage source, const ImageLayout sourceLayout, const VkBuffer destination, const std::initializer_list regions): CopyImageToBufferInfo2DArray{source, sourceLayout, destination, Containers::arrayView(regions)} {} + +CopyImageToBufferInfoCubeMap::CopyImageToBufferInfoCubeMap(const VkImage source, const ImageLayout sourceLayout, const VkBuffer destination, const Containers::ArrayView regions): CopyImageToBufferInfo{source, sourceLayout, destination, Containers::arrayCast(regions)} {} + +CopyImageToBufferInfoCubeMap::CopyImageToBufferInfoCubeMap(const VkImage source, const ImageLayout sourceLayout, const VkBuffer destination, const std::initializer_list regions): CopyImageToBufferInfoCubeMap{source, sourceLayout, destination, Containers::arrayView(regions)} {} + +CopyImageToBufferInfoCubeMapArray::CopyImageToBufferInfoCubeMapArray(const VkImage source, const ImageLayout sourceLayout, const VkBuffer destination, const Containers::ArrayView regions): CopyImageToBufferInfo{source, sourceLayout, destination, Containers::arrayCast(regions)} {} + +CopyImageToBufferInfoCubeMapArray::CopyImageToBufferInfoCubeMapArray(const VkImage source, const ImageLayout sourceLayout, const VkBuffer destination, const std::initializer_list regions): CopyImageToBufferInfoCubeMapArray{source, sourceLayout, destination, Containers::arrayView(regions)} {} + +CommandBuffer& CommandBuffer::clearColorImage(const VkImage image, const ImageLayout layout, const Color4& color) { + VkImageSubresourceRange range{}; + range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + range.baseMipLevel = 0; + range.levelCount = VK_REMAINING_MIP_LEVELS; + range.baseArrayLayer = 0; + range.layerCount = VK_REMAINING_ARRAY_LAYERS; + + /* Why this is passed via a pointer, why?! */ + const VkClearColorValue clear(color); + (**_device).CmdClearColorImage(_handle, image, VkImageLayout(layout), &clear, 1, &range); + return *this; +} + +CommandBuffer& CommandBuffer::clearColorImage(const VkImage image, const ImageLayout layout, const Vector4i& color) { + VkImageSubresourceRange range{}; + range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + range.baseMipLevel = 0; + range.levelCount = VK_REMAINING_MIP_LEVELS; + range.baseArrayLayer = 0; + range.layerCount = VK_REMAINING_ARRAY_LAYERS; + + /* Why this is passed via a pointer, why?! */ + const VkClearColorValue clear(color); + (**_device).CmdClearColorImage(_handle, image, VkImageLayout(layout), &clear, 1, &range); + return *this; +} + +CommandBuffer& CommandBuffer::clearColorImage(const VkImage image, const ImageLayout layout, const Vector4ui& color) { + VkImageSubresourceRange range{}; + range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + range.baseMipLevel = 0; + range.levelCount = VK_REMAINING_MIP_LEVELS; + range.baseArrayLayer = 0; + range.layerCount = VK_REMAINING_ARRAY_LAYERS; + + /* Why this is passed via a pointer, why?! */ + const VkClearColorValue clear(color); + (**_device).CmdClearColorImage(_handle, image, VkImageLayout(layout), &clear, 1, &range); + return *this; +} + +CommandBuffer& CommandBuffer::clearDepthStencilImage(const VkImage image, const ImageLayout layout, const Float depth, const UnsignedInt stencil) { + VkImageSubresourceRange range{}; + range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT|VK_IMAGE_ASPECT_STENCIL_BIT; + range.baseMipLevel = 0; + range.levelCount = VK_REMAINING_MIP_LEVELS; + range.baseArrayLayer = 0; + range.layerCount = VK_REMAINING_ARRAY_LAYERS; + + /* Why this is passed via a pointer, why?! */ + const VkClearDepthStencilValue clear{depth, stencil}; + (**_device).CmdClearDepthStencilImage(_handle, image, VkImageLayout(layout), &clear, 1, &range); + return *this; +} + +CommandBuffer& CommandBuffer::clearDepthImage(const VkImage image, const ImageLayout layout, const Float depth) { + VkImageSubresourceRange range{}; + range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + range.baseMipLevel = 0; + range.levelCount = VK_REMAINING_MIP_LEVELS; + range.baseArrayLayer = 0; + range.layerCount = VK_REMAINING_ARRAY_LAYERS; + + /* Why this is passed via a pointer, why?! */ + const VkClearDepthStencilValue clear{depth, 0}; + (**_device).CmdClearDepthStencilImage(_handle, image, VkImageLayout(layout), &clear, 1, &range); + return *this; +} + +CommandBuffer& CommandBuffer::clearStencilImage(const VkImage image, const ImageLayout layout, const UnsignedInt stencil) { + VkImageSubresourceRange range{}; + range.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; + range.baseMipLevel = 0; + range.levelCount = VK_REMAINING_MIP_LEVELS; + range.baseArrayLayer = 0; + range.layerCount = VK_REMAINING_ARRAY_LAYERS; + + /* Why this is passed via a pointer, why?! Also, the depth value will get + unused anyway, but I think it's good to maintain the fact that default + depth clear value is 1.0. */ + const VkClearDepthStencilValue clear{1.0f, stencil}; + (**_device).CmdClearDepthStencilImage(_handle, image, VkImageLayout(layout), &clear, 1, &range); + return *this; +} + +CommandBuffer& CommandBuffer::copyImage(const CopyImageInfo& info) { + _device->state().cmdCopyImageImplementation(*this, info); + return *this; +} + +void CommandBuffer::copyImageImplementationDefault(CommandBuffer& self, const CopyImageInfo& info) { + CORRADE_ASSERT(!info->pNext, + "Vk::CommandBuffer::copyImage(): disallowing extraction of CopyImageInfo with non-empty pNext to prevent information loss", ); + return (**self._device).CmdCopyImage(self, info->srcImage, info->srcImageLayout, info->dstImage, info->dstImageLayout, info->regionCount, info.vkImageCopies()); +} + +void CommandBuffer::copyImageImplementationKHR(CommandBuffer& self, const CopyImageInfo& info) { + return (**self._device).CmdCopyImage2KHR(self, info); +} + +CommandBuffer& CommandBuffer::copyBufferToImage(const CopyBufferToImageInfo& info) { + _device->state().cmdCopyBufferToImageImplementation(*this, info); + return *this; +} + +void CommandBuffer::copyBufferToImageImplementationDefault(CommandBuffer& self, const CopyBufferToImageInfo& info) { + CORRADE_ASSERT(!info->pNext, + "Vk::CommandBuffer::copyBufferToImage(): disallowing extraction of CopyBufferToImageInfo with non-empty pNext to prevent information loss", ); + return (**self._device).CmdCopyBufferToImage(self, info->srcBuffer, info->dstImage, info->dstImageLayout, info->regionCount, info.vkBufferImageCopies()); +} + +void CommandBuffer::copyBufferToImageImplementationKHR(CommandBuffer& self, const CopyBufferToImageInfo& info) { + return (**self._device).CmdCopyBufferToImage2KHR(self, info); +} + +CommandBuffer& CommandBuffer::copyImageToBuffer(const CopyImageToBufferInfo& info) { + _device->state().cmdCopyImageToBufferImplementation(*this, info); + return *this; +} + +void CommandBuffer::copyImageToBufferImplementationDefault(CommandBuffer& self, const CopyImageToBufferInfo& info) { + CORRADE_ASSERT(!info->pNext, + "Vk::CommandBuffer::copyImageToBuffer(): disallowing extraction of CopyImageToBufferInfo with non-empty pNext to prevent information loss", ); + return (**self._device).CmdCopyImageToBuffer(self, info->srcImage, info->srcImageLayout, info->dstBuffer, info->regionCount, info.vkBufferImageCopies()); +} + +void CommandBuffer::copyImageToBufferImplementationKHR(CommandBuffer& self, const CopyImageToBufferInfo& info) { + return (**self._device).CmdCopyImageToBuffer2KHR(self, info); +} + }} diff --git a/src/Magnum/Vk/Image.h b/src/Magnum/Vk/Image.h index 707a34c05..23d1a9435 100644 --- a/src/Magnum/Vk/Image.h +++ b/src/Magnum/Vk/Image.h @@ -30,6 +30,9 @@ * @m_since_latest */ +#include +#include + #include "Magnum/Magnum.h" #include "Magnum/Vk/Memory.h" #include "Magnum/Vk/Vk.h" @@ -241,6 +244,65 @@ available through @ref dedicatedMemory(). This matches current behavior of the above, except that you have more control over choosing and allocating the memory. +@section Vk-Image-usage Image usage + +@subsection Vk-Image-usage-clear Clearing image data + +Usually an image is cleared implicitly at the start of a render pass using +@ref AttachmentLoadOperation::Clear for the corresponding attachment and +specifying the clear color using @ref RenderPassBeginInfo::clearColor() / +@ref RenderPassBeginInfo::clearDepthStencil() "clearDepthStencil()". If you +need to do a clear outside of a render pass, it can be done using +@ref CommandBuffer::clearColorImage() / +@ref CommandBuffer::clearDepthStencilImage() "clearDepthStencilImage()" / +@ref CommandBuffer::clearDepthImage() "clearDepthImage()" / +@ref CommandBuffer::clearStencilImage() "clearStencilImage()". In most cases +you'll also need to perform a layout transition first using a +@ref CommandBuffer::pipelineBarrier(PipelineStages, PipelineStages, Containers::ArrayView, DependencyFlags) "pipelineBarrier()": + +@snippet MagnumVk.cpp Image-usage-clear + +@todoc expand with @fn_vk{CmdClearAttachments} when exposed + +@subsection Vk-Image-usage-copy Copying image data + +The most common image copy operation is uploading texture data from a +host-visible buffer to a device-local image. This is the preferred workflow +over using a host-visible linear image directly, since linear images are poorly +supported, have suboptimal access performance, and host-visible memory usually +isn't the fastest for device access. Similarly, for downloading a rendered +framebuffer back to the host it's recommended to linearize to a buffer instead +of rendering to a linear image, which isn't widely supported. + +The copy is done using @ref CommandBuffer::copyBufferToImage() / +@ref CommandBuffer::copyImageToBuffer() "copyImageToBuffer()". For convenience, +you're encouraged to use the @ref BufferImageCopy1D, @ref BufferImageCopy2D +etc. constructors that will correctly set the remaining parameters for certain +image type. In most cases you'll also need to add two +@ref CommandBuffer::pipelineBarrier(PipelineStages, PipelineStages, Containers::ArrayView, DependencyFlags) "pipelineBarrier()" +commands to perform a layout transition before, and make the memory visible for +subsequent operations after. For example: + +@snippet MagnumVk.cpp Image-usage-copy-from-buffer + +Alternatively you can use @ref CopyBufferToImageInfo1D / +@ref CopyImageToBufferInfo1D, @ref CopyBufferToImageInfo2D / +@ref CopyImageToBufferInfo2D etc., as both @ref CopyBufferToImageInfo / +@ref CopyImageToBufferInfo and @ref BufferImageCopy (sub)classes have implicit +constructors. This can be handy when uploading multiple regions --- for example +uploading all mip levels of an image at the same time: + +@snippet MagnumVk.cpp Image-usage-copy-from-buffer-multiple + +Image/image copy is possible as well and is done using +@ref CommandBuffer::copyImage(). Because there's a lot of combinations of +source and destination image types, no convenience classes are provided in that +case. Together with a layout transition +@ref CommandBuffer::pipelineBarrier(PipelineStages, PipelineStages, Containers::ArrayView, DependencyFlags) "pipelineBarrier()" +for both images it could look like this: + +@snippet MagnumVk.cpp Image-usage-copy-from-image + @see @ref Buffer */ class MAGNUM_VK_EXPORT Image { @@ -426,6 +488,1000 @@ class MAGNUM_VK_EXPORT Image { Memory _dedicatedMemory; }; +/** +@brief Image copy region +@m_since_latest + +Wraps a @type_vk_keyword{ImageCopy2KHR}. This calss is subsequently passed to a +@ref CopyImageInfo and then used in @ref CommandBuffer::copyImage(). See +@ref Vk-Image-usage-copy for usage information and examples. + +@section Vk-ImageCopy-compatibility Compatibility with VkImageCopy + +While the class operates on the @type_vk{ImageCopy2KHR} structure that's +provided by the @vk_extension{KHR,copy_commands2} extension, conversion from +and to @type_vk{ImageCopy} is provided to some extent --- you can create +a @ref ImageCopy from it, call various methods on the instance and then get a +@type_vk{ImageCopy} back again using @ref vkImageCopy(). + +For direct editing of the Vulkan structure, it's recommended to edit the +@type_vk{ImageCopy2KHR} fields and then perform the conversion instead of +editing the resulting @type_vk{ImageCopy}, as additional safety checks may be +done during the conversion to ensure no information is lost. + +@see @ref BufferCopy, @ref BufferImageCopy +*/ +class MAGNUM_VK_EXPORT ImageCopy { + public: + /** + * @brief Constructor + * @param aspects Aspects to copy between the images + * @param sourceLevel Source image mip level + * @param sourceLayerOffset Source image layer offset + * @param sourceLayerCount Source image layer count + * @param sourceOffset Source image offset + * @param destinationLevel Destination image level + * @param destinationLayerOffset Destination image layer offset + * @param destinationLayerCount Destination image layer count. + * Should be the same as @p sourceLayerCount except when copying + * data between 2D (array) and 3D images, in which case layers of + * one image correspond to depth of the other. + * @param destinationOffset Destination image offset + * @param size Size of the copied region + * + * The following @type_vk{ImageCopy2KHR} fields are pre-filled in + * addition to `sType`, everything else is zero-filled: + * + * - @cpp srcSubresource.aspectMask @ce to @p aspects + * - @cpp srcSubresource.mipLevel @ce to @p sourceLevel + * - @cpp srcSubresource.baseArrayLayer @ce to @p sourceLayerOffset + * - @cpp srcSubresource.layerCount @ce to @p sourceLayerCount + * - `srcOffset` to @p sourceOffset + * - @cpp dstSubresource.aspectMask @ce to @p aspects + * - @cpp dstSubresource.mipLevel @ce to @p destinationLevel + * - @cpp dstSubresource.baseArrayLayer @ce to + * @p destinationLayerOffset + * - @cpp dstSubresource.layerCount @ce to @p destinationLayerCount + * - `dstOffset` to @p destinationOffset + * - `extent` to @p size + * + * There are various restrictions on @p sourceLayerCount, + * @p destinationLayerCount and @p size depending on whether the source + * and destination images have additional dimensions or layers. + * However, because there is many possible combinations, this class + * doesn't provide convenience classes for particular image types like + * @ref BufferImageCopy does. + * + * @todo specifying different source/destination aspect, only for + * multi-planar image copies + */ + /*implicit*/ ImageCopy(ImageAspects aspects, Int sourceLevel, Int sourceLayerOffset, Int sourceLayerCount, const Vector3i& sourceOffset, Int destinationLevel, Int destinationLayerOffset, Int destinationLayerCount, const Vector3i& destinationOffset, const Vector3i& size); + + /** + * @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 ImageCopy(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 ImageCopy(const VkImageCopy2KHR& copy); + + /** + * @brief Construct from a @type_vk{ImageCopy} + * + * Compared to the above, fills the common subset of + * @type_vk{ImageCopy2KHR}, sets `sType` and zero-fills `pNext`. + * @see @ref vkImageCopy() + */ + explicit ImageCopy(const VkImageCopy& copy); + + /** + * @brief Corresponding @type_vk{ImageCopy} structure + * + * Provided for compatibility with Vulkan implementations that don't + * support the @vk_extension{KHR,copy_commands2} extensions. See + * @ref Vk-ImageCopy-compatibility for more information. + * @see @ref ImageCopy(const VkImageCopy&), + * @ref CopyBufferInfo::vkBufferCopies(), + * @ref BufferCopy::vkBufferCopy(), + * @ref CopyImageInfo::vkImageCopies(), + * @ref CopyImageToBufferInfo::vkBufferImageCopies(), + * @ref CopyBufferToImageInfo::vkBufferImageCopies(), + * @ref BufferImageCopy::vkBufferImageCopy() + */ + VkImageCopy vkImageCopy() const; + + /** @brief Underlying @type_vk{ImageCopy2KHR} structure */ + VkImageCopy2KHR& operator*() { return _copy; } + /** @overload */ + const VkImageCopy2KHR& operator*() const { return _copy; } + /** @overload */ + VkImageCopy2KHR* operator->() { return &_copy; } + /** @overload */ + const VkImageCopy2KHR* operator->() const { return &_copy; } + /** @overload */ + operator const VkImageCopy2KHR*() const { return &_copy; } + + /** + * @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 VkImageCopy2KHR&() const { return _copy; } + + private: + VkImageCopy2KHR _copy; +}; + +/** +@brief Image copy command +@m_since_latest + +Wraps a @type_vk_keyword{CopyImageInfo2KHR}. This class is subsequently used in +@ref CommandBuffer::copyImage(). See @ref Vk-Image-usage-copy for more +information. + +@section Vk-CopyImageInfo-compatibility Compatibility with vkCmdCopyImager() + +While the class operates on the @type_vk{CopyImageInfo2KHR} structure that's +provided by the @vk_extension{KHR,copy_commands2} extension, conversion from +and to the set of parameters accepted by @fn_vk{CmdCopyImage} is provided to +some extent --- you can create @ref ImageCopy instances out of +@type_vk{ImageCopy} structures, pass them together with the rest to +@ref CopyImageInfo and then get a @type_vk{ImageCopy} list back again using +@ref vkImageCopies(). + +For direct editing of the Vulkan structure, it's recommended to edit the +@type_vk{CopyImageInfo2KHR} fields and then perform the conversion instead of +editing the resulting @type_vk{ImageCopy} list, as additional safety checks may +be done during the conversion to ensure no information is lost. + +@see @ref CopyBufferInfo, @ref CopyBufferToImageInfo, + @ref CopyImageToBufferInfo +*/ +class MAGNUM_VK_EXPORT CopyImageInfo { + public: + /** + * @brief Constructor + * @param source Source @ref Image or a raw Vulkan image + * handle. Expected to have been created with + * @ref ImageUsage::TransferSource and a @ref PixelFormat usable + * for transfer source. + * @param sourceLayout Source image layout. Can be either + * @ref ImageLayout::General or @ref ImageLayout::TransferSource. + * @param destination Destination @ref Image or a raw Vulkan + * image handle. Expected to have been created with + * @ref ImageUsage::TransferDestination and a @ref PixelFormat + * usable for transfer destination. + * @param destinationLayout Destination image layout. Can be either + * @ref ImageLayout::General or + * @ref ImageLayout::TransferDestination. + * @param regions Regions to copy. THere has to be at least + * one. + * + * The following @type_vk{CopyImageInfo2KHR} fields are pre-filled in + * addition to `sType`, everything else is zero-filled: + * + * - `srcImage` to @p source + * - `srcImageLayout` to @p sourceLayout + * - `dstImage` to @p destination + * - `dstImageLayout` to @p destinationLayout + * - `regionCount` and `pRegions` to @p regions + * + * @todoc mention ImageLayout::SharedPresent being allowed once the + * extension is exposed + */ + /*implicit*/ CopyImageInfo(VkImage source, ImageLayout sourceLayout, VkImage destination, ImageLayout destinationLayout, Containers::ArrayView regions); + /** @overload */ + /*implicit*/ CopyImageInfo(VkImage source, ImageLayout sourceLayout, VkImage destination, ImageLayout destinationLayout, std::initializer_list regions); + + /** + * @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 CopyImageInfo(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 CopyImageInfo(const VkCopyImageInfo2KHR& info); + + /** + * @brief Corresponding @type_vk{ImageCopy} structures + * + * Provided for compatibility with Vulkan implementations that don't + * support the @vk_extension{KHR,copy_commands2} extension. See + * @ref Vk-CopyImageInfo-compatibility for more information. + * @see @ref CopyBufferInfo::vkBufferCopies(), + * @ref BufferCopy::vkBufferCopy(), + * @ref ImageCopy::vkImageCopy(), + * @ref CopyBufferToImageInfo::vkBufferImageCopies(), + * @ref CopyImageToBufferInfo::vkBufferImageCopies(), + * @ref BufferImageCopy::vkBufferImageCopy() + */ + Containers::Array vkImageCopies() const; + + /** @brief Underlying @type_vk{CopyImageInfo2KHR} structure */ + VkCopyImageInfo2KHR& operator*() { return _info; } + /** @overload */ + const VkCopyImageInfo2KHR& operator*() const { return _info; } + /** @overload */ + VkCopyImageInfo2KHR* operator->() { return &_info; } + /** @overload */ + const VkCopyImageInfo2KHR* operator->() const { return &_info; } + /** @overload */ + operator const VkCopyImageInfo2KHR*() const { return &_info; } + + private: + VkCopyImageInfo2KHR _info; + Containers::ArrayTuple _data; +}; + +/** +@brief Buffer / image copy region +@m_since_latest + +Wraps a @type_vk_keyword{BufferImageCopy2KHR}. This class is subsequently +passed to a @ref CopyBufferToImageInfo / @ref CopyImageToBufferInfo and then +used in @ref CommandBuffer::copyBufferToImage() / +@ref CommandBuffer::copyImageToBuffer(). See @ref Vk-Image-usage-copy for usage +information and examples. + +@section Vk-BufferImageCopy-compatibility Compatibility with VkBufferImageCopy + +While the class operates on the @type_vk{BufferImageCopy2KHR} structure that's +provided by the @vk_extension{KHR,copy_commands2} extension, conversion from +and to @type_vk{BufferImageCopy} is provided to some extent --- you can create +a @ref BufferImageCopy from it, call various methods on the instance and then +get a @type_vk{BufferImageCopy} back again using @ref vkBufferImageCopy(). + +For direct editing of the Vulkan structure, it's recommended to edit the +@type_vk{BufferImageCopy2KHR} fields and then perform the conversion instead +of editing the resulting @type_vk{BufferImageCopy}, as additional safety +checks may be done during the conversion to ensure no information is lost. + +@see @ref BufferImageCopy1D, @ref BufferImageCopy2D, @ref BufferImageCopy3D, + @ref BufferImageCopy1DArray, @ref BufferImageCopy2DArray, + @ref BufferImageCopyCubeMap, @ref BufferImageCopyCubeMapArray, + @ref BufferCopy, @ref ImageCopy +*/ +class MAGNUM_VK_EXPORT BufferImageCopy { + public: + /** + * @brief Constructor + * @param bufferOffset Buffer offset in bytes + * @param bufferRowLength Row length when linearized in the buffer, + * in pixels. Use @cpp 0 @ce for tightly packed rows, which is + * equivalent to width of @p imageRange. + * @param bufferImageHeight Image height when linearized in the buffer, + * in pixels. Using @cpp 0 @ce for tightly packed images, which is + * equivalent to height of @p imageRange. + * @param imageAspect Image aspect to copy. Only one aspect can + * be copied at a time, so this is not @ref ImageAspects. + * @param imageLevel Image mip level + * @param imageLayerOffset Image layer offset + * @param imageLayerCount Image layer count + * @param imageRange Image range + * + * The following @type_vk{BufferImageCopy2KHR} fields are pre-filled in + * addition to `sType`, everything else is zero-filled: + * + * - `bufferOffset` + * - `bufferRowLength` + * - `bufferImageHeight` + * - @cpp imageSubresource.aspectMask @ce to @p imageAspect + * - @cpp imageSubresource.mipLevel @ce to @p imageLevel + * - @cpp imageSubresource.baseArrayLayer @ce to @p imageLayerOffset + * - @cpp imageSubresource.layerCount @ce to @p imageLayerCount + * - `imageOffset` to @p imageRange @ref Math::Range::min() "min()" + * - `imageExtent` to @p imageRange @ref Math::Range::size() "size()" + * + * There are various restrictions on @p imageLayerCount and + * @p imageRange depending on whether the image has additional + * dimensions or layers and you're encouraged to make use of + * @ref BufferImageCopy1D, @ref BufferImageCopy3D, + * @ref BufferImageCopy3D, @ref BufferImageCopy1DArray, + * @ref BufferImageCopy2DArray, @ref BufferImageCopyCubeMap and + * @ref BufferImageCopyCubeMapArray convenience classes instead of this + * constructor. + */ + /*implicit*/ BufferImageCopy(UnsignedLong bufferOffset, UnsignedInt bufferRowLength, UnsignedInt bufferImageHeight, ImageAspect imageAspect, Int imageLevel, Int imageLayerOffset, Int imageLayerCount, const Range3Di& imageRange); + + /** + * @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 BufferImageCopy(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 BufferImageCopy(const VkBufferImageCopy2KHR& copy); + + /** + * @brief Construct from a @type_vk{BufferImageCopy} + * + * Compared to the above, fills the common subset of + * @type_vk{BufferImageCopy2KHR}, sets `sType` and zero-fills `pNext`. + * @see @ref vkBufferImageCopy() + */ + explicit BufferImageCopy(const VkBufferImageCopy& copy); + + /** + * @brief Corresponding @type_vk{BufferImageCopy} structure + * + * Provided for compatibility with Vulkan implementations that don't + * support the @vk_extension{KHR,copy_commands2} extensions. See + * @ref Vk-BufferImageCopy-compatibility for more information. + * @see @ref BufferImageCopy(const VkBufferImageCopy&), + * @ref CopyBufferInfo::vkBufferCopies(), + * @ref BufferCopy::vkBufferCopy(), + * @ref CopyImageInfo::vkImageCopies(), + * @ref ImageCopy::vkImageCopy(), + * @ref CopyImageToBufferInfo::vkBufferImageCopies(), + * @ref CopyBufferToImageInfo::vkBufferImageCopies() + */ + VkBufferImageCopy vkBufferImageCopy() const; + + /** @brief Underlying @type_vk{BufferImageCopy2KHR} structure */ + VkBufferImageCopy2KHR& operator*() { return _copy; } + /** @overload */ + const VkBufferImageCopy2KHR& operator*() const { return _copy; } + /** @overload */ + VkBufferImageCopy2KHR* operator->() { return &_copy; } + /** @overload */ + const VkBufferImageCopy2KHR* operator->() const { return &_copy; } + /** @overload */ + operator const VkBufferImageCopy2KHR*() const { return &_copy; } + + /** + * @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 VkBufferImageCopy2KHR&() const { return _copy; } + + private: + VkBufferImageCopy2KHR _copy; +}; + +/** +@brief Convenience constructor for buffer / 1D image copy region +@m_since_latest + +Compared to the base @ref BufferImageCopy::BufferImageCopy() "BufferImageCopy constructor" +sets `bufferRowLength` and `bufferImageHeight` to @cpp 0 @ce, +@cpp imageSubresource.baseArrayLayer @ce to @cpp 0 @ce, +@cpp imageSubresource.layerCount @ce to @cpp 1 @ce, Y and Z coordinate of +`imageOffset` to @cpp 0 @ce and Y and Z coordinate of `imageExtent` to +@cpp 1 @ce. +*/ +class MAGNUM_VK_EXPORT BufferImageCopy1D: public BufferImageCopy { + public: + /** @brief Constructor */ + /*implicit*/ BufferImageCopy1D(UnsignedLong bufferOffset, ImageAspect imageAspect, Int imageLevel, const Range1Di& imageRange); +}; + +/** +@brief Convenience constructor for buffer / 2D image copy region +@m_since_latest + +Compared to the base @ref BufferImageCopy::BufferImageCopy() "BufferImageCopy constructor" +sets `bufferImageHeight` to @cpp 0 @ce, @cpp imageSubresource.baseArrayLayer @ce +to @cpp 0 @ce, @cpp imageSubresource.layerCount @ce to @cpp 1 @ce, Z coordinate +of `imageOffset` to @cpp 0 @ce and Z coordinate of `imageExtent` to @cpp 1 @ce. +*/ +class MAGNUM_VK_EXPORT BufferImageCopy2D: public BufferImageCopy { + public: + /** @brief Constructor */ + /*implicit*/ BufferImageCopy2D(UnsignedLong bufferOffset, UnsignedInt bufferRowLength, ImageAspect imageAspect, Int imageLevel, const Range2Di& imageRange); + + /** + * @brief Copy tightly packed rows + * + * Equivalent to calling @ref BufferImageCopy2D(UnsignedLong, UnsignedInt, ImageAspect, Int, const Range2Di&) + * with @p bufferRowLength set to @cpp 0 @ce. + */ + /*implicit*/ BufferImageCopy2D(UnsignedLong bufferOffset, ImageAspect imageAspect, Int imageLevel, const Range2Di& imageRange): BufferImageCopy2D{bufferOffset, 0, imageAspect, imageLevel, imageRange} {} +}; + +/** +@brief Convenience constructor for buffer / 3D image copy region +@m_since_latest + +Compared to the base @ref BufferImageCopy::BufferImageCopy() "BufferImageCopy constructor" +sets @cpp imageSubresource.baseArrayLayer @ce to @cpp 0 @ce and +@cpp imageSubresource.layerCount @ce to @cpp 1 @ce. +*/ +class MAGNUM_VK_EXPORT BufferImageCopy3D: public BufferImageCopy { + public: + /** @brief Constructor */ + /*implicit*/ BufferImageCopy3D(UnsignedLong bufferOffset, UnsignedInt bufferRowLength, UnsignedInt bufferImageHeight, ImageAspect imageAspect, Int imageLevel, const Range3Di& imageRange); + + /** + * @brief Copy tightly packed images + * + * Equivalent to calling @ref BufferImageCopy3D(UnsignedLong, UnsignedInt, UnsignedInt, ImageAspect, Int, const Range3Di&) + * with @p bufferRowLength and @p bufferImageHeight set to @cpp 0 @ce. + */ + /*implicit*/ BufferImageCopy3D(UnsignedLong bufferOffset, ImageAspect imageAspect, Int imageLevel, const Range3Di& imageRange): BufferImageCopy3D{bufferOffset, 0, 0, imageAspect, imageLevel, imageRange} {} +}; + +/** +@brief Convenience constructor for buffer / 1D array image copy region +@m_since_latest + +Compared to the base @ref BufferImageCopy::BufferImageCopy() "BufferImageCopy constructor" +sets `bufferImageHeight` to @cpp 0 @ce, @cpp imageSubresource.baseArrayLayer @ce +to @cpp imageRange.min().y() @ce, @cpp imageSubresource.layerCount @ce to +@cpp imageRange.sizeY() @ce, Y and Z coordinate of `imageOffset` to @cpp 0 @ce +and Y and Z coordinate of `imageExtent` to @cpp 1 @ce. +*/ +class MAGNUM_VK_EXPORT BufferImageCopy1DArray: public BufferImageCopy { + public: + /** @brief Constructor */ + /*implicit*/ BufferImageCopy1DArray(UnsignedLong bufferOffset, UnsignedInt bufferRowLength, ImageAspect imageAspect, Int imageLevel, const Range2Di& imageRange); + + /** + * @brief Copy tightly packed images + * + * Equivalent to calling @ref BufferImageCopy1DArray(UnsignedLong, UnsignedInt, ImageAspect, Int, const Range2Di&) + * with @p bufferRowLength set to @cpp 0 @ce. + */ + /*implicit*/ BufferImageCopy1DArray(UnsignedLong bufferOffset, ImageAspect imageAspect, Int imageLevel, const Range2Di& imageRange): BufferImageCopy1DArray{bufferOffset, 0, imageAspect, imageLevel, imageRange} {} +}; + +/** +@brief Convenience constructor for buffer / 2D array image copy region +@m_since_latest + +Compared to the base @ref BufferImageCopy::BufferImageCopy() "BufferImageCopy constructor" +sets @cpp imageSubresource.baseArrayLayer @ce to @cpp imageRange.min().z() @ce, +@cpp imageSubresource.layerCount @ce to @cpp imageRange.sizeZ() @ce Z +coordinate of `imageOffset` to @cpp 0 @ce and Z coordinate of `imageExtent` to +@cpp 1 @ce. +*/ +class MAGNUM_VK_EXPORT BufferImageCopy2DArray: public BufferImageCopy { + public: + /** @brief Constructor */ + /*implicit*/ BufferImageCopy2DArray(UnsignedLong bufferOffset, UnsignedInt bufferRowLength, UnsignedInt bufferImageHeight, ImageAspect imageAspect, Int imageLevel, const Range3Di& imageRange); + + /** + * @brief Copy tightly packed images + * + * Equivalent tocalling @ref BufferImageCopy2DArray(UnsignedLong, UnsignedInt, UnsignedInt, ImageAspect, Int, const Range3Di&) + * with @p bufferRowLength and @p bufferImageHeight set to @cpp 0 @ce. + */ + /*implicit*/ BufferImageCopy2DArray(UnsignedLong bufferOffset, ImageAspect imageAspect, Int imageLevel, const Range3Di& imageRange): BufferImageCopy2DArray{bufferOffset, 0, 0, imageAspect, imageLevel, imageRange} {} +}; + +/** +@brief Convenience constructor for buffer / cube map image copy region +@m_since_latest + +Compared to the base @ref BufferImageCopy::BufferImageCopy() "BufferImageCopy constructor" +sets @cpp imageSubresource.baseArrayLayer @ce to @cpp 0 @ce, +@cpp imageSubresource.layerCount @to @cpp 6 @ce, Z coordinate of `imageOffset` +to @cpp 0 @ce and Z coordinate of `imageExtent` to @cpp 1 @ce. +*/ +class MAGNUM_VK_EXPORT BufferImageCopyCubeMap: public BufferImageCopy { + public: + /** @brief Constructor */ + /*implicit*/ BufferImageCopyCubeMap(UnsignedLong bufferOffset, UnsignedInt bufferRowLength, UnsignedInt bufferImageHeight, ImageAspect imageAspect, Int imageLevel, const Range2Di& imageRange); + + /** + * @brief Copy tightly packed images + * + * Equivalent tocalling @ref BufferImageCopyCubeMap(UnsignedLong, UnsignedInt, UnsignedInt, ImageAspect, Int, const Range2Di&) + * with @p bufferRowLength and @p bufferImageHeight set to @cpp 0 @ce. + */ + /*implicit*/ BufferImageCopyCubeMap(UnsignedLong bufferOffset, ImageAspect imageAspect, Int imageLevel, const Range2Di& imageRange): BufferImageCopyCubeMap{bufferOffset, 0, 0, imageAspect, imageLevel, imageRange} {} +}; + +/** +@brief Convenience constructor for buffer / cube map image copy region +@m_since_latest + +Compared to the base @ref BufferImageCopy::BufferImageCopy() "BufferImageCopy constructor" +sets @cpp imageSubresource.baseArrayLayer @ce to @cpp imageRange.min().z() @ce, +@cpp imageSubresource.layerCount @to @cpp imageRange.sizeZ() @ce, Z coordinate +of `imageOffset` to @cpp 0 @ce and Z coordinate of `imageExtent` to @cpp 1 @ce. +*/ +class MAGNUM_VK_EXPORT BufferImageCopyCubeMapArray: public BufferImageCopy { + public: + /** @brief Constructor */ + /*implicit*/ BufferImageCopyCubeMapArray(UnsignedLong bufferOffset, UnsignedInt bufferRowLength, UnsignedInt bufferImageHeight, ImageAspect imageAspect, Int imageLevel, const Range3Di& imageRange); + + /** + * @brief Copy tightly packed images + * + * Equivalent tocalling @ref BufferImageCopyCubeMapArray(UnsignedLong, UnsignedInt, UnsignedInt, ImageAspect, Int, const Range3Di&) + * with @p bufferRowLength and @p bufferImageHeight set to @cpp 0 @ce. + */ + /*implicit*/ BufferImageCopyCubeMapArray(UnsignedLong bufferOffset, ImageAspect imageAspect, Int imageLevel, const Range3Di& imageRange): BufferImageCopyCubeMapArray{bufferOffset, 0, 0, imageAspect, imageLevel, imageRange} {} +}; + +/** +@brief Buffer to image copy command +@m_since_latest + +Wraps a @type_vk_keyword{CopyBufferToImageInfo2KHR}. This class is subsequently +used in @ref CommandBuffer::copyBufferToImage(). See @ref Vk-Image-usage-copy +for more information. + +@section Vk-CopyBufferToImageInfo-compatibility Compatibility with vkCmdCopyBufferToImage() + +While the class operates on the @type_vk{CopyBufferToImageInfo2KHR} structure +that's provided by the @vk_extension{KHR,copy_commands2} extension, conversion +from and to the set of parameters accepted by @fn_vk{CmdCopyBufferToImage} is +provided to some extent --- you can create @ref BufferImageCopy instances out +of @type_vk{BufferImageCopy} structures, pass them together with the rest to +@ref CopyBufferToImageInfo and then get a @type_vk{BufferImageCopy} list back +again using @ref vkBufferImageCopies(). + +For direct editing of the Vulkan structure, it's recommended to edit the +@type_vk{CopyBufferToImageInfo2KHR} fields and then perform the conversion +instead of editing the resulting @type_vk{BufferImageCopy} list, as additional +safety checks may be done during the conversion to ensure no information is +lost. + +@see @ref CopyBufferToImageInfo1D, @ref CopyBufferToImageInfo2D, + @ref CopyBufferToImageInfo3D, @ref CopyBufferToImageInfo1DArray, + @ref CopyBufferToImageInfo2DArray, @ref CopyBufferToImageInfoCubeMap, + @ref CopyBufferToImageInfoCubeMapArray, @ref CopyBufferInfo, + @ref CopyImageInfo, @ref CopyImageToBufferInfo +*/ +class MAGNUM_VK_EXPORT CopyBufferToImageInfo { + public: + /** + * @brief Constructor + * @param source Source @ref Buffer or a raw Vulkan buffer + * handle. Expected to have been created with + * @ref BufferUsage::TransferSource. + * @param destination Destination @ref Image or a raw Vulkan + * image handle. Expected to have been created with + * @ref ImageUsage::TransferDestination and a @ref PixelFormat + * usable for transfer destination. + * @param destinationLayout Destination image layout. Can be either + * @ref ImageLayout::General or + * @ref ImageLayout::TransferDestination. + * @param regions Regions to copy. There has to be at least + * one. + * + * The following @type_vk{CopyBufferToImageInfo2KHR} fields are + * pre-filled in addition to `sType`, everything else is zero-filled: + * + * - `srcBuffer` to @p source + * - `dstImage` to @p destination + * - `dstImageLayout` to @p destinationLayout + * - `regionCount` and `pRegions` to @p regions + * + * @todoc mention ImageLayout::SharedPresent being allowed once the + * extension is exposed + */ + /*implicit*/ CopyBufferToImageInfo(VkBuffer source, VkImage destination, ImageLayout destinationLayout, Containers::ArrayView regions); + /** @overload */ + /*implicit*/ CopyBufferToImageInfo(VkBuffer source, VkImage destination, ImageLayout destinationLayout, std::initializer_list regions); + + /** + * @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 CopyBufferToImageInfo(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 CopyBufferToImageInfo(const VkCopyBufferToImageInfo2KHR& info); + + /** + * @brief Corresponding @type_vk{BufferImageCopy} structures + * + * Provided for compatibility with Vulkan implementations that don't + * support the @vk_extension{KHR,copy_commands2} extension. See + * @ref Vk-CopyBufferToImageInfo-compatibility for more information. + * @see @ref CopyBufferInfo::vkBufferCopies(), + * @ref BufferCopy::vkBufferCopy(), + * @ref CopyImageInfo::vkImageCopies(), + * @ref ImageCopy::vkImageCopy(), + * @ref CopyImageToBufferInfo::vkBufferImageCopies(), + * @ref BufferImageCopy::vkBufferImageCopy() + */ + Containers::Array vkBufferImageCopies() const; + + /** @brief Underlying @type_vk{CopyBufferToImageInfo2KHR} structure */ + VkCopyBufferToImageInfo2KHR& operator*() { return _info; } + /** @overload */ + const VkCopyBufferToImageInfo2KHR& operator*() const { return _info; } + /** @overload */ + VkCopyBufferToImageInfo2KHR* operator->() { return &_info; } + /** @overload */ + const VkCopyBufferToImageInfo2KHR* operator->() const { return &_info; } + /** @overload */ + operator const VkCopyBufferToImageInfo2KHR*() const { return &_info; } + + private: + VkCopyBufferToImageInfo2KHR _info; + Containers::ArrayTuple _data; +}; + +/** +@brief Convenience constructor for a buffer to 1D image copy command +@m_since_latest + +Compared to the base @ref CopyBufferToImageInfo::CopyBufferToImageInfo() "CopyBufferToImageInfo constructor" +accepts a list of @ref BufferImageCopy1D convenience structures that are more +suited for copying 1D images. See @ref Vk-Image-usage-copy for example usage of +these convenience classes. +*/ +class MAGNUM_VK_EXPORT CopyBufferToImageInfo1D: public CopyBufferToImageInfo { + public: + /** @brief Constructor */ + /*implicit*/ CopyBufferToImageInfo1D(VkBuffer source, VkImage destination, ImageLayout destinationLayout, Containers::ArrayView regions); + /** @overload */ + /*implicit*/ CopyBufferToImageInfo1D(VkBuffer source, VkImage destination, ImageLayout destinationLayout, std::initializer_list regions); +}; + +/** +@brief Convenience constructor for a buffer to 2D image copy command +@m_since_latest + +Compared to the base @ref CopyBufferToImageInfo::CopyBufferToImageInfo() "CopyBufferToImageInfo constructor" +accepts a list of @ref BufferImageCopy2D convenience structures that are more +suited for copying 2D images. See @ref Vk-Image-usage-copy for example usage of +these convenience classes. +*/ +class MAGNUM_VK_EXPORT CopyBufferToImageInfo2D: public CopyBufferToImageInfo { + public: + /** @brief Constructor */ + /*implicit*/ CopyBufferToImageInfo2D(VkBuffer source, VkImage destination, ImageLayout destinationLayout, Containers::ArrayView regions); + /** @overload */ + /*implicit*/ CopyBufferToImageInfo2D(VkBuffer source, VkImage destination, ImageLayout destinationLayout, std::initializer_list regions); +}; + +/** +@brief Convenience constructor for a buffer to 3D image copy command +@m_since_latest + +Compared to the base @ref CopyBufferToImageInfo::CopyBufferToImageInfo() "CopyBufferToImageInfo constructor" +accepts a list of @ref BufferImageCopy3D convenience structures that are more +suited for copying 3D images. See @ref Vk-Image-usage-copy for example usage of +these convenience classes. +*/ +class MAGNUM_VK_EXPORT CopyBufferToImageInfo3D: public CopyBufferToImageInfo { + public: + /** @brief Constructor */ + /*implicit*/ CopyBufferToImageInfo3D(VkBuffer source, VkImage destination, ImageLayout destinationLayout, Containers::ArrayView regions); + /** @overload */ + /*implicit*/ CopyBufferToImageInfo3D(VkBuffer source, VkImage destination, ImageLayout destinationLayout, std::initializer_list regions); +}; + +/** +@brief Convenience constructor for a buffer to 1D array image copy command +@m_since_latest + +Compared to the base @ref CopyBufferToImageInfo::CopyBufferToImageInfo() "CopyBufferToImageInfo constructor" +accepts a list of @ref BufferImageCopy1DArray convenience structures that are +more suited for copying 1D array images. See @ref Vk-Image-usage-copy for +example usage of these convenience classes. +*/ +class MAGNUM_VK_EXPORT CopyBufferToImageInfo1DArray: public CopyBufferToImageInfo { + public: + /** @brief Constructor */ + /*implicit*/ CopyBufferToImageInfo1DArray(VkBuffer source, VkImage destination, ImageLayout destinationLayout, Containers::ArrayView regions); + /** @overload */ + /*implicit*/ CopyBufferToImageInfo1DArray(VkBuffer source, VkImage destination, ImageLayout destinationLayout, std::initializer_list regions); +}; + +/** +@brief Convenience constructor for a buffer to 2D array image copy command +@m_since_latest + +Compared to the base @ref CopyBufferToImageInfo::CopyBufferToImageInfo() "CopyBufferToImageInfo constructor" +accepts a list of @ref BufferImageCopy2DArray convenience structures that are +more suited for copying 2D array images. See @ref Vk-Image-usage-copy for +example usage of these convenience classes. +*/ +class MAGNUM_VK_EXPORT CopyBufferToImageInfo2DArray: public CopyBufferToImageInfo { + public: + /** @brief Constructor */ + /*implicit*/ CopyBufferToImageInfo2DArray(VkBuffer source, VkImage destination, ImageLayout destinationLayout, Containers::ArrayView regions); + /** @overload */ + /*implicit*/ CopyBufferToImageInfo2DArray(VkBuffer source, VkImage destination, ImageLayout destinationLayout, std::initializer_list regions); +}; + +/** +@brief Convenience constructor for a buffer to cube map image copy command +@m_since_latest + +Compared to the base @ref CopyBufferToImageInfo::CopyBufferToImageInfo() "CopyBufferToImageInfo constructor" +accepts a list of @ref BufferImageCopyCubeMap convenience structures that are +more suited for copying cube map images. See @ref Vk-Image-usage-copy for +example usage of these convenience classes. +*/ +class MAGNUM_VK_EXPORT CopyBufferToImageInfoCubeMap: public CopyBufferToImageInfo { + public: + /** @brief Constructor */ + /*implicit*/ CopyBufferToImageInfoCubeMap(VkBuffer source, VkImage destination, ImageLayout destinationLayout, Containers::ArrayView regions); + /** @overload */ + /*implicit*/ CopyBufferToImageInfoCubeMap(VkBuffer source, VkImage destination, ImageLayout destinationLayout, std::initializer_list regions); +}; + +/** +@brief Convenience constructor for a buffer to cube map array image copy command +@m_since_latest + +Compared to the base @ref CopyBufferToImageInfo::CopyBufferToImageInfo() "CopyBufferToImageInfo constructor" +accepts a list of @ref BufferImageCopyCubeMapArray convenience structures that +are more suited for copying cube map images. See @ref Vk-Image-usage-copy for +example usage of these convenience classes. +*/ +class MAGNUM_VK_EXPORT CopyBufferToImageInfoCubeMapArray: public CopyBufferToImageInfo { + public: + /** @brief Constructor */ + /*implicit*/ CopyBufferToImageInfoCubeMapArray(VkBuffer source, VkImage destination, ImageLayout destinationLayout, Containers::ArrayView regions); + /** @overload */ + /*implicit*/ CopyBufferToImageInfoCubeMapArray(VkBuffer source, VkImage destination, ImageLayout destinationLayout, std::initializer_list regions); +}; + +/** +@brief Image to buffer copy command +@m_since_latest + +Wraps a @type_vk_keyword{CopyImageToBufferInfo2KHR}. This class is subsequently +used in @ref CommandBuffer::copyImageToBuffer(). See @ref Vk-Image-usage-copy +for more information. + +@section Vk-CopyImageToBufferInfo-compatibility Compatibility with vkCmdCopyImageToBuffer() + +While the class operates on the @type_vk{CopyImageToBufferInfo2KHR} structure +that's provided by the @vk_extension{KHR,copy_commands2} extension, conversion +from and to the set of parameters accepted by @fn_vk{CmdCopyImageToBuffer} is +provided to some extent --- you can create @ref BufferImageCopy instances out +of @type_vk{BufferImageCopy} structures, pass them together with the rest to +@ref CopyImageToBufferInfo and then get a @type_vk{BufferImageCopy} list back +again using @ref vkBufferImageCopies(). + +For direct editing of the Vulkan structure, it's recommended to edit the +@type_vk{CopyImageToBufferInfo2KHR} fields and then perform the conversion +instead of editing the resulting @type_vk{BufferImageCopy} list, as additional +safety checks may be done during the conversion to ensure no information is +lost. + +@see @ref CopyImageToBufferInfo1D, @ref CopyImageToBufferInfo2D, + @ref CopyImageToBufferInfo3D, @ref CopyImageToBufferInfo1DArray, + @ref CopyImageToBufferInfo2DArray, @ref CopyImageToBufferInfoCubeMap, + @ref CopyImageToBufferInfoCubeMapArray, @ref CopyBufferInfo, + @ref CopyImageInfo, @ref CopyBufferToImageInfo +*/ +class MAGNUM_VK_EXPORT CopyImageToBufferInfo { + public: + /** + * @brief Constructor + * @param source Source @ref Image or a raw Vulkan image + * handle. Expected to have been created with + * @ref ImageUsage::TransferSource and a @ref PixelFormat usable + * for transfer source. + * @param sourceLayout Source image layout. Can be either + * @ref ImageLayout::General or @ref ImageLayout::TransferSource. + * @param destination Destination @ref Buffer or a raw Vulkan + * buffer handle. Expected to have been created with + * @ref BufferUsage::TransferDestination. + * @param regions Regions to copy. There has to be at least + * one. + * + * The following @type_vk{CopyBufferToImageInfo2KHR} fields are + * pre-filled in addition to `sType`, everything else is zero-filled: + * + * - `srcImage` to @p source + * - `srcImageLayout` to @p sourceLayout + * - `dstBuffer` to @p destination + * - `regionCount` and `pRegions` to @p regions + * + * @todoc mention ImageLayout::SharedPresent being allowed once the + * extension is exposed + */ + /*implicit*/ CopyImageToBufferInfo(VkImage source, ImageLayout sourceLayout, VkBuffer destination, Containers::ArrayView regions); + /** @overload */ + /*implicit*/ CopyImageToBufferInfo(VkImage source, ImageLayout sourceLayout, VkBuffer destination, std::initializer_list regions); + + /** + * @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 CopyImageToBufferInfo(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 CopyImageToBufferInfo(const VkCopyImageToBufferInfo2KHR& info); + + /** + * @brief Corresponding @type_vk{BufferImageCopy} structures + * + * Provided for compatibility with Vulkan implementations that don't + * support the @vk_extension{KHR,copy_commands2} extension. See + * @ref Vk-CopyImageToBufferInfo-compatibility for more information. + * @see @ref CopyBufferInfo::vkBufferCopies(), + * @ref BufferCopy::vkBufferCopy(), + * @ref CopyImageInfo::vkImageCopies(), + * @ref ImageCopy::vkImageCopy(), + * @ref CopyBufferToImageInfo::vkBufferImageCopies(), + * @ref BufferImageCopy::vkBufferImageCopy() + */ + Containers::Array vkBufferImageCopies() const; + + /** @brief Underlying @type_vk{CopyImageToBufferInfo2KHR} structure */ + VkCopyImageToBufferInfo2KHR& operator*() { return _info; } + /** @overload */ + const VkCopyImageToBufferInfo2KHR& operator*() const { return _info; } + /** @overload */ + VkCopyImageToBufferInfo2KHR* operator->() { return &_info; } + /** @overload */ + const VkCopyImageToBufferInfo2KHR* operator->() const { return &_info; } + /** @overload */ + operator const VkCopyImageToBufferInfo2KHR*() const { return &_info; } + + private: + VkCopyImageToBufferInfo2KHR _info; + Containers::ArrayTuple _data; +}; + +/** +@brief Convenience constructor for a 1D image to buffer copy command +@m_since_latest + +Compared to the base @ref CopyImageToBufferInfo::CopyImageToBufferInfo() "CopyImageToBufferInfo constructor" +accepts a list of @ref BufferImageCopy1D convenience structures that are more +suited for copying 1D images. See @ref Vk-Image-usage-copy for example usage of +these convenience classes. +*/ +class MAGNUM_VK_EXPORT CopyImageToBufferInfo1D: public CopyImageToBufferInfo { + public: + /** @brief Constructor */ + /*implicit*/ CopyImageToBufferInfo1D(VkImage source, ImageLayout sourceLayout, VkBuffer destination, Containers::ArrayView regions); + /** @overload */ + /*implicit*/ CopyImageToBufferInfo1D(VkImage source, ImageLayout sourceLayout, VkBuffer destination, std::initializer_list regions); +}; + +/** +@brief Convenience constructor for a 2D image to buffer copy command +@m_since_latest + +Compared to the base @ref CopyImageToBufferInfo::CopyImageToBufferInfo() "CopyImageToBufferInfo constructor" +accepts a list of @ref BufferImageCopy2D convenience structures that are more +suited for copying 2D images. See @ref Vk-Image-usage-copy for example usage of +these convenience classes. +*/ +class MAGNUM_VK_EXPORT CopyImageToBufferInfo2D: public CopyImageToBufferInfo { + public: + /** @brief Constructor */ + /*implicit*/ CopyImageToBufferInfo2D(VkImage source, ImageLayout sourceLayout, VkBuffer destination, Containers::ArrayView regions); + /** @overload */ + /*implicit*/ CopyImageToBufferInfo2D(VkImage source, ImageLayout sourceLayout, VkBuffer destination, std::initializer_list regions); +}; + +/** +@brief Convenience constructor for a 3D image to buffer copy command +@m_since_latest + +Compared to the base @ref CopyImageToBufferInfo::CopyImageToBufferInfo() "CopyImageToBufferInfo constructor" +accepts a list of @ref BufferImageCopy3D convenience structures that are more +suited for copying 3D images. See @ref Vk-Image-usage-copy for example usage of +these convenience classes. +*/ +class MAGNUM_VK_EXPORT CopyImageToBufferInfo3D: public CopyImageToBufferInfo { + public: + /** @brief Constructor */ + /*implicit*/ CopyImageToBufferInfo3D(VkImage source, ImageLayout sourceLayout, VkBuffer destination, Containers::ArrayView regions); + /** @overload */ + /*implicit*/ CopyImageToBufferInfo3D(VkImage source, ImageLayout sourceLayout, VkBuffer destination, std::initializer_list regions); +}; + +/** +@brief Convenience constructor for a 1D array image to buffer copy command +@m_since_latest + +Compared to the base @ref CopyImageToBufferInfo::CopyImageToBufferInfo() "CopyImageToBufferInfo constructor" +accepts a list of @ref BufferImageCopy1DArray convenience structures that are +more suited for copying 1D array images. See @ref Vk-Image-usage-copy for +example usage of these convenience classes. +*/ +class MAGNUM_VK_EXPORT CopyImageToBufferInfo1DArray: public CopyImageToBufferInfo { + public: + /** @brief Constructor */ + /*implicit*/ CopyImageToBufferInfo1DArray(VkImage source, ImageLayout sourceLayout, VkBuffer destination, Containers::ArrayView regions); + /** @overload */ + /*implicit*/ CopyImageToBufferInfo1DArray(VkImage source, ImageLayout sourceLayout, VkBuffer destination, std::initializer_list regions); +}; + +/** +@brief Convenience constructor for a 2D array image to buffer copy command +@m_since_latest + +Compared to the base @ref CopyImageToBufferInfo::CopyImageToBufferInfo() "CopyImageToBufferInfo constructor" +accepts a list of @ref BufferImageCopy2DArray convenience structures that are +more suited for copying 2D array images. See @ref Vk-Image-usage-copy for +example usage of these convenience classes. +*/ +class MAGNUM_VK_EXPORT CopyImageToBufferInfo2DArray: public CopyImageToBufferInfo { + public: + /** @brief Constructor */ + /*implicit*/ CopyImageToBufferInfo2DArray(VkImage source, ImageLayout sourceLayout, VkBuffer destination, Containers::ArrayView regions); + /** @overload */ + /*implicit*/ CopyImageToBufferInfo2DArray(VkImage source, ImageLayout sourceLayout, VkBuffer destination, std::initializer_list regions); +}; + +/** +@brief Convenience constructor for a cube map image to buffer copy command +@m_since_latest + +Compared to the base @ref CopyImageToBufferInfo::CopyImageToBufferInfo() "CopyImageToBufferInfo constructor" +accepts a list of @ref BufferImageCopyCubeMap convenience structures that are +more suited for copying cube map images. See @ref Vk-Image-usage-copy for +example usage of these convenience classes. +*/ +class MAGNUM_VK_EXPORT CopyImageToBufferInfoCubeMap: public CopyImageToBufferInfo { + public: + /** @brief Constructor */ + /*implicit*/ CopyImageToBufferInfoCubeMap(VkImage source, ImageLayout sourceLayout, VkBuffer destination, Containers::ArrayView regions); + /** @overload */ + /*implicit*/ CopyImageToBufferInfoCubeMap(VkImage source, ImageLayout sourceLayout, VkBuffer destination, std::initializer_list regions); +}; + +/** +@brief Convenience constructor for a cube map array image to buffer copy command +@m_since_latest + +Compared to the base @ref CopyImageToBufferInfo::CopyImageToBufferInfo() "CopyImageToBufferInfo constructor" +accepts a list of @ref BufferImageCopyCubeMapArray convenience structures that +are more suited for copying cube map array images. See @ref Vk-Image-usage-copy +for example usage of these convenience classes. +*/ +class MAGNUM_VK_EXPORT CopyImageToBufferInfoCubeMapArray: public CopyImageToBufferInfo { + public: + /** @brief Constructor */ + /*implicit*/ CopyImageToBufferInfoCubeMapArray(VkImage source, ImageLayout sourceLayout, VkBuffer destination, Containers::ArrayView regions); + /** @overload */ + /*implicit*/ CopyImageToBufferInfoCubeMapArray(VkImage source, ImageLayout sourceLayout, VkBuffer destination, std::initializer_list regions); +}; + }} #endif diff --git a/src/Magnum/Vk/Implementation/DeviceState.cpp b/src/Magnum/Vk/Implementation/DeviceState.cpp index 69b71ea08..459b80382 100644 --- a/src/Magnum/Vk/Implementation/DeviceState.cpp +++ b/src/Magnum/Vk/Implementation/DeviceState.cpp @@ -80,6 +80,18 @@ DeviceState::DeviceState(Device& device) { cmdNextSubpassImplementation = &CommandBuffer::nextSubpassImplementationDefault; cmdEndRenderPassImplementation = &CommandBuffer::endRenderPassImplementationDefault; } + + if(device.isExtensionEnabled()) { + cmdCopyBufferImplementation = &CommandBuffer::copyBufferImplementationKHR; + cmdCopyImageImplementation = &CommandBuffer::copyImageImplementationKHR; + cmdCopyBufferToImageImplementation = &CommandBuffer::copyBufferToImageImplementationKHR; + cmdCopyImageToBufferImplementation = &CommandBuffer::copyImageToBufferImplementationKHR; + } else { + cmdCopyBufferImplementation = &CommandBuffer::copyBufferImplementationDefault; + cmdCopyImageImplementation = &CommandBuffer::copyImageImplementationDefault; + cmdCopyBufferToImageImplementation = &CommandBuffer::copyBufferToImageImplementationDefault; + cmdCopyImageToBufferImplementation = &CommandBuffer::copyImageToBufferImplementationDefault; + } } }}} diff --git a/src/Magnum/Vk/Implementation/DeviceState.h b/src/Magnum/Vk/Implementation/DeviceState.h index ed7cc265c..60b2b3b91 100644 --- a/src/Magnum/Vk/Implementation/DeviceState.h +++ b/src/Magnum/Vk/Implementation/DeviceState.h @@ -53,6 +53,11 @@ struct DeviceState { void(*cmdBeginRenderPassImplementation)(CommandBuffer&, const VkRenderPassBeginInfo&, const VkSubpassBeginInfo&); void(*cmdNextSubpassImplementation)(CommandBuffer&, const VkSubpassEndInfo&, const VkSubpassBeginInfo&); void(*cmdEndRenderPassImplementation)(CommandBuffer&, const VkSubpassEndInfo&); + + void(*cmdCopyBufferImplementation)(CommandBuffer&, const CopyBufferInfo&); + void(*cmdCopyImageImplementation)(CommandBuffer&, const CopyImageInfo&); + void(*cmdCopyBufferToImageImplementation)(CommandBuffer&, const CopyBufferToImageInfo&); + void(*cmdCopyImageToBufferImplementation)(CommandBuffer&, const CopyImageToBufferInfo&); }; }}} diff --git a/src/Magnum/Vk/Pipeline.h b/src/Magnum/Vk/Pipeline.h index f59646597..bdb751ef7 100644 --- a/src/Magnum/Vk/Pipeline.h +++ b/src/Magnum/Vk/Pipeline.h @@ -401,7 +401,9 @@ CORRADE_ENUMSET_OPERATORS(DependencyFlags) @m_since_latest Wraps a @type_vk_keyword{MemoryBarrier}. This class is subsequently used in -@ref CommandBuffer::pipelineBarrier(). +@ref CommandBuffer::pipelineBarrier(), see @ref Vk-Image-usage-copy and +@ref Vk-Buffer-usage-copy for usage examples. +@see @ref BufferMemoryBarrier, @ref ImageMemoryBarrier */ class MAGNUM_VK_EXPORT MemoryBarrier { public: @@ -458,7 +460,8 @@ class MAGNUM_VK_EXPORT MemoryBarrier { Wraps a @type_vk_keyword{BufferMemoryBarrier}. Compared to @ref MemoryBarrier only affects a single buffer. This class is subsequently used in -@ref CommandBuffer::pipelineBarrier(). +@ref CommandBuffer::pipelineBarrier(), see @ref Vk-Buffer-usage-copy for usage +examples. */ class MAGNUM_VK_EXPORT BufferMemoryBarrier { public: @@ -525,7 +528,8 @@ class MAGNUM_VK_EXPORT BufferMemoryBarrier { Wraps a @type_vk_keyword{ImageMemoryBarrier}. Compared to @ref MemoryBarrier only affects a single image and additionally performs @ref ImageLayout transitions. This class is subsequently used in -@ref CommandBuffer::pipelineBarrier(). +@ref CommandBuffer::pipelineBarrier(), see @ref Vk-Image-usage-copy for usage +examples. */ class MAGNUM_VK_EXPORT ImageMemoryBarrier { public: diff --git a/src/Magnum/Vk/RenderPass.h b/src/Magnum/Vk/RenderPass.h index 94c54e1c6..68ed91005 100644 --- a/src/Magnum/Vk/RenderPass.h +++ b/src/Magnum/Vk/RenderPass.h @@ -312,7 +312,8 @@ class MAGNUM_VK_EXPORT RenderPassBeginInfo { * @brief Clear a floating-point or normalized color attachment * @return Reference to self (for method chaining) * - * @see @ref AttachmentLoadOperation::Clear + * @see @ref AttachmentLoadOperation::Clear, + * @ref CommandBuffer::clearColorImage(VkImage, ImageLayout, const Color4&) */ RenderPassBeginInfo& clearColor(UnsignedInt attachment, const Color4& color); @@ -320,7 +321,8 @@ class MAGNUM_VK_EXPORT RenderPassBeginInfo { * @brief Clear a signed integral color attachment * @return Reference to self (for method chaining) * - * @see @ref AttachmentLoadOperation::Clear + * @see @ref AttachmentLoadOperation::Clear, + * @ref CommandBuffer::clearColorImage(VkImage, ImageLayout, const Vector4i&) */ RenderPassBeginInfo& clearColor(UnsignedInt attachment, const Vector4i& color); @@ -328,7 +330,8 @@ class MAGNUM_VK_EXPORT RenderPassBeginInfo { * @brief Clear an unsigned integral color attachment * @return Reference to self (for method chaining) * - * @see @ref AttachmentLoadOperation::Clear + * @see @ref AttachmentLoadOperation::Clear, + * @ref CommandBuffer::clearColorImage(VkImage, ImageLayout, const Vector4ui&) */ RenderPassBeginInfo& clearColor(UnsignedInt attachment, const Vector4ui& color); @@ -338,7 +341,10 @@ class MAGNUM_VK_EXPORT RenderPassBeginInfo { * * If the attachment is not a combined depth/stencil format, the unused * value is ignored. - * @see @ref AttachmentLoadOperation::Clear + * @see @ref AttachmentLoadOperation::Clear, + * @ref CommandBuffer::clearDepthStencilImage(VkImage, ImageLayout, Float, UnsignedInt), + * @ref CommandBuffer::clearDepthImage(VkImage, ImageLayout, Float), + * @ref CommandBuffer::clearStencilImage(VkImage, ImageLayout, UnsignedInt) */ RenderPassBeginInfo& clearDepthStencil(UnsignedInt attachment, Float depth, UnsignedInt stencil); diff --git a/src/Magnum/Vk/Test/BufferTest.cpp b/src/Magnum/Vk/Test/BufferTest.cpp index cc02a78ec..673b902eb 100644 --- a/src/Magnum/Vk/Test/BufferTest.cpp +++ b/src/Magnum/Vk/Test/BufferTest.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -43,6 +44,23 @@ struct BufferTest: TestSuite::Tester { void constructCopy(); void dedicatedMemoryNotDedicated(); + + /* While *ConstructFromVk() tests that going from VkFromThing -> Vk::Thing + -> VkToThing doesn't result in information loss, the *ConvertToVk() + tests additionally check that all calls both on our APIs and by editing + the contained structure are correctly propagated to the resulting + structures. */ + + void bufferCopyConstruct(); + void bufferCopyConstructNoInit(); + template void bufferCopyConstructFromVk(); + template void bufferCopyConvertToVk(); + void bufferCopyConvertDisallowed(); + + void copyBufferInfoConstruct(); + void copyBufferInfoConstructNoInit(); + void copyBufferInfoConstructFromVk(); + void copyBufferInfoConvertToVk(); }; BufferTest::BufferTest() { @@ -53,9 +71,41 @@ BufferTest::BufferTest() { &BufferTest::constructNoCreate, &BufferTest::constructCopy, - &BufferTest::dedicatedMemoryNotDedicated}); + &BufferTest::dedicatedMemoryNotDedicated, + + &BufferTest::bufferCopyConstruct, + &BufferTest::bufferCopyConstructNoInit, + &BufferTest::bufferCopyConstructFromVk, + &BufferTest::bufferCopyConstructFromVk, + &BufferTest::bufferCopyConstructFromVk, + &BufferTest::bufferCopyConstructFromVk, + &BufferTest::bufferCopyConvertToVk, + &BufferTest::bufferCopyConvertToVk, + &BufferTest::bufferCopyConvertDisallowed, + + &BufferTest::copyBufferInfoConstruct, + &BufferTest::copyBufferInfoConstructNoInit, + &BufferTest::copyBufferInfoConstructFromVk, + &BufferTest::copyBufferInfoConvertToVk}); } +template struct Traits; +#define _c(type) \ + template<> struct Traits { \ + static const char* name() { return #type; } \ + static Vk ## type convert(const type& instance) { \ + return instance.vk ## type (); \ + } \ + }; \ + template<> struct Traits { \ + static const char* name() { return #type "2KHR"; } \ + static Vk ## type ## 2KHR convert(const type& instance) { \ + return instance; \ + } \ + }; +_c(BufferCopy) +#undef _c + void BufferTest::createInfoConstruct() { /** @todo use a real flag once at least one is exposed */ BufferCreateInfo info{BufferUsage::UniformBuffer, 1024, BufferCreateInfo::Flag(VK_BUFFER_CREATE_PROTECTED_BIT)}; @@ -113,6 +163,111 @@ void BufferTest::dedicatedMemoryNotDedicated() { CORRADE_COMPARE(out.str(), "Vk::Buffer::dedicatedMemory(): buffer doesn't have a dedicated memory\n"); } +void BufferTest::bufferCopyConstruct() { + BufferCopy copy{3, 5, 7}; + CORRADE_COMPARE(copy->srcOffset, 3); + CORRADE_COMPARE(copy->dstOffset, 5); + CORRADE_COMPARE(copy->size, 7); +} + +void BufferTest::bufferCopyConstructNoInit() { + BufferCopy copy{NoInit}; + copy->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + new(©) BufferCopy{NoInit}; + CORRADE_COMPARE(copy->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)); +} + +template void BufferTest::bufferCopyConstructFromVk() { + setTestCaseTemplateName({Traits::name(), Traits::name()}); + + From from{}; + from.srcOffset = 3; + from.dstOffset = 5; + from.size = 7; + + BufferCopy copy{from}; + To to = Traits::convert(copy); + CORRADE_COMPARE(to.srcOffset, 3); + CORRADE_COMPARE(to.dstOffset, 5); + CORRADE_COMPARE(to.size, 7); +} + +template void BufferTest::bufferCopyConvertToVk() { + BufferCopy copy{3, 5, 7}; + + T out = Traits::convert(copy); + CORRADE_COMPARE(out.srcOffset, 3); + CORRADE_COMPARE(out.dstOffset, 5); + CORRADE_COMPARE(out.size, 7); +} + +void BufferTest::bufferCopyConvertDisallowed() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + BufferCopy copy{0, 0, 0}; + copy->pNext = © + + std::ostringstream out; + Error redirectError{&out}; + copy.vkBufferCopy(); + CORRADE_COMPARE(out.str(), "Vk::BufferCopy: disallowing conversion to VkBufferCopy with non-empty pNext to prevent information loss\n"); +} + +void BufferTest::copyBufferInfoConstruct() { + auto a = reinterpret_cast(0xdead); + auto b = reinterpret_cast(0xcafe); + + CopyBufferInfo info{a, b, { + {3, 0, 0}, + {0, 5, 0} + }}; + CORRADE_COMPARE(info->srcBuffer, a); + CORRADE_COMPARE(info->dstBuffer, b); + CORRADE_COMPARE(info->regionCount, 2); + CORRADE_VERIFY(info->pRegions); + CORRADE_COMPARE(info->pRegions[0].srcOffset, 3); + CORRADE_COMPARE(info->pRegions[1].dstOffset, 5); +} + +void BufferTest::copyBufferInfoConstructNoInit() { + CopyBufferInfo info{NoInit}; + info->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + new(&info) CopyBufferInfo{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 BufferTest::copyBufferInfoConstructFromVk() { + VkCopyBufferInfo2KHR vkInfo; + vkInfo.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + + CopyBufferInfo info{vkInfo}; + CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); +} + +void BufferTest::copyBufferInfoConvertToVk() { + CopyBufferInfo info{VkBuffer{}, VkBuffer{}, { + {3, 0, 0}, + {0, 5, 0} + }}; + + Containers::Array copies = info.vkBufferCopies(); + CORRADE_COMPARE(copies.size(), 2); + CORRADE_COMPARE(copies[0].srcOffset, 3); + CORRADE_COMPARE(copies[1].dstOffset, 5); +} + }}}} CORRADE_TEST_MAIN(Magnum::Vk::Test::BufferTest) diff --git a/src/Magnum/Vk/Test/BufferVkTest.cpp b/src/Magnum/Vk/Test/BufferVkTest.cpp index 40d5e8fcc..eaaaf5083 100644 --- a/src/Magnum/Vk/Test/BufferVkTest.cpp +++ b/src/Magnum/Vk/Test/BufferVkTest.cpp @@ -23,15 +23,18 @@ DEALINGS IN THE SOFTWARE. */ +#include #include #include #include #include +#include #include "Magnum/Vk/BufferCreateInfo.h" #include "Magnum/Vk/CommandBuffer.h" #include "Magnum/Vk/CommandPoolCreateInfo.h" #include "Magnum/Vk/DeviceProperties.h" +#include "Magnum/Vk/Extensions.h" #include "Magnum/Vk/Fence.h" #include "Magnum/Vk/Handle.h" #include "Magnum/Vk/MemoryAllocateInfo.h" @@ -57,6 +60,8 @@ struct BufferVkTest: VulkanTester { void directAllocation(); void cmdFillBuffer(); + void cmdCopyBuffer(); + void cmdCopyBufferDisallowedConversion(); }; BufferVkTest::BufferVkTest() { @@ -72,7 +77,9 @@ BufferVkTest::BufferVkTest() { &BufferVkTest::directAllocation, - &BufferVkTest::cmdFillBuffer}); + &BufferVkTest::cmdFillBuffer, + &BufferVkTest::cmdCopyBuffer, + &BufferVkTest::cmdCopyBufferDisallowedConversion}); } using namespace Containers::Literals; @@ -194,10 +201,9 @@ void BufferVkTest::cmdFillBuffer() { cmd.begin() .fillBuffer(a, 4, 8, 0x2e2e2e2e) - .pipelineBarrier( - PipelineStage::Transfer, PipelineStage::Host, - {{Access::TransferWrite, Access::HostRead}}, - {}, {}) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Host, { + {Access::TransferWrite, Access::HostRead} + }, {}, {}) .end(); queue().submit({SubmitInfo{}.setCommandBuffers({cmd})}).wait(); CORRADE_COMPARE(arrayView(a.dedicatedMemory().mapRead()), "0123........cdef"_s); @@ -206,15 +212,63 @@ void BufferVkTest::cmdFillBuffer() { pool.reset(); cmd.begin() .fillBuffer(a, 0x2e2e2e2e) - .pipelineBarrier( - PipelineStage::Transfer, PipelineStage::Host, - {{Access::TransferWrite, Access::HostRead}}, - {}, {}) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Host, { + {Access::TransferWrite, Access::HostRead} + }, {}, {}) .end(); queue().submit({SubmitInfo{}.setCommandBuffers({cmd})}).wait(); CORRADE_COMPARE(arrayView(a.dedicatedMemory().mapRead()), "................"_s); } +void BufferVkTest::cmdCopyBuffer() { + CommandPool pool{device(), CommandPoolCreateInfo{ + device().properties().pickQueueFamily(QueueFlag::Graphics)}}; + CommandBuffer cmd = pool.allocate(); + + /* Source buffer */ + Buffer a{device(), BufferCreateInfo{BufferUsage::TransferSource, 7}, MemoryFlag::HostVisible}; + Utility::copy("__ABCD_"_s, a.dedicatedMemory().map()); + + /* Destination buffer, clear it to have predictable output */ + Buffer b{device(), BufferCreateInfo{BufferUsage::TransferDestination, 10}, MemoryFlag::HostVisible}; + Utility::copy(".........."_s, b.dedicatedMemory().map()); + + cmd.begin() + .copyBuffer({a, b, {{2, 5, 4}}}) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Host, { + {Access::TransferWrite, Access::HostRead} + }, {}, {}) + .end(); + queue().submit({SubmitInfo{}.setCommandBuffers({cmd})}).wait(); + + CORRADE_COMPARE(arrayView(b.dedicatedMemory().mapRead()), + ".....ABCD."_s); +} + +void BufferVkTest::cmdCopyBufferDisallowedConversion() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + if(device().isExtensionEnabled()) + CORRADE_SKIP("KHR_copy_commands2 enabled on the device, can't test"); + + CommandPool pool{device(), CommandPoolCreateInfo{ + device().properties().pickQueueFamily(QueueFlag::Graphics)}}; + CommandBuffer cmd = pool.allocate(); + + CopyBufferInfo a{{}, {}, {}}; + a->pNext = &a; + + /* The commands shouldn't do anything, so it should be fine to just call + them without any render pass set up */ + std::ostringstream out; + Error redirectError{&out}; + cmd.copyBuffer(a); + CORRADE_COMPARE(out.str(), + "Vk::CommandBuffer::copyBuffer(): disallowing extraction of CopyBufferInfo with non-empty pNext to prevent information loss\n"); +} + }}}} CORRADE_TEST_MAIN(Magnum::Vk::Test::BufferVkTest) diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index d17dda6db..b420c09db 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -156,7 +156,7 @@ if(BUILD_VK_TESTS) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/configure.h) - corrade_add_test(VkBufferVkTest BufferVkTest.cpp LIBRARIES MagnumVulkanTester) + corrade_add_test(VkBufferVkTest BufferVkTest.cpp LIBRARIES MagnumVkTestLib MagnumVulkanTester) corrade_add_test(VkCommandBufferVkTest CommandBufferVkTest.cpp LIBRARIES MagnumVulkanTester) corrade_add_test(VkCommandPoolVkTest CommandPoolVkTest.cpp LIBRARIES MagnumVulkanTester) corrade_add_test(VkDeviceVkTest DeviceVkTest.cpp LIBRARIES MagnumVkTestLib MagnumVulkanTester) @@ -165,7 +165,7 @@ if(BUILD_VK_TESTS) corrade_add_test(VkFenceVkTest FenceVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester) 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(VkImageVkTest ImageVkTest.cpp LIBRARIES MagnumVkTestLib MagnumVulkanTester) 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) diff --git a/src/Magnum/Vk/Test/ImageTest.cpp b/src/Magnum/Vk/Test/ImageTest.cpp index f5f32b6c4..36d5894be 100644 --- a/src/Magnum/Vk/Test/ImageTest.cpp +++ b/src/Magnum/Vk/Test/ImageTest.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -60,6 +61,46 @@ struct ImageTest: TestSuite::Tester { void dedicatedMemoryNotDedicated(); + /* While *ConstructFromVk() tests that going from VkFromThing -> Vk::Thing + -> VkToThing doesn't result in information loss, the *ConvertToVk() + tests additionally check that all calls both on our APIs and by editing + the contained structure are correctly propagated to the resulting + structures. */ + + void imageCopyConstruct(); + void imageCopyConstructNoInit(); + template void imageCopyConstructFromVk(); + template void imageCopyConvertToVk(); + void imageCopyConvertDisallowed(); + + void copyImageInfoConstruct(); + void copyImageInfoConstructNoInit(); + void copyImageInfoConstructFromVk(); + void copyImageInfoConvertToVk(); + + void bufferImageCopyConstruct(); + void bufferImageCopyConstruct1D(); + void bufferImageCopyConstruct2D(); + void bufferImageCopyConstruct3D(); + void bufferImageCopyConstruct1DArray(); + void bufferImageCopyConstruct2DArray(); + void bufferImageCopyConstructCubeMap(); + void bufferImageCopyConstructCubeMapArray(); + void bufferImageCopyConstructNoInit(); + template void bufferImageCopyConstructFromVk(); + template void bufferImageCopyConvertToVk(); + void bufferImageCopyConvertDisallowed(); + + void copyBufferToImageInfoConstruct(); + void copyBufferToImageInfoConstructNoInit(); + void copyBufferToImageInfoConstructFromVk(); + void copyBufferToImageInfoConvertToVk(); + + void copyImageToBufferInfoConstruct(); + void copyImageToBufferInfoConstructNoInit(); + void copyImageToBufferInfoConstructFromVk(); + void copyImageToBufferInfoConvertToVk(); + void debugAspect(); void debugAspects(); }; @@ -101,10 +142,70 @@ ImageTest::ImageTest() { &ImageTest::dedicatedMemoryNotDedicated, + &ImageTest::imageCopyConstruct, + &ImageTest::imageCopyConstructNoInit, + &ImageTest::imageCopyConstructFromVk, + &ImageTest::imageCopyConstructFromVk, + &ImageTest::imageCopyConstructFromVk, + &ImageTest::imageCopyConstructFromVk, + &ImageTest::imageCopyConvertToVk, + &ImageTest::imageCopyConvertToVk, + &ImageTest::imageCopyConvertDisallowed, + + &ImageTest::copyImageInfoConstruct, + &ImageTest::copyImageInfoConstructNoInit, + &ImageTest::copyImageInfoConstructFromVk, + &ImageTest::copyImageInfoConvertToVk, + + &ImageTest::bufferImageCopyConstruct, + &ImageTest::bufferImageCopyConstruct1D, + &ImageTest::bufferImageCopyConstruct2D, + &ImageTest::bufferImageCopyConstruct3D, + &ImageTest::bufferImageCopyConstruct1DArray, + &ImageTest::bufferImageCopyConstruct2DArray, + &ImageTest::bufferImageCopyConstructCubeMap, + &ImageTest::bufferImageCopyConstructCubeMapArray, + &ImageTest::bufferImageCopyConstructNoInit, + &ImageTest::bufferImageCopyConstructFromVk, + &ImageTest::bufferImageCopyConstructFromVk, + &ImageTest::bufferImageCopyConstructFromVk, + &ImageTest::bufferImageCopyConstructFromVk, + &ImageTest::bufferImageCopyConvertToVk, + &ImageTest::bufferImageCopyConvertToVk, + &ImageTest::bufferImageCopyConvertDisallowed, + + &ImageTest::copyBufferToImageInfoConstruct, + &ImageTest::copyBufferToImageInfoConstructNoInit, + &ImageTest::copyBufferToImageInfoConstructFromVk, + &ImageTest::copyBufferToImageInfoConvertToVk, + + &ImageTest::copyImageToBufferInfoConstruct, + &ImageTest::copyImageToBufferInfoConstructNoInit, + &ImageTest::copyImageToBufferInfoConstructFromVk, + &ImageTest::copyImageToBufferInfoConvertToVk, + &ImageTest::debugAspect, &ImageTest::debugAspects}); } +template struct Traits; +#define _c(type) \ + template<> struct Traits { \ + static const char* name() { return #type; } \ + static Vk ## type convert(const type& instance) { \ + return instance.vk ## type (); \ + } \ + }; \ + template<> struct Traits { \ + static const char* name() { return #type "2KHR"; } \ + static Vk ## type ## 2KHR convert(const type& instance) { \ + return instance; \ + } \ + }; +_c(ImageCopy) +_c(BufferImageCopy) +#undef _c + template void ImageTest::createInfoConstruct() { setTestCaseTemplateName(PixelFormatTraits::name()); @@ -292,6 +393,438 @@ void ImageTest::dedicatedMemoryNotDedicated() { CORRADE_COMPARE(out.str(), "Vk::Image::dedicatedMemory(): image doesn't have a dedicated memory\n"); } +void ImageTest::imageCopyConstruct() { + ImageCopy copy{ImageAspect::Color|ImageAspect::Depth, 3, 5, 7, {9, 11, 13}, 4, 6, 8, {10, 12, 14}, {1, 2, 15}}; + CORRADE_COMPARE(copy->srcSubresource.aspectMask, VK_IMAGE_ASPECT_COLOR_BIT|VK_IMAGE_ASPECT_DEPTH_BIT); + CORRADE_COMPARE(copy->srcSubresource.mipLevel, 3); + CORRADE_COMPARE(copy->srcSubresource.baseArrayLayer, 5); + CORRADE_COMPARE(copy->srcSubresource.layerCount, 7); + CORRADE_COMPARE(Vector3i{copy->srcOffset}, (Vector3i{9, 11, 13})); + CORRADE_COMPARE(copy->dstSubresource.aspectMask, VK_IMAGE_ASPECT_COLOR_BIT|VK_IMAGE_ASPECT_DEPTH_BIT); + CORRADE_COMPARE(copy->dstSubresource.mipLevel, 4); + CORRADE_COMPARE(copy->dstSubresource.baseArrayLayer, 6); + CORRADE_COMPARE(copy->dstSubresource.layerCount, 8); + CORRADE_COMPARE(Vector3i{copy->dstOffset}, (Vector3i{10, 12, 14})); + CORRADE_COMPARE(Vector3i{copy->extent}, (Vector3i{1, 2, 15})); +} + +void ImageTest::imageCopyConstructNoInit() { + ImageCopy copy{NoInit}; + copy->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + new(©) ImageCopy{NoInit}; + CORRADE_COMPARE(copy->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)); +} + +template void ImageTest::imageCopyConstructFromVk() { + setTestCaseTemplateName({Traits::name(), Traits::name()}); + + From from{}; + from.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + from.srcSubresource.mipLevel = 3; + from.srcSubresource.baseArrayLayer = 5; + from.srcSubresource.layerCount = 7; + from.srcOffset = {9, 11, 13}; + /* Deliberately using a different src/dst aspect to verify it's not + conflated */ + from.dstSubresource.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + from.dstSubresource.mipLevel = 4; + from.dstSubresource.baseArrayLayer = 6; + from.dstSubresource.layerCount = 8; + from.dstOffset = {10, 12, 14}; + from.extent = {1, 2, 15}; + + ImageCopy copy{from}; + To to = Traits::convert(copy); + CORRADE_COMPARE(to.srcSubresource.aspectMask, VK_IMAGE_ASPECT_COLOR_BIT); + CORRADE_COMPARE(to.srcSubresource.mipLevel, 3); + CORRADE_COMPARE(to.srcSubresource.baseArrayLayer, 5); + CORRADE_COMPARE(to.srcSubresource.layerCount, 7); + CORRADE_COMPARE(Vector3i{to.srcOffset}, (Vector3i{9, 11, 13})); + CORRADE_COMPARE(to.dstSubresource.aspectMask, VK_IMAGE_ASPECT_DEPTH_BIT); + CORRADE_COMPARE(to.dstSubresource.mipLevel, 4); + CORRADE_COMPARE(to.dstSubresource.baseArrayLayer, 6); + CORRADE_COMPARE(to.dstSubresource.layerCount, 8); + CORRADE_COMPARE(Vector3i{to.dstOffset}, (Vector3i{10, 12, 14})); + CORRADE_COMPARE(Vector3i{to.extent}, (Vector3i{1, 2, 15})); +} + +template void ImageTest::imageCopyConvertToVk() { + ImageCopy copy{ImageAspect::Color|ImageAspect::Depth, 3, 5, 7, {9, 11, 13}, 4, 6, 8, {10, 12, 14}, {1, 2, 15}}; + + T out = Traits::convert(copy); + CORRADE_COMPARE(out.srcSubresource.aspectMask, VK_IMAGE_ASPECT_COLOR_BIT|VK_IMAGE_ASPECT_DEPTH_BIT); + CORRADE_COMPARE(out.srcSubresource.mipLevel, 3); + CORRADE_COMPARE(out.srcSubresource.baseArrayLayer, 5); + CORRADE_COMPARE(out.srcSubresource.layerCount, 7); + CORRADE_COMPARE(Vector3i{out.srcOffset}, (Vector3i{9, 11, 13})); + CORRADE_COMPARE(out.dstSubresource.aspectMask, VK_IMAGE_ASPECT_COLOR_BIT|VK_IMAGE_ASPECT_DEPTH_BIT); + CORRADE_COMPARE(out.dstSubresource.mipLevel, 4); + CORRADE_COMPARE(out.dstSubresource.baseArrayLayer, 6); + CORRADE_COMPARE(out.dstSubresource.layerCount, 8); + CORRADE_COMPARE(Vector3i{out.dstOffset}, (Vector3i{10, 12, 14})); + CORRADE_COMPARE(Vector3i{out.extent}, (Vector3i{1, 2, 15})); +} + +void ImageTest::imageCopyConvertDisallowed() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + ImageCopy copy{ImageAspect{}, 0, 0, 0, {}, 0, 0, 0, {}, {}}; + copy->pNext = © + + std::ostringstream out; + Error redirectError{&out}; + copy.vkImageCopy(); + CORRADE_COMPARE(out.str(), "Vk::ImageCopy: disallowing conversion to VkImageCopy with non-empty pNext to prevent information loss\n"); +} + +void ImageTest::copyImageInfoConstruct() { + auto a = reinterpret_cast(0xdead); + auto b = reinterpret_cast(0xcafe); + + CopyImageInfo info{a, ImageLayout::Preinitialized, b, ImageLayout::General, { + {ImageAspect::Color, 3, 0, 0, {}, 0, 0, 0, {}, {}}, + {ImageAspect::Depth, 0, 5, 0, {}, 0, 0, 0, {}, {}} + }}; + CORRADE_COMPARE(info->srcImage, a); + CORRADE_COMPARE(info->srcImageLayout, VK_IMAGE_LAYOUT_PREINITIALIZED); + CORRADE_COMPARE(info->dstImage, b); + CORRADE_COMPARE(info->dstImageLayout, VK_IMAGE_LAYOUT_GENERAL); + CORRADE_COMPARE(info->regionCount, 2); + CORRADE_VERIFY(info->pRegions); + CORRADE_COMPARE(info->pRegions[0].srcSubresource.aspectMask, VK_IMAGE_ASPECT_COLOR_BIT); + CORRADE_COMPARE(info->pRegions[0].srcSubresource.mipLevel, 3); + CORRADE_COMPARE(info->pRegions[1].dstSubresource.aspectMask, VK_IMAGE_ASPECT_DEPTH_BIT); + CORRADE_COMPARE(info->pRegions[1].srcSubresource.baseArrayLayer, 5); +} + +void ImageTest::copyImageInfoConstructNoInit() { + CopyImageInfo info{NoInit}; + info->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + new(&info) CopyImageInfo{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::copyImageInfoConstructFromVk() { + VkCopyImageInfo2KHR vkInfo; + vkInfo.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + + CopyImageInfo info{vkInfo}; + CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); +} + +void ImageTest::copyImageInfoConvertToVk() { + CopyImageInfo info{VkImage{}, ImageLayout{}, VkImage{}, ImageLayout{}, { + {ImageAspect::Color, 3, 0, 0, {}, 0, 0, 0, {}, {}}, + {ImageAspect::Depth, 0, 5, 0, {}, 0, 0, 0, {}, {}} + }}; + + Containers::Array copies = info.vkImageCopies(); + CORRADE_COMPARE(copies.size(), 2); + CORRADE_COMPARE(copies[0].srcSubresource.aspectMask, VK_IMAGE_ASPECT_COLOR_BIT); + CORRADE_COMPARE(copies[0].srcSubresource.mipLevel, 3); + CORRADE_COMPARE(copies[1].dstSubresource.aspectMask, VK_IMAGE_ASPECT_DEPTH_BIT); + CORRADE_COMPARE(copies[1].srcSubresource.baseArrayLayer, 5); +} + +void ImageTest::bufferImageCopyConstruct() { + /* It's min/max, not offset + size in the default Range constructor */ + BufferImageCopy copy{3, 5, 7, ImageAspect::Stencil, 9, 11, 13, {{2, 4, 6}, {10, 14, 18}}}; + CORRADE_COMPARE(copy->bufferOffset, 3); + CORRADE_COMPARE(copy->bufferRowLength, 5); + CORRADE_COMPARE(copy->bufferImageHeight, 7); + CORRADE_COMPARE(copy->imageSubresource.aspectMask, VK_IMAGE_ASPECT_STENCIL_BIT); + CORRADE_COMPARE(copy->imageSubresource.mipLevel, 9); + CORRADE_COMPARE(copy->imageSubresource.baseArrayLayer, 11); + CORRADE_COMPARE(copy->imageSubresource.layerCount, 13); + CORRADE_COMPARE(Vector3i{copy->imageOffset}, (Vector3i{2, 4, 6})); + CORRADE_COMPARE(Vector3i{copy->imageExtent}, (Vector3i{8, 10, 12})); +} + +void ImageTest::bufferImageCopyConstruct1D() { + /* It's min/max, not offset + size in the default Range constructor */ + BufferImageCopy1D copy{3, ImageAspect::Stencil, 9, {2, 10}}; + CORRADE_COMPARE(copy->bufferOffset, 3); + CORRADE_COMPARE(copy->bufferRowLength, 0); + CORRADE_COMPARE(copy->bufferImageHeight, 0); + CORRADE_COMPARE(copy->imageSubresource.aspectMask, VK_IMAGE_ASPECT_STENCIL_BIT); + CORRADE_COMPARE(copy->imageSubresource.mipLevel, 9); + CORRADE_COMPARE(copy->imageSubresource.baseArrayLayer, 0); + CORRADE_COMPARE(copy->imageSubresource.layerCount, 1); + CORRADE_COMPARE(Vector3i{copy->imageOffset}, (Vector3i{2, 0, 0})); + CORRADE_COMPARE(Vector3i{copy->imageExtent}, (Vector3i{8, 1, 1})); +} + +void ImageTest::bufferImageCopyConstruct2D() { + /* It's min/max, not offset + size in the default Range constructor */ + BufferImageCopy2D copy{3, 5, ImageAspect::Stencil, 9, {{2, 4}, {10, 14}}}; + CORRADE_COMPARE(copy->bufferOffset, 3); + CORRADE_COMPARE(copy->bufferRowLength, 5); + CORRADE_COMPARE(copy->bufferImageHeight, 0); + CORRADE_COMPARE(copy->imageSubresource.aspectMask, VK_IMAGE_ASPECT_STENCIL_BIT); + CORRADE_COMPARE(copy->imageSubresource.mipLevel, 9); + CORRADE_COMPARE(copy->imageSubresource.baseArrayLayer, 0); + CORRADE_COMPARE(copy->imageSubresource.layerCount, 1); + CORRADE_COMPARE(Vector3i{copy->imageOffset}, (Vector3i{2, 4, 0})); + CORRADE_COMPARE(Vector3i{copy->imageExtent}, (Vector3i{8, 10, 1})); +} + +void ImageTest::bufferImageCopyConstruct3D() { + /* It's min/max, not offset + size in the default Range constructor */ + BufferImageCopy3D copy{3, 5, 7, ImageAspect::Stencil, 9, {{2, 4, 6}, {10, 14, 18}}}; + CORRADE_COMPARE(copy->bufferOffset, 3); + CORRADE_COMPARE(copy->bufferRowLength, 5); + CORRADE_COMPARE(copy->bufferImageHeight, 7); + CORRADE_COMPARE(copy->imageSubresource.aspectMask, VK_IMAGE_ASPECT_STENCIL_BIT); + CORRADE_COMPARE(copy->imageSubresource.mipLevel, 9); + CORRADE_COMPARE(copy->imageSubresource.baseArrayLayer, 0); + CORRADE_COMPARE(copy->imageSubresource.layerCount, 1); + CORRADE_COMPARE(Vector3i{copy->imageOffset}, (Vector3i{2, 4, 6})); + CORRADE_COMPARE(Vector3i{copy->imageExtent}, (Vector3i{8, 10, 12})); +} + +void ImageTest::bufferImageCopyConstruct1DArray() { + /* It's min/max, not offset + size in the default Range constructor */ + BufferImageCopy1DArray copy{3, 5, ImageAspect::Stencil, 9, {{2, 11}, {10, 24}}}; + CORRADE_COMPARE(copy->bufferOffset, 3); + CORRADE_COMPARE(copy->bufferRowLength, 5); + CORRADE_COMPARE(copy->bufferImageHeight, 0); + CORRADE_COMPARE(copy->imageSubresource.aspectMask, VK_IMAGE_ASPECT_STENCIL_BIT); + CORRADE_COMPARE(copy->imageSubresource.mipLevel, 9); + CORRADE_COMPARE(copy->imageSubresource.baseArrayLayer, 11); + CORRADE_COMPARE(copy->imageSubresource.layerCount, 13); + CORRADE_COMPARE(Vector3i{copy->imageOffset}, (Vector3i{2, 0, 0})); + CORRADE_COMPARE(Vector3i{copy->imageExtent}, (Vector3i{8, 1, 1})); +} + +void ImageTest::bufferImageCopyConstruct2DArray() { + /* It's min/max, not offset + size in the default Range constructor */ + BufferImageCopy2DArray copy{3, 5, 7, ImageAspect::Stencil, 9, {{2, 4, 11}, {10, 14, 24}}}; + CORRADE_COMPARE(copy->bufferOffset, 3); + CORRADE_COMPARE(copy->bufferRowLength, 5); + CORRADE_COMPARE(copy->bufferImageHeight, 7); + CORRADE_COMPARE(copy->imageSubresource.aspectMask, VK_IMAGE_ASPECT_STENCIL_BIT); + CORRADE_COMPARE(copy->imageSubresource.mipLevel, 9); + CORRADE_COMPARE(copy->imageSubresource.baseArrayLayer, 11); + CORRADE_COMPARE(copy->imageSubresource.layerCount, 13); + CORRADE_COMPARE(Vector3i{copy->imageOffset}, (Vector3i{2, 4, 0})); + CORRADE_COMPARE(Vector3i{copy->imageExtent}, (Vector3i{8, 10, 1})); +} + +void ImageTest::bufferImageCopyConstructCubeMap() { + /* It's min/max, not offset + size in the default Range constructor */ + BufferImageCopyCubeMap copy{3, 5, 7, ImageAspect::Stencil, 9, {{2, 4}, {10, 14}}}; + CORRADE_COMPARE(copy->bufferOffset, 3); + CORRADE_COMPARE(copy->bufferRowLength, 5); + CORRADE_COMPARE(copy->bufferImageHeight, 7); + CORRADE_COMPARE(copy->imageSubresource.aspectMask, VK_IMAGE_ASPECT_STENCIL_BIT); + CORRADE_COMPARE(copy->imageSubresource.mipLevel, 9); + CORRADE_COMPARE(copy->imageSubresource.baseArrayLayer, 0); + CORRADE_COMPARE(copy->imageSubresource.layerCount, 6); + CORRADE_COMPARE(Vector3i{copy->imageOffset}, (Vector3i{2, 4, 0})); + CORRADE_COMPARE(Vector3i{copy->imageExtent}, (Vector3i{8, 10, 1})); +} + +void ImageTest::bufferImageCopyConstructCubeMapArray() { + /* It's min/max, not offset + size in the default Range constructor */ + BufferImageCopyCubeMapArray copy{3, 5, 7, ImageAspect::Stencil, 9, {{2, 4, 11}, {10, 14, 24}}}; + CORRADE_COMPARE(copy->bufferOffset, 3); + CORRADE_COMPARE(copy->bufferRowLength, 5); + CORRADE_COMPARE(copy->bufferImageHeight, 7); + CORRADE_COMPARE(copy->imageSubresource.aspectMask, VK_IMAGE_ASPECT_STENCIL_BIT); + CORRADE_COMPARE(copy->imageSubresource.mipLevel, 9); + CORRADE_COMPARE(copy->imageSubresource.baseArrayLayer, 11); + CORRADE_COMPARE(copy->imageSubresource.layerCount, 13); + CORRADE_COMPARE(Vector3i{copy->imageOffset}, (Vector3i{2, 4, 0})); + CORRADE_COMPARE(Vector3i{copy->imageExtent}, (Vector3i{8, 10, 1})); +} + +void ImageTest::bufferImageCopyConstructNoInit() { + BufferImageCopy copy{NoInit}; + copy->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + new(©) BufferImageCopy{NoInit}; + CORRADE_COMPARE(copy->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)); +} + +template void ImageTest::bufferImageCopyConstructFromVk() { + setTestCaseTemplateName({Traits::name(), Traits::name()}); + + From from{}; + from.bufferOffset = 3; + from.bufferRowLength = 5; + from.bufferImageHeight = 7; + from.imageSubresource.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; + from.imageSubresource.mipLevel = 9; + from.imageSubresource.baseArrayLayer = 11; + from.imageSubresource.layerCount = 13; + from.imageOffset = {2, 4, 6}; + from.imageExtent = {8, 10, 12}; + + BufferImageCopy copy{from}; + To to = Traits::convert(copy); + CORRADE_COMPARE(to.bufferOffset, 3); + CORRADE_COMPARE(to.bufferRowLength, 5); + CORRADE_COMPARE(to.bufferImageHeight, 7); + CORRADE_COMPARE(to.imageSubresource.aspectMask, VK_IMAGE_ASPECT_STENCIL_BIT); + CORRADE_COMPARE(to.imageSubresource.mipLevel, 9); + CORRADE_COMPARE(to.imageSubresource.baseArrayLayer, 11); + CORRADE_COMPARE(to.imageSubresource.layerCount, 13); + CORRADE_COMPARE(Vector3i{to.imageOffset}, (Vector3i{2, 4, 6})); + CORRADE_COMPARE(Vector3i{to.imageExtent}, (Vector3i{8, 10, 12})); +} + +template void ImageTest::bufferImageCopyConvertToVk() { + /* It's min/max, not offset + size in the default Range constructor */ + BufferImageCopy copy{3, 5, 7, ImageAspect::Stencil, 9, 11, 13, {{2, 4, 6}, {10, 14, 18}}}; + + T out = Traits::convert(copy); + CORRADE_COMPARE(out.bufferOffset, 3); + CORRADE_COMPARE(out.bufferRowLength, 5); + CORRADE_COMPARE(out.bufferImageHeight, 7); + CORRADE_COMPARE(out.imageSubresource.aspectMask, VK_IMAGE_ASPECT_STENCIL_BIT); + CORRADE_COMPARE(out.imageSubresource.mipLevel, 9); + CORRADE_COMPARE(out.imageSubresource.baseArrayLayer, 11); + CORRADE_COMPARE(out.imageSubresource.layerCount, 13); + CORRADE_COMPARE(Vector3i{out.imageOffset}, (Vector3i{2, 4, 6})); + CORRADE_COMPARE(Vector3i{out.imageExtent}, (Vector3i{8, 10, 12})); +} + +void ImageTest::bufferImageCopyConvertDisallowed() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + BufferImageCopy copy{0, 0, 0, ImageAspect{}, 0, 0, 0, {}}; + copy->pNext = © + + std::ostringstream out; + Error redirectError{&out}; + copy.vkBufferImageCopy(); + CORRADE_COMPARE(out.str(), "Vk::BufferImageCopy: disallowing conversion to VkBufferImageCopy with non-empty pNext to prevent information loss\n"); +} + +void ImageTest::copyBufferToImageInfoConstruct() { + auto a = reinterpret_cast(0xdead); + auto b = reinterpret_cast(0xcafe); + + CopyBufferToImageInfo info{a, b, ImageLayout::TransferDestination, { + BufferImageCopy1D{5, ImageAspect::Color, 0, {}}, + BufferImageCopy1D{0, ImageAspect::Stencil, 3, {}} + }}; + CORRADE_COMPARE(info->srcBuffer, a); + CORRADE_COMPARE(info->dstImage, b); + CORRADE_COMPARE(info->dstImageLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + CORRADE_COMPARE(info->regionCount, 2); + CORRADE_VERIFY(info->pRegions); + CORRADE_COMPARE(info->pRegions[0].bufferOffset, 5); + CORRADE_COMPARE(info->pRegions[0].imageSubresource.aspectMask, VK_IMAGE_ASPECT_COLOR_BIT); + CORRADE_COMPARE(info->pRegions[1].imageSubresource.aspectMask, VK_IMAGE_ASPECT_STENCIL_BIT); + CORRADE_COMPARE(info->pRegions[1].imageSubresource.mipLevel, 3); +} + +void ImageTest::copyBufferToImageInfoConstructNoInit() { + CopyBufferToImageInfo info{NoInit}; + info->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + new(&info) CopyBufferToImageInfo{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::copyBufferToImageInfoConstructFromVk() { + VkCopyBufferToImageInfo2KHR vkInfo; + vkInfo.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + + CopyBufferToImageInfo info{vkInfo}; + CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); +} + +void ImageTest::copyBufferToImageInfoConvertToVk() { + CopyBufferToImageInfo info{VkBuffer{}, VkImage{}, ImageLayout{}, { + BufferImageCopy1D{5, ImageAspect::Color, 0, {}}, + BufferImageCopy1D{0, ImageAspect::Stencil, 3, {}}, + }}; + + Containers::Array copies = info.vkBufferImageCopies(); + CORRADE_COMPARE(copies.size(), 2); + CORRADE_COMPARE(copies[0].bufferOffset, 5); + CORRADE_COMPARE(copies[0].imageSubresource.aspectMask, VK_IMAGE_ASPECT_COLOR_BIT); + CORRADE_COMPARE(copies[1].imageSubresource.aspectMask, VK_IMAGE_ASPECT_STENCIL_BIT); + CORRADE_COMPARE(copies[1].imageSubresource.mipLevel, 3); +} + +void ImageTest::copyImageToBufferInfoConstruct() { + auto a = reinterpret_cast(0xcafe); + auto b = reinterpret_cast(0xdead); + + CopyImageToBufferInfo info{a, ImageLayout::TransferSource, b, { + BufferImageCopy1D{5, ImageAspect::Color, 0, {}}, + BufferImageCopy1D{0, ImageAspect::Stencil, 3, {}} + }}; + CORRADE_COMPARE(info->srcImage, a); + CORRADE_COMPARE(info->srcImageLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + CORRADE_COMPARE(info->dstBuffer, b); + CORRADE_COMPARE(info->regionCount, 2); + CORRADE_VERIFY(info->pRegions); + CORRADE_COMPARE(info->pRegions[0].bufferOffset, 5); + CORRADE_COMPARE(info->pRegions[0].imageSubresource.aspectMask, VK_IMAGE_ASPECT_COLOR_BIT); + CORRADE_COMPARE(info->pRegions[1].imageSubresource.aspectMask, VK_IMAGE_ASPECT_STENCIL_BIT); + CORRADE_COMPARE(info->pRegions[1].imageSubresource.mipLevel, 3); +} + +void ImageTest::copyImageToBufferInfoConstructNoInit() { + CopyImageToBufferInfo info{NoInit}; + info->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + new(&info) CopyImageToBufferInfo{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::copyImageToBufferInfoConstructFromVk() { + VkCopyImageToBufferInfo2KHR vkInfo; + vkInfo.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + + CopyImageToBufferInfo info{vkInfo}; + CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); +} + +void ImageTest::copyImageToBufferInfoConvertToVk() { + CopyImageToBufferInfo info{VkImage{}, ImageLayout{}, VkBuffer{}, { + BufferImageCopy1D{5, ImageAspect::Color, 0, {}}, + BufferImageCopy1D{0, ImageAspect::Stencil, 3, {}}, + }}; + + Containers::Array copies = info.vkBufferImageCopies(); + CORRADE_COMPARE(copies.size(), 2); + CORRADE_COMPARE(copies[0].bufferOffset, 5); + CORRADE_COMPARE(copies[0].imageSubresource.aspectMask, VK_IMAGE_ASPECT_COLOR_BIT); + CORRADE_COMPARE(copies[1].imageSubresource.aspectMask, VK_IMAGE_ASPECT_STENCIL_BIT); + CORRADE_COMPARE(copies[1].imageSubresource.mipLevel, 3); +} + void ImageTest::debugAspect() { std::ostringstream out; Debug{&out} << ImageAspect::Depth << ImageAspect(0xdeadcafe); diff --git a/src/Magnum/Vk/Test/ImageVkTest.cpp b/src/Magnum/Vk/Test/ImageVkTest.cpp index a8fb449f9..2f2476721 100644 --- a/src/Magnum/Vk/Test/ImageVkTest.cpp +++ b/src/Magnum/Vk/Test/ImageVkTest.cpp @@ -23,13 +23,26 @@ DEALINGS IN THE SOFTWARE. */ +#include +#include #include +#include #include +#include +#include +#include "Magnum/Math/Color.h" +#include "Magnum/Math/Range.h" +#include "Magnum/Vk/BufferCreateInfo.h" +#include "Magnum/Vk/CommandPoolCreateInfo.h" +#include "Magnum/Vk/CommandBuffer.h" #include "Magnum/Vk/DeviceProperties.h" +#include "Magnum/Vk/Extensions.h" +#include "Magnum/Vk/Fence.h" #include "Magnum/Vk/Handle.h" #include "Magnum/Vk/ImageCreateInfo.h" #include "Magnum/Vk/MemoryAllocateInfo.h" +#include "Magnum/Vk/Pipeline.h" #include "Magnum/Vk/Result.h" #include "Magnum/Vk/VulkanTester.h" @@ -57,6 +70,25 @@ struct ImageVkTest: VulkanTester { void bindDedicatedMemory(); void directAllocation(); + + void cmdClearColorImageFloat(); + void cmdClearColorImageSignedIntegral(); + void cmdClearColorImageUnsignedIntegral(); + void cmdClearDepthStencilImage(); + void cmdClearDepthImage(); + void cmdClearStencilImage(); + + void cmdCopyImage2D(); + void cmdCopyImageDisallowedConversion(); + + void cmdCopyBufferImage1D(); + void cmdCopyBufferImage2D(); + void cmdCopyBufferImage3D(); + void cmdCopyBufferImage1DArray(); + void cmdCopyBufferImage2DArray(); + void cmdCopyBufferImageCubeMap(); + void cmdCopyBufferImageCubeMapArray(); + void cmdCopyBufferImageDisallowedConversion(); }; ImageVkTest::ImageVkTest() { @@ -78,9 +110,31 @@ ImageVkTest::ImageVkTest() { &ImageVkTest::bindMemory, &ImageVkTest::bindDedicatedMemory, - &ImageVkTest::directAllocation}); + &ImageVkTest::directAllocation, + + &ImageVkTest::cmdClearColorImageFloat, + &ImageVkTest::cmdClearColorImageSignedIntegral, + &ImageVkTest::cmdClearColorImageUnsignedIntegral, + &ImageVkTest::cmdClearDepthStencilImage, + &ImageVkTest::cmdClearDepthImage, + &ImageVkTest::cmdClearStencilImage, + + &ImageVkTest::cmdCopyImage2D, + &ImageVkTest::cmdCopyImageDisallowedConversion, + + &ImageVkTest::cmdCopyBufferImage1D, + &ImageVkTest::cmdCopyBufferImage2D, + &ImageVkTest::cmdCopyBufferImage3D, + &ImageVkTest::cmdCopyBufferImage1DArray, + &ImageVkTest::cmdCopyBufferImage2DArray, + &ImageVkTest::cmdCopyBufferImageCubeMap, + &ImageVkTest::cmdCopyBufferImageCubeMapArray, + &ImageVkTest::cmdCopyBufferImageDisallowedConversion}); } +using namespace Containers::Literals; +using namespace Math::Literals; + void ImageVkTest::construct1D() { { Image image{device(), ImageCreateInfo1D{ImageUsage::Sampled, @@ -289,6 +343,905 @@ void ImageVkTest::directAllocation() { CORRADE_VERIFY(image.dedicatedMemory().handle()); } +void ImageVkTest::cmdClearColorImageFloat() { + CommandPool pool{device(), CommandPoolCreateInfo{ + device().properties().pickQueueFamily(QueueFlag::Graphics)}}; + CommandBuffer cmd = pool.allocate(); + + /* To avoid going through a buffer which can guarantee the packing we want, + the tests uses a linear tiling image. These are poorly supported, have + weird paddings and the required allocation size is usually much larger + than expected. To prevent issues as much as possible, we'll thus create + images with non-insane sizes, 4-byte-aligned pixel format and explicitly + slice the mapped memory. */ + + /* Source image */ + ImageCreateInfo2D aInfo{ImageUsage::TransferDestination, + PixelFormat::RGBA8Unorm, {4, 4}, 1, 1, ImageLayout::Undefined}; + aInfo->tiling = VK_IMAGE_TILING_LINEAR; + Image a{device(), aInfo, MemoryFlag::HostVisible}; + + cmd.begin() + .pipelineBarrier(PipelineStage::TopOfPipe, PipelineStage::Transfer, { + {{}, Access::TransferWrite, + ImageLayout::Undefined, ImageLayout::TransferDestination, a}, + }) + .clearColorImage(a, ImageLayout::TransferDestination, 0xdeadc0de_rgbaf) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Host, { + {Access::TransferWrite, Access::HostRead} + }) + .end(); + queue().submit({SubmitInfo{}.setCommandBuffers({cmd})}).wait(); + + CORRADE_COMPARE_AS(Containers::arrayCast(a.dedicatedMemory().mapRead().prefix(4*4*4)), Containers::arrayView({ + 0xdeadc0de_rgba, 0xdeadc0de_rgba, 0xdeadc0de_rgba, 0xdeadc0de_rgba, + 0xdeadc0de_rgba, 0xdeadc0de_rgba, 0xdeadc0de_rgba, 0xdeadc0de_rgba, + 0xdeadc0de_rgba, 0xdeadc0de_rgba, 0xdeadc0de_rgba, 0xdeadc0de_rgba, + 0xdeadc0de_rgba, 0xdeadc0de_rgba, 0xdeadc0de_rgba, 0xdeadc0de_rgba + }), TestSuite::Compare::Container); +} + +void ImageVkTest::cmdClearColorImageSignedIntegral() { + CommandPool pool{device(), CommandPoolCreateInfo{ + device().properties().pickQueueFamily(QueueFlag::Graphics)}}; + CommandBuffer cmd = pool.allocate(); + + /* To avoid going through a buffer which can guarantee the packing we want, + the tests uses a linear tiling image. These are poorly supported, have + weird paddings and the required allocation size is usually much larger + than expected. To prevent issues as much as possible, we'll thus create + images with non-insane sizes, 4-byte pixel format and explicitly slice + the mapped memory. */ + + /* Source image */ + ImageCreateInfo2D aInfo{ImageUsage::TransferDestination, + PixelFormat::RGBA8I, {4, 4}, 1, 1, ImageLayout::Undefined}; + aInfo->tiling = VK_IMAGE_TILING_LINEAR; + Image a{device(), aInfo, MemoryFlag::HostVisible}; + + cmd.begin() + .pipelineBarrier(PipelineStage::TopOfPipe, PipelineStage::Transfer, { + {{}, Access::TransferWrite, + ImageLayout::Undefined, ImageLayout::TransferDestination, a}, + }) + .clearColorImage(a, ImageLayout::TransferDestination, Vector4i{15, -7, 2, -1}) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Host, { + {Access::TransferWrite, Access::HostRead} + }) + .end(); + queue().submit({SubmitInfo{}.setCommandBuffers({cmd})}).wait(); + + CORRADE_COMPARE_AS(Containers::arrayCast(a.dedicatedMemory().mapRead().prefix(4*4*4)), Containers::arrayView({ + {15, -7, 2, -1}, {15, -7, 2, -1}, {15, -7, 2, -1}, {15, -7, 2, -1}, + {15, -7, 2, -1}, {15, -7, 2, -1}, {15, -7, 2, -1}, {15, -7, 2, -1}, + {15, -7, 2, -1}, {15, -7, 2, -1}, {15, -7, 2, -1}, {15, -7, 2, -1}, + {15, -7, 2, -1}, {15, -7, 2, -1}, {15, -7, 2, -1}, {15, -7, 2, -1}, + }), TestSuite::Compare::Container); +} + +void ImageVkTest::cmdClearColorImageUnsignedIntegral() { + CommandPool pool{device(), CommandPoolCreateInfo{ + device().properties().pickQueueFamily(QueueFlag::Graphics)}}; + CommandBuffer cmd = pool.allocate(); + + /* To avoid going through a buffer which can guarantee the packing we want, + the tests uses a linear tiling image. These are poorly supported, have + weird paddings and the required allocation size is usually much larger + than expected. To prevent issues as much as possible, we'll thus create + images with non-insane sizes, 4-byte pixel format and explicitly slice + the mapped memory. */ + + /* Source image */ + ImageCreateInfo2D aInfo{ImageUsage::TransferDestination, + PixelFormat::RGBA8UI, {4, 4}, 1, 1, ImageLayout::Undefined}; + aInfo->tiling = VK_IMAGE_TILING_LINEAR; + Image a{device(), aInfo, MemoryFlag::HostVisible}; + + cmd.begin() + .pipelineBarrier(PipelineStage::TopOfPipe, PipelineStage::Transfer, { + {{}, Access::TransferWrite, + ImageLayout::Undefined, ImageLayout::TransferDestination, a}, + }) + .clearColorImage(a, ImageLayout::TransferDestination, Vector4ui{15, 37, 2, 1}) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Host, { + {Access::TransferWrite, Access::HostRead} + }) + .end(); + queue().submit({SubmitInfo{}.setCommandBuffers({cmd})}).wait(); + + CORRADE_COMPARE_AS(Containers::arrayCast(a.dedicatedMemory().mapRead().prefix(4*4*4)), Containers::arrayView({ + {15, 37, 2, 1}, {15, 37, 2, 1}, {15, 37, 2, 1}, {15, 37, 2, 1}, + {15, 37, 2, 1}, {15, 37, 2, 1}, {15, 37, 2, 1}, {15, 37, 2, 1}, + {15, 37, 2, 1}, {15, 37, 2, 1}, {15, 37, 2, 1}, {15, 37, 2, 1}, + {15, 37, 2, 1}, {15, 37, 2, 1}, {15, 37, 2, 1}, {15, 37, 2, 1}, + }), TestSuite::Compare::Container); +} + +void ImageVkTest::cmdClearDepthStencilImage() { + CommandPool pool{device(), CommandPoolCreateInfo{ + device().properties().pickQueueFamily(QueueFlag::Graphics)}}; + CommandBuffer cmd = pool.allocate(); + + /* Depth/stencil images aren't supported in a linear tiling, so do the + verification through a buffer copy */ + + /* Source image */ + Image a{device(), ImageCreateInfo2D{ + ImageUsage::TransferDestination|ImageUsage::TransferSource, + PixelFormat::Depth32FStencil8UI, {4, 4}, 1, 1, ImageLayout::Undefined + }, MemoryFlag::DeviceLocal}; + + /* Destination buffers */ + Buffer depth{device(), BufferCreateInfo{ + BufferUsage::TransferDestination, 4*4*4 + }, MemoryFlag::HostVisible}; + Buffer stencil{device(), BufferCreateInfo{ + BufferUsage::TransferDestination, 4*4 + }, MemoryFlag::HostVisible}; + + cmd.begin() + .pipelineBarrier(PipelineStage::TopOfPipe, PipelineStage::Transfer, { + {{}, Access::TransferWrite, + ImageLayout::Undefined, ImageLayout::TransferDestination, a}, + }) + .clearDepthStencilImage(a, ImageLayout::TransferDestination, 0.75f, 133) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Transfer, { + {Access::TransferWrite, Access::TransferRead, + ImageLayout::TransferDestination, ImageLayout::TransferSource, a}, + }) + .copyImageToBuffer(CopyImageToBufferInfo2D{a, ImageLayout::TransferSource, depth, { + {0, ImageAspect::Depth, 0, {{}, {4, 4}}} + }}) + .copyImageToBuffer(CopyImageToBufferInfo2D{a, ImageLayout::TransferSource, stencil, { + {0, ImageAspect::Stencil, 0, {{}, {4, 4}}} + }}) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Host, { + {Access::TransferWrite, Access::HostRead} + }) + .end(); + queue().submit({SubmitInfo{}.setCommandBuffers({cmd})}).wait(); + + CORRADE_COMPARE_AS((Containers::arrayCast(depth.dedicatedMemory().mapRead().prefix(4*4*4))), (Containers::arrayView({ + 0.75f, 0.75f, 0.75f, 0.75f, + 0.75f, 0.75f, 0.75f, 0.75f, + 0.75f, 0.75f, 0.75f, 0.75f, + 0.75f, 0.75f, 0.75f, 0.75f + })), TestSuite::Compare::Container); + + CORRADE_COMPARE_AS((Containers::arrayCast(stencil.dedicatedMemory().mapRead().prefix(4*4))), (Containers::arrayView({ + 133, 133, 133, 133, + 133, 133, 133, 133, + 133, 133, 133, 133, + 133, 133, 133, 133 + })), TestSuite::Compare::Container); +} + +void ImageVkTest::cmdClearDepthImage() { + CommandPool pool{device(), CommandPoolCreateInfo{ + device().properties().pickQueueFamily(QueueFlag::Graphics)}}; + CommandBuffer cmd = pool.allocate(); + + /* Depth/stencil images aren't supported in a linear tiling, so do the + verification through a buffer copy */ + + /* Source image */ + Image a{device(), ImageCreateInfo2D{ + ImageUsage::TransferDestination|ImageUsage::TransferSource, + PixelFormat::Depth32F, {4, 4}, 1, 1, ImageLayout::Undefined + }, MemoryFlag::DeviceLocal}; + + /* Destination buffers */ + Buffer depth{device(), BufferCreateInfo{ + BufferUsage::TransferDestination, 4*4*4 + }, MemoryFlag::HostVisible}; + + cmd.begin() + .pipelineBarrier(PipelineStage::TopOfPipe, PipelineStage::Transfer, { + {{}, Access::TransferWrite, + ImageLayout::Undefined, ImageLayout::TransferDestination, a}, + }) + .clearDepthImage(a, ImageLayout::TransferDestination, 0.75f) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Transfer, { + {Access::TransferWrite, Access::TransferRead, + ImageLayout::TransferDestination, ImageLayout::TransferSource, a}, + }) + .copyImageToBuffer(CopyImageToBufferInfo2D{a, ImageLayout::TransferSource, depth, { + {0, ImageAspect::Depth, 0, {{}, {4, 4}}} + }}) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Host, { + {Access::TransferWrite, Access::HostRead} + }) + .end(); + queue().submit({SubmitInfo{}.setCommandBuffers({cmd})}).wait(); + + CORRADE_COMPARE_AS((Containers::arrayCast(depth.dedicatedMemory().mapRead().prefix(4*4*4))), (Containers::arrayView({ + 0.75f, 0.75f, 0.75f, 0.75f, + 0.75f, 0.75f, 0.75f, 0.75f, + 0.75f, 0.75f, 0.75f, 0.75f, + 0.75f, 0.75f, 0.75f, 0.75f + })), TestSuite::Compare::Container); +} + +void ImageVkTest::cmdClearStencilImage() { + CommandPool pool{device(), CommandPoolCreateInfo{ + device().properties().pickQueueFamily(QueueFlag::Graphics)}}; + CommandBuffer cmd = pool.allocate(); + + /* Depth/stencil images aren't supported in a linear tiling, so do the + verification through a buffer copy */ + + /* Source image */ + Image a{device(), ImageCreateInfo2D{ + ImageUsage::TransferDestination|ImageUsage::TransferSource, + PixelFormat::Stencil8UI, {4, 4}, 1, 1, ImageLayout::Undefined + }, MemoryFlag::DeviceLocal}; + + /* Destination buffers */ + Buffer stencil{device(), BufferCreateInfo{ + BufferUsage::TransferDestination, 4*4 + }, MemoryFlag::HostVisible}; + + cmd.begin() + .pipelineBarrier(PipelineStage::TopOfPipe, PipelineStage::Transfer, { + {{}, Access::TransferWrite, + ImageLayout::Undefined, ImageLayout::TransferDestination, a}, + }) + .clearStencilImage(a, ImageLayout::TransferDestination, 133) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Transfer, { + {Access::TransferWrite, Access::TransferRead, + ImageLayout::TransferDestination, ImageLayout::TransferSource, a}, + }) + .copyImageToBuffer(CopyImageToBufferInfo2D{a, ImageLayout::TransferSource, stencil, { + {0, ImageAspect::Stencil, 0, {{}, {4, 4}}} + }}) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Host, { + {Access::TransferWrite, Access::HostRead} + }) + .end(); + queue().submit({SubmitInfo{}.setCommandBuffers({cmd})}).wait(); + + CORRADE_COMPARE_AS((Containers::arrayCast(stencil.dedicatedMemory().mapRead().prefix(4*4))), (Containers::arrayView({ + 133, 133, 133, 133, + 133, 133, 133, 133, + 133, 133, 133, 133, + 133, 133, 133, 133 + })), TestSuite::Compare::Container); +} + +void ImageVkTest::cmdCopyImage2D() { + CommandPool pool{device(), CommandPoolCreateInfo{ + device().properties().pickQueueFamily(QueueFlag::Graphics)}}; + CommandBuffer cmd = pool.allocate(); + + /* To avoid going through a buffer which can guarantee the packing we want, + the tests uses a linear tiling image. These are poorly supported, have + weird paddings and the required allocation size is usually much larger + than expected. To prevent issues as much as possible, we'll thus create + images with non-insane sizes (so not 6 or 7 pixels wide, but 8), 4-byte + pixel format and explicitly slice the mapped memory. */ + + /* Source image */ + ImageCreateInfo2D aInfo{ImageUsage::TransferSource, + PixelFormat::RGBA8UI, {8, 10}, 1, 1, ImageLayout::Preinitialized}; + aInfo->tiling = VK_IMAGE_TILING_LINEAR; + Image a{device(), aInfo, MemoryFlag::HostVisible}; + Utility::copy("________________________________" + "________________________________" + "________________________________" + "________________________________" + "____________AaaaAaaaAaaaAaaa____" + "____________BbbbBbbbBbbbBbbb____" + "____________CcccCcccCcccCccc____" + "____________DdddDdddDdddDddd____" + "________________________________" + "________________________________"_s, a.dedicatedMemory().map().prefix(8*10*4)); + + /* Destination image */ + ImageCreateInfo2D bInfo{ImageUsage::TransferDestination, + PixelFormat::RGBA8UI, {8, 5}, 1}; + bInfo->tiling = VK_IMAGE_TILING_LINEAR; + Image b{device(), bInfo, MemoryFlag::HostVisible}; + + cmd.begin() + .pipelineBarrier(PipelineStage::TopOfPipe, PipelineStage::Transfer, { + {Accesses{}, Access::TransferRead, + ImageLayout::Preinitialized, ImageLayout::TransferSource, a}, + {Accesses{}, Access::TransferWrite, + ImageLayout::Undefined, ImageLayout::TransferDestination, b}, + }) + .clearColorImage(b, ImageLayout::TransferDestination, Vector4ui{'-'}) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Transfer, { + {Access::TransferWrite, Access::TransferWrite, + ImageLayout::TransferDestination, ImageLayout::TransferDestination, b} + }) + .copyImage({a, ImageLayout::TransferSource, b, ImageLayout::TransferDestination, { + {ImageAspect::Color, 0, 0, 1, {3, 4, 0}, 0, 0, 1, {1, 1, 0}, {4, 4, 1}} + }}) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Host, { + {Access::TransferWrite, Access::HostRead, + ImageLayout::TransferDestination, ImageLayout::TransferDestination, b} + }) + .end(); + queue().submit({SubmitInfo{}.setCommandBuffers({cmd})}).wait(); + + CORRADE_COMPARE(b.dedicatedMemory().mapRead().prefix(8*5*4), + "--------------------------------" + "----AaaaAaaaAaaaAaaa------------" + "----BbbbBbbbBbbbBbbb------------" + "----CcccCcccCcccCccc------------" + "----DdddDdddDdddDddd------------"_s); +} + +void ImageVkTest::cmdCopyImageDisallowedConversion() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + if(device().isExtensionEnabled()) + CORRADE_SKIP("KHR_copy_commands2 enabled on the device, can't test"); + + CommandPool pool{device(), CommandPoolCreateInfo{ + device().properties().pickQueueFamily(QueueFlag::Graphics)}}; + CommandBuffer cmd = pool.allocate(); + + CopyImageInfo a{{}, {}, {}, {}, {}}; + a->pNext = &a; + + /* The commands shouldn't do anything, so it should be fine to just call + them without any render pass set up */ + std::ostringstream out; + Error redirectError{&out}; + cmd.copyImage(a); + CORRADE_COMPARE(out.str(), + "Vk::CommandBuffer::copyImage(): disallowing extraction of CopyImageInfo with non-empty pNext to prevent information loss\n"); +} + +void ImageVkTest::cmdCopyBufferImage1D() { + CommandPool pool{device(), CommandPoolCreateInfo{ + device().properties().pickQueueFamily(QueueFlag::Graphics)}}; + CommandBuffer cmd = pool.allocate(); + + /* Source buffer */ + Buffer a{device(), BufferCreateInfo{ + BufferUsage::TransferSource, 7*4 + }, MemoryFlag::HostVisible}; + Utility::copy("________AaaaBbbbCcccDddd____"_s, a.dedicatedMemory().map()); + + /* Destination & source image */ + Image b{device(), ImageCreateInfo1D{ + ImageUsage::TransferDestination|ImageUsage::TransferSource, + PixelFormat::RGBA8UI, 6, 1 + }, MemoryFlag::HostVisible}; + + /* Destination buffer, clear as well */ + Buffer c{device(), BufferCreateInfo{ + BufferUsage::TransferDestination, 7*4 + }, MemoryFlag::HostVisible}; + Utility::copy("............................"_s, c.dedicatedMemory().map()); + + cmd.begin() + .pipelineBarrier(PipelineStage::TopOfPipe, PipelineStage::Transfer, { + {Accesses{}, Access::TransferWrite, + ImageLayout::Undefined, ImageLayout::TransferDestination, b} + }) + .fillBuffer(c, 0x2e2e2e2e) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Transfer, { + {Access::TransferWrite, Access::TransferRead, c} + }) + .copyBufferToImage(CopyBufferToImageInfo1D{a, b, ImageLayout::TransferDestination, { + {2*4, ImageAspect::Color, 0, Range1Di::fromSize(2, 4)} + }}) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Transfer, { + {Access::TransferWrite, Access::TransferRead, + ImageLayout::TransferDestination, ImageLayout::TransferSource, b} + }) + .copyImageToBuffer(CopyImageToBufferInfo1D{b, ImageLayout::TransferSource, c, { + {2*4, ImageAspect::Color, 0, Range1Di::fromSize(2, 4)} + }}) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Host, { + {Access::TransferWrite, Access::HostRead, c} + }) + .end(); + queue().submit({SubmitInfo{}.setCommandBuffers({cmd})}).wait(); + + CORRADE_COMPARE(arrayView(c.dedicatedMemory().mapRead()), + "........AaaaBbbbCcccDddd...."_s); +} + +void ImageVkTest::cmdCopyBufferImage2D() { + CommandPool pool{device(), CommandPoolCreateInfo{ + device().properties().pickQueueFamily(QueueFlag::Graphics)}}; + CommandBuffer cmd = pool.allocate(); + + /* Source buffer */ + Buffer a{device(), BufferCreateInfo{ + BufferUsage::TransferSource, 7*10*4 + }, MemoryFlag::HostVisible}; + Utility::copy("____________________________" + "____________________________" + "____________________________" + "____________________________" + "________AaaaAaaaAaaaAaaa____" + "________BbbbBbbbBbbbBbbb____" + "________CcccCcccCcccCccc____" + "________DdddDdddDdddDddd____" + "____________________________" + "____________________________"_s, a.dedicatedMemory().map()); + + /* Destination & source image */ + Image b{device(), ImageCreateInfo2D{ + ImageUsage::TransferDestination|ImageUsage::TransferSource, + PixelFormat::RGBA8UI, {6, 5}, 1 + }, MemoryFlag::HostVisible}; + + /* Destination buffer */ + Buffer c{device(), BufferCreateInfo{ + BufferUsage::TransferDestination, 7*10*4 + }, MemoryFlag::HostVisible}; + + cmd.begin() + .pipelineBarrier(PipelineStage::TopOfPipe, PipelineStage::Transfer, { + {Accesses{}, Access::TransferWrite, + ImageLayout::Undefined, ImageLayout::TransferDestination, b} + }) + .fillBuffer(c, 0x2e2e2e2e) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Transfer, { + {Access::TransferWrite, Access::TransferRead, c} + }) + .copyBufferToImage(CopyBufferToImageInfo2D{a, b, ImageLayout::TransferDestination, { + {(4*7 + 2)*4, 7, ImageAspect::Color, 0, Range2Di::fromSize({2, 1}, {4, 4})} + }}) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Transfer, { + {Access::TransferWrite, Access::TransferRead, + ImageLayout::TransferDestination, ImageLayout::TransferSource, b} + }) + .copyImageToBuffer(CopyImageToBufferInfo2D{b, ImageLayout::TransferSource, c, { + {(4*7 + 2)*4, 7, ImageAspect::Color, 0, Range2Di::fromSize({2, 1}, {4, 4})} + }}) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Host, { + {Access::TransferWrite, Access::HostRead, c} + }) + .end(); + queue().submit({SubmitInfo{}.setCommandBuffers({cmd})}).wait(); + + CORRADE_COMPARE(arrayView(c.dedicatedMemory().mapRead()), + "............................" + "............................" + "............................" + "............................" + "........AaaaAaaaAaaaAaaa...." + "........BbbbBbbbBbbbBbbb...." + "........CcccCcccCcccCccc...." + "........DdddDdddDdddDddd...." + "............................" + "............................"_s); +} + +void ImageVkTest::cmdCopyBufferImage3D() { + CommandPool pool{device(), CommandPoolCreateInfo{ + device().properties().pickQueueFamily(QueueFlag::Graphics)}}; + CommandBuffer cmd = pool.allocate(); + + /* Source buffer */ + Buffer a{device(), BufferCreateInfo{ + BufferUsage::TransferSource, 6*7*2*4 + }, MemoryFlag::HostVisible}; + Utility::copy("________________________" + "________________________" + "________________________" + "________AaaaAaaaAaaa____" + "________BbbbBbbbBbbb____" + "________CcccCcccCccc____" + "________________________" + + "________________________" + "________________________" + "________________________" + "________DdddDdddDddd____" + "________EeeeEeeeEeee____" + "________FfffFfffFfff____" + "________________________"_s, a.dedicatedMemory().map()); + + /* Destination & source image */ + Image b{device(), ImageCreateInfo3D{ + ImageUsage::TransferDestination|ImageUsage::TransferSource, + PixelFormat::RGBA8UI, {5, 4, 3}, 1 + }, MemoryFlag::HostVisible}; + + /* Destination buffer */ + Buffer c{device(), BufferCreateInfo{ + BufferUsage::TransferDestination, 6*7*2*4 + }, MemoryFlag::HostVisible}; + + cmd.begin() + .pipelineBarrier(PipelineStage::TopOfPipe, PipelineStage::Transfer, { + {Accesses{}, Access::TransferWrite, + ImageLayout::Undefined, ImageLayout::TransferDestination, b} + }) + .fillBuffer(c, 0x2e2e2e2e) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Transfer, { + {Access::TransferWrite, Access::TransferRead, c} + }) + .copyBufferToImage(CopyBufferToImageInfo3D{a, b, ImageLayout::TransferDestination, { + {(3*6 + 2)*4, 6, 7, ImageAspect::Color, 0, Range3Di::fromSize({2, 1, 1}, {3, 3, 2})} + }}) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Transfer, { + {Access::TransferWrite, Access::TransferRead, + ImageLayout::TransferDestination, ImageLayout::TransferSource, b} + }) + .copyImageToBuffer(CopyImageToBufferInfo3D{b, ImageLayout::TransferSource, c, { + {(3*6 + 2)*4, 6, 7, ImageAspect::Color, 0, Range3Di::fromSize({2, 1, 1}, {3, 3, 2})} + }}) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Host, { + {Access::TransferWrite, Access::HostRead, c} + }) + .end(); + queue().submit({SubmitInfo{}.setCommandBuffers({cmd})}).wait(); + + CORRADE_COMPARE(arrayView(c.dedicatedMemory().mapRead()), + "........................" + "........................" + "........................" + "........AaaaAaaaAaaa...." + "........BbbbBbbbBbbb...." + "........CcccCcccCccc...." + "........................" + + "........................" + "........................" + "........................" + "........DdddDdddDddd...." + "........EeeeEeeeEeee...." + "........FfffFfffFfff...." + "........................"_s); +} + +void ImageVkTest::cmdCopyBufferImage1DArray() { + CommandPool pool{device(), CommandPoolCreateInfo{ + device().properties().pickQueueFamily(QueueFlag::Graphics)}}; + CommandBuffer cmd = pool.allocate(); + + /* Source buffer */ + Buffer a{device(), BufferCreateInfo{ + BufferUsage::TransferSource, 6*5*4 + }, MemoryFlag::HostVisible}; + Utility::copy("________________________" + "________________________" + "________AaaaAaaaAaaa____" + "________BbbbBbbbBbbb____" + "________________________"_s, a.dedicatedMemory().map()); + + /* Destination & source image */ + Image b{device(), ImageCreateInfo1DArray{ + ImageUsage::TransferDestination|ImageUsage::TransferSource, + PixelFormat::RGBA8UI, {4, 3}, 1 + }, MemoryFlag::HostVisible}; + + /* Destination buffer */ + Buffer c{device(), BufferCreateInfo{BufferUsage::TransferDestination, 6*5*4}, MemoryFlag::HostVisible}; + + cmd.begin() + .pipelineBarrier(PipelineStage::TopOfPipe, PipelineStage::Transfer, { + {Accesses{}, Access::TransferWrite, + ImageLayout::Undefined, ImageLayout::TransferDestination, b} + }) + .fillBuffer(c, 0x2e2e2e2e) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Transfer, { + {Access::TransferWrite, Access::TransferRead, c} + }) + .copyBufferToImage(CopyBufferToImageInfo1DArray{a, b, ImageLayout::TransferDestination, { + {(2*6 + 2)*4, 6, ImageAspect::Color, 0, Range2Di::fromSize({1, 1}, {3, 2})} + }}) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Transfer, { + {Access::TransferWrite, Access::TransferRead, + ImageLayout::TransferDestination, ImageLayout::TransferSource, b} + }) + .copyImageToBuffer(CopyImageToBufferInfo1DArray{b, ImageLayout::TransferSource, c, { + {(2*6 + 2)*4, 6, ImageAspect::Color, 0, Range2Di::fromSize({1, 1}, {3, 2})} + }}) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Host, { + {Access::TransferWrite, Access::HostRead, c} + }) + .end(); + queue().submit({SubmitInfo{}.setCommandBuffers({cmd})}).wait(); + + CORRADE_COMPARE(arrayView(c.dedicatedMemory().mapRead()), + "........................" + "........................" + "........AaaaAaaaAaaa...." + "........BbbbBbbbBbbb...." + "........................"_s); +} + +void ImageVkTest::cmdCopyBufferImage2DArray() { + CommandPool pool{device(), CommandPoolCreateInfo{ + device().properties().pickQueueFamily(QueueFlag::Graphics)}}; + CommandBuffer cmd = pool.allocate(); + + /* Source buffer */ + Buffer a{device(), BufferCreateInfo{ + BufferUsage::TransferSource, 4*5*2*4 + }, MemoryFlag::HostVisible}; + Utility::copy("____________________" + "________AaaaAaaa____" + "________BbbbBbbb____" + "____________________" + + "____________________" + "________CcccCccc____" + "________DdddDddd____" + "____________________"_s, a.dedicatedMemory().map()); + + /* Destination & source image */ + Image b{device(), ImageCreateInfo2DArray{ + ImageUsage::TransferDestination|ImageUsage::TransferSource, + PixelFormat::RGBA8UI, {4, 4, 3}, 1 + }, MemoryFlag::HostVisible}; + + /* Destination buffer */ + Buffer c{device(), BufferCreateInfo{BufferUsage::TransferDestination, 4*5*2*4}, MemoryFlag::HostVisible}; + + cmd.begin() + .pipelineBarrier(PipelineStage::TopOfPipe, PipelineStage::Transfer, { + {Accesses{}, Access::TransferWrite, + ImageLayout::Undefined, ImageLayout::TransferDestination, b} + }) + .fillBuffer(c, 0x2e2e2e2e) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Transfer, { + {Access::TransferWrite, Access::TransferRead, c} + }) + .copyBufferToImage(CopyBufferToImageInfo2DArray{a, b, ImageLayout::TransferDestination, { + {(1*5 + 2)*4, 5, 4, ImageAspect::Color, 0, Range3Di::fromSize({2, 1, 1}, {2, 2, 2})} + }}) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Transfer, { + {Access::TransferWrite, Access::TransferRead, + ImageLayout::TransferDestination, ImageLayout::TransferSource, b} + }) + .copyImageToBuffer(CopyImageToBufferInfo2DArray{b, ImageLayout::TransferSource, c, { + {(1*5 + 2)*4, 5, 4, ImageAspect::Color, 0, Range3Di::fromSize({2, 1, 1}, {2, 2, 2})} + }}) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Host, { + {Access::TransferWrite, Access::HostRead, c} + }) + .end(); + queue().submit({SubmitInfo{}.setCommandBuffers({cmd})}).wait(); + + CORRADE_COMPARE(arrayView(c.dedicatedMemory().mapRead()), + "...................." + "........AaaaAaaa...." + "........BbbbBbbb...." + "...................." + + "...................." + "........CcccCccc...." + "........DdddDddd...." + "...................."_s); +} + +void ImageVkTest::cmdCopyBufferImageCubeMap() { + CommandPool pool{device(), CommandPoolCreateInfo{ + device().properties().pickQueueFamily(QueueFlag::Graphics)}}; + CommandBuffer cmd = pool.allocate(); + + /* Source buffer */ + Buffer a{device(), BufferCreateInfo{ + BufferUsage::TransferSource, 3*4*6*4 + }, MemoryFlag::HostVisible}; + Utility::copy("________________" + "________Aaaa____" + "________Bbbb____" + + "________________" + "________Cccc____" + "________Dddd____" + + "________________" + "________Eeee____" + "________Ffff____" + + "________________" + "________Gggg____" + "________Hhhh____" + + "________________" + "________Iiii____" + "________Jjjj____" + + "________________" + "________Kkkk____" + "________Llll____"_s, a.dedicatedMemory().map()); + + /* Destination & source image */ + Image b{device(), ImageCreateInfoCubeMap{ + ImageUsage::TransferDestination|ImageUsage::TransferSource, + PixelFormat::RGBA8UI, {4, 4}, 1 + }, MemoryFlag::HostVisible}; + + /* Destination buffer */ + Buffer c{device(), BufferCreateInfo{ + BufferUsage::TransferDestination, 3*4*6*4 + }, MemoryFlag::HostVisible}; + + cmd.begin() + .pipelineBarrier(PipelineStage::TopOfPipe, PipelineStage::Transfer, { + {Accesses{}, Access::TransferWrite, + ImageLayout::Undefined, ImageLayout::TransferDestination, b} + }) + .fillBuffer(c, 0x2e2e2e2e) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Transfer, { + {Access::TransferWrite, Access::TransferRead, c} + }) + .copyBufferToImage(CopyBufferToImageInfoCubeMap{a, b, ImageLayout::TransferDestination, { + {(1*4 + 2)*4, 4, 3, ImageAspect::Color, 0, Range2Di::fromSize({3, 1}, {1, 2})} + }}) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Transfer, { + {Access::TransferWrite, Access::TransferRead, + ImageLayout::TransferDestination, ImageLayout::TransferSource, b} + }) + .copyImageToBuffer(CopyImageToBufferInfoCubeMap{b, ImageLayout::TransferSource, c, { + {(1*4 + 2)*4, 4, 3, ImageAspect::Color, 0, Range2Di::fromSize({3, 1}, {1, 2})} + }}) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Host, { + {Access::TransferWrite, Access::HostRead, c} + }) + .end(); + queue().submit({SubmitInfo{}.setCommandBuffers({cmd})}).wait(); + + CORRADE_COMPARE(arrayView(c.dedicatedMemory().mapRead()), + "................" + "........Aaaa...." + "........Bbbb...." + + "................" + "........Cccc...." + "........Dddd...." + + "................" + "........Eeee...." + "........Ffff...." + + "................" + "........Gggg...." + "........Hhhh...." + + "................" + "........Iiii...." + "........Jjjj...." + + "................" + "........Kkkk...." + "........Llll...."_s); +} + +void ImageVkTest::cmdCopyBufferImageCubeMapArray() { + CommandPool pool{device(), CommandPoolCreateInfo{ + device().properties().pickQueueFamily(QueueFlag::Graphics)}}; + CommandBuffer cmd = pool.allocate(); + + /* Source buffer */ + Buffer a{device(), BufferCreateInfo{ + BufferUsage::TransferSource, 3*4*7*4 + }, MemoryFlag::HostVisible}; + Utility::copy("________________" + "________Aaaa____" + "________Bbbb____" + + "________________" + "________Cccc____" + "________Dddd____" + + "________________" + "________Eeee____" + "________Ffff____" + + "________________" + "________Gggg____" + "________Hhhh____" + + "________________" + "________Iiii____" + "________Jjjj____" + + "________________" + "________Kkkk____" + "________Llll____" + + "________________" + "________Mmmm____" + "________Nnnn____"_s, a.dedicatedMemory().map()); + + /* Destination & source image */ + Image b{device(), ImageCreateInfoCubeMapArray{ + ImageUsage::TransferDestination|ImageUsage::TransferSource, + PixelFormat::RGBA8UI, {4, 4, 8}, 1 + }, MemoryFlag::HostVisible}; + + /* Destination buffer */ + Buffer c{device(), BufferCreateInfo{BufferUsage::TransferDestination, 3*4*7*4}, MemoryFlag::HostVisible}; + + cmd.begin() + .pipelineBarrier(PipelineStage::TopOfPipe, PipelineStage::Transfer, { + {Accesses{}, Access::TransferWrite, + ImageLayout::Undefined, ImageLayout::TransferDestination, b} + }) + .fillBuffer(c, 0x2e2e2e2e) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Transfer, { + {Access::TransferWrite, Access::TransferRead, c} + }) + .copyBufferToImage(CopyBufferToImageInfoCubeMapArray{a, b, ImageLayout::TransferDestination, { + {(1*4 + 2)*4, 4, 3, ImageAspect::Color, 0, Range3Di::fromSize({3, 1, 1}, {1, 2, 7})} + }}) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Transfer, { + {Access::TransferWrite, Access::TransferRead, + ImageLayout::TransferDestination, ImageLayout::TransferSource, b} + }) + .copyImageToBuffer(CopyImageToBufferInfoCubeMapArray{b, ImageLayout::TransferSource, c, { + {(1*4 + 2)*4, 4, 3, ImageAspect::Color, 0, Range3Di::fromSize({3, 1, 1}, {1, 2, 7})} + }}) + .pipelineBarrier(PipelineStage::Transfer, PipelineStage::Host, { + {Access::TransferWrite, Access::HostRead, c} + }) + .end(); + queue().submit({SubmitInfo{}.setCommandBuffers({cmd})}).wait(); + + CORRADE_COMPARE(arrayView(c.dedicatedMemory().mapRead()), + "................" + "........Aaaa...." + "........Bbbb...." + + "................" + "........Cccc...." + "........Dddd...." + + "................" + "........Eeee...." + "........Ffff...." + + "................" + "........Gggg...." + "........Hhhh...." + + "................" + "........Iiii...." + "........Jjjj...." + + "................" + "........Kkkk...." + "........Llll...." + + "................" + "........Mmmm...." + "........Nnnn...."_s); +} + +void ImageVkTest::cmdCopyBufferImageDisallowedConversion() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + if(device().isExtensionEnabled()) + CORRADE_SKIP("KHR_copy_commands2 enabled on the device, can't test"); + + CommandPool pool{device(), CommandPoolCreateInfo{ + device().properties().pickQueueFamily(QueueFlag::Graphics)}}; + CommandBuffer cmd = pool.allocate(); + + CopyBufferToImageInfo a{{}, {}, {}, {}}; + a->pNext = &a; + CopyImageToBufferInfo b{{}, {}, {}, {}}; + b->pNext = &b; + + /* The commands shouldn't do anything, so it should be fine to just call + them without any render pass set up */ + std::ostringstream out; + Error redirectError{&out}; + cmd.copyBufferToImage(a) + .copyImageToBuffer(b); + CORRADE_COMPARE(out.str(), + "Vk::CommandBuffer::copyBufferToImage(): disallowing extraction of CopyBufferToImageInfo with non-empty pNext to prevent information loss\n" + "Vk::CommandBuffer::copyImageToBuffer(): disallowing extraction of CopyImageToBufferInfo with non-empty pNext to prevent information loss\n"); +} + }}}} CORRADE_TEST_MAIN(Magnum::Vk::Test::ImageVkTest) diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index 572d647bb..0b4967cac 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -45,6 +45,15 @@ class CommandBuffer; /* CommandBufferBeginInfo is useful only in combination with CommandBuffer */ class CommandPool; class CommandPoolCreateInfo; +/* BufferCopy used only directly inside CopyBufferInfo */ +class CopyBufferInfo; +/* ImageCopy used only directly inside CopyImageInfo */ +class CopyImageInfo; +/* BufferImageCopy used only directly inside CopyBufferToImageInfo / + CopyImageToBufferInfo */ +class CopyBufferToImageInfo; +class CopyImageToBufferInfo; +/* Not forward-declaring CopyBufferToImageInfo1D etc right now, I see no need */ enum class DependencyFlag: UnsignedInt; typedef Containers::EnumSet DependencyFlags; class Device;