/*
 * Copyright (c) 2025-2026 The Khronos Group Inc.
 * Copyright (C) 2025 Arm Limited.
 *
 * 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 "descriptor_helper.h"
#include "data_graph_objects.h"
#include "generated/pnext_chain_extraction.h"
#include "layers/utils/vk_struct_compare.h"
#include <vector>

class NegativeDataGraph : public DataGraphTest {};

TEST_F(NegativeDataGraph, CreateDataGraphPipelinesFeatureNotEnabled) {
    TEST_DESCRIPTION("Try to create a DataGraphPipeline when the dataGraph feature is not enabled");
    // add all the requirements of InitBasicDataGraph except dataGraph
    SetTargetApiVersion(VK_API_VERSION_1_4);
    AddRequiredExtensions(VK_ARM_TENSORS_EXTENSION_NAME);
    AddRequiredExtensions(VK_ARM_DATA_GRAPH_EXTENSION_NAME);
    AddRequiredFeature(vkt::Feature::tensors);
    AddRequiredFeature(vkt::Feature::dataGraphShaderModule);
    AddRequiredFeature(vkt::Feature::shaderTensorAccess);
    AddRequiredFeature(vkt::Feature::vulkanMemoryModel);
    AddRequiredFeature(vkt::Feature::shaderInt8);
    RETURN_IF_SKIP(Init());

    // this error is generated by the spirv verification, which runs already in the constructor
    m_errorMonitor->SetAllowedFailureMsg("VUID-VkShaderModuleCreateInfo-pCode-08740");
    vkt::dg::DataGraphPipelineHelper pipeline(*this);

    m_errorMonitor->SetDesiredError("VUID-vkCreateDataGraphPipelinesARM-dataGraph-09760");
    pipeline.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, CreateDataGraphPipelinesDeferredOperationNotNull) {
    TEST_DESCRIPTION("Try to create a DataGraphPipeline when deferredOperation is not VK_NULL_HANDLE");
    AddRequiredExtensions(VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME);
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline_helper(*this);
    VkDeferredOperationKHR deferred_operation;
    vk::CreateDeferredOperationKHR(*m_device, nullptr, &deferred_operation);
    VkPipeline pipeline;
    m_errorMonitor->SetDesiredError("VUID-vkCreateDataGraphPipelinesARM-deferredOperation-09761");
    vk::CreateDataGraphPipelinesARM(*m_device, deferred_operation, VK_NULL_HANDLE, 1, &pipeline_helper.pipeline_ci_, nullptr,
                                    &pipeline);
    m_errorMonitor->VerifyFound();
    vk::DestroyDeferredOperationKHR(*m_device, deferred_operation, nullptr);
}

TEST_F(NegativeDataGraph, CreateDataGraphPipelinesDeferredOperationWrongFlags) {
    TEST_DESCRIPTION("Try to create a DataGraphPipeline with deferredOperation and invalid pipeline flags");
    AddRequiredExtensions(VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME);
    AddRequiredFeature(vkt::Feature::pipelineCreationCacheControl);
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline_helper(*this);
    VkDeferredOperationKHR deferred_operation;
    vk::CreateDeferredOperationKHR(*m_device, nullptr, &deferred_operation);
    VkPipeline pipeline;

    // set the wrong flags in the pipeline create info
    pipeline_helper.pipeline_ci_.flags |= VK_PIPELINE_CREATE_EARLY_RETURN_ON_FAILURE_BIT;

    m_errorMonitor->SetDesiredError("VUID-vkCreateDataGraphPipelinesARM-deferredOperation-09916");
    // this is also triggered because deferredOperations currently MUST be NULL
    m_errorMonitor->SetDesiredError("VUID-vkCreateDataGraphPipelinesARM-deferredOperation-09761");
    vk::CreateDataGraphPipelinesARM(*m_device, deferred_operation, VK_NULL_HANDLE, 1, &pipeline_helper.pipeline_ci_, nullptr,
                                    &pipeline);
    m_errorMonitor->VerifyFound();
    vk::DestroyDeferredOperationKHR(*m_device, deferred_operation, nullptr);
}

TEST_F(NegativeDataGraph, CreateDataGraphPipelinesInvalidFlags) {
    TEST_DESCRIPTION("Try to create a DataGraphPipeline with invalid flags in create_info");
    RETURN_IF_SKIP(InitBasicDataGraph());

    auto set_info = [](vkt::dg::DataGraphPipelineHelper& pipeline) {
        pipeline.pipeline_ci_.flags = VK_PIPELINE_CREATE_2_VIEW_INDEX_FROM_DEVICE_INDEX_BIT_KHR;
    };
    vkt::dg::DataGraphPipelineHelper::OneshotTest(*this, set_info, 0, "VUID-VkDataGraphPipelineCreateInfoARM-flags-09764");
}

TEST_F(NegativeDataGraph, CreateDataGraphPipelinesNoProtectedAccessButFeatureNotEnabled) {
    TEST_DESCRIPTION(
        "Try to create a DataGraphPipeline where flags include VK_PIPELINE_CREATE_2_NO_PROTECTED_ACCESS_BIT_EXT but "
        "pipelineProtectedAccess is not enabled");
    RETURN_IF_SKIP(InitBasicDataGraph());

    auto set_info = [](vkt::dg::DataGraphPipelineHelper& pipeline) {
        pipeline.pipeline_ci_.flags = VK_PIPELINE_CREATE_2_NO_PROTECTED_ACCESS_BIT_EXT;
    };
    vkt::dg::DataGraphPipelineHelper::OneshotTest(*this, set_info, 0,
                                                  "VUID-VkDataGraphPipelineCreateInfoARM-pipelineProtectedAccess-09772");
}

TEST_F(NegativeDataGraph, CreateDataGraphPipelinesProtectedAccessOnlyButFeatureNotEnabled) {
    TEST_DESCRIPTION(
        "Try to create a DataGraphPipeline where flags include VK_PIPELINE_CREATE_2_PROTECTED_ACCESS_ONLY_BIT_EXT but "
        "pipelineProtectedAccess is not enabled");
    RETURN_IF_SKIP(InitBasicDataGraph());

    auto set_info = [](vkt::dg::DataGraphPipelineHelper& pipeline) {
        pipeline.pipeline_ci_.flags = VK_PIPELINE_CREATE_2_PROTECTED_ACCESS_ONLY_BIT_EXT;
    };
    vkt::dg::DataGraphPipelineHelper::OneshotTest(*this, set_info, 0,
                                                  "VUID-VkDataGraphPipelineCreateInfoARM-pipelineProtectedAccess-09772");
}

TEST_F(NegativeDataGraph, CreateDataGraphPipelinesBothProtectedAccessBits) {
    TEST_DESCRIPTION(
        "Try to create a DataGraphPipeline where flags include both VK_PIPELINE_CREATE_2_NO_PROTECTED_ACCESS_BIT_EXT and "
        "VK_PIPELINE_CREATE_2_PROTECTED_ACCESS_ONLY_BIT_EXT");
    AddRequiredExtensions(VK_EXT_PIPELINE_PROTECTED_ACCESS_EXTENSION_NAME);
    AddRequiredFeature(vkt::Feature::pipelineProtectedAccess);
    RETURN_IF_SKIP(InitBasicDataGraph());

    auto set_info = [](vkt::dg::DataGraphPipelineHelper& pipeline) {
        pipeline.pipeline_ci_.flags =
            VK_PIPELINE_CREATE_2_NO_PROTECTED_ACCESS_BIT_EXT | VK_PIPELINE_CREATE_2_PROTECTED_ACCESS_ONLY_BIT_EXT;
    };
    vkt::dg::DataGraphPipelineHelper::OneshotTest(*this, set_info, 0, "VUID-VkDataGraphPipelineCreateInfoARM-flags-09773");
}

TEST_F(NegativeDataGraph, CreateDataGraphPipelinesStageCreationFeedbackCountNotZero) {
    TEST_DESCRIPTION(
        "Try to create a DataGraphPipeline where pNext contains a VkPipelineCreationFeedbackCreateInfo structure but the "
        "pipelineStageCreationFeedbackCount is not 0");
    RETURN_IF_SKIP(InitBasicDataGraph());

    VkPipelineCreationFeedback creation_feedback;
    VkPipelineCreationFeedbackCreateInfo creation_feedback_create_info = vku::InitStructHelper();
    creation_feedback_create_info.pPipelineCreationFeedback = &creation_feedback;
    creation_feedback_create_info.pipelineStageCreationFeedbackCount = 1;
    creation_feedback_create_info.pPipelineStageCreationFeedbacks = &creation_feedback;
    auto set_info = [&](vkt::dg::DataGraphPipelineHelper& pipeline) {
        pipeline.shader_module_ci_.pNext = &creation_feedback_create_info;
    };
    vkt::dg::DataGraphPipelineHelper::OneshotTest(*this, set_info, 0, "VUID-VkDataGraphPipelineCreateInfoARM-pNext-09804");
}

TEST_F(NegativeDataGraph, CreateDataGraphPipelinesPushConstantCountNotZero) {
    TEST_DESCRIPTION(
        "Try to create a DataGraphPipeline where the layout was created with a non-zero pushConstantRangeCount and non-NULL "
        "pushConstRange");
    RETURN_IF_SKIP(InitBasicDataGraph());

    std::vector<VkPushConstantRange> pcr = {{VK_SHADER_STAGE_ALL, 0, sizeof(uint32_t)}};
    auto set_info = [&](vkt::dg::DataGraphPipelineHelper& pipeline) { pipeline.CreatePipelineLayout(pcr); };
    vkt::dg::DataGraphPipelineHelper::OneshotTest(*this, set_info, 0, "VUID-VkDataGraphPipelineCreateInfoARM-layout-09767");
}

TEST_F(NegativeDataGraph, CreateDataGraphPipelinesUpdateAfterBindFeatureNotEnabled) {
    TEST_DESCRIPTION(
        "Try to create a DataGraphPipeline where the descriptorSetLayout used sets the BIND_AFTER_USE_BIT but the "
        "dataGraphUpdateAfterBind is not enabled");
    RETURN_IF_SKIP(InitBasicDataGraph());

    auto set_info = [&](vkt::dg::DataGraphPipelineHelper& pipeline) {
        pipeline.descriptor_set_.reset(new OneOffDescriptorSet(pipeline.device_, pipeline.descriptor_set_layout_bindings_,
                                                               VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT, nullptr,
                                                               VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT));
        pipeline.CreatePipelineLayout();
    };
    vkt::dg::DataGraphPipelineHelper::OneshotTest(*this, set_info, 0,
                                                  "VUID-VkDataGraphPipelineCreateInfoARM-dataGraphUpdateAfterBind-09768");
}

TEST_F(NegativeDataGraph, CreateDataGraphPipelinesMutableDescriptor) {
    TEST_DESCRIPTION("Try to create a DataGraphPipeline with a MUTABLE descriptor (not allowed in datagraph)");
    AddRequiredExtensions(VK_EXT_MUTABLE_DESCRIPTOR_TYPE_EXTENSION_NAME);
    AddRequiredFeature(vkt::Feature::mutableDescriptorType);
    RETURN_IF_SKIP(InitBasicDataGraph());

    VkDescriptorType types[] = {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE};
    VkMutableDescriptorTypeListEXT mutable_descriptor_type_list = {1, types};

    VkMutableDescriptorTypeCreateInfoEXT mutable_descriptor_info = vku::InitStructHelper();
    mutable_descriptor_info.mutableDescriptorTypeListCount = 1;
    mutable_descriptor_info.pMutableDescriptorTypeLists = &mutable_descriptor_type_list;

    auto set_info = [&](vkt::dg::DataGraphPipelineHelper& pipeline) {
        pipeline.descriptor_set_layout_bindings_[0].descriptorType =
            VK_DESCRIPTOR_TYPE_MUTABLE_EXT;  // the pipeline sets this to tensor
        pipeline.descriptor_set_.reset(
            new OneOffDescriptorSet(pipeline.device_, pipeline.descriptor_set_layout_bindings_, 0, &mutable_descriptor_info));
        pipeline.CreatePipelineLayout();
    };
    vkt::dg::DataGraphPipelineHelper::OneshotTest(*this, set_info, 0, "VUID-VkDataGraphPipelineCreateInfoARM-pSetLayouts-09770");
}

TEST_F(NegativeDataGraph, CreateDataGraphPipelinesEarlyReturnFlagCacheControlNotEnabled) {
    TEST_DESCRIPTION(
        "Try to create a DataGraphPipeline when flags contains VK_PIPELINE_CREATE_2_EARLY_RETURN_ON_FAILURE_BIT_KHR but the "
        "pipelineCreationCacheControl feature is not enabled");
    RETURN_IF_SKIP(InitBasicDataGraph());

    auto set_info = [](vkt::dg::DataGraphPipelineHelper& pipeline) {
        pipeline.pipeline_ci_.flags = VK_PIPELINE_CREATE_2_EARLY_RETURN_ON_FAILURE_BIT_KHR;
    };
    vkt::dg::DataGraphPipelineHelper::OneshotTest(*this, set_info, 0,
                                                  "VUID-VkDataGraphPipelineCreateInfoARM-pipelineCreationCacheControl-09871");
}

TEST_F(NegativeDataGraph, CreateDataGraphPipelinesFailOnPipelineCompileFlagCacheControlNotEnabled) {
    TEST_DESCRIPTION(
        "Try to create a DataGraphPipeline when flags contains VK_PIPELINE_CREATE_2_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT_KHR but "
        "the pipelineCreationCacheControl feature is not enabled");
    RETURN_IF_SKIP(InitBasicDataGraph());

    auto set_info = [](vkt::dg::DataGraphPipelineHelper& pipeline) {
        pipeline.pipeline_ci_.flags = VK_PIPELINE_CREATE_2_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT_KHR;
    };
    vkt::dg::DataGraphPipelineHelper::OneshotTest(*this, set_info, 0,
                                                  "VUID-VkDataGraphPipelineCreateInfoARM-pipelineCreationCacheControl-09871");
}

TEST_F(NegativeDataGraph, CreateDataGraphPipelinesTypeMismatch) {
    TEST_DESCRIPTION(
        "Try to create a DataGraphPipeline where the descriptor slot in layout does not match the resource item used in the Shader "
        "Module");
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::HelperParameters params;
    params.desc_type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;  // should be tensor
    vkt::dg::DataGraphPipelineHelper pipeline(*this, params);
    // 2 tensors, 2 errors
    m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineCreateInfoARM-layout-09769");
    m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineCreateInfoARM-layout-09769");
    pipeline.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

static void InitDefaultComputePipeline(CreateComputePipelineHelper& pipeline, VkRenderFramework* framework) {
    std::vector<VkDescriptorSetLayoutBinding> bindings = {
        {0, VK_DESCRIPTOR_TYPE_TENSOR_ARM, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr},
        {1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr}};

    pipeline.cs_ = VkShaderObj::CreateFromGLSL(framework, kMinimalTensorGlsl, VK_SHADER_STAGE_COMPUTE_BIT);
    pipeline.dsl_bindings_.resize(bindings.size());
    memcpy(pipeline.dsl_bindings_.data(), bindings.data(), bindings.size() * sizeof(VkDescriptorSetLayoutBinding));
    pipeline.CreateComputePipeline();
}

TEST_F(NegativeDataGraph, GetDataGraphPipelinePropertiesPipelineNotCreatedWithCreateDataGraphPipeline) {
    TEST_DESCRIPTION(
        "Try to get the datagraph pipeline properties for a pipeline not created with vkCreateDataGraphPipelinesARM (i.e. created "
        "with vkCreateComputePipeline)");
    RETURN_IF_SKIP(InitBasicDataGraph());

    CreateComputePipelineHelper pipeline(*m_device);
    InitDefaultComputePipeline(pipeline, this);
    VkDataGraphPipelineInfoARM pipeline_info = vku::InitStructHelper();
    pipeline_info.dataGraphPipeline = pipeline;

    // query with `pData` null, to get back the required `dataSize`. Enough to trigger the VUID
    VkDataGraphPipelinePropertyQueryResultARM query_result;
    query_result = vku::InitStructHelper();
    query_result.property = VK_DATA_GRAPH_PIPELINE_PROPERTY_CREATION_LOG_ARM;
    query_result.pData = nullptr;
    query_result.dataSize = 0;
    uint32_t prop_count = 1;
    m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineInfoARM-dataGraphPipeline-09803");
    vk::GetDataGraphPipelinePropertiesARM(m_device->handle(), &pipeline_info, prop_count, &query_result);
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, GetDataGraphPipelinePropertiesDuplicatedProperty) {
    TEST_DESCRIPTION("Duplicate property in datagraph pipeline properties request");
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    pipeline.CreateDataGraphPipeline();

    // query with `pData` null, to get back the required `dataSize`. Enough to trigger the VUID
    VkDataGraphPipelineInfoARM pipeline_info = vku::InitStructHelper();
    pipeline_info.dataGraphPipeline = pipeline;
    std::array<VkDataGraphPipelinePropertyQueryResultARM, 3> query_results;
    // properties 0 and 2 are the same: error
    query_results[0] = vku::InitStructHelper();
    query_results[0].property = VK_DATA_GRAPH_PIPELINE_PROPERTY_CREATION_LOG_ARM;
    query_results[0].pData = nullptr;
    query_results[0].dataSize = 0;
    query_results[1] = vku::InitStructHelper();
    query_results[1].property = VK_DATA_GRAPH_PIPELINE_PROPERTY_IDENTIFIER_ARM;
    query_results[1].pData = nullptr;
    query_results[1].dataSize = 0;
    query_results[2] = vku::InitStructHelper();
    query_results[2].property = VK_DATA_GRAPH_PIPELINE_PROPERTY_CREATION_LOG_ARM;
    query_results[2].pData = nullptr;
    query_results[2].dataSize = 0;

    m_errorMonitor->SetDesiredError("VUID-vkGetDataGraphPipelinePropertiesARM-pProperties-09889");
    vk::GetDataGraphPipelinePropertiesARM(m_device->handle(), &pipeline_info, query_results.size(), query_results.data());
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, SessionCreateInfoInvalidGraphPipeline) {
    TEST_DESCRIPTION(
        "Try to create a DataGraphPipelineSession where the dataGraphPipeline member of VkDataGraphPipelineSessionCreateInfoARM "
        "was "
        "not created by vkCreateDataGraphPipelinesARM");
    RETURN_IF_SKIP(InitBasicDataGraph());

    CreateComputePipelineHelper pipeline(*m_device);
    InitDefaultComputePipeline(pipeline, this);

    VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
    session_ci.dataGraphPipeline = pipeline;

    VkDataGraphPipelineSessionARM session;
    m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineSessionCreateInfoARM-dataGraphPipeline-09781");
    vk::CreateDataGraphPipelineSessionARM(*m_device, &session_ci, nullptr, &session);
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, SessionCreateInfoProtectedMemoryFeatureNotEnabled) {
    TEST_DESCRIPTION(
        "Try to create a DataGraphPipelineSession where the flags member of VkDataGraphPipelineSessionCreateInfoARM contains "
        "VK_DATA_GRAPH_PIPELINE_SESSION_CREATE_PROTECTED_BIT_ARM but the protectedMemory feature is not enabled");
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    pipeline.CreateDataGraphPipeline();

    VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
    session_ci.dataGraphPipeline = pipeline;
    session_ci.flags = VK_DATA_GRAPH_PIPELINE_SESSION_CREATE_PROTECTED_BIT_ARM;

    VkDataGraphPipelineSessionARM session;
    m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineSessionCreateInfoARM-protectedMemory-09782");
    vk::CreateDataGraphPipelineSessionARM(*m_device, &session_ci, nullptr, &session);
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, SessionGetMemoryRequirementsBindPointNotGottenPrior) {
    TEST_DESCRIPTION(
        "Try to get the memory requirements for a session without a prior call to "
        "vkGetDataGraphPipelineSessionBindPointRequirementsARM");
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    pipeline.CreateDataGraphPipeline();

    VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
    session_ci.dataGraphPipeline = pipeline;

    vkt::DataGraphPipelineSession session(*m_device, session_ci, vkt::no_mem);

    VkDataGraphPipelineSessionMemoryRequirementsInfoARM session_mem_reqs = vku::InitStructHelper();
    session_mem_reqs.session = session;
    session_mem_reqs.bindPoint = VK_DATA_GRAPH_PIPELINE_SESSION_BIND_POINT_TRANSIENT_ARM;

    VkMemoryRequirements2 mem_reqs = vku::InitStructHelper();

    m_errorMonitor->SetDesiredError("VUID-vkGetDataGraphPipelineSessionMemoryRequirementsARM-bindPoint-09784");
    vk::GetDataGraphPipelineSessionMemoryRequirementsARM(*m_device, &session_mem_reqs, &mem_reqs);
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, BindSessionTwice) {
    TEST_DESCRIPTION("Try to create a bind DataGraphPipelineSession on the same bindpoint twice");
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    pipeline.CreateDataGraphPipeline();

    VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
    session_ci.dataGraphPipeline = pipeline;
    vkt::DataGraphPipelineSession session(*m_device, session_ci);

    std::vector<vkt::DeviceMemory> device_mem(session.BindPointsCount());
    session.AllocSessionMem(device_mem);

    auto session_bind_infos = InitSessionBindInfo(session, device_mem);
    vk::BindDataGraphPipelineSessionMemoryARM(*m_device, session_bind_infos.size(), session_bind_infos.data());

    // bind again to trigger error
    m_errorMonitor->SetDesiredError("VUID-VkBindDataGraphPipelineSessionMemoryInfoARM-session-09785");
    vk::BindDataGraphPipelineSessionMemoryARM(*m_device, session_bind_infos.size(), session_bind_infos.data());
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, BindSessionMemoryOffsetLargerThanSize) {
    TEST_DESCRIPTION(
        "Try to create a bind DataGraphPipelineSession to DeviceMemory at an offset which is larger than the allocated memory "
        "size");
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    pipeline.CreateDataGraphPipeline();

    VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
    session_ci.dataGraphPipeline = pipeline;
    vkt::DataGraphPipelineSession session(*m_device, session_ci);

    std::vector<vkt::DeviceMemory> device_mem(session.BindPointsCount());
    session.AllocSessionMem(device_mem);

    auto session_bind_infos = InitSessionBindInfo(session, device_mem);
    auto& mem_req = session.MemReqs()[0];
    session_bind_infos[0].memoryOffset = mem_req.memoryRequirements.size + 2 * mem_req.memoryRequirements.alignment;

    m_errorMonitor->SetDesiredError("VUID-VkBindDataGraphPipelineSessionMemoryInfoARM-memoryOffset-09787");
    vk::BindDataGraphPipelineSessionMemoryARM(*m_device, session_bind_infos.size(), session_bind_infos.data());
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, BindSessionMemoryInvalidMemoryBits) {
    TEST_DESCRIPTION(
        "Try to create a bind DataGraphPipelineSession on a memory type who's memoryTypeBits is incompatible with the required "
        "bits");
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    pipeline.CreateDataGraphPipeline();

    VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
    session_ci.dataGraphPipeline = pipeline;
    vkt::DataGraphPipelineSession session(*m_device, session_ci);

    std::vector<vkt::DeviceMemory> device_mem(session.BindPointsCount());
    auto& bind_point_reqs = session.BindPointReqs();
    auto& mem_reqs = session.MemReqs();
    for (uint32_t i = 0; i < bind_point_reqs.size(); i++) {
        if (bind_point_reqs[i].bindPointType != VK_DATA_GRAPH_PIPELINE_SESSION_BIND_POINT_TYPE_MEMORY_ARM) {
            continue;
        }

        VkMemoryAllocateInfo session_alloc_info = vku::InitStructHelper();
        session_alloc_info.allocationSize = mem_reqs[i].memoryRequirements.size;
        if (!m_device->Physical().SetMemoryType(~mem_reqs[i].memoryRequirements.memoryTypeBits, &session_alloc_info, 0)) {
            GTEST_SKIP() << "Memory type not found";
        }
        device_mem[i] = vkt::DeviceMemory(*m_device, session_alloc_info);
    }

    auto session_bind_infos = InitSessionBindInfo(session, device_mem);
    m_errorMonitor->SetDesiredError("VUID-VkBindDataGraphPipelineSessionMemoryInfoARM-memory-09788");
    vk::BindDataGraphPipelineSessionMemoryARM(*m_device, session_bind_infos.size(), session_bind_infos.data());
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, BindSessionMemoryInvalidOffsetAlignment) {
    TEST_DESCRIPTION(
        "Try to create a bind DataGraphPipelineSession at an offset which is not an integer multiple of the required alignment");
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    pipeline.CreateDataGraphPipeline();

    VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
    session_ci.dataGraphPipeline = pipeline;
    vkt::DataGraphPipelineSession session(*m_device, session_ci);

    std::vector<vkt::DeviceMemory> device_mem(session.BindPointsCount());
    session.AllocSessionMem(device_mem, false, 2);

    auto session_bind_infos = InitSessionBindInfo(session, device_mem);
    auto& mem_reqs = session.MemReqs();
    session_bind_infos[0].memoryOffset = mem_reqs[0].memoryRequirements.alignment - 1;

    m_errorMonitor->SetDesiredError("VUID-VkBindDataGraphPipelineSessionMemoryInfoARM-memoryOffset-09789");
    vk::BindDataGraphPipelineSessionMemoryARM(*m_device, session_bind_infos.size(), session_bind_infos.data());
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, BindSessionMemoryTooSmall) {
    TEST_DESCRIPTION("Try to create a bind DataGraphPipelineSession to device memory which is too small");
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    pipeline.CreateDataGraphPipeline();

    VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
    session_ci.dataGraphPipeline = pipeline;
    vkt::DataGraphPipelineSession session(*m_device, session_ci);

    std::vector<vkt::DeviceMemory> device_mem(session.BindPointsCount());
    session.AllocSessionMem(device_mem, false, 1, -1);
    auto session_bind_infos = InitSessionBindInfo(session, device_mem);

    m_errorMonitor->SetDesiredError("VUID-VkBindDataGraphPipelineSessionMemoryInfoARM-size-09790");
    vk::BindDataGraphPipelineSessionMemoryARM(*m_device, session_bind_infos.size(), session_bind_infos.data());
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, BindProtectedSessionToUnprotectedMemory) {
    TEST_DESCRIPTION("Try to bind a protected DataGraphPipelineSession to unprotected memory");
    AddRequiredFeature(vkt::Feature::protectedMemory);
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    pipeline.CreateDataGraphPipeline();

    VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
    session_ci.flags = VK_DATA_GRAPH_PIPELINE_SESSION_CREATE_PROTECTED_BIT_ARM;
    session_ci.dataGraphPipeline = pipeline;
    vkt::DataGraphPipelineSession session(*m_device, session_ci);

    // allocate unprotected memory
    std::vector<vkt::DeviceMemory> device_mem(session.BindPointsCount());
    auto& mem_reqs = session.MemReqs();
    for (uint32_t i = 0; i < mem_reqs.size(); i++) {
        VkMemoryAllocateInfo session_alloc_info = vku::InitStructHelper();
        session_alloc_info.allocationSize = mem_reqs[i].memoryRequirements.size;
        auto memoryTypeBits = mem_reqs[i].memoryRequirements.memoryTypeBits;
        if (!m_device->Physical().SetMemoryType(memoryTypeBits, &session_alloc_info, 0, VK_MEMORY_PROPERTY_PROTECTED_BIT)) {
            GTEST_SKIP() << "Memory type not found";
        }
        device_mem[i] = vkt::DeviceMemory(*m_device, session_alloc_info);
    }

    auto session_bind_infos = InitSessionBindInfo(session, device_mem);
    m_errorMonitor->SetDesiredError("VUID-VkBindDataGraphPipelineSessionMemoryInfoARM-session-09791");
    // we are using a different memory type from the session, which also causes an error with memoryBits
    m_errorMonitor->SetAllowedFailureMsg("VUID-VkBindDataGraphPipelineSessionMemoryInfoARM-memory-09788");
    vk::BindDataGraphPipelineSessionMemoryARM(*m_device, session_bind_infos.size(), session_bind_infos.data());
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, BindUnprotectedSessionToProtectedMemory) {
    TEST_DESCRIPTION("Try to bind an unprotected DataGraphPipelineSession to protected memory");
    AddRequiredFeature(vkt::Feature::protectedMemory);
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    pipeline.CreateDataGraphPipeline();

    VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
    session_ci.dataGraphPipeline = pipeline;
    vkt::DataGraphPipelineSession session(*m_device, session_ci);

    // allocate protected memory
    std::vector<vkt::DeviceMemory> device_mem(session.BindPointsCount());
    auto& mem_reqs = session.MemReqs();
    for (uint32_t i = 0; i < mem_reqs.size(); i++) {
        VkMemoryAllocateInfo session_alloc_info = vku::InitStructHelper();
        session_alloc_info.allocationSize = mem_reqs[i].memoryRequirements.size;
        auto memoryTypeBits = mem_reqs[i].memoryRequirements.memoryTypeBits;
        if (!m_device->Physical().SetMemoryType(memoryTypeBits, &session_alloc_info, VK_MEMORY_PROPERTY_PROTECTED_BIT)) {
            GTEST_SKIP() << "Memory type not found";
        }
        device_mem[i] = vkt::DeviceMemory(*m_device, session_alloc_info);
    }

    auto session_bind_infos = InitSessionBindInfo(session, device_mem);
    m_errorMonitor->SetDesiredError("VUID-VkBindDataGraphPipelineSessionMemoryInfoARM-session-09792");
    // we are using a different memory type from the session, which also causes an error with memoryBits
    m_errorMonitor->SetAllowedFailureMsg("VUID-VkBindDataGraphPipelineSessionMemoryInfoARM-memory-09788");
    vk::BindDataGraphPipelineSessionMemoryARM(*m_device, session_bind_infos.size(), session_bind_infos.data());
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, BindSessionObjectIndexTooLarge) {
    TEST_DESCRIPTION(
        "Try to bind a DataGraphPipelineSession when the resource index is larger than the numObjects for the bindPoint");
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    pipeline.CreateDataGraphPipeline();

    VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
    session_ci.dataGraphPipeline = pipeline;
    vkt::DataGraphPipelineSession session(*m_device, session_ci);

    std::vector<vkt::DeviceMemory> device_mem(session.BindPointsCount());
    session.AllocSessionMem(device_mem);

    auto session_bind_infos = InitSessionBindInfo(session, device_mem);
    session_bind_infos[0].objectIndex = session.BindPointReqs()[0].numObjects + 1;

    m_errorMonitor->SetDesiredError("VUID-VkBindDataGraphPipelineSessionMemoryInfoARM-objectIndex-09805");
    vk::BindDataGraphPipelineSessionMemoryARM(*m_device, session_bind_infos.size(), session_bind_infos.data());
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, BindSessionObjectWrongBindPoint) {
    TEST_DESCRIPTION("Try to bind a DataGraphPipelineSession with the wrong bindpoint");
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    pipeline.CreateDataGraphPipeline();

    VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
    session_ci.dataGraphPipeline = pipeline;
    vkt::DataGraphPipelineSession session(*m_device, session_ci);

    std::vector<vkt::DeviceMemory> device_mem(session.BindPointsCount());
    session.AllocSessionMem(device_mem);

    auto session_bind_infos = InitSessionBindInfo(session, device_mem);
    session_bind_infos[0].bindPoint =
        static_cast<VkDataGraphPipelineSessionBindPointARM>(VK_DATA_GRAPH_PIPELINE_SESSION_BIND_POINT_MAX_ENUM_ARM - 1);

    m_errorMonitor->SetDesiredError("VUID-VkBindDataGraphPipelineSessionMemoryInfoARM-bindPoint-09786");
    // VK_DATA_GRAPH_PIPELINE_SESSION_BIND_POINT_TRANSIENT_ARM is the only legal value for bindPoint, so whatever we
    // put in place of it, we also trigger this implicit check:
    m_errorMonitor->SetAllowedFailureMsg("VUID-VkBindDataGraphPipelineSessionMemoryInfoARM-bindPoint-parameter");
    vk::BindDataGraphPipelineSessionMemoryARM(*m_device, session_bind_infos.size(), session_bind_infos.data());
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, DestroySessionInUse) {
    TEST_DESCRIPTION("Try destroying a datagraph pipeline session while it is in use");
    SetTargetApiVersion(VK_API_VERSION_1_4);
    AddRequiredFeature(vkt::Feature::timelineSemaphore);
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    pipeline.CreateDataGraphPipeline();

    VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
    session_ci.dataGraphPipeline = pipeline;
    vkt::DataGraphPipelineSession session(*m_device, session_ci);

    std::vector<vkt::DeviceMemory> device_mem(session.BindPointsCount());
    session.AllocSessionMem(device_mem);
    auto session_bind_infos = InitSessionBindInfo(session, device_mem);
    vk::BindDataGraphPipelineSessionMemoryARM(*m_device, session_bind_infos.size(), session_bind_infos.data());

    pipeline.descriptor_set_->WriteDescriptorTensorInfo(0, &pipeline.tensor_views_[0]->handle(), 0);
    pipeline.descriptor_set_->WriteDescriptorTensorInfo(1, &pipeline.tensor_views_[1]->handle(), 0);
    pipeline.descriptor_set_->UpdateDescriptorSets();

    m_command_buffer.Begin();
    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, pipeline);
    vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, pipeline.pipeline_layout_, 0, 1,
                              &pipeline.descriptor_set_.get()->set_, 0, nullptr);
    vk::CmdDispatchDataGraphARM(m_command_buffer, session, nullptr);
    m_command_buffer.End();

    VkSemaphoreTypeCreateInfo sem_type = vku::InitStructHelper();
    sem_type.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE;
    sem_type.initialValue = 0;
    VkSemaphoreCreateInfo create_sem = vku::InitStructHelper(&sem_type);

    vkt::Semaphore sem(*m_device, create_sem);
    VkTimelineSemaphoreSubmitInfo timeline_info = vku::InitStructHelper();
    const uint64_t wait_value = 1;
    timeline_info.waitSemaphoreValueCount = 1;
    timeline_info.pWaitSemaphoreValues = &wait_value;

    VkPipelineStageFlags dst_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;

    VkSubmitInfo submit_info = vku::InitStructHelper(&timeline_info);
    submit_info.waitSemaphoreCount = 1;
    submit_info.pWaitSemaphores = &sem.handle();
    submit_info.pWaitDstStageMask = &dst_stage_mask;
    submit_info.commandBufferCount = 1;
    submit_info.pCommandBuffers = &m_command_buffer.handle();

    vk::QueueSubmit(m_default_queue->handle(), 1, &submit_info, VK_NULL_HANDLE);

    m_errorMonitor->SetDesiredError("VUID-vkDestroyDataGraphPipelineSessionARM-session-09793");
    vk::DestroyDataGraphPipelineSessionARM(*m_device, session, nullptr);
    m_errorMonitor->VerifyFound();

    VkSemaphoreSignalInfo signal_sem = vku::InitStructHelper();
    signal_sem.semaphore = sem;
    signal_sem.value = 1;
    vk::SignalSemaphore(*m_device, &signal_sem);

    m_default_queue->Wait();
}

TEST_F(NegativeDataGraph, DestroySessionCreatedWithDestroyWithoutCallbacks) {
    TEST_DESCRIPTION("Try destroying without callbacks a datagraph pipeline session with callbacks");
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    pipeline.CreateDataGraphPipeline();

    VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
    session_ci.dataGraphPipeline = pipeline;

    VkDataGraphPipelineSessionARM session;
    vk::CreateDataGraphPipelineSessionARM(*m_device, &session_ci, vkt::DefaultAllocator(), &session);

    m_errorMonitor->SetDesiredError("VUID-vkDestroyDataGraphPipelineSessionARM-session-09794");
    vk::DestroyDataGraphPipelineSessionARM(*m_device, session, nullptr);
    m_errorMonitor->VerifyFound();

    vk::DestroyDataGraphPipelineSessionARM(*m_device, session, vkt::DefaultAllocator());

    vk::CreateDataGraphPipelineSessionARM(*m_device, &session_ci, nullptr, &session);

    m_errorMonitor->SetDesiredError("VUID-vkDestroyDataGraphPipelineSessionARM-session-09795");
    vk::DestroyDataGraphPipelineSessionARM(*m_device, session, vkt::DefaultAllocator());
    m_errorMonitor->VerifyFound();

    vk::DestroyDataGraphPipelineSessionARM(*m_device, session, nullptr);
}

TEST_F(NegativeDataGraph, DestroySessionCreatedWithoutDestroyWithCallbacks) {
    TEST_DESCRIPTION("Try destroying with callbacks a datagraph pipeline session without callbacks");
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    pipeline.CreateDataGraphPipeline();

    VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
    session_ci.dataGraphPipeline = pipeline;

    VkDataGraphPipelineSessionARM session;
    vk::CreateDataGraphPipelineSessionARM(*m_device, &session_ci, nullptr, &session);

    m_errorMonitor->SetDesiredError("VUID-vkDestroyDataGraphPipelineSessionARM-session-09795");
    vk::DestroyDataGraphPipelineSessionARM(*m_device, session, vkt::DefaultAllocator());
    m_errorMonitor->VerifyFound();

    vk::DestroyDataGraphPipelineSessionARM(*m_device, session, nullptr);
}

TEST_F(NegativeDataGraph, CmdDispatchPipelineNotBound) {
    TEST_DESCRIPTION(
        "Try to add a CmdDispatchDataGraphARM to a command buffer when the pipeline was not bound to "
        "VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM");
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    pipeline.CreateDataGraphPipeline();

    VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
    session_ci.dataGraphPipeline = pipeline;
    vkt::DataGraphPipelineSession session(*m_device, session_ci);

    std::vector<vkt::DeviceMemory> device_mem(session.BindPointsCount());
    session.AllocSessionMem(device_mem);
    auto session_bind_infos = InitSessionBindInfo(session, device_mem);
    vk::BindDataGraphPipelineSessionMemoryARM(*m_device, session_bind_infos.size(), session_bind_infos.data());

    pipeline.descriptor_set_->WriteDescriptorTensorInfo(0, &pipeline.tensor_views_[0]->handle(), 0);
    pipeline.descriptor_set_->WriteDescriptorTensorInfo(1, &pipeline.tensor_views_[1]->handle(), 0);
    pipeline.descriptor_set_->UpdateDescriptorSets();

    m_command_buffer.Begin();
    vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, pipeline.pipeline_layout_, 0, 1,
                              &pipeline.descriptor_set_.get()->set_, 0, nullptr);
    m_errorMonitor->SetDesiredError("VUID-vkCmdDispatchDataGraphARM-None-08606");
    vk::CmdDispatchDataGraphARM(m_command_buffer, session, nullptr);
    m_errorMonitor->VerifyFound();
    m_command_buffer.End();
}

TEST_F(NegativeDataGraph, CmdDispatchDescriptorSetNotBound) {
    TEST_DESCRIPTION("Try to dispatch a datagraph when the required descriptor set is not bound");
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    pipeline.CreateDataGraphPipeline();

    VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
    session_ci.dataGraphPipeline = pipeline;
    vkt::DataGraphPipelineSession session(*m_device, session_ci);

    std::vector<vkt::DeviceMemory> device_mem(session.BindPointsCount());
    session.AllocSessionMem(device_mem);
    auto session_bind_infos = InitSessionBindInfo(session, device_mem);
    vk::BindDataGraphPipelineSessionMemoryARM(*m_device, session_bind_infos.size(), session_bind_infos.data());

    pipeline.descriptor_set_->WriteDescriptorTensorInfo(0, &pipeline.tensor_views_[0]->handle(), 0);
    pipeline.descriptor_set_->WriteDescriptorTensorInfo(1, &pipeline.tensor_views_[1]->handle(), 0);
    pipeline.descriptor_set_->UpdateDescriptorSets();

    m_command_buffer.Begin();
    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, pipeline);
    m_errorMonitor->SetDesiredError("VUID-vkCmdDispatchDataGraphARM-None-08600");
    vk::CmdDispatchDataGraphARM(m_command_buffer, session, nullptr);
    m_errorMonitor->VerifyFound();
    m_command_buffer.End();
}

TEST_F(NegativeDataGraph, CmdDispatchSessionNotBound) {
    TEST_DESCRIPTION("Try dispatching a graph command when not all required session resources have been bound");
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    pipeline.CreateDataGraphPipeline();

    VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
    session_ci.dataGraphPipeline = pipeline;
    vkt::DataGraphPipelineSession session(*m_device, session_ci);

    pipeline.descriptor_set_->WriteDescriptorTensorInfo(0, &pipeline.tensor_views_[0]->handle(), 0);
    pipeline.descriptor_set_->WriteDescriptorTensorInfo(1, &pipeline.tensor_views_[1]->handle(), 0);
    pipeline.descriptor_set_->UpdateDescriptorSets();

    m_command_buffer.Begin();
    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, pipeline);
    vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, pipeline.pipeline_layout_, 0, 1,
                              &pipeline.descriptor_set_.get()->set_, 0, nullptr);
    m_errorMonitor->SetDesiredError("VUID-vkCmdDispatchDataGraphARM-session-09796");
    vk::CmdDispatchDataGraphARM(m_command_buffer, session, nullptr);
    m_errorMonitor->VerifyFound();
    m_command_buffer.End();
}

TEST_F(NegativeDataGraph, CmdDispatchProtectedNoFaultUnsupportedUnprotectedCmdBufferProtectedTensor) {
    TEST_DESCRIPTION(
        "Try dispatching a datagraph with protected resources - bound pipeline tensors have VK_TENSOR_CREATE_PROTECTED_BIT_ARM set "
        "- to an unprotected command buffer when protectedNoFault is not supported");
    AddRequiredFeature(vkt::Feature::protectedMemory);
    RETURN_IF_SKIP(InitBasicDataGraph());

    VkPhysicalDeviceProtectedMemoryProperties protected_memory_properties = vku::InitStructHelper();
    GetPhysicalDeviceProperties2(protected_memory_properties);
    if (protected_memory_properties.protectedNoFault) {
        GTEST_SKIP() << "protectedNoFault is supported";
    }

    vkt::dg::HelperParameters params;
    params.protected_tensors = true;
    vkt::dg::DataGraphPipelineHelper pipeline(*this, params);
    pipeline.CreateDataGraphPipeline();

    VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
    session_ci.dataGraphPipeline = pipeline;
    vkt::DataGraphPipelineSession session(*m_device, session_ci);

    std::vector<vkt::DeviceMemory> device_mem(session.BindPointsCount());
    session.AllocSessionMem(device_mem);
    auto session_bind_infos = InitSessionBindInfo(session, device_mem);
    vk::BindDataGraphPipelineSessionMemoryARM(*m_device, session_bind_infos.size(), session_bind_infos.data());

    pipeline.descriptor_set_->WriteDescriptorTensorInfo(0, &pipeline.tensor_views_[0]->handle(), 0);
    pipeline.descriptor_set_->WriteDescriptorTensorInfo(1, &pipeline.tensor_views_[1]->handle(), 0);
    pipeline.descriptor_set_->UpdateDescriptorSets();

    m_command_buffer.Begin();
    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, pipeline);
    vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, pipeline.pipeline_layout_, 0, 1,
                              &pipeline.descriptor_set_.get()->set_, 0, nullptr);
    m_errorMonitor->SetDesiredError("VUID-vkCmdDispatchDataGraphARM-commandBuffer-09800");
    m_errorMonitor->SetDesiredError(
        "VUID-vkCmdDispatchDataGraphARM-commandBuffer-09800");  // We are using 2 protected resources in this unprotected command
                                                                // buffer and so need to log the error twice
    vk::CmdDispatchDataGraphARM(m_command_buffer, session, nullptr);
    m_errorMonitor->VerifyFound();
    m_command_buffer.End();
}

TEST_F(NegativeDataGraph, CmdDispatchProtectedNoFaultUnsupportedProtectedCmdBufferUnprotectedTensor) {
    TEST_DESCRIPTION(
        "Try dispatching a datagraph with unprotected resources to a protected command buffer - command buffer created with "
        "VK_COMMAND_POOL_CREATE_PROTECTED_BIT set -  when protectedNoFault is not supported");
    SetTargetApiVersion(VK_API_VERSION_1_4);
    AddRequiredExtensions(VK_ARM_TENSORS_EXTENSION_NAME);
    AddRequiredExtensions(VK_ARM_DATA_GRAPH_EXTENSION_NAME);
    AddRequiredFeature(vkt::Feature::tensors);
    AddRequiredFeature(vkt::Feature::dataGraph);
    AddRequiredFeature(vkt::Feature::dataGraphShaderModule);
    AddRequiredFeature(vkt::Feature::shaderTensorAccess);
    AddRequiredFeature(vkt::Feature::vulkanMemoryModel);
    AddRequiredFeature(vkt::Feature::shaderInt8);
    AddRequiredFeature(vkt::Feature::protectedMemory);
    RETURN_IF_SKIP(InitFramework());
    RETURN_IF_SKIP(InitState(nullptr, nullptr, VK_COMMAND_POOL_CREATE_PROTECTED_BIT));

    VkPhysicalDeviceProtectedMemoryProperties protected_memory_properties = vku::InitStructHelper();
    GetPhysicalDeviceProperties2(protected_memory_properties);
    if (protected_memory_properties.protectedNoFault) {
        GTEST_SKIP() << "protectedNoFault is supported";
    }

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    pipeline.CreateDataGraphPipeline();

    VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
    session_ci.dataGraphPipeline = pipeline;
    vkt::DataGraphPipelineSession session(*m_device, session_ci);
    std::vector<vkt::DeviceMemory> device_mem(session.BindPointsCount());
    session.AllocSessionMem(device_mem);

    auto session_bind_infos = InitSessionBindInfo(session, device_mem);
    vk::BindDataGraphPipelineSessionMemoryARM(*m_device, session_bind_infos.size(), session_bind_infos.data());

    pipeline.descriptor_set_->WriteDescriptorTensorInfo(0, &pipeline.tensor_views_[0]->handle(), 0);
    pipeline.descriptor_set_->WriteDescriptorTensorInfo(1, &pipeline.tensor_views_[1]->handle(), 0);
    pipeline.descriptor_set_->UpdateDescriptorSets();

    m_command_buffer.Begin();
    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, pipeline);
    vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, pipeline.pipeline_layout_, 0, 1,
                              &pipeline.descriptor_set_.get()->set_, 0, nullptr);
    m_errorMonitor->SetDesiredError("VUID-vkCmdDispatchDataGraphARM-commandBuffer-09801");
    m_errorMonitor->SetDesiredError(
        "VUID-vkCmdDispatchDataGraphARM-commandBuffer-09801");  // We are using 2 unprotected resources in this protected command
                                                                // buffer and so need to log the error twice
    vk::CmdDispatchDataGraphARM(m_command_buffer, session, nullptr);
    m_errorMonitor->VerifyFound();
    m_command_buffer.End();
}

TEST_F(NegativeDataGraph, CmdDispatchInvalidDescriptorNoUpdate) {
    TEST_DESCRIPTION("Try dispatching a datagraph but the descriptor has not been updated");
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    pipeline.CreateDataGraphPipeline();

    VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
    session_ci.dataGraphPipeline = pipeline.Handle();
    vkt::DataGraphPipelineSession session(*m_device, session_ci);
    auto& bind_point_reqs = session.BindPointReqs();
    std::vector<vkt::DeviceMemory> device_mem(bind_point_reqs.size());
    session.AllocSessionMem(device_mem);
    auto session_bind_infos = InitSessionBindInfo(session, device_mem);
    vk::BindDataGraphPipelineSessionMemoryARM(*m_device, session_bind_infos.size(), session_bind_infos.data());

    // update only 1 of 2 descriptors
    pipeline.descriptor_set_->WriteDescriptorTensorInfo(0, &pipeline.tensor_views_[0]->handle(), 0);
    pipeline.descriptor_set_->UpdateDescriptorSets();

    m_command_buffer.Begin();
    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, pipeline.Handle());
    vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, pipeline.pipeline_layout_.handle(), 0, 1,
                              &pipeline.descriptor_set_.get()->set_, 0, nullptr);
    m_errorMonitor->SetDesiredError("VUID-vkCmdDispatchDataGraphARM-None-08114");
    vk::CmdDispatchDataGraphARM(m_command_buffer, session.handle(), nullptr);
    m_errorMonitor->VerifyFound();
    m_command_buffer.End();
}

TEST_F(NegativeDataGraph, CmdDispatchInvalidDescriptorDeletedObject) {
    TEST_DESCRIPTION("Try dispatching a datagraph but the tensor or the view are destroyed before dispatch");
    RETURN_IF_SKIP(InitBasicDataGraph());

    // 2 runs: 1st time delete the tensor, 2nd the view, both make the descriptor invalid
    for (uint32_t i = 0; i < 2; i++) {
        vkt::dg::DataGraphPipelineHelper pipeline(*this);
        pipeline.CreateDataGraphPipeline();

        VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
        session_ci.dataGraphPipeline = pipeline.Handle();
        vkt::DataGraphPipelineSession session(*m_device, session_ci);

        auto& bind_point_reqs = session.BindPointReqs();
        std::vector<vkt::DeviceMemory> device_mem(bind_point_reqs.size());
        session.AllocSessionMem(device_mem);
        auto session_bind_infos = InitSessionBindInfo(session, device_mem);
        vk::BindDataGraphPipelineSessionMemoryARM(*m_device, session_bind_infos.size(), session_bind_infos.data());

        pipeline.descriptor_set_->WriteDescriptorTensorInfo(0, &pipeline.tensor_views_[0]->handle(), 0);
        pipeline.descriptor_set_->WriteDescriptorTensorInfo(1, &pipeline.tensor_views_[1]->handle(), 0);
        pipeline.descriptor_set_->UpdateDescriptorSets();

        // deleting either of these 2 invalidates the descriptor
        if (i == 0) {
            pipeline.tensors_[0]->Destroy();
        } else {
            pipeline.tensor_views_[0]->Destroy();
        }

        m_command_buffer.Begin();
        vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, pipeline.Handle());
        vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, pipeline.pipeline_layout_.handle(), 0, 1,
                                  &pipeline.descriptor_set_.get()->set_, 0, nullptr);
        m_errorMonitor->SetDesiredError("VUID-vkCmdDispatchDataGraphARM-None-08114");
        vk::CmdDispatchDataGraphARM(m_command_buffer, session.handle(), nullptr);
        m_errorMonitor->VerifyFound();
        m_command_buffer.End();
    }
}

TEST_F(NegativeDataGraph, CmdDispatchInvalidDescriptorBufferBit) {
    TEST_DESCRIPTION(
        "Try dispatching a datagraph with VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT set, but using descriptor sets.");
    AddRequiredFeature(vkt::Feature::dataGraphDescriptorBuffer);
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    pipeline.pipeline_ci_.flags |= VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT;
    pipeline.CreateDataGraphPipeline();

    VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
    session_ci.dataGraphPipeline = pipeline.Handle();
    vkt::DataGraphPipelineSession session(*m_device, session_ci);
    auto& bind_point_reqs = session.BindPointReqs();
    std::vector<vkt::DeviceMemory> device_mem(bind_point_reqs.size());
    session.AllocSessionMem(device_mem);
    auto session_bind_infos = InitSessionBindInfo(session, device_mem);
    vk::BindDataGraphPipelineSessionMemoryARM(*m_device, session_bind_infos.size(), session_bind_infos.data());

    pipeline.descriptor_set_->WriteDescriptorTensorInfo(0, &pipeline.tensor_views_[0]->handle(), 0);
    pipeline.descriptor_set_->WriteDescriptorTensorInfo(1, &pipeline.tensor_views_[1]->handle(), 0);
    pipeline.descriptor_set_->UpdateDescriptorSets();

    m_command_buffer.Begin();
    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, pipeline.Handle());
    vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, pipeline.pipeline_layout_.handle(), 0, 1,
                              &pipeline.descriptor_set_.get()->set_, 0, nullptr);
    m_errorMonitor->SetDesiredError("VUID-vkCmdDispatchDataGraphARM-None-08115");
    vk::CmdDispatchDataGraphARM(m_command_buffer, session.handle(), nullptr);
    m_errorMonitor->VerifyFound();
    m_command_buffer.End();
}

TEST_F(NegativeDataGraph, CmdDispatchMissingDescriptorBufferBit) {
    TEST_DESCRIPTION(
        "Try dispatching a datagraph with descriptor buffers but without the VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT.");
    SetTargetApiVersion(VK_API_VERSION_1_4);
    AddRequiredExtensions(VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME);
    AddRequiredExtensions(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME);
    AddRequiredFeature(vkt::Feature::descriptorBuffer);
    AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
    AddRequiredFeature(vkt::Feature::dataGraphDescriptorBuffer);
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);

    // create a pipeline layout with the required flags
    VkDescriptorSetLayoutCreateInfo dslci = vku::InitStructHelper();
    dslci.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT;
    dslci.bindingCount = pipeline.descriptor_set_layout_bindings_.size();
    dslci.pBindings = pipeline.descriptor_set_layout_bindings_.data();

    vkt::DescriptorSetLayout dsl(*m_device, dslci);
    vkt::PipelineLayout pipeline_layout(*m_device, {&dsl});
    ASSERT_TRUE(pipeline_layout.initialized());

    // set layout for descriptor buffer, but not the flags
    // pipeline.pipeline_ci_.flags |= VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT;
    pipeline.pipeline_ci_.layout = pipeline_layout;
    pipeline.CreateDataGraphPipeline();

    VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
    session_ci.dataGraphPipeline = pipeline.Handle();
    vkt::DataGraphPipelineSession session(*m_device, session_ci);
    auto& bind_point_reqs = session.BindPointReqs();
    std::vector<vkt::DeviceMemory> device_mem(bind_point_reqs.size());
    session.AllocSessionMem(device_mem);
    auto session_bind_infos = InitSessionBindInfo(session, device_mem);
    vk::BindDataGraphPipelineSessionMemoryARM(*m_device, session_bind_infos.size(), session_bind_infos.data());

    pipeline.descriptor_set_->WriteDescriptorTensorInfo(0, &pipeline.tensor_views_[0]->handle(), 0);
    pipeline.descriptor_set_->WriteDescriptorTensorInfo(1, &pipeline.tensor_views_[1]->handle(), 0);
    pipeline.descriptor_set_->UpdateDescriptorSets();

    m_command_buffer.Begin();
    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, pipeline.Handle());

    vkt::Buffer buffer(*m_device, 4096, VK_BUFFER_USAGE_SAMPLER_DESCRIPTOR_BUFFER_BIT_EXT, vkt::device_address);
    VkDescriptorBufferBindingInfoEXT dbbi = vku::InitStructHelper();
    dbbi.address = buffer.Address();
    dbbi.usage = VK_BUFFER_USAGE_SAMPLER_DESCRIPTOR_BUFFER_BIT_EXT;

    vk::CmdBindDescriptorBuffersEXT(m_command_buffer, 1, &dbbi);

    uint32_t index = 0;
    VkDeviceSize offset = 0;
    vk::CmdSetDescriptorBufferOffsetsEXT(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, pipeline_layout, 0, 1, &index,
                                         &offset);

    m_errorMonitor->SetDesiredError("VUID-vkCmdDispatchDataGraphARM-None-08117");
    m_errorMonitor->SetDesiredError("VUID-vkCmdDispatchDataGraphARM-None-08600");
    vk::CmdDispatchDataGraphARM(m_command_buffer, session.handle(), nullptr);
    m_errorMonitor->VerifyFound();
    m_command_buffer.End();
}

TEST_F(NegativeDataGraph, ShaderModuleCreateInfoInvalidConstantID) {
    TEST_DESCRIPTION(
        "Try creating a datagraph pipeline where the VkDataGraphPipelineShaderModuleCreateInfoARM has a "
        "VkDataGraphPipelineConstantARM whose id member is not valid");
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    VkDataGraphPipelineConstantARM graph_pipeline_constant = vku::InitStructHelper();
    graph_pipeline_constant.id = 42;  // Arbitrary value not used by OpGraphConstantARM in shader module
    int constant_data = 42;
    graph_pipeline_constant.pConstantData = &constant_data;
    pipeline.shader_module_ci_.constantCount = 1;
    pipeline.shader_module_ci_.pConstants = &graph_pipeline_constant;
    m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-pNext-09921");
    pipeline.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, TensorSparsitySuppliedMissingDescription) {
    TEST_DESCRIPTION(
        "Try creating a datagraph pipeline with a tensor sparsity structure but missing a tensor description structure");
    RETURN_IF_SKIP(InitBasicDataGraph());

    const std::string& spirv_string = vkt::dg::DataGraphPipelineHelper::GetSpirvConstantDataGraph();

    vkt::dg::HelperParameters params;
    params.spirv_source = spirv_string.c_str();
    vkt::dg::DataGraphPipelineHelper pipeline(*this, params);

    VkDataGraphPipelineConstantTensorSemiStructuredSparsityInfoARM tensor_sparsity = vku::InitStructHelper();
    tensor_sparsity.groupSize = 1;

    // GetConstant puts the correct description in the pNext, by overwriting it we lose the description and cause the error
    VkDataGraphPipelineConstantARM constant = GetConstant();
    constant.pNext = &tensor_sparsity;
    pipeline.shader_module_ci_.constantCount = 1;
    pipeline.shader_module_ci_.pConstants = &constant;

    m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineConstantARM-pNext-09775");
    // 9921 will also be triggered since we need a description for this
    // Graph Constant ID and we are intentionally not passing one
    m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-pNext-09921");
    pipeline.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, TensorSparsityDimensionTooLarge) {
    TEST_DESCRIPTION(
        "Try creating a datagraph pipeline with a tensor sparsity structure but the supplied dimension is larger than the "
        "dimensionCount in the tensor description supplied");
    RETURN_IF_SKIP(InitBasicDataGraph());

    const std::string& spirv_string = vkt::dg::DataGraphPipelineHelper::GetSpirvConstantDataGraph();

    vkt::dg::HelperParameters params;
    params.spirv_source = spirv_string.c_str();
    vkt::dg::DataGraphPipelineHelper pipeline(*this, params);

    VkTensorDescriptionARM constant_tensor_desc = DefaultConstantTensorDesc();

    VkDataGraphPipelineConstantTensorSemiStructuredSparsityInfoARM tensor_sparsity = vku::InitStructHelper();
    tensor_sparsity.groupSize = 1;
    tensor_sparsity.dimension = constant_tensor_desc.dimensionCount + 1;
    constant_tensor_desc.pNext = &tensor_sparsity;

    VkDataGraphPipelineConstantARM constant = GetConstant(constant_tensor_desc);
    pipeline.shader_module_ci_.constantCount = 1;
    pipeline.shader_module_ci_.pConstants = &constant;

    m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineConstantARM-pNext-09776");
    pipeline.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, TensorSparsityDescriptionDimensionNotMultipleOfSparsityGroupSize) {
    TEST_DESCRIPTION(
        "Try creating a datagraph pipeline with a tensor sparsity structure but dimension[sparsity->dimension] is not a multiple "
        "of sparsity->groupSize");
    RETURN_IF_SKIP(InitBasicDataGraph());

    const std::string& spirv_string = vkt::dg::DataGraphPipelineHelper::GetSpirvConstantDataGraph();

    vkt::dg::HelperParameters params;
    params.spirv_source = spirv_string.c_str();
    vkt::dg::DataGraphPipelineHelper pipeline(*this, params);

    VkTensorDescriptionARM constant_tensor_desc = DefaultConstantTensorDesc();

    VkDataGraphPipelineConstantTensorSemiStructuredSparsityInfoARM tensor_sparsity = vku::InitStructHelper();
    tensor_sparsity.dimension = 2;
    int64_t dim_2 = constant_tensor_desc.pDimensions[tensor_sparsity.dimension];
    ASSERT_TRUE((dim_2 >= 1) && (dim_2 <= static_cast<int64_t>(UINT32_MAX)));
    tensor_sparsity.groupSize =
        static_cast<uint32_t>(dim_2 - 1);  // ensure that dim_2 (the sparsity dimension) is NOT a multiple of groupSize
    constant_tensor_desc.pNext = &tensor_sparsity;

    VkDataGraphPipelineConstantARM constant = GetConstant(constant_tensor_desc);
    pipeline.shader_module_ci_.constantCount = 1;
    pipeline.shader_module_ci_.pConstants = &constant;

    m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineConstantARM-pNext-09777");
    pipeline.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, TensorSparsityDoubleDefinition) {
    TEST_DESCRIPTION("Try creating a datagraph pipeline with a tensor sparsity defined twice for the same dimension");
    RETURN_IF_SKIP(InitBasicDataGraph());

    const std::string& spirv_string = vkt::dg::DataGraphPipelineHelper::GetSpirvConstantDataGraph();

    vkt::dg::HelperParameters params;
    params.spirv_source = spirv_string.c_str();
    vkt::dg::DataGraphPipelineHelper pipeline(*this, params);

    VkTensorDescriptionARM constant_tensor_desc = DefaultConstantTensorDesc();

    // add 3 sparsity structures but 2 are for the same dimension -> ERROR
    VkDataGraphPipelineConstantTensorSemiStructuredSparsityInfoARM tensor_sparsity0 = vku::InitStructHelper();
    tensor_sparsity0.groupSize = 1;
    tensor_sparsity0.dimension = 2;
    constant_tensor_desc.pNext = &tensor_sparsity0;
    VkDataGraphPipelineConstantTensorSemiStructuredSparsityInfoARM tensor_sparsity1 = vku::InitStructHelper();
    tensor_sparsity1.groupSize = 2;
    tensor_sparsity1.dimension = 3;
    tensor_sparsity0.pNext = &tensor_sparsity1;
    VkDataGraphPipelineConstantTensorSemiStructuredSparsityInfoARM tensor_sparsity2 = vku::InitStructHelper();
    tensor_sparsity2.groupSize = 2;
    tensor_sparsity2.dimension = 2;
    tensor_sparsity1.pNext = &tensor_sparsity2;

    VkDataGraphPipelineConstantARM constant = GetConstant(constant_tensor_desc);
    pipeline.shader_module_ci_.constantCount = 1;
    pipeline.shader_module_ci_.pConstants = &constant;

    m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineConstantARM-pNext-09870");
    pipeline.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, GraphConstantTensorWrongID) {
    TEST_DESCRIPTION("Try creating a datagraph pipeline with a constant that has an id different from the spirv definition");
    RETURN_IF_SKIP(InitBasicDataGraph());

    const std::string& spirv_string = vkt::dg::DataGraphPipelineHelper::GetSpirvConstantDataGraph();

    vkt::dg::HelperParameters params;
    params.spirv_source = spirv_string.c_str();
    vkt::dg::DataGraphPipelineHelper pipeline(*this, params);

    VkTensorDescriptionARM desc = DefaultConstantTensorDesc();
    VkDataGraphPipelineConstantARM constant = GetConstant(desc);
    constant.id = 42;
    pipeline.shader_module_ci_.constantCount = 1;
    pipeline.shader_module_ci_.pConstants = &constant;

    // 9921 triggered 2 times:
    // - Vulkan pConstants[x].id = 42 has no correspondence in the spirv code
    // - spirv OpGraphConstantARM has no correspondence in the Vulkan pConstants
    m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-pNext-09921");
    m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-pNext-09921");
    pipeline.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, GraphConstantTensorWrongRank) {
    TEST_DESCRIPTION(
        "Try creating a datagraph pipeline with a constant based on a tensor with rank different from the spirv definition");
    RETURN_IF_SKIP(InitBasicDataGraph());

    const std::string& spirv_string = vkt::dg::DataGraphPipelineHelper::GetSpirvConstantDataGraph();

    vkt::dg::HelperParameters params;
    params.spirv_source = spirv_string.c_str();
    vkt::dg::DataGraphPipelineHelper pipeline(*this, params);

    // define a tensor with rank 3, the spirv has rank 4
    VkTensorDescriptionARM desc = vku::InitStructHelper();
    const std::vector<int64_t> dimensions{1, 2, 4};
    desc.tiling = VK_TENSOR_TILING_LINEAR_ARM;
    desc.format = VK_FORMAT_R32_SINT;
    desc.dimensionCount = dimensions.size();
    desc.pDimensions = dimensions.data();
    desc.usage = VK_TENSOR_USAGE_DATA_GRAPH_BIT_ARM;

    VkDataGraphPipelineConstantARM constant = GetConstant(desc);
    pipeline.shader_module_ci_.constantCount = 1;
    pipeline.shader_module_ci_.pConstants = &constant;

    m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-pNext-09921");
    pipeline.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, GraphConstantTensorWrongDimensions) {
    TEST_DESCRIPTION(
        "Try creating a datagraph pipeline with a constant based on a tensor with dimensions different from the spirv definition");
    RETURN_IF_SKIP(InitBasicDataGraph());

    const std::string& spirv_string = vkt::dg::DataGraphPipelineHelper::GetSpirvConstantDataGraph();

    vkt::dg::HelperParameters params;
    params.spirv_source = spirv_string.c_str();
    vkt::dg::DataGraphPipelineHelper pipeline(*this, params);

    // dim[3] is different from the spirv (4)
    VkTensorDescriptionARM desc = vku::InitStructHelper();
    const std::vector<int64_t> dimensions{1, 2, 4, 1};  // dim[3] is 4 in the spirv
    desc.tiling = VK_TENSOR_TILING_LINEAR_ARM;
    desc.format = VK_FORMAT_R32_SINT;
    desc.dimensionCount = dimensions.size();
    desc.pDimensions = dimensions.data();
    desc.usage = VK_TENSOR_USAGE_DATA_GRAPH_BIT_ARM;

    VkDataGraphPipelineConstantARM constant = GetConstant(desc);
    pipeline.shader_module_ci_.constantCount = 1;
    pipeline.shader_module_ci_.pConstants = &constant;

    m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-pNext-09921");
    pipeline.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, GraphConstantTensorWrongFormat) {
    TEST_DESCRIPTION(
        "Try creating a datagraph pipeline with a constant based on a tensor with format different from the spirv definition");
    RETURN_IF_SKIP(InitBasicDataGraph());

    const std::string& spirv_string = vkt::dg::DataGraphPipelineHelper::GetSpirvConstantDataGraph();

    vkt::dg::HelperParameters params;
    params.spirv_source = spirv_string.c_str();

    // try a few different formats, different for bit width, float encoding and type
    // compatible VK_FORMAT_R32_SINT and VK_FORMAT_R32_UINT also included
    for (auto format :
         {VK_FORMAT_R32_UINT, VK_FORMAT_R32_SINT, VK_FORMAT_R8_SINT, VK_FORMAT_R8_BOOL_ARM, VK_FORMAT_R32_SFLOAT, VK_FORMAT_R8_SFLOAT_FPENCODING_FLOAT8E4M3_ARM}) {
        vkt::dg::DataGraphPipelineHelper pipeline(*this, params);

        VkTensorDescriptionARM desc = DefaultConstantTensorDesc();
        desc.format = format;
        VkDataGraphPipelineConstantARM constant = GetConstant(desc);
        pipeline.shader_module_ci_.constantCount = 1;
        pipeline.shader_module_ci_.pConstants = &constant;

        // SPIRV is signless, so both VK_FORMAT_R32_SINT and VK_FORMAT_R32_UINT must not trigger the error
        if (format == VK_FORMAT_R32_SINT || format == VK_FORMAT_R32_UINT) {
            pipeline.CreateDataGraphPipeline();
        } else {
            m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-pNext-09921");
            pipeline.CreateDataGraphPipeline();
            m_errorMonitor->VerifyFound();
        }
    }
}

TEST_F(NegativeDataGraph, GraphConstantTensorMissingDescription) {
    TEST_DESCRIPTION("Try creating a datagraph pipeline with a constant that is missing the tensor description");
    RETURN_IF_SKIP(InitBasicDataGraph());

    const std::string& spirv_string = vkt::dg::DataGraphPipelineHelper::GetSpirvConstantDataGraph();

    vkt::dg::HelperParameters params;
    params.spirv_source = spirv_string.c_str();
    vkt::dg::DataGraphPipelineHelper pipeline(*this, params);

    // GetConstant puts the correct description in the pNext, remove it to cause the error
    VkDataGraphPipelineConstantARM constant = GetConstant();
    constant.pNext = nullptr;
    pipeline.shader_module_ci_.constantCount = 1;
    pipeline.shader_module_ci_.pConstants = &constant;

    m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-pNext-09921");
    pipeline.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, GraphConstantTensorWrongTiling) {
    TEST_DESCRIPTION("Try creating a datagraph pipeline with a constant which corresponds to a tensor with the incorrect tiling");
    RETURN_IF_SKIP(InitBasicDataGraph());

    const std::string& spirv_string = vkt::dg::DataGraphPipelineHelper::GetSpirvConstantDataGraph();

    vkt::dg::HelperParameters params;
    params.spirv_source = spirv_string.c_str();
    vkt::dg::DataGraphPipelineHelper pipeline(*this, params);

    VkTensorDescriptionARM desc = DefaultConstantTensorDesc();
    desc.tiling = VK_TENSOR_TILING_OPTIMAL_ARM;  // should be VK_TENSOR_TILING_LINEAR_ARM
    VkDataGraphPipelineConstantARM constant = GetConstant(desc);
    pipeline.shader_module_ci_.constantCount = 1;
    pipeline.shader_module_ci_.pConstants = &constant;

    m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineConstantARM-pNext-09917");
    pipeline.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, GraphConstantTensorMissingUsageFlags) {
    TEST_DESCRIPTION("Try creating a datagraph pipeline with a constant based on a tensor with the incorrect usage flags");
    RETURN_IF_SKIP(InitBasicDataGraph());

    const std::string& spirv_string = vkt::dg::DataGraphPipelineHelper::GetSpirvConstantDataGraph();

    vkt::dg::HelperParameters params;
    params.spirv_source = spirv_string.c_str();
    vkt::dg::DataGraphPipelineHelper pipeline(*this, params);

    VkTensorDescriptionARM desc = DefaultConstantTensorDesc();
    desc.usage = VK_TENSOR_USAGE_SHADER_BIT_ARM;  // should be VK_TENSOR_USAGE_DATA_GRAPH_BIT_ARM
    VkDataGraphPipelineConstantARM constant = GetConstant(desc);
    pipeline.shader_module_ci_.constantCount = 1;
    pipeline.shader_module_ci_.pConstants = &constant;

    m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineConstantARM-id-09850");
    pipeline.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, ResourceTensorWrongDescriptorSet) {
    TEST_DESCRIPTION("Try creating a datagraph pipeline with a resource with descriptorSet different from the spirv definition");
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);

    // incorrect descriptorSet on one resource
    pipeline.resources_[0].descriptorSet = 42;

    // 9923 triggered 2 times:
    // - Vulkan pResourceInfos[x].descriptorSet = 42 has no correspondence in the spirv code
    // - spirv OpVariable has no correspondence in the Vulkan pResourceInfos
    m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-pNext-09923");
    m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-pNext-09923");
    pipeline.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, ResourceTensorWrongBinding) {
    TEST_DESCRIPTION("Try creating a datagraph pipeline with a resource with binding different from the spirv definition");
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);

    // incorrect binding on one resource
    pipeline.resources_[0].binding = 42;

    // 9923 triggered 2 times:
    // - Vulkan pResourceInfos[x].binding = 42 has no correspondence in the spirv code
    // - spirv OpVariable has no correspondence in the Vulkan pResourceInfos
    m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-pNext-09923");
    m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-pNext-09923");
    pipeline.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, ResourceTensorArrayElementNotZero) {
    TEST_DESCRIPTION("Try creating a datagraph pipeline with a resource with arrayElement greater than zero");
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);

    // incorrect arrayElement on one resource
    pipeline.resources_[0].arrayElement = 42;

    m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-pNext-09923");
    pipeline.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, ResourceTensorWrongRank) {
    TEST_DESCRIPTION(
        "Try creating a datagraph pipeline with a resource based on a tensor with rank different from the spirv definition");
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);

    // define a tensor with rank 3, the spirv has rank 4
    auto* desc =
        const_cast<VkTensorDescriptionARM*>(vku::FindStructInPNextChain<VkTensorDescriptionARM>(pipeline.resources_[0].pNext));
    const std::vector<int64_t> dimensions{1, 4, 16};
    desc->dimensionCount = dimensions.size();
    desc->pDimensions = dimensions.data();

    m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-pNext-09923");
    pipeline.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, ResourceTensorWrongDimensions) {
    TEST_DESCRIPTION(
        "Try creating a datagraph pipeline with a resource based on a tensor with dimensions different from the spirv definition");
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);

    // dim[3] is different from the spirv (4)
    auto* desc =
        const_cast<VkTensorDescriptionARM*>(vku::FindStructInPNextChain<VkTensorDescriptionARM>(pipeline.resources_[0].pNext));
    const std::vector<int64_t> dimensions{1, 8, 16, 1};  // dim[3] is 4 in the spirv
    desc->dimensionCount = dimensions.size();
    desc->pDimensions = dimensions.data();

    m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-pNext-09923");
    pipeline.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, ResourceTensorWrongFormat) {
    TEST_DESCRIPTION(
        "Try creating a datagraph pipeline with a resource based on a tensor with format different from the spirv definition");
    RETURN_IF_SKIP(InitBasicDataGraph());

    // try a few different formats, different for bit width, type and float encoding
    // NOTE: VK_FORMAT_R8_SINT included as a sanity check: it is different only by sign from the actual format,
    // meaning it is compatible, and it must NOT trigger the VU
    for (auto format :
         {VK_FORMAT_R8_SINT, VK_FORMAT_R8_BOOL_ARM, VK_FORMAT_R32_SINT, VK_FORMAT_R32_SFLOAT, VK_FORMAT_R8_SFLOAT_FPENCODING_FLOAT8E4M3_ARM}) {
        vkt::dg::DataGraphPipelineHelper pipeline(*this);

        auto* desc =
            const_cast<VkTensorDescriptionARM*>(vku::FindStructInPNextChain<VkTensorDescriptionARM>(pipeline.resources_[0].pNext));
        desc->format = format;

        if (format == VK_FORMAT_R8_SINT) {
            // INT check must not consider the sign, as required by TOSA function specifications
            pipeline.CreateDataGraphPipeline();
        } else {
            m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-pNext-09923");
            pipeline.CreateDataGraphPipeline();
            m_errorMonitor->VerifyFound();
        }
    }
}

TEST_F(NegativeDataGraph, ResourceTensorMissingDescription) {
    TEST_DESCRIPTION("Try creating a datagraph pipeline with a resource missing the tensor description");
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);

    // cut the connection to the tensor description
    pipeline.resources_[0].pNext = nullptr;

    m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-pNext-09923");
    pipeline.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, ResourceTensorInvalidUsage) {
    TEST_DESCRIPTION(
        "Try creating a datagraph pipeline when the VkTensorDescriptionARM struct in the pNext of resources in resourceInfo do "
        "not have a valid Usage member");
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);

    // set an incorrect usage in the tensor description
    auto* desc =
        const_cast<VkTensorDescriptionARM*>(vku::FindStructInPNextChain<VkTensorDescriptionARM>(pipeline.resources_[0].pNext));
    desc->usage = VK_TENSOR_USAGE_SHADER_BIT_ARM;  // should be VK_TENSOR_USAGE_DATA_GRAPH_BIT_ARM
    m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineResourceInfoARM-descriptorSet-09851");
    pipeline.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, ResourceInfoImageLayoutsNoUnifiedImageLayouts) {
    RETURN_IF_SKIP(InitBasicDataGraph(true));

    // Need to create a data graph with image resources instead of the usual tensors, so use optical flow.
    vkt::dg::OpticalFlowHelper optical_flow(*this);
    /* Fail to chain a VkDataGraphPipelineResourceInfoImageLayoutARM structure. Since the unifiedImageLayouts feature is not
     * enabled this is invalid usage. */
    optical_flow.dg_pipeline_.resources_[0].pNext = nullptr;
    optical_flow.CreateDataGraphPipeline();
    optical_flow.SetupImageDescriptors();

    VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
    session_ci.dataGraphPipeline = optical_flow.dg_pipeline_;
    vkt::DataGraphPipelineSession session(*m_device, session_ci);
    std::vector<vkt::DeviceMemory> device_mem(session.BindPointReqs().size());
    session.AllocSessionMem(device_mem);
    std::vector<VkBindDataGraphPipelineSessionMemoryInfoARM> session_bind_infos =
        DataGraphTest::InitSessionBindInfo(session, device_mem);
    vk::BindDataGraphPipelineSessionMemoryARM(*m_device, session_bind_infos.size(), session_bind_infos.data());

    VkDataGraphPipelineOpticalFlowDispatchInfoARM optical_flow_di = vku::InitStructHelper();
    VkDataGraphPipelineDispatchInfoARM pipeline_di = vku::InitStructHelper(&optical_flow_di);

    m_command_buffer.Begin();
    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, optical_flow.dg_pipeline_);
    vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, optical_flow.dg_pipeline_.pipeline_layout_,
                              0, 1, &optical_flow.dg_pipeline_.descriptor_set_.get()->set_, 0, nullptr);

    m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineResourceInfoARM-descriptorSet-09962");
    vk::CmdDispatchDataGraphARM(m_command_buffer, session, &pipeline_di);
    m_errorMonitor->VerifyFound();

    m_command_buffer.End();
}

