#include "SDL_internal.h"
#ifdef 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 SDL_VULKAN_VALIDATION_LAYER_NAME "VK_LAYER_KHRONOS_validation"
#define VK_NO_PROTOTYPES
#include "../../video/SDL_vulkan_internal.h"
#include "../../video/SDL_sysvideo.h"
#include "../SDL_sysrender.h"
#include "../SDL_d3dmath.h"
#include "../../video/SDL_pixels_c.h"
#include "SDL_shaders_vulkan.h"
#define SET_ERROR_CODE(message, rc) \
if (SDL_GetHintBoolean(SDL_HINT_RENDER_VULKAN_DEBUG, false)) { \
SDL_LogError(SDL_LOG_CATEGORY_RENDER, "%s: %s", message, SDL_Vulkan_GetResultString(rc)); \
SDL_TriggerBreakpoint(); \
} \
SDL_SetError("%s: %s", message, SDL_Vulkan_GetResultString(rc)) \
#define SET_ERROR_MESSAGE(message) \
if (SDL_GetHintBoolean(SDL_HINT_RENDER_VULKAN_DEBUG, false)) { \
SDL_LogError(SDL_LOG_CATEGORY_RENDER, "%s", message); \
SDL_TriggerBreakpoint(); \
} \
SDL_SetError("%s", message) \
#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) \
VULKAN_OPTIONAL_INSTANCE_FUNCTION(vkGetPhysicalDeviceFeatures2KHR) \
VULKAN_OPTIONAL_INSTANCE_FUNCTION(vkGetPhysicalDeviceFormatProperties2KHR) \
VULKAN_OPTIONAL_INSTANCE_FUNCTION(vkGetPhysicalDeviceImageFormatProperties2KHR) \
VULKAN_OPTIONAL_INSTANCE_FUNCTION(vkGetPhysicalDeviceMemoryProperties2KHR) \
VULKAN_OPTIONAL_INSTANCE_FUNCTION(vkGetPhysicalDeviceProperties2KHR) \
VULKAN_OPTIONAL_DEVICE_FUNCTION(vkCreateSamplerYcbcrConversionKHR) \
VULKAN_OPTIONAL_DEVICE_FUNCTION(vkDestroySamplerYcbcrConversionKHR) \
#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;
#define VULKAN_OPTIONAL_INSTANCE_FUNCTION(name) static PFN_##name name = NULL;
#define VULKAN_OPTIONAL_DEVICE_FUNCTION(name) static PFN_##name name = NULL;
VULKAN_FUNCTIONS()
#undef VULKAN_DEVICE_FUNCTION
#undef VULKAN_GLOBAL_FUNCTION
#undef VULKAN_INSTANCE_FUNCTION
#undef VULKAN_OPTIONAL_INSTANCE_FUNCTION
#undef VULKAN_OPTIONAL_DEVICE_FUNCTION
typedef enum {
VULKAN_RENDERPASS_LOAD = 0,
VULKAN_RENDERPASS_CLEAR = 1,
VULKAN_RENDERPASS_COUNT
} VULKAN_RenderPass;
typedef struct
{
Float4X4 model;
Float4X4 projectionAndView;
} VULKAN_VertexShaderConstants;
static const float TONEMAP_NONE = 0;
static const float TONEMAP_CHROME = 2;
static const float TEXTURETYPE_RGB = 1;
static const float TEXTURETYPE_RGB_PIXELART = 2;
static const float TEXTURETYPE_PALETTE_NEAREST = 3;
static const float TEXTURETYPE_PALETTE_LINEAR = 4;
static const float TEXTURETYPE_PALETTE_PIXELART = 5;
static const float INPUTTYPE_UNSPECIFIED = 0;
static const float INPUTTYPE_SRGB = 1;
static const float INPUTTYPE_SCRGB = 2;
static const float INPUTTYPE_HDR10 = 3;
typedef struct
{
float scRGB_output;
float texture_type;
float input_type;
float color_scale;
float texel_width;
float texel_height;
float texture_width;
float texture_height;
float tonemap_method;
float tonemap_factor1;
float tonemap_factor2;
float sdr_white_point;
} VULKAN_PixelShaderConstants;
typedef struct
{
float pos[2];
float tex[2];
SDL_FColor color;
} VULKAN_VertexPositionColor;
typedef struct
{
VkDeviceMemory deviceMemory;
VkBuffer buffer;
VkDeviceSize size;
void *mappedBufferPtr;
} VULKAN_Buffer;
typedef struct
{
bool allocatedImage;
VkImage image;
VkImageView imageView;
VkDeviceMemory deviceMemory;
VkImageLayout imageLayout;
VkFormat format;
} VULKAN_Image;
typedef struct
{
VULKAN_Image image;
} VULKAN_PaletteData;
typedef struct
{
VULKAN_Image mainImage;
VkRenderPass mainRenderpasses[VULKAN_RENDERPASS_COUNT];
VkFramebuffer mainFramebuffer;
VULKAN_Buffer stagingBuffer;
SDL_Rect lockedRect;
int width;
int height;
VkSamplerYcbcrConversion samplerYcbcrConversion;
VkSampler samplerYcbcr;
VkDescriptorSetLayout descriptorSetLayoutYcbcr;
VkPipelineLayout pipelineLayoutYcbcr;
} VULKAN_TextureData;
typedef struct
{
VULKAN_Shader shader;
VULKAN_PixelShaderConstants shader_constants;
SDL_BlendMode blendMode;
VkPrimitiveTopology topology;
VkFormat format;
VkPipelineLayout pipelineLayout;
VkDescriptorSetLayout descriptorSetLayout;
VkPipeline pipeline;
} VULKAN_PipelineState;
typedef struct
{
VkBuffer vertexBuffer;
} VULKAN_DrawStateCache;
typedef struct
{
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
VkInstance instance;
bool instance_external;
VkSurfaceKHR surface;
bool surface_external;
VkPhysicalDevice physicalDevice;
VkPhysicalDeviceProperties physicalDeviceProperties;
VkPhysicalDeviceMemoryProperties physicalDeviceMemoryProperties;
VkPhysicalDeviceFeatures physicalDeviceFeatures;
VkQueue graphicsQueue;
VkQueue presentQueue;
VkDevice device;
bool device_external;
uint32_t graphicsQueueFamilyIndex;
uint32_t presentQueueFamilyIndex;
VkSwapchainKHR swapchain;
VkCommandPool commandPool;
VkCommandBuffer *commandBuffers;
uint32_t currentCommandBufferIndex;
VkCommandBuffer currentCommandBuffer;
VkFence *fences;
VkSurfaceCapabilitiesKHR surfaceCapabilities;
VkSurfaceFormatKHR *surfaceFormats;
bool recreateSwapchain;
int vsync;
SDL_PropertiesID create_props;
VkFramebuffer *framebuffers;
VkRenderPass renderPasses[VULKAN_RENDERPASS_COUNT];
VkRenderPass currentRenderPass;
VkShaderModule vertexShaderModules[NUM_SHADERS];
VkShaderModule fragmentShaderModules[NUM_SHADERS];
VkDescriptorSetLayout descriptorSetLayout;
VkPipelineLayout pipelineLayout;
VULKAN_Buffer vertexBuffers[SDL_VULKAN_NUM_VERTEX_BUFFERS];
VULKAN_VertexShaderConstants vertexShaderConstantsData;
VULKAN_Buffer **uploadBuffers;
int *currentUploadBuffer;
VULKAN_Buffer **constantBuffers;
uint32_t *numConstantBuffers;
uint32_t currentConstantBufferIndex;
int32_t currentConstantBufferOffset;
VkSampler samplers[RENDER_SAMPLER_COUNT];
VkDescriptorPool **descriptorPools;
uint32_t *numDescriptorPools;
uint32_t currentDescriptorPoolIndex;
uint32_t currentDescriptorSetIndex;
int pipelineStateCount;
VULKAN_PipelineState *pipelineStates;
VULKAN_PipelineState *currentPipelineState;
bool supportsEXTSwapchainColorspace;
bool supportsKHRGetPhysicalDeviceProperties2;
bool supportsKHRSamplerYCbCrConversion;
uint32_t surfaceFormatsAllocatedCount;
uint32_t surfaceFormatsCount;
uint32_t swapchainDesiredImageCount;
VkSurfaceFormatKHR surfaceFormat;
VkExtent2D swapchainSize;
VkSurfaceTransformFlagBitsKHR swapChainPreTransform;
uint32_t swapchainImageCount;
VkImage *swapchainImages;
VkImageView *swapchainImageViews;
VkImageLayout *swapchainImageLayouts;
VkSemaphore *imageAvailableSemaphores;
VkSemaphore *renderingFinishedSemaphores;
VkSemaphore currentImageAvailableSemaphore;
uint32_t currentSwapchainImageIndex;
VkPipelineStageFlags *waitDestStageMasks;
VkSemaphore *waitRenderSemaphores;
uint32_t waitRenderSemaphoreCount;
uint32_t waitRenderSemaphoreMax;
VkSemaphore *signalRenderSemaphores;
uint32_t signalRenderSemaphoreCount;
uint32_t signalRenderSemaphoreMax;
VULKAN_TextureData *textureRenderTarget;
bool cliprectDirty;
bool currentCliprectEnabled;
SDL_Rect currentCliprect;
SDL_Rect currentViewport;
int currentViewportRotation;
bool viewportDirty;
Float4X4 identity;
VkComponentMapping identitySwizzle;
int currentVertexBuffer;
bool issueBatch;
} VULKAN_RenderData;
static bool 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);
static SDL_PixelFormat VULKAN_VkFormatToSDLPixelFormat(VkFormat vkFormat)
{
switch (vkFormat) {
case VK_FORMAT_R8G8B8A8_UNORM:
return SDL_PIXELFORMAT_RGBA32;
case VK_FORMAT_B8G8R8A8_UNORM:
return SDL_PIXELFORMAT_BGRA32;
case VK_FORMAT_A8B8G8R8_UNORM_PACK32:
return SDL_PIXELFORMAT_ABGR8888;
case VK_FORMAT_A2R10G10B10_UNORM_PACK32:
return SDL_PIXELFORMAT_ABGR2101010;
case VK_FORMAT_R16G16B16A16_SFLOAT:
return SDL_PIXELFORMAT_RGBA64_FLOAT;
default:
return SDL_PIXELFORMAT_UNKNOWN;
}
}
static int VULKAN_VkFormatGetNumPlanes(VkFormat vkFormat)
{
switch (vkFormat) {
case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
return 3;
case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:
return 2;
default:
return 1;
}
}
static VkDeviceSize VULKAN_GetBytesPerPixel(VkFormat vkFormat, int plane)
{
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_R8G8B8A8_SRGB:
case VK_FORMAT_R8G8B8A8_UNORM:
case VK_FORMAT_B8G8R8A8_SRGB:
case VK_FORMAT_B8G8R8A8_UNORM:
case VK_FORMAT_A8B8G8R8_SRGB_PACK32:
case VK_FORMAT_A8B8G8R8_UNORM_PACK32:
case VK_FORMAT_A2R10G10B10_UNORM_PACK32:
return 4;
case VK_FORMAT_R16G16B16A16_SFLOAT:
return 8;
case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
return 1;
case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
return (plane == 0) ? 1 : 2;
case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:
return (plane == 0) ? 2 : 4;
default:
return 4;
}
}
static VkFormat SDLPixelFormatToVkTextureFormat(Uint32 format, Uint32 output_colorspace)
{
switch (format) {
case SDL_PIXELFORMAT_RGBA64_FLOAT:
return VK_FORMAT_R16G16B16A16_SFLOAT;
case SDL_PIXELFORMAT_ABGR2101010:
return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
case SDL_PIXELFORMAT_RGBA32:
if (output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
return VK_FORMAT_R8G8B8A8_SRGB;
}
return VK_FORMAT_R8G8B8A8_UNORM;
case SDL_PIXELFORMAT_BGRA32:
if (output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
return VK_FORMAT_B8G8R8A8_SRGB;
}
return VK_FORMAT_B8G8R8A8_UNORM;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
case SDL_PIXELFORMAT_ABGR8888:
if (output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
return VK_FORMAT_A8B8G8R8_SRGB_PACK32;
}
return VK_FORMAT_A8B8G8R8_UNORM_PACK32;
#endif
case SDL_PIXELFORMAT_INDEX8:
return VK_FORMAT_R8_UNORM;
case SDL_PIXELFORMAT_YUY2:
return VK_FORMAT_G8B8G8R8_422_UNORM;
case SDL_PIXELFORMAT_UYVY:
return VK_FORMAT_B8G8R8G8_422_UNORM;
case SDL_PIXELFORMAT_YV12:
case SDL_PIXELFORMAT_IYUV:
return VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM;
case SDL_PIXELFORMAT_NV12:
case SDL_PIXELFORMAT_NV21:
return VK_FORMAT_G8_B8R8_2PLANE_420_UNORM;
case SDL_PIXELFORMAT_P010:
return VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16;
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 void VULKAN_EnsureCommandBuffer(VULKAN_RenderData *rendererData);
static void VULKAN_RecordPipelineImageBarrier(VULKAN_RenderData *rendererData, VkAccessFlags sourceAccessMask, VkAccessFlags destAccessMask, VkPipelineStageFlags srcStageFlags, VkPipelineStageFlags dstStageFlags, VkImageLayout destLayout, VkImage image, VkImageLayout *imageLayout);
static bool VULKAN_FindMemoryTypeIndex(VULKAN_RenderData *rendererData, uint32_t typeBits, VkMemoryPropertyFlags requiredFlags, VkMemoryPropertyFlags desiredFlags, uint32_t *memoryTypeIndexOut);
static VkResult VULKAN_CreateWindowSizeDependentResources(SDL_Renderer *renderer);
static VkDescriptorPool VULKAN_AllocateDescriptorPool(VULKAN_RenderData *rendererData);
static VkResult VULKAN_CreateDescriptorSetAndPipelineLayout(VULKAN_RenderData *rendererData, VkSampler samplerYcbcr, VkDescriptorSetLayout *descriptorSetLayoutOut, VkPipelineLayout *pipelineLayoutOut);
static VkSurfaceTransformFlagBitsKHR VULKAN_GetRotationForCurrentRenderTarget(VULKAN_RenderData *rendererData);
static bool VULKAN_IsDisplayRotated90Degrees(VkSurfaceTransformFlagBitsKHR rotation);
static void VULKAN_DestroyAll(SDL_Renderer *renderer)
{
VULKAN_RenderData *rendererData;
if (renderer == NULL) {
return;
}
rendererData = (VULKAN_RenderData *)renderer->internal;
if (rendererData == NULL) {
return;
}
for (SDL_Texture *texture = renderer->textures; texture; texture = texture->next) {
VULKAN_DestroyTexture(renderer, texture);
}
if (rendererData->waitDestStageMasks) {
SDL_free(rendererData->waitDestStageMasks);
rendererData->waitDestStageMasks = NULL;
}
if (rendererData->waitRenderSemaphores) {
SDL_free(rendererData->waitRenderSemaphores);
rendererData->waitRenderSemaphores = NULL;
}
if (rendererData->signalRenderSemaphores) {
SDL_free(rendererData->signalRenderSemaphores);
rendererData->signalRenderSemaphores = NULL;
}
if (rendererData->surfaceFormats != NULL) {
SDL_free(rendererData->surfaceFormats);
rendererData->surfaceFormats = NULL;
rendererData->surfaceFormatsAllocatedCount = 0;
}
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_zeroa(rendererData->vertexBuffers);
for (uint32_t i = 0; i < VULKAN_RENDERPASS_COUNT; i++) {
if (rendererData->renderPasses[i] != VK_NULL_HANDLE) {
vkDestroyRenderPass(rendererData->device, rendererData->renderPasses[i], NULL);
rendererData->renderPasses[i] = VK_NULL_HANDLE;
}
}
if (rendererData->imageAvailableSemaphores) {
for (uint32_t i = 0; i < rendererData->swapchainImageCount; ++i) {
if (rendererData->imageAvailableSemaphores[i] != VK_NULL_HANDLE) {
vkDestroySemaphore(rendererData->device, rendererData->imageAvailableSemaphores[i], NULL);
}
}
SDL_free(rendererData->imageAvailableSemaphores);
rendererData->imageAvailableSemaphores = NULL;
}
if (rendererData->renderingFinishedSemaphores) {
for (uint32_t i = 0; i < rendererData->swapchainImageCount; ++i) {
if (rendererData->renderingFinishedSemaphores[i] != VK_NULL_HANDLE) {
vkDestroySemaphore(rendererData->device, rendererData->renderingFinishedSemaphores[i], NULL);
}
}
SDL_free(rendererData->renderingFinishedSemaphores);
rendererData->renderingFinishedSemaphores = NULL;
}
if (rendererData->commandBuffers) {
vkFreeCommandBuffers(rendererData->device, rendererData->commandPool, rendererData->swapchainImageCount, rendererData->commandBuffers);
SDL_free(rendererData->commandBuffers);
rendererData->commandBuffers = NULL;
rendererData->currentCommandBuffer = VK_NULL_HANDLE;
rendererData->currentCommandBufferIndex = 0;
}
if (rendererData->commandPool) {
vkDestroyCommandPool(rendererData->device, rendererData->commandPool, NULL);
rendererData->commandPool = VK_NULL_HANDLE;
}
if (rendererData->descriptorPools) {
SDL_assert(rendererData->numDescriptorPools);
for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) {
for (uint32_t j = 0; j < rendererData->numDescriptorPools[i]; j++) {
if (rendererData->descriptorPools[i][j] != VK_NULL_HANDLE) {
vkDestroyDescriptorPool(rendererData->device, rendererData->descriptorPools[i][j], NULL);
}
}
SDL_free(rendererData->descriptorPools[i]);
}
SDL_free(rendererData->descriptorPools);
rendererData->descriptorPools = NULL;
SDL_free(rendererData->numDescriptorPools);
rendererData->numDescriptorPools = 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->descriptorSetLayout != VK_NULL_HANDLE) {
vkDestroyDescriptorSetLayout(rendererData->device, rendererData->descriptorSetLayout, NULL);
rendererData->descriptorSetLayout = VK_NULL_HANDLE;
}
if (rendererData->pipelineLayout != VK_NULL_HANDLE) {
vkDestroyPipelineLayout(rendererData->device, rendererData->pipelineLayout, NULL);
rendererData->pipelineLayout = VK_NULL_HANDLE;
}
for (int i = 0; i < rendererData->pipelineStateCount; i++) {
vkDestroyPipeline(rendererData->device, rendererData->pipelineStates[i].pipeline, NULL);
}
SDL_free(rendererData->pipelineStates);
rendererData->pipelineStates = NULL;
rendererData->pipelineStateCount = 0;
if (rendererData->currentUploadBuffer) {
for (uint32_t i = 0; i < rendererData->swapchainImageCount; ++i) {
for (int j = 0; j < rendererData->currentUploadBuffer[i]; ++j) {
VULKAN_DestroyBuffer(rendererData, &rendererData->uploadBuffers[i][j]);
}
SDL_free(rendererData->uploadBuffers[i]);
}
SDL_free(rendererData->uploadBuffers);
rendererData->uploadBuffers = NULL;
SDL_free(rendererData->currentUploadBuffer);
rendererData->currentUploadBuffer = NULL;
}
if (rendererData->constantBuffers) {
SDL_assert(rendererData->numConstantBuffers);
for (uint32_t i = 0; i < rendererData->swapchainImageCount; ++i) {
for (uint32_t j = 0; j < rendererData->numConstantBuffers[i]; j++) {
VULKAN_DestroyBuffer(rendererData, &rendererData->constantBuffers[i][j]);
}
SDL_free(rendererData->constantBuffers[i]);
}
SDL_free(rendererData->constantBuffers);
rendererData->constantBuffers = NULL;
SDL_free(rendererData->numConstantBuffers);
rendererData->numConstantBuffers = NULL;
}
if (rendererData->device != VK_NULL_HANDLE && !rendererData->device_external) {
vkDestroyDevice(rendererData->device, NULL);
rendererData->device = VK_NULL_HANDLE;
}
if (rendererData->surface != VK_NULL_HANDLE && !rendererData->surface_external) {
vkDestroySurfaceKHR(rendererData->instance, rendererData->surface, NULL);
rendererData->surface = VK_NULL_HANDLE;
}
if (rendererData->instance != VK_NULL_HANDLE && !rendererData->instance_external) {
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_zerop(vulkanBuffer);
}
static VkResult VULKAN_AllocateBuffer(VULKAN_RenderData *rendererData, VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags requiredMemoryProps, VkMemoryPropertyFlags desiredMemoryProps, 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) {
SET_ERROR_CODE("vkCreateBuffer()", result);
return result;
}
VkMemoryRequirements memoryRequirements = { 0 };
vkGetBufferMemoryRequirements(rendererData->device, bufferOut->buffer, &memoryRequirements);
if (result != VK_SUCCESS) {
VULKAN_DestroyBuffer(rendererData, bufferOut);
SET_ERROR_CODE("vkGetBufferMemoryRequirements()", result);
return result;
}
uint32_t memoryTypeIndex = 0;
if (!VULKAN_FindMemoryTypeIndex(rendererData, memoryRequirements.memoryTypeBits, requiredMemoryProps, desiredMemoryProps, &memoryTypeIndex)) {
VULKAN_DestroyBuffer(rendererData, bufferOut);
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);
SET_ERROR_CODE("vkAllocateMemory()", result);
return result;
}
result = vkBindBufferMemory(rendererData->device, bufferOut->buffer, bufferOut->deviceMemory, 0);
if (result != VK_SUCCESS) {
VULKAN_DestroyBuffer(rendererData, bufferOut);
SET_ERROR_CODE("vkBindBufferMemory()", result);
return result;
}
result = vkMapMemory(rendererData->device, bufferOut->deviceMemory, 0, size, 0, &bufferOut->mappedBufferPtr);
if (result != VK_SUCCESS) {
VULKAN_DestroyBuffer(rendererData, bufferOut);
SET_ERROR_CODE("vkMapMemory()", 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) {
if (vulkanImage->allocatedImage) {
vkFreeMemory(rendererData->device, vulkanImage->deviceMemory, NULL);
}
vulkanImage->deviceMemory = VK_NULL_HANDLE;
}
SDL_zerop(vulkanImage);
}
static VkResult VULKAN_AllocateImage(VULKAN_RenderData *rendererData, SDL_PropertiesID create_props, uint32_t width, uint32_t height, VkFormat format, VkImageUsageFlags imageUsage, VkComponentMapping swizzle, VkSamplerYcbcrConversionKHR samplerYcbcrConversion, VULKAN_Image *imageOut)
{
VkResult result;
VkSamplerYcbcrConversionInfoKHR samplerYcbcrConversionInfo = { 0 };
SDL_zerop(imageOut);
imageOut->format = format;
imageOut->image = (VkImage)SDL_GetNumberProperty(create_props, SDL_PROP_TEXTURE_CREATE_VULKAN_TEXTURE_NUMBER, 0);
if (imageOut->image == VK_NULL_HANDLE) {
imageOut->allocatedImage = VK_TRUE;
VkImageCreateInfo imageCreateInfo = { 0 };
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;
result = vkCreateImage(rendererData->device, &imageCreateInfo, NULL, &imageOut->image);
if (result != VK_SUCCESS) {
VULKAN_DestroyImage(rendererData, imageOut);
SET_ERROR_CODE("vkCreateImage()", result);
return result;
}
VkMemoryRequirements memoryRequirements = { 0 };
vkGetImageMemoryRequirements(rendererData->device, imageOut->image, &memoryRequirements);
if (result != VK_SUCCESS) {
VULKAN_DestroyImage(rendererData, imageOut);
SET_ERROR_CODE("vkGetImageMemoryRequirements()", result);
return result;
}
uint32_t memoryTypeIndex = 0;
if (!VULKAN_FindMemoryTypeIndex(rendererData, memoryRequirements.memoryTypeBits, 0, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &memoryTypeIndex)) {
VULKAN_DestroyImage(rendererData, imageOut);
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);
SET_ERROR_CODE("vkAllocateMemory()", result);
return result;
}
result = vkBindImageMemory(rendererData->device, imageOut->image, imageOut->deviceMemory, 0);
if (result != VK_SUCCESS) {
VULKAN_DestroyImage(rendererData, imageOut);
SET_ERROR_CODE("vkBindImageMemory()", result);
return result;
}
} else {
imageOut->imageLayout = (VkImageLayout)SDL_GetNumberProperty(create_props, SDL_PROP_TEXTURE_CREATE_VULKAN_LAYOUT_NUMBER, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
}
if (imageOut->imageLayout != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
VULKAN_EnsureCommandBuffer(rendererData);
VULKAN_RecordPipelineImageBarrier(rendererData,
VK_ACCESS_NONE,
VK_ACCESS_SHADER_READ_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
imageOut->image,
&imageOut->imageLayout);
}
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 = 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;
if (samplerYcbcrConversion != VK_NULL_HANDLE) {
samplerYcbcrConversionInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO_KHR;
samplerYcbcrConversionInfo.conversion = samplerYcbcrConversion;
imageViewCreateInfo.pNext = &samplerYcbcrConversionInfo;
}
result = vkCreateImageView(rendererData->device, &imageViewCreateInfo, NULL, &imageOut->imageView);
if (result != VK_SUCCESS) {
VULKAN_DestroyImage(rendererData, imageOut);
SET_ERROR_CODE("vkCreateImageView()", 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)
{
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->internal;
VkResult result;
rendererData->currentImageAvailableSemaphore = VK_NULL_HANDLE;
result = vkAcquireNextImageKHR(rendererData->device, rendererData->swapchain, UINT64_MAX,
rendererData->imageAvailableSemaphores[rendererData->currentCommandBufferIndex], VK_NULL_HANDLE, &rendererData->currentSwapchainImageIndex);
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_ERROR_SURFACE_LOST_KHR) {
if (!(renderer->window->flags & SDL_WINDOW_MINIMIZED)) {
result = VULKAN_CreateWindowSizeDependentResources(renderer);
}
return result;
} else if(result == VK_SUBOPTIMAL_KHR) {
} else if (result != VK_SUCCESS) {
SET_ERROR_CODE("vkAcquireNextImageKHR()", result);
return result;
}
rendererData->currentImageAvailableSemaphore = rendererData->imageAvailableSemaphores[rendererData->currentCommandBufferIndex];
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[VULKAN_RENDERPASS_CLEAR] :
rendererData->renderPasses[VULKAN_RENDERPASS_CLEAR];
break;
case VK_ATTACHMENT_LOAD_OP_LOAD:
default:
rendererData->currentRenderPass = rendererData->textureRenderTarget ?
rendererData->textureRenderTarget->mainRenderpasses[VULKAN_RENDERPASS_LOAD] :
rendererData->renderPasses[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);
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 bool VULKAN_ActivateCommandBuffer(SDL_Renderer *renderer, VkAttachmentLoadOp loadOp, VkClearColorValue *clearColor, VULKAN_DrawStateCache *stateCache)
{
VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
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);
}
if (stateCache->vertexBuffer != VK_NULL_HANDLE) {
VkDeviceSize offset = 0;
vkCmdBindVertexBuffers(rendererData->currentCommandBuffer, 0, 1, &stateCache->vertexBuffer, &offset);
}
return true;
}
static void VULKAN_WaitForGPU(VULKAN_RenderData *rendererData)
{
vkQueueWaitIdle(rendererData->graphicsQueue);
}
static void VULKAN_ResetCommandList(VULKAN_RenderData *rendererData)
{
vkResetCommandBuffer(rendererData->currentCommandBuffer, 0);
for (uint32_t i = 0; i < rendererData->numDescriptorPools[rendererData->currentCommandBufferIndex]; i++) {
vkResetDescriptorPool(rendererData->device, rendererData->descriptorPools[rendererData->currentCommandBufferIndex][i], 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 = false;
rendererData->cliprectDirty = true;
rendererData->currentDescriptorSetIndex = 0;
rendererData->currentDescriptorPoolIndex = 0;
rendererData->currentConstantBufferOffset = -1;
rendererData->currentConstantBufferIndex = 0;
for (int 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 = true;
vkEndCommandBuffer(rendererData->currentCommandBuffer);
VkSubmitInfo submitInfo = { 0 };
VkPipelineStageFlags waitDestStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &rendererData->currentCommandBuffer;
if (rendererData->waitRenderSemaphoreCount > 0) {
Uint32 additionalSemaphoreCount = (rendererData->currentImageAvailableSemaphore != VK_NULL_HANDLE) ? 1 : 0;
submitInfo.waitSemaphoreCount = rendererData->waitRenderSemaphoreCount + additionalSemaphoreCount;
if (additionalSemaphoreCount > 0) {
rendererData->waitRenderSemaphores[rendererData->waitRenderSemaphoreCount] = rendererData->currentImageAvailableSemaphore;
rendererData->waitDestStageMasks[rendererData->waitRenderSemaphoreCount] = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
}
submitInfo.pWaitSemaphores = rendererData->waitRenderSemaphores;
submitInfo.pWaitDstStageMask = rendererData->waitDestStageMasks;
rendererData->waitRenderSemaphoreCount = 0;
} else if (rendererData->currentImageAvailableSemaphore != VK_NULL_HANDLE) {
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = &rendererData->currentImageAvailableSemaphore;
submitInfo.pWaitDstStageMask = &waitDestStageMask;
}
result = vkQueueSubmit(rendererData->graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
rendererData->currentImageAvailableSemaphore = VK_NULL_HANDLE;
VULKAN_WaitForGPU(rendererData);
VULKAN_ResetCommandList(rendererData);
return result;
}
static void VULKAN_DestroyRenderer(SDL_Renderer *renderer)
{
VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
if (rendererData) {
if (rendererData->device != VK_NULL_HANDLE) {
vkDeviceWaitIdle(rendererData->device);
VULKAN_DestroyAll(renderer);
}
SDL_free(rendererData);
}
}
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, VkPipelineLayout pipelineLayout, VkDescriptorSetLayout descriptorSetLayout, SDL_BlendMode blendMode, VkPrimitiveTopology topology, VkFormat format)
{
VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
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;
const char *name = "main";
for (uint32_t i = 0; i < 2; i++) {
SDL_zero(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];
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;
inputAssemblyStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
inputAssemblyStateCreateInfo.topology = topology;
inputAssemblyStateCreateInfo.primitiveRestartEnable = VK_FALSE;
viewportStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportStateCreateInfo.scissorCount = 1;
viewportStateCreateInfo.viewportCount = 1;
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;
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;
multisampleStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
VkSampleMask multiSampleMask = 0xFFFFFFFF;
multisampleStateCreateInfo.pSampleMask = &multiSampleMask;
multisampleStateCreateInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
depthStencilStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
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;
pipelineCreateInfo.renderPass = rendererData->currentRenderPass;
pipelineCreateInfo.subpass = 0;
pipelineCreateInfo.layout = pipelineLayout;
result = vkCreateGraphicsPipelines(rendererData->device, VK_NULL_HANDLE, 1, &pipelineCreateInfo, NULL, &pipeline);
if (result != VK_SUCCESS) {
SET_ERROR_CODE("vkCreateGraphicsPipelines()", result);
return NULL;
}
pipelineStates = (VULKAN_PipelineState *)SDL_realloc(rendererData->pipelineStates, (rendererData->pipelineStateCount + 1) * sizeof(*pipelineStates));
if (!pipelineStates) {
return NULL;
}
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].descriptorSetLayout = descriptorSetLayout;
pipelineStates[rendererData->pipelineStateCount].pipelineLayout = pipelineCreateInfo.layout;
rendererData->pipelineStates = pipelineStates;
++rendererData->pipelineStateCount;
return &pipelineStates[rendererData->pipelineStateCount - 1];
}
static bool VULKAN_FindMemoryTypeIndex(VULKAN_RenderData *rendererData, uint32_t typeBits, VkMemoryPropertyFlags requiredFlags, VkMemoryPropertyFlags desiredFlags, uint32_t *memoryTypeIndexOut)
{
uint32_t memoryTypeIndex = 0;
bool foundExactMatch = false;
desiredFlags |= requiredFlags;
for (memoryTypeIndex = 0; memoryTypeIndex < rendererData->physicalDeviceMemoryProperties.memoryTypeCount; memoryTypeIndex++) {
if (typeBits & (1 << memoryTypeIndex)) {
if (rendererData->physicalDeviceMemoryProperties.memoryTypes[memoryTypeIndex].propertyFlags == desiredFlags) {
foundExactMatch = true;
break;
}
}
}
if (!foundExactMatch) {
for (memoryTypeIndex = 0; memoryTypeIndex < rendererData->physicalDeviceMemoryProperties.memoryTypeCount; memoryTypeIndex++) {
if (typeBits & (1 << memoryTypeIndex)) {
if ((rendererData->physicalDeviceMemoryProperties.memoryTypes[memoryTypeIndex].propertyFlags & requiredFlags) == requiredFlags) {
break;
}
}
}
}
if (memoryTypeIndex >= rendererData->physicalDeviceMemoryProperties.memoryTypeCount) {
SET_ERROR_MESSAGE("Unable to find memory type for allocation");
return false;
}
*memoryTypeIndexOut = memoryTypeIndex;
return 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_HOST_COHERENT_BIT |
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
&rendererData->vertexBuffers[vbidx]);
if (result != VK_SUCCESS) {
return result;
}
return result;
}
static bool 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) { \
SET_ERROR_MESSAGE("vkGetInstanceProcAddr(VK_NULL_HANDLE, \"" #name "\") failed"); \
return false; \
}
#define VULKAN_INSTANCE_FUNCTION(name)
#define VULKAN_OPTIONAL_INSTANCE_FUNCTION(name)
#define VULKAN_OPTIONAL_DEVICE_FUNCTION(name)
VULKAN_FUNCTIONS()
#undef VULKAN_DEVICE_FUNCTION
#undef VULKAN_GLOBAL_FUNCTION
#undef VULKAN_INSTANCE_FUNCTION
#undef VULKAN_OPTIONAL_INSTANCE_FUNCTION
#undef VULKAN_OPTIONAL_DEVICE_FUNCTION
return true;
}
static bool 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) { \
SET_ERROR_MESSAGE("vkGetInstanceProcAddr(instance, \"" #name "\") failed"); \
return false; \
}
#define VULKAN_OPTIONAL_INSTANCE_FUNCTION(name) \
name = (PFN_##name)rendererData->vkGetInstanceProcAddr(rendererData->instance, #name);
#define VULKAN_OPTIONAL_DEVICE_FUNCTION(name)
VULKAN_FUNCTIONS()
#undef VULKAN_DEVICE_FUNCTION
#undef VULKAN_GLOBAL_FUNCTION
#undef VULKAN_INSTANCE_FUNCTION
#undef VULKAN_OPTIONAL_INSTANCE_FUNCTION
#undef VULKAN_OPTIONAL_DEVICE_FUNCTION
return true;
}
static bool VULKAN_LoadDeviceFunctions(VULKAN_RenderData *rendererData)
{
#define VULKAN_DEVICE_FUNCTION(name) \
name = (PFN_##name)vkGetDeviceProcAddr(rendererData->device, #name); \
if (!name) { \
SET_ERROR_MESSAGE("vkGetDeviceProcAddr(device, \"" #name "\") failed"); \
return false; \
}
#define VULKAN_GLOBAL_FUNCTION(name)
#define VULKAN_OPTIONAL_DEVICE_FUNCTION(name) \
name = (PFN_##name)vkGetDeviceProcAddr(rendererData->device, #name);
#define VULKAN_INSTANCE_FUNCTION(name)
#define VULKAN_OPTIONAL_INSTANCE_FUNCTION(name)
VULKAN_FUNCTIONS()
#undef VULKAN_DEVICE_FUNCTION
#undef VULKAN_GLOBAL_FUNCTION
#undef VULKAN_INSTANCE_FUNCTION
#undef VULKAN_OPTIONAL_INSTANCE_FUNCTION
#undef VULKAN_OPTIONAL_DEVICE_FUNCTION
return true;
}
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) {
SET_ERROR_CODE("vkEnumeratePhysicalDevices()", result);
return result;
}
if (physicalDeviceCount == 0) {
SET_ERROR_MESSAGE("vkEnumeratePhysicalDevices(): no physical devices");
return VK_ERROR_UNKNOWN;
}
physicalDevices = (VkPhysicalDevice *)SDL_malloc(sizeof(VkPhysicalDevice) * physicalDeviceCount);
result = vkEnumeratePhysicalDevices(rendererData->instance, &physicalDeviceCount, physicalDevices);
if (result != VK_SUCCESS) {
SDL_free(physicalDevices);
SET_ERROR_CODE("vkEnumeratePhysicalDevices()", result);
return result;
}
rendererData->physicalDevice = NULL;
for (physicalDeviceIndex = 0; physicalDeviceIndex < physicalDeviceCount; physicalDeviceIndex++) {
uint32_t queueFamiliesCount = 0;
uint32_t queueFamilyIndex;
uint32_t deviceExtensionCount = 0;
bool hasSwapchainExtension = 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);
SET_ERROR_CODE("vkGetPhysicalDeviceSurfaceSupportKHR()", result);
return VK_ERROR_UNKNOWN;
}
if (supported) {
rendererData->presentQueueFamilyIndex = queueFamilyIndex;
if (queueFamiliesProperties[queueFamilyIndex].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
break; }
}
}
if (rendererData->graphicsQueueFamilyIndex == queueFamiliesCount) { continue;
}
if (rendererData->presentQueueFamilyIndex == queueFamiliesCount) { continue;
}
result = vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &deviceExtensionCount, NULL);
if (result != VK_SUCCESS) {
SDL_free(physicalDevices);
SDL_free(queueFamiliesProperties);
SDL_free(deviceExtensions);
SET_ERROR_CODE("vkEnumerateDeviceExtensionProperties()", result);
return VK_ERROR_UNKNOWN;
}
if (deviceExtensionCount == 0) {
continue;
}
if (deviceExtensionsAllocatedSize < deviceExtensionCount) {
SDL_free(deviceExtensions);
deviceExtensionsAllocatedSize = deviceExtensionCount;
deviceExtensions = (VkExtensionProperties *)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);
SET_ERROR_CODE("vkEnumerateDeviceExtensionProperties()", result);
return result;
}
for (i = 0; i < deviceExtensionCount; i++) {
if (SDL_strcmp(deviceExtensions[i].extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME) == 0) {
hasSwapchainExtension = true;
break;
}
}
if (!hasSwapchainExtension) {
continue;
}
rendererData->physicalDevice = physicalDevice;
break;
}
SDL_free(physicalDevices);
SDL_free(queueFamiliesProperties);
SDL_free(deviceExtensions);
if (!rendererData->physicalDevice) {
SET_ERROR_MESSAGE("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;
SET_ERROR_CODE("vkGetPhysicalDeviceSurfaceFormatsKHR()", 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;
SET_ERROR_CODE("vkGetPhysicalDeviceSurfaceFormatsKHR()", 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) {
SET_ERROR_CODE("vkCreateSemaphore()", result);
return VK_NULL_HANDLE;
}
return semaphore;
}
static bool VULKAN_DeviceExtensionsFound(VULKAN_RenderData *rendererData, int extensionsToCheck, const char * const *extNames)
{
uint32_t extensionCount;
bool foundExtensions = true;
VkResult result = vkEnumerateDeviceExtensionProperties(rendererData->physicalDevice, NULL, &extensionCount, NULL);
if (result != VK_SUCCESS ) {
SET_ERROR_CODE("vkEnumerateDeviceExtensionProperties()", result);
return false;
}
if (extensionCount > 0 ) {
VkExtensionProperties *extensionProperties = (VkExtensionProperties *)SDL_calloc(extensionCount, sizeof(VkExtensionProperties));
result = vkEnumerateDeviceExtensionProperties(rendererData->physicalDevice, NULL, &extensionCount, extensionProperties);
if (result != VK_SUCCESS ) {
SET_ERROR_CODE("vkEnumerateDeviceExtensionProperties()", result);
SDL_free(extensionProperties);
return false;
}
for (int ext = 0; ext < extensionsToCheck && foundExtensions; ext++) {
bool foundExtension = false;
for (uint32_t i = 0; i < extensionCount; i++) {
if (SDL_strcmp(extensionProperties[i].extensionName, extNames[ext]) == 0) {
foundExtension = true;
break;
}
}
foundExtensions &= foundExtension;
}
SDL_free(extensionProperties);
}
return foundExtensions;
}
static bool VULKAN_InstanceExtensionFound(VULKAN_RenderData *rendererData, const char *extName)
{
uint32_t extensionCount;
VkResult result = vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, NULL);
if (result != VK_SUCCESS ) {
SET_ERROR_CODE("vkEnumerateInstanceExtensionProperties()", result);
return false;
}
if (extensionCount > 0 ) {
VkExtensionProperties *extensionProperties = (VkExtensionProperties *)SDL_calloc(extensionCount, sizeof(VkExtensionProperties));
result = vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, extensionProperties);
if (result != VK_SUCCESS ) {
SET_ERROR_CODE("vkEnumerateInstanceExtensionProperties()", result);
SDL_free(extensionProperties);
return false;
}
for (uint32_t i = 0; i< extensionCount; i++) {
if (SDL_strcmp(extensionProperties[i].extensionName, extName) == 0) {
SDL_free(extensionProperties);
return true;
}
}
SDL_free(extensionProperties);
}
return false;
}
static bool VULKAN_ValidationLayersFound(void)
{
uint32_t instanceLayerCount = 0;
uint32_t i;
bool foundValidation = false;
vkEnumerateInstanceLayerProperties(&instanceLayerCount, NULL);
if (instanceLayerCount > 0) {
VkLayerProperties *instanceLayers = (VkLayerProperties *)SDL_calloc(instanceLayerCount, sizeof(VkLayerProperties));
vkEnumerateInstanceLayerProperties(&instanceLayerCount, instanceLayers);
for (i = 0; i < instanceLayerCount; i++) {
if (!SDL_strcmp(SDL_VULKAN_VALIDATION_LAYER_NAME, instanceLayers[i].layerName)) {
foundValidation = true;
break;
}
}
SDL_free(instanceLayers);
}
return foundValidation;
}
static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer, SDL_PropertiesID create_props)
{
static const char *const deviceExtensionNames[] = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME,
VK_KHR_MAINTENANCE1_EXTENSION_NAME,
VK_KHR_BIND_MEMORY_2_EXTENSION_NAME,
VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME,
};
VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
VkResult result = VK_SUCCESS;
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL;
bool createDebug = SDL_GetHintBoolean(SDL_HINT_RENDER_VULKAN_DEBUG, false);
const char *validationLayerName[] = { SDL_VULKAN_VALIDATION_LAYER_NAME };
if (!SDL_Vulkan_LoadLibrary(NULL)) {
SDL_LogDebug(SDL_LOG_CATEGORY_RENDER, "SDL_Vulkan_LoadLibrary failed" );
return VK_ERROR_UNKNOWN;
}
vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_Vulkan_GetVkGetInstanceProcAddr();
if(!vkGetInstanceProcAddr) {
SDL_LogDebug(SDL_LOG_CATEGORY_RENDER, "vkGetInstanceProcAddr is NULL" );
return VK_ERROR_UNKNOWN;
}
rendererData->vkGetInstanceProcAddr = vkGetInstanceProcAddr;
if (!VULKAN_LoadGlobalFunctions(rendererData)) {
return VK_ERROR_UNKNOWN;
}
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_SetError("Using HDR output but %s not supported", VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME);
return VK_ERROR_UNKNOWN;
}
}
rendererData->supportsKHRGetPhysicalDeviceProperties2 = VULKAN_InstanceExtensionFound(rendererData, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
rendererData->instance = (VkInstance)SDL_GetPointerProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_INSTANCE_POINTER, NULL);
if (rendererData->instance) {
rendererData->instance_external = true;
} else {
VkInstanceCreateInfo instanceCreateInfo = { 0 };
VkApplicationInfo appInfo = { 0 };
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.apiVersion = VK_API_VERSION_1_0;
instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instanceCreateInfo.pApplicationInfo = &appInfo;
char const *const *instanceExtensions = SDL_Vulkan_GetInstanceExtensions(&instanceCreateInfo.enabledExtensionCount);
const char **instanceExtensionsCopy = (const char **)SDL_calloc(instanceCreateInfo.enabledExtensionCount + 2, sizeof(const char *));
for (uint32_t i = 0; i < instanceCreateInfo.enabledExtensionCount; i++) {
instanceExtensionsCopy[i] = instanceExtensions[i];
}
if (rendererData->supportsEXTSwapchainColorspace) {
instanceExtensionsCopy[instanceCreateInfo.enabledExtensionCount] = VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME;
instanceCreateInfo.enabledExtensionCount++;
}
if (rendererData->supportsKHRGetPhysicalDeviceProperties2) {
instanceExtensionsCopy[instanceCreateInfo.enabledExtensionCount] = VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME;
instanceCreateInfo.enabledExtensionCount++;
}
instanceCreateInfo.ppEnabledExtensionNames = (const char *const *)instanceExtensionsCopy;
if (createDebug && VULKAN_ValidationLayersFound()) {
instanceCreateInfo.ppEnabledLayerNames = validationLayerName;
instanceCreateInfo.enabledLayerCount = 1;
}
result = vkCreateInstance(&instanceCreateInfo, NULL, &rendererData->instance);
SDL_free((void *)instanceExtensionsCopy);
if (result != VK_SUCCESS) {
SET_ERROR_CODE("vkCreateInstance()", result);
return result;
}
}
if (!VULKAN_LoadInstanceFunctions(rendererData)) {
VULKAN_DestroyAll(renderer);
return VK_ERROR_UNKNOWN;
}
rendererData->surface = (VkSurfaceKHR)SDL_GetNumberProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_SURFACE_NUMBER, 0);
if (rendererData->surface) {
rendererData->surface_external = true;
} else {
if (!SDL_Vulkan_CreateSurface(renderer->window, rendererData->instance, NULL, &rendererData->surface)) {
VULKAN_DestroyAll(renderer);
return VK_ERROR_UNKNOWN;
}
}
rendererData->physicalDevice = (VkPhysicalDevice)SDL_GetPointerProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_PHYSICAL_DEVICE_POINTER, NULL);
if (rendererData->physicalDevice) {
vkGetPhysicalDeviceMemoryProperties(rendererData->physicalDevice, &rendererData->physicalDeviceMemoryProperties);
vkGetPhysicalDeviceFeatures(rendererData->physicalDevice, &rendererData->physicalDeviceFeatures);
} else {
if (VULKAN_FindPhysicalDevice(rendererData) != VK_SUCCESS) {
VULKAN_DestroyAll(renderer);
return VK_ERROR_UNKNOWN;
}
}
if (SDL_HasProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER)) {
rendererData->graphicsQueueFamilyIndex = (uint32_t)SDL_GetNumberProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER, 0);
}
if (SDL_HasProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER)) {
rendererData->presentQueueFamilyIndex = (uint32_t)SDL_GetNumberProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER, 0);
}
if (rendererData->supportsKHRGetPhysicalDeviceProperties2 &&
VULKAN_DeviceExtensionsFound(rendererData, 4, &deviceExtensionNames[1])) {
rendererData->supportsKHRSamplerYCbCrConversion = true;
}
rendererData->device = (VkDevice)SDL_GetPointerProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_DEVICE_POINTER, NULL);
if (rendererData->device) {
rendererData->device_external = true;
} else {
VkPhysicalDeviceSamplerYcbcrConversionFeatures deviceSamplerYcbcrConversionFeatures = { 0 };
VkDeviceQueueCreateInfo deviceQueueCreateInfo[2] = { { 0 }, { 0 } };
static const float queuePriority[] = { 1.0f };
VkDeviceCreateInfo deviceCreateInfo = { 0 };
deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
deviceCreateInfo.queueCreateInfoCount = 0;
deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfo;
deviceCreateInfo.pEnabledFeatures = NULL;
deviceCreateInfo.enabledExtensionCount = (rendererData->supportsKHRSamplerYCbCrConversion) ? 5 : 1;
deviceCreateInfo.ppEnabledExtensionNames = deviceExtensionNames;
deviceQueueCreateInfo[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
deviceQueueCreateInfo[0].queueFamilyIndex = rendererData->graphicsQueueFamilyIndex;
deviceQueueCreateInfo[0].queueCount = 1;
deviceQueueCreateInfo[0].pQueuePriorities = queuePriority;
++deviceCreateInfo.queueCreateInfoCount;
if (rendererData->presentQueueFamilyIndex != rendererData->graphicsQueueFamilyIndex) {
deviceQueueCreateInfo[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
deviceQueueCreateInfo[1].queueFamilyIndex = rendererData->presentQueueFamilyIndex;
deviceQueueCreateInfo[1].queueCount = 1;
deviceQueueCreateInfo[1].pQueuePriorities = queuePriority;
++deviceCreateInfo.queueCreateInfoCount;
}
if (rendererData->supportsKHRSamplerYCbCrConversion) {
deviceSamplerYcbcrConversionFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES;
deviceSamplerYcbcrConversionFeatures.samplerYcbcrConversion = VK_TRUE;
deviceSamplerYcbcrConversionFeatures.pNext = (void *)deviceCreateInfo.pNext;
deviceCreateInfo.pNext = &deviceSamplerYcbcrConversionFeatures;
}
result = vkCreateDevice(rendererData->physicalDevice, &deviceCreateInfo, NULL, &rendererData->device);
if (result != VK_SUCCESS) {
SET_ERROR_CODE("vkCreateDevice()", result);
VULKAN_DestroyAll(renderer);
return result;
}
}
if (!VULKAN_LoadDeviceFunctions(rendererData)) {
VULKAN_DestroyAll(renderer);
return VK_ERROR_UNKNOWN;
}
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;
}
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);
SET_ERROR_CODE("vkCreateCommandPool()", result);
return result;
}
if (VULKAN_GetSurfaceFormats(rendererData) != VK_SUCCESS) {
VULKAN_DestroyAll(renderer);
return result;
}
for (uint32_t i = 0; i < NUM_SHADERS; i++) {
VULKAN_Shader shader = (VULKAN_Shader)i;
VkShaderModuleCreateInfo shaderModuleCreateInfo = { 0 };
shaderModuleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
VULKAN_GetVertexShader(shader, &shaderModuleCreateInfo.pCode, &shaderModuleCreateInfo.codeSize);
result = vkCreateShaderModule(rendererData->device, &shaderModuleCreateInfo, NULL, &rendererData->vertexShaderModules[i]);
if (result != VK_SUCCESS) {
VULKAN_DestroyAll(renderer);
SET_ERROR_CODE("vkCreateShaderModule()", result);
return result;
}
VULKAN_GetPixelShader(shader, &shaderModuleCreateInfo.pCode, &shaderModuleCreateInfo.codeSize);
result = vkCreateShaderModule(rendererData->device, &shaderModuleCreateInfo, NULL, &rendererData->fragmentShaderModules[i]);
if (result != VK_SUCCESS) {
VULKAN_DestroyAll(renderer);
SET_ERROR_CODE("vkCreateShaderModule()", result);
return result;
}
}
result = VULKAN_CreateDescriptorSetAndPipelineLayout(rendererData, VK_NULL_HANDLE, &rendererData->descriptorSetLayout, &rendererData->pipelineLayout);
if (result != VK_SUCCESS) {
VULKAN_DestroyAll(renderer);
return result;
}
for (uint32_t i = 0; i < SDL_VULKAN_NUM_VERTEX_BUFFERS; ++i) {
VULKAN_CreateVertexBuffer(rendererData, i, SDL_VULKAN_VERTEX_BUFFER_DEFAULT_SIZE);
}
SDL_PropertiesID props = SDL_GetRendererProperties(renderer);
SDL_SetPointerProperty(props, SDL_PROP_RENDERER_VULKAN_INSTANCE_POINTER, rendererData->instance);
SDL_SetNumberProperty(props, SDL_PROP_RENDERER_VULKAN_SURFACE_NUMBER, (Sint64)rendererData->surface);
SDL_SetPointerProperty(props, SDL_PROP_RENDERER_VULKAN_PHYSICAL_DEVICE_POINTER, rendererData->physicalDevice);
SDL_SetPointerProperty(props, SDL_PROP_RENDERER_VULKAN_DEVICE_POINTER, rendererData->device);
SDL_SetNumberProperty(props, SDL_PROP_RENDERER_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER, rendererData->graphicsQueueFamilyIndex);
SDL_SetNumberProperty(props, SDL_PROP_RENDERER_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER, rendererData->presentQueueFamilyIndex);
return VK_SUCCESS;
}
static VkResult VULKAN_CreateFramebuffersAndRenderPasses(SDL_Renderer *renderer, int w, int h,
VkFormat format, int imageViewCount, VkImageView *imageViews, VkFramebuffer *framebuffers, VkRenderPass renderPasses[VULKAN_RENDERPASS_COUNT])
{
VULKAN_RenderData *rendererData = (VULKAN_RenderData *) renderer->internal;
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 = VK_SAMPLE_COUNT_1_BIT;
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[VULKAN_RENDERPASS_LOAD]);
if (result != VK_SUCCESS) {
SET_ERROR_CODE("vkCreateRenderPass()", result);
return result;
}
attachmentDescription.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
result = vkCreateRenderPass(rendererData->device, &renderPassCreateInfo, NULL, &renderPasses[VULKAN_RENDERPASS_CLEAR]);
if (result != VK_SUCCESS) {
SET_ERROR_CODE("vkCreateRenderPass()", result);
return result;
}
VkFramebufferCreateInfo framebufferCreateInfo = { 0 };
framebufferCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebufferCreateInfo.pNext = NULL;
framebufferCreateInfo.renderPass = rendererData->renderPasses[VULKAN_RENDERPASS_LOAD];
framebufferCreateInfo.attachmentCount = 1;
framebufferCreateInfo.width = w;
framebufferCreateInfo.height = h;
framebufferCreateInfo.layers = 1;
for (int i = 0; i < imageViewCount; i++) {
framebufferCreateInfo.pAttachments = &imageViews[i];
result = vkCreateFramebuffer(rendererData->device, &framebufferCreateInfo, NULL, &framebuffers[i]);
if (result != VK_SUCCESS) {
SET_ERROR_CODE("vkCreateFramebuffer()", result);
return result;
}
}
return result;
}
static VkResult VULKAN_CreateSwapChain(SDL_Renderer *renderer, int w, int h)
{
VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
VkResult result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(rendererData->physicalDevice, rendererData->surface, &rendererData->surfaceCapabilities);
if (result != VK_SUCCESS) {
SET_ERROR_CODE("vkGetPhysicalDeviceSurfaceCapabilitiesKHR()", result);
return result;
}
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 = NULL;
}
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 = NULL;
}
if (rendererData->commandBuffers) {
vkResetCommandPool(rendererData->device, rendererData->commandPool, 0);
SDL_free(rendererData->commandBuffers);
rendererData->commandBuffers = NULL;
rendererData->currentCommandBuffer = VK_NULL_HANDLE;
rendererData->currentCommandBufferIndex = 0;
}
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;
}
if (rendererData->descriptorPools) {
SDL_assert(rendererData->numDescriptorPools);
for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) {
for (uint32_t j = 0; j < rendererData->numDescriptorPools[i]; j++) {
if (rendererData->descriptorPools[i][j] != VK_NULL_HANDLE) {
vkDestroyDescriptorPool(rendererData->device, rendererData->descriptorPools[i][j], NULL);
}
}
SDL_free(rendererData->descriptorPools[i]);
}
SDL_free(rendererData->descriptorPools);
rendererData->descriptorPools = NULL;
SDL_free(rendererData->numDescriptorPools);
rendererData->numDescriptorPools = NULL;
}
if (rendererData->imageAvailableSemaphores) {
for (uint32_t i = 0; i < rendererData->swapchainImageCount; ++i) {
if (rendererData->imageAvailableSemaphores[i] != VK_NULL_HANDLE) {
vkDestroySemaphore(rendererData->device, rendererData->imageAvailableSemaphores[i], NULL);
}
}
SDL_free(rendererData->imageAvailableSemaphores);
rendererData->imageAvailableSemaphores = NULL;
}
if (rendererData->renderingFinishedSemaphores) {
for (uint32_t i = 0; i < rendererData->swapchainImageCount; ++i) {
if (rendererData->renderingFinishedSemaphores[i] != VK_NULL_HANDLE) {
vkDestroySemaphore(rendererData->device, rendererData->renderingFinishedSemaphores[i], NULL);
}
}
SDL_free(rendererData->renderingFinishedSemaphores);
rendererData->renderingFinishedSemaphores = NULL;
}
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 = NULL;
}
if (rendererData->constantBuffers) {
SDL_assert(rendererData->numConstantBuffers);
for (uint32_t i = 0; i < rendererData->swapchainImageCount; ++i) {
for (uint32_t j = 0; j < rendererData->numConstantBuffers[i]; j++) {
VULKAN_DestroyBuffer(rendererData, &rendererData->constantBuffers[i][j]);
}
SDL_free(rendererData->constantBuffers[i]);
}
SDL_free(rendererData->constantBuffers);
rendererData->constantBuffers = NULL;
SDL_free(rendererData->numConstantBuffers);
rendererData->numConstantBuffers = NULL;
}
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)) {
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);
rendererData->swapChainPreTransform = rendererData->surfaceCapabilities.currentTransform;
if (rendererData->swapChainPreTransform == VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR ||
rendererData->swapChainPreTransform == VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR) {
uint32_t tempWidth = rendererData->swapchainSize.width;
rendererData->swapchainSize.width = rendererData->swapchainSize.height;
rendererData->swapchainSize.height = tempWidth;
}
if (rendererData->swapchainSize.width == 0 && rendererData->swapchainSize.height == 0) {
return VK_ERROR_OUT_OF_DATE_KHR;
}
VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR;
if (rendererData->vsync <= 0) {
uint32_t presentModeCount = 0;
result = vkGetPhysicalDeviceSurfacePresentModesKHR(rendererData->physicalDevice, rendererData->surface, &presentModeCount, NULL);
if (result != VK_SUCCESS) {
SET_ERROR_CODE("vkGetPhysicalDeviceSurfacePresentModesKHR()", result);
return result;
}
if (presentModeCount > 0) {
VkPresentModeKHR *presentModes = (VkPresentModeKHR *)SDL_calloc(presentModeCount, sizeof(VkPresentModeKHR));
result = vkGetPhysicalDeviceSurfacePresentModesKHR(rendererData->physicalDevice, rendererData->surface, &presentModeCount, presentModes);
if (result != VK_SUCCESS) {
SET_ERROR_CODE("vkGetPhysicalDeviceSurfacePresentModesKHR()", result);
SDL_free(presentModes);
return result;
}
if (rendererData->vsync == 0) {
for (uint32_t i = 0; i < presentModeCount; i++) {
if (presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR) {
presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
break;
}
else if (presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR) {
presentMode = VK_PRESENT_MODE_MAILBOX_KHR;
}
else if ((presentMode != VK_PRESENT_MODE_MAILBOX_KHR) &&
(presentModes[i] == VK_PRESENT_MODE_FIFO_RELAXED_KHR)) {
presentMode = VK_PRESENT_MODE_FIFO_RELAXED_KHR;
}
}
} else if (rendererData->vsync == -1) {
for (uint32_t i = 0; i < presentModeCount; i++) {
if (presentModes[i] == VK_PRESENT_MODE_FIFO_RELAXED_KHR) {
presentMode = VK_PRESENT_MODE_FIFO_RELAXED_KHR;
break;
}
}
}
SDL_free(presentModes);
}
}
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->swapChainPreTransform;
swapchainCreateInfo.compositeAlpha = (renderer->window->flags & SDL_WINDOW_TRANSPARENT) ? (VkCompositeAlphaFlagBitsKHR)0 : VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
swapchainCreateInfo.presentMode = presentMode;
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;
SET_ERROR_CODE("vkCreateSwapchainKHR()", 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;
SET_ERROR_CODE("vkGetSwapchainImagesKHR()", result);
return result;
}
rendererData->swapchainImages = (VkImage *)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;
SET_ERROR_CODE("vkGetSwapchainImagesKHR()", result);
return result;
}
{
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;
rendererData->swapchainImageViews = (VkImageView *)SDL_calloc(rendererData->swapchainImageCount, sizeof(VkImageView));
SDL_free(rendererData->swapchainImageLayouts);
rendererData->swapchainImageLayouts = (VkImageLayout *)SDL_calloc(rendererData->swapchainImageCount, sizeof(VkImageLayout));
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);
SET_ERROR_CODE("vkCreateImageView()", 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;
rendererData->commandBuffers = (VkCommandBuffer *)SDL_calloc(rendererData->swapchainImageCount, sizeof(VkCommandBuffer));
result = vkAllocateCommandBuffers(rendererData->device, &commandBufferAllocateInfo, rendererData->commandBuffers);
if (result != VK_SUCCESS) {
VULKAN_DestroyAll(renderer);
SET_ERROR_CODE("vkAllocateCommandBuffers()", result);
return result;
}
rendererData->fences = (VkFence *)SDL_calloc(rendererData->swapchainImageCount, sizeof(VkFence));
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);
SET_ERROR_CODE("vkCreateFence()", result);
return result;
}
}
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 = (VkFramebuffer *)SDL_calloc(rendererData->swapchainImageCount, sizeof(VkFramebuffer));
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);
SET_ERROR_CODE("VULKAN_CreateFramebuffersAndRenderPasses()", result);
return result;
}
rendererData->descriptorPools = (VkDescriptorPool **)SDL_calloc(rendererData->swapchainImageCount, sizeof(VkDescriptorPool *));
rendererData->numDescriptorPools = (uint32_t *)SDL_calloc(rendererData->swapchainImageCount, sizeof(uint32_t));
for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) {
rendererData->numDescriptorPools[i] = 1;
rendererData->descriptorPools[i] = (VkDescriptorPool *)SDL_calloc(1, sizeof(VkDescriptorPool));
rendererData->descriptorPools[i][0] = VULKAN_AllocateDescriptorPool(rendererData);
if (result != VK_SUCCESS) {
VULKAN_DestroyAll(renderer);
return result;
}
}
rendererData->imageAvailableSemaphores = (VkSemaphore *)SDL_calloc(rendererData->swapchainImageCount, sizeof(VkSemaphore));
rendererData->renderingFinishedSemaphores = (VkSemaphore *)SDL_calloc(rendererData->swapchainImageCount, sizeof(VkSemaphore));
for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) {
rendererData->imageAvailableSemaphores[i] = VULKAN_CreateSemaphore(rendererData);
if (rendererData->imageAvailableSemaphores[i] == VK_NULL_HANDLE) {
VULKAN_DestroyAll(renderer);
return VK_ERROR_UNKNOWN;
}
rendererData->renderingFinishedSemaphores[i] = VULKAN_CreateSemaphore(rendererData);
if (rendererData->renderingFinishedSemaphores[i] == VK_NULL_HANDLE) {
VULKAN_DestroyAll(renderer);
return VK_ERROR_UNKNOWN;
}
}
rendererData->uploadBuffers = (VULKAN_Buffer **)SDL_calloc(rendererData->swapchainImageCount, sizeof(VULKAN_Buffer *));
for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) {
rendererData->uploadBuffers[i] = (VULKAN_Buffer *)SDL_calloc(SDL_VULKAN_NUM_UPLOAD_BUFFERS, sizeof(VULKAN_Buffer));
}
SDL_free(rendererData->currentUploadBuffer);
rendererData->currentUploadBuffer = (int *)SDL_calloc(rendererData->swapchainImageCount, sizeof(int));
rendererData->constantBuffers = (VULKAN_Buffer **)SDL_calloc(rendererData->swapchainImageCount, sizeof(VULKAN_Buffer *));
rendererData->numConstantBuffers = (uint32_t *)SDL_calloc(rendererData->swapchainImageCount, sizeof(uint32_t));
for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) {
rendererData->numConstantBuffers[i] = 1;
rendererData->constantBuffers[i] = (VULKAN_Buffer *)SDL_calloc(1, sizeof(VULKAN_Buffer));
result = VULKAN_AllocateBuffer(rendererData,
SDL_VULKAN_CONSTANT_BUFFER_DEFAULT_SIZE,
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
&rendererData->constantBuffers[i][0]);
if (result != VK_SUCCESS) {
VULKAN_DestroyAll(renderer);
return result;
}
}
rendererData->currentConstantBufferOffset = -1;
rendererData->currentConstantBufferIndex = 0;
VULKAN_AcquireNextSwapchainImage(renderer);
SDL_PropertiesID props = SDL_GetRendererProperties(renderer);
SDL_SetNumberProperty(props, SDL_PROP_RENDERER_VULKAN_SWAPCHAIN_IMAGE_COUNT_NUMBER, rendererData->swapchainImageCount);
return result;
}
static VkResult VULKAN_CreateWindowSizeDependentResources(SDL_Renderer *renderer)
{
VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
VkResult result = VK_SUCCESS;
int w, h;
VULKAN_IssueBatch(rendererData);
VULKAN_WaitForGPU(rendererData);
SDL_GetWindowSizeInPixels(renderer->window, &w, &h);
result = VULKAN_CreateSwapChain(renderer, w, h);
if (result != VK_SUCCESS) {
rendererData->recreateSwapchain = true;
}
rendererData->viewportDirty = true;
return result;
}
static bool VULKAN_HandleDeviceLost(SDL_Renderer *renderer)
{
VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
bool recovered = false;
VULKAN_DestroyAll(renderer);
if (VULKAN_CreateDeviceResources(renderer, rendererData->create_props) == VK_SUCCESS &&
VULKAN_CreateWindowSizeDependentResources(renderer) == VK_SUCCESS) {
recovered = true;
} else {
SDL_LogError(SDL_LOG_CATEGORY_RENDER, "Renderer couldn't recover from device lost: %s", SDL_GetError());
VULKAN_DestroyAll(renderer);
}
SDL_Event event;
SDL_zero(event);
event.type = recovered ? SDL_EVENT_RENDER_DEVICE_RESET : SDL_EVENT_RENDER_DEVICE_LOST;
event.render.windowID = SDL_GetWindowID(SDL_GetRenderWindow(renderer));
SDL_PushEvent(&event);
return recovered;
}
static VkResult VULKAN_UpdateForWindowSizeChange(SDL_Renderer *renderer)
{
VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
VULKAN_WaitForGPU(rendererData);
return VULKAN_CreateWindowSizeDependentResources(renderer);
}
static void VULKAN_WindowEvent(SDL_Renderer *renderer, const SDL_WindowEvent *event)
{
VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
if (event->type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) {
rendererData->recreateSwapchain = true;
}
#ifdef SDL_PLATFORM_ANDROID
if (event->type == SDL_EVENT_WINDOW_RESTORED) {
VULKAN_HandleDeviceLost(renderer);
}
#endif
}
static 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 false;
}
return true;
}
static bool VULKAN_CreatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette)
{
VULKAN_RenderData *data = (VULKAN_RenderData *)renderer->internal;
VULKAN_PaletteData *palettedata = (VULKAN_PaletteData *)SDL_calloc(1, sizeof(*palettedata));
if (!palettedata) {
return false;
}
palette->internal = palettedata;
VkFormat format = SDLPixelFormatToVkTextureFormat(SDL_PIXELFORMAT_RGBA32, renderer->output_colorspace);
VkImageUsageFlags usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
VkComponentMapping imageViewSwizzle = data->identitySwizzle;
VkResult result = VULKAN_AllocateImage(data, 0, 256, 1, format, usage, imageViewSwizzle, VK_NULL_HANDLE, &palettedata->image);
if (result != VK_SUCCESS) {
SET_ERROR_CODE("VULKAN_AllocateImage()", result);
return false;
}
return true;
}
static bool VULKAN_UpdatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette, int ncolors, SDL_Color *colors)
{
VULKAN_RenderData *data = (VULKAN_RenderData *)renderer->internal;
VULKAN_PaletteData *palettedata = (VULKAN_PaletteData *)palette->internal;
return VULKAN_UpdateTextureInternal(data, palettedata->image.image, palettedata->image.format, 0, 0, 0, ncolors, 1, colors, ncolors * sizeof(*colors), &palettedata->image.imageLayout);
}
static void VULKAN_DestroyPalette(SDL_Renderer *renderer, SDL_TexturePalette *palette)
{
VULKAN_RenderData *data = (VULKAN_RenderData *)renderer->internal;
VULKAN_PaletteData *palettedata = (VULKAN_PaletteData *)palette->internal;
if (palettedata) {
VULKAN_DestroyImage(data, &palettedata->image);
SDL_free(palettedata);
}
}
static bool VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props)
{
VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
VULKAN_TextureData *textureData;
VkResult result;
VkFormat textureFormat = SDLPixelFormatToVkTextureFormat(texture->format, renderer->output_colorspace);
uint32_t width = texture->w;
uint32_t height = texture->h;
VkComponentMapping imageViewSwizzle = rendererData->identitySwizzle;
if (!rendererData->device) {
return SDL_SetError("Device lost and couldn't be recovered");
}
if (textureFormat == VK_FORMAT_UNDEFINED) {
return SDL_SetError("%s, An unsupported SDL pixel format (0x%x) was specified", SDL_FUNCTION, texture->format);
}
textureData = (VULKAN_TextureData *)SDL_calloc(1, sizeof(*textureData));
if (!textureData) {
return false;
}
texture->internal = textureData;
#ifdef SDL_HAVE_YUV
if (texture->format == SDL_PIXELFORMAT_YV12 ||
texture->format == SDL_PIXELFORMAT_IYUV ||
texture->format == SDL_PIXELFORMAT_NV12 ||
texture->format == SDL_PIXELFORMAT_NV21 ||
texture->format == SDL_PIXELFORMAT_P010) {
const uint32_t YUV_SD_THRESHOLD = 576;
if (!rendererData->supportsKHRSamplerYCbCrConversion) {
return SDL_SetError("YUV textures require a Vulkan device that supports VK_KHR_sampler_ycbcr_conversion");
}
VkSamplerYcbcrConversionCreateInfoKHR samplerYcbcrConversionCreateInfo = { 0 };
samplerYcbcrConversionCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO_KHR;
width = (width + 1) & ~1;
height = (height + 1) & ~1;
samplerYcbcrConversionCreateInfo.format = textureFormat;
switch (SDL_COLORSPACEMATRIX(texture->colorspace)) {
case SDL_MATRIX_COEFFICIENTS_BT470BG:
case SDL_MATRIX_COEFFICIENTS_BT601:
samplerYcbcrConversionCreateInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601_KHR;
break;
case SDL_MATRIX_COEFFICIENTS_BT709:
samplerYcbcrConversionCreateInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709_KHR;
break;
case SDL_MATRIX_COEFFICIENTS_BT2020_NCL:
samplerYcbcrConversionCreateInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020_KHR;
break;
case SDL_MATRIX_COEFFICIENTS_UNSPECIFIED:
if (texture->format == SDL_PIXELFORMAT_P010) {
samplerYcbcrConversionCreateInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020_KHR;
} else if (height > YUV_SD_THRESHOLD) {
samplerYcbcrConversionCreateInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709_KHR;
} else {
samplerYcbcrConversionCreateInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601_KHR;
}
break;
default:
return SDL_SetError("Unsupported Ycbcr colorspace: %d", SDL_COLORSPACEMATRIX(texture->colorspace));
}
samplerYcbcrConversionCreateInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
samplerYcbcrConversionCreateInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
samplerYcbcrConversionCreateInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
samplerYcbcrConversionCreateInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
if (texture->format == SDL_PIXELFORMAT_YV12 ||
texture->format == SDL_PIXELFORMAT_NV21) {
samplerYcbcrConversionCreateInfo.components.r = VK_COMPONENT_SWIZZLE_B;
samplerYcbcrConversionCreateInfo.components.b = VK_COMPONENT_SWIZZLE_R;
}
switch (SDL_COLORSPACERANGE(texture->colorspace)) {
case SDL_COLOR_RANGE_LIMITED:
samplerYcbcrConversionCreateInfo.ycbcrRange = VK_SAMPLER_YCBCR_RANGE_ITU_NARROW_KHR;
break;
case SDL_COLOR_RANGE_FULL:
default:
samplerYcbcrConversionCreateInfo.ycbcrRange = VK_SAMPLER_YCBCR_RANGE_ITU_FULL_KHR;
break;
}
switch (SDL_COLORSPACECHROMA(texture->colorspace)) {
case SDL_CHROMA_LOCATION_LEFT:
samplerYcbcrConversionCreateInfo.xChromaOffset = VK_CHROMA_LOCATION_COSITED_EVEN_KHR;
samplerYcbcrConversionCreateInfo.yChromaOffset = VK_CHROMA_LOCATION_MIDPOINT_KHR;
break;
case SDL_CHROMA_LOCATION_TOPLEFT:
samplerYcbcrConversionCreateInfo.xChromaOffset = VK_CHROMA_LOCATION_COSITED_EVEN_KHR;
samplerYcbcrConversionCreateInfo.yChromaOffset = VK_CHROMA_LOCATION_COSITED_EVEN_KHR;
break;
case SDL_CHROMA_LOCATION_NONE:
case SDL_CHROMA_LOCATION_CENTER:
default:
samplerYcbcrConversionCreateInfo.xChromaOffset = VK_CHROMA_LOCATION_MIDPOINT_KHR;
samplerYcbcrConversionCreateInfo.yChromaOffset = VK_CHROMA_LOCATION_MIDPOINT_KHR;
break;
}
samplerYcbcrConversionCreateInfo.chromaFilter = VK_FILTER_LINEAR;
samplerYcbcrConversionCreateInfo.forceExplicitReconstruction = VK_FALSE;
result = vkCreateSamplerYcbcrConversionKHR(rendererData->device, &samplerYcbcrConversionCreateInfo, NULL, &textureData->samplerYcbcrConversion);
if (result != VK_SUCCESS) {
SET_ERROR_CODE("vkCreateSamplerYcbcrConversionKHR()", result);
return false;
}
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;
VkSamplerYcbcrConversionInfoKHR samplerYcbcrConversionInfo = { 0 };
samplerYcbcrConversionInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO_KHR;
samplerYcbcrConversionInfo.conversion = textureData->samplerYcbcrConversion;
samplerCreateInfo.pNext = &samplerYcbcrConversionInfo;
result = vkCreateSampler(rendererData->device, &samplerCreateInfo, NULL, &textureData->samplerYcbcr);
if (result != VK_SUCCESS) {
SET_ERROR_CODE("vkCreateSampler()", result);
return false;
}
result = VULKAN_CreateDescriptorSetAndPipelineLayout(rendererData, textureData->samplerYcbcr, &textureData->descriptorSetLayoutYcbcr, &textureData->pipelineLayoutYcbcr);
if (result != VK_SUCCESS) {
return false;
}
}
#endif
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;
}
result = VULKAN_AllocateImage(rendererData, create_props, width, height, textureFormat, usage, imageViewSwizzle, textureData->samplerYcbcrConversion, &textureData->mainImage);
if (result != VK_SUCCESS) {
SET_ERROR_CODE("VULKAN_AllocateImage()", result);
return false;
}
SDL_PropertiesID props = SDL_GetTextureProperties(texture);
SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_VULKAN_TEXTURE_NUMBER, (Sint64)textureData->mainImage.image);
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) {
SET_ERROR_CODE("VULKAN_CreateFramebuffersAndRenderPasses()", result);
return false;
}
}
return true;
}
static void VULKAN_DestroyTexture(SDL_Renderer *renderer,
SDL_Texture *texture)
{
VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
VULKAN_TextureData *textureData = (VULKAN_TextureData *)texture->internal;
if (!textureData) {
return;
}
VULKAN_IssueBatch(rendererData);
VULKAN_WaitForGPU(rendererData);
VULKAN_DestroyImage(rendererData, &textureData->mainImage);
#ifdef SDL_HAVE_YUV
if (textureData->samplerYcbcrConversion != VK_NULL_HANDLE) {
vkDestroySamplerYcbcrConversionKHR(rendererData->device, textureData->samplerYcbcrConversion, NULL);
textureData->samplerYcbcrConversion = VK_NULL_HANDLE;
}
if (textureData->samplerYcbcr != VK_NULL_HANDLE) {
vkDestroySampler(rendererData->device, textureData->samplerYcbcr, NULL);
textureData->samplerYcbcr = VK_NULL_HANDLE;
}
if (textureData->pipelineLayoutYcbcr != VK_NULL_HANDLE) {
vkDestroyPipelineLayout(rendererData->device, textureData->pipelineLayoutYcbcr, NULL);
textureData->pipelineLayoutYcbcr = VK_NULL_HANDLE;
}
if (textureData->descriptorSetLayoutYcbcr != VK_NULL_HANDLE) {
vkDestroyDescriptorSetLayout(rendererData->device, textureData->descriptorSetLayoutYcbcr, NULL);
textureData->descriptorSetLayoutYcbcr = VK_NULL_HANDLE;
}
#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->internal = NULL;
}
static bool 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, plane);
VkDeviceSize length = w * pixelSize;
VkDeviceSize uploadBufferSize = length * h;
const Uint8 *src;
Uint8 *dst;
VkResult rc;
int planeCount = VULKAN_VkFormatGetNumPlanes(format);
VULKAN_EnsureCommandBuffer(rendererData);
int currentUploadBufferIndex = rendererData->currentUploadBuffer[rendererData->currentCommandBufferIndex];
VULKAN_Buffer *uploadBuffer = &rendererData->uploadBuffers[rendererData->currentCommandBufferIndex][currentUploadBufferIndex];
rc = VULKAN_AllocateBuffer(rendererData, uploadBufferSize,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
uploadBuffer);
if (rc != VK_SUCCESS) {
return false;
}
src = (const Uint8 *)pixels;
dst = (Uint8 *)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, (size_t)length);
src += pitch;
dst += length;
}
}
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;
if (planeCount <= 1) {
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
} else {
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_PLANE_0_BIT << plane;
}
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);
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 (rendererData->currentUploadBuffer[rendererData->currentCommandBufferIndex] == SDL_VULKAN_NUM_UPLOAD_BUFFERS) {
VULKAN_IssueBatch(rendererData);
}
return true;
}
#ifdef SDL_HAVE_YUV
static bool VULKAN_UpdateTextureNV(SDL_Renderer *renderer, SDL_Texture *texture,
const SDL_Rect *rect,
const Uint8 *Yplane, int Ypitch,
const Uint8 *UVplane, int UVpitch);
static bool 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);
#endif
static bool VULKAN_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
const SDL_Rect *rect, const void *srcPixels,
int srcPitch)
{
VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
VULKAN_TextureData *textureData = (VULKAN_TextureData *)texture->internal;
if (!textureData) {
return SDL_SetError("Texture is not currently available");
}
#ifdef SDL_HAVE_YUV
Uint32 numPlanes = VULKAN_VkFormatGetNumPlanes(textureData->mainImage.format);
if (numPlanes == 2) {
int UVbpp = (int)VULKAN_GetBytesPerPixel(textureData->mainImage.format, 1);
int Ypitch = srcPitch;
int UVpitch = (srcPitch + (UVbpp - 1)) & ~(UVbpp - 1);
const Uint8 *plane0 = (const Uint8 *)srcPixels;
const Uint8 *plane1 = plane0 + rect->h * srcPitch;
return VULKAN_UpdateTextureNV(renderer, texture, rect, plane0, Ypitch, plane1, UVpitch);
} else if (numPlanes == 3) {
int Ypitch = srcPitch;
int UVpitch = ((Ypitch + 1) / 2);
const Uint8 *plane0 = (const Uint8 *)srcPixels;
const Uint8 *plane1 = plane0 + rect->h * Ypitch;
const Uint8 *plane2 = plane1 + ((rect->h + 1) / 2) * UVpitch;
if (texture->format == SDL_PIXELFORMAT_YV12) {
return VULKAN_UpdateTextureYUV(renderer, texture, rect, plane0, Ypitch, plane2, UVpitch, plane1, UVpitch);
} else {
return VULKAN_UpdateTextureYUV(renderer, texture, rect, plane0, Ypitch, plane1, UVpitch, plane2, UVpitch);
}
}
#endif
if (!VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, 0, rect->x, rect->y, rect->w, rect->h, srcPixels, srcPitch, &textureData->mainImage.imageLayout)) {
return false;
}
return true;
}
#ifdef SDL_HAVE_YUV
static bool 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->internal;
VULKAN_TextureData *textureData = (VULKAN_TextureData *)texture->internal;
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)) {
return false;
}
if (texture->format == SDL_PIXELFORMAT_YV12) {
if (!VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, 1, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Vplane, Vpitch, &textureData->mainImage.imageLayout)) {
return false;
}
if (!VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, 2, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Uplane, Upitch, &textureData->mainImage.imageLayout)) {
return false;
}
} else {
if (!VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, 1, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Uplane, Upitch, &textureData->mainImage.imageLayout)) {
return false;
}
if (!VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, 2, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Vplane, Vpitch, &textureData->mainImage.imageLayout)) {
return false;
}
}
return true;
}
static bool 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->internal;
VULKAN_TextureData *textureData = (VULKAN_TextureData *)texture->internal;
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)) {
return false;
}
if (!VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, 1, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, UVplane, UVpitch, &textureData->mainImage.imageLayout)) {
return false;
}
return true;
}
#endif
static bool VULKAN_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture,
const SDL_Rect *rect, void **pixels, int *pitch)
{
VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
VULKAN_TextureData *textureData = (VULKAN_TextureData *)texture->internal;
VkResult rc;
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, 0);
VkDeviceSize length = rect->w * pixelSize;
VkDeviceSize stagingBufferSize = length * rect->h;
rc = VULKAN_AllocateBuffer(rendererData,
stagingBufferSize,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
&textureData->stagingBuffer);
if (rc != VK_SUCCESS) {
return false;
}
textureData->lockedRect = *rect;
*pixels = textureData->stagingBuffer.mappedBufferPtr;
*pitch = (int)length;
return true;
}
static void VULKAN_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture)
{
VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
VULKAN_TextureData *textureData = (VULKAN_TextureData *)texture->internal;
if (!textureData) {
return;
}
VULKAN_EnsureCommandBuffer(rendererData);
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);
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);
VULKAN_IssueBatch(rendererData);
VULKAN_DestroyBuffer(rendererData, &textureData->stagingBuffer);
}
static bool VULKAN_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture)
{
VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
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 true;
}
textureData = (VULKAN_TextureData *)texture->internal;
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 true;
}
static bool VULKAN_QueueNoOp(SDL_Renderer *renderer, SDL_RenderCommand *cmd)
{
return true; }
static bool VULKAN_QueueDrawPoints(SDL_Renderer *renderer, SDL_RenderCommand *cmd, const SDL_FPoint *points, int count)
{
VULKAN_VertexPositionColor *verts = (VULKAN_VertexPositionColor *)SDL_AllocateRenderVertices(renderer, count * sizeof(VULKAN_VertexPositionColor), 0, &cmd->data.draw.first);
int i;
SDL_FColor color = cmd->data.draw.color;
bool convert_color = SDL_RenderingLinearSpace(renderer);
if (!verts) {
return false;
}
cmd->data.draw.count = count;
if (convert_color) {
SDL_ConvertToLinear(&color);
}
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 = color;
verts++;
}
return true;
}
static bool 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;
VULKAN_VertexPositionColor *verts = (VULKAN_VertexPositionColor *)SDL_AllocateRenderVertices(renderer, count * sizeof(VULKAN_VertexPositionColor), 0, &cmd->data.draw.first);
bool convert_color = SDL_RenderingLinearSpace(renderer);
VULKAN_TextureData *textureData = texture ? (VULKAN_TextureData *)texture->internal : 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 false;
}
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 true;
}
static bool VULKAN_UpdateVertexBuffer(SDL_Renderer *renderer,
const void *vertexData, size_t dataSizeInBytes, VULKAN_DrawStateCache *stateCache)
{
VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
const int vbidx = rendererData->currentVertexBuffer;
VULKAN_Buffer *vertexBuffer;
if (dataSizeInBytes == 0) {
return true; }
if (rendererData->issueBatch) {
if (VULKAN_IssueBatch(rendererData) != VK_SUCCESS) {
return SDL_SetError("Failed to issue intermediate batch");
}
}
if (dataSizeInBytes > rendererData->vertexBuffers[vbidx].size) {
VULKAN_IssueBatch(rendererData);
VULKAN_WaitForGPU(rendererData);
VULKAN_CreateVertexBuffer(rendererData, vbidx, dataSizeInBytes);
}
vertexBuffer = &rendererData->vertexBuffers[vbidx];
SDL_memcpy(vertexBuffer->mappedBufferPtr, vertexData, dataSizeInBytes);
stateCache->vertexBuffer = vertexBuffer->buffer;
rendererData->currentVertexBuffer = vbidx + 1;
if (rendererData->currentVertexBuffer >= SDL_VULKAN_NUM_VERTEX_BUFFERS) {
rendererData->currentVertexBuffer = 0;
rendererData->issueBatch = true;
}
return true;
}
static VkSurfaceTransformFlagBitsKHR VULKAN_GetRotationForCurrentRenderTarget(VULKAN_RenderData *rendererData)
{
if (rendererData->textureRenderTarget) {
return VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
} else {
return rendererData->swapChainPreTransform;
}
}
static bool VULKAN_IsDisplayRotated90Degrees(VkSurfaceTransformFlagBitsKHR rotation)
{
switch (rotation) {
case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
return true;
default:
return false;
}
}
static bool VULKAN_UpdateViewport(SDL_Renderer *renderer)
{
VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
const SDL_Rect *viewport = &rendererData->currentViewport;
Float4X4 projection;
Float4X4 view;
VkSurfaceTransformFlagBitsKHR rotation = VULKAN_GetRotationForCurrentRenderTarget(rendererData);
bool swapDimensions;
if (viewport->w == 0 || viewport->h == 0) {
return false;
}
switch (rotation) {
case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
projection = MatrixRotationZ(SDL_PI_F * 0.5f);
break;
case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR:
projection = MatrixRotationZ(SDL_PI_F);
break;
case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
projection = MatrixRotationZ(-SDL_PI_F * 0.5f);
break;
case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR:
default:
projection = MatrixIdentity();
break;
}
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;
swapDimensions = VULKAN_IsDisplayRotated90Degrees(rotation);
if (swapDimensions) {
vkViewport.x = viewport->y;
vkViewport.y = viewport->x;
vkViewport.width = viewport->h;
vkViewport.height = viewport->w;
}
else {
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);
rendererData->viewportDirty = false;
return true;
}
static bool VULKAN_UpdateClipRect(SDL_Renderer *renderer)
{
VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
const SDL_Rect *viewport = &rendererData->currentViewport;
VkSurfaceTransformFlagBitsKHR rotation = VULKAN_GetRotationForCurrentRenderTarget(rendererData);
bool swapDimensions = VULKAN_IsDisplayRotated90Degrees(rotation);
VkRect2D scissor;
if (rendererData->currentCliprectEnabled) {
scissor.offset.x = viewport->x + rendererData->currentCliprect.x;
scissor.offset.y = viewport->y + rendererData->currentCliprect.y;
scissor.extent.width = rendererData->currentCliprect.w;
scissor.extent.height = rendererData->currentCliprect.h;
} else {
scissor.offset.x = viewport->x;
scissor.offset.y = viewport->y;
scissor.extent.width = viewport->w;
scissor.extent.height = viewport->h;
}
if (swapDimensions) {
VkRect2D scissorTemp = scissor;
scissor.offset.x = scissorTemp.offset.y;
scissor.offset.y = scissorTemp.offset.x;
scissor.extent.width = scissorTemp.extent.height;
scissor.extent.height = scissorTemp.extent.width;
}
vkCmdSetScissor(rendererData->currentCommandBuffer, 0, 1, &scissor);
rendererData->cliprectDirty = false;
return true;
}
static void VULKAN_SetupShaderConstants(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, const SDL_Texture *texture, VULKAN_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) {
switch (texture->format) {
case SDL_PIXELFORMAT_YV12:
case SDL_PIXELFORMAT_IYUV:
case SDL_PIXELFORMAT_NV12:
case SDL_PIXELFORMAT_NV21:
constants->input_type = INPUTTYPE_SRGB;
break;
case SDL_PIXELFORMAT_P010:
constants->input_type = INPUTTYPE_HDR10;
break;
default:
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;
}
if (texture->format == SDL_PIXELFORMAT_INDEX8) {
switch (cmd->data.draw.texture_scale_mode) {
case SDL_SCALEMODE_NEAREST:
constants->texture_type = TEXTURETYPE_PALETTE_NEAREST;
break;
case SDL_SCALEMODE_LINEAR:
constants->texture_type = TEXTURETYPE_PALETTE_LINEAR;
break;
case SDL_SCALEMODE_PIXELART:
constants->texture_type = TEXTURETYPE_PALETTE_PIXELART;
break;
default:
SDL_assert(!"Unknown scale mode");
break;
}
} else {
if (cmd->data.draw.texture_scale_mode == SDL_SCALEMODE_PIXELART) {
constants->texture_type = TEXTURETYPE_RGB_PIXELART;
} else {
constants->texture_type = TEXTURETYPE_RGB;
}
}
if (constants->texture_type == TEXTURETYPE_PALETTE_LINEAR ||
constants->texture_type == TEXTURETYPE_PALETTE_PIXELART ||
constants->texture_type == TEXTURETYPE_RGB_PIXELART) {
constants->texture_width = texture->w;
constants->texture_height = texture->h;
constants->texel_width = 1.0f / constants->texture_width;
constants->texel_height = 1.0f / constants->texture_height;
}
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 && output_headroom > 0.0f) {
constants->tonemap_method = TONEMAP_CHROME;
constants->tonemap_factor1 = (output_headroom / (texture->HDR_headroom * texture->HDR_headroom));
constants->tonemap_factor2 = (1.0f / output_headroom);
}
}
}
static VULKAN_Shader SelectShader(const VULKAN_PixelShaderConstants *shader_constants)
{
if (!shader_constants) {
return SHADER_SOLID;
}
if (shader_constants->texture_type == TEXTURETYPE_RGB &&
shader_constants->input_type == INPUTTYPE_UNSPECIFIED &&
shader_constants->tonemap_method == TONEMAP_NONE) {
return SHADER_RGB;
}
return SHADER_ADVANCED;
}
static VkDescriptorPool VULKAN_AllocateDescriptorPool(VULKAN_RenderData *rendererData)
{
VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
VkDescriptorPoolSize descriptorPoolSizes[3];
VkResult result;
descriptorPoolSizes[0].descriptorCount = SDL_VULKAN_MAX_DESCRIPTOR_SETS;
descriptorPoolSizes[0].type = VK_DESCRIPTOR_TYPE_SAMPLER;
descriptorPoolSizes[1].descriptorCount = SDL_VULKAN_MAX_DESCRIPTOR_SETS;
descriptorPoolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorPoolSizes[2].descriptorCount = SDL_VULKAN_MAX_DESCRIPTOR_SETS;
descriptorPoolSizes[2].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
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, &descriptorPool);
if (result != VK_SUCCESS) {
SET_ERROR_CODE("vkCreateDescrptorPool()", result);
return VK_NULL_HANDLE;
}
return descriptorPool;
}
static VkResult VULKAN_CreateDescriptorSetAndPipelineLayout(VULKAN_RenderData *rendererData, VkSampler samplerYcbcr, VkDescriptorSetLayout *descriptorSetLayoutOut,
VkPipelineLayout *pipelineLayoutOut)
{
VkResult result;
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = { 0 };
descriptorSetLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
descriptorSetLayoutCreateInfo.flags = 0;
VkDescriptorSetLayoutBinding layoutBindings[3];
layoutBindings[0].binding = 0;
layoutBindings[0].descriptorCount = 1;
layoutBindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
layoutBindings[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
layoutBindings[0].pImmutableSamplers = NULL;
layoutBindings[1].binding = 1;
layoutBindings[1].descriptorCount = 1;
layoutBindings[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
layoutBindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
layoutBindings[1].pImmutableSamplers = (samplerYcbcr != VK_NULL_HANDLE) ? &samplerYcbcr : NULL;
layoutBindings[2].binding = 2;
layoutBindings[2].descriptorCount = 1;
layoutBindings[2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
layoutBindings[2].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
layoutBindings[2].pImmutableSamplers = (samplerYcbcr != VK_NULL_HANDLE) ? &samplerYcbcr : NULL;
descriptorSetLayoutCreateInfo.bindingCount = 3;
descriptorSetLayoutCreateInfo.pBindings = layoutBindings;
result = vkCreateDescriptorSetLayout(rendererData->device, &descriptorSetLayoutCreateInfo, NULL, descriptorSetLayoutOut);
if (result != VK_SUCCESS) {
SET_ERROR_CODE("vkCreateDescriptorSetLayout()", result);
return result;
}
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = { 0 };
VkPushConstantRange pushConstantRange;
pushConstantRange.size = sizeof( VULKAN_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 = descriptorSetLayoutOut;
pipelineLayoutCreateInfo.pushConstantRangeCount = 1;
pipelineLayoutCreateInfo.pPushConstantRanges = &pushConstantRange;
result = vkCreatePipelineLayout(rendererData->device, &pipelineLayoutCreateInfo, NULL, pipelineLayoutOut);
if (result != VK_SUCCESS) {
SET_ERROR_CODE("vkCreatePipelineLayout()", result);
return result;
}
return result;
}
static VkDescriptorSet VULKAN_AllocateDescriptorSet(SDL_Renderer *renderer, VULKAN_Shader shader, VkDescriptorSetLayout descriptorSetLayout,
VkBuffer constantBuffer, VkDeviceSize constantBufferOffset, int numImages, VkImageView *imageViews, int numSamplers, VkSampler *samplers)
{
VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
uint32_t currentDescriptorPoolIndex = rendererData->currentDescriptorPoolIndex;
VkDescriptorPool descriptorPool = rendererData->descriptorPools[rendererData->currentCommandBufferIndex][currentDescriptorPoolIndex];
VkDescriptorSetAllocateInfo descriptorSetAllocateInfo = { 0 };
descriptorSetAllocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
descriptorSetAllocateInfo.descriptorSetCount = 1;
descriptorSetAllocateInfo.descriptorPool = descriptorPool;
descriptorSetAllocateInfo.pSetLayouts = &descriptorSetLayout;
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);
}
if (result != VK_SUCCESS) {
currentDescriptorPoolIndex++;
if (currentDescriptorPoolIndex < rendererData->numDescriptorPools[rendererData->currentCommandBufferIndex]) {
descriptorPool = rendererData->descriptorPools[rendererData->currentCommandBufferIndex][currentDescriptorPoolIndex];
descriptorSetAllocateInfo.descriptorPool = descriptorPool;
result = vkAllocateDescriptorSets(rendererData->device, &descriptorSetAllocateInfo, &descriptorSet);
if (result != VK_SUCCESS) {
SDL_SetError("Unable to allocate descriptor set");
return VK_NULL_HANDLE;
}
rendererData->currentDescriptorPoolIndex = currentDescriptorPoolIndex;
rendererData->currentDescriptorSetIndex = 0;
}
else {
descriptorPool = VULKAN_AllocateDescriptorPool(rendererData);
if (descriptorPool == VK_NULL_HANDLE) {
return VK_NULL_HANDLE;
}
rendererData->numDescriptorPools[rendererData->currentCommandBufferIndex]++;
VkDescriptorPool *descriptorPools = (VkDescriptorPool *)SDL_realloc(rendererData->descriptorPools[rendererData->currentCommandBufferIndex],
sizeof(VkDescriptorPool) * rendererData->numDescriptorPools[rendererData->currentCommandBufferIndex]);
descriptorPools[rendererData->numDescriptorPools[rendererData->currentCommandBufferIndex] - 1] = descriptorPool;
rendererData->descriptorPools[rendererData->currentCommandBufferIndex] = descriptorPools;
rendererData->currentDescriptorPoolIndex = currentDescriptorPoolIndex;
rendererData->currentDescriptorSetIndex = 0;
return VULKAN_AllocateDescriptorSet(renderer, shader, descriptorSetLayout, constantBuffer, constantBufferOffset, numImages, imageViews, numSamplers, samplers);
}
}
rendererData->currentDescriptorSetIndex++;
VkDescriptorImageInfo combinedImageSamplerDescriptor[2];
VkDescriptorBufferInfo bufferDescriptor = { 0 };
bufferDescriptor.buffer = constantBuffer;
bufferDescriptor.offset = constantBufferOffset;
bufferDescriptor.range = sizeof(VULKAN_PixelShaderConstants);
VkWriteDescriptorSet descriptorWrites[3];
SDL_zero(descriptorWrites);
uint32_t descriptorCount = 1;
descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrites[0].dstSet = descriptorSet;
descriptorWrites[0].dstBinding = 0;
descriptorWrites[0].dstArrayElement = 0;
descriptorWrites[0].descriptorCount = 1;
descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
descriptorWrites[0].pBufferInfo = &bufferDescriptor;
SDL_assert(numSamplers == numImages);
for (int i = 0; i < numImages; ++i) {
SDL_assert(i < SDL_arraysize(combinedImageSamplerDescriptor));
VkDescriptorImageInfo *pImageInfo = &combinedImageSamplerDescriptor[i];
SDL_zerop(pImageInfo);
if (descriptorSetLayout == rendererData->descriptorSetLayout) {
pImageInfo->sampler = samplers[i];
} else {
}
pImageInfo->imageView = imageViews[i];
pImageInfo->imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
SDL_assert(descriptorCount < SDL_arraysize(descriptorWrites));
VkWriteDescriptorSet *pDescriptorSet = &descriptorWrites[descriptorCount++];
pDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
pDescriptorSet->dstSet = descriptorSet;
pDescriptorSet->dstBinding = 1 + i;
pDescriptorSet->dstArrayElement = 0;
pDescriptorSet->descriptorCount = 1;
pDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
pDescriptorSet->pImageInfo = pImageInfo;
}
vkUpdateDescriptorSets(rendererData->device, descriptorCount, descriptorWrites, 0, NULL);
return descriptorSet;
}
static bool VULKAN_SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, VkPipelineLayout pipelineLayout, VkDescriptorSetLayout descriptorSetLayout,
const VULKAN_PixelShaderConstants *shader_constants, VkPrimitiveTopology topology, int numImages, VkImageView *imageViews, int numSamplers, VkSampler *samplers, const Float4X4 *matrix, VULKAN_DrawStateCache *stateCache)
{
VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
const SDL_BlendMode blendMode = cmd->data.draw.blend;
VkFormat format = rendererData->surfaceFormat.format;
const Float4X4 *newmatrix = matrix ? matrix : &rendererData->identity;
bool updateConstants = false;
VULKAN_Shader shader = SelectShader(shader_constants);
VULKAN_PixelShaderConstants solid_constants;
VkDescriptorSet descriptorSet;
VkBuffer constantBuffer;
VkDeviceSize constantBufferOffset;
int i;
if (!VULKAN_ActivateCommandBuffer(renderer, VK_ATTACHMENT_LOAD_OP_LOAD, NULL, stateCache)) {
return false;
}
if (!rendererData->currentPipelineState ||
rendererData->currentPipelineState->shader != shader ||
rendererData->currentPipelineState->blendMode != blendMode ||
rendererData->currentPipelineState->topology != topology ||
rendererData->currentPipelineState->format != format ||
rendererData->currentPipelineState->pipelineLayout != pipelineLayout ||
rendererData->currentPipelineState->descriptorSetLayout != descriptorSetLayout) {
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 &&
candidatePiplineState->pipelineLayout == pipelineLayout &&
candidatePiplineState->descriptorSetLayout == descriptorSetLayout) {
rendererData->currentPipelineState = candidatePiplineState;
break;
}
}
if (!rendererData->currentPipelineState) {
rendererData->currentPipelineState = VULKAN_CreatePipelineState(renderer, shader, pipelineLayout, descriptorSetLayout, blendMode, topology, format);
}
if (!rendererData->currentPipelineState) {
return SDL_SetError("Unable to create required pipeline state");
}
vkCmdBindPipeline(rendererData->currentCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, rendererData->currentPipelineState->pipeline);
updateConstants = true;
}
if (rendererData->viewportDirty) {
if (VULKAN_UpdateViewport(renderer)) {
updateConstants = true;
}
}
if (rendererData->cliprectDirty) {
VULKAN_UpdateClipRect(renderer);
}
if (updateConstants == 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][rendererData->currentConstantBufferIndex].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) {
rendererData->currentConstantBufferOffset = 0;
constantBufferOffset = 0;
}
else {
VkDeviceSize alignment = rendererData->physicalDeviceProperties.limits.minUniformBufferOffsetAlignment;
SDL_assert(rendererData->currentConstantBufferOffset >= 0 );
rendererData->currentConstantBufferOffset += (int32_t)(sizeof(VULKAN_PixelShaderConstants) + alignment - 1) & ~(alignment - 1);
constantBufferOffset = rendererData->currentConstantBufferOffset;
}
if (rendererData->currentConstantBufferOffset >= SDL_VULKAN_CONSTANT_BUFFER_DEFAULT_SIZE) {
uint32_t newConstantBufferIndex = (rendererData->currentConstantBufferIndex + 1);
if (newConstantBufferIndex >= rendererData->numConstantBuffers[rendererData->currentCommandBufferIndex]) {
VULKAN_Buffer newConstantBuffer;
VkResult result = VULKAN_AllocateBuffer(rendererData,
SDL_VULKAN_CONSTANT_BUFFER_DEFAULT_SIZE,
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
&newConstantBuffer);
if (result != VK_SUCCESS) {
return false;
}
rendererData->numConstantBuffers[rendererData->currentCommandBufferIndex]++;
VULKAN_Buffer *newConstantBuffers = (VULKAN_Buffer *)SDL_realloc(rendererData->constantBuffers[rendererData->currentCommandBufferIndex],
sizeof(VULKAN_Buffer) * rendererData->numConstantBuffers[rendererData->currentCommandBufferIndex]);
newConstantBuffers[rendererData->numConstantBuffers[rendererData->currentCommandBufferIndex] - 1] = newConstantBuffer;
rendererData->constantBuffers[rendererData->currentCommandBufferIndex] = newConstantBuffers;
}
rendererData->currentConstantBufferIndex = newConstantBufferIndex;
rendererData->currentConstantBufferOffset = 0;
constantBufferOffset = 0;
constantBuffer = rendererData->constantBuffers[rendererData->currentCommandBufferIndex][rendererData->currentConstantBufferIndex].buffer;
}
SDL_memcpy(&rendererData->currentPipelineState->shader_constants, shader_constants, sizeof(*shader_constants));
uint8_t *dst = (uint8_t *)rendererData->constantBuffers[rendererData->currentCommandBufferIndex][rendererData->currentConstantBufferIndex].mappedBufferPtr;
dst += constantBufferOffset;
SDL_memcpy(dst, &rendererData->currentPipelineState->shader_constants, sizeof(VULKAN_PixelShaderConstants));
}
descriptorSet = VULKAN_AllocateDescriptorSet(renderer, shader, descriptorSetLayout, constantBuffer, constantBufferOffset, numImages, imageViews, numSamplers, samplers);
if (descriptorSet == VK_NULL_HANDLE) {
return false;
}
vkCmdBindDescriptorSets(rendererData->currentCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, rendererData->currentPipelineState->pipelineLayout,
0, 1, &descriptorSet, 0, NULL);
return true;
}
static VkSampler VULKAN_GetSampler(VULKAN_RenderData *data, SDL_PixelFormat format, SDL_ScaleMode scale_mode, SDL_TextureAddressMode address_u, SDL_TextureAddressMode address_v)
{
if (format == SDL_PIXELFORMAT_INDEX8) {
scale_mode = SDL_SCALEMODE_NEAREST;
}
Uint32 key = RENDER_SAMPLER_HASHKEY(scale_mode, address_u, address_v);
SDL_assert(key < SDL_arraysize(data->samplers));
if (!data->samplers[key]) {
VkSamplerCreateInfo samplerCreateInfo = { 0 };
samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
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;
switch (scale_mode) {
case SDL_SCALEMODE_NEAREST:
samplerCreateInfo.magFilter = VK_FILTER_NEAREST;
samplerCreateInfo.minFilter = VK_FILTER_NEAREST;
break;
case SDL_SCALEMODE_PIXELART: case SDL_SCALEMODE_LINEAR:
samplerCreateInfo.magFilter = VK_FILTER_LINEAR;
samplerCreateInfo.minFilter = VK_FILTER_LINEAR;
break;
default:
SDL_SetError("Unknown scale mode: %d", scale_mode);
return VK_NULL_HANDLE;
}
switch (address_u) {
case SDL_TEXTURE_ADDRESS_CLAMP:
samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
break;
case SDL_TEXTURE_ADDRESS_WRAP:
samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
break;
default:
SDL_SetError("Unknown texture address mode: %d", address_u);
return VK_NULL_HANDLE;
}
switch (address_v) {
case SDL_TEXTURE_ADDRESS_CLAMP:
samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
break;
case SDL_TEXTURE_ADDRESS_WRAP:
samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
break;
default:
SDL_SetError("Unknown texture address mode: %d", address_v);
return VK_NULL_HANDLE;
}
VkResult result = vkCreateSampler(data->device, &samplerCreateInfo, NULL, &data->samplers[key]);
if (result != VK_SUCCESS) {
SET_ERROR_CODE("vkCreateSampler()", result);
return VK_NULL_HANDLE;
}
}
return data->samplers[key];
}
static 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->internal;
VULKAN_TextureData *textureData = (VULKAN_TextureData *)texture->internal;
int numImageViews = 0;
VkImageView imageViews[2];
int numSamplers = 0;
VkSampler samplers[2];
VULKAN_PixelShaderConstants constants;
VkDescriptorSetLayout descriptorSetLayout = (textureData->descriptorSetLayoutYcbcr != VK_NULL_HANDLE) ? textureData->descriptorSetLayoutYcbcr : rendererData->descriptorSetLayout;
VkPipelineLayout pipelineLayout = (textureData->pipelineLayoutYcbcr != VK_NULL_HANDLE) ? textureData->pipelineLayoutYcbcr : rendererData->pipelineLayout;
VULKAN_SetupShaderConstants(renderer, cmd, texture, &constants);
if (textureData->mainImage.imageLayout != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
bool stoppedRenderPass = false;
if (rendererData->currentRenderPass != VK_NULL_HANDLE) {
vkCmdEndRenderPass(rendererData->currentCommandBuffer);
rendererData->currentRenderPass = VK_NULL_HANDLE;
stoppedRenderPass = 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);
}
}
imageViews[numImageViews++] = textureData->mainImage.imageView;
samplers[numSamplers] = VULKAN_GetSampler(rendererData, texture->format, cmd->data.draw.texture_scale_mode, cmd->data.draw.texture_address_mode_u, cmd->data.draw.texture_address_mode_v);
if (samplers[numSamplers] == VK_NULL_HANDLE) {
return false;
}
++numSamplers;
if (texture->palette) {
VULKAN_PaletteData *palette = (VULKAN_PaletteData *)texture->palette->internal;
if (palette->image.imageLayout != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
bool stoppedRenderPass = false;
if (rendererData->currentRenderPass != VK_NULL_HANDLE) {
vkCmdEndRenderPass(rendererData->currentCommandBuffer);
rendererData->currentRenderPass = VK_NULL_HANDLE;
stoppedRenderPass = 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,
palette->image.image,
&palette->image.imageLayout);
if (stoppedRenderPass) {
VULKAN_BeginRenderPass(rendererData, VK_ATTACHMENT_LOAD_OP_LOAD, NULL);
}
}
imageViews[numImageViews++] = palette->image.imageView;
samplers[numSamplers] = VULKAN_GetSampler(rendererData, SDL_PIXELFORMAT_UNKNOWN, SDL_SCALEMODE_NEAREST, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP);
if (samplers[numSamplers] == VK_NULL_HANDLE) {
return false;
}
++numSamplers;
} else {
imageViews[numImageViews++] = imageViews[0];
samplers[numSamplers++] = samplers[0];
}
return VULKAN_SetDrawState(renderer, cmd, pipelineLayout, descriptorSetLayout, &constants, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, numImageViews, imageViews, numSamplers, samplers, 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->internal;
vkCmdDraw(rendererData->currentCommandBuffer, (uint32_t)vertexCount, 1, (uint32_t)vertexStart, 0);
}
static void VULKAN_InvalidateCachedState(SDL_Renderer *renderer)
{
VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
rendererData->currentPipelineState = NULL;
rendererData->cliprectDirty = true;
}
static bool VULKAN_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize)
{
VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
VkSurfaceTransformFlagBitsKHR currentRotation = VULKAN_GetRotationForCurrentRenderTarget(rendererData);
VULKAN_DrawStateCache stateCache;
SDL_zero(stateCache);
if (!rendererData->device) {
return SDL_SetError("Device lost and couldn't be recovered");
}
if(rendererData->currentViewportRotation != currentRotation) {
rendererData->currentViewportRotation = currentRotation;
rendererData->viewportDirty = true;
rendererData->cliprectDirty = true;
}
if (rendererData->recreateSwapchain) {
if (VULKAN_UpdateForWindowSizeChange(renderer) != VK_SUCCESS) {
return false;
}
rendererData->recreateSwapchain = false;
}
if (!VULKAN_UpdateVertexBuffer(renderer, vertices, vertsize, &stateCache)) {
return false;
}
while (cmd) {
switch (cmd->command) {
case SDL_RENDERCMD_SETDRAWCOLOR:
{
break; }
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 = true;
rendererData->cliprectDirty = true;
}
break;
}
case SDL_RENDERCMD_SETCLIPRECT:
{
const SDL_Rect *rect = &cmd->data.cliprect.rect;
if (rendererData->currentCliprectEnabled != cmd->data.cliprect.enabled) {
rendererData->currentCliprectEnabled = cmd->data.cliprect.enabled;
rendererData->cliprectDirty = true;
}
if (SDL_memcmp(&rendererData->currentCliprect, rect, sizeof(*rect)) != 0) {
SDL_copyp(&rendererData->currentCliprect, rect);
rendererData->cliprectDirty = true;
}
break;
}
case SDL_RENDERCMD_CLEAR:
{
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_LINES:
{
size_t count = cmd->data.draw.count;
const size_t first = cmd->data.draw.first;
const size_t start = first / sizeof(VULKAN_VertexPositionColor);
const VULKAN_VertexPositionColor *verts = (VULKAN_VertexPositionColor *)(((Uint8 *)vertices) + first);
bool have_point_draw_state = false;
size_t line_start = 0;
size_t line_end = line_start + count - 1;
if (verts[line_start].pos[0] != verts[line_end].pos[0] || verts[line_start].pos[1] != verts[line_end].pos[1]) {
VULKAN_SetDrawState(renderer, cmd, rendererData->pipelineLayout, rendererData->descriptorSetLayout, NULL, VK_PRIMITIVE_TOPOLOGY_POINT_LIST, 0, NULL, 0, NULL, NULL, &stateCache);
VULKAN_DrawPrimitives(renderer, VK_PRIMITIVE_TOPOLOGY_POINT_LIST, start + line_end, 1);
have_point_draw_state = true;
}
if (count > 2) {
VULKAN_SetDrawState(renderer, cmd, rendererData->pipelineLayout, rendererData->descriptorSetLayout, NULL, VK_PRIMITIVE_TOPOLOGY_LINE_STRIP, 0, NULL, 0, NULL, NULL, &stateCache);
VULKAN_DrawPrimitives(renderer, VK_PRIMITIVE_TOPOLOGY_LINE_STRIP, start, count);
} else {
SDL_RenderCommand *finalcmd = cmd;
SDL_RenderCommand *nextcmd;
float thiscolorscale = cmd->data.draw.color_scale;
SDL_BlendMode thisblend = cmd->data.draw.blend;
for (nextcmd = cmd->next; nextcmd; nextcmd = nextcmd->next) {
const SDL_RenderCommandType nextcmdtype = nextcmd->command;
if (nextcmdtype != SDL_RENDERCMD_DRAW_LINES) {
if (nextcmdtype == SDL_RENDERCMD_SETDRAWCOLOR) {
continue;
}
break; } else if (nextcmd->data.draw.count != 2) {
break; } else if (nextcmd->data.draw.blend != thisblend ||
nextcmd->data.draw.color_scale != thiscolorscale) {
break; } else {
finalcmd = nextcmd;
line_start = count;
line_end = line_start + nextcmd->data.draw.count - 1;
if (verts[line_start].pos[0] != verts[line_end].pos[0] || verts[line_start].pos[1] != verts[line_end].pos[1]) {
if (!have_point_draw_state) {
VULKAN_SetDrawState(renderer, cmd, rendererData->pipelineLayout, rendererData->descriptorSetLayout, NULL, VK_PRIMITIVE_TOPOLOGY_POINT_LIST, 0, NULL, 0, NULL, NULL, &stateCache);
have_point_draw_state = true;
}
VULKAN_DrawPrimitives(renderer, VK_PRIMITIVE_TOPOLOGY_POINT_LIST, start + line_end, 1);
}
count += nextcmd->data.draw.count;
}
}
VULKAN_SetDrawState(renderer, cmd, rendererData->pipelineLayout, rendererData->descriptorSetLayout, NULL, VK_PRIMITIVE_TOPOLOGY_LINE_LIST, 0, NULL, 0, NULL, NULL, &stateCache);
VULKAN_DrawPrimitives(renderer, VK_PRIMITIVE_TOPOLOGY_LINE_LIST, start, count);
cmd = finalcmd; }
break;
}
case SDL_RENDERCMD_FILL_RECTS: break;
case SDL_RENDERCMD_COPY: break;
case SDL_RENDERCMD_COPY_EX: break;
case SDL_RENDERCMD_DRAW_POINTS:
case SDL_RENDERCMD_GEOMETRY:
{
float thiscolorscale = cmd->data.draw.color_scale;
SDL_Texture *thistexture = cmd->data.draw.texture;
SDL_BlendMode thisblend = cmd->data.draw.blend;
SDL_ScaleMode thisscalemode = cmd->data.draw.texture_scale_mode;
SDL_TextureAddressMode thisaddressmode_u = cmd->data.draw.texture_address_mode_u;
SDL_TextureAddressMode thisaddressmode_v = cmd->data.draw.texture_address_mode_v;
const SDL_RenderCommandType thiscmdtype = cmd->command;
SDL_RenderCommand *finalcmd = cmd;
SDL_RenderCommand *nextcmd;
size_t count = cmd->data.draw.count;
const size_t first = cmd->data.draw.first;
const size_t start = first / sizeof(VULKAN_VertexPositionColor);
for (nextcmd = cmd->next; nextcmd; nextcmd = nextcmd->next) {
const SDL_RenderCommandType nextcmdtype = nextcmd->command;
if (nextcmdtype != thiscmdtype) {
if (nextcmdtype == SDL_RENDERCMD_SETDRAWCOLOR) {
continue;
}
break; } else if (nextcmd->data.draw.texture != thistexture ||
nextcmd->data.draw.texture_scale_mode != thisscalemode ||
nextcmd->data.draw.texture_address_mode_u != thisaddressmode_u ||
nextcmd->data.draw.texture_address_mode_v != thisaddressmode_v ||
nextcmd->data.draw.blend != thisblend ||
nextcmd->data.draw.color_scale != thiscolorscale) {
break; } else {
finalcmd = nextcmd; count += nextcmd->data.draw.count;
}
}
if (thiscmdtype == SDL_RENDERCMD_GEOMETRY) {
if (thistexture) {
VULKAN_SetCopyState(renderer, cmd, NULL, &stateCache);
} else {
VULKAN_SetDrawState(renderer, cmd, rendererData->pipelineLayout, rendererData->descriptorSetLayout, NULL, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, NULL, 0, NULL, NULL, &stateCache);
}
VULKAN_DrawPrimitives(renderer, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, start, count);
} else {
VULKAN_SetDrawState(renderer, cmd, rendererData->pipelineLayout, rendererData->descriptorSetLayout, NULL, VK_PRIMITIVE_TOPOLOGY_POINT_LIST, 0, NULL, 0, NULL, NULL, &stateCache);
VULKAN_DrawPrimitives(renderer, VK_PRIMITIVE_TOPOLOGY_POINT_LIST, start, count);
}
cmd = finalcmd; break;
}
case SDL_RENDERCMD_NO_OP:
break;
}
cmd = cmd->next;
}
return true;
}
static SDL_Surface *VULKAN_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect)
{
VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
VkImage backBuffer;
VkImageLayout *imageLayout;
VULKAN_Buffer readbackBuffer;
VkDeviceSize pixelSize;
VkDeviceSize length;
VkDeviceSize readbackBufferSize;
VkFormat vkFormat;
SDL_Surface *output;
VULKAN_EnsureCommandBuffer(rendererData);
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, 0);
length = rect->w * pixelSize;
readbackBufferSize = length * rect->h;
if (VULKAN_AllocateBuffer(rendererData, readbackBufferSize,
VK_BUFFER_USAGE_TRANSFER_DST_BIT,
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
&readbackBuffer) != VK_SUCCESS) {
return NULL;
}
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);
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);
VULKAN_IssueBatch(rendererData);
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,
(int)length);
VULKAN_DestroyBuffer(rendererData, &readbackBuffer);
return output;
}
static bool VULKAN_AddVulkanRenderSemaphores(SDL_Renderer *renderer, Uint32 wait_stage_mask, Sint64 wait_semaphore, Sint64 signal_semaphore)
{
VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
if (wait_semaphore) {
if (rendererData->waitRenderSemaphoreCount == rendererData->waitRenderSemaphoreMax) {
VkPipelineStageFlags *waitDestStageMasks = (VkPipelineStageFlags *)SDL_realloc(rendererData->waitDestStageMasks, (rendererData->waitRenderSemaphoreMax + 2) * sizeof(*waitDestStageMasks));
if (!waitDestStageMasks) {
return false;
}
rendererData->waitDestStageMasks = waitDestStageMasks;
VkSemaphore *semaphores = (VkSemaphore *)SDL_realloc(rendererData->waitRenderSemaphores, (rendererData->waitRenderSemaphoreMax + 2) * sizeof(*semaphores));
if (!semaphores) {
return false;
}
rendererData->waitRenderSemaphores = semaphores;
++rendererData->waitRenderSemaphoreMax;
}
rendererData->waitDestStageMasks[rendererData->waitRenderSemaphoreCount] = wait_stage_mask;
rendererData->waitRenderSemaphores[rendererData->waitRenderSemaphoreCount] = (VkSemaphore)wait_semaphore;
++rendererData->waitRenderSemaphoreCount;
}
if (signal_semaphore) {
if (rendererData->signalRenderSemaphoreCount == rendererData->signalRenderSemaphoreMax) {
VkSemaphore *semaphores = (VkSemaphore *)SDL_realloc(rendererData->signalRenderSemaphores, (rendererData->signalRenderSemaphoreMax + 2) * sizeof(*semaphores));
if (!semaphores) {
return false;
}
rendererData->signalRenderSemaphores = semaphores;
++rendererData->signalRenderSemaphoreMax;
}
rendererData->signalRenderSemaphores[rendererData->signalRenderSemaphoreCount] = (VkSemaphore)signal_semaphore;
++rendererData->signalRenderSemaphoreCount;
}
return true;
}
static bool VULKAN_RenderPresent(SDL_Renderer *renderer)
{
VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
VkResult result = VK_SUCCESS;
if (!rendererData->device) {
return SDL_SetError("Device lost and couldn't be recovered");
}
if (rendererData->currentCommandBuffer) {
rendererData->currentPipelineState = VK_NULL_HANDLE;
rendererData->viewportDirty = 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) {
SET_ERROR_CODE("vkResetFences()", result);
return false;
}
VkPipelineStageFlags waitDestStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
VkSubmitInfo submitInfo = { 0 };
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
if (rendererData->waitRenderSemaphoreCount > 0) {
Uint32 additionalSemaphoreCount = (rendererData->currentImageAvailableSemaphore != VK_NULL_HANDLE) ? 1 : 0;
submitInfo.waitSemaphoreCount = rendererData->waitRenderSemaphoreCount + additionalSemaphoreCount;
if (additionalSemaphoreCount > 0) {
rendererData->waitRenderSemaphores[rendererData->waitRenderSemaphoreCount] = rendererData->currentImageAvailableSemaphore;
rendererData->waitDestStageMasks[rendererData->waitRenderSemaphoreCount] = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
}
submitInfo.pWaitSemaphores = rendererData->waitRenderSemaphores;
submitInfo.pWaitDstStageMask = rendererData->waitDestStageMasks;
rendererData->waitRenderSemaphoreCount = 0;
} else if (rendererData->currentImageAvailableSemaphore != VK_NULL_HANDLE) {
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = &rendererData->currentImageAvailableSemaphore;
submitInfo.pWaitDstStageMask = &waitDestStageMask;
}
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &rendererData->currentCommandBuffer;
if (rendererData->signalRenderSemaphoreCount > 0) {
submitInfo.signalSemaphoreCount = rendererData->signalRenderSemaphoreCount + 1;
rendererData->signalRenderSemaphores[rendererData->signalRenderSemaphoreCount] = rendererData->renderingFinishedSemaphores[rendererData->currentCommandBufferIndex];
submitInfo.pSignalSemaphores = rendererData->signalRenderSemaphores;
rendererData->signalRenderSemaphoreCount = 0;
} else {
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = &rendererData->renderingFinishedSemaphores[rendererData->currentCommandBufferIndex];
}
result = vkQueueSubmit(rendererData->graphicsQueue, 1, &submitInfo, rendererData->fences[rendererData->currentCommandBufferIndex]);
if (result != VK_SUCCESS) {
if (result == VK_ERROR_DEVICE_LOST) {
if (VULKAN_HandleDeviceLost(renderer)) {
SDL_SetError("Present failed, device lost");
} else {
}
} else {
SET_ERROR_CODE("vkQueueSubmit()", result);
}
return false;
}
rendererData->currentCommandBuffer = VK_NULL_HANDLE;
rendererData->currentImageAvailableSemaphore = VK_NULL_HANDLE;
VkPresentInfoKHR presentInfo = { 0 };
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = &rendererData->renderingFinishedSemaphores[rendererData->currentCommandBufferIndex];
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 )) {
SET_ERROR_CODE("vkQueuePresentKHR()", result);
return false;
}
rendererData->currentCommandBufferIndex = ( rendererData->currentCommandBufferIndex + 1 ) % rendererData->swapchainImageCount;
result = vkWaitForFences(rendererData->device, 1, &rendererData->fences[rendererData->currentCommandBufferIndex], VK_TRUE, UINT64_MAX);
if (result != VK_SUCCESS) {
if (result == VK_ERROR_DEVICE_LOST) {
if (VULKAN_HandleDeviceLost(renderer)) {
SDL_SetError("Present failed, device lost");
} else {
}
} else {
SET_ERROR_CODE("vkWaitForFences()", result);
}
return false;
}
VULKAN_AcquireNextSwapchainImage(renderer);
}
return true;
}
static bool VULKAN_SetVSync(SDL_Renderer *renderer, const int vsync)
{
VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
switch (vsync) {
case -1:
case 0:
case 1:
break;
default:
return SDL_Unsupported();
}
if (vsync != rendererData->vsync) {
rendererData->vsync = vsync;
rendererData->recreateSwapchain = true;
}
return true;
}
static bool VULKAN_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_PropertiesID create_props)
{
VULKAN_RenderData *rendererData;
SDL_SetupRendererColorspace(renderer, create_props);
if (renderer->output_colorspace != SDL_COLORSPACE_SRGB &&
renderer->output_colorspace != SDL_COLORSPACE_SRGB_LINEAR
) {
return SDL_SetError("Unsupported output colorspace");
}
rendererData = (VULKAN_RenderData *)SDL_calloc(1, sizeof(*rendererData));
if (!rendererData) {
return false;
}
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;
rendererData->create_props = SDL_CreateProperties();
if (!SDL_CopyProperties(create_props, rendererData->create_props)) {
SDL_free(rendererData);
return false;
}
renderer->WindowEvent = VULKAN_WindowEvent;
renderer->SupportsBlendMode = VULKAN_SupportsBlendMode;
renderer->CreatePalette = VULKAN_CreatePalette;
renderer->UpdatePalette = VULKAN_UpdatePalette;
renderer->DestroyPalette = VULKAN_DestroyPalette;
renderer->CreateTexture = VULKAN_CreateTexture;
renderer->UpdateTexture = VULKAN_UpdateTexture;
#ifdef SDL_HAVE_YUV
renderer->UpdateTextureYUV = VULKAN_UpdateTextureYUV;
renderer->UpdateTextureNV = VULKAN_UpdateTextureNV;
#endif
renderer->LockTexture = VULKAN_LockTexture;
renderer->UnlockTexture = VULKAN_UnlockTexture;
renderer->SetRenderTarget = VULKAN_SetRenderTarget;
renderer->QueueSetViewport = VULKAN_QueueNoOp;
renderer->QueueSetDrawColor = VULKAN_QueueNoOp;
renderer->QueueDrawPoints = VULKAN_QueueDrawPoints;
renderer->QueueDrawLines = VULKAN_QueueDrawPoints; renderer->QueueGeometry = VULKAN_QueueGeometry;
renderer->InvalidateCachedState = VULKAN_InvalidateCachedState;
renderer->RunCommandQueue = VULKAN_RunCommandQueue;
renderer->RenderReadPixels = VULKAN_RenderReadPixels;
renderer->AddVulkanRenderSemaphores = VULKAN_AddVulkanRenderSemaphores;
renderer->RenderPresent = VULKAN_RenderPresent;
renderer->DestroyTexture = VULKAN_DestroyTexture;
renderer->DestroyRenderer = VULKAN_DestroyRenderer;
renderer->SetVSync = VULKAN_SetVSync;
renderer->internal = rendererData;
VULKAN_InvalidateCachedState(renderer);
renderer->name = VULKAN_RenderDriver.name;
SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_BGRA32); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_RGBA32);
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ABGR8888);
#endif
SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ABGR2101010);
SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_RGBA64_FLOAT);
SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_INDEX8);
SDL_SetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER, 16384);
renderer->window = window;
if (VULKAN_CreateDeviceResources(renderer, create_props) != VK_SUCCESS) {
return false;
}
if (VULKAN_CreateWindowSizeDependentResources(renderer) != VK_SUCCESS) {
return false;
}
#ifdef SDL_HAVE_YUV
if (rendererData->supportsKHRSamplerYCbCrConversion) {
SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12);
SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV);
SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV12);
SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV21);
SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P010);
}
#endif
return true;
}
SDL_RenderDriver VULKAN_RenderDriver = {
VULKAN_CreateRenderer, "vulkan"
};
#endif