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