TEST_F(NegativeDataGraph, SessionGetMemoryRequirementsIndexTooLarge) {
    TEST_DESCRIPTION("Try to get the memory requirements for a session with an out-of-bounds value for objectIndex");
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    pipeline.CreateDataGraphPipeline();

    VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
    session_ci.dataGraphPipeline = pipeline;

    vkt::DataGraphPipelineSession session(*m_device, session_ci, vkt::no_mem);

    uint32_t bind_point_req_count = 0;
    VkDataGraphPipelineSessionBindPointRequirementsInfoARM bind_point_req_info = vku::InitStructHelper();
    bind_point_req_info.session = session;
    vk::GetDataGraphPipelineSessionBindPointRequirementsARM(*m_device, &bind_point_req_info, &bind_point_req_count, nullptr);
    if (bind_point_req_count == 0) {
        GTEST_FAIL() << "No bind points, test incorrect. Possible causes: incorrect spirv, or inconsistency between spirv and tensor/constant declarations\n";
    }
    std::vector<VkDataGraphPipelineSessionBindPointRequirementARM> bind_point_reqs(bind_point_req_count);
    for (auto& bp_req : bind_point_reqs) {
        bp_req = vku::InitStructHelper();
    }
    vk::GetDataGraphPipelineSessionBindPointRequirementsARM(*m_device, &bind_point_req_info, &bind_point_req_count,
                                                            bind_point_reqs.data());

    VkDataGraphPipelineSessionMemoryRequirementsInfoARM session_mem_reqs = vku::InitStructHelper();
    session_mem_reqs.session = session;
    session_mem_reqs.bindPoint = bind_point_reqs[0].bindPoint;
    session_mem_reqs.objectIndex = bind_point_reqs[0].numObjects;  // one over the limit

    VkMemoryRequirements2 mem_reqs = vku::InitStructHelper();

    m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineSessionMemoryRequirementsInfoARM-objectIndex-09855");
    vk::GetDataGraphPipelineSessionMemoryRequirementsARM(*m_device, &session_mem_reqs, &mem_reqs);
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, ShaderUsesSpecConstantsFeatureNotEnabled) {
    TEST_DESCRIPTION(
        "Try to create a datagraph with a VkSpecializationInfo used in the shader module when the feature is not enabled");
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    const VkSpecializationMapEntry entry = {
        0,                // id
        0,                // offset
        sizeof(uint32_t)  // size
    };
    uint32_t data = 0;
    const VkSpecializationInfo specialization_info = {
        1,
        &entry,
        1 * sizeof(uint32_t),
        &data,
    };
    pipeline.shader_module_ci_.pSpecializationInfo = &specialization_info;
    m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineShaderModuleCreateInfoARM-dataGraphSpecializationConstants-09849");
    pipeline.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, ShaderSpirvUsesOpSpecFeatureNotEnabled) {
    TEST_DESCRIPTION(
        "Try to create a datagraph with a shader module which contains OpSpec commands when the feature is not enabled");
    RETURN_IF_SKIP(InitBasicDataGraph());

    // inject a dummy line in the spirv to trigger the error
    vkt::dg::ModifiableShaderParameters spirv_params;
    spirv_params.types = R"(%dummy_spec_constant = OpSpecConstant %uint 3)";
    const std::string& spirv_string = vkt::dg::DataGraphPipelineHelper::GetSpirvModifyableDataGraph(spirv_params);

    // ShaderModule in VkDataGraphPipelineShaderModuleCreateInfoARM::module
    {
        vkt::dg::HelperParameters params;
        params.spirv_source = spirv_string.c_str();
        vkt::dg::DataGraphPipelineHelper pipeline(*this, params);

        m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineShaderModuleCreateInfoARM-dataGraphSpecializationConstants-09849");
        pipeline.CreateDataGraphPipeline();
        m_errorMonitor->VerifyFound();
    }

    // ShaderModule in the pNext chain of VkDataGraphPipelineCreateInfoARM
    {
        spvtools::SpirvTools tools{SPV_ENV_UNIVERSAL_1_6};
        std::vector<uint32_t> spirv_binary;
        if (!tools.Assemble(spirv_string, &spirv_binary)) {
            Monitor().SetError("Failed to compile SPIRV shader module");
            return;
        }
        VkShaderModuleCreateInfo shader_module_create_info = vku::InitStructHelper();
        shader_module_create_info.codeSize = spirv_binary.size() * sizeof(uint32_t);
        shader_module_create_info.pCode = spirv_binary.data();

        vkt::dg::DataGraphPipelineHelper pipeline(*this);
        // the helper constructor adds the shader module as VkDataGraphPipelineShaderModuleCreateInfoARM::module, get rid of it
        pipeline.shader_module_ci_.module = VK_NULL_HANDLE;
        // add the shader info in pNext chain
        vvl::PnextChainAdd(&pipeline.pipeline_ci_, &shader_module_create_info);

        m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineShaderModuleCreateInfoARM-dataGraphSpecializationConstants-09849");
        pipeline.CreateDataGraphPipeline();
        m_errorMonitor->VerifyFound();
    }
}

