4#include <TargetConditionals.h>
5#import <CoreFoundation/CoreFoundation.h>
8#if defined(__APPLE__) && (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
10#import <MetalKit/MetalKit.h>
24#if defined(__APPLE__) && (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
25MTLPixelFormat GetMetalPixelFormatForSDL(uint32_t format) {
27 case SDL_PIXELFORMAT_ARGB8888:
28 case SDL_PIXELFORMAT_BGRA8888:
29 return MTLPixelFormatBGRA8Unorm;
30 case SDL_PIXELFORMAT_RGBA8888:
31 case SDL_PIXELFORMAT_ABGR8888:
32 return MTLPixelFormatRGBA8Unorm;
34 return MTLPixelFormatRGBA8Unorm;
38int BytesPerPixel(MTLPixelFormat format) {
40 case MTLPixelFormatBGRA8Unorm:
41 case MTLPixelFormatRGBA8Unorm:
57#if defined(__APPLE__) && (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
59 LOG_WARN(
"MetalRenderer",
"Metal view not attached");
63 id<MTLDevice> device = view.device;
65 device = MTLCreateSystemDefaultDevice();
69 LOG_WARN(
"MetalRenderer",
"Failed to create Metal device");
73 id<MTLCommandQueue> queue = [device newCommandQueue];
91#if defined(__APPLE__) && (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
97 id<MTLDevice> device = view.device;
99 device = MTLCreateSystemDefaultDevice();
100 view.device = device;
106#if defined(__APPLE__) && (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
107 MTLPixelFormat default_format = MTLPixelFormatRGBA8Unorm;
109 MTLPixelFormat default_format = MTLPixelFormatBGRA8Unorm;
111 MTLTextureDescriptor* descriptor =
112 [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:default_format
116 descriptor.usage = MTLTextureUsageShaderRead;
117 descriptor.storageMode = MTLStorageModeShared;
119 id<MTLTexture> texture = [device newTextureWithDescriptor:descriptor];
120 return texture ? (__bridge_retained
void*)texture :
nullptr;
129#if defined(__APPLE__) && (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
135 id<MTLDevice> device = view.device;
137 device = MTLCreateSystemDefaultDevice();
138 view.device = device;
144#if defined(__APPLE__) && (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
145 MTLPixelFormat default_format = MTLPixelFormatRGBA8Unorm;
147 MTLPixelFormat default_format = MTLPixelFormatBGRA8Unorm;
149 MTLTextureDescriptor* descriptor =
150 [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:default_format
154 descriptor.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget;
155 descriptor.storageMode = MTLStorageModeShared;
157 id<MTLTexture> texture = [device newTextureWithDescriptor:descriptor];
158 return texture ? (__bridge_retained
void*)texture :
nullptr;
169#if defined(__APPLE__) && (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
175 id<MTLDevice> device = view.device;
177 device = MTLCreateSystemDefaultDevice();
178 view.device = device;
184 MTLPixelFormat pixel_format = GetMetalPixelFormatForSDL(format);
185 MTLTextureDescriptor* descriptor =
186 [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:pixel_format
190 descriptor.usage = MTLTextureUsageShaderRead;
191 if (access == SDL_TEXTUREACCESS_TARGET) {
192 descriptor.usage |= MTLTextureUsageRenderTarget;
194 descriptor.storageMode = MTLStorageModeShared;
196 id<MTLTexture> texture = [device newTextureWithDescriptor:descriptor];
197 return texture ? (__bridge_retained
void*)texture :
nullptr;
206#if defined(__APPLE__) && (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
211 SDL_Surface* surface = bitmap.
surface();
212 if (!surface || !surface->pixels || surface->w <= 0 || surface->h <= 0) {
216 id<MTLTexture> metal_texture = (__bridge id<MTLTexture>)texture;
217 const MTLPixelFormat pixel_format = metal_texture.pixelFormat;
220 uint32_t target_format = SDL_PIXELFORMAT_RGBA32;
221 if (pixel_format == MTLPixelFormatBGRA8Unorm) {
222 target_format = SDL_PIXELFORMAT_BGRA32;
225 auto converted_surface =
226 std::unique_ptr<SDL_Surface, util::SDL_Surface_Deleter>(
228 if (!converted_surface || !converted_surface->pixels) {
234 {
static_cast<NSUInteger
>(converted_surface->w),
235 static_cast<NSUInteger
>(converted_surface->h),
237 [metal_texture replaceRegion:region
239 withBytes:converted_surface->pixels
240 bytesPerRow:converted_surface->pitch];
255#if defined(__APPLE__) && (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
263 void** pixels,
int* pitch) {
264#if defined(__APPLE__) && (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
265 if (!texture || !pixels || !pitch) {
269 id<MTLTexture> metal_texture = (__bridge id<MTLTexture>)texture;
270 const int width =
static_cast<int>(metal_texture.width);
271 const int height =
static_cast<int>(metal_texture.height);
272 const int bytes_per_pixel = BytesPerPixel(metal_texture.pixelFormat);
273 const int row_pitch = width * bytes_per_pixel;
274 if (row_pitch <= 0 || height <= 0) {
279 staging.width = width;
280 staging.height = height;
281 staging.pitch = row_pitch;
282 staging.data.resize(
static_cast<size_t>(row_pitch) *
283 static_cast<size_t>(height));
292 *pixels = staging.data.data();
305#if defined(__APPLE__) && (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
314 id<MTLTexture> metal_texture = (__bridge id<MTLTexture>)texture;
315 auto& staging = it->second;
316 if (staging.data.empty()) {
322 {
static_cast<NSUInteger
>(staging.width),
323 static_cast<NSUInteger
>(staging.height),
325 [metal_texture replaceRegion:region
327 withBytes:staging.data.data()
328 bytesPerRow:staging.pitch];
341 const SDL_Rect* dstrect) {
342#if defined(__APPLE__) && (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
347 id<MTLTexture> source = (__bridge id<MTLTexture>)texture;
350 int src_x = srcrect ? srcrect->x : 0;
351 int src_y = srcrect ? srcrect->y : 0;
352 int src_w = srcrect ? srcrect->w :
static_cast<int>(source.width);
353 int src_h = srcrect ? srcrect->h :
static_cast<int>(source.height);
355 int dst_x = dstrect ? dstrect->x : 0;
356 int dst_y = dstrect ? dstrect->y : 0;
358 src_w = std::min(src_w,
static_cast<int>(source.width) - src_x);
359 src_h = std::min(src_h,
static_cast<int>(source.height) - src_y);
361 if (src_w <= 0 || src_h <= 0) {
365 MTLOrigin src_origin = {
static_cast<NSUInteger
>(src_x),
366 static_cast<NSUInteger
>(src_y),
368 MTLSize src_size = {
static_cast<NSUInteger
>(src_w),
369 static_cast<NSUInteger
>(src_h),
371 MTLOrigin dst_origin = {
static_cast<NSUInteger
>(dst_x),
372 static_cast<NSUInteger
>(dst_y),
375 id<MTLCommandQueue> queue = (__bridge id<MTLCommandQueue>)
command_queue_;
376 id<MTLCommandBuffer> command_buffer = [queue commandBuffer];
377 id<MTLBlitCommandEncoder> blit = [command_buffer blitCommandEncoder];
378 [blit copyFromTexture:source
381 sourceOrigin:src_origin
386 destinationOrigin:dst_origin];
388 [command_buffer commit];
389 [command_buffer waitUntilCompleted];
Represents a bitmap image optimized for SNES ROM hacking.
SDL_Surface * surface() const
#define LOG_WARN(category, format,...)
void * TextureHandle
An abstract handle representing a texture.
SDL2/SDL3 compatibility layer.