/* Copyright (c) 2026 The Khronos Group Inc.
 * Copyright (c) 2026 Valve Corporation
 * Copyright (c) 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
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "sync_val_tests.h"
#include "render_pass_helper.h"

struct NegativeSyncValRenderPass : public VkSyncValTest {};

TEST_F(NegativeSyncValRenderPass, ClearColorAttachmentWAW) {
    TEST_DESCRIPTION("WAW hazard when color attachment is cleared inside render pass");
    AddRequiredExtensions(VK_KHR_LOAD_STORE_OP_NONE_EXTENSION_NAME);
    RETURN_IF_SKIP(InitSyncVal());

    const uint32_t width = 256;
    const uint32_t height = 128;
    const VkFormat color_format = VK_FORMAT_B8G8R8A8_UNORM;

    vkt::Image src_image(*m_device, width, height, color_format, VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
    vkt::Image image(*m_device, width, height, color_format, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
    vkt::ImageView image_view = image.CreateView();

    RenderPassSingleSubpass render_pass(*this);
    render_pass.AddAttachmentDescription(color_format, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL, VK_ATTACHMENT_LOAD_OP_NONE,
                                         VK_ATTACHMENT_STORE_OP_NONE);
    render_pass.AddColorAttachment(0, VK_IMAGE_LAYOUT_GENERAL);
    render_pass.CreateRenderPass();

    vkt::Framebuffer framebuffer(*m_device, render_pass, 1, &image_view.handle(), width, height);

    // Copy to the left half
    VkImageCopy copy_region = {};
    copy_region.srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
    copy_region.dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
    copy_region.extent = {width / 2, height, 1};

    // Clear the right half
    const VkClearAttachment clear_attachment = {VK_IMAGE_ASPECT_COLOR_BIT, 0};
    VkClearRect clear_rect = {};
    clear_rect.rect = {{width / 2, 0}, {width / 2, height}};
    clear_rect.baseArrayLayer = 0;
    clear_rect.layerCount = 1;

    m_command_buffer.Begin();
    vk::CmdCopyImage(m_command_buffer, src_image, VK_IMAGE_LAYOUT_GENERAL, image, VK_IMAGE_LAYOUT_GENERAL, 1, &copy_region);

    // loadOp/storeOp=NONE, so no hazard can be detected at this point
    m_command_buffer.BeginRenderPass(render_pass, framebuffer, width, height);

    // Clear operation hazards with the previous copy.
    // The copy region and the clear region do not overlap but the clear operation can
    // access the entire attachment, so it can race with copy accesses and makes result
    // of the copy operation undefined. Note, that storeOp=NONE works as DONT_CARE here
    // (due to write) and the clear results are discarded.
    m_errorMonitor->SetDesiredError("SYNC-HAZARD-WRITE-AFTER-WRITE");
    vk::CmdClearAttachments(m_command_buffer, 1, &clear_attachment, 1, &clear_rect);
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeSyncValRenderPass, ClearDepthAspectWAW) {
    TEST_DESCRIPTION("WAW hazard when depth aspect of attachment is cleared inside render pass");
    AddRequiredExtensions(VK_KHR_LOAD_STORE_OP_NONE_EXTENSION_NAME);
    RETURN_IF_SKIP(InitSyncVal());

    const uint32_t width = 256;
    const uint32_t height = 128;
    const VkFormat depth_stencil_format = FindSupportedDepthStencilFormat(Gpu());

    vkt::Image src_image(*m_device, width, height, depth_stencil_format, VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
    vkt::Image image(*m_device, width, height, depth_stencil_format,
                     VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
    vkt::ImageView image_view = image.CreateView(VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);

    RenderPassSingleSubpass render_pass(*this);
    render_pass.AddAttachmentDescription(depth_stencil_format, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL,
                                         VK_ATTACHMENT_LOAD_OP_NONE, VK_ATTACHMENT_STORE_OP_NONE);
    render_pass.AddDepthStencilAttachment(0, VK_IMAGE_LAYOUT_GENERAL);
    render_pass.CreateRenderPass();

    vkt::Framebuffer framebuffer(*m_device, render_pass, 1, &image_view.handle(), width, height);

    // Copy to the left half
    VkImageCopy copy_region = {};
    copy_region.srcSubresource = {VK_IMAGE_ASPECT_DEPTH_BIT, 0, 0, 1};
    copy_region.dstSubresource = {VK_IMAGE_ASPECT_DEPTH_BIT, 0, 0, 1};
    copy_region.extent = {width / 2, height, 1};

    // Clear the right half
    const VkClearAttachment clear_attachment = {VK_IMAGE_ASPECT_DEPTH_BIT, 0};
    VkClearRect clear_rect = {};
    clear_rect.rect = {{width / 2, 0}, {width / 2, height}};
    clear_rect.baseArrayLayer = 0;
    clear_rect.layerCount = 1;

    m_command_buffer.Begin();
    vk::CmdCopyImage(m_command_buffer, src_image, VK_IMAGE_LAYOUT_GENERAL, image, VK_IMAGE_LAYOUT_GENERAL, 1, &copy_region);

    // loadOp/storeOp=NONE, so no hazard can be detected at this point
    m_command_buffer.BeginRenderPass(render_pass, framebuffer, width, height);

    // Clear operation hazards with the previous copy.
    // The copy region and the clear region do not overlap but the clear operation can
    // access the entire attachment, so it can race with copy accesses and makes result
    // of the copy operation undefined. Note, that storeOp=NONE works as DONT_CARE here
    // (due to write) and the clear results are discarded.
    m_errorMonitor->SetDesiredError("SYNC-HAZARD-WRITE-AFTER-WRITE");
    vk::CmdClearAttachments(m_command_buffer, 1, &clear_attachment, 1, &clear_rect);
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeSyncValRenderPass, ClearStencilAspectWAW) {
    TEST_DESCRIPTION("WAW hazard when stencil aspect of attachment is cleared inside render pass");
    AddRequiredExtensions(VK_KHR_LOAD_STORE_OP_NONE_EXTENSION_NAME);
    RETURN_IF_SKIP(InitSyncVal());

    const uint32_t width = 256;
    const uint32_t height = 128;
    const VkFormat depth_stencil_format = FindSupportedDepthStencilFormat(Gpu());

    vkt::Image src_image(*m_device, width, height, depth_stencil_format, VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
    vkt::Image image(*m_device, width, height, depth_stencil_format,
                     VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
    vkt::ImageView image_view = image.CreateView(VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);

    VkAttachmentDescription attachment{};
    attachment.format = depth_stencil_format;
    attachment.samples = VK_SAMPLE_COUNT_1_BIT;
    attachment.loadOp = VK_ATTACHMENT_LOAD_OP_NONE;
    attachment.storeOp = VK_ATTACHMENT_STORE_OP_NONE;
    attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_NONE;
    attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_NONE;
    attachment.initialLayout = VK_IMAGE_LAYOUT_GENERAL;
    attachment.finalLayout = VK_IMAGE_LAYOUT_GENERAL;

    RenderPassSingleSubpass render_pass(*this);
    render_pass.AddAttachmentDescription(attachment);
    render_pass.AddDepthStencilAttachment(0, VK_IMAGE_LAYOUT_GENERAL);
    render_pass.CreateRenderPass();

    vkt::Framebuffer framebuffer(*m_device, render_pass, 1, &image_view.handle(), width, height);

    // Copy to the left half
    VkImageCopy copy_region = {};
    copy_region.srcSubresource = {VK_IMAGE_ASPECT_STENCIL_BIT, 0, 0, 1};
    copy_region.dstSubresource = {VK_IMAGE_ASPECT_STENCIL_BIT, 0, 0, 1};
    copy_region.extent = {width / 2, height, 1};

    // Clear the right half
    const VkClearAttachment clear_attachment = {VK_IMAGE_ASPECT_STENCIL_BIT};
    VkClearRect clear_rect = {};
    clear_rect.rect = {{width / 2, 0}, {width / 2, height}};
    clear_rect.baseArrayLayer = 0;
    clear_rect.layerCount = 1;

    m_command_buffer.Begin();
    vk::CmdCopyImage(m_command_buffer, src_image, VK_IMAGE_LAYOUT_GENERAL, image, VK_IMAGE_LAYOUT_GENERAL, 1, &copy_region);

    // loadOp/storeOp/stencilLoadOp/stencilStoreOp=NONE, so no hazard can be detected at this point
    m_command_buffer.BeginRenderPass(render_pass, framebuffer, width, height);

    // Clear operation hazards with the previous copy.
    // The copy region and the clear region do not overlap but the clear operation can
    // access the entire attachment, so it can race with copy accesses and makes result
    // of the copy operation undefined. Note, that storeOp=NONE works as DONT_CARE here
    // (due to write) and the clear results are discarded.
    m_errorMonitor->SetDesiredError("SYNC-HAZARD-WRITE-AFTER-WRITE");
    vk::CmdClearAttachments(m_command_buffer, 1, &clear_attachment, 1, &clear_rect);
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeSyncValRenderPass, ClearAttachmentUsesClearAspectMask) {
    TEST_DESCRIPTION("CmdClearAttachments uses the clear attachment aspect mask, not the view aspect mask");
    AddRequiredExtensions(VK_KHR_LOAD_STORE_OP_NONE_EXTENSION_NAME);
    RETURN_IF_SKIP(InitSyncVal());

    const VkFormat depth_stencil_format = FindSupportedDepthStencilFormat(Gpu());

    vkt::Image src_image(*m_device, 64, 64, depth_stencil_format, VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
    vkt::Image image(*m_device, 64, 64, depth_stencil_format,
                     VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);

    // Spec: "When an image view of a depth/stencil image is used as a depth/stencil framebuffer attachment,
    // the aspectMask is ignored and both depth and stencil image subresources are used.
    //
    // Use a depth-only view to verify that CmdClearAttachments uses STENCIL aspect when it is specified by
    // VkClearAttachment::aspectMask.
    vkt::ImageView image_view = image.CreateView(VK_IMAGE_ASPECT_DEPTH_BIT);

    VkAttachmentDescription attachment{};
    attachment.format = depth_stencil_format;
    attachment.samples = VK_SAMPLE_COUNT_1_BIT;
    attachment.loadOp = VK_ATTACHMENT_LOAD_OP_NONE;
    attachment.storeOp = VK_ATTACHMENT_STORE_OP_NONE;
    attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_NONE;
    attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_NONE;
    attachment.initialLayout = VK_IMAGE_LAYOUT_GENERAL;
    attachment.finalLayout = VK_IMAGE_LAYOUT_GENERAL;

    RenderPassSingleSubpass render_pass(*this);
    render_pass.AddAttachmentDescription(attachment);
    render_pass.AddDepthStencilAttachment(0, VK_IMAGE_LAYOUT_GENERAL);
    render_pass.CreateRenderPass();

    vkt::Framebuffer framebuffer(*m_device, render_pass, 1, &image_view.handle(), 64, 64);

    VkImageCopy copy_region = {};
    copy_region.srcSubresource = {VK_IMAGE_ASPECT_STENCIL_BIT, 0, 0, 1};
    copy_region.dstSubresource = {VK_IMAGE_ASPECT_STENCIL_BIT, 0, 0, 1};
    copy_region.extent = {64, 64, 1};

    // The render pass instance specifies both depth/stencil attachment.
    // We can clear them both, the view aspect mask (DEPTH) is ignored
    const VkClearAttachment clear_attachment = {VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT};
    VkClearRect clear_rect = {};
    clear_rect.rect = {{0, 0}, {64, 64}};
    clear_rect.baseArrayLayer = 0;
    clear_rect.layerCount = 1;

    m_command_buffer.Begin();
    vk::CmdCopyImage(m_command_buffer, src_image, VK_IMAGE_LAYOUT_GENERAL, image, VK_IMAGE_LAYOUT_GENERAL, 1, &copy_region);
    m_command_buffer.BeginRenderPass(render_pass, framebuffer, 64, 64);

    // Create a hazard by writing to the STENCIL aspect.
    // We had regression where CmdClearAttachments used the view aspect (DEPTH),
    // so this did not report a hazard with the previous STENCIL copy
    m_errorMonitor->SetDesiredError("SYNC-HAZARD-WRITE-AFTER-WRITE");
    vk::CmdClearAttachments(m_command_buffer, 1, &clear_attachment, 1, &clear_rect);
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeSyncValRenderPass, InputAttachmentReadAndResolveWrite) {
    TEST_DESCRIPTION("Insufficient synchronization between input attachment read and multisample resolve write");
    RETURN_IF_SKIP(InitSyncVal());

    VkImageCreateInfo multi_sample_image_ci =
        vkt::Image::ImageCreateInfo2D(32, 32, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
    multi_sample_image_ci.samples = VK_SAMPLE_COUNT_4_BIT;
    vkt::Image multi_sample_image(*m_device, multi_sample_image_ci);
    vkt::ImageView multi_sample_image_view = multi_sample_image.CreateView();

    VkImageCreateInfo single_sample_image_ci = vkt::Image::ImageCreateInfo2D(
        32, 32, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT);
    vkt::Image single_sample_image(*m_device, single_sample_image_ci);
    vkt::ImageView single_sample_image_view = single_sample_image.CreateView();

    VkAttachmentDescription multi_sample_attachment = {};
    multi_sample_attachment.format = VK_FORMAT_R8G8B8A8_UNORM;
    multi_sample_attachment.samples = VK_SAMPLE_COUNT_4_BIT;
    multi_sample_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
    multi_sample_attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
    multi_sample_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    multi_sample_attachment.finalLayout = VK_IMAGE_LAYOUT_GENERAL;

    VkAttachmentDescription single_sample_attachment = {};
    single_sample_attachment.format = VK_FORMAT_R8G8B8A8_UNORM;
    single_sample_attachment.samples = VK_SAMPLE_COUNT_1_BIT;
    single_sample_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
    single_sample_attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
    single_sample_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    single_sample_attachment.finalLayout = VK_IMAGE_LAYOUT_GENERAL;

    const VkAttachmentReference multi_sample_attachment_ref = {0, VK_IMAGE_LAYOUT_GENERAL};
    const VkAttachmentReference single_sample_attachment_ref = {1, VK_IMAGE_LAYOUT_GENERAL};

    VkSubpassDescription subpass0{};
    subpass0.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
    subpass0.colorAttachmentCount = 1;
    subpass0.pColorAttachments = &multi_sample_attachment_ref;
    subpass0.pResolveAttachments = &single_sample_attachment_ref;

    VkSubpassDescription subpass1{};
    subpass1.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
    subpass1.inputAttachmentCount = 1;
    subpass1.pInputAttachments = &single_sample_attachment_ref;

    VkSubpassDependency subpass_dependency{};
    subpass_dependency.srcSubpass = 0;
    subpass_dependency.dstSubpass = 1;
    subpass_dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
    subpass_dependency.dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
    // this does not make resolve writes visible
    subpass_dependency.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
    // try to read resolved attachment as input attachment in the second subpass
    subpass_dependency.dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;

    const VkSubpassDescription subpasses[2] = {subpass0, subpass1};
    const VkAttachmentDescription attachments[2] = {multi_sample_attachment, single_sample_attachment};
    const VkImageView image_views[2] = {multi_sample_image_view, single_sample_image_view};

    VkRenderPassCreateInfo renderpass_ci = vku::InitStructHelper();
    renderpass_ci.attachmentCount = 2;
    renderpass_ci.pAttachments = attachments;
    renderpass_ci.subpassCount = 2;
    renderpass_ci.pSubpasses = subpasses;
    renderpass_ci.dependencyCount = 1;
    renderpass_ci.pDependencies = &subpass_dependency;

    const vkt::RenderPass render_pass(*m_device, renderpass_ci);
    const vkt::Framebuffer framebuffer(*m_device, render_pass, 2, image_views, 32, 32);

    VkShaderObj vs(*m_device, kVertexMinimalGlsl, VK_SHADER_STAGE_VERTEX_BIT);
    VkShaderObj fs_input_attachment_read(*m_device, kFragmentSubpassLoadGlsl, VK_SHADER_STAGE_FRAGMENT_BIT);

    VkPipelineMultisampleStateCreateInfo ms_state_ci = vku::InitStructHelper();
    ms_state_ci.rasterizationSamples = VK_SAMPLE_COUNT_4_BIT;

    CreatePipelineHelper pipe(*this);
    pipe.shader_stages_ = {vs.GetStageCreateInfo(), fs_input_attachment_read.GetStageCreateInfo()};
    pipe.dsl_bindings_[0] = {0, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1, VK_SHADER_STAGE_FRAGMENT_BIT};
    pipe.gp_ci_.renderPass = render_pass;
    pipe.gp_ci_.subpass = 1;
    pipe.ms_ci_ = ms_state_ci;
    pipe.CreateGraphicsPipeline();
    pipe.descriptor_set_->WriteDescriptorImageInfo(0, single_sample_image_view, VK_NULL_HANDLE, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,
                                                   VK_IMAGE_LAYOUT_GENERAL);
    pipe.descriptor_set_->UpdateDescriptorSets();

    m_command_buffer.Begin();
    m_command_buffer.BeginRenderPass(render_pass, framebuffer, 32, 32);
    // Resolve at the end of this subpass
    m_command_buffer.NextSubpass();
    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe);
    vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.pipeline_layout_, 0, 1,
                              &pipe.descriptor_set_->set_, 0, nullptr);
    // Read resolved data as input attachment
    m_errorMonitor->SetDesiredError("SYNC-HAZARD-READ-AFTER-WRITE");
    vk::CmdDraw(m_command_buffer, 1, 0, 0, 0);
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeSyncValRenderPass, StoreOpWAR) {
    TEST_DESCRIPTION("StoreOp WAR hazard");
    AddRequiredExtensions(VK_KHR_LOAD_STORE_OP_NONE_EXTENSION_NAME);
    RETURN_IF_SKIP(InitSyncVal());

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

    vkt::Buffer buffer(*m_device, 64 * 64 * 4, VK_BUFFER_USAGE_TRANSFER_DST_BIT);

    RenderPassSingleSubpass render_pass(*this);
    render_pass.AddAttachmentDescription(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL,
                                         VK_ATTACHMENT_LOAD_OP_NONE, VK_ATTACHMENT_STORE_OP_STORE);
    render_pass.AddColorAttachment(0, VK_IMAGE_LAYOUT_GENERAL);
    render_pass.CreateRenderPass();
    vkt::Framebuffer framebuffer(*m_device, render_pass, 1, &image_view.handle(), 64, 64);

    VkBufferImageCopy region{};
    region.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
    region.imageExtent = {64, 64, 1};

    m_command_buffer.Begin();
    // Copy read
    vk::CmdCopyImageToBuffer(m_command_buffer, image, VK_IMAGE_LAYOUT_GENERAL, buffer, 1, &region);

    // There is no loadOp access (LOAD_OP_NONE)
    m_command_buffer.BeginRenderPass(render_pass, framebuffer, 64, 64);

    // StoreOp write
    m_errorMonitor->SetDesiredError("SYNC-HAZARD-WRITE-AFTER-READ");
    m_command_buffer.EndRenderPass();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeSyncValRenderPass, MultisampleAttachmentRAW) {
    TEST_DESCRIPTION("Insufficient synchronization between multisample resolve read and previous attachment clear");
    RETURN_IF_SKIP(InitSyncVal());

    VkImageCreateInfo multi_sample_image_ci =
        vkt::Image::ImageCreateInfo2D(32, 32, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
    multi_sample_image_ci.samples = VK_SAMPLE_COUNT_4_BIT;
    vkt::Image multi_sample_image(*m_device, multi_sample_image_ci);
    vkt::ImageView multi_sample_image_view = multi_sample_image.CreateView();

    VkImageCreateInfo single_sample_image_ci = vkt::Image::ImageCreateInfo2D(
        32, 32, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT);
    vkt::Image single_sample_image(*m_device, single_sample_image_ci);
    vkt::ImageView single_sample_image_view = single_sample_image.CreateView();

    VkAttachmentDescription multi_sample_attachment = {};
    multi_sample_attachment.format = VK_FORMAT_R8G8B8A8_UNORM;
    multi_sample_attachment.samples = VK_SAMPLE_COUNT_4_BIT;
    multi_sample_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
    multi_sample_attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
    multi_sample_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    multi_sample_attachment.finalLayout = VK_IMAGE_LAYOUT_GENERAL;

    VkAttachmentDescription single_sample_attachment = {};
    single_sample_attachment.format = VK_FORMAT_R8G8B8A8_UNORM;
    single_sample_attachment.samples = VK_SAMPLE_COUNT_1_BIT;
    single_sample_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
    single_sample_attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
    single_sample_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    single_sample_attachment.finalLayout = VK_IMAGE_LAYOUT_GENERAL;

    const VkAttachmentReference multi_sample_attachment_ref = {0, VK_IMAGE_LAYOUT_GENERAL};
    const VkAttachmentReference single_sample_attachment_ref = {1, VK_IMAGE_LAYOUT_GENERAL};

    VkSubpassDescription subpass0{};
    subpass0.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
    subpass0.colorAttachmentCount = 1;
    subpass0.pColorAttachments = &multi_sample_attachment_ref;

    VkSubpassDescription subpass1{};
    subpass1.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
    subpass1.colorAttachmentCount = 1;
    subpass1.pColorAttachments = &multi_sample_attachment_ref;
    subpass1.pResolveAttachments = &single_sample_attachment_ref;

    VkSubpassDependency subpass_dependency{};
    subpass_dependency.srcSubpass = 0;
    subpass_dependency.dstSubpass = 1;
    subpass_dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
    subpass_dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
    subpass_dependency.srcAccessMask = 0;  // Hazard: do not make clear writes available
    subpass_dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;

    const VkSubpassDescription subpasses[2] = {subpass0, subpass1};
    const VkAttachmentDescription attachments[2] = {multi_sample_attachment, single_sample_attachment};
    const VkImageView image_views[2] = {multi_sample_image_view, single_sample_image_view};

    VkRenderPassCreateInfo renderpass_ci = vku::InitStructHelper();
    renderpass_ci.attachmentCount = 2;
    renderpass_ci.pAttachments = attachments;
    renderpass_ci.subpassCount = 2;
    renderpass_ci.pSubpasses = subpasses;
    renderpass_ci.dependencyCount = 1;
    renderpass_ci.pDependencies = &subpass_dependency;

    const vkt::RenderPass render_pass(*m_device, renderpass_ci);
    const vkt::Framebuffer framebuffer(*m_device, render_pass, 2, image_views, 32, 32);

    VkClearAttachment clear_attachment{};
    clear_attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    clear_attachment.colorAttachment = 0;

    VkClearRect clear_rect{};
    clear_rect.rect = {{0, 0}, {32, 32}};
    clear_rect.baseArrayLayer = 0;
    clear_rect.layerCount = 1;

    m_command_buffer.Begin();
    m_command_buffer.BeginRenderPass(render_pass, framebuffer, 32, 32);
    vk::CmdClearAttachments(m_command_buffer, 1, &clear_attachment, 1, &clear_rect);

    m_command_buffer.NextSubpass();

    // Hazard when reading multi sample attachment due to incomplete sync with clear
    m_errorMonitor->SetDesiredError("SYNC-HAZARD-READ-AFTER-WRITE");
    m_command_buffer.EndRenderPass();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeSyncValRenderPass, InputAttachmentRAW) {
    TEST_DESCRIPTION("Input attachment read hazards with previous attachment write");
    RETURN_IF_SKIP(InitSyncVal());

    RenderPassSingleSubpass rp(*this);
    rp.AddAttachmentDescription(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL,
                                VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_DONT_CARE);
    rp.AddColorAttachment(0, VK_IMAGE_LAYOUT_GENERAL);
    rp.AddInputAttachment(0, VK_IMAGE_LAYOUT_GENERAL);
    rp.CreateRenderPass();

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

    vkt::Framebuffer framebuffer(*m_device, rp, 1, &image_view.handle(), 64, 64);

    VkShaderObj vs(*m_device, kVertexMinimalGlsl, VK_SHADER_STAGE_VERTEX_BIT);
    VkShaderObj fs_write(*m_device, kFragmentMinimalGlsl, VK_SHADER_STAGE_FRAGMENT_BIT);
    VkShaderObj fs_read(*m_device, kFragmentSubpassLoadGlsl, VK_SHADER_STAGE_FRAGMENT_BIT);

    CreatePipelineHelper pipe_write(*this);
    pipe_write.shader_stages_ = {vs.GetStageCreateInfo(), fs_write.GetStageCreateInfo()};
    pipe_write.gp_ci_.renderPass = rp;
    pipe_write.CreateGraphicsPipeline();

    CreatePipelineHelper pipe_read(*this);
    pipe_read.shader_stages_ = {vs.GetStageCreateInfo(), fs_read.GetStageCreateInfo()};
    pipe_read.dsl_bindings_[0] = {0, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1, VK_SHADER_STAGE_FRAGMENT_BIT};
    pipe_read.gp_ci_.renderPass = rp;
    pipe_read.CreateGraphicsPipeline();
    pipe_read.descriptor_set_->WriteDescriptorImageInfo(0, image_view, VK_NULL_HANDLE, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,
                                                        VK_IMAGE_LAYOUT_GENERAL);
    pipe_read.descriptor_set_->UpdateDescriptorSets();

    m_command_buffer.Begin();
    m_command_buffer.BeginRenderPass(rp, framebuffer, 64, 64);

    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe_write);
    vk::CmdDraw(m_command_buffer, 1, 0, 0, 0);

    // Input attachmnent read should be synchronized with writes from previous draws.
    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe_read);
    vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe_read.pipeline_layout_, 0, 1,
                              &pipe_read.descriptor_set_->set_, 0, nullptr);
    m_errorMonitor->SetDesiredError("SYNC-HAZARD-READ-AFTER-WRITE");
    vk::CmdDraw(m_command_buffer, 1, 0, 0, 0);
    m_errorMonitor->VerifyFound();

    m_command_buffer.EndRenderPass();
    m_command_buffer.End();
}

TEST_F(NegativeSyncValRenderPass, InputAttachmentWAR) {
    TEST_DESCRIPTION("Attachment write hazards with the previous input attachment read");
    RETURN_IF_SKIP(InitSyncVal());

    RenderPassSingleSubpass rp(*this);
    rp.AddAttachmentDescription(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL,
                                VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_DONT_CARE);
    rp.AddColorAttachment(0, VK_IMAGE_LAYOUT_GENERAL);
    rp.AddInputAttachment(0, VK_IMAGE_LAYOUT_GENERAL);
    rp.CreateRenderPass();

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

    vkt::Framebuffer framebuffer(*m_device, rp, 1, &image_view.handle(), 64, 64);

    VkShaderObj vs(*m_device, kVertexMinimalGlsl, VK_SHADER_STAGE_VERTEX_BIT);
    VkShaderObj fs_read(*m_device, kFragmentSubpassLoadGlsl, VK_SHADER_STAGE_FRAGMENT_BIT);
    VkShaderObj fs_write(*m_device, kFragmentMinimalGlsl, VK_SHADER_STAGE_FRAGMENT_BIT);

    CreatePipelineHelper pipe_read(*this);
    pipe_read.shader_stages_ = {vs.GetStageCreateInfo(), fs_read.GetStageCreateInfo()};
    pipe_read.dsl_bindings_[0] = {0, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1, VK_SHADER_STAGE_FRAGMENT_BIT};
    pipe_read.gp_ci_.renderPass = rp;
    pipe_read.CreateGraphicsPipeline();
    pipe_read.descriptor_set_->WriteDescriptorImageInfo(0, image_view, VK_NULL_HANDLE, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,
                                                        VK_IMAGE_LAYOUT_GENERAL);
    pipe_read.descriptor_set_->UpdateDescriptorSets();

    CreatePipelineHelper pipe_write(*this);
    pipe_write.shader_stages_ = {vs.GetStageCreateInfo(), fs_write.GetStageCreateInfo()};
    pipe_write.gp_ci_.renderPass = rp;
    pipe_write.CreateGraphicsPipeline();

    m_command_buffer.Begin();
    m_command_buffer.BeginRenderPass(rp, framebuffer, 64, 64);

    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe_read);
    vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe_read.pipeline_layout_, 0, 1,
                              &pipe_read.descriptor_set_->set_, 0, nullptr);
    vk::CmdDraw(m_command_buffer, 1, 0, 0, 0);

    // This draw is not synchronized with input attachment reads (requires subpass pipeline barrier).
    // NOTE: the reason is that input attachment reads are not in the raster order (shader execution
    // accesses in general are not in raster order). The raster order itself defines execution
    // dependency which is enough to resolve WAR but it's not applicable here.
    // https://gitlab.khronos.org/vulkan/vulkan/-/issues/4693
    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe_write);
    m_errorMonitor->SetDesiredError("SYNC-HAZARD-WRITE-AFTER-READ");
    vk::CmdDraw(m_command_buffer, 1, 0, 0, 0);
    m_errorMonitor->VerifyFound();

    m_command_buffer.EndRenderPass();
    m_command_buffer.End();
}

TEST_F(NegativeSyncValRenderPass, LayoutTransitonVsResolveHazard) {
    TEST_DESCRIPTION("Layout transition between subpasses (not into finalLayout) hazards with the previous resolve write");
    RETURN_IF_SKIP(InitSyncVal());

    VkImageCreateInfo image_ci =
        vkt::Image::ImageCreateInfo2D(32, 32, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
    image_ci.samples = VK_SAMPLE_COUNT_4_BIT;
    vkt::Image image(*m_device, image_ci);
    vkt::ImageView image_view = image.CreateView();

    VkImageCreateInfo resolve_image_ci = vkt::Image::ImageCreateInfo2D(
        32, 32, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT);
    vkt::Image resolve_image(*m_device, resolve_image_ci);
    vkt::ImageView resolve_image_view = resolve_image.CreateView();

    VkAttachmentDescription attachment = {};
    attachment.format = VK_FORMAT_R8G8B8A8_UNORM;
    attachment.samples = VK_SAMPLE_COUNT_4_BIT;
    attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
    attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
    attachment.initialLayout = VK_IMAGE_LAYOUT_GENERAL;
    attachment.finalLayout = VK_IMAGE_LAYOUT_GENERAL;

    VkAttachmentDescription resolve_attachment = {};
    resolve_attachment.format = VK_FORMAT_R8G8B8A8_UNORM;
    resolve_attachment.samples = VK_SAMPLE_COUNT_1_BIT;
    resolve_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
    resolve_attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
    resolve_attachment.initialLayout = VK_IMAGE_LAYOUT_GENERAL;
    resolve_attachment.finalLayout = VK_IMAGE_LAYOUT_GENERAL;

    const VkAttachmentReference attachment_ref = {0, VK_IMAGE_LAYOUT_GENERAL};
    const VkAttachmentReference resolve_attachment_ref = {1, VK_IMAGE_LAYOUT_GENERAL};
    const VkAttachmentReference resolve_attachment_ref2 = {1, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL};

    VkSubpassDescription subpass0{};
    subpass0.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
    subpass0.colorAttachmentCount = 1;
    subpass0.pColorAttachments = &attachment_ref;
    subpass0.pResolveAttachments = &resolve_attachment_ref;

    VkSubpassDescription subpass1{};
    subpass1.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
    subpass1.inputAttachmentCount = 1;
    subpass1.pInputAttachments = &resolve_attachment_ref2;

    VkSubpassDependency subpass_dependency{};
    subpass_dependency.srcSubpass = 0;
    subpass_dependency.dstSubpass = 1;
    subpass_dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
    subpass_dependency.dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;

    // Execution dependency with COLOR_ATTACHMENT_OUTPUT does not protect layout transition from previous resolve write
    subpass_dependency.srcAccessMask = 0;

    subpass_dependency.dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;

    const VkSubpassDescription subpasses[2] = {subpass0, subpass1};
    const VkAttachmentDescription attachments[2] = {attachment, resolve_attachment};
    const VkImageView image_views[2] = {image_view, resolve_image_view};

    VkRenderPassCreateInfo renderpass_ci = vku::InitStructHelper();
    renderpass_ci.attachmentCount = 2;
    renderpass_ci.pAttachments = attachments;
    renderpass_ci.subpassCount = 2;
    renderpass_ci.pSubpasses = subpasses;
    renderpass_ci.dependencyCount = 1;
    renderpass_ci.pDependencies = &subpass_dependency;

    const vkt::RenderPass render_pass(*m_device, renderpass_ci);
    const vkt::Framebuffer framebuffer(*m_device, render_pass, 2, image_views, 32, 32);

    m_command_buffer.Begin();
    m_command_buffer.BeginRenderPass(render_pass, framebuffer, 32, 32);
    // Resolve at the end of this subpass then perform layout transition
    m_errorMonitor->SetDesiredError("SYNC-HAZARD-WRITE-AFTER-WRITE");
    m_command_buffer.NextSubpass();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeSyncValRenderPass, FinalLayoutTransitionHazard) {
    TEST_DESCRIPTION("Hazard between final layout transition and attachment access before render pass instance");
    SetTargetApiVersion(VK_API_VERSION_1_4);
    RETURN_IF_SKIP(InitSyncVal());

    vkt::Buffer buffer(*m_device, 32 * 32 * 4, VK_BUFFER_USAGE_TRANSFER_SRC_BIT);

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

    RenderPassSingleSubpass rp(*this);
    rp.AddAttachmentDescription(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
                                VK_ATTACHMENT_LOAD_OP_NONE, VK_ATTACHMENT_STORE_OP_NONE);
    rp.AddColorAttachment(0, VK_IMAGE_LAYOUT_GENERAL);
    rp.CreateRenderPass();
    vkt::Framebuffer framebuffer(*m_device, rp, 1, &image_view.handle());

    VkBufferImageCopy region{};
    region.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
    region.imageExtent = {32, 32, 1};

    m_command_buffer.Begin();
    vk::CmdCopyBufferToImage(m_command_buffer, buffer, image, VK_IMAGE_LAYOUT_GENERAL, 1, &region);
    m_command_buffer.BeginRenderPass(rp, framebuffer, 32, 32);
    // loadOp=NONE and storeOp=NONE, so image copy is followed directly by layout transition.
    // There is implicit dependency with VK_SUBPASS_EXTERNAL but it protects only color output
    // accesses and COPY_WRITE remains unprotected.
    m_errorMonitor->SetDesiredError("SYNC-HAZARD-WRITE-AFTER-WRITE");
    m_command_buffer.EndRenderPass();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeSyncValRenderPass, UnusedAttachmentFinalLayoutTransitionHazard) {
    TEST_DESCRIPTION("https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/5179");
    RETURN_IF_SKIP(InitSyncVal());

    vkt::Buffer buffer(*m_device, 32 * 32 * 4, VK_BUFFER_USAGE_TRANSFER_SRC_BIT);

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

    RenderPassSingleSubpass rp(*this);
    rp.AddAttachmentDescription(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
                                VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE);
    rp.CreateRenderPass();
    vkt::Framebuffer framebuffer(*m_device, rp, 1, &image_view.handle());

    VkBufferImageCopy region{};
    region.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
    region.imageExtent = {32, 32, 1};

    m_command_buffer.Begin();
    vk::CmdCopyBufferToImage(m_command_buffer, buffer, image, VK_IMAGE_LAYOUT_GENERAL, 1, &region);
    m_command_buffer.BeginRenderPass(rp, framebuffer, 32, 32);

    // The attachment is not referenced by the subpass, so loadOp/storeOp are ignored.
    // The final layout transition from initialLayout to finalLayout still executes and
    // hazards with the previous copy.
    m_errorMonitor->SetDesiredError("SYNC-HAZARD-WRITE-AFTER-WRITE");
    m_command_buffer.EndRenderPass();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeSyncValRenderPass, UnusedAttachmentFinalLayoutTransitionRecorded) {
    TEST_DESCRIPTION("Test that unused attachment final layout transition is recorded");
    RETURN_IF_SKIP(InitSyncVal());

    vkt::Buffer buffer(*m_device, 32 * 32 * 4, VK_BUFFER_USAGE_TRANSFER_SRC_BIT);

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

    RenderPassSingleSubpass rp(*this);
    rp.AddAttachmentDescription(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
                                VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE);
    rp.CreateRenderPass();
    vkt::Framebuffer framebuffer(*m_device, rp, 1, &image_view.handle());

    VkBufferImageCopy region{};
    region.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
    region.imageExtent = {32, 32, 1};

    m_command_buffer.Begin();
    m_command_buffer.BeginRenderPass(rp, framebuffer, 32, 32);
    m_command_buffer.EndRenderPass();

    m_errorMonitor->SetDesiredError("SYNC-HAZARD-WRITE-AFTER-WRITE");
    vk::CmdCopyBufferToImage(m_command_buffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeSyncValRenderPass, UnusedAttachmentFinalLayoutTransitionSubmitHazard) {
    TEST_DESCRIPTION("Submit-time validation detects unused attachment final layout transition hazards");
    RETURN_IF_SKIP(InitSyncVal());

    vkt::Buffer buffer(*m_device, 32 * 32 * 4, VK_BUFFER_USAGE_TRANSFER_SRC_BIT);

    vkt::Image image(*m_device, 32, 32, VK_FORMAT_R8G8B8A8_UNORM,
                     VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
    image.SetLayout(VK_IMAGE_LAYOUT_GENERAL);
    vkt::ImageView image_view = image.CreateView();

    RenderPassSingleSubpass rp(*this);
    rp.AddAttachmentDescription(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
                                VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE);
    rp.CreateRenderPass();
    vkt::Framebuffer framebuffer(*m_device, rp, 1, &image_view.handle());

    VkBufferImageCopy region{};
    region.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
    region.imageExtent = {32, 32, 1};

    vkt::CommandBuffer write_cb(*m_device, m_command_pool);
    write_cb.Begin();
    vk::CmdCopyBufferToImage(write_cb, buffer, image, VK_IMAGE_LAYOUT_GENERAL, 1, &region);
    write_cb.End();

    vkt::CommandBuffer render_pass_cb(*m_device, m_command_pool);
    render_pass_cb.Begin();
    render_pass_cb.BeginRenderPass(rp, framebuffer, 32, 32);
    render_pass_cb.EndRenderPass();
    render_pass_cb.End();

    m_default_queue->Submit(write_cb);

    // The attachment is not referenced by any subpass, so the only access in this command
    // buffer is the final layout transition from initialLayout to finalLayout
    m_errorMonitor->SetDesiredError("SYNC-HAZARD-WRITE-AFTER-WRITE");
    m_default_queue->Submit(render_pass_cb);
    m_errorMonitor->VerifyFound();
    m_default_queue->Wait();
}

TEST_F(NegativeSyncValRenderPass, FinalLayoutTransitionSubmitHazard) {
    TEST_DESCRIPTION("Submit-time validation detects final layout transition hazards");
    SetTargetApiVersion(VK_API_VERSION_1_4);
    RETURN_IF_SKIP(InitSyncVal());

    vkt::Buffer buffer(*m_device, 32 * 32 * 4, VK_BUFFER_USAGE_TRANSFER_SRC_BIT);

    vkt::Image image(*m_device, 32, 32, VK_FORMAT_R8G8B8A8_UNORM,
                     VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
    image.SetLayout(VK_IMAGE_LAYOUT_GENERAL);
    vkt::ImageView image_view = image.CreateView();

    RenderPassSingleSubpass rp(*this);
    rp.AddAttachmentDescription(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
                                VK_ATTACHMENT_LOAD_OP_NONE, VK_ATTACHMENT_STORE_OP_NONE);
    rp.AddColorAttachment(0, VK_IMAGE_LAYOUT_GENERAL);
    rp.CreateRenderPass();
    vkt::Framebuffer framebuffer(*m_device, rp, 1, &image_view.handle());

    VkBufferImageCopy region{};
    region.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
    region.imageExtent = {32, 32, 1};

    vkt::CommandBuffer write_cb(*m_device, m_command_pool);
    write_cb.Begin();
    vk::CmdCopyBufferToImage(write_cb, buffer, image, VK_IMAGE_LAYOUT_GENERAL, 1, &region);
    write_cb.End();

    vkt::CommandBuffer render_pass_cb(*m_device, m_command_pool);
    render_pass_cb.Begin();
    render_pass_cb.BeginRenderPass(rp, framebuffer, 32, 32);
    render_pass_cb.EndRenderPass();
    render_pass_cb.End();

    m_default_queue->Submit(write_cb);

    // The attachment is referenced by the subpass, but loadOp/storeOp are NONE,
    // so the only access in this command buffer is the final layout transition.
    m_errorMonitor->SetDesiredError("SYNC-HAZARD-WRITE-AFTER-WRITE");
    m_default_queue->Submit(render_pass_cb);
    m_errorMonitor->VerifyFound();
    m_default_queue->Wait();
}

TEST_F(NegativeSyncValRenderPass, MultiviewSameView) {
    TEST_DESCRIPTION("Two async subpasses render to the same view");
    SetTargetApiVersion(VK_API_VERSION_1_1);
    AddRequiredFeature(vkt::Feature::multiview);
    RETURN_IF_SKIP(InitSyncVal());

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

    VkAttachmentDescription attachment{};
    attachment.format = VK_FORMAT_R8G8B8A8_UNORM;
    attachment.samples = VK_SAMPLE_COUNT_1_BIT;
    attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
    attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
    attachment.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
    attachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

    const VkAttachmentReference color_ref = {0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};

    VkSubpassDescription subpasses[2] = {};
    subpasses[0].pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
    subpasses[0].colorAttachmentCount = 1;
    subpasses[0].pColorAttachments = &color_ref;

    subpasses[1] = subpasses[0];

    // Both subpasses render to the same view
    const uint32_t view_masks[2] = {1, 1};
    VkRenderPassMultiviewCreateInfo multiview_ci = vku::InitStructHelper();
    multiview_ci.subpassCount = 2;
    multiview_ci.pViewMasks = view_masks;

    VkRenderPassCreateInfo render_pass_ci = vku::InitStructHelper(&multiview_ci);
    render_pass_ci.attachmentCount = 1;
    render_pass_ci.pAttachments = &attachment;
    render_pass_ci.subpassCount = 2;
    render_pass_ci.pSubpasses = subpasses;
    vkt::RenderPass render_pass(*m_device, render_pass_ci);

    vkt::Framebuffer framebuffer(*m_device, render_pass, 1, &image_view.handle(), 128, 128);
    VkClearValue clear_value{};

    m_command_buffer.Begin();
    m_command_buffer.BeginRenderPass(render_pass, framebuffer, 128, 128, 1, &clear_value);
    m_errorMonitor->SetDesiredError("SYNC-HAZARD-WRITE-RACING-WRITE");
    m_command_buffer.NextSubpass();
    m_command_buffer.EndRenderPass();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeSyncValRenderPass, MultiviewSharedView) {
    TEST_DESCRIPTION("Two async subpasses have a shared view");
    SetTargetApiVersion(VK_API_VERSION_1_1);
    AddRequiredFeature(vkt::Feature::multiview);
    RETURN_IF_SKIP(InitSyncVal());

    const VkImageCreateInfo image_ci =
        vkt::Image::ImageCreateInfo2D(128, 128, 1, 3 /*layers*/, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
    vkt::Image image(*m_device, image_ci);
    vkt::ImageView image_view = image.CreateView(VK_IMAGE_VIEW_TYPE_2D_ARRAY, 0, 1, 0, 3 /*layers*/);

    VkAttachmentDescription attachment{};
    attachment.format = VK_FORMAT_R8G8B8A8_UNORM;
    attachment.samples = VK_SAMPLE_COUNT_1_BIT;
    attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
    attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
    attachment.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
    attachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

    const VkAttachmentReference color_ref = {0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};

    VkSubpassDescription subpasses[2] = {};
    subpasses[0].pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
    subpasses[0].colorAttachmentCount = 1;
    subpasses[0].pColorAttachments = &color_ref;

    subpasses[1] = subpasses[0];

    // Subpasses share layer 1
    const uint32_t view_masks[2] = {0x3 /* layer0 + layer 1*/, 0x6 /* layer 1 + layer 2*/};
    VkRenderPassMultiviewCreateInfo multiview_ci = vku::InitStructHelper();
    multiview_ci.subpassCount = 2;
    multiview_ci.pViewMasks = view_masks;

    VkRenderPassCreateInfo render_pass_ci = vku::InitStructHelper(&multiview_ci);
    render_pass_ci.attachmentCount = 1;
    render_pass_ci.pAttachments = &attachment;
    render_pass_ci.subpassCount = 2;
    render_pass_ci.pSubpasses = subpasses;
    vkt::RenderPass render_pass(*m_device, render_pass_ci);

    vkt::Framebuffer framebuffer(*m_device, render_pass, 1, &image_view.handle(), 128, 128);
    VkClearValue clear_value{};

    m_command_buffer.Begin();
    m_command_buffer.BeginRenderPass(render_pass, framebuffer, 128, 128, 1, &clear_value);
    m_command_buffer.NextSubpass();
    m_errorMonitor->SetDesiredError("SYNC-HAZARD-WRITE-RACING-WRITE");
    m_command_buffer.EndRenderPass();
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeSyncValRenderPass, MultiviewMultipleStoreOps) {
    TEST_DESCRIPTION("Multiview broadcasts storeOp to different subpasses");
    SetTargetApiVersion(VK_API_VERSION_1_4);
    AddRequiredFeature(vkt::Feature::multiview);
    RETURN_IF_SKIP(InitSyncVal());

    const VkImageCreateInfo image_ci = vkt::Image::ImageCreateInfo2D(
        128, 128, 1, 2, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
    vkt::Image image(*m_device, image_ci);
    vkt::ImageView image_view = image.CreateView(VK_IMAGE_VIEW_TYPE_2D_ARRAY, 0, 1, 0, 2);

    vkt::Buffer buffer(*m_device, 128 * 128 * 4 * 2, VK_BUFFER_USAGE_TRANSFER_SRC_BIT);

    VkAttachmentDescription attachment{};
    attachment.format = VK_FORMAT_R8G8B8A8_UNORM;
    attachment.samples = VK_SAMPLE_COUNT_1_BIT;
    attachment.loadOp = VK_ATTACHMENT_LOAD_OP_NONE;
    attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
    attachment.initialLayout = VK_IMAGE_LAYOUT_GENERAL;
    attachment.finalLayout = VK_IMAGE_LAYOUT_GENERAL;

    const VkAttachmentReference color_ref = {0, VK_IMAGE_LAYOUT_GENERAL};

    VkSubpassDescription subpasses[2] = {};
    subpasses[0].pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
    subpasses[0].colorAttachmentCount = 1;
    subpasses[0].pColorAttachments = &color_ref;

    subpasses[1] = subpasses[0];

    // Define dependency with external copy for the second subpass but not for the first one
    VkSubpassDependency subpass_dependency{};
    subpass_dependency.srcSubpass = 1;
    subpass_dependency.dstSubpass = VK_SUBPASS_EXTERNAL;
    subpass_dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
    subpass_dependency.dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
    subpass_dependency.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
    subpass_dependency.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;

    const uint32_t view_masks[2] = {1, 2};
    VkRenderPassMultiviewCreateInfo multiview_ci = vku::InitStructHelper();
    multiview_ci.subpassCount = 2;
    multiview_ci.pViewMasks = view_masks;

    VkRenderPassCreateInfo render_pass_ci = vku::InitStructHelper(&multiview_ci);
    render_pass_ci.attachmentCount = 1;
    render_pass_ci.pAttachments = &attachment;
    render_pass_ci.subpassCount = 2;
    render_pass_ci.pSubpasses = subpasses;
    render_pass_ci.dependencyCount = 1;
    render_pass_ci.pDependencies = &subpass_dependency;

    vkt::RenderPass render_pass(*m_device, render_pass_ci);
    vkt::Framebuffer framebuffer(*m_device, render_pass, 1, &image_view.handle(), 128, 128);

    VkBufferImageCopy region{};
    region.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 2};
    region.imageExtent = {128, 128, 1};

    m_command_buffer.Begin();

    m_command_buffer.BeginRenderPass(render_pass, framebuffer, 128, 128);
    // storeOp for view 0 happens at the end of this subpass (0).
    // This storeOp is not synchronized by external subpass dependency

    m_command_buffer.NextSubpass();
    // storeOp for view 1 happens at the end of this subpass (1)

    m_command_buffer.EndRenderPass();

    m_errorMonitor->SetDesiredError("SYNC-HAZARD-WRITE-AFTER-WRITE");
    vk::CmdCopyBufferToImage(m_command_buffer, buffer, image, VK_IMAGE_LAYOUT_GENERAL, 1, &region);
    m_errorMonitor->VerifyFound();
    m_command_buffer.End();
}

