About Social Code
aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFaith Ekstrand <faith.ekstrand@collabora.com>2025-10-23 15:29:47 -0400
committerMarge Bot <marge-bot@fdo.invalid>2025-11-06 15:27:29 +0000
commitcb7df84430e6b588b469801c3ad9b30931c9f5b6 (patch)
tree1686e5c8a84be007e13da9eba22f7f466b94ac9a
parent59a89cd762ebb5fa7c2e0da89f79e3baa0d90f97 (diff)
vulkan/runtime: Add an environment variable to validate shader binaries
Setting MESA_VK_VALIDATE_SHADER_BINARIES will cause the shader code to round-trip every shader through [de]serialize and only ever use the deserialized version. This catches bugs where the driver may drop things in the [de]serialization process. It also deserializes the new shader again and compares it against the original to ensure that deserialize -> serialize is idempotent. Acked-by: Eric R. Smith <eric.smith@collabora.com> Reviewed-by: Lars-Ivar Hesselberg Simonsen <lars-ivar.simonsen@arm.com> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/36647>
-rw-r--r--docs/envvars.rst5
-rw-r--r--src/vulkan/runtime/vk_shader.c71
-rw-r--r--src/vulkan/runtime/vk_shader.h2
3 files changed, 78 insertions, 0 deletions
diff --git a/docs/envvars.rst b/docs/envvars.rst
index 6fbf8e6bc03..22123800ce7 100644
--- a/docs/envvars.rst
+++ b/docs/envvars.rst
@@ -353,6 +353,11 @@ Core Mesa environment variables
lost device. This is extremely useful when testing as it prevents the
test suite from continuing on with a lost device.
+.. envvar:: MESA_VK_VALIDATE_SHADER_BINARIES
+
+ enables extra validation of shader and pipeline binaries to ensure
+ consistency of driver binaries.
+
.. envvar:: MESA_VK_ENABLE_SUBMIT_THREAD
for Vulkan drivers which support real timeline semaphores, this forces
diff --git a/src/vulkan/runtime/vk_shader.c b/src/vulkan/runtime/vk_shader.c
index 6ba9d450905..755dec5232d 100644
--- a/src/vulkan/runtime/vk_shader.c
+++ b/src/vulkan/runtime/vk_shader.c
@@ -102,6 +102,15 @@ vk_shader_free(struct vk_device *device,
vk_free2(&device->alloc, alloc, shader);
}
+DEBUG_GET_ONCE_BOOL_OPTION(vk_validate_shader_binaries,
+ "MESA_VK_VALIDATE_SHADER_BINARIES",
+ false);
+bool
+vk_validate_shader_binaries(void)
+{
+ return debug_get_option_vk_validate_shader_binaries();
+}
+
int
vk_shader_cmp_graphics_stages(mesa_shader_stage a, mesa_shader_stage b)
{
@@ -139,6 +148,63 @@ vk_shader_cmp_rt_stages(mesa_shader_stage a, mesa_shader_stage b)
return stage_order[a] - stage_order[b];
}
+/** Tries to re-create the shader by round-tripping through serialization
+ *
+ * If the [de]serialize fails, the original shader is left intact but it will
+ * assert fail in debug builds. If re-creation succeeds, the original shader
+ * is replaced with the new one, ensuring that the driver only ever executes
+ * shaders that come from binaries.
+ */
+static void
+vk_shader_recreate(struct vk_device *device,
+ const VkAllocationCallbacks* pAllocator,
+ struct vk_shader **shader_inout)
+{
+ const struct vk_device_shader_ops *ops = device->shader_ops;
+ const uint32_t binary_version =
+ device->physical->properties.shaderBinaryVersion;
+
+ struct blob writer;
+ blob_init(&writer);
+
+ struct vk_shader *old_shader = *shader_inout;
+ bool success = old_shader->ops->serialize(device, old_shader, &writer);
+ if (!success) {
+ assert(!"Failed to serialize shader");
+ blob_finish(&writer);
+ return;
+ }
+
+ struct blob_reader reader;
+ blob_reader_init(&reader, writer.data, writer.size);
+
+ struct vk_shader *new_shader;
+ VkResult result = ops->deserialize(device, &reader, binary_version,
+ pAllocator, &new_shader);
+ if (result != VK_SUCCESS) {
+ assert(!"Failed to deserialize shader");
+ blob_finish(&writer);
+ return;
+ }
+
+ /* Serialize again and assert that they're the same */
+#ifndef NDEBUG
+ {
+ struct blob writer2;
+ blob_init(&writer2);
+ success = new_shader->ops->serialize(device, new_shader, &writer2);
+ assert(success && "Failed to serialize shader");
+ assert(writer.size == writer2.size);
+ assert(memcmp(writer.data, writer2.data, writer.size) == 0);
+ blob_finish(&writer2);
+ }
+#endif
+
+ blob_finish(&writer);
+ vk_shader_destroy(device, old_shader, pAllocator);
+ *shader_inout = new_shader;
+}
+
VkResult
vk_compile_shaders(struct vk_device *device,
uint32_t shader_count,
@@ -156,6 +222,11 @@ vk_compile_shaders(struct vk_device *device,
if (result != VK_SUCCESS)
return result;
+ if (vk_validate_shader_binaries()) {
+ for (uint32_t i = 0; i < shader_count; i++)
+ vk_shader_recreate(device, pAllocator, &shaders_out[i]);
+ }
+
return VK_SUCCESS;
}
diff --git a/src/vulkan/runtime/vk_shader.h b/src/vulkan/runtime/vk_shader.h
index 57e940b3bba..12040c38c5f 100644
--- a/src/vulkan/runtime/vk_shader.h
+++ b/src/vulkan/runtime/vk_shader.h
@@ -47,6 +47,8 @@ struct vk_physical_device;
struct vk_pipeline;
struct vk_pipeline_robustness_state;
+bool vk_validate_shader_binaries(void);
+
int vk_shader_cmp_graphics_stages(mesa_shader_stage a, mesa_shader_stage b);
int vk_shader_cmp_rt_stages(mesa_shader_stage a, mesa_shader_stage b);