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

#include "layer_validation_tests.h"
#include "shader_helper.h"

class PositiveDeviceQueue : public VkLayerTest {};

TEST_F(PositiveDeviceQueue, NoQueues) {
    SetTargetApiVersion(VK_API_VERSION_1_1);
    AddRequiredExtensions(VK_KHR_MAINTENANCE_9_EXTENSION_NAME);
    AddRequiredFeature(vkt::Feature::maintenance9);
    RETURN_IF_SKIP(InitFramework());

    VkDeviceCreateInfo device_ci = vku::InitStructHelper();
    device_ci.pNext = requested_features_.GetEnabledFeatures2();
    device_ci.enabledExtensionCount = static_cast<uint32_t>(m_device_extension_names.size());
    device_ci.ppEnabledExtensionNames = m_device_extension_names.data();

    vkt::Device device(gpu_, device_ci);

    VkShaderObj cs(device, kMinimalShaderGlsl, VK_SHADER_STAGE_COMPUTE_BIT);

    std::vector<VkDescriptorSetLayoutBinding> bindings(0);
    const vkt::DescriptorSetLayout pipeline_dsl(device, bindings);
    const vkt::PipelineLayout pipeline_layout(device, {&pipeline_dsl});

    VkComputePipelineCreateInfo compute_create_info = vku::InitStructHelper();
    compute_create_info.stage = cs.GetStageCreateInfo();
    compute_create_info.layout = pipeline_layout;

    VkPipeline pipeline;
    vk::CreateComputePipelines(device, VK_NULL_HANDLE, 1, &compute_create_info, nullptr, &pipeline);
    vk::DestroyPipeline(device, pipeline, nullptr);
}

TEST_F(PositiveDeviceQueue, QueueFamilyIndexDifferentCreateFlags) {
    SetTargetApiVersion(VK_API_VERSION_1_1);
    AddRequiredExtensions(VK_KHR_INTERNALLY_SYNCHRONIZED_QUEUES_EXTENSION_NAME);
    AddRequiredFeature(vkt::Feature::internallySynchronizedQueues);
    RETURN_IF_SKIP(InitFramework());

    bool found_queue = false;
    VkQueueFamilyProperties queue_properties;
    uint32_t queue_family_index = 0u;
    uint32_t queue_family_count = 0u;
    vk::GetPhysicalDeviceQueueFamilyProperties(Gpu(), &queue_family_count, nullptr);
    std::vector<VkQueueFamilyProperties> queue_families(queue_family_count);
    vk::GetPhysicalDeviceQueueFamilyProperties(Gpu(), &queue_family_count, queue_families.data());

    for (size_t i = 0; i < queue_families.size(); i++) {
        if (queue_families[i].queueCount > 1) {
            found_queue = true;
            queue_family_index = i;
            queue_properties = queue_families[i];
            break;
        }
    }

    if (found_queue == false) {
        GTEST_SKIP() << "test requires queue family with 2 queues, not available";
    }

    float queue_priority = 1.0f;
    VkDeviceQueueCreateInfo device_queue_ci[2];
    device_queue_ci[0] = vku::InitStructHelper();
    device_queue_ci[0].flags = 0u;
    device_queue_ci[0].queueFamilyIndex = queue_family_index;
    device_queue_ci[0].queueCount = 1u;
    device_queue_ci[0].pQueuePriorities = &queue_priority;
    device_queue_ci[1] = vku::InitStructHelper();
    device_queue_ci[1].flags = VK_DEVICE_QUEUE_CREATE_INTERNALLY_SYNCHRONIZED_BIT_KHR;
    device_queue_ci[1].queueFamilyIndex = queue_family_index;
    device_queue_ci[1].queueCount = 1u;
    device_queue_ci[1].pQueuePriorities = &queue_priority;

    VkDevice test_device = VK_NULL_HANDLE;
    VkDeviceCreateInfo device_ci = vku::InitStructHelper();
    device_ci.pNext = requested_features_.GetEnabledFeatures2();
    device_ci.queueCreateInfoCount = 2u;
    device_ci.pQueueCreateInfos = device_queue_ci;
    device_ci.enabledExtensionCount = static_cast<uint32_t>(m_device_extension_names.size());
    device_ci.ppEnabledExtensionNames = m_device_extension_names.data();
    vk::CreateDevice(Gpu(), &device_ci, nullptr, &test_device);
    vk::DestroyDevice(test_device, nullptr);
}

TEST_F(PositiveDeviceQueue, MultipleQueuePerfHint) {
    TEST_DESCRIPTION("Test vkQueueSetPerfHintQCOM on all supported queues.");
    SetTargetApiVersion(VK_API_VERSION_1_3);
    AddRequiredExtensions(VK_QCOM_QUEUE_PERF_HINT_EXTENSION_NAME);
    AddRequiredFeature(vkt::Feature::queuePerfHint);
    RETURN_IF_SKIP(InitFramework());

    VkPhysicalDeviceQueuePerfHintPropertiesQCOM perf_hint_props = vku::InitStructHelper();
    GetPhysicalDeviceProperties2(perf_hint_props);
    const VkQueueFlags supported_queues = perf_hint_props.supportedQueues;

    uint32_t queue_family_count = 0;
    vk::GetPhysicalDeviceQueueFamilyProperties(Gpu(), &queue_family_count, nullptr);
    std::vector<VkQueueFamilyProperties> queue_family_props(queue_family_count);
    vk::GetPhysicalDeviceQueueFamilyProperties(Gpu(), &queue_family_count, queue_family_props.data());

    std::vector<uint32_t> supported_queue_ids{};
    for (uint32_t index = 0; index < queue_family_count; ++index) {
        if ((queue_family_props[index].queueFlags & supported_queues) != 0) {
            supported_queue_ids.push_back(index);
        }
    }
    if (supported_queue_ids.empty()) {
        GTEST_SKIP() << "Failed to find the queue family that supports queue perf-hint property, skipping test.";
    }

    RETURN_IF_SKIP(InitState());

    constexpr std::array<VkPerfHintTypeQCOM, 3> hint_types{
        VK_PERF_HINT_TYPE_FREQUENCY_SCALED_QCOM,
        VK_PERF_HINT_TYPE_DEFAULT_QCOM,
        VK_PERF_HINT_TYPE_FREQUENCY_MIN_QCOM,
    };

    uint32_t index = 0;
    for (const uint32_t family_id : supported_queue_ids) {
        VkQueue queue = VK_NULL_HANDLE;
        vk::GetDeviceQueue(device(), family_id, 0, &queue);

        VkPerfHintInfoQCOM perf_hint_info = vku::InitStructHelper();
        perf_hint_info.type = hint_types[index];
        perf_hint_info.scale = 0;

        if (hint_types[index] == VK_PERF_HINT_TYPE_FREQUENCY_SCALED_QCOM) {
            perf_hint_info.scale = 65;
        }

        vk::QueueSetPerfHintQCOM(queue, &perf_hint_info);

        ++index;
        index = index % hint_types.size();
    }
}
