From cab20117e6177c6f9143a5e11d250a1b2537c9d2 Mon Sep 17 00:00:00 2001
From: Dan Ginsburg <[EMAIL REDACTED]>
Date: Thu, 22 Feb 2024 17:58:11 -0500
Subject: [PATCH] Vulkan Renderer (#9114)
This pull request adds an implementation of a Vulkan Render backend to SDL. I have so far tested this primarily on Windows, but also smoke tested on Linux and macOS (MoltenVK). I have not tried it yet on Android, but it should be usable there as well (sans any bugs I missed). This began as a port of the SDL Direct3D12 Renderer, which is the closest thing to Vulkan as existed in the SDL codebase. The shaders are more or less identical (with the only differences being in descriptor bindings vs root descriptors). The shaders are built using the HLSL frontend of glslang.
Everything in the code is pure Vulkan 1.0 (no extensions), with the exception of HDR support which requires the Vulkan instance extension `VK_EXT_swapchain_colorspace`. The code could have been simplified considerably if I used dynamic rendering, push descriptors, extended dynamic state, and other modern Vulkan-isms, but I felt it was more important to make the code as vanilla Vulkan as possible so that it would run on any Vulkan implementation.
The main differences with the Direct3D12 renderer are:
* Having to manage renderpasses for performing clears. There is likely some optimization that would still remain for more efficient use of TBDR hardware where there might be some unnecessary load/stores, but it does attempt to do clears using renderpasses.
* Constant buffer data couldn't be directly updated in the command buffer since I didn't want to rely on push descriptors, so there is a persistently mapped buffer with increasing offset per swapchain image where CB data gets written.
* Many more resources are dependent on the swapchain resizing due to i.e. Vulkan requiring the VkFramebuffer to reference the VkImageView of the swapchain, so there is a bit more code around handling that than was necessary in D3D12.
* For NV12/NV21 textures, rather than there being plane data in the texture itself, the UV data is placed in a separate `VkImage`/`VkImageView`.
I've verified that `testcolorspace` works with both sRGB and HDR linear. I've tested `testoverlay` works with the various YUV/NV12/NV21 formats. I've tested `testsprite`. I've checked that window resizing and swapchain out-of-date handling when minimizing are working. I've run through `testautomation` with the render tests. I also have run several of the tests with Vulkan validation and synchronization validation. Surely I will have missed some things, but I think it's in a good state to be merged and build out from here.
---
CMakeLists.txt | 7 +
cmake/sdlchecks.cmake | 1 +
include/SDL3/SDL_hints.h | 12 +
include/SDL3/SDL_render.h | 15 +
include/build_config/SDL_build_config.h.cmake | 1 +
src/SDL_internal.h | 8 +-
src/render/SDL_d3dmath.c | 2 +-
src/render/SDL_d3dmath.h | 4 +-
src/render/SDL_render.c | 3 +
src/render/SDL_sysrender.h | 1 +
src/render/vulkan/SDL_render_vulkan.c | 3741 +++++++++++++++++
src/render/vulkan/SDL_shaders_vulkan.c | 60 +
src/render/vulkan/SDL_shaders_vulkan.h | 44 +
.../vulkan/VULKAN_PixelShader_Advanced.h | 373 ++
.../vulkan/VULKAN_PixelShader_Advanced.hlsl | 6 +
src/render/vulkan/VULKAN_PixelShader_Colors.h | 47 +
.../vulkan/VULKAN_PixelShader_Colors.hlsl | 7 +
.../vulkan/VULKAN_PixelShader_Common.incl | 235 ++
.../vulkan/VULKAN_PixelShader_Textures.h | 60 +
.../vulkan/VULKAN_PixelShader_Textures.hlsl | 6 +
src/render/vulkan/VULKAN_VertexShader.h | 48 +
src/render/vulkan/VULKAN_VertexShader.hlsl | 41 +
src/render/vulkan/compile_shaders.bat | 5 +
23 files changed, 4723 insertions(+), 4 deletions(-)
create mode 100644 src/render/vulkan/SDL_render_vulkan.c
create mode 100644 src/render/vulkan/SDL_shaders_vulkan.c
create mode 100644 src/render/vulkan/SDL_shaders_vulkan.h
create mode 100644 src/render/vulkan/VULKAN_PixelShader_Advanced.h
create mode 100644 src/render/vulkan/VULKAN_PixelShader_Advanced.hlsl
create mode 100644 src/render/vulkan/VULKAN_PixelShader_Colors.h
create mode 100644 src/render/vulkan/VULKAN_PixelShader_Colors.hlsl
create mode 100644 src/render/vulkan/VULKAN_PixelShader_Common.incl
create mode 100644 src/render/vulkan/VULKAN_PixelShader_Textures.h
create mode 100644 src/render/vulkan/VULKAN_PixelShader_Textures.hlsl
create mode 100644 src/render/vulkan/VULKAN_VertexShader.h
create mode 100644 src/render/vulkan/VULKAN_VertexShader.hlsl
create mode 100644 src/render/vulkan/compile_shaders.bat
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2c7c1b3578f61..566879bb2e78c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -335,6 +335,7 @@ dep_option(SDL_RENDER_D3D "Enable the Direct3D render driver" ON "SDL_R
dep_option(SDL_RENDER_METAL "Enable the Metal render driver" ON "SDL_RENDER;${APPLE}" OFF)
dep_option(SDL_VIVANTE "Use Vivante EGL video driver" ON "${UNIX_SYS};SDL_CPU_ARM32" OFF)
dep_option(SDL_VULKAN "Enable Vulkan support" ON "SDL_VIDEO;ANDROID OR APPLE OR LINUX OR WINDOWS" OFF)
+dep_option(SDL_RENDER_VULKAN "Enable the Vulkan render driver" ON "SDL_RENDER;SDL_VULKAN;ANDROID OR APPLE OR LINUX OR WINDOWS" OFF)
dep_option(SDL_METAL "Enable Metal support" ON "APPLE" OFF)
dep_option(SDL_KMSDRM "Use KMS DRM video driver" ${UNIX_SYS} "SDL_VIDEO" OFF)
dep_option(SDL_KMSDRM_SHARED "Dynamically load KMS DRM support" ON "SDL_KMSDRM" OFF)
@@ -2007,6 +2008,9 @@ elseif(WINDOWS)
if(SDL_VULKAN)
set(SDL_VIDEO_VULKAN 1)
set(HAVE_VULKAN TRUE)
+ if(SDL_RENDER_VULKAN)
+ set(SDL_VIDEO_RENDER_VULKAN 1)
+ endif()
endif()
endif()
@@ -2248,6 +2252,9 @@ elseif(APPLE)
if(SDL_VULKAN)
set(SDL_VIDEO_VULKAN 1)
set(HAVE_VULKAN TRUE)
+ if(SDL_RENDER_VULKAN)
+ set(SDL_VIDEO_RENDER_VULKAN 1)
+ endif()
endif()
if(SDL_METAL)
set(SDL_VIDEO_METAL 1)
diff --git a/cmake/sdlchecks.cmake b/cmake/sdlchecks.cmake
index 55e939a8ca93f..4cf36f3e8a30e 100644
--- a/cmake/sdlchecks.cmake
+++ b/cmake/sdlchecks.cmake
@@ -725,6 +725,7 @@ macro(CheckVulkan)
if(SDL_VULKAN)
set(SDL_VIDEO_VULKAN 1)
set(HAVE_VULKAN TRUE)
+ set(SDL_VIDEO_RENDER_VULKAN 1)
endif()
endmacro()
diff --git a/include/SDL3/SDL_hints.h b/include/SDL3/SDL_hints.h
index 7c30a707dc8a8..dd43c3ed11730 100644
--- a/include/SDL3/SDL_hints.h
+++ b/include/SDL3/SDL_hints.h
@@ -1736,6 +1736,18 @@ extern "C" {
*/
#define SDL_HINT_RENDER_DIRECT3D11_DEBUG "SDL_RENDER_DIRECT3D11_DEBUG"
+/**
+ * A variable controlling whether to enable Vulkan Validation Layers
+ *
+ *
+ * This variable can be set to the following values:
+ * "0" - Disable Validation Layer use
+ * "1" - Enable Validation Layer use
+ *
+ * By default, SDL does not use Vulkan Validation Layers.
+ */
+#define SDL_HINT_RENDER_VULKAN_DEBUG "SDL_RENDER_VULKAN_DEBUG"
+
/**
* A variable specifying which render driver to use.
*
diff --git a/include/SDL3/SDL_render.h b/include/SDL3/SDL_render.h
index e2d1ec256a680..e36a9deba635d 100644
--- a/include/SDL3/SDL_render.h
+++ b/include/SDL3/SDL_render.h
@@ -639,6 +639,17 @@ extern DECLSPEC SDL_Texture *SDLCALL SDL_CreateTextureWithProperties(SDL_Rendere
* - `SDL_PROP_TEXTURE_D3D12_TEXTURE_V_POINTER`: the ID3D12Resource associated
* with the V plane of a YUV texture
*
+ * With the vulkan renderer:
+ *
+ * - `SDL_PROP_TEXTURE_VULKAN_TEXTURE_POINTER`: the VkImage associated
+ * with the texture
+ * - `SDL_PROP_TEXTURE_VULKAN_TEXTURE_U_POINTER`: the VkImage associated
+ * with the U plane of a YUV texture
+ * - `SDL_PROP_TEXTURE_VULKAN_TEXTURE_V_POINTER`: the VkImage associated
+ * with the V plane of a YUV texture
+ * - `SDL_PROP_TEXTURE_VULKAN_TEXTURE_UV_POINTER`: the VkImage associated
+ * with the UV plane of a NV12/NV21 texture
+ *
* With the opengl renderer:
*
* - `SDL_PROP_TEXTURE_OPENGL_TEXTURE_NUMBER`: the GLuint texture associated
@@ -701,6 +712,10 @@ extern DECLSPEC SDL_PropertiesID SDLCALL SDL_GetTextureProperties(SDL_Texture *t
#define SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_U_NUMBER "SDL.texture.opengles2.texture_u"
#define SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_V_NUMBER "SDL.texture.opengles2.texture_v"
#define SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_TARGET_NUMBER "SDL.texture.opengles2.target"
+#define SDL_PROP_TEXTURE_VULKAN_TEXTURE_POINTER "SDL.texture.vulkan.texture"
+#define SDL_PROP_TEXTURE_VULKAN_TEXTURE_U_POINTER "SDL.texture.vulkan.texture_u"
+#define SDL_PROP_TEXTURE_VULKAN_TEXTURE_V_POINTER "SDL.texture.vulkan.texture_v"
+#define SDL_PROP_TEXTURE_VULKAN_TEXTURE_UV_POINTER "SDL.texture.vulkan.texture_uv"
/**
* Get the renderer that created an SDL_Texture.
diff --git a/include/build_config/SDL_build_config.h.cmake b/include/build_config/SDL_build_config.h.cmake
index d25b31b632072..be0ac3f460b7f 100644
--- a/include/build_config/SDL_build_config.h.cmake
+++ b/include/build_config/SDL_build_config.h.cmake
@@ -412,6 +412,7 @@
#cmakedefine SDL_VIDEO_RENDER_D3D11 @SDL_VIDEO_RENDER_D3D11@
#cmakedefine SDL_VIDEO_RENDER_D3D12 @SDL_VIDEO_RENDER_D3D12@
#cmakedefine SDL_VIDEO_RENDER_METAL @SDL_VIDEO_RENDER_METAL@
+#cmakedefine SDL_VIDEO_RENDER_VULKAN @SDL_VIDEO_RENDER_VULKAN@
#cmakedefine SDL_VIDEO_RENDER_OGL @SDL_VIDEO_RENDER_OGL@
#cmakedefine SDL_VIDEO_RENDER_OGL_ES2 @SDL_VIDEO_RENDER_OGL_ES2@
#cmakedefine SDL_VIDEO_RENDER_PS2 @SDL_VIDEO_RENDER_PS2@
diff --git a/src/SDL_internal.h b/src/SDL_internal.h
index a8e077583b68d..1494b9a4a6a7a 100644
--- a/src/SDL_internal.h
+++ b/src/SDL_internal.h
@@ -223,6 +223,9 @@
#ifndef SDL_VIDEO_RENDER_VITA_GXM
#define SDL_VIDEO_RENDER_VITA_GXM 0
#endif
+#ifndef SDL_VIDEO_RENDER_VULKAN
+#define SDL_VIDEO_RENDER_VULKAN 0
+#endif
#else /* define all as 0 */
#undef SDL_VIDEO_RENDER_SW
#define SDL_VIDEO_RENDER_SW 0
@@ -244,6 +247,8 @@
#define SDL_VIDEO_RENDER_PSP 0
#undef SDL_VIDEO_RENDER_VITA_GXM
#define SDL_VIDEO_RENDER_VITA_GXM 0
+#undef SDL_VIDEO_RENDER_VULKAN
+#define SDL_VIDEO_RENDER_VULKAN 0
#endif /* SDL_RENDER_DISABLED */
#define SDL_HAS_RENDER_DRIVER \
@@ -256,7 +261,8 @@
SDL_VIDEO_RENDER_OGL_ES2 | \
SDL_VIDEO_RENDER_PS2 | \
SDL_VIDEO_RENDER_PSP | \
- SDL_VIDEO_RENDER_VITA_GXM)
+ SDL_VIDEO_RENDER_VITA_GXM | \
+ SDL_VIDEO_RENDER_VULKAN )
#if !defined(SDL_RENDER_DISABLED) && !SDL_HAS_RENDER_DRIVER
#error SDL_RENDER enabled without any backend drivers.
diff --git a/src/render/SDL_d3dmath.c b/src/render/SDL_d3dmath.c
index 664f615b91345..ade1adebef3d7 100644
--- a/src/render/SDL_d3dmath.c
+++ b/src/render/SDL_d3dmath.c
@@ -20,7 +20,7 @@
*/
#include "SDL_internal.h"
-#if (SDL_VIDEO_RENDER_D3D || SDL_VIDEO_RENDER_D3D11 || SDL_VIDEO_RENDER_D3D12)
+#if (SDL_VIDEO_RENDER_D3D || SDL_VIDEO_RENDER_D3D11 || SDL_VIDEO_RENDER_D3D12 || SDL_VIDEO_RENDER_VULKAN)
#include "SDL_d3dmath.h"
diff --git a/src/render/SDL_d3dmath.h b/src/render/SDL_d3dmath.h
index b1ca3517ab8c1..ab8d2895b3f12 100644
--- a/src/render/SDL_d3dmath.h
+++ b/src/render/SDL_d3dmath.h
@@ -20,7 +20,7 @@
*/
#include "SDL_internal.h"
-#if (SDL_VIDEO_RENDER_D3D || SDL_VIDEO_RENDER_D3D11 || SDL_VIDEO_RENDER_D3D12)
+#if (SDL_VIDEO_RENDER_D3D || SDL_VIDEO_RENDER_D3D11 || SDL_VIDEO_RENDER_D3D12 || SDL_VIDEO_RENDER_VULKAN)
/* Set up for C function definitions, even when using C++ */
#ifdef __cplusplus
@@ -78,4 +78,4 @@ Float4X4 MatrixRotationZ(float r);
}
#endif
-#endif /* (SDL_VIDEO_RENDER_D3D || SDL_VIDEO_RENDER_D3D11 || SDL_VIDEO_RENDER_D3D12) */
+#endif /* (SDL_VIDEO_RENDER_D3D || SDL_VIDEO_RENDER_D3D11 || SDL_VIDEO_RENDER_D3D12 || SDL_VIDEO_RENDER_VULKAN)*/
diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c
index 6b239cba02f02..ae06ab67724e2 100644
--- a/src/render/SDL_render.c
+++ b/src/render/SDL_render.c
@@ -116,6 +116,9 @@ static const SDL_RenderDriver *render_drivers[] = {
#if SDL_VIDEO_RENDER_VITA_GXM
&VITA_GXM_RenderDriver,
#endif
+#if SDL_VIDEO_RENDER_VULKAN
+ &VULKAN_RenderDriver,
+#endif
#if SDL_VIDEO_RENDER_SW
&SW_RenderDriver
#endif
diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h
index 177980f07f948..9ab2a7979c4c2 100644
--- a/src/render/SDL_sysrender.h
+++ b/src/render/SDL_sysrender.h
@@ -305,6 +305,7 @@ extern SDL_RenderDriver D3D12_RenderDriver;
extern SDL_RenderDriver GL_RenderDriver;
extern SDL_RenderDriver GLES2_RenderDriver;
extern SDL_RenderDriver METAL_RenderDriver;
+extern SDL_RenderDriver VULKAN_RenderDriver;
extern SDL_RenderDriver PS2_RenderDriver;
extern SDL_RenderDriver PSP_RenderDriver;
extern SDL_RenderDriver SW_RenderDriver;
diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c
new file mode 100644
index 0000000000000..9fcba37aa9363
--- /dev/null
+++ b/src/render/vulkan/SDL_render_vulkan.c
@@ -0,0 +1,3741 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#if SDL_VIDEO_RENDER_VULKAN
+
+#define SDL_VULKAN_FRAME_QUEUE_DEPTH 2
+#define SDL_VULKAN_NUM_VERTEX_BUFFERS 256
+#define SDL_VULKAN_VERTEX_BUFFER_DEFAULT_SIZE 65536
+#define SDL_VULKAN_CONSTANT_BUFFER_DEFAULT_SIZE 65536
+#define SDL_VULKAN_NUM_UPLOAD_BUFFERS 32
+#define SDL_VULKAN_MAX_DESCRIPTOR_SETS 4096
+
+
+#define VK_NO_PROTOTYPES
+#include "SDL_vulkan.h"
+#include "SDL_shaders_vulkan.h"
+#include <vulkan/vulkan.h>
+#include "../SDL_sysrender.h"
+#include "../SDL_sysvideo.h"
+#include "../SDL_d3dmath.h"
+#include "../../video/SDL_pixels_c.h"
+
+extern const char *SDL_Vulkan_GetResultString(VkResult result);
+
+#define VULKAN_FUNCTIONS() \
+ VULKAN_DEVICE_FUNCTION(vkAcquireNextImageKHR) \
+ VULKAN_DEVICE_FUNCTION(vkAllocateCommandBuffers) \
+ VULKAN_DEVICE_FUNCTION(vkAllocateDescriptorSets) \
+ VULKAN_DEVICE_FUNCTION(vkAllocateMemory) \
+ VULKAN_DEVICE_FUNCTION(vkBeginCommandBuffer) \
+ VULKAN_DEVICE_FUNCTION(vkBindBufferMemory) \
+ VULKAN_DEVICE_FUNCTION(vkBindImageMemory) \
+ VULKAN_DEVICE_FUNCTION(vkCmdBeginRenderPass) \
+ VULKAN_DEVICE_FUNCTION(vkCmdBindDescriptorSets) \
+ VULKAN_DEVICE_FUNCTION(vkCmdBindPipeline) \
+ VULKAN_DEVICE_FUNCTION(vkCmdBindVertexBuffers) \
+ VULKAN_DEVICE_FUNCTION(vkCmdClearColorImage) \
+ VULKAN_DEVICE_FUNCTION(vkCmdCopyBufferToImage) \
+ VULKAN_DEVICE_FUNCTION(vkCmdCopyImageToBuffer) \
+ VULKAN_DEVICE_FUNCTION(vkCmdDraw) \
+ VULKAN_DEVICE_FUNCTION(vkCmdEndRenderPass) \
+ VULKAN_DEVICE_FUNCTION(vkCmdPipelineBarrier) \
+ VULKAN_DEVICE_FUNCTION(vkCmdPushConstants) \
+ VULKAN_DEVICE_FUNCTION(vkCmdSetScissor) \
+ VULKAN_DEVICE_FUNCTION(vkCmdSetViewport) \
+ VULKAN_DEVICE_FUNCTION(vkCreateBuffer) \
+ VULKAN_DEVICE_FUNCTION(vkCreateCommandPool) \
+ VULKAN_DEVICE_FUNCTION(vkCreateDescriptorPool) \
+ VULKAN_DEVICE_FUNCTION(vkCreateDescriptorSetLayout) \
+ VULKAN_DEVICE_FUNCTION(vkCreateFence) \
+ VULKAN_DEVICE_FUNCTION(vkCreateFramebuffer) \
+ VULKAN_DEVICE_FUNCTION(vkCreateGraphicsPipelines) \
+ VULKAN_DEVICE_FUNCTION(vkCreateImage) \
+ VULKAN_DEVICE_FUNCTION(vkCreateImageView) \
+ VULKAN_DEVICE_FUNCTION(vkCreatePipelineLayout) \
+ VULKAN_DEVICE_FUNCTION(vkCreateRenderPass) \
+ VULKAN_DEVICE_FUNCTION(vkCreateSampler) \
+ VULKAN_DEVICE_FUNCTION(vkCreateSemaphore) \
+ VULKAN_DEVICE_FUNCTION(vkCreateShaderModule) \
+ VULKAN_DEVICE_FUNCTION(vkCreateSwapchainKHR) \
+ VULKAN_DEVICE_FUNCTION(vkDestroyBuffer) \
+ VULKAN_DEVICE_FUNCTION(vkDestroyCommandPool) \
+ VULKAN_DEVICE_FUNCTION(vkDestroyDevice) \
+ VULKAN_DEVICE_FUNCTION(vkDestroyDescriptorPool) \
+ VULKAN_DEVICE_FUNCTION(vkDestroyDescriptorSetLayout) \
+ VULKAN_DEVICE_FUNCTION(vkDestroyFence) \
+ VULKAN_DEVICE_FUNCTION(vkDestroyFramebuffer) \
+ VULKAN_DEVICE_FUNCTION(vkDestroyImage) \
+ VULKAN_DEVICE_FUNCTION(vkDestroyImageView) \
+ VULKAN_DEVICE_FUNCTION(vkDestroyPipeline) \
+ VULKAN_DEVICE_FUNCTION(vkDestroyPipelineLayout) \
+ VULKAN_DEVICE_FUNCTION(vkDestroyRenderPass) \
+ VULKAN_DEVICE_FUNCTION(vkDestroySampler) \
+ VULKAN_DEVICE_FUNCTION(vkDestroySemaphore) \
+ VULKAN_DEVICE_FUNCTION(vkDestroyShaderModule) \
+ VULKAN_DEVICE_FUNCTION(vkDestroySwapchainKHR) \
+ VULKAN_DEVICE_FUNCTION(vkDeviceWaitIdle) \
+ VULKAN_DEVICE_FUNCTION(vkEndCommandBuffer) \
+ VULKAN_DEVICE_FUNCTION(vkFreeCommandBuffers) \
+ VULKAN_DEVICE_FUNCTION(vkFreeMemory) \
+ VULKAN_DEVICE_FUNCTION(vkGetBufferMemoryRequirements) \
+ VULKAN_DEVICE_FUNCTION(vkGetImageMemoryRequirements) \
+ VULKAN_DEVICE_FUNCTION(vkGetDeviceQueue) \
+ VULKAN_DEVICE_FUNCTION(vkGetFenceStatus) \
+ VULKAN_DEVICE_FUNCTION(vkGetSwapchainImagesKHR) \
+ VULKAN_DEVICE_FUNCTION(vkMapMemory) \
+ VULKAN_DEVICE_FUNCTION(vkQueuePresentKHR) \
+ VULKAN_DEVICE_FUNCTION(vkQueueSubmit) \
+ VULKAN_DEVICE_FUNCTION(vkResetCommandBuffer) \
+ VULKAN_DEVICE_FUNCTION(vkResetCommandPool) \
+ VULKAN_DEVICE_FUNCTION(vkResetDescriptorPool) \
+ VULKAN_DEVICE_FUNCTION(vkResetFences) \
+ VULKAN_DEVICE_FUNCTION(vkUnmapMemory) \
+ VULKAN_DEVICE_FUNCTION(vkUpdateDescriptorSets) \
+ VULKAN_DEVICE_FUNCTION(vkWaitForFences) \
+ VULKAN_GLOBAL_FUNCTION(vkCreateInstance) \
+ VULKAN_GLOBAL_FUNCTION(vkEnumerateInstanceExtensionProperties) \
+ VULKAN_GLOBAL_FUNCTION(vkEnumerateInstanceLayerProperties) \
+ VULKAN_INSTANCE_FUNCTION(vkCreateDevice) \
+ VULKAN_INSTANCE_FUNCTION(vkDestroyInstance) \
+ VULKAN_INSTANCE_FUNCTION(vkDestroySurfaceKHR) \
+ VULKAN_INSTANCE_FUNCTION(vkEnumerateDeviceExtensionProperties) \
+ VULKAN_INSTANCE_FUNCTION(vkEnumeratePhysicalDevices) \
+ VULKAN_INSTANCE_FUNCTION(vkGetDeviceProcAddr) \
+ VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceFeatures) \
+ VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceProperties) \
+ VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceMemoryProperties) \
+ VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceQueueFamilyProperties) \
+ VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfaceCapabilitiesKHR) \
+ VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfaceFormatsKHR) \
+ VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfacePresentModesKHR) \
+ VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfaceSupportKHR) \
+ VULKAN_INSTANCE_FUNCTION(vkQueueWaitIdle)
+
+#define VULKAN_DEVICE_FUNCTION(name) static PFN_##name name = NULL;
+#define VULKAN_GLOBAL_FUNCTION(name) static PFN_##name name = NULL;
+#define VULKAN_INSTANCE_FUNCTION(name) static PFN_##name name = NULL;
+VULKAN_FUNCTIONS()
+#undef VULKAN_DEVICE_FUNCTION
+#undef VULKAN_GLOBAL_FUNCTION
+#undef VULKAN_INSTANCE_FUNCTION
+
+/* Renderpass types */
+typedef enum {
+ SDL_VULKAN_RENDERPASS_LOAD = 0,
+ SDL_VULKAN_RENDERPASS_CLEAR = 1,
+ SDL_VULKAN_NUM_RENDERPASSES
+} SDL_vulkan_renderpass_type;
+
+/* Sampler types */
+typedef enum {
+ SDL_VULKAN_SAMPLER_NEAREST = 0,
+ SDL_VULKAN_SAMPLER_LINEAR = 1,
+ SDL_VULKAN_NUM_SAMPLERS
+} SDL_vulkan_sampler_type;
+
+/* Vertex shader, common values */
+typedef struct
+{
+ Float4X4 model;
+ Float4X4 projectionAndView;
+} VertexShaderConstants;
+
+/* These should mirror the definitions in VULKAN_PixelShader_Common.incl */
+//static const float TONEMAP_NONE = 0;
+//static const float TONEMAP_LINEAR = 1;
+static const float TONEMAP_CHROME = 2;
+
+//static const float TEXTURETYPE_NONE = 0;
+static const float TEXTURETYPE_RGB = 1;
+static const float TEXTURETYPE_NV12 = 2;
+static const float TEXTURETYPE_NV21 = 3;
+static const float TEXTURETYPE_YUV = 4;
+
+static const float INPUTTYPE_UNSPECIFIED = 0;
+static const float INPUTTYPE_SRGB = 1;
+static const float INPUTTYPE_SCRGB = 2;
+static const float INPUTTYPE_HDR10 = 3;
+
+/* Pixel shader constants, common values */
+typedef struct
+{
+ float scRGB_output;
+ float texture_type;
+ float input_type;
+ float color_scale;
+
+ float tonemap_method;
+ float tonemap_factor1;
+ float tonemap_factor2;
+ float sdr_white_point;
+
+ float YCbCr_matrix[16];
+} PixelShaderConstants;
+
+/* Per-vertex data */
+typedef struct
+{
+ float pos[2];
+ float tex[2];
+ SDL_FColor color;
+} VertexPositionColor;
+
+/* Vulkan Buffer */
+typedef struct
+{
+ VkDeviceMemory deviceMemory;
+ VkBuffer buffer;
+ VkDeviceSize size;
+ void *mappedBufferPtr;
+
+} VULKAN_Buffer;
+
+/* Vulkan image */
+typedef struct
+{
+ SDL_bool allocatedImage;
+ VkImage image;
+ VkImageView imageView;
+ VkDeviceMemory deviceMemory;
+ VkImageLayout imageLayout;
+ VkFormat format;
+} VULKAN_Image;
+
+/* Per-texture data */
+typedef struct
+{
+ VULKAN_Image mainImage;
+ VkRenderPass mainRenderpasses[SDL_VULKAN_NUM_RENDERPASSES];
+ VkFramebuffer mainFramebuffer;
+ VULKAN_Buffer stagingBuffer;
+ VkFilter scaleMode;
+ SDL_Rect lockedRect;
+ int width;
+ int height;
+ VULKAN_Shader shader;
+ const float *YCbCr_matrix;
+
+#if SDL_HAVE_YUV
+ /* YV12 texture support */
+ SDL_bool yuv;
+ VULKAN_Image mainImageU;
+ VULKAN_Image mainImageV;
+
+ /* NV12 texture support */
+ SDL_bool nv12;
+ VULKAN_Image mainImageUV;
+#endif
+
+} VULKAN_TextureData;
+
+/* Pipeline State Object data */
+typedef struct
+{
+ VULKAN_Shader shader;
+ PixelShaderConstants shader_constants;
+ SDL_BlendMode blendMode;
+ VkPrimitiveTopology topology;
+ VkFormat format;
+ VkPipelineLayout pipelineLayout;
+ VkPipeline pipeline;
+} VULKAN_PipelineState;
+
+typedef struct
+{
+ VkBuffer vertexBuffer;
+} VULKAN_DrawStateCache;
+
+/* Private renderer data */
+typedef struct
+{
+ PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
+ VkInstance instance;
+ VkSurfaceKHR surface;
+ VkPhysicalDevice physicalDevice;
+ VkPhysicalDeviceProperties physicalDeviceProperties;
+ VkPhysicalDeviceMemoryProperties physicalDeviceMemoryProperties;
+ VkPhysicalDeviceFeatures physicalDeviceFeatures;
+ VkQueue graphicsQueue;
+ VkQueue presentQueue;
+ VkDevice device;
+ uint32_t graphicsQueueFamilyIndex;
+ uint32_t presentQueueFamilyIndex;
+ VkSwapchainKHR swapchain;
+ VkCommandPool commandPool;
+ VkCommandBuffer *commandBuffers;
+ uint32_t currentCommandBufferIndex;
+ VkCommandBuffer currentCommandBuffer;
+ VkFence *fences;
+ VkSurfaceCapabilitiesKHR surfaceCapabilities;
+ VkSurfaceFormatKHR *surfaceFormats;
+ SDL_bool pixelSizeChanged;
+
+ VkFramebuffer *framebuffers;
+ VkRenderPass renderPasses[SDL_VULKAN_NUM_RENDERPASSES];
+ VkRenderPass currentRenderPass;
+
+ VkShaderModule vertexShaderModules[NUM_SHADERS];
+ VkShaderModule fragmentShaderModules[NUM_SHADERS];
+ VkDescriptorSetLayout descriptorSetLayouts[NUM_SHADERS];
+ VkPipelineLayout pipelineLayouts[NUM_SHADERS];
+
+ /* Vertex buffer data */
+ VULKAN_Buffer vertexBuffers[SDL_VULKAN_NUM_VERTEX_BUFFERS];
+ VertexShaderConstants vertexShaderConstantsData;
+
+ /* Data for staging/allocating textures */
+ VULKAN_Buffer **uploadBuffers;
+ int *currentUploadBuffer;
+
+ /* Data for updating constants */
+ VULKAN_Buffer *constantBuffers;
+ int32_t currentConstantBufferOffset;
+
+ VkSampler samplers[SDL_VULKAN_NUM_SAMPLERS];
+ VkDescriptorPool *descriptorPools;
+ uint32_t currentDescriptorSetIndex;
+
+ int pipelineStateCount;
+ VULKAN_PipelineState *pipelineStates;
+ VULKAN_PipelineState *currentPipelineState;
+
+ SDL_bool supportsEXTSwapchainColorspace;
+ uint32_t surfaceFormatsAllocatedCount;
+ uint32_t surfaceFormatsCount;
+ uint32_t swapchainDesiredImageCount;
+ VkSurfaceFormatKHR surfaceFormat;
+ VkExtent2D swapchainSize;
+ uint32_t swapchainImageCount;
+ VkImage *swapchainImages;
+ VkImageView *swapchainImageViews;
+ VkImageLayout *swapchainImageLayouts;
+ VkSemaphore imageAvailableSemaphore;
+ VkSemaphore renderingFinishedSemaphore;
+ uint32_t currentSwapchainImageIndex;
+
+ /* Cached renderer properties */
+ VULKAN_TextureData *textureRenderTarget;
+ SDL_bool cliprectDirty;
+ SDL_bool currentCliprectEnabled;
+ SDL_Rect currentCliprect;
+ SDL_Rect currentViewport;
+ int currentViewportRotation;
+ SDL_bool viewportDirty;
+ Float4X4 identity;
+ VkComponentMapping identitySwizzle;
+ int currentVertexBuffer;
+ SDL_bool issueBatch;
+} VULKAN_RenderData;
+
+Uint32 VULKAN_VkFormatToSDLPixelFormat(VkFormat vkFormat)
+{
+ switch (vkFormat) {
+
+ case VK_FORMAT_B8G8R8A8_UNORM:
+ return SDL_PIXELFORMAT_ARGB8888;
+ case VK_FORMAT_A2R10G10B10_UNORM_PACK32:
+ return SDL_PIXELFORMAT_XBGR2101010;
+ case VK_FORMAT_R16G16B16A16_SFLOAT:
+ return SDL_PIXELFORMAT_RGBA64_FLOAT;
+ default:
+ return SDL_PIXELFORMAT_UNKNOWN;
+ }
+}
+
+VkDeviceSize VULKAN_GetBytesPerPixel(VkFormat vkFormat)
+{
+ switch (vkFormat) {
+ case VK_FORMAT_R8_UNORM:
+ return 1;
+ case VK_FORMAT_R8G8_UNORM:
+ return 2;
+ case VK_FORMAT_R16G16_UNORM:
+ return 4;
+ case VK_FORMAT_B8G8R8A8_SRGB:
+ case VK_FORMAT_B8G8R8A8_UNORM:
+ case VK_FORMAT_A2R10G10B10_UNORM_PACK32:
+ return 4;
+ case VK_FORMAT_R16G16B16A16_SFLOAT:
+ return 8;
+ default:
+ return 4;
+ }
+}
+
+static VkFormat SDLPixelFormatToVkTextureFormat(Uint32 format, Uint32 colorspace)
+{
+ switch (format) {
+ case SDL_PIXELFORMAT_RGBA64_FLOAT:
+ return VK_FORMAT_R16G16B16A16_SFLOAT;
+ case SDL_PIXELFORMAT_XBGR2101010:
+ return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
+ case SDL_PIXELFORMAT_ARGB8888:
+ case SDL_PIXELFORMAT_XRGB8888:
+ if (colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
+ return VK_FORMAT_B8G8R8A8_SRGB;
+ }
+ return VK_FORMAT_B8G8R8A8_UNORM;
+ case SDL_PIXELFORMAT_YV12:
+ case SDL_PIXELFORMAT_IYUV:
+ case SDL_PIXELFORMAT_NV12: /* Y plane */
+ case SDL_PIXELFORMAT_NV21: /* Y plane */
+ return VK_FORMAT_R8_UNORM;
+ default:
+ return VK_FORMAT_UNDEFINED;
+ }
+}
+static void VULKAN_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture);
+static void VULKAN_DestroyBuffer(VULKAN_RenderData *rendererData, VULKAN_Buffer *vulkanBuffer);
+static void VULKAN_DestroyImage(VULKAN_RenderData *rendererData, VULKAN_Image *vulkanImage);
+static void VULKAN_ResetCommandList(VULKAN_RenderData *rendererData);
+static SDL_bool VULKAN_FindMemoryTypeIndex(VULKAN_RenderData *rendererData, uint32_t typeBits, VkMemoryPropertyFlags flags, uint32_t *memoryTypeIndexOut);
+static VkResult VULKAN_CreateWindowSizeDependentResources(SDL_Renderer *renderer);
+
+static void VULKAN_DestroyAll(SDL_Renderer *renderer)
+{
+ VULKAN_RenderData *rendererData;
+ if (renderer == NULL) {
+ return;
+ }
+ rendererData = (VULKAN_RenderData *)renderer->driverdata;
+ if (rendererData == NULL) {
+ return;
+ }
+
+ if (rendererData->surfaceFormats != NULL) {
+ SDL_free(rendererData->surfaceFormats);
+ rendererData->surfaceFormats = NULL;
+ }
+ if (rendererData->swapchainImages != NULL) {
+ SDL_free(rendererData->swapchainImages);
+ rendererData->swapchainImages = NULL;
+ }
+ if (rendererData->swapchain) {
+ vkDestroySwapchainKHR(rendererData->device, rendererData->swapchain, NULL);
+ rendererData->swapchain = VK_NULL_HANDLE;
+ }
+ if (rendererData->fences != NULL) {
+ for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) {
+ if (rendererData->fences[i] != VK_NULL_HANDLE) {
+ vkDestroyFence(rendererData->device, rendererData->fences[i], NULL);
+ rendererData->fences[i] = VK_NULL_HANDLE;
+ }
+ }
+ SDL_free(rendererData->fences);
+ rendererData->fences = NULL;
+ }
+ if (rendererData->swapchainImageViews) {
+ for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) {
+ if (rendererData->swapchainImageViews[i] != VK_NULL_HANDLE) {
+ vkDestroyImageView(rendererData->device, rendererData->swapchainImageViews[i], NULL);
+ }
+ }
+ SDL_free(rendererData->swapchainImageViews);
+ rendererData->swapchainImageViews = NULL;
+ }
+ if (rendererData->swapchainImageLayouts) {
+ SDL_free(rendererData->swapchainImageLayouts);
+ rendererData->swapchainImageLayouts = NULL;
+
(Patch may be truncated, please check the link at the top of this post.)