Magnum::Vk::Image class new in Git master

Image.

Wraps a VkImage and its memory.

Image creation

Pass one of the ImageCreateInfo subclasses depending on desired image type with desired usage, format, size and other propoerties to the Image constructor together with specifying MemoryFlags for memory allocation.

#include <Magnum/Vk/ImageCreateInfo.h>



Vk::Image image{device, Vk::ImageCreateInfo2D{
    Vk::ImageUsage::Sampled, PixelFormat::RGBA8Srgb, {1024, 1024}, 1
}, Vk::MemoryFlag::DeviceLocal};

With an Image ready, you may want to proceed to ImageView creation.

Custom memory allocation

Using Image(Device&, const ImageCreateInfo&, NoAllocateT), the image will be created without any memory attached. Image memory requirements can be subsequently queried using memoryRequirements() and an allocated memory bound with bindMemory(). See Memory for further details about memory allocation.

Vk::Image image{device, Vk::ImageCreateInfo2D{
    Vk::ImageUsage::Sampled, PixelFormat::RGBA8Srgb, {1024, 1024}, 1
}, NoAllocate};

Vk::MemoryRequirements requirements = image.memoryRequirements();
Vk::Memory memory{device, Vk::MemoryAllocateInfo{
    requirements.size(),
    device.properties().pickMemory(Vk::MemoryFlag::DeviceLocal,
        requirements.memories())
}};

image.bindMemory(memory, 0);

Using bindDedicatedMemory() instead of bindMemory() will transfer ownership of the Memory to the image instance, making it subsequently available through dedicatedMemory(). This matches current behavior of the Image(Device&, const ImageCreateInfo&, MemoryFlags) constructor shown above, except that you have more control over choosing and allocating the memory.

Image usage

Clearing image data

Usually an image is cleared implicitly at the start of a render pass using AttachmentLoadOperation::Clear for the corresponding attachment and specifying the clear color using RenderPassBeginInfo::clearColor() / clearDepthStencil(). If you need to do a clear outside of a render pass, it can be done using CommandBuffer::clearColorImage() / clearDepthStencilImage() / clearDepthImage() / clearStencilImage(). In most cases you'll also need to perform a layout transition first using a pipelineBarrier():

Vk::Image image{device, Vk::ImageCreateInfo2D{
    Vk::ImageUsage::TransferDestination|, Vk::PixelFormat::RGBA8Srgb, 
}, };



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);

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 CommandBuffer::copyBufferToImage() / copyImageToBuffer(). For convenience, you're encouraged to use the BufferImageCopy1D, BufferImageCopy2D etc. constructors that will correctly set the remaining parameters for certain image type. In most cases you'll also need to add two pipelineBarrier() commands to perform a layout transition before, and make the memory visible for subsequent operations after. For example:

Vk::Buffer input{device, Vk::BufferCreateInfo{
    Vk::BufferUsage::TransferSource, 256*256*4 
}, Vk::MemoryFlag::HostVisible};
Vk::Image texture{device, Vk::ImageCreateInfo2D{
    Vk::ImageUsage::TransferDestination|Vk::ImageUsage::Sampled,
    Vk::PixelFormat::RGBA8Srgb, {256, 256}, 
}, Vk::MemoryFlag::DeviceLocal};



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}
    });

Alternatively you can use CopyBufferToImageInfo1D / CopyImageToBufferInfo1D, CopyBufferToImageInfo2D / CopyImageToBufferInfo2D etc., as both CopyBufferToImageInfo / CopyImageToBufferInfo and 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:

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}}},
        
    }
});

Image/image copy is possible as well and is done using 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 pipelineBarrier() for both images it could look like this:

Vk::Image a{device, Vk::ImageCreateInfo2D{
    Vk::ImageUsage::TransferSource|,
    Vk::PixelFormat::RGBA8Srgb, {256, 256}, 
}, };
Vk::Image b{device, Vk::ImageCreateInfo2D{
    Vk::ImageUsage::TransferDestination|, Vk::PixelFormat::RGBA8Srgb, {256, 256}, 
}, };



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::, 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}}
    }});

Public static functions

static auto wrap(Device& device, VkImage handle, PixelFormat format, HandleFlags flags = {}) -> Image
Wrap existing Vulkan handle.
static auto wrap(Device& device, VkImage handle, Magnum::PixelFormat format, HandleFlags flags = {}) -> Image
static auto wrap(Device& device, VkImage handle, Magnum::CompressedPixelFormat format, HandleFlags flags = {}) -> Image

