/*
 * Copyright (c) 2023-2026 LunarG, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 */
#include "layer_validation_tests.h"
#include "pipeline_helper.h"
#include "render_pass_helper.h"
#include "utils/math_utils.h"

class NegativeTileMemoryHeap : public TileMemoryHeapTest {};

TEST_F(NegativeTileMemoryHeap, CreateBufferTest) {
    TEST_DESCRIPTION("Use VK_BUFFER_USAGE_TILE_MEMORY_BIT_QCOM without enabling the tileMemoryHeap feature");
    SetTargetApiVersion(VK_API_VERSION_1_1);
    AddRequiredExtensions(VK_QCOM_TILE_MEMORY_HEAP_EXTENSION_NAME);
    RETURN_IF_SKIP(Init());

    VkBufferCreateInfo buffer_ci = vku::InitStructHelper();
    buffer_ci.size = 4096;
    buffer_ci.usage = VK_BUFFER_USAGE_TILE_MEMORY_BIT_QCOM;
    CreateBufferTest(buffer_ci, "VUID-VkBufferCreateInfo-tileMemoryHeap-10762");
}

TEST_F(NegativeTileMemoryHeap, CreateBufferProtectedMemoryFlag) {
    TEST_DESCRIPTION("Test Protected flag in CreateBuffer with tileMemoryHeap feature");
    SetTargetApiVersion(VK_API_VERSION_1_1);
    AddRequiredExtensions(VK_QCOM_TILE_MEMORY_HEAP_EXTENSION_NAME);
    AddRequiredFeature(vkt::Feature::tileMemoryHeap);
    AddRequiredFeature(vkt::Feature::protectedMemory);
    RETURN_IF_SKIP(Init());

    VkBufferCreateInfo buffer_ci = vku::InitStructHelper();
    buffer_ci.size = 4096;
    buffer_ci.usage = VK_BUFFER_USAGE_TILE_MEMORY_BIT_QCOM;
    buffer_ci.flags = VK_BUFFER_CREATE_PROTECTED_BIT;
    m_errorMonitor->SetDesiredError("VUID-VkBufferCreateInfo-flags-09641");
    CreateBufferTest(buffer_ci, "VUID-VkBufferCreateInfo-usage-10763");
}

TEST_F(NegativeTileMemoryHeap, CreateBufferTestIndexUsageFlags) {
    TEST_DESCRIPTION("Test Index Usage Flag in CreateBuffer with tileMemoryHeap feature");
    SetTargetApiVersion(VK_API_VERSION_1_1);
    AddRequiredExtensions(VK_QCOM_TILE_MEMORY_HEAP_EXTENSION_NAME);
    AddRequiredFeature(vkt::Feature::tileMemoryHeap);
    RETURN_IF_SKIP(Init());

    VkBufferCreateInfo buffer_ci = vku::InitStructHelper();
    buffer_ci.size = 4096;
    buffer_ci.usage = VK_BUFFER_USAGE_TILE_MEMORY_BIT_QCOM | VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
    CreateBufferTest(buffer_ci, "VUID-VkBufferCreateInfo-usage-10764");
}

