layers: Add min/maxTexelOffset check
diff --git a/layers/core_validation.h b/layers/core_validation.h
index 3fa418a..e052452 100644
--- a/layers/core_validation.h
+++ b/layers/core_validation.h
@@ -535,7 +535,7 @@
VkShaderStageFlagBits stage) const;
bool ValidatePrimitiveRateShaderState(const PIPELINE_STATE* pipeline, SHADER_MODULE_STATE const* src,
spirv_inst_iter entrypoint, VkShaderStageFlagBits stage) const;
- bool ValidateTexelGatherOffset(SHADER_MODULE_STATE const* src, spirv_inst_iter& insn) const;
+ bool ValidateTexelOffsetLimits(SHADER_MODULE_STATE const* src, spirv_inst_iter& insn) const;
bool ValidateShaderCapabilitiesAndExtensions(SHADER_MODULE_STATE const* src, spirv_inst_iter& insn) const;
bool ValidateShaderStageWritableOrAtomicDescriptor(VkShaderStageFlagBits stage, bool has_writable_descriptor,
bool has_atomic_descriptor) const;
diff --git a/layers/shader_validation.cpp b/layers/shader_validation.cpp
index 1fec5bb..b9f6c2a 100644
--- a/layers/shader_validation.cpp
+++ b/layers/shader_validation.cpp
@@ -2141,18 +2141,21 @@
return skip;
}
-bool CoreChecks::ValidateTexelGatherOffset(SHADER_MODULE_STATE const *src, spirv_inst_iter &insn) const {
+// Checks for both TexelOffset and TexelGatherOffset limits
+bool CoreChecks::ValidateTexelOffsetLimits(SHADER_MODULE_STATE const *src, spirv_inst_iter &insn) const {
bool skip = false;
const uint32_t opcode = insn.opcode();
- if (ImageGatherOperation(opcode)) {
+ if (ImageGatherOperation(opcode) || ImageSampleOperation(opcode) || ImageFetchOperation(opcode)) {
uint32_t image_operand_position = ImageOperandsParam(opcode);
- // Image operands are optional
+ // Image operands can be optional
if (image_operand_position != 0 && insn.len() > image_operand_position) {
auto image_operand = insn.word(image_operand_position);
- // Bits we are validating
+ // Bits we are validating (sample/fetch only check ConstOffset)
uint32_t offset_bits =
- spv::ImageOperandsOffsetMask | spv::ImageOperandsConstOffsetMask | spv::ImageOperandsConstOffsetsMask;
+ ImageGatherOperation(opcode)
+ ? (spv::ImageOperandsOffsetMask | spv::ImageOperandsConstOffsetMask | spv::ImageOperandsConstOffsetsMask)
+ : (spv::ImageOperandsConstOffsetMask);
if (image_operand & (offset_bits)) {
// Operand values follow
uint32_t index = image_operand_position + 1;
@@ -2171,25 +2174,46 @@
const auto &comp_type = src->get_def(comp.word(1));
// Get operand value
const uint32_t offset = comp.word(3);
+ // spec requires minTexelGatherOffset/minTexelOffset to be -8 or less so never can compare if
+ // unsigned spec requires maxTexelGatherOffset/maxTexelOffset to be 7 or greater so never can
+ // compare if signed is less then zero
const int32_t signed_offset = static_cast<int32_t>(offset);
const bool use_signed = (comp_type.opcode() == spv::OpTypeInt && comp_type.word(3) != 0);
- // spec requires minTexelGatherOffset to be -8 or less so never can compare if unsigned
- // spec requires maxTexelGatherOffset to be 7 or greater so never can compare if signed is less
- // then zero
- if (use_signed && (signed_offset < phys_dev_props.limits.minTexelGatherOffset)) {
- skip |= LogError(device, "VUID-RuntimeSpirv-OpImage-06376",
+ // There are 2 sets of VU being covered where the only main difference is the opcode
+ if (ImageGatherOperation(opcode)) {
+ // min/maxTexelGatherOffset
+ if (use_signed && (signed_offset < phys_dev_props.limits.minTexelGatherOffset)) {
+ skip |=
+ LogError(device, "VUID-RuntimeSpirv-OpImage-06376",
"vkCreateShaderModule(): Shader uses %s with offset (%" PRIi32
") less than VkPhysicalDeviceLimits::minTexelGatherOffset (%" PRIi32 ").",
string_SpvOpcode(opcode), signed_offset,
phys_dev_props.limits.minTexelGatherOffset);
- } else if ((offset > phys_dev_props.limits.maxTexelGatherOffset) &&
- (!use_signed || (use_signed && signed_offset > 0))) {
- skip |=
- LogError(device, "VUID-RuntimeSpirv-OpImage-06377",
- "vkCreateShaderModule(): Shader uses %s with offset (%" PRIu32
- ") greater than VkPhysicalDeviceLimits::maxTexelGatherOffset (%" PRIu32 ").",
- string_SpvOpcode(opcode), offset, phys_dev_props.limits.maxTexelGatherOffset);
+ } else if ((offset > phys_dev_props.limits.maxTexelGatherOffset) &&
+ (!use_signed || (use_signed && signed_offset > 0))) {
+ skip |= LogError(
+ device, "VUID-RuntimeSpirv-OpImage-06377",
+ "vkCreateShaderModule(): Shader uses %s with offset (%" PRIu32
+ ") greater than VkPhysicalDeviceLimits::maxTexelGatherOffset (%" PRIu32 ").",
+ string_SpvOpcode(opcode), offset, phys_dev_props.limits.maxTexelGatherOffset);
+ }
+ } else {
+ // min/maxTexelOffset
+ if (use_signed && (signed_offset < phys_dev_props.limits.minTexelOffset)) {
+ skip |= LogError(device, "VUID-RuntimeSpirv-OpImageSample-06435",
+ "vkCreateShaderModule(): Shader uses %s with offset (%" PRIi32
+ ") less than VkPhysicalDeviceLimits::minTexelOffset (%" PRIi32 ").",
+ string_SpvOpcode(opcode), signed_offset,
+ phys_dev_props.limits.minTexelOffset);
+ } else if ((offset > phys_dev_props.limits.maxTexelOffset) &&
+ (!use_signed || (use_signed && signed_offset > 0))) {
+ skip |=
+ LogError(device, "VUID-RuntimeSpirv-OpImageSample-06436",
+ "vkCreateShaderModule(): Shader uses %s with offset (%" PRIu32
+ ") greater than VkPhysicalDeviceLimits::maxTexelOffset (%" PRIu32 ").",
+ string_SpvOpcode(opcode), offset, phys_dev_props.limits.maxTexelOffset);
+ }
}
}
}
@@ -2354,7 +2378,7 @@
// and mainly only checking the instruction in detail for a single operation
uint32_t total_shared_size = 0;
for (auto insn : *module) {
- skip |= ValidateTexelGatherOffset(module, insn);
+ skip |= ValidateTexelOffsetLimits(module, insn);
skip |= ValidateShaderCapabilitiesAndExtensions(module, insn);
skip |= ValidateShaderClock(module, insn);
skip |= ValidateShaderStageGroupNonUniform(module, pStage->stage, insn);
diff --git a/tests/vklayertests_pipeline_shader.cpp b/tests/vklayertests_pipeline_shader.cpp
index 8aaa96d..0e9a08a 100644
--- a/tests/vklayertests_pipeline_shader.cpp
+++ b/tests/vklayertests_pipeline_shader.cpp
@@ -14018,8 +14018,8 @@
%uint_n100 = OpConstant %uint 4294967196
%int_100 = OpConstant %int 100
%int_0 = OpConstant %int 0
- %22 = OpConstantComposite %v2int %int_n100 %int_100
- %23 = OpConstantComposite %v2int %int_0 %uint_n100
+ %offset_100 = OpConstantComposite %v2int %int_n100 %int_100
+%offset_n100 = OpConstantComposite %v2int %int_0 %uint_n100
; Function main
%main = OpFunction %void None %3
@@ -14027,9 +14027,9 @@
%color = OpVariable %_ptr_Function_v4float Function
%14 = OpLoad %11 %samp
; Should trigger min and max
- %24 = OpImageGather %v4float %14 %17 %int_0 ConstOffset %22
+ %24 = OpImageGather %v4float %14 %17 %int_0 ConstOffset %offset_100
; Should only trigger max since uint
- %25 = OpImageGather %v4float %14 %17 %int_0 ConstOffset %23
+ %25 = OpImageGather %v4float %14 %17 %int_0 ConstOffset %offset_n100
OpStore %color %24
OpReturn
OpFunctionEnd
@@ -14057,6 +14057,91 @@
m_errorMonitor->VerifyFound();
}
+TEST_F(VkLayerTest, TestMinAndMaxTexelOffset) {
+ TEST_DESCRIPTION("Test shader with offset less than minTexelOffset and greather than maxTexelOffset");
+
+ ASSERT_NO_FATAL_FAILURE(Init());
+ ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
+
+ if (m_device->phy().properties().limits.minTexelOffset <= -100 || m_device->phy().properties().limits.maxTexelOffset >= 100) {
+ printf("%s test needs minTexelOffset greater than -100 and maxTexelOffset less than 100. Skipping.\n", kSkipPrefix);
+ return;
+ }
+
+ const std::string spv_source = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main"
+ OpExecutionMode %main OriginUpperLeft
+ OpSource GLSL 450
+ OpDecorate %textureSampler DescriptorSet 0
+ OpDecorate %textureSampler Binding 0
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+ %10 = OpTypeImage %float 2D 0 0 0 1 Unknown
+ %11 = OpTypeSampledImage %10
+%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11
+%textureSampler = OpVariable %_ptr_UniformConstant_11 UniformConstant
+ %v2float = OpTypeVector %float 2
+ %float_0 = OpConstant %float 0
+ %17 = OpConstantComposite %v2float %float_0 %float_0
+ ; set up composite to be validated
+ %uint = OpTypeInt 32 0
+ %int = OpTypeInt 32 1
+ %v2int = OpTypeVector %int 2
+ %int_0 = OpConstant %int 0
+ %int_n100 = OpConstant %int -100
+ %uint_n100 = OpConstant %uint 4294967196
+ %int_100 = OpConstant %int 100
+ %offset_100 = OpConstantComposite %v2int %int_n100 %int_100
+%offset_n100 = OpConstantComposite %v2int %int_0 %uint_n100
+ %24 = OpConstantComposite %v2int %int_0 %int_0
+
+ %main = OpFunction %void None %3
+ %label = OpLabel
+ %14 = OpLoad %11 %textureSampler
+ %26 = OpImage %10 %14
+ ; Should trigger min and max
+ %result0 = OpImageSampleImplicitLod %v4float %14 %17 ConstOffset %offset_100
+ %result1 = OpImageFetch %v4float %26 %24 ConstOffset %offset_100
+ ; Should only trigger max since uint
+ %result2 = OpImageSampleImplicitLod %v4float %14 %17 ConstOffset %offset_n100
+ %result3 = OpImageFetch %v4float %26 %24 ConstOffset %offset_n100
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ OneOffDescriptorSet descriptor_set(m_device,
+ {
+ {0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr},
+ });
+
+ VkShaderObj const fs(m_device, spv_source, VK_SHADER_STAGE_FRAGMENT_BIT, this);
+
+ CreatePipelineHelper pipe(*this);
+ pipe.InitInfo();
+ pipe.shader_stages_ = {pipe.vs_->GetStageCreateInfo(), fs.GetStageCreateInfo()};
+ pipe.InitState();
+ pipe.pipeline_layout_ = VkPipelineLayoutObj(m_device, {&descriptor_set.layout_});
+ // as commented in SPIR-V should trigger the limits as following
+ //
+ // OpImageSampleImplicitLod
+ m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-RuntimeSpirv-OpImageSample-06435");
+ m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-RuntimeSpirv-OpImageSample-06436");
+ m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-RuntimeSpirv-OpImageSample-06436");
+ // // OpImageFetch
+ m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-RuntimeSpirv-OpImageSample-06435");
+ m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-RuntimeSpirv-OpImageSample-06436");
+ m_errorMonitor->SetDesiredFailureMsg(kErrorBit, "VUID-RuntimeSpirv-OpImageSample-06436");
+ pipe.CreateGraphicsPipeline();
+
+ m_errorMonitor->VerifyFound();
+}
+
TEST_F(VkLayerTest, RayTracingLibraryFlags) {
TEST_DESCRIPTION("Validate ray tracing pipeline flags match library flags.");