blob: e1a598a67c425cf3138d9482c1f799ef5cbf7759 [file] [log] [blame]
/*
* Copyright (c) 2015-2023 The Khronos Group Inc.
* Copyright (c) 2015-2023 Valve Corporation
* Copyright (c) 2015-2023 LunarG, Inc.
* Copyright (c) 2015-2023 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 "../framework/layer_validation_tests.h"
#include "../framework/pipeline_helper.h"
TEST_F(PositivePipelineTopology, PointSizeWriteInFunction) {
TEST_DESCRIPTION("Create a pipeline using TOPOLOGY_POINT_LIST and write PointSize in vertex shader function.");
RETURN_IF_SKIP(Init())
InitRenderTarget();
// Create VS declaring PointSize and write to it in a function call.
VkShaderObj vs(this, kVertexPointSizeGlsl, VK_SHADER_STAGE_VERTEX_BIT);
VkShaderObj ps(this, kFragmentMinimalGlsl, VK_SHADER_STAGE_FRAGMENT_BIT);
{
CreatePipelineHelper pipe(*this);
pipe.shader_stages_ = {vs.GetStageCreateInfo(), ps.GetStageCreateInfo()};
pipe.ia_ci_.topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
pipe.InitState();
pipe.CreateGraphicsPipeline();
}
}
TEST_F(PositivePipelineTopology, PointSizeGeomShaderSuccess) {
TEST_DESCRIPTION(
"Create a pipeline using TOPOLOGY_POINT_LIST, set PointSize vertex shader, and write in the final geometry stage.");
RETURN_IF_SKIP(Init())
if ((!m_device->phy().features().geometryShader) || (!m_device->phy().features().shaderTessellationAndGeometryPointSize)) {
GTEST_SKIP() << "Device does not support the required geometry shader features";
}
InitRenderTarget();
// Create VS declaring PointSize and writing to it
VkShaderObj vs(this, kVertexPointSizeGlsl, VK_SHADER_STAGE_VERTEX_BIT);
VkShaderObj gs(this, kGeometryPointSizeGlsl, VK_SHADER_STAGE_GEOMETRY_BIT);
VkShaderObj ps(this, kFragmentMinimalGlsl, VK_SHADER_STAGE_FRAGMENT_BIT);
CreatePipelineHelper pipe(*this);
pipe.shader_stages_ = {vs.GetStageCreateInfo(), gs.GetStageCreateInfo(), ps.GetStageCreateInfo()};
// Set Input Assembly to TOPOLOGY POINT LIST
pipe.ia_ci_.topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
pipe.InitState();
pipe.CreateGraphicsPipeline();
}
TEST_F(PositivePipelineTopology, PointSizeGeomShaderDontEmit) {
TEST_DESCRIPTION("If vertex is not emitted, don't need Point Size in Geometry shader");
RETURN_IF_SKIP(Init())
if ((!m_device->phy().features().geometryShader) || (!m_device->phy().features().shaderTessellationAndGeometryPointSize)) {
GTEST_SKIP() << "Device does not support the required geometry shader features";
}
InitRenderTarget();
// Never calls OpEmitVertex
static char const *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);
}
)glsl";
VkShaderObj vs(this, kVertexPointSizeGlsl, VK_SHADER_STAGE_VERTEX_BIT);
VkShaderObj gs(this, 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(VkPositiveLayerTest, LoosePointSizeWrite) {
TEST_DESCRIPTION("Create a pipeline using TOPOLOGY_POINT_LIST and write PointSize outside of a structure.");
RETURN_IF_SKIP(Init())
InitRenderTarget();
const char *LoosePointSizeWrite = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main" %glposition %glpointsize %gl_VertexIndex
OpSource GLSL 450
OpName %main "main"
OpName %vertices "vertices"
OpName %glposition "glposition"
OpName %glpointsize "glpointsize"
OpName %gl_VertexIndex "gl_VertexIndex"
OpDecorate %glposition BuiltIn Position
OpDecorate %glpointsize BuiltIn PointSize
OpDecorate %gl_VertexIndex BuiltIn VertexIndex
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%v2float = OpTypeVector %float 2
%uint = OpTypeInt 32 0
%uint_3 = OpConstant %uint 3
%_arr_v2float_uint_3 = OpTypeArray %v2float %uint_3
%_ptr_Private__arr_v2float_uint_3 = OpTypePointer Private %_arr_v2float_uint_3
%vertices = OpVariable %_ptr_Private__arr_v2float_uint_3 Private
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%float_n1 = OpConstant %float -1
%16 = OpConstantComposite %v2float %float_n1 %float_n1
%_ptr_Private_v2float = OpTypePointer Private %v2float
%int_1 = OpConstant %int 1
%float_1 = OpConstant %float 1
%21 = OpConstantComposite %v2float %float_1 %float_n1
%int_2 = OpConstant %int 2
%float_0 = OpConstant %float 0
%25 = OpConstantComposite %v2float %float_0 %float_1
%v4float = OpTypeVector %float 4
%_ptr_Output_gl_Position = OpTypePointer Output %v4float
%glposition = OpVariable %_ptr_Output_gl_Position Output
%_ptr_Output_gl_PointSize = OpTypePointer Output %float
%glpointsize = OpVariable %_ptr_Output_gl_PointSize Output
%_ptr_Input_int = OpTypePointer Input %int
%gl_VertexIndex = OpVariable %_ptr_Input_int Input
%int_3 = OpConstant %int 3
%_ptr_Output_v4float = OpTypePointer Output %v4float
%_ptr_Output_float = OpTypePointer Output %float
%main = OpFunction %void None %3
%5 = OpLabel
%18 = OpAccessChain %_ptr_Private_v2float %vertices %int_0
OpStore %18 %16
%22 = OpAccessChain %_ptr_Private_v2float %vertices %int_1
OpStore %22 %21
%26 = OpAccessChain %_ptr_Private_v2float %vertices %int_2
OpStore %26 %25
%33 = OpLoad %int %gl_VertexIndex
%35 = OpSMod %int %33 %int_3
%36 = OpAccessChain %_ptr_Private_v2float %vertices %35
%37 = OpLoad %v2float %36
%38 = OpCompositeExtract %float %37 0
%39 = OpCompositeExtract %float %37 1
%40 = OpCompositeConstruct %v4float %38 %39 %float_0 %float_1
%42 = OpAccessChain %_ptr_Output_v4float %glposition
OpStore %42 %40
OpStore %glpointsize %float_1
OpReturn
OpFunctionEnd
)";
// Create VS declaring PointSize and write to it in a function call.
VkShaderObj vs(this, LoosePointSizeWrite, VK_SHADER_STAGE_VERTEX_BIT, SPV_ENV_VULKAN_1_0, SPV_SOURCE_ASM);
VkShaderObj ps(this, kFragmentMinimalGlsl, VK_SHADER_STAGE_FRAGMENT_BIT);
{
CreatePipelineHelper pipe(*this);
pipe.shader_stages_ = {vs.GetStageCreateInfo(), ps.GetStageCreateInfo()};
// Set Input Assembly to TOPOLOGY POINT LIST
pipe.ia_ci_.topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
pipe.InitState();
pipe.CreateGraphicsPipeline();
}
}
TEST_F(PositivePipelineTopology, PointSizeStructMemeberWritten) {
TEST_DESCRIPTION("Write built-in PointSize within a struct");
SetTargetApiVersion(VK_API_VERSION_1_1); // At least 1.1 is required for maintenance4
AddRequiredExtensions(VK_KHR_MAINTENANCE_4_EXTENSION_NAME);
RETURN_IF_SKIP(InitFramework())
VkPhysicalDeviceMaintenance4FeaturesKHR maint4features = vku::InitStructHelper();
GetPhysicalDeviceFeatures2(maint4features);
RETURN_IF_SKIP(InitState(nullptr, &maint4features));
InitRenderTarget();
const std::string vs_src = R"asm(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main" %14 %25 %47 %52
OpSource GLSL 450
OpMemberDecorate %12 0 BuiltIn Position
OpMemberDecorate %12 1 BuiltIn PointSize
OpMemberDecorate %12 2 BuiltIn ClipDistance
OpMemberDecorate %12 3 BuiltIn CullDistance
OpDecorate %12 Block
OpMemberDecorate %18 0 ColMajor
OpMemberDecorate %18 0 Offset 0
OpMemberDecorate %18 0 MatrixStride 16
OpMemberDecorate %18 1 Offset 64
OpMemberDecorate %18 2 Offset 80
OpDecorate %18 Block
OpDecorate %25 Location 0
OpDecorate %47 Location 1
OpDecorate %52 Location 0
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%7 = OpTypeFloat 32
%8 = OpTypeVector %7 4
%9 = OpTypeInt 32 0
%10 = OpConstant %9 1
%11 = OpTypeArray %7 %10
%12 = OpTypeStruct %8 %7 %11 %11
%13 = OpTypePointer Output %12
%14 = OpVariable %13 Output
%15 = OpTypeInt 32 1
%16 = OpConstant %15 0
%17 = OpTypeMatrix %8 4
%18 = OpTypeStruct %17 %7 %8
%19 = OpTypePointer PushConstant %18
%20 = OpVariable %19 PushConstant
%21 = OpTypePointer PushConstant %17
%24 = OpTypePointer Input %8
%25 = OpVariable %24 Input
%28 = OpTypePointer Output %8
%30 = OpConstant %7 0.5
%31 = OpConstant %9 2
%32 = OpTypePointer Output %7
%36 = OpConstant %9 3
%46 = OpConstant %15 1
%47 = OpVariable %24 Input
%48 = OpTypePointer Input %7
%52 = OpVariable %28 Output
%53 = OpTypeVector %7 3
%56 = OpConstant %7 1
%main = OpFunction %3 None %4
%6 = OpLabel
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; For the following, only the _first_ index of the access chain
; should be used for output validation, as subsequent indices refer
; to individual components within the output variable of interest.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
%22 = OpAccessChain %21 %20 %16
%23 = OpLoad %17 %22
%26 = OpLoad %8 %25
%27 = OpMatrixTimesVector %8 %23 %26
%29 = OpAccessChain %28 %14 %16
OpStore %29 %27
%33 = OpAccessChain %32 %14 %16 %31
%34 = OpLoad %7 %33
%35 = OpFMul %7 %30 %34
%37 = OpAccessChain %32 %14 %16 %36
%38 = OpLoad %7 %37
%39 = OpFMul %7 %30 %38
%40 = OpFAdd %7 %35 %39
%41 = OpAccessChain %32 %14 %16 %31
OpStore %41 %40
%42 = OpAccessChain %32 %14 %16 %10
%43 = OpLoad %7 %42
%44 = OpFNegate %7 %43
%45 = OpAccessChain %32 %14 %16 %10
OpStore %45 %44
%49 = OpAccessChain %48 %47 %36
%50 = OpLoad %7 %49
%51 = OpAccessChain %32 %14 %46
OpStore %51 %50
%54 = OpLoad %8 %47
%55 = OpVectorShuffle %53 %54 %54 0 1 2
%57 = OpCompositeExtract %7 %55 0
%58 = OpCompositeExtract %7 %55 1
%59 = OpCompositeExtract %7 %55 2
%60 = OpCompositeConstruct %8 %57 %58 %59 %56
OpStore %52 %60
OpReturn
OpFunctionEnd
)asm";
auto vs = VkShaderObj::CreateFromASM(this, vs_src.c_str(), VK_SHADER_STAGE_VERTEX_BIT);
if (vs) {
// struct has {
// mat4x4
// float
// vec4
// }
// but std140 padding so the vec4 is offset 80
VkPushConstantRange push_constant_ranges[1]{{VK_SHADER_STAGE_VERTEX_BIT, 0, 96}};
VkPipelineLayoutCreateInfo const pipeline_layout_info{
VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0, 0, nullptr, 1, push_constant_ranges};
VkVertexInputBindingDescription input_binding[2] = {
{0, 16, VK_VERTEX_INPUT_RATE_VERTEX},
{1, 16, VK_VERTEX_INPUT_RATE_VERTEX},
};
VkVertexInputAttributeDescription input_attribs[2] = {
{0, 0, VK_FORMAT_R32G32B32A32_SFLOAT, 0},
{1, 1, VK_FORMAT_R32G32B32A32_SFLOAT, 0},
};
CreatePipelineHelper pipe(*this);
pipe.shader_stages_ = {vs->GetStageCreateInfo(), pipe.fs_->GetStageCreateInfo()};
pipe.pipeline_layout_ci_ = pipeline_layout_info;
pipe.ia_ci_.topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
pipe.vi_ci_.pVertexBindingDescriptions = input_binding;
pipe.vi_ci_.vertexBindingDescriptionCount = 2;
pipe.vi_ci_.pVertexAttributeDescriptions = input_attribs;
pipe.vi_ci_.vertexAttributeDescriptionCount = 2;
pipe.InitState();
pipe.CreateGraphicsPipeline();
} else {
printf("Error creating shader from assembly\n");
}
}
TEST_F(VkPositiveLayerTest, PSOPolygonModeValid) {
TEST_DESCRIPTION("Verify that using a solid polygon fill mode works correctly.");
RETURN_IF_SKIP(Init())
InitRenderTarget();
std::vector<const char *> device_extension_names;
auto features = m_device->phy().features();
// Artificially disable support for non-solid fill modes
features.fillModeNonSolid = false;
// The sacrificial device object
vkt::Device test_device(gpu(), device_extension_names, &features);
VkAttachmentReference attach = {};
attach.layout = VK_IMAGE_LAYOUT_GENERAL;
VkSubpassDescription subpass = {};
subpass.pColorAttachments = &attach;
subpass.colorAttachmentCount = 1;
VkAttachmentDescription attach_desc = {};
attach_desc.format = VK_FORMAT_B8G8R8A8_UNORM;
attach_desc.samples = VK_SAMPLE_COUNT_1_BIT;
attach_desc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attach_desc.finalLayout = VK_IMAGE_LAYOUT_GENERAL;
attach_desc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attach_desc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
VkRenderPassCreateInfo rpci = vku::InitStructHelper();
rpci.subpassCount = 1;
rpci.pSubpasses = &subpass;
rpci.attachmentCount = 1;
rpci.pAttachments = &attach_desc;
vkt::RenderPass render_pass(test_device, rpci);
const vkt::PipelineLayout pipeline_layout(test_device);
VkPipelineRasterizationStateCreateInfo rs_ci = vku::InitStructHelper();
rs_ci.lineWidth = 1.0f;
rs_ci.rasterizerDiscardEnable = false;
VkShaderObj vs(this, kVertexMinimalGlsl, VK_SHADER_STAGE_VERTEX_BIT, SPV_ENV_VULKAN_1_0, SPV_SOURCE_GLSL_TRY);
VkShaderObj fs(this, kFragmentMinimalGlsl, VK_SHADER_STAGE_FRAGMENT_BIT, SPV_ENV_VULKAN_1_0, SPV_SOURCE_GLSL_TRY);
vs.InitFromGLSLTry(false, &test_device);
fs.InitFromGLSLTry(false, &test_device);
// Set polygonMode=FILL. No error is expected
{
CreatePipelineHelper pipe(*this);
pipe.device_ = &test_device;
pipe.InitState();
pipe.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()};
pipe.gp_ci_.layout = pipeline_layout.handle();
pipe.gp_ci_.renderPass = render_pass.handle();
// Set polygonMode to a good value
rs_ci.polygonMode = VK_POLYGON_MODE_FILL;
pipe.gp_ci_.pRasterizationState = &rs_ci;
pipe.CreateGraphicsPipeline();
}
}
TEST_F(PositivePipelineTopology, NotPointSizeGeometry) {
TEST_DESCRIPTION("Create a pipeline using TOPOLOGY_POINT_LIST, but geometry shader doesn't include PointSize.");
RETURN_IF_SKIP(Init())
if ((!m_device->phy().features().geometryShader)) {
GTEST_SKIP() << "Device does not support the required geometry shader features";
}
InitRenderTarget();
static char const geom_src[] = R"glsl(
#version 460
layout(points) in;
layout(triangle_strip, max_vertices=3) out;
void main() {
gl_Position = vec4(1);
EmitVertex();
}
)glsl";
VkShaderObj gs(this, geom_src, VK_SHADER_STAGE_GEOMETRY_BIT);
CreatePipelineHelper pipe(*this);
pipe.shader_stages_ = {pipe.vs_->GetStageCreateInfo(), gs.GetStageCreateInfo(), pipe.fs_->GetStageCreateInfo()};
pipe.ia_ci_.topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
pipe.InitState();
pipe.CreateGraphicsPipeline();
}
TEST_F(VkPositiveLayerTest, TopologyAtRasterizer) {
TEST_DESCRIPTION("Test topology set when creating a pipeline with tessellation and geometry shader.");
RETURN_IF_SKIP(Init())
InitRenderTarget();
if (!m_device->phy().features().tessellationShader) {
GTEST_SKIP() << "Device does not support tessellation shaders";
}
char const *tcsSource = R"glsl(
#version 450
layout(vertices = 3) out;
void main(){
gl_TessLevelOuter[0] = gl_TessLevelOuter[1] = gl_TessLevelOuter[2] = 1;
gl_TessLevelInner[0] = 1;
}
)glsl";
char const *tesSource = R"glsl(
#version 450
layout(triangles, equal_spacing, cw) in;
void main(){
gl_Position.xyz = gl_TessCoord;
gl_Position.w = 1.0f;
}
)glsl";
static char const *gsSource = R"glsl(
#version 450
layout (triangles) in;
layout (triangle_strip) out;
layout (max_vertices = 1) out;
void main() {
gl_Position = vec4(1.0, 0.5, 0.5, 0.0);
EmitVertex();
}
)glsl";
VkShaderObj tcs(this, tcsSource, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT);
VkShaderObj tes(this, tesSource, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT);
VkShaderObj gs(this, gsSource, VK_SHADER_STAGE_GEOMETRY_BIT);
VkPipelineInputAssemblyStateCreateInfo iasci{VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, nullptr, 0,
VK_PRIMITIVE_TOPOLOGY_PATCH_LIST, VK_FALSE};
VkPipelineTessellationStateCreateInfo tsci{VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO, nullptr, 0, 3};
CreatePipelineHelper pipe(*this);
pipe.gp_ci_.pTessellationState = &tsci;
pipe.gp_ci_.pInputAssemblyState = &iasci;
pipe.shader_stages_.emplace_back(gs.GetStageCreateInfo());
pipe.shader_stages_.emplace_back(tcs.GetStageCreateInfo());
pipe.shader_stages_.emplace_back(tes.GetStageCreateInfo());
pipe.InitState();
pipe.AddDynamicState(VK_DYNAMIC_STATE_LINE_WIDTH);
pipe.CreateGraphicsPipeline();
VkRenderPassBeginInfo rpbi = vku::InitStructHelper();
rpbi.renderPass = m_renderPass;
rpbi.framebuffer = m_framebuffer;
rpbi.renderArea.offset.x = 0;
rpbi.renderArea.offset.y = 0;
rpbi.renderArea.extent.width = 32;
rpbi.renderArea.extent.height = 32;
rpbi.clearValueCount = static_cast<uint32_t>(m_renderPassClearValues.size());
rpbi.pClearValues = m_renderPassClearValues.data();
m_commandBuffer->begin();
vk::CmdBeginRenderPass(m_commandBuffer->handle(), &rpbi, VK_SUBPASS_CONTENTS_INLINE);
vk::CmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.pipeline_);
vk::CmdDraw(m_commandBuffer->handle(), 4, 1, 0, 0);
m_commandBuffer->EndRenderPass();
m_commandBuffer->end();
}
TEST_F(PositivePipelineTopology, LineTopologyClasses) {
TEST_DESCRIPTION("Check different line topologies within the same topology class");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredExtensions(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
RETURN_IF_SKIP(InitFramework())
VkPhysicalDeviceExtendedDynamicStateFeaturesEXT extended_dynamic_state_features = vku::InitStructHelper();
GetPhysicalDeviceFeatures2(extended_dynamic_state_features);
if (!extended_dynamic_state_features.extendedDynamicState) {
GTEST_SKIP() << "Test requires (unsupported) extendedDynamicState";
}
RETURN_IF_SKIP(InitState(nullptr, &extended_dynamic_state_features));
InitRenderTarget();
// Verify each vkCmdSet command
CreatePipelineHelper pipe(*this);
pipe.AddDynamicState(VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT);
pipe.vi_ci_.vertexBindingDescriptionCount = 1;
VkVertexInputBindingDescription inputBinding = {0, sizeof(float), VK_VERTEX_INPUT_RATE_VERTEX};
pipe.vi_ci_.pVertexBindingDescriptions = &inputBinding;
pipe.vi_ci_.vertexAttributeDescriptionCount = 1;
VkVertexInputAttributeDescription attribute = {0, 0, VK_FORMAT_R32_SFLOAT, 0};
pipe.vi_ci_.pVertexAttributeDescriptions = &attribute;
pipe.ia_ci_.topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
pipe.InitState();
pipe.CreateGraphicsPipeline();
vkt::Buffer vbo(*m_device, sizeof(float) * 3, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
vkt::CommandBuffer cb(m_device, m_commandPool);
cb.begin();
cb.BeginRenderPass(m_renderPassBeginInfo);
vk::CmdBindPipeline(cb.handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.pipeline_);
vk::CmdBindVertexBuffers(cb.handle(), 0, 1, &vbo.handle(), &kZeroDeviceSize);
vk::CmdSetPrimitiveTopologyEXT(cb.handle(), VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY);
vk::CmdDraw(cb.handle(), 1, 1, 0, 0);
cb.EndRenderPass();
cb.end();
}
TEST_F(PositivePipelineTopology, PointSizeDynamicAndUnestricted) {
TEST_DESCRIPTION(
"Create a pipeline using TOPOLOGY_POINT_LIST but do not set PointSize in vertex shader, but it is valid with both dynamic "
"state and dynamicPrimitiveTopologyUnrestricted is true.");
AddRequiredExtensions(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
AddRequiredExtensions(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
RETURN_IF_SKIP(InitFramework())
VkPhysicalDeviceExtendedDynamicStateFeaturesEXT extended_dynamic_state_features = vku::InitStructHelper();
GetPhysicalDeviceFeatures2(extended_dynamic_state_features);
if (!extended_dynamic_state_features.extendedDynamicState) {
GTEST_SKIP() << "Test requires (unsupported) extendedDynamicState";
}
RETURN_IF_SKIP(InitState(nullptr, &extended_dynamic_state_features));
InitRenderTarget();
VkPhysicalDeviceExtendedDynamicState3PropertiesEXT dynamic_state_3_props = vku::InitStructHelper();
GetPhysicalDeviceProperties2(dynamic_state_3_props);
if (!dynamic_state_3_props.dynamicPrimitiveTopologyUnrestricted) {
GTEST_SKIP() << "dynamicPrimitiveTopologyUnrestricted is VK_TRUE";
}
static const char *vs_source = R"glsl(
#version 450
void main() {
gl_PointSize = 1.0;
}
)glsl";
VkShaderObj vs(this, vs_source, VK_SHADER_STAGE_VERTEX_BIT);
const VkDynamicState dyn_state = VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY;
VkPipelineDynamicStateCreateInfo dyn_state_ci = vku::InitStructHelper();
dyn_state_ci.dynamicStateCount = 1;
dyn_state_ci.pDynamicStates = &dyn_state;
auto set_info = [&](CreatePipelineHelper &helper) {
helper.ia_ci_.topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
helper.dyn_state_ci_ = dyn_state_ci;
helper.shader_stages_ = {vs.GetStageCreateInfo(), helper.fs_->GetStageCreateInfo()};
};
CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit);
}
TEST_F(PositivePipelineTopology, PointSizeMaintenance5) {
TEST_DESCRIPTION(
"Create a pipeline using TOPOLOGY_POINT_LIST but do not set PointSize in vertex shader, but have maintenance5.");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredExtensions(VK_KHR_MAINTENANCE_5_EXTENSION_NAME);
RETURN_IF_SKIP(InitFramework())
VkPhysicalDeviceMaintenance5FeaturesKHR maintenance5_features = vku::InitStructHelper();
GetPhysicalDeviceFeatures2(maintenance5_features);
RETURN_IF_SKIP(InitState(nullptr, &maintenance5_features));
InitRenderTarget();
const char *source = R"glsl(
#version 450
vec2 vertices[3];
out gl_PerVertex
{
vec4 gl_Position;
float gl_PointSize;
};
void main() {
vertices[0] = vec2(-1.0, -1.0);
vertices[1] = vec2( 1.0, -1.0);
vertices[2] = vec2( 0.0, 1.0);
gl_Position = vec4(vertices[gl_VertexIndex % 3], 0.0, 1.0);
}
)glsl";
VkShaderObj vs(this, source, VK_SHADER_STAGE_VERTEX_BIT);
auto set_info = [&](CreatePipelineHelper &helper) {
helper.ia_ci_.topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
helper.shader_stages_ = {vs.GetStageCreateInfo(), helper.fs_->GetStageCreateInfo()};
};
CreatePipelineHelper::OneshotTest(*this, set_info, kErrorBit);
}