TEST_F(NegativeDataGraph, DataGraphShaderModuleCreateInfoIncorrectName) {
    TEST_DESCRIPTION(
        "Create a datagraph pipeline where VkDataGraphPipelineShaderModuleCreateInfoARM::pName doesn't match the name in the spirv "
        "code.");
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    pipeline.shader_module_ci_.pName = "NOT_the_correct_name";
    m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineShaderModuleCreateInfoARM-pName-09872");
    pipeline.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, DataGraphShaderModuleCreateInfoHasModuleAndShaderModuleCreateInfo) {
    TEST_DESCRIPTION(
        "Create a datagraph pipeline where VkDataGraphPipelineShaderModuleCreateInfoARM::module is not null, but it also includes "
        "a VkShaderModuleCreateInfo structure in its pNext chain.");
    RETURN_IF_SKIP(InitBasicDataGraph());

    // the pipeline constructor adds the shader module in the "normal" way, as VkDataGraphPipelineShaderModuleCreateInfoARM::module
    vkt::dg::DataGraphPipelineHelper pipeline(*this);

    // also add the same ShaderModule in the pNext chain
    spvtools::SpirvTools tools{SPV_ENV_UNIVERSAL_1_6};
    const std::string& spirv_source = vkt::dg::DataGraphPipelineHelper::GetSpirvBasicDataGraph();
    std::vector<uint32_t> spirv_binary;
    if (!tools.Assemble(spirv_source, &spirv_binary)) {
        Monitor().SetError("Failed to compile SPIRV shader module");
        return;
    }
    VkShaderModuleCreateInfo shader_module_create_info = vku::InitStructHelper();
    shader_module_create_info.codeSize = spirv_binary.size() * sizeof(uint32_t);
    shader_module_create_info.pCode = spirv_binary.data();

    // add the shader info in VkDataGraphPipelineShaderModuleCreateInfoARM::pNext
    {
        pipeline.shader_module_ci_.pNext = &shader_module_create_info;

        m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineShaderModuleCreateInfoARM-pNext-09873");
        pipeline.CreateDataGraphPipeline();
        m_errorMonitor->VerifyFound();
    }

    // add the shader info in VkDataGraphPipelineCreateInfoARM::pNext
    {
        // rearrange the pNext chain, we should get the same result
        pipeline.shader_module_ci_.pNext = nullptr;
        pipeline.pipeline_ci_.pNext = &shader_module_create_info;
        shader_module_create_info.pNext = &pipeline.shader_module_ci_;

        m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineShaderModuleCreateInfoARM-pNext-09873");
        pipeline.CreateDataGraphPipeline();
        m_errorMonitor->VerifyFound();
    }
}

