blob: 505f59c832eb566e29383a89396c5f568a835cb8 [file] [log] [blame]
/*
* Copyright (c) 2015-2025 The Khronos Group Inc.
* Copyright (c) 2015-2025 Valve Corporation
* Copyright (c) 2015-2025 LunarG, Inc.
* Copyright (c) 2015-2025 Google, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*/
#include <vulkan/vulkan_core.h>
#include "../framework/layer_validation_tests.h"
#include "../framework/pipeline_helper.h"
#include "../framework/descriptor_helper.h"
#include "../framework/render_pass_helper.h"
#include "utils/convert_utils.h"
class PositiveRenderPass : public VkLayerTest {};
TEST_F(PositiveRenderPass, AttachmentUsedTwiceOK) {
TEST_DESCRIPTION("Attachment is used simultaneously as color and input, with the same layout. This is OK.");
RETURN_IF_SKIP(Init());
RenderPassSingleSubpass rp(*this);
rp.AddAttachmentDescription(VK_FORMAT_R8G8B8A8_UNORM);
rp.AddAttachmentReference({0, VK_IMAGE_LAYOUT_GENERAL});
rp.AddInputAttachment(0);
rp.AddColorAttachment(0);
rp.CreateRenderPass();
}
TEST_F(PositiveRenderPass, InitialLayoutUndefined) {
TEST_DESCRIPTION(
"Ensure that CmdBeginRenderPass with an attachment's initialLayout of VK_IMAGE_LAYOUT_UNDEFINED works when the command "
"buffer has prior knowledge of that attachment's layout.");
RETURN_IF_SKIP(Init());
// A renderpass with one color attachment.
RenderPassSingleSubpass rp(*this);
rp.AddAttachmentDescription(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
rp.AddAttachmentReference({0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL});
rp.AddColorAttachment(0);
rp.CreateRenderPass();
// A compatible framebuffer.
vkt::Image image(*m_device, 32, 32, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
vkt::ImageView view = image.CreateView();
vkt::Framebuffer fb(*m_device, rp, 1, &view.handle());
// Record a single command buffer which uses this renderpass twice. The
// bug is triggered at the beginning of the second renderpass, when the
// command buffer already has a layout recorded for the attachment.
m_command_buffer.Begin();
m_command_buffer.BeginRenderPass(rp, fb, 32, 32);
m_command_buffer.EndRenderPass();
m_errorMonitor->SetAllowedFailureMsg("SYNC-HAZARD-WRITE-AFTER-WRITE"); // if running with sync val
m_command_buffer.BeginRenderPass(rp, fb, 32, 32);
m_command_buffer.EndRenderPass();
m_command_buffer.End();
}
TEST_F(PositiveRenderPass, AttachmentLayoutWithLoadOpThenReadOnly) {
TEST_DESCRIPTION(
"Positive test where we create a renderpass with an attachment that uses LOAD_OP_CLEAR, the first subpass has a valid "
"layout, and a second subpass then uses a valid *READ_ONLY* layout.");
RETURN_IF_SKIP(Init());
auto depth_format = FindSupportedDepthStencilFormat(Gpu());
VkAttachmentReference attach[2] = {};
attach[0].attachment = 0;
attach[0].layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attach[1].attachment = 0;
attach[1].layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
VkSubpassDescription subpasses[2] = {};
// First subpass clears DS attach on load
subpasses[0].pDepthStencilAttachment = &attach[0];
// 2nd subpass reads in DS as input attachment
subpasses[1].inputAttachmentCount = 1;
subpasses[1].pInputAttachments = &attach[1];
VkAttachmentDescription attach_desc = {};
attach_desc.format = depth_format;
attach_desc.samples = VK_SAMPLE_COUNT_1_BIT;
attach_desc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attach_desc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attach_desc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attach_desc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attach_desc.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attach_desc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
VkRenderPassCreateInfo rpci = vku::InitStructHelper();
rpci.attachmentCount = 1;
rpci.pAttachments = &attach_desc;
rpci.subpassCount = 2;
rpci.pSubpasses = subpasses;
// Now create RenderPass and verify no errors
vkt::RenderPass rp(*m_device, rpci);
}
TEST_F(PositiveRenderPass, BeginSubpassZeroTransitionsApplied) {
TEST_DESCRIPTION("Ensure that CmdBeginRenderPass applies the layout transitions for the first subpass");
RETURN_IF_SKIP(Init());
// A renderpass with one color attachment.
RenderPassSingleSubpass rp(*this);
rp.AddAttachmentDescription(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
rp.AddAttachmentReference({0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL});
rp.AddColorAttachment(0);
rp.AddSubpassDependency();
rp.CreateRenderPass();
// A compatible framebuffer.
vkt::Image image(*m_device, 32, 32, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
image.SetLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
vkt::ImageView view = image.CreateView();
vkt::Framebuffer fb(*m_device, rp, 1, &view.handle());
// Record a single command buffer which issues a pipeline barrier w/
// image memory barrier for the attachment. This detects the previously
// missing tracking of the subpass layout by throwing a validation error
// if it doesn't occur.
m_command_buffer.Begin();
m_command_buffer.BeginRenderPass(rp, fb, 32, 32);
image.ImageMemoryBarrier(m_command_buffer, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
m_command_buffer.EndRenderPass();
m_command_buffer.End();
}
TEST_F(PositiveRenderPass, BeginTransitionsAttachmentUnused) {
TEST_DESCRIPTION(
"Ensure that layout transitions work correctly without errors, when an attachment reference is VK_ATTACHMENT_UNUSED");
RETURN_IF_SKIP(Init());
// A renderpass with no attachments
VkAttachmentReference att_ref = {VK_ATTACHMENT_UNUSED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
VkSubpassDescription subpass = {0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, nullptr, 1, &att_ref, nullptr, nullptr, 0, nullptr};
VkRenderPassCreateInfo rpci = {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, nullptr, 0, 0, nullptr, 1, &subpass, 0, nullptr};
vkt::RenderPass rp(*m_device, rpci);
// A compatible framebuffer.
vkt::Framebuffer fb(*m_device, rp, 0, nullptr);
// Record a command buffer which just begins and ends the renderpass. The
// bug manifests in BeginRenderPass.
m_command_buffer.Begin();
m_command_buffer.BeginRenderPass(rp, fb, 32, 32);
m_command_buffer.EndRenderPass();
m_command_buffer.End();
}
TEST_F(PositiveRenderPass, BeginStencilLoadOp) {
TEST_DESCRIPTION("Create a stencil-only attachment with a LOAD_OP set to CLEAR. stencil[Load|Store]Op used to be ignored.");
RETURN_IF_SKIP(Init());
VkFormat depth_stencil_fmt = FindSupportedDepthStencilFormat(Gpu());
VkImageFormatProperties formatProps;
vk::GetPhysicalDeviceImageFormatProperties(Gpu(), depth_stencil_fmt, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 0,
&formatProps);
if (formatProps.maxExtent.width < 100 || formatProps.maxExtent.height < 100) {
GTEST_SKIP() << "Image format max extent is too small";
}
m_depthStencil->Init(*m_device, 100, 100, 1, depth_stencil_fmt,
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
m_depthStencil->SetLayout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
RenderPassSingleSubpass rp(*this);
rp.AddAttachmentDescription(depth_stencil_fmt, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
rp.AddAttachmentReference({0, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL});
rp.AddDepthStencilAttachment(0);
rp.CreateRenderPass();
VkClearValue clear;
clear.depthStencil.depth = 1.0;
clear.depthStencil.stencil = 0;
vkt::ImageView depth_image_view = m_depthStencil->CreateView(VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);
vkt::Framebuffer fb(*m_device, rp, 1, &depth_image_view.handle(), 100, 100);
m_command_buffer.Begin();
m_command_buffer.BeginRenderPass(rp, fb, 100, 100, 1, &clear);
m_command_buffer.EndRenderPass();
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
vkt::Image destImage(*m_device, 100, 100, depth_stencil_fmt,
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
vkt::CommandBuffer cmdbuf(*m_device, m_command_pool);
cmdbuf.Begin();
m_depthStencil->ImageMemoryBarrier(cmdbuf, VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT,
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
destImage.ImageMemoryBarrier(cmdbuf, 0, VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
VkImageCopy cregion;
cregion.srcSubresource = {VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, 0, 0, 1};
cregion.srcOffset = {0, 0, 0};
cregion.dstSubresource = {VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, 0, 0, 1};
cregion.dstOffset = {0, 0, 0};
cregion.extent = {100, 100, 1};
vk::CmdCopyImage(cmdbuf, m_depthStencil->handle(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, destImage,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &cregion);
cmdbuf.End();
m_default_queue->SubmitAndWait(cmdbuf);
}
TEST_F(PositiveRenderPass, BeginInlineAndSecondaryCommandBuffers) {
RETURN_IF_SKIP(Init());
InitRenderTarget();
m_command_buffer.Begin();
vk::CmdBeginRenderPass(m_command_buffer, &m_renderPassBeginInfo, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS);
m_command_buffer.EndRenderPass();
vk::CmdBeginRenderPass(m_command_buffer, &m_renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
m_command_buffer.EndRenderPass();
m_command_buffer.End();
}
TEST_F(PositiveRenderPass, BeginDepthStencilLayoutTransitionFromUndefined) {
TEST_DESCRIPTION(
"Create a render pass with depth-stencil attachment where layout transition from UNDEFINED TO DS_READ_ONLY_OPTIMAL is set "
"by render pass and verify that transition has correctly occurred at queue submit time with no validation errors.");
RETURN_IF_SKIP(Init());
auto depth_format = FindSupportedDepthStencilFormat(Gpu());
VkImageFormatProperties format_props;
vk::GetPhysicalDeviceImageFormatProperties(Gpu(), depth_format, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, 0, &format_props);
if (format_props.maxExtent.width < 32 || format_props.maxExtent.height < 32) {
GTEST_SKIP() << "Depth extent too small";
}
InitRenderTarget();
// A renderpass with one depth/stencil attachment.
RenderPassSingleSubpass rp(*this);
rp.AddAttachmentDescription(depth_format, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
rp.AddAttachmentReference({0, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL});
rp.AddDepthStencilAttachment(0);
rp.CreateRenderPass();
// A compatible ds image.
vkt::Image image(*m_device, 32, 32, depth_format, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
image.SetLayout(VK_IMAGE_LAYOUT_GENERAL);
vkt::ImageView view = image.CreateView(VK_IMAGE_ASPECT_DEPTH_BIT);
vkt::Framebuffer fb(*m_device, rp, 1, &view.handle());
m_command_buffer.Begin();
m_command_buffer.BeginRenderPass(rp, fb, 32, 32);
m_command_buffer.EndRenderPass();
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
}
TEST_F(PositiveRenderPass, DestroyPipeline) {
TEST_DESCRIPTION("Draw using a pipeline whose create renderPass has been destroyed.");
RETURN_IF_SKIP(Init());
InitRenderTarget();
// Create a renderPass that's compatible with Draw-time renderPass
RenderPassSingleSubpass rp(*this);
rp.AddAttachmentDescription(m_render_target_fmt, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_STORE);
rp.AddAttachmentReference({0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL});
rp.AddColorAttachment(0);
rp.CreateRenderPass();
CreatePipelineHelper pipe(*this);
pipe.gp_ci_.renderPass = rp;
pipe.CreateGraphicsPipeline();
m_command_buffer.Begin();
m_command_buffer.BeginRenderPass(m_renderPassBeginInfo);
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe);
// Destroy renderPass before pipeline is used in Draw
// We delay until after CmdBindPipeline to verify that invalid binding isn't
// created between CB & renderPass, which we used to do.
rp.Destroy();
vk::CmdDraw(m_command_buffer, 3, 1, 0, 0);
m_command_buffer.EndRenderPass();
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
}
TEST_F(PositiveRenderPass, ImagelessFramebufferNonZeroBaseMip) {
TEST_DESCRIPTION("Use a 1D image view for an imageless framebuffer with base mip level > 0.");
AddRequiredExtensions(VK_KHR_IMAGELESS_FRAMEBUFFER_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::imagelessFramebuffer);
RETURN_IF_SKIP(Init());
constexpr uint32_t width = 512;
constexpr uint32_t height = 1;
VkFormat formats[2] = {VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8A8_UNORM};
VkFormat fb_attachments[2] = {VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8A8_UNORM};
constexpr uint32_t base_mip = 1;
// Create a renderPass with a single attachment
RenderPassSingleSubpass rp(*this);
rp.AddAttachmentDescription(formats[0]);
rp.AddAttachmentReference({0, VK_IMAGE_LAYOUT_GENERAL});
rp.AddColorAttachment(0);
rp.CreateRenderPass();
VkFramebufferAttachmentImageInfo fb_attachment_image_info = vku::InitStructHelper();
fb_attachment_image_info.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
fb_attachment_image_info.width = width;
fb_attachment_image_info.height = height;
fb_attachment_image_info.layerCount = 1;
fb_attachment_image_info.viewFormatCount = 2;
fb_attachment_image_info.pViewFormats = fb_attachments;
fb_attachment_image_info.height = 1;
fb_attachment_image_info.width = width >> base_mip;
VkFramebufferAttachmentsCreateInfo fb_attachments_ci = vku::InitStructHelper();
fb_attachments_ci.attachmentImageInfoCount = 1;
fb_attachments_ci.pAttachmentImageInfos = &fb_attachment_image_info;
VkFramebufferCreateInfo fb_ci = vku::InitStructHelper(&fb_attachments_ci);
fb_ci.flags = VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT;
fb_ci.width = width >> base_mip;
fb_ci.height = height;
fb_ci.layers = 1;
fb_ci.attachmentCount = 1;
fb_ci.pAttachments = nullptr;
fb_ci.renderPass = rp;
vkt::Framebuffer fb(*m_device, fb_ci);
ASSERT_TRUE(fb.initialized());
auto image_ci = vkt::Image::ImageCreateInfo2D(width, 1, 2, 1, formats[0],
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
image_ci.imageType = VK_IMAGE_TYPE_1D;
vkt::Image image(*m_device, image_ci, vkt::set_layout);
vkt::ImageView image_view_obj = image.CreateView(VK_IMAGE_VIEW_TYPE_1D_ARRAY, base_mip, 1, 0, 1);
VkImageView image_view = image_view_obj;
VkRenderPassAttachmentBeginInfo rp_attachment_begin_info = vku::InitStructHelper();
rp_attachment_begin_info.attachmentCount = 1;
rp_attachment_begin_info.pAttachments = &image_view;
VkRenderPassBeginInfo rp_begin_info = vku::InitStructHelper(&rp_attachment_begin_info);
rp_begin_info.renderPass = rp;
rp_begin_info.renderArea.extent.width = width >> base_mip;
rp_begin_info.renderArea.extent.height = height;
rp_begin_info.framebuffer = fb;
VkCommandBufferBeginInfo cmd_begin_info = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, nullptr,
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, nullptr};
m_command_buffer.Begin(&cmd_begin_info);
vk::CmdBeginRenderPass(m_command_buffer, &rp_begin_info, VK_SUBPASS_CONTENTS_INLINE);
}
TEST_F(PositiveRenderPass, ValidStages) {
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;
VkSubpassDependency dependency = {};
// to be filled later by tests
VkRenderPassCreateInfo rpci = vku::InitStructHelper();
rpci.subpassCount = 2;
rpci.pSubpasses = sci;
rpci.dependencyCount = 1;
rpci.pDependencies = &dependency;
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;
dependency.srcSubpass = 0;
dependency.dstSubpass = 1;
dependency.srcStageMask = kGraphicsStages;
dependency.dstStageMask = kGraphicsStages;
{
vkt::RenderPass rp(*m_device, rpci);
if (rp2_supported) {
vkt::RenderPass rp2(*m_device, *ConvertVkRenderPassCreateInfoToV2KHR(rpci).ptr());
}
}
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
dependency.dstSubpass = 0;
dependency.srcStageMask = kGraphicsStages | VK_PIPELINE_STAGE_HOST_BIT;
dependency.dstStageMask = kGraphicsStages;
{
vkt::RenderPass rp(*m_device, rpci);
if (rp2_supported) {
vkt::RenderPass rp2(*m_device, *ConvertVkRenderPassCreateInfoToV2KHR(rpci).ptr());
}
}
dependency.srcSubpass = 0;
dependency.dstSubpass = VK_SUBPASS_EXTERNAL;
dependency.srcStageMask = kGraphicsStages;
dependency.dstStageMask = VK_PIPELINE_STAGE_HOST_BIT;
{
vkt::RenderPass rp(*m_device, rpci);
if (rp2_supported) {
vkt::RenderPass rp2(*m_device, *ConvertVkRenderPassCreateInfoToV2KHR(rpci).ptr());
}
}
}
TEST_F(PositiveRenderPass, SingleMipTransition) {
TEST_DESCRIPTION("Ensure that the validation message contains the correct miplevel");
RETURN_IF_SKIP(Init());
RenderPassSingleSubpass rp(*this);
rp.AddAttachmentDescription(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_UNDEFINED);
rp.AddAttachmentDescription(VK_FORMAT_D32_SFLOAT, VK_IMAGE_LAYOUT_UNDEFINED);
rp.AddAttachmentReference({0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL});
rp.AddAttachmentReference({1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL});
rp.AddColorAttachment(0);
rp.AddDepthStencilAttachment(1);
rp.AddSubpassDependency(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
VK_ACCESS_SHADER_READ_BIT);
rp.CreateRenderPass();
// Create Framebuffer.
const VkImageCreateInfo color_ci =
vkt::Image::ImageCreateInfo2D(32, 32, 2, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
vkt::Image colorImage(*m_device, color_ci);
const VkImageCreateInfo depth_ci = vkt::Image::ImageCreateInfo2D(
32, 32, 2, 1, VK_FORMAT_D32_SFLOAT, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
vkt::Image depthImage(*m_device, depth_ci);
vkt::ImageView color_view = colorImage.CreateView(VK_IMAGE_VIEW_TYPE_2D, /*baseMipLevel*/ 0, /*levelCount*/ 1);
vkt::ImageView depth_view = depthImage.CreateView(VK_IMAGE_VIEW_TYPE_2D, /*baseMipLevel*/ 0, /*levelCount*/ 1, 0,
VK_REMAINING_ARRAY_LAYERS, VK_IMAGE_ASPECT_DEPTH_BIT);
VkImageView baseViews[] = {color_view, depth_view};
VkImageViewCreateInfo vinfo = vku::InitStructHelper();
vinfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
vinfo.components.r = VK_COMPONENT_SWIZZLE_R;
vinfo.components.g = VK_COMPONENT_SWIZZLE_G;
vinfo.components.b = VK_COMPONENT_SWIZZLE_B;
vinfo.components.a = VK_COMPONENT_SWIZZLE_A;
vinfo.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 1, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS};
vinfo.image = colorImage;
vinfo.format = VK_FORMAT_R8G8B8A8_UNORM;
vinfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
vkt::ImageView fullView0(*m_device, vinfo);
vinfo.image = depthImage;
vinfo.format = VK_FORMAT_D32_SFLOAT;
vinfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
vkt::ImageView fullView1(*m_device, vinfo);
VkImageView fullViews[] = {fullView0, fullView1};
vkt::Framebuffer fb(*m_device, rp, 2, baseViews);
// Create shader modules
const char fsSource[] = R"glsl(
#version 450
layout(location=0) out vec4 x;
layout(set=0, binding=2) uniform sampler2D depth;
void main() {
x = texture(depth, vec2(0));
}
)glsl";
VkShaderObj fs(*m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT);
// Create descriptor set and friends.
vkt::Sampler sampler(*m_device, SafeSaneSamplerCreateInfo());
std::vector<VkDescriptorSetLayoutBinding> binding_defs = {
{2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, nullptr}};
const vkt::DescriptorSetLayout pipeline_dsl(*m_device, binding_defs);
const vkt::PipelineLayout pipeline_layout(*m_device, {&pipeline_dsl});
OneOffDescriptorSet descriptor_set(m_device, binding_defs);
descriptor_set.WriteDescriptorImageInfo(2, fullViews[1], sampler, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL);
descriptor_set.UpdateDescriptorSets();
VkPipelineDepthStencilStateCreateInfo ds_ci = vku::InitStructHelper();
ds_ci.depthTestEnable = VK_TRUE;
ds_ci.depthCompareOp = VK_COMPARE_OP_LESS;
CreatePipelineHelper pipe(*this);
pipe.shader_stages_[1] = fs.GetStageCreateInfo();
pipe.gp_ci_.layout = pipeline_layout;
pipe.gp_ci_.renderPass = rp;
pipe.ds_ci_ = ds_ci;
pipe.CreateGraphicsPipeline();
// Start pushing commands.
m_command_buffer.Begin();
m_command_buffer.BeginRenderPass(rp, fb, 32, 32);
vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_set.set_, 0,
NULL);
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe);
vk::CmdDraw(m_command_buffer, 3, 1, 0, 0);
m_command_buffer.EndRenderPass();
// At this point the first miplevel should be in GENERAL due to the "finalLayout" in the render pass.
// Note that these image barriers attempt to transition *all* miplevels, even though only 1 miplevel has transitioned.
colorImage.ImageMemoryBarrier(m_command_buffer, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
m_command_buffer.End();
}
TEST_F(PositiveRenderPass, BeginDedicatedStencilLayout) {
TEST_DESCRIPTION("Render pass using a dedicated stencil layout, different from depth layout");
SetTargetApiVersion(VK_API_VERSION_1_2);
AddRequiredFeature(vkt::Feature::separateDepthStencilLayouts);
RETURN_IF_SKIP(Init());
// Create depth stencil image
const VkFormat ds_format = FindSupportedDepthStencilFormat(Gpu());
vkt::Image ds_image(*m_device, 32, 32, ds_format,
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
vkt::ImageView ds_view = ds_image.CreateView(VK_IMAGE_ASPECT_DEPTH_BIT);
VkAttachmentDescriptionStencilLayout attachment_desc_stencil_layout = vku::InitStructHelper();
attachment_desc_stencil_layout.stencilInitialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attachment_desc_stencil_layout.stencilFinalLayout = VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL;
VkAttachmentReferenceStencilLayout attachment_ref_stencil_layout = vku::InitStructHelper();
attachment_ref_stencil_layout.stencilLayout = VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL;
RenderPass2SingleSubpass rp(*this);
rp.AddAttachmentDescription(ds_format, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL);
rp.SetAttachmentDescriptionPNext(0, &attachment_desc_stencil_layout);
rp.AddAttachmentReference(0, VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL, 0, &attachment_ref_stencil_layout);
rp.AddDepthStencilAttachment(0);
rp.CreateRenderPass();
vkt::Framebuffer fb(*m_device, rp, 1, &ds_view.handle(), ds_image.Width(), ds_image.Height());
// Use helper to create graphics pipeline
VkPipelineDepthStencilStateCreateInfo ds_state = vku::InitStructHelper();
// One stencil op is not OP_KEEP, both write mask are not 0
ds_state.front.failOp = VK_STENCIL_OP_ZERO;
ds_state.front.writeMask = 0x1;
ds_state.back.writeMask = 0x1;
CreatePipelineHelper helper(*this);
helper.gp_ci_.pDepthStencilState = &ds_state;
helper.gp_ci_.renderPass = rp;
helper.CreateGraphicsPipeline();
m_command_buffer.Begin();
m_command_buffer.BeginRenderPass(rp, fb, ds_image.Width(), ds_image.Height());
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, helper);
// If the stencil layout was not specified separately using the separateDepthStencilLayouts feature,
// and used in the validation code, 06887 would trigger with the following draw call
vk::CmdDraw(m_command_buffer, 3, 1, 0, 0);
m_command_buffer.EndRenderPass();
m_command_buffer.End();
}
TEST_F(PositiveRenderPass, StoreOpNoneExt) {
AddRequiredExtensions(VK_EXT_LOAD_STORE_OP_NONE_EXTENSION_NAME);
RETURN_IF_SKIP(Init());
RenderPassSingleSubpass rp(*this);
rp.AddAttachmentDescription(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_NONE);
rp.AddAttachmentReference({0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL});
rp.AddColorAttachment(0);
rp.CreateRenderPass();
}
TEST_F(PositiveRenderPass, FramebufferCreateDepthStencilLayoutTransitionForDepthOnlyImageView) {
TEST_DESCRIPTION(
"Validate that when an imageView 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.");
RETURN_IF_SKIP(Init());
VkFormatProperties format_properties;
vk::GetPhysicalDeviceFormatProperties(Gpu(), VK_FORMAT_D32_SFLOAT_S8_UINT, &format_properties);
if (!(format_properties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) {
GTEST_SKIP() << "Image format does not support sampling";
}
RenderPassSingleSubpass rp(*this);
rp.AddAttachmentDescription(VK_FORMAT_D32_SFLOAT_S8_UINT, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
rp.AddAttachmentReference({0, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL});
rp.AddDepthStencilAttachment(0);
rp.AddSubpassDependency();
rp.CreateRenderPass();
vkt::Image image(*m_device, 32, 32, VK_FORMAT_D32_SFLOAT_S8_UINT, 0x26 /* usage */);
image.SetLayout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
vkt::ImageView view = image.CreateView(VK_IMAGE_ASPECT_DEPTH_BIT);
vkt::Framebuffer fb(*m_device, rp, 1, &view.handle());
m_command_buffer.Begin();
VkImageMemoryBarrier imb = vku::InitStructHelper();
imb.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
imb.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
imb.oldLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
imb.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
imb.srcQueueFamilyIndex = 0;
imb.dstQueueFamilyIndex = 0;
imb.image = image;
imb.subresourceRange.aspectMask = 0x6;
imb.subresourceRange.baseMipLevel = 0;
imb.subresourceRange.levelCount = 0x1;
imb.subresourceRange.baseArrayLayer = 0;
imb.subresourceRange.layerCount = 0x1;
vk::CmdPipelineBarrier(m_command_buffer, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
VK_DEPENDENCY_BY_REGION_BIT, 0, nullptr, 0, nullptr, 1, &imb);
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
}
TEST_F(PositiveRenderPass, FramebufferWithAttachmentsTo3DImageMultipleSubpasses) {
TEST_DESCRIPTION(
"Test no false overlap is reported with multi attachment framebuffer (attachments are slices of a 3D image). Multiple "
"subpasses that draw to a single slice of a 3D image");
AddRequiredExtensions(VK_KHR_MAINTENANCE_1_EXTENSION_NAME);
RETURN_IF_SKIP(Init());
constexpr unsigned depth_count = 2u;
// 3D image with 2 depths
VkImageCreateInfo image_info = vku::InitStructHelper();
image_info.flags = VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
image_info.imageType = VK_IMAGE_TYPE_3D;
image_info.format = VK_FORMAT_R8G8B8A8_UNORM;
image_info.extent = {64, 64, depth_count};
image_info.mipLevels = 1u;
image_info.arrayLayers = 1u;
image_info.samples = VK_SAMPLE_COUNT_1_BIT;
image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
image_info.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
vkt::Image image_3d(*m_device, image_info, vkt::set_layout);
// 2D image views to be used as color attchments for framebuffer
VkImageViewCreateInfo view_info = vku::InitStructHelper();
view_info.image = image_3d;
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
view_info.format = image_info.format;
view_info.components.r = VK_COMPONENT_SWIZZLE_R;
view_info.components.g = VK_COMPONENT_SWIZZLE_G;
view_info.components.b = VK_COMPONENT_SWIZZLE_B;
view_info.components.a = VK_COMPONENT_SWIZZLE_A;
view_info.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
vkt::ImageView image_views[depth_count];
VkImageView views[depth_count] = {VK_NULL_HANDLE};
for (unsigned i = 0; i < depth_count; ++i) {
view_info.subresourceRange.baseArrayLayer = i;
image_views[i].Init(*m_device, view_info);
views[i] = image_views[i];
}
// Render pass with 2 subpasses
VkAttachmentReference attach[depth_count] = {};
VkSubpassDescription subpasses[depth_count] = {};
for (unsigned i = 0; i < depth_count; ++i) {
attach[i].attachment = i;
attach[i].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
subpasses[i].pColorAttachments = &attach[i];
subpasses[i].colorAttachmentCount = 1;
}
VkAttachmentDescription attach_desc[depth_count] = {};
for (unsigned i = 0; i < depth_count; ++i) {
attach_desc[i].format = image_info.format;
attach_desc[i].samples = VK_SAMPLE_COUNT_1_BIT;
attach_desc[i].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attach_desc[i].finalLayout = VK_IMAGE_LAYOUT_GENERAL;
}
VkSubpassDependency dependency;
dependency.srcSubpass = 0u;
dependency.dstSubpass = 1u;
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
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 = 0u;
VkRenderPassCreateInfo rp_info = vku::InitStructHelper();
rp_info.subpassCount = depth_count;
rp_info.pSubpasses = subpasses;
rp_info.attachmentCount = depth_count;
rp_info.pAttachments = attach_desc;
rp_info.dependencyCount = 1u;
rp_info.pDependencies = &dependency;
vkt::RenderPass renderpass(*m_device, rp_info);
vkt::Framebuffer framebuffer(*m_device, renderpass, depth_count, views, image_info.extent.width, image_info.extent.height);
m_command_buffer.Begin();
m_command_buffer.BeginRenderPass(renderpass, framebuffer, image_info.extent.width, image_info.extent.height);
for (unsigned i = 0; i < (depth_count - 1); ++i) {
m_command_buffer.NextSubpass();
}
m_command_buffer.EndRenderPass();
m_command_buffer.End();
}
TEST_F(PositiveRenderPass, ImageLayoutTransitionOf3dImageWith2dViews) {
TEST_DESCRIPTION(
"Test that transitioning the layout of a mip level of a 3D image using a view of one of its slice applies to the entire 3D "
"image: all views referencing different slices of the same mip level should also see their layout transitioned");
AddRequiredExtensions(VK_KHR_MAINTENANCE_1_EXTENSION_NAME);
RETURN_IF_SKIP(Init());
constexpr unsigned image_depth = 2u;
// 3D image
VkImageCreateInfo image_info = vku::InitStructHelper();
image_info.flags = VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
image_info.imageType = VK_IMAGE_TYPE_3D;
image_info.format = VK_FORMAT_R8G8B8A8_UNORM;
image_info.extent = {64, 64, image_depth};
image_info.mipLevels = 1u;
image_info.arrayLayers = 1u;
image_info.samples = VK_SAMPLE_COUNT_1_BIT;
image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
image_info.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
vkt::Image image_3d(*m_device, image_info, vkt::set_layout);
// 2D image views for each slice of the 3D image
VkImageViewCreateInfo view_info = vku::InitStructHelper();
view_info.image = image_3d;
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
view_info.format = image_info.format;
view_info.components.r = VK_COMPONENT_SWIZZLE_R;
view_info.components.g = VK_COMPONENT_SWIZZLE_G;
view_info.components.b = VK_COMPONENT_SWIZZLE_B;
view_info.components.a = VK_COMPONENT_SWIZZLE_A;
view_info.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
vkt::ImageView image_views[image_depth];
VkImageView views[image_depth] = {VK_NULL_HANDLE};
for (unsigned i = 0; i < image_depth; ++i) {
view_info.subresourceRange.baseArrayLayer = i;
image_views[i].Init(*m_device, view_info);
views[i] = image_views[i];
}
// Render pass 1, referencing first slice
RenderPassSingleSubpass rp_1(*this);
rp_1.AddAttachmentDescription(image_info.format, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
rp_1.AddAttachmentReference({0, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL});
rp_1.AddInputAttachment(0);
rp_1.CreateRenderPass();
vkt::Framebuffer framebuffer_1(*m_device, rp_1, 1, &views[0], image_info.extent.width, image_info.extent.height);
// Render pass 2, referencing second slice
RenderPassSingleSubpass rp_2(*this);
// Since the previous render pass' framebuffer was using a 2D view of the first slice of the 3D image,
// the layout transition should have applied to all the slices of the 3D image,
// thus the 2nd image view created on the second slice of the 3D image should found its image layout to be
// VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
rp_2.AddAttachmentDescription(image_info.format, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
rp_2.AddAttachmentReference({0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL});
rp_2.AddColorAttachment(0);
rp_2.CreateRenderPass();
vkt::Framebuffer framebuffer_2(*m_device, rp_2, 1, &views[1], image_info.extent.width, image_info.extent.height);
m_command_buffer.Begin();
m_command_buffer.BeginRenderPass(rp_1, framebuffer_1, image_info.extent.width, image_info.extent.height);
m_command_buffer.EndRenderPass();
m_command_buffer.FullMemoryBarrier();
m_command_buffer.BeginRenderPass(rp_2, framebuffer_2, image_info.extent.width, image_info.extent.height);
m_command_buffer.EndRenderPass();
m_command_buffer.End();
}
TEST_F(PositiveRenderPass, SubpassWithReadOnlyLayoutWithoutDependency) {
TEST_DESCRIPTION("When both subpasses' attachments are the same and layouts are read-only, they don't need dependency.");
RETURN_IF_SKIP(Init());
auto depth_format = FindSupportedDepthStencilFormat(Gpu());
// A renderpass with one color attachment.
VkAttachmentDescription attachment = {0,
depth_format,
VK_SAMPLE_COUNT_1_BIT,
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
VK_ATTACHMENT_STORE_OP_STORE,
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
VK_ATTACHMENT_STORE_OP_DONT_CARE,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL};
const int size = 2;
std::array<VkAttachmentDescription, size> attachments = {{attachment, attachment}};
VkAttachmentReference att_ref_depth_stencil = {0, VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL};
std::array<VkSubpassDescription, size> subpasses;
subpasses[0] = {0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, 0, 0, nullptr, nullptr, &att_ref_depth_stencil, 0, nullptr};
subpasses[1] = {0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, 0, 0, nullptr, nullptr, &att_ref_depth_stencil, 0, nullptr};
VkSubpassDependency dependency;
dependency.srcSubpass = 0u;
dependency.dstSubpass = 1u;
dependency.srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
dependency.dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
dependency.srcAccessMask = 0u;
dependency.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dependency.dependencyFlags = 0u;
VkRenderPassCreateInfo rpci = {
VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, nullptr, 0, size, attachments.data(), size, subpasses.data(), 1, &dependency};
vkt::RenderPass rp(*m_device, rpci);
// A compatible framebuffer.
vkt::Image image(*m_device, 32, 32, depth_format, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
vkt::ImageView view = image.CreateView(VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);
std::array<VkImageView, size> views = {{view, view}};
vkt::Framebuffer fb(*m_device, rp, size, views.data());
m_command_buffer.Begin();
m_command_buffer.BeginRenderPass(rp, fb, 32, 32);
m_command_buffer.NextSubpass();
m_command_buffer.EndRenderPass();
m_command_buffer.End();
}
TEST_F(PositiveRenderPass, SeparateDepthStencilSubresourceLayout) {
TEST_DESCRIPTION("Test that separate depth stencil layouts are tracked correctly.");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredExtensions(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_SEPARATE_DEPTH_STENCIL_LAYOUTS_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::separateDepthStencilLayouts);
RETURN_IF_SKIP(Init());
VkFormat ds_format = VK_FORMAT_D24_UNORM_S8_UINT;
VkFormatProperties props;
vk::GetPhysicalDeviceFormatProperties(Gpu(), ds_format, &props);
if ((props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) == 0) {
ds_format = VK_FORMAT_D32_SFLOAT_S8_UINT;
vk::GetPhysicalDeviceFormatProperties(Gpu(), ds_format, &props);
ASSERT_TRUE((props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0);
}
auto image_ci = vkt::Image::ImageCreateInfo2D(64, 64, 1, 6, ds_format, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
vkt::Image image(*m_device, image_ci);
const VkImageSubresourceRange depth_range = image.SubresourceRange(VK_IMAGE_ASPECT_DEPTH_BIT);
const VkImageSubresourceRange stencil_range = image.SubresourceRange(VK_IMAGE_ASPECT_STENCIL_BIT);
const VkImageSubresourceRange depth_stencil_range =
image.SubresourceRange(VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);
VkImageViewCreateInfo view_info = vku::InitStructHelper();
view_info.image = image;
view_info.subresourceRange = depth_stencil_range;
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
view_info.format = ds_format;
vkt::ImageView view(*m_device, view_info);
std::vector<VkImageMemoryBarrier> barriers;
{
m_command_buffer.Begin();
auto depth_barrier =
image.ImageMemoryBarrier(0, 0, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, depth_range);
auto stencil_barrier =
image.ImageMemoryBarrier(0, 0, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL, stencil_range);
vk::CmdPipelineBarrier(m_command_buffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0,
nullptr, 0, nullptr, 1, &depth_barrier);
vk::CmdPipelineBarrier(m_command_buffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0,
nullptr, 0, nullptr, 1, &stencil_barrier);
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
m_command_buffer.Reset();
}
m_command_buffer.Begin();
// Test that we handle initial layout in command buffer.
barriers.push_back(image.ImageMemoryBarrier(0, 0, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, depth_stencil_range));
// Test that we can transition aspects separately and use specific layouts.
barriers.push_back(image.ImageMemoryBarrier(0, 0, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL, depth_range));
barriers.push_back(image.ImageMemoryBarrier(0, 0, VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL, stencil_range));
// Test that transition from UNDEFINED on depth aspect does not clobber stencil layout.
barriers.push_back(
image.ImageMemoryBarrier(0, 0, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, depth_range));
// Test that we can transition aspects separately and use combined layouts. (Only care about the aspect in question).
barriers.push_back(image.ImageMemoryBarrier(0, 0, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL, depth_range));
barriers.push_back(image.ImageMemoryBarrier(0, 0, VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, stencil_range));
// Test that we can transition back again with combined layout.
barriers.push_back(image.ImageMemoryBarrier(0, 0, VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, depth_stencil_range));
VkRenderPassCreateInfo2 rp2 = vku::InitStructHelper();
VkAttachmentDescription2 desc = vku::InitStructHelper();
VkSubpassDescription2 sub = vku::InitStructHelper();
VkAttachmentReference2 att = vku::InitStructHelper();
VkAttachmentDescriptionStencilLayout stencil_desc = vku::InitStructHelper();
VkAttachmentReferenceStencilLayout stencil_att = vku::InitStructHelper();
// Test that we can discard stencil layout.
stencil_desc.stencilInitialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
stencil_desc.stencilFinalLayout = VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL;
stencil_att.stencilLayout = VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL;
desc.format = ds_format;
desc.initialLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL;
desc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL;
desc.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
desc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
desc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
desc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
desc.samples = VK_SAMPLE_COUNT_1_BIT;
desc.pNext = &stencil_desc;
att.layout = VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL;
att.attachment = 0;
att.pNext = &stencil_att;
sub.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
sub.pDepthStencilAttachment = &att;
rp2.subpassCount = 1;
rp2.pSubpasses = &sub;
rp2.attachmentCount = 1;
rp2.pAttachments = &desc;
vkt::RenderPass render_pass_separate(*m_device, rp2);
desc.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
desc.finalLayout = desc.initialLayout;
desc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
desc.pNext = nullptr;
att.layout = desc.initialLayout;
att.pNext = nullptr;
vkt::RenderPass render_pass_combined(*m_device, rp2);
vkt::Framebuffer framebuffer_separate(*m_device, render_pass_separate, 1, &view.handle(), 1, 1);
vkt::Framebuffer framebuffer_combined(*m_device, render_pass_combined, 1, &view.handle(), 1, 1);
for (auto &barrier : barriers) {
vk::CmdPipelineBarrier(m_command_buffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0,
nullptr, 0, nullptr, 1, &barrier);
}
m_command_buffer.BeginRenderPass(render_pass_separate, framebuffer_separate);
m_command_buffer.EndRenderPass();
m_command_buffer.BeginRenderPass(render_pass_combined, framebuffer_combined);
m_command_buffer.EndRenderPass();
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
}
TEST_F(PositiveRenderPass, InputResolve) {
TEST_DESCRIPTION("Create render pass where input attachment == resolve attachment");
AddRequiredExtensions(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
AddOptionalExtensions(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME);
RETURN_IF_SKIP(Init());
RenderPassSingleSubpass rp(*this);
// input attachments
rp.AddAttachmentDescription(VK_FORMAT_R8G8B8A8_UNORM);
// color attachments
rp.AddAttachmentDescription(VK_FORMAT_R8G8B8A8_UNORM, VK_SAMPLE_COUNT_4_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
// resolve attachment
rp.AddAttachmentDescription(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
rp.AddAttachmentReference({0, VK_IMAGE_LAYOUT_GENERAL});
rp.AddAttachmentReference({1, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL});
rp.AddAttachmentReference({0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL});
rp.AddInputAttachment(0);
rp.AddColorAttachment(1);
rp.AddResolveAttachment(2);
rp.CreateRenderPass();
}
TEST_F(PositiveRenderPass, TestDepthStencilRenderPassTransition) {
TEST_DESCRIPTION(
"Create framebuffer with a depth/stencil attachment that has only depth or stencil aspect and transition it in render "
"pass. Then create a barrier on both aspect, for this to be valid *both* aspects must have been correctly tracked as "
"transitioned to the new layout.");
SetTargetApiVersion(VK_API_VERSION_1_1);
RETURN_IF_SKIP(InitFramework());
if (DeviceValidationVersion() < VK_API_VERSION_1_1) {
GTEST_SKIP() << "At least Vulkan version 1.1 is required";
}
RETURN_IF_SKIP(InitState(nullptr, nullptr, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT));
const VkFormat ds_format = FindSupportedDepthStencilFormat(m_device->Physical());
vkt::Image depthImage(*m_device, 32, 32, ds_format, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
for (size_t i = 0; i < 2; i++) {
const vkt::ImageView depth_or_stencil_view(
*m_device, depthImage.BasicViewCreatInfo(i == 0 ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_STENCIL_BIT));
RenderPassSingleSubpass rp(*this);
rp.AddAttachmentDescription(ds_format, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
rp.AddAttachmentReference({0, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL});
rp.AddDepthStencilAttachment(0);
rp.CreateRenderPass();
const vkt::Framebuffer fb(*m_device, rp, 1, &depth_or_stencil_view.handle());
m_command_buffer.Begin();
m_command_buffer.BeginRenderPass(rp, fb, 32, 32);
m_command_buffer.EndRenderPass();
VkImageMemoryBarrier img_barrier = vku::InitStructHelper();
img_barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
img_barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
img_barrier.oldLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
img_barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
img_barrier.image = depthImage;
img_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
img_barrier.subresourceRange.layerCount = 1;
img_barrier.subresourceRange.levelCount = 1;
vk::CmdPipelineBarrier(m_command_buffer,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &img_barrier);
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
}
}
TEST_F(PositiveRenderPass, BeginRenderPassWithRenderPassStriped) {
TEST_DESCRIPTION("Test to validate renderpass with VK_ARM_render_pass_striped.");
AddRequiredExtensions(VK_ARM_RENDER_PASS_STRIPED_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::renderPassStriped);
AddRequiredExtensions(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::synchronization2);
RETURN_IF_SKIP(Init());
InitRenderTarget();
VkPhysicalDeviceRenderPassStripedPropertiesARM rp_striped_props = vku::InitStructHelper();
GetPhysicalDeviceProperties2(rp_striped_props);
const uint32_t stripe_width = rp_striped_props.renderPassStripeGranularity.width;
const uint32_t stripe_height = rp_striped_props.renderPassStripeGranularity.height;
const uint32_t stripe_count = 4;
std::vector<VkRenderPassStripeInfoARM> stripe_infos(stripe_count);
for (uint32_t i = 0; i < stripe_count; ++i) {
stripe_infos[i] = vku::InitStructHelper();
stripe_infos[i].stripeArea.offset.x = stripe_width * i;
stripe_infos[i].stripeArea.offset.y = 0;
stripe_infos[i].stripeArea.extent = {stripe_width, stripe_height};
}
VkRenderPassStripeBeginInfoARM rp_stripes_info = vku::InitStructHelper();
rp_stripes_info.stripeInfoCount = stripe_count;
rp_stripes_info.pStripeInfos = stripe_infos.data();
m_renderPassBeginInfo.pNext = &rp_stripes_info;
m_renderPassBeginInfo.renderArea = {{0, 0}, {stripe_width * stripe_count, stripe_height}};
vkt::CommandPool command_pool(*m_device, m_device->graphics_queue_node_index_);
vkt::CommandBuffer cmd_buffer(*m_device, command_pool);
VkCommandBufferBeginInfo cmd_begin = vku::InitStructHelper();
cmd_buffer.Begin(&cmd_begin);
cmd_buffer.BeginRenderPass(m_renderPassBeginInfo);
cmd_buffer.EndRenderPass();
cmd_buffer.End();
VkSemaphoreCreateInfo semaphore_create_info = vku::InitStructHelper();
vkt::Semaphore semaphores[stripe_count];
VkSemaphoreSubmitInfo semaphore_submit_infos[stripe_count];
for (uint32_t i = 0; i < stripe_count; ++i) {
semaphores[i].Init(*m_device, semaphore_create_info);
semaphore_submit_infos[i] = vku::InitStructHelper();
semaphore_submit_infos[i].semaphore = semaphores[i];
}
VkRenderPassStripeSubmitInfoARM rp_stripe_submit_info = vku::InitStructHelper();
rp_stripe_submit_info.stripeSemaphoreInfoCount = stripe_count;
rp_stripe_submit_info.pStripeSemaphoreInfos = semaphore_submit_infos;
VkCommandBufferSubmitInfo cb_submit_info = vku::InitStructHelper(&rp_stripe_submit_info);
cb_submit_info.commandBuffer = cmd_buffer;
VkSubmitInfo2 submit_info = vku::InitStructHelper();
submit_info.commandBufferInfoCount = 1;
submit_info.pCommandBufferInfos = &cb_submit_info;
vk::QueueSubmit2KHR(m_default_queue->handle(), 1, &submit_info, VK_NULL_HANDLE);
m_default_queue->Wait();
}
TEST_F(PositiveRenderPass, NestedCommandBuffersFeatureMaintenance7) {
TEST_DESCRIPTION(
"Use VK_SUBPASS_CONTENTS_INLINE_AND_SECONDARY_COMMAND_BUFFERS_KHR with maintenance7, but not nextedCommandBuffers is not "
"enabled");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredExtensions(VK_EXT_NESTED_COMMAND_BUFFER_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_MAINTENANCE_7_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::maintenance7);
RETURN_IF_SKIP(Init());
InitRenderTarget();
m_command_buffer.Begin();
vk::CmdBeginRenderPass(m_command_buffer, &m_renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE_AND_SECONDARY_COMMAND_BUFFERS_KHR);
vk::CmdEndRenderPass(m_command_buffer);
m_command_buffer.End();
}
TEST_F(PositiveRenderPass, RenderPassSampleLocationsBeginInfo) {
TEST_DESCRIPTION("https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/8388");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredExtensions(VK_EXT_SAMPLE_LOCATIONS_EXTENSION_NAME);
RETURN_IF_SKIP(Init());
VkPhysicalDeviceSampleLocationsPropertiesEXT sample_location_properties = vku::InitStructHelper();
GetPhysicalDeviceProperties2(sample_location_properties);
if (sample_location_properties.variableSampleLocations) {
GTEST_SKIP() << "variableSampleLocations must not be supported";
}
if ((sample_location_properties.sampleLocationSampleCounts & VK_SAMPLE_COUNT_1_BIT) == 0) {
GTEST_SKIP() << "sampleLocationSampleCounts does not contain VK_SAMPLE_COUNT_1_BIT";
}
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_DONT_CARE;
attach_desc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attach_desc.finalLayout = VK_IMAGE_LAYOUT_GENERAL;
VkAttachmentReference attach = {};
attach.layout = VK_IMAGE_LAYOUT_GENERAL;
VkSubpassDescription subpass = {};
subpass.pColorAttachments = &attach;
subpass.colorAttachmentCount = 1;
VkRenderPassCreateInfo rpci = vku::InitStructHelper();
rpci.attachmentCount = 1;
rpci.pAttachments = &attach_desc;
rpci.subpassCount = 1;
rpci.pSubpasses = &subpass;
vkt::RenderPass render_pass(*m_device, rpci);
vkt::Image image(*m_device, 32, 32, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
vkt::ImageView image_view = image.CreateView();
vkt::Framebuffer framebuffer(*m_device, render_pass, 1, &image_view.handle());
VkSampleLocationEXT sample_location = {0.5f, 0.5f};
VkSampleLocationsInfoEXT sample_locations_info = vku::InitStructHelper();
sample_locations_info.sampleLocationsPerPixel = VK_SAMPLE_COUNT_1_BIT;
sample_locations_info.sampleLocationGridSize = {1u, 1u};
sample_locations_info.sampleLocationsCount = 1u;
sample_locations_info.pSampleLocations = &sample_location;
VkPipelineSampleLocationsStateCreateInfoEXT sample_locations_state = vku::InitStructHelper();
sample_locations_state.sampleLocationsEnable = VK_TRUE;
sample_locations_state.sampleLocationsInfo = sample_locations_info;
VkPipelineMultisampleStateCreateInfo multi_sample_state = vku::InitStructHelper(&sample_locations_state);
multi_sample_state.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
multi_sample_state.sampleShadingEnable = VK_FALSE;
multi_sample_state.minSampleShading = 1.0;
CreatePipelineHelper pipe(*this);
pipe.gp_ci_.pMultisampleState = &multi_sample_state;
pipe.gp_ci_.renderPass = render_pass;
pipe.CreateGraphicsPipeline();
VkClearValue color_clear_value;
color_clear_value.color.float32[0] = 0.0f;
color_clear_value.color.float32[1] = 0.0f;
color_clear_value.color.float32[2] = 0.0f;
color_clear_value.color.float32[3] = 1.0f;
VkSampleLocationsInfoEXT sample_loc_info = vku::InitStructHelper();
sample_loc_info.sampleLocationsPerPixel = VK_SAMPLE_COUNT_1_BIT;
sample_loc_info.sampleLocationGridSize = {1u, 1u};
sample_loc_info.sampleLocationsCount = 1u;
sample_loc_info.pSampleLocations = &sample_location;
VkAttachmentSampleLocationsEXT attachment_sample_loc;
attachment_sample_loc.attachmentIndex = 0u;
attachment_sample_loc.sampleLocationsInfo = sample_loc_info;
VkSubpassSampleLocationsEXT subpass_sample_loc;
subpass_sample_loc.subpassIndex = 0u;
subpass_sample_loc.sampleLocationsInfo = sample_loc_info;
VkRenderPassSampleLocationsBeginInfoEXT sample_locations_begin_info = vku::InitStructHelper();
sample_locations_begin_info.attachmentInitialSampleLocationsCount = 1u;
sample_locations_begin_info.pAttachmentInitialSampleLocations = &attachment_sample_loc;
sample_locations_begin_info.postSubpassSampleLocationsCount = 1u;
sample_locations_begin_info.pPostSubpassSampleLocations = &subpass_sample_loc;
VkRenderPassBeginInfo render_pass_begin_info = vku::InitStructHelper(&sample_locations_begin_info);
render_pass_begin_info.renderPass = render_pass;
render_pass_begin_info.framebuffer = framebuffer;
render_pass_begin_info.renderArea.extent = {32, 32};
render_pass_begin_info.renderArea.offset = {0, 0};
render_pass_begin_info.clearValueCount = 1;
render_pass_begin_info.pClearValues = &color_clear_value;
m_command_buffer.Begin();
m_command_buffer.BeginRenderPass(render_pass_begin_info);
sample_location.x = 1.0f;
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe);
m_command_buffer.EndRenderPass();
m_command_buffer.End();
}
TEST_F(PositiveRenderPass, MultisampledRenderToSingleSampled) {
TEST_DESCRIPTION("Test VK_EXT_multisampled_render_to_single_sampled");
SetTargetApiVersion(VK_API_VERSION_1_2);
AddRequiredExtensions(VK_EXT_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::imagelessFramebuffer);
AddRequiredFeature(vkt::Feature::multisampledRenderToSingleSampled);
AddRequiredFeature(vkt::Feature::dynamicRendering);
RETURN_IF_SKIP(Init());
InitRenderTarget();
VkAttachmentReference2 attachment_ref = vku::InitStructHelper();
attachment_ref.layout = VK_IMAGE_LAYOUT_GENERAL;
attachment_ref.attachment = 0;
VkMultisampledRenderToSingleSampledInfoEXT ms_render_to_ss = vku::InitStructHelper();
ms_render_to_ss.multisampledRenderToSingleSampledEnable = VK_TRUE;
ms_render_to_ss.rasterizationSamples = VK_SAMPLE_COUNT_2_BIT;
VkSubpassDescription2 subpass = vku::InitStructHelper(&ms_render_to_ss);
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &attachment_ref;
subpass.pDepthStencilAttachment = nullptr;
VkAttachmentDescription2 attach_desc = vku::InitStructHelper();
attach_desc.format = VK_FORMAT_B8G8R8A8_UNORM;
attach_desc.samples = VK_SAMPLE_COUNT_2_BIT;
attach_desc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attach_desc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attach_desc.finalLayout = VK_IMAGE_LAYOUT_GENERAL;
VkRenderPassCreateInfo2 rpci = vku::InitStructHelper();
rpci.subpassCount = 1;
rpci.pSubpasses = &subpass;
rpci.attachmentCount = 1;
rpci.pAttachments = &attach_desc;
vkt::RenderPass test_rp(*m_device, rpci);
VkPipelineMultisampleStateCreateInfo ms_state = vku::InitStructHelper();
ms_state.rasterizationSamples = VK_SAMPLE_COUNT_4_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_helper(*this);
pipe_helper.gp_ci_.renderPass = test_rp;
pipe_helper.ms_ci_ = ms_state;
pipe_helper.ms_ci_.rasterizationSamples = VK_SAMPLE_COUNT_2_BIT;
pipe_helper.CreateGraphicsPipeline();
VkRenderingAttachmentInfo color_attachment = vku::InitStructHelper();
color_attachment.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkRenderingInfo begin_rendering_info = vku::InitStructHelper(&ms_render_to_ss);
begin_rendering_info.layerCount = 1;
begin_rendering_info.colorAttachmentCount = 1;
begin_rendering_info.pColorAttachments = &color_attachment;
begin_rendering_info.renderArea = {{0, 0}, {1, 1}};
{
ms_render_to_ss.rasterizationSamples = VK_SAMPLE_COUNT_4_BIT;
m_command_buffer.Begin();
m_command_buffer.BeginRendering(begin_rendering_info);
// ms_render_to_ss.rasterizationSamples != ms_state.rasterizationSamples
// Valid because never hit draw time
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe_helper);
m_command_buffer.EndRendering();
m_command_buffer.End();
ms_render_to_ss.rasterizationSamples = VK_SAMPLE_COUNT_2_BIT;
}
VkImageFormatProperties2 image_format_prop = vku::InitStructHelper();
VkPhysicalDeviceImageFormatInfo2 image_format_info = vku::InitStructHelper();
image_format_info.tiling = VK_IMAGE_TILING_OPTIMAL;
image_format_info.type = VK_IMAGE_TYPE_2D;
image_format_info.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
image_format_info.format = VK_FORMAT_B8G8R8A8_UNORM;
VkResult result = vk::GetPhysicalDeviceImageFormatProperties2(m_device->Physical(), &image_format_info, &image_format_prop);
if ((result != VK_SUCCESS) || !(image_format_prop.imageFormatProperties.sampleCounts & VK_SAMPLE_COUNT_2_BIT)) {
GTEST_SKIP() << "Cannot create an image with format VK_FORMAT_B8G8R8A8_UNORM and sample count VK_SAMPLE_COUNT_2_BIT. "
"Skipping remainder of the test";
}
VkImageCreateInfo image_create_info = vku::InitStructHelper();
image_create_info.flags = 0;
image_create_info.imageType = VK_IMAGE_TYPE_2D;
image_create_info.format = VK_FORMAT_B8G8R8A8_UNORM;
image_create_info.extent = {64, 64, 1};
image_create_info.mipLevels = 1;
image_create_info.arrayLayers = 1;
image_create_info.samples = VK_SAMPLE_COUNT_2_BIT;
image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
image_create_info.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
vkt::Image two_count_image(*m_device, image_create_info, vkt::set_layout);
vkt::ImageView two_count_image_view(*m_device, two_count_image.BasicViewCreatInfo());
image_create_info.flags = VK_IMAGE_CREATE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_BIT_EXT;
image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
vkt::Image good_one_count_image(*m_device, image_create_info, vkt::set_layout);
auto one_count_image_view_ci = good_one_count_image.BasicViewCreatInfo();
vkt::ImageView one_count_image_view(*m_device, one_count_image_view_ci);
// Image view with VK_SAMPLE_COUNT_1_BIT should not get error 07285 in pipeline created with attachment with
// VK_SAMPLE_COUNT_2_BIT
VkPipelineRenderingCreateInfo rendering_ci = vku::InitStructHelper();
rendering_ci.colorAttachmentCount = 1;
rendering_ci.pColorAttachmentFormats = &image_create_info.format;
CreatePipelineHelper dr_pipe_helper(*this, &rendering_ci);
dr_pipe_helper.gp_ci_.renderPass = VK_NULL_HANDLE;
dr_pipe_helper.ms_ci_ = ms_state;
dr_pipe_helper.CreateGraphicsPipeline();
color_attachment.resolveImageLayout = VK_IMAGE_LAYOUT_GENERAL;
color_attachment.imageView = one_count_image_view;
color_attachment.resolveImageView = VK_NULL_HANDLE;
color_attachment.resolveMode = VK_RESOLVE_MODE_NONE;
begin_rendering_info.pNext = nullptr;
m_command_buffer.Begin();
m_command_buffer.BeginRendering(begin_rendering_info);
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, dr_pipe_helper);
vk::CmdDraw(m_command_buffer, 1, 1, 0, 0);
m_command_buffer.EndRendering();
// Same as previous test but using render pass and should not get error 08644
color_attachment.resolveMode = VK_RESOLVE_MODE_AVERAGE_BIT;
CreatePipelineHelper test_pipe(*this);
test_pipe.ms_ci_ = ms_state;
test_pipe.CreateGraphicsPipeline();
m_command_buffer.BeginRenderPass(m_renderPassBeginInfo);
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, test_pipe);
vk::CmdDraw(m_command_buffer, 1, 1, 0, 0);
m_command_buffer.EndRenderPass();
}