SDL: Allow using an external Vulkan device with the vulkan renderer

From 614630df69c111402291beb955671a2fd6892288 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Tue, 27 Feb 2024 18:51:08 -0800
Subject: [PATCH] Allow using an external Vulkan device with the vulkan
 renderer

---
 include/SDL3/SDL_render.h             |  71 +++++++---
 src/render/vulkan/SDL_render_vulkan.c | 187 ++++++++++++++++----------
 2 files changed, 174 insertions(+), 84 deletions(-)

diff --git a/include/SDL3/SDL_render.h b/include/SDL3/SDL_render.h
index b02361cf7f5c..52b773a6e39b 100644
--- a/include/SDL3/SDL_render.h
+++ b/include/SDL3/SDL_render.h
@@ -250,6 +250,15 @@ extern DECLSPEC SDL_Renderer * SDLCALL SDL_CreateRenderer(SDL_Window *window, co
  * - `SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_BOOLEAN`: true if you want
  *   present synchronized with the refresh rate
  *
+ * With the vulkan renderer:
+ *
+ * - `SDL_PROP_RENDERER_CREATE_VULKAN_INSTANCE_POINTER`: the VkInstance to use with the renderer, optional.
+ * - `SDL_PROP_RENDERER_CREATE_VULKAN_SURFACE_NUMBER`: the VkSurfaceKHR to use with the renderer, optional.
+ * - `SDL_PROP_RENDERER_CREATE_VULKAN_PHYSICAL_DEVICE_POINTER`: the VkPhysicalDevice to use with the renderer, optional.
+ * - `SDL_PROP_RENDERER_CREATE_VULKAN_DEVICE_POINTER`: the VkDevice to use with the renderer, optional.
+ * - `SDL_PROP_RENDERER_CREATE_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER`: the queue family index used for rendering.
+ * - `SDL_PROP_RENDERER_CREATE_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER`: the queue family index used for presentation.
+ *
  * \param props the properties to use
  * \returns a valid rendering context or NULL if there was an error; call
  *          SDL_GetError() for more information.
@@ -263,11 +272,17 @@ extern DECLSPEC SDL_Renderer * SDLCALL SDL_CreateRenderer(SDL_Window *window, co
  */
 extern DECLSPEC SDL_Renderer * SDLCALL SDL_CreateRendererWithProperties(SDL_PropertiesID props);
 
-#define SDL_PROP_RENDERER_CREATE_NAME_STRING                    "name"
-#define SDL_PROP_RENDERER_CREATE_WINDOW_POINTER                 "window"
-#define SDL_PROP_RENDERER_CREATE_SURFACE_POINTER                "surface"
-#define SDL_PROP_RENDERER_CREATE_OUTPUT_COLORSPACE_NUMBER       "output_colorspace"
-#define SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_BOOLEAN          "present_vsync"
+#define SDL_PROP_RENDERER_CREATE_NAME_STRING                                "name"
+#define SDL_PROP_RENDERER_CREATE_WINDOW_POINTER                             "window"
+#define SDL_PROP_RENDERER_CREATE_SURFACE_POINTER                            "surface"
+#define SDL_PROP_RENDERER_CREATE_OUTPUT_COLORSPACE_NUMBER                   "output_colorspace"
+#define SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_BOOLEAN                      "present_vsync"
+#define SDL_PROP_RENDERER_CREATE_VULKAN_INSTANCE_POINTER                    "vulkan.instance"
+#define SDL_PROP_RENDERER_CREATE_VULKAN_SURFACE_NUMBER                      "vulkan.surface"
+#define SDL_PROP_RENDERER_CREATE_VULKAN_PHYSICAL_DEVICE_POINTER             "vulkan.physical_device"
+#define SDL_PROP_RENDERER_CREATE_VULKAN_DEVICE_POINTER                      "vulkan.device"
+#define SDL_PROP_RENDERER_CREATE_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER  "vulkan.graphics_queue_family_index"
+#define SDL_PROP_RENDERER_CREATE_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER   "vulkan.present_queue_family_index"
 
 /**
  * Create a 2D software rendering context for a surface.
@@ -354,15 +369,33 @@ extern DECLSPEC int SDLCALL SDL_GetRendererInfo(SDL_Renderer *renderer, SDL_Rend
  *   that can be displayed, in terms of the SDR white point. When HDR is not
  *   enabled, this will be 1.0. This property can change dynamically when
  *   SDL_EVENT_DISPLAY_HDR_STATE_CHANGED is sent.
+ *
+ * With the direct3d renderer:
+ *
  * - `SDL_PROP_RENDERER_D3D9_DEVICE_POINTER`: the IDirect3DDevice9 associated
  *   with the renderer
+ *
+ * With the direct3d11 renderer:
+ *
  * - `SDL_PROP_RENDERER_D3D11_DEVICE_POINTER`: the ID3D11Device associated
  *   with the renderer
+ *
+ * With the direct3d12 renderer:
+ *
  * - `SDL_PROP_RENDERER_D3D12_DEVICE_POINTER`: the ID3D12Device associated
  *   with the renderer
  * - `SDL_PROP_RENDERER_D3D12_COMMAND_QUEUE_POINTER`: the ID3D12CommandQueue
  *   associated with the renderer
  *
+ * With the vulkan renderer:
+ *
+ * - `SDL_PROP_RENDERER_VULKAN_INSTANCE_POINTER`: the VkInstance associated with the renderer
+ * - `SDL_PROP_RENDERER_VULKAN_SURFACE_NUMBER`: the VkSurfaceKHR associated with the renderer
+ * - `SDL_PROP_RENDERER_VULKAN_PHYSICAL_DEVICE_POINTER`: the VkPhysicalDevice associated with the renderer
+ * - `SDL_PROP_RENDERER_VULKAN_DEVICE_POINTER`: the VkDevice associated with the renderer
+ * - `SDL_PROP_RENDERER_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER`: the queue family index used for rendering
+ * - `SDL_PROP_RENDERER_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER`: the queue family index used for presentation
+ *
  * \param renderer the rendering context
  * \returns a valid property ID on success or 0 on failure; call
  *          SDL_GetError() for more information.
@@ -374,17 +407,23 @@ extern DECLSPEC int SDLCALL SDL_GetRendererInfo(SDL_Renderer *renderer, SDL_Rend
  */
 extern DECLSPEC SDL_PropertiesID SDLCALL SDL_GetRendererProperties(SDL_Renderer *renderer);
 
-#define SDL_PROP_RENDERER_NAME_STRING                   "SDL.renderer.name"
-#define SDL_PROP_RENDERER_WINDOW_POINTER                "SDL.renderer.window"
-#define SDL_PROP_RENDERER_SURFACE_POINTER               "SDL.renderer.surface"
-#define SDL_PROP_RENDERER_OUTPUT_COLORSPACE_NUMBER      "SDL.renderer.output_colorspace"
-#define SDL_PROP_RENDERER_HDR_ENABLED_BOOLEAN           "SDL.renderer.HDR_enabled"
-#define SDL_PROP_RENDERER_SDR_WHITE_POINT_FLOAT         "SDL.renderer.SDR_white_point"
-#define SDL_PROP_RENDERER_HDR_HEADROOM_FLOAT            "SDL.renderer.HDR_headroom"
-#define SDL_PROP_RENDERER_D3D9_DEVICE_POINTER           "SDL.renderer.d3d9.device"
-#define SDL_PROP_RENDERER_D3D11_DEVICE_POINTER          "SDL.renderer.d3d11.device"
-#define SDL_PROP_RENDERER_D3D12_DEVICE_POINTER          "SDL.renderer.d3d12.device"
-#define SDL_PROP_RENDERER_D3D12_COMMAND_QUEUE_POINTER   "SDL.renderer.d3d12.command_queue"
+#define SDL_PROP_RENDERER_NAME_STRING                               "SDL.renderer.name"
+#define SDL_PROP_RENDERER_WINDOW_POINTER                            "SDL.renderer.window"
+#define SDL_PROP_RENDERER_SURFACE_POINTER                           "SDL.renderer.surface"
+#define SDL_PROP_RENDERER_OUTPUT_COLORSPACE_NUMBER                  "SDL.renderer.output_colorspace"
+#define SDL_PROP_RENDERER_HDR_ENABLED_BOOLEAN                       "SDL.renderer.HDR_enabled"
+#define SDL_PROP_RENDERER_SDR_WHITE_POINT_FLOAT                     "SDL.renderer.SDR_white_point"
+#define SDL_PROP_RENDERER_HDR_HEADROOM_FLOAT                        "SDL.renderer.HDR_headroom"
+#define SDL_PROP_RENDERER_D3D9_DEVICE_POINTER                       "SDL.renderer.d3d9.device"
+#define SDL_PROP_RENDERER_D3D11_DEVICE_POINTER                      "SDL.renderer.d3d11.device"
+#define SDL_PROP_RENDERER_D3D12_DEVICE_POINTER                      "SDL.renderer.d3d12.device"
+#define SDL_PROP_RENDERER_D3D12_COMMAND_QUEUE_POINTER               "SDL.renderer.d3d12.command_queue"
+#define SDL_PROP_RENDERER_VULKAN_INSTANCE_POINTER                   "SDL.renderer.vulkan.instance"
+#define SDL_PROP_RENDERER_VULKAN_SURFACE_NUMBER                     "SDL.renderer.vulkan.surface"
+#define SDL_PROP_RENDERER_VULKAN_PHYSICAL_DEVICE_POINTER            "SDL.renderer.vulkan.physical_device"
+#define SDL_PROP_RENDERER_VULKAN_DEVICE_POINTER                     "SDL.renderer.vulkan.device"
+#define SDL_PROP_RENDERER_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER "SDL.renderer.vulkan.graphics_queue_family_index"
+#define SDL_PROP_RENDERER_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER  "SDL.renderer.vulkan.present_queue_family_index"
 
 /**
  * Get the output size in pixels of a rendering context.
diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c
index d5564cfd33ca..18f398241405 100644
--- a/src/render/vulkan/SDL_render_vulkan.c
+++ b/src/render/vulkan/SDL_render_vulkan.c
@@ -270,7 +270,9 @@ typedef struct
 {
     PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
     VkInstance instance;
+    SDL_bool instance_external;
     VkSurfaceKHR surface;
+    SDL_bool surface_external;
     VkPhysicalDevice physicalDevice;
     VkPhysicalDeviceProperties physicalDeviceProperties;
     VkPhysicalDeviceMemoryProperties physicalDeviceMemoryProperties;
@@ -278,6 +280,7 @@ typedef struct
     VkQueue graphicsQueue;
     VkQueue presentQueue;
     VkDevice device;
+    SDL_bool device_external;
     uint32_t graphicsQueueFamilyIndex;
     uint32_t presentQueueFamilyIndex;
     VkSwapchainKHR swapchain;
@@ -567,15 +570,15 @@ static void VULKAN_DestroyAll(SDL_Renderer *renderer)
         rendererData->constantBuffers = NULL;
     }
 
-    if (rendererData->device != VK_NULL_HANDLE) {
+    if (rendererData->device != VK_NULL_HANDLE && !rendererData->device_external) {
         vkDestroyDevice(rendererData->device, NULL);
         rendererData->device = VK_NULL_HANDLE;
     }
-    if (rendererData->surface != VK_NULL_HANDLE) {
+    if (rendererData->surface != VK_NULL_HANDLE && !rendererData->surface_external) {
         vkDestroySurfaceKHR(rendererData->instance, rendererData->surface, NULL);
         rendererData->surface = VK_NULL_HANDLE;
     }
-    if (rendererData->instance != VK_NULL_HANDLE) {
+    if (rendererData->instance != VK_NULL_HANDLE && !rendererData->instance_external) {
         vkDestroyInstance(rendererData->instance, NULL);
         rendererData->instance = VK_NULL_HANDLE;
     }
@@ -1524,7 +1527,7 @@ static SDL_bool VULKAN_ValidationLayersFound()
 }
 
 /* Create resources that depend on the device. */
-static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer)
+static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer, SDL_PropertiesID create_props)
 {
     VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata;
     SDL_VideoDevice *device = SDL_GetVideoDevice();
@@ -1549,46 +1552,53 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer)
         return VK_ERROR_UNKNOWN;
     }
 
