From cab20117e6177c6f9143a5e11d250a1b2537c9d2 Mon Sep 17 00:00:00 2001 From: Dan Ginsburg 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 2c7c1b357..566879bb2 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 55e939a8c..4cf36f3e8 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 7c30a707d..dd43c3ed1 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 e2d1ec256..e36a9deba 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 d25b31b63..be0ac3f46 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 a8e077583..1494b9a4a 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 664f615b9..ade1adebe 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 b1ca3517a..ab8d2895b 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 6b239cba0..ae06ab677 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 177980f07..9ab2a7979 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 000000000..9fcba37aa --- /dev/null +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -0,0 +1,3741 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + 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 +#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; + } + if (rendererData->framebuffers) { + for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) { + if (rendererData->framebuffers[i] != VK_NULL_HANDLE) { + vkDestroyFramebuffer(rendererData->device, rendererData->framebuffers[i], NULL); + } + } + SDL_free(rendererData->framebuffers); + rendererData->framebuffers = NULL; + } + for (uint32_t i = 0; i < SDL_arraysize(rendererData->samplers); i++) { + if (rendererData->samplers[i] != VK_NULL_HANDLE) { + vkDestroySampler(rendererData->device, rendererData->samplers[i], NULL); + rendererData->samplers[i] = VK_NULL_HANDLE; + } + } + for (uint32_t i = 0; i < SDL_arraysize(rendererData->vertexBuffers); i++ ) { + VULKAN_DestroyBuffer(rendererData, &rendererData->vertexBuffers[i]); + } + SDL_memset(rendererData->vertexBuffers, 0, sizeof(rendererData->vertexBuffers)); + for (uint32_t i = 0; i < SDL_VULKAN_NUM_RENDERPASSES; i++) { + if (rendererData->renderPasses[i] != VK_NULL_HANDLE) { + vkDestroyRenderPass(rendererData->device, rendererData->renderPasses[i], NULL); + rendererData->renderPasses[i] = VK_NULL_HANDLE; + } + } + if (rendererData->imageAvailableSemaphore != VK_NULL_HANDLE) { + vkDestroySemaphore(rendererData->device, rendererData->imageAvailableSemaphore, NULL); + rendererData->imageAvailableSemaphore = VK_NULL_HANDLE; + } + if (rendererData->renderingFinishedSemaphore != VK_NULL_HANDLE) { + vkDestroySemaphore(rendererData->device, rendererData->renderingFinishedSemaphore, NULL); + rendererData->renderingFinishedSemaphore = VK_NULL_HANDLE; + } + if (rendererData->commandPool) { + if (rendererData->commandBuffers) { + vkFreeCommandBuffers(rendererData->device, rendererData->commandPool, rendererData->swapchainImageCount, rendererData->commandBuffers); + SDL_free(rendererData->commandBuffers); + rendererData->commandBuffers = NULL; + } + vkDestroyCommandPool(rendererData->device, rendererData->commandPool, NULL); + rendererData->commandPool = VK_NULL_HANDLE; + } + if (rendererData->descriptorPools) { + for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) { + if (rendererData->descriptorPools[i] != VK_NULL_HANDLE) { + vkDestroyDescriptorPool(rendererData->device, rendererData->descriptorPools[i], NULL); + } + } + SDL_free(rendererData->descriptorPools); + rendererData->descriptorPools = NULL; + } + for (uint32_t i = 0; i < NUM_SHADERS; i++) { + if (rendererData->vertexShaderModules[i] != VK_NULL_HANDLE) { + vkDestroyShaderModule(rendererData->device, rendererData->vertexShaderModules[i], NULL); + rendererData->vertexShaderModules[i] = VK_NULL_HANDLE; + } + if (rendererData->fragmentShaderModules[i] != VK_NULL_HANDLE) { + vkDestroyShaderModule(rendererData->device, rendererData->fragmentShaderModules[i], NULL); + rendererData->fragmentShaderModules[i] = VK_NULL_HANDLE; + } + if (rendererData->descriptorSetLayouts[i] != VK_NULL_HANDLE) { + vkDestroyDescriptorSetLayout(rendererData->device, rendererData->descriptorSetLayouts[i], NULL); + rendererData->descriptorSetLayouts[i] = VK_NULL_HANDLE; + } + if (rendererData->pipelineLayouts[i] != VK_NULL_HANDLE) { + vkDestroyPipelineLayout(rendererData->device, rendererData->pipelineLayouts[i], NULL); + rendererData->pipelineLayouts[i] = VK_NULL_HANDLE; + } + } + for (uint32_t i = 0; i < rendererData->pipelineStateCount; i++) { + vkDestroyPipeline(rendererData->device, rendererData->pipelineStates[i].pipeline, NULL); + } + SDL_free(rendererData->pipelineStates); + rendererData->pipelineStateCount = 0; + + if (rendererData->currentUploadBuffer) { + for (uint32_t i = 0; i < rendererData->swapchainImageCount; ++i) { + for (uint32_t j = 0; j < rendererData->currentUploadBuffer[i]; ++j) { + VULKAN_DestroyBuffer(rendererData, &rendererData->uploadBuffers[i][j]); + } + SDL_free(rendererData->uploadBuffers[i]); + } + SDL_free(rendererData->uploadBuffers); + SDL_free(rendererData->currentUploadBuffer); + } + + if (rendererData->constantBuffers) { + for (uint32_t i = 0; i < rendererData->swapchainImageCount; ++i) { + VULKAN_DestroyBuffer(rendererData, &rendererData->constantBuffers[i]); + } + SDL_free(rendererData->constantBuffers); + rendererData->constantBuffers = NULL; + } + + if (rendererData->device != VK_NULL_HANDLE) { + vkDestroyDevice(rendererData->device, NULL); + rendererData->device = VK_NULL_HANDLE; + } + if (rendererData->surface != VK_NULL_HANDLE) { + vkDestroySurfaceKHR(rendererData->instance, rendererData->surface, NULL); + rendererData->surface = VK_NULL_HANDLE; + } + if (rendererData->instance != VK_NULL_HANDLE) { + vkDestroyInstance(rendererData->instance, NULL); + rendererData->instance = VK_NULL_HANDLE; + } +} + +static void VULKAN_DestroyBuffer(VULKAN_RenderData *rendererData, VULKAN_Buffer *vulkanBuffer) +{ + if (vulkanBuffer->buffer != VK_NULL_HANDLE) { + vkDestroyBuffer(rendererData->device, vulkanBuffer->buffer, NULL); + vulkanBuffer->buffer = VK_NULL_HANDLE; + } + if (vulkanBuffer->deviceMemory != VK_NULL_HANDLE) { + vkFreeMemory(rendererData->device, vulkanBuffer->deviceMemory, NULL); + vulkanBuffer->deviceMemory = VK_NULL_HANDLE; + } + SDL_memset(vulkanBuffer, 0, sizeof(VULKAN_Buffer)); +} + +static VkResult VULKAN_AllocateBuffer(VULKAN_RenderData *rendererData, VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags memoryProps, VULKAN_Buffer *bufferOut) +{ + VkResult result; + VkBufferCreateInfo bufferCreateInfo = { 0 }; + bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferCreateInfo.size = size; + bufferCreateInfo.usage = usage; + result = vkCreateBuffer(rendererData->device, &bufferCreateInfo, NULL, &bufferOut->buffer); + if (result != VK_SUCCESS) { + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateBuffer(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + + VkMemoryRequirements memoryRequirements = { 0 }; + vkGetBufferMemoryRequirements(rendererData->device, bufferOut->buffer, &memoryRequirements); + if (result != VK_SUCCESS) { + VULKAN_DestroyBuffer(rendererData, bufferOut); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkGetBufferMemoryRequirements(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + + uint32_t memoryTypeIndex = 0; + if (!VULKAN_FindMemoryTypeIndex(rendererData, memoryRequirements.memoryTypeBits, memoryProps, &memoryTypeIndex)) { + VULKAN_DestroyBuffer(rendererData, bufferOut); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "VULKAN_FindMemoryTypeIndex failed.\n"); + return VK_ERROR_UNKNOWN;; + } + + VkMemoryAllocateInfo memoryAllocateInfo = { 0 }; + memoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memoryAllocateInfo.allocationSize = memoryRequirements.size; + memoryAllocateInfo.memoryTypeIndex = memoryTypeIndex; + result = vkAllocateMemory(rendererData->device, &memoryAllocateInfo, NULL, &bufferOut->deviceMemory); + if (result != VK_SUCCESS) { + VULKAN_DestroyBuffer(rendererData, bufferOut); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkAllocateMemory(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + result = vkBindBufferMemory(rendererData->device, bufferOut->buffer, bufferOut->deviceMemory, 0); + if (result != VK_SUCCESS) { + VULKAN_DestroyBuffer(rendererData, bufferOut); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkBindBufferMemory(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + + result = vkMapMemory(rendererData->device, bufferOut->deviceMemory, 0, size, 0, &bufferOut->mappedBufferPtr); + if (result != VK_SUCCESS) { + VULKAN_DestroyBuffer(rendererData, bufferOut); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkMapMemory(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + bufferOut->size = size; + return result; +} + +static void VULKAN_DestroyImage(VULKAN_RenderData *rendererData, VULKAN_Image *vulkanImage) +{ + if (vulkanImage->imageView != VK_NULL_HANDLE) { + vkDestroyImageView(rendererData->device, vulkanImage->imageView, NULL); + vulkanImage->imageView = VK_NULL_HANDLE; + } + if (vulkanImage->image != VK_NULL_HANDLE) { + if (vulkanImage->allocatedImage) { + vkDestroyImage(rendererData->device, vulkanImage->image, NULL); + } + vulkanImage->image = VK_NULL_HANDLE; + } + + if (vulkanImage->deviceMemory != VK_NULL_HANDLE) { + vkFreeMemory(rendererData->device, vulkanImage->deviceMemory, NULL); + vulkanImage->deviceMemory = VK_NULL_HANDLE; + } + SDL_memset(vulkanImage, 0, sizeof(VULKAN_Image)); +} + +static VkResult VULKAN_AllocateImage(VULKAN_RenderData *rendererData, uint32_t width, uint32_t height, VkFormat format, VkImageUsageFlags imageUsage, VkComponentMapping swizzle, VkImage externalImage, VULKAN_Image *imageOut) +{ + VkResult result; + VkImageCreateInfo imageCreateInfo = { 0 }; + + SDL_memset(imageOut, 0, sizeof( VULKAN_Image)); + imageOut->format = format; + imageOut->imageLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageCreateInfo.flags = 0; + imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; + imageCreateInfo.format = format; + imageCreateInfo.extent.width = width; + imageCreateInfo.extent.height = height; + imageCreateInfo.extent.depth = 1; + imageCreateInfo.mipLevels = 1; + imageCreateInfo.arrayLayers = 1; + imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageCreateInfo.usage = imageUsage; + imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageCreateInfo.queueFamilyIndexCount = 0; + imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + imageOut->allocatedImage = VK_FALSE; + if (externalImage == VK_NULL_HANDLE) { + imageOut->allocatedImage = VK_TRUE; + result = vkCreateImage(rendererData->device, &imageCreateInfo, NULL, &imageOut->image); + if (result != VK_SUCCESS) { + VULKAN_DestroyImage(rendererData, imageOut); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateImage(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + + VkMemoryRequirements memoryRequirements = { 0 }; + vkGetImageMemoryRequirements(rendererData->device, imageOut->image, &memoryRequirements); + if (result != VK_SUCCESS) { + VULKAN_DestroyImage(rendererData, imageOut); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkGetImageMemoryRequirements(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + + uint32_t memoryTypeIndex = 0; + if (!VULKAN_FindMemoryTypeIndex(rendererData, memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &memoryTypeIndex)) { + VULKAN_DestroyImage(rendererData, imageOut); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "VULKAN_FindMemoryTypeIndex failed.\n"); + return VK_ERROR_UNKNOWN; + } + + VkMemoryAllocateInfo memoryAllocateInfo = { 0 }; + memoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memoryAllocateInfo.allocationSize = memoryRequirements.size; + memoryAllocateInfo.memoryTypeIndex = memoryTypeIndex; + result = vkAllocateMemory(rendererData->device, &memoryAllocateInfo, NULL, &imageOut->deviceMemory); + if (result != VK_SUCCESS) { + VULKAN_DestroyImage(rendererData, imageOut); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkAllocateMemory(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + result = vkBindImageMemory(rendererData->device, imageOut->image, imageOut->deviceMemory, 0); + if (result != VK_SUCCESS) { + VULKAN_DestroyImage(rendererData, imageOut); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkBindImageMemory(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + } + + VkImageViewCreateInfo imageViewCreateInfo = { 0 }; + imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + imageViewCreateInfo.image = imageOut->image; + imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + imageViewCreateInfo.format = imageCreateInfo.format; + imageViewCreateInfo.components = swizzle; + imageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageViewCreateInfo.subresourceRange.baseMipLevel = 0; + imageViewCreateInfo.subresourceRange.levelCount = 1; + imageViewCreateInfo.subresourceRange.baseArrayLayer = 0; + imageViewCreateInfo.subresourceRange.layerCount = 1; + result = vkCreateImageView(rendererData->device, &imageViewCreateInfo, NULL, &imageOut->imageView); + if (result != VK_SUCCESS) { + VULKAN_DestroyImage(rendererData, imageOut); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateImageView(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + + return result; +} + + +static void VULKAN_RecordPipelineImageBarrier(VULKAN_RenderData *rendererData, VkAccessFlags sourceAccessMask, VkAccessFlags destAccessMask, + VkPipelineStageFlags srcStageFlags, VkPipelineStageFlags dstStageFlags, VkImageLayout destLayout, VkImage image, VkImageLayout *imageLayout) +{ + /* Stop any outstanding renderpass if open */ + if (rendererData->currentRenderPass != VK_NULL_HANDLE) { + vkCmdEndRenderPass(rendererData->currentCommandBuffer); + rendererData->currentRenderPass = VK_NULL_HANDLE; + } + + VkImageMemoryBarrier barrier = { 0 }; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.srcAccessMask = sourceAccessMask; + barrier.dstAccessMask = destAccessMask; + barrier.oldLayout = *imageLayout; + barrier.newLayout = destLayout; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = image; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.baseMipLevel = 0; + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = 1; + vkCmdPipelineBarrier(rendererData->currentCommandBuffer, srcStageFlags, dstStageFlags, 0, 0, NULL, 0, NULL, 1, &barrier); + *imageLayout = destLayout; +} + +static VkResult VULKAN_AcquireNextSwapchainImage(SDL_Renderer *renderer) +{ + VULKAN_RenderData *rendererData = ( VULKAN_RenderData * )renderer->driverdata; + + VkResult result; + + result = vkAcquireNextImageKHR(rendererData->device, rendererData->swapchain, UINT64_MAX, + rendererData->imageAvailableSemaphore, VK_NULL_HANDLE, &rendererData->currentSwapchainImageIndex); + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_ERROR_SURFACE_LOST_KHR) { + result = VULKAN_CreateWindowSizeDependentResources(renderer); + return result; + } else if(result == VK_SUBOPTIMAL_KHR) { + /* Suboptimal, but we can contiue */ + } + else if (result != VK_SUCCESS) { + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkAcquireNextImageKHR(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + + return result; +} + +static void VULKAN_BeginRenderPass(VULKAN_RenderData *rendererData, VkAttachmentLoadOp loadOp, VkClearColorValue *clearColor) +{ + int width = rendererData->swapchainSize.width; + int height = rendererData->swapchainSize.height; + if (rendererData->textureRenderTarget) { + width = rendererData->textureRenderTarget->width; + height = rendererData->textureRenderTarget->height; + } + switch (loadOp) { + case VK_ATTACHMENT_LOAD_OP_CLEAR: + rendererData->currentRenderPass = rendererData->textureRenderTarget ? + rendererData->textureRenderTarget->mainRenderpasses[SDL_VULKAN_RENDERPASS_CLEAR] : + rendererData->renderPasses[SDL_VULKAN_RENDERPASS_CLEAR]; + break; + + case VK_ATTACHMENT_LOAD_OP_LOAD: + default: + rendererData->currentRenderPass = rendererData->textureRenderTarget ? + rendererData->textureRenderTarget->mainRenderpasses[SDL_VULKAN_RENDERPASS_LOAD] : + rendererData->renderPasses[SDL_VULKAN_RENDERPASS_LOAD]; + break; + } + + VkFramebuffer framebuffer = rendererData->textureRenderTarget ? + rendererData->textureRenderTarget->mainFramebuffer : + rendererData->framebuffers[rendererData->currentSwapchainImageIndex]; + + VkRenderPassBeginInfo renderPassBeginInfo = { 0 }; + renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + renderPassBeginInfo.pNext = NULL; + renderPassBeginInfo.renderPass = rendererData->currentRenderPass; + renderPassBeginInfo.framebuffer = framebuffer; + renderPassBeginInfo.renderArea.offset.x = 0; + renderPassBeginInfo.renderArea.offset.y = 0; + renderPassBeginInfo.renderArea.extent.width = width; + renderPassBeginInfo.renderArea.extent.height = height; + renderPassBeginInfo.clearValueCount = (clearColor == NULL) ? 0 : 1; + VkClearValue clearValue; + if (clearColor != NULL) { + clearValue.color = *clearColor; + renderPassBeginInfo.pClearValues = &clearValue; + } + vkCmdBeginRenderPass(rendererData->currentCommandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); +} + +static void VULKAN_EnsureCommandBuffer(VULKAN_RenderData *rendererData) +{ + if (rendererData->currentCommandBuffer == VK_NULL_HANDLE) { + rendererData->currentCommandBuffer = rendererData->commandBuffers[rendererData->currentCommandBufferIndex]; + VULKAN_ResetCommandList(rendererData); + + /* Ensure the swapchain is in the correct layout */ + if (rendererData->swapchainImageLayouts[rendererData->currentSwapchainImageIndex] == VK_IMAGE_LAYOUT_UNDEFINED) { + VULKAN_RecordPipelineImageBarrier(rendererData, + 0, + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + rendererData->swapchainImages[rendererData->currentSwapchainImageIndex], + &rendererData->swapchainImageLayouts[rendererData->currentSwapchainImageIndex]); + } + else if (rendererData->swapchainImageLayouts[rendererData->currentCommandBufferIndex] != VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { + VULKAN_RecordPipelineImageBarrier(rendererData, + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT, + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + rendererData->swapchainImages[rendererData->currentSwapchainImageIndex], + &rendererData->swapchainImageLayouts[rendererData->currentSwapchainImageIndex]); + } + } +} + +static SDL_bool VULKAN_ActivateCommandBuffer(SDL_Renderer *renderer, VkAttachmentLoadOp loadOp, VkClearColorValue *clearColor, VULKAN_DrawStateCache *stateCache) +{ + VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; + + VULKAN_EnsureCommandBuffer(rendererData); + + if (rendererData->currentRenderPass == VK_NULL_HANDLE || loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR) { + if (rendererData->currentRenderPass != VK_NULL_HANDLE) { + vkCmdEndRenderPass(rendererData->currentCommandBuffer); + rendererData->currentRenderPass = VK_NULL_HANDLE; + } + VULKAN_BeginRenderPass(rendererData, loadOp, clearColor); + } + + // Bind cached VB now + if (stateCache->vertexBuffer != VK_NULL_HANDLE) { + VkDeviceSize offset = 0; + vkCmdBindVertexBuffers(rendererData->currentCommandBuffer, 0, 1, &stateCache->vertexBuffer, &offset); + } + + return SDL_TRUE; +} + +static void VULKAN_WaitForGPU(VULKAN_RenderData *rendererData) +{ + vkQueueWaitIdle(rendererData->graphicsQueue); +} + + +static void VULKAN_ResetCommandList(VULKAN_RenderData *rendererData) +{ + vkResetCommandBuffer(rendererData->currentCommandBuffer, 0); + vkResetDescriptorPool(rendererData->device, rendererData->descriptorPools[rendererData->currentCommandBufferIndex], 0); + + VkCommandBufferBeginInfo beginInfo = { 0 }; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = 0; + vkBeginCommandBuffer(rendererData->currentCommandBuffer, &beginInfo); + + rendererData->currentPipelineState = NULL; + rendererData->currentVertexBuffer = 0; + rendererData->issueBatch = SDL_FALSE; + rendererData->cliprectDirty = SDL_TRUE; + rendererData->currentDescriptorSetIndex = 0; + rendererData->currentConstantBufferOffset = -1; + + /* Release any upload buffers that were inflight */ + for (uint32_t i = 0; i < rendererData->currentUploadBuffer[rendererData->currentCommandBufferIndex]; ++i) { + VULKAN_DestroyBuffer(rendererData, &rendererData->uploadBuffers[rendererData->currentCommandBufferIndex][i]); + } + rendererData->currentUploadBuffer[rendererData->currentCommandBufferIndex] = 0; +} + +static VkResult VULKAN_IssueBatch(VULKAN_RenderData *rendererData) +{ + VkResult result; + if (rendererData->currentCommandBuffer == VK_NULL_HANDLE) { + return VK_SUCCESS; + } + + if (rendererData->currentRenderPass) { + vkCmdEndRenderPass(rendererData->currentCommandBuffer); + rendererData->currentRenderPass = VK_NULL_HANDLE; + } + + rendererData->currentPipelineState = VK_NULL_HANDLE; + rendererData->viewportDirty = SDL_TRUE; + + vkEndCommandBuffer(rendererData->currentCommandBuffer); + + VkSubmitInfo submitInfo = { 0 }; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &rendererData->currentCommandBuffer; + result = vkQueueSubmit(rendererData->graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); + + VULKAN_WaitForGPU(rendererData); + + VULKAN_ResetCommandList(rendererData); + + return result; +} + +static void VULKAN_DestroyRenderer(SDL_Renderer *renderer) +{ + VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; + if (rendererData->device != VK_NULL_HANDLE) { + VULKAN_WaitForGPU(rendererData); + VULKAN_DestroyAll(renderer); + } + if (rendererData) { + SDL_free(rendererData); + } + SDL_free(renderer); +} + +static VkBlendFactor GetBlendFactor(SDL_BlendFactor factor) +{ + switch (factor) { + case SDL_BLENDFACTOR_ZERO: + return VK_BLEND_FACTOR_ZERO; + case SDL_BLENDFACTOR_ONE: + return VK_BLEND_FACTOR_ONE; + case SDL_BLENDFACTOR_SRC_COLOR: + return VK_BLEND_FACTOR_SRC_COLOR; + case SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR: + return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR; + case SDL_BLENDFACTOR_SRC_ALPHA: + return VK_BLEND_FACTOR_SRC_ALPHA; + case SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: + return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + case SDL_BLENDFACTOR_DST_COLOR: + return VK_BLEND_FACTOR_DST_COLOR; + case SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR: + return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR; + case SDL_BLENDFACTOR_DST_ALPHA: + return VK_BLEND_FACTOR_DST_ALPHA; + case SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA: + return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA; + default: + return VK_BLEND_FACTOR_MAX_ENUM; + } +} + +static VkBlendOp GetBlendOp(SDL_BlendOperation operation) +{ + switch (operation) { + case SDL_BLENDOPERATION_ADD: + return VK_BLEND_OP_ADD; + case SDL_BLENDOPERATION_SUBTRACT: + return VK_BLEND_OP_SUBTRACT; + case SDL_BLENDOPERATION_REV_SUBTRACT: + return VK_BLEND_OP_REVERSE_SUBTRACT; + case SDL_BLENDOPERATION_MINIMUM: + return VK_BLEND_OP_MIN; + case SDL_BLENDOPERATION_MAXIMUM: + return VK_BLEND_OP_MAX; + default: + return VK_BLEND_OP_MAX_ENUM; + } +} + + +static VULKAN_PipelineState *VULKAN_CreatePipelineState(SDL_Renderer *renderer, + VULKAN_Shader shader, SDL_BlendMode blendMode, VkPrimitiveTopology topology, VkFormat format) +{ + VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; + VULKAN_PipelineState *pipelineStates; + VkPipeline pipeline = VK_NULL_HANDLE; + VkResult result = VK_SUCCESS; + VkPipelineVertexInputStateCreateInfo vertexInputCreateInfo = { 0 }; + VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo = { 0 }; + VkVertexInputAttributeDescription attributeDescriptions[3]; + VkVertexInputBindingDescription bindingDescriptions[1]; + VkPipelineShaderStageCreateInfo shaderStageCreateInfo[2]; + VkPipelineDynamicStateCreateInfo dynamicStateCreateInfo = { 0 }; + VkPipelineViewportStateCreateInfo viewportStateCreateInfo = { 0 }; + VkPipelineRasterizationStateCreateInfo rasterizationStateCreateInfo = { 0 }; + VkPipelineMultisampleStateCreateInfo multisampleStateCreateInfo = { 0 }; + VkPipelineDepthStencilStateCreateInfo depthStencilStateCreateInfo = { 0 }; + VkPipelineColorBlendStateCreateInfo colorBlendStateCreateInfo = { 0 }; + + VkGraphicsPipelineCreateInfo pipelineCreateInfo = { 0 }; + pipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineCreateInfo.flags = 0; + pipelineCreateInfo.pStages = shaderStageCreateInfo; + pipelineCreateInfo.pVertexInputState = &vertexInputCreateInfo; + pipelineCreateInfo.pInputAssemblyState = &inputAssemblyStateCreateInfo; + pipelineCreateInfo.pViewportState = &viewportStateCreateInfo; + pipelineCreateInfo.pRasterizationState = &rasterizationStateCreateInfo; + pipelineCreateInfo.pMultisampleState = &multisampleStateCreateInfo; + pipelineCreateInfo.pDepthStencilState = &depthStencilStateCreateInfo; + pipelineCreateInfo.pColorBlendState = &colorBlendStateCreateInfo; + pipelineCreateInfo.pDynamicState = &dynamicStateCreateInfo; + + /* Shaders */ + const char *name = "main"; + for (uint32_t i = 0; i < 2; i++) { + SDL_memset(&shaderStageCreateInfo[i], 0, sizeof(shaderStageCreateInfo[i])); + shaderStageCreateInfo[i].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shaderStageCreateInfo[i].module = (i == 0) ? rendererData->vertexShaderModules[shader] : rendererData->fragmentShaderModules[shader]; + shaderStageCreateInfo[i].stage = (i == 0) ? VK_SHADER_STAGE_VERTEX_BIT : VK_SHADER_STAGE_FRAGMENT_BIT; + shaderStageCreateInfo[i].pName = name; + } + pipelineCreateInfo.stageCount = 2; + pipelineCreateInfo.pStages = &shaderStageCreateInfo[0]; + + + /* Vertex input */ + vertexInputCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputCreateInfo.vertexAttributeDescriptionCount = 3; + vertexInputCreateInfo.pVertexAttributeDescriptions = &attributeDescriptions[0]; + vertexInputCreateInfo.vertexBindingDescriptionCount = 1; + vertexInputCreateInfo.pVertexBindingDescriptions = &bindingDescriptions[0]; + + attributeDescriptions[ 0 ].binding = 0; + attributeDescriptions[ 0 ].format = VK_FORMAT_R32G32_SFLOAT; + attributeDescriptions[ 0 ].location = 0; + attributeDescriptions[ 0 ].offset = 0; + attributeDescriptions[ 1 ].binding = 0; + attributeDescriptions[ 1 ].format = VK_FORMAT_R32G32_SFLOAT; + attributeDescriptions[ 1 ].location = 1; + attributeDescriptions[ 1 ].offset = 8; + attributeDescriptions[ 2 ].binding = 0; + attributeDescriptions[ 2 ].format = VK_FORMAT_R32G32B32A32_SFLOAT; + attributeDescriptions[ 2 ].location = 2; + attributeDescriptions[ 2 ].offset = 16; + + bindingDescriptions[ 0 ].binding = 0; + bindingDescriptions[ 0 ].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + bindingDescriptions[ 0 ].stride = 32; + + /* Input assembly */ + inputAssemblyStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssemblyStateCreateInfo.topology = ( VkPrimitiveTopology ) topology; + inputAssemblyStateCreateInfo.primitiveRestartEnable = VK_FALSE; + + viewportStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportStateCreateInfo.scissorCount = 1; + viewportStateCreateInfo.viewportCount = 1; + + /* Dynamic states */ + dynamicStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + VkDynamicState dynamicStates[2] = { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR + }; + dynamicStateCreateInfo.dynamicStateCount = SDL_arraysize(dynamicStates); + dynamicStateCreateInfo.pDynamicStates = dynamicStates; + + /* Rasterization state */ + rasterizationStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizationStateCreateInfo.depthClampEnable = VK_FALSE; + rasterizationStateCreateInfo.rasterizerDiscardEnable = VK_FALSE; + rasterizationStateCreateInfo.cullMode = VK_CULL_MODE_NONE; + rasterizationStateCreateInfo.polygonMode = VK_POLYGON_MODE_FILL; + rasterizationStateCreateInfo.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rasterizationStateCreateInfo.depthBiasEnable = VK_FALSE; + rasterizationStateCreateInfo.depthBiasConstantFactor = 0.0f; + rasterizationStateCreateInfo.depthBiasClamp = 0.0f; + rasterizationStateCreateInfo.depthBiasSlopeFactor = 0.0f; + rasterizationStateCreateInfo.lineWidth = 1.0f; + + /* MSAA state */ + multisampleStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + VkSampleMask multiSampleMask = 0xFFFFFFFF; + multisampleStateCreateInfo.pSampleMask = &multiSampleMask; + multisampleStateCreateInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + /* Depth Stencil */ + depthStencilStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + + /* Color blend */ + VkPipelineColorBlendAttachmentState colorBlendAttachment = { 0 }; + colorBlendStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlendStateCreateInfo.attachmentCount = 1; + colorBlendStateCreateInfo.pAttachments = &colorBlendAttachment; + colorBlendAttachment.blendEnable = VK_TRUE; + colorBlendAttachment.srcColorBlendFactor = GetBlendFactor(SDL_GetBlendModeSrcColorFactor(blendMode)); + colorBlendAttachment.srcAlphaBlendFactor = GetBlendFactor(SDL_GetBlendModeSrcAlphaFactor(blendMode)); + colorBlendAttachment.colorBlendOp = GetBlendOp(SDL_GetBlendModeColorOperation(blendMode)); + colorBlendAttachment.dstColorBlendFactor = GetBlendFactor(SDL_GetBlendModeDstColorFactor(blendMode)); + colorBlendAttachment.dstAlphaBlendFactor = GetBlendFactor(SDL_GetBlendModeDstAlphaFactor(blendMode)); + colorBlendAttachment.alphaBlendOp = GetBlendOp(SDL_GetBlendModeAlphaOperation(blendMode)); + colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + + /* Renderpass / layout */ + pipelineCreateInfo.renderPass = rendererData->currentRenderPass; + pipelineCreateInfo.subpass = 0; + pipelineCreateInfo.layout = rendererData->pipelineLayouts[shader]; + + result = vkCreateGraphicsPipelines(rendererData->device, VK_NULL_HANDLE, 1, &pipelineCreateInfo, NULL, &pipeline); + if (result != VK_SUCCESS) { + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateGraphicsPipelines(): %s\n", SDL_Vulkan_GetResultString(result)); + return NULL; + } + + pipelineStates = (VULKAN_PipelineState *)SDL_realloc(rendererData->pipelineStates, (rendererData->pipelineStateCount + 1) * sizeof(*pipelineStates)); + pipelineStates[rendererData->pipelineStateCount].shader = shader; + pipelineStates[rendererData->pipelineStateCount].blendMode = blendMode; + pipelineStates[rendererData->pipelineStateCount].topology = topology; + pipelineStates[rendererData->pipelineStateCount].format = format; + pipelineStates[rendererData->pipelineStateCount].pipeline = pipeline; + pipelineStates[rendererData->pipelineStateCount].pipelineLayout = pipelineCreateInfo.layout; + rendererData->pipelineStates = pipelineStates; + ++rendererData->pipelineStateCount; + + return &pipelineStates[rendererData->pipelineStateCount - 1]; +} + +static SDL_bool VULKAN_FindMemoryTypeIndex(VULKAN_RenderData *rendererData, uint32_t typeBits, VkMemoryPropertyFlags flags, uint32_t *memoryTypeIndexOut) +{ + uint32_t memoryTypeIndex = 0; + SDL_bool foundExactMatch = SDL_FALSE; + for (memoryTypeIndex = 0; memoryTypeIndex < rendererData->physicalDeviceMemoryProperties.memoryTypeCount; memoryTypeIndex++) { + if (typeBits & (1 << memoryTypeIndex)) { + if (rendererData->physicalDeviceMemoryProperties.memoryTypes[memoryTypeIndex].propertyFlags == flags) { + foundExactMatch = SDL_TRUE; + break; + } + } + } + if (!foundExactMatch) { + for (memoryTypeIndex = 0; memoryTypeIndex < rendererData->physicalDeviceMemoryProperties.memoryTypeCount; memoryTypeIndex++) { + if (typeBits & (1 << memoryTypeIndex)) { + if (rendererData->physicalDeviceMemoryProperties.memoryTypes[memoryTypeIndex].propertyFlags & flags) { + break; + } + } + } + } + + if (memoryTypeIndex >= rendererData->physicalDeviceMemoryProperties.memoryTypeCount) { + SDL_SetError("[Vulkan] Unable to find memory type for allocation."); + return SDL_FALSE; + } + *memoryTypeIndexOut = memoryTypeIndex; + return SDL_TRUE; +} + +static VkResult VULKAN_CreateVertexBuffer(VULKAN_RenderData *rendererData, size_t vbidx, size_t size) +{ + VkResult result; + + VULKAN_DestroyBuffer(rendererData, &rendererData->vertexBuffers[vbidx]); + + result = VULKAN_AllocateBuffer(rendererData, size, + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, + &rendererData->vertexBuffers[vbidx]); + if (result != VK_SUCCESS) { + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "VULKAN_AllocateBuffer(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + return result; +} + +static int VULKAN_LoadGlobalFunctions(VULKAN_RenderData *rendererData) +{ +#define VULKAN_DEVICE_FUNCTION(name) +#define VULKAN_GLOBAL_FUNCTION(name) \ + name = (PFN_##name)rendererData->vkGetInstanceProcAddr(VK_NULL_HANDLE, #name); \ + if (!name) { \ + SDL_LogError(SDL_LOG_CATEGORY_RENDER, \ + "vkGetInstanceProcAddr(VK_NULL_HANDLE, \"" #name "\") failed\n"); \ + return -1; \ + } +#define VULKAN_INSTANCE_FUNCTION(name) + VULKAN_FUNCTIONS() +#undef VULKAN_DEVICE_FUNCTION +#undef VULKAN_GLOBAL_FUNCTION +#undef VULKAN_INSTANCE_FUNCTION + + return 0; +} + +static int VULKAN_LoadInstanceFunctions(VULKAN_RenderData *rendererData) +{ +#define VULKAN_DEVICE_FUNCTION(name) +#define VULKAN_GLOBAL_FUNCTION(name) +#define VULKAN_INSTANCE_FUNCTION(name) \ + name = (PFN_##name)rendererData->vkGetInstanceProcAddr(rendererData->instance, #name); \ + if (!name) { \ + SDL_LogError(SDL_LOG_CATEGORY_RENDER, \ + "vkGetInstanceProcAddr(instance, \"" #name "\") failed\n"); \ + return -1; \ + } + VULKAN_FUNCTIONS() +#undef VULKAN_DEVICE_FUNCTION +#undef VULKAN_GLOBAL_FUNCTION +#undef VULKAN_INSTANCE_FUNCTION + + return 0; +} + +static int VULKAN_LoadDeviceFunctions(VULKAN_RenderData *rendererData) +{ +#define VULKAN_DEVICE_FUNCTION(name) \ + name = (PFN_##name)vkGetDeviceProcAddr(rendererData->device, #name); \ + if (!name) { \ + SDL_LogError(SDL_LOG_CATEGORY_RENDER, \ + "vkGetDeviceProcAddr(device, \"" #name "\") failed\n"); \ + return -1; \ + } +#define VULKAN_GLOBAL_FUNCTION(name) +#define VULKAN_INSTANCE_FUNCTION(name) + VULKAN_FUNCTIONS() +#undef VULKAN_DEVICE_FUNCTION +#undef VULKAN_GLOBAL_FUNCTION +#undef VULKAN_INSTANCE_FUNCTION + return 0; +} + +static VkResult VULKAN_FindPhysicalDevice(VULKAN_RenderData *rendererData) +{ + uint32_t physicalDeviceCount = 0; + VkPhysicalDevice *physicalDevices; + VkQueueFamilyProperties *queueFamiliesProperties = NULL; + uint32_t queueFamiliesPropertiesAllocatedSize = 0; + VkExtensionProperties *deviceExtensions = NULL; + uint32_t deviceExtensionsAllocatedSize = 0; + uint32_t physicalDeviceIndex; + VkResult result; + + result = vkEnumeratePhysicalDevices(rendererData->instance, &physicalDeviceCount, NULL); + if (result != VK_SUCCESS) { + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkEnumeratePhysicalDevices(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + if (physicalDeviceCount == 0) { + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkEnumeratePhysicalDevices(): no physical devices\n"); + return VK_ERROR_UNKNOWN; + } + physicalDevices = (VkPhysicalDevice *)SDL_malloc(sizeof(VkPhysicalDevice) * physicalDeviceCount); + result = vkEnumeratePhysicalDevices(rendererData->instance, &physicalDeviceCount, physicalDevices); + if (result != VK_SUCCESS) { + SDL_free(physicalDevices); + SDL_LogError(SDL_LOG_CATEGORY_RENDER,"vkEnumeratePhysicalDevices(): %s\n",SDL_Vulkan_GetResultString(result)); + return result; + } + rendererData->physicalDevice = NULL; + for (physicalDeviceIndex = 0; physicalDeviceIndex < physicalDeviceCount; physicalDeviceIndex++) { + uint32_t queueFamiliesCount = 0; + uint32_t queueFamilyIndex; + uint32_t deviceExtensionCount = 0; + SDL_bool hasSwapchainExtension = SDL_FALSE; + uint32_t i; + + VkPhysicalDevice physicalDevice = physicalDevices[physicalDeviceIndex]; + vkGetPhysicalDeviceProperties(physicalDevice, &rendererData->physicalDeviceProperties); + if (VK_VERSION_MAJOR(rendererData->physicalDeviceProperties.apiVersion) < 1) { + continue; + } + vkGetPhysicalDeviceMemoryProperties(physicalDevice, &rendererData->physicalDeviceMemoryProperties); + vkGetPhysicalDeviceFeatures(physicalDevice, &rendererData->physicalDeviceFeatures); + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamiliesCount, NULL); + if (queueFamiliesCount == 0) { + continue; + } + if (queueFamiliesPropertiesAllocatedSize < queueFamiliesCount) { + SDL_free(queueFamiliesProperties); + queueFamiliesPropertiesAllocatedSize = queueFamiliesCount; + queueFamiliesProperties = (VkQueueFamilyProperties *)SDL_malloc(sizeof(VkQueueFamilyProperties) * queueFamiliesPropertiesAllocatedSize); + if (!queueFamiliesProperties) { + SDL_free(physicalDevices); + SDL_free(deviceExtensions); + return VK_ERROR_UNKNOWN; + } + } + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamiliesCount, queueFamiliesProperties); + rendererData->graphicsQueueFamilyIndex = queueFamiliesCount; + rendererData->presentQueueFamilyIndex = queueFamiliesCount; + for (queueFamilyIndex = 0; queueFamilyIndex < queueFamiliesCount; queueFamilyIndex++) { + VkBool32 supported = 0; + + if (queueFamiliesProperties[queueFamilyIndex].queueCount == 0) { + continue; + } + + if (queueFamiliesProperties[queueFamilyIndex].queueFlags & VK_QUEUE_GRAPHICS_BIT) { + rendererData->graphicsQueueFamilyIndex = queueFamilyIndex; + } + + result = vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, rendererData->surface, &supported); + if (result != VK_SUCCESS) { + SDL_free(physicalDevices); + SDL_free(queueFamiliesProperties); + SDL_free(deviceExtensions); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkGetPhysicalDeviceSurfaceSupportKHR(): %s\n", SDL_Vulkan_GetResultString(result)); + return VK_ERROR_UNKNOWN; + } + if (supported) { + rendererData->presentQueueFamilyIndex = queueFamilyIndex; + if (queueFamiliesProperties[queueFamilyIndex].queueFlags & VK_QUEUE_GRAPHICS_BIT) { + break; // use this queue because it can present and do graphics + } + } + } + + if (rendererData->graphicsQueueFamilyIndex == queueFamiliesCount) { // no good queues found + continue; + } + if (rendererData->presentQueueFamilyIndex == queueFamiliesCount) { // no good queues found + continue; + } + result = vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &deviceExtensionCount, NULL); + if (result != VK_SUCCESS) { + SDL_free(physicalDevices); + SDL_free(queueFamiliesProperties); + SDL_free(deviceExtensions); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkEnumerateDeviceExtensionProperties(): %s\n", SDL_Vulkan_GetResultString(result)); + return VK_ERROR_UNKNOWN; + } + if (deviceExtensionCount == 0) { + continue; + } + if (deviceExtensionsAllocatedSize < deviceExtensionCount) { + SDL_free(deviceExtensions); + deviceExtensionsAllocatedSize = deviceExtensionCount; + deviceExtensions = SDL_malloc(sizeof(VkExtensionProperties) * deviceExtensionsAllocatedSize); + if (!deviceExtensions) { + SDL_free(physicalDevices); + SDL_free(queueFamiliesProperties); + return VK_ERROR_UNKNOWN; + } + } + result = vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &deviceExtensionCount, deviceExtensions); + if (result != VK_SUCCESS) { + SDL_free(physicalDevices); + SDL_free(queueFamiliesProperties); + SDL_free(deviceExtensions); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkEnumerateDeviceExtensionProperties(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + for (i = 0; i < deviceExtensionCount; i++) { + if (SDL_strcmp(deviceExtensions[i].extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME) == 0) { + hasSwapchainExtension = SDL_TRUE; + break; + } + } + if (!hasSwapchainExtension) { + continue; + } + rendererData->physicalDevice = physicalDevice; + break; + } + SDL_free(physicalDevices); + SDL_free(queueFamiliesProperties); + SDL_free(deviceExtensions); + if (!rendererData->physicalDevice) { + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "Vulkan: no viable physical devices found"); + return VK_ERROR_UNKNOWN; + } + return VK_SUCCESS; +} + +static VkResult VULKAN_GetSurfaceFormats(VULKAN_RenderData *rendererData) +{ + VkResult result = vkGetPhysicalDeviceSurfaceFormatsKHR(rendererData->physicalDevice, + rendererData->surface, + &rendererData->surfaceFormatsCount, + NULL); + if (result != VK_SUCCESS) { + rendererData->surfaceFormatsCount = 0; + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkGetPhysicalDeviceSurfaceFormatsKHR(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + if (rendererData->surfaceFormatsCount > rendererData->surfaceFormatsAllocatedCount) { + rendererData->surfaceFormatsAllocatedCount = rendererData->surfaceFormatsCount; + SDL_free(rendererData->surfaceFormats); + rendererData->surfaceFormats = (VkSurfaceFormatKHR *)SDL_malloc(sizeof(VkSurfaceFormatKHR) * rendererData->surfaceFormatsAllocatedCount); + } + result = vkGetPhysicalDeviceSurfaceFormatsKHR(rendererData->physicalDevice, + rendererData->surface, + &rendererData->surfaceFormatsCount, + rendererData->surfaceFormats); + if (result != VK_SUCCESS) { + rendererData->surfaceFormatsCount = 0; + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkGetPhysicalDeviceSurfaceFormatsKHR(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + + return VK_SUCCESS; +} + +static VkSemaphore VULKAN_CreateSemaphore(VULKAN_RenderData *rendererData) +{ + VkResult result; + VkSemaphore semaphore = VK_NULL_HANDLE; + + VkSemaphoreCreateInfo semaphoreCreateInfo = { 0 }; + semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + result = vkCreateSemaphore(rendererData->device, &semaphoreCreateInfo, NULL, &semaphore); + if (result != VK_SUCCESS) { + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateSemaphore(): %s\n", SDL_Vulkan_GetResultString(result)); + return VK_NULL_HANDLE; + } + return semaphore; +} + +static SDL_bool VULKAN_InstanceExtensionFound(VULKAN_RenderData *rendererData, const char *extName) +{ + uint32_t extensionCount; + VkResult result = vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, NULL); + if (result != VK_SUCCESS ) { + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkEnumerateInstanceExtensionProperties( NULL, ... ): %s.\n", SDL_Vulkan_GetResultString(result)); + return SDL_FALSE; + } + if (extensionCount > 0 ) { + VkExtensionProperties *extensionProperties = SDL_calloc(sizeof(VkExtensionProperties), extensionCount); + result = vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, extensionProperties); + if (result != VK_SUCCESS ) { + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkEnumerateInstanceExtensionProperties( NULL, ... ): %s.\n", SDL_Vulkan_GetResultString(result)); + SDL_free(extensionProperties); + return SDL_FALSE; + } + for (uint32_t i = 0; i< extensionCount; i++) { + if (SDL_strcmp(extensionProperties[i].extensionName, extName) == 0) { + SDL_free(extensionProperties); + return SDL_TRUE; + } + } + SDL_free(extensionProperties); + } + + return SDL_FALSE; +} + +static SDL_bool VULKAN_ValidationLayersFound() +{ + const char *validationLayerName = "VK_LAYER_KHRONOS_validation"; + uint32_t instanceLayerCount = 0; + uint32_t i; + SDL_bool foundValidation = SDL_FALSE; + + vkEnumerateInstanceLayerProperties(&instanceLayerCount, NULL); + if (instanceLayerCount > 0) { + VkLayerProperties *instanceLayers = SDL_calloc(instanceLayerCount, sizeof(VkLayerProperties)); + vkEnumerateInstanceLayerProperties(&instanceLayerCount, instanceLayers); + for (i = 0; i < instanceLayerCount; i++) { + if (!SDL_strcmp(validationLayerName, instanceLayers[i].layerName)) { + foundValidation = SDL_TRUE; + break; + } + } + SDL_free(instanceLayers); + } + + return foundValidation; +} + +/* Create resources that depend on the device. */ +static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer) +{ + VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; + SDL_VideoDevice *device = SDL_GetVideoDevice(); + VkResult result = VK_SUCCESS; + PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL; + SDL_bool createDebug = SDL_GetHintBoolean(SDL_HINT_RENDER_VULKAN_DEBUG, SDL_FALSE); + + if (SDL_Vulkan_LoadLibrary(NULL) < 0) { + SDL_LogDebug(SDL_LOG_CATEGORY_RENDER, "SDL_Vulkan_LoadLibrary failed." ); + return VK_ERROR_UNKNOWN; + } + vkGetInstanceProcAddr = device ? (PFN_vkGetInstanceProcAddr)device->vulkan_config.vkGetInstanceProcAddr : NULL; + if(!vkGetInstanceProcAddr) { + SDL_LogDebug(SDL_LOG_CATEGORY_RENDER, "vkGetInstanceProcAddr is NULL" ); + return VK_ERROR_UNKNOWN; + } + + /* Load global Vulkan functions */ + rendererData->vkGetInstanceProcAddr = vkGetInstanceProcAddr; + if (VULKAN_LoadGlobalFunctions(rendererData) != 0) { + 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; + + 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) { + 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()) { + const char *validationLayerName[] = { "VK_LAYER_KHRONOS_validation" }; + 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); + /* Load instance Vulkan functions */ + if (VULKAN_LoadInstanceFunctions(rendererData) != 0) { + VULKAN_DestroyAll(renderer); + return VK_ERROR_UNKNOWN; + } + + /* 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; + } + + /* Choose Vulkan physical device */ + if (VULKAN_FindPhysicalDevice(rendererData) != VK_SUCCESS) { + VULKAN_DestroyAll(renderer); + return VK_ERROR_UNKNOWN; + } + + /* 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, + }; + + 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; + } + + if(VULKAN_LoadDeviceFunctions(rendererData) != 0) { + VULKAN_DestroyAll(renderer); + return VK_ERROR_UNKNOWN; + } + + /* Get graphics/present queues */ + vkGetDeviceQueue(rendererData->device, rendererData->graphicsQueueFamilyIndex, 0, &rendererData->graphicsQueue); + if (rendererData->graphicsQueueFamilyIndex != rendererData->presentQueueFamilyIndex) { + vkGetDeviceQueue(rendererData->device, rendererData->presentQueueFamilyIndex, 0, &rendererData->presentQueue); + } else { + rendererData->presentQueue = rendererData->graphicsQueue; + } + + /* Create command pool/command buffers */ + VkCommandPoolCreateInfo commandPoolCreateInfo = { 0 }; + commandPoolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + commandPoolCreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + commandPoolCreateInfo.queueFamilyIndex = rendererData->graphicsQueueFamilyIndex; + result = vkCreateCommandPool(rendererData->device, &commandPoolCreateInfo, NULL, &rendererData->commandPool); + if (result != VK_SUCCESS) { + VULKAN_DestroyAll(renderer); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateCommandPool(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + + if (VULKAN_GetSurfaceFormats(rendererData) != VK_SUCCESS) { + VULKAN_DestroyAll(renderer); + return result; + } + + /* Create shaders / layouts */ + for (uint32_t i = 0; i < NUM_SHADERS; i++) { + VkShaderModuleCreateInfo shaderModuleCreateInfo = { 0 }; + shaderModuleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + VULKAN_GetVertexShader(i, &shaderModuleCreateInfo.pCode, &shaderModuleCreateInfo.codeSize); + result = vkCreateShaderModule(rendererData->device, &shaderModuleCreateInfo, NULL, &rendererData->vertexShaderModules[i]); + if (result != VK_SUCCESS) { + VULKAN_DestroyAll(renderer); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateShaderModule(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + VULKAN_GetPixelShader(i, &shaderModuleCreateInfo.pCode, &shaderModuleCreateInfo.codeSize); + result = vkCreateShaderModule(rendererData->device, &shaderModuleCreateInfo, NULL, &rendererData->fragmentShaderModules[i]); + if (result != VK_SUCCESS) { + VULKAN_DestroyAll(renderer); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateShaderModule(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + + /* Descriptor set layout */ + VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = { 0 }; + descriptorSetLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + descriptorSetLayoutCreateInfo.flags = 0; + VkDescriptorSetLayoutBinding layoutBindings[5]; + /* PixelShaderConstants */ + layoutBindings[0].binding = 4; + layoutBindings[0].descriptorCount = 1; + layoutBindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + layoutBindings[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + layoutBindings[0].pImmutableSamplers = NULL; + + /* sampler0 */ + layoutBindings[1].binding = 0; + layoutBindings[1].descriptorCount = 1; + layoutBindings[1].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; + layoutBindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + layoutBindings[1].pImmutableSamplers = NULL; + + /* texture0 */ + layoutBindings[2].binding = 1; + layoutBindings[2].descriptorCount = 1; + layoutBindings[2].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + layoutBindings[2].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + layoutBindings[2].pImmutableSamplers = NULL; + + /* texture1 */ + layoutBindings[3].binding = 2; + layoutBindings[3].descriptorCount = 1; + layoutBindings[3].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + layoutBindings[3].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + layoutBindings[3].pImmutableSamplers = NULL; + + /* texture2 */ + layoutBindings[4].binding = 3; + layoutBindings[4].descriptorCount = 1; + layoutBindings[4].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + layoutBindings[4].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + layoutBindings[4].pImmutableSamplers = NULL; + + descriptorSetLayoutCreateInfo.bindingCount = 5; + descriptorSetLayoutCreateInfo.pBindings = layoutBindings; + result = vkCreateDescriptorSetLayout(rendererData->device, &descriptorSetLayoutCreateInfo, NULL, &rendererData->descriptorSetLayouts[i]); + if (result != VK_SUCCESS) { + VULKAN_DestroyAll(renderer); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateDescriptorSetLayout(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + + /* Pipeline layout */ + VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = { 0 }; + VkPushConstantRange pushConstantRange; + pushConstantRange.size = sizeof( VertexShaderConstants ); + pushConstantRange.offset = 0; + pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutCreateInfo.setLayoutCount = 1; + pipelineLayoutCreateInfo.pSetLayouts = &rendererData->descriptorSetLayouts[i]; + pipelineLayoutCreateInfo.pushConstantRangeCount = 1; + pipelineLayoutCreateInfo.pPushConstantRanges = &pushConstantRange; + result = vkCreatePipelineLayout(rendererData->device, &pipelineLayoutCreateInfo, NULL, &rendererData->pipelineLayouts[i]); + if (result != VK_SUCCESS) { + VULKAN_DestroyAll(renderer); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreatePipelineLayout(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + } + + /* Create default vertex buffers */ + for (uint32_t i = 0; i < SDL_VULKAN_NUM_VERTEX_BUFFERS; ++i) { + VULKAN_CreateVertexBuffer(rendererData, i, SDL_VULKAN_VERTEX_BUFFER_DEFAULT_SIZE); + } + + /* Create samplers */ + { + VkSamplerCreateInfo samplerCreateInfo = { 0 }; + samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + samplerCreateInfo.magFilter = VK_FILTER_NEAREST; + samplerCreateInfo.minFilter = VK_FILTER_NEAREST; + samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; + samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerCreateInfo.mipLodBias = 0.0f; + samplerCreateInfo.anisotropyEnable = VK_FALSE; + samplerCreateInfo.maxAnisotropy = 1.0f; + samplerCreateInfo.minLod = 0.0f; + samplerCreateInfo.maxLod = 1000.0f; + result = vkCreateSampler(rendererData->device, &samplerCreateInfo, NULL, &rendererData->samplers[SDL_VULKAN_SAMPLER_NEAREST]); + if (result != VK_SUCCESS) { + VULKAN_DestroyAll(renderer); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateSampler(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + + samplerCreateInfo.magFilter = VK_FILTER_LINEAR; + samplerCreateInfo.minFilter = VK_FILTER_LINEAR; + samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + result = vkCreateSampler(rendererData->device, &samplerCreateInfo, NULL, &rendererData->samplers[SDL_VULKAN_SAMPLER_LINEAR]); + if (result != VK_SUCCESS) { + VULKAN_DestroyAll(renderer); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateSampler(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + } + + return VK_SUCCESS; +} + +static VkResult VULKAN_CreateFramebuffersAndRenderPasses(SDL_Renderer *renderer, int w, int h, + VkFormat format, int imageViewCount, VkImageView *imageViews, VkFramebuffer *framebuffers, VkRenderPass renderPasses[SDL_VULKAN_NUM_RENDERPASSES]) +{ + VULKAN_RenderData *rendererData = (VULKAN_RenderData *) renderer->driverdata; + VkResult result; + + VkAttachmentDescription attachmentDescription = { 0 }; + attachmentDescription.format = format; + attachmentDescription.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + attachmentDescription.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachmentDescription.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachmentDescription.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachmentDescription.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachmentDescription.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachmentDescription.samples = 1; + attachmentDescription.flags = 0; + + VkAttachmentReference colorAttachmentReference = { 0 }; + colorAttachmentReference.attachment = 0; + colorAttachmentReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpassDescription = { 0 }; + subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpassDescription.flags = 0; + subpassDescription.inputAttachmentCount = 0; + subpassDescription.pInputAttachments = NULL; + subpassDescription.colorAttachmentCount = 1; + subpassDescription.pColorAttachments = &colorAttachmentReference; + subpassDescription.pResolveAttachments = NULL; + subpassDescription.pDepthStencilAttachment = NULL; + subpassDescription.preserveAttachmentCount = 0; + subpassDescription.pPreserveAttachments = NULL; + + VkSubpassDependency subPassDependency = { 0 }; + subPassDependency.srcSubpass = VK_SUBPASS_EXTERNAL; + subPassDependency.dstSubpass = 0; + subPassDependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + subPassDependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + subPassDependency.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + subPassDependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; + subPassDependency.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + VkRenderPassCreateInfo renderPassCreateInfo = { 0 }; + renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassCreateInfo.flags = 0; + renderPassCreateInfo.attachmentCount = 1; + renderPassCreateInfo.pAttachments = &attachmentDescription; + renderPassCreateInfo.subpassCount = 1; + renderPassCreateInfo.pSubpasses = &subpassDescription; + renderPassCreateInfo.dependencyCount = 1; + renderPassCreateInfo.pDependencies = &subPassDependency; + + result = vkCreateRenderPass(rendererData->device, &renderPassCreateInfo, NULL, &renderPasses[SDL_VULKAN_RENDERPASS_LOAD]); + if (result != VK_SUCCESS) { + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateRenderPass(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + + attachmentDescription.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + result = vkCreateRenderPass(rendererData->device, &renderPassCreateInfo, NULL, &renderPasses[SDL_VULKAN_RENDERPASS_CLEAR]); + if (result != VK_SUCCESS) { + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateRenderPass(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + + VkFramebufferCreateInfo framebufferCreateInfo = { 0 }; + framebufferCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebufferCreateInfo.pNext = NULL; + framebufferCreateInfo.renderPass = rendererData->renderPasses[SDL_VULKAN_RENDERPASS_LOAD]; + framebufferCreateInfo.attachmentCount = 1; + framebufferCreateInfo.width = rendererData->swapchainSize.width; + framebufferCreateInfo.height = rendererData->swapchainSize.height; + framebufferCreateInfo.layers = 1; + + for (uint32_t i = 0; i < imageViewCount; i++) { + framebufferCreateInfo.pAttachments = &imageViews[i]; + result = vkCreateFramebuffer(rendererData->device, &framebufferCreateInfo, NULL, &framebuffers[i]); + if (result != VK_SUCCESS) { + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateFramebuffer(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + } + + return result; +} + +static VkResult VULKAN_CreateSwapChain(SDL_Renderer *renderer, int w, int h) +{ + VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; + VkResult result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(rendererData->physicalDevice, rendererData->surface, &rendererData->surfaceCapabilities); + if (result != VK_SUCCESS) { + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + + // pick an image count + rendererData->swapchainDesiredImageCount = rendererData->surfaceCapabilities.minImageCount + SDL_VULKAN_FRAME_QUEUE_DEPTH; + if ((rendererData->swapchainDesiredImageCount > rendererData->surfaceCapabilities.maxImageCount) && + (rendererData->surfaceCapabilities.maxImageCount > 0)) { + rendererData->swapchainDesiredImageCount = rendererData->surfaceCapabilities.maxImageCount; + } + + VkFormat desiredFormat = VK_FORMAT_B8G8R8A8_UNORM; + VkColorSpaceKHR desiredColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; + if (renderer->output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) { + desiredFormat = VK_FORMAT_R16G16B16A16_SFLOAT; + desiredColorSpace = VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT; + } + else if (renderer->output_colorspace == SDL_COLORSPACE_HDR10) { + desiredFormat = VK_FORMAT_A2B10G10R10_UNORM_PACK32; + desiredColorSpace = VK_COLOR_SPACE_HDR10_ST2084_EXT; + } + + if ((rendererData->surfaceFormatsCount == 1) && + (rendererData->surfaceFormats[0].format == VK_FORMAT_UNDEFINED)) { + // aren't any preferred formats, so we pick + rendererData->surfaceFormat.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; + rendererData->surfaceFormat.format = desiredFormat; + } else { + rendererData->surfaceFormat = rendererData->surfaceFormats[0]; + rendererData->surfaceFormat.colorSpace = rendererData->surfaceFormats[0].colorSpace; + for (uint32_t i = 0; i < rendererData->surfaceFormatsCount; i++) { + if (rendererData->surfaceFormats[i].format == desiredFormat && + rendererData->surfaceFormats[i].colorSpace == desiredColorSpace) { + rendererData->surfaceFormat.colorSpace = rendererData->surfaceFormats[i].colorSpace; + rendererData->surfaceFormat = rendererData->surfaceFormats[i]; + break; + } + } + } + + rendererData->swapchainSize.width = SDL_clamp((uint32_t)w, + rendererData->surfaceCapabilities.minImageExtent.width, + rendererData->surfaceCapabilities.maxImageExtent.width); + + rendererData->swapchainSize.height = SDL_clamp((uint32_t)h, + rendererData->surfaceCapabilities.minImageExtent.height, + rendererData->surfaceCapabilities.maxImageExtent.height); + + if (rendererData->swapchainSize.width == 0 && rendererData->swapchainSize.height == 0) { + /* Don't recreate the swapchain if size is (0,0), just fail and continue attempting creation */ + return VK_ERROR_OUT_OF_DATE_KHR; + } + + + VkSwapchainCreateInfoKHR swapchainCreateInfo = { 0 }; + swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + swapchainCreateInfo.surface = rendererData->surface; + swapchainCreateInfo.minImageCount = rendererData->swapchainDesiredImageCount; + swapchainCreateInfo.imageFormat = rendererData->surfaceFormat.format; + swapchainCreateInfo.imageColorSpace = rendererData->surfaceFormat.colorSpace; + swapchainCreateInfo.imageExtent = rendererData->swapchainSize; + swapchainCreateInfo.imageArrayLayers = 1; + swapchainCreateInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + swapchainCreateInfo.preTransform = rendererData->surfaceCapabilities.currentTransform; + swapchainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + swapchainCreateInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR; // TODO + swapchainCreateInfo.clipped = VK_TRUE; + swapchainCreateInfo.oldSwapchain = rendererData->swapchain; + result = vkCreateSwapchainKHR(rendererData->device, &swapchainCreateInfo, NULL, &rendererData->swapchain); + + if (swapchainCreateInfo.oldSwapchain != VK_NULL_HANDLE) { + vkDestroySwapchainKHR(rendererData->device, swapchainCreateInfo.oldSwapchain, NULL); + } + + if (result != VK_SUCCESS) { + rendererData->swapchain = VK_NULL_HANDLE; + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateSwapchainKHR(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + + SDL_free(rendererData->swapchainImages); + rendererData->swapchainImages = NULL; + result = vkGetSwapchainImagesKHR(rendererData->device, rendererData->swapchain, &rendererData->swapchainImageCount, NULL); + if (result != VK_SUCCESS) { + rendererData->swapchainImageCount = 0; + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkGetSwapchainImagesKHR(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + + rendererData->swapchainImages = SDL_malloc(sizeof(VkImage) * rendererData->swapchainImageCount); + result = vkGetSwapchainImagesKHR(rendererData->device, + rendererData->swapchain, + &rendererData->swapchainImageCount, + rendererData->swapchainImages); + if (result != VK_SUCCESS) { + SDL_free(rendererData->swapchainImages); + rendererData->swapchainImages = NULL; + rendererData->swapchainImageCount = 0; + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkGetSwapchainImagesKHR(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + + /* Create VkImageView's for swapchain images */ + { + VkImageViewCreateInfo imageViewCreateInfo = { 0 }; + imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + imageViewCreateInfo.flags = 0; + imageViewCreateInfo.format = rendererData->surfaceFormat.format; + imageViewCreateInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + imageViewCreateInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + imageViewCreateInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + imageViewCreateInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + imageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageViewCreateInfo.subresourceRange.baseArrayLayer = 0; + imageViewCreateInfo.subresourceRange.baseMipLevel = 0; + imageViewCreateInfo.subresourceRange.layerCount = 1; + imageViewCreateInfo.subresourceRange.levelCount = 1; + imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + if (rendererData->swapchainImageViews) { + for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) { + vkDestroyImageView(rendererData->device, rendererData->swapchainImageViews[i], NULL); + } + SDL_free(rendererData->swapchainImageViews); + } + rendererData->swapchainImageViews = SDL_calloc(sizeof(VkImageView), rendererData->swapchainImageCount); + SDL_free(rendererData->swapchainImageLayouts); + rendererData->swapchainImageLayouts = SDL_calloc(sizeof(VkImageLayout), rendererData->swapchainImageCount); + for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) { + imageViewCreateInfo.image = rendererData->swapchainImages[i]; + result = vkCreateImageView(rendererData->device, &imageViewCreateInfo, NULL, &rendererData->swapchainImageViews[i]); + if (result != VK_SUCCESS) { + VULKAN_DestroyAll(renderer); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateImageView(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + rendererData->swapchainImageLayouts[i] = VK_IMAGE_LAYOUT_UNDEFINED; + } + + } + + VkCommandBufferAllocateInfo commandBufferAllocateInfo = { 0 }; + commandBufferAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + commandBufferAllocateInfo.commandPool = rendererData->commandPool; + commandBufferAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + commandBufferAllocateInfo.commandBufferCount = rendererData->swapchainImageCount; + if (rendererData->commandBuffers != NULL) { + vkResetCommandPool(rendererData->device, rendererData->commandPool, 0); + SDL_free(rendererData->commandBuffers); + rendererData->currentCommandBuffer = VK_NULL_HANDLE; + rendererData->currentCommandBufferIndex = 0; + } + rendererData->commandBuffers = SDL_calloc(sizeof(VkCommandBuffer), rendererData->swapchainImageCount); + result = vkAllocateCommandBuffers(rendererData->device, &commandBufferAllocateInfo, rendererData->commandBuffers); + if (result != VK_SUCCESS) { + VULKAN_DestroyAll(renderer); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkAllocateCommandBuffers(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + + /* Create fences */ + if (rendererData->fences) { + for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) { + if (rendererData->fences[i] != VK_NULL_HANDLE) { + vkDestroyFence(rendererData->device, rendererData->fences[i], NULL); + } + } + SDL_free(rendererData->fences); + } + rendererData->fences = SDL_calloc(sizeof(VkFence), rendererData->swapchainImageCount); + for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) { + VkFenceCreateInfo fenceCreateInfo = { 0 }; + fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fenceCreateInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; + result = vkCreateFence(rendererData->device, &fenceCreateInfo, NULL, &rendererData->fences[i]); + if (result != VK_SUCCESS) { + VULKAN_DestroyAll(renderer); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateFence(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + } + + /* Create renderpasses and framebuffer */ + if (rendererData->framebuffers) { + for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) { + if (rendererData->framebuffers[i] != VK_NULL_HANDLE) { + vkDestroyFramebuffer(rendererData->device, rendererData->framebuffers[i], NULL); + } + } + SDL_free(rendererData->framebuffers); + } + for (uint32_t i = 0; i < SDL_arraysize(rendererData->renderPasses); i++) { + if (rendererData->renderPasses[i] != VK_NULL_HANDLE) { + vkDestroyRenderPass(rendererData->device, rendererData->renderPasses[i], NULL); + rendererData->renderPasses[i] = VK_NULL_HANDLE; + } + } + rendererData->framebuffers = SDL_calloc(sizeof(VkFramebuffer), rendererData->swapchainImageCount); + result = VULKAN_CreateFramebuffersAndRenderPasses(renderer, + rendererData->swapchainSize.width, + rendererData->swapchainSize.height, + rendererData->surfaceFormat.format, + rendererData->swapchainImageCount, + rendererData->swapchainImageViews, + rendererData->framebuffers, + rendererData->renderPasses); + if (result != VK_SUCCESS) { + VULKAN_DestroyAll(renderer); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "VULKAN_CreateFramebuffersAndRenderPasses(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + + /* Create descriptor pools */ + if (rendererData->descriptorPools) { + for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) { + if (rendererData->descriptorPools[i] != VK_NULL_HANDLE) { + vkDestroyDescriptorPool(rendererData->device, rendererData->descriptorPools[i], NULL); + } + } + SDL_free(rendererData->descriptorPools); + } + rendererData->descriptorPools = SDL_calloc(sizeof(VkDescriptorPool), rendererData->swapchainImageCount); + for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) { + VkDescriptorPoolSize descriptorPoolSizes[2]; + descriptorPoolSizes[0].descriptorCount = SDL_VULKAN_MAX_DESCRIPTOR_SETS; + descriptorPoolSizes[0].type = VK_DESCRIPTOR_TYPE_SAMPLER; + + /* Allocate enough to hold a maximum of each descriptor set having YUV textures */ + const int numTexturesPerYUV = 3; + descriptorPoolSizes[1].descriptorCount = SDL_VULKAN_MAX_DESCRIPTOR_SETS * numTexturesPerYUV; + descriptorPoolSizes[1].type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + + VkDescriptorPoolCreateInfo descriptorPoolCreateInfo = { 0 }; + descriptorPoolCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + descriptorPoolCreateInfo.poolSizeCount = SDL_arraysize(descriptorPoolSizes); + descriptorPoolCreateInfo.pPoolSizes = descriptorPoolSizes; + descriptorPoolCreateInfo.maxSets = SDL_VULKAN_MAX_DESCRIPTOR_SETS; + result = vkCreateDescriptorPool(rendererData->device, &descriptorPoolCreateInfo, NULL, &rendererData->descriptorPools[i]); + if (result != VK_SUCCESS) { + VULKAN_DestroyAll(renderer); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateDescriptorPool(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + } + + /* Create semaphores */ + if (rendererData->imageAvailableSemaphore != VK_NULL_HANDLE) { + vkDestroySemaphore(rendererData->device, rendererData->imageAvailableSemaphore, NULL); + } + rendererData->imageAvailableSemaphore = VULKAN_CreateSemaphore(rendererData); + if (rendererData->imageAvailableSemaphore == VK_NULL_HANDLE) { + VULKAN_DestroyAll(renderer); + return VK_ERROR_UNKNOWN; + } + if (rendererData->renderingFinishedSemaphore != VK_NULL_HANDLE) { + vkDestroySemaphore(rendererData->device, rendererData->renderingFinishedSemaphore, NULL); + } + rendererData->renderingFinishedSemaphore = VULKAN_CreateSemaphore(rendererData); + if (rendererData->renderingFinishedSemaphore == VK_NULL_HANDLE) { + VULKAN_DestroyAll(renderer); + return VK_ERROR_UNKNOWN; + } + + /* Upload buffers */ + if (rendererData->uploadBuffers) { + for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) { + for (uint32_t j = 0; j < SDL_VULKAN_NUM_UPLOAD_BUFFERS; j++) { + VULKAN_DestroyBuffer(rendererData, &rendererData->uploadBuffers[i][j]); + } + SDL_free(rendererData->uploadBuffers[i]); + } + SDL_free(rendererData->uploadBuffers); + } + rendererData->uploadBuffers = SDL_calloc(sizeof(VULKAN_Buffer*), rendererData->swapchainImageCount); + for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) { + rendererData->uploadBuffers[i] = SDL_calloc(sizeof(VULKAN_Buffer), SDL_VULKAN_NUM_UPLOAD_BUFFERS); + } + SDL_free(rendererData->currentUploadBuffer); + rendererData->currentUploadBuffer = SDL_calloc(sizeof(int), rendererData->swapchainImageCount); + + /* Constant buffers */ + if (rendererData->constantBuffers) { + for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) { + VULKAN_DestroyBuffer(rendererData, &rendererData->constantBuffers[i]); + } + SDL_free(rendererData->constantBuffers); + } + rendererData->constantBuffers = SDL_calloc(sizeof(VULKAN_Buffer), rendererData->swapchainImageCount); + for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) { + result = VULKAN_AllocateBuffer(rendererData, + SDL_VULKAN_CONSTANT_BUFFER_DEFAULT_SIZE, + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &rendererData->constantBuffers[i]); + if (result != VK_SUCCESS) { + VULKAN_DestroyAll(renderer); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "VULKAN_AllocateBuffer(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + } + rendererData->currentConstantBufferOffset = -1; + + VULKAN_AcquireNextSwapchainImage(renderer); + + return result; +} + +/* Initialize all resources that change when the window's size changes. */ +static VkResult VULKAN_CreateWindowSizeDependentResources(SDL_Renderer *renderer) +{ + VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; + VkResult result = VK_SUCCESS; + int w, h; + + /* Release resources in the current command list */ + VULKAN_IssueBatch(rendererData); + VULKAN_WaitForGPU(rendererData); + + /* The width and height of the swap chain must be based on the display's + * non-rotated size. + */ + SDL_GetWindowSizeInPixels(renderer->window, &w, &h); + + result = VULKAN_CreateSwapChain(renderer, w, h); + if (result != VK_SUCCESS) { + rendererData->pixelSizeChanged = VK_TRUE; + } + + rendererData->viewportDirty = SDL_TRUE; + + return result; +} + +/* This method is called when the window's size changes. */ +static VkResult VULKAN_UpdateForWindowSizeChange(SDL_Renderer *renderer) +{ + VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; + /* If the GPU has previous work, wait for it to be done first */ + VULKAN_WaitForGPU(rendererData); + + return VULKAN_CreateWindowSizeDependentResources(renderer); +} + +static void VULKAN_WindowEvent(SDL_Renderer *renderer, const SDL_WindowEvent *event) +{ + VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; + + if (event->type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) { + rendererData->pixelSizeChanged = SDL_TRUE; + } +} + +static SDL_bool VULKAN_SupportsBlendMode(SDL_Renderer *renderer, SDL_BlendMode blendMode) +{ + SDL_BlendFactor srcColorFactor = SDL_GetBlendModeSrcColorFactor(blendMode); + SDL_BlendFactor srcAlphaFactor = SDL_GetBlendModeSrcAlphaFactor(blendMode); + SDL_BlendOperation colorOperation = SDL_GetBlendModeColorOperation(blendMode); + SDL_BlendFactor dstColorFactor = SDL_GetBlendModeDstColorFactor(blendMode); + SDL_BlendFactor dstAlphaFactor = SDL_GetBlendModeDstAlphaFactor(blendMode); + SDL_BlendOperation alphaOperation = SDL_GetBlendModeAlphaOperation(blendMode); + + if (GetBlendFactor(srcColorFactor) == VK_BLEND_FACTOR_MAX_ENUM || + GetBlendFactor(srcAlphaFactor) == VK_BLEND_FACTOR_MAX_ENUM || + GetBlendOp(colorOperation) == VK_BLEND_OP_MAX_ENUM || + GetBlendFactor(dstColorFactor) == VK_BLEND_FACTOR_MAX_ENUM || + GetBlendFactor(dstAlphaFactor) == VK_BLEND_FACTOR_MAX_ENUM || + GetBlendOp(alphaOperation) == VK_BLEND_OP_MAX_ENUM) { + return SDL_FALSE; + } + return SDL_TRUE; +} + +static int GetTextureProperty(SDL_PropertiesID props, const char *name, VkImage *image) +{ + VkImage *propImage = (VkImage*)SDL_GetProperty(props, name, NULL); + if (propImage) { + *image = *propImage; + } + return 0; +} + + +static int VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props) +{ + VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; + VULKAN_TextureData *textureData; + VkResult result; + VkImage externalImage = VK_NULL_HANDLE; + VkFormat textureFormat = SDLPixelFormatToVkTextureFormat(texture->format, renderer->output_colorspace); + uint32_t width = texture->w; + uint32_t height = texture->h; + + if (textureFormat == VK_FORMAT_UNDEFINED) { + return SDL_SetError("%s, An unsupported SDL pixel format (0x%x) was specified", __FUNCTION__, texture->format); + } + + textureData = (VULKAN_TextureData *)SDL_calloc(1, sizeof(*textureData)); + if (!textureData) { + return -1; + } + texture->driverdata = textureData; + if (SDL_COLORSPACETRANSFER(texture->colorspace) == SDL_TRANSFER_CHARACTERISTICS_SRGB) { + textureData->shader = SHADER_RGB; + } else { + textureData->shader = SHADER_ADVANCED; + } + textureData->scaleMode = (texture->scaleMode == SDL_SCALEMODE_NEAREST) ? VK_FILTER_NEAREST : VK_FILTER_LINEAR; + + /* NV12 textures must have even width and height */ + if (texture->format == SDL_PIXELFORMAT_NV12 || + texture->format == SDL_PIXELFORMAT_NV21 || + texture->format == SDL_PIXELFORMAT_P010) { + width = (width + 1) & ~1; + height = (height + 1) & ~1; + } + textureData->width = width; + textureData->height = height; + + VkImageUsageFlags usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + if (texture->access == SDL_TEXTUREACCESS_TARGET) { + usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + } + + if (GetTextureProperty(create_props, "vulkan.texture", &externalImage) < 0) { + return -1; + } + + result = VULKAN_AllocateImage(rendererData, width, height, textureFormat, usage, rendererData->identitySwizzle, externalImage, &textureData->mainImage); + if (result != VK_SUCCESS) { + VULKAN_DestroyTexture(renderer, texture); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "VULKAN_AllocateImage(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + + SDL_SetProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_VULKAN_TEXTURE_POINTER, &textureData->mainImage.image); + + +#if SDL_HAVE_YUV + /* YUV Images */ + if (texture->format == SDL_PIXELFORMAT_YV12 || + texture->format == SDL_PIXELFORMAT_IYUV) { + textureData->yuv = SDL_TRUE; + + width = (width + 1) / 2; + height = (height + 1) / 2; + + /* Create U Image */ + if (GetTextureProperty(create_props, "vulkan.texture_u", &externalImage) < 0) { + return -1; + } + + result = VULKAN_AllocateImage(rendererData, width, height, textureFormat, usage, rendererData->identitySwizzle, externalImage, &textureData->mainImageU); + if (result != VK_SUCCESS) { + VULKAN_DestroyTexture(renderer, texture); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "VULKAN_AllocateImage(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + SDL_SetProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_VULKAN_TEXTURE_U_POINTER, &textureData->mainImageU.image); + + /* Create V image */ + if (GetTextureProperty(create_props, "vulkan.texture_v", &externalImage) < 0) { + return -1; + } + result = VULKAN_AllocateImage(rendererData, width, height, textureFormat, usage, rendererData->identitySwizzle, externalImage, &textureData->mainImageV); + if (result != VK_SUCCESS) { + VULKAN_DestroyTexture(renderer, texture); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "VULKAN_AllocateImage(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + SDL_SetProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_VULKAN_TEXTURE_V_POINTER, &textureData->mainImageV.image); + + textureData->YCbCr_matrix = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, 8); + if (!textureData->YCbCr_matrix) { + return SDL_SetError("Unsupported YUV colorspace"); + } + } + else if (texture->format == SDL_PIXELFORMAT_NV12 || + texture->format == SDL_PIXELFORMAT_NV21 || + texture->format == SDL_PIXELFORMAT_P010) { + int bits_per_pixel; + VkFormat uvFormat = VK_FORMAT_R8G8_UNORM; + if (texture->format == SDL_PIXELFORMAT_P010 || texture->format == SDL_PIXELFORMAT_P016) { + uvFormat = VK_FORMAT_R16G16_UNORM; + } + textureData->nv12 = SDL_TRUE; + + width = (width + 1) / 2; + height = (height + 1) / 2; + + /* Allocate interleaved UV plane as R8G8 */ + result = VULKAN_AllocateImage(rendererData, width, height, uvFormat, usage, rendererData->identitySwizzle, VK_NULL_HANDLE, &textureData->mainImageUV); + if (result != VK_SUCCESS) { + VULKAN_DestroyTexture(renderer, texture); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "VULKAN_AllocateImage(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + + switch (texture->format) { + case SDL_PIXELFORMAT_P010: + bits_per_pixel = 10; + break; + default: + bits_per_pixel = 8; + break; + } + + SDL_SetProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_VULKAN_TEXTURE_UV_POINTER, &textureData->mainImageUV.image); + + textureData->YCbCr_matrix = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, bits_per_pixel); + if (!textureData->YCbCr_matrix) { + return SDL_SetError("Unsupported YUV colorspace"); + } + } +#endif + + if (texture->access == SDL_TEXTUREACCESS_TARGET) { + result = VULKAN_CreateFramebuffersAndRenderPasses(renderer, + texture->w, + texture->h, + textureFormat, + 1, + &textureData->mainImage.imageView, + &textureData->mainFramebuffer, + textureData->mainRenderpasses); + if (result != VK_SUCCESS) { + VULKAN_DestroyTexture(renderer, texture); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "VULKAN_CreateFramebuffersAndRenderPasses(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + } + return result; +} + +static void VULKAN_DestroyTexture(SDL_Renderer *renderer, + SDL_Texture *texture) +{ + VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; + VULKAN_TextureData *textureData = (VULKAN_TextureData *)texture->driverdata; + + if (!textureData) { + return; + } + + /* Because SDL_DestroyTexture might be called while the data is in-flight, we need to issue the batch first + Unfortunately, this means that deleting a lot of textures mid-frame will have poor performance. */ + VULKAN_IssueBatch(rendererData); + VULKAN_WaitForGPU(rendererData); + + VULKAN_DestroyImage(rendererData, &textureData->mainImage); + +#if SDL_HAVE_YUV + VULKAN_DestroyImage(rendererData, &textureData->mainImageU); + VULKAN_DestroyImage(rendererData, &textureData->mainImageV); + VULKAN_DestroyImage(rendererData, &textureData->mainImageUV); +#endif + + VULKAN_DestroyBuffer(rendererData, &textureData->stagingBuffer); + if (textureData->mainFramebuffer != VK_NULL_HANDLE) { + vkDestroyFramebuffer(rendererData->device, textureData->mainFramebuffer, NULL); + textureData->mainFramebuffer = VK_NULL_HANDLE; + } + for (uint32_t i = 0; i < SDL_arraysize(textureData->mainRenderpasses); i++) { + if (textureData->mainRenderpasses[i] != VK_NULL_HANDLE) { + vkDestroyRenderPass(rendererData->device, textureData->mainRenderpasses[i], NULL); + textureData->mainRenderpasses[i] = VK_NULL_HANDLE; + } + } + + SDL_free(textureData); + texture->driverdata = NULL; +} + +static VkResult VULKAN_UpdateTextureInternal(VULKAN_RenderData *rendererData, VkImage image, VkFormat format, int plane, int x, int y, int w, int h, const void *pixels, int pitch, VkImageLayout *imageLayout) +{ + VkDeviceSize pixelSize = VULKAN_GetBytesPerPixel(format); + VkDeviceSize length = w * pixelSize; + VkDeviceSize uploadBufferSize = length * h; + const Uint8 *src; + Uint8 *dst; + VkResult result; + + VULKAN_EnsureCommandBuffer(rendererData); + + int currentUploadBufferIndex = rendererData->currentUploadBuffer[rendererData->currentCommandBufferIndex]; + VULKAN_Buffer *uploadBuffer = &rendererData->uploadBuffers[rendererData->currentCommandBufferIndex][currentUploadBufferIndex]; + + result = VULKAN_AllocateBuffer(rendererData, uploadBufferSize, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + uploadBuffer); + if (result != VK_SUCCESS) { + return result; + } + + src = (const Uint8 *)pixels; + dst = uploadBuffer->mappedBufferPtr; + if (length == (VkDeviceSize)pitch) { + SDL_memcpy(dst, src, (size_t)length * h); + } else { + if (length > (VkDeviceSize)pitch) { + length = pitch; + } + for (VkDeviceSize row = h; row--; ) { + SDL_memcpy(dst, src, length); + src += pitch; + dst += pitch; + } + } + + /* Make sure the destination is in the correct resource state */ + VULKAN_RecordPipelineImageBarrier(rendererData, + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + image, + imageLayout); + + VkBufferImageCopy region; + region.bufferOffset = 0; + region.bufferRowLength = 0; + region.bufferImageHeight = 0; + region.imageSubresource.baseArrayLayer = 0; + region.imageSubresource.layerCount = 1; + region.imageSubresource.mipLevel = 0; + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageOffset.x = x; + region.imageOffset.y = y; + region.imageOffset.z = 0; + region.imageExtent.width = w; + region.imageExtent.height = h; + region.imageExtent.depth = 1; + + vkCmdCopyBufferToImage(rendererData->currentCommandBuffer, uploadBuffer->buffer, image, *imageLayout, 1, ®ion); + + /* Transition the texture to be shader accessible */ + VULKAN_RecordPipelineImageBarrier(rendererData, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_ACCESS_SHADER_READ_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + image, + imageLayout); + + rendererData->currentUploadBuffer[rendererData->currentCommandBufferIndex]++; + + /* If we've used up all the upload buffers, we need to issue the batch */ + if (rendererData->currentUploadBuffer[rendererData->currentCommandBufferIndex] == SDL_VULKAN_NUM_UPLOAD_BUFFERS) { + VULKAN_IssueBatch(rendererData); + } + + return 0; +} + + +static int VULKAN_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, + const SDL_Rect *rect, const void *srcPixels, + int srcPitch) +{ + VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; + VULKAN_TextureData *textureData = (VULKAN_TextureData *)texture->driverdata; + + if (!textureData) { + return SDL_SetError("Texture is not currently available"); + } + + if (VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, 0, rect->x, rect->y, rect->w, rect->h, srcPixels, srcPitch, &textureData->mainImage.imageLayout) < 0) { + return -1; + } +#if SDL_HAVE_YUV + if (textureData->yuv) { + /* Skip to the correct offset into the next texture */ + srcPixels = (const void *)((const Uint8 *)srcPixels + rect->h * srcPitch); + + if (VULKAN_UpdateTextureInternal(rendererData, + texture->format == SDL_PIXELFORMAT_YV12 ? textureData->mainImageV.image : textureData->mainImageU.image, + textureData->mainImageU.format, + 0, + rect->x / 2, + rect->y / 2, + (rect->w + 1) / 2, + (rect->h + 1) / 2, + srcPixels, + (srcPitch + 1) / 2, + texture->format == SDL_PIXELFORMAT_YV12 ? &textureData->mainImageV.imageLayout : &textureData->mainImageU.imageLayout) < 0) { + return -1; + } + + /* Skip to the correct offset into the next texture */ + srcPixels = (const void *)((const Uint8 *)srcPixels + ((rect->h + 1) / 2) * ((srcPitch + 1) / 2)); + if (VULKAN_UpdateTextureInternal(rendererData, + texture->format == SDL_PIXELFORMAT_YV12 ? textureData->mainImageU.image : textureData->mainImageV.image, + textureData->mainImageV.format, + 0, + rect->x / 2, + rect->y / 2, + (rect->w + 1) / 2, + (rect->h + 1) / 2, + srcPixels, + (srcPitch + 1) / 2, + texture->format == SDL_PIXELFORMAT_YV12 ? &textureData->mainImageU.imageLayout : &textureData->mainImageV.imageLayout) < 0) { + return -1; + } + } + if (textureData->nv12) { + /* Skip to the correct offset into the next texture */ + srcPixels = (const void *)((const Uint8 *)srcPixels + rect->h * srcPitch); + + if (VULKAN_UpdateTextureInternal(rendererData, + textureData->mainImageUV.image, + textureData->mainImageUV.format, + 1, + rect->x / 2, + rect->y / 2, + (rect->w + 1) / 2, + (rect->h + 1) / 2, + srcPixels, + srcPitch, + &textureData->mainImageUV.imageLayout) < 0) { + return -1; + } + } +#endif /* SDL_HAVE_YUV */ + return 0; +} + +#if SDL_HAVE_YUV +static int VULKAN_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture, + const SDL_Rect *rect, + const Uint8 *Yplane, int Ypitch, + const Uint8 *Uplane, int Upitch, + const Uint8 *Vplane, int Vpitch) +{ + VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; + VULKAN_TextureData *textureData = (VULKAN_TextureData *)texture->driverdata; + + if (!textureData) { + return SDL_SetError("Texture is not currently available"); + } + + if (VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, 0, rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch, &textureData->mainImage.imageLayout) < 0) { + return -1; + } + if (VULKAN_UpdateTextureInternal(rendererData, textureData->mainImageU.image, textureData->mainImageU.format, 0, rect->x / 2, rect->y / 2, rect->w / 2, rect->h / 2, Uplane, Upitch, &textureData->mainImageU.imageLayout) < 0) { + return -1; + } + if (VULKAN_UpdateTextureInternal(rendererData, textureData->mainImageV.image, textureData->mainImageV.format, 0, rect->x / 2, rect->y / 2, rect->w / 2, rect->h / 2, Vplane, Vpitch, &textureData->mainImageV.imageLayout) < 0) { + return -1; + } + return 0; +} + +static int VULKAN_UpdateTextureNV(SDL_Renderer *renderer, SDL_Texture *texture, + const SDL_Rect *rect, + const Uint8 *Yplane, int Ypitch, + const Uint8 *UVplane, int UVpitch) +{ + VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; + VULKAN_TextureData *textureData = (VULKAN_TextureData *)texture->driverdata; + + if (!textureData) { + return SDL_SetError("Texture is not currently available"); + } + + if (VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, 0, rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch, &textureData->mainImage.imageLayout) < 0) { + return -1; + } + + if (VULKAN_UpdateTextureInternal(rendererData, textureData->mainImageUV.image, textureData->mainImageUV.format, 1, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, UVplane, UVpitch, &textureData->mainImageUV.imageLayout) < 0) { + return -1; + } + return 0; +} +#endif + +static int VULKAN_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, + const SDL_Rect *rect, void **pixels, int *pitch) +{ + VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; + VULKAN_TextureData *textureData = (VULKAN_TextureData *)texture->driverdata; + VkResult result; + if (!textureData) { + return SDL_SetError("Texture is not currently available"); + } + + if (textureData->stagingBuffer.buffer != VK_NULL_HANDLE) { + return SDL_SetError("texture is already locked"); + } + + VkDeviceSize pixelSize = VULKAN_GetBytesPerPixel(textureData->mainImage.format); + VkDeviceSize length = rect->w * pixelSize; + VkDeviceSize stagingBufferSize = length * rect->h; + result = VULKAN_AllocateBuffer(rendererData, + stagingBufferSize, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, + &textureData->stagingBuffer); + if (result != VK_SUCCESS) { + return SDL_SetError("[Vulkan] VULKAN_AllocateBuffer with result %s", SDL_Vulkan_GetResultString(result)); + } + + /* Make note of where the staging texture will be written to + * (on a call to SDL_UnlockTexture): + */ + textureData->lockedRect = *rect; + + /* Make sure the caller has information on the texture's pixel buffer, + * then return: + */ + *pixels = textureData->stagingBuffer.mappedBufferPtr; + *pitch = length; + return 0; + +} + +static void VULKAN_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture) +{ + VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; + VULKAN_TextureData *textureData = (VULKAN_TextureData *)texture->driverdata; + + if (!textureData) { + return; + } + + VULKAN_EnsureCommandBuffer(rendererData); + + /* Make sure the destination is in the correct resource state */ + VULKAN_RecordPipelineImageBarrier(rendererData, + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + textureData->mainImage.image, + &textureData->mainImage.imageLayout); + + VkBufferImageCopy region; + region.bufferOffset = 0; + region.bufferRowLength = 0; + region.bufferImageHeight = 0; + region.imageSubresource.baseArrayLayer = 0; + region.imageSubresource.layerCount = 1; + region.imageSubresource.mipLevel = 0; + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageOffset.x = textureData->lockedRect.x; + region.imageOffset.y = textureData->lockedRect.y; + region.imageOffset.z = 0; + region.imageExtent.width = textureData->lockedRect.w; + region.imageExtent.height = textureData->lockedRect.h; + region.imageExtent.depth = 1; + vkCmdCopyBufferToImage(rendererData->currentCommandBuffer, textureData->stagingBuffer.buffer, textureData->mainImage.image, textureData->mainImage.imageLayout, 1, ®ion); + + /* Transition the texture to be shader accessible */ + VULKAN_RecordPipelineImageBarrier(rendererData, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_ACCESS_SHADER_READ_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + textureData->mainImage.image, + &textureData->mainImage.imageLayout); + + /* Execute the command list before releasing the staging buffer */ + VULKAN_IssueBatch(rendererData); + + VULKAN_DestroyBuffer(rendererData, &textureData->stagingBuffer); +} + +static void VULKAN_SetTextureScaleMode(SDL_Renderer *renderer, SDL_Texture *texture, SDL_ScaleMode scaleMode) +{ + VULKAN_TextureData *textureData = (VULKAN_TextureData *)texture->driverdata; + + if (!textureData) { + return; + } + + textureData->scaleMode = (scaleMode == SDL_SCALEMODE_NEAREST) ? VK_FILTER_NEAREST : VK_FILTER_LINEAR; +} + +static int VULKAN_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture) +{ + VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; + VULKAN_TextureData *textureData = NULL; + + VULKAN_EnsureCommandBuffer(rendererData); + + if (!texture) { + if (rendererData->textureRenderTarget) { + + VULKAN_RecordPipelineImageBarrier(rendererData, + VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + VK_ACCESS_SHADER_READ_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + rendererData->textureRenderTarget->mainImage.image, + &rendererData->textureRenderTarget->mainImage.imageLayout); + } + rendererData->textureRenderTarget = NULL; + return 0; + } + + textureData = (VULKAN_TextureData *)texture->driverdata; + + if (textureData->mainImage.imageView == VK_NULL_HANDLE) { + return SDL_SetError("specified texture is not a render target"); + } + + rendererData->textureRenderTarget = textureData; + VULKAN_RecordPipelineImageBarrier(rendererData, + VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + rendererData->textureRenderTarget->mainImage.image, + &rendererData->textureRenderTarget->mainImage.imageLayout); + + return 0; +} + +static int VULKAN_QueueNoOp(SDL_Renderer *renderer, SDL_RenderCommand *cmd) +{ + return 0; /* nothing to do in this backend. */ +} + +static int VULKAN_QueueDrawPoints(SDL_Renderer *renderer, SDL_RenderCommand *cmd, const SDL_FPoint *points, int count) +{ + VertexPositionColor *verts = (VertexPositionColor *)SDL_AllocateRenderVertices(renderer, count * sizeof(VertexPositionColor), 0, &cmd->data.draw.first); + int i; + SDL_bool convert_color = SDL_RenderingLinearSpace(renderer); + + if (!verts) { + return -1; + } + + cmd->data.draw.count = count; + for (i = 0; i < count; i++) { + verts->pos[0] = points[i].x + 0.5f; + verts->pos[1] = points[i].y + 0.5f; + verts->tex[0] = 0.0f; + verts->tex[1] = 0.0f; + verts->color = cmd->data.draw.color; + if (convert_color) { + SDL_ConvertToLinear(&verts->color); + } + verts++; + } + return 0; +} + +static int VULKAN_QueueGeometry(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture, + const float *xy, int xy_stride, const SDL_FColor *color, int color_stride, const float *uv, int uv_stride, + int num_vertices, const void *indices, int num_indices, int size_indices, + float scale_x, float scale_y) +{ + int i; + int count = indices ? num_indices : num_vertices; + VertexPositionColor *verts = (VertexPositionColor *)SDL_AllocateRenderVertices(renderer, count * sizeof(VertexPositionColor), 0, &cmd->data.draw.first); + SDL_bool convert_color = SDL_RenderingLinearSpace(renderer); + VULKAN_TextureData *textureData = texture ? (VULKAN_TextureData *)texture->driverdata : NULL; + float u_scale = textureData ? (float)texture->w / textureData->width : 0.0f; + float v_scale = textureData ? (float)texture->h / textureData->height : 0.0f; + + if (!verts) { + return -1; + } + + cmd->data.draw.count = count; + size_indices = indices ? size_indices : 0; + + for (i = 0; i < count; i++) { + int j; + float *xy_; + if (size_indices == 4) { + j = ((const Uint32 *)indices)[i]; + } else if (size_indices == 2) { + j = ((const Uint16 *)indices)[i]; + } else if (size_indices == 1) { + j = ((const Uint8 *)indices)[i]; + } else { + j = i; + } + + xy_ = (float *)((char *)xy + j * xy_stride); + + verts->pos[0] = xy_[0] * scale_x; + verts->pos[1] = xy_[1] * scale_y; + verts->color = *(SDL_FColor *)((char *)color + j * color_stride); + if (convert_color) { + SDL_ConvertToLinear(&verts->color); + } + + if (texture) { + float *uv_ = (float *)((char *)uv + j * uv_stride); + verts->tex[0] = uv_[0] * u_scale; + verts->tex[1] = uv_[1] * v_scale; + } else { + verts->tex[0] = 0.0f; + verts->tex[1] = 0.0f; + } + + verts += 1; + } + return 0; +} + +static SDL_bool VULKAN_UpdateVertexBuffer(SDL_Renderer *renderer, + const void *vertexData, size_t dataSizeInBytes, VULKAN_DrawStateCache *stateCache) +{ + VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; + const int vbidx = rendererData->currentVertexBuffer; + VULKAN_Buffer *vertexBuffer; + + if (dataSizeInBytes == 0) { + return 0; /* nothing to do. */ + } + + if (rendererData->issueBatch) { + if (VULKAN_IssueBatch(rendererData) != VK_SUCCESS) { + SDL_SetError("Failed to issue intermediate batch"); + return SDL_FALSE; + } + } + /* If the existing vertex buffer isn't big enough, we need to recreate a big enough one */ + if (dataSizeInBytes > rendererData->vertexBuffers[vbidx].size) { + VULKAN_CreateVertexBuffer(rendererData, vbidx, dataSizeInBytes); + } + + vertexBuffer = &rendererData->vertexBuffers[vbidx]; + SDL_memcpy(vertexBuffer->mappedBufferPtr, vertexData, dataSizeInBytes); + + stateCache->vertexBuffer = vertexBuffer->buffer; + + rendererData->currentVertexBuffer++; + if (rendererData->currentVertexBuffer >= SDL_VULKAN_NUM_VERTEX_BUFFERS) { + rendererData->currentVertexBuffer = 0; + rendererData->issueBatch = SDL_TRUE; + } + + return SDL_TRUE; +} + +static int VULKAN_UpdateViewport(SDL_Renderer *renderer) +{ + VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; + const SDL_Rect *viewport = &rendererData->currentViewport; + Float4X4 projection; + Float4X4 view; + + if (viewport->w == 0 || viewport->h == 0) { + /* If the viewport is empty, assume that it is because + * SDL_CreateRenderer is calling it, and will call it again later + * with a non-empty viewport. + */ + /* SDL_Log("%s, no viewport was set!\n", __FUNCTION__); */ + return -1; + } + + projection = MatrixIdentity(); + + /* Update the view matrix */ + SDL_zero(view); + view.m[0][0] = 2.0f / viewport->w; + view.m[1][1] = -2.0f / viewport->h; + view.m[2][2] = 1.0f; + view.m[3][0] = -1.0f; + view.m[3][1] = 1.0f; + view.m[3][3] = 1.0f; + + rendererData->vertexShaderConstantsData.projectionAndView = MatrixMultiply( + view, + projection); + + VkViewport vkViewport; + vkViewport.x = viewport->x; + vkViewport.y = viewport->y; + vkViewport.width = viewport->w; + vkViewport.height = viewport->h; + vkViewport.minDepth = 0.0f; + vkViewport.maxDepth = 1.0f; + vkCmdSetViewport(rendererData->currentCommandBuffer, 0, 1, &vkViewport); + + VkRect2D scissor; + scissor.offset.x = viewport->x; + scissor.offset.y = viewport->y; + scissor.extent.width = viewport->w; + scissor.extent.height = viewport->h; + vkCmdSetScissor(rendererData->currentCommandBuffer, 0, 1, &scissor); + + rendererData->viewportDirty = SDL_FALSE; + return 0; +} + +static void VULKAN_SetupShaderConstants(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, const SDL_Texture *texture, PixelShaderConstants *constants) +{ + float output_headroom; + + SDL_zerop(constants); + + constants->scRGB_output = (float)SDL_RenderingLinearSpace(renderer); + constants->color_scale = cmd->data.draw.color_scale; + + if (texture) { + VULKAN_TextureData *textureData = (VULKAN_TextureData *)texture->driverdata; + + switch (texture->format) { + case SDL_PIXELFORMAT_YV12: + case SDL_PIXELFORMAT_IYUV: + constants->texture_type = TEXTURETYPE_YUV; + constants->input_type = INPUTTYPE_SRGB; + break; + case SDL_PIXELFORMAT_NV12: + constants->texture_type = TEXTURETYPE_NV12; + constants->input_type = INPUTTYPE_SRGB; + break; + case SDL_PIXELFORMAT_NV21: + constants->texture_type = TEXTURETYPE_NV21; + constants->input_type = INPUTTYPE_SRGB; + break; + case SDL_PIXELFORMAT_P010: + constants->texture_type = TEXTURETYPE_NV12; + constants->input_type = INPUTTYPE_HDR10; + break; + default: + constants->texture_type = TEXTURETYPE_RGB; + if (texture->colorspace == SDL_COLORSPACE_SRGB_LINEAR) { + constants->input_type = INPUTTYPE_SCRGB; + } else if (SDL_COLORSPACEPRIMARIES(texture->colorspace) == SDL_COLOR_PRIMARIES_BT2020 && + SDL_COLORSPACETRANSFER(texture->colorspace) == SDL_TRANSFER_CHARACTERISTICS_PQ) { + constants->input_type = INPUTTYPE_HDR10; + } else { + constants->input_type = INPUTTYPE_UNSPECIFIED; + } + break; + } + + constants->sdr_white_point = texture->SDR_white_point; + + if (renderer->target) { + output_headroom = renderer->target->HDR_headroom; + } else { + output_headroom = renderer->HDR_headroom; + } + + if (texture->HDR_headroom > output_headroom) { + constants->tonemap_method = TONEMAP_CHROME; + constants->tonemap_factor1 = (output_headroom / (texture->HDR_headroom * texture->HDR_headroom)); + constants->tonemap_factor2 = (1.0f / output_headroom); + } + + if (textureData->YCbCr_matrix) { + SDL_memcpy(constants->YCbCr_matrix, textureData->YCbCr_matrix, sizeof(constants->YCbCr_matrix)); + } + } +} + +static SDL_bool VULKAN_SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, VULKAN_Shader shader, const PixelShaderConstants *shader_constants, + VkPrimitiveTopology topology, int imageViewCount, VkImageView *imageViews, VkSampler sampler, const Float4X4 *matrix, VULKAN_DrawStateCache *stateCache) + +{ + VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; + const SDL_BlendMode blendMode = cmd->data.draw.blend; + VkFormat format = rendererData->surfaceFormat.format; // TEMP + const Float4X4 *newmatrix = matrix ? matrix : &rendererData->identity; + SDL_bool updateConstants = SDL_FALSE; + PixelShaderConstants solid_constants; + VkBuffer constantBuffer; + VkDeviceSize constantBufferOffset; + int i; + + if (!VULKAN_ActivateCommandBuffer(renderer, VK_ATTACHMENT_LOAD_OP_LOAD, NULL, stateCache)) { + return SDL_FALSE; + } + + /* See if we need to change the pipeline state */ + if (!rendererData->currentPipelineState || + rendererData->currentPipelineState->shader != shader || + rendererData->currentPipelineState->blendMode != blendMode || + rendererData->currentPipelineState->topology != topology || + rendererData->currentPipelineState->format != format) { + + rendererData->currentPipelineState = NULL; + for (i = 0; i < rendererData->pipelineStateCount; ++i) { + VULKAN_PipelineState *candidatePiplineState = &rendererData->pipelineStates[i]; + if (candidatePiplineState->shader == shader && + candidatePiplineState->blendMode == blendMode && + candidatePiplineState->topology == topology && + candidatePiplineState->format == format) { + rendererData->currentPipelineState = candidatePiplineState; + break; + } + } + + /* If we didn't find a match, create a new one -- it must mean the blend mode is non-standard */ + if (!rendererData->currentPipelineState) { + rendererData->currentPipelineState = VULKAN_CreatePipelineState(renderer, shader, blendMode, topology, format); + } + + if (!rendererData->currentPipelineState) { + return SDL_SetError("[Vulkan] Unable to create required pipeline state"); + } + + vkCmdBindPipeline(rendererData->currentCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, rendererData->currentPipelineState->pipeline); + updateConstants = SDL_TRUE; + } + + if (rendererData->viewportDirty) { + if (VULKAN_UpdateViewport(renderer) == 0) { + /* vertexShaderConstantsData.projectionAndView has changed */ + updateConstants = SDL_TRUE; + } + } + + if (updateConstants == SDL_TRUE || SDL_memcmp(&rendererData->vertexShaderConstantsData.model, newmatrix, sizeof(*newmatrix)) != 0) { + SDL_memcpy(&rendererData->vertexShaderConstantsData.model, newmatrix, sizeof(*newmatrix)); + vkCmdPushConstants(rendererData->currentCommandBuffer, rendererData->currentPipelineState->pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, + sizeof(rendererData->vertexShaderConstantsData), + &rendererData->vertexShaderConstantsData); + } + + if (!shader_constants) { + VULKAN_SetupShaderConstants(renderer, cmd, NULL, &solid_constants); + shader_constants = &solid_constants; + } + constantBuffer = rendererData->constantBuffers[rendererData->currentCommandBufferIndex].buffer; + constantBufferOffset = (rendererData->currentConstantBufferOffset < 0) ? 0 : rendererData->currentConstantBufferOffset; + if (updateConstants || + SDL_memcmp(shader_constants, &rendererData->currentPipelineState->shader_constants, sizeof(*shader_constants)) != 0) { + + if (rendererData->currentConstantBufferOffset == -1) { + /* First time, grab offset 0 */ + rendererData->currentConstantBufferOffset = 0; + constantBufferOffset = 0; + } + else { + /* Align the next address to the minUniformBufferOffsetAlignment */ + VkDeviceSize alignment = rendererData->physicalDeviceProperties.limits.minUniformBufferOffsetAlignment; + SDL_assert(rendererData->currentConstantBufferOffset >= 0 ); + rendererData->currentConstantBufferOffset += (sizeof(PixelShaderConstants) + alignment - 1) & ~(alignment - 1); + constantBufferOffset = rendererData->currentConstantBufferOffset; + } + + /* Upload constants to persistently mapped buffer */ + if (rendererData->currentConstantBufferOffset > SDL_VULKAN_CONSTANT_BUFFER_DEFAULT_SIZE) { + VULKAN_IssueBatch(rendererData); + rendererData->currentConstantBufferOffset = 0; + constantBufferOffset = 0; + } + uint8_t *dst = rendererData->constantBuffers[rendererData->currentCommandBufferIndex].mappedBufferPtr; + dst += constantBufferOffset; + SDL_memcpy(dst, &rendererData->currentPipelineState->shader_constants, sizeof(PixelShaderConstants)); + + SDL_memcpy(&rendererData->currentPipelineState->shader_constants, shader_constants, sizeof(*shader_constants)); + } + + /* Allocate the descriptor set */ + { + VkDescriptorSetAllocateInfo descriptorSetAllocateInfo = { 0 }; + descriptorSetAllocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + descriptorSetAllocateInfo.descriptorSetCount = 1; + descriptorSetAllocateInfo.descriptorPool = rendererData->descriptorPools[rendererData->currentCommandBufferIndex]; + descriptorSetAllocateInfo.pSetLayouts = &rendererData->descriptorSetLayouts[shader]; + + VkDescriptorSet descriptorSet = VK_NULL_HANDLE; + VkResult result = (rendererData->currentDescriptorSetIndex >= SDL_VULKAN_MAX_DESCRIPTOR_SETS) ? VK_ERROR_OUT_OF_DEVICE_MEMORY : VK_SUCCESS; + if (result == VK_SUCCESS) { + result = vkAllocateDescriptorSets(rendererData->device, &descriptorSetAllocateInfo, &descriptorSet); + } + // Out of descriptor sets + if (result != VK_SUCCESS) { + VULKAN_IssueBatch(rendererData); + result = vkAllocateDescriptorSets(rendererData->device, &descriptorSetAllocateInfo, &descriptorSet); + if (result != VK_SUCCESS) { + SDL_SetError("[Vulkan] Unable to allocate descriptor set."); + } + } + rendererData->currentDescriptorSetIndex++; + VkDescriptorImageInfo samplerDescriptor = { 0 }; + samplerDescriptor.sampler = sampler; + + VkDescriptorImageInfo imageDescriptors[3]; + SDL_memset(imageDescriptors, 0, sizeof(imageDescriptors)); + VkDescriptorBufferInfo bufferDescriptor = { 0 }; + bufferDescriptor.buffer = constantBuffer; + bufferDescriptor.offset = constantBufferOffset; + bufferDescriptor.range = sizeof(PixelShaderConstants); + + VkWriteDescriptorSet descriptorWrites[5]; + SDL_memset(descriptorWrites, 0, sizeof(descriptorWrites)); + uint32_t descriptorCount = 1; /* Always have the uniform buffer */ + + descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[0].dstSet = descriptorSet; + descriptorWrites[0].dstBinding = 4; + descriptorWrites[0].dstArrayElement = 0; + descriptorWrites[0].descriptorCount = 1; + descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorWrites[0].pBufferInfo = &bufferDescriptor; + + if (sampler != VK_NULL_HANDLE) { + descriptorCount++; + descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[1].dstSet = descriptorSet; + descriptorWrites[1].dstBinding = 0; + descriptorWrites[1].dstArrayElement = 0; + descriptorWrites[1].descriptorCount = 1; + descriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; + descriptorWrites[1].pImageInfo = &samplerDescriptor; + } + + uint32_t startImageViews = descriptorCount; + for (i = 0; i < 3 && imageViewCount > 0; i++) { + descriptorCount++; + imageDescriptors[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + /* There are up to 3 images in the shader, if we haven't specified that many, duplicate the first + one. There is dynamic branching that determines how many actually get fetched, but we need + them all populated for validation. */ + imageDescriptors[i].imageView = (i < imageViewCount) ? imageViews[i] : imageViews[0]; + descriptorWrites[i+startImageViews].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[i+startImageViews].dstSet = descriptorSet; + descriptorWrites[i+startImageViews].dstBinding = 1 + i; + descriptorWrites[i+startImageViews].dstArrayElement = 0; + descriptorWrites[i+startImageViews].descriptorCount = 1; + descriptorWrites[i+startImageViews].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + descriptorWrites[i+startImageViews].pImageInfo = &imageDescriptors[i]; + } + vkUpdateDescriptorSets(rendererData->device, descriptorCount, descriptorWrites, 0, NULL); + vkCmdBindDescriptorSets(rendererData->currentCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, rendererData->currentPipelineState->pipelineLayout, + 0, 1, &descriptorSet, 0, NULL); + } + return SDL_TRUE; +} + + +static SDL_bool VULKAN_SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, const Float4X4 *matrix, VULKAN_DrawStateCache *stateCache) +{ + SDL_Texture *texture = cmd->data.draw.texture; + VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; + VULKAN_TextureData *textureData = (VULKAN_TextureData *)texture->driverdata; + VkSampler textureSampler = VK_NULL_HANDLE; + PixelShaderConstants constants; + + VULKAN_SetupShaderConstants(renderer, cmd, texture, &constants); + + switch (textureData->scaleMode) { + case VK_FILTER_NEAREST: + textureSampler = rendererData->samplers[SDL_VULKAN_SAMPLER_NEAREST]; + break; + case VK_FILTER_LINEAR: + textureSampler = rendererData->samplers[SDL_VULKAN_SAMPLER_LINEAR]; + break; + default: + return SDL_SetError("Unknown scale mode: %d\n", textureData->scaleMode); + } + + + if (textureData->mainImage.imageLayout != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + SDL_bool stoppedRenderPass = SDL_FALSE; + if (rendererData->currentRenderPass != VK_NULL_HANDLE) { + vkCmdEndRenderPass(rendererData->currentCommandBuffer); + rendererData->currentRenderPass = VK_NULL_HANDLE; + stoppedRenderPass = SDL_TRUE; + } + + VULKAN_RecordPipelineImageBarrier(rendererData, + VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + VK_ACCESS_SHADER_READ_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + textureData->mainImage.image, + &textureData->mainImage.imageLayout); + + if (stoppedRenderPass) { + VULKAN_BeginRenderPass(rendererData, VK_ATTACHMENT_LOAD_OP_LOAD, NULL); + } + } + +#if SDL_HAVE_YUV + if (textureData->yuv) { + + /* Make sure each texture is in the correct state to be accessed by the pixel shader. */ + VULKAN_RecordPipelineImageBarrier(rendererData, + VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + VK_ACCESS_SHADER_READ_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + textureData->mainImageU.image, + &textureData->mainImageU.imageLayout); + VULKAN_RecordPipelineImageBarrier(rendererData, + VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + VK_ACCESS_SHADER_READ_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + textureData->mainImageV.image, + &textureData->mainImageV.imageLayout); + + VkImageView imageViews[3] = { + textureData->mainImage.imageView, + textureData->mainImageU.imageView, + textureData->mainImageV.imageView + }; + + return VULKAN_SetDrawState(renderer, cmd, textureData->shader, &constants, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, SDL_arraysize(imageViews), imageViews, + textureSampler, matrix, stateCache); + } else if (textureData->nv12) { + + /* Make sure each texture is in the correct state to be accessed by the pixel shader. */ + VULKAN_RecordPipelineImageBarrier(rendererData, + VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + VK_ACCESS_SHADER_READ_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + textureData->mainImageUV.image, + &textureData->mainImageUV.imageLayout); + + VkImageView imageViews[2] = { + textureData->mainImage.imageView, + textureData->mainImageUV.imageView, + }; + + return VULKAN_SetDrawState(renderer, cmd, textureData->shader, &constants, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, SDL_arraysize(imageViews), imageViews, + textureSampler, matrix, stateCache); + } +#endif + return VULKAN_SetDrawState(renderer, cmd, textureData->shader, &constants, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 1, &textureData->mainImage.imageView, textureSampler, matrix, stateCache); +} + +static void VULKAN_DrawPrimitives(SDL_Renderer *renderer, VkPrimitiveTopology primitiveTopology, const size_t vertexStart, const size_t vertexCount) +{ + VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; + vkCmdDraw(rendererData->currentCommandBuffer, vertexCount, 1, vertexStart, 0); +} + +static void VULKAN_InvalidateCachedState(SDL_Renderer *renderer) +{ + VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; + rendererData->currentPipelineState = NULL; + rendererData->currentVertexBuffer = 0; + rendererData->issueBatch = SDL_FALSE; + rendererData->cliprectDirty = SDL_TRUE; + rendererData->currentDescriptorSetIndex = 0; + rendererData->currentConstantBufferOffset = 0; +} + +static int VULKAN_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize) +{ + VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; + VULKAN_DrawStateCache stateCache; + SDL_memset(&stateCache, 0, sizeof(stateCache)); + + if (rendererData->pixelSizeChanged) { + if (VULKAN_UpdateForWindowSizeChange(renderer) != VK_SUCCESS) { + return -1; + } + rendererData->pixelSizeChanged = SDL_FALSE; + } + + if (VULKAN_UpdateVertexBuffer(renderer, vertices, vertsize, &stateCache) < 0) { + return -1; + } + + while (cmd) { + switch (cmd->command) { + case SDL_RENDERCMD_SETDRAWCOLOR: + { + break; /* this isn't currently used in this render backend. */ + } + + case SDL_RENDERCMD_SETVIEWPORT: + { + SDL_Rect *viewport = &rendererData->currentViewport; + if (SDL_memcmp(viewport, &cmd->data.viewport.rect, sizeof(cmd->data.viewport.rect)) != 0) { + SDL_copyp(viewport, &cmd->data.viewport.rect); + rendererData->viewportDirty = SDL_TRUE; + } + break; + } + + case SDL_RENDERCMD_SETCLIPRECT: + { + const SDL_Rect *rect = &cmd->data.cliprect.rect; + SDL_Rect viewport_cliprect; + if (rendererData->currentCliprectEnabled != cmd->data.cliprect.enabled) { + rendererData->currentCliprectEnabled = cmd->data.cliprect.enabled; + rendererData->cliprectDirty = SDL_TRUE; + } + if (!rendererData->currentCliprectEnabled) { + /* If the clip rect is disabled, then the scissor rect should be the whole viewport, + since direct3d12 doesn't allow disabling the scissor rectangle */ + viewport_cliprect.x = 0; + viewport_cliprect.y = 0; + viewport_cliprect.w = rendererData->currentViewport.w; + viewport_cliprect.h = rendererData->currentViewport.h; + rect = &viewport_cliprect; + } + if (SDL_memcmp(&rendererData->currentCliprect, rect, sizeof(*rect)) != 0) { + SDL_copyp(&rendererData->currentCliprect, rect); + rendererData->cliprectDirty = SDL_TRUE; + } + break; + } + + case SDL_RENDERCMD_CLEAR: + { + SDL_bool convert_color = SDL_RenderingLinearSpace(renderer); + SDL_FColor color = cmd->data.color.color; + if (convert_color) { + SDL_ConvertToLinear(&color); + } + color.r *= cmd->data.color.color_scale; + color.g *= cmd->data.color.color_scale; + color.b *= cmd->data.color.color_scale; + + VkClearColorValue clearColor; + clearColor.float32[0] = color.r; + clearColor.float32[1] = color.g; + clearColor.float32[2] = color.b; + clearColor.float32[3] = color.a; + VULKAN_ActivateCommandBuffer(renderer, VK_ATTACHMENT_LOAD_OP_CLEAR, &clearColor, &stateCache); + break; + } + + case SDL_RENDERCMD_DRAW_POINTS: + { + const size_t count = cmd->data.draw.count; + const size_t first = cmd->data.draw.first; + const size_t start = first / sizeof(VertexPositionColor); + VULKAN_SetDrawState(renderer, cmd, SHADER_SOLID, NULL, VK_PRIMITIVE_TOPOLOGY_POINT_LIST, 0, NULL, VK_NULL_HANDLE, NULL, &stateCache); + VULKAN_DrawPrimitives(renderer, VK_PRIMITIVE_TOPOLOGY_POINT_LIST, start, count); + break; + } + + case SDL_RENDERCMD_DRAW_LINES: + { + const size_t count = cmd->data.draw.count; + const size_t first = cmd->data.draw.first; + const size_t start = first / sizeof(VertexPositionColor); + const VertexPositionColor *verts = (VertexPositionColor *)(((Uint8 *)vertices) + first); + VULKAN_SetDrawState(renderer, cmd, SHADER_SOLID, NULL, VK_PRIMITIVE_TOPOLOGY_LINE_STRIP, 0, NULL, VK_NULL_HANDLE, NULL, &stateCache); + VULKAN_DrawPrimitives(renderer, VK_PRIMITIVE_TOPOLOGY_LINE_STRIP, start, count); + if (verts[0].pos[0] != verts[count - 1].pos[0] || verts[0].pos[1] != verts[count - 1].pos[1]) { + VULKAN_SetDrawState(renderer, cmd, SHADER_SOLID, NULL, VK_PRIMITIVE_TOPOLOGY_POINT_LIST, 0, NULL, VK_NULL_HANDLE, NULL, &stateCache); + VULKAN_DrawPrimitives(renderer, VK_PRIMITIVE_TOPOLOGY_POINT_LIST, start + (count - 1), 1); + } + break; + } + + case SDL_RENDERCMD_FILL_RECTS: /* unused */ + break; + + case SDL_RENDERCMD_COPY: /* unused */ + break; + + case SDL_RENDERCMD_COPY_EX: /* unused */ + break; + + case SDL_RENDERCMD_GEOMETRY: + { + SDL_Texture *texture = cmd->data.draw.texture; + const size_t count = cmd->data.draw.count; + const size_t first = cmd->data.draw.first; + const size_t start = first / sizeof(VertexPositionColor); + + if (texture) { + VULKAN_SetCopyState(renderer, cmd, NULL, &stateCache); + } else { + VULKAN_SetDrawState(renderer, cmd, SHADER_SOLID, NULL, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, NULL, VK_NULL_HANDLE, NULL, &stateCache); + } + + VULKAN_DrawPrimitives(renderer, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, start, count); + break; + } + + case SDL_RENDERCMD_NO_OP: + break; + } + + cmd = cmd->next; + } + return 0; +} + +static SDL_Surface* VULKAN_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect) +{ + VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; + VkImage backBuffer; + VkImageLayout *imageLayout; + VULKAN_Buffer readbackBuffer; + VkDeviceSize pixelSize; + VkDeviceSize length; + VkDeviceSize readbackBufferSize; + VkFormat vkFormat; + SDL_Surface *output; + + VULKAN_EnsureCommandBuffer(rendererData); + + /* Stop any outstanding renderpass if open */ + if (rendererData->currentRenderPass != VK_NULL_HANDLE) { + vkCmdEndRenderPass(rendererData->currentCommandBuffer); + rendererData->currentRenderPass = VK_NULL_HANDLE; + } + + if (rendererData->textureRenderTarget) { + backBuffer = rendererData->textureRenderTarget->mainImage.image; + imageLayout = &rendererData->textureRenderTarget->mainImage.imageLayout; + vkFormat = rendererData->textureRenderTarget->mainImage.format; + } else { + backBuffer = rendererData->swapchainImages[rendererData->currentSwapchainImageIndex]; + imageLayout = &rendererData->swapchainImageLayouts[rendererData->currentSwapchainImageIndex]; + vkFormat = rendererData->surfaceFormat.format; + } + + pixelSize = VULKAN_GetBytesPerPixel(vkFormat); + length = rect->w * pixelSize; + readbackBufferSize = length * rect->h; + if (VULKAN_AllocateBuffer(rendererData, readbackBufferSize, + VK_BUFFER_USAGE_TRANSFER_DST_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, + &readbackBuffer) != VK_SUCCESS) { + SDL_SetError("[Vulkan] Failed to allocate buffer for readback."); + return NULL; + } + + + /* Make sure the source is in the correct resource state */ + VULKAN_RecordPipelineImageBarrier(rendererData, + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, + VK_ACCESS_TRANSFER_READ_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + backBuffer, + imageLayout); + + /* Copy the image to the readback buffer */ + VkBufferImageCopy region; + region.bufferOffset = 0; + region.bufferRowLength = 0; + region.bufferImageHeight = 0; + region.imageSubresource.baseArrayLayer = 0; + region.imageSubresource.layerCount = 1; + region.imageSubresource.mipLevel = 0; + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageOffset.x = rect->x; + region.imageOffset.y = rect->y; + region.imageOffset.z = 0; + region.imageExtent.width = rect->w; + region.imageExtent.height = rect->h; + region.imageExtent.depth = 1; + vkCmdCopyImageToBuffer(rendererData->currentCommandBuffer, backBuffer, *imageLayout, readbackBuffer.buffer, 1, ®ion); + + /* We need to issue the command list for the copy to finish */ + VULKAN_IssueBatch(rendererData); + + /* Transition the render target back to a render target */ + VULKAN_RecordPipelineImageBarrier(rendererData, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + backBuffer, + imageLayout); + + output = SDL_DuplicatePixels( + rect->w, rect->h, + VULKAN_VkFormatToSDLPixelFormat(vkFormat), + renderer->target ? renderer->target->colorspace : renderer->output_colorspace, + readbackBuffer.mappedBufferPtr, + length); + + VULKAN_DestroyBuffer(rendererData, &readbackBuffer); + + return output; +} + +static int VULKAN_RenderPresent(SDL_Renderer *renderer) +{ + VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; + VkResult result = VK_SUCCESS; + if (rendererData->currentCommandBuffer) { + + rendererData->currentPipelineState = VK_NULL_HANDLE; + rendererData->viewportDirty = SDL_TRUE; + + VULKAN_RecordPipelineImageBarrier(rendererData, + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + rendererData->swapchainImages[rendererData->currentSwapchainImageIndex], + &rendererData->swapchainImageLayouts[rendererData->currentSwapchainImageIndex]); + + vkEndCommandBuffer(rendererData->currentCommandBuffer); + + result = vkResetFences(rendererData->device, 1, &rendererData->fences[rendererData->currentCommandBufferIndex]); + if (result != VK_SUCCESS) { + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkResetFences(): %s\n", SDL_Vulkan_GetResultString(result)); + return -1; + } + + + VkPipelineStageFlags waitDestStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + VkSubmitInfo submitInfo = { 0 }; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.waitSemaphoreCount = 1; + submitInfo.pWaitSemaphores = &rendererData->imageAvailableSemaphore; + submitInfo.pWaitDstStageMask = &waitDestStageMask; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &rendererData->currentCommandBuffer; + submitInfo.signalSemaphoreCount = 1; + submitInfo.pSignalSemaphores = &rendererData->renderingFinishedSemaphore; + result = vkQueueSubmit(rendererData->graphicsQueue, 1, &submitInfo, rendererData->fences[rendererData->currentCommandBufferIndex]); + if (result != VK_SUCCESS) { + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkQueueSubmit(): %s\n", SDL_Vulkan_GetResultString(result)); + return -1; + } + rendererData->currentCommandBuffer = VK_NULL_HANDLE; + + VkPresentInfoKHR presentInfo = { 0 }; + presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + presentInfo.waitSemaphoreCount = 1; + presentInfo.pWaitSemaphores = &rendererData->renderingFinishedSemaphore; + presentInfo.swapchainCount = 1; + presentInfo.pSwapchains = &rendererData->swapchain; + presentInfo.pImageIndices = &rendererData->currentSwapchainImageIndex; + result = vkQueuePresentKHR(rendererData->presentQueue, &presentInfo); + if ((result != VK_SUCCESS) && (result != VK_ERROR_OUT_OF_DATE_KHR) && (result != VK_ERROR_SURFACE_LOST_KHR) && (result != VK_SUBOPTIMAL_KHR )) { + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkQueuePresentKHR(): %s\n", SDL_Vulkan_GetResultString(result)); + return -1; + } + + rendererData->currentCommandBufferIndex = ( rendererData->currentCommandBufferIndex + 1 ) % rendererData->swapchainImageCount; + + /* Wait for previous time this command buffer was submitted, will be N frames ago */ + result = vkWaitForFences(rendererData->device, 1, &rendererData->fences[rendererData->currentCommandBufferIndex], VK_TRUE, UINT64_MAX); + if (result != VK_SUCCESS) { + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkWaitForFences(): %s\n", SDL_Vulkan_GetResultString(result)); + return -1; + } + } + + VULKAN_AcquireNextSwapchainImage(renderer); + + return (result == VK_SUCCESS); +} + +static int VULKAN_SetVSync(SDL_Renderer *renderer, const int vsync) +{ + if (vsync) { + renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC; + } else { + renderer->info.flags &= ~SDL_RENDERER_PRESENTVSYNC; + } + return 0; +} + +SDL_Renderer *VULKAN_CreateRenderer(SDL_Window *window, SDL_PropertiesID create_props) +{ + SDL_Renderer *renderer; + VULKAN_RenderData *rendererData; + + renderer = (SDL_Renderer *)SDL_calloc(1, sizeof(*renderer)); + if (!renderer) { + return NULL; + } + renderer->magic = &SDL_renderer_magic; + SDL_SetupRendererColorspace(renderer, create_props); + + if (renderer->output_colorspace != SDL_COLORSPACE_SRGB && + renderer->output_colorspace != SDL_COLORSPACE_SRGB_LINEAR && + renderer->output_colorspace != SDL_COLORSPACE_HDR10) { + SDL_SetError("Unsupported output colorspace"); + SDL_free(renderer); + return NULL; + } + + rendererData = (VULKAN_RenderData *)SDL_calloc(1, sizeof(*rendererData)); + if (!rendererData) { + SDL_free(renderer); + return NULL; + } + + rendererData->identity = MatrixIdentity(); + rendererData->identitySwizzle.r = VK_COMPONENT_SWIZZLE_IDENTITY; + rendererData->identitySwizzle.g = VK_COMPONENT_SWIZZLE_IDENTITY; + rendererData->identitySwizzle.b = VK_COMPONENT_SWIZZLE_IDENTITY; + rendererData->identitySwizzle.a = VK_COMPONENT_SWIZZLE_IDENTITY; + + renderer->WindowEvent = VULKAN_WindowEvent; + renderer->SupportsBlendMode = VULKAN_SupportsBlendMode; + renderer->CreateTexture = VULKAN_CreateTexture; + renderer->UpdateTexture = VULKAN_UpdateTexture; +#if SDL_HAVE_YUV + renderer->UpdateTextureYUV = VULKAN_UpdateTextureYUV; + renderer->UpdateTextureNV = VULKAN_UpdateTextureNV; +#endif + renderer->LockTexture = VULKAN_LockTexture; + renderer->UnlockTexture = VULKAN_UnlockTexture; + renderer->SetTextureScaleMode = VULKAN_SetTextureScaleMode; + renderer->SetRenderTarget = VULKAN_SetRenderTarget; + renderer->QueueSetViewport = VULKAN_QueueNoOp; + renderer->QueueSetDrawColor = VULKAN_QueueNoOp; + renderer->QueueDrawPoints = VULKAN_QueueDrawPoints; + renderer->QueueDrawLines = VULKAN_QueueDrawPoints; /* lines and points queue vertices the same way. */ + renderer->QueueGeometry = VULKAN_QueueGeometry; + renderer->InvalidateCachedState = VULKAN_InvalidateCachedState; + renderer->RunCommandQueue = VULKAN_RunCommandQueue; + renderer->RenderReadPixels = VULKAN_RenderReadPixels; + renderer->RenderPresent = VULKAN_RenderPresent; + renderer->DestroyTexture = VULKAN_DestroyTexture; + renderer->DestroyRenderer = VULKAN_DestroyRenderer; + renderer->info = VULKAN_RenderDriver.info; + renderer->info.flags = SDL_RENDERER_ACCELERATED; + renderer->driverdata = rendererData; + VULKAN_InvalidateCachedState(renderer); + + if (SDL_GetBooleanProperty(create_props, SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_BOOLEAN, SDL_FALSE)) { + renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC; + } + renderer->SetVSync = VULKAN_SetVSync; + + /* HACK: make sure the SDL_Renderer references the SDL_Window data now, in + * order to give init functions access to the underlying window handle: + */ + renderer->window = window; + + /* Initialize Direct3D resources */ + if (VULKAN_CreateDeviceResources(renderer) != VK_SUCCESS) { + VULKAN_DestroyRenderer(renderer); + return NULL; + } + if (VULKAN_CreateWindowSizeDependentResources(renderer) != VK_SUCCESS) { + VULKAN_DestroyRenderer(renderer); + return NULL; + } + + return renderer; +} + +SDL_RenderDriver VULKAN_RenderDriver = { + VULKAN_CreateRenderer, + { + "vulkan", + (SDL_RENDERER_ACCELERATED | + SDL_RENDERER_PRESENTVSYNC), /* flags. see SDL_RendererFlags */ + 9, /* num_texture_formats */ + { /* texture_formats */ + SDL_PIXELFORMAT_ARGB8888, + SDL_PIXELFORMAT_XRGB8888, + SDL_PIXELFORMAT_XBGR2101010, + SDL_PIXELFORMAT_RGBA64_FLOAT, + SDL_PIXELFORMAT_YV12, + SDL_PIXELFORMAT_IYUV, + SDL_PIXELFORMAT_NV12, + SDL_PIXELFORMAT_NV21, + SDL_PIXELFORMAT_P010 }, + 16384, /* max_texture_width */ + 16384 /* max_texture_height */ + } +}; + +#endif /* SDL_VIDEO_RENDER_VULKAN */ diff --git a/src/render/vulkan/SDL_shaders_vulkan.c b/src/render/vulkan/SDL_shaders_vulkan.c new file mode 100644 index 000000000..77aac9e69 --- /dev/null +++ b/src/render/vulkan/SDL_shaders_vulkan.c @@ -0,0 +1,60 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + 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 + +#include "SDL_shaders_vulkan.h" + +/* The shaders here were compiled with compile_shaders.bat */ +#include "VULKAN_PixelShader_Colors.h" +#include "VULKAN_PixelShader_Textures.h" +#include "VULKAN_PixelShader_Advanced.h" +#include "VULKAN_VertexShader.h" + +static struct +{ + const void *ps_shader_data; + size_t ps_shader_size; + const void *vs_shader_data; + size_t vs_shader_size; +} VULKAN_shaders[NUM_SHADERS] = { + { VULKAN_PixelShader_Colors, sizeof(VULKAN_PixelShader_Colors), + VULKAN_VertexShader, sizeof(VULKAN_VertexShader) }, + { VULKAN_PixelShader_Textures, sizeof(VULKAN_PixelShader_Textures), + VULKAN_VertexShader, sizeof(VULKAN_VertexShader) }, + { VULKAN_PixelShader_Advanced, sizeof(VULKAN_PixelShader_Advanced), + VULKAN_VertexShader, sizeof(VULKAN_VertexShader) }, +}; + +void VULKAN_GetVertexShader(VULKAN_Shader shader, const uint32_t **outBytecode, size_t *outSize) +{ + *outBytecode = VULKAN_shaders[shader].vs_shader_data; + *outSize = VULKAN_shaders[shader].vs_shader_size; +} + +void VULKAN_GetPixelShader(VULKAN_Shader shader, const uint32_t **outBytecode, size_t *outSize) +{ + *outBytecode = VULKAN_shaders[shader].ps_shader_data; + *outSize = VULKAN_shaders[shader].ps_shader_size; +} + +#endif /* SDL_VIDEO_RENDER_VULKAN */ diff --git a/src/render/vulkan/SDL_shaders_vulkan.h b/src/render/vulkan/SDL_shaders_vulkan.h new file mode 100644 index 000000000..21a6f75af --- /dev/null +++ b/src/render/vulkan/SDL_shaders_vulkan.h @@ -0,0 +1,44 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + 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" + +/* Vulkan shader implementation */ + +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum +{ + SHADER_SOLID, + SHADER_RGB, + SHADER_ADVANCED, + NUM_SHADERS +} VULKAN_Shader; + +extern void VULKAN_GetVertexShader(VULKAN_Shader shader, const uint32_t **outBytecode, size_t *outSize); +extern void VULKAN_GetPixelShader(VULKAN_Shader shader, const uint32_t **outBytecode, size_t *outSize); + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif diff --git a/src/render/vulkan/VULKAN_PixelShader_Advanced.h b/src/render/vulkan/VULKAN_PixelShader_Advanced.h new file mode 100644 index 000000000..9a100f27a --- /dev/null +++ b/src/render/vulkan/VULKAN_PixelShader_Advanced.h @@ -0,0 +1,373 @@ + // 1113.1.1 + #pragma once +const uint32_t VULKAN_PixelShader_Advanced[] = { + 0x07230203,0x00010000,0x0008000b,0x000005e4,0x00000000,0x00020011,0x00000001,0x0006000b, + 0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001, + 0x0008000f,0x00000004,0x00000004,0x6e69616d,0x00000000,0x00000275,0x00000278,0x0000027c, + 0x00030010,0x00000004,0x00000007,0x00030003,0x00000005,0x000001f4,0x00040005,0x00000004, + 0x6e69616d,0x00000000,0x00050005,0x00000075,0x736e6f43,0x746e6174,0x00000073,0x00070006, + 0x00000075,0x00000000,0x47526373,0x756f5f42,0x74757074,0x00000000,0x00070006,0x00000075, + 0x00000001,0x74786574,0x5f657275,0x65707974,0x00000000,0x00060006,0x00000075,0x00000002, + 0x75706e69,0x79745f74,0x00006570,0x00060006,0x00000075,0x00000003,0x6f6c6f63,0x63735f72, + 0x00656c61,0x00070006,0x00000075,0x00000004,0x656e6f74,0x5f70616d,0x6874656d,0x0000646f, + 0x00070006,0x00000075,0x00000005,0x656e6f74,0x5f70616d,0x74636166,0x0031726f,0x00070006, + 0x00000075,0x00000006,0x656e6f74,0x5f70616d,0x74636166,0x0032726f,0x00070006,0x00000075, + 0x00000007,0x5f726473,0x74696877,0x6f705f65,0x00746e69,0x00050006,0x00000075,0x00000008, + 0x66666f59,0x00746573,0x00050006,0x00000075,0x00000009,0x656f6352,0x00006666,0x00050006, + 0x00000075,0x0000000a,0x656f6347,0x00006666,0x00050006,0x00000075,0x0000000b,0x656f6342, + 0x00006666,0x00030005,0x00000077,0x00000000,0x00050005,0x000000f2,0x74786574,0x30657275, + 0x00000000,0x00050005,0x000000f6,0x706d6173,0x3072656c,0x00000000,0x00050005,0x0000010d, + 0x74786574,0x31657275,0x00000000,0x00050005,0x00000182,0x74786574,0x32657275,0x00000000, + 0x00050005,0x00000275,0x75706e69,0x65742e74,0x00000078,0x00050005,0x00000278,0x75706e69, + 0x6f632e74,0x00726f6c,0x00070005,0x0000027c,0x746e6540,0x6f507972,0x4f746e69,0x75707475, + 0x00000074,0x00050048,0x00000075,0x00000000,0x00000023,0x00000000,0x00050048,0x00000075, + 0x00000001,0x00000023,0x00000004,0x00050048,0x00000075,0x00000002,0x00000023,0x00000008, + 0x00050048,0x00000075,0x00000003,0x00000023,0x0000000c,0x00050048,0x00000075,0x00000004, + 0x00000023,0x00000010,0x00050048,0x00000075,0x00000005,0x00000023,0x00000014,0x00050048, + 0x00000075,0x00000006,0x00000023,0x00000018,0x00050048,0x00000075,0x00000007,0x00000023, + 0x0000001c,0x00050048,0x00000075,0x00000008,0x00000023,0x00000020,0x00050048,0x00000075, + 0x00000009,0x00000023,0x00000030,0x00050048,0x00000075,0x0000000a,0x00000023,0x00000040, + 0x00050048,0x00000075,0x0000000b,0x00000023,0x00000050,0x00030047,0x00000075,0x00000002, + 0x00040047,0x00000077,0x00000022,0x00000000,0x00040047,0x00000077,0x00000021,0x00000004, + 0x00040047,0x000000f2,0x00000022,0x00000000,0x00040047,0x000000f2,0x00000021,0x00000001, + 0x00040047,0x000000f6,0x00000022,0x00000000,0x00040047,0x000000f6,0x00000021,0x00000000, + 0x00040047,0x0000010d,0x00000022,0x00000000,0x00040047,0x0000010d,0x00000021,0x00000002, + 0x00040047,0x00000182,0x00000022,0x00000000,0x00040047,0x00000182,0x00000021,0x00000003, + 0x00040047,0x00000275,0x0000001e,0x00000000,0x00040047,0x00000278,0x0000001e,0x00000001, + 0x00040047,0x0000027c,0x0000001e,0x00000000,0x00020013,0x00000002,0x00030021,0x00000003, + 0x00000002,0x00030016,0x00000006,0x00000020,0x00040017,0x0000000f,0x00000006,0x00000003, + 0x00040017,0x00000018,0x00000006,0x00000004,0x00040017,0x00000019,0x00000006,0x00000002, + 0x0004002b,0x00000006,0x00000032,0x3d25aee6,0x00020014,0x00000033,0x0004002b,0x00000006, + 0x00000038,0x414eb852,0x0004002b,0x00000006,0x0000003c,0x3d6147ae,0x0004002b,0x00000006, + 0x0000003f,0x3f870a3d,0x0004002b,0x00000006,0x00000041,0x4019999a,0x0004002b,0x00000006, + 0x00000047,0x3b4d2e1c,0x0004002b,0x00000006,0x00000050,0x3ed55555,0x0004002b,0x00000006, + 0x0000005a,0x3c4fcdac,0x0006002c,0x0000000f,0x0000005b,0x0000005a,0x0000005a,0x0000005a, + 0x0004002b,0x00000006,0x0000005d,0x3f560000,0x0004002b,0x00000006,0x00000060,0x00000000, + 0x0006002c,0x0000000f,0x00000061,0x00000060,0x00000060,0x00000060,0x0004002b,0x00000006, + 0x00000064,0x4196d000,0x0004002b,0x00000006,0x00000065,0x41958000,0x0004002b,0x00000006, + 0x0000006c,0x461c4000,0x0004002b,0x00000006,0x00000071,0x40c8e06b,0x0006002c,0x0000000f, + 0x00000072,0x00000071,0x00000071,0x00000071,0x000e001e,0x00000075,0x00000006,0x00000006, + 0x00000006,0x00000006,0x00000006,0x00000006,0x00000006,0x00000006,0x00000018,0x00000018, + 0x00000018,0x00000018,0x00040020,0x00000076,0x00000002,0x00000075,0x0004003b,0x00000076, + 0x00000077,0x00000002,0x00040015,0x00000078,0x00000020,0x00000001,0x0004002b,0x00000078, + 0x00000079,0x00000007,0x00040020,0x0000007a,0x00000002,0x00000006,0x0004002b,0x00000078, + 0x00000081,0x00000004,0x0004002b,0x00000006,0x00000084,0x3f800000,0x0004002b,0x00000078, + 0x00000088,0x00000005,0x0004002b,0x00000006,0x00000090,0x40000000,0x0004002b,0x00000078, + 0x00000094,0x00000002,0x00040018,0x0000009b,0x0000000f,0x00000003,0x0004002b,0x00000006, + 0x0000009c,0x3f209d8c,0x0004002b,0x00000006,0x0000009d,0x3ea897c8,0x0004002b,0x00000006, + 0x0000009e,0x3d3168f9,0x0006002c,0x0000000f,0x0000009f,0x0000009c,0x0000009d,0x0000009e, + 0x0004002b,0x00000006,0x000000a0,0x3d8d82ba,0x0004002b,0x00000006,0x000000a1,0x3f6b670a, + 0x0004002b,0x00000006,0x000000a2,0x3c3a27af,0x0006002c,0x0000000f,0x000000a3,0x000000a0, + 0x000000a1,0x000000a2,0x0004002b,0x00000006,0x000000a4,0x3c86466b,0x0004002b,0x00000006, + 0x000000a5,0x3db44029,0x0004002b,0x00000006,0x000000a6,0x3f6545b7,0x0006002c,0x0000000f, + 0x000000a7,0x000000a4,0x000000a5,0x000000a6,0x0006002c,0x0000009b,0x000000a8,0x0000009f, + 0x000000a3,0x000000a7,0x0004002b,0x00000078,0x000000c1,0x00000006,0x0004002b,0x00000006, + 0x000000d1,0x3fd48b22,0x0004002b,0x00000006,0x000000d2,0xbf1670a0,0x0004002b,0x00000006, + 0x000000d3,0xbd952d23,0x0006002c,0x0000000f,0x000000d4,0x000000d1,0x000000d2,0x000000d3, + 0x0004002b,0x00000006,0x000000d5,0xbdff127f,0x0004002b,0x00000006,0x000000d6,0x3f9102b4, + 0x0004002b,0x00000006,0x000000d7,0xbc08c60d,0x0006002c,0x0000000f,0x000000d8,0x000000d5, + 0x000000d6,0x000000d7,0x0004002b,0x00000006,0x000000d9,0xbc94b7b3,0x0004002b,0x00000006, + 0x000000da,0xbdce05cd,0x0004002b,0x00000006,0x000000db,0x3f8f333c,0x0006002c,0x0000000f, + 0x000000dc,0x000000d9,0x000000da,0x000000db,0x0006002c,0x0000009b,0x000000dd,0x000000d4, + 0x000000d8,0x000000dc,0x0004002b,0x00000078,0x000000e2,0x00000001,0x0007002c,0x00000018, + 0x000000e9,0x00000084,0x00000084,0x00000084,0x00000084,0x00090019,0x000000f0,0x00000006, + 0x00000001,0x00000000,0x00000000,0x00000000,0x00000001,0x00000000,0x00040020,0x000000f1, + 0x00000000,0x000000f0,0x0004003b,0x000000f1,0x000000f2,0x00000000,0x0002001a,0x000000f4, + 0x00040020,0x000000f5,0x00000000,0x000000f4,0x0004003b,0x000000f5,0x000000f6,0x00000000, + 0x0003001b,0x000000f8,0x000000f0,0x0004003b,0x000000f1,0x0000010d,0x00000000,0x0004002b, + 0x00000078,0x00000119,0x00000008,0x00040020,0x0000011a,0x00000002,0x00000018,0x0004002b, + 0x00000078,0x00000121,0x00000009,0x0004002b,0x00000078,0x00000128,0x0000000a,0x0004002b, + 0x00000078,0x0000012f,0x0000000b,0x0004002b,0x00000006,0x0000013a,0x40400000,0x0004002b, + 0x00000006,0x0000016d,0x40800000,0x0004003b,0x000000f1,0x00000182,0x00000000,0x0004002b, + 0x00000078,0x000001ae,0x00000003,0x0004002b,0x00000078,0x000001be,0x00000000,0x00040020, + 0x00000270,0x00000001,0x00000018,0x00040020,0x00000274,0x00000001,0x00000019,0x0004003b, + 0x00000274,0x00000275,0x00000001,0x0004003b,0x00000270,0x00000278,0x00000001,0x00040020, + 0x0000027b,0x00000003,0x00000018,0x0004003b,0x0000027b,0x0000027c,0x00000003,0x0006002c, + 0x0000000f,0x0000059a,0x0000005d,0x0000005d,0x0000005d,0x0006002c,0x0000000f,0x0000059b, + 0x00000064,0x00000064,0x00000064,0x0006002c,0x0000000f,0x000005a0,0x00000084,0x00000084, + 0x00000084,0x0004002b,0x00000006,0x000005a6,0x3f72a76f,0x0004002b,0x00000006,0x000005a7, + 0x3d9e8391,0x0007002c,0x00000018,0x000005a9,0x00000084,0x00000060,0x00000060,0x00000084, + 0x0004002b,0x00000006,0x000005aa,0xbd6147ae,0x00030001,0x00000018,0x000005e3,0x00050036, + 0x00000002,0x00000004,0x00000000,0x00000003,0x000200f8,0x00000005,0x0004003d,0x00000019, + 0x00000276,0x00000275,0x0004003d,0x00000018,0x00000279,0x00000278,0x00050041,0x0000007a, + 0x000002f9,0x00000077,0x000000e2,0x0004003d,0x00000006,0x000002fa,0x000002f9,0x000500b4, + 0x00000033,0x000002fb,0x000002fa,0x00000060,0x000300f7,0x000003a6,0x00000000,0x000400fa, + 0x000002fb,0x000002fc,0x000002fd,0x000200f8,0x000002fc,0x000200f9,0x000003a6,0x000200f8, + 0x000002fd,0x00050041,0x0000007a,0x000002fe,0x00000077,0x000000e2,0x0004003d,0x00000006, + 0x000002ff,0x000002fe,0x000500b4,0x00000033,0x00000300,0x000002ff,0x00000084,0x000300f7, + 0x000003a5,0x00000000,0x000400fa,0x00000300,0x00000301,0x00000308,0x000200f8,0x00000301, + 0x0004003d,0x000000f0,0x00000302,0x000000f2,0x0004003d,0x000000f4,0x00000303,0x000000f6, + 0x00050056,0x000000f8,0x00000304,0x00000302,0x00000303,0x00050057,0x00000018,0x00000307, + 0x00000304,0x00000276,0x000200f9,0x000003a5,0x000200f8,0x00000308,0x00050041,0x0000007a, + 0x00000309,0x00000077,0x000000e2,0x0004003d,0x00000006,0x0000030a,0x00000309,0x000500b4, + 0x00000033,0x0000030b,0x0000030a,0x00000090,0x000300f7,0x000003a4,0x00000000,0x000400fa, + 0x0000030b,0x0000030c,0x00000338,0x000200f8,0x0000030c,0x0004003d,0x000000f0,0x0000030d, + 0x000000f2,0x0004003d,0x000000f4,0x0000030e,0x000000f6,0x00050056,0x000000f8,0x0000030f, + 0x0000030d,0x0000030e,0x00050057,0x00000018,0x00000312,0x0000030f,0x00000276,0x00050051, + 0x00000006,0x00000313,0x00000312,0x00000000,0x0004003d,0x000000f0,0x00000315,0x0000010d, + 0x0004003d,0x000000f4,0x00000316,0x000000f6,0x00050056,0x000000f8,0x00000317,0x00000315, + 0x00000316,0x00050057,0x00000018,0x0000031a,0x00000317,0x00000276,0x00050051,0x00000006, + 0x0000031d,0x0000031a,0x00000000,0x00050051,0x00000006,0x0000031f,0x0000031a,0x00000001, + 0x00060050,0x0000000f,0x00000598,0x00000313,0x0000031d,0x0000031f,0x00050041,0x0000011a, + 0x00000320,0x00000077,0x00000119,0x0004003d,0x00000018,0x00000321,0x00000320,0x0008004f, + 0x0000000f,0x00000322,0x00000321,0x00000321,0x00000000,0x00000001,0x00000002,0x00050081, + 0x0000000f,0x00000324,0x00000598,0x00000322,0x00050041,0x0000011a,0x00000326,0x00000077, + 0x00000121,0x0004003d,0x00000018,0x00000327,0x00000326,0x0008004f,0x0000000f,0x00000328, + 0x00000327,0x00000327,0x00000000,0x00000001,0x00000002,0x00050094,0x00000006,0x00000329, + 0x00000324,0x00000328,0x00050041,0x0000011a,0x0000032c,0x00000077,0x00000128,0x0004003d, + 0x00000018,0x0000032d,0x0000032c,0x0008004f,0x0000000f,0x0000032e,0x0000032d,0x0000032d, + 0x00000000,0x00000001,0x00000002,0x00050094,0x00000006,0x0000032f,0x00000324,0x0000032e, + 0x00050041,0x0000011a,0x00000332,0x00000077,0x0000012f,0x0004003d,0x00000018,0x00000333, + 0x00000332,0x0008004f,0x0000000f,0x00000334,0x00000333,0x00000333,0x00000000,0x00000001, + 0x00000002,0x00050094,0x00000006,0x00000335,0x00000324,0x00000334,0x00070050,0x00000018, + 0x00000599,0x00000329,0x0000032f,0x00000335,0x00000084,0x000200f9,0x000003a4,0x000200f8, + 0x00000338,0x00050041,0x0000007a,0x00000339,0x00000077,0x000000e2,0x0004003d,0x00000006, + 0x0000033a,0x00000339,0x000500b4,0x00000033,0x0000033b,0x0000033a,0x0000013a,0x000300f7, + 0x000003a3,0x00000000,0x000400fa,0x0000033b,0x0000033c,0x00000368,0x000200f8,0x0000033c, + 0x0004003d,0x000000f0,0x0000033d,0x000000f2,0x0004003d,0x000000f4,0x0000033e,0x000000f6, + 0x00050056,0x000000f8,0x0000033f,0x0000033d,0x0000033e,0x00050057,0x00000018,0x00000342, + 0x0000033f,0x00000276,0x00050051,0x00000006,0x00000343,0x00000342,0x00000000,0x0004003d, + 0x000000f0,0x00000345,0x0000010d,0x0004003d,0x000000f4,0x00000346,0x000000f6,0x00050056, + 0x000000f8,0x00000347,0x00000345,0x00000346,0x00050057,0x00000018,0x0000034a,0x00000347, + 0x00000276,0x00050051,0x00000006,0x0000034d,0x0000034a,0x00000001,0x00050051,0x00000006, + 0x0000034f,0x0000034a,0x00000000,0x00060050,0x0000000f,0x00000596,0x00000343,0x0000034d, + 0x0000034f,0x00050041,0x0000011a,0x00000350,0x00000077,0x00000119,0x0004003d,0x00000018, + 0x00000351,0x00000350,0x0008004f,0x0000000f,0x00000352,0x00000351,0x00000351,0x00000000, + 0x00000001,0x00000002,0x00050081,0x0000000f,0x00000354,0x00000596,0x00000352,0x00050041, + 0x0000011a,0x00000356,0x00000077,0x00000121,0x0004003d,0x00000018,0x00000357,0x00000356, + 0x0008004f,0x0000000f,0x00000358,0x00000357,0x00000357,0x00000000,0x00000001,0x00000002, + 0x00050094,0x00000006,0x00000359,0x00000354,0x00000358,0x00050041,0x0000011a,0x0000035c, + 0x00000077,0x00000128,0x0004003d,0x00000018,0x0000035d,0x0000035c,0x0008004f,0x0000000f, + 0x0000035e,0x0000035d,0x0000035d,0x00000000,0x00000001,0x00000002,0x00050094,0x00000006, + 0x0000035f,0x00000354,0x0000035e,0x00050041,0x0000011a,0x00000362,0x00000077,0x0000012f, + 0x0004003d,0x00000018,0x00000363,0x00000362,0x0008004f,0x0000000f,0x00000364,0x00000363, + 0x00000363,0x00000000,0x00000001,0x00000002,0x00050094,0x00000006,0x00000365,0x00000354, + 0x00000364,0x00070050,0x00000018,0x00000597,0x00000359,0x0000035f,0x00000365,0x00000084, + 0x000200f9,0x000003a3,0x000200f8,0x00000368,0x00050041,0x0000007a,0x00000369,0x00000077, + 0x000000e2,0x0004003d,0x00000006,0x0000036a,0x00000369,0x000500b4,0x00000033,0x0000036b, + 0x0000036a,0x0000016d,0x000300f7,0x000003a2,0x00000000,0x000400fa,0x0000036b,0x0000036c, + 0x0000039d,0x000200f8,0x0000036c,0x0004003d,0x000000f0,0x0000036d,0x000000f2,0x0004003d, + 0x000000f4,0x0000036e,0x000000f6,0x00050056,0x000000f8,0x0000036f,0x0000036d,0x0000036e, + 0x00050057,0x00000018,0x00000372,0x0000036f,0x00000276,0x00050051,0x00000006,0x00000373, + 0x00000372,0x00000000,0x0004003d,0x000000f0,0x00000375,0x0000010d,0x0004003d,0x000000f4, + 0x00000376,0x000000f6,0x00050056,0x000000f8,0x00000377,0x00000375,0x00000376,0x00050057, + 0x00000018,0x0000037a,0x00000377,0x00000276,0x00050051,0x00000006,0x0000037b,0x0000037a, + 0x00000000,0x0004003d,0x000000f0,0x0000037d,0x00000182,0x0004003d,0x000000f4,0x0000037e, + 0x000000f6,0x00050056,0x000000f8,0x0000037f,0x0000037d,0x0000037e,0x00050057,0x00000018, + 0x00000382,0x0000037f,0x00000276,0x00050051,0x00000006,0x00000383,0x00000382,0x00000000, + 0x00060050,0x0000000f,0x00000594,0x00000373,0x0000037b,0x00000383,0x00050041,0x0000011a, + 0x00000385,0x00000077,0x00000119,0x0004003d,0x00000018,0x00000386,0x00000385,0x0008004f, + 0x0000000f,0x00000387,0x00000386,0x00000386,0x00000000,0x00000001,0x00000002,0x00050081, + 0x0000000f,0x00000389,0x00000594,0x00000387,0x00050041,0x0000011a,0x0000038b,0x00000077, + 0x00000121,0x0004003d,0x00000018,0x0000038c,0x0000038b,0x0008004f,0x0000000f,0x0000038d, + 0x0000038c,0x0000038c,0x00000000,0x00000001,0x00000002,0x00050094,0x00000006,0x0000038e, + 0x00000389,0x0000038d,0x00050041,0x0000011a,0x00000391,0x00000077,0x00000128,0x0004003d, + 0x00000018,0x00000392,0x00000391,0x0008004f,0x0000000f,0x00000393,0x00000392,0x00000392, + 0x00000000,0x00000001,0x00000002,0x00050094,0x00000006,0x00000394,0x00000389,0x00000393, + 0x00050041,0x0000011a,0x00000397,0x00000077,0x0000012f,0x0004003d,0x00000018,0x00000398, + 0x00000397,0x0008004f,0x0000000f,0x00000399,0x00000398,0x00000398,0x00000000,0x00000001, + 0x00000002,0x00050094,0x00000006,0x0000039a,0x00000389,0x00000399,0x00070050,0x00000018, + 0x00000595,0x0000038e,0x00000394,0x0000039a,0x00000084,0x000200f9,0x000003a2,0x000200f8, + 0x0000039d,0x000200f9,0x000003a2,0x000200f8,0x000003a2,0x000700f5,0x00000018,0x000005af, + 0x00000595,0x0000036c,0x000005a9,0x0000039d,0x000200f9,0x000003a3,0x000200f8,0x000003a3, + 0x000700f5,0x00000018,0x000005ae,0x00000597,0x0000033c,0x000005af,0x000003a2,0x000200f9, + 0x000003a4,0x000200f8,0x000003a4,0x000700f5,0x00000018,0x000005ad,0x00000599,0x0000030c, + 0x000005ae,0x000003a3,0x000200f9,0x000003a5,0x000200f8,0x000003a5,0x000700f5,0x00000018, + 0x000005ac,0x00000307,0x00000301,0x000005ad,0x000003a4,0x000200f9,0x000003a6,0x000200f8, + 0x000003a6,0x000700f5,0x00000018,0x000005ab,0x000000e9,0x000002fc,0x000005ac,0x000003a5, + 0x00050041,0x0000007a,0x00000292,0x00000077,0x00000094,0x0004003d,0x00000006,0x00000293, + 0x00000292,0x000500b4,0x00000033,0x00000294,0x00000293,0x0000013a,0x000300f7,0x0000029f, + 0x00000000,0x000400fa,0x00000294,0x00000295,0x0000029f,0x000200f8,0x00000295,0x0008004f, + 0x0000000f,0x00000297,0x000005ab,0x000005ab,0x00000000,0x00000001,0x00000002,0x0006000c, + 0x0000000f,0x000003ad,0x00000001,0x00000004,0x00000297,0x0007000c,0x0000000f,0x000003ae, + 0x00000001,0x0000001a,0x000003ad,0x0000005b,0x00050083,0x0000000f,0x000003b0,0x000003ae, + 0x0000059a,0x0007000c,0x0000000f,0x000003b1,0x00000001,0x00000028,0x000003b0,0x00000061, + 0x0006000c,0x0000000f,0x000003b3,0x00000001,0x00000004,0x00000297,0x0007000c,0x0000000f, + 0x000003b4,0x00000001,0x0000001a,0x000003b3,0x0000005b,0x0005008e,0x0000000f,0x000003b5, + 0x000003b4,0x00000065,0x00050083,0x0000000f,0x000003b7,0x0000059b,0x000003b5,0x00050088, + 0x0000000f,0x000003ba,0x000003b1,0x000003b7,0x0006000c,0x0000000f,0x000003bb,0x00000001, + 0x00000004,0x000003ba,0x0007000c,0x0000000f,0x000003bc,0x00000001,0x0000001a,0x000003bb, + 0x00000072,0x0005008e,0x0000000f,0x000003bd,0x000003bc,0x0000006c,0x00050041,0x0000007a, + 0x000003be,0x00000077,0x00000079,0x0004003d,0x00000006,0x000003bf,0x000003be,0x00060050, + 0x0000000f,0x000003c0,0x000003bf,0x000003bf,0x000003bf,0x00050088,0x0000000f,0x000003c1, + 0x000003bd,0x000003c0,0x00050051,0x00000006,0x0000029a,0x000003c1,0x00000000,0x00060052, + 0x00000018,0x00000540,0x0000029a,0x000005ab,0x00000000,0x00050051,0x00000006,0x0000029c, + 0x000003c1,0x00000001,0x00060052,0x00000018,0x00000542,0x0000029c,0x00000540,0x00000001, + 0x00050051,0x00000006,0x0000029e,0x000003c1,0x00000002,0x00060052,0x00000018,0x00000544, + 0x0000029e,0x00000542,0x00000002,0x000200f9,0x0000029f,0x000200f8,0x0000029f,0x000700f5, + 0x00000018,0x000005b0,0x000005ab,0x000003a6,0x00000544,0x00000295,0x00050041,0x0000007a, + 0x000002a0,0x00000077,0x00000081,0x0004003d,0x00000006,0x000002a1,0x000002a0,0x000500b7, + 0x00000033,0x000002a2,0x000002a1,0x00000060,0x000300f7,0x000002ad,0x00000000,0x000400fa, + 0x000002a2,0x000002a3,0x000002ad,0x000200f8,0x000002a3,0x0008004f,0x0000000f,0x000002a5, + 0x000005b0,0x000005b0,0x00000000,0x00000001,0x00000002,0x00050041,0x0000007a,0x000003c6, + 0x00000077,0x00000081,0x0004003d,0x00000006,0x000003c7,0x000003c6,0x000500b4,0x00000033, + 0x000003c8,0x000003c7,0x00000084,0x000300f7,0x000003fc,0x00000000,0x000400fa,0x000003c8, + 0x000003c9,0x000003ce,0x000200f8,0x000003c9,0x00050041,0x0000007a,0x000003ca,0x00000077, + 0x00000088,0x0004003d,0x00000006,0x000003cb,0x000003ca,0x0005008e,0x0000000f,0x000003cd, + 0x000002a5,0x000003cb,0x000200f9,0x000003fc,0x000200f8,0x000003ce,0x00050041,0x0000007a, + 0x000003cf,0x00000077,0x00000081,0x0004003d,0x00000006,0x000003d0,0x000003cf,0x000500b4, + 0x00000033,0x000003d1,0x000003d0,0x00000090,0x000300f7,0x000003fb,0x00000000,0x000400fa, + 0x000003d1,0x000003d2,0x000003fb,0x000200f8,0x000003d2,0x00050041,0x0000007a,0x000003d3, + 0x00000077,0x00000094,0x0004003d,0x00000006,0x000003d4,0x000003d3,0x000500b4,0x00000033, + 0x000003d5,0x000003d4,0x00000090,0x000300f7,0x000003d9,0x00000000,0x000400fa,0x000003d5, + 0x000003d6,0x000003d9,0x000200f8,0x000003d6,0x00050090,0x0000000f,0x000003d8,0x000002a5, + 0x000000a8,0x000200f9,0x000003d9,0x000200f8,0x000003d9,0x000700f5,0x0000000f,0x000005b1, + 0x000002a5,0x000003d2,0x000003d8,0x000003d6,0x00050051,0x00000006,0x000003db,0x000005b1, + 0x00000000,0x00050051,0x00000006,0x000003dd,0x000005b1,0x00000001,0x00050051,0x00000006, + 0x000003df,0x000005b1,0x00000002,0x0007000c,0x00000006,0x000003e0,0x00000001,0x00000028, + 0x000003dd,0x000003df,0x0007000c,0x00000006,0x000003e1,0x00000001,0x00000028,0x000003db, + 0x000003e0,0x000500ba,0x00000033,0x000003e3,0x000003e1,0x00000060,0x000300f7,0x000003f3, + 0x00000000,0x000400fa,0x000003e3,0x000003e4,0x000003f3,0x000200f8,0x000003e4,0x00050041, + 0x0000007a,0x000003e5,0x00000077,0x00000088,0x0004003d,0x00000006,0x000003e6,0x000003e5, + 0x0008000c,0x00000006,0x000003e9,0x00000001,0x00000032,0x000003e6,0x000003e1,0x00000084, + 0x00050041,0x0000007a,0x000003ea,0x00000077,0x000000c1,0x0004003d,0x00000006,0x000003eb, + 0x000003ea,0x0008000c,0x00000006,0x000003ee,0x00000001,0x00000032,0x000003eb,0x000003e1, + 0x00000084,0x00050088,0x00000006,0x000003ef,0x000003e9,0x000003ee,0x0005008e,0x0000000f, + 0x000003f2,0x000005b1,0x000003ef,0x000200f9,0x000003f3,0x000200f8,0x000003f3,0x000700f5, + 0x0000000f,0x000005b2,0x000005b1,0x000003d9,0x000003f2,0x000003e4,0x00050041,0x0000007a, + 0x000003f4,0x00000077,0x00000094,0x0004003d,0x00000006,0x000003f5,0x000003f4,0x000500b4, + 0x00000033,0x000003f6,0x000003f5,0x00000090,0x000300f7,0x000003fa,0x00000000,0x000400fa, + 0x000003f6,0x000003f7,0x000003fa,0x000200f8,0x000003f7,0x00050090,0x0000000f,0x000003f9, + 0x000005b2,0x000000dd,0x000200f9,0x000003fa,0x000200f8,0x000003fa,0x000700f5,0x0000000f, + 0x000005b5,0x000005b2,0x000003f3,0x000003f9,0x000003f7,0x000200f9,0x000003fb,0x000200f8, + 0x000003fb,0x000700f5,0x0000000f,0x000005b4,0x000002a5,0x000003ce,0x000005b5,0x000003fa, + 0x000200f9,0x000003fc,0x000200f8,0x000003fc,0x000700f5,0x0000000f,0x000005b3,0x000003cd, + 0x000003c9,0x000005b4,0x000003fb,0x00050051,0x00000006,0x000002a8,0x000005b3,0x00000000, + 0x00060052,0x00000018,0x00000549,0x000002a8,0x000005b0,0x00000000,0x00050051,0x00000006, + 0x000002aa,0x000005b3,0x00000001,0x00060052,0x00000018,0x0000054b,0x000002aa,0x00000549, + 0x00000001,0x00050051,0x00000006,0x000002ac,0x000005b3,0x00000002,0x00060052,0x00000018, + 0x0000054d,0x000002ac,0x0000054b,0x00000002,0x000200f9,0x000002ad,0x000200f8,0x000002ad, + 0x000700f5,0x00000018,0x000005bb,0x000005b0,0x0000029f,0x0000054d,0x000003fc,0x00050041, + 0x0000007a,0x000002ae,0x00000077,0x00000094,0x0004003d,0x00000006,0x000002af,0x000002ae, + 0x000500b4,0x00000033,0x000002b0,0x000002af,0x00000084,0x000300f7,0x000002ee,0x00000000, + 0x000400fa,0x000002b0,0x000002b1,0x000002be,0x000200f8,0x000002b1,0x0008004f,0x0000000f, + 0x000002b3,0x000005bb,0x000005bb,0x00000000,0x00000001,0x00000002,0x00050041,0x0000007a, + 0x00000404,0x00000077,0x000001be,0x0004003d,0x00000006,0x00000405,0x00000404,0x000500b7, + 0x00000033,0x00000406,0x00000405,0x00000060,0x000300f7,0x00000414,0x00000000,0x000400fa, + 0x00000406,0x00000407,0x00000414,0x000200f8,0x00000407,0x00050051,0x00000006,0x00000409, + 0x000005bb,0x00000000,0x000500bc,0x00000033,0x0000041d,0x00000409,0x00000032,0x000300f7, + 0x00000427,0x00000000,0x000400fa,0x0000041d,0x0000041e,0x00000421,0x000200f8,0x0000041e, + 0x00050085,0x00000006,0x00000420,0x00000409,0x000005a7,0x000200f9,0x00000427,0x000200f8, + 0x00000421,0x00050081,0x00000006,0x00000423,0x00000409,0x0000003c,0x0006000c,0x00000006, + 0x00000424,0x00000001,0x00000004,0x00000423,0x00050085,0x00000006,0x00000425,0x00000424, + 0x000005a6,0x0007000c,0x00000006,0x00000426,0x00000001,0x0000001a,0x00000425,0x00000041, + 0x000200f9,0x00000427,0x000200f8,0x00000427,0x000700f5,0x00000006,0x000005d2,0x00000420, + 0x0000041e,0x00000426,0x00000421,0x00050051,0x00000006,0x0000040d,0x000005bb,0x00000001, + 0x000500bc,0x00000033,0x0000042c,0x0000040d,0x00000032,0x000300f7,0x00000436,0x00000000, + 0x000400fa,0x0000042c,0x0000042d,0x00000430,0x000200f8,0x0000042d,0x00050085,0x00000006, + 0x0000042f,0x0000040d,0x000005a7,0x000200f9,0x00000436,0x000200f8,0x00000430,0x00050081, + 0x00000006,0x00000432,0x0000040d,0x0000003c,0x0006000c,0x00000006,0x00000433,0x00000001, + 0x00000004,0x00000432,0x00050085,0x00000006,0x00000434,0x00000433,0x000005a6,0x0007000c, + 0x00000006,0x00000435,0x00000001,0x0000001a,0x00000434,0x00000041,0x000200f9,0x00000436, + 0x000200f8,0x00000436,0x000700f5,0x00000006,0x000005d4,0x0000042f,0x0000042d,0x00000435, + 0x00000430,0x00050051,0x00000006,0x00000411,0x000005bb,0x00000002,0x000500bc,0x00000033, + 0x0000043b,0x00000411,0x00000032,0x000300f7,0x00000445,0x00000000,0x000400fa,0x0000043b, + 0x0000043c,0x0000043f,0x000200f8,0x0000043c,0x00050085,0x00000006,0x0000043e,0x00000411, + 0x000005a7,0x000200f9,0x00000445,0x000200f8,0x0000043f,0x00050081,0x00000006,0x00000441, + 0x00000411,0x0000003c,0x0006000c,0x00000006,0x00000442,0x00000001,0x00000004,0x00000441, + 0x00050085,0x00000006,0x00000443,0x00000442,0x000005a6,0x0007000c,0x00000006,0x00000444, + 0x00000001,0x0000001a,0x00000443,0x00000041,0x000200f9,0x00000445,0x000200f8,0x00000445, + 0x000700f5,0x00000006,0x000005d6,0x0000043e,0x0000043c,0x00000444,0x0000043f,0x00060050, + 0x0000000f,0x000005e2,0x000005d2,0x000005d4,0x000005d6,0x000200f9,0x00000414,0x000200f8, + 0x00000414,0x000700f5,0x0000000f,0x000005d8,0x000002b3,0x000002b1,0x000005e2,0x00000445, + 0x00050041,0x0000007a,0x00000416,0x00000077,0x000001ae,0x0004003d,0x00000006,0x00000417, + 0x00000416,0x0005008e,0x0000000f,0x00000418,0x000005d8,0x00000417,0x00050051,0x00000006, + 0x000002b6,0x00000418,0x00000000,0x00050051,0x00000006,0x000002b8,0x00000418,0x00000001, + 0x00050051,0x00000006,0x000002ba,0x00000418,0x00000002,0x00050051,0x00000006,0x000002bc, + 0x000005bb,0x00000003,0x00070050,0x00000018,0x000005a8,0x000002b6,0x000002b8,0x000002ba, + 0x000002bc,0x000200f9,0x000002ee,0x000200f8,0x000002be,0x00050041,0x0000007a,0x000002bf, + 0x00000077,0x00000094,0x0004003d,0x00000006,0x000002c0,0x000002bf,0x000500b4,0x00000033, + 0x000002c1,0x000002c0,0x00000090,0x000300f7,0x000002ed,0x00000000,0x000400fa,0x000002c1, + 0x000002c2,0x000002cf,0x000200f8,0x000002c2,0x0008004f,0x0000000f,0x000002c4,0x000005bb, + 0x000005bb,0x00000000,0x00000001,0x00000002,0x00050041,0x0000007a,0x0000044e,0x00000077, + 0x000001ae,0x0004003d,0x00000006,0x0000044f,0x0000044e,0x0005008e,0x0000000f,0x00000450, + 0x000002c4,0x0000044f,0x00050041,0x0000007a,0x00000451,0x00000077,0x000001be,0x0004003d, + 0x00000006,0x00000452,0x00000451,0x000500b7,0x00000033,0x00000453,0x00000452,0x00000060, + 0x000400a8,0x00000033,0x00000454,0x00000453,0x000300f7,0x00000466,0x00000000,0x000400fa, + 0x00000454,0x00000455,0x00000466,0x000200f8,0x00000455,0x00050051,0x00000006,0x00000457, + 0x00000450,0x00000000,0x000500bc,0x00000033,0x0000046b,0x00000457,0x00000047,0x000300f7, + 0x00000475,0x00000000,0x000400fa,0x0000046b,0x0000046c,0x0000046f,0x000200f8,0x0000046c, + 0x00050085,0x00000006,0x0000046e,0x00000457,0x00000038,0x000200f9,0x00000475,0x000200f8, + 0x0000046f,0x0006000c,0x00000006,0x00000471,0x00000001,0x00000004,0x00000457,0x0007000c, + 0x00000006,0x00000472,0x00000001,0x0000001a,0x00000471,0x00000050,0x0008000c,0x00000006, + 0x00000474,0x00000001,0x00000032,0x00000472,0x0000003f,0x000005aa,0x000200f9,0x00000475, + 0x000200f8,0x00000475,0x000700f5,0x00000006,0x000005c7,0x0000046e,0x0000046c,0x00000474, + 0x0000046f,0x00050051,0x00000006,0x0000045b,0x00000450,0x00000001,0x000500bc,0x00000033, + 0x0000047a,0x0000045b,0x00000047,0x000300f7,0x00000484,0x00000000,0x000400fa,0x0000047a, + 0x0000047b,0x0000047e,0x000200f8,0x0000047b,0x00050085,0x00000006,0x0000047d,0x0000045b, + 0x00000038,0x000200f9,0x00000484,0x000200f8,0x0000047e,0x0006000c,0x00000006,0x00000480, + 0x00000001,0x00000004,0x0000045b,0x0007000c,0x00000006,0x00000481,0x00000001,0x0000001a, + 0x00000480,0x00000050,0x0008000c,0x00000006,0x00000483,0x00000001,0x00000032,0x00000481, + 0x0000003f,0x000005aa,0x000200f9,0x00000484,0x000200f8,0x00000484,0x000700f5,0x00000006, + 0x000005c9,0x0000047d,0x0000047b,0x00000483,0x0000047e,0x00050051,0x00000006,0x0000045f, + 0x00000450,0x00000002,0x000500bc,0x00000033,0x00000489,0x0000045f,0x00000047,0x000300f7, + 0x00000493,0x00000000,0x000400fa,0x00000489,0x0000048a,0x0000048d,0x000200f8,0x0000048a, + 0x00050085,0x00000006,0x0000048c,0x0000045f,0x00000038,0x000200f9,0x00000493,0x000200f8, + 0x0000048d,0x0006000c,0x00000006,0x0000048f,0x00000001,0x00000004,0x0000045f,0x0007000c, + 0x00000006,0x00000490,0x00000001,0x0000001a,0x0000048f,0x00000050,0x0008000c,0x00000006, + 0x00000492,0x00000001,0x00000032,0x00000490,0x0000003f,0x000005aa,0x000200f9,0x00000493, + 0x000200f8,0x00000493,0x000700f5,0x00000006,0x000005cb,0x0000048c,0x0000048a,0x00000492, + 0x0000048d,0x00060050,0x0000000f,0x000005e1,0x000005c7,0x000005c9,0x000005cb,0x0008000c, + 0x0000000f,0x00000465,0x00000001,0x0000002b,0x000005e1,0x00000061,0x000005a0,0x000200f9, + 0x00000466,0x000200f8,0x00000466,0x000700f5,0x0000000f,0x000005cd,0x00000450,0x000002c2, + 0x00000465,0x00000493,0x00050051,0x00000006,0x000002c7,0x000005cd,0x00000000,0x00050051, + 0x00000006,0x000002c9,0x000005cd,0x00000001,0x00050051,0x00000006,0x000002cb,0x000005cd, + 0x00000002,0x00050051,0x00000006,0x000002cd,0x000005bb,0x00000003,0x00070050,0x00000018, + 0x000005a5,0x000002c7,0x000002c9,0x000002cb,0x000002cd,0x000200f9,0x000002ed,0x000200f8, + 0x000002cf,0x00050041,0x0000007a,0x000002d0,0x00000077,0x00000094,0x0004003d,0x00000006, + 0x000002d1,0x000002d0,0x000500b4,0x00000033,0x000002d2,0x000002d1,0x0000013a,0x000300f7, + 0x000002ec,0x00000000,0x000400fa,0x000002d2,0x000002d3,0x000002e9,0x000200f8,0x000002d3, + 0x0008004f,0x0000000f,0x000002d5,0x000005bb,0x000005bb,0x00000000,0x00000001,0x00000002, + 0x00050090,0x0000000f,0x000002d6,0x000002d5,0x000000dd,0x00050051,0x00000006,0x000002d8, + 0x000002d6,0x00000000,0x00060052,0x00000018,0x00000573,0x000002d8,0x000005e3,0x00000000, + 0x00050051,0x00000006,0x000002da,0x000002d6,0x00000001,0x00060052,0x00000018,0x00000575, + 0x000002da,0x00000573,0x00000001,0x00050051,0x00000006,0x000002dc,0x000002d6,0x00000002, + 0x00060052,0x00000018,0x00000577,0x000002dc,0x00000575,0x00000002,0x0008004f,0x0000000f, + 0x000002de,0x00000577,0x00000577,0x00000000,0x00000001,0x00000002,0x00050041,0x0000007a, + 0x0000049c,0x00000077,0x000001ae,0x0004003d,0x00000006,0x0000049d,0x0000049c,0x0005008e, + 0x0000000f,0x0000049e,0x000002de,0x0000049d,0x00050041,0x0000007a,0x0000049f,0x00000077, + 0x000001be,0x0004003d,0x00000006,0x000004a0,0x0000049f,0x000500b7,0x00000033,0x000004a1, + 0x000004a0,0x00000060,0x000400a8,0x00000033,0x000004a2,0x000004a1,0x000300f7,0x000004b4, + 0x00000000,0x000400fa,0x000004a2,0x000004a3,0x000004b4,0x000200f8,0x000004a3,0x00050051, + 0x00000006,0x000004a5,0x0000049e,0x00000000,0x000500bc,0x00000033,0x000004b9,0x000004a5, + 0x00000047,0x000300f7,0x000004c3,0x00000000,0x000400fa,0x000004b9,0x000004ba,0x000004bd, + 0x000200f8,0x000004ba,0x00050085,0x00000006,0x000004bc,0x000004a5,0x00000038,0x000200f9, + 0x000004c3,0x000200f8,0x000004bd,0x0006000c,0x00000006,0x000004bf,0x00000001,0x00000004, + 0x000004a5,0x0007000c,0x00000006,0x000004c0,0x00000001,0x0000001a,0x000004bf,0x00000050, + 0x0008000c,0x00000006,0x000004c2,0x00000001,0x00000032,0x000004c0,0x0000003f,0x000005aa, + 0x000200f9,0x000004c3,0x000200f8,0x000004c3,0x000700f5,0x00000006,0x000005bc,0x000004bc, + 0x000004ba,0x000004c2,0x000004bd,0x00050051,0x00000006,0x000004a9,0x0000049e,0x00000001, + 0x000500bc,0x00000033,0x000004c8,0x000004a9,0x00000047,0x000300f7,0x000004d2,0x00000000, + 0x000400fa,0x000004c8,0x000004c9,0x000004cc,0x000200f8,0x000004c9,0x00050085,0x00000006, + 0x000004cb,0x000004a9,0x00000038,0x000200f9,0x000004d2,0x000200f8,0x000004cc,0x0006000c, + 0x00000006,0x000004ce,0x00000001,0x00000004,0x000004a9,0x0007000c,0x00000006,0x000004cf, + 0x00000001,0x0000001a,0x000004ce,0x00000050,0x0008000c,0x00000006,0x000004d1,0x00000001, + 0x00000032,0x000004cf,0x0000003f,0x000005aa,0x000200f9,0x000004d2,0x000200f8,0x000004d2, + 0x000700f5,0x00000006,0x000005be,0x000004cb,0x000004c9,0x000004d1,0x000004cc,0x00050051, + 0x00000006,0x000004ad,0x0000049e,0x00000002,0x000500bc,0x00000033,0x000004d7,0x000004ad, + 0x00000047,0x000300f7,0x000004e1,0x00000000,0x000400fa,0x000004d7,0x000004d8,0x000004db, + 0x000200f8,0x000004d8,0x00050085,0x00000006,0x000004da,0x000004ad,0x00000038,0x000200f9, + 0x000004e1,0x000200f8,0x000004db,0x0006000c,0x00000006,0x000004dd,0x00000001,0x00000004, + 0x000004ad,0x0007000c,0x00000006,0x000004de,0x00000001,0x0000001a,0x000004dd,0x00000050, + 0x0008000c,0x00000006,0x000004e0,0x00000001,0x00000032,0x000004de,0x0000003f,0x000005aa, + 0x000200f9,0x000004e1,0x000200f8,0x000004e1,0x000700f5,0x00000006,0x000005c0,0x000004da, + 0x000004d8,0x000004e0,0x000004db,0x00060050,0x0000000f,0x000005e0,0x000005bc,0x000005be, + 0x000005c0,0x0008000c,0x0000000f,0x000004b3,0x00000001,0x0000002b,0x000005e0,0x00000061, + 0x000005a0,0x000200f9,0x000004b4,0x000200f8,0x000004b4,0x000700f5,0x0000000f,0x000005c2, + 0x0000049e,0x000002d3,0x000004b3,0x000004e1,0x00050051,0x00000006,0x000002e1,0x000005c2, + 0x00000000,0x00050051,0x00000006,0x000002e3,0x000005c2,0x00000001,0x00050051,0x00000006, + 0x000002e5,0x000005c2,0x00000002,0x00050051,0x00000006,0x000002e7,0x000005bb,0x00000003, + 0x00070050,0x00000018,0x000005a1,0x000002e1,0x000002e3,0x000002e5,0x000002e7,0x000200f9, + 0x000002ec,0x000200f8,0x000002e9,0x0008004f,0x0000000f,0x000004e7,0x000005bb,0x000005bb, + 0x00000000,0x00000001,0x00000002,0x00050041,0x0000007a,0x000004e8,0x00000077,0x000001ae, + 0x0004003d,0x00000006,0x000004e9,0x000004e8,0x0005008e,0x0000000f,0x000004ea,0x000004e7, + 0x000004e9,0x00050051,0x00000006,0x000004ec,0x000004ea,0x00000000,0x00050051,0x00000006, + 0x000004ee,0x000004ea,0x00000001,0x00050051,0x00000006,0x000004f0,0x000004ea,0x00000002, + 0x00050051,0x00000006,0x000004f2,0x000005bb,0x00000003,0x00070050,0x00000018,0x0000059c, + 0x000004ec,0x000004ee,0x000004f0,0x000004f2,0x000200f9,0x000002ec,0x000200f8,0x000002ec, + 0x000700f5,0x00000018,0x000005df,0x000005a1,0x000004b4,0x0000059c,0x000002e9,0x000200f9, + 0x000002ed,0x000200f8,0x000002ed,0x000700f5,0x00000018,0x000005de,0x000005a5,0x00000466, + 0x000005df,0x000002ec,0x000200f9,0x000002ee,0x000200f8,0x000002ee,0x000700f5,0x00000018, + 0x000005dd,0x000005a8,0x00000414,0x000005de,0x000002ed,0x00050085,0x00000018,0x000002f2, + 0x000005dd,0x00000279,0x0003003e,0x0000027c,0x000002f2,0x000100fd,0x00010038 +}; diff --git a/src/render/vulkan/VULKAN_PixelShader_Advanced.hlsl b/src/render/vulkan/VULKAN_PixelShader_Advanced.hlsl new file mode 100644 index 000000000..6269d0b5b --- /dev/null +++ b/src/render/vulkan/VULKAN_PixelShader_Advanced.hlsl @@ -0,0 +1,6 @@ +#include "VULKAN_PixelShader_Common.incl" + +float4 main(PixelShaderInput input) : SV_TARGET +{ + return AdvancedPixelShader(input); +} diff --git a/src/render/vulkan/VULKAN_PixelShader_Colors.h b/src/render/vulkan/VULKAN_PixelShader_Colors.h new file mode 100644 index 000000000..fbf1fab6f --- /dev/null +++ b/src/render/vulkan/VULKAN_PixelShader_Colors.h @@ -0,0 +1,47 @@ + // 1113.1.1 + #pragma once +const uint32_t VULKAN_PixelShader_Colors[] = { + 0x07230203,0x00010000,0x0008000b,0x000000a7,0x00000000,0x00020011,0x00000001,0x0006000b, + 0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001, + 0x0007000f,0x00000004,0x00000004,0x6e69616d,0x00000000,0x00000049,0x0000004d,0x00030010, + 0x00000004,0x00000007,0x00030003,0x00000005,0x000001f4,0x00040005,0x00000004,0x6e69616d, + 0x00000000,0x00050005,0x00000018,0x736e6f43,0x746e6174,0x00000073,0x00070006,0x00000018, + 0x00000000,0x47526373,0x756f5f42,0x74757074,0x00000000,0x00070006,0x00000018,0x00000001, + 0x74786574,0x5f657275,0x65707974,0x00000000,0x00060006,0x00000018,0x00000002,0x75706e69, + 0x79745f74,0x00006570,0x00060006,0x00000018,0x00000003,0x6f6c6f63,0x63735f72,0x00656c61, + 0x00070006,0x00000018,0x00000004,0x656e6f74,0x5f70616d,0x6874656d,0x0000646f,0x00070006, + 0x00000018,0x00000005,0x656e6f74,0x5f70616d,0x74636166,0x0031726f,0x00070006,0x00000018, + 0x00000006,0x656e6f74,0x5f70616d,0x74636166,0x0032726f,0x00070006,0x00000018,0x00000007, + 0x5f726473,0x74696877,0x6f705f65,0x00746e69,0x00050006,0x00000018,0x00000008,0x66666f59, + 0x00746573,0x00050006,0x00000018,0x00000009,0x656f6352,0x00006666,0x00050006,0x00000018, + 0x0000000a,0x656f6347,0x00006666,0x00050006,0x00000018,0x0000000b,0x656f6342,0x00006666, + 0x00030005,0x0000001a,0x00000000,0x00050005,0x00000049,0x75706e69,0x6f632e74,0x00726f6c, + 0x00070005,0x0000004d,0x746e6540,0x6f507972,0x4f746e69,0x75707475,0x00000074,0x00050048, + 0x00000018,0x00000000,0x00000023,0x00000000,0x00050048,0x00000018,0x00000001,0x00000023, + 0x00000004,0x00050048,0x00000018,0x00000002,0x00000023,0x00000008,0x00050048,0x00000018, + 0x00000003,0x00000023,0x0000000c,0x00050048,0x00000018,0x00000004,0x00000023,0x00000010, + 0x00050048,0x00000018,0x00000005,0x00000023,0x00000014,0x00050048,0x00000018,0x00000006, + 0x00000023,0x00000018,0x00050048,0x00000018,0x00000007,0x00000023,0x0000001c,0x00050048, + 0x00000018,0x00000008,0x00000023,0x00000020,0x00050048,0x00000018,0x00000009,0x00000023, + 0x00000030,0x00050048,0x00000018,0x0000000a,0x00000023,0x00000040,0x00050048,0x00000018, + 0x0000000b,0x00000023,0x00000050,0x00030047,0x00000018,0x00000002,0x00040047,0x0000001a, + 0x00000022,0x00000000,0x00040047,0x0000001a,0x00000021,0x00000004,0x00040047,0x00000049, + 0x0000001e,0x00000001,0x00040047,0x0000004d,0x0000001e,0x00000000,0x00020013,0x00000002, + 0x00030021,0x00000003,0x00000002,0x00030016,0x00000006,0x00000020,0x00040017,0x00000007, + 0x00000006,0x00000004,0x00040017,0x00000015,0x00000006,0x00000003,0x000e001e,0x00000018, + 0x00000006,0x00000006,0x00000006,0x00000006,0x00000006,0x00000006,0x00000006,0x00000006, + 0x00000007,0x00000007,0x00000007,0x00000007,0x00040020,0x00000019,0x00000002,0x00000018, + 0x0004003b,0x00000019,0x0000001a,0x00000002,0x00040015,0x0000001b,0x00000020,0x00000001, + 0x0004002b,0x0000001b,0x0000001c,0x00000003,0x00040020,0x0000001d,0x00000002,0x00000006, + 0x0004002b,0x00000006,0x00000033,0x3f800000,0x00040020,0x0000003f,0x00000001,0x00000007, + 0x0004003b,0x0000003f,0x00000049,0x00000001,0x00040020,0x0000004c,0x00000003,0x00000007, + 0x0004003b,0x0000004c,0x0000004d,0x00000003,0x0006002c,0x00000015,0x000000a5,0x00000033, + 0x00000033,0x00000033,0x00050036,0x00000002,0x00000004,0x00000000,0x00000003,0x000200f8, + 0x00000005,0x0004003d,0x00000007,0x0000004a,0x00000049,0x00050041,0x0000001d,0x00000084, + 0x0000001a,0x0000001c,0x0004003d,0x00000006,0x00000085,0x00000084,0x0005008e,0x00000015, + 0x00000086,0x000000a5,0x00000085,0x00050051,0x00000006,0x00000088,0x00000086,0x00000000, + 0x00050051,0x00000006,0x0000008a,0x00000086,0x00000001,0x00050051,0x00000006,0x0000008c, + 0x00000086,0x00000002,0x00070050,0x00000007,0x000000a6,0x00000088,0x0000008a,0x0000008c, + 0x00000033,0x00050085,0x00000007,0x0000007e,0x000000a6,0x0000004a,0x0003003e,0x0000004d, + 0x0000007e,0x000100fd,0x00010038 +}; diff --git a/src/render/vulkan/VULKAN_PixelShader_Colors.hlsl b/src/render/vulkan/VULKAN_PixelShader_Colors.hlsl new file mode 100644 index 000000000..e84234859 --- /dev/null +++ b/src/render/vulkan/VULKAN_PixelShader_Colors.hlsl @@ -0,0 +1,7 @@ + +#include "VULKAN_PixelShader_Common.incl" + +float4 main(PixelShaderInput input) : SV_TARGET0 +{ + return GetOutputColor(1.0) * input.color; +} diff --git a/src/render/vulkan/VULKAN_PixelShader_Common.incl b/src/render/vulkan/VULKAN_PixelShader_Common.incl new file mode 100644 index 000000000..21afb87a2 --- /dev/null +++ b/src/render/vulkan/VULKAN_PixelShader_Common.incl @@ -0,0 +1,235 @@ + +SamplerState sampler0 : register(s0); +Texture2D texture0 : register(t1); +Texture2D texture1 : register(t2); +Texture2D texture2 : register(t3); + +struct PixelShaderInput +{ + float4 pos : SV_POSITION; + float2 tex : TEXCOORD0; + float4 color : COLOR0; +}; + +// These should mirror the definitions in SDL_render_d3d12.c +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; + +cbuffer Constants : register(b4) +{ + 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; + + float4 Yoffset; + float4 Rcoeff; + float4 Gcoeff; + float4 Bcoeff; +}; + +static const float3x3 mat709to2020 = { + { 0.627404, 0.329283, 0.043313 }, + { 0.069097, 0.919541, 0.011362 }, + { 0.016391, 0.088013, 0.895595 } +}; + +static const float3x3 mat2020to709 = { + { 1.660496, -0.587656, -0.072840 }, + { -0.124547, 1.132895, -0.008348 }, + { -0.018154, -0.100597, 1.118751 } +}; + +float sRGBtoLinear(float v) +{ + if (v <= 0.04045) { + v = (v / 12.92); + } else { + v = pow(abs(v + 0.055) / 1.055, 2.4); + } + return v; +} + +float sRGBfromLinear(float v) +{ + if (v <= 0.0031308) { + v = (v * 12.92); + } else { + v = (pow(abs(v), 1.0 / 2.4) * 1.055 - 0.055); + } + return v; +} + +float3 PQtoLinear(float3 v) +{ + const float c1 = 0.8359375; + const float c2 = 18.8515625; + const float c3 = 18.6875; + const float oo_m1 = 1.0 / 0.1593017578125; + const float oo_m2 = 1.0 / 78.84375; + + float3 num = max(pow(abs(v), oo_m2) - c1, 0.0); + float3 den = c2 - c3 * pow(abs(v), oo_m2); + return (10000.0 * pow(abs(num / den), oo_m1) / sdr_white_point); +} + +float3 ApplyTonemap(float3 v) +{ + if (tonemap_method == TONEMAP_LINEAR) { + v *= tonemap_factor1; + } else if (tonemap_method == TONEMAP_CHROME) { + if (input_type == INPUTTYPE_SCRGB) { + // Convert to BT.2020 colorspace for tone mapping + v = mul(mat709to2020, v); + } + + float vmax = max(v.r, max(v.g, v.b)); + if (vmax > 0.0) { + float scale = (1.0 + tonemap_factor1 * vmax) / (1.0 + tonemap_factor2 * vmax); + v *= scale; + } + + if (input_type == INPUTTYPE_SCRGB) { + // Convert to BT.709 colorspace after tone mapping + v = mul(mat2020to709, v); + } + } + return v; +} + +float4 GetInputColor(PixelShaderInput input) +{ + float4 rgba; + + if (texture_type == TEXTURETYPE_NONE) { + rgba = 1.0; + } else if (texture_type == TEXTURETYPE_RGB) { + rgba = texture0.Sample(sampler0, input.tex); + } else if (texture_type == TEXTURETYPE_NV12) { + float3 yuv; + yuv.x = texture0.Sample(sampler0, input.tex).r; + yuv.yz = texture1.Sample(sampler0, input.tex).rg; + + yuv += Yoffset.xyz; + rgba.r = dot(yuv, Rcoeff.xyz); + rgba.g = dot(yuv, Gcoeff.xyz); + rgba.b = dot(yuv, Bcoeff.xyz); + rgba.a = 1.0; + } else if (texture_type == TEXTURETYPE_NV21) { + float3 yuv; + yuv.x = texture0.Sample(sampler0, input.tex).r; + yuv.yz = texture1.Sample(sampler0, input.tex).gr; + + yuv += Yoffset.xyz; + rgba.r = dot(yuv, Rcoeff.xyz); + rgba.g = dot(yuv, Gcoeff.xyz); + rgba.b = dot(yuv, Bcoeff.xyz); + rgba.a = 1.0; + } else if (texture_type == TEXTURETYPE_YUV) { + float3 yuv; + yuv.x = texture0.Sample(sampler0, input.tex).r; + yuv.y = texture1.Sample(sampler0, input.tex).r; + yuv.z = texture2.Sample(sampler0, input.tex).r; + + yuv += Yoffset.xyz; + rgba.r = dot(yuv, Rcoeff.xyz); + rgba.g = dot(yuv, Gcoeff.xyz); + rgba.b = dot(yuv, Bcoeff.xyz); + rgba.a = 1.0; + } else { + // Error! + rgba.r = 1.0; + rgba.g = 0.0; + rgba.b = 0.0; + rgba.a = 1.0; + } + return rgba; +} + +float4 GetOutputColor(float4 rgba) +{ + float4 output; + + output.rgb = rgba.rgb * color_scale; + output.a = rgba.a; + + return output; +} + +float3 GetOutputColorFromSRGB(float3 rgb) +{ + float3 output; + + if (scRGB_output) { + rgb.r = sRGBtoLinear(rgb.r); + rgb.g = sRGBtoLinear(rgb.g); + rgb.b = sRGBtoLinear(rgb.b); + } + + output.rgb = rgb * color_scale; + + return output; +} + +float3 GetOutputColorFromLinear(float3 rgb) +{ + float3 output; + + output.rgb = rgb * color_scale; + + if (!scRGB_output) { + output.r = sRGBfromLinear(output.r); + output.g = sRGBfromLinear(output.g); + output.b = sRGBfromLinear(output.b); + output.rgb = saturate(output.rgb); + } + + return output; +} + +float4 AdvancedPixelShader(PixelShaderInput input) +{ + float4 rgba = GetInputColor(input); + float4 output; + + if (input_type == INPUTTYPE_HDR10) { + rgba.rgb = PQtoLinear(rgba.rgb); + } + + if (tonemap_method != TONEMAP_NONE) { + rgba.rgb = ApplyTonemap(rgba.rgb); + } + + if (input_type == INPUTTYPE_SRGB) { + output.rgb = GetOutputColorFromSRGB(rgba.rgb); + output.a = rgba.a; + } else if (input_type == INPUTTYPE_SCRGB) { + output.rgb = GetOutputColorFromLinear(rgba.rgb); + output.a = rgba.a; + } else if (input_type == INPUTTYPE_HDR10) { + rgba.rgb = mul(mat2020to709, rgba.rgb); + output.rgb = GetOutputColorFromLinear(rgba.rgb); + output.a = rgba.a; + } else { + output = GetOutputColor(rgba); + } + + return output * input.color; +} diff --git a/src/render/vulkan/VULKAN_PixelShader_Textures.h b/src/render/vulkan/VULKAN_PixelShader_Textures.h new file mode 100644 index 000000000..33ae15141 --- /dev/null +++ b/src/render/vulkan/VULKAN_PixelShader_Textures.h @@ -0,0 +1,60 @@ + // 1113.1.1 + #pragma once +const uint32_t VULKAN_PixelShader_Textures[] = { + 0x07230203,0x00010000,0x0008000b,0x000000b3,0x00000000,0x00020011,0x00000001,0x0006000b, + 0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001, + 0x0008000f,0x00000004,0x00000004,0x6e69616d,0x00000000,0x00000051,0x00000054,0x00000058, + 0x00030010,0x00000004,0x00000007,0x00030003,0x00000005,0x000001f4,0x00040005,0x00000004, + 0x6e69616d,0x00000000,0x00050005,0x00000018,0x736e6f43,0x746e6174,0x00000073,0x00070006, + 0x00000018,0x00000000,0x47526373,0x756f5f42,0x74757074,0x00000000,0x00070006,0x00000018, + 0x00000001,0x74786574,0x5f657275,0x65707974,0x00000000,0x00060006,0x00000018,0x00000002, + 0x75706e69,0x79745f74,0x00006570,0x00060006,0x00000018,0x00000003,0x6f6c6f63,0x63735f72, + 0x00656c61,0x00070006,0x00000018,0x00000004,0x656e6f74,0x5f70616d,0x6874656d,0x0000646f, + 0x00070006,0x00000018,0x00000005,0x656e6f74,0x5f70616d,0x74636166,0x0031726f,0x00070006, + 0x00000018,0x00000006,0x656e6f74,0x5f70616d,0x74636166,0x0032726f,0x00070006,0x00000018, + 0x00000007,0x5f726473,0x74696877,0x6f705f65,0x00746e69,0x00050006,0x00000018,0x00000008, + 0x66666f59,0x00746573,0x00050006,0x00000018,0x00000009,0x656f6352,0x00006666,0x00050006, + 0x00000018,0x0000000a,0x656f6347,0x00006666,0x00050006,0x00000018,0x0000000b,0x656f6342, + 0x00006666,0x00030005,0x0000001a,0x00000000,0x00050005,0x00000035,0x74786574,0x30657275, + 0x00000000,0x00050005,0x00000039,0x706d6173,0x3072656c,0x00000000,0x00050005,0x00000051, + 0x75706e69,0x65742e74,0x00000078,0x00050005,0x00000054,0x75706e69,0x6f632e74,0x00726f6c, + 0x00070005,0x00000058,0x746e6540,0x6f507972,0x4f746e69,0x75707475,0x00000074,0x00050048, + 0x00000018,0x00000000,0x00000023,0x00000000,0x00050048,0x00000018,0x00000001,0x00000023, + 0x00000004,0x00050048,0x00000018,0x00000002,0x00000023,0x00000008,0x00050048,0x00000018, + 0x00000003,0x00000023,0x0000000c,0x00050048,0x00000018,0x00000004,0x00000023,0x00000010, + 0x00050048,0x00000018,0x00000005,0x00000023,0x00000014,0x00050048,0x00000018,0x00000006, + 0x00000023,0x00000018,0x00050048,0x00000018,0x00000007,0x00000023,0x0000001c,0x00050048, + 0x00000018,0x00000008,0x00000023,0x00000020,0x00050048,0x00000018,0x00000009,0x00000023, + 0x00000030,0x00050048,0x00000018,0x0000000a,0x00000023,0x00000040,0x00050048,0x00000018, + 0x0000000b,0x00000023,0x00000050,0x00030047,0x00000018,0x00000002,0x00040047,0x0000001a, + 0x00000022,0x00000000,0x00040047,0x0000001a,0x00000021,0x00000004,0x00040047,0x00000035, + 0x00000022,0x00000000,0x00040047,0x00000035,0x00000021,0x00000001,0x00040047,0x00000039, + 0x00000022,0x00000000,0x00040047,0x00000039,0x00000021,0x00000000,0x00040047,0x00000051, + 0x0000001e,0x00000000,0x00040047,0x00000054,0x0000001e,0x00000001,0x00040047,0x00000058, + 0x0000001e,0x00000000,0x00020013,0x00000002,0x00030021,0x00000003,0x00000002,0x00030016, + 0x00000006,0x00000020,0x00040017,0x00000007,0x00000006,0x00000004,0x00040017,0x0000000d, + 0x00000006,0x00000002,0x00040017,0x00000015,0x00000006,0x00000003,0x000e001e,0x00000018, + 0x00000006,0x00000006,0x00000006,0x00000006,0x00000006,0x00000006,0x00000006,0x00000006, + 0x00000007,0x00000007,0x00000007,0x00000007,0x00040020,0x00000019,0x00000002,0x00000018, + 0x0004003b,0x00000019,0x0000001a,0x00000002,0x00040015,0x0000001b,0x00000020,0x00000001, + 0x0004002b,0x0000001b,0x0000001c,0x00000003,0x00040020,0x0000001d,0x00000002,0x00000006, + 0x00090019,0x00000033,0x00000006,0x00000001,0x00000000,0x00000000,0x00000000,0x00000001, + 0x00000000,0x00040020,0x00000034,0x00000000,0x00000033,0x0004003b,0x00000034,0x00000035, + 0x00000000,0x0002001a,0x00000037,0x00040020,0x00000038,0x00000000,0x00000037,0x0004003b, + 0x00000038,0x00000039,0x00000000,0x0003001b,0x0000003b,0x00000033,0x00040020,0x0000004c, + 0x00000001,0x00000007,0x00040020,0x00000050,0x00000001,0x0000000d,0x0004003b,0x00000050, + 0x00000051,0x00000001,0x0004003b,0x0000004c,0x00000054,0x00000001,0x00040020,0x00000057, + 0x00000003,0x00000007,0x0004003b,0x00000057,0x00000058,0x00000003,0x00050036,0x00000002, + 0x00000004,0x00000000,0x00000003,0x000200f8,0x00000005,0x0004003d,0x0000000d,0x00000052, + 0x00000051,0x0004003d,0x00000007,0x00000055,0x00000054,0x0004003d,0x00000033,0x00000081, + 0x00000035,0x0004003d,0x00000037,0x00000082,0x00000039,0x00050056,0x0000003b,0x00000083, + 0x00000081,0x00000082,0x00050057,0x00000007,0x00000086,0x00000083,0x00000052,0x0008004f, + 0x00000015,0x0000008f,0x00000086,0x00000086,0x00000000,0x00000001,0x00000002,0x00050041, + 0x0000001d,0x00000090,0x0000001a,0x0000001c,0x0004003d,0x00000006,0x00000091,0x00000090, + 0x0005008e,0x00000015,0x00000092,0x0000008f,0x00000091,0x00050051,0x00000006,0x00000094, + 0x00000092,0x00000000,0x00050051,0x00000006,0x00000096,0x00000092,0x00000001,0x00050051, + 0x00000006,0x00000098,0x00000092,0x00000002,0x00050051,0x00000006,0x0000009a,0x00000086, + 0x00000003,0x00070050,0x00000007,0x000000b2,0x00000094,0x00000096,0x00000098,0x0000009a, + 0x00050085,0x00000007,0x0000008a,0x000000b2,0x00000055,0x0003003e,0x00000058,0x0000008a, + 0x000100fd,0x00010038 +}; diff --git a/src/render/vulkan/VULKAN_PixelShader_Textures.hlsl b/src/render/vulkan/VULKAN_PixelShader_Textures.hlsl new file mode 100644 index 000000000..c48aee7c0 --- /dev/null +++ b/src/render/vulkan/VULKAN_PixelShader_Textures.hlsl @@ -0,0 +1,6 @@ +#include "VULKAN_PixelShader_Common.incl" + +float4 main(PixelShaderInput input) : SV_TARGET +{ + return GetOutputColor(texture0.Sample(sampler0, input.tex)) * input.color; +} diff --git a/src/render/vulkan/VULKAN_VertexShader.h b/src/render/vulkan/VULKAN_VertexShader.h new file mode 100644 index 000000000..b6c55d5a1 --- /dev/null +++ b/src/render/vulkan/VULKAN_VertexShader.h @@ -0,0 +1,48 @@ + // 1113.1.1 + #pragma once +const uint32_t VULKAN_VertexShader[] = { + 0x07230203,0x00010000,0x0008000b,0x000000a1,0x00000000,0x00020011,0x00000001,0x0006000b, + 0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001, + 0x000b000f,0x00000000,0x00000004,0x6e69616d,0x00000000,0x0000003c,0x00000040,0x00000044, + 0x00000056,0x00000059,0x0000005c,0x00030003,0x00000005,0x000001f4,0x00040005,0x00000004, + 0x6e69616d,0x00000000,0x00060005,0x0000001e,0x68737570,0x736e6f43,0x746e6174,0x00000073, + 0x00050006,0x0000001e,0x00000000,0x65646f6d,0x0000006c,0x00080006,0x0000001e,0x00000001, + 0x6a6f7270,0x69746365,0x6e416e6f,0x65695664,0x00000077,0x00060005,0x00000020,0x68737570, + 0x736e6f43,0x746e6174,0x00000073,0x00050005,0x0000003c,0x75706e69,0x6f702e74,0x00000073, + 0x00050005,0x00000040,0x75706e69,0x65742e74,0x00000078,0x00050005,0x00000044,0x75706e69, + 0x6f632e74,0x00726f6c,0x00080005,0x00000056,0x746e6540,0x6f507972,0x4f746e69,0x75707475, + 0x6f702e74,0x00000073,0x00080005,0x00000059,0x746e6540,0x6f507972,0x4f746e69,0x75707475, + 0x65742e74,0x00000078,0x00080005,0x0000005c,0x746e6540,0x6f507972,0x4f746e69,0x75707475, + 0x6f632e74,0x00726f6c,0x00040048,0x0000001e,0x00000000,0x00000005,0x00050048,0x0000001e, + 0x00000000,0x00000023,0x00000000,0x00050048,0x0000001e,0x00000000,0x00000007,0x00000010, + 0x00040048,0x0000001e,0x00000001,0x00000005,0x00050048,0x0000001e,0x00000001,0x00000023, + 0x00000040,0x00050048,0x0000001e,0x00000001,0x00000007,0x00000010,0x00030047,0x0000001e, + 0x00000002,0x00040047,0x0000003c,0x0000001e,0x00000000,0x00040047,0x00000040,0x0000001e, + 0x00000001,0x00040047,0x00000044,0x0000001e,0x00000002,0x00040047,0x00000056,0x0000000b, + 0x00000000,0x00040047,0x00000059,0x0000001e,0x00000000,0x00040047,0x0000005c,0x0000001e, + 0x00000001,0x00020013,0x00000002,0x00030021,0x00000003,0x00000002,0x00030016,0x00000006, + 0x00000020,0x00040017,0x00000007,0x00000006,0x00000003,0x00040017,0x00000008,0x00000006, + 0x00000002,0x00040017,0x00000009,0x00000006,0x00000004,0x00040015,0x00000013,0x00000020, + 0x00000001,0x0004002b,0x00000013,0x00000014,0x00000000,0x0004002b,0x00000006,0x00000018, + 0x3f800000,0x00040018,0x0000001d,0x00000009,0x00000004,0x0004001e,0x0000001e,0x0000001d, + 0x0000001d,0x00040020,0x0000001f,0x00000009,0x0000001e,0x0004003b,0x0000001f,0x00000020, + 0x00000009,0x00040020,0x00000021,0x00000009,0x0000001d,0x0004002b,0x00000013,0x00000026, + 0x00000001,0x00040020,0x0000003b,0x00000001,0x00000007,0x0004003b,0x0000003b,0x0000003c, + 0x00000001,0x00040020,0x0000003f,0x00000001,0x00000008,0x0004003b,0x0000003f,0x00000040, + 0x00000001,0x00040020,0x00000043,0x00000001,0x00000009,0x0004003b,0x00000043,0x00000044, + 0x00000001,0x00040020,0x00000055,0x00000003,0x00000009,0x0004003b,0x00000055,0x00000056, + 0x00000003,0x00040020,0x00000058,0x00000003,0x00000008,0x0004003b,0x00000058,0x00000059, + 0x00000003,0x0004003b,0x00000055,0x0000005c,0x00000003,0x00050036,0x00000002,0x00000004, + 0x00000000,0x00000003,0x000200f8,0x00000005,0x0004003d,0x00000007,0x0000003d,0x0000003c, + 0x0004003d,0x00000008,0x00000041,0x00000040,0x0004003d,0x00000009,0x00000045,0x00000044, + 0x00050051,0x00000006,0x00000065,0x0000003d,0x00000000,0x00050051,0x00000006,0x00000066, + 0x0000003d,0x00000001,0x00050051,0x00000006,0x00000067,0x0000003d,0x00000002,0x00070050, + 0x00000009,0x00000068,0x00000065,0x00000066,0x00000067,0x00000018,0x00050041,0x00000021, + 0x00000069,0x00000020,0x00000014,0x0004003d,0x0000001d,0x0000006a,0x00000069,0x00050091, + 0x00000009,0x0000006c,0x0000006a,0x00000068,0x00050041,0x00000021,0x0000006d,0x00000020, + 0x00000026,0x0004003d,0x0000001d,0x0000006e,0x0000006d,0x00050091,0x00000009,0x00000070, + 0x0000006e,0x0000006c,0x00050051,0x00000006,0x00000052,0x00000070,0x00000001,0x0004007f, + 0x00000006,0x00000053,0x00000052,0x00060052,0x00000009,0x000000a0,0x00000053,0x00000070, + 0x00000001,0x0003003e,0x00000056,0x000000a0,0x0003003e,0x00000059,0x00000041,0x0003003e, + 0x0000005c,0x00000045,0x000100fd,0x00010038 +}; diff --git a/src/render/vulkan/VULKAN_VertexShader.hlsl b/src/render/vulkan/VULKAN_VertexShader.hlsl new file mode 100644 index 000000000..b7d5e4630 --- /dev/null +++ b/src/render/vulkan/VULKAN_VertexShader.hlsl @@ -0,0 +1,41 @@ +#pragma pack_matrix( row_major ) + +struct VertexShaderConstants +{ + matrix model; + matrix projectionAndView; +}; +[[vk::push_constant]] +ConstantBuffer pushConstants; + +struct VertexShaderInput +{ + float3 pos : POSITION; + float2 tex : TEXCOORD0; + float4 color : COLOR0; +}; + +struct VertexShaderOutput +{ + float4 pos : SV_POSITION; + float2 tex : TEXCOORD0; + float4 color : COLOR0; +}; + +VertexShaderOutput mainColor(VertexShaderInput input) +{ + VertexShaderOutput output; + float4 pos = float4(input.pos, 1.0f); + + // Transform the vertex position into projected space. + pos = mul(pos, pushConstants.model); + pos = mul(pos, pushConstants.projectionAndView); + output.pos = pos; + + // Pass through texture coordinates and color values without transformation + output.tex = input.tex; + output.color = input.color; + + return output; +} + diff --git a/src/render/vulkan/compile_shaders.bat b/src/render/vulkan/compile_shaders.bat new file mode 100644 index 000000000..446020a7a --- /dev/null +++ b/src/render/vulkan/compile_shaders.bat @@ -0,0 +1,5 @@ +glslangValidator -D --sep main -e main -S frag --target-env vulkan1.0 --vn VULKAN_PixelShader_Colors -o VULKAN_PixelShader_Colors.h VULKAN_PixelShader_Colors.hlsl +glslangValidator -D --sep main -e main -S frag --target-env vulkan1.0 --vn VULKAN_PixelShader_Textures -o VULKAN_PixelShader_Textures.h VULKAN_PixelShader_Textures.hlsl +glslangValidator -D --sep main -e main -S frag --target-env vulkan1.0 --vn VULKAN_PixelShader_Advanced -o VULKAN_PixelShader_Advanced.h VULKAN_PixelShader_Advanced.hlsl + +glslangValidator -D --sep mainColor -e main -S vert --iy --target-env vulkan1.0 --vn VULKAN_VertexShader -o VULKAN_VertexShader.h VULKAN_VertexShader.hlsl