TEST_F(NegativeDataGraph, DataGraphShaderModuleCreateInfoInvalidModule) {
    TEST_DESCRIPTION(
        "Create a datagraph pipeline where VkDataGraphPipelineShaderModuleCreateInfoARM::module is NULL and there is no "
        "VkShaderModuleCreateInfo structure in its pNext chain.");
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    pipeline.shader_module_ci_.module = VK_NULL_HANDLE;
    m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineShaderModuleCreateInfoARM-pNext-09874");
    pipeline.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, DataGraphShaderModuleCreateInfoDescriptorBufferNoFeature) {
    TEST_DESCRIPTION("Create a datagraph pipeline with the flag for descriptor buffers but the feature is not enabled.");
    // NOT adding vkt::Feature::dataGraphDescriptorBuffer
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    pipeline.pipeline_ci_.flags |= VK_PIPELINE_CREATE_2_DESCRIPTOR_BUFFER_BIT_EXT;
    m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineCreateInfoARM-dataGraphDescriptorBuffer-09885");
    pipeline.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, DataGraphShaderModuleNoFeature) {
    TEST_DESCRIPTION("Try to create a datagraph without the dataGraphShaderModule feature.");
    // add all the requirements of InitBasicDataGraph except dataGraphShaderModule
    SetTargetApiVersion(VK_API_VERSION_1_4);
    AddRequiredExtensions(VK_ARM_TENSORS_EXTENSION_NAME);
    AddRequiredExtensions(VK_ARM_DATA_GRAPH_EXTENSION_NAME);
    AddRequiredFeature(vkt::Feature::tensors);
    AddRequiredFeature(vkt::Feature::dataGraph);
    AddRequiredFeature(vkt::Feature::shaderTensorAccess);
    AddRequiredFeature(vkt::Feature::vulkanMemoryModel);
    AddRequiredFeature(vkt::Feature::shaderInt8);
    RETURN_IF_SKIP(Init());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineCreateInfoARM-dataGraphShaderModule-09886");
    pipeline.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, DataGraphWrongCreateInfoStructs) {
    TEST_DESCRIPTION("None or too many of the required info structures passed in pNext of vkCreateDataGraphPipelinesARM.");
    AddRequiredFeature(vkt::Feature::pipelineCreationCacheControl);
    RETURN_IF_SKIP(InitBasicDataGraph());

    // none of the structs included
    {
        vkt::dg::DataGraphPipelineHelper pipeline(*this);
        pipeline.pipeline_ci_.pNext = nullptr;
        m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineCreateInfoARM-pNext-09977");
        pipeline.CreateDataGraphPipeline();
        m_errorMonitor->VerifyFound();
    }

    // too many of the structs included
    {
        vkt::dg::DataGraphPipelineHelper pipeline(*this);
        // a VkDataGraphPipelineShaderModuleCreateInfoARM is already included by the helper, add also
        // a VkDataGraphPipelineIdentifierCreateInfoARM (can't have both)
        VkDataGraphPipelineIdentifierCreateInfoARM pipeline_id = vku::InitStructHelper();
        constexpr uint8_t dummy_data = 1;
        pipeline_id.identifierSize = 1;
        pipeline_id.pIdentifier = &dummy_data;
        vvl::PnextChainAdd(&pipeline.pipeline_ci_, &pipeline_id);
        pipeline.pipeline_ci_.flags |= VK_PIPELINE_CREATE_2_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT;
        pipeline.pipeline_ci_.pResourceInfos = nullptr;
        pipeline.pipeline_ci_.resourceInfoCount = 0;

        VkPipelineCache pipeline_cache;
        VkPipelineCacheCreateInfo cache_create_info = vku::InitStructHelper();
        cache_create_info.initialDataSize = 0;
        VkResult err = vk::CreatePipelineCache(device(), &cache_create_info, nullptr, &pipeline_cache);
        ASSERT_EQ(VK_SUCCESS, err);

        m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineCreateInfoARM-pNext-09977");
        pipeline.CreateDataGraphPipeline(pipeline_cache);
        m_errorMonitor->VerifyFound();
        vk::DestroyPipelineCache(device(), pipeline_cache, nullptr);
    }
}