-    /* Create VkInstance */
-    VkInstanceCreateInfo instanceCreateInfo = { 0 };
-    VkApplicationInfo appInfo = { 0 };
-    appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
-    appInfo.apiVersion = VK_API_VERSION_1_0;
-    instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
-    instanceCreateInfo.pApplicationInfo = &appInfo;
-    char const* const* instanceExtensions = SDL_Vulkan_GetInstanceExtensions(&instanceCreateInfo.enabledExtensionCount);
-    rendererData->supportsEXTSwapchainColorspace = VK_FALSE;
-
+    /* Check for colorspace extension */
     if (renderer->output_colorspace == SDL_COLORSPACE_SRGB_LINEAR ||
         renderer->output_colorspace == SDL_COLORSPACE_HDR10) {
         rendererData->supportsEXTSwapchainColorspace = VULKAN_InstanceExtensionFound(rendererData, VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME);
-        if (rendererData->supportsEXTSwapchainColorspace == SDL_FALSE) {
+        if (!rendererData->supportsEXTSwapchainColorspace) {
             return SDL_SetError("[Vulkan] Using HDR output but %s not supported.", VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME);
         }
     }
-    char **instanceExtensionsCopy = SDL_calloc(sizeof(const char *), instanceCreateInfo.enabledExtensionCount + 1);
-    for (uint32_t i = 0; i < instanceCreateInfo.enabledExtensionCount; i++) {
-        instanceExtensionsCopy[i] = SDL_strdup(instanceExtensions[i]);
-    }
-    if (rendererData->supportsEXTSwapchainColorspace) {
-        instanceExtensionsCopy[instanceCreateInfo.enabledExtensionCount] = SDL_strdup(VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME);
-        instanceCreateInfo.enabledExtensionCount++;
-    }
-    instanceCreateInfo.ppEnabledExtensionNames = (const char* const*) instanceExtensionsCopy;
-    if (createDebug && VULKAN_ValidationLayersFound()) {
-        instanceCreateInfo.ppEnabledLayerNames = validationLayerName;
-        instanceCreateInfo.enabledLayerCount = 1;
-    }
-    result = vkCreateInstance(&instanceCreateInfo, NULL, &rendererData->instance);
-    if (result != VK_SUCCESS) {
-        SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateInstance(): %s\n", SDL_Vulkan_GetResultString(result));
-        return result;
-    }
 
-    for (uint32_t i = 0; i < instanceCreateInfo.enabledExtensionCount; i++) {
-        SDL_free(instanceExtensionsCopy[i]);
+    /* Create VkInstance */
+    rendererData->instance = (VkInstance)SDL_GetProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_INSTANCE_POINTER, NULL);
+    if (rendererData->instance) {
+        rendererData->instance_external = SDL_TRUE;
+    } else {
+        VkInstanceCreateInfo instanceCreateInfo = { 0 };
+        VkApplicationInfo appInfo = { 0 };
+        appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
+        appInfo.apiVersion = VK_API_VERSION_1_0;
+        instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
+        instanceCreateInfo.pApplicationInfo = &appInfo;
+        char const *const *instanceExtensions = SDL_Vulkan_GetInstanceExtensions(&instanceCreateInfo.enabledExtensionCount);
+
+        char **instanceExtensionsCopy = SDL_calloc(sizeof(const char *), instanceCreateInfo.enabledExtensionCount + 1);
+        for (uint32_t i = 0; i < instanceCreateInfo.enabledExtensionCount; i++) {
+            instanceExtensionsCopy[i] = SDL_strdup(instanceExtensions[i]);
+        }
+        if (rendererData->supportsEXTSwapchainColorspace) {
+            instanceExtensionsCopy[instanceCreateInfo.enabledExtensionCount] = SDL_strdup(VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME);
+            instanceCreateInfo.enabledExtensionCount++;
+        }
+        instanceCreateInfo.ppEnabledExtensionNames = (const char *const *)instanceExtensionsCopy;
+        if (createDebug && VULKAN_ValidationLayersFound()) {
+            instanceCreateInfo.ppEnabledLayerNames = validationLayerName;
+            instanceCreateInfo.enabledLayerCount = 1;
+        }
+        result = vkCreateInstance(&instanceCreateInfo, NULL, &rendererData->instance);
+        if (result != VK_SUCCESS) {
+            SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateInstance(): %s\n", SDL_Vulkan_GetResultString(result));
+            return result;
+        }
+
+        for (uint32_t i = 0; i < instanceCreateInfo.enabledExtensionCount; i++) {
+            SDL_free(instanceExtensionsCopy[i]);
+        }
+        SDL_free(instanceExtensionsCopy);
     }
-    SDL_free(instanceExtensionsCopy);
+
     /* Load instance Vulkan functions */
     if (VULKAN_LoadInstanceFunctions(rendererData) != 0) {
         VULKAN_DestroyAll(renderer);
@@ -1596,45 +1606,78 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer)
     }
 
     /* Create Vulkan surface */
