Browse Source

GPU: Expose debug information from devices

Co-authored-by: Nikita Kogut <glinka1202@gmail.com>
Lucas Murray 2 weeks ago
parent
commit
f78aa4d8ea

+ 128 - 0
include/SDL3/SDL_gpu.h

@@ -2147,6 +2147,8 @@ extern SDL_DECLSPEC SDL_GPUDevice * SDLCALL SDL_CreateGPUDevice(
  *   properties and validations, defaults to true.
  * - `SDL_PROP_GPU_DEVICE_CREATE_PREFERLOWPOWER_BOOLEAN`: enable to prefer
  *   energy efficiency over maximum GPU performance, defaults to false.
+ * - `SDL_PROP_GPU_DEVICE_CREATE_VERBOSE_BOOLEAN`: enable to automatically log
+ *   useful debug information on device creation, defaults to true.
  * - `SDL_PROP_GPU_DEVICE_CREATE_NAME_STRING`: the name of the GPU driver to
  *   use, if a specific one is desired.
  *
@@ -2186,6 +2188,7 @@ extern SDL_DECLSPEC SDL_GPUDevice * SDLCALL SDL_CreateGPUDeviceWithProperties(
 
 #define SDL_PROP_GPU_DEVICE_CREATE_DEBUGMODE_BOOLEAN          "SDL.gpu.device.create.debugmode"
 #define SDL_PROP_GPU_DEVICE_CREATE_PREFERLOWPOWER_BOOLEAN     "SDL.gpu.device.create.preferlowpower"
+#define SDL_PROP_GPU_DEVICE_CREATE_VERBOSE_BOOLEAN            "SDL.gpu.device.create.verbose"
 #define SDL_PROP_GPU_DEVICE_CREATE_NAME_STRING                "SDL.gpu.device.create.name"
 #define SDL_PROP_GPU_DEVICE_CREATE_SHADERS_PRIVATE_BOOLEAN    "SDL.gpu.device.create.shaders.private"
 #define SDL_PROP_GPU_DEVICE_CREATE_SHADERS_SPIRV_BOOLEAN      "SDL.gpu.device.create.shaders.spirv"
@@ -2257,6 +2260,131 @@ extern SDL_DECLSPEC const char * SDLCALL SDL_GetGPUDeviceDriver(SDL_GPUDevice *d
  */
 extern SDL_DECLSPEC SDL_GPUShaderFormat SDLCALL SDL_GetGPUShaderFormats(SDL_GPUDevice *device);
 
+/**
+ * Returns a property group containing read-only debug information associated
+ * with this GPU context. All properties are optionally provided and may differ
+ * between GPU backends and SDL versions.
+ *
+ * The following properties are provided by SDL:
+ *
+ * ### `SDL_PROP_GPU_DEVICE_DEBUG_NAME_STRING`
+ *
+ * Contains the name of the underlying device as reported by the system driver.
+ * This string has no standardized format, is highly inconsistent between
+ * hardware devices and drivers, and is able to change at any time. Do not
+ * attempt to parse this string as it is bound to fail at some point in the
+ * future when system drivers are updated, new hardware devices are introduced,
+ * or when SDL adds new GPU backends or modifies existing ones.
+ *
+ * Strings that have been found in the wild include:
+ *
+ * - GTX 970
+ * - GeForce GTX 970
+ * - NVIDIA GeForce GTX 970
+ * - Microsoft Direct3D12 (NVIDIA GeForce GTX 970)
+ * - NVIDIA Graphics Device
+ * - GeForce GPU
+ * - P106-100
+ * - AMD 15D8:C9
+ * - AMD Custom GPU 0405
+ * - AMD Radeon (TM) Graphics
+ * - ASUS Radeon RX 470 Series
+ * - Intel(R) Arc(tm) A380 Graphics (DG2)
+ * - Virtio-GPU Venus (NVIDIA TITAN V)
+ * - SwiftShader Device (LLVM 16.0.0)
+ * - llvmpipe (LLVM 15.0.4, 256 bits)
+ * - Microsoft Basic Render Driver
+ * - unknown device
+ *
+ * The above list shows that the same device can have different formats, the
+ * vendor name may or may not appear in the string, the included vendor name may
+ * not be the vendor of the chipset on the device, some manufacturers include
+ * pseudo-legal marks while others don't, some devices may not use a marketing
+ * name in the string, the device string may be wrapped by the name of a
+ * translation interface, the device may be emulated in software, or the string
+ * may contain generic text that does not identify the device at all.
+ *
+ * ### `SDL_PROP_GPU_DEVICE_DEBUG_DRIVER_NAME_STRING`
+ *
+ * Contains the self-reported name of the underlying system driver.
+ *
+ * Strings that have been found in the wild include:
+ *
+ * - Intel Corporation
+ * - Intel open-source Mesa driver
+ * - Qualcomm Technologies Inc. Adreno Vulkan Driver
+ * - MoltenVK
+ * - Mali-G715
+ * - venus
+ *
+ * ### `SDL_PROP_GPU_DEVICE_DEBUG_DRIVER_VERSION_STRING`
+ *
+ * Contains the self-reported version of the underlying system driver. This is a
+ * relatively short version string in an unspecified format. If
+ * SDL_PROP_GPU_DEVICE_DEBUG_DRIVER_INFO_STRING is available then that property
+ * should be preferred over this one as it may contain additional information
+ * that is useful for identifying the exact driver version used.
+ *
+ * Strings that have been found in the wild include:
+ *
+ * - 53.0.0
+ * - 0.405.2463
+ * - 32.0.15.6614
+ *
+ * ### `SDL_PROP_GPU_DEVICE_DEBUG_DRIVER_INFO_STRING`
+ *
+ * Contains the detailed version information of the underlying system driver as
+ * reported by the driver. This is an arbitrary string with no standardized
+ * format and it may contain newlines. This property should be preferred over
+ * SDL_PROP_GPU_DEVICE_DEBUG_DRIVER_VERSION_STRING if it is available as it
+ * usually contains the same information but in a format that is easier to read.
+ *
+ * Strings that have been found in the wild include:
+ *
+ * - 101.6559
+ * - 1.2.11
+ * - Mesa 21.2.2 (LLVM 12.0.1)
+ * - Mesa 22.2.0-devel (git-f226222 2022-04-14 impish-oibaf-ppa)
+ * - v1.r53p0-00eac0.824c4f31403fb1fbf8ee1042422c2129
+ *
+ * As well as the multiline string (which has a trailing newline):
+ *
+ * ```
+ * Driver Build: 85da404, I46ff5fc46f, 1606794520
+ * Date: 11/30/20
+ * Compiler Version: EV031.31.04.01
+ * Driver Branch: promo490_3_Google
+ * ```
+ *
+ * ### `SDL_PROP_GPU_DEVICE_DEBUG_VULKAN_CONFORMANCE_STRING`
+ *
+ * When using the Vulkan backend, contains the highest Vulkan version number
+ * that the system driver claims that the underlying hardware conforms to. This
+ * is self-reported and may not be truthful.
+ *
+ * Strings that have been found in the wild include:
+ *
+ * - 0.0.0.0
+ * - 1.0.0.0
+ * - 1.3.8.2
+ *
+ * \param device a GPU context to query.
+ * \returns a valid property ID or 0. A value of 0 indicates that no properties
+ *          are available and is not an error. The returned property group is
+ *          owned by SDL and has the same lifetime as the containing context. It
+ *          should not be destroyed manually when no longer needed.
+ *
+ * \since This function is available since SDL 3.4.0.
+ */
+extern SDL_DECLSPEC SDL_PropertiesID SDLCALL SDL_GetGPUDeviceDebugProperties(
+    SDL_GPUDevice *device);
+
+#define SDL_PROP_GPU_DEVICE_DEBUG_NAME_STRING               "SDL.gpu.device.debug.name"
+#define SDL_PROP_GPU_DEVICE_DEBUG_DRIVER_NAME_STRING        "SDL.gpu.device.debug.driver_name"
+#define SDL_PROP_GPU_DEVICE_DEBUG_DRIVER_VERSION_STRING     "SDL.gpu.device.debug.driver_version"
+#define SDL_PROP_GPU_DEVICE_DEBUG_DRIVER_INFO_STRING        "SDL.gpu.device.debug.driver_info"
+#define SDL_PROP_GPU_DEVICE_DEBUG_VULKAN_CONFORMANCE_STRING "SDL.gpu.device.debug.vulkan.conformance"
+
 /* State Creation */
 
 /**

+ 1 - 0
src/dynapi/SDL_dynapi.sym

@@ -1248,6 +1248,7 @@ SDL3_0.0.0 {
     SDL_GetWindowProgressValue;
     SDL_SetRenderTextureAddressMode;
     SDL_GetRenderTextureAddressMode;
+    SDL_GetGPUDeviceDebugProperties;
     # extra symbols go here (don't modify this line)
   local: *;
 };

+ 1 - 0
src/dynapi/SDL_dynapi_overrides.h

@@ -1273,3 +1273,4 @@
 #define SDL_GetWindowProgressValue SDL_GetWindowProgressValue_REAL
 #define SDL_SetRenderTextureAddressMode SDL_SetRenderTextureAddressMode_REAL
 #define SDL_GetRenderTextureAddressMode SDL_GetRenderTextureAddressMode_REAL
+#define SDL_GetGPUDeviceDebugProperties SDL_GetGPUDeviceDebugProperties_REAL

+ 1 - 0
src/dynapi/SDL_dynapi_procs.h

@@ -1281,3 +1281,4 @@ SDL_DYNAPI_PROC(SDL_ProgressState,SDL_GetWindowProgressState,(SDL_Window *a),(a)
 SDL_DYNAPI_PROC(float,SDL_GetWindowProgressValue,(SDL_Window *a),(a),return)
 SDL_DYNAPI_PROC(bool,SDL_SetRenderTextureAddressMode,(SDL_Renderer *a,SDL_TextureAddressMode b,SDL_TextureAddressMode c),(a,b,c),return)
 SDL_DYNAPI_PROC(bool,SDL_GetRenderTextureAddressMode,(SDL_Renderer *a,SDL_TextureAddressMode *b,SDL_TextureAddressMode *c),(a,b,c),return)
+SDL_DYNAPI_PROC(SDL_PropertiesID,SDL_GetGPUDeviceDebugProperties,(SDL_GPUDevice *a),(a),return)

+ 7 - 0
src/gpu/SDL_gpu.c

@@ -586,6 +586,13 @@ SDL_GPUShaderFormat SDL_GetGPUShaderFormats(SDL_GPUDevice *device)
     return device->shader_formats;
 }
 
+SDL_PropertiesID SDL_GetGPUDeviceDebugProperties(SDL_GPUDevice *device)
+{
+    CHECK_DEVICE_MAGIC(device, 0);
+
+    return device->GetDeviceDebugProperties(device);
+}
+
 Uint32 SDL_GPUTextureFormatTexelBlockSize(
     SDL_GPUTextureFormat format)
 {

+ 4 - 1
src/gpu/SDL_sysgpu.h

@@ -459,10 +459,12 @@ typedef struct SDL_GPURenderer SDL_GPURenderer;
 
 struct SDL_GPUDevice
 {
-    // Quit
+    // Device
 
     void (*DestroyDevice)(SDL_GPUDevice *device);
 
+    SDL_PropertiesID (*GetDeviceDebugProperties)(SDL_GPUDevice *device);
+
     // State Creation
 
     SDL_GPUComputePipeline *(*CreateComputePipeline)(
@@ -894,6 +896,7 @@ struct SDL_GPUDevice
     result->func = name##_##func;
 #define ASSIGN_DRIVER(name)                                 \
     ASSIGN_DRIVER_FUNC(DestroyDevice, name)                 \
+    ASSIGN_DRIVER_FUNC(GetDeviceDebugProperties, name)      \
     ASSIGN_DRIVER_FUNC(CreateComputePipeline, name)         \
     ASSIGN_DRIVER_FUNC(CreateGraphicsPipeline, name)        \
     ASSIGN_DRIVER_FUNC(CreateSampler, name)                 \

+ 43 - 5
src/gpu/d3d12/SDL_gpu_d3d12.c

@@ -765,6 +765,7 @@ struct D3D12Renderer
     // FIXME: these might not be necessary since we're not using custom heaps
     bool UMA;
     bool UMACacheCoherent;
+    SDL_PropertiesID debugProps;
     Uint32 allowedFramesInFlight;
 
     // Indirect command signatures
@@ -1535,6 +1536,8 @@ static void D3D12_INTERNAL_DestroyRenderer(D3D12Renderer *renderer)
     SDL_free(renderer->graphicsPipelinesToDestroy);
     SDL_free(renderer->computePipelinesToDestroy);
 
+    SDL_DestroyProperties(renderer->debugProps);
+
     // Tear down D3D12 objects
     if (renderer->indirectDrawCommandSignature) {
         ID3D12CommandSignature_Release(renderer->indirectDrawCommandSignature);
@@ -1622,6 +1625,12 @@ static void D3D12_DestroyDevice(SDL_GPUDevice *device)
     SDL_free(device);
 }
 
+static SDL_PropertiesID D3D12_GetDeviceDebugProperties(SDL_GPUDevice *device)
+{
+    D3D12Renderer *renderer = (D3D12Renderer *)device->driverData;
+    return renderer->debugProps;
+}
+
 // Barriers
 
 static inline Uint32 D3D12_INTERNAL_CalcSubresource(
@@ -8512,6 +8521,11 @@ static SDL_GPUDevice *D3D12_CreateDevice(bool debugMode, bool preferLowPower, SD
     D3D12_FEATURE_DATA_ARCHITECTURE architecture;
     D3D12_COMMAND_QUEUE_DESC queueDesc;
 
+    bool verboseLogs = SDL_GetBooleanProperty(
+        props,
+        SDL_PROP_GPU_DEVICE_CREATE_VERBOSE_BOOLEAN,
+        true);
+
     renderer = (D3D12Renderer *)SDL_calloc(1, sizeof(D3D12Renderer));
 
 #if !(defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
@@ -8612,15 +8626,39 @@ static SDL_GPUDevice *D3D12_CreateDevice(bool debugMode, bool preferLowPower, SD
         CHECK_D3D12_ERROR_AND_RETURN("Could not get adapter driver version", NULL);
     }
 
-    SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "SDL_GPU Driver: D3D12");
-    SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "D3D12 Adapter: %S", adapterDesc.Description);
-    SDL_LogInfo(
-        SDL_LOG_CATEGORY_GPU,
-        "D3D12 Driver: %d.%d.%d.%d",
+    renderer->debugProps = SDL_CreateProperties();
+    if (verboseLogs) {
+        SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "SDL_GPU Driver: D3D12");
+    }
+
+    // Record device name
+    char *deviceName = SDL_iconv_wchar_utf8(&adapterDesc.Description[0]);
+    SDL_SetStringProperty(
+        renderer->debugProps,
+        SDL_PROP_GPU_DEVICE_DEBUG_NAME_STRING,
+        deviceName);
+    if (verboseLogs) {
+        SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "D3D12 Adapter: %s", deviceName);
+    }
+    SDL_free(deviceName);
+
+    // Record driver version
+    char driverVer[64];
+    (void)SDL_snprintf(
+        driverVer,
+        SDL_arraysize(driverVer),
+        "%d.%d.%d.%d",
         HIWORD(umdVersion.HighPart),
         LOWORD(umdVersion.HighPart),
         HIWORD(umdVersion.LowPart),
         LOWORD(umdVersion.LowPart));
+    SDL_SetStringProperty(
+        renderer->debugProps,
+        SDL_PROP_GPU_DEVICE_DEBUG_DRIVER_VERSION_STRING,
+        driverVer);
+    if (verboseLogs) {
+        SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "D3D12 Driver: %s", driverVer);
+    }
 #endif
 
     // Load the D3D library

+ 29 - 6
src/gpu/metal/SDL_gpu_metal.m

@@ -643,6 +643,7 @@ struct MetalRenderer
     id<MTLCommandQueue> queue;
 
     bool debugMode;
+    SDL_PropertiesID debugProps;
     Uint32 allowedFramesInFlight;
 
     MetalWindowData **claimedWindows;
@@ -765,11 +766,20 @@ static void METAL_DestroyDevice(SDL_GPUDevice *device)
     // Release the command queue
     renderer->queue = nil;
 
+    // Release debug properties
+    SDL_DestroyProperties(renderer->debugProps);
+
     // Free the primary structures
     SDL_free(renderer);
     SDL_free(device);
 }
 
+static SDL_PropertiesID METAL_GetDeviceDebugProperties(SDL_GPUDevice *device)
+{
+    MetalRenderer *renderer = (MetalRenderer *)device->driverData;
+    return renderer->debugProps;
+}
+
 // Resource tracking
 
 static void METAL_INTERNAL_TrackBuffer(
@@ -4444,6 +4454,11 @@ static SDL_GPUDevice *METAL_CreateDevice(bool debugMode, bool preferLowPower, SD
         id<MTLDevice> device = NULL;
         bool hasHardwareSupport = false;
 
+        bool verboseLogs = SDL_GetBooleanProperty(
+            props,
+            SDL_PROP_GPU_DEVICE_CREATE_VERBOSE_BOOLEAN,
+            true);
+
         if (debugMode) {
             /* Due to a Metal driver quirk, once a MTLDevice has been created
              * with this environment variable set, the Metal validation layers
@@ -4497,12 +4512,20 @@ static SDL_GPUDevice *METAL_CreateDevice(bool debugMode, bool preferLowPower, SD
         renderer->device = device;
         renderer->queue = [device newCommandQueue];
 
-        // Print driver info
-        SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "SDL_GPU Driver: Metal");
-        SDL_LogInfo(
-            SDL_LOG_CATEGORY_GPU,
-            "Metal Device: %s",
-            [device.name UTF8String]);
+        renderer->debugProps = SDL_CreateProperties();
+        if (verboseLogs) {
+            SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "SDL_GPU Driver: Metal");
+        }
+
+        // Record device name
+        const char *deviceName = [device.name UTF8String];
+        SDL_SetStringProperty(
+            renderer->debugProps,
+            SDL_PROP_GPU_DEVICE_DEBUG_NAME_STRING,
+            deviceName);
+        if (verboseLogs) {
+            SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "Metal Device: %s", deviceName);
+        }
 
         // Remember debug mode
         renderer->debugMode = debugMode;

+ 112 - 14
src/gpu/vulkan/SDL_gpu_vulkan.c

@@ -1096,6 +1096,7 @@ struct VulkanRenderer
 
     bool debugMode;
     bool preferLowPower;
+    SDL_PropertiesID debugProps;
     Uint32 allowedFramesInFlight;
 
     VulkanExtensions supports;
@@ -4917,11 +4918,20 @@ static void VULKAN_DestroyDevice(
     renderer->vkDestroyDevice(renderer->logicalDevice, NULL);
     renderer->vkDestroyInstance(renderer->instance, NULL);
 
+    SDL_DestroyProperties(renderer->debugProps);
+
     SDL_free(renderer);
     SDL_free(device);
     SDL_Vulkan_UnloadLibrary();
 }
 
+static SDL_PropertiesID VULKAN_GetDeviceDebugProperties(
+    SDL_GPUDevice *device)
+{
+    VulkanRenderer *renderer = (VulkanRenderer *)device->driverData;
+    return renderer->debugProps;
+}
+
 static DescriptorSetCache *VULKAN_INTERNAL_AcquireDescriptorSetCache(
     VulkanRenderer *renderer)
 {
@@ -11568,6 +11578,11 @@ static SDL_GPUDevice *VULKAN_CreateDevice(bool debugMode, bool preferLowPower, S
     SDL_GPUDevice *result;
     Uint32 i;
 
+    bool verboseLogs = SDL_GetBooleanProperty(
+        props,
+        SDL_PROP_GPU_DEVICE_CREATE_VERBOSE_BOOLEAN,
+        true);
+
     if (!SDL_Vulkan_LoadLibrary(NULL)) {
         SDL_assert(!"This should have failed in PrepareDevice first!");
         return NULL;
@@ -11585,25 +11600,108 @@ static SDL_GPUDevice *VULKAN_CreateDevice(bool debugMode, bool preferLowPower, S
         SET_STRING_ERROR_AND_RETURN("Failed to initialize Vulkan!", NULL);
     }
 
-    SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "SDL_GPU Driver: Vulkan");
-    SDL_LogInfo(
-        SDL_LOG_CATEGORY_GPU,
-        "Vulkan Device: %s",
-        renderer->physicalDeviceProperties.properties.deviceName);
+    renderer->debugProps = SDL_CreateProperties();
+    if (verboseLogs) {
+        SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "SDL_GPU Driver: Vulkan");
+    }
+
+    // Record device name
+    const char *deviceName = renderer->physicalDeviceProperties.properties.deviceName;
+    SDL_SetStringProperty(
+        renderer->debugProps,
+        SDL_PROP_GPU_DEVICE_DEBUG_NAME_STRING,
+        deviceName);
+    if (verboseLogs) {
+        SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "Vulkan Device: %s", deviceName);
+    }
+
+    // Record driver version. This is provided as a backup if
+    // VK_KHR_driver_properties is not available but as most drivers support it
+    // this property should be rarely used.
+    //
+    // This uses a vendor-specific encoding and it isn't well documented. The
+    // vendor ID is the registered PCI ID of the vendor and can be found in
+    // online databases.
+    char driverVer[64];
+    Uint32 rawDriverVer = renderer->physicalDeviceProperties.properties.driverVersion;
+    Uint32 vendorId = renderer->physicalDeviceProperties.properties.vendorID;
+    if (vendorId == 0x10de) {
+        // Nvidia uses 10|8|8|6 encoding.
+        (void)SDL_snprintf(
+            driverVer,
+            SDL_arraysize(driverVer),
+            "%d.%d.%d.%d",
+            (rawDriverVer >> 22) & 0x3ff,
+            (rawDriverVer >> 14) & 0xff,
+            (rawDriverVer >> 6) & 0xff,
+            rawDriverVer & 0x3f);
+    }
+#ifdef SDL_PLATFORM_WINDOWS
+    else if (vendorId == 0x8086) {
+        // Intel uses 18|14 encoding on Windows only.
+        (void)SDL_snprintf(
+            driverVer,
+            SDL_arraysize(driverVer),
+            "%d.%d",
+            (rawDriverVer >> 14) & 0x3ffff,
+            rawDriverVer & 0x3fff);
+    }
+#endif
+    else {
+        // Assume standard Vulkan 10|10|12 encoding for everything else. AMD and
+        // Mesa are known to use this encoding.
+        (void)SDL_snprintf(
+            driverVer,
+            SDL_arraysize(driverVer),
+            "%d.%d.%d",
+            (rawDriverVer >> 22) & 0x3ff,
+            (rawDriverVer >> 12) & 0x3ff,
+            rawDriverVer & 0xfff);
+    }
+    SDL_SetStringProperty(
+        renderer->debugProps,
+        SDL_PROP_GPU_DEVICE_DEBUG_DRIVER_VERSION_STRING,
+        driverVer);
+    // Log this only if VK_KHR_driver_properties is not available.
+
     if (renderer->supports.KHR_driver_properties) {
-        SDL_LogInfo(
-            SDL_LOG_CATEGORY_GPU,
-            "Vulkan Driver: %s %s",
-            renderer->physicalDeviceDriverProperties.driverName,
-            renderer->physicalDeviceDriverProperties.driverInfo);
-        SDL_LogInfo(
-            SDL_LOG_CATEGORY_GPU,
-            "Vulkan Conformance: %u.%u.%u",
+        // Record driver name and version
+        const char *driverName = renderer->physicalDeviceDriverProperties.driverName;
+        const char *driverInfo = renderer->physicalDeviceDriverProperties.driverInfo;
+        SDL_SetStringProperty(
+            renderer->debugProps,
+            SDL_PROP_GPU_DEVICE_DEBUG_DRIVER_NAME_STRING,
+            driverName);
+        SDL_SetStringProperty(
+            renderer->debugProps,
+            SDL_PROP_GPU_DEVICE_DEBUG_DRIVER_INFO_STRING,
+            driverInfo);
+        if (verboseLogs) {
+            // FIXME: driverInfo can be a multiline string.
+            SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "Vulkan Driver: %s %s", driverName, driverInfo);
+        }
+
+        // Record conformance level
+        char conformance[64];
+        (void)SDL_snprintf(
+            conformance,
+            SDL_arraysize(conformance),
+            "%u.%u.%u.%u",
             renderer->physicalDeviceDriverProperties.conformanceVersion.major,
             renderer->physicalDeviceDriverProperties.conformanceVersion.minor,
+            renderer->physicalDeviceDriverProperties.conformanceVersion.subminor,
             renderer->physicalDeviceDriverProperties.conformanceVersion.patch);
+        SDL_SetStringProperty(
+            renderer->debugProps,
+            SDL_PROP_GPU_DEVICE_DEBUG_VULKAN_CONFORMANCE_STRING,
+            conformance);
+        if (verboseLogs) {
+            SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "Vulkan Conformance: %s", conformance);
+        }
     } else {
-        SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "KHR_driver_properties unsupported! Bother your vendor about this!");
+        if (verboseLogs) {
+            SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "Vulkan Driver: %s", driverVer);
+        }
     }
 
     if (!VULKAN_INTERNAL_CreateLogicalDevice(