From f976bf822302ff5f50df475e4fe73fa0395b5129 Mon Sep 17 00:00:00 2001 From: spencer-lunarg Date: Mon, 25 Nov 2024 23:20:35 -0500 Subject: [PATCH] gpu: Reduce post processing already done on CPU --- layers/drawdispatch/descriptor_validator.cpp | 17 +- layers/state_tracker/descriptor_sets.cpp | 10 +- layers/state_tracker/descriptor_sets.h | 4 +- tests/unit/gpu_av.cpp | 163 +++++------ tests/unit/gpu_av_positive.cpp | 13 - tests/unit/sampler.cpp | 291 ++++++++++++++++++- 6 files changed, 379 insertions(+), 119 deletions(-) diff --git a/layers/drawdispatch/descriptor_validator.cpp b/layers/drawdispatch/descriptor_validator.cpp index 4bf101a5274..0e8165c96e0 100644 --- a/layers/drawdispatch/descriptor_validator.cpp +++ b/layers/drawdispatch/descriptor_validator.cpp @@ -152,6 +152,15 @@ bool DescriptorValidator::ValidateBindingDynamic(const DescriptorBindingInfo &bi auto binding_ptr = descriptor_set.GetBinding(binding_info.first); ASSERT_AND_RETURN_SKIP(binding_ptr); auto &binding = *binding_ptr; + + // If we already validated/updated the descriptor on the CPU, no reason to redo on the Post Processing of GPU-AV. + // There is no descriptor aliasing on the CPU, so we automatically know it needs Post Processing + if (binding_info.second.size() == 1) { + if (!descriptor_set.ValidateBindingOnGPU(binding, binding_info.second[0].variable->is_dynamic_accessed)) { + return skip; + } + } + switch (binding.descriptor_class) { case DescriptorClass::GeneralBuffer: skip |= ValidateDescriptorsDynamic(binding_info, static_cast(binding), indices); @@ -160,7 +169,7 @@ bool DescriptorValidator::ValidateBindingDynamic(const DescriptorBindingInfo &bi auto &imgs_binding = static_cast(binding); for (auto index : indices) { auto &descriptor = imgs_binding.descriptors[index]; - descriptor.UpdateDrawState(&dev_state, cb_state); + descriptor.UpdateDrawState(cb_state); } skip |= ValidateDescriptorsDynamic(binding_info, imgs_binding, indices); break; @@ -169,7 +178,7 @@ bool DescriptorValidator::ValidateBindingDynamic(const DescriptorBindingInfo &bi auto &img_binding = static_cast(binding); for (auto index : indices) { auto &descriptor = img_binding.descriptors[index]; - descriptor.UpdateDrawState(&dev_state, cb_state); + descriptor.UpdateDrawState(cb_state); } skip |= ValidateDescriptorsDynamic(binding_info, img_binding, indices); break; @@ -307,6 +316,10 @@ bool DescriptorValidator::ValidateDescriptor(const DescriptorBindingInfo &bindin if (image_descriptor.GetClass() == DescriptorClass::ImageSampler) { sampler_states.emplace_back(static_cast(image_descriptor).GetSamplerState()); + } else if (is_gpu_av) { + // TODO - This will skip for GPU-AV because we don't currently capture array of samplers with array of sampled images + // https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/8922 + // To not give false positve, we will skip all Sampler related checks since sampler_states will be empty } else { for (const auto &req : binding_info.second) { if (!req.variable || req.variable->samplers_used_by_image.size() <= index) { diff --git a/layers/state_tracker/descriptor_sets.cpp b/layers/state_tracker/descriptor_sets.cpp index cce2281f4c1..e26992cad75 100644 --- a/layers/state_tracker/descriptor_sets.cpp +++ b/layers/state_tracker/descriptor_sets.cpp @@ -708,21 +708,21 @@ void vvl::DescriptorSet::UpdateDrawStates(ValidationStateTracker *device_data, v case DescriptorClass::Image: { auto *image_binding = static_cast(binding); for (uint32_t i = 0; i < image_binding->count; ++i) { - image_binding->descriptors[i].UpdateDrawState(device_data, cb_state); + image_binding->descriptors[i].UpdateDrawState(cb_state); } break; } case DescriptorClass::ImageSampler: { auto *image_binding = static_cast(binding); for (uint32_t i = 0; i < image_binding->count; ++i) { - image_binding->descriptors[i].UpdateDrawState(device_data, cb_state); + image_binding->descriptors[i].UpdateDrawState(cb_state); } break; } case DescriptorClass::Mutable: { auto *mutable_binding = static_cast(binding); for (uint32_t i = 0; i < mutable_binding->count; ++i) { - mutable_binding->descriptors[i].UpdateDrawState(device_data, cb_state); + mutable_binding->descriptors[i].UpdateDrawState(cb_state); } break; } @@ -873,7 +873,7 @@ void vvl::ImageDescriptor::CopyUpdate(DescriptorSet &set_state, const Validation UpdateKnownValidView(is_bindless); } -void vvl::ImageDescriptor::UpdateDrawState(ValidationStateTracker *dev_data, vvl::CommandBuffer &cb_state) { +void vvl::ImageDescriptor::UpdateDrawState(vvl::CommandBuffer &cb_state) { // Add binding for image auto iv_state = GetImageViewState(); if (iv_state) { @@ -1276,7 +1276,7 @@ VkDeviceSize vvl::MutableDescriptor::GetEffectiveRange() const { } } -void vvl::MutableDescriptor::UpdateDrawState(ValidationStateTracker *dev_data, vvl::CommandBuffer &cb_state) { +void vvl::MutableDescriptor::UpdateDrawState(vvl::CommandBuffer &cb_state) { const vvl::DescriptorClass active_class = ActiveClass(); if (active_class == DescriptorClass::Image || active_class == DescriptorClass::ImageSampler) { if (image_view_state_) { diff --git a/layers/state_tracker/descriptor_sets.h b/layers/state_tracker/descriptor_sets.h index 3b03e6c1d6b..20e9175dd5d 100644 --- a/layers/state_tracker/descriptor_sets.h +++ b/layers/state_tracker/descriptor_sets.h @@ -449,7 +449,7 @@ class ImageDescriptor : public Descriptor { bool is_bindless) override; void CopyUpdate(DescriptorSet &set_state, const ValidationStateTracker &dev_data, const Descriptor &, bool is_bindless, VkDescriptorType type) override; - void UpdateDrawState(ValidationStateTracker *, vvl::CommandBuffer &cb_state); + void UpdateDrawState(vvl::CommandBuffer &cb_state); VkImageView GetImageView() const; const vvl::ImageView *GetImageViewState() const { return image_view_state_.get(); } vvl::ImageView *GetImageViewState() { return image_view_state_.get(); } @@ -613,7 +613,7 @@ class MutableDescriptor : public Descriptor { return acc_khr != VK_NULL_HANDLE; } - void UpdateDrawState(ValidationStateTracker *, vvl::CommandBuffer &cb_state); + void UpdateDrawState(vvl::CommandBuffer &cb_state); bool AddParent(StateObject *state_object) override; void RemoveParent(StateObject *state_object) override; diff --git a/tests/unit/gpu_av.cpp b/tests/unit/gpu_av.cpp index 193df848721..a2a2518c817 100644 --- a/tests/unit/gpu_av.cpp +++ b/tests/unit/gpu_av.cpp @@ -588,9 +588,9 @@ TEST_F(NegativeGpuAV, UnnormalizedCoordinatesCopyObject) { sampler_ci.unnormalizedCoordinates = VK_TRUE; sampler_ci.maxLod = 0; vkt::Sampler sampler(*m_device, sampler_ci); - g_pipe.descriptor_set_->WriteDescriptorImageInfo(0, view_pass, sampler.handle(), VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + g_pipe.descriptor_set_->WriteDescriptorImageInfo(0, view_pass, sampler, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, 0); - g_pipe.descriptor_set_->WriteDescriptorImageInfo(0, view_pass, sampler.handle(), VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + g_pipe.descriptor_set_->WriteDescriptorImageInfo(0, view_pass, sampler, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, 1); g_pipe.descriptor_set_->UpdateDescriptorSets(); @@ -608,10 +608,11 @@ TEST_F(NegativeGpuAV, UnnormalizedCoordinatesCopyObject) { m_command_buffer.End(); } -TEST_F(NegativeGpuAV, UnnormalizedCoordinatesSeparateSamplerSharedSampler) { +// TODO - https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/8922 +TEST_F(NegativeGpuAV, DISABLED_UnnormalizedCoordinatesSeparateSamplerSharedSamplerRuntime) { TEST_DESCRIPTION("Doesn't use COMBINED_IMAGE_SAMPLER, but multiple OpLoad share Sampler OpVariable"); - - AddRequiredExtensions(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + AddRequiredExtensions(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME); + AddRequiredFeature(vkt::Feature::runtimeDescriptorArray); RETURN_IF_SKIP(InitGpuAvFramework()); RETURN_IF_SKIP(InitState()); InitRenderTarget(); @@ -619,25 +620,31 @@ TEST_F(NegativeGpuAV, UnnormalizedCoordinatesSeparateSamplerSharedSampler) { // There are 2 OpLoad/OpAccessChain that point the same OpVariable const char fsSource[] = R"glsl( #version 450 + #extension GL_EXT_nonuniform_qualifier : enable // VK_DESCRIPTOR_TYPE_SAMPLER layout(set = 0, binding = 0) uniform sampler s1; // VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE - layout(set = 0, binding = 1) uniform texture2D si_good; - layout(set = 0, binding = 2) uniform texture3D si_bad[2]; // 3D image view + layout(set = 0, binding = 1) uniform texture2D si_good[]; + layout(set = 0, binding = 2) uniform texture3D si_bad[]; // 3D image view + layout(set = 0, binding = 3) uniform UBO { uint bad_index; }; layout(location=0) out vec4 color; void main() { - vec4 x = texture(sampler2D(si_good, s1), vec2(0)); - vec4 y = texture(sampler3D(si_bad[1], s1), vec3(0)); + vec4 x = texture(sampler2D(si_good[bad_index], s1), vec2(0)); + vec4 y = texture(sampler3D(si_bad[bad_index], s1), vec3(0)); color = vec4(x + y); } )glsl"; VkShaderObj vs(this, kVertexDrawPassthroughGlsl, VK_SHADER_STAGE_VERTEX_BIT); VkShaderObj fs(this, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT); - OneOffDescriptorSet descriptor_set(m_device, {{0, VK_DESCRIPTOR_TYPE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}, - {1, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}, - {2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 2, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}}); + OneOffDescriptorSet descriptor_set(m_device, + { + {0, VK_DESCRIPTOR_TYPE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}, + {1, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 2, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}, + {2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 2, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}, + {3, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}, + }); const vkt::PipelineLayout pipeline_layout(*m_device, {&descriptor_set.layout_}); CreatePipelineHelper g_pipe(*this); @@ -660,11 +667,19 @@ TEST_F(NegativeGpuAV, UnnormalizedCoordinatesSeparateSamplerSharedSampler) { sampler_ci.maxLod = 0; vkt::Sampler sampler(*m_device, sampler_ci); - descriptor_set.WriteDescriptorImageInfo(0, VK_NULL_HANDLE, sampler.handle(), VK_DESCRIPTOR_TYPE_SAMPLER); + vkt::Buffer uniform_buffer(*m_device, 32, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, kHostVisibleMemProps); + uint32_t *uniform_buffer_ptr = (uint32_t *)uniform_buffer.Memory().Map(); + uniform_buffer_ptr[0] = 1; // bad_index + uniform_buffer.Memory().Unmap(); + + descriptor_set.WriteDescriptorImageInfo(0, VK_NULL_HANDLE, sampler, VK_DESCRIPTOR_TYPE_SAMPLER); descriptor_set.WriteDescriptorImageInfo(1, image_view, VK_NULL_HANDLE, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE); + descriptor_set.WriteDescriptorImageInfo(1, image_view, VK_NULL_HANDLE, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, 1); descriptor_set.WriteDescriptorImageInfo(2, image_view_3d, VK_NULL_HANDLE, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE); descriptor_set.WriteDescriptorImageInfo(2, image_view_3d, VK_NULL_HANDLE, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, 1); + descriptor_set.WriteDescriptorBufferInfo(3, uniform_buffer, 0, VK_WHOLE_SIZE); descriptor_set.UpdateDescriptorSets(); m_command_buffer.Begin(); @@ -673,16 +688,7 @@ TEST_F(NegativeGpuAV, UnnormalizedCoordinatesSeparateSamplerSharedSampler) { vk::CmdBindDescriptorSets(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout.handle(), 0, 1, &descriptor_set.set_, 0, nullptr); - // Core validation triggers these errors, causing the following draw to be skipped. - // GPU-AV will thus not be able to validate this draw call. - // Descriptor arrays are not validated in core validation - // (See call to `descriptor_set.SkipBinding()` in `CoreChecks::ValidateDrawState()`) - if (!m_gpuav_disable_core) { - m_errorMonitor->SetAllowedFailureMsg("VUID-vkCmdDraw-None-08609"); - m_errorMonitor->SetAllowedFailureMsg("VUID-vkCmdDraw-None-08610"); - } vk::CmdDraw(m_command_buffer.handle(), 3, 1, 0, 0); - m_command_buffer.EndRenderPass(); m_command_buffer.End(); @@ -693,75 +699,37 @@ TEST_F(NegativeGpuAV, UnnormalizedCoordinatesSeparateSamplerSharedSampler) { m_errorMonitor->VerifyFound(); } -TEST_F(NegativeGpuAV, ShareOpSampledImage) { - TEST_DESCRIPTION( - "Have two OpImageSampleImplicitLod share the same OpSampledImage. This needs to be in the same block post-shader " - "instrumentation."); - AddRequiredExtensions(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); +// TODO - https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/8922 +TEST_F(NegativeGpuAV, DISABLED_UnnormalizedCoordinatesSeparateSamplerSharedSamplerSpecConstant) { + TEST_DESCRIPTION("Doesn't use COMBINED_IMAGE_SAMPLER, but multiple OpLoad share Sampler OpVariable"); + RETURN_IF_SKIP(InitGpuAvFramework()); RETURN_IF_SKIP(InitState()); InitRenderTarget(); - // #version 450 - // layout(set = 0, binding = 0) uniform sampler s1; - // layout(set = 0, binding = 1) uniform texture2D si_good; - // layout(location=0) out vec4 color; - // void main() { - // color = texture(sampler2D(si_good, s1), vec2(0)); - // color += texture(sampler2D(si_good, s1), vec2(color.x)); - // } - const char *fsSource = R"( - OpCapability Shader - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %main "main" %color - OpExecutionMode %main OriginUpperLeft - OpDecorate %color Location 0 - OpDecorate %si_good DescriptorSet 0 - OpDecorate %si_good Binding 1 - OpDecorate %s1 DescriptorSet 0 - OpDecorate %s1 Binding 0 - %void = OpTypeVoid - %3 = OpTypeFunction %void - %float = OpTypeFloat 32 - %v4float = OpTypeVector %float 4 -%_ptr_Output_v4float = OpTypePointer Output %v4float - %color = OpVariable %_ptr_Output_v4float Output - %10 = OpTypeImage %float 2D 0 0 0 1 Unknown -%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10 - %si_good = OpVariable %_ptr_UniformConstant_10 UniformConstant - %14 = OpTypeSampler -%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14 - %s1 = OpVariable %_ptr_UniformConstant_14 UniformConstant - %18 = OpTypeSampledImage %10 - %v2float = OpTypeVector %float 2 - %float_0 = OpConstant %float 0 - %22 = OpConstantComposite %v2float %float_0 %float_0 - %uint = OpTypeInt 32 0 - %uint_0 = OpConstant %uint 0 -%_ptr_Output_float = OpTypePointer Output %float - %main = OpFunction %void None %3 - %5 = OpLabel - %13 = OpLoad %10 %si_good - %17 = OpLoad %14 %s1 - ; the results (%19) needs to be in same block as what consumes it - %19 = OpSampledImage %18 %13 %17 - %23 = OpImageSampleImplicitLod %v4float %19 %22 - OpStore %color %23 - %30 = OpAccessChain %_ptr_Output_float %color %uint_0 - %31 = OpLoad %float %30 - %32 = OpCompositeConstruct %v2float %31 %31 - %33 = OpImageSampleImplicitLod %v4float %19 %32 - %34 = OpLoad %v4float %color - %35 = OpFAdd %v4float %34 %33 - OpStore %color %35 - OpReturn - OpFunctionEnd - )"; + // There are 2 OpLoad/OpAccessChain that point the same OpVariable + const char fsSource[] = R"glsl( + #version 450 + // VK_DESCRIPTOR_TYPE_SAMPLER + layout(set = 0, binding = 0) uniform sampler s1; + // VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE + layout(set = 0, binding = 1) uniform texture2D si_good[2]; + layout(set = 0, binding = 2) uniform texture3D si_bad[2]; // 3D image view + layout(constant_id = 0) const uint bad_index = 1; + + layout(location=0) out vec4 color; + void main() { + vec4 x = texture(sampler2D(si_good[bad_index], s1), vec2(0)); + vec4 y = texture(sampler3D(si_bad[bad_index], s1), vec3(0)); + color = vec4(x + y); + } + )glsl"; VkShaderObj vs(this, kVertexDrawPassthroughGlsl, VK_SHADER_STAGE_VERTEX_BIT); - VkShaderObj fs(this, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT, SPV_ENV_VULKAN_1_0, SPV_SOURCE_ASM); + VkShaderObj fs(this, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT); OneOffDescriptorSet descriptor_set(m_device, {{0, VK_DESCRIPTOR_TYPE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}, - {1, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}}); + {1, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 2, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}, + {2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 2, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}}); const vkt::PipelineLayout pipeline_layout(*m_device, {&descriptor_set.layout_}); CreatePipelineHelper g_pipe(*this); @@ -769,34 +737,41 @@ TEST_F(NegativeGpuAV, ShareOpSampledImage) { g_pipe.gp_ci_.layout = pipeline_layout.handle(); g_pipe.CreateGraphicsPipeline(); - auto image_ci = vkt::Image::ImageCreateInfo2D( - 128, 128, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, - VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); + const VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + const VkFormat format = VK_FORMAT_R8G8B8A8_UNORM; + auto image_ci = vkt::Image::ImageCreateInfo2D(128, 128, 1, 1, format, usage); vkt::Image image(*m_device, image_ci, vkt::set_layout); vkt::ImageView image_view = image.CreateView(); + image_ci.imageType = VK_IMAGE_TYPE_3D; + vkt::Image image_3d(*m_device, image_ci, vkt::set_layout); + vkt::ImageView image_view_3d = image_3d.CreateView(VK_IMAGE_VIEW_TYPE_3D); + VkSamplerCreateInfo sampler_ci = SafeSaneSamplerCreateInfo(); sampler_ci.unnormalizedCoordinates = VK_TRUE; sampler_ci.maxLod = 0; vkt::Sampler sampler(*m_device, sampler_ci); - descriptor_set.WriteDescriptorImageInfo(0, VK_NULL_HANDLE, sampler.handle(), VK_DESCRIPTOR_TYPE_SAMPLER); + descriptor_set.WriteDescriptorImageInfo(0, VK_NULL_HANDLE, sampler, VK_DESCRIPTOR_TYPE_SAMPLER); descriptor_set.WriteDescriptorImageInfo(1, image_view, VK_NULL_HANDLE, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE); + descriptor_set.WriteDescriptorImageInfo(1, image_view, VK_NULL_HANDLE, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, 1); + descriptor_set.WriteDescriptorImageInfo(2, image_view_3d, VK_NULL_HANDLE, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE); + descriptor_set.WriteDescriptorImageInfo(2, image_view_3d, VK_NULL_HANDLE, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, 1); descriptor_set.UpdateDescriptorSets(); m_command_buffer.Begin(); m_command_buffer.BeginRenderPass(m_renderPassBeginInfo); + vk::CmdBindPipeline(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, g_pipe.Handle()); vk::CmdBindDescriptorSets(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout.handle(), 0, 1, &descriptor_set.set_, 0, nullptr); - vk::CmdBindPipeline(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, g_pipe.Handle()); - if (!m_gpuav_disable_core) { - m_errorMonitor->SetAllowedFailureMsg("VUID-vkCmdDraw-None-08610"); - } vk::CmdDraw(m_command_buffer.handle(), 3, 1, 0, 0); m_command_buffer.EndRenderPass(); m_command_buffer.End(); + m_errorMonitor->SetDesiredError("VUID-vkCmdDraw-None-08609"); m_errorMonitor->SetDesiredError("VUID-vkCmdDraw-None-08610"); m_default_queue->Submit(m_command_buffer); m_default_queue->Wait(); @@ -1441,11 +1416,9 @@ TEST_F(NegativeGpuAV, AliasImageMultisampleDescriptorSetsPartiallyBound) { char const *cs_source = R"glsl( #version 460 #extension GL_EXT_samplerless_texture_functions : require - layout(set = 0, binding = 0) uniform texture2D BaseTexture; layout(set = 0, binding = 1) uniform sampler BaseTextureSampler; layout(set = 0, binding = 2) buffer SSBO { vec4 dummy; }; - void main() { dummy = texture(sampler2D(BaseTexture, BaseTextureSampler), vec2(0)); } @@ -1454,10 +1427,8 @@ TEST_F(NegativeGpuAV, AliasImageMultisampleDescriptorSetsPartiallyBound) { char const *cs_source_ms = R"glsl( #version 460 #extension GL_EXT_samplerless_texture_functions : require - layout(set = 0, binding = 0) uniform texture2DMS BaseTextureMS; layout(set = 0, binding = 2) buffer SSBO { vec4 dummy; }; - void main() { dummy = texelFetch(BaseTextureMS, ivec2(0), 0); } diff --git a/tests/unit/gpu_av_positive.cpp b/tests/unit/gpu_av_positive.cpp index f57bb2310cb..747f88262fd 100644 --- a/tests/unit/gpu_av_positive.cpp +++ b/tests/unit/gpu_av_positive.cpp @@ -1383,11 +1383,9 @@ TEST_F(PositiveGpuAV, AliasImageMultisampleDescriptorSets) { char const *cs_source = R"glsl( #version 460 #extension GL_EXT_samplerless_texture_functions : require - layout(set = 0, binding = 0) uniform texture2D BaseTexture; layout(set = 0, binding = 1) uniform sampler BaseTextureSampler; layout(set = 0, binding = 2) buffer SSBO { vec4 dummy; }; - void main() { dummy = texture(sampler2D(BaseTexture, BaseTextureSampler), vec2(0)); } @@ -1396,10 +1394,8 @@ TEST_F(PositiveGpuAV, AliasImageMultisampleDescriptorSets) { char const *cs_source_ms = R"glsl( #version 460 #extension GL_EXT_samplerless_texture_functions : require - layout(set = 0, binding = 0) uniform texture2DMS BaseTextureMS; layout(set = 0, binding = 2) buffer SSBO { vec4 dummy; }; - void main() { dummy = texelFetch(BaseTextureMS, ivec2(0), 0); } @@ -1466,32 +1462,25 @@ TEST_F(PositiveGpuAV, AliasImageMultisampleDescriptorSetsPartiallyBound) { AddRequiredFeature(vkt::Feature::descriptorBindingPartiallyBound); RETURN_IF_SKIP(InitGpuAvFramework()); RETURN_IF_SKIP(InitState()); - char const *cs_source = R"glsl( #version 460 #extension GL_EXT_samplerless_texture_functions : require - layout(set = 0, binding = 0) uniform texture2D BaseTexture; layout(set = 0, binding = 1) uniform sampler BaseTextureSampler; layout(set = 0, binding = 2) buffer SSBO { vec4 dummy; }; - void main() { dummy = texture(sampler2D(BaseTexture, BaseTextureSampler), vec2(0)); } )glsl"; - char const *cs_source_ms = R"glsl( #version 460 #extension GL_EXT_samplerless_texture_functions : require - layout(set = 0, binding = 0) uniform texture2DMS BaseTextureMS; layout(set = 0, binding = 2) buffer SSBO { vec4 dummy; }; - void main() { dummy = texelFetch(BaseTextureMS, ivec2(0), 0); } )glsl"; - vkt::Buffer buffer(*m_device, 64, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, kHostVisibleMemProps); vkt::Sampler sampler(*m_device, SafeSaneSamplerCreateInfo()); auto image_ci = vkt::Image::ImageCreateInfo2D(64, 64, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT); @@ -1500,12 +1489,10 @@ TEST_F(PositiveGpuAV, AliasImageMultisampleDescriptorSetsPartiallyBound) { image_ci.samples = VK_SAMPLE_COUNT_4_BIT; vkt::Image ms_image(*m_device, image_ci, vkt::set_layout); vkt::ImageView ms_image_view = ms_image.CreateView(); - VkDescriptorBindingFlagsEXT ds_binding_flags[3] = {VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT_EXT, 0, 0}; VkDescriptorSetLayoutBindingFlagsCreateInfoEXT ds_layout_binding_flags = vku::InitStructHelper(); ds_layout_binding_flags.bindingCount = 3; ds_layout_binding_flags.pBindingFlags = ds_binding_flags; - OneOffDescriptorSet descriptor_set0(m_device, { {0, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_ALL, nullptr}, diff --git a/tests/unit/sampler.cpp b/tests/unit/sampler.cpp index 08a48478ea8..887476c51bc 100644 --- a/tests/unit/sampler.cpp +++ b/tests/unit/sampler.cpp @@ -844,7 +844,6 @@ TEST_F(NegativeSampler, CustomBorderColor) { TEST_F(NegativeSampler, CustomBorderColorFormatUndefined) { TEST_DESCRIPTION("Tests for VUID-VkSamplerCustomBorderColorCreateInfoEXT-format-04015"); - AddRequiredExtensions(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); AddRequiredExtensions(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); AddRequiredFeature(vkt::Feature::customBorderColors); AddRequiredFeature(vkt::Feature::customBorderColorWithoutFormat); @@ -898,6 +897,125 @@ TEST_F(NegativeSampler, CustomBorderColorFormatUndefined) { m_command_buffer.End(); } +TEST_F(NegativeSampler, CustomBorderColorFormatUndefinedNonCombined) { + AddRequiredExtensions(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); + AddRequiredFeature(vkt::Feature::customBorderColors); + AddRequiredFeature(vkt::Feature::customBorderColorWithoutFormat); + RETURN_IF_SKIP(Init()); + InitRenderTarget(); + + VkSamplerCreateInfo sampler_info = SafeSaneSamplerCreateInfo(); + sampler_info.borderColor = VK_BORDER_COLOR_INT_CUSTOM_EXT; + VkSamplerCustomBorderColorCreateInfoEXT custom_color_cinfo = vku::InitStructHelper(); + custom_color_cinfo.format = VK_FORMAT_UNDEFINED; + sampler_info.pNext = &custom_color_cinfo; + vkt::Sampler sampler(*m_device, sampler_info); + + vkt::Image image(*m_device, 32, 32, 1, VK_FORMAT_B4G4R4A4_UNORM_PACK16, VK_IMAGE_USAGE_SAMPLED_BIT); + image.Layout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + OneOffDescriptorSet descriptor_set(m_device, { + {0, VK_DESCRIPTOR_TYPE_SAMPLER, 1, VK_SHADER_STAGE_ALL, nullptr}, + {1, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_ALL, nullptr}, + }); + const vkt::PipelineLayout pipeline_layout(*m_device, {&descriptor_set.layout_}); + auto image_view_create_info = image.BasicViewCreatInfo(); + vkt::ImageView image_view(*m_device, image_view_create_info); + + descriptor_set.WriteDescriptorImageInfo(0, VK_NULL_HANDLE, sampler, VK_DESCRIPTOR_TYPE_SAMPLER); + descriptor_set.WriteDescriptorImageInfo(1, image_view, VK_NULL_HANDLE, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE); + descriptor_set.UpdateDescriptorSets(); + + char const *fsSource = R"glsl( + #version 450 + layout(set=0, binding=0) uniform sampler s; + layout(set=0, binding=1) uniform texture2D t; + layout(location=0) out vec4 x; + void main(){ + x = texture(sampler2D(t, s), vec2(1.0)); + } + )glsl"; + VkShaderObj fs(this, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT); + + CreatePipelineHelper pipe(*this); + pipe.shader_stages_[1] = fs.GetStageCreateInfo(); + pipe.gp_ci_.layout = pipeline_layout.handle(); + pipe.CreateGraphicsPipeline(); + + m_command_buffer.Begin(); + m_command_buffer.BeginRenderPass(m_renderPassBeginInfo); + vk::CmdBindPipeline(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.Handle()); + vk::CmdBindDescriptorSets(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout.handle(), 0, 1, + &descriptor_set.set_, 0, NULL); + m_errorMonitor->SetDesiredError("VUID-VkSamplerCustomBorderColorCreateInfoEXT-format-04015"); + vk::CmdDraw(m_command_buffer.handle(), 3, 1, 0, 0); + m_errorMonitor->VerifyFound(); + m_command_buffer.EndRenderPass(); + m_command_buffer.End(); +} + +// TODO - https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/8922 +// we currently only check the same descriptor set for the sampler pair +TEST_F(NegativeSampler, DISABLED_CustomBorderColorFormatUndefinedNonCombinedMultipleSets) { + AddRequiredExtensions(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); + AddRequiredFeature(vkt::Feature::customBorderColors); + AddRequiredFeature(vkt::Feature::customBorderColorWithoutFormat); + RETURN_IF_SKIP(Init()); + InitRenderTarget(); + + VkSamplerCreateInfo sampler_info = SafeSaneSamplerCreateInfo(); + sampler_info.borderColor = VK_BORDER_COLOR_INT_CUSTOM_EXT; + VkSamplerCustomBorderColorCreateInfoEXT custom_color_cinfo = vku::InitStructHelper(); + custom_color_cinfo.format = VK_FORMAT_UNDEFINED; + sampler_info.pNext = &custom_color_cinfo; + vkt::Sampler sampler(*m_device, sampler_info); + + vkt::Image image(*m_device, 32, 32, 1, VK_FORMAT_B4G4R4A4_UNORM_PACK16, VK_IMAGE_USAGE_SAMPLED_BIT); + image.Layout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + OneOffDescriptorSet descriptor_set0(m_device, { + {2, VK_DESCRIPTOR_TYPE_SAMPLER, 1, VK_SHADER_STAGE_ALL, nullptr}, + }); + OneOffDescriptorSet descriptor_set1(m_device, { + {3, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_ALL, nullptr}, + }); + const vkt::PipelineLayout pipeline_layout(*m_device, {&descriptor_set0.layout_, &descriptor_set1.layout_}); + auto image_view_create_info = image.BasicViewCreatInfo(); + vkt::ImageView image_view(*m_device, image_view_create_info); + + descriptor_set0.WriteDescriptorImageInfo(2, VK_NULL_HANDLE, sampler, VK_DESCRIPTOR_TYPE_SAMPLER); + descriptor_set0.UpdateDescriptorSets(); + descriptor_set1.WriteDescriptorImageInfo(3, image_view, VK_NULL_HANDLE, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE); + descriptor_set1.UpdateDescriptorSets(); + + char const *fsSource = R"glsl( + #version 450 + layout(set=0, binding=2) uniform sampler s; + layout(set=1, binding=3) uniform texture2D t; + layout(location=0) out vec4 x; + void main(){ + x = texture(sampler2D(t, s), vec2(1.0)); + } + )glsl"; + VkShaderObj fs(this, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT); + + CreatePipelineHelper pipe(*this); + pipe.shader_stages_[1] = fs.GetStageCreateInfo(); + pipe.gp_ci_.layout = pipeline_layout.handle(); + pipe.CreateGraphicsPipeline(); + + m_command_buffer.Begin(); + m_command_buffer.BeginRenderPass(m_renderPassBeginInfo); + vk::CmdBindPipeline(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.Handle()); + vk::CmdBindDescriptorSets(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout.handle(), 0, 1, + &descriptor_set0.set_, 0, NULL); + vk::CmdBindDescriptorSets(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout.handle(), 1, 1, + &descriptor_set1.set_, 0, NULL); + m_errorMonitor->SetDesiredError("VUID-VkSamplerCustomBorderColorCreateInfoEXT-format-04015"); + vk::CmdDraw(m_command_buffer.handle(), 3, 1, 0, 0); + m_errorMonitor->VerifyFound(); + m_command_buffer.EndRenderPass(); + m_command_buffer.End(); +} + TEST_F(NegativeSampler, UnnormalizedCoordinatesCombinedSampler) { TEST_DESCRIPTION( "If a samper is unnormalizedCoordinates, the imageview has to be some specific types. Uses COMBINED_IMAGE_SAMPLER"); @@ -1234,6 +1352,74 @@ TEST_F(NegativeSampler, UnnormalizedCoordinatesSeparateSamplerSharedSampler) { m_command_buffer.End(); } +TEST_F(NegativeSampler, UnnormalizedCoordinatesSeparateSamplerSharedSamplerArray) { + TEST_DESCRIPTION("Doesn't use COMBINED_IMAGE_SAMPLER, but multiple OpLoad share Sampler OpVariable"); + AddRequiredExtensions(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + RETURN_IF_SKIP(Init()); + InitRenderTarget(); + + // There are 2 OpLoad/OpAccessChain that point the same OpVariable + const char fsSource[] = R"glsl( + #version 450 + // VK_DESCRIPTOR_TYPE_SAMPLER + layout(set = 0, binding = 0) uniform sampler s1; + // VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE + layout(set = 0, binding = 1) uniform texture2D si_good; + layout(set = 0, binding = 2) uniform texture3D si_bad[2]; // 3D image view + layout(location=0) out vec4 color; + void main() { + vec4 x = texture(sampler2D(si_good, s1), vec2(0)); + vec4 y = texture(sampler3D(si_bad[1], s1), vec3(0)); + color = vec4(x + y); + } + )glsl"; + VkShaderObj vs(this, kVertexDrawPassthroughGlsl, VK_SHADER_STAGE_VERTEX_BIT); + VkShaderObj fs(this, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT); + + OneOffDescriptorSet descriptor_set(m_device, {{0, VK_DESCRIPTOR_TYPE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}, + {1, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}, + {2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 2, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}}); + const vkt::PipelineLayout pipeline_layout(*m_device, {&descriptor_set.layout_}); + + CreatePipelineHelper g_pipe(*this); + g_pipe.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()}; + g_pipe.gp_ci_.layout = pipeline_layout.handle(); + g_pipe.CreateGraphicsPipeline(); + + const VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + const VkFormat format = VK_FORMAT_R8G8B8A8_UNORM; + auto image_ci = vkt::Image::ImageCreateInfo2D(128, 128, 1, 1, format, usage); + vkt::Image image(*m_device, image_ci, vkt::set_layout); + vkt::ImageView image_view = image.CreateView(); + + image_ci.imageType = VK_IMAGE_TYPE_3D; + vkt::Image image_3d(*m_device, image_ci, vkt::set_layout); + vkt::ImageView image_view_3d = image_3d.CreateView(VK_IMAGE_VIEW_TYPE_3D); + + VkSamplerCreateInfo sampler_ci = SafeSaneSamplerCreateInfo(); + sampler_ci.unnormalizedCoordinates = VK_TRUE; + sampler_ci.maxLod = 0; + vkt::Sampler sampler(*m_device, sampler_ci); + + descriptor_set.WriteDescriptorImageInfo(0, VK_NULL_HANDLE, sampler.handle(), VK_DESCRIPTOR_TYPE_SAMPLER); + descriptor_set.WriteDescriptorImageInfo(1, image_view, VK_NULL_HANDLE, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE); + descriptor_set.WriteDescriptorImageInfo(2, image_view_3d, VK_NULL_HANDLE, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE); + descriptor_set.WriteDescriptorImageInfo(2, image_view_3d, VK_NULL_HANDLE, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, 1); + descriptor_set.UpdateDescriptorSets(); + + m_command_buffer.Begin(); + m_command_buffer.BeginRenderPass(m_renderPassBeginInfo); + vk::CmdBindPipeline(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, g_pipe.Handle()); + vk::CmdBindDescriptorSets(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout.handle(), 0, 1, + &descriptor_set.set_, 0, nullptr); + + m_errorMonitor->SetDesiredError("VUID-vkCmdDraw-None-08609"); + m_errorMonitor->SetDesiredError("VUID-vkCmdDraw-None-08610"); + vk::CmdDraw(m_command_buffer.handle(), 3, 1, 0, 0); + m_errorMonitor->VerifyFound(); +} + TEST_F(NegativeSampler, UnnormalizedCoordinatesInBoundsAccess) { TEST_DESCRIPTION("If a samper is unnormalizedCoordinates, but using OpInBoundsAccessChain"); @@ -1464,6 +1650,109 @@ TEST_F(NegativeSampler, UnnormalizedCoordinatesLevelCount) { m_command_buffer.End(); } +TEST_F(NegativeSampler, ShareOpSampledImage) { + TEST_DESCRIPTION( + "Have two OpImageSampleImplicitLod share the same OpSampledImage. This needs to be in the same block post-shader " + "instrumentation."); + AddRequiredExtensions(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + RETURN_IF_SKIP(Init()); + InitRenderTarget(); + + // #version 450 + // layout(set = 0, binding = 0) uniform sampler s1; + // layout(set = 0, binding = 1) uniform texture2D si_good; + // layout(location=0) out vec4 color; + // void main() { + // color = texture(sampler2D(si_good, s1), vec2(0)); + // color += texture(sampler2D(si_good, s1), vec2(color.x)); + // } + const char *fsSource = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %color + OpExecutionMode %main OriginUpperLeft + OpDecorate %color Location 0 + OpDecorate %si_good DescriptorSet 0 + OpDecorate %si_good Binding 1 + OpDecorate %s1 DescriptorSet 0 + OpDecorate %s1 Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %color = OpVariable %_ptr_Output_v4float Output + %10 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10 + %si_good = OpVariable %_ptr_UniformConstant_10 UniformConstant + %14 = OpTypeSampler +%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14 + %s1 = OpVariable %_ptr_UniformConstant_14 UniformConstant + %18 = OpTypeSampledImage %10 + %v2float = OpTypeVector %float 2 + %float_0 = OpConstant %float 0 + %22 = OpConstantComposite %v2float %float_0 %float_0 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Output_float = OpTypePointer Output %float + %main = OpFunction %void None %3 + %5 = OpLabel + %13 = OpLoad %10 %si_good + %17 = OpLoad %14 %s1 + ; the results (%19) needs to be in same block as what consumes it + %19 = OpSampledImage %18 %13 %17 + %23 = OpImageSampleImplicitLod %v4float %19 %22 + OpStore %color %23 + %30 = OpAccessChain %_ptr_Output_float %color %uint_0 + %31 = OpLoad %float %30 + %32 = OpCompositeConstruct %v2float %31 %31 + %33 = OpImageSampleImplicitLod %v4float %19 %32 + %34 = OpLoad %v4float %color + %35 = OpFAdd %v4float %34 %33 + OpStore %color %35 + OpReturn + OpFunctionEnd + )"; + VkShaderObj vs(this, kVertexDrawPassthroughGlsl, VK_SHADER_STAGE_VERTEX_BIT); + VkShaderObj fs(this, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT, SPV_ENV_VULKAN_1_0, SPV_SOURCE_ASM); + + OneOffDescriptorSet descriptor_set(m_device, {{0, VK_DESCRIPTOR_TYPE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}, + {1, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}}); + const vkt::PipelineLayout pipeline_layout(*m_device, {&descriptor_set.layout_}); + + CreatePipelineHelper g_pipe(*this); + g_pipe.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()}; + g_pipe.gp_ci_.layout = pipeline_layout.handle(); + g_pipe.CreateGraphicsPipeline(); + + auto image_ci = vkt::Image::ImageCreateInfo2D( + 128, 128, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); + vkt::Image image(*m_device, image_ci, vkt::set_layout); + vkt::ImageView image_view = image.CreateView(); + + VkSamplerCreateInfo sampler_ci = SafeSaneSamplerCreateInfo(); + sampler_ci.unnormalizedCoordinates = VK_TRUE; + sampler_ci.maxLod = 0; + vkt::Sampler sampler(*m_device, sampler_ci); + + descriptor_set.WriteDescriptorImageInfo(0, VK_NULL_HANDLE, sampler.handle(), VK_DESCRIPTOR_TYPE_SAMPLER); + descriptor_set.WriteDescriptorImageInfo(1, image_view, VK_NULL_HANDLE, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE); + descriptor_set.UpdateDescriptorSets(); + + m_command_buffer.Begin(); + m_command_buffer.BeginRenderPass(m_renderPassBeginInfo); + vk::CmdBindDescriptorSets(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout.handle(), 0, 1, + &descriptor_set.set_, 0, nullptr); + vk::CmdBindPipeline(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, g_pipe.Handle()); + + m_errorMonitor->SetDesiredError("VUID-vkCmdDraw-None-08610"); + vk::CmdDraw(m_command_buffer.handle(), 3, 1, 0, 0); + m_errorMonitor->VerifyFound(); + m_command_buffer.EndRenderPass(); + m_command_buffer.End(); +} + TEST_F(NegativeSampler, ReductionModeFeature) { TEST_DESCRIPTION("Test using VkSamplerReductionModeCreateInfo without required feature.");