-    if (!device->Vulkan_CreateSurface || !device->Vulkan_CreateSurface(device, renderer->window, rendererData->instance, NULL, &rendererData->surface)) {
-        VULKAN_DestroyAll(renderer);
-        SDL_LogError(SDL_LOG_CATEGORY_RENDER, "Vulkan_CreateSurface() failed.\n");
-        return VK_ERROR_UNKNOWN;
+    rendererData->surface = (VkSurfaceKHR)SDL_GetNumberProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_SURFACE_NUMBER, 0);
+    if (rendererData->surface) {
+        rendererData->surface_external = SDL_TRUE;
+    } else {
+        if (!device->Vulkan_CreateSurface || !device->Vulkan_CreateSurface(device, renderer->window, rendererData->instance, NULL, &rendererData->surface)) {
+            VULKAN_DestroyAll(renderer);
+            SDL_LogError(SDL_LOG_CATEGORY_RENDER, "Vulkan_CreateSurface() failed.\n");
+            return VK_ERROR_UNKNOWN;
+        }
     }
 
     /* Choose Vulkan physical device */
-    if (VULKAN_FindPhysicalDevice(rendererData) != VK_SUCCESS) {
-        VULKAN_DestroyAll(renderer);
-        return VK_ERROR_UNKNOWN;
+    rendererData->physicalDevice = (VkPhysicalDevice)SDL_GetProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_PHYSICAL_DEVICE_POINTER, NULL);
+    if (rendererData->physicalDevice) {
+        vkGetPhysicalDeviceMemoryProperties(rendererData->physicalDevice, &rendererData->physicalDeviceMemoryProperties);
+        vkGetPhysicalDeviceFeatures(rendererData->physicalDevice, &rendererData->physicalDeviceFeatures);
+    } else {
+        if (VULKAN_FindPhysicalDevice(rendererData) != VK_SUCCESS) {
+            VULKAN_DestroyAll(renderer);
+            return VK_ERROR_UNKNOWN;
+        }
+    }
+
+    if (SDL_HasProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER)) {
+        rendererData->graphicsQueueFamilyIndex = (uint32_t)SDL_GetNumberProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER, 0);
+    }
+    if (SDL_HasProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER)) {
+        rendererData->presentQueueFamilyIndex = (uint32_t)SDL_GetNumberProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER, 0);
     }
 
     /* Create Vulkan device */