TEST_F(NegativeTileMemoryHeap, AllocateMemory) {
    TEST_DESCRIPTION("Allocate Tile Memory without the Tile Memory feature enabled.");
    AddRequiredExtensions(VK_QCOM_TILE_MEMORY_HEAP_EXTENSION_NAME);
    RETURN_IF_SKIP(Init());

    VkMemoryAllocateInfo alloc_info = vku::InitStructHelper();
    alloc_info.allocationSize = 256;

    bool pass = m_device->Physical().SetMemoryType(0xFFFFFFFF, &alloc_info, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0,
                                                   VK_MEMORY_HEAP_TILE_MEMORY_BIT_QCOM);
    if (!pass) {
        GTEST_SKIP() << "Could not find an eligible Tile Memory Type.";
    }

    m_errorMonitor->SetDesiredError("VUID-vkAllocateMemory-tileMemoryHeap-10976");
    vkt::DeviceMemory buffer_memory(*m_device, alloc_info);
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeTileMemoryHeap, BindBufferMemorySize) {
    TEST_DESCRIPTION("Bind Tile Memory to a Buffer with too small of size");
    SetTargetApiVersion(VK_API_VERSION_1_1);
    AddRequiredExtensions(VK_QCOM_TILE_MEMORY_HEAP_EXTENSION_NAME);
    AddRequiredFeature(vkt::Feature::tileMemoryHeap);

    RETURN_IF_SKIP(Init());

    vkt::Buffer buffer(*m_device,
                       vkt::Buffer::CreateInfo(4096, VK_BUFFER_USAGE_2_TILE_MEMORY_BIT_QCOM | VK_BUFFER_USAGE_2_STORAGE_BUFFER_BIT),
                       vkt::no_mem);

    VkBufferMemoryRequirementsInfo2 buffer_info = vku::InitStructHelper();
    VkTileMemoryRequirementsQCOM tile_mem_reqs = vku::InitStructHelper();
    VkMemoryRequirements2 buffer_reqs = vku::InitStructHelper(&tile_mem_reqs);
    buffer_info.buffer = buffer;
    vk::GetBufferMemoryRequirements2(device(), &buffer_info, &buffer_reqs);

    if (tile_mem_reqs.size == 0) {
        GTEST_SKIP() << "Buffer not eligible to be bound with Tile Memory.";
    }

    // Find a memory configuration for the Tile Memory Buffer, otherwise exit
    VkMemoryAllocateInfo bad_alloc_info = vku::InitStructHelper();
    bad_alloc_info.memoryTypeIndex = 0;
    // Purposely subtract 1 from size
    bad_alloc_info.allocationSize = tile_mem_reqs.size - 1;
    bool pass = m_device->Physical().SetMemoryType(buffer_reqs.memoryRequirements.memoryTypeBits, &bad_alloc_info,
                                                   VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0, VK_MEMORY_HEAP_TILE_MEMORY_BIT_QCOM);

    if (!pass) {
        GTEST_SKIP() << "Could not find an eligible Tile Memory Type.";
    }

    vkt::DeviceMemory buffer_memory(*m_device, bad_alloc_info);

    m_errorMonitor->SetDesiredError("VUID-vkBindBufferMemory-memory-10742");
    vk::BindBufferMemory(device(), buffer, buffer_memory, 0);
    m_errorMonitor->VerifyFound();

    VkBindBufferMemoryInfo bind_buffer_info = vku::InitStructHelper();
    bind_buffer_info.buffer = buffer;
    bind_buffer_info.memory = buffer_memory;
    bind_buffer_info.memoryOffset = 0;

    m_errorMonitor->SetDesiredError("VUID-VkBindBufferMemoryInfo-memory-10742");
    vk::BindBufferMemory2(device(), 1, &bind_buffer_info);
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeTileMemoryHeap, BindBufferMemoryAlignment) {
    TEST_DESCRIPTION("Bind Tile Memory to a Buffer with an offset that is not a multiple of alignment");
    SetTargetApiVersion(VK_API_VERSION_1_1);
    AddRequiredExtensions(VK_QCOM_TILE_MEMORY_HEAP_EXTENSION_NAME);
    AddRequiredFeature(vkt::Feature::tileMemoryHeap);
    RETURN_IF_SKIP(Init());

    vkt::Buffer buffer(*m_device,
                       vkt::Buffer::CreateInfo(4096, VK_BUFFER_USAGE_2_TILE_MEMORY_BIT_QCOM | VK_BUFFER_USAGE_2_STORAGE_BUFFER_BIT),
                       vkt::no_mem);

    // Query Tile Memory Buffer Requirements
    VkBufferMemoryRequirementsInfo2 buffer_info = vku::InitStructHelper();
    VkTileMemoryRequirementsQCOM tile_mem_reqs = vku::InitStructHelper();
    VkMemoryRequirements2 buffer_reqs = vku::InitStructHelper(&tile_mem_reqs);
    buffer_info.buffer = buffer;
    vk::GetBufferMemoryRequirements2(device(), &buffer_info, &buffer_reqs);

    if (tile_mem_reqs.size == 0) {
        GTEST_SKIP() << "Buffer not eligible to be bound with Tile Memory.";
    }

    // Find a memory configuration for the Tile Memory Buffer, otherwise exit
    const uint32_t badOffset = 1;
    VkMemoryAllocateInfo bad_alloc_info = vku::InitStructHelper();
    bad_alloc_info.memoryTypeIndex = 0;
    // Purposely add 1 to size for offset
    bad_alloc_info.allocationSize = tile_mem_reqs.size + badOffset;
    bool pass = m_device->Physical().SetMemoryType(buffer_reqs.memoryRequirements.memoryTypeBits, &bad_alloc_info,
                                                   VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0, VK_MEMORY_HEAP_TILE_MEMORY_BIT_QCOM);

    if (!pass) {
        GTEST_SKIP() << "Could not find an eligible Tile Memory Type.";
    }

    vkt::DeviceMemory buffer_memory(*m_device, bad_alloc_info);

    m_errorMonitor->SetDesiredError("VUID-vkBindBufferMemory-memory-10740");
    vk::BindBufferMemory(device(), buffer, buffer_memory, badOffset);
    m_errorMonitor->VerifyFound();

    VkBindBufferMemoryInfo bind_buffer_info = vku::InitStructHelper();
    bind_buffer_info.buffer = buffer;
    bind_buffer_info.memory = buffer_memory;
    bind_buffer_info.memoryOffset = badOffset;

    m_errorMonitor->SetDesiredError("VUID-VkBindBufferMemoryInfo-memory-10740");
    vk::BindBufferMemory2(device(), 1, &bind_buffer_info);
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeTileMemoryHeap, CreateImageTest) {
    TEST_DESCRIPTION("Use VK_IMAGE_USAGE_TILE_MEMORY_BIT_QCOM in CreateImage without enabling the tileMemoryHeap feature");
    SetTargetApiVersion(VK_API_VERSION_1_1);
    AddRequiredExtensions(VK_QCOM_TILE_MEMORY_HEAP_EXTENSION_NAME);
    RETURN_IF_SKIP(Init());

    CreateImageTest(vkt::Image::ImageCreateInfo2D(256, 256, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_TILE_MEMORY_BIT_QCOM),
                    "VUID-VkImageCreateInfo-tileMemoryHeap-10766");
}

