aboutsummaryrefslogtreecommitdiff
path: root/layers/shader_validation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layers/shader_validation.cpp')
-rw-r--r--layers/shader_validation.cpp291
1 files changed, 183 insertions, 108 deletions
diff --git a/layers/shader_validation.cpp b/layers/shader_validation.cpp
index b95928c1c..8f02a8de6 100644
--- a/layers/shader_validation.cpp
+++ b/layers/shader_validation.cpp
@@ -38,6 +38,50 @@
#include "spirv-tools/libspirv.h"
#include "xxhash.h"
+void decoration_set::add(uint32_t decoration, uint32_t value) {
+ switch (decoration) {
+ case spv::DecorationLocation:
+ flags |= location_bit;
+ location = value;
+ break;
+ case spv::DecorationPatch:
+ flags |= patch_bit;
+ break;
+ case spv::DecorationRelaxedPrecision:
+ flags |= relaxed_precision_bit;
+ break;
+ case spv::DecorationBlock:
+ flags |= block_bit;
+ break;
+ case spv::DecorationBufferBlock:
+ flags |= buffer_block_bit;
+ break;
+ case spv::DecorationComponent:
+ flags |= component_bit;
+ component = value;
+ break;
+ case spv::DecorationInputAttachmentIndex:
+ flags |= input_attachment_index_bit;
+ input_attachment_index = value;
+ break;
+ case spv::DecorationDescriptorSet:
+ flags |= descriptor_set_bit;
+ descriptor_set = value;
+ break;
+ case spv::DecorationBinding:
+ flags |= binding_bit;
+ binding = value;
+ break;
+ case spv::DecorationNonWritable:
+ flags |= nonwritable_bit;
+ break;
+ case spv::DecorationBuiltIn:
+ flags |= builtin_bit;
+ builtin = value;
+ break;
+ }
+}
+
enum FORMAT_TYPE {
FORMAT_TYPE_FLOAT = 1, // UNORM, SNORM, FLOAT, USCALED, SSCALED, SRGB -- anything we consider float in the shader
FORMAT_TYPE_SINT = 2,
@@ -122,6 +166,16 @@ void SHADER_MODULE_STATE::BuildDefIndex() {
def_index[insn.word(2)] = insn.offset();
break;
+ // Decorations
+ case spv::OpDecorate: {
+ auto targetId = insn.word(1);
+ decorations[targetId].add(insn.word(2), insn.len() > 3u ? insn.word(3) : 0u);
+ } break;
+ case spv::OpGroupDecorate: {
+ auto const &src = decorations[insn.word(1)];
+ for (auto i = 2u; i < insn.len(); i++) decorations[insn.word(i)].merge(src);
+ } break;
+
// Entry points ... add to the entrypoint table
case spv::OpEntryPoint: {
// Entry points do not have an id (the id is the function id) and thus need their own table
@@ -131,6 +185,7 @@ void SHADER_MODULE_STATE::BuildDefIndex() {
entry_points.emplace(entrypoint_name, EntryPoint{insn.offset(), entrypoint_stage});
break;
}
+
default:
// We don't care about any other defs for now.
break;
@@ -548,11 +603,11 @@ static spirv_inst_iter GetStructType(SHADER_MODULE_STATE const *src, spirv_inst_
}
static bool CollectInterfaceBlockMembers(SHADER_MODULE_STATE const *src, std::map<location_t, interface_var> *out,
- std::unordered_map<unsigned, unsigned> const &blocks, bool is_array_of_verts, uint32_t id,
- uint32_t type_id, bool is_patch, int /*first_location*/) {
+ bool is_array_of_verts, uint32_t id, uint32_t type_id, bool is_patch,
+ int /*first_location*/) {
// Walk down the type_id presented, trying to determine whether it's actually an interface block.
auto type = GetStructType(src, src->get_def(type_id), is_array_of_verts && !is_patch);
- if (type == src->end() || blocks.find(type.word(1)) == blocks.end()) {
+ if (type == src->end() || !(src->get_decorations(type.word(1)).flags & decoration_set::block_bit)) {
// This isn't an interface block.
return false;
}
@@ -616,6 +671,8 @@ static bool CollectInterfaceBlockMembers(SHADER_MODULE_STATE const *src, std::ma
}
static std::vector<uint32_t> FindEntrypointInterfaces(spirv_inst_iter entrypoint) {
+ assert(entrypoint.opcode() == spv::OpEntryPoint);
+
std::vector<uint32_t> interfaces;
// Find the end of the entrypoint's name string. additional zero bytes follow the actual null terminator, to fill out the
// rest of the word - so we only need to look at the last byte in the word to determine which word contains the terminator.
@@ -632,66 +689,29 @@ static std::vector<uint32_t> FindEntrypointInterfaces(spirv_inst_iter entrypoint
static std::map<location_t, interface_var> CollectInterfaceByLocation(SHADER_MODULE_STATE const *src, spirv_inst_iter entrypoint,
spv::StorageClass sinterface, bool is_array_of_verts) {
- std::unordered_map<unsigned, unsigned> var_locations;
- std::unordered_map<unsigned, unsigned> var_builtins;
- std::unordered_map<unsigned, unsigned> var_components;
- std::unordered_map<unsigned, unsigned> blocks;
- std::unordered_map<unsigned, unsigned> var_patch;
- std::unordered_map<unsigned, unsigned> var_relaxed_precision;
-
- for (auto insn : *src) {
- // We consider two interface models: SSO rendezvous-by-location, and builtins. Complain about anything that
- // fits neither model.
- if (insn.opcode() == spv::OpDecorate) {
- if (insn.word(2) == spv::DecorationLocation) {
- var_locations[insn.word(1)] = insn.word(3);
- }
-
- if (insn.word(2) == spv::DecorationBuiltIn) {
- var_builtins[insn.word(1)] = insn.word(3);
- }
-
- if (insn.word(2) == spv::DecorationComponent) {
- var_components[insn.word(1)] = insn.word(3);
- }
-
- if (insn.word(2) == spv::DecorationBlock) {
- blocks[insn.word(1)] = 1;
- }
-
- if (insn.word(2) == spv::DecorationPatch) {
- var_patch[insn.word(1)] = 1;
- }
-
- if (insn.word(2) == spv::DecorationRelaxedPrecision) {
- var_relaxed_precision[insn.word(1)] = 1;
- }
- }
- }
-
- // TODO: handle grouped decorations
// TODO: handle index=1 dual source outputs from FS -- two vars will have the same location, and we DON'T want to clobber.
std::map<location_t, interface_var> out;
- for (uint32_t word : FindEntrypointInterfaces(entrypoint)) {
- auto insn = src->get_def(word);
+ for (uint32_t iid : FindEntrypointInterfaces(entrypoint)) {
+ auto insn = src->get_def(iid);
assert(insn != src->end());
assert(insn.opcode() == spv::OpVariable);
if (insn.word(3) == static_cast<uint32_t>(sinterface)) {
+ auto d = src->get_decorations(iid);
unsigned id = insn.word(2);
unsigned type = insn.word(1);
- int location = ValueOrDefault(var_locations, id, static_cast<unsigned>(-1));
- int builtin = ValueOrDefault(var_builtins, id, static_cast<unsigned>(-1));
- unsigned component = ValueOrDefault(var_components, id, 0); // Unspecified is OK, is 0
- bool is_patch = var_patch.find(id) != var_patch.end();
- bool is_relaxed_precision = var_relaxed_precision.find(id) != var_relaxed_precision.end();
+ int location = d.location;
+ int builtin = d.builtin;
+ unsigned component = d.component;
+ bool is_patch = (d.flags & decoration_set::patch_bit) != 0;
+ bool is_relaxed_precision = (d.flags & decoration_set::relaxed_precision_bit) != 0;
if (builtin != -1)
continue;
- else if (!CollectInterfaceBlockMembers(src, &out, blocks, is_array_of_verts, id, type, is_patch, location)) {
+ else if (!CollectInterfaceBlockMembers(src, &out, is_array_of_verts, id, type, is_patch, location)) {
// A user-defined interface variable, with a location. Where a variable occupied multiple locations, emit
// one result for each.
unsigned num_locations = GetLocationsConsumedByType(src, type, is_array_of_verts && !is_patch);
@@ -844,15 +864,10 @@ static bool IsWritableDescriptorType(SHADER_MODULE_STATE const *module, uint32_t
case spv::OpTypeStruct: {
std::unordered_set<unsigned> nonwritable_members;
+ if (module->get_decorations(type.word(1)).flags & decoration_set::buffer_block_bit) is_storage_buffer = true;
for (auto insn : *module) {
- if (insn.opcode() == spv::OpDecorate && insn.word(1) == type.word(1)) {
- if (insn.word(2) == spv::DecorationBufferBlock) {
- // Legacy storage block in the Uniform storage class
- // has its struct type decorated with BufferBlock.
- is_storage_buffer = true;
- }
- } else if (insn.opcode() == spv::OpMemberDecorate && insn.word(1) == type.word(1) &&
- insn.word(3) == spv::DecorationNonWritable) {
+ if (insn.opcode() == spv::OpMemberDecorate && insn.word(1) == type.word(1) &&
+ insn.word(3) == spv::DecorationNonWritable) {
nonwritable_members.insert(insn.word(2));
}
}
@@ -869,30 +884,6 @@ static bool IsWritableDescriptorType(SHADER_MODULE_STATE const *module, uint32_t
static std::vector<std::pair<descriptor_slot_t, interface_var>> CollectInterfaceByDescriptorSlot(
debug_report_data const *report_data, SHADER_MODULE_STATE const *src, std::unordered_set<uint32_t> const &accessible_ids,
bool *has_writable_descriptor) {
- std::unordered_map<unsigned, unsigned> var_sets;
- std::unordered_map<unsigned, unsigned> var_bindings;
- std::unordered_map<unsigned, unsigned> var_nonwritable;
-
- for (auto insn : *src) {
- // All variables in the Uniform or UniformConstant storage classes are required to be decorated with both
- // DecorationDescriptorSet and DecorationBinding.
- if (insn.opcode() == spv::OpDecorate) {
- if (insn.word(2) == spv::DecorationDescriptorSet) {
- var_sets[insn.word(1)] = insn.word(3);
- }
-
- if (insn.word(2) == spv::DecorationBinding) {
- var_bindings[insn.word(1)] = insn.word(3);
- }
-
- // Note: do toplevel DecorationNonWritable out here; it applies to
- // the OpVariable rather than the type.
- if (insn.word(2) == spv::DecorationNonWritable) {
- var_nonwritable[insn.word(1)] = 1;
- }
- }
- }
-
std::vector<std::pair<descriptor_slot_t, interface_var>> out;
for (auto id : accessible_ids) {
@@ -902,15 +893,16 @@ static std::vector<std::pair<descriptor_slot_t, interface_var>> CollectInterface
if (insn.opcode() == spv::OpVariable &&
(insn.word(3) == spv::StorageClassUniform || insn.word(3) == spv::StorageClassUniformConstant ||
insn.word(3) == spv::StorageClassStorageBuffer)) {
- unsigned set = ValueOrDefault(var_sets, insn.word(2), 0);
- unsigned binding = ValueOrDefault(var_bindings, insn.word(2), 0);
+ auto d = src->get_decorations(insn.word(2));
+ unsigned set = d.descriptor_set;
+ unsigned binding = d.binding;
interface_var v = {};
v.id = insn.word(2);
v.type_id = insn.word(1);
out.emplace_back(std::make_pair(set, binding), v);
- if (var_nonwritable.find(id) == var_nonwritable.end() &&
+ if (!(d.flags & decoration_set::nonwritable_bit) &&
IsWritableDescriptorType(src, insn.word(1), insn.word(3) == spv::StorageClassStorageBuffer)) {
*has_writable_descriptor = true;
}
@@ -1559,6 +1551,8 @@ bool CoreChecks::ValidateShaderCapabilities(SHADER_MODULE_STATE const *src, VkSh
: IsEnabled([=](const DeviceFeatures &features) { return features.shader_image_footprint_features.*ptr; }) {}
FeaturePointer(VkBool32 VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT::*ptr)
: IsEnabled([=](const DeviceFeatures &features) { return features.fragment_shader_interlock_features.*ptr; }) {}
+ FeaturePointer(VkBool32 VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT::*ptr)
+ : IsEnabled([=](const DeviceFeatures &features) { return features.demote_to_helper_invocation_features.*ptr; }) {}
};
struct CapabilityInfo {
@@ -1670,6 +1664,7 @@ bool CoreChecks::ValidateShaderCapabilities(SHADER_MODULE_STATE const *src, VkSh
{spv::CapabilityFragmentShaderSampleInterlockEXT, {"VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT::fragmentShaderSampleInterlock", &VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT::fragmentShaderSampleInterlock, &DeviceExtensions::vk_ext_fragment_shader_interlock}},
{spv::CapabilityFragmentShaderPixelInterlockEXT, {"VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT::fragmentShaderPixelInterlock", &VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT::fragmentShaderPixelInterlock, &DeviceExtensions::vk_ext_fragment_shader_interlock}},
{spv::CapabilityFragmentShaderShadingRateInterlockEXT, {"VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT::fragmentShaderShadingRateInterlock", &VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT::fragmentShaderShadingRateInterlock, &DeviceExtensions::vk_ext_fragment_shader_interlock}},
+ {spv::CapabilityDemoteToHelperInvocationEXT, {"VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT::shaderDemoteToHelperInvocation", &VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT::shaderDemoteToHelperInvocation, &DeviceExtensions::vk_ext_shader_demote_to_helper_invocation}},
};
// clang-format on
@@ -2353,12 +2348,32 @@ bool CoreChecks::ValidateExecutionModes(SHADER_MODULE_STATE const *src, spirv_in
if (first_denorm_execution_mode.first == spv::ExecutionModeMax) {
// Register the first denorm execution mode found
first_denorm_execution_mode = std::make_pair(static_cast<spv::ExecutionMode>(mode), bit_width);
- } else if (first_denorm_execution_mode.first != mode && first_denorm_execution_mode.second != bit_width &&
- !enabled_features.float_controls.separateDenormSettings) {
- skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
- kVUID_Core_Shader_FeatureNotEnabled,
- "Shader uses separate denorm execution modes for different bit widths but "
- "SeparateDenormSettings is not enabled on the device");
+ } else if (first_denorm_execution_mode.first != mode && first_denorm_execution_mode.second != bit_width) {
+ switch (enabled_features.float_controls.denormBehaviorIndependence) {
+ case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY_KHR:
+ if (first_rounding_mode.second != 32 && bit_width != 32) {
+ skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT,
+ VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0, kVUID_Core_Shader_FeatureNotEnabled,
+ "Shader uses different denorm execution modes for 16 and 64-bit but "
+ "denormBehaviorIndependence is "
+ "VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY_KHR on the device");
+ }
+ break;
+
+ case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL_KHR:
+ break;
+
+ case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE_KHR:
+ skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT,
+ 0, kVUID_Core_Shader_FeatureNotEnabled,
+ "Shader uses different denorm execution modes for different bit widths but "
+ "denormBehaviorIndependence is "
+ "VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE_KHR on the device");
+ break;
+
+ default:
+ break;
+ }
}
break;
}
@@ -2377,12 +2392,32 @@ bool CoreChecks::ValidateExecutionModes(SHADER_MODULE_STATE const *src, spirv_in
if (first_denorm_execution_mode.first == spv::ExecutionModeMax) {
// Register the first denorm execution mode found
first_denorm_execution_mode = std::make_pair(static_cast<spv::ExecutionMode>(mode), bit_width);
- } else if (first_denorm_execution_mode.first != mode && first_denorm_execution_mode.second != bit_width &&
- !enabled_features.float_controls.separateDenormSettings) {
- skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
- kVUID_Core_Shader_FeatureNotEnabled,
- "Shader uses separate denorm execution modes for different bit widths but "
- "SeparateDenormSettings is not enabled on the device");
+ } else if (first_denorm_execution_mode.first != mode && first_denorm_execution_mode.second != bit_width) {
+ switch (enabled_features.float_controls.denormBehaviorIndependence) {
+ case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY_KHR:
+ if (first_rounding_mode.second != 32 && bit_width != 32) {
+ skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT,
+ VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0, kVUID_Core_Shader_FeatureNotEnabled,
+ "Shader uses different denorm execution modes for 16 and 64-bit but "
+ "denormBehaviorIndependence is "
+ "VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY_KHR on the device");
+ }
+ break;
+
+ case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL_KHR:
+ break;
+
+ case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE_KHR:
+ skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT,
+ 0, kVUID_Core_Shader_FeatureNotEnabled,
+ "Shader uses different denorm execution modes for different bit widths but "
+ "denormBehaviorIndependence is "
+ "VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE_KHR on the device");
+ break;
+
+ default:
+ break;
+ }
}
break;
}
@@ -2401,12 +2436,32 @@ bool CoreChecks::ValidateExecutionModes(SHADER_MODULE_STATE const *src, spirv_in
if (first_rounding_mode.first == spv::ExecutionModeMax) {
// Register the first rounding mode found
first_rounding_mode = std::make_pair(static_cast<spv::ExecutionMode>(mode), bit_width);
- } else if (first_rounding_mode.first != mode && first_rounding_mode.second != bit_width &&
- !enabled_features.float_controls.separateRoundingModeSettings) {
- skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
- kVUID_Core_Shader_FeatureNotEnabled,
- "Shader uses separate rounding modes for different bit widths but "
- "SeparateRoundingModeSettings is not enabled on the device");
+ } else if (first_rounding_mode.first != mode && first_rounding_mode.second != bit_width) {
+ switch (enabled_features.float_controls.roundingModeIndependence) {
+ case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY_KHR:
+ if (first_rounding_mode.second != 32 && bit_width != 32) {
+ skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT,
+ VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0, kVUID_Core_Shader_FeatureNotEnabled,
+ "Shader uses different rounding modes for 16 and 64-bit but "
+ "roundingModeIndependence is "
+ "VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY_KHR on the device");
+ }
+ break;
+
+ case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL_KHR:
+ break;
+
+ case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE_KHR:
+ skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT,
+ 0, kVUID_Core_Shader_FeatureNotEnabled,
+ "Shader uses different rounding modes for different bit widths but "
+ "roundingModeIndependence is "
+ "VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE_KHR on the device");
+ break;
+
+ default:
+ break;
+ }
}
break;
}
@@ -2425,12 +2480,32 @@ bool CoreChecks::ValidateExecutionModes(SHADER_MODULE_STATE const *src, spirv_in
if (first_rounding_mode.first == spv::ExecutionModeMax) {
// Register the first rounding mode found
first_rounding_mode = std::make_pair(static_cast<spv::ExecutionMode>(mode), bit_width);
- } else if (first_rounding_mode.first != mode && first_rounding_mode.second != bit_width &&
- !enabled_features.float_controls.separateRoundingModeSettings) {
- skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
- kVUID_Core_Shader_FeatureNotEnabled,
- "Shader uses separate rounding modes for different bit widths but "
- "SeparateRoundingModeSettings is not enabled on the device");
+ } else if (first_rounding_mode.first != mode && first_rounding_mode.second != bit_width) {
+ switch (enabled_features.float_controls.roundingModeIndependence) {
+ case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY_KHR:
+ if (first_rounding_mode.second != 32 && bit_width != 32) {
+ skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT,
+ VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0, kVUID_Core_Shader_FeatureNotEnabled,
+ "Shader uses different rounding modes for 16 and 64-bit but "
+ "roundingModeIndependence is "
+ "VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY_KHR on the device");
+ }
+ break;
+
+ case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL_KHR:
+ break;
+
+ case VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE_KHR:
+ skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT,
+ 0, kVUID_Core_Shader_FeatureNotEnabled,
+ "Shader uses different rounding modes for different bit widths but "
+ "roundingModeIndependence is "
+ "VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE_KHR on the device");
+ break;
+
+ default:
+ break;
+ }
}
break;
}