TEST_F(NegativeSyncValRenderPass, MultiviewMultipleLoadOps) {
    TEST_DESCRIPTION("Multiview broadcasts loadOp to different subpasses");
    SetTargetApiVersion(VK_API_VERSION_1_4);
    AddRequiredFeature(vkt::Feature::multiview);
    RETURN_IF_SKIP(InitSyncVal());

    const VkImageCreateInfo image_ci = vkt::Image::ImageCreateInfo2D(
        128, 128, 1, 2, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
    vkt::Image image(*m_device, image_ci);
    vkt::ImageView image_view = image.CreateView(VK_IMAGE_VIEW_TYPE_2D_ARRAY, 0, 1, 0, 2);

    vkt::Buffer buffer(*m_device, 128 * 128 * 4 * 2, VK_BUFFER_USAGE_TRANSFER_SRC_BIT);

    VkAttachmentDescription attachment{};
    attachment.format = VK_FORMAT_R8G8B8A8_UNORM;
    attachment.samples = VK_SAMPLE_COUNT_1_BIT;
    attachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
    attachment.storeOp = VK_ATTACHMENT_STORE_OP_NONE;
    attachment.initialLayout = VK_IMAGE_LAYOUT_GENERAL;
    attachment.finalLayout = VK_IMAGE_LAYOUT_GENERAL;

    const VkAttachmentReference color_ref = {0, VK_IMAGE_LAYOUT_GENERAL};

    VkSubpassDescription subpasses[2] = {};
    subpasses[0].pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
    subpasses[0].colorAttachmentCount = 1;
    subpasses[0].pColorAttachments = &color_ref;

    subpasses[1] = subpasses[0];

    // Define dependency with external copy for the first subpass but not for the second one
    VkSubpassDependency subpass_dependency{};
    subpass_dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
    subpass_dependency.dstSubpass = 0;
    subpass_dependency.srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
    subpass_dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
    subpass_dependency.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
    subpass_dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;

    const uint32_t view_masks[2] = {1, 2};
    VkRenderPassMultiviewCreateInfo multiview_ci = vku::InitStructHelper();
    multiview_ci.subpassCount = 2;
    multiview_ci.pViewMasks = view_masks;

    VkRenderPassCreateInfo render_pass_ci = vku::InitStructHelper(&multiview_ci);
    render_pass_ci.attachmentCount = 1;
    render_pass_ci.pAttachments = &attachment;
    render_pass_ci.subpassCount = 2;
    render_pass_ci.pSubpasses = subpasses;
    render_pass_ci.dependencyCount = 1;
    render_pass_ci.pDependencies = &subpass_dependency;

    vkt::RenderPass render_pass(*m_device, render_pass_ci);
    vkt::Framebuffer framebuffer(*m_device, render_pass, 1, &image_view.handle(), 128, 128);

    VkBufferImageCopy region{};
    region.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 2};
    region.imageExtent = {128, 128, 1};

    m_command_buffer.Begin();

    vk::CmdCopyBufferToImage(m_command_buffer, buffer, image, VK_IMAGE_LAYOUT_GENERAL, 1, &region);

    m_command_buffer.BeginRenderPass(render_pass, framebuffer, 128, 128);
    // loadOp for view 0 happens at the beginning of this subpass (0).

    m_errorMonitor->SetDesiredError("SYNC-HAZARD-READ-AFTER-WRITE");
    m_command_buffer.NextSubpass();
    // loadOp for view 1 happens at the beginning of this subpass (1).
    // This load is not synchronized by external subpass dependency

    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeSyncValRenderPass, MultiviewClearAttachments) {
    TEST_DESCRIPTION("Test that CmdClearAttachment clear itw own view");
    SetTargetApiVersion(VK_API_VERSION_1_4);
    AddRequiredFeature(vkt::Feature::multiview);
    RETURN_IF_SKIP(InitSyncVal());

    const VkImageCreateInfo image_ci = vkt::Image::ImageCreateInfo2D(
        128, 128, 1, 2, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
    vkt::Image image(*m_device, image_ci);
    vkt::ImageView image_view = image.CreateView(VK_IMAGE_VIEW_TYPE_2D_ARRAY, 0, 1, 0, 2);

    vkt::Buffer buffer(*m_device, 128 * 128 * 4 * 2, VK_BUFFER_USAGE_TRANSFER_SRC_BIT);

    const uint32_t view_mask = 2;
    VkRenderPassMultiviewCreateInfo multiview_ci = vku::InitStructHelper();
    multiview_ci.subpassCount = 1;
    multiview_ci.pViewMasks = &view_mask;

    RenderPassSingleSubpass render_pass(*this);
    render_pass.AddAttachmentDescription(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL,
                                         VK_ATTACHMENT_LOAD_OP_NONE, VK_ATTACHMENT_STORE_OP_STORE);
    render_pass.AddColorAttachment(0, VK_IMAGE_LAYOUT_GENERAL);
    render_pass.CreateRenderPass(&multiview_ci);

    vkt::Framebuffer framebuffer(*m_device, render_pass, 1, &image_view.handle(), 128, 128);

    VkClearAttachment clear_attachment{};
    clear_attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;

    // With multi view clear rect baseLayer/layerCount is not used (should be 0/1)
    // and the actual layers are selected based on view mask. In our case it's layer 1.
    VkClearRect clear_rect{};
    clear_rect.rect.extent = {128, 128};
    clear_rect.baseArrayLayer = 0;
    clear_rect.layerCount = 1;

    VkBufferImageCopy region{};
    region.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 1};  // copy to layer 1
    region.imageExtent = {128, 128, 1};

    m_command_buffer.Begin();

    vk::CmdCopyBufferToImage(m_command_buffer, buffer, image, VK_IMAGE_LAYOUT_GENERAL, 1, &region);

    // Even though clear rect references layer 0, the actual layer that is cleared is layer 1 (based on view mask).
    // The copy also writes to layer mask which causes harard.
    //
    // NOTE: Without multiview this test would pass: the clear rect selects layer 0 to clear
    // and this does not interfere with the copy layer (1). In this regard this test works as
    // a regression test: if multiview breaks (or disabled by mistake) we would not get hazard
    m_command_buffer.BeginRenderPass(render_pass, framebuffer, 128, 128);
    m_errorMonitor->SetDesiredError("SYNC-HAZARD-WRITE-AFTER-WRITE");
    vk::CmdClearAttachments(m_command_buffer, 1, &clear_attachment, 1, &clear_rect);
    m_errorMonitor->VerifyFound();
}

