blob: 10cfabad071a2cffd9116dce349353cb415614c3 [file]
/*
* Copyright (c) 2015-2026 The Khronos Group Inc.
* Copyright (c) 2015-2026 Valve Corporation
* Copyright (c) 2015-2026 LunarG, Inc.
* Copyright (c) 2015-2026 Google, Inc.
* Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*/
#include "../framework/layer_validation_tests.h"
#include "../framework/pipeline_helper.h"
class PositiveGeometryTessellation : public VkLayerTest {};
TEST_F(PositiveGeometryTessellation, PointSizeGeomShaderDontWriteMaintenance5) {
TEST_DESCRIPTION(
"Create a pipeline using TOPOLOGY_POINT_LIST, set PointSize vertex shader, but not in the final geometry stage, but have "
"maintenance5.");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredExtensions(VK_KHR_MAINTENANCE_5_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::maintenance5);
AddRequiredFeature(vkt::Feature::geometryShader);
AddRequiredFeature(vkt::Feature::shaderTessellationAndGeometryPointSize);
RETURN_IF_SKIP(Init());
InitRenderTarget();
// Create GS declaring PointSize and writing to it
const char* gsSource = R"glsl(
#version 450
layout (points) in;
layout (points) out;
layout (max_vertices = 1) out;
void main() {
gl_Position = vec4(1.0, 0.5, 0.5, 0.0);
EmitVertex();
}
)glsl";
VkShaderObj vs(*m_device, kVertexPointSizeGlsl, VK_SHADER_STAGE_VERTEX_BIT);
VkShaderObj gs(*m_device, gsSource, VK_SHADER_STAGE_GEOMETRY_BIT);
auto set_info = [&](CreatePipelineHelper& helper) {
helper.ia_ci_.topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
helper.shader_stages_ = {vs.GetStageCreateInfo(), gs.GetStageCreateInfo(), helper.fs_->GetStageCreateInfo()};
};
CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit);
}
TEST_F(PositiveGeometryTessellation, IncompatibleDynamicPrimitiveTopology) {
TEST_DESCRIPTION(
"Create pipeline with primitive topology incompatible with shaders, but use VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY.");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredExtensions(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::extendedDynamicState);
AddRequiredFeature(vkt::Feature::geometryShader);
AddRequiredFeature(vkt::Feature::shaderTessellationAndGeometryPointSize);
RETURN_IF_SKIP(Init());
InitRenderTarget();
const char* gsSource = R"glsl(
#version 450
layout (points) in;
layout (triangle_strip) out;
layout (max_vertices = 3) out;
in gl_PerVertex
{
vec4 gl_Position;
float gl_PointSize;
} gl_in[];
void main()
{
gl_Position = gl_in[0].gl_Position;
gl_PointSize = gl_in[0].gl_PointSize;
EmitVertex();
}
)glsl";
VkShaderObj vs(*m_device, kVertexPointSizeGlsl, VK_SHADER_STAGE_VERTEX_BIT);
VkShaderObj gs(*m_device, gsSource, VK_SHADER_STAGE_GEOMETRY_BIT);
CreatePipelineHelper pipe(*this);
pipe.ia_ci_.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
pipe.AddDynamicState(VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY);
pipe.shader_stages_ = {vs.GetStageCreateInfo(), gs.GetStageCreateInfo(), pipe.fs_->GetStageCreateInfo()};
pipe.CreateGraphicsPipeline();
}
TEST_F(PositiveGeometryTessellation, DrawDynamicPrimitiveTopology) {
TEST_DESCRIPTION("https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/8319");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredExtensions(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::extendedDynamicState);
AddRequiredFeature(vkt::Feature::geometryShader);
AddRequiredFeature(vkt::Feature::shaderTessellationAndGeometryPointSize);
RETURN_IF_SKIP(Init());
InitRenderTarget();
const char* gsSource = R"glsl(
#version 450
layout (points) in;
layout (triangle_strip) out;
layout (max_vertices = 3) out;
in gl_PerVertex
{
vec4 gl_Position;
float gl_PointSize;
} gl_in[];
void main()
{
gl_Position = gl_in[0].gl_Position;
gl_PointSize = gl_in[0].gl_PointSize;
EmitVertex();
}
)glsl";
VkShaderObj vs(*m_device, kVertexPointSizeGlsl, VK_SHADER_STAGE_VERTEX_BIT);
VkShaderObj gs(*m_device, gsSource, VK_SHADER_STAGE_GEOMETRY_BIT);
CreatePipelineHelper pipe(*this);
pipe.ia_ci_.topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
pipe.AddDynamicState(VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY);
pipe.shader_stages_ = {vs.GetStageCreateInfo(), gs.GetStageCreateInfo(), pipe.fs_->GetStageCreateInfo()};
pipe.CreateGraphicsPipeline();
m_command_buffer.Begin();
m_command_buffer.BeginRenderPass(m_renderPassBeginInfo);
vk::CmdSetPrimitiveTopologyEXT(m_command_buffer, VK_PRIMITIVE_TOPOLOGY_POINT_LIST);
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe);
vk::CmdDraw(m_command_buffer, 3, 1, 0, 0);
m_command_buffer.EndRenderPass();
m_command_buffer.End();
}
TEST_F(PositiveGeometryTessellation, TessellationPointMode) {
TEST_DESCRIPTION("Create pipeline with tessellation evaluation shader using point mode");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredFeature(vkt::Feature::geometryShader);
AddRequiredFeature(vkt::Feature::tessellationShader);
AddRequiredFeature(vkt::Feature::shaderTessellationAndGeometryPointSize);
RETURN_IF_SKIP(Init());
InitRenderTarget();
const char tess_src[] = R"glsl(
#version 460
layout(triangles, equal_spacing, cw, point_mode) in;
void main() { gl_Position = vec4(1); }
)glsl";
const char geom_src[] = R"glsl(
#version 450
layout (points) in;
layout (points) out;
layout (max_vertices = 1) out;
void main() {
gl_Position = vec4(1.0f, 0.5f, 0.5f, 0.0f);
gl_PointSize = 1.0f;
EmitVertex();
}
)glsl";
VkShaderObj tcs(*m_device, kTessellationControlMinimalGlsl, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT);
VkShaderObj tes(*m_device, tess_src, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT);
VkShaderObj gs(*m_device, geom_src, VK_SHADER_STAGE_GEOMETRY_BIT);
VkPipelineTessellationStateCreateInfo tess_ci = vku::InitStructHelper();
tess_ci.patchControlPoints = 4u;
CreatePipelineHelper pipe(*this);
pipe.shader_stages_ = {pipe.vs_->GetStageCreateInfo(), tcs.GetStageCreateInfo(), tes.GetStageCreateInfo(),
gs.GetStageCreateInfo(), pipe.fs_->GetStageCreateInfo()};
pipe.ia_ci_.topology = VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
pipe.tess_ci_ = tess_ci;
pipe.CreateGraphicsPipeline();
}
TEST_F(PositiveGeometryTessellation, InterfaceComponents) {
TEST_DESCRIPTION("https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/8443");
AddRequiredFeature(vkt::Feature::geometryShader);
RETURN_IF_SKIP(Init());
InitRenderTarget();
const char* vs_source = R"glsl(
#version 450
layout(location = 0) out ivec4 a;
void main() {
a = ivec4(1);
}
)glsl";
const char* geom_source = R"glsl(
#version 450
layout(triangles) in;
layout(triangle_strip) out;
layout(location = 0) in ivec4 a[3];
layout(location = 0) out vec4 b;
layout(max_vertices = 3) out;
void main() {
b = vec4(a[0]);
gl_Position = a[0];
EmitVertex();
b = vec4(a[1]);
gl_Position = a[1];
EmitVertex();
b = vec4(a[2]);
gl_Position = a[2];
EmitVertex();
EndPrimitive();
}
)glsl";
const char* fs_source = R"glsl(
#version 450
layout(location = 0) in vec4 b;
layout(location = 0) out vec4 c;
void main() {
c = b;
}
)glsl";
VkShaderObj vert(*m_device, vs_source, VK_SHADER_STAGE_VERTEX_BIT);
VkShaderObj geom(*m_device, geom_source, VK_SHADER_STAGE_GEOMETRY_BIT);
VkShaderObj frag(*m_device, fs_source, VK_SHADER_STAGE_FRAGMENT_BIT);
CreatePipelineHelper pipe(*this);
pipe.shader_stages_ = {vert.GetStageCreateInfo(), frag.GetStageCreateInfo(), geom.GetStageCreateInfo()};
pipe.CreateGraphicsPipeline();
}
TEST_F(PositiveGeometryTessellation, TessGeomPointPrimitiveTopology) {
TEST_DESCRIPTION("https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/9821");
AddRequiredFeature(vkt::Feature::geometryShader);
AddRequiredFeature(vkt::Feature::shaderTessellationAndGeometryPointSize);
AddRequiredFeature(vkt::Feature::tessellationShader);
RETURN_IF_SKIP(Init());
InitRenderTarget();
const char* tcsSource = R"asm(
OpCapability Tessellation
%2 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint TessellationControl %main "main" %x
OpExecutionMode %main OutputVertices 3
OpExecutionMode %main Triangles
OpExecutionMode %main SpacingEqual
OpExecutionMode %main PointMode
OpDecorate %x Location 0
%void = OpTypeVoid
%4 = OpTypeFunction %void
%int = OpTypeInt 32 1
%uint = OpTypeInt 32 0
%uint_3 = OpConstant %uint 3
%_arr_int_uint_3 = OpTypeArray %int %uint_3
%_ptr_Output__arr_int_uint_3 = OpTypePointer Output %_arr_int_uint_3
%x = OpVariable %_ptr_Output__arr_int_uint_3 Output
%main = OpFunction %void None %4
%6 = OpLabel
OpReturn
OpFunctionEnd
)asm";
const char* tesSource = R"glsl(
#version 450
layout(triangles, equal_spacing, cw) in;
layout(location=0) patch in int x;
void main(){
gl_Position.xyz = gl_TessCoord;
gl_Position.w = x;
}
)glsl";
const char* gsSource = R"glsl(
#version 450
layout (points) in;
layout (triangle_strip) out;
layout (max_vertices = 3) out;
void main()
{
gl_Position = gl_in[0].gl_Position;
gl_PointSize = gl_in[0].gl_PointSize;
EmitVertex();
}
)glsl";
VkShaderObj vs(*m_device, kVertexPointSizeGlsl, VK_SHADER_STAGE_VERTEX_BIT);
VkShaderObj tcs(*m_device, tcsSource, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, SPV_ENV_VULKAN_1_0, SPV_SOURCE_ASM);
VkShaderObj tes(*m_device, tesSource, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT);
VkShaderObj gs(*m_device, gsSource, VK_SHADER_STAGE_GEOMETRY_BIT);
VkPipelineTessellationStateCreateInfo tsci{VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO, nullptr, 0, 3};
CreatePipelineHelper pipe(*this);
pipe.ia_ci_.topology = VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
pipe.gp_ci_.pTessellationState = &tsci;
pipe.shader_stages_ = {vs.GetStageCreateInfo(), gs.GetStageCreateInfo(), tcs.GetStageCreateInfo(), tes.GetStageCreateInfo(),
pipe.fs_->GetStageCreateInfo()};
pipe.CreateGraphicsPipeline();
}