Added support for using Android hardware buffers with the Vulkan renderer

This commit is contained in:
Sam Lantinga
2026-09-11 11:12:12 -07:00
parent 4b40cff5d4
commit 982333a48d
2 changed files with 358 additions and 79 deletions

View File

@@ -806,6 +806,9 @@ extern SDL_DECLSPEC SDL_Texture * SDLCALL SDL_CreateTextureFromSurface(SDL_Rende
* with the texture, if you want to wrap an existing texture.
* - `SDL_PROP_TEXTURE_CREATE_VULKAN_LAYOUT_NUMBER`: the VkImageLayout for the
* VkImage, defaults to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL.
* - `SDL_PROP_TEXTURE_CREATE_VULKAN_ANDROID_HARDWARE_BUFFER_POINTER`: the AHardwareBuffer to sample from, if you want to use an existing Android hardware buffer as the texture. You must use SDL_PIXELFORMAT_EXTERNAL_OES for the texture format.
* You can't directly update the texture or use it as a render target. If the Android buffer contents change, you must recreate the texture to pick up the changes.
* The texture holds a reference to the buffer, so you can release your own reference once the texture has been created.
*
* With the GPU renderer:
*
@@ -869,6 +872,7 @@ extern SDL_DECLSPEC SDL_Texture * SDLCALL SDL_CreateTextureWithProperties(SDL_Re
#define SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_V_NUMBER "SDL.texture.create.opengles2.texture_v"
#define SDL_PROP_TEXTURE_CREATE_VULKAN_TEXTURE_NUMBER "SDL.texture.create.vulkan.texture"
#define SDL_PROP_TEXTURE_CREATE_VULKAN_LAYOUT_NUMBER "SDL.texture.create.vulkan.layout"
#define SDL_PROP_TEXTURE_CREATE_VULKAN_ANDROID_HARDWARE_BUFFER_POINTER "SDL.texture.create.vulkan.android_hardware_buffer"
#define SDL_PROP_TEXTURE_CREATE_GPU_TEXTURE_POINTER "SDL.texture.create.gpu.texture"
#define SDL_PROP_TEXTURE_CREATE_GPU_TEXTURE_UV_POINTER "SDL.texture.create.gpu.texture_uv"
#define SDL_PROP_TEXTURE_CREATE_GPU_TEXTURE_U_POINTER "SDL.texture.create.gpu.texture_u"

View File

@@ -40,6 +40,13 @@
#include "../../video/SDL_yuv_c.h"
#include "SDL_shaders_vulkan.h"
#ifdef SDL_PLATFORM_ANDROID
#include <dlfcn.h>
#include <android/hardware_buffer.h>
typedef void (*pfnAHardwareBuffer_describe)(const AHardwareBuffer *, AHardwareBuffer_Desc *);
#endif
#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)); \
@@ -54,6 +61,12 @@
} \
SDL_SetError("%s", message) \
#ifdef SDL_PLATFORM_ANDROID
#define ANDROID_OPTIONAL_DEVICE_FUNCTION(X) VULKAN_OPTIONAL_DEVICE_FUNCTION(X)
#else
#define ANDROID_OPTIONAL_DEVICE_FUNCTION(X)
#endif
#define VULKAN_FUNCTIONS() \
VULKAN_DEVICE_FUNCTION(vkAcquireNextImageKHR) \
VULKAN_DEVICE_FUNCTION(vkAllocateCommandBuffers) \
@@ -150,6 +163,7 @@
VULKAN_OPTIONAL_INSTANCE_FUNCTION(vkGetPhysicalDeviceProperties2KHR) \
VULKAN_OPTIONAL_DEVICE_FUNCTION(vkCreateSamplerYcbcrConversionKHR) \
VULKAN_OPTIONAL_DEVICE_FUNCTION(vkDestroySamplerYcbcrConversionKHR) \
ANDROID_OPTIONAL_DEVICE_FUNCTION(vkGetAndroidHardwareBufferPropertiesANDROID) \
#define VULKAN_DEVICE_FUNCTION(name) static PFN_##name name = NULL;
#define VULKAN_GLOBAL_FUNCTION(name) static PFN_##name name = NULL;
@@ -221,6 +235,19 @@ typedef struct
SDL_FColor color;
} VULKAN_VertexPositionColor;
// Vulkan OES buffer info
typedef struct
{
#ifdef SDL_PLATFORM_ANDROID
struct AHardwareBuffer *buffer;
VkAndroidHardwareBufferPropertiesANDROID properties;
VkAndroidHardwareBufferFormatPropertiesANDROID formatProperties;
VkExternalFormatANDROID externalFormat;
#else
int dummy;
#endif
} VULKAN_ExternalBufferInfo;
// Vulkan Buffer
typedef struct
{
@@ -249,10 +276,17 @@ typedef struct
} VULKAN_PaletteData;
// YUV conversion data
typedef struct
{
Uint64 externalFormat;
VkFilter filter;
VkSamplerYcbcrConversionCreateInfoKHR samplerYcbcrConversionCreateInfo;
} VULKAN_YUVPipelineCreateInfo;
typedef struct VULKAN_YUVPipeline
{
// The information used to create this entry
VkSamplerYcbcrConversionCreateInfoKHR samplerYcbcrConversionCreateInfo;
VULKAN_YUVPipelineCreateInfo createInfo;
// Object passed to VkImageView and VkSampler for doing Ycbcr -> RGB conversion
VkSamplerYcbcrConversion samplerYcbcrConversion;
@@ -374,6 +408,8 @@ typedef struct
bool supportsEXTSwapchainColorspace;
bool supportsKHRGetPhysicalDeviceProperties2;
bool supportsKHRSamplerYCbCrConversion;
bool supportsKHRExternalMemoryCapabilities;
bool supportsANDROIDExternalMemoryAndroidHardwareBuffer;
uint32_t surfaceFormatsAllocatedCount;
uint32_t surfaceFormatsCount;
uint32_t swapchainDesiredImageCount;
@@ -532,13 +568,14 @@ static void VULKAN_DestroyBuffer(VULKAN_RenderData *rendererData, VULKAN_Buffer
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_RecordExternalImageBarrier(VULKAN_RenderData *rendererData, VkAccessFlags sourceAccessMask, VkAccessFlags destAccessMask, VkPipelineStageFlags srcStageFlags, VkPipelineStageFlags dstStageFlags, VkImageLayout destLayout, VkImage image, VkImageLayout *imageLayout);
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 VULKAN_YUVPipeline *VULKAN_GetYUVPipeline(VULKAN_RenderData *rendererData, const VkSamplerYcbcrConversionCreateInfoKHR *samplerYcbcrConversionCreateInfo);
static void VULKAN_CleanupYUVPipeline(VULKAN_RenderData *rendererData, VULKAN_YUVPipeline *data);
static VULKAN_YUVPipeline *VULKAN_GetYUVPipeline(VULKAN_RenderData *rendererData, const VULKAN_YUVPipelineCreateInfo *createInfo);
static void VULKAN_CleanupYUVPipeline(VULKAN_RenderData *rendererData, VULKAN_YUVPipeline *pipeline);
static VkSurfaceTransformFlagBitsKHR VULKAN_GetRotationForCurrentRenderTarget(VULKAN_RenderData *rendererData);
static bool VULKAN_IsDisplayRotated90Degrees(VkSurfaceTransformFlagBitsKHR rotation);
@@ -837,7 +874,7 @@ static void VULKAN_DestroyImage(VULKAN_RenderData *rendererData, VULKAN_Image *v
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)
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_ExternalBufferInfo *external, VULKAN_Image *imageOut)
{
VkResult result;
VkSamplerYcbcrConversionInfoKHR samplerYcbcrConversionInfo = { 0 };
@@ -866,8 +903,18 @@ static VkResult VULKAN_AllocateImage(VULKAN_RenderData *rendererData, SDL_Proper
imageCreateInfo.queueFamilyIndexCount = 0;
imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
#ifdef SDL_PLATFORM_ANDROID
VkExternalMemoryImageCreateInfoKHR externalMemoryImageCreateInfo = { 0 };
if (external) {
externalMemoryImageCreateInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR;
externalMemoryImageCreateInfo.pNext = &external->externalFormat;
externalMemoryImageCreateInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID;
imageCreateInfo.pNext = &externalMemoryImageCreateInfo;
}
#endif
// Allow writing to planar YUV textures from application code
if (VULKAN_VkFormatGetNumPlanes(format) > 1) {
if (VULKAN_VkFormatGetNumPlanes(format) > 1 && !external) {
imageCreateInfo.flags |= (VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | VK_IMAGE_CREATE_EXTENDED_USAGE_BIT);
imageCreateInfo.usage |= VK_IMAGE_USAGE_STORAGE_BIT;
}
@@ -880,11 +927,19 @@ static VkResult VULKAN_AllocateImage(VULKAN_RenderData *rendererData, SDL_Proper
}
VkMemoryRequirements memoryRequirements = { 0 };
vkGetImageMemoryRequirements(rendererData->device, imageOut->image, &memoryRequirements);
if (result != VK_SUCCESS) {
VULKAN_DestroyImage(rendererData, imageOut);
SET_ERROR_CODE("vkGetImageMemoryRequirements()", result);
return result;
#ifdef SDL_PLATFORM_ANDROID
if (external) {
memoryRequirements.memoryTypeBits = external->properties.memoryTypeBits;
memoryRequirements.size = external->properties.allocationSize;
} else
#endif
{
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;
@@ -897,6 +952,21 @@ static VkResult VULKAN_AllocateImage(VULKAN_RenderData *rendererData, SDL_Proper
memoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
memoryAllocateInfo.allocationSize = memoryRequirements.size;
memoryAllocateInfo.memoryTypeIndex = memoryTypeIndex;
#ifdef SDL_PLATFORM_ANDROID
VkImportAndroidHardwareBufferInfoANDROID importHardwareBufferInfo = { 0 };
VkMemoryDedicatedAllocateInfoKHR dedicatedAllocateInfo = { 0 };
if (external) {
importHardwareBufferInfo.sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID;
importHardwareBufferInfo.buffer = external->buffer;
dedicatedAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR;
dedicatedAllocateInfo.pNext = &importHardwareBufferInfo;
dedicatedAllocateInfo.image = imageOut->image;
memoryAllocateInfo.pNext = &dedicatedAllocateInfo;
}
#endif
result = vkAllocateMemory(rendererData->device, &memoryAllocateInfo, NULL, &imageOut->deviceMemory);
if (result != VK_SUCCESS) {
VULKAN_DestroyImage(rendererData, imageOut);
@@ -915,14 +985,25 @@ static VkResult VULKAN_AllocateImage(VULKAN_RenderData *rendererData, SDL_Proper
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);
if (external) {
VULKAN_RecordExternalImageBarrier(rendererData,
VK_ACCESS_NONE,
VK_ACCESS_SHADER_READ_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
imageOut->image,
&imageOut->imageLayout);
} else {
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 };
@@ -960,6 +1041,33 @@ static VkResult VULKAN_AllocateImage(VULKAN_RenderData *rendererData, SDL_Proper
}
static void VULKAN_RecordExternalImageBarrier(VULKAN_RenderData *rendererData, VkAccessFlags sourceAccessMask, VkAccessFlags destAccessMask,
VkPipelineStageFlags srcStageFlags, VkPipelineStageFlags dstStageFlags, VkImageLayout destLayout, VkImage image, VkImageLayout *imageLayout)
{
// Stop any outstanding renderpass if open
if (rendererData->currentRenderPass != VK_NULL_HANDLE) {
vkCmdEndRenderPass(rendererData->currentCommandBuffer);
rendererData->currentRenderPass = VK_NULL_HANDLE;
}
VkImageMemoryBarrier barrier = { 0 };
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.srcAccessMask = sourceAccessMask;
barrier.dstAccessMask = destAccessMask;
barrier.oldLayout = *imageLayout;
barrier.newLayout = destLayout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT;
barrier.dstQueueFamilyIndex = rendererData->graphicsQueueFamilyIndex;
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 void VULKAN_RecordPipelineImageBarrier(VULKAN_RenderData *rendererData, VkAccessFlags sourceAccessMask, VkAccessFlags destAccessMask,
VkPipelineStageFlags srcStageFlags, VkPipelineStageFlags dstStageFlags, VkImageLayout destLayout, VkImage image, VkImageLayout *imageLayout)
{
@@ -1796,18 +1904,34 @@ static bool VULKAN_ValidationLayersFound(void)
// Create resources that depend on the device.
static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer, SDL_PropertiesID create_props)
{
/* The list of extension names, in dependency order.
* If there are extension blocks that are not dependent upon each other, you'll need
* to dynamically build the list of extension names instead of using this array.
*/
static const char *const deviceExtensionNames[] = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
/* VK_KHR_sampler_ycbcr_conversion + dependent extensions.
Note VULKAN_DeviceExtensionsFound() call below, if these get moved in this
array, update that check too.
*/
// VK_KHR_sampler_ycbcr_conversion + dependent extensions
#define YCBCR_EXTENSION_OFFSET 1
#define YCBCR_EXTENSION_COUNT 5
VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME,
VK_KHR_MAINTENANCE1_EXTENSION_NAME,
VK_KHR_MAINTENANCE2_EXTENSION_NAME,
VK_KHR_BIND_MEMORY_2_EXTENSION_NAME,
VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME,
#ifdef SDL_PLATFORM_ANDROID
// VK_ANDROID_external_memory_android_hardware_buffer + dependent extensions
#define ANDROID_EXTENSION_OFFSET (YCBCR_EXTENSION_OFFSET + YCBCR_EXTENSION_COUNT)
#define ANDROID_EXTENSION_COUNT 4
VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME,
VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME,
VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME,
VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME,
#else
#define ANDROID_EXTENSION_COUNT 0
#endif
};
SDL_COMPILE_TIME_ASSERT(deviceExtensionNames, SDL_arraysize(deviceExtensionNames) == (YCBCR_EXTENSION_OFFSET + YCBCR_EXTENSION_COUNT + ANDROID_EXTENSION_COUNT));
VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
VkResult result = VK_SUCCESS;
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL;
@@ -1844,6 +1968,9 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer, SDL_Propert
// Check for VK_KHR_get_physical_device_properties2
rendererData->supportsKHRGetPhysicalDeviceProperties2 = VULKAN_InstanceExtensionFound(rendererData, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
// Check for VK_KHR_external_memory_capabilities, which we need to import hardware buffers
rendererData->supportsKHRExternalMemoryCapabilities = VULKAN_InstanceExtensionFound(rendererData, VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME);
// Create VkInstance
rendererData->instance = (VkInstance)SDL_GetPointerProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_INSTANCE_POINTER, NULL);
if (rendererData->instance) {
@@ -1857,7 +1984,7 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer, SDL_Propert
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 *));
const char **instanceExtensionsCopy = (const char **)SDL_calloc(instanceCreateInfo.enabledExtensionCount + 3, sizeof(const char *));
for (uint32_t i = 0; i < instanceCreateInfo.enabledExtensionCount; i++) {
instanceExtensionsCopy[i] = instanceExtensions[i];
}
@@ -1869,6 +1996,10 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer, SDL_Propert
instanceExtensionsCopy[instanceCreateInfo.enabledExtensionCount] = VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME;
instanceCreateInfo.enabledExtensionCount++;
}
if (rendererData->supportsKHRExternalMemoryCapabilities) {
instanceExtensionsCopy[instanceCreateInfo.enabledExtensionCount] = VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME;
instanceCreateInfo.enabledExtensionCount++;
}
instanceCreateInfo.ppEnabledExtensionNames = (const char *const *)instanceExtensionsCopy;
if (createDebug && VULKAN_ValidationLayersFound()) {
instanceCreateInfo.ppEnabledLayerNames = validationLayerName;
@@ -1920,10 +2051,18 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer, SDL_Propert
}
if (rendererData->supportsKHRGetPhysicalDeviceProperties2 &&
VULKAN_DeviceExtensionsFound(rendererData, 4, &deviceExtensionNames[1])) {
VULKAN_DeviceExtensionsFound(rendererData, YCBCR_EXTENSION_COUNT, &deviceExtensionNames[YCBCR_EXTENSION_OFFSET])) {
rendererData->supportsKHRSamplerYCbCrConversion = true;
}
#ifdef SDL_PLATFORM_ANDROID
if (rendererData->supportsKHRSamplerYCbCrConversion &&
rendererData->supportsKHRExternalMemoryCapabilities &&
VULKAN_DeviceExtensionsFound(rendererData, ANDROID_EXTENSION_COUNT, &deviceExtensionNames[ANDROID_EXTENSION_OFFSET])) {
rendererData->supportsANDROIDExternalMemoryAndroidHardwareBuffer = true;
}
#endif
// Create Vulkan device
rendererData->device = (VkDevice)SDL_GetPointerProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_DEVICE_POINTER, NULL);
if (rendererData->device) {
@@ -1938,7 +2077,15 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer, SDL_Propert
deviceCreateInfo.queueCreateInfoCount = 0;
deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfo;
deviceCreateInfo.pEnabledFeatures = NULL;
deviceCreateInfo.enabledExtensionCount = (rendererData->supportsKHRSamplerYCbCrConversion) ? SDL_arraysize(deviceExtensionNames) : 1;
deviceCreateInfo.enabledExtensionCount = 1;
if (rendererData->supportsKHRSamplerYCbCrConversion) {
deviceCreateInfo.enabledExtensionCount += YCBCR_EXTENSION_COUNT;
}
#ifdef SDL_PLATFORM_ANDROID
if (rendererData->supportsANDROIDExternalMemoryAndroidHardwareBuffer) {
deviceCreateInfo.enabledExtensionCount += ANDROID_EXTENSION_COUNT;
}
#endif
deviceCreateInfo.ppEnabledExtensionNames = deviceExtensionNames;
deviceQueueCreateInfo[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
@@ -1975,6 +2122,12 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer, SDL_Propert
return VK_ERROR_UNKNOWN;
}
#ifdef SDL_PLATFORM_ANDROID
if (!vkGetAndroidHardwareBufferPropertiesANDROID) {
rendererData->supportsANDROIDExternalMemoryAndroidHardwareBuffer = false;
}
#endif
// Get graphics/present queues
vkGetDeviceQueue(rendererData->device, rendererData->graphicsQueueFamilyIndex, 0, &rendererData->graphicsQueue);
if (rendererData->graphicsQueueFamilyIndex != rendererData->presentQueueFamilyIndex) {
@@ -2644,7 +2797,7 @@ static bool VULKAN_CreatePalette(SDL_Renderer *renderer, SDL_TexturePalette *pal
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);
VkResult result = VULKAN_AllocateImage(data, 0, 256, 1, format, usage, imageViewSwizzle, VK_NULL_HANDLE, NULL, &palettedata->image);
if (result != VK_SUCCESS) {
SET_ERROR_CODE("VULKAN_AllocateImage()", result);
return false;
@@ -2678,6 +2831,30 @@ static void VULKAN_DestroyPalette(SDL_Renderer *renderer, SDL_TexturePalette *pa
SDL_free(palettedata);
}
#ifdef SDL_PLATFORM_ANDROID
static void ANDROID_GetBufferSize(struct AHardwareBuffer *buffer, uint32_t *width, uint32_t *height)
{
static pfnAHardwareBuffer_describe AHardwareBuffer_describeFunc;
if (!AHardwareBuffer_describeFunc) {
void *lib = dlopen("libandroid.so", RTLD_NOW | RTLD_LOCAL);
if (lib) {
AHardwareBuffer_describeFunc = (pfnAHardwareBuffer_describe)dlsym(lib, "AHardwareBuffer_describe");
// Leaving the library loaded so we can use the function again without another dlopen()
}
}
if (AHardwareBuffer_describeFunc) {
AHardwareBuffer_Desc desc;
SDL_zero(desc);
AHardwareBuffer_describeFunc(buffer, &desc);
if (desc.width > 0 && desc.height > 0) {
*width = desc.width;
*height = desc.height;
}
}
}
#endif // SDL_PLATFORM_ANDROID
static bool VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props)
{
VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
@@ -2687,12 +2864,14 @@ static bool VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, S
uint32_t width = texture->w;
uint32_t height = texture->h;
VkComponentMapping imageViewSwizzle = rendererData->identitySwizzle;
VULKAN_ExternalBufferInfo externalBufferInfo;
VULKAN_ExternalBufferInfo *external = NULL;
if (!rendererData->device) {
return SDL_SetError("Device lost and couldn't be recovered");
}
if (textureFormat == VK_FORMAT_UNDEFINED) {
if (textureFormat == VK_FORMAT_UNDEFINED && texture->format != SDL_PIXELFORMAT_EXTERNAL_OES) {
return SDL_SetError("%s, An unsupported SDL pixel format (0x%x) was specified", SDL_FUNCTION, texture->format);
}
@@ -2702,25 +2881,95 @@ static bool VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, S
}
texture->internal = textureData;
// YUV textures must have even width and height. Also create Ycbcr conversion
if (texture->format == SDL_PIXELFORMAT_YV12 ||
texture->format == SDL_PIXELFORMAT_IYUV ||
texture->format == SDL_PIXELFORMAT_I444 ||
texture->format == SDL_PIXELFORMAT_NV12 ||
texture->format == SDL_PIXELFORMAT_NV21 ||
texture->format == SDL_PIXELFORMAT_P010 ||
texture->format == SDL_PIXELFORMAT_I0FL ||
texture->format == SDL_PIXELFORMAT_I4FL) {
const uint32_t YUV_SD_THRESHOLD = 576;
if (texture->format == SDL_PIXELFORMAT_EXTERNAL_OES) {
SDL_zero(externalBufferInfo);
external = &externalBufferInfo;
// Check that we have VK_KHR_sampler_ycbcr_conversion support
if (!rendererData->supportsKHRSamplerYCbCrConversion) {
return SDL_SetError("YUV textures require a Vulkan device that supports VK_KHR_sampler_ycbcr_conversion");
if (texture->access == SDL_TEXTUREACCESS_TARGET) {
return SDL_SetError("SDL_PIXELFORMAT_EXTERNAL_OES textures can't be render targets");
}
VkSamplerYcbcrConversionCreateInfoKHR samplerYcbcrConversionCreateInfo;
SDL_zero(samplerYcbcrConversionCreateInfo);
samplerYcbcrConversionCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO_KHR;
#ifdef SDL_PLATFORM_ANDROID
external->buffer = (struct AHardwareBuffer *)SDL_GetPointerProperty(create_props, SDL_PROP_TEXTURE_CREATE_VULKAN_ANDROID_HARDWARE_BUFFER_POINTER, NULL);
if (!external->buffer) {
return SDL_SetError("SDL_PIXELFORMAT_EXTERNAL_OES texture requires a hardware buffer");
}
// Get the real buffer width and height
ANDROID_GetBufferSize(external->buffer, &width, &height);
VkAndroidHardwareBufferFormatPropertiesANDROID *formatProperties = &external->formatProperties;
formatProperties->sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID;
VkAndroidHardwareBufferPropertiesANDROID *properties = &external->properties;
properties->sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID;
properties->pNext = formatProperties;
result = vkGetAndroidHardwareBufferPropertiesANDROID(rendererData->device, external->buffer, properties);
if (result != VK_SUCCESS) {
SET_ERROR_CODE("vkGetAndroidHardwareBufferPropertiesANDROID()", result);
return false;
}
VkExternalFormatANDROID *externalFormat = &external->externalFormat;
externalFormat->sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID;
externalFormat->externalFormat = formatProperties->externalFormat;
textureFormat = formatProperties->format;
if (textureFormat == VK_FORMAT_UNDEFINED ||
formatProperties->suggestedYcbcrModel != VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY_KHR) {
VkFilter chromaFilter = VK_FILTER_LINEAR;
VkFilter lumaFilter = VK_FILTER_LINEAR;
if (!(formatProperties->formatFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT)) {
chromaFilter = VK_FILTER_NEAREST;
}
if (!(formatProperties->formatFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) {
lumaFilter = VK_FILTER_NEAREST;
}
if (!(formatProperties->formatFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT)) {
lumaFilter = chromaFilter;
}
VULKAN_YUVPipelineCreateInfo createInfo;
SDL_zero(createInfo); // Must zero padding bytes for cache
createInfo.externalFormat = formatProperties->externalFormat;
createInfo.filter = lumaFilter;
VkSamplerYcbcrConversionCreateInfoKHR *samplerYcbcrConversionCreateInfo = &createInfo.samplerYcbcrConversionCreateInfo;
samplerYcbcrConversionCreateInfo->sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO_KHR;
samplerYcbcrConversionCreateInfo->pNext = externalFormat;
samplerYcbcrConversionCreateInfo->format = formatProperties->format;
samplerYcbcrConversionCreateInfo->ycbcrModel = formatProperties->suggestedYcbcrModel;
samplerYcbcrConversionCreateInfo->ycbcrRange = formatProperties->suggestedYcbcrRange;
samplerYcbcrConversionCreateInfo->components = formatProperties->samplerYcbcrConversionComponents;
samplerYcbcrConversionCreateInfo->xChromaOffset = formatProperties->suggestedXChromaOffset;
samplerYcbcrConversionCreateInfo->yChromaOffset = formatProperties->suggestedYChromaOffset;
samplerYcbcrConversionCreateInfo->chromaFilter = chromaFilter;
textureData->yuvPipeline = VULKAN_GetYUVPipeline(rendererData, &createInfo);
if (!textureData->yuvPipeline) {
return false;
}
textureData->yuv = true;
}
#endif // SDL_PLATFORM_ANDROID
} else if (texture->format == SDL_PIXELFORMAT_YV12 ||
texture->format == SDL_PIXELFORMAT_IYUV ||
texture->format == SDL_PIXELFORMAT_I444 ||
texture->format == SDL_PIXELFORMAT_NV12 ||
texture->format == SDL_PIXELFORMAT_NV21 ||
texture->format == SDL_PIXELFORMAT_P010 ||
texture->format == SDL_PIXELFORMAT_I0FL ||
texture->format == SDL_PIXELFORMAT_I4FL) {
const uint32_t YUV_SD_THRESHOLD = 576;
VULKAN_YUVPipelineCreateInfo createInfo;
SDL_zero(createInfo); // Must zero padding bytes for cache
createInfo.filter = VK_FILTER_LINEAR;
VkSamplerYcbcrConversionCreateInfoKHR *samplerYcbcrConversionCreateInfo = &createInfo.samplerYcbcrConversionCreateInfo;
samplerYcbcrConversionCreateInfo->sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO_KHR;
if (texture->format != SDL_PIXELFORMAT_I444 &&
texture->format != SDL_PIXELFORMAT_I4FL) {
@@ -2730,70 +2979,69 @@ static bool VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, S
}
// Create samplerYcbcrConversion which will be used on the VkImageView and VkSampler
samplerYcbcrConversionCreateInfo.format = textureFormat;
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;
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;
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;
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;
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;
samplerYcbcrConversionCreateInfo->ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709_KHR;
} else {
samplerYcbcrConversionCreateInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601_KHR;
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;
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;
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;
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;
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;
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;
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;
samplerYcbcrConversionCreateInfo->xChromaOffset = VK_CHROMA_LOCATION_MIDPOINT_KHR;
samplerYcbcrConversionCreateInfo->yChromaOffset = VK_CHROMA_LOCATION_MIDPOINT_KHR;
break;
}
samplerYcbcrConversionCreateInfo.chromaFilter = VK_FILTER_LINEAR;
samplerYcbcrConversionCreateInfo.forceExplicitReconstruction = VK_FALSE;
samplerYcbcrConversionCreateInfo->chromaFilter = VK_FILTER_LINEAR;
textureData->yuvPipeline = VULKAN_GetYUVPipeline(rendererData, &samplerYcbcrConversionCreateInfo);
textureData->yuvPipeline = VULKAN_GetYUVPipeline(rendererData, &createInfo);
if (!textureData->yuvPipeline) {
return false;
}
@@ -2803,13 +3051,16 @@ static bool VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, S
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;
VkImageUsageFlags usage = VK_IMAGE_USAGE_SAMPLED_BIT;
if (!external) {
usage |= (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;
}
VkSamplerYcbcrConversionKHR samplerYcbcrConversion = (textureData->yuvPipeline ? textureData->yuvPipeline->samplerYcbcrConversion : VK_NULL_HANDLE);
result = VULKAN_AllocateImage(rendererData, create_props, width, height, textureFormat, usage, imageViewSwizzle, samplerYcbcrConversion, &textureData->mainImage);
result = VULKAN_AllocateImage(rendererData, create_props, width, height, textureFormat, usage, imageViewSwizzle, samplerYcbcrConversion, external, &textureData->mainImage);
if (result != VK_SUCCESS) {
SET_ERROR_CODE("VULKAN_AllocateImage()", result);
return false;
@@ -2982,6 +3233,10 @@ static bool VULKAN_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
return SDL_SetError("Texture is not currently available");
}
if (texture->format == SDL_PIXELFORMAT_EXTERNAL_OES) {
return SDL_Unsupported();
}
Uint32 numPlanes = VULKAN_VkFormatGetNumPlanes(textureData->mainImage.format);
if (numPlanes == 2) {
// NV12/NV21 data
@@ -3034,6 +3289,10 @@ static bool VULKAN_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture
return SDL_SetError("Texture is not currently available");
}
if (texture->format == SDL_PIXELFORMAT_EXTERNAL_OES) {
return SDL_Unsupported();
}
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;
}
@@ -3074,6 +3333,10 @@ static bool VULKAN_UpdateTextureNV(SDL_Renderer *renderer, SDL_Texture *texture,
return SDL_SetError("Texture is not currently available");
}
if (texture->format == SDL_PIXELFORMAT_EXTERNAL_OES) {
return SDL_Unsupported();
}
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;
}
@@ -3090,6 +3353,7 @@ static bool VULKAN_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture,
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");
}
@@ -3098,6 +3362,10 @@ static bool VULKAN_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture,
return SDL_SetError("texture is already locked");
}
if (texture->format == SDL_PIXELFORMAT_EXTERNAL_OES) {
return SDL_Unsupported();
}
if (textureData->yuv) {
// It's more efficient to upload directly...
if (!textureData->pixels) {
@@ -3156,6 +3424,10 @@ static void VULKAN_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture)
return;
}
if (texture->format == SDL_PIXELFORMAT_EXTERNAL_OES) {
return;
}
if (textureData->yuv) {
const SDL_Rect *rect = &textureData->lockedRect;
const Uint8 *pixels = textureData->pixels + rect->y * textureData->pitch + rect->x * SDL_BYTESPERPIXEL(texture->format);
@@ -3824,18 +4096,17 @@ static VkDescriptorSet VULKAN_AllocateDescriptorSet(SDL_Renderer *renderer, VULK
return descriptorSet;
}
static VULKAN_YUVPipeline *VULKAN_GetYUVPipeline(VULKAN_RenderData *rendererData, const VkSamplerYcbcrConversionCreateInfoKHR *samplerYcbcrConversionCreateInfo)
static VULKAN_YUVPipeline *VULKAN_GetYUVPipeline(VULKAN_RenderData *rendererData, const VULKAN_YUVPipelineCreateInfo *createInfo)
{
VULKAN_YUVPipeline *pipeline;
VkResult result;
// Save pNext and set it to NULL for the cache comparison
VkSamplerYcbcrConversionCreateInfoKHR cacheKey;
SDL_memcpy(&cacheKey, samplerYcbcrConversionCreateInfo, sizeof(cacheKey));
cacheKey.pNext = NULL;
VULKAN_YUVPipelineCreateInfo cacheKey;
SDL_memcpy(&cacheKey, createInfo, sizeof(cacheKey));
cacheKey.samplerYcbcrConversionCreateInfo.pNext = NULL;
for (pipeline = rendererData->yuvPipelineCache; pipeline; pipeline = pipeline->next) {
if (SDL_memcmp(&pipeline->samplerYcbcrConversionCreateInfo, &cacheKey, sizeof(cacheKey)) == 0) {
if (SDL_memcmp(&pipeline->createInfo, &cacheKey, sizeof(cacheKey)) == 0) {
return pipeline;
}
}
@@ -3844,9 +4115,9 @@ static VULKAN_YUVPipeline *VULKAN_GetYUVPipeline(VULKAN_RenderData *rendererData
if (!pipeline) {
return NULL;
}
SDL_memcpy(&pipeline->samplerYcbcrConversionCreateInfo, &cacheKey, sizeof(cacheKey));
SDL_memcpy(&pipeline->createInfo, &cacheKey, sizeof(cacheKey));
result = vkCreateSamplerYcbcrConversionKHR(rendererData->device, samplerYcbcrConversionCreateInfo, NULL, &pipeline->samplerYcbcrConversion);
result = vkCreateSamplerYcbcrConversionKHR(rendererData->device, &createInfo->samplerYcbcrConversionCreateInfo, NULL, &pipeline->samplerYcbcrConversion);
if (result != VK_SUCCESS) {
SET_ERROR_CODE("vkCreateSamplerYcbcrConversionKHR()", result);
VULKAN_CleanupYUVPipeline(rendererData, pipeline);
@@ -3856,8 +4127,8 @@ static VULKAN_YUVPipeline *VULKAN_GetYUVPipeline(VULKAN_RenderData *rendererData
// Also create VkSampler object which we will need to pass to the PSO as an immutable sampler
VkSamplerCreateInfo samplerCreateInfo = { 0 };
samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samplerCreateInfo.magFilter = VK_FILTER_LINEAR;
samplerCreateInfo.minFilter = VK_FILTER_LINEAR;
samplerCreateInfo.magFilter = createInfo->filter;
samplerCreateInfo.minFilter = createInfo->filter;
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;
@@ -4831,6 +5102,10 @@ static bool VULKAN_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SD
SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_I4FL);
}
if (rendererData->supportsANDROIDExternalMemoryAndroidHardwareBuffer) {
SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_EXTERNAL_OES);
}
return true;
}