## 28.8. Examples

The following examples illustrate basic creation of sparse images and binding them to physical memory.

### 28.8.1. Basic Sparse Resources

This basic example creates a normal `VkImage` object but uses fine-grained memory allocation to back the resource with multiple memory ranges.

    VkDevice                device;
    VkQueue                 queue;
    VkImage                 sparseImage;
    VkMemoryRequirements    memoryRequirements = {};
    VkDeviceSize            offset = 0;
    VkSparseMemoryBind      binds[MAX_CHUNKS] = {}; // MAX_CHUNKS is NOT part of Vulkan
    uint32_t                bindCount = 0;

    // ...

    // Allocate image object
    const VkImageCreateInfo sparseImageInfo =
    {
        VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,        // sType
        NULL,                                       // pNext
        VK_IMAGE_CREATE_SPARSE_BINDING_BIT | ...,   // flags
        ...
    };
    vkCreateImage(device, &sparseImageInfo, &sparseImage);

    // Get memory requirements
    vkGetImageMemoryRequirements(
        device,
        sparseImage,
        &memoryRequirements);

    // Bind memory in fine-grained fashion, find available memory ranges
    // from potentially multiple VkDeviceMemory pools.
    // (Illustration purposes only, can be optimized for perf)
    while (memoryRequirements.size && bindCount < MAX_CHUNKS)
    {
        VkSparseMemoryBind* pBind = &binds[bindCount];
        pBind->resourceOffset = offset;

        AllocateOrGetMemoryRange(
            device,
            &memoryRequirements,
            &pBind->memory,
            &pBind->memoryOffset,
            &pBind->size);

        // memory ranges must be sized as multiples of the alignment
        assert(IsMultiple(pBind->size, memoryRequirements.alignment));
        assert(IsMultiple(pBind->memoryOffset, memoryRequirements.alignment));

        memoryRequirements.size -= pBind->size;
        offset                  += pBind->size;
        bindCount++;
    }

    // Ensure all image has backing
    if (memoryRequirements.size)
    {
        // Error condition - too many chunks
    }

    const VkSparseImageOpaqueMemoryBindInfo opaqueBindInfo =
    {
        sparseImage,                                // image
        bindCount,                                  // bindCount
        binds                                       // pBinds
    };

    const VkBindSparseInfo bindSparseInfo =
    {
        VK_STRUCTURE_TYPE_BIND_SPARSE_INFO,         // sType
        NULL,                                       // pNext
        ...
        1,                                          // imageOpaqueBindCount
        &opaqueBindInfo,                            // pImageOpaqueBinds
        ...
    };

    // vkQueueBindSparse is application synchronized per queue object.
    AcquireQueueOwnership(queue);

    // Actually bind memory
    vkQueueBindSparse(queue, 1, &bindSparseInfo, VK_NULL_HANDLE);

    ReleaseQueueOwnership(queue);

### 28.8.2. Advanced Sparse Resources

