/*
 * Copyright (c) 2015-2026 The Khronos Group Inc.
 * Copyright (c) 2015-2026 Valve Corporation
 * Copyright (c) 2015-2026 LunarG, Inc.
 * Copyright (c) 2015-2026 Google, Inc.
 * Modifications Copyright (C) 2020-2026 Advanced Micro Devices, Inc. All rights reserved.
 *
 * 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 "render_pass_helper.h"

class PositiveEvent : public SyncObjectTest {};

TEST_F(PositiveEvent, EventStageMask) {
    RETURN_IF_SKIP(Init());

    vkt::Event event(*m_device);

    m_command_buffer.Begin();
    m_command_buffer.SetEvent(event, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
    vk::CmdWaitEvents(m_command_buffer, 1, &event.handle(), VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
                      0, nullptr, 0, nullptr, 0, nullptr);
    m_command_buffer.End();
}

TEST_F(PositiveEvent, EventStageMaskTwoSubmits) {
    RETURN_IF_SKIP(Init());

    vkt::CommandBuffer commandBuffer1(*m_device, m_command_pool);
    vkt::CommandBuffer commandBuffer2(*m_device, m_command_pool);
    vkt::Event event(*m_device);

    commandBuffer1.Begin();
    vk::CmdSetEvent(commandBuffer1, event, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
    commandBuffer1.End();
    m_default_queue->Submit(commandBuffer1);

    commandBuffer2.Begin();
    vk::CmdWaitEvents(commandBuffer2, 1, &event.handle(), VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
                      0, nullptr, 0, nullptr, 0, nullptr);
    commandBuffer2.End();
    m_default_queue->Submit(commandBuffer2);

    m_default_queue->Wait();
}

TEST_F(PositiveEvent, TwoCommandBuffers) {
    RETURN_IF_SKIP(Init());
    vkt::Event event(*m_device);

    vkt::CommandBuffer command_buffer(*m_device, m_command_pool);
    command_buffer.Begin();
    command_buffer.SetEvent(event, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
    command_buffer.End();

    vkt::CommandBuffer command_buffer2(*m_device, m_command_pool);
    command_buffer2.Begin();
    command_buffer2.WaitEvent(event, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT);
    command_buffer2.End();

    const VkCommandBuffer command_buffers[2] = {command_buffer, command_buffer2};

    VkSubmitInfo submit_info = vku::InitStructHelper();
    submit_info.commandBufferCount = 2;
    submit_info.pCommandBuffers = command_buffers;
    vk::QueueSubmit(*m_default_queue, 1, &submit_info, VK_NULL_HANDLE);
    m_default_queue->Wait();
}

TEST_F(PositiveEvent, TwoBatches) {
    RETURN_IF_SKIP(Init());
    vkt::Event event(*m_device);

    vkt::CommandBuffer command_buffer(*m_device, m_command_pool);
    command_buffer.Begin();
    command_buffer.ResetEvent(event);
    command_buffer.SetEvent(event, VK_PIPELINE_STAGE_TRANSFER_BIT);
    command_buffer.End();

    vkt::CommandBuffer command_buffer2(*m_device, m_command_pool);
    command_buffer2.Begin();
    // This signal goes after the signal from another command buffer and is ignored
    command_buffer2.SetEvent(event, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT);

    command_buffer2.WaitEvent(event, VK_PIPELINE_STAGE_TRANSFER_BIT);
    command_buffer2.End();

    // Need to use semaphore to guarantee ordering of CmdSetEvents
    vkt::Semaphore semaphore(*m_device);

    VkSubmitInfo submit_infos[2];
    submit_infos[0] = vku::InitStructHelper();
    submit_infos[0].commandBufferCount = 1;
    submit_infos[0].pCommandBuffers = &command_buffer.handle();
    submit_infos[0].signalSemaphoreCount = 1;
    submit_infos[0].pSignalSemaphores = &semaphore.handle();

    const VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
    submit_infos[1] = vku::InitStructHelper();
    submit_infos[1].waitSemaphoreCount = 1;
    submit_infos[1].pWaitSemaphores = &semaphore.handle();
    submit_infos[1].pWaitDstStageMask = &wait_stage;
    submit_infos[1].commandBufferCount = 1;
    submit_infos[1].pCommandBuffers = &command_buffer2.handle();

    vk::QueueSubmit(*m_default_queue, 2, submit_infos, VK_NULL_HANDLE);
    m_default_queue->Wait();
}

TEST_F(PositiveEvent, TwoEventsTwoSubmits) {
    RETURN_IF_SKIP(Init());

    vkt::Event event(*m_device);
    vkt::Event event2(*m_device);
    const VkEvent events[2] = {event, event2};

    vkt::CommandBuffer command_buffer(*m_device, m_command_pool);
    command_buffer.Begin();
    command_buffer.SetEvent(event, VK_PIPELINE_STAGE_TRANSFER_BIT);
    command_buffer.End();

    vkt::CommandBuffer command_buffer2(*m_device, m_command_pool);
    command_buffer2.Begin();
    command_buffer2.SetEvent(event2, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
    vk::CmdWaitEvents(command_buffer2, 2, events, VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
                      VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, nullptr, 0, nullptr, 0, nullptr);
    command_buffer2.End();

    m_default_queue->Submit(command_buffer);
    m_default_queue->SubmitAndWait(command_buffer2);
}

TEST_F(PositiveEvent, StageMaskTwoEventsTwoSubmits2) {
    RETURN_IF_SKIP(Init());

    vkt::Event event(*m_device);
    vkt::Event event2(*m_device);
    const VkEvent events[2] = {event, event2};

    vkt::CommandBuffer command_buffer(*m_device, m_command_pool);
    command_buffer.Begin();
    command_buffer.SetEvent(event, VK_PIPELINE_STAGE_TRANSFER_BIT);
    command_buffer.End();

    vkt::CommandBuffer command_buffer2(*m_device, m_command_pool);
    command_buffer2.Begin();
    command_buffer2.SetEvent(event2, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
    command_buffer2.End();

    vkt::CommandBuffer command_buffer3(*m_device, m_command_pool);
    command_buffer3.Begin();
    vk::CmdWaitEvents(command_buffer3, 2, events, VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
                      VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, nullptr, 0, nullptr, 0, nullptr);
    command_buffer3.End();

    m_default_queue->SubmitAndWait(command_buffer);
    m_default_queue->SubmitAndWait(command_buffer2);
    m_default_queue->SubmitAndWait(command_buffer3);
}

TEST_F(PositiveEvent, EventStageMaskHostSubmit) {
    RETURN_IF_SKIP(Init());
    vkt::Event event(*m_device);

    m_command_buffer.Begin();
    m_command_buffer.WaitEvent(event, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT);
    m_command_buffer.End();

    event.Set();
    m_default_queue->SubmitAndWait(m_command_buffer);
}

TEST_F(PositiveEvent, PrimarySetSecondaryWaitCantDetectMismatch) {
    TEST_DESCRIPTION("CmdSetEvent is not possible to validate during record time without preceding CmdResetEvent");
    RETURN_IF_SKIP(Init());
    vkt::Event event(*m_device);

    vkt::CommandBuffer secondary(*m_device, m_command_pool, VK_COMMAND_BUFFER_LEVEL_SECONDARY);
    secondary.Begin();
    secondary.WaitEvent(event, VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
                        VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
    secondary.End();

    m_command_buffer.Begin();
    // We don't know if signal is ignored or not (there could be earlier signal which takes priority),
    // that's why we can't check for signal/wait source stage mismatch
    m_command_buffer.SetEvent(event, VK_PIPELINE_STAGE_TRANSFER_BIT);
    m_command_buffer.ExecuteCommands(secondary);
    m_command_buffer.End();
}

TEST_F(PositiveEvent, SecondarySetAndWaitMismatch) {
    RETURN_IF_SKIP(Init());
    vkt::Event event(*m_device);

    vkt::CommandBuffer secondary(*m_device, m_command_pool, VK_COMMAND_BUFFER_LEVEL_SECONDARY);
    secondary.Begin();
    secondary.SetEvent(event, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
    secondary.WaitEvent(event, VK_PIPELINE_STAGE_TRANSFER_BIT);
    secondary.End();

    m_command_buffer.Begin();
    // For the secondary command buffer we can't validate signal/wait stage mismatch
    // at record-time. The signal can be ignored if it is preceded by another one
    m_command_buffer.ExecuteCommands(secondary);
    m_command_buffer.End();
}

TEST_F(PositiveEvent, SecondarySetAndWaitSubmit) {
    RETURN_IF_SKIP(Init());
    vkt::Event event(*m_device);

    vkt::CommandBuffer secondary(*m_device, m_command_pool, VK_COMMAND_BUFFER_LEVEL_SECONDARY);
    secondary.Begin();
    secondary.SetEvent(event, VK_PIPELINE_STAGE_TRANSFER_BIT);
    secondary.WaitEvent(event, VK_PIPELINE_STAGE_TRANSFER_BIT);
    secondary.End();

    m_command_buffer.Begin();
    m_command_buffer.ExecuteCommands(secondary);
    m_command_buffer.End();
    m_default_queue->SubmitAndWait(m_command_buffer);
}

TEST_F(PositiveEvent, SecondarySetPrimaryWait) {
    RETURN_IF_SKIP(Init());
    vkt::Event event(*m_device);

    vkt::CommandBuffer secondary(*m_device, m_command_pool, VK_COMMAND_BUFFER_LEVEL_SECONDARY);
    secondary.Begin();
    secondary.SetEvent(event, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
    secondary.End();

    m_command_buffer.Begin();
    m_command_buffer.ExecuteCommands(secondary);
    m_command_buffer.WaitEvent(event, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
    m_command_buffer.End();

    // If due to regression ExecuteCommands has no effect then submit validation will report an error
    m_default_queue->SubmitAndWait(m_command_buffer);
}

TEST_F(PositiveEvent, PrimarySetSecondaryWait) {
    RETURN_IF_SKIP(Init());
    vkt::Event event(*m_device);

    vkt::CommandBuffer secondary(*m_device, m_command_pool, VK_COMMAND_BUFFER_LEVEL_SECONDARY);
    secondary.Begin();
    secondary.WaitEvent(event, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
    secondary.End();

    m_command_buffer.Begin();
    m_command_buffer.SetEvent(event, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
    m_command_buffer.ExecuteCommands(secondary);
    m_command_buffer.End();

    // The src stage mask validation of WaitEvents happens during CmdExecuteCommands.
    // The following submit tests that src stage mask validation is not happening during submit time
    // (if validation leaks into submit processing it will likely report false positive)
    m_default_queue->SubmitAndWait(m_command_buffer);
}

TEST_F(PositiveEvent, PrimaryResetSecondarySetAndWait) {
    RETURN_IF_SKIP(Init());
    vkt::Event event(*m_device);

    vkt::CommandBuffer secondary(*m_device, m_command_pool, VK_COMMAND_BUFFER_LEVEL_SECONDARY);
    secondary.Begin();
    secondary.SetEvent(event, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
    secondary.WaitEvent(event, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
    secondary.End();

    m_command_buffer.Begin();
    m_command_buffer.ResetEvent(event);
    m_command_buffer.ExecuteCommands(secondary);
    m_command_buffer.End();
}

TEST_F(PositiveEvent, PrimaryResetSecondarySetAndWait2) {
    SetTargetApiVersion(VK_API_VERSION_1_3);
    AddRequiredFeature(vkt::Feature::synchronization2);
    RETURN_IF_SKIP(Init());

    vkt::Event event(*m_device);

    VkMemoryBarrier2 barrier = vku::InitStructHelper();
    barrier.srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT;

    vkt::CommandBuffer secondary(*m_device, m_command_pool, VK_COMMAND_BUFFER_LEVEL_SECONDARY);
    secondary.Begin();
    secondary.SetEvent2(event, barrier);
    secondary.WaitEvent2(event, barrier);
    secondary.End();

    m_command_buffer.Begin();
    m_command_buffer.ResetEvent2(event);
    m_command_buffer.ExecuteCommands(secondary);
    m_command_buffer.End();
}

TEST_F(PositiveEvent, SecondaryWaitTwoEvents) {
    RETURN_IF_SKIP(Init());
    vkt::Event event(*m_device);
    vkt::Event event2(*m_device);

    const VkEvent events[2] = {event, event2};

    vkt::CommandBuffer secondary(*m_device, m_command_pool, VK_COMMAND_BUFFER_LEVEL_SECONDARY);
    secondary.Begin();
    vk::CmdWaitEvents(secondary, 2, events, VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
                      VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, nullptr, 0, nullptr, 0, nullptr);
    secondary.End();

    vkt::CommandBuffer command_buffer(*m_device, m_command_pool);
    command_buffer.Begin();
    command_buffer.SetEvent(event2, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
    command_buffer.End();

    vkt::CommandBuffer command_buffer2(*m_device, m_command_pool);
    command_buffer2.Begin();
    command_buffer2.SetEvent(event, VK_PIPELINE_STAGE_TRANSFER_BIT);
    command_buffer2.ExecuteCommands(secondary);
    command_buffer2.End();

    m_default_queue->Submit(command_buffer);
    m_default_queue->SubmitAndWait(command_buffer2);
}

TEST_F(PositiveEvent, BasicSetAndWaitEvent) {
    TEST_DESCRIPTION("Sets event and then wait for it using CmdSetEvent/CmdWaitEvents");
    RETURN_IF_SKIP(Init());

    const vkt::Event event(*m_device);

    m_command_buffer.Begin();
    vk::CmdSetEvent(m_command_buffer, event, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
    vk::CmdWaitEvents(m_command_buffer, 1, &event.handle(), VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
                      0, nullptr, 0, nullptr, 0, nullptr);
    m_command_buffer.End();

    m_default_queue->Submit(m_command_buffer);
    m_device->Wait();
}

TEST_F(PositiveEvent, BasicSetAndWaitEvent2) {
    TEST_DESCRIPTION("Sets event and then wait for it using CmdSetEvent2/CmdWaitEvents2");
    SetTargetApiVersion(VK_API_VERSION_1_3);
    AddRequiredFeature(vkt::Feature::synchronization2);
    RETURN_IF_SKIP(Init());

    VkMemoryBarrier2 barrier = vku::InitStructHelper();
    barrier.srcAccessMask = 0;
    barrier.dstAccessMask = 0;
    barrier.srcStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
    barrier.dstStageMask = VK_PIPELINE_STAGE_NONE;

    VkDependencyInfo dependency_info = vku::InitStructHelper();
    dependency_info.memoryBarrierCount = 1;
    dependency_info.pMemoryBarriers = &barrier;

    const vkt::Event event(*m_device);

    // Record time validation
    m_command_buffer.Begin();
    vk::CmdSetEvent2(m_command_buffer, event, &dependency_info);
    vk::CmdWaitEvents2(m_command_buffer, 1, &event.handle(), &dependency_info);
    m_command_buffer.End();

    // Also submit to the queue to test submit time validation
    m_default_queue->Submit(m_command_buffer);
    m_device->Wait();
}

TEST_F(PositiveEvent, WaitEvent2HostStage) {
    SetTargetApiVersion(VK_API_VERSION_1_1);
    AddRequiredExtensions(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME);
    AddRequiredFeature(vkt::Feature::synchronization2);
    RETURN_IF_SKIP(Init());
    InitRenderTarget();

    vkt::Event event(*m_device);

    VkMemoryBarrier2 barrier = vku::InitStructHelper();
    barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
    barrier.dstAccessMask = VK_ACCESS_HOST_READ_BIT;
    barrier.srcStageMask = VK_PIPELINE_STAGE_2_HOST_BIT;  // Ok to use if outside the renderpass
    barrier.dstStageMask = VK_PIPELINE_STAGE_2_HOST_BIT;
    VkDependencyInfo dependency_info = vku::InitStructHelper();
    dependency_info.memoryBarrierCount = 1;
    dependency_info.pMemoryBarriers = &barrier;

    m_command_buffer.Begin();
    vk::CmdWaitEvents2KHR(m_command_buffer, 1, &event.handle(), &dependency_info);
    m_command_buffer.End();
}

TEST_F(PositiveEvent, SetEvent2Flags) {
    SetTargetApiVersion(VK_API_VERSION_1_3);
    AddRequiredExtensions(VK_KHR_MAINTENANCE_9_EXTENSION_NAME);
    AddRequiredFeature(vkt::Feature::maintenance9);
    AddRequiredFeature(vkt::Feature::synchronization2);
    RETURN_IF_SKIP(Init());

    m_command_buffer.Begin();

    VkMemoryBarrier2 memory_barrier = vku::InitStructHelper();

    VkDependencyInfo dependency_info = vku::InitStructHelper();
    dependency_info.dependencyFlags = VK_DEPENDENCY_ASYMMETRIC_EVENT_BIT_KHR;
    dependency_info.memoryBarrierCount = 1u;
    dependency_info.pMemoryBarriers = &memory_barrier;

    vkt::Event event(*m_device);
    vk::CmdSetEvent2(m_command_buffer, event, &dependency_info);
}

TEST_F(PositiveEvent, AsymmetricWaitEvent2) {
    SetTargetApiVersion(VK_API_VERSION_1_3);
    AddRequiredExtensions(VK_KHR_MAINTENANCE_9_EXTENSION_NAME);
    AddRequiredFeature(vkt::Feature::maintenance9);
    AddRequiredFeature(vkt::Feature::synchronization2);
    RETURN_IF_SKIP(Init());

    VkMemoryBarrier2 barrier = vku::InitStructHelper();
    barrier.srcStageMask = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;

    VkDependencyInfo dependency_info = vku::InitStructHelper();
    dependency_info.dependencyFlags = VK_DEPENDENCY_ASYMMETRIC_EVENT_BIT_KHR;
    dependency_info.memoryBarrierCount = 1u;
    dependency_info.pMemoryBarriers = &barrier;

    const vkt::Event event(*m_device);

    m_command_buffer.Begin();

    vk::CmdSetEvent2(m_command_buffer, event, &dependency_info);

    barrier.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
    barrier.dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
    vk::CmdWaitEvents2(m_command_buffer, 1, &event.handle(), &dependency_info);

    m_command_buffer.End();

    m_default_queue->Submit(m_command_buffer);
    m_default_queue->Wait();
}

TEST_F(PositiveEvent, AsymmetricEventNoMemorySubmit) {
    SetTargetApiVersion(VK_API_VERSION_1_3);
    AddRequiredExtensions(VK_KHR_MAINTENANCE_9_EXTENSION_NAME);
    AddRequiredFeature(vkt::Feature::maintenance9);
    AddRequiredFeature(vkt::Feature::synchronization2);
    RETURN_IF_SKIP(Init());

    VkDependencyInfo dependency_info = vku::InitStructHelper();
    dependency_info.dependencyFlags = VK_DEPENDENCY_ASYMMETRIC_EVENT_BIT_KHR;
    vkt::Event event(*m_device);

    monitor_.SetAllowedFailureMsg("VUID-vkCmdSetEvent2-dependencyFlags-10786");
    m_command_buffer.Begin();
    vk::CmdSetEvent2(m_command_buffer, event, &dependency_info);
    vk::CmdWaitEvents2(m_command_buffer, 1, &event.handle(), &dependency_info);
    m_command_buffer.End();

    // Check that missing memory barrier does not confuse submit validation
    m_default_queue->SubmitAndWait(m_command_buffer);
}

TEST_F(PositiveEvent, ResetAfterWait) {
    RETURN_IF_SKIP(Init());

    vkt::Event event(*m_device);

    m_command_buffer.Begin();
    m_command_buffer.SetEvent(event);
    m_command_buffer.WaitEvent(event, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
    m_command_buffer.ResetEvent(event, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
    m_command_buffer.End();
}

TEST_F(PositiveEvent, ResetAfterWait2) {
    SetTargetApiVersion(VK_API_VERSION_1_3);
    AddRequiredFeature(vkt::Feature::synchronization2);
    RETURN_IF_SKIP(Init());

    vkt::Event event(*m_device);

    VkMemoryBarrier2 barrier = vku::InitStructHelper();
    barrier.srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT;
    barrier.dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT;

    m_command_buffer.Begin();
    m_command_buffer.SetEvent2(event, barrier);
    m_command_buffer.WaitEvent2(event, barrier);
    m_command_buffer.ResetEvent(event, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
    m_command_buffer.End();
}

TEST_F(PositiveEvent, ResetAfterWait3) {
    SetTargetApiVersion(VK_API_VERSION_1_3);
    AddRequiredFeature(vkt::Feature::synchronization2);
    RETURN_IF_SKIP(Init());

    vkt::Event event(*m_device);

    VkMemoryBarrier2 barrier = vku::InitStructHelper();
    barrier.srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT;
    barrier.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT;

    m_command_buffer.Begin();
    m_command_buffer.SetEvent2(event, barrier);
    m_command_buffer.WaitEvent2(event, barrier);
    m_command_buffer.ResetEvent(event, VK_PIPELINE_STAGE_2_TRANSFER_BIT);
    m_command_buffer.End();
}

TEST_F(PositiveEvent, ResetAfterWaitBarrierExecutionDependency) {
    RETURN_IF_SKIP(Init());
    vkt::Event event(*m_device);

    m_command_buffer.Begin();
    m_command_buffer.SetEvent(event);
    m_command_buffer.WaitEvent(event, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);

    vk::CmdPipelineBarrier(m_command_buffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0,
                           nullptr, 0, nullptr);

    m_command_buffer.ResetEvent(event, VK_PIPELINE_STAGE_TRANSFER_BIT);
    m_command_buffer.End();
}

TEST_F(PositiveEvent, ResetAfterWaitBarrierExecutionDependency2) {
    SetTargetApiVersion(VK_API_VERSION_1_3);
    AddRequiredFeature(vkt::Feature::synchronization2);
    RETURN_IF_SKIP(Init());

    vkt::Event event(*m_device);

    VkMemoryBarrier2 barrier = vku::InitStructHelper();
    barrier.srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT;
    barrier.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT;

    m_command_buffer.Begin();
    m_command_buffer.SetEvent(event);
    m_command_buffer.WaitEvent(event, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
    m_command_buffer.Barrier(barrier);
    m_command_buffer.ResetEvent(event, VK_PIPELINE_STAGE_TRANSFER_BIT);
    m_command_buffer.End();
}

TEST_F(PositiveEvent, ResetAfterWaitBarrierExecutionDependency3) {
    SetTargetApiVersion(VK_API_VERSION_1_3);
    AddRequiredFeature(vkt::Feature::synchronization2);
    RETURN_IF_SKIP(Init());

    vkt::Event event(*m_device);

    VkMemoryBarrier2 wait_barrier = vku::InitStructHelper();
    wait_barrier.srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT;
    wait_barrier.dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT;

    VkMemoryBarrier2 transfer_barrier = vku::InitStructHelper();
    transfer_barrier.srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT;
    // Use TRANSFER meta stage (potentially expanded by the validation) to check it correctly
    // chains with TRANSFER in the next barrier
    transfer_barrier.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT;

    VkMemoryBarrier2 color_barrier = vku::InitStructHelper();
    color_barrier.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT;
    color_barrier.dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;

    m_command_buffer.Begin();
    m_command_buffer.SetEvent2(event, wait_barrier);
    m_command_buffer.WaitEvent2(event, wait_barrier);
    m_command_buffer.Barrier(transfer_barrier);
    m_command_buffer.Barrier(color_barrier);
    m_command_buffer.ResetEvent(event, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
    m_command_buffer.End();
}

TEST_F(PositiveEvent, ResetAfterWaitEmptyBarrier2) {
    SetTargetApiVersion(VK_API_VERSION_1_3);
    AddRequiredFeature(vkt::Feature::synchronization2);
    RETURN_IF_SKIP(Init());

    vkt::Event event(*m_device);

    VkDependencyInfo empty_dep_info = vku::InitStructHelper();

    m_command_buffer.Begin();
    m_command_buffer.SetEvent(event);
    m_command_buffer.WaitEvent(event, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);

    // The empty dependency should not affect execution dependency between Wait and Reset via COMPUTE_STAGE
    m_command_buffer.Barrier(empty_dep_info);

    m_command_buffer.ResetEvent(event, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
    m_command_buffer.End();
}

TEST_F(PositiveEvent, ResetAfterWaitSecondaryBarrier) {
    RETURN_IF_SKIP(Init());
    vkt::Event event(*m_device);

    vkt::CommandBuffer secondary(*m_device, m_command_pool, VK_COMMAND_BUFFER_LEVEL_SECONDARY);
    secondary.Begin();
    vk::CmdPipelineBarrier(secondary, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0,
                           nullptr, 0, nullptr);
    secondary.End();

    m_command_buffer.Begin();
    m_command_buffer.SetEvent(event, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
    m_command_buffer.WaitEvent(event, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
    m_command_buffer.ExecuteCommands(secondary);
    m_command_buffer.ResetEvent(event, VK_PIPELINE_STAGE_TRANSFER_BIT);
    m_command_buffer.End();
}

TEST_F(PositiveEvent, ResetAfterWaitSecondaryReset) {
    RETURN_IF_SKIP(Init());
    vkt::Event event(*m_device);

    vkt::CommandBuffer secondary(*m_device, m_command_pool, VK_COMMAND_BUFFER_LEVEL_SECONDARY);
    secondary.Begin();
    secondary.ResetEvent(event, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
    secondary.End();

    m_command_buffer.Begin();
    m_command_buffer.SetEvent(event, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
    m_command_buffer.WaitEvent(event, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
    m_command_buffer.ExecuteCommands(secondary);
    m_command_buffer.ResetEvent(event, VK_PIPELINE_STAGE_TRANSFER_BIT);
    m_command_buffer.End();
}

TEST_F(PositiveEvent, HostReset) {
    TEST_DESCRIPTION("https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/7970");
    RETURN_IF_SKIP(Init());

    vkt::Event event(*m_device);

    vkt::CommandBuffer command_buffer(*m_device, m_command_pool);
    command_buffer.Begin();
    command_buffer.SetEvent(event, VK_PIPELINE_STAGE_TRANSFER_BIT);
    command_buffer.WaitEvent(event, VK_PIPELINE_STAGE_TRANSFER_BIT,
                             VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT);
    command_buffer.End();
    m_default_queue->SubmitAndWait(command_buffer);

    event.Reset();

    vkt::CommandBuffer command_buffer2(*m_device, m_command_pool);
    command_buffer2.Begin();
    command_buffer2.SetEvent(event, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT);
    command_buffer2.WaitEvent(event, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT,
                              VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
    command_buffer2.End();
    m_default_queue->SubmitAndWait(command_buffer2);
}

TEST_F(PositiveEvent, WaitWithOnlyHostDependency) {
    SetTargetApiVersion(VK_API_VERSION_1_3);
    AddRequiredFeature(vkt::Feature::synchronization2);
    RETURN_IF_SKIP(Init());

    vkt::Event event(*m_device);
    event.Set();

    VkMemoryBarrier2 barrier = vku::InitStructHelper();
    barrier.srcStageMask = VK_PIPELINE_STAGE_2_HOST_BIT;

    m_command_buffer.Begin();
    m_command_buffer.WaitEvent2(event, barrier);
    m_command_buffer.End();
    m_default_queue->SubmitAndWait(m_command_buffer);
}

TEST_F(PositiveEvent, WaitWithOnlyHostDependencySecondary) {
    SetTargetApiVersion(VK_API_VERSION_1_3);
    AddRequiredFeature(vkt::Feature::synchronization2);
    RETURN_IF_SKIP(Init());

    vkt::Event event(*m_device);
    event.Set();

    VkMemoryBarrier2 barrier = vku::InitStructHelper();
    barrier.srcStageMask = VK_PIPELINE_STAGE_2_HOST_BIT;

    vkt::CommandBuffer secondary(*m_device, m_command_pool, VK_COMMAND_BUFFER_LEVEL_SECONDARY);
    secondary.Begin();
    secondary.WaitEvent2(event, barrier);
    secondary.End();

    m_command_buffer.Begin();
    m_command_buffer.ExecuteCommands(secondary);
    m_command_buffer.End();
    m_default_queue->SubmitAndWait(m_command_buffer);
}

TEST_F(PositiveEvent, WaitWithOnlyHostDependencyIgnoredSignal) {
    SetTargetApiVersion(VK_API_VERSION_1_3);
    AddRequiredFeature(vkt::Feature::synchronization2);
    RETURN_IF_SKIP(Init());

    vkt::Event event(*m_device);
    event.Set();

    VkMemoryBarrier2 barrier = vku::InitStructHelper();
    barrier.srcStageMask = VK_PIPELINE_STAGE_2_HOST_BIT;

    m_command_buffer.Begin();
    m_command_buffer.SetEvent(event);
    m_command_buffer.WaitEvent2(event, barrier);
    m_command_buffer.End();
    m_default_queue->SubmitAndWait(m_command_buffer);
}

TEST_F(PositiveEvent, WaitWithOnlyHostDependencyIgnoredSignalSecondary) {
    SetTargetApiVersion(VK_API_VERSION_1_3);
    AddRequiredFeature(vkt::Feature::synchronization2);
    RETURN_IF_SKIP(Init());

    vkt::Event event(*m_device);
    event.Set();  // signals event, the next signal is ignored

    VkMemoryBarrier2 barrier = vku::InitStructHelper();
    barrier.srcStageMask = VK_PIPELINE_STAGE_2_HOST_BIT;

    vkt::CommandBuffer secondary(*m_device, m_command_pool, VK_COMMAND_BUFFER_LEVEL_SECONDARY);
    secondary.Begin();
    secondary.SetEvent(event);  // ignored signal
    secondary.WaitEvent2(event, barrier);
    secondary.End();

    m_command_buffer.Begin();
    m_command_buffer.ExecuteCommands(secondary);
    m_command_buffer.End();
    m_default_queue->SubmitAndWait(m_command_buffer);
}

TEST_F(PositiveEvent, Wait2IgnoredSignal) {
    SetTargetApiVersion(VK_API_VERSION_1_3);
    AddRequiredFeature(vkt::Feature::synchronization2);
    RETURN_IF_SKIP(Init());

    vkt::Event event(*m_device);

    VkMemoryBarrier2 barrier = vku::InitStructHelper();
    barrier.srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT;

    vkt::CommandBuffer command_buffer(*m_device, m_command_pool);
    command_buffer.Begin();
    command_buffer.SetEvent2(event, barrier);
    command_buffer.End();
    m_default_queue->SubmitAndWait(command_buffer);

    vkt::CommandBuffer command_buffer2(*m_device, m_command_pool);
    command_buffer2.Begin();
    command_buffer2.SetEvent(event);  // ignored signal
    command_buffer2.WaitEvent2(event, barrier);
    command_buffer2.End();
    m_default_queue->SubmitAndWait(command_buffer2);
}

TEST_F(PositiveEvent, Wait2IgnoredSignal2) {
    SetTargetApiVersion(VK_API_VERSION_1_3);
    AddRequiredFeature(vkt::Feature::synchronization2);
    RETURN_IF_SKIP(Init());

    vkt::Event event(*m_device);

    VkMemoryBarrier2 barrier = vku::InitStructHelper();
    barrier.srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT;

    vkt::CommandBuffer command_buffer(*m_device, m_command_pool);
    command_buffer.Begin();
    command_buffer.SetEvent2(event, barrier);
    command_buffer.End();
    m_default_queue->SubmitAndWait(command_buffer);

    vkt::CommandBuffer command_buffer2(*m_device, m_command_pool);
    command_buffer2.Begin();
    command_buffer2.SetEvent(event);  // ignored signal
    command_buffer2.End();
    m_default_queue->SubmitAndWait(command_buffer2);

    vkt::CommandBuffer command_buffer3(*m_device, m_command_pool);
    command_buffer3.Begin();
    command_buffer3.WaitEvent2(event, barrier);
    command_buffer3.End();
    m_default_queue->SubmitAndWait(command_buffer3);
}

TEST_F(PositiveEvent, WaitHostStageAndIgnoredSignal) {
    SetTargetApiVersion(VK_API_VERSION_1_3);
    AddRequiredFeature(vkt::Feature::synchronization2);
    RETURN_IF_SKIP(Init());

    vkt::Event event(*m_device);
    event.Set();  // signals event, the next signal is ignored

    VkMemoryBarrier2 barrier = vku::InitStructHelper();
    barrier.srcStageMask = VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT;

    m_command_buffer.Begin();
    m_command_buffer.SetEvent2(event, barrier);  // ignored signal
    m_command_buffer.WaitEvent(event, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
    m_command_buffer.End();
    m_default_queue->SubmitAndWait(m_command_buffer);
}

TEST_F(PositiveEvent, WaitEventsSameLayoutInsideRenderPass) {
    RETURN_IF_SKIP(Init());

    RenderPassSingleSubpass rp(*this);
    rp.AddAttachmentDescription(VK_FORMAT_R8G8B8A8_UNORM);
    rp.AddColorAttachment(0, VK_IMAGE_LAYOUT_GENERAL);
    rp.AddSubpassSelfDependency();
    rp.CreateRenderPass();

    vkt::Image image(*m_device, 64, 64, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
    vkt::ImageView image_view = image.CreateView();
    vkt::Framebuffer fb(*m_device, rp, 1, &image_view.handle());
    vkt::Event event(*m_device);

    VkImageMemoryBarrier image_barrier = vku::InitStructHelper();
    image_barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
    image_barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
    image_barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
    image_barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
    image_barrier.image = image;
    image_barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};

    m_command_buffer.Begin();
    m_command_buffer.BeginRenderPass(rp, fb);
    vk::CmdWaitEvents(m_command_buffer, 1, &event.handle(), VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
                      VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, nullptr, 0, nullptr, 1, &image_barrier);
    m_command_buffer.EndRenderPass();
    m_command_buffer.End();
}

TEST_F(PositiveEvent, WaitEventsSameLayoutInsideRenderPass2) {
    SetTargetApiVersion(VK_API_VERSION_1_3);
    AddRequiredFeature(vkt::Feature::synchronization2);
    AddRequiredFeature(vkt::Feature::dynamicRendering);
    RETURN_IF_SKIP(Init());

    vkt::Image image(*m_device, 64, 64, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
    vkt::ImageView image_view = image.CreateView();

    vkt::Event event(*m_device);

    VkRenderingAttachmentInfo attachment = vku::InitStructHelper();
    attachment.imageView = image_view;
    attachment.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
    attachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
    attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;

    VkRenderingInfo rendering_info = vku::InitStructHelper();
    rendering_info.renderArea.extent = {64, 64};
    rendering_info.layerCount = 1;
    rendering_info.colorAttachmentCount = 1;
    rendering_info.pColorAttachments = &attachment;

    VkImageMemoryBarrier2 image_barrier = vku::InitStructHelper();
    image_barrier.srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;
    image_barrier.srcAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT;
    image_barrier.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
    image_barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
    image_barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
    image_barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
    image_barrier.image = image;
    image_barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};

    VkDependencyInfo dep_info = vku::InitStructHelper();
    dep_info.imageMemoryBarrierCount = 1;
    dep_info.pImageMemoryBarriers = &image_barrier;

    m_command_buffer.Begin();
    m_command_buffer.BeginRendering(rendering_info);
    vk::CmdWaitEvents2(m_command_buffer, 1, &event.handle(), &dep_info);
    m_command_buffer.EndRendering();
    m_command_buffer.End();
}

TEST_F(PositiveEvent, HostResetAfterWait) {
    RETURN_IF_SKIP(Init());

    vkt::Event event(*m_device);

    vkt::Fence fence(*m_device);

    m_command_buffer.Begin();
    m_command_buffer.SetEvent(event);
    m_command_buffer.WaitEvent(event);
    m_command_buffer.End();
    m_default_queue->Submit(m_command_buffer, fence);

    // Wait for fence to create execution dependency between event Wait and host Reset
    fence.Wait(kWaitTimeout);

    event.Reset();
}

TEST_F(PositiveEvent, HostResetAfterWait2) {
    SetTargetApiVersion(VK_API_VERSION_1_3);
    AddRequiredFeature(vkt::Feature::synchronization2);
    RETURN_IF_SKIP(Init());

    vkt::Event event(*m_device);

    VkMemoryBarrier2 barrier = vku::InitStructHelper();
    barrier.srcStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
    barrier.dstStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;

    m_command_buffer.Begin();
    m_command_buffer.SetEvent2(event, barrier);
    m_command_buffer.WaitEvent2(event, barrier);
    m_command_buffer.End();
    m_default_queue->Submit(m_command_buffer);

    // Wait for the queue to create execution dependency between event and host Reset
    m_default_queue->Wait();

    event.Reset();
}

TEST_F(PositiveEvent, HostResetAfterWait3) {
    RETURN_IF_SKIP(Init());

    vkt::Event event(*m_device);
    vkt::Event event2(*m_device);

    vkt::CommandBuffer command_buffer(*m_device, m_command_pool);
    command_buffer.Begin();
    command_buffer.SetEvent(event);
    command_buffer.WaitEvent(event);
    command_buffer.End();

    vkt::CommandBuffer command_buffer2(*m_device, m_command_pool);
    command_buffer2.Begin();
    command_buffer2.SetEvent(event2);
    command_buffer2.WaitEvent(event2);
    command_buffer2.End();

    m_default_queue->SubmitAndWait(command_buffer);
    m_default_queue->Submit(command_buffer2);

    // We have command_buffer2 pending (with event2), but the first event was set
    // and waited on in the first command buffer, so it is safe to call host reset
    // on the first event
    event.Reset();
    m_default_queue->Wait();
}
