layers: validate capabilities against device features
V2: squashed in feedback.
V3: More careful about VkBool32
Signed-off-by: Chris Forbes <chrisforbes@google.com>
diff --git a/demos/tri.c b/demos/tri.c
index 9f3cfac..aa0fbce 100644
--- a/demos/tri.c
+++ b/demos/tri.c
@@ -2068,6 +2068,14 @@
demo->queue_props);
assert(demo->queue_count >= 1);
+ VkPhysicalDeviceFeatures features;
+ vkGetPhysicalDeviceFeatures(demo->gpu, &features);
+
+ if (!features.shaderClipDistance) {
+ ERR_EXIT("Required device feature `shaderClipDistance` not supported\n",
+ "GetPhysicalDeviceFeatures failure");
+ }
+
// Graphics queue and MemMgr queue can be separate.
// TODO: Add support for separate queues, including synchronization,
// and appropriate tracking for QueueSubmit
@@ -2084,6 +2092,10 @@
.queueCount = 1,
.pQueuePriorities = queue_priorities};
+ VkPhysicalDeviceFeatures features = {
+ .shaderClipDistance = VK_TRUE,
+ };
+
VkDeviceCreateInfo device = {
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pNext = NULL,
@@ -2096,6 +2108,7 @@
: NULL),
.enabledExtensionCount = demo->enabled_extension_count,
.ppEnabledExtensionNames = (const char *const *)demo->extension_names,
+ .pEnabledFeatures = &features,
};
err = vkCreateDevice(demo->gpu, &device, NULL, &demo->device);
diff --git a/layers/core_validation.cpp b/layers/core_validation.cpp
index b6a81e3..b0cb275 100644
--- a/layers/core_validation.cpp
+++ b/layers/core_validation.cpp
@@ -2519,6 +2519,161 @@
}
}
+static VkBool32 require_feature(layer_data *my_data, VkBool32 feature, char const *feature_name) {
+ if (!feature) {
+ if (log_msg(my_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT,
+ /* dev */ 0, __LINE__, SHADER_CHECKER_FEATURE_NOT_ENABLED, "SC",
+ "Shader requires VkPhysicalDeviceFeatures::%s but is not "
+ "enabled on the device",
+ feature_name)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static VkBool32 validate_shader_capabilities(layer_data *my_data, VkDevice dev, shader_module const *src)
+{
+ VkBool32 pass = VK_TRUE;
+
+ auto enabledFeatures = &my_data->physDevProperties.features;
+
+ for (auto insn : *src) {
+ if (insn.opcode() == spv::OpCapability) {
+ switch (insn.word(1)) {
+ case spv::CapabilityMatrix:
+ case spv::CapabilityShader:
+ case spv::CapabilityInputAttachment:
+ case spv::CapabilitySampled1D:
+ case spv::CapabilityImage1D:
+ case spv::CapabilitySampledBuffer:
+ case spv::CapabilityImageBuffer:
+ case spv::CapabilityImageQuery:
+ case spv::CapabilityDerivativeControl:
+ // Always supported by a Vulkan 1.0 implementation -- no feature bits.
+ break;
+
+ case spv::CapabilityGeometry:
+ pass &= require_feature(my_data, enabledFeatures->geometryShader, "geometryShader");
+ break;
+
+ case spv::CapabilityTessellation:
+ pass &= require_feature(my_data, enabledFeatures->tessellationShader, "tessellationShader");
+ break;
+
+ case spv::CapabilityFloat64:
+ pass &= require_feature(my_data, enabledFeatures->shaderFloat64, "shaderFloat64");
+ break;
+
+ case spv::CapabilityInt64:
+ pass &= require_feature(my_data, enabledFeatures->shaderInt64, "shaderInt64");
+ break;
+
+ case spv::CapabilityTessellationPointSize:
+ case spv::CapabilityGeometryPointSize:
+ pass &= require_feature(my_data, enabledFeatures->shaderTessellationAndGeometryPointSize,
+ "shaderTessellationAndGeometryPointSize");
+ break;
+
+ case spv::CapabilityImageGatherExtended:
+ pass &= require_feature(my_data, enabledFeatures->shaderImageGatherExtended, "shaderImageGatherExtended");
+ break;
+
+ case spv::CapabilityStorageImageMultisample:
+ pass &= require_feature(my_data, enabledFeatures->shaderStorageImageMultisample, "shaderStorageImageMultisample");
+ break;
+
+ case spv::CapabilityUniformBufferArrayDynamicIndexing:
+ pass &= require_feature(my_data, enabledFeatures->shaderUniformBufferArrayDynamicIndexing,
+ "shaderUniformBufferArrayDynamicIndexing");
+ break;
+
+ case spv::CapabilitySampledImageArrayDynamicIndexing:
+ pass &= require_feature(my_data, enabledFeatures->shaderSampledImageArrayDynamicIndexing,
+ "shaderSampledImageArrayDynamicIndexing");
+ break;
+
+ case spv::CapabilityStorageBufferArrayDynamicIndexing:
+ pass &= require_feature(my_data, enabledFeatures->shaderStorageBufferArrayDynamicIndexing,
+ "shaderStorageBufferArrayDynamicIndexing");
+ break;
+
+ case spv::CapabilityStorageImageArrayDynamicIndexing:
+ pass &= require_feature(my_data, enabledFeatures->shaderStorageImageArrayDynamicIndexing,
+ "shaderStorageImageArrayDynamicIndexing");
+ break;
+
+ case spv::CapabilityClipDistance:
+ pass &= require_feature(my_data, enabledFeatures->shaderClipDistance, "shaderClipDistance");
+ break;
+
+ case spv::CapabilityCullDistance:
+ pass &= require_feature(my_data, enabledFeatures->shaderCullDistance, "shaderCullDistance");
+ break;
+
+ case spv::CapabilityImageCubeArray:
+ pass &= require_feature(my_data, enabledFeatures->imageCubeArray, "imageCubeArray");
+ break;
+
+ case spv::CapabilitySampleRateShading:
+ pass &= require_feature(my_data, enabledFeatures->sampleRateShading, "sampleRateShading");
+ break;
+
+ case spv::CapabilitySparseResidency:
+ pass &= require_feature(my_data, enabledFeatures->shaderResourceResidency, "shaderResourceResidency");
+ break;
+
+ case spv::CapabilityMinLod:
+ pass &= require_feature(my_data, enabledFeatures->shaderResourceMinLod, "shaderResourceMinLod");
+ break;
+
+ case spv::CapabilitySampledCubeArray:
+ pass &= require_feature(my_data, enabledFeatures->imageCubeArray, "imageCubeArray");
+ break;
+
+ case spv::CapabilityImageMSArray:
+ pass &= require_feature(my_data, enabledFeatures->shaderStorageImageMultisample, "shaderStorageImageMultisample");
+ break;
+
+ case spv::CapabilityStorageImageExtendedFormats:
+ pass &= require_feature(my_data, enabledFeatures->shaderStorageImageExtendedFormats,
+ "shaderStorageImageExtendedFormats");
+ break;
+
+ case spv::CapabilityInterpolationFunction:
+ pass &= require_feature(my_data, enabledFeatures->sampleRateShading, "sampleRateShading");
+ break;
+
+ case spv::CapabilityStorageImageReadWithoutFormat:
+ pass &= require_feature(my_data, enabledFeatures->shaderStorageImageReadWithoutFormat,
+ "shaderStorageImageReadWithoutFormat");
+ break;
+
+ case spv::CapabilityStorageImageWriteWithoutFormat:
+ pass &= require_feature(my_data, enabledFeatures->shaderStorageImageWriteWithoutFormat,
+ "shaderStorageImageWriteWithoutFormat");
+ break;
+
+ case spv::CapabilityMultiViewport:
+ pass &= require_feature(my_data, enabledFeatures->multiViewport, "multiViewport");
+ break;
+
+ default:
+ if (log_msg(my_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT, /* dev */0,
+ __LINE__, SHADER_CHECKER_BAD_CAPABILITY, "SC",
+ "Shader declares capability %u, not supported in Vulkan.",
+ insn.word(1)))
+ pass = VK_FALSE;
+ break;
+ }
+ }
+ }
+
+ return pass;
+}
+
+
// Validate that the shaders used by the given pipeline
// As a side effect this function also records the sets that are actually used by the pipeline
static VkBool32 validate_pipeline_shaders(layer_data *my_data, VkDevice dev, PIPELINE_NODE *pPipeline) {
@@ -2564,6 +2719,9 @@
}
}
+ /* validate shader capabilities against enabled device features */
+ pass = validate_shader_capabilities(my_data, dev, module) && pass;
+
/* mark accessible ids */
std::unordered_set<uint32_t> accessible_ids;
mark_accessible_ids(module, entrypoints[stage_id], accessible_ids);
diff --git a/layers/core_validation.h b/layers/core_validation.h
index 8d8941f..fea8ffd 100644
--- a/layers/core_validation.h
+++ b/layers/core_validation.h
@@ -404,6 +404,8 @@
SHADER_CHECKER_PUSH_CONSTANT_NOT_ACCESSIBLE_FROM_STAGE, // Push constant range exists, but not accessible from stage
SHADER_CHECKER_DESCRIPTOR_TYPE_MISMATCH, // Descriptor type does not match shader resource type
SHADER_CHECKER_DESCRIPTOR_NOT_ACCESSIBLE_FROM_STAGE, // Descriptor used by shader, but not accessible from stage
+ SHADER_CHECKER_FEATURE_NOT_ENABLED, // Shader uses capability requiring a feature not enabled on device
+ SHADER_CHECKER_BAD_CAPABILITY, // Shader uses capability not supported by Vulkan (OpenCL features)
} SHADER_CHECKER_ERROR;
typedef enum _DRAW_TYPE {
diff --git a/layers/vk_validation_layer_details.md b/layers/vk_validation_layer_details.md
index ec36b3b..7bc554e 100644
--- a/layers/vk_validation_layer_details.md
+++ b/layers/vk_validation_layer_details.md
@@ -136,6 +136,8 @@
| Push constant not accessible from stage | Flags error if the push constant range containing a push constant block member is not accessible from the current shader stage. | PUSH_CONSTANT_NOT_ACCESSIBLE_FROM_STAGE | vkCreateGraphicsPipelines | TBD | NA |
| Descriptor not accessible from stage | Flags error if a descriptor used by a shader stage does not include that stage in its stageFlags | DESCRIPTOR_NOT_ACCESSIBLE_FROM_STAGE | vkCreateGraphicsPipelines | TBD | NA |
| Descriptor type mismatch | Flags error if a descriptor type does not match the shader resource type. | DESCRIPTOR_TYPE_MISMATCH | vkCreateGraphicsPipelines | TBD | NA |
+| Feature not enabled | Flags error if a capability declared by the shader requires a feature not enabled on the device | FEATURE_NOT_ENABLED | vkCreateGraphicsPipelines | TBD | NA |
+| Bad capability | Flags error if a capability declared by the shader is not supported by Vulkan shaders | BAD_CAPABILITY | vkCreateGraphicsPipelines | TBD | NA |
| NA | Enum used for informational messages | NONE | | NA | None |
### VK_LAYER_LUNARG_core_validation Shader Checker Pending Work