This more advanced example creates an arrayed color attachment / texture image and binds only LOD zero and the required metadata to physical memory.

    VkDevice                            device;
    VkQueue                             queue;
    VkImage                             sparseImage;
    VkMemoryRequirements                memoryRequirements = {};
    uint32_t                            sparseRequirementsCount = 0;
    VkSparseImageMemoryRequirements*    pSparseReqs = NULL;
    VkSparseMemoryBind                  binds[MY_IMAGE_ARRAY_SIZE] = {};
    VkSparseImageMemoryBind             imageBinds[MY_IMAGE_ARRAY_SIZE] = {};
    uint32_t                            bindCount = 0;

    // Allocate image object (both renderable and sampleable)
    const VkImageCreateInfo sparseImageInfo =
    {
        VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,        // sType
        NULL,                                       // pNext
        VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT | ..., // flags
        ...
        VK_FORMAT_R8G8B8A8_UNORM,                   // format
        ...
        MY_IMAGE_ARRAY_SIZE,                        // arrayLayers
        ...
        VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
        VK_IMAGE_USAGE_SAMPLED_BIT,                 // usage
        ...
    };
    vkCreateImage(device, &sparseImageInfo, &sparseImage);

    // Get memory requirements
    vkGetImageMemoryRequirements(
        device,
        sparseImage,
        &memoryRequirements);

    // Get sparse image aspect properties
    vkGetImageSparseMemoryRequirements(
        device,
        sparseImage,
        &sparseRequirementsCount,
        NULL);

    pSparseReqs = (VkSparseImageMemoryRequirements*)
        malloc(sparseRequirementsCount * sizeof(VkSparseImageMemoryRequirements));

    vkGetImageSparseMemoryRequirements(
        device,
        sparseImage,
        &sparseRequirementsCount,
        pSparseReqs);

    // Bind LOD level 0 and any required metadata to memory
    for (uint32_t i = 0; i < sparseRequirementsCount; ++i)
    {
        if (pSparseReqs[i].formatProperties.aspectMask &
            VK_IMAGE_ASPECT_METADATA_BIT)
        {
            // Metadata must not be combined with other aspects
            assert(pSparseReqs[i].formatProperties.aspectMask ==
                   VK_IMAGE_ASPECT_METADATA_BIT);

            if (pSparseReqs[i].formatProperties.flags &
                VK_SPARSE_IMAGE_FORMAT_SINGLE_MIPTAIL_BIT)
            {
                VkSparseMemoryBind* pBind = &binds[bindCount];
                pBind->memorySize = pSparseReqs[i].imageMipTailSize;
                bindCount++;

                // ... Allocate memory range

                pBind->resourceOffset = pSparseReqs[i].imageMipTailOffset;
                pBind->memoryOffset = /* allocated memoryOffset */;
                pBind->memory = /* allocated memory */;
                pBind->flags = VK_SPARSE_MEMORY_BIND_METADATA_BIT;

            }
            else
            {
                // Need a mip tail region per array layer.
                for (uint32_t a = 0; a < sparseImageInfo.arrayLayers; ++a)
                {
                    VkSparseMemoryBind* pBind = &binds[bindCount];
                    pBind->memorySize = pSparseReqs[i].imageMipTailSize;
                    bindCount++;

                    // ... Allocate memory range

                    pBind->resourceOffset = pSparseReqs[i].imageMipTailOffset +
                                            (a * pSparseReqs[i].imageMipTailStride);

                    pBind->memoryOffset = /* allocated memoryOffset */;
                    pBind->memory = /* allocated memory */
                    pBind->flags = VK_SPARSE_MEMORY_BIND_METADATA_BIT;
                }
            }
        }
        else
        {
            // resource data
            VkExtent3D lod0BlockSize =
            {
                AlignedDivide(
                    sparseImageInfo.extent.width,
                    pSparseReqs[i].formatProperties.imageGranularity.width);
                AlignedDivide(
                    sparseImageInfo.extent.height,
                    pSparseReqs[i].formatProperties.imageGranularity.height);
                AlignedDivide(
                    sparseImageInfo.extent.depth,
                    pSparseReqs[i].formatProperties.imageGranularity.depth);
            }
            size_t totalBlocks =
                lod0BlockSize.width *
                lod0BlockSize.height *
                lod0BlockSize.depth;

            VkDeviceSize lod0MemSize = totalBlocks * memoryRequirements.alignment;

            // Allocate memory for each array layer
            for (uint32_t a = 0; a < sparseImageInfo.arrayLayers; ++a)
            {
                // ... Allocate memory range

                VkSparseImageMemoryBind* pBind = &imageBinds[a];
                pBind->subresource.aspectMask = pSparseReqs[i].formatProperties.aspectMask;
                pBind->subresource.mipLevel = 0;
                pBind->subresource.arrayLayer = a;

                pBind->offset = (VkOffset3D){0, 0, 0};
                pBind->extent = sparseImageInfo.extent;
                pBind->memoryOffset = /* allocated memoryOffset */;
                pBind->memory = /* allocated memory */;
                pBind->flags = 0;
            }
        }

        free(pSparseReqs);
    }

    const VkSparseImageOpaqueMemoryBindInfo opaqueBindInfo =
    {
        sparseImage,                                // image
        bindCount,                                  // bindCount
        binds                                       // pBinds
    };

    const VkSparseImageMemoryBindInfo imageBindInfo =
    {
        sparseImage,                                // image
        sparseImageInfo.arrayLayers,                // bindCount
        imageBinds                                  // pBinds
    };

    const VkBindSparseInfo bindSparseInfo =
    {
        VK_STRUCTURE_TYPE_BIND_SPARSE_INFO,         // sType
        NULL,                                       // pNext
        ...
        1,                                          // imageOpaqueBindCount
        &opaqueBindInfo,                            // pImageOpaqueBinds
        1,                                          // imageBindCount
        &imageBindInfo,                             // pImageBinds
        ...
    };

    // vkQueueBindSparse is application synchronized per queue object.
    AcquireQueueOwnership(queue);

    // Actually bind memory
    vkQueueBindSparse(queue, 1, &bindSparseInfo, VK_NULL_HANDLE);

    ReleaseQueueOwnership(queue);