From fe85b4782d520f443cda247a19ecc1c4409ecdcc Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Thu, 30 Jul 2026 16:38:00 -0700
Subject: [PATCH] Support extended interop with the 2D Metal renderer
Also fixed an issue where an external surface couldn't be used with IYUV and YV12 textures.
---
include/SDL3/SDL_render.h | 54 +
src/render/metal/SDL_render_metal.m | 161 +-
src/render/metal/SDL_shaders_metal.metal | 7 +-
src/render/metal/SDL_shaders_metal_ios.h | 2243 +++++++-------
.../metal/SDL_shaders_metal_iphonesimulator.h | 2686 ++++++++---------
src/render/metal/SDL_shaders_metal_macos.h | 2077 +++++++------
src/render/metal/SDL_shaders_metal_tvos.h | 2243 +++++++-------
.../metal/SDL_shaders_metal_tvsimulator.h | 2615 ++++++++--------
8 files changed, 6074 insertions(+), 6012 deletions(-)
diff --git a/include/SDL3/SDL_render.h b/include/SDL3/SDL_render.h
index ee32cf912b9a6..8c48a5aea8da3 100644
--- a/include/SDL3/SDL_render.h
+++ b/include/SDL3/SDL_render.h
@@ -306,6 +306,13 @@ extern SDL_DECLSPEC SDL_Renderer * SDLCALL SDL_CreateRenderer(SDL_Window *window
* - `SDL_PROP_RENDERER_CREATE_GPU_SHADERS_MSL_BOOLEAN`: the app is able to
* provide MSL shaders to SDL_GPURenderState, optional.
*
+ * With the metal renderer:
+ *
+ * - `SDL_PROP_RENDERER_CREATE_METAL_DEVICE_POINTER`: the MTLDevice to use with the
+ * renderer, optional.
+ * - `SDL_PROP_RENDERER_CREATE_METAL_COMMAND_QUEUE_POINTER`: the MTLCommandQueue
+ * to use with the renderer, optional. If you set this property it will implicitly set (and override) `SDL_PROP_RENDERER_CREATE_METAL_DEVICE_POINTER`.
+ *
* With the vulkan renderer:
*
* - `SDL_PROP_RENDERER_CREATE_VULKAN_INSTANCE_POINTER`: the VkInstance to use
@@ -346,6 +353,8 @@ extern SDL_DECLSPEC SDL_Renderer * SDLCALL SDL_CreateRendererWithProperties(SDL_
#define SDL_PROP_RENDERER_CREATE_GPU_SHADERS_SPIRV_BOOLEAN "SDL.renderer.create.gpu.shaders_spirv"
#define SDL_PROP_RENDERER_CREATE_GPU_SHADERS_DXIL_BOOLEAN "SDL.renderer.create.gpu.shaders_dxil"
#define SDL_PROP_RENDERER_CREATE_GPU_SHADERS_MSL_BOOLEAN "SDL.renderer.create.gpu.shaders_msl"
+#define SDL_PROP_RENDERER_CREATE_METAL_DEVICE_POINTER "SDL.renderer.create.metal.device"
+#define SDL_PROP_RENDERER_CREATE_METAL_COMMAND_QUEUE_POINTER "SDL.renderer.create.metal.command_queue"
#define SDL_PROP_RENDERER_CREATE_VULKAN_INSTANCE_POINTER "SDL.renderer.create.vulkan.instance"
#define SDL_PROP_RENDERER_CREATE_VULKAN_SURFACE_NUMBER "SDL.renderer.create.vulkan.surface"
#define SDL_PROP_RENDERER_CREATE_VULKAN_PHYSICAL_DEVICE_POINTER "SDL.renderer.create.vulkan.physical_device"
@@ -518,6 +527,15 @@ extern SDL_DECLSPEC const char * SDLCALL SDL_GetRendererName(SDL_Renderer *rende
* - `SDL_PROP_RENDERER_D3D12_COMMAND_QUEUE_POINTER`: the ID3D12CommandQueue
* associated with the renderer
*
+ * With the metal renderer:
+ *
+ * - `SDL_PROP_RENDERER_METAL_DEVICE_POINTER`: the MTLDevice associated with the
+ * renderer
+ * - `SDL_PROP_RENDERER_METAL_COMMAND_QUEUE_POINTER`: the MTLCommandQueue
+ * associated with the renderer. Work submitted on this queue will be ordered
+ * relative to other rendering. SDL_FlushRenderer() can be used to guarantee
+ * the current rendering has been submitted.
+ *
* With the vulkan renderer:
*
* - `SDL_PROP_RENDERER_VULKAN_INSTANCE_POINTER`: the VkInstance associated
@@ -568,6 +586,8 @@ extern SDL_DECLSPEC SDL_PropertiesID SDLCALL SDL_GetRendererProperties(SDL_Rende
#define SDL_PROP_RENDERER_D3D12_DEVICE_POINTER "SDL.renderer.d3d12.device"
#define SDL_PROP_RENDERER_D3D12_SWAPCHAIN_POINTER "SDL.renderer.d3d12.swap_chain"
#define SDL_PROP_RENDERER_D3D12_COMMAND_QUEUE_POINTER "SDL.renderer.d3d12.command_queue"
+#define SDL_PROP_RENDERER_METAL_DEVICE_POINTER "SDL.renderer.metal.device"
+#define SDL_PROP_RENDERER_METAL_COMMAND_QUEUE_POINTER "SDL.renderer.metal.command_queue"
#define SDL_PROP_RENDERER_VULKAN_INSTANCE_POINTER "SDL.renderer.vulkan.instance"
#define SDL_PROP_RENDERER_VULKAN_SURFACE_NUMBER "SDL.renderer.vulkan.surface"
#define SDL_PROP_RENDERER_VULKAN_PHYSICAL_DEVICE_POINTER "SDL.renderer.vulkan.physical_device"
@@ -736,6 +756,19 @@ extern SDL_DECLSPEC SDL_Texture * SDLCALL SDL_CreateTextureFromSurface(SDL_Rende
* - `SDL_PROP_TEXTURE_CREATE_METAL_PIXELBUFFER_POINTER`: the CVPixelBufferRef
* associated with the texture, if you want to create a texture from an
* existing pixel buffer.
+ * - `SDL_PROP_TEXTURE_CREATE_METAL_TEXTURE_POINTER`: the MTLTexture
+ * associated with the texture, if you want to wrap an existing texture.
+ * - `SDL_PROP_TEXTURE_CREATE_METAL_TEXTURE_UV_POINTER`: the MTLTexture
+ * associated with the UV plane of an NV12 texture, if you want to wrap an
+ * existing texture.
+ * - `SDL_PROP_TEXTURE_CREATE_METAL_TEXTURE_U_POINTER`: the MTLTexture
+ * associated with the U plane of a YUV texture, if you want to wrap an
+ * existing texture.
+ * - `SDL_PROP_TEXTURE_CREATE_METAL_TEXTURE_V_POINTER`: the MTLTexture
+ * associated with the V plane of a YUV texture, if you want to wrap an
+ * existing texture.
+ * - `SDL_PROP_TEXTURE_CREATE_METAL_TEXTURE_USAGE_NUMBER`: any additional
+ * MTLTextureUsage that this texture should have, defaults to 0.
*
* With the opengl renderer:
*
@@ -819,6 +852,11 @@ extern SDL_DECLSPEC SDL_Texture * SDLCALL SDL_CreateTextureWithProperties(SDL_Re
#define SDL_PROP_TEXTURE_CREATE_D3D12_TEXTURE_U_POINTER "SDL.texture.create.d3d12.texture_u"
#define SDL_PROP_TEXTURE_CREATE_D3D12_TEXTURE_V_POINTER "SDL.texture.create.d3d12.texture_v"
#define SDL_PROP_TEXTURE_CREATE_METAL_PIXELBUFFER_POINTER "SDL.texture.create.metal.pixelbuffer"
+#define SDL_PROP_TEXTURE_CREATE_METAL_TEXTURE_POINTER "SDL.texture.create.metal.texture"
+#define SDL_PROP_TEXTURE_CREATE_METAL_TEXTURE_UV_POINTER "SDL.texture.create.metal.texture_uv"
+#define SDL_PROP_TEXTURE_CREATE_METAL_TEXTURE_U_POINTER "SDL.texture.create.metal.texture_u"
+#define SDL_PROP_TEXTURE_CREATE_METAL_TEXTURE_V_POINTER "SDL.texture.create.metal.texture_v"
+#define SDL_PROP_TEXTURE_CREATE_METAL_TEXTURE_USAGE_NUMBER "SDL.texture.create.metal.texture_usage"
#define SDL_PROP_TEXTURE_CREATE_OPENGL_TEXTURE_NUMBER "SDL.texture.create.opengl.texture"
#define SDL_PROP_TEXTURE_CREATE_OPENGL_TEXTURE_UV_NUMBER "SDL.texture.create.opengl.texture_uv"
#define SDL_PROP_TEXTURE_CREATE_OPENGL_TEXTURE_U_NUMBER "SDL.texture.create.opengl.texture_u"
@@ -877,6 +915,18 @@ extern SDL_DECLSPEC SDL_Texture * SDLCALL SDL_CreateTextureWithProperties(SDL_Re
* - `SDL_PROP_TEXTURE_D3D12_TEXTURE_V_POINTER`: the ID3D12Resource associated
* with the V plane of a YUV texture
*
+ * With the metal renderer:
+ *
+ * - `SDL_PROP_TEXTURE_METAL_TEXTURE_POINTER`: the MTLTexture associated with
+ * the texture
+ * - `SDL_PROP_TEXTURE_METAL_TEXTURE_UV_POINTER`: the MTLTexture associated
+ * with the UV plane of an NV12 texture
+ * - `SDL_PROP_TEXTURE_METAL_TEXTURE_U_POINTER`: the MTLTexture associated with
+ * the U plane of a YUV texture
+ * style texture
+ * - `SDL_PROP_TEXTURE_METAL_TEXTURE_V_POINTER`: the MTLTexture associated with
+ * the V plane of a YUV texture
+ *
* With the vulkan renderer:
*
* - `SDL_PROP_TEXTURE_VULKAN_TEXTURE_NUMBER`: the VkImage associated with the
@@ -946,6 +996,10 @@ extern SDL_DECLSPEC SDL_PropertiesID SDLCALL SDL_GetTextureProperties(SDL_Textur
#define SDL_PROP_TEXTURE_D3D12_TEXTURE_POINTER "SDL.texture.d3d12.texture"
#define SDL_PROP_TEXTURE_D3D12_TEXTURE_U_POINTER "SDL.texture.d3d12.texture_u"
#define SDL_PROP_TEXTURE_D3D12_TEXTURE_V_POINTER "SDL.texture.d3d12.texture_v"
+#define SDL_PROP_TEXTURE_METAL_TEXTURE_POINTER "SDL.texture.metal.texture"
+#define SDL_PROP_TEXTURE_METAL_TEXTURE_UV_POINTER "SDL.texture.metal.texture_uv"
+#define SDL_PROP_TEXTURE_METAL_TEXTURE_U_POINTER "SDL.texture.metal.texture_u"
+#define SDL_PROP_TEXTURE_METAL_TEXTURE_V_POINTER "SDL.texture.metal.texture_v"
#define SDL_PROP_TEXTURE_OPENGL_TEXTURE_NUMBER "SDL.texture.opengl.texture"
#define SDL_PROP_TEXTURE_OPENGL_TEXTURE_UV_NUMBER "SDL.texture.opengl.texture_uv"
#define SDL_PROP_TEXTURE_OPENGL_TEXTURE_U_NUMBER "SDL.texture.opengl.texture_u"
diff --git a/src/render/metal/SDL_render_metal.m b/src/render/metal/SDL_render_metal.m
index 15a0634e29304..332c1ca06281a 100644
--- a/src/render/metal/SDL_render_metal.m
+++ b/src/render/metal/SDL_render_metal.m
@@ -161,7 +161,9 @@ @implementation SDL3METAL_PaletteData
@interface SDL3METAL_TextureData : NSObject
@property(nonatomic, retain) id<MTLTexture> mtltexture;
@property(nonatomic, retain) id<MTLTexture> mtlpalette;
-@property(nonatomic, retain) id<MTLTexture> mtltextureUv;
+@property(nonatomic, retain) id<MTLTexture> mtltextureUV;
+@property(nonatomic, retain) id<MTLTexture> mtltextureU;
+@property(nonatomic, retain) id<MTLTexture> mtltextureV;
@property(nonatomic, assign) SDL_MetalFragmentFunction fragmentFunction;
#ifdef SDL_HAVE_YUV
@property(nonatomic, assign) BOOL yuv;
@@ -699,8 +701,8 @@ static bool METAL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
SDL3METAL_RenderData *data = (__bridge SDL3METAL_RenderData *)renderer->internal;
MTLPixelFormat pixfmt = MTLPixelFormatInvalid;
MTLTextureDescriptor *mtltexdesc;
- id<MTLTexture> mtltexture = nil, mtltextureUv = nil;
- SDL3METAL_TextureData *texturedata;
+ id<MTLTexture> mtltexture = nil;
+ SDL3METAL_TextureData *texturedata = [[SDL3METAL_TextureData alloc] init];
CVPixelBufferRef pixelbuffer = nil;
IOSurfaceRef surface = nil;
@@ -786,23 +788,24 @@ static bool METAL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
} else {
mtltexdesc.usage = MTLTextureUsageShaderRead;
}
+ mtltexdesc.usage |= SDL_GetNumberProperty(create_props, SDL_PROP_TEXTURE_CREATE_METAL_TEXTURE_USAGE_NUMBER, 0);
- if (surface) {
- mtltexture = [data.mtldevice newTextureWithDescriptor:mtltexdesc iosurface:surface plane:0];
- } else {
+ mtltexture = (__bridge id<MTLTexture>)SDL_GetPointerProperty(create_props, SDL_PROP_TEXTURE_CREATE_METAL_TEXTURE_POINTER, nil);
+ if (mtltexture == nil) {
mtltexture = [data.mtldevice newTextureWithDescriptor:mtltexdesc];
}
if (mtltexture == nil) {
return SDL_SetError("Texture allocation failed");
}
+ texturedata.mtltexture = mtltexture;
+ SDL_SetPointerProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_METAL_TEXTURE_POINTER, (__bridge void *)mtltexture);
- mtltextureUv = nil;
#ifdef SDL_HAVE_YUV
BOOL yuv = (texture->format == SDL_PIXELFORMAT_IYUV || texture->format == SDL_PIXELFORMAT_YV12 || texture->format == SDL_PIXELFORMAT_P408 || texture->format == SDL_PIXELFORMAT_P416);
BOOL nv12 = (texture->format == SDL_PIXELFORMAT_NV12 || texture->format == SDL_PIXELFORMAT_NV21 || texture->format == SDL_PIXELFORMAT_P010);
if (yuv) {
- mtltexdesc.pixelFormat = pixfmt;
+ mtltexdesc.pixelFormat = pixfmt;
if (texture->format == SDL_PIXELFORMAT_P408 || texture->format == SDL_PIXELFORMAT_P416) {
mtltexdesc.width = texture->w;
mtltexdesc.height = texture->h;
@@ -810,8 +813,6 @@ static bool METAL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
mtltexdesc.width = (texture->w + 1) / 2;
mtltexdesc.height = (texture->h + 1) / 2;
}
- mtltexdesc.textureType = MTLTextureType2DArray;
- mtltexdesc.arrayLength = 2;
} else if (texture->format == SDL_PIXELFORMAT_P010) {
mtltexdesc.pixelFormat = MTLPixelFormatRG16Unorm;
mtltexdesc.width = (texture->w + 1) / 2;
@@ -822,18 +823,40 @@ static bool METAL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
mtltexdesc.height = (texture->h + 1) / 2;
}
- if (yuv || nv12) {
- if (surface) {
- mtltextureUv = [data.mtldevice newTextureWithDescriptor:mtltexdesc iosurface:surface plane:1];
- } else {
- mtltextureUv = [data.mtldevice newTextureWithDescriptor:mtltexdesc];
+ if (yuv) {
+ mtltexture = (__bridge id<MTLTexture>)SDL_GetPointerProperty(create_props, SDL_PROP_TEXTURE_CREATE_METAL_TEXTURE_U_POINTER, nil);
+ if (mtltexture == nil) {
+ mtltexture = [data.mtldevice newTextureWithDescriptor:mtltexdesc];
}
- if (mtltextureUv == nil) {
+ if (mtltexture == nil) {
return SDL_SetError("Texture allocation failed");
}
+ texturedata.mtltextureU = mtltexture;
+ SDL_SetPointerProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_METAL_TEXTURE_U_POINTER, (__bridge void *)mtltexture);
+
+ mtltexture = (__bridge id<MTLTexture>)SDL_GetPointerProperty(create_props, SDL_PROP_TEXTURE_CREATE_METAL_TEXTURE_V_POINTER, nil);
+ if (mtltexture == nil) {
+ mtltexture = [data.mtldevice newTextureWithDescriptor:mtltexdesc];
+ }
+ if (mtltexture == nil) {
+ return SDL_SetError("Texture allocation failed");
+ }
+ texturedata.mtltextureV = mtltexture;
+ SDL_SetPointerProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_METAL_TEXTURE_V_POINTER, (__bridge void *)mtltexture);
+
+ } else if (nv12) {
+ mtltexture = (__bridge id<MTLTexture>)SDL_GetPointerProperty(create_props, SDL_PROP_TEXTURE_CREATE_METAL_TEXTURE_UV_POINTER, nil);
+ if (mtltexture == nil) {
+ mtltexture = [data.mtldevice newTextureWithDescriptor:mtltexdesc];
+ }
+ if (mtltexture == nil) {
+ return SDL_SetError("Texture allocation failed");
+ }
+ texturedata.mtltextureUV = mtltexture;
+ SDL_SetPointerProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_METAL_TEXTURE_UV_POINTER, (__bridge void *)mtltexture);
}
+
#endif // SDL_HAVE_YUV
- texturedata = [[SDL3METAL_TextureData alloc] init];
if (texture->format == SDL_PIXELFORMAT_INDEX8) {
texturedata.fragmentFunction = SDL_METAL_FRAGMENT_PALETTE;
#ifdef SDL_HAVE_YUV
@@ -845,8 +868,6 @@ static bool METAL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
} else {
texturedata.fragmentFunction = SDL_METAL_FRAGMENT_COPY;
}
- texturedata.mtltexture = mtltexture;
- texturedata.mtltextureUv = mtltextureUv;
#ifdef SDL_HAVE_YUV
texturedata.yuv = yuv;
texturedata.nv12 = nv12;
@@ -959,8 +980,9 @@ static bool METAL_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
}
#ifdef SDL_HAVE_YUV
if (texturedata.yuv) {
- int Uslice = texture->format == SDL_PIXELFORMAT_YV12 ? 1 : 0;
- int Vslice = texture->format == SDL_PIXELFORMAT_YV12 ? 0 : 1;
+ // YV12 stores V before U, so the plane order is swapped for it.
+ id<MTLTexture> firstplane = texture->format == SDL_PIXELFORMAT_YV12 ? texturedata.mtltextureV : texturedata.mtltextureU;
+ id<MTLTexture> secondplane = texture->format == SDL_PIXELFORMAT_YV12 ? texturedata.mtltextureU : texturedata.mtltextureV;
int UVpitch;
SDL_Rect UVrect;
if (texture->format == SDL_PIXELFORMAT_P408 || texture->format == SDL_PIXELFORMAT_P416) {
@@ -976,13 +998,13 @@ static bool METAL_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
// Skip to the correct offset into the next texture
pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch);
- if (!METAL_UpdateTextureInternal(renderer, texturedata.hasdata, texturedata.mtltextureUv, UVrect, Uslice, pixels, UVpitch)) {
+ if (!METAL_UpdateTextureInternal(renderer, texturedata.hasdata, firstplane, UVrect, 0, pixels, UVpitch)) {
return false;
}
// Skip to the correct offset into the next texture
pixels = (const void *)((const Uint8 *)pixels + UVrect.h * UVpitch);
- if (!METAL_UpdateTextureInternal(renderer, texturedata.hasdata, texturedata.mtltextureUv, UVrect, Vslice, pixels, UVpitch)) {
+ if (!METAL_UpdateTextureInternal(renderer, texturedata.hasdata, secondplane, UVrect, 0, pixels, UVpitch)) {
return false;
}
}
@@ -993,7 +1015,7 @@ static bool METAL_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
// Skip to the correct offset into the next texture
pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch);
- if (!METAL_UpdateTextureInternal(renderer, texturedata.hasdata, texturedata.mtltextureUv, UVrect, 0, pixels, UVpitch)) {
+ if (!METAL_UpdateTextureInternal(renderer, texturedata.hasdata, texturedata.mtltextureUV, UVrect, 0, pixels, UVpitch)) {
return false;
}
}
@@ -1013,8 +1035,6 @@ static bool METAL_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture,
{
@autoreleasepool {
SDL3METAL_TextureData *texturedata = (__bridge SDL3METAL_TextureData *)texture->internal;
- const int Uslice = 0;
- const int Vslice = 1;
SDL_Rect UVrect;
if (texture->format == SDL_PIXELFORMAT_P408 || texture->format == SDL_PIXELFORMAT_P416) {
UVrect = *rect;
@@ -1033,10 +1053,10 @@ static bool METAL_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture,
if (!METAL_UpdateTextureInternal(renderer, texturedata.hasdata, texturedata.mtltexture, *rect, 0, Yplane, Ypitch)) {
return false;
}
- if (!METAL_UpdateTextureInternal(renderer, texturedata.hasdata, texturedata.mtltextureUv, UVrect, Uslice, Uplane, Upitch)) {
+ if (!METAL_UpdateTextureInternal(renderer, texturedata.hasdata, texturedata.mtltextureU, UVrect, 0, Uplane, Upitch)) {
return false;
}
- if (!METAL_UpdateTextureInternal(renderer, texturedata.hasdata, texturedata.mtltextureUv, UVrect, Vslice, Vplane, Vpitch)) {
+ if (!METAL_UpdateTextureInternal(renderer, texturedata.hasdata, texturedata.mtltextureV, UVrect, 0, Vplane, Vpitch)) {
return false;
}
@@ -1064,7 +1084,7 @@ static bool METAL_UpdateTextureNV(SDL_Renderer *renderer, SDL_Texture *texture,
return false;
}
- if (!METAL_UpdateTextureInternal(renderer, texturedata.hasdata, texturedata.mtltextureUv, UVrect, 0, UVplane, UVpitch)) {
+ if (!METAL_UpdateTextureInternal(renderer, texturedata.hasdata, texturedata.mtltextureUV, UVrect, 0, UVplane, UVpitch)) {
return false;
}
@@ -1149,8 +1169,9 @@ static void METAL_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture)
destinationOrigin:MTLOriginMake(rect.x, rect.y, 0)];
#ifdef SDL_HAVE_YUV
if (texturedata.yuv) {
- int Uslice = texture->format == SDL_PIXELFORMAT_YV12 ? 1 : 0;
- int Vslice = texture->format == SDL_PIXELFORMAT_YV12 ? 0 : 1;
+ // YV12 stores V before U, so the plane order is swapped for it.
+ id<MTLTexture> firstplane = texture->format == SDL_PIXELFORMAT_YV12 ? texturedata.mtltextureV : texturedata.mtltextureU;
+ id<MTLTexture> secondplane = texture->format == SDL_PIXELFORMAT_YV12 ? texturedata.mtltextureU : texturedata.mtltextureV;
int UVpitch = (pitch + 1) / 2;
[blitcmd copyFromBuffer:texturedata.lockedbuffer
@@ -1158,8 +1179,8 @@ static void METAL_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture)
sourceBytesPerRow:UVpitch
sourceBytesPerImage:UVpitch * UVrect.h
sourceSize:MTLSizeMake(UVrect.w, UVrect.h, 1)
- toTexture:texturedata.mtltextureUv
- destinationSlice:Uslice
+ toTexture:firstplane
+ destinationSlice:0
destinationLevel:0
destinationOrigin:MTLOriginMake(UVrect.x, UVrect.y, 0)];
@@ -1168,8 +1189,8 @@ static void METAL_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture)
sourceBytesPerRow:UVpitch
sourceBytesPerImage:UVpitch * UVrect.h
sourceSize:MTLSizeMake(UVrect.w, UVrect.h, 1)
- toTexture:texturedata.mtltextureUv
- destinationSlice:Vslice
+ toTexture:secondplane
+ destinationSlice:0
destinationLevel:0
destinationOrigin:MTLOriginMake(UVrect.x, UVrect.y, 0)];
}
@@ -1182,7 +1203,7 @@ static void METAL_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture)
sourceBytesPerRow:UVpitch
sourceBytesPerImage:0
sourceSize:MTLSizeMake(UVrect.w, UVrect.h, 1)
- toTexture:texturedata.mtltextureUv
+ toTexture:texturedata.mtltextureUV
destinationSlice:0
destinationLevel:0
destinationOrigin:MTLOriginMake(UVrect.x, UVrect.y, 0)];
@@ -1708,7 +1729,12 @@ static bool SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, c
}
#ifdef SDL_HAVE_YUV
if (texturedata.yuv || texturedata.nv12) {
- [data.mtlcmdencoder setFragmentTexture:texturedata.mtltextureUv atIndex:1];
+ if (texturedata.yuv) {
+ [data.mtlcmdencoder setFragmentTexture:texturedata.mtltextureU atIndex:1];
+ [data.mtlcmdencoder setFragmentTexture:texturedata.mtltextureV atIndex:2];
+ } else {
+ [data.mtlcmdencoder setFragmentTexture:texturedata.mtltextureUV atIndex:1];
+ }
[data.mtlcmdencoder setFragmentBuffer:data.mtlbufconstants offset:texturedata.conversionBufferOffset atIndex:1];
}
#endif
@@ -1747,7 +1773,22 @@ static bool SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, c
static void METAL_InvalidateCachedState(SDL_Renderer *renderer)
{
- // METAL_DrawStateCache only exists during a run of METAL_RunCommandQueue, so there's nothing to invalidate!
+ @autoreleasepool {
+ SDL3METAL_RenderData *data = (__bridge SDL3METAL_RenderData *)renderer->internal;
+
+ // METAL_DrawStateCache only exists during a run of METAL_RunCommandQueue, so there's nothing to invalidate!
+
+ // Make sure any queued drawing is submitted before returning to the application
+ if (data.mtlcmdencoder != nil) {
+ [data.mtlcmdencoder endEncoding];
+ data.mtlcmdencoder = nil;
+ }
+
+ if (data.mtlcmdbuffer != nil) {
+ [data.mtlcmdbuffer commit];
+ data.mtlcmdbuffer = nil;
+ }
+ }
}
static bool METAL_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize)
@@ -2351,14 +2392,25 @@ static bool METAL_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL
}
}
+ mtlcmdqueue = (__bridge id<MTLCommandQueue>)SDL_GetPointerProperty(create_props, SDL_PROP_RENDERER_CREATE_METAL_COMMAND_QUEUE_POINTER, nil);
+ if (mtlcmdqueue != nil) {
+ mtldevice = mtlcmdqueue.device;
+ }
+
+ if (mtldevice == nil) {
+ mtldevice = (__bridge id<MTLDevice>)SDL_GetPointerProperty(create_props, SDL_PROP_RENDERER_CREATE_METAL_DEVICE_POINTER, nil);
+ }
+
#ifdef SDL_PLATFORM_MACOS
- if (SDL_GetHintBoolean(SDL_HINT_RENDER_METAL_PREFER_LOW_POWER_DEVICE, true)) {
- NSArray<id<MTLDevice>> *devices = MTLCopyAllDevices();
+ if (mtldevice == nil) {
+ if (SDL_GetHintBoolean(SDL_HINT_RENDER_METAL_PREFER_LOW_POWER_DEVICE, true)) {
+ NSArray<id<MTLDevice>> *devices = MTLCopyAllDevices();
- for (id<MTLDevice> device in devices) {
- if (device.isLowPower) {
- mtldevice = device;
- break;
+ for (id<MTLDevice> device in devices) {
+ if (device.isLowPower) {
+ mtldevice = device;
+ break;
+ }
}
}
}
@@ -2428,7 +2480,9 @@ in case we want to use it later (recreating the renderer)
data.mtldevice = layer.device;
data.mtllayer = layer;
- mtlcmdqueue = [data.mtldevice newCommandQueue];
+ if (mtlcmdqueue == nil) {
+ mtlcmdqueue = [data.mtldevice newCommandQueue];
+ }
data.mtlcmdqueue = mtlcmdqueue;
data.mtlcmdqueue.label = @"SDL Metal Renderer";
data.mtlpassdesc = [MTLRenderPassDescriptor renderPassDescriptor];
@@ -2556,27 +2610,30 @@ in case we want to use it later (recreating the renderer)
#endif
// https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
- maxtexsize = 4096;
#if defined(SDL_PLATFORM_MACOS) || TARGET_OS_MACCATALYST
maxtexsize = 16384;
#elif defined(SDL_PLATFORM_TVOS)
- maxtexsize = 8192;
if ([mtldevice supportsFeatureSet:MTLFeatureSet_tvOS_GPUFamily2_v1]) {
maxtexsize = 16384;
+ } else {
+ maxtexsize = 8192;
}
#else
- if ([mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily4_v1]) {
- maxtexsize = 16384;
- } else if ([mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1]) {
+ if ([mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily4_v1] ||
+ [mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1]) {
maxtexsize = 16384;
- } else if ([mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v2] || [mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v2]) {
+ } else if ([mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v2] ||
+ [mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v2]) {
maxtexsize = 8192;
} else {
maxtexsize = 4096;
}
#endif
- SDL_SetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER, maxtexsize);
+ SDL_PropertiesID props = SDL_GetRendererProperties(renderer);
+ SDL_SetPointerProperty(props, SDL_PROP_RENDERER_METAL_DEVICE_POINTER, (__bridge void *)data.mtldevice);
+ SDL_SetPointerProperty(props, SDL_PROP_RENDERER_METAL_COMMAND_QUEUE_POINTER, (__bridge void *)data.mtlcmdqueue);
+ SDL_SetNumberProperty(props, SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER, maxtexsize);
return true;
}
diff --git a/src/render/metal/SDL_shaders_metal.metal b/src/render/metal/SDL_shaders_metal.metal
index d9bbf75e4ce09..7958130cb21ee 100644
--- a/src/render/metal/SDL_shaders_metal.metal
+++ b/src/render/metal/SDL_shaders_metal.metal
@@ -337,13 +337,14 @@ fragment float4 SDL_YUV_fragment(CopyVertexOutput vert [[stage_in]],
constant ShaderConstants &c [[buffer(0)]],
constant YUVDecode &decode [[buffer(1)]],
texture2d<float> texY [[texture(0)]],
- texture2d_array<float> texUV [[texture(1)]],
+ texture2d<float> texU [[texture(1)]],
+ texture2d<float> texV [[texture(2)]],
sampler s [[sampler(0)]])
{
float3 yuv;
yuv.x = texY.sample(s, vert.texcoord).r;
- yuv.y = texUV.sample(s, vert.texcoord, 0).r;
- yuv.z = texUV.sample(s, vert.texcoord, 1).r;
+ yuv.y = texU.sample(s, vert.texcoord).r;
+ yuv.z = texV.sample(s, vert.texcoord).r;
float4 rgba;
rgba.rgb = (yuv + decode.offset) * decode.matrix;
diff --git a/src/render/metal/SDL_shaders_metal_ios.h b/src/render/metal/SDL_shaders_metal_ios.h
index 303ffe33b7c6e..44557677a6ce4 100644
--- a/src/render/metal/SDL_shaders_metal_ios.h
+++ b/src/render/metal/SDL_shaders_metal_ios.h
@@ -1,28 +1,28 @@
const unsigned char sdl_metallib[] = {
0x4d, 0x54, 0x4c, 0x42, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x21, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xb1, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x03, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xab, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x8e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x04, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x71, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x9f, 0x00, 0x00,
+ 0x71, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x9f, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00,
0x4e, 0x41, 0x4d, 0x45, 0x11, 0x00, 0x53, 0x44, 0x4c, 0x5f, 0x53, 0x6f,
0x6c, 0x69, 0x64, 0x5f, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x00, 0x54,
0x59, 0x50, 0x45, 0x01, 0x00, 0x00, 0x48, 0x41, 0x53, 0x48, 0x20, 0x00,
- 0x60, 0x7a, 0x81, 0x65, 0xda, 0xf6, 0x14, 0xc4, 0x56, 0x47, 0x0b, 0x7a,
- 0x77, 0x81, 0xf4, 0xc0, 0x01, 0xee, 0xcd, 0x26, 0x7c, 0x2d, 0x8d, 0x7b,
- 0x28, 0x66, 0x63, 0x5c, 0x85, 0xe9, 0xdd, 0xb9, 0x4f, 0x46, 0x46, 0x54,
+ 0xe9, 0x4e, 0xb9, 0x7c, 0x66, 0x26, 0x51, 0xda, 0x95, 0x03, 0xbb, 0x2f,
+ 0x16, 0x77, 0xdd, 0xdb, 0x2b, 0x8a, 0x66, 0xb8, 0x54, 0xe4, 0x9f, 0xf7,
+ 0x7e, 0xa2, 0x3a, 0x3f, 0x67, 0x4a, 0x4f, 0xae, 0x4f, 0x46, 0x46, 0x54,
0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x56, 0x45, 0x52, 0x53, 0x08, 0x00, 0x01, 0x00, 0x08, 0x00,
0x01, 0x00, 0x01, 0x00, 0x45, 0x4e, 0x44, 0x54, 0x77, 0x00, 0x00, 0x00,
0x4e, 0x41, 0x4d, 0x45, 0x10, 0x00, 0x53, 0x44, 0x4c, 0x5f, 0x43, 0x6f,
0x70, 0x79, 0x5f, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x00, 0x54, 0x59,
- 0x50, 0x45, 0x01, 0x00, 0x00, 0x48, 0x41, 0x53, 0x48, 0x20, 0x00, 0x6e,
- 0xc3, 0x21, 0x35, 0x82, 0x00, 0x01, 0xe8, 0xa4, 0x27, 0xe1, 0xec, 0x73,
- 0xf1, 0xe9, 0x58, 0x2e, 0x4a, 0x0d, 0xc0, 0x5a, 0x63, 0x1e, 0x7f, 0x35,
- 0xf1, 0x6e, 0x35, 0x4e, 0xbf, 0x18, 0xae, 0x4f, 0x46, 0x46, 0x54, 0x18,
+ 0x50, 0x45, 0x01, 0x00, 0x00, 0x48, 0x41, 0x53, 0x48, 0x20, 0x00, 0xed,
+ 0x0f, 0xc1, 0xa4, 0x27, 0xa9, 0x18, 0x07, 0x3d, 0xfb, 0x0c, 0xb6, 0x92,
+ 0xaf, 0x0c, 0xb2, 0x66, 0xf7, 0x24, 0x24, 0x41, 0xf5, 0xd9, 0x1d, 0xac,
+ 0x1f, 0xdc, 0xc0, 0x8d, 0xfa, 0xb6, 0x03, 0x4f, 0x46, 0x46, 0x54, 0x18,
0x00, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x56, 0x45, 0x52, 0x53, 0x08, 0x00, 0x01, 0x00, 0x08, 0x00, 0x01,
@@ -30,9 +30,9 @@ const unsigned char sdl_metallib[] =
(Patch may be truncated, please check the link at the top of this post.)