blob: 6e95ae7618dd8703940352e6926f26042e03c974 [file]
/*
* Copyright (c) 2015-2026 The Khronos Group Inc.
* Copyright (c) 2015-2026 Valve Corporation
* Copyright (c) 2015-2026 LunarG, Inc.
* Copyright (c) 2015-2025 Google, Inc.
* Modifications Copyright (C) 2020-2022 Advanced Micro Devices, Inc. All rights reserved.
* Modifications Copyright (C) 2021 ARM, 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 "../framework/layer_validation_tests.h"
#include "../framework/pipeline_helper.h"
#include "../framework/render_pass_helper.h"
#include "utils/convert_utils.h"
class NegativeSubpass : public VkLayerTest {};
TEST_F(NegativeSubpass, NonGraphicsPipeline) {
TEST_DESCRIPTION("Create a subpass with the compute pipeline bind point");
AddRequiredExtensions(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
AddOptionalExtensions(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME);
RETURN_IF_SKIP(Init());
const bool rp2Supported = IsExtensionsEnabled(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME);
VkSubpassDescription subpasses[] = {
{0, VK_PIPELINE_BIND_POINT_COMPUTE, 0, nullptr, 0, nullptr, nullptr, nullptr, 0, nullptr},
};
auto rpci = vku::InitStruct<VkRenderPassCreateInfo>(nullptr, 0u, 0u, nullptr, 1u, subpasses, 0u, nullptr);
CreateRenderPassTest(rpci, rp2Supported, "VUID-VkSubpassDescription-pipelineBindPoint-04952",
"VUID-VkSubpassDescription2-pipelineBindPoint-04953");
}
TEST_F(NegativeSubpass, InputAttachmentParameters) {
TEST_DESCRIPTION("Create a subpass with parameters in the input attachment ref which are invalid");
// Check for VK_KHR_get_physical_device_properties2
AddRequiredExtensions(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME);
RETURN_IF_SKIP(Init());
VkAttachmentDescription2 attach_desc = vku::InitStructHelper();
attach_desc.format = VK_FORMAT_R32_UINT;
attach_desc.samples = VK_SAMPLE_COUNT_1_BIT;
attach_desc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attach_desc.finalLayout = VK_IMAGE_LAYOUT_GENERAL;
VkAttachmentReference2 reference = vku::InitStructHelper();
reference.attachment = 0;
reference.layout = VK_IMAGE_LAYOUT_GENERAL;
reference.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
VkSubpassDescription2 subpass = vku::InitStructHelper();
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.viewMask = 0;
subpass.inputAttachmentCount = 1;
subpass.pInputAttachments = &reference;
auto rpci2 = vku::InitStruct<VkRenderPassCreateInfo2KHR>(nullptr, 0u, 1u, &attach_desc, 1u, &subpass, 0u, nullptr, 0u, nullptr);
attach_desc.format = VK_FORMAT_R8G8B8A8_UNORM;
reference.aspectMask = 0;
// Test for aspect mask of 0
m_errorMonitor->SetUnexpectedError("VUID-VkRenderPassCreateInfo2-attachment-02525");
m_errorMonitor->SetUnexpectedError("VUID-VkSubpassDescription2-pInputAttachments-02897");
m_errorMonitor->SetDesiredError("VUID-VkSubpassDescription2-attachment-02800");
vkt::RenderPass rp1(*m_device, rpci2);
m_errorMonitor->VerifyFound();
// Test for invalid aspect mask bits
reference.aspectMask = 0x40000000; // invalid VkImageAspectFlagBits value
m_errorMonitor->SetUnexpectedError("VUID-VkRenderPassCreateInfo2-attachment-02525");
m_errorMonitor->SetUnexpectedError("VUID-VkSubpassDescription2-pInputAttachments-02897");
m_errorMonitor->SetDesiredError("VUID-VkSubpassDescription2-attachment-02799");
vkt::RenderPass rp2(*m_device, rpci2);
m_errorMonitor->VerifyFound();
// Test for invalid use of VK_IMAGE_ASPECT_METADATA_BIT
reference.aspectMask = VK_IMAGE_ASPECT_METADATA_BIT;
m_errorMonitor->SetUnexpectedError("VUID-VkRenderPassCreateInfo2-attachment-02525");
m_errorMonitor->SetUnexpectedError("VUID-VkSubpassDescription2-pInputAttachments-02897");
m_errorMonitor->SetDesiredError("VUID-VkSubpassDescription2-attachment-02801");
vkt::RenderPass rp3(*m_device, rpci2);
m_errorMonitor->VerifyFound();
}
TEST_F(NegativeSubpass, SubpassDependencies) {
AddRequiredExtensions(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
AddOptionalExtensions(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME);
AddOptionalExtensions(VK_KHR_MULTIVIEW_EXTENSION_NAME);
RETURN_IF_SKIP(InitFramework());
const bool rp2_supported = IsExtensionsEnabled(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME);
const bool multiview_supported =
IsExtensionsEnabled(VK_KHR_MULTIVIEW_EXTENSION_NAME) || (DeviceValidationVersion() >= VK_API_VERSION_1_1);
VkPhysicalDeviceMultiviewFeatures multiview_features = vku::InitStructHelper();
auto features2 = GetPhysicalDeviceFeatures2(multiview_features);
if (multiview_features.multiview == VK_FALSE) {
GTEST_SKIP() << "multiview feature not supported";
}
// Add a device features struct enabling NO features
features2.features = {};
RETURN_IF_SKIP(InitState(nullptr, &features2));
// Create two dummy subpasses
VkSubpassDescription subpasses[] = {
{0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, nullptr, 0, nullptr, nullptr, nullptr, 0, nullptr},
{0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, nullptr, 0, nullptr, nullptr, nullptr, 0, nullptr},
};
VkSubpassDependency dependency;
auto rpci = vku::InitStruct<VkRenderPassCreateInfo>(nullptr, 0u, 0u, nullptr, 2u, subpasses, 1u, &dependency);
// Non graphics stages in subpass dependency
dependency = {0, 1, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT};
CreateRenderPassTest(rpci, rp2_supported, "VUID-VkRenderPassCreateInfo-pDependencies-00837",
"VUID-VkRenderPassCreateInfo2-pDependencies-03054");
dependency = {0, 1, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, 0};
CreateRenderPassTest(rpci, rp2_supported, "VUID-VkRenderPassCreateInfo-pDependencies-00837",
"VUID-VkRenderPassCreateInfo2-pDependencies-03054");
dependency = {0, 1, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT};
CreateRenderPassTest(rpci, rp2_supported, "VUID-VkRenderPassCreateInfo-pDependencies-00838",
"VUID-VkRenderPassCreateInfo2-pDependencies-03055");
dependency = {0, 1, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0, 0, 0};
CreateRenderPassTest(rpci, rp2_supported, "VUID-VkRenderPassCreateInfo-pDependencies-00838",
"VUID-VkRenderPassCreateInfo2-pDependencies-03055");
dependency = {0, VK_SUBPASS_EXTERNAL, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, 0};
CreateRenderPassTest(rpci, rp2_supported, "VUID-VkRenderPassCreateInfo-pDependencies-00837",
"VUID-VkRenderPassCreateInfo2-pDependencies-03054");
dependency = {VK_SUBPASS_EXTERNAL, 0, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, 0};
CreateRenderPassTest(rpci, rp2_supported, "VUID-VkRenderPassCreateInfo-pDependencies-00838",
"VUID-VkRenderPassCreateInfo2-pDependencies-03055");
dependency = {0, 0, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, 0};
CreateRenderPassTest(rpci, rp2_supported, "VUID-VkRenderPassCreateInfo-pDependencies-00837",
"VUID-VkRenderPassCreateInfo2-pDependencies-03054");
// Geometry shaders not enabled source
dependency = {0, 1, VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, 0};
CreateRenderPassTest(rpci, rp2_supported, "VUID-VkSubpassDependency-srcStageMask-04090",
"VUID-VkSubpassDependency2-srcStageMask-04090");
// Geometry shaders not enabled destination
dependency = {0, 1, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT, 0, 0, 0};
CreateRenderPassTest(rpci, rp2_supported, "VUID-VkSubpassDependency-dstStageMask-04090",
"VUID-VkSubpassDependency2-dstStageMask-04090");
// Tessellation not enabled source
dependency = {0, 1, VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, 0};
CreateRenderPassTest(rpci, rp2_supported, "VUID-VkSubpassDependency-srcStageMask-04091",
"VUID-VkSubpassDependency2-srcStageMask-04091");
// Tessellation not enabled destination
dependency = {0, 1, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT, 0, 0, 0};
CreateRenderPassTest(rpci, rp2_supported, "VUID-VkSubpassDependency-dstStageMask-04091",
"VUID-VkSubpassDependency2-dstStageMask-04091");
// Potential cyclical dependency
dependency = {1, 0, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, 0};
CreateRenderPassTest(rpci, rp2_supported, "VUID-VkSubpassDependency-srcSubpass-00864",
"VUID-VkSubpassDependency2-srcSubpass-03084");
// EXTERNAL to EXTERNAL dependency
dependency = {
VK_SUBPASS_EXTERNAL, VK_SUBPASS_EXTERNAL, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, 0};
CreateRenderPassTest(rpci, rp2_supported, "VUID-VkSubpassDependency-srcSubpass-00865",
"VUID-VkSubpassDependency2-srcSubpass-03085");
// srcStage contains framebuffer space, and dstStage contains non-framebuffer space
dependency = {0,
0,
VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
0,
0,
0};
CreateRenderPassTest(rpci, rp2_supported, "VUID-VkSubpassDependency-srcSubpass-06809",
"VUID-VkSubpassDependency2-srcSubpass-06810");
// framebuffer space stages in self dependency with region bit
dependency = {0, 0, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, 0, 0, 0};
CreateRenderPassTest(rpci, rp2_supported, "VUID-VkSubpassDependency-srcSubpass-02243",
"VUID-VkSubpassDependency2-srcSubpass-02245");
// Same test but make sure the logical invalid order does not trip other VUID since both are framebuffer space stages
dependency = {0, 0, VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, 0, 0, 0};
CreateRenderPassTest(rpci, rp2_supported, "VUID-VkSubpassDependency-srcSubpass-02243",
"VUID-VkSubpassDependency2-srcSubpass-02245");
// Source access mask mismatch with source stage mask
dependency = {0, 1, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_ACCESS_UNIFORM_READ_BIT, 0, 0};
CreateRenderPassTest(rpci, rp2_supported, "VUID-VkSubpassDependency-srcAccessMask-00868",
"VUID-VkSubpassDependency2-srcAccessMask-03088");
// Destination access mask mismatch with destination stage mask
dependency = {
0, 1, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, 0};
CreateRenderPassTest(rpci, rp2_supported, "VUID-VkSubpassDependency-dstAccessMask-00869",
"VUID-VkSubpassDependency2-dstAccessMask-03089");
// srcSubpass larger than subpassCount
dependency = {3, 0, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, 0};
CreateRenderPassTest(rpci, rp2_supported, "VUID-VkRenderPassCreateInfo-pDependencies-06866",
"VUID-VkRenderPassCreateInfo2-srcSubpass-02526");
// dstSubpass larger than subpassCount
dependency = {0, 3, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, 0};
CreateRenderPassTest(rpci, rp2_supported, "VUID-VkRenderPassCreateInfo-pDependencies-06867",
"VUID-VkRenderPassCreateInfo2-dstSubpass-02527");
if (multiview_supported) {
// VIEW_LOCAL_BIT but multiview is not enabled
dependency = {0, 1, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
0, 0, VK_DEPENDENCY_VIEW_LOCAL_BIT};
CreateRenderPassTest(rpci, rp2_supported, nullptr, "VUID-VkRenderPassCreateInfo2-viewMask-03059");
// Enable multiview
uint32_t pViewMasks[2] = {0x3u, 0x3u};
int32_t pViewOffsets[2] = {0, 0};
auto rpmvci = vku::InitStruct<VkRenderPassMultiviewCreateInfo>(nullptr, 2u, pViewMasks, 0u, nullptr, 0u, nullptr);
rpci.pNext = &rpmvci;
// Excessive view offsets
dependency = {0, 1, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
0, 0, VK_DEPENDENCY_VIEW_LOCAL_BIT};
rpmvci.pViewOffsets = pViewOffsets;
rpmvci.dependencyCount = 2;
CreateRenderPassTest(rpci, false, "VUID-VkRenderPassCreateInfo-pNext-01929", nullptr);
rpmvci.dependencyCount = 0;
// View offset with subpass self dependency
dependency = {0, 0, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
0, 0, VK_DEPENDENCY_VIEW_LOCAL_BIT};
rpmvci.pViewOffsets = pViewOffsets;
pViewOffsets[0] = 1;
rpmvci.dependencyCount = 1;
CreateRenderPassTest(rpci, false, "VUID-VkRenderPassCreateInfo-pNext-01930", "VUID-VkSubpassDependency2-viewOffset-02530");
rpmvci.dependencyCount = 0;
// View offset with no view local bit
if (rp2_supported) {
dependency = {0, VK_SUBPASS_EXTERNAL, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, 0};
rpmvci.pViewOffsets = pViewOffsets;
pViewOffsets[0] = 1;
rpmvci.dependencyCount = 1;
CreateRenderPassTest(rpci, rp2_supported, nullptr, "VUID-VkSubpassDependency2-dependencyFlags-03092");
rpmvci.dependencyCount = 0;
}
// EXTERNAL subpass with VIEW_LOCAL_BIT - source subpass
dependency = {VK_SUBPASS_EXTERNAL, 1, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0,
VK_DEPENDENCY_VIEW_LOCAL_BIT};
CreateRenderPassTest(rpci, rp2_supported, "VUID-VkSubpassDependency-dependencyFlags-02520",
"VUID-VkSubpassDependency2-dependencyFlags-03090");
// EXTERNAL subpass with VIEW_LOCAL_BIT - destination subpass
dependency = {0, VK_SUBPASS_EXTERNAL, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0,
0, VK_DEPENDENCY_VIEW_LOCAL_BIT};
CreateRenderPassTest(rpci, rp2_supported, "VUID-VkSubpassDependency-dependencyFlags-02521",
"VUID-VkSubpassDependency2-dependencyFlags-03091");
// Multiple views but no view local bit in self-dependency
dependency = {0, 0, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, 0};
CreateRenderPassTest(rpci, rp2_supported, "VUID-VkSubpassDependency-srcSubpass-00872",
"VUID-VkRenderPassCreateInfo2-pDependencies-03060");
}
}
TEST_F(NegativeSubpass, NextSubpassExcessive) {
TEST_DESCRIPTION("Test that an error is produced when CmdNextSubpass is called too many times in a renderpass instance");
AddRequiredExtensions(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
AddOptionalExtensions(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME);
RETURN_IF_SKIP(Init());
const bool rp2Supported = IsExtensionsEnabled(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME);
InitRenderTarget();
m_command_buffer.Begin();
m_command_buffer.BeginRenderPass(m_renderPassBeginInfo);
m_errorMonitor->SetDesiredError("VUID-vkCmdNextSubpass-None-00909");
m_command_buffer.NextSubpass();
m_errorMonitor->VerifyFound();
if (rp2Supported) {
auto subpassBeginInfo = vku::InitStruct<VkSubpassBeginInfo>(nullptr, VK_SUBPASS_CONTENTS_INLINE);
VkSubpassEndInfo subpassEndInfo = vku::InitStructHelper();
m_errorMonitor->SetDesiredError("VUID-vkCmdNextSubpass2-None-03102");
vk::CmdNextSubpass2KHR(m_command_buffer, &subpassBeginInfo, &subpassEndInfo);
m_errorMonitor->VerifyFound();
}
m_command_buffer.EndRenderPass();
m_command_buffer.End();
}
TEST_F(NegativeSubpass, RenderPassEndBeforeFinalSubpass) {
TEST_DESCRIPTION("Test that an error is produced when CmdEndRenderPass is called before the final subpass has been reached");
AddRequiredExtensions(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
AddOptionalExtensions(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME);
RETURN_IF_SKIP(Init());
const bool rp2Supported = IsExtensionsEnabled(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME);
VkSubpassDescription sd[2] = {{0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, nullptr, 0, nullptr, nullptr, nullptr, 0, nullptr},
{0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, nullptr, 0, nullptr, nullptr, nullptr, 0, nullptr}};
auto rcpi = vku::InitStruct<VkRenderPassCreateInfo>(nullptr, 0u, 0u, nullptr, 2u, sd, 0u, nullptr);
vkt::RenderPass rp(*m_device, rcpi);
vkt::Framebuffer fb(*m_device, rp, 0u, nullptr, 16, 16);
m_command_buffer.Begin();
m_command_buffer.BeginRenderPass(rp, fb, 16, 16);
m_errorMonitor->SetDesiredError("VUID-vkCmdEndRenderPass-None-00910");
m_command_buffer.EndRenderPass();
m_errorMonitor->VerifyFound();
if (rp2Supported) {
VkSubpassEndInfo subpassEndInfo = vku::InitStructHelper();
m_command_buffer.Reset();
m_command_buffer.Begin();
m_command_buffer.BeginRenderPass(rp, fb, 16, 16);
m_errorMonitor->SetDesiredError("VUID-vkCmdEndRenderPass2-None-03103");
vk::CmdEndRenderPass2KHR(m_command_buffer, &subpassEndInfo);
m_errorMonitor->VerifyFound();
}
}
TEST_F(NegativeSubpass, SubpassIndices) {
TEST_DESCRIPTION("Create render pass with valid stages");
AddRequiredExtensions(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
AddOptionalExtensions(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME);
RETURN_IF_SKIP(Init());
const bool rp2_supported = IsExtensionsEnabled(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME);
VkSubpassDescription sci[2] = {};
sci[0].pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
sci[1].pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
const VkPipelineStageFlags kGraphicsStages =
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT | VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT |
VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
VkSubpassDependency dependency = {};
// Use only 2 subpasses, so these values should trigger validation errors
dependency.srcSubpass = 4;
dependency.dstSubpass = 4;
dependency.srcStageMask = kGraphicsStages;
dependency.dstStageMask = kGraphicsStages;
VkRenderPassCreateInfo rpci = vku::InitStructHelper();
rpci.subpassCount = 2;
rpci.pSubpasses = sci;
rpci.dependencyCount = 1;
rpci.pDependencies = &dependency;
VkRenderPass render_pass = VK_NULL_HANDLE;
m_errorMonitor->SetDesiredError("VUID-VkRenderPassCreateInfo-pDependencies-06866");
m_errorMonitor->SetDesiredError("VUID-VkRenderPassCreateInfo-pDependencies-06867");
vk::CreateRenderPass(device(), &rpci, nullptr, &render_pass);
m_errorMonitor->VerifyFound();
if (rp2_supported) {
auto create_info2 = ConvertVkRenderPassCreateInfoToV2KHR(rpci);
m_errorMonitor->SetDesiredError("VUID-VkRenderPassCreateInfo2-srcSubpass-02526");
m_errorMonitor->SetDesiredError("VUID-VkRenderPassCreateInfo2-dstSubpass-02527");
vk::CreateRenderPass2KHR(device(), create_info2.ptr(), nullptr, &render_pass);
m_errorMonitor->VerifyFound();
}
}
TEST_F(NegativeSubpass, DrawWithPipelineIncompatibleWithSubpass) {
TEST_DESCRIPTION("Use a pipeline for the wrong subpass in a render pass instance");
RETURN_IF_SKIP(Init());
// A renderpass with two subpasses, both writing the same attachment.
VkAttachmentDescription attach[] = {
{0, VK_FORMAT_R8G8B8A8_UNORM, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE,
VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL},
};
VkAttachmentReference ref = {0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
VkSubpassDescription subpasses[] = {
{0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, nullptr, 1, &ref, nullptr, nullptr, 0, nullptr},
{0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, nullptr, 1, &ref, nullptr, nullptr, 0, nullptr},
};
VkSubpassDependency dep = {0,
1,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_DEPENDENCY_BY_REGION_BIT};
auto rpci = vku::InitStruct<VkRenderPassCreateInfo>(nullptr, 0u, 1u, attach, 2u, subpasses, 1u, &dep);
vkt::RenderPass rp(*m_device, rpci);
vkt::Image image(*m_device, 32, 32, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
vkt::ImageView imageView = image.CreateView();
vkt::Framebuffer fb(*m_device, rp, 1u, &imageView.handle());
CreatePipelineHelper pipe(*this);
pipe.gp_ci_.renderPass = rp;
pipe.CreateGraphicsPipeline();
m_command_buffer.Begin();
// subtest 1: bind in the wrong subpass
m_command_buffer.BeginRenderPass(rp, fb, 32, 32);
m_command_buffer.NextSubpass();
m_errorMonitor->SetDesiredError("built for subpass 0 but used in subpass 1");
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe);
vk::CmdDraw(m_command_buffer, 3, 1, 0, 0);
m_errorMonitor->VerifyFound();
m_command_buffer.EndRenderPass();
m_command_buffer.FullMemoryBarrier();
// subtest 2: bind in correct subpass, then transition to next subpass
m_command_buffer.BeginRenderPass(rp, fb, 32, 32);
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe);
m_command_buffer.NextSubpass();
m_errorMonitor->SetDesiredError("built for subpass 0 but used in subpass 1");
vk::CmdDraw(m_command_buffer, 3, 1, 0, 0);
m_errorMonitor->VerifyFound();
m_command_buffer.EndRenderPass();
m_command_buffer.End();
}
TEST_F(NegativeSubpass, ImageBarrierSubpassConflict) {
TEST_DESCRIPTION("Check case where subpass index references different image from image barrier");
RETURN_IF_SKIP(Init());
// Create RP/FB combo where subpass has incorrect index attachment, this is 2nd half of "VUID-vkCmdPipelineBarrier-image-02635"
VkAttachmentDescription attach[] = {
{0, VK_FORMAT_R8G8B8A8_UNORM, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE,
VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL},
{0, VK_FORMAT_R8G8B8A8_UNORM, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE,
VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL},
};
// ref attachment points to wrong attachment index compared to img_barrier below
VkAttachmentReference ref = {1, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
VkSubpassDescription subpasses[] = {
{0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, nullptr, 1, &ref, nullptr, nullptr, 0, nullptr},
};
VkSubpassDependency dep = {0,
0,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_DEPENDENCY_BY_REGION_BIT};
auto rpci = vku::InitStruct<VkRenderPassCreateInfo>(nullptr, 0u, 2u, attach, 1u, subpasses, 1u, &dep);
vkt::RenderPass rp(*m_device, rpci);
vkt::Image image(*m_device, 32, 32, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
vkt::ImageView imageView = image.CreateView();
vkt::Image image2(*m_device, 32, 32, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
vkt::ImageView imageView2 = image2.CreateView();
// re-use imageView from start of test
VkImageView iv_array[2] = {imageView, imageView2};
vkt::Framebuffer fb(*m_device, rp, 2u, iv_array);
VkImageMemoryBarrier img_barrier = vku::InitStructHelper();
img_barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
img_barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
img_barrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
img_barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
img_barrier.image = image; /* barrier references image from attachment index 0 */
img_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
img_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
img_barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
m_command_buffer.Begin();
m_command_buffer.BeginRenderPass(rp, fb, 32, 32);
m_errorMonitor->SetDesiredError("VUID-vkCmdPipelineBarrier-image-04073");
vk::CmdPipelineBarrier(m_command_buffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_DEPENDENCY_BY_REGION_BIT, 0, nullptr, 0, nullptr, 1,
&img_barrier);
m_errorMonitor->VerifyFound();
}
TEST_F(NegativeSubpass, SubpassInputNotBoundDescriptorSet) {
TEST_DESCRIPTION("Validate subpass input isn't bound to fragment shader or descriptor set");
RETURN_IF_SKIP(Init());
InitRenderTarget();
VkImageUsageFlags usage_input =
VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
VkFormat format = VK_FORMAT_R8G8B8A8_UNORM;
auto image_ci = vkt::Image::ImageCreateInfo2D(64, 64, 1, 1, format, usage_input);
vkt::Image image_input(*m_device, image_ci);
vkt::ImageView view_input = image_input.CreateView();
const VkAttachmentDescription inputAttachment = {
0u,
format,
VK_SAMPLE_COUNT_1_BIT,
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
VK_ATTACHMENT_STORE_OP_DONT_CARE,
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
VK_ATTACHMENT_STORE_OP_DONT_CARE,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
};
std::vector<VkAttachmentDescription> attachmentDescs;
attachmentDescs.push_back(inputAttachment);
VkAttachmentReference inputRef = {
0,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
};
std::vector<VkAttachmentReference> inputAttachments;
inputAttachments.push_back(inputRef);
const VkSubpassDescription subpass = {
0u, VK_PIPELINE_BIND_POINT_GRAPHICS, size32(inputAttachments), inputAttachments.data(), 0, nullptr, 0u, nullptr, 0u,
nullptr,
};
const std::vector<VkSubpassDescription> subpasses(1u, subpass);
const auto rpci = vku::InitStruct<VkRenderPassCreateInfo>(nullptr, 0u, size32(attachmentDescs), attachmentDescs.data(),
size32(subpasses), subpasses.data(), 0u, nullptr);
vkt::RenderPass rp(*m_device, rpci);
vkt::Framebuffer fb(*m_device, rp, 1, &view_input.handle(), 64, 64);
vkt::Sampler sampler(*m_device, SafeSaneSamplerCreateInfo());
VkShaderObj vs(*m_device, kVertexMinimalGlsl, VK_SHADER_STAGE_VERTEX_BIT);
{
// input index is wrong, it doesn't exist in supbass input attachments and the set and binding is undefined
// It causes desired failures.
const char* fsSource_fail = R"glsl(
#version 450
layout(input_attachment_index=1, set=0, binding=1) uniform subpassInput x;
void main() {
vec4 color = subpassLoad(x);
}
)glsl";
VkShaderObj fs_fail(*m_device, fsSource_fail, VK_SHADER_STAGE_FRAGMENT_BIT);
CreatePipelineHelper g_pipe(*this);
g_pipe.shader_stages_ = {vs.GetStageCreateInfo(), fs_fail.GetStageCreateInfo()};
g_pipe.dsl_bindings_[0] = {0, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr};
g_pipe.gp_ci_.renderPass = rp;
m_errorMonitor->SetDesiredError("VUID-VkGraphicsPipelineCreateInfo-renderPass-06038");
m_errorMonitor->SetDesiredError("VUID-VkGraphicsPipelineCreateInfo-layout-07988");
g_pipe.CreateGraphicsPipeline();
m_errorMonitor->VerifyFound();
}
{ // Binds input attachment
const char* fsSource = R"glsl(
#version 450
layout(input_attachment_index=0, set=0, binding=0) uniform subpassInput x;
void main() {
vec4 color = subpassLoad(x);
}
)glsl";
VkShaderObj fs(*m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT);
CreatePipelineHelper g_pipe(*this);
g_pipe.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()};
g_pipe.dsl_bindings_[0] = {0, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr};
g_pipe.gp_ci_.renderPass = rp;
g_pipe.CreateGraphicsPipeline();
g_pipe.descriptor_set_->WriteDescriptorImageInfo(0, view_input, sampler, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT);
g_pipe.descriptor_set_->UpdateDescriptorSets();
m_command_buffer.Begin();
image_input.SetLayout(m_command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
m_renderPassBeginInfo.renderArea = {{0, 0}, {64, 64}};
m_renderPassBeginInfo.renderPass = rp;
m_renderPassBeginInfo.framebuffer = fb;
m_command_buffer.BeginRenderPass(m_renderPassBeginInfo);
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, g_pipe);
vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, g_pipe.pipeline_layout_, 0, 1,
&g_pipe.descriptor_set_->set_, 0, nullptr);
vk::CmdDraw(m_command_buffer, 1, 0, 0, 0);
m_command_buffer.EndRenderPass();
m_command_buffer.End();
}
}
TEST_F(NegativeSubpass, SubpassDescriptionViewMask) {
TEST_DESCRIPTION("Test creating render with invalid view mask bit");
SetTargetApiVersion(VK_API_VERSION_1_2);
AddRequiredExtensions(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::multiview);
RETURN_IF_SKIP(Init());
VkPhysicalDeviceMultiviewProperties render_pass_multiview_props = vku::InitStructHelper();
GetPhysicalDeviceProperties2(render_pass_multiview_props);
if (render_pass_multiview_props.maxMultiviewViewCount >= 32) {
GTEST_SKIP() << "maxMultiviewViewCount too high";
}
VkAttachmentDescription2 attach_desc = vku::InitStructHelper();
attach_desc.format = VK_FORMAT_R8G8B8A8_UNORM;
attach_desc.samples = VK_SAMPLE_COUNT_1_BIT;
attach_desc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attach_desc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attach_desc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attach_desc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attach_desc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attach_desc.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkSubpassDescription2 subpass =
vku::InitStructHelper(); //{0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, nullptr, 0, nullptr, nullptr,
// nullptr, 0, nullptr};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.viewMask = 1 << render_pass_multiview_props.maxMultiviewViewCount;
VkRenderPassCreateInfo2 render_pass_ci = vku::InitStructHelper();
render_pass_ci.attachmentCount = 1;
render_pass_ci.pAttachments = &attach_desc;
render_pass_ci.subpassCount = 1;
render_pass_ci.pSubpasses = &subpass;
VkRenderPass render_pass;
m_errorMonitor->SetDesiredError("VUID-VkSubpassDescription2-viewMask-06706");
vk::CreateRenderPass2(device(), &render_pass_ci, nullptr, &render_pass);
m_errorMonitor->VerifyFound();
}
TEST_F(NegativeSubpass, PipelineSubpassIndex) {
TEST_DESCRIPTION("Test using pipeline with incompatible subpass index for current renderpass subpass");
AddRequiredExtensions(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
RETURN_IF_SKIP(Init());
VkAttachmentDescription attach_desc = {};
attach_desc.format = VK_FORMAT_R8G8B8A8_UNORM;
attach_desc.samples = VK_SAMPLE_COUNT_1_BIT;
attach_desc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attach_desc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attach_desc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attach_desc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attach_desc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attach_desc.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkAttachmentReference attach_ref = {};
attach_ref.attachment = 0;
attach_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkSubpassDescription sci[2] = {};
sci[0].pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
sci[1].pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
sci[1].colorAttachmentCount = 1;
sci[1].pColorAttachments = &attach_ref;
VkSubpassDependency dependency = {};
dependency.srcSubpass = 0;
dependency.dstSubpass = 1;
dependency.srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
dependency.dstStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
VkRenderPassCreateInfo render_pass_ci = vku::InitStructHelper();
render_pass_ci.subpassCount = 2;
render_pass_ci.pSubpasses = sci;
render_pass_ci.dependencyCount = 1;
render_pass_ci.pDependencies = &dependency;
render_pass_ci.attachmentCount = 1;
render_pass_ci.pAttachments = &attach_desc;
vkt::RenderPass render_pass(*m_device, render_pass_ci);
vkt::Image image(*m_device, 32, 32, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
vkt::ImageView imageView = image.CreateView();
vkt::Framebuffer framebuffer(*m_device, render_pass, 1, &imageView.handle());
CreatePipelineHelper pipe1(*this);
pipe1.gp_ci_.renderPass = render_pass;
pipe1.gp_ci_.subpass = 0;
pipe1.CreateGraphicsPipeline();
CreatePipelineHelper pipe2(*this);
pipe2.gp_ci_.renderPass = render_pass;
pipe2.gp_ci_.subpass = 1;
pipe2.CreateGraphicsPipeline();
VkClearValue clear_value = {};
clear_value.color = {{0, 0, 0, 0}};
m_command_buffer.Begin();
m_command_buffer.BeginRenderPass(render_pass, framebuffer, 32, 32, 1, &clear_value);
m_errorMonitor->SetDesiredError("VUID-vkCmdDraw-subpass-02685");
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe2);
vk::CmdDraw(m_command_buffer, 3, 1, 0, 0);
m_errorMonitor->VerifyFound();
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe1);
vk::CmdDraw(m_command_buffer, 3, 1, 0, 0);
m_command_buffer.NextSubpass();
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe2);
vk::CmdDraw(m_command_buffer, 3, 1, 0, 0);
m_errorMonitor->SetDesiredError("VUID-vkCmdDraw-subpass-02685");
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe1);
vk::CmdDraw(m_command_buffer, 3, 1, 0, 0);
m_errorMonitor->VerifyFound();
m_command_buffer.EndRenderPass();
m_command_buffer.End();
}
TEST_F(NegativeSubpass, SubpassDependencyMasksSync2) {
// Testing from the spec:
// If a VkMemoryBarrier2 is included in the pNext chain,
// srcStageMask, dstStageMask, srcAccessMask, and dstAccessMask parameters are ignored.
// The synchronization and access scopes instead are defined by the parameters of VkMemoryBarrier2.
SetTargetApiVersion(VK_API_VERSION_1_2); // VK_KHR_create_renderpass2
AddRequiredExtensions(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::synchronization2);
RETURN_IF_SKIP(Init());
InitRenderTarget();
VkAttachmentReference2 attach_ref = vku::InitStructHelper();
attach_ref.attachment = 0;
attach_ref.layout = VK_IMAGE_LAYOUT_GENERAL;
VkSubpassDescription2 subpass = vku::InitStructHelper();
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &attach_ref;
subpass.viewMask = 0;
VkAttachmentDescription2 attach_desc = vku::InitStructHelper();
attach_desc.format = VK_FORMAT_R8G8B8A8_UNORM;
attach_desc.samples = VK_SAMPLE_COUNT_1_BIT;
attach_desc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attach_desc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attach_desc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attach_desc.finalLayout = VK_IMAGE_LAYOUT_GENERAL;
VkMemoryBarrier2 mem_barrier = vku::InitStructHelper();
mem_barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
mem_barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
mem_barrier.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
mem_barrier.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
VkSubpassDependency2 dependency = vku::InitStructHelper();
dependency.srcSubpass = 0;
dependency.dstSubpass = 0;
dependency.srcStageMask = 0X8000000; // not real value, VK_PIPELINE_STAGE_VIDEO_ENCODE_BIT_KHR doesn't exist
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependency.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
dependency.viewOffset = 0;
VkRenderPassCreateInfo2 rpci = vku::InitStructHelper();
rpci.subpassCount = 1;
rpci.pSubpasses = &subpass;
rpci.attachmentCount = 1;
rpci.pAttachments = &attach_desc;
rpci.dependencyCount = 1;
rpci.pDependencies = &dependency;
{
m_errorMonitor->SetDesiredError("VUID-VkSubpassDependency2-srcStageMask-parameter");
vkt::RenderPass render_pass(*m_device, rpci);
m_errorMonitor->VerifyFound();
}
dependency.pNext = &mem_barrier; // srcStageMask should be ignored now
{
vkt::RenderPass render_pass(*m_device, rpci);
}
mem_barrier.srcStageMask = 0x8000000000000000ULL; // not real value
{
m_errorMonitor->SetDesiredError("VUID-VkMemoryBarrier2-srcStageMask-parameter");
vkt::RenderPass render_pass(*m_device, rpci);
m_errorMonitor->VerifyFound();
}
}
TEST_F(NegativeSubpass, InputAttachmentReferences) {
TEST_DESCRIPTION("Create a subpass with the meta data aspect mask set for an input attachment");
AddRequiredExtensions(VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_MAINTENANCE_2_EXTENSION_NAME);
RETURN_IF_SKIP(Init());
VkAttachmentDescription attach = {0,
VK_FORMAT_R8G8B8A8_UNORM,
VK_SAMPLE_COUNT_1_BIT,
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
VK_ATTACHMENT_STORE_OP_DONT_CARE,
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
VK_ATTACHMENT_STORE_OP_DONT_CARE,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL};
VkAttachmentReference ref = {0, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL};
VkSubpassDescription subpass = {0, VK_PIPELINE_BIND_POINT_GRAPHICS, 1, &ref, 0, nullptr, nullptr, nullptr, 0, nullptr};
VkInputAttachmentAspectReference iaar = {0, 0, VK_IMAGE_ASPECT_METADATA_BIT};
auto rpiaaci = vku::InitStruct<VkRenderPassInputAttachmentAspectCreateInfo>(nullptr, 1u, &iaar);
auto rpci = vku::InitStruct<VkRenderPassCreateInfo>(&rpiaaci, 0u, 1u, &attach, 1u, &subpass, 0u, nullptr);
// Invalid aspect masks
// Cannot/should not avoid getting the unxpected ones too
iaar.aspectMask = VK_IMAGE_ASPECT_METADATA_BIT;
m_errorMonitor->SetUnexpectedError("VUID-VkRenderPassCreateInfo-pNext-01963");
m_errorMonitor->SetUnexpectedError("VUID-VkRenderPassCreateInfo2-attachment-02525");
CreateRenderPassTest(rpci, false, "VUID-VkInputAttachmentAspectReference-aspectMask-01964", nullptr);
iaar.aspectMask = VK_IMAGE_ASPECT_MEMORY_PLANE_1_BIT_EXT;
m_errorMonitor->SetUnexpectedError("VUID-VkRenderPassCreateInfo-pNext-01963");
m_errorMonitor->SetUnexpectedError("VUID-VkRenderPassCreateInfo2-attachment-02525");
CreateRenderPassTest(rpci, false, "VUID-VkInputAttachmentAspectReference-aspectMask-02250", nullptr);
// Aspect not present
iaar.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
CreateRenderPassTest(rpci, false, "VUID-VkRenderPassCreateInfo-pNext-01963", "VUID-VkRenderPassCreateInfo2-attachment-02525");
// Invalid subpass index
iaar.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
iaar.subpass = 1;
CreateRenderPassTest(rpci, false, "VUID-VkRenderPassCreateInfo-pNext-01926", nullptr);
iaar.subpass = 0;
// Invalid input attachment index
iaar.inputAttachmentIndex = 1;
CreateRenderPassTest(rpci, false, "VUID-VkRenderPassCreateInfo-pNext-01927", nullptr);
}
TEST_F(NegativeSubpass, InputAttachmentMissing) {
TEST_DESCRIPTION(
"Test that an error is produced for a shader consuming an input attachment which is not included in the subpass "
"description");
RETURN_IF_SKIP(Init());
InitRenderTarget();
const char* fsSource = R"glsl(
#version 450
layout(input_attachment_index=0, set=0, binding=0) uniform subpassInput x;
layout(location=0) out vec4 color;
void main() {
color = subpassLoad(x);
}
)glsl";
VkShaderObj fs(*m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT);
const auto set_info = [&](CreatePipelineHelper& helper) {
helper.shader_stages_ = {helper.vs_->GetStageCreateInfo(), fs.GetStageCreateInfo()};
helper.dsl_bindings_[0] = {0, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr};
};
CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit, "VUID-VkGraphicsPipelineCreateInfo-renderPass-06038");
}
TEST_F(NegativeSubpass, InputAttachmentMissingArray) {
TEST_DESCRIPTION(
"Test that an error is produced for a shader consuming an input attachment which is not included in the subpass "
"description -- array case");
RETURN_IF_SKIP(Init());
InitRenderTarget();
const char* fsSource = R"glsl(
#version 450
layout(input_attachment_index=0, set=0, binding=0) uniform subpassInput xs[1];
layout(location=0) out vec4 color;
void main() {
color = subpassLoad(xs[0]);
}
)glsl";
VkShaderObj fs(*m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT);
const auto set_info = [&](CreatePipelineHelper& helper) {
helper.shader_stages_ = {helper.vs_->GetStageCreateInfo(), fs.GetStageCreateInfo()};
helper.dsl_bindings_[0] = {0, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 2, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr};
};
CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit, "VUID-VkGraphicsPipelineCreateInfo-renderPass-06038");
}
TEST_F(NegativeSubpass, InputAttachmentMissingArray2) {
RETURN_IF_SKIP(Init());
InitRenderTarget();
RenderPassSingleSubpass rp(*this);
rp.AddAttachmentDescription(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
rp.AddAttachmentDescription(VK_FORMAT_R8G8B8A8_UNORM);
rp.AddColorAttachment(0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
rp.AddInputAttachment(1, VK_IMAGE_LAYOUT_GENERAL);
rp.CreateRenderPass();
const char* fsSource = R"glsl(
#version 450
layout (constant_id = 0) const int index = 2;
layout(input_attachment_index=0, set=0, binding=0) uniform subpassInput xs[2];
layout(location=0) out vec4 color;
void main() {
color = subpassLoad(xs[1]);
}
)glsl";
uint32_t data = 4; // over VkDescriptorSetLayoutBinding::descriptorCount
VkSpecializationMapEntry entry = {0, 0, sizeof(uint32_t)};
VkSpecializationInfo specialization_info = {1, &entry, sizeof(uint32_t), &data};
VkShaderObj fs(*m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT, SPV_ENV_VULKAN_1_0, SPV_SOURCE_GLSL, &specialization_info);
CreatePipelineHelper pipe(*this);
pipe.gp_ci_.renderPass = rp;
pipe.shader_stages_ = {pipe.vs_->GetStageCreateInfo(), fs.GetStageCreateInfo()};
pipe.dsl_bindings_[0] = {0, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 2, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr};
m_errorMonitor->SetDesiredError("VUID-VkGraphicsPipelineCreateInfo-renderPass-06038");
pipe.CreateGraphicsPipeline();
m_errorMonitor->VerifyFound();
}
TEST_F(NegativeSubpass, InputAttachmentMissingSpecConstant) {
SetTargetApiVersion(VK_API_VERSION_1_2);
AddRequiredFeature(vkt::Feature::shaderInputAttachmentArrayDynamicIndexing);
RETURN_IF_SKIP(Init());
InitRenderTarget();
RenderPassSingleSubpass rp(*this);
rp.AddAttachmentDescription(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
rp.AddAttachmentDescription(VK_FORMAT_R8G8B8A8_UNORM);
rp.AddColorAttachment(0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
rp.AddInputAttachment(1, VK_IMAGE_LAYOUT_GENERAL);
rp.CreateRenderPass();
const char* fsSource = R"glsl(
#version 450
layout (constant_id = 0) const int index = 1;
layout(input_attachment_index=0, set=0, binding=0) uniform subpassInput xs[2];
layout(location=0) out vec4 color;
void main() {
color = subpassLoad(xs[index]);
}
)glsl";
uint32_t data = 4; // over VkDescriptorSetLayoutBinding::descriptorCount
VkSpecializationMapEntry entry = {0, 0, sizeof(uint32_t)};
VkSpecializationInfo specialization_info = {1, &entry, sizeof(uint32_t), &data};
VkShaderObj fs(*m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT, SPV_ENV_VULKAN_1_0, SPV_SOURCE_GLSL, &specialization_info);
CreatePipelineHelper pipe(*this);
pipe.gp_ci_.renderPass = rp;
pipe.shader_stages_ = {pipe.vs_->GetStageCreateInfo(), fs.GetStageCreateInfo()};
pipe.dsl_bindings_[0] = {0, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 2, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr};
m_errorMonitor->SetDesiredError("VUID-VkGraphicsPipelineCreateInfo-renderPass-06038");
pipe.CreateGraphicsPipeline();
m_errorMonitor->VerifyFound();
}
TEST_F(NegativeSubpass, InputAttachmentMissingSpecConstant2) {
RETURN_IF_SKIP(Init());
InitRenderTarget();
RenderPassSingleSubpass rp(*this);
rp.AddAttachmentDescription(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
rp.AddAttachmentDescription(VK_FORMAT_R8G8B8A8_UNORM);
rp.AddColorAttachment(0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
rp.AddInputAttachment(1, VK_IMAGE_LAYOUT_GENERAL);
rp.CreateRenderPass();
const char* fsSource = R"glsl(
#version 450
layout (constant_id = 0) const int index = 4;
layout(input_attachment_index=0, set=0, binding=0) uniform subpassInput xs[index];
layout(location=0) out vec4 color;
void main() {
color = subpassLoad(xs[0]);
}
)glsl";
uint32_t data = 4; // over VkDescriptorSetLayoutBinding::descriptorCount
VkSpecializationMapEntry entry = {0, 0, sizeof(uint32_t)};
VkSpecializationInfo specialization_info = {1, &entry, sizeof(uint32_t), &data};
VkShaderObj fs(*m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT, SPV_ENV_VULKAN_1_0, SPV_SOURCE_GLSL, &specialization_info);
CreatePipelineHelper pipe(*this);
pipe.gp_ci_.renderPass = rp;
pipe.shader_stages_ = {pipe.vs_->GetStageCreateInfo(), fs.GetStageCreateInfo()};
pipe.dsl_bindings_[0] = {0, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 2, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr};
m_errorMonitor->SetDesiredError("VUID-VkGraphicsPipelineCreateInfo-layout-07991");
pipe.CreateGraphicsPipeline();
m_errorMonitor->VerifyFound();
}
TEST_F(NegativeSubpass, InputAttachmentSharingVariable) {
TEST_DESCRIPTION("Make sure if 2 loads use same variable, both are tracked");
RETURN_IF_SKIP(Init());
const VkAttachmentDescription inputAttachmentDescription = {0,
m_render_target_fmt,
VK_SAMPLE_COUNT_1_BIT,
VK_ATTACHMENT_LOAD_OP_LOAD,
VK_ATTACHMENT_STORE_OP_STORE,
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
VK_ATTACHMENT_STORE_OP_DONT_CARE,
VK_IMAGE_LAYOUT_GENERAL,
VK_IMAGE_LAYOUT_GENERAL};
// index 0 is unused
// index 1 is is valid (for both color and input)
const VkAttachmentReference inputAttachmentReferences[2] = {{VK_ATTACHMENT_UNUSED, VK_IMAGE_LAYOUT_GENERAL},
{0, VK_IMAGE_LAYOUT_GENERAL}};
const VkSubpassDescription subpassDescription = {(VkSubpassDescriptionFlags)0,
VK_PIPELINE_BIND_POINT_GRAPHICS,
2,
inputAttachmentReferences,
1,
&inputAttachmentReferences[1],
nullptr,
nullptr,
0,
nullptr};
VkRenderPassCreateInfo renderPassInfo = vku::InitStructHelper();
renderPassInfo.attachmentCount = 1;
renderPassInfo.pAttachments = &inputAttachmentDescription;
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpassDescription;
vkt::RenderPass renderPass(*m_device, renderPassInfo);
// There are 2 OpLoad/OpAccessChain that point the same OpVariable
// Make sure we are not just taking the first load and checking all loads on a variable
const char* fs_source = R"glsl(
#version 460
layout(input_attachment_index=0, set=0, binding=0) uniform subpassInput xs[2];
layout(location=0) out vec4 color;
void main() {
color = subpassLoad(xs[1]); // valid
color = subpassLoad(xs[0]); // invalid
}
)glsl";
VkShaderObj fs(*m_device, fs_source, VK_SHADER_STAGE_FRAGMENT_BIT, SPV_ENV_VULKAN_1_0, SPV_SOURCE_GLSL);
const auto set_info = [&](CreatePipelineHelper& helper) {
helper.shader_stages_ = {helper.vs_->GetStageCreateInfo(), fs.GetStageCreateInfo()};
helper.dsl_bindings_[0] = {0, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 2, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr};
helper.gp_ci_.renderPass = renderPass;
};
CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit, "VUID-VkGraphicsPipelineCreateInfo-renderPass-06038");
}
TEST_F(NegativeSubpass, SubpassInputWithoutFormat) {
TEST_DESCRIPTION("Non-InputAttachment shader input with unknown image format");
SetTargetApiVersion(VK_API_VERSION_1_2);
RETURN_IF_SKIP(Init());
InitRenderTarget();
if (DeviceExtensionSupported(Gpu(), nullptr, VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME)) {
GTEST_SKIP() << "VK_KHR_format_feature_flags2 is supported";
}
const std::string fs_source = R"(
OpCapability Shader
OpCapability StorageImageReadWithoutFormat
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %color %img
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 460
OpName %main "main"
OpName %color "color"
OpName %img "img"
OpDecorate %color Location 0
OpDecorate %img DescriptorSet 0
OpDecorate %img Binding 0
OpDecorate %img NonWritable
OpDecorate %img NonReadable
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%color = OpVariable %_ptr_Output_v4float Output
;
; Image has unknown format, but dimension != SubpassData and
; shaderStorageImageReadWithoutFormat == VK_FALSE, which is invalid
;
%10 = OpTypeImage %float 2D 0 0 0 2 Unknown
%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10
%img = OpVariable %_ptr_UniformConstant_10 UniformConstant
%int = OpTypeInt 32 1
%v2int = OpTypeVector %int 2
%int_0 = OpConstant %int 0
%17 = OpConstantComposite %v2int %int_0 %int_0
%main = OpFunction %void None %3
%5 = OpLabel
%13 = OpLoad %10 %img
%18 = OpImageRead %v4float %13 %17
OpStore %color %18
OpReturn
OpFunctionEnd
)";
m_errorMonitor->SetDesiredError("VUID-VkShaderModuleCreateInfo-pCode-08740");
VkShaderObj fs(*m_device, fs_source.c_str(), VK_SHADER_STAGE_FRAGMENT_BIT, SPV_ENV_VULKAN_1_2, SPV_SOURCE_ASM);
m_errorMonitor->VerifyFound();
}
TEST_F(NegativeSubpass, NextSubpassNoRenderPass) {
TEST_DESCRIPTION("call next subpass outside a renderpass");
RETURN_IF_SKIP(Init());
InitRenderTarget();
m_command_buffer.Begin();
m_errorMonitor->SetDesiredError("VUID-vkCmdNextSubpass-renderpass");
m_command_buffer.NextSubpass();
m_errorMonitor->VerifyFound();
m_command_buffer.BeginRenderPass(m_renderPassBeginInfo);
m_command_buffer.EndRenderPass();
m_errorMonitor->SetDesiredError("VUID-vkCmdNextSubpass-renderpass");
m_command_buffer.NextSubpass();
m_errorMonitor->VerifyFound();
m_command_buffer.End();
}
TEST_F(NegativeSubpass, FramebufferNoAttachmentsSampleCounts) {
TEST_DESCRIPTION("Create no attachment subpass that goes against framebufferNoAttachmentsSampleCounts");
RETURN_IF_SKIP(Init());
InitRenderTarget();
if ((m_device->Physical().limits_.framebufferNoAttachmentsSampleCounts & VK_SAMPLE_COUNT_8_BIT) != 0) {
GTEST_SKIP() << "Need framebufferNoAttachmentsSampleCounts with no support";
}
RenderPassSingleSubpass rp(*this);
rp.AddAttachmentDescription(VK_FORMAT_R8G8B8A8_UNORM);
rp.CreateRenderPass();
VkPipelineMultisampleStateCreateInfo ms_state = vku::InitStructHelper();
ms_state.rasterizationSamples = VK_SAMPLE_COUNT_8_BIT;
ms_state.sampleShadingEnable = VK_FALSE;
ms_state.minSampleShading = 0.0f;
ms_state.pSampleMask = nullptr;
ms_state.alphaToCoverageEnable = VK_FALSE;
ms_state.alphaToOneEnable = VK_FALSE;
CreatePipelineHelper pipe(*this);
pipe.gp_ci_.renderPass = rp;
pipe.ms_ci_ = ms_state;
m_errorMonitor->SetDesiredError("VUID-VkGraphicsPipelineCreateInfo-subpass-00758");
pipe.CreateGraphicsPipeline();
m_errorMonitor->VerifyFound();
}
TEST_F(NegativeSubpass, FamilyOwnershipMaintenance8) {
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredExtensions(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_MAINTENANCE_8_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::maintenance8);
RETURN_IF_SKIP(Init());
VkSubpassDescription subpasses[] = {
{0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, nullptr, 0, nullptr, nullptr, nullptr, 0, nullptr},
{0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, nullptr, 0, nullptr, nullptr, nullptr, 0, nullptr},
};
VkSubpassDependency dependency;
auto rpci = vku::InitStruct<VkRenderPassCreateInfo>(nullptr, 0u, 0u, nullptr, 2u, subpasses, 1u, &dependency);
dependency = {0,
1,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
0,
0,
VK_DEPENDENCY_QUEUE_FAMILY_OWNERSHIP_TRANSFER_USE_ALL_STAGES_BIT_KHR};
CreateRenderPassTest(rpci, true, "VUID-VkSubpassDependency-dependencyFlags-10203",
"VUID-VkSubpassDependency2-dependencyFlags-10204");
}