Constructors, destructors, conversion operators

Image(Device& device, const ImageCreateInfo& info, NoAllocateT) explicit
Construct an image without allocating.
Image(Device& device, const ImageCreateInfo& info, MemoryFlags memoryFlags) explicit
Construct an image.
Image(NoCreateT) explicit
Construct without creating the image.
Image(const Image&) deleted
Copying is not allowed.
Image(Image&& other) noexcept
Move constructor.
~Image()
Destructor.
operator VkImage()

Public functions

auto operator=(const Image&) -> Image& deleted
Copying is not allowed.
auto operator=(Image&& other) -> Image& noexcept
Move assignment.
auto handle() -> VkImage
Underlying VkImage handle.
auto handleFlags() const -> HandleFlags
Handle flags.
auto format() const -> PixelFormat
Image format.
auto memoryRequirements() const -> MemoryRequirements
Image memory requirements.
void bindMemory(Memory& memory, UnsignedLong offset)
Bind image memory.
void bindDedicatedMemory(Memory&& memory)
Bind a dedicated image memory.
auto hasDedicatedMemory() const -> bool
Whether the image has a dedicated memory.
auto dedicatedMemory() -> Memory&
Dedicated image memory.
auto release() -> VkImage
Release the underlying Vulkan image.

Function documentation

static Image Magnum::Vk::Image::wrap(Device& device, VkImage handle, PixelFormat format, HandleFlags flags = {})

Wrap existing Vulkan handle.

Parameters
device Vulkan device the image is created on
handle The VkImage handle
format Image format. Available through format() afterwards.
flags Handle flags

The handle is expected to be originating from device. The format parameter is used for convenience ImageView creation. If it's unknown, use a PixelFormat{} — you will then be able to only create image views by passing a concrete format to ImageViewCreateInfo.

Unlike an image created using a constructor, the Vulkan image is by default not deleted on destruction, use flags for different behavior.

static Image Magnum::Vk::Image::wrap(Device& device, VkImage handle, Magnum::PixelFormat format, HandleFlags flags = {})

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

static Image Magnum::Vk::Image::wrap(Device& device, VkImage handle, Magnum::CompressedPixelFormat format, HandleFlags flags = {})

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

Magnum::Vk::Image::Image(Device& device, const ImageCreateInfo& info, NoAllocateT) explicit

Construct an image without allocating.

Parameters
device Vulkan device to create the image on
info Image creation info

Use memoryRequirements(), Memory and bindMemory() to bind a memory (sub)allocation to the image.

Magnum::Vk::Image::Image(Device& device, const ImageCreateInfo& info, MemoryFlags memoryFlags) explicit

Construct an image.

Parameters
device Vulkan device to create the image on
info Image creation info
memoryFlags Memory allocation flags

Compared to Image(Device&, const ImageCreateInfo&, NoAllocateT) allocates a memory satisfying memoryFlags as well.

Magnum::Vk::Image::Image(NoCreateT) explicit

Construct without creating the image.

The constructed instance is equivalent to moved-from state. Useful in cases where you will overwrite the instance later anyway. Move another object over it to make it useful.

Magnum::Vk::Image::~Image()

Destructor.

Destroys associated VkImage handle, unless the instance was created using wrap() without HandleFlag::DestroyOnDestruction specified.

Magnum::Vk::Image::operator VkImage()

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

void Magnum::Vk::Image::bindMemory(Memory& memory, UnsignedLong offset)

Bind image memory.

Assumes that memory type, the amount of memory at offset and offset alignment corresponds to image memory requirements.

void Magnum::Vk::Image::bindDedicatedMemory(Memory&& memory)

Bind a dedicated image memory.

Equivalent to bindMemory() with offset set to 0, with the additional effect that memory ownership transfers to the image and is then available through dedicatedMemory().

bool Magnum::Vk::Image::hasDedicatedMemory() const

Whether the image has a dedicated memory.

Returns true if the image memory was bound using bindDedicatedMemory(), false otherwise.

Memory& Magnum::Vk::Image::dedicatedMemory()

Dedicated image memory.

Expects that the image has a dedicated memory.

VkImage Magnum::Vk::Image::release()

Release the underlying Vulkan image.

Releases ownership of the Vulkan image and returns its handle so vkDestroyImage() is not called on destruction. The internal state is then equivalent to moved-from state.