diff options
| author | Faith Ekstrand <faith.ekstrand@collabora.com> | 2025-10-23 15:29:47 -0400 |
|---|---|---|
| committer | Marge Bot <marge-bot@fdo.invalid> | 2025-11-06 15:27:29 +0000 |
| commit | cb7df84430e6b588b469801c3ad9b30931c9f5b6 (patch) | |
| tree | 1686e5c8a84be007e13da9eba22f7f466b94ac9a | |
| parent | 59a89cd762ebb5fa7c2e0da89f79e3baa0d90f97 (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.rst | 5 | ||||
| -rw-r--r-- | src/vulkan/runtime/vk_shader.c | 71 | ||||
| -rw-r--r-- | src/vulkan/runtime/vk_shader.h | 2 |
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); |