-    VkDeviceQueueCreateInfo deviceQueueCreateInfo[1] = { { 0 } };
-    static const float queuePriority[] = { 1.0f };
-    VkDeviceCreateInfo deviceCreateInfo = { 0 };
-    static const char *const deviceExtensionNames[] = {
-        VK_KHR_SWAPCHAIN_EXTENSION_NAME,
-    };
+    rendererData->device = (VkDevice)SDL_GetProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_DEVICE_POINTER, NULL);
+    if (rendererData->device) {
+        rendererData->device_external = SDL_TRUE;
+    } else {
+        VkDeviceQueueCreateInfo deviceQueueCreateInfo[2] = { { 0 }, { 0 } };
+        static const float queuePriority[] = { 1.0f };
+        VkDeviceCreateInfo deviceCreateInfo = { 0 };
+        static const char *const deviceExtensionNames[] = {
+            VK_KHR_SWAPCHAIN_EXTENSION_NAME,
+        };
 
-    deviceQueueCreateInfo->sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
-    deviceQueueCreateInfo->queueFamilyIndex = rendererData->graphicsQueueFamilyIndex;
-    deviceQueueCreateInfo->queueCount = 1;
-    deviceQueueCreateInfo->pQueuePriorities = &queuePriority[0];
-
-    deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
-    deviceCreateInfo.queueCreateInfoCount = 1;
-    deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfo;
-    deviceCreateInfo.pEnabledFeatures = NULL;
-    deviceCreateInfo.enabledExtensionCount = SDL_arraysize(deviceExtensionNames);
-    deviceCreateInfo.ppEnabledExtensionNames = deviceExtensionNames;
-    result = vkCreateDevice(rendererData->physicalDevice, &deviceCreateInfo, NULL, &rendererData->device);
-    if (result != VK_SUCCESS) {
-        SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateDevice(): %s\n", SDL_Vulkan_GetResultString(result));
-        VULKAN_DestroyAll(renderer);
-        return result;
+        deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
+        deviceCreateInfo.queueCreateInfoCount = 0;
+        deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfo;
+        deviceCreateInfo.pEnabledFeatures = NULL;
+        deviceCreateInfo.enabledExtensionCount = SDL_arraysize(deviceExtensionNames);
+        deviceCreateInfo.ppEnabledExtensionNames = deviceExtensionNames;
+
+        deviceQueueCreateInfo[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
+        deviceQueueCreateInfo[0].queueFamilyIndex = rendererData->graphicsQueueFamilyIndex;
+        deviceQueueCreateInfo[0].queueCount = 1;
+        deviceQueueCreateInfo[0].pQueuePriorities = queuePriority;
+        ++deviceCreateInfo.queueCreateInfoCount;
+
+        if (rendererData->presentQueueFamilyIndex != rendererData->graphicsQueueFamilyIndex) {
+            deviceQueueCreateInfo[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
+            deviceQueueCreateInfo[1].queueFamilyIndex = rendererData->presentQueueFamilyIndex;
+            deviceQueueCreateInfo[1].queueCount = 1;
+            deviceQueueCreateInfo[1].pQueuePriorities = queuePriority;
+            ++deviceCreateInfo.queueCreateInfoCount;
+        }
+
+        result = vkCreateDevice(rendererData->physicalDevice, &deviceCreateInfo, NULL, &rendererData->device);
+        if (result != VK_SUCCESS) {
+            SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateDevice(): %s\n", SDL_Vulkan_GetResultString(result));
+            VULKAN_DestroyAll(renderer);
+            return result;
+        }
     }
 
-    if(VULKAN_LoadDeviceFunctions(rendererData) != 0) {
+    if (VULKAN_LoadDeviceFunctions(rendererData) != 0) {
         VULKAN_DestroyAll(renderer);
         return VK_ERROR_UNKNOWN;
     }
@@ -1789,6 +1832,14 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer)
         }
     }
 
+    SDL_PropertiesID props = SDL_GetRendererProperties(renderer);
+    SDL_SetProperty(props, SDL_PROP_RENDERER_VULKAN_INSTANCE_POINTER, rendererData->instance);
+    SDL_SetNumberProperty(props, SDL_PROP_RENDERER_VULKAN_SURFACE_NUMBER, (Sint64)rendererData->surface);
+    SDL_SetProperty(props, SDL_PROP_RENDERER_VULKAN_PHYSICAL_DEVICE_POINTER, rendererData->physicalDevice);
+    SDL_SetProperty(props, SDL_PROP_RENDERER_VULKAN_DEVICE_POINTER, rendererData->device);
+    SDL_SetNumberProperty(props, SDL_PROP_RENDERER_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER, rendererData->graphicsQueueFamilyIndex);
+    SDL_SetNumberProperty(props, SDL_PROP_RENDERER_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER, rendererData->presentQueueFamilyIndex);
+
     return VK_SUCCESS;
 }
 
@@ -3880,8 +3931,8 @@ SDL_Renderer *VULKAN_CreateRenderer(SDL_Window *window, SDL_PropertiesID create_
      */
     renderer->window = window;
 
-    /* Initialize Direct3D resources */
-    if (VULKAN_CreateDeviceResources(renderer) != VK_SUCCESS) {
+    /* Initialize Vulkan resources */
+    if (VULKAN_CreateDeviceResources(renderer, create_props) != VK_SUCCESS) {
         VULKAN_DestroyRenderer(renderer);
         return NULL;
     }