TEST_F(NegativeDataGraph, DataGraphShaderModuleSpirvArrayWrongSize) {
    TEST_DESCRIPTION("Create a datagraph with Vulkan resource arrays not matching the spirv.");
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::HelperParameters params;
    params.graph_variant = vkt::dg::GraphVariant::AddTensorArraySpirv;
    vkt::dg::DataGraphPipelineHelper pipeline(*this, params);

    // override the DataGraphPipelineHelper constructor: set the wrong array length and recreate the layout
    pipeline.descriptor_set_layout_bindings_[0].descriptorCount = 1;  // ERROR 9934: the spirv code defines an array of 2
    pipeline.descriptor_set_.reset(new OneOffDescriptorSet(pipeline.device_, pipeline.descriptor_set_layout_bindings_));
    pipeline.CreatePipelineLayout();

    m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineCreateInfoARM-layout-09934");
    // tensor arrays also triggers VU 9923s (2 tensors, 2 errors)
    m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-pNext-09923");
    m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-pNext-09923");
    pipeline.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, DataGraphShaderModuleSpirvRuntimeArraySizeZero) {
    TEST_DESCRIPTION("Create a datagraph where a Vulkan resource is a runtime array with count 0.");
    SetTargetApiVersion(VK_API_VERSION_1_4);
    AddRequiredFeature(vkt::Feature::runtimeDescriptorArray);
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::HelperParameters params;
    params.graph_variant = vkt::dg::GraphVariant::AddRuntimeTensorArraySpirv;
    vkt::dg::DataGraphPipelineHelper pipeline(*this, params);

    // override the DataGraphPipelineHelper constructor: set the wrong array length and recreate the layout
    pipeline.descriptor_set_layout_bindings_[0].descriptorCount = 0;  // ERROR 9934: OpTypeRuntimeArray needs > 0
    pipeline.descriptor_set_.reset(new OneOffDescriptorSet(pipeline.device_, pipeline.descriptor_set_layout_bindings_));
    pipeline.CreatePipelineLayout();

    m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineCreateInfoARM-layout-09934");
    // tensor arrays also triggers VU 9923s (2 tensors, 2 errors)
    m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-pNext-09923");
    m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-pNext-09923");
    pipeline.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, DataGraphTensorNoShape) {
    TEST_DESCRIPTION("Create a datagraph using tensors without shape.");
    RETURN_IF_SKIP(InitBasicDataGraph());

    // input and output variables are tensors without a shape, rank only (%tensor_r4)
    static const char* tensorNoShapeDataGraphSpirv = R"spirv(
                            OpCapability GraphARM
                            OpCapability TensorsARM
                            OpCapability Int8
                            OpCapability Shader
                            OpCapability VulkanMemoryModel
                            OpCapability Matrix
                            OpExtension "SPV_ARM_graph"
                            OpExtension "SPV_ARM_tensors"
                            OpExtension "SPV_KHR_vulkan_memory_model"
                    %tosa = OpExtInstImport "TOSA.001000.1"
                            OpMemoryModel Logical Vulkan
                            OpName %main_arg_0 "main_arg_0"
                            OpName %main_res_0 "main_res_0"
                            OpDecorate %main_arg_0 Binding 0
                            OpDecorate %main_arg_0 DescriptorSet 0
                            OpDecorate %main_res_0 Binding 1
                            OpDecorate %main_res_0 DescriptorSet 0
                      %i8 = OpTypeInt 8 0
                     %i32 = OpTypeInt 32 0
                   %i32_0 = OpConstant %i32 0
                   %i32_1 = OpConstant %i32 1
                   %i32_2 = OpConstant %i32 2
                   %i32_4 = OpConstant %i32 4
               %i32_arr_1 = OpTypeArray %i32 %i32_1
               %i32_arr_4 = OpTypeArray %i32 %i32_4
             %i32_arr_1_2 = OpConstantComposite %i32_arr_1 %i32_2
             %i32_arr_1_4 = OpConstantComposite %i32_arr_1 %i32_4
            %i32_2_tensor = OpTypeTensorARM %i32 %i32_1 %i32_arr_1_2
            %i32_4_tensor = OpTypeTensorARM %i32 %i32_1 %i32_arr_1_4
               %tensor_r4 = OpTypeTensorARM %i8 %i32_4
           %ptr_tensor_r4 = OpTypePointer UniformConstant %tensor_r4
        %i32_2_tensor_2_2 = OpConstantComposite %i32_2_tensor %i32_2 %i32_2
    %i32_4_tensor_0_0_0_0 = OpConstantComposite %i32_4_tensor %i32_0 %i32_0 %i32_0 %i32_0
              %main_arg_0 = OpVariable %ptr_tensor_r4 UniformConstant
              %main_res_0 = OpVariable %ptr_tensor_r4 UniformConstant
              %graph_type = OpTypeGraphARM 1 %tensor_r4 %tensor_r4
                            OpGraphEntryPointARM %graph_0 "main" %main_arg_0 %main_res_0
                 %graph_0 = OpGraphARM %graph_type
                    %in_0 = OpGraphInputARM %tensor_r4 %i32_0
                    %op_0 = OpExtInst %tensor_r4 %tosa MAX_POOL2D  %i32_2_tensor_2_2 %i32_2_tensor_2_2 %i32_4_tensor_0_0_0_0 %i32_0 %in_0
                    %op_1 = OpExtInst %tensor_r4 %tosa MAX_POOL2D  %i32_2_tensor_2_2 %i32_2_tensor_2_2 %i32_4_tensor_0_0_0_0 %i32_0 %op_0
                            OpGraphSetOutputARM %op_1 %i32_0
                            OpGraphEndARM
)spirv";

    vkt::dg::HelperParameters params;
    params.spirv_source = tensorNoShapeDataGraphSpirv;
    vkt::dg::DataGraphPipelineHelper pipeline(*this, params);

    // 2 tensors, 2 errors
    m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-pNext-09919");
    m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-pNext-09919");
    pipeline.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, DataGraphPipelineIdentifierNoFlag) {
    TEST_DESCRIPTION("Create a datagraph with the ARM cache but the wrong flags.");
    AddRequiredFeature(vkt::Feature::pipelineCreationCacheControl);
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    VkDataGraphPipelineIdentifierCreateInfoARM pipeline_id = vku::InitStructHelper();
    constexpr uint8_t dummy_data = 1;
    pipeline_id.identifierSize = 1;
    pipeline_id.pIdentifier = &dummy_data;
    // replace the pNext chain, to remove the VkDataGraphPipelineShaderModuleCreateInfoARM added in the helper constructor
    pipeline.pipeline_ci_.pNext = &pipeline_id;
    // NOT setting VK_PIPELINE_CREATE_2_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT in the flags: ERROR
    pipeline.pipeline_ci_.pResourceInfos = nullptr;
    pipeline.pipeline_ci_.resourceInfoCount = 0;

    VkPipelineCache pipeline_cache;
    VkPipelineCacheCreateInfo cache_create_info = vku::InitStructHelper();
    cache_create_info.initialDataSize = 0;
    VkResult err = vk::CreatePipelineCache(device(), &cache_create_info, nullptr, &pipeline_cache);
    ASSERT_EQ(VK_SUCCESS, err);

    m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineCreateInfoARM-None-11840");
    pipeline.CreateDataGraphPipeline(pipeline_cache);
    m_errorMonitor->VerifyFound();
    vk::DestroyPipelineCache(device(), pipeline_cache, nullptr);
}