TEST_F(NegativeSyncValRenderPass, MultiviewClearAttachmentsDepth) {
    TEST_DESCRIPTION("The same as MultiviewClearAttachments but for depth image");
    SetTargetApiVersion(VK_API_VERSION_1_4);
    AddRequiredFeature(vkt::Feature::multiview);
    RETURN_IF_SKIP(InitSyncVal());

    const VkFormat depth_format = FindSupportedDepthOnlyFormat(Gpu());

    const VkImageCreateInfo image_ci = vkt::Image::ImageCreateInfo2D(
        128, 128, 1, 2, depth_format, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
    vkt::Image image(*m_device, image_ci);
    vkt::ImageView image_view = image.CreateView(VK_IMAGE_VIEW_TYPE_2D_ARRAY, 0, 1, 0, 2, VK_IMAGE_ASPECT_DEPTH_BIT);

    vkt::Buffer buffer(*m_device, 128 * 128 * 4 * 2, VK_BUFFER_USAGE_TRANSFER_SRC_BIT);

    const uint32_t view_mask = 2;
    VkRenderPassMultiviewCreateInfo multiview_ci = vku::InitStructHelper();
    multiview_ci.subpassCount = 1;
    multiview_ci.pViewMasks = &view_mask;

    RenderPassSingleSubpass render_pass(*this);
    render_pass.AddAttachmentDescription(depth_format, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL, VK_ATTACHMENT_LOAD_OP_NONE,
                                         VK_ATTACHMENT_STORE_OP_STORE);
    render_pass.AddDepthStencilAttachment(0, VK_IMAGE_LAYOUT_GENERAL);
    render_pass.CreateRenderPass(&multiview_ci);

    vkt::Framebuffer framebuffer(*m_device, render_pass, 1, &image_view.handle(), 128, 128);

    VkClearAttachment clear_attachment{};
    clear_attachment.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;

    VkClearRect clear_rect{};
    clear_rect.rect.extent = {128, 128};
    clear_rect.baseArrayLayer = 0;
    clear_rect.layerCount = 1;

    VkBufferImageCopy region{};
    region.imageSubresource = {VK_IMAGE_ASPECT_DEPTH_BIT, 0, 1, 1};  // copy to layer 1
    region.imageExtent = {128, 128, 1};

    m_command_buffer.Begin();
    vk::CmdCopyBufferToImage(m_command_buffer, buffer, image, VK_IMAGE_LAYOUT_GENERAL, 1, &region);
    m_command_buffer.BeginRenderPass(render_pass, framebuffer, 128, 128);
    m_errorMonitor->SetDesiredError("SYNC-HAZARD-WRITE-AFTER-WRITE");
    vk::CmdClearAttachments(m_command_buffer, 1, &clear_attachment, 1, &clear_rect);
    m_errorMonitor->VerifyFound();
}