TEST_F(NegativeTileMemoryHeap, BindImageMemorySize) {
    TEST_DESCRIPTION("Bind Tile Memory to an Image with too small of size");
    SetTargetApiVersion(VK_API_VERSION_1_1);
    AddRequiredExtensions(VK_QCOM_TILE_MEMORY_HEAP_EXTENSION_NAME);
    AddRequiredFeature(vkt::Feature::tileMemoryHeap);
    RETURN_IF_SKIP(Init());

    auto image_create_info = vkt::Image::ImageCreateInfo2D(
        32u, 32u, 1u, 1u, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TILE_MEMORY_BIT_QCOM);
    vkt::Image image(*m_device, image_create_info, vkt::no_mem);

    VkImageMemoryRequirementsInfo2 image_info = vku::InitStructHelper();
    VkTileMemoryRequirementsQCOM tile_mem_reqs = vku::InitStructHelper();
    VkMemoryRequirements2 image_reqs = vku::InitStructHelper(&tile_mem_reqs);
    image_info.image = image;
    vk::GetImageMemoryRequirements2(device(), &image_info, &image_reqs);

    if (tile_mem_reqs.size == 0) {
        GTEST_SKIP() << "Image not eligible to be bound with Tile Memory.";
    }

    // Find a memory configuration for the Tile Memory Image, otherwise exit
    VkMemoryAllocateInfo bad_alloc_info = vku::InitStructHelper();
    bad_alloc_info.memoryTypeIndex = 0;
    // Purposely subtract 1 from size
    bad_alloc_info.allocationSize = tile_mem_reqs.size - 1;
    bool pass = m_device->Physical().SetMemoryType(image_reqs.memoryRequirements.memoryTypeBits, &bad_alloc_info,
                                                   VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0, VK_MEMORY_HEAP_TILE_MEMORY_BIT_QCOM);

    if (!pass) {
        GTEST_SKIP() << "Could not find an eligible Tile Memory Type.";
    }

    vkt::DeviceMemory image_memory(*m_device, bad_alloc_info);

    m_errorMonitor->SetDesiredError("VUID-vkBindImageMemory-memory-10738");
    vk::BindImageMemory(device(), image, image_memory, 0);
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeTileMemoryHeap, BindImageMemoryAlignment) {
    TEST_DESCRIPTION("Bind Tile Memory to an Image with an offset that is not a multiple of alignment");
    SetTargetApiVersion(VK_API_VERSION_1_1);
    AddRequiredExtensions(VK_QCOM_TILE_MEMORY_HEAP_EXTENSION_NAME);
    AddRequiredFeature(vkt::Feature::tileMemoryHeap);
    RETURN_IF_SKIP(Init());

    auto image_create_info = vkt::Image::ImageCreateInfo2D(
        32u, 32u, 1u, 1u, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TILE_MEMORY_BIT_QCOM);
    vkt::Image image(*m_device, image_create_info, vkt::no_mem);

    VkImageMemoryRequirementsInfo2 image_info = vku::InitStructHelper();
    VkTileMemoryRequirementsQCOM tile_mem_reqs = vku::InitStructHelper();
    VkMemoryRequirements2 image_reqs = vku::InitStructHelper(&tile_mem_reqs);
    image_info.image = image;
    vk::GetImageMemoryRequirements2(device(), &image_info, &image_reqs);

    if (tile_mem_reqs.size == 0) {
        GTEST_SKIP() << "Image not eligible to be bound with Tile Memory.";
    }

    // Find a memory configuration for the Tile Memory Image, otherwise exit
    const uint32_t badOffset = 1;
    VkMemoryAllocateInfo bad_alloc_info = vku::InitStructHelper();
    bad_alloc_info.memoryTypeIndex = 0;
    // Purposely add 1 to size for offset
    bad_alloc_info.allocationSize = tile_mem_reqs.size + badOffset;
    bool pass = m_device->Physical().SetMemoryType(image_reqs.memoryRequirements.memoryTypeBits, &bad_alloc_info,
                                                   VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0, VK_MEMORY_HEAP_TILE_MEMORY_BIT_QCOM);

    if (!pass) {
        GTEST_SKIP() << "Could not find an eligible Tile Memory Type.";
    }

    vkt::DeviceMemory image_memory(*m_device, bad_alloc_info);

    m_errorMonitor->SetDesiredError("VUID-vkBindImageMemory-memory-10736");
    vk::BindImageMemory(device(), image, image_memory, badOffset);
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeTileMemoryHeap, BindNonTileMemoryCommandBuffer) {
    TEST_DESCRIPTION("Bind non Tile Memory with vkCmdBindTileMemoryQCOM in Primary/Secondary Command Buffer.");
    SetTargetApiVersion(VK_API_VERSION_1_1);
    AddRequiredExtensions(VK_QCOM_TILE_MEMORY_HEAP_EXTENSION_NAME);
    AddRequiredFeature(vkt::Feature::tileMemoryHeap);
    RETURN_IF_SKIP(Init());

    VkMemoryAllocateInfo mem_alloc = vku::InitStructHelper();
    mem_alloc.allocationSize = 256;
    mem_alloc.memoryTypeIndex = 0;
    VkPhysicalDeviceMemoryProperties memory_info;
    vk::GetPhysicalDeviceMemoryProperties(Gpu(), &memory_info);

    uint32_t i = 0;
    for (; i < memory_info.memoryTypeCount; i++) {
        // Would require deviceCoherentMemory feature
        if (memory_info.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD) {
            continue;
        }
        // Would require protected feature
        if (memory_info.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_PROTECTED_BIT) {
            continue;
        }
        if (!(memory_info.memoryHeaps[memory_info.memoryTypes[i].heapIndex].flags & VK_MEMORY_HEAP_TILE_MEMORY_BIT_QCOM)) {
            mem_alloc.memoryTypeIndex = i;
            break;
        }
    }

    if (i >= memory_info.memoryTypeCount) {
        GTEST_SKIP() << "No invalid memory type index could be found";
    }

    vkt::DeviceMemory non_tile_memory(*m_device, mem_alloc);

    VkTileMemoryBindInfoQCOM tile_mem_bind_info = vku::InitStructHelper();
    tile_mem_bind_info.memory = non_tile_memory;

    m_command_buffer.Begin();
    m_errorMonitor->SetDesiredError("VUID-VkTileMemoryBindInfoQCOM-memory-10726");
    vk::CmdBindTileMemoryQCOM(m_command_buffer, &tile_mem_bind_info);
    m_errorMonitor->VerifyFound();

    const VkCommandBufferInheritanceInfo cmdbuff_ii = vku::InitStructHelper(&tile_mem_bind_info);
    VkCommandBufferBeginInfo cmdbuff_bi = vku::InitStructHelper();
    cmdbuff_bi.pInheritanceInfo = &cmdbuff_ii;
    vkt::CommandBuffer secondary(*m_device, m_command_pool, VK_COMMAND_BUFFER_LEVEL_SECONDARY);

    m_errorMonitor->SetDesiredError("VUID-VkTileMemoryBindInfoQCOM-memory-10726");
    vk::BeginCommandBuffer(secondary, &cmdbuff_bi);
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeTileMemoryHeap, TileMemoryMismatchResourceCommandBuffer) {
    TEST_DESCRIPTION("Mismatch VkDeviceMemory between underlying Resource and Command Buffer");
    SetTargetApiVersion(VK_API_VERSION_1_2);
    AddRequiredExtensions(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME);
    AddRequiredExtensions(VK_QCOM_TILE_MEMORY_HEAP_EXTENSION_NAME);
    AddRequiredFeature(vkt::Feature::tileMemoryHeap);
    AddRequiredFeature(vkt::Feature::dynamicRendering);
    RETURN_IF_SKIP(Init());

    auto image_create_info = vkt::Image::ImageCreateInfo2D(
        32u, 32u, 1u, 1u, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TILE_MEMORY_BIT_QCOM);
    vkt::Image image(*m_device, image_create_info, vkt::no_mem);

    VkImageMemoryRequirementsInfo2 image_info = vku::InitStructHelper();
    VkTileMemoryRequirementsQCOM tile_mem_reqs = vku::InitStructHelper();
    VkMemoryRequirements2 image_reqs = vku::InitStructHelper(&tile_mem_reqs);
    image_info.image = image;
    vk::GetImageMemoryRequirements2(device(), &image_info, &image_reqs);

    if (tile_mem_reqs.size == 0) {
        GTEST_SKIP() << "Image not eligible to be bound with Tile Memory.";
    }

    // Find a memory configuration for the Tile Memory Image, otherwise exit
    VkMemoryAllocateInfo alloc_info = vku::InitStructHelper();
    alloc_info.memoryTypeIndex = 0;
    alloc_info.allocationSize = tile_mem_reqs.size;
    bool pass = m_device->Physical().SetMemoryType(image_reqs.memoryRequirements.memoryTypeBits, &alloc_info,
                                                   VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0, VK_MEMORY_HEAP_TILE_MEMORY_BIT_QCOM);

    if (!pass) {
        GTEST_SKIP() << "Could not find an eligible Tile Memory Type.";
    }

    vkt::DeviceMemory image_memory(*m_device, alloc_info);
    vk::BindImageMemory(device(), image, image_memory, 0);
    vkt::ImageView image_view = image.CreateView();

    VkFormat color_formats = VK_FORMAT_R8G8B8A8_UNORM;
    VkPipelineRenderingCreateInfo pipeline_rendering_info = vku::InitStructHelper();
    pipeline_rendering_info.colorAttachmentCount = 1;
    pipeline_rendering_info.pColorAttachmentFormats = &color_formats;

    CreatePipelineHelper pipe(*this, &pipeline_rendering_info);
    pipe.CreateGraphicsPipeline();

    VkRenderingAttachmentInfo color_attachment = vku::InitStructHelper();
    color_attachment.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
    color_attachment.imageView = image_view;

    VkRenderingInfo begin_rendering_info = vku::InitStructHelper();
    begin_rendering_info.colorAttachmentCount = 1;
    begin_rendering_info.pColorAttachments = &color_attachment;
    begin_rendering_info.layerCount = 1;
    begin_rendering_info.renderArea = {{0, 0}, {32, 32}};

    m_command_buffer.Begin();
    m_command_buffer.BeginRendering(begin_rendering_info);
    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe);
    m_errorMonitor->SetDesiredError("VUID-vkCmdDraw-commandBuffer-10746");
    vk::CmdDraw(m_command_buffer, 3, 1, 0, 0);
    m_errorMonitor->VerifyFound();
    m_command_buffer.EndRendering();

    VkAttachmentReference2 attachment_ref = vku::InitStructHelper();
    attachment_ref.layout = VK_IMAGE_LAYOUT_GENERAL;
    attachment_ref.attachment = 0;

    VkSubpassDescription2 subpass[2] = {};
    subpass[0] = vku::InitStructHelper();
    subpass[0].colorAttachmentCount = 1;
    subpass[0].pColorAttachments = &attachment_ref;
    subpass[1] = vku::InitStructHelper();
    subpass[1].colorAttachmentCount = 1;
    subpass[1].pColorAttachments = &attachment_ref;

    VkAttachmentDescription2 attach_desc = vku::InitStructHelper();
    attach_desc.format = VK_FORMAT_R8G8B8A8_UNORM;
    attach_desc.samples = VK_SAMPLE_COUNT_1_BIT;
    attach_desc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
    attach_desc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    attach_desc.finalLayout = VK_IMAGE_LAYOUT_GENERAL;

    VkRenderPassCreateInfo2 rp = vku::InitStructHelper();
    rp.subpassCount = 2;
    rp.pSubpasses = &subpass[0];
    rp.attachmentCount = 1;
    rp.pAttachments = &attach_desc;
    vkt::RenderPass test_rp(*m_device, rp);

    VkImageView fb_image_view = image_view;
    auto frame_buffer_create_info =
        vku::InitStruct<VkFramebufferCreateInfo>(nullptr, 0u, test_rp.handle(), 1u, &fb_image_view, 32u, 32u, 1u);
    vkt::Framebuffer fb(*m_device, frame_buffer_create_info);
    CreatePipelineHelper render_pass_pipe(*this);
    render_pass_pipe.gp_ci_.renderPass = test_rp;
    render_pass_pipe.gp_ci_.subpass = 0;
    render_pass_pipe.CreateGraphicsPipeline();

    m_command_buffer.BeginRenderPass(test_rp, fb, 32u, 32u, 0, nullptr);
    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, render_pass_pipe);
    m_errorMonitor->SetDesiredError("VUID-vkCmdDraw-commandBuffer-10746");
    vk::CmdDraw(m_command_buffer, 3, 1, 0, 0);
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeTileMemoryHeap, SecondaryCommandBufferTileMemory) {
    SetTargetApiVersion(VK_API_VERSION_1_2);
    AddRequiredExtensions(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME);
    AddRequiredExtensions(VK_QCOM_TILE_MEMORY_HEAP_EXTENSION_NAME);
    AddRequiredFeature(vkt::Feature::tileMemoryHeap);
    AddRequiredFeature(vkt::Feature::dynamicRendering);
    RETURN_IF_SKIP(Init());

    auto image_create_info =
        vkt::Image::ImageCreateInfo2D(32u, 32u, 1u, 1u, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
    vkt::Image image(*m_device, image_create_info);

    VkCommandBufferAllocateInfo secondary_cmd_buffer_alloc_info = vku::InitStructHelper();
    secondary_cmd_buffer_alloc_info.commandPool = m_command_pool;
    secondary_cmd_buffer_alloc_info.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY;
    secondary_cmd_buffer_alloc_info.commandBufferCount = 1;

    vkt::CommandBuffer secondary_cmd_buffer(*m_device, secondary_cmd_buffer_alloc_info);
    VkPhysicalDeviceMemoryProperties memory_info;
    int mem_type_index = -1;
    vk::GetPhysicalDeviceMemoryProperties(Gpu(), &memory_info);
    for (uint32_t i = 0; i < memory_info.memoryTypeCount; i++) {
        int heap_index = memory_info.memoryTypes[i].heapIndex;
        if (memory_info.memoryHeaps[heap_index].flags & VK_MEMORY_HEAP_TILE_MEMORY_BIT_QCOM) {
            mem_type_index = i;
            break;
        }
    }

    if (mem_type_index == -1) {
        GTEST_SKIP() << "Could not find an eligible Tile Memory Type.";
    }

    VkMemoryAllocateInfo tile_mem_alloc_info = vku::InitStructHelper();
    tile_mem_alloc_info.memoryTypeIndex = mem_type_index;
    tile_mem_alloc_info.allocationSize = 1024;
    vkt::DeviceMemory tile_mem1(*m_device, tile_mem_alloc_info);
    tile_mem_alloc_info.allocationSize = 512;
    vkt::DeviceMemory tile_mem2(*m_device, tile_mem_alloc_info);
    VkTileMemoryBindInfoQCOM bind_info = vku::InitStructHelper();
    bind_info.memory = tile_mem1;

    VkTileMemoryBindInfoQCOM tile_memory_bind_info = vku::InitStructHelper();
    tile_memory_bind_info.memory = tile_mem2;

    VkFormat color_formats = VK_FORMAT_R8G8B8A8_UNORM;

    VkCommandBufferInheritanceRenderingInfo inheritance_rendering_info = vku::InitStructHelper(&tile_memory_bind_info);
    inheritance_rendering_info.colorAttachmentCount = 1;
    inheritance_rendering_info.pColorAttachmentFormats = &color_formats;
    inheritance_rendering_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
    inheritance_rendering_info.flags = VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT;

    VkCommandBufferInheritanceInfo cmd_buffer_inheritance_info = vku::InitStructHelper(&inheritance_rendering_info);
    VkCommandBufferBeginInfo secondary_cmd_buffer_begin_info = vku::InitStructHelper{};
    secondary_cmd_buffer_begin_info.flags = VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT;
    secondary_cmd_buffer_begin_info.pInheritanceInfo = &cmd_buffer_inheritance_info;

    secondary_cmd_buffer.Begin(&secondary_cmd_buffer_begin_info);
    secondary_cmd_buffer.End();

    vkt::ImageView image_view = image.CreateView();
    VkRenderingAttachmentInfo color_attachment = vku::InitStructHelper();
    color_attachment.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
    color_attachment.imageView = image_view;

    VkRenderingInfo begin_rendering_info = vku::InitStructHelper();
    begin_rendering_info.flags = VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT;
    begin_rendering_info.colorAttachmentCount = 1;
    begin_rendering_info.pColorAttachments = &color_attachment;
    begin_rendering_info.layerCount = 1;
    begin_rendering_info.renderArea = {{0, 0}, {32, 32}};

    m_command_buffer.Begin();
    vk::CmdBindTileMemoryQCOM(m_command_buffer, &bind_info);
    m_command_buffer.BeginRendering(begin_rendering_info);
    m_errorMonitor->SetDesiredError("VUID-vkCmdExecuteCommands-memory-10724");
    vk::CmdExecuteCommands(m_command_buffer, 1, &secondary_cmd_buffer.handle());
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeTileMemoryHeap, TileProperties) {
    TEST_DESCRIPTION("Provide a Tile Memory size that is greater than the largest Tile Memory heap.");
    SetTargetApiVersion(VK_API_VERSION_1_1);
    AddRequiredExtensions(VK_QCOM_TILE_MEMORY_HEAP_EXTENSION_NAME);
    AddRequiredExtensions(VK_QCOM_TILE_PROPERTIES_EXTENSION_NAME);
    AddRequiredExtensions(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME);
    AddRequiredFeature(vkt::Feature::tileMemoryHeap);
    AddRequiredFeature(vkt::Feature::tileProperties);
    RETURN_IF_SKIP(Init());

    VkPhysicalDeviceMemoryProperties memory_info;
    vk::GetPhysicalDeviceMemoryProperties(Gpu(), &memory_info);
    uint64_t max_tile_memory_heap_size = 0;

    uint32_t i = 0;
    for (; i < memory_info.memoryHeapCount; i++) {
        if (memory_info.memoryHeaps[i].flags & VK_MEMORY_HEAP_TILE_MEMORY_BIT_QCOM) {
            max_tile_memory_heap_size = std::max(memory_info.memoryHeaps[i].size, max_tile_memory_heap_size);
        }
    }

    if (max_tile_memory_heap_size == 0xFFFFFFFFFFFFFFFF) {
        GTEST_SKIP() << "Tile Memory heap exposes max 64 bit value.";
    }

    VkTilePropertiesQCOM tile_properties = vku::InitStructHelper();
    VkTileMemorySizeInfoQCOM tile_memory_size_info = vku::InitStructHelper();
    VkRenderingAttachmentInfo color_attachment = vku::InitStructHelper();
    color_attachment.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

    tile_memory_size_info.size = max_tile_memory_heap_size + 1;

    VkRenderingInfo begin_rendering_info = vku::InitStructHelper(&tile_memory_size_info);
    begin_rendering_info.colorAttachmentCount = 1;
    begin_rendering_info.pColorAttachments = &color_attachment;
    begin_rendering_info.layerCount = 1;
    begin_rendering_info.renderArea = {{0, 0}, {1, 1}};

    m_errorMonitor->SetDesiredError("VUID-VkTileMemorySizeInfoQCOM-size-10729");
    vk::GetDynamicRenderingTilePropertiesQCOM(device(), &begin_rendering_info, &tile_properties);
    m_errorMonitor->VerifyFound();

    RenderPass2SingleSubpass rp2(*this);
    rp2.AddAttachmentDescription(VK_FORMAT_R8_UINT);

    m_errorMonitor->SetDesiredError("VUID-VkTileMemorySizeInfoQCOM-size-10729");
    rp2.CreateRenderPass(&tile_memory_size_info);
    m_errorMonitor->VerifyFound();

    RenderPassSingleSubpass rp(*this);
    rp.AddAttachmentDescription(VK_FORMAT_R8_UINT);

    m_errorMonitor->SetDesiredError("VUID-VkTileMemorySizeInfoQCOM-size-10729");
    rp.CreateRenderPass(&tile_memory_size_info);
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeTileMemoryHeap, ResolveAttachment) {
    TEST_DESCRIPTION("Resolve to a Tile Memory Attachment.");
    SetTargetApiVersion(VK_API_VERSION_1_2);
    AddRequiredExtensions(VK_QCOM_TILE_MEMORY_HEAP_EXTENSION_NAME);
    AddRequiredExtensions(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME);
    AddRequiredFeature(vkt::Feature::tileMemoryHeap);
    AddRequiredFeature(vkt::Feature::dynamicRendering);
    AddRequiredFeature(vkt::Feature::imagelessFramebuffer);
    RETURN_IF_SKIP(Init());

    VkImageCreateInfo image_create_info = vku::InitStructHelper();
    image_create_info.imageType = VK_IMAGE_TYPE_2D;
    image_create_info.format = VK_FORMAT_R8G8B8A8_UNORM;
    image_create_info.extent = {32, 32, 1};
    image_create_info.mipLevels = 1;
    image_create_info.arrayLayers = 1;
    image_create_info.samples = VK_SAMPLE_COUNT_4_BIT;
    image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
    image_create_info.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
    vkt::Image msaa_image(*m_device, image_create_info, vkt::set_layout);
    vkt::ImageView msaa_image_view = msaa_image.CreateView();

    auto tile_image_create_info = vkt::Image::ImageCreateInfo2D(
        32u, 32u, 1u, 1u, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TILE_MEMORY_BIT_QCOM);
    vkt::Image tile_memory_image(*m_device, tile_image_create_info, vkt::no_mem);

    VkImageMemoryRequirementsInfo2 image_info = vku::InitStructHelper();
    VkTileMemoryRequirementsQCOM tile_mem_reqs = vku::InitStructHelper();
    VkMemoryRequirements2 image_reqs = vku::InitStructHelper(&tile_mem_reqs);
    image_info.image = tile_memory_image;
    vk::GetImageMemoryRequirements2(device(), &image_info, &image_reqs);

    if (tile_mem_reqs.size == 0) {
        GTEST_SKIP() << "Image not eligible to be bound with Tile Memory.";
    }

    // Find a memory configuration for the Tile Memory Image, otherwise exit
    VkMemoryAllocateInfo alloc_info = vku::InitStructHelper();
    alloc_info.memoryTypeIndex = 0;
    alloc_info.allocationSize = tile_mem_reqs.size;
    bool pass = m_device->Physical().SetMemoryType(image_reqs.memoryRequirements.memoryTypeBits, &alloc_info,
                                                   VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0, VK_MEMORY_HEAP_TILE_MEMORY_BIT_QCOM);

    if (!pass) {
        GTEST_SKIP() << "Could not find an eligible Tile Memory Type.";
    }

    vkt::DeviceMemory image_memory(*m_device, alloc_info);
    vk::BindImageMemory(device(), tile_memory_image, image_memory, 0);

    vkt::ImageView resolve_image_view = tile_memory_image.CreateView();

    VkRenderingAttachmentInfo color_attachment = vku::InitStructHelper();
    color_attachment.imageView = msaa_image_view;
    color_attachment.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
    color_attachment.resolveMode = VK_RESOLVE_MODE_AVERAGE_BIT;
    color_attachment.resolveImageLayout = VK_IMAGE_LAYOUT_GENERAL;
    color_attachment.resolveImageView = resolve_image_view;

    VkRenderingInfo begin_rendering_info = vku::InitStructHelper();
    begin_rendering_info.colorAttachmentCount = 1;
    begin_rendering_info.pColorAttachments = &color_attachment;
    begin_rendering_info.layerCount = 1;
    begin_rendering_info.renderArea = {{0, 0}, {1, 1}};

    m_command_buffer.Begin();
    m_errorMonitor->SetDesiredError("VUID-VkRenderingAttachmentInfo-resolveImageView-10728");
    m_command_buffer.BeginRendering(begin_rendering_info);
    m_errorMonitor->VerifyFound();
    m_command_buffer.End();
    m_command_buffer.Reset();

    VkAttachmentReference2 msaa_attachment_ref = vku::InitStructHelper();
    msaa_attachment_ref.layout = VK_IMAGE_LAYOUT_GENERAL;
    msaa_attachment_ref.attachment = 0;

    VkAttachmentReference2 resolve_attachment_ref = vku::InitStructHelper();
    resolve_attachment_ref.layout = VK_IMAGE_LAYOUT_GENERAL;
    resolve_attachment_ref.attachment = 1;

    VkSubpassDescription2 subpass = vku::InitStructHelper();
    subpass.colorAttachmentCount = 1;
    subpass.pColorAttachments = &msaa_attachment_ref;
    subpass.pResolveAttachments = &resolve_attachment_ref;

    VkAttachmentDescription2 attach_desc[2] = {};
    attach_desc[0] = vku::InitStructHelper();
    attach_desc[0].format = VK_FORMAT_R8G8B8A8_UNORM;
    attach_desc[0].samples = VK_SAMPLE_COUNT_4_BIT;
    attach_desc[0].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
    attach_desc[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    attach_desc[0].finalLayout = VK_IMAGE_LAYOUT_GENERAL;
    attach_desc[1] = vku::InitStructHelper();
    attach_desc[1].format = VK_FORMAT_R8G8B8A8_UNORM;
    attach_desc[1].samples = VK_SAMPLE_COUNT_1_BIT;
    attach_desc[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
    attach_desc[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    attach_desc[1].finalLayout = VK_IMAGE_LAYOUT_GENERAL;

    VkRenderPassCreateInfo2 rp = vku::InitStructHelper();
    rp.subpassCount = 1;
    rp.pSubpasses = &subpass;
    rp.attachmentCount = 2;
    rp.pAttachments = &attach_desc[0];
    vkt::RenderPass test_rp(*m_device, rp);

    VkImageView views[2] = {};
    views[0] = msaa_image_view;
    views[1] = resolve_image_view;
    auto frame_buffer_create_info =
        vku::InitStruct<VkFramebufferCreateInfo>(nullptr, 0u, test_rp.handle(), 2u, &views[0], 32u, 32u, 1u);
    VkFramebuffer fb;

    m_errorMonitor->SetDesiredError("VUID-VkFramebufferCreateInfo-pAttachments-12327");
    vk::CreateFramebuffer(device(), &frame_buffer_create_info, nullptr, &fb);
    m_errorMonitor->VerifyFound();

    // Imageless FBO
    VkFormat framebuffer_attachment_formats = VK_FORMAT_R8G8B8A8_UNORM;
    VkFramebufferAttachmentImageInfo framebuffer_attachment_image_info[2] = {};
    framebuffer_attachment_image_info[0] = vku::InitStructHelper();
    framebuffer_attachment_image_info[0].usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
    framebuffer_attachment_image_info[0].width = 32;
    framebuffer_attachment_image_info[0].height = 32;
    framebuffer_attachment_image_info[0].layerCount = 1;
    framebuffer_attachment_image_info[0].viewFormatCount = 1;
    framebuffer_attachment_image_info[0].pViewFormats = &framebuffer_attachment_formats;
    framebuffer_attachment_image_info[1] = vku::InitStructHelper();
    framebuffer_attachment_image_info[1].usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TILE_MEMORY_BIT_QCOM;
    framebuffer_attachment_image_info[1].width = 32;
    framebuffer_attachment_image_info[1].height = 32;
    framebuffer_attachment_image_info[1].layerCount = 1;
    framebuffer_attachment_image_info[1].viewFormatCount = 1;
    framebuffer_attachment_image_info[1].pViewFormats = &framebuffer_attachment_formats;
    VkFramebufferAttachmentsCreateInfo framebuffer_attachment_ci = vku::InitStructHelper();
    framebuffer_attachment_ci.attachmentImageInfoCount = 2;
    framebuffer_attachment_ci.pAttachmentImageInfos = &framebuffer_attachment_image_info[0];

    auto imageless_fbci = vku::InitStruct<VkFramebufferCreateInfo>(nullptr, 0u, test_rp.handle(), 2u, nullptr, 32u, 32u, 1u);
    imageless_fbci.pNext = &framebuffer_attachment_ci;
    imageless_fbci.flags = VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT;
    vkt::Framebuffer imageless_fb(*m_device, imageless_fbci);

    VkImageView image_views[2] = {msaa_image_view, resolve_image_view};
    VkRenderPassAttachmentBeginInfo rp_attachment_begin_info = vku::InitStructHelper();
    rp_attachment_begin_info.attachmentCount = 2;
    rp_attachment_begin_info.pAttachments = &image_views[0];
    VkRenderPassBeginInfo rp_begin_info = vku::InitStructHelper(&rp_attachment_begin_info);
    rp_begin_info.renderPass = test_rp.handle();
    rp_begin_info.renderArea.extent = {32, 32};
    rp_begin_info.framebuffer = imageless_fb;

    m_command_buffer.Begin();
    m_errorMonitor->SetDesiredError("VUID-VkRenderPassBeginInfo-framebuffer-12328");
    m_command_buffer.BeginRenderPass(rp_begin_info);
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeTileMemoryHeap, BufferDescriptorMismatchTileMemory) {
    TEST_DESCRIPTION("Create tile memory storage buffer and use it within a dispatch.");
    SetTargetApiVersion(VK_API_VERSION_1_1);

    AddRequiredExtensions(VK_QCOM_TILE_MEMORY_HEAP_EXTENSION_NAME);
    AddRequiredFeature(vkt::Feature::tileMemoryHeap);

    RETURN_IF_SKIP(Init());

    // Tile Memory Buffer
    vkt::Buffer buffer(*m_device,
                       vkt::Buffer::CreateInfo(4096, VK_BUFFER_USAGE_2_TILE_MEMORY_BIT_QCOM | VK_BUFFER_USAGE_2_STORAGE_BUFFER_BIT),
                       vkt::no_mem);
    // Tile Memory Texel Buffer
    vkt::Buffer texel_buffer(
        *m_device,
        vkt::Buffer::CreateInfo(4096, VK_BUFFER_USAGE_2_TILE_MEMORY_BIT_QCOM | VK_BUFFER_USAGE_2_STORAGE_TEXEL_BUFFER_BIT),
        vkt::no_mem);

    // Query Tile Memory Buffer Requirements
    VkDeviceSize buffer_size = 0;
    VkDeviceSize texel_buffer_size = 0;
    VkBufferMemoryRequirementsInfo2 buffer_info = vku::InitStructHelper();
    VkTileMemoryRequirementsQCOM tile_mem_reqs = vku::InitStructHelper();
    VkMemoryRequirements2 buffer_reqs = vku::InitStructHelper(&tile_mem_reqs);
    buffer_info.buffer = buffer;
    vk::GetBufferMemoryRequirements2(device(), &buffer_info, &buffer_reqs);

    if (tile_mem_reqs.size == 0) {
        GTEST_SKIP() << "Buffer not eligible to be bound with Tile Memory.";
    }

    buffer_size = tile_mem_reqs.size;

    buffer_info.buffer = texel_buffer;
    vk::GetBufferMemoryRequirements2(device(), &buffer_info, &buffer_reqs);

    if (tile_mem_reqs.size == 0) {
        GTEST_SKIP() << "Texel Buffer not eligible to be bound with Tile Memory.";
    }

    buffer_size = AlignToMultiple(buffer_size, tile_mem_reqs.alignment);
    texel_buffer_size = tile_mem_reqs.size;

    // Find a memory configuration for the Tile Memory Resources, otherwise exit
    VkMemoryAllocateInfo alloc_info = vku::InitStructHelper();
    alloc_info.memoryTypeIndex = 0;
    alloc_info.allocationSize = buffer_size + texel_buffer_size;
    bool pass = m_device->Physical().SetMemoryType(buffer_reqs.memoryRequirements.memoryTypeBits, &alloc_info,
                                                   VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0, VK_MEMORY_HEAP_TILE_MEMORY_BIT_QCOM);

    if (!pass) {
        GTEST_SKIP() << "Could not find an eligible Tile Memory Type.";
    }

    // Bind Tile Memory to Buffer
    vkt::DeviceMemory buffer_memory(*m_device, alloc_info);
    vk::BindBufferMemory(device(), buffer, buffer_memory, 0);

    // Bind Tile Memory to Texel Buffer with offset
    vk::BindBufferMemory(device(), texel_buffer, buffer_memory, buffer_size);
    VkBufferUsageFlags2CreateInfo buffer_usage_flags = vku::InitStructHelper();
    buffer_usage_flags.usage = VK_BUFFER_USAGE_2_STORAGE_TEXEL_BUFFER_BIT;

    VkBufferViewCreateInfo bvci = vku::InitStructHelper(&buffer_usage_flags);
    bvci.buffer = texel_buffer;
    bvci.format = VK_FORMAT_R32_SFLOAT;
    bvci.range = VK_WHOLE_SIZE;
    vkt::BufferView texel_buffer_view(*m_device, bvci);

    // Create Compute Shader to write to Tile Memory Buffer
    const char* cs_source = R"glsl(
        #version 450
        layout(set = 0, binding = 0) buffer ssbo { float tileMemBuffer; };
        layout(set = 0, binding = 1, r32f) uniform imageBuffer s_buffer;
        void main() {
           tileMemBuffer = 1.0f;
           imageStore(s_buffer, 0, vec4(0.5f));
        }
    )glsl";

    CreateComputePipelineHelper pipe(*this);
    pipe.dsl_bindings_ = {{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr},
                          {1, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}};
    pipe.cs_ = VkShaderObj(*m_device, cs_source, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_1);
    pipe.CreateComputePipeline();

    pipe.descriptor_set_.WriteDescriptorBufferInfo(0, buffer, 0, 4096, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
    pipe.descriptor_set_.WriteDescriptorBufferView(1, texel_buffer_view, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER);
    pipe.descriptor_set_.UpdateDescriptorSets();

    m_command_buffer.Begin();
    m_errorMonitor->SetDesiredError("VUID-vkCmdDispatch-commandBuffer-10746", 2);
    vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe.pipeline_layout_, 0, 1,
                              &pipe.descriptor_set_.set_, 0, nullptr);
    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe);
    vk::CmdDispatch(m_command_buffer, 1, 1, 1);
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeTileMemoryHeap, ImageDescriptorMismatchTileMemory) {
    TEST_DESCRIPTION("Create tile memory storage buffer and use it within a dispatch.");
    SetTargetApiVersion(VK_API_VERSION_1_1);

    AddRequiredExtensions(VK_QCOM_TILE_MEMORY_HEAP_EXTENSION_NAME);
    AddRequiredFeature(vkt::Feature::tileMemoryHeap);
    RETURN_IF_SKIP(Init());

    // Create a Tile Memory Image
    auto image_create_info = vkt::Image::ImageCreateInfo2D(32u, 32u, 1u, 1u, VK_FORMAT_R8G8B8A8_UNORM,
                                                           VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TILE_MEMORY_BIT_QCOM);
    vkt::Image image(*m_device, image_create_info, vkt::no_mem);

    // Query Tile Memory Image Requirements
    VkImageMemoryRequirementsInfo2 image_info = vku::InitStructHelper();
    VkTileMemoryRequirementsQCOM tile_mem_reqs = vku::InitStructHelper();
    VkMemoryRequirements2 image_reqs = vku::InitStructHelper(&tile_mem_reqs);
    image_info.image = image;
    vk::GetImageMemoryRequirements2(device(), &image_info, &image_reqs);

    if (tile_mem_reqs.size == 0) {
        GTEST_SKIP() << "Image not eligible to be bound with Tile Memory.";
    }

    // Find a memory configuration for the Tile Memory Image, otherwise exit
    VkMemoryAllocateInfo alloc_info = vku::InitStructHelper();
    alloc_info.memoryTypeIndex = 0;
    alloc_info.allocationSize = tile_mem_reqs.size;
    bool pass = m_device->Physical().SetMemoryType(image_reqs.memoryRequirements.memoryTypeBits, &alloc_info,
                                                   VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0, VK_MEMORY_HEAP_TILE_MEMORY_BIT_QCOM);

    if (!pass) {
        GTEST_SKIP() << "Could not find an eligible Tile Memory Type.";
    }

    vkt::DeviceMemory image_memory(*m_device, alloc_info);
    vk::BindImageMemory(device(), image, image_memory, 0);
    vkt::ImageView image_view = image.CreateView();

    OneOffDescriptorSet descriptor_set(m_device, {
                                                     {0, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_ALL, nullptr},
                                                 });
    descriptor_set.WriteDescriptorImageInfo(0, image_view, VK_NULL_HANDLE, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
                                            VK_IMAGE_LAYOUT_GENERAL);
    descriptor_set.UpdateDescriptorSets();

    const char* cs_source = R"glsl(
        #version 450
        layout(set=0, binding=0, rgba8) uniform readonly image2D storage_image;
        void main(){
            vec4 data = imageLoad(storage_image, ivec2(0));
        }
    )glsl";

    CreateComputePipelineHelper pipe(*this);
    pipe.cs_ = VkShaderObj(*m_device, cs_source, VK_SHADER_STAGE_COMPUTE_BIT);
    pipe.pipeline_layout_ = vkt::PipelineLayout(*m_device, {&descriptor_set.layout_});
    pipe.CreateComputePipeline();

    m_command_buffer.Begin();
    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe);
    vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe.pipeline_layout_, 0, 1, &descriptor_set.set_,
                              0, nullptr);
    m_errorMonitor->SetDesiredError("VUID-vkCmdDispatch-commandBuffer-10746");
    vk::CmdDispatch(m_command_buffer, 1, 1, 1);
    m_errorMonitor->VerifyFound();
    m_command_buffer.End();
}