TEST_F(NegativeDataGraph, DataGraphPipelineIdentifierHasResources) {
    TEST_DESCRIPTION("Create a datagraph with the ARM cache but resources info still included.");
    AddRequiredFeature(vkt::Feature::pipelineCreationCacheControl);
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    VkDataGraphPipelineIdentifierCreateInfoARM pipeline_id = vku::InitStructHelper();
    constexpr uint8_t dummy_data = 1;
    pipeline_id.identifierSize = 1;
    pipeline_id.pIdentifier = &dummy_data;
    // replace the pNext chain, to remove the VkDataGraphPipelineShaderModuleCreateInfoARM added in the helper constructor
    pipeline.pipeline_ci_.pNext = &pipeline_id;
    // set the correct flags, but leave pResourceInfos
    pipeline.pipeline_ci_.flags |= VK_PIPELINE_CREATE_2_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT;

    VkPipelineCache pipeline_cache;
    VkPipelineCacheCreateInfo cache_create_info = vku::InitStructHelper();
    cache_create_info.initialDataSize = 0;
    VkResult err = vk::CreatePipelineCache(device(), &cache_create_info, nullptr, &pipeline_cache);
    ASSERT_EQ(VK_SUCCESS, err);

    m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineCreateInfoARM-None-12363");
    pipeline.CreateDataGraphPipeline(pipeline_cache);
    m_errorMonitor->VerifyFound();
    vk::DestroyPipelineCache(device(), pipeline_cache, nullptr);
}

TEST_F(NegativeDataGraph, DataGraphCreateInfoResourceCountZero) {
    TEST_DESCRIPTION("Create a datagraph with the ARM cache, resource count is zero, but resource pointer is not null.");
    AddRequiredFeature(vkt::Feature::pipelineCreationCacheControl);
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    VkDataGraphPipelineIdentifierCreateInfoARM pipeline_id = vku::InitStructHelper();
    constexpr uint8_t dummy_data = 1;
    pipeline_id.identifierSize = 1;
    pipeline_id.pIdentifier = &dummy_data;
    // replace the pNext chain, to remove the VkDataGraphPipelineShaderModuleCreateInfoARM added in the helper constructor
    pipeline.pipeline_ci_.pNext = &pipeline_id;
    // set the correct flags, set resourceInfoCount to 0, but leave pResourceInfos
    pipeline.pipeline_ci_.flags |= VK_PIPELINE_CREATE_2_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT;
    pipeline.pipeline_ci_.resourceInfoCount = 0;

    VkPipelineCache pipeline_cache;
    VkPipelineCacheCreateInfo cache_create_info = vku::InitStructHelper();
    cache_create_info.initialDataSize = 0;
    VkResult err = vk::CreatePipelineCache(device(), &cache_create_info, nullptr, &pipeline_cache);
    ASSERT_EQ(VK_SUCCESS, err);

    m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineCreateInfoARM-resourceInfoCount-12364");
    pipeline.CreateDataGraphPipeline(pipeline_cache);
    m_errorMonitor->VerifyFound();
    vk::DestroyPipelineCache(device(), pipeline_cache, nullptr);
}

TEST_F(NegativeDataGraph, DataGraphCreateInfoNullResources) {
    TEST_DESCRIPTION("Create a datagraph but resource count is zero.");
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    pipeline.pipeline_ci_.pResourceInfos = nullptr;  // this is to avoid VU 12364
    pipeline.pipeline_ci_.resourceInfoCount = 0;

    m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineCreateInfoARM-None-12365");
    // we also get 2x this because the spirv still includes 2 resources
    m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-pNext-09923");
    m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-pNext-09923");
    pipeline.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, DataGraphPipelineIdentifierNoCache) {
    TEST_DESCRIPTION("Create a datagraph using the cache identifier but without a valid cache object.");
    AddRequiredFeature(vkt::Feature::pipelineCreationCacheControl);
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);

    // add the cache identifier object
    VkDataGraphPipelineIdentifierCreateInfoARM pipeline_id = vku::InitStructHelper();
    constexpr uint8_t dummy_data = 1;
    pipeline_id.identifierSize = 1;
    pipeline_id.pIdentifier = &dummy_data;
    vvl::PnextChainAdd(&pipeline.pipeline_ci_, &pipeline_id);
    pipeline.pipeline_ci_.flags |= VK_PIPELINE_CREATE_2_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT;

    // ... but pass a NULL handle for the cache object
    VkPipelineCache pipeline_cache = VK_NULL_HANDLE;

    m_errorMonitor->SetDesiredError("VUID-vkCreateDataGraphPipelinesARM-pNext-09928");
    pipeline.CreateDataGraphPipeline(pipeline_cache);
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, DataGraphOpGraphConstantARMNoShape) {
    TEST_DESCRIPTION("Try to create a datagraph with an OpGraphConstantARM defined on a tensor without shape");
    RETURN_IF_SKIP(InitBasicDataGraph());

    // inject in the spirv a constant based on a shapeless tensor
    vkt::dg::ModifiableShaderParameters spirv_params;
    spirv_params.types = R"(
    %i32_1_2_4_4_tensor = OpTypeTensorARM %uint %uint_4 %uint_arr_4_1_2_4_4
    %tensor_r4 = OpTypeTensorARM %uint %uint_4
    %constant_no_shape = OpGraphConstantARM %tensor_r4 0)";
    spirv_params.instructions = R"(
    %i32_op_1 = OpExtInst %i32_1_2_4_4_tensor %tosa CAST %op_1
    %dummy = OpExtInst %i32_1_2_4_4_tensor %tosa ADD %i32_op_1 %constant_no_shape)";

    const std::string& spirv_string = vkt::dg::DataGraphPipelineHelper::GetSpirvModifyableDataGraph(spirv_params);

    vkt::dg::HelperParameters params;
    params.spirv_source = spirv_string.c_str();
    vkt::dg::DataGraphPipelineHelper pipeline(*this, params);

    VkDataGraphPipelineConstantARM constant = GetConstant();
    pipeline.shader_module_ci_.constantCount = 1;
    pipeline.shader_module_ci_.pConstants = &constant;

    m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-pNext-09920");
    pipeline.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, DataGraphNoConstant) {
    TEST_DESCRIPTION("Try to create a datagraph without a required constant.");
    RETURN_IF_SKIP(InitBasicDataGraph());

    // spirv has a constant, but we don't define it in Vulkan
    const std::string spirv_string = vkt::dg::DataGraphPipelineHelper::GetSpirvConstantDataGraph();

    vkt::dg::HelperParameters params;
    params.spirv_source = spirv_string.c_str();
    // helper constructor does NOT initialize the constant
    vkt::dg::DataGraphPipelineHelper pipeline(*this, params);
    m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-pNext-09921");
    pipeline.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, DataGraphOpGraphConstantARMNotTensor) {
    TEST_DESCRIPTION("Try to create a datagraph with an OpGraphConstantARM that is not a tensor");
    RETURN_IF_SKIP(InitBasicDataGraph());

    // inject in the spirv constants based on a scalar, not tensors
    vkt::dg::ModifiableShaderParameters spirv_params;
    spirv_params.types = R"(%constant_scalar_min = OpGraphConstantARM %uint 0
                %constant_scalar_max = OpGraphConstantARM %uint 128)";
    spirv_params.instructions =
        "%dummy = OpExtInst %uchar_1_2_4_4_tensor %tosa CLAMP %op_1 %constant_scalar_min %constant_scalar_max %uint_2";
    const std::string& spirv_string = vkt::dg::DataGraphPipelineHelper::GetSpirvModifyableDataGraph(spirv_params);

    vkt::dg::HelperParameters params;
    params.spirv_source = spirv_string.c_str();

    m_errorMonitor->SetDesiredError("VUID-VkShaderModuleCreateInfo-pCode-08737");
    VkShaderObj shader(*m_device, spirv_string.c_str(), VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_4, SPV_SOURCE_ASM);
    m_errorMonitor->VerifyFound();
}

// NOTE: This is meant as a positive test for tensor arrays.
// Right now they are illegal, but they will be legal at some point.
// When this happens, remove the SetDesiredError and move to data_graph_positive.cpp
TEST_F(NegativeDataGraph, DataGraphShaderModuleSpirvArray) {
    TEST_DESCRIPTION("Create a datagraph using a tensor array as input.");
    RETURN_IF_SKIP(InitBasicDataGraph());

    // currently tensor arrays are banned by VU 9923. The mock ICD doesn't create a pipeline, so we can still test
    // successfully if we ignore it, but a real driver will actually try to create something illegal, and likely crash
    if (!IsPlatformMockICD()) {
        GTEST_SKIP() << "Test only supported by MockICD";
    }

    vkt::dg::HelperParameters params;
    params.graph_variant = vkt::dg::GraphVariant::AddTensorArraySpirv;
    vkt::dg::DataGraphPipelineHelper pipeline(*this, params);

    m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-pNext-09923");
    m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-pNext-09923");
    pipeline.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

// NOTE: This is meant as a positive test for tensor arrays.
// Right now they are illegal, but they will be legal at some point.
// When this happens, remove the SetDesiredError and move to data_graph_positive.cpp
TEST_F(NegativeDataGraph, DataGraphShaderModuleSpirvRuntimeArray) {
    TEST_DESCRIPTION("Create a datagraph using a tensor runtime array as input.");
    SetTargetApiVersion(VK_API_VERSION_1_4);
    AddRequiredFeature(vkt::Feature::runtimeDescriptorArray);
    RETURN_IF_SKIP(InitBasicDataGraph());

    // currently tensor arrays are banned by VU 9923. The mock ICD doesn't create a pipeline, so we can still test
    // successfully if we ignore it, but a real driver will actually try to create something illegal, and likely crash
    if (!IsPlatformMockICD()) {
        GTEST_SKIP() << "Test only supported by MockICD";
    }

    {
        // the helper constructs a layout with descriptorCount == 2, matching the size of the spirv array
        vkt::dg::HelperParameters params;
        params.graph_variant = vkt::dg::GraphVariant::AddRuntimeTensorArraySpirv;
        vkt::dg::DataGraphPipelineHelper pipeline(*this, params);

        // tensor arrays triggers VU 9923, we need to suppress it (2 tensors, 2 errors)
        m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-pNext-09923");
        m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-pNext-09923");
        pipeline.CreateDataGraphPipeline();
        m_errorMonitor->VerifyFound();
    }
    {
        vkt::dg::HelperParameters params;
        params.graph_variant = vkt::dg::GraphVariant::AddRuntimeTensorArraySpirv;
        vkt::dg::DataGraphPipelineHelper pipeline(*this, params);

        // override the DataGraphPipelineHelper constructor: set a bigger element count, runtime array will handle this
        pipeline.descriptor_set_layout_bindings_[0].descriptorCount = 3;
        pipeline.descriptor_set_.reset(new OneOffDescriptorSet(pipeline.device_, pipeline.descriptor_set_layout_bindings_));
        pipeline.CreatePipelineLayout();

        // tensor arrays triggers VU 9923, we need to suppress it (2 tensors, 2 errors)
        m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-pNext-09923");
        m_errorMonitor->SetDesiredError("VUID-RuntimeSpirv-pNext-09923");
        pipeline.CreateDataGraphPipeline();
        m_errorMonitor->VerifyFound();
    }
}

TEST_F(NegativeDataGraph, CmdDispatchWrongPipeline) {
    TEST_DESCRIPTION("Try to create a datagraph where session and command buffer are bound to different pipelines.");
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    pipeline.CreateDataGraphPipeline();

    // make a _copy_ of the command buffer pipeline, so everything else checks out, but it's NOT the same object
    VkDataGraphPipelineCreateInfoARM pipeline_ci = pipeline.pipeline_ci_;
    vkt::Pipeline pipeline_copy(*m_device, pipeline_ci);

    // bind the session to the _copy_ pipeline
    VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
    session_ci.dataGraphPipeline = pipeline_copy.handle();
    vkt::DataGraphPipelineSession session(*m_device, session_ci);
    // setup the command buffer with the _default_ pipeline, as usual.
    auto& bind_point_reqs = session.BindPointReqs();
    std::vector<vkt::DeviceMemory> device_mem(bind_point_reqs.size());
    session.AllocSessionMem(device_mem);
    auto session_bind_infos = InitSessionBindInfo(session, device_mem);
    vk::BindDataGraphPipelineSessionMemoryARM(*m_device, session_bind_infos.size(), session_bind_infos.data());

    pipeline.descriptor_set_->WriteDescriptorTensorInfo(0, &pipeline.tensor_views_[0]->handle(), 0);
    pipeline.descriptor_set_->WriteDescriptorTensorInfo(1, &pipeline.tensor_views_[1]->handle(), 0);
    pipeline.descriptor_set_->UpdateDescriptorSets();

    m_command_buffer.Begin();
    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, pipeline);
    vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, pipeline.pipeline_layout_, 0, 1,
                              &pipeline.descriptor_set_.get()->set_, 0, nullptr);

    m_errorMonitor->SetDesiredError("VUID-vkCmdDispatchDataGraphARM-dataGraphPipeline-09951");
    vk::CmdDispatchDataGraphARM(m_command_buffer, session, nullptr);
    m_command_buffer.End();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, CmdDispatchWrongTensorUsage) {
    TEST_DESCRIPTION("Create and execute a datagraph where tensors have the wrong usage flag");
    RETURN_IF_SKIP(InitBasicDataGraph());

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    pipeline.CreateDataGraphPipeline();

    VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
    session_ci.dataGraphPipeline = pipeline;
    vkt::DataGraphPipelineSession session(*m_device, session_ci);
    auto& bind_point_reqs = session.BindPointReqs();
    std::vector<vkt::DeviceMemory> device_mem(bind_point_reqs.size());
    session.AllocSessionMem(device_mem);
    auto session_bind_infos = InitSessionBindInfo(session, device_mem);
    vk::BindDataGraphPipelineSessionMemoryARM(*m_device, session_bind_infos.size(), session_bind_infos.data());

    // pass a tensor with the wrong usage into the pipeline
    VkTensorDescriptionARM wrong_desc = pipeline.GetTensorDesc(vkt::dg::TensorType::BASIC_SPIRV_IN);
    wrong_desc.usage = VK_TENSOR_USAGE_SHADER_BIT_ARM;  // should be VK_TENSOR_USAGE_DATA_GRAPH_BIT_ARM
    vkt::Tensor wrong_tensor;
    vkt::TensorView wrong_view;
    pipeline.InitTensor(wrong_tensor, wrong_view, wrong_desc);

    pipeline.descriptor_set_->WriteDescriptorTensorInfo(0, &wrong_view.handle(), 0);
    pipeline.descriptor_set_->WriteDescriptorTensorInfo(1, &pipeline.tensor_views_[1]->handle(), 0);
    pipeline.descriptor_set_->UpdateDescriptorSets();

    m_command_buffer.Begin();
    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, pipeline);
    vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, pipeline.pipeline_layout_, 0, 1,
                              &pipeline.descriptor_set_.get()->set_, 0, nullptr);

    m_errorMonitor->SetDesiredError("VUID-vkCmdDispatchDataGraphARM-pDescription-09900");
    vk::CmdDispatchDataGraphARM(m_command_buffer, session, nullptr);
    m_errorMonitor->VerifyFound();
    m_command_buffer.End();
}

TEST_F(NegativeDataGraph, BindPipelineCommandPoolFromWrongQueue) {
    TEST_DESCRIPTION("Try to bind a datagraph pipeline with a command buffer using the wrong queue");
    RETURN_IF_SKIP(InitBasicDataGraph());
    InitRenderTarget();

    // find a queue that doesn't support datagraph. Also require supporting either graphics
    // or compute, to avoid "VUID-vkCmdBindPipeline-commandBuffer-cmdpool"
    uint32_t queueFamilyIndex_no_datagraph_support = vvl::kU32Max;
    const auto q_props = m_device->Physical().queue_properties_;
    for (uint32_t i = 0; i < (uint32_t)q_props.size(); i++) {
        if ((q_props[i].queueFlags & VK_QUEUE_DATA_GRAPH_BIT_ARM) == 0 &&
            (q_props[i].queueFlags & (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT))) {
            queueFamilyIndex_no_datagraph_support = i;
            break;
        }
    }
    if (queueFamilyIndex_no_datagraph_support == vvl::kU32Max) {
        GTEST_SKIP() << "No queue found that doesn't support VK_QUEUE_DATA_GRAPH_BIT_ARM";
    }
    vkt::CommandPool commandPool(*m_device, queueFamilyIndex_no_datagraph_support);
    vkt::CommandBuffer commandBuffer(*m_device, commandPool);

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    pipeline.CreateDataGraphPipeline();

    commandBuffer.Begin();
    m_errorMonitor->SetDesiredError("VUID-vkCmdBindPipeline-pipelineBindPoint-09910");
    vk::CmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, pipeline);
    m_errorMonitor->VerifyFound();
    commandBuffer.End();
}

TEST_F(NegativeDataGraph, BindWrongBindPoint) {
    TEST_DESCRIPTION("Try to bind a graphics pipeline to the datagraph bind point");
    RETURN_IF_SKIP(InitBasicDataGraph());

    InitRenderTarget();
    CreatePipelineHelper pipeline(*this);
    pipeline.CreateGraphicsPipeline();

    m_command_buffer.Begin();
    m_errorMonitor->SetDesiredError("VUID-vkCmdBindPipeline-pipelineBindPoint-09911");
    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, pipeline);
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, CmdDispatchWrongQueue) {
    TEST_DESCRIPTION("Try to create a datagraph where the command buffer is allocated on a queue that does not support TOSA 1.0");
    RETURN_IF_SKIP(InitBasicDataGraph());

    // find a queue family that does NOT support TOSA
    const VkQueueFamilyDataGraphPropertiesARM tosa_1_0_property{
        VK_STRUCTURE_TYPE_QUEUE_FAMILY_DATA_GRAPH_PROPERTIES_ARM,
        nullptr,
        {VK_PHYSICAL_DEVICE_DATA_GRAPH_PROCESSING_ENGINE_TYPE_DEFAULT_ARM, false},
        {VK_PHYSICAL_DEVICE_DATA_GRAPH_OPERATION_TYPE_SPIRV_EXTENDED_INSTRUCTION_SET_ARM, "TOSA.001000.1", 0}};
    uint32_t queue_without_tosa_1_0_idx = UINT32_MAX;
    uint32_t n_queue_families;
    vk::GetPhysicalDeviceQueueFamilyProperties(Gpu(), &n_queue_families, nullptr);
    for (uint32_t qfi = 0; qfi < n_queue_families; qfi++) {
        uint32_t n_properties;
        vk::GetPhysicalDeviceQueueFamilyDataGraphPropertiesARM(Gpu(), qfi, &n_properties, nullptr);
        std::vector<VkQueueFamilyDataGraphPropertiesARM> properties(n_properties,
                                                                    {VK_STRUCTURE_TYPE_QUEUE_FAMILY_DATA_GRAPH_PROPERTIES_ARM});
        vk::GetPhysicalDeviceQueueFamilyDataGraphPropertiesARM(Gpu(), qfi, &n_properties, properties.data());

        bool queue_supports_tosa_1_0 = false;
        for (const auto& p : properties) {
            if (CompareVkQueueFamilyDataGraphPropertiesARM(tosa_1_0_property, p)) {
                queue_supports_tosa_1_0 = true;
                break;
            }
        }
        if (!queue_supports_tosa_1_0) {
            queue_without_tosa_1_0_idx = qfi;
            break;
        }
    }

    if (UINT32_MAX == queue_without_tosa_1_0_idx) {
        GTEST_SKIP() << "All the queues support TOSA, impossible to create the error condition, skip.";
    }

    // TODO: use queue with index queue_without_tosa_1_0_idx

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    pipeline.CreateDataGraphPipeline();

    VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
    session_ci.dataGraphPipeline = pipeline;
    vkt::DataGraphPipelineSession session(*m_device, session_ci);
    auto& bind_point_reqs = session.BindPointReqs();
    std::vector<vkt::DeviceMemory> device_mem(bind_point_reqs.size());
    session.AllocSessionMem(device_mem);
    auto session_bind_infos = InitSessionBindInfo(session, device_mem);
    vk::BindDataGraphPipelineSessionMemoryARM(*m_device, session_bind_infos.size(), session_bind_infos.data());

    pipeline.descriptor_set_->WriteDescriptorTensorInfo(0, &pipeline.tensor_views_[0]->handle(), 0);
    pipeline.descriptor_set_->WriteDescriptorTensorInfo(1, &pipeline.tensor_views_[1]->handle(), 0);
    pipeline.descriptor_set_->UpdateDescriptorSets();

    m_command_buffer.Begin();
    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, pipeline);
    vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, pipeline.pipeline_layout_, 0, 1,
                              &pipeline.descriptor_set_.get()->set_, 0, nullptr);
    m_errorMonitor->SetDesiredError("VUID-vkCmdDispatchDataGraphARM-commandBuffer-09941");
    vk::CmdDispatchDataGraphARM(m_command_buffer, session, nullptr);
    m_errorMonitor->VerifyFound();
    m_command_buffer.End();
}

TEST_F(NegativeDataGraph, OpticalFlowWrongImageFormat) {
    TEST_DESCRIPTION("Try to create an optical flow data graph pipeline with an unsupported input image format");

    RETURN_IF_SKIP(InitBasicDataGraph(true));

    vkt::dg::OpticalFlowHelper optical_flow(*this);

    /* Set an unsupported imageFormat. ASTC are block formats, incompatible with optical flow operations. */
    optical_flow.optical_flow_ci_.imageFormat = VK_FORMAT_ASTC_4x4_UNORM_BLOCK;

    m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineOpticalFlowCreateInfoARM-imageFormat-09968");
    (void)optical_flow.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, OpticalFlowWrongFlowVectorFormat) {
    TEST_DESCRIPTION("Try to create an optical flow data graph pipeline with an unsupported flow vector image format");

    RETURN_IF_SKIP(InitBasicDataGraph(true));

    vkt::dg::OpticalFlowHelper optical_flow(*this);

    /* Set an unsupported flowVectorFormat. ASTC are block formats, incompatible with optical flow operations. */
    optical_flow.optical_flow_ci_.flowVectorFormat = VK_FORMAT_ASTC_4x4_UNORM_BLOCK;

    m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineOpticalFlowCreateInfoARM-flowVectorFormat-09969");
    (void)optical_flow.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, OpticalFlowWrongCostFormat) {
    TEST_DESCRIPTION("Try to create an optical flow data graph pipeline with an unsupported cost image format");

    RETURN_IF_SKIP(InitBasicDataGraph(true));

    auto opt_flow_properties = vkt::dg::OpticalFlowHelper::QueryOpticalFlowProperties(*this, DefaultQueue()->family_index);
    if (!opt_flow_properties.costSupported) {
        GTEST_SKIP() << "Cost not supported, skip.";
    }

    vkt::dg::OpticalFlowHelper optical_flow(*this);
    /* Set an unsupported costFormat. ASTC are block formats, incompatible with optical flow operations. */
    optical_flow.optical_flow_ci_.costFormat = VK_FORMAT_ASTC_4x4_UNORM_BLOCK;

    m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineOpticalFlowCreateInfoARM-costFormat-09970");
    (void)optical_flow.CreateDataGraphPipeline();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, OpticalFlowWrongHint) {
    TEST_DESCRIPTION("Execute optical flow with a wrong meanFlowL1NormHint value");
    RETURN_IF_SKIP(InitBasicDataGraph(true));

    auto opt_flow_properties = vkt::dg::OpticalFlowHelper::QueryOpticalFlowProperties(*this, DefaultQueue()->family_index);
    if (!opt_flow_properties.hintSupported) {
        GTEST_SKIP() << "Hint not supported, skip.";
    }

    vkt::dg::OpticalFlowHelper optical_flow(*this);
    optical_flow.CreateDataGraphPipeline();
    optical_flow.SetupImageDescriptors();

    VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
    session_ci.dataGraphPipeline = optical_flow.dg_pipeline_;
    vkt::DataGraphPipelineSession session(*m_device, session_ci);

    auto& bind_point_reqs = session.BindPointReqs();
    std::vector<vkt::DeviceMemory> device_mem(bind_point_reqs.size());
    session.AllocSessionMem(device_mem);
    auto session_bind_infos = InitSessionBindInfo(session, device_mem);
    vk::BindDataGraphPipelineSessionMemoryARM(*m_device, session_bind_infos.size(), session_bind_infos.data());

    // set a hint that is too high
    VkDataGraphPipelineOpticalFlowDispatchInfoARM optical_flow_di = vku::InitStructHelper();
    optical_flow_di.meanFlowL1NormHint = optical_flow.optical_flow_ci_.height + 1;
    VkDataGraphPipelineDispatchInfoARM pipeline_di = vku::InitStructHelper(&optical_flow_di);

    m_command_buffer.Begin();
    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, optical_flow.dg_pipeline_);
    vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, optical_flow.PipelineLayout(), 0, 1,
                              optical_flow.DescriptorSet(), 0, nullptr);
    m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineOpticalFlowDispatchInfoARM-meanFlowL1NormHint-09976");
    vk::CmdDispatchDataGraphARM(m_command_buffer, session, &pipeline_di);
    m_errorMonitor->VerifyFound();
    m_command_buffer.End();
}

TEST_F(NegativeDataGraph, OpticalFlowNoCacheSession) {
    RETURN_IF_SKIP(InitBasicDataGraph(true));

    vkt::dg::OpticalFlowHelper optical_flow(*this);
    optical_flow.CreateDataGraphPipeline();
    optical_flow.SetupImageDescriptors();

    VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
    session_ci.dataGraphPipeline = optical_flow.dg_pipeline_;

    vkt::DataGraphPipelineSession session(*m_device, session_ci);
    std::vector<vkt::DeviceMemory> device_mem(session.BindPointReqs().size());
    session.AllocSessionMem(device_mem);
    std::vector<VkBindDataGraphPipelineSessionMemoryInfoARM> session_bind_infos =
        DataGraphTest::InitSessionBindInfo(session, device_mem);
    vk::BindDataGraphPipelineSessionMemoryARM(*m_device, session_bind_infos.size(), session_bind_infos.data());

    VkDataGraphPipelineOpticalFlowDispatchInfoARM optical_flow_di = vku::InitStructHelper();
    // Set one of the disallowed bits
    optical_flow_di.flags = VK_DATA_GRAPH_OPTICAL_FLOW_EXECUTE_REFERENCE_UNCHANGED_BIT_ARM;

    m_command_buffer.Begin();
    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, optical_flow.dg_pipeline_);
    vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, optical_flow.dg_pipeline_.pipeline_layout_,
                              0, 1, &optical_flow.dg_pipeline_.descriptor_set_.get()->set_, 0, nullptr);

    m_errorMonitor->SetDesiredError("VUID-vkCmdDispatchDataGraphARM-pInfo-09964");
    VkDataGraphPipelineDispatchInfoARM pipeline_di = vku::InitStructHelper(&optical_flow_di);
    vk::CmdDispatchDataGraphARM(m_command_buffer, session, &pipeline_di);
    m_errorMonitor->VerifyFound();

    m_command_buffer.End();
}

TEST_F(NegativeDataGraph, OpticalFlowDispatchWithoutOpticalFlowCreateInfo) {
    RETURN_IF_SKIP(InitBasicDataGraph(true));

    vkt::dg::DataGraphPipelineHelper pipeline(*this);
    pipeline.CreateDataGraphPipeline();

    pipeline.descriptor_set_->WriteDescriptorTensorInfo(0, &pipeline.tensor_views_[0]->handle(), 0);
    pipeline.descriptor_set_->WriteDescriptorTensorInfo(1, &pipeline.tensor_views_[1]->handle(), 0);
    pipeline.descriptor_set_->UpdateDescriptorSets();

    VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
    session_ci.dataGraphPipeline = pipeline;

    vkt::DataGraphPipelineSession session(*m_device, session_ci);
    std::vector<vkt::DeviceMemory> device_mem(session.BindPointReqs().size());
    session.AllocSessionMem(device_mem);

    std::vector<VkBindDataGraphPipelineSessionMemoryInfoARM> session_bind_infos =
        DataGraphTest::InitSessionBindInfo(session, device_mem);

    vk::BindDataGraphPipelineSessionMemoryARM(*m_device, session_bind_infos.size(), session_bind_infos.data());

    // Set optical flow dispatch info for `pipeline` which was not created with optical flow create info.
    VkDataGraphPipelineOpticalFlowDispatchInfoARM optical_flow_di = vku::InitStructHelper();
    VkDataGraphPipelineDispatchInfoARM pipeline_di = vku::InitStructHelper(&optical_flow_di);

    m_command_buffer.Begin();
    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, pipeline);
    vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, pipeline.pipeline_layout_, 0, 1,
                              &pipeline.descriptor_set_.get()->set_, 0, nullptr);

    m_errorMonitor->SetDesiredError("VUID-vkCmdDispatchDataGraphARM-nodeType-09980");
    vk::CmdDispatchDataGraphARM(m_command_buffer, session, &pipeline_di);
    m_errorMonitor->VerifyFound();

    m_command_buffer.End();
}

TEST_F(NegativeDataGraph, OpticalFlowConnectionsImageLayoutsNoUnifiedImageLayouts) {
    RETURN_IF_SKIP(InitBasicDataGraph(true));

    vkt::dg::OpticalFlowHelper optical_flow(*this);
    // Set the resource info image layout to VK_IMAGE_LAYOUT_GENERAL, where the connection references a descriptor in the
    // VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL layout. Since unifiedImageLayouts feature is not enabled this is invalid usage.
    optical_flow.image_layouts_[0].layout = VK_IMAGE_LAYOUT_GENERAL;

    optical_flow.CreateDataGraphPipeline();
    optical_flow.SetupImageDescriptors();

    VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
    session_ci.dataGraphPipeline = optical_flow.dg_pipeline_;

    vkt::DataGraphPipelineSession session(*m_device, session_ci);
    std::vector<vkt::DeviceMemory> device_mem(session.BindPointReqs().size());
    session.AllocSessionMem(device_mem);
    std::vector<VkBindDataGraphPipelineSessionMemoryInfoARM> session_bind_infos =
        DataGraphTest::InitSessionBindInfo(session, device_mem);
    vk::BindDataGraphPipelineSessionMemoryARM(*m_device, session_bind_infos.size(), session_bind_infos.data());

    VkDataGraphPipelineOpticalFlowDispatchInfoARM optical_flow_di = vku::InitStructHelper();
    VkDataGraphPipelineDispatchInfoARM pipeline_di = vku::InitStructHelper(&optical_flow_di);

    m_command_buffer.Begin();
    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, optical_flow.dg_pipeline_);
    vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, optical_flow.dg_pipeline_.pipeline_layout_,
                              0, 1, &optical_flow.dg_pipeline_.descriptor_set_.get()->set_, 0, nullptr);

    m_errorMonitor->SetDesiredError("VUID-vkCmdDispatchDataGraphARM-nodeType-09981");
    vk::CmdDispatchDataGraphARM(m_command_buffer, session, &pipeline_di);
    m_errorMonitor->VerifyFound();

    m_command_buffer.End();
}

TEST_F(NegativeDataGraph, OpticalFlowConnectionsImageLayoutsUnifiedImageLayouts) {
    AddRequiredFeature(vkt::Feature::unifiedImageLayouts);
    RETURN_IF_SKIP(InitBasicDataGraph(true));

    vkt::dg::OpticalFlowHelper optical_flow(*this);
    // Set the resource info image layout to VK_IMAGE_LAYOUT_UNDEFINED, where the connection references a descriptor in the
    // VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL layout.
    optical_flow.image_layouts_[0].layout = VK_IMAGE_LAYOUT_UNDEFINED;
    optical_flow.CreateDataGraphPipeline();
    optical_flow.SetupImageDescriptors();

    VkDataGraphPipelineSessionCreateInfoARM session_ci = vku::InitStructHelper();
    session_ci.dataGraphPipeline = optical_flow.dg_pipeline_;

    vkt::DataGraphPipelineSession session(*m_device, session_ci);
    std::vector<vkt::DeviceMemory> device_mem(session.BindPointReqs().size());
    session.AllocSessionMem(device_mem);
    std::vector<VkBindDataGraphPipelineSessionMemoryInfoARM> session_bind_infos =
        DataGraphTest::InitSessionBindInfo(session, device_mem);
    vk::BindDataGraphPipelineSessionMemoryARM(*m_device, session_bind_infos.size(), session_bind_infos.data());

    VkDataGraphPipelineOpticalFlowDispatchInfoARM optical_flow_di = vku::InitStructHelper();
    VkDataGraphPipelineDispatchInfoARM pipeline_di = vku::InitStructHelper(&optical_flow_di);

    m_command_buffer.Begin();
    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, optical_flow.dg_pipeline_);
    vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_DATA_GRAPH_ARM, optical_flow.dg_pipeline_.pipeline_layout_,
                              0, 1, &optical_flow.dg_pipeline_.descriptor_set_.get()->set_, 0, nullptr);

    m_errorMonitor->SetDesiredError("VUID-vkCmdDispatchDataGraphARM-nodeType-09981");
    vk::CmdDispatchDataGraphARM(m_command_buffer, session, &pipeline_di);
    m_errorMonitor->VerifyFound();

    m_command_buffer.End();
}

TEST_F(NegativeDataGraph, OpticalFlowMissingOpticalFlowCreateInfo) {
    TEST_DESCRIPTION("Try to create an optical flow datagraph without a VkDataGraphPipelineOpticalFlowCreateInfoARM structure");
    RETURN_IF_SKIP(InitBasicDataGraph(true));

    vkt::dg::OpticalFlowHelper optical_flow(*this);

    // pipeline_ci_.pNext points to optical_flow_ci_: skip one link to remove it from the chain
    optical_flow.dg_pipeline_.pipeline_ci_.pNext = optical_flow.optical_flow_ci_.pNext;

    m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineSingleNodeCreateInfoARM-nodeType-09963");
    ASSERT_EQ(VK_ERROR_VALIDATION_FAILED, optical_flow.dg_pipeline_.CreateDataGraphPipeline());
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, OpticalFlowWrongNumberOfInputs) {
    TEST_DESCRIPTION("Try to create an optical flow datagraph with a missing or too many input connections");
    RETURN_IF_SKIP(InitBasicDataGraph(true));

    for (uint32_t n : {0, 2}) {
        vkt::dg::OfHelperParameters params;
        params.n_inputs = n;
        vkt::dg::OpticalFlowHelper optical_flow(*this, params);

        m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineSingleNodeCreateInfoARM-nodeType-09978");
        ASSERT_EQ(VK_ERROR_VALIDATION_FAILED, optical_flow.dg_pipeline_.CreateDataGraphPipeline());
        m_errorMonitor->VerifyFound();
    }
}

TEST_F(NegativeDataGraph, OpticalFlowWrongNumberOfReferences) {
    TEST_DESCRIPTION("Try to create an optical flow datagraph with a missing or too many reference connections");
    RETURN_IF_SKIP(InitBasicDataGraph(true));

    for (uint32_t n : {0, 2}) {
        vkt::dg::OfHelperParameters params;
        params.n_references = n;
        vkt::dg::OpticalFlowHelper optical_flow(*this, params);

        m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineSingleNodeCreateInfoARM-nodeType-09978");
        ASSERT_EQ(VK_ERROR_VALIDATION_FAILED, optical_flow.dg_pipeline_.CreateDataGraphPipeline());
        m_errorMonitor->VerifyFound();
    }
}

TEST_F(NegativeDataGraph, OpticalFlowWrongNumberOfOutputs) {
    TEST_DESCRIPTION("Try to create an optical flow datagraph with a missing or too many output connections");
    RETURN_IF_SKIP(InitBasicDataGraph(true));

    for (uint32_t n : {0, 2}) {
        vkt::dg::OfHelperParameters params;
        params.n_outputs = n;
        vkt::dg::OpticalFlowHelper optical_flow(*this, params);

        m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineSingleNodeCreateInfoARM-nodeType-09978");
        ASSERT_EQ(VK_ERROR_VALIDATION_FAILED, optical_flow.dg_pipeline_.CreateDataGraphPipeline());
        m_errorMonitor->VerifyFound();
    }
}

TEST_F(NegativeDataGraph, OpticalFlowWrongNumberOfHints) {
    TEST_DESCRIPTION("Try to create an optical flow datagraph with hintGridSize > 0 but missing or too many hint connections");
    RETURN_IF_SKIP(InitBasicDataGraph(true));

    uint32_t queue_index = DefaultQueue()->family_index;

    auto opt_flow_properties = vkt::dg::OpticalFlowHelper::QueryOpticalFlowProperties(*this, queue_index);
    if (!opt_flow_properties.hintSupported) {
        GTEST_SKIP() << "Hint not supported, skip.";
    }
    uint32_t hint_grid_size = vkt::dg::OpticalFlowHelper::GetAnyOpticalFlowGridSize(opt_flow_properties);

    for (uint32_t n : {0, 2}) {
        vkt::dg::OfHelperParameters params;
        params.n_hints = n;  // force the wrong number of hint images
        params.hintGridSize = hint_grid_size;
        vkt::dg::OpticalFlowHelper optical_flow(*this, params);

        m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineSingleNodeCreateInfoARM-nodeType-09979");
        ASSERT_EQ(VK_ERROR_VALIDATION_FAILED, optical_flow.dg_pipeline_.CreateDataGraphPipeline());
        m_errorMonitor->VerifyFound();
    }
}

TEST_F(NegativeDataGraph, OpticalFlowNoHint) {
    TEST_DESCRIPTION("Try to create an optical flow datagraph using a hint image but the feature is not supported");
    RETURN_IF_SKIP(InitBasicDataGraph(true));

    uint32_t n_queue_families;
    vk::GetPhysicalDeviceQueueFamilyProperties(Gpu(), &n_queue_families, nullptr);
    uint32_t no_hint_index = UINT32_MAX;
    for (uint32_t qfi = 0; qfi < n_queue_families; qfi++) {
        auto of_support_property = vkt::dg::OpticalFlowHelper::GetOpticalFlowSupportProperty(*this, qfi);
        if (std::nullopt == of_support_property) {
            continue;
        }

        VkQueueFamilyDataGraphOpticalFlowPropertiesARM op_flow_properties = vku::InitStructHelper();
        ASSERT_EQ(VK_SUCCESS,
                  vk::GetPhysicalDeviceQueueFamilyDataGraphEngineOperationPropertiesARM(
                      Gpu(), qfi, &of_support_property.value(), reinterpret_cast<VkBaseOutStructure*>(&op_flow_properties)));
        if (!op_flow_properties.hintSupported) {
            no_hint_index = qfi;
            break;
        }
    }
    if (UINT32_MAX == no_hint_index) {
        GTEST_SKIP() << "All the queues support optical flow hint, impossible to create the error condition, skip.";
    }

    // TODO: use queue with index no_hint_index
    vkt::dg::OpticalFlowHelper optical_flow(*this);

    // force the hint flag
    optical_flow.optical_flow_ci_.flags |= VK_DATA_GRAPH_OPTICAL_FLOW_CREATE_ENABLE_HINT_BIT_ARM;

    m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineOpticalFlowCreateInfoARM-flags-09974");
    ASSERT_EQ(VK_ERROR_VALIDATION_FAILED, optical_flow.CreateDataGraphPipeline());
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDataGraph, OpticalFlowNoCost) {
    TEST_DESCRIPTION("Try to create an optical flow datagraph using a cost image but the feature is not supported");
    RETURN_IF_SKIP(InitBasicDataGraph(true));

    uint32_t n_queue_families;
    vk::GetPhysicalDeviceQueueFamilyProperties(Gpu(), &n_queue_families, nullptr);
    uint32_t no_cost_index = UINT32_MAX;
    for (uint32_t qfi = 0; qfi < n_queue_families; qfi++) {
        auto of_support_property = vkt::dg::OpticalFlowHelper::GetOpticalFlowSupportProperty(*this, qfi);
        if (std::nullopt == of_support_property) {
            continue;
        }

        VkQueueFamilyDataGraphOpticalFlowPropertiesARM op_flow_properties = vku::InitStructHelper();
        ASSERT_EQ(VK_SUCCESS,
                  vk::GetPhysicalDeviceQueueFamilyDataGraphEngineOperationPropertiesARM(
                      Gpu(), qfi, &of_support_property.value(), reinterpret_cast<VkBaseOutStructure*>(&op_flow_properties)));
        if (!op_flow_properties.costSupported) {
            no_cost_index = qfi;
            break;
        }
    }
    if (UINT32_MAX == no_cost_index) {
        GTEST_SKIP() << "All the queues support optical flow cost, impossible to create the error condition, skip.";
    }

    // TODO: use queue with index no_cost_index
    vkt::dg::OpticalFlowHelper optical_flow(*this);

    // force the cost flag, and also a format to avoid 9970
    optical_flow.optical_flow_ci_.flags |= VK_DATA_GRAPH_OPTICAL_FLOW_CREATE_ENABLE_COST_BIT_ARM;
    optical_flow.optical_flow_ci_.costFormat = optical_flow.GetAnyOpticalFlowFormat(VK_DATA_GRAPH_OPTICAL_FLOW_IMAGE_USAGE_COST_BIT_ARM);

    m_errorMonitor->SetDesiredError("VUID-VkDataGraphPipelineOpticalFlowCreateInfoARM-flags-09975");
    ASSERT_EQ(VK_ERROR_VALIDATION_FAILED, optical_flow.CreateDataGraphPipeline());
    m_errorMonitor->VerifyFound();
}
