metal: Implement SDL_LockTexture for YUV formats.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
diff --git a/src/render/metal/SDL_render_metal.m b/src/render/metal/SDL_render_metal.m
index 272ae6d..1c5f16d 100644
--- a/src/render/metal/SDL_render_metal.m
+++ b/src/render/metal/SDL_render_metal.m
@@ -789,16 +789,22 @@ METAL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
{ @autoreleasepool {
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
METAL_TextureData *texturedata = (__bridge METAL_TextureData *)texture->driverdata;
+ int buffersize = 0;
- if (texturedata.yuv || texturedata.nv12) {
- /* FIXME: write me */
- return SDL_Unsupported();
+ if (rect->w <= 0 || rect->h <= 0) {
+ return SDL_SetError("Invalid rectangle dimensions for LockTexture.");
}
*pitch = SDL_BYTESPERPIXEL(texture->format) * rect->w;
+ if (texturedata.yuv || texturedata.nv12) {
+ buffersize = ((*pitch) * rect->h) + (2 * (*pitch + 1) / 2) * ((rect->h + 1) / 2);
+ } else {
+ buffersize = (*pitch) * rect->h;
+ }
+
texturedata.lockedrect = *rect;
- texturedata.lockedbuffer = [data.mtldevice newBufferWithLength:(*pitch)*rect->h options:MTLResourceStorageModeShared];
+ texturedata.lockedbuffer = [data.mtldevice newBufferWithLength:buffersize options:MTLResourceStorageModeShared];
if (texturedata.lockedbuffer == nil) {
return SDL_OutOfMemory();
}
@@ -814,6 +820,8 @@ METAL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture)
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
METAL_TextureData *texturedata = (__bridge METAL_TextureData *)texture->driverdata;
SDL_Rect rect = texturedata.lockedrect;
+ int pitch = SDL_BYTESPERPIXEL(texture->format) * rect.w;
+ SDL_Rect UVrect = {rect.x / 2, rect.y / 2, (rect.w + 1) / 2, (rect.h + 1) / 2};
if (texturedata.lockedbuffer == nil) {
return;
@@ -832,7 +840,7 @@ METAL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture)
[blitcmd copyFromBuffer:texturedata.lockedbuffer
sourceOffset:0
- sourceBytesPerRow:SDL_BYTESPERPIXEL(texture->format) * rect.w
+ sourceBytesPerRow:pitch
sourceBytesPerImage:0
sourceSize:MTLSizeMake(rect.w, rect.h, 1)
toTexture:texturedata.mtltexture
@@ -840,6 +848,46 @@ METAL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture)
destinationLevel:0
destinationOrigin:MTLOriginMake(rect.x, rect.y, 0)];
+ if (texturedata.yuv) {
+ int Uslice = texture->format == SDL_PIXELFORMAT_YV12 ? 1 : 0;
+ int Vslice = texture->format == SDL_PIXELFORMAT_YV12 ? 0 : 1;
+ int UVpitch = (pitch + 1) / 2;
+
+ [blitcmd copyFromBuffer:texturedata.lockedbuffer
+ sourceOffset:rect.h * pitch
+ sourceBytesPerRow:UVpitch
+ sourceBytesPerImage:UVpitch * UVrect.h
+ sourceSize:MTLSizeMake(UVrect.w, UVrect.h, 1)
+ toTexture:texturedata.mtltexture_uv
+ destinationSlice:Uslice
+ destinationLevel:0
+ destinationOrigin:MTLOriginMake(UVrect.x, UVrect.y, 0)];
+
+ [blitcmd copyFromBuffer:texturedata.lockedbuffer
+ sourceOffset:(rect.h * pitch) + UVrect.h * UVpitch
+ sourceBytesPerRow:UVpitch
+ sourceBytesPerImage:UVpitch * UVrect.h
+ sourceSize:MTLSizeMake(UVrect.w, UVrect.h, 1)
+ toTexture:texturedata.mtltexture_uv
+ destinationSlice:Vslice
+ destinationLevel:0
+ destinationOrigin:MTLOriginMake(UVrect.x, UVrect.y, 0)];
+ }
+
+ if (texturedata.nv12) {
+ int UVpitch = 2 * ((pitch + 1) / 2);
+
+ [blitcmd copyFromBuffer:texturedata.lockedbuffer
+ sourceOffset:rect.h * pitch
+ sourceBytesPerRow:UVpitch
+ sourceBytesPerImage:0
+ sourceSize:MTLSizeMake(UVrect.w, UVrect.h, 1)
+ toTexture:texturedata.mtltexture_uv
+ destinationSlice:0
+ destinationLevel:0
+ destinationOrigin:MTLOriginMake(UVrect.x, UVrect.y, 0)];
+ }
+
[blitcmd endEncoding];
[data.mtlcmdbuffer commit];