mirror of
https://github.com/libsdl-org/SDL.git
synced 2026-09-17 10:34:48 +00:00
Added support for using Android hardware buffers with the Vulkan renderer
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user