From 5e1f991e32b397a85375ba840196251c0fa61567 Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Fri, 29 Jun 2018 22:57:48 -0700 Subject: [PATCH] feat(Metal): initial xmb and glui menu support TODOs * menu shader pipeline effects * refactor / optimize graphics rendering --- gfx/common/metal/Context.h | 19 +- gfx/common/metal/Context.m | 187 +++- gfx/common/metal/MenuDisplay.h | 27 + gfx/common/metal/MenuDisplay.m | 257 +++++ gfx/common/metal/MetalRenderer.h | 10 - gfx/common/metal/PixelConverter+private.h | 13 - gfx/common/metal/PixelConverter.h | 20 - gfx/common/metal/PixelConverter.m | 49 - gfx/common/metal/PixelConverter.metal | 49 - gfx/common/metal/Renderer.h | 37 - gfx/common/metal/Renderer.m | 263 ----- gfx/common/metal/RendererCommon.m | 76 +- gfx/common/metal/ShaderTypes.h | 35 +- gfx/common/metal/Shaders.metal | 81 +- gfx/common/metal/TexturedView.h | 6 +- gfx/common/metal/TexturedView.m | 12 +- gfx/common/metal/View.h | 15 - gfx/common/metal/View.m | 1 + gfx/common/metal/metal_common.h | 3 +- gfx/common/metal_common.h | 22 +- gfx/common/metal_common.m | 949 ++++++++++++------ gfx/drivers/metal.m | 170 ++-- gfx/drivers_font/metal_raster_font.m | 236 +++-- griffin/griffin_objc.m | 3 +- menu/drivers_display/menu_display_metal.m | 102 +- .../RetroArch_Metal.xcodeproj/project.pbxproj | 246 ++++- 26 files changed, 1753 insertions(+), 1135 deletions(-) create mode 100644 gfx/common/metal/MenuDisplay.h create mode 100644 gfx/common/metal/MenuDisplay.m delete mode 100644 gfx/common/metal/MetalRenderer.h delete mode 100644 gfx/common/metal/PixelConverter+private.h delete mode 100644 gfx/common/metal/PixelConverter.h delete mode 100644 gfx/common/metal/PixelConverter.m delete mode 100644 gfx/common/metal/PixelConverter.metal delete mode 100644 gfx/common/metal/Renderer.h delete mode 100644 gfx/common/metal/Renderer.m diff --git a/gfx/common/metal/Context.h b/gfx/common/metal/Context.h index b410d8eff8..98f0101842 100644 --- a/gfx/common/metal/Context.h +++ b/gfx/common/metal/Context.h @@ -8,9 +8,14 @@ #import #import +#import "RendererCommon.h" -NS_ASSUME_NONNULL_BEGIN +@interface Texture : NSObject +@property (readonly) id texture; +@property (readonly) id sampler; +@end +/*! @brief Context contains the render state used by various components */ @interface Context : NSObject @property (readonly) id device; @@ -21,10 +26,12 @@ NS_ASSUME_NONNULL_BEGIN @property (readonly) id nextDrawable; @property (readonly) id renderTexture; -+ (instancetype)newContextWithDevice:(id)d - layer:(CAMetalLayer *)layer - library:(id)l - commandQueue:(id)q; +- (instancetype)initWithDevice:(id)d + layer:(CAMetalLayer *)layer + library:(id)l; + +- (Texture *)newTexture:(struct texture_image)image filter:(enum texture_filter_type)filter; +- (void)convertFormat:(RPixelFormat)fmt from:(id)src to:(id)dst; /*! @brief begin marks the beginning of a frame */ - (void)begin; @@ -33,5 +40,3 @@ NS_ASSUME_NONNULL_BEGIN - (void)end; @end - -NS_ASSUME_NONNULL_END diff --git a/gfx/common/metal/Context.m b/gfx/common/metal/Context.m index 14df7a7217..f525617841 100644 --- a/gfx/common/metal/Context.m +++ b/gfx/common/metal/Context.m @@ -7,43 +7,178 @@ // #import "Context.h" +#import "Filter.h" #import -@interface Context() -{ - CAMetalLayer *_layer; - id _drawable; -} +@interface Texture() +@property (readwrite) id texture; +@property (readwrite) id sampler; +@end +@interface Context() +@property (readonly) id blitCommandBuffer; +- (bool)_initConversionFilters; @end @implementation Context - -+ (instancetype)newContextWithDevice:(id)d - layer:(CAMetalLayer *)layer - library:(id)l - commandQueue:(id)q { - Context *c = [Context new]; - c->_device = d; - c->_layer = layer; - c->_library = l; - c->_commandQueue = q; - - return c; + id _commandQueue; + CAMetalLayer *_layer; + id _drawable; + id _samplers[TEXTURE_FILTER_MIPMAP_NEAREST + 1]; + Filter *_filters[RPixelFormatCount]; // convert to bgra8888 + + id _blitCommandBuffer; } -- (id)nextDrawable { - if (_drawable == nil) { +- (instancetype)initWithDevice:(id)d + layer:(CAMetalLayer *)layer + library:(id)l +{ + if (self = [super init]) + { + _device = d; + _layer = layer; + _library = l; + _commandQueue = [_device newCommandQueue]; + + MTLSamplerDescriptor *sd = [MTLSamplerDescriptor new]; + + sd.label = @"NEAREST"; + _samplers[TEXTURE_FILTER_NEAREST] = [d newSamplerStateWithDescriptor:sd]; + + sd.mipFilter = MTLSamplerMipFilterNearest; + sd.label = @"MIPMAP_NEAREST"; + _samplers[TEXTURE_FILTER_MIPMAP_NEAREST] = [d newSamplerStateWithDescriptor:sd]; + + sd.mipFilter = MTLSamplerMipFilterNotMipmapped; + sd.minFilter = MTLSamplerMinMagFilterLinear; + sd.magFilter = MTLSamplerMinMagFilterLinear; + sd.label = @"LINEAR"; + _samplers[TEXTURE_FILTER_LINEAR] = [d newSamplerStateWithDescriptor:sd]; + + sd.mipFilter = MTLSamplerMipFilterLinear; + sd.label = @"MIPMAP_LINEAR"; + _samplers[TEXTURE_FILTER_MIPMAP_LINEAR] = [d newSamplerStateWithDescriptor:sd]; + + if (![self _initConversionFilters]) + return nil; + } + return self; +} + +- (bool)_initConversionFilters +{ + NSError *err = nil; + _filters[RPixelFormatBGRA4Unorm] = [Filter newFilterWithFunctionName:@"convert_bgra4444_to_bgra8888" + device:_device + library:_library + error:&err]; + if (err) + { + RARCH_LOG("[Metal]: unable to create 'convert_bgra4444_to_bgra8888' conversion filter: %s\n", + err.localizedDescription.UTF8String); + return NO; + } + + _filters[RPixelFormatB5G6R5Unorm] = [Filter newFilterWithFunctionName:@"convert_rgb565_to_bgra8888" + device:_device + library:_library + error:&err]; + if (err) + { + RARCH_LOG("[Metal]: unable to create 'convert_rgb565_to_bgra8888' conversion filter: %s\n", + err.localizedDescription.UTF8String); + return NO; + } + + return YES; +} + +- (Texture *)newTexture:(struct texture_image)image filter:(enum texture_filter_type)filter +{ + if (!image.pixels && !image.width && !image.height) + { + /* Create a dummy texture instead. */ +#define T0 0xff000000u +#define T1 0xffffffffu + static const uint32_t checkerboard[] = { + T0, T1, T0, T1, T0, T1, T0, T1, + T1, T0, T1, T0, T1, T0, T1, T0, + T0, T1, T0, T1, T0, T1, T0, T1, + T1, T0, T1, T0, T1, T0, T1, T0, + T0, T1, T0, T1, T0, T1, T0, T1, + T1, T0, T1, T0, T1, T0, T1, T0, + T0, T1, T0, T1, T0, T1, T0, T1, + T1, T0, T1, T0, T1, T0, T1, T0, + }; +#undef T0 +#undef T1 + + image.pixels = (uint32_t *)checkerboard; + image.width = 8; + image.height = 8; + } + + // TODO(sgc): mipmapping is not working + BOOL mipmapped = filter == TEXTURE_FILTER_MIPMAP_LINEAR || filter == TEXTURE_FILTER_MIPMAP_NEAREST; + + MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm + width:image.width + height:image.height + mipmapped:mipmapped]; + + id t = [_device newTextureWithDescriptor:td]; + [t replaceRegion:MTLRegionMake2D(0, 0, image.width, image.height) + mipmapLevel:0 + withBytes:image.pixels + bytesPerRow:4 * image.width]; + + if (mipmapped) + { + id cb = self.blitCommandBuffer; + id bce = [cb blitCommandEncoder]; + [bce generateMipmapsForTexture:t]; + [bce endEncoding]; + } + + Texture *tex = [Texture new]; + tex.texture = t; + tex.sampler = _samplers[filter]; + + return tex; +} + +- (id)nextDrawable +{ + if (_drawable == nil) + { _drawable = _layer.nextDrawable; } return _drawable; } -- (id)renderTexture { +- (id)renderTexture +{ return self.nextDrawable.texture; } +- (void)convertFormat:(RPixelFormat)fmt from:(id)src to:(id)dst +{ + assert(dst.width * dst.height == src.length / RPixelFormatToBPP(fmt)); + assert(fmt >= 0 && fmt < RPixelFormatCount); + Filter *conv = _filters[fmt]; + assert(conv != nil); + [conv apply:self.commandBuffer inBuf:src outTex:dst]; +} + +- (id)blitCommandBuffer +{ + if (!_blitCommandBuffer) + _blitCommandBuffer = [_commandQueue commandBuffer]; + return _blitCommandBuffer; +} + - (void)begin { assert(_commandBuffer == nil); @@ -53,9 +188,21 @@ - (void)end { assert(self->_commandBuffer != nil); + + if (_blitCommandBuffer) + { + // pending blits for mipmaps + [_blitCommandBuffer commit]; + [_blitCommandBuffer waitUntilCompleted]; + _blitCommandBuffer = nil; + } + [_commandBuffer commit]; _commandBuffer = nil; _drawable = nil; } @end + +@implementation Texture +@end diff --git a/gfx/common/metal/MenuDisplay.h b/gfx/common/metal/MenuDisplay.h new file mode 100644 index 0000000000..fb8bef3c62 --- /dev/null +++ b/gfx/common/metal/MenuDisplay.h @@ -0,0 +1,27 @@ +// +// Created by Stuart Carnie on 6/24/18. +// + +#import +#import "ShaderTypes.h" + +@class Context; +@class MetalDriver; + +@interface MenuDisplay : NSObject + +@property (readwrite) BOOL blend; +@property (readwrite) MTLClearColor clearColor; + +- (instancetype)initWithDriver:(MetalDriver *)driver; +- (void)draw:(menu_display_ctx_draw_t *)draw video:(video_frame_info_t *)video; + +#pragma mark - static methods + ++ (const float *)defaultVertices; ++ (const float *)defaultTexCoords; ++ (const float *)defaultColor; ++ (const float *)defaultMatrix; + + +@end diff --git a/gfx/common/metal/MenuDisplay.m b/gfx/common/metal/MenuDisplay.m new file mode 100644 index 0000000000..0510f655ce --- /dev/null +++ b/gfx/common/metal/MenuDisplay.m @@ -0,0 +1,257 @@ +// +// Created by Stuart Carnie on 6/24/18. +// + +#import "Context.h" +#import "MenuDisplay.h" +#import "ShaderTypes.h" +#import "menu_driver.h" +#import +// TODO(sgc): this dependency is incorrect +#import "../metal_common.h" + +@implementation MenuDisplay +{ + __weak MetalDriver *_driver; + Context *_context; + MTLClearColor _clearColor; + bool _clearNextRender; + Uniforms _uniforms; +} + +- (instancetype)initWithDriver:(MetalDriver *)driver +{ + if (self = [super init]) + { + _driver = driver; + _context = driver.context; + _clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 1.0); + _uniforms.projectionMatrix = matrix_proj_ortho(0, 1, 0, 1); + } + return self; +} + ++ (const float *)defaultVertices +{ + static float dummy[] = { + 0.0f, 1.0f, + 1.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 0.0f, + }; + return &dummy[0]; +} + ++ (const float *)defaultTexCoords +{ + static float dummy[] = { + 0.0f, 1.0f, + 1.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 0.0f, + }; + return &dummy[0]; +} + ++ (const float *)defaultColor +{ + static float dummy[] = { + 1.0f, 0.0f, 1.0f, 1.0f, + 1.0f, 0.0f, 1.0f, 1.0f, + 1.0f, 0.0f, 1.0f, 1.0f, + 1.0f, 0.0f, 1.0f, 1.0f, + }; + return &dummy[0]; +} + ++ (const float *)defaultMatrix +{ + static matrix_float4x4 dummy; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + dummy = matrix_proj_ortho(0, 1, 0, 1); + }); + return &dummy; +} + +- (void)setClearColor:(MTLClearColor)clearColor +{ + _clearColor = clearColor; + _clearNextRender = YES; +} + +- (MTLClearColor)clearColor +{ + return _clearColor; +} + +- (MTLPrimitiveType)_toPrimitiveType:(enum menu_display_prim_type)prim +{ + switch (prim) + { + case MENU_DISPLAY_PRIM_TRIANGLESTRIP: + return MTLPrimitiveTypeTriangleStrip; + case MENU_DISPLAY_PRIM_TRIANGLES: + return MTLPrimitiveTypeTriangle; + default: + RARCH_LOG("unexpected primitive type %d\n", prim); + return MTLPrimitiveTypeTriangle; + } +} + +static INLINE void write_quad4(SpriteVertex *pv, + float x, float y, float width, float height, float scale, + float tex_x, float tex_y, float tex_width, float tex_height, + const float *color) +{ + unsigned i; + static const float strip[2 * 4] = { + 0.0f, 1.0f, + 1.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 0.0f, + }; + + float swidth = width * scale; + float sheight = height * scale; + + x += (width - swidth) / 2; + y += (height - sheight) / 2; + + for (i = 0; i < 4; i++) + { + pv[i].position = simd_make_float2(x + strip[2 * i + 0] * swidth, + y + strip[2 * i + 1] * sheight); + pv[i].texCoord = simd_make_float2(tex_x + strip[2 * i + 0] * tex_width, + tex_y + strip[2 * i + 1] * tex_height); + pv[i].color = simd_make_float4(color[0], color[1], color[2], color[3]); + color += 4; + } +} + +static INLINE void write_quad4a(SpriteVertex *pv, + float x, float y, float width, float height, float scale, + float tex_x, float tex_y, float tex_width, float tex_height, + const float *color) +{ + unsigned i; + static const float vert[2 * 4] = { + 0.0f, 1.0f, + 1.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 0.0f, + }; + static const float tex[2 * 4] = { + 0.0f, 0.0f, + 1.0f, 0.0f, + 0.0f, 1.0f, + 1.0f, 1.0f, + }; + + float swidth = width * scale; + float sheight = height * scale; + + x += (width - swidth) / 2; + y += (height - sheight) / 2; + + for (i = 0; i < 4; i++) + { + pv[i].position = simd_make_float2(x + vert[2 * i + 0] * swidth, + y + vert[2 * i + 1] * sheight); + pv[i].texCoord = simd_make_float2(tex_x + tex[2 * i + 0] * tex_width, + tex_y + tex[2 * i + 1] * tex_height); + pv[i].color = simd_make_float4(color[0], color[1], color[2], color[3]); + color += 4; + } +} + +- (void)draw:(menu_display_ctx_draw_t *)draw video:(video_frame_info_t *)video +{ + Texture *tex = (__bridge Texture *)(void *)draw->texture; + const float *vertex = draw->coords->vertex; + const float *tex_coord = draw->coords->tex_coord; + const float *color = draw->coords->color; + + if (!vertex) + vertex = MenuDisplay.defaultVertices; + if (!tex_coord) + tex_coord = MenuDisplay.defaultTexCoords; + if (!draw->coords->lut_tex_coord) + draw->coords->lut_tex_coord = MenuDisplay.defaultTexCoords; + + // TODO(sgc): is this necessary? + // if (!texture) + // texture = &vk->display.blank_texture; + if (!color) + color = MenuDisplay.defaultColor; + + assert(draw->coords->vertices <= 4); + SpriteVertex buf[4]; + SpriteVertex *pv = buf; + Uniforms *uniforms; + if (draw->coords->vertex == NULL) + { + write_quad4a(pv, + draw->x, + draw->y, + draw->width, + draw->height, + draw->scale_factor, + 0.0, 0.0, 1.0, 1.0, color); + + uniforms = _driver.viewportMVP; + } + else + { + for (unsigned i = 0; i < draw->coords->vertices; i++, pv++) + { + /* Y-flip. We're using top-left coordinates */ + pv->position = simd_make_float2(vertex[0], vertex[1]); + vertex += 2; + + pv->texCoord = simd_make_float2(tex_coord[0], tex_coord[1]); + tex_coord += 2; + + pv->color = simd_make_float4(color[0], color[1], color[2], color[3]); + color += 4; + } + uniforms = &_uniforms; + } + + switch (draw->pipeline.id) + { +#ifdef HAVE_SHADERPIPELINE +#endif + default: + { + MTLRenderPassDescriptor *rpd = [MTLRenderPassDescriptor new]; + if (_clearNextRender) + { + rpd.colorAttachments[0].clearColor = _clearColor; + rpd.colorAttachments[0].loadAction = MTLLoadActionClear; + _clearNextRender = NO; + } + else + { + rpd.colorAttachments[0].loadAction = MTLLoadActionLoad; + } + rpd.colorAttachments[0].storeAction = MTLStoreActionStore; + rpd.colorAttachments[0].texture = _context.nextDrawable.texture; + + id cb = _context.commandBuffer; + + id rce = [cb renderCommandEncoderWithDescriptor:rpd]; + [rce setRenderPipelineState:[_driver getStockShader:VIDEO_SHADER_STOCK_BLEND blend:_blend]]; + [rce setVertexBytes:uniforms length:sizeof(*uniforms) atIndex:BufferIndexUniforms]; + [rce setVertexBytes:buf length:sizeof(buf) atIndex:BufferIndexPositions]; + [rce setFragmentTexture:tex.texture atIndex:TextureIndexColor]; + [rce setFragmentSamplerState:tex.sampler atIndex:SamplerIndexDraw]; + [rce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4]; + [rce endEncoding]; + + break; + } + } +} +@end diff --git a/gfx/common/metal/MetalRenderer.h b/gfx/common/metal/MetalRenderer.h deleted file mode 100644 index 184d7ec637..0000000000 --- a/gfx/common/metal/MetalRenderer.h +++ /dev/null @@ -1,10 +0,0 @@ -// -// MetalRenderer.h -// MetalRenderer -// -// Created by Stuart Carnie on 6/7/18. -// Copyright © 2018 Stuart Carnie. All rights reserved. -// - -#import "Renderer.h" -#import "RView.h" diff --git a/gfx/common/metal/PixelConverter+private.h b/gfx/common/metal/PixelConverter+private.h deleted file mode 100644 index b72790f359..0000000000 --- a/gfx/common/metal/PixelConverter+private.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// PixelConverter+private.h -// MetalRenderer -// -// Created by Stuart Carnie on 6/9/18. -// Copyright © 2018 Stuart Carnie. All rights reserved. -// -#import "PixelConverter.h" -#import "Context.h" - -@interface PixelConverter() -- (instancetype)initWithContext:(Context *)c; -@end diff --git a/gfx/common/metal/PixelConverter.h b/gfx/common/metal/PixelConverter.h deleted file mode 100644 index 7386ce83ad..0000000000 --- a/gfx/common/metal/PixelConverter.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// PixelConverter.h -// MetalRenderer -// -// Created by Stuart Carnie on 6/9/18. -// Copyright © 2018 Stuart Carnie. All rights reserved. -// - -#import -#import - -#import "RendererCommon.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface PixelConverter : NSObject -- (void)convertFormat:(RPixelFormat)fmt from:(id)src to:(id)dst; -@end - -NS_ASSUME_NONNULL_END diff --git a/gfx/common/metal/PixelConverter.m b/gfx/common/metal/PixelConverter.m deleted file mode 100644 index 24aa14e9a8..0000000000 --- a/gfx/common/metal/PixelConverter.m +++ /dev/null @@ -1,49 +0,0 @@ -// -// PixelConverter.m -// MetalRenderer -// -// Created by Stuart Carnie on 6/9/18. -// Copyright © 2018 Stuart Carnie. All rights reserved. -// - -#import "PixelConverter+private.h" -#import "Filter.h" -#import "Context.h" - -@implementation PixelConverter { - Context *_context; - Filter *_filters[RPixelFormatCount]; // convert to bgra8888 -} - -- (instancetype)initWithContext:(Context *)c -{ - if (self = [super init]) - { - _context = c; - NSError *err = nil; - _filters[RPixelFormatBGRA4Unorm] = [Filter newFilterWithFunctionName:@"convert_bgra4444_to_bgra8888" - device:c.device library:c.library - error:&err]; - _filters[RPixelFormatB5G6R5Unorm] = [Filter newFilterWithFunctionName:@"convert_rgb565_to_bgra8888" - device:c.device - library:c.library - error:&err]; - if (err) - { - NSLog(@"unable to create pixel conversion filter: %@", err.localizedDescription); - abort(); - } - } - return self; -} - -- (void)convertFormat:(RPixelFormat)fmt from:(id)src to:(id)dst -{ - assert(dst.width*dst.height == src.length/RPixelFormatToBPP(fmt)); - assert(fmt >= 0 && fmt < RPixelFormatCount); - Filter *conv = _filters[fmt]; - assert(conv != nil); - [conv apply:_context.commandBuffer inBuf:src outTex:dst]; -} - -@end diff --git a/gfx/common/metal/PixelConverter.metal b/gfx/common/metal/PixelConverter.metal deleted file mode 100644 index 97fc1ae636..0000000000 --- a/gfx/common/metal/PixelConverter.metal +++ /dev/null @@ -1,49 +0,0 @@ -// -// PixelConverter.metal -// MetalRenderer -// -// Created by Stuart Carnie on 6/9/18. -// Copyright © 2018 Stuart Carnie. All rights reserved. -// - -#include -using namespace metal; - - -#pragma mark - filter kernels - -kernel void convert_bgra4444_to_bgra8888(device uint16_t * in [[ buffer(0) ]], - texture2d out [[ texture(0) ]], - uint id [[ thread_position_in_grid ]]) -{ - uint16_t pix = in[id]; - uchar4 pix2 = uchar4( - extract_bits(pix, 4, 4), - extract_bits(pix, 8, 4), - extract_bits(pix, 12, 4), - extract_bits(pix, 0, 4) - ); - - uint ypos = id / out.get_width(); - uint xpos = id % out.get_width(); - - out.write(half4(pix2) / 15.0, uint2(xpos, ypos)); -} - -kernel void convert_rgb565_to_bgra8888(device uint16_t * in [[ buffer(0) ]], - texture2d out [[ texture(0) ]], - uint id [[ thread_position_in_grid ]]) -{ - uint16_t pix = in[id]; - uchar4 pix2 = uchar4( - extract_bits(pix, 11, 5), - extract_bits(pix, 5, 6), - extract_bits(pix, 0, 5), - 0xf - ); - - uint ypos = id / out.get_width(); - uint xpos = id % out.get_width(); - - out.write(half4(pix2) / half4(0x1f, 0x3f, 0x1f, 0xf), uint2(xpos, ypos)); -} diff --git a/gfx/common/metal/Renderer.h b/gfx/common/metal/Renderer.h deleted file mode 100644 index 5142e7f09a..0000000000 --- a/gfx/common/metal/Renderer.h +++ /dev/null @@ -1,37 +0,0 @@ -// -// Renderer.h -// MetalRenderer -// -// Created by Stuart Carnie on 5/31/18. -// Copyright © 2018 Stuart Carnie. All rights reserved. -// - - -#import -#import -#import "Context.h" -#import "PixelConverter.h" - -@protocol View; - -@interface Renderer : NSObject - -@property (readonly) Context* context; -@property (readonly) PixelConverter* conv; - -- (instancetype)initWithDevice:(id)device layer:(CAMetalLayer *)layer; -- (void)drawableSizeWillChange:(CGSize)size; - -- (void)beginFrame; -- (void)drawViews; -- (void)endFrame; - -#pragma mark - view management - -- (void)addView:(id)view; -- (void)removeView:(id)view; -- (void)bringViewToFront:(id)view; -- (void)sendViewToBack:(id)view; - -@end - diff --git a/gfx/common/metal/Renderer.m b/gfx/common/metal/Renderer.m deleted file mode 100644 index f7d53af9d7..0000000000 --- a/gfx/common/metal/Renderer.m +++ /dev/null @@ -1,263 +0,0 @@ -// -// Renderer.m -// MetalRenderer -// -// Created by Stuart Carnie on 5/31/18. -// Copyright © 2018 Stuart Carnie. All rights reserved. -// - -#import - -#import "RendererCommon.h" -#import "Renderer.h" -#import "View.h" -#import "PixelConverter+private.h" - -// Include header shared between C code here, which executes Metal API commands, and .metal files -#import "ShaderTypes.h" - -@implementation Renderer -{ - dispatch_semaphore_t _inflightSemaphore; - id _device; - id _library; - id _commandQueue; - Context *_context; - - PixelConverter *_conv; - - CAMetalLayer *_layer; - - // render target layer state - id _t_pipelineState; - id _t_pipelineStateNoAlpha; - MTLRenderPassDescriptor *_t_rpd; - - id _samplerStateLinear; - id _samplerStateNearest; - - // views - - NSMutableArray> *_views; - - // other state - Uniforms _uniforms; - BOOL _begin, _end; -} - -- (instancetype)initWithDevice:(id)device layer:(CAMetalLayer *)layer -{ - self = [super init]; - if (self) { - _inflightSemaphore = dispatch_semaphore_create(MAX_INFLIGHT); - _device = device; - _layer = layer; - _views = [NSMutableArray new]; - [self _initMetal]; - - _conv = [[PixelConverter alloc] initWithContext:_context]; - _begin = NO; - _end = NO; - } - - return self; -} - -- (void)_initMetal -{ - _commandQueue = [_device newCommandQueue]; - _library = [_device newDefaultLibrary]; - _context = [Context newContextWithDevice:_device - layer:_layer - library:_library - commandQueue:_commandQueue]; - - { - MTLVertexDescriptor *vd = [MTLVertexDescriptor new]; - vd.attributes[0].offset = 0; - vd.attributes[0].format = MTLVertexFormatFloat3; - vd.attributes[1].offset = offsetof(Vertex, texCoord); - vd.attributes[1].format = MTLVertexFormatFloat2; - vd.layouts[0].stride = sizeof(Vertex); - vd.layouts[0].stepFunction = MTLVertexStepFunctionPerVertex; - - MTLRenderPipelineDescriptor *psd = [MTLRenderPipelineDescriptor new]; - psd.label = @"Pipeline+Alpha"; - - MTLRenderPipelineColorAttachmentDescriptor *ca = psd.colorAttachments[0]; - ca.pixelFormat = _layer.pixelFormat; - ca.blendingEnabled = YES; - ca.sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha; - ca.sourceRGBBlendFactor = MTLBlendFactorSourceAlpha; - ca.destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha; - ca.destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha; - - psd.sampleCount = 1; - psd.vertexDescriptor = vd; - psd.vertexFunction = [_library newFunctionWithName:@"basic_vertex_proj_tex"]; - psd.fragmentFunction = [_library newFunctionWithName:@"basic_fragment_proj_tex"]; - - NSError *err; - _t_pipelineState = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; - if (err != nil) { - NSLog(@"error creating pipeline state: %@", err.localizedDescription); - abort(); - } - - ca.blendingEnabled = NO; - _t_pipelineStateNoAlpha = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; - if (err != nil) { - NSLog(@"error creating pipeline state: %@", err.localizedDescription); - abort(); - } - } - - { - MTLRenderPassDescriptor *rpd = [MTLRenderPassDescriptor new]; - rpd.colorAttachments[0].loadAction = MTLLoadActionDontCare; - rpd.colorAttachments[0].storeAction = MTLStoreActionStore; - _t_rpd = rpd; - } - - { - MTLSamplerDescriptor *sd = [MTLSamplerDescriptor new]; - _samplerStateNearest = [_device newSamplerStateWithDescriptor:sd]; - - sd.minFilter = MTLSamplerMinMagFilterLinear; - sd.magFilter = MTLSamplerMinMagFilterLinear; - _samplerStateLinear = [_device newSamplerStateWithDescriptor:sd]; - } -} - -- (void)_updateUniforms -{ - //CGSize s = _layer.drawableSize; - //_uniforms.projectionMatrix = matrix_proj_ortho(0, s.width, 0, s.height); - _uniforms.projectionMatrix = matrix_proj_ortho(0, 1, 0, 1); -} - -- (void)beginFrame -{ - assert(!_begin && !_end); - _begin = YES; - dispatch_semaphore_wait(_inflightSemaphore, DISPATCH_TIME_FOREVER); - [_context begin]; - [self _updateUniforms]; -} - -- (void)endFrame -{ - assert(!_begin && _end); - _end = NO; - - id cb = _context.commandBuffer; - __block dispatch_semaphore_t inflight = _inflightSemaphore; - [cb addCompletedHandler:^(id _) { - dispatch_semaphore_signal(inflight); - }]; - - [cb presentDrawable:_context.nextDrawable]; - [_context end]; -} - -- (void)drawViews -{ - @autoreleasepool { - assert(_begin && !_end); - _begin = NO; - _end = YES; - - id cb = _context.commandBuffer; - cb.label = @"renderer cb"; - - for (id v in _views) { - if (!v.visible) continue; - if ([v respondsToSelector:@selector(drawWithContext:)]) { - [v drawWithContext:_context]; - } - } - - BOOL pendingDraws = NO; - for (id v in _views) { - if (v.visible && (v.drawState & ViewDrawStateEncoder) != 0) { - pendingDraws = YES; - break; - } - } - - if (pendingDraws) { - id drawable = _context.nextDrawable; - _t_rpd.colorAttachments[0].texture = drawable.texture; - - id rce = [cb renderCommandEncoderWithDescriptor:_t_rpd]; - [rce setVertexBytes:&_uniforms length:sizeof(_uniforms) atIndex:BufferIndexUniforms]; - - for (id v in _views) { - if (!v.visible || - ![v respondsToSelector:@selector(drawWithEncoder:)] || - (v.drawState & ViewDrawStateEncoder) == 0) { - continue; - } - - // set view state - if (v.format == RPixelFormatBGRX8Unorm || v.format == RPixelFormatB5G6R5Unorm) { - [rce setRenderPipelineState:_t_pipelineStateNoAlpha]; - } - else { - [rce setRenderPipelineState:_t_pipelineState]; - } - - if (v.filter == RTextureFilterNearest) { - [rce setFragmentSamplerState:_samplerStateNearest atIndex:SamplerIndexDraw]; - } - else { - [rce setFragmentSamplerState:_samplerStateLinear atIndex:SamplerIndexDraw]; - } - - [v drawWithEncoder:rce]; - } - - [rce endEncoding]; - } - } -} - -#pragma mark - view APIs - -- (void)bringViewToFront:(id)view -{ - NSUInteger pos = [_views indexOfObject:view]; - if (pos == NSNotFound || pos == _views.count - 1) - return; - [_views removeObjectAtIndex:pos]; - [_views addObject:view]; -} - -- (void)sendViewToBack:(id)view -{ - NSUInteger pos = [_views indexOfObject:view]; - if (pos == NSNotFound || pos == 0) - return; - [_views removeObjectAtIndex:pos]; - [_views insertObject:view atIndex:0]; -} - -- (void)addView:(id)view -{ - [_views addObject:view]; -} - -- (void)removeView:(id)view -{ - NSUInteger pos = [_views indexOfObject:view]; - if (pos == NSNotFound) - return; - [_views removeObjectAtIndex:pos]; -} - -- (void)drawableSizeWillChange:(CGSize)size -{ - _layer.drawableSize = size; -} - -@end diff --git a/gfx/common/metal/RendererCommon.m b/gfx/common/metal/RendererCommon.m index 4491e8ea44..b914c108d3 100644 --- a/gfx/common/metal/RendererCommon.m +++ b/gfx/common/metal/RendererCommon.m @@ -11,64 +11,62 @@ NSUInteger RPixelFormatToBPP(RPixelFormat format) { - switch (format) - { - case RPixelFormatBGRA8Unorm: - case RPixelFormatBGRX8Unorm: - return 4; - - case RPixelFormatB5G6R5Unorm: - case RPixelFormatBGRA4Unorm: - return 2; - - default: - NSLog(@"Unknown format %ld", format); - abort(); - } + switch (format) { + case RPixelFormatBGRA8Unorm: + case RPixelFormatBGRX8Unorm: + return 4; + + case RPixelFormatB5G6R5Unorm: + case RPixelFormatBGRA4Unorm: + return 2; + + default: + RARCH_ERR("[Metal]: unknown RPixel format: %d\n", format); + return 4; + } } -static NSString * RPixelStrings[RPixelFormatCount]; +static NSString *RPixelStrings[RPixelFormatCount]; NSString *NSStringFromRPixelFormat(RPixelFormat format) { - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + #define STRING(literal) RPixelStrings[literal] = @#literal - STRING(RPixelFormatInvalid); - STRING(RPixelFormatB5G6R5Unorm); - STRING(RPixelFormatBGRA4Unorm); - STRING(RPixelFormatBGRA8Unorm); - STRING(RPixelFormatBGRX8Unorm); + STRING(RPixelFormatInvalid); + STRING(RPixelFormatB5G6R5Unorm); + STRING(RPixelFormatBGRA4Unorm); + STRING(RPixelFormatBGRA8Unorm); + STRING(RPixelFormatBGRX8Unorm); #undef STRING - - }); - - if (format >= RPixelFormatCount) - { - format = 0; - } - - return RPixelStrings[format]; + + }); + + if (format >= RPixelFormatCount) { + format = RPixelFormatInvalid; + } + + return RPixelStrings[format]; } matrix_float4x4 matrix_proj_ortho(float left, float right, float top, float bottom) { float near = 0; float far = 1; - + float sx = 2 / (right - left); float sy = 2 / (top - bottom); float sz = 1 / (far - near); float tx = (right + left) / (left - right); float ty = (top + bottom) / (bottom - top); float tz = near / (far - near); - - vector_float4 P = {sx, 0, 0, 0}; - vector_float4 Q = {0, sy, 0, 0}; - vector_float4 R = {0, 0, sz, 0}; - vector_float4 S = {tx, ty, tz, 1}; - + + simd_float4 P = {sx, 0, 0, 0}; + simd_float4 Q = {0, sy, 0, 0}; + simd_float4 R = {0, 0, sz, 0}; + simd_float4 S = {tx, ty, tz, 1}; + matrix_float4x4 mat = {P, Q, R, S}; return mat; } diff --git a/gfx/common/metal/ShaderTypes.h b/gfx/common/metal/ShaderTypes.h index f6ffdc9eb9..42a588a1fe 100644 --- a/gfx/common/metal/ShaderTypes.h +++ b/gfx/common/metal/ShaderTypes.h @@ -13,49 +13,44 @@ #define ShaderTypes_h #ifdef __METAL_VERSION__ - #define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type #define NSInteger metal::int32_t #define METAL_ATTRIBUTE(x) [[attribute(x)]] #define METAL_POSITION [[position]] - #else - #import - #define METAL_ATTRIBUTE(x) #define METAL_POSITION - #endif #include typedef NS_ENUM(NSInteger, BufferIndex) { - BufferIndexPositions = 0, - BufferIndexUniforms = 1 + BufferIndexPositions = 0, + BufferIndexUniforms = 1 }; typedef NS_ENUM(NSInteger, VertexAttribute) { - VertexAttributePosition = 0, - VertexAttributeTexcoord = 1, - VertexAttributeColor = 2, + VertexAttributePosition = 0, + VertexAttributeTexcoord = 1, + VertexAttributeColor = 2, }; typedef NS_ENUM(NSInteger, TextureIndex) { - TextureIndexColor = 0, + TextureIndexColor = 0, }; - typedef NS_ENUM(NSInteger, SamplerIndex) { - SamplerIndexDraw = 0, + SamplerIndexDraw = 0, }; -typedef struct { +typedef struct +{ vector_float3 position METAL_ATTRIBUTE(VertexAttributePosition); vector_float2 texCoord METAL_ATTRIBUTE(VertexAttributeTexcoord); } Vertex; @@ -68,16 +63,20 @@ typedef struct typedef struct { - matrix_float4x4 projectionMatrix; + matrix_float4x4 projectionMatrix; + vector_float2 outputSize; + float time; } Uniforms; -typedef struct { +typedef struct +{ vector_float2 position METAL_ATTRIBUTE(VertexAttributePosition); vector_float2 texCoord METAL_ATTRIBUTE(VertexAttributeTexcoord); vector_float4 color METAL_ATTRIBUTE(VertexAttributeColor); -} FontVertex; +} SpriteVertex; -typedef struct { +typedef struct +{ vector_float4 position METAL_POSITION; vector_float2 texCoord; vector_float4 color; diff --git a/gfx/common/metal/Shaders.metal b/gfx/common/metal/Shaders.metal index 365408ebc1..619442f50b 100644 --- a/gfx/common/metal/Shaders.metal +++ b/gfx/common/metal/Shaders.metal @@ -36,27 +36,9 @@ fragment float4 basic_fragment_proj_tex(ColorInOut in [[stage_in]], return float4(colorSample); } -#pragma mark - functions using normalized device coordinates +#pragma mark - functions for rendering sprites -vertex ColorInOut basic_vertex_ndc_tex(const Vertex in [[ stage_in ]]) -{ - ColorInOut out; - out.position = float4(in.position, 1.0); - out.texCoord = in.texCoord; - return out; -} - -fragment float4 basic_fragment_ndc_tex(ColorInOut in [[stage_in]], - texture2d tex [[ texture(TextureIndexColor) ]], - sampler samp [[ sampler(SamplerIndexDraw) ]]) -{ - half4 colorSample = tex.sample(samp, in.texCoord.xy); - return float4(colorSample); -} - -#pragma mark - functions for rendering fonts - -vertex FontFragmentIn font_vertex(const FontVertex in [[ stage_in ]], const device Uniforms &uniforms [[ buffer(BufferIndexUniforms) ]]) +vertex FontFragmentIn sprite_vertex(const SpriteVertex in [[ stage_in ]], const device Uniforms &uniforms [[ buffer(BufferIndexUniforms) ]]) { FontFragmentIn out; out.position = uniforms.projectionMatrix * float4(in.position, 0, 1); @@ -65,10 +47,67 @@ vertex FontFragmentIn font_vertex(const FontVertex in [[ stage_in ]], const devi return out; } -fragment float4 font_fragment(FontFragmentIn in [[ stage_in ]], +fragment float4 sprite_fragment_a8(FontFragmentIn in [[ stage_in ]], texture2d tex [[ texture(TextureIndexColor) ]], sampler samp [[ sampler(SamplerIndexDraw) ]]) { half4 colorSample = tex.sample(samp, in.texCoord.xy); return float4(in.color.rgb, in.color.a * colorSample.r); } + +#pragma mark - functions for rendering sprites + +vertex FontFragmentIn stock_vertex(const SpriteVertex in [[ stage_in ]], const device Uniforms &uniforms [[ buffer(BufferIndexUniforms) ]]) +{ + FontFragmentIn out; + out.position = uniforms.projectionMatrix * float4(in.position, 0, 1); + out.texCoord = in.texCoord; + out.color = in.color; + return out; +} + +fragment float4 stock_fragment(FontFragmentIn in [[ stage_in ]], + texture2d tex [[ texture(TextureIndexColor) ]], + sampler samp [[ sampler(SamplerIndexDraw) ]]) +{ + float4 colorSample = tex.sample(samp, in.texCoord.xy); + return colorSample * in.color; +} + +#pragma mark - filter kernels + +kernel void convert_bgra4444_to_bgra8888(device uint16_t * in [[ buffer(0) ]], + texture2d out [[ texture(0) ]], + uint id [[ thread_position_in_grid ]]) +{ + uint16_t pix = in[id]; + uchar4 pix2 = uchar4( + extract_bits(pix, 4, 4), + extract_bits(pix, 8, 4), + extract_bits(pix, 12, 4), + extract_bits(pix, 0, 4) + ); + + uint ypos = id / out.get_width(); + uint xpos = id % out.get_width(); + + out.write(half4(pix2) / 15.0, uint2(xpos, ypos)); +} + +kernel void convert_rgb565_to_bgra8888(device uint16_t * in [[ buffer(0) ]], + texture2d out [[ texture(0) ]], + uint id [[ thread_position_in_grid ]]) +{ + uint16_t pix = in[id]; + uchar4 pix2 = uchar4( + extract_bits(pix, 11, 5), + extract_bits(pix, 5, 6), + extract_bits(pix, 0, 5), + 0xf + ); + + uint ypos = id / out.get_width(); + uint xpos = id % out.get_width(); + + out.write(half4(pix2) / half4(0x1f, 0x3f, 0x1f, 0xf), uint2(xpos, ypos)); +} diff --git a/gfx/common/metal/TexturedView.h b/gfx/common/metal/TexturedView.h index aa5398ae4f..5ed51728b3 100644 --- a/gfx/common/metal/TexturedView.h +++ b/gfx/common/metal/TexturedView.h @@ -4,9 +4,7 @@ #import "View.h" -@class Renderer; - -@interface TexturedView : NSObject +@interface TexturedView : NSObject @property (readonly) RPixelFormat format; @property (readonly) RTextureFilter filter; @@ -15,7 +13,7 @@ @property (readwrite) CGSize size; @property (readonly) ViewDrawState drawState; -- (instancetype)initWithDescriptor:(ViewDescriptor *)td renderer:(Renderer *)renderer; +- (instancetype)initWithDescriptor:(ViewDescriptor *)td context:(Context *)c; - (void)drawWithContext:(Context *)ctx; - (void)drawWithEncoder:(id)rce; diff --git a/gfx/common/metal/TexturedView.m b/gfx/common/metal/TexturedView.m index 4dcc8634cc..830e8728b1 100644 --- a/gfx/common/metal/TexturedView.m +++ b/gfx/common/metal/TexturedView.m @@ -4,16 +4,13 @@ #import "TexturedView.h" #import "RendererCommon.h" -#import "Renderer.h" #import "View.h" #import "Filter.h" #import "ShaderTypes.h" - @implementation TexturedView { - __weak Renderer *_renderer; Context *_context; id _texture; // optimal render texture Vertex _v[4]; @@ -25,15 +22,14 @@ bool _pixelsDirty; } -- (instancetype)initWithDescriptor:(ViewDescriptor *)d renderer:(Renderer *)r +- (instancetype)initWithDescriptor:(ViewDescriptor *)d context:(Context *)c { self = [super init]; if (self) { - _renderer = r; _format = d.format; _bpp = RPixelFormatToBPP(_format); _filter = d.filter; - _context = r.context; + _context = c; _visible = YES; if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) { _drawState = ViewDrawStateEncoder; @@ -114,7 +110,7 @@ if (!_pixelsDirty) return; - [_renderer.conv convertFormat:_format from:_pixels to:_texture]; + [_context convertFormat:_format from:_pixels to:_texture]; _pixelsDirty = NO; } @@ -134,7 +130,7 @@ if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) { [_texture replaceRegion:MTLRegionMake2D(0, 0, (NSUInteger)_size.width, (NSUInteger)_size.height) mipmapLevel:0 withBytes:src - bytesPerRow:(NSUInteger)(4 * _size.width)]; + bytesPerRow:(NSUInteger)(4 * pitch)]; } else { void *dst = _pixels.contents; diff --git a/gfx/common/metal/View.h b/gfx/common/metal/View.h index 5f390cbc34..7345a6bd7b 100644 --- a/gfx/common/metal/View.h +++ b/gfx/common/metal/View.h @@ -19,21 +19,6 @@ typedef NS_ENUM(NSInteger, ViewDrawState) ViewDrawStateAll = 0x03, }; -@protocol View - -@property (readonly) RPixelFormat format; -@property (readonly) RTextureFilter filter; -@property (readwrite) BOOL visible; -@property (readwrite) CGRect frame; -@property (readwrite) CGSize size; -@property (readonly) ViewDrawState drawState; - -@optional -- (void)drawWithContext:(Context *)ctx; -- (void)drawWithEncoder:(id)rce; - -@end - @interface ViewDescriptor : NSObject @property (readwrite) RPixelFormat format; @property (readwrite) RTextureFilter filter; diff --git a/gfx/common/metal/View.m b/gfx/common/metal/View.m index 72cfeaca2c..db7c29bb32 100644 --- a/gfx/common/metal/View.m +++ b/gfx/common/metal/View.m @@ -6,6 +6,7 @@ // Copyright © 2018 Stuart Carnie. All rights reserved. // +#import "View.h" #import "RendererCommon.h" @implementation ViewDescriptor diff --git a/gfx/common/metal/metal_common.h b/gfx/common/metal/metal_common.h index 63bfc0f43d..45232b236d 100644 --- a/gfx/common/metal/metal_common.h +++ b/gfx/common/metal/metal_common.h @@ -6,6 +6,7 @@ // #import "RendererCommon.h" -#import "Renderer.h" +#import "Context.h" #import "View.h" #import "TexturedView.h" +#import "MenuDisplay.h" diff --git a/gfx/common/metal_common.h b/gfx/common/metal_common.h index aa64ea6209..821c405bcf 100644 --- a/gfx/common/metal_common.h +++ b/gfx/common/metal_common.h @@ -26,7 +26,7 @@ extern MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt); #pragma mark - Classes -@interface FrameView : NSObject +@interface FrameView : NSObject @property (readonly) RPixelFormat format; @property (readonly) RTextureFilter filter; @@ -35,7 +35,6 @@ extern MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt); @property (readwrite) CGSize size; @property (readonly) ViewDrawState drawState; @property (readonly) struct video_shader* shader; - @property (readwrite) uint64_t frameCount; - (void)setFilteringIndex:(int)index smooth:(bool)smooth; @@ -63,17 +62,26 @@ extern MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt); @property (readonly) video_viewport_t* viewport; @property (readwrite) bool keepAspect; @property (readonly) MetalMenu* menu; -@property (readwrite) uint64_t frameCount; @property (readonly) FrameView* frameView; +@property (readonly) MenuDisplay* display; @property (readonly) Context* context; +@property (readonly) Uniforms* viewportMVP; -- (instancetype)init NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithVideo:(const video_info_t *)video + input:(const input_driver_t **)input + inputData:(void **)inputData; - (void)setVideo:(const video_info_t *)video; +- (void)setShaderIndex:(NSUInteger)index; +- (bool)renderFrame:(const void *)data + width:(unsigned)width + height:(unsigned)height + frameCount:(uint64_t)frameCount + pitch:(unsigned)pitch + msg:(const char *)msg + info:(video_frame_info_t *)video_info; -- (void)beginFrame; -- (void)drawViews; -- (void)endFrame; +- (id)getStockShader:(int)index blend:(bool)blend; /*! @brief setNeedsResize triggers a display resize */ - (void)setNeedsResize; diff --git a/gfx/common/metal_common.m b/gfx/common/metal_common.m index 55e24128bb..7f84ecaf65 100644 --- a/gfx/common/metal_common.m +++ b/gfx/common/metal_common.m @@ -14,6 +14,7 @@ #import #import #include +#import "Context.h" #define STRUCT_ASSIGN(x, y) \ { \ @@ -25,40 +26,115 @@ } \ if (__y != nil) \ x = (__bridge __typeof__(x))(__bridge_retained void *)((NSObject *)__y); \ -} + } + +#pragma mark - private categories @interface FrameView() @property (readwrite) video_viewport_t *viewport; -- (instancetype)initWithDescriptor:(ViewDescriptor *)td renderer:(Renderer *)renderer; +- (instancetype)initWithDescriptor:(ViewDescriptor *)td context:(Context *)context; - (void)drawWithContext:(Context *)ctx; - (void)drawWithEncoder:(id)rce; @end -#pragma mark - private categories - @interface MetalMenu() -@property (readwrite) Renderer *renderer; +@property (readonly) TexturedView *view; +- (instancetype)initWithContext:(Context *)context; @end @implementation MetalDriver { - id _device; - - Renderer *_renderer; FrameView *_frameView; - + MetalMenu *_menu; + video_info_t _video; + + dispatch_semaphore_t _inflightSemaphore; + id _device; + id _library; + Context *_context; + + CAMetalLayer *_layer; + + // render target layer state + id _t_pipelineState; + id _t_pipelineStateNoAlpha; + MTLRenderPassDescriptor *_t_rpd; + + id _samplerStateLinear; + id _samplerStateNearest; + + // + id _states[GFX_MAX_SHADERS][2]; + + // other state + Uniforms _uniforms; + Uniforms _viewportMVP; + BOOL _begin, _end; } -- (instancetype)init +- (instancetype)initWithVideo:(const video_info_t *)video + input:(const input_driver_t **)input + inputData:(void **)inputData { - if (self = [super init]) { - _frameCount = 0; + if (self = [super init]) + { + _inflightSemaphore = dispatch_semaphore_create(MAX_INFLIGHT); + _device = MTLCreateSystemDefaultDevice(); + MetalView *view = (MetalView *)apple_platform.renderView; + view.device = _device; + view.delegate = self; + _layer = (CAMetalLayer *)view.layer; + + if (![self _initMetal]) + { + return nil; + } + + if (![self _initStates]) + { + return nil; + } + + _begin = NO; + _end = NO; + + _video = *video; _viewport = (video_viewport_t *)calloc(1, sizeof(video_viewport_t)); - _menu = [MetalMenu new]; + + _keepAspect = _video.force_aspect; + + gfx_ctx_mode_t mode = { + .width = _video.width, + .height = _video.height, + .fullscreen = _video.fullscreen, + }; + [apple_platform setVideoMode:mode]; + + *input = NULL; + *inputData = NULL; + + // menu display + _display = [[MenuDisplay alloc] initWithDriver:self]; + + // menu view + _menu = [[MetalMenu alloc] initWithContext:_context]; + + // frame buffer view + { + ViewDescriptor *vd = [ViewDescriptor new]; + vd.format = _video.rgb32 ? RPixelFormatBGRX8Unorm : RPixelFormatB5G6R5Unorm; + vd.size = CGSizeMake(video->width, video->height); + vd.filter = _video.smooth ? RTextureFilterLinear : RTextureFilterNearest; + _frameView = [[FrameView alloc] initWithDescriptor:vd context:_context]; + _frameView.viewport = _viewport; + [_frameView setFilteringIndex:0 smooth:video->smooth]; + } + + font_driver_init_osd((__bridge void *)self, false, video->is_threaded, FONT_DRIVER_RENDER_METAL_API); } return self; } @@ -66,60 +142,271 @@ - (void)dealloc { RARCH_LOG("[MetalDriver]: destroyed\n"); - if (_viewport) { + if (_viewport) + { free(_viewport); _viewport = nil; } + font_driver_free_osd(); } -- (Context *)context { - return _renderer.context; +- (bool)_initMetal +{ + _library = [_device newDefaultLibrary]; + _context = [[Context alloc] initWithDevice:_device + layer:_layer + library:_library]; + + { + MTLVertexDescriptor *vd = [MTLVertexDescriptor new]; + vd.attributes[0].offset = 0; + vd.attributes[0].format = MTLVertexFormatFloat3; + vd.attributes[1].offset = offsetof(Vertex, texCoord); + vd.attributes[1].format = MTLVertexFormatFloat2; + vd.layouts[0].stride = sizeof(Vertex); + + MTLRenderPipelineDescriptor *psd = [MTLRenderPipelineDescriptor new]; + psd.label = @"Pipeline+Alpha"; + + MTLRenderPipelineColorAttachmentDescriptor *ca = psd.colorAttachments[0]; + ca.pixelFormat = _layer.pixelFormat; + ca.blendingEnabled = YES; + ca.sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha; + ca.sourceRGBBlendFactor = MTLBlendFactorSourceAlpha; + ca.destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha; + ca.destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha; + + psd.sampleCount = 1; + psd.vertexDescriptor = vd; + psd.vertexFunction = [_library newFunctionWithName:@"basic_vertex_proj_tex"]; + psd.fragmentFunction = [_library newFunctionWithName:@"basic_fragment_proj_tex"]; + + NSError *err; + _t_pipelineState = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; + if (err != nil) + { + RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String); + return NO; + } + + psd.label = @"Pipeline+No Alpha"; + ca.blendingEnabled = NO; + _t_pipelineStateNoAlpha = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; + if (err != nil) + { + RARCH_ERR("[Metal]: error creating pipeline state (no alpha) %s\n", err.localizedDescription.UTF8String); + return NO; + } + } + + { + MTLRenderPassDescriptor *rpd = [MTLRenderPassDescriptor new]; + rpd.colorAttachments[0].loadAction = MTLLoadActionDontCare; + rpd.colorAttachments[0].storeAction = MTLStoreActionStore; + _t_rpd = rpd; + } + + { + MTLSamplerDescriptor *sd = [MTLSamplerDescriptor new]; + _samplerStateNearest = [_device newSamplerStateWithDescriptor:sd]; + + sd.minFilter = MTLSamplerMinMagFilterLinear; + sd.magFilter = MTLSamplerMinMagFilterLinear; + _samplerStateLinear = [_device newSamplerStateWithDescriptor:sd]; + } + return YES; +} + +- (bool)_initStates +{ + MTLVertexDescriptor *vd = [MTLVertexDescriptor new]; + vd.attributes[0].offset = 0; + vd.attributes[0].format = MTLVertexFormatFloat2; + vd.attributes[1].offset = offsetof(SpriteVertex, texCoord); + vd.attributes[1].format = MTLVertexFormatFloat2; + vd.attributes[2].offset = offsetof(SpriteVertex, color); + vd.attributes[2].format = MTLVertexFormatFloat4; + vd.layouts[0].stride = sizeof(SpriteVertex); + + { + MTLRenderPipelineDescriptor *psd = [MTLRenderPipelineDescriptor new]; + psd.label = @"stock_blend"; + + MTLRenderPipelineColorAttachmentDescriptor *ca = psd.colorAttachments[0]; + ca.pixelFormat = _layer.pixelFormat; + ca.blendingEnabled = NO; + ca.sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha; + ca.sourceRGBBlendFactor = MTLBlendFactorSourceAlpha; + ca.destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha; + ca.destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha; + + psd.sampleCount = 1; + psd.vertexDescriptor = vd; + psd.vertexFunction = [_library newFunctionWithName:@"stock_vertex"]; + psd.fragmentFunction = [_library newFunctionWithName:@"stock_fragment"]; + + NSError *err; + _states[VIDEO_SHADER_STOCK_BLEND][0] = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; + if (err != nil) + { + RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String); + return NO; + } + + ca.blendingEnabled = YES; + _states[VIDEO_SHADER_STOCK_BLEND][1] = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; + if (err != nil) + { + RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String); + return NO; + } + } + return YES; +} + +- (void)_updateUniforms +{ + _uniforms.projectionMatrix = matrix_proj_ortho(0, 1, 0, 1); +} + +#pragma mark - shaders + +- (id)getStockShader:(int)index blend:(bool)blend +{ + assert(index > 0 && index < GFX_MAX_SHADERS); + + return _states[index][blend ? 1 : 0]; +} + +- (void)setShaderIndex:(NSUInteger)index +{ + } #pragma mark - video - (void)setVideo:(const video_info_t *)video { - _video = *video; - - if (!_renderer) { - id device = MTLCreateSystemDefaultDevice(); - _device = device; - MetalView *view = (MetalView *)apple_platform.renderView; - view.device = device; - CAMetalLayer *layer = (CAMetalLayer *)view.layer; - //layer.device = device; - _renderer = [[Renderer alloc] initWithDevice:device layer:layer]; - _menu.renderer = _renderer; - } - - if (!_frameView) { - ViewDescriptor *vd = [ViewDescriptor new]; - vd.format = _video.rgb32 ? RPixelFormatBGRX8Unorm : RPixelFormatB5G6R5Unorm; - vd.size = CGSizeMake(video->width, video->height); - vd.filter = _video.smooth ? RTextureFilterLinear : RTextureFilterNearest; - _frameView = [[FrameView alloc] initWithDescriptor:vd renderer:_renderer]; - _frameView.viewport = _viewport; - [_renderer addView:_frameView]; - [_renderer sendViewToBack:_frameView]; - [_frameView setFilteringIndex:0 smooth:video->smooth]; - } + } -- (void)beginFrame +- (bool)renderFrame:(const void *)data + width:(unsigned)width + height:(unsigned)height + frameCount:(uint64_t)frameCount + pitch:(unsigned)pitch + msg:(const char *)msg + info:(video_frame_info_t *)video_info +{ + @autoreleasepool + { + [self _beginFrame]; + + _frameView.frameCount = frameCount; + _frameView.size = CGSizeMake(width, height); + [_frameView updateFrame:data pitch:pitch]; + + [self _drawViews:video_info]; + + if (video_info->statistics_show) + { + struct font_params *osd_params = (struct font_params *)&video_info->osd_stat_params; + + if (osd_params) + { + font_driver_render_msg(video_info, NULL, video_info->stat_text, osd_params); + } + } + + if (msg && *msg) + { + font_driver_render_msg(video_info, NULL, msg, NULL); + } + + [self _endFrame]; + } + + return YES; +} + +- (void)_beginFrame { video_driver_update_viewport(_viewport, NO, _keepAspect); - - [_renderer beginFrame]; + + assert(!_begin && !_end); + _begin = YES; + dispatch_semaphore_wait(_inflightSemaphore, DISPATCH_TIME_FOREVER); + [_context begin]; + [self _updateUniforms]; } -- (void)drawViews { - [_renderer drawViews]; -} - -- (void)endFrame +- (void)_drawViews:(video_frame_info_t *)video_info { - [_renderer endFrame]; + assert(_begin && !_end); + _begin = NO; + _end = YES; + + id cb = _context.commandBuffer; + cb.label = @"renderer cb"; + + // draw back buffer + [_frameView drawWithContext:_context]; + + id drawable = _context.nextDrawable; + _t_rpd.colorAttachments[0].texture = drawable.texture; + + if ((_frameView.drawState & ViewDrawStateEncoder) != 0) + { + id rce = [cb renderCommandEncoderWithDescriptor:_t_rpd]; + [rce setVertexBytes:&_uniforms length:sizeof(_uniforms) atIndex:BufferIndexUniforms]; + [rce setRenderPipelineState:_t_pipelineStateNoAlpha]; + if (_frameView.filter == RTextureFilterNearest) + { + [rce setFragmentSamplerState:_samplerStateNearest atIndex:SamplerIndexDraw]; + } + else + { + [rce setFragmentSamplerState:_samplerStateLinear atIndex:SamplerIndexDraw]; + } + [_frameView drawWithEncoder:rce]; + [rce endEncoding]; + } + +#if defined(HAVE_MENU) + if (_menu.enabled) + { + menu_driver_frame(video_info); + [_menu.view drawWithContext:_context]; + id rce = [cb renderCommandEncoderWithDescriptor:_t_rpd]; + [rce setVertexBytes:&_uniforms length:sizeof(_uniforms) atIndex:BufferIndexUniforms]; + [rce setRenderPipelineState:_t_pipelineState]; + if (_menu.view.filter == RTextureFilterNearest) + { + [rce setFragmentSamplerState:_samplerStateNearest atIndex:SamplerIndexDraw]; + } + else + { + [rce setFragmentSamplerState:_samplerStateLinear atIndex:SamplerIndexDraw]; + } + [_menu.view drawWithEncoder:rce]; + [rce endEncoding]; + } +#endif +} + +- (void)_endFrame +{ + assert(!_begin && _end); + _end = NO; + + id cb = _context.commandBuffer; + __block dispatch_semaphore_t inflight = _inflightSemaphore; + [cb addCompletedHandler:^(id _) { + dispatch_semaphore_signal(inflight); + }]; + + [cb presentDrawable:_context.nextDrawable]; + [_context end]; } - (void)setNeedsResize @@ -127,30 +414,48 @@ // TODO(sgc): resize all drawables } +- (Uniforms *)viewportMVP +{ + return &_viewportMVP; +} + #pragma mark - MTKViewDelegate -- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size { - RARCH_LOG("[MetalDriver] drawableSizeWillChange: %s\n", NSStringFromSize(size).UTF8String); +- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size +{ _viewport->full_width = (unsigned int)size.width; _viewport->full_height = (unsigned int)size.height; video_driver_set_size(&_viewport->full_width, &_viewport->full_height); - [_renderer drawableSizeWillChange:size]; + _layer.drawableSize = size; video_driver_update_viewport(_viewport, NO, _keepAspect); + + _viewportMVP.outputSize = simd_make_float2(_viewport->full_width, _viewport->full_height); + _viewportMVP.projectionMatrix = matrix_proj_ortho(0, _viewport->full_width, _viewport->full_height, 0); } -- (void)drawInMTKView:(MTKView *)view { - +- (void)drawInMTKView:(MTKView *)view +{ + } @end @implementation MetalMenu { - Renderer *_renderer; + Context *_context; TexturedView *_view; BOOL _enabled; } +- (instancetype)initWithContext:(Context *)context +{ + if (self = [super init]) + { + _context = context; + } + return self; +} + - (void)setEnabled:(BOOL)enabled { if (_enabled == enabled) return; @@ -169,23 +474,24 @@ filter:(RTextureFilter)filter { CGSize size = CGSizeMake(width, height); - - if (_view) { + + if (_view) + { if (!(CGSizeEqualToSize(_view.size, size) && _view.format == format && - _view.filter == filter)) { - [_renderer removeView:_view]; + _view.filter == filter)) + { _view = nil; } } - - if (!_view) { + + if (!_view) + { ViewDescriptor *vd = [ViewDescriptor new]; vd.format = format; vd.filter = filter; vd.size = size; - _view = [[TexturedView alloc] initWithDescriptor:vd renderer:_renderer]; - [_renderer addView:_view]; + _view = [[TexturedView alloc] initWithDescriptor:vd context:_context]; _view.visible = _enabled; } } @@ -218,14 +524,14 @@ typedef struct texture typedef struct ALIGN(16) { matrix_float4x4 mvp; - + struct { texture_t texture[GFX_MAX_FRAME_HISTORY + 1]; MTLViewport viewport; float4_t output_size; } frame; - + struct { __unsafe_unretained id buffers[SLANG_CBUFFER_MAX]; @@ -236,53 +542,54 @@ typedef struct ALIGN(16) MTLViewport viewport; __unsafe_unretained id _state; } pass[GFX_MAX_SHADERS]; - + texture_t luts[GFX_MAX_TEXTURES]; - + } engine_t; @implementation FrameView { - __weak Renderer *_renderer; Context *_context; id _texture; // final render texture Vertex _v[4]; CGSize _size; // size of view in pixels CGRect _frame; NSUInteger _bpp; - + id _pixels; // frame buffer in _srcFmt bool _pixelsDirty; - + id _samplers[RARCH_FILTER_MAX][RARCH_WRAP_MAX]; struct video_shader *_shader; - id _fence; - + engine_t _engine; - + bool resize_render_targets; bool init_history; video_viewport_t *_viewport; } -- (instancetype)initWithDescriptor:(ViewDescriptor *)d renderer:(Renderer *)r +- (instancetype)initWithDescriptor:(ViewDescriptor *)d context:(Context *)c { self = [super init]; - if (self) { - _renderer = r; - _context = r.context; + if (self) + { + _context = c; _format = d.format; _bpp = RPixelFormatToBPP(_format); _filter = d.filter; - if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) { + if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) + { _drawState = ViewDrawStateEncoder; - } else { + } + else + { _drawState = ViewDrawStateAll; } _visible = YES; _engine.mvp = matrix_proj_ortho(0, 1, 0, 1); [self _initSamplers]; - + self.size = d.size; self.frame = CGRectMake(0, 0, 1, 1); resize_render_targets = YES; @@ -293,26 +600,28 @@ typedef struct ALIGN(16) - (void)_initSamplers { MTLSamplerDescriptor *sd = [MTLSamplerDescriptor new]; - + /* Initialize samplers */ - for (unsigned i = 0; i < RARCH_WRAP_MAX; i++) { - switch (i) { + for (unsigned i = 0; i < RARCH_WRAP_MAX; i++) + { + switch (i) + { case RARCH_WRAP_BORDER: sd.sAddressMode = MTLSamplerAddressModeClampToBorderColor; break; - + case RARCH_WRAP_EDGE: sd.sAddressMode = MTLSamplerAddressModeClampToEdge; break; - + case RARCH_WRAP_REPEAT: sd.sAddressMode = MTLSamplerAddressModeRepeat; break; - + case RARCH_WRAP_MIRRORED_REPEAT: sd.sAddressMode = MTLSamplerAddressModeMirrorRepeat; break; - + default: continue; } @@ -320,13 +629,13 @@ typedef struct ALIGN(16) sd.rAddressMode = sd.sAddressMode; sd.minFilter = MTLSamplerMinMagFilterLinear; sd.magFilter = MTLSamplerMinMagFilterLinear; - + id ss = [_context.device newSamplerStateWithDescriptor:sd]; _samplers[RARCH_FILTER_LINEAR][i] = ss; - + sd.minFilter = MTLSamplerMinMagFilterNearest; sd.magFilter = MTLSamplerMinMagFilterNearest; - + ss = [_context.device newSamplerStateWithDescriptor:sd]; _samplers[RARCH_FILTER_NEAREST][i] = ss; } @@ -334,7 +643,8 @@ typedef struct ALIGN(16) - (void)setFilteringIndex:(int)index smooth:(bool)smooth { - for (int i = 0; i < RARCH_WRAP_MAX; i++) { + for (int i = 0; i < RARCH_WRAP_MAX; i++) + { if (smooth) _samplers[RARCH_FILTER_UNSPEC][i] = _samplers[RARCH_FILTER_LINEAR][i]; else @@ -344,15 +654,17 @@ typedef struct ALIGN(16) - (void)setSize:(CGSize)size { - if (CGSizeEqualToSize(_size, size)) { + if (CGSizeEqualToSize(_size, size)) + { return; } - + _size = size; - + resize_render_targets = YES; - - if (_format != RPixelFormatBGRA8Unorm && _format != RPixelFormatBGRX8Unorm) { + + if (_format != RPixelFormatBGRA8Unorm && _format != RPixelFormatBGRX8Unorm) + { _pixels = [_context.device newBufferWithLength:(NSUInteger)(size.width * size.height * 2) options:MTLResourceStorageModeManaged]; } @@ -365,26 +677,27 @@ typedef struct ALIGN(16) - (void)setFrame:(CGRect)frame { - if (CGRectEqualToRect(_frame, frame)) { + if (CGRectEqualToRect(_frame, frame)) + { return; } - + _frame = frame; - + // update vertices CGPoint o = frame.origin; CGSize s = frame.size; - + CGFloat l = o.x; CGFloat t = o.y; CGFloat r = o.x + s.width; CGFloat b = o.y + s.height; - + Vertex v[4] = { - {{l, b, 0}, {0, 1}}, - {{r, b, 0}, {1, 1}}, - {{l, t, 0}, {0, 0}}, - {{r, t, 0}, {1, 0}}, + {simd_make_float3(l, b, 0), simd_make_float2(0, 1)}, + {simd_make_float3(r, b, 0), simd_make_float2(1, 1)}, + {simd_make_float3(l, t, 0), simd_make_float2(0, 0)}, + {simd_make_float3(r, t, 0), simd_make_float2(1, 0)}, }; memcpy(_v, v, sizeof(_v)); } @@ -398,21 +711,24 @@ typedef struct ALIGN(16) { if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) return; - + if (!_pixelsDirty) return; - - [_renderer.conv convertFormat:_format from:_pixels to:_texture]; + + [_context convertFormat:_format from:_pixels to:_texture]; _pixelsDirty = NO; } - (void)_updateHistory { - if (_shader) { - if (_shader->history_size) { + if (_shader) + { + if (_shader->history_size) + { if (init_history) [self _initHistory]; - else { + else + { int k; /* todo: what about frame-duping ? * maybe clone d3d10_texture_t with AddRef */ @@ -423,10 +739,11 @@ typedef struct ALIGN(16) } } } - + /* either no history, or we moved a texture of a different size in the front slot */ if (_engine.frame.texture[0].size_data.x != _size.width || - _engine.frame.texture[0].size_data.y != _size.height) { + _engine.frame.texture[0].size_data.y != _size.height) + { MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm width:(NSUInteger)_size.width height:(NSUInteger)_size.height @@ -439,10 +756,11 @@ typedef struct ALIGN(16) - (void)updateFrame:(void const *)src pitch:(NSUInteger)pitch { if (_shader && (_engine.frame.output_size.x != _viewport->width || - _engine.frame.output_size.y != _viewport->height)) { + _engine.frame.output_size.y != _viewport->height)) + { resize_render_targets = YES; } - + _engine.frame.viewport.originX = _viewport->x; _engine.frame.viewport.originY = _viewport->y; _engine.frame.viewport.width = _viewport->width; @@ -453,35 +771,41 @@ typedef struct ALIGN(16) _engine.frame.output_size.y = _viewport->height; _engine.frame.output_size.z = 1.0f / _viewport->width; _engine.frame.output_size.w = 1.0f / _viewport->height; - - if (resize_render_targets) { + + if (resize_render_targets) + { [self _updateRenderTargets]; } - + [self _updateHistory]; - - if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) { + + if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) + { id tex = _engine.frame.texture[0].view; [tex replaceRegion:MTLRegionMake2D(0, 0, (NSUInteger)_size.width, (NSUInteger)_size.height) mipmapLevel:0 withBytes:src - bytesPerRow:(NSUInteger)(4 * _size.width)]; + bytesPerRow:(NSUInteger)(4 * pitch)]; } - else { + else + { void *dst = _pixels.contents; size_t len = (size_t)(_bpp * _size.width); assert(len <= pitch); // the length can't be larger? - - if (len < pitch) { - for (int i = 0; i < _size.height; i++) { + + if (len < pitch) + { + for (int i = 0; i < _size.height; i++) + { memcpy(dst, src, len); dst += len; src += pitch; } } - else { + else + { memcpy(dst, src, _pixels.length); } - + [_pixels didModifyRange:NSMakeRange(0, _pixels.length)]; _pixelsDirty = YES; } @@ -503,8 +827,9 @@ typedef struct ALIGN(16) height:(NSUInteger)_size.height mipmapped:false]; td.usage = MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite | MTLTextureUsageRenderTarget; - - for (int i = 0; i < _shader->history_size + 1; i++) { + + for (int i = 0; i < _shader->history_size + 1; i++) + { [self _initTexture:&_engine.frame.texture[i] withDescriptor:td]; } init_history = NO; @@ -525,7 +850,8 @@ static vertex_t vertex_bytes[] = { - (void)drawWithEncoder:(id)rce { - if (_texture) { + if (_texture) + { [rce setViewport:_engine.frame.viewport]; [rce setVertexBytes:&_v length:sizeof(_v) atIndex:BufferIndexPositions]; [rce setFragmentTexture:_texture atIndex:TextureIndexColor]; @@ -537,104 +863,111 @@ static vertex_t vertex_bytes[] = { { _texture = _engine.frame.texture[0].view; [self _convertFormat]; - - if (!_shader || _shader->passes == 0) { + + if (!_shader || _shader->passes == 0) + { return; } - - for (unsigned i = 0; i < _shader->passes; i++) { - if (_shader->pass[i].feedback) { + + for (unsigned i = 0; i < _shader->passes; i++) + { + if (_shader->pass[i].feedback) + { texture_t tmp = _engine.pass[i].feedback; _engine.pass[i].feedback = _engine.pass[i].rt; _engine.pass[i].rt = tmp; } } - + id cb = ctx.commandBuffer; - + MTLRenderPassDescriptor *rpd = [MTLRenderPassDescriptor new]; rpd.colorAttachments[0].clearColor = MTLClearColorMake(0, 0, 0, 1.0); rpd.colorAttachments[0].loadAction = MTLLoadActionClear; rpd.colorAttachments[0].storeAction = MTLStoreActionStore; - - BOOL firstPass = YES; - for (unsigned i = 0; i < _shader->passes; i++) { + for (unsigned i = 0; i < _shader->passes; i++) + { BOOL backBuffer = (_engine.pass[i].rt.view == nil); - - if (backBuffer) { + + if (backBuffer) + { rpd.colorAttachments[0].texture = _context.nextDrawable.texture; } - else { + else + { rpd.colorAttachments[0].texture = _engine.pass[i].rt.view; } - + id rce = [cb renderCommandEncoderWithDescriptor:rpd]; - if (firstPass) { - firstPass = NO; - } else { - [rce waitForFence:_fence beforeStages:MTLRenderStageVertex]; - } [rce setRenderPipelineState:_engine.pass[i]._state]; - + _engine.pass[i].frame_count = (uint32_t)_frameCount; if (_shader->pass[i].frame_count_mod) _engine.pass[i].frame_count %= _shader->pass[i].frame_count_mod; - - for (unsigned j = 0; j < SLANG_CBUFFER_MAX; j++) { + + for (unsigned j = 0; j < SLANG_CBUFFER_MAX; j++) + { id buffer = _engine.pass[i].buffers[j]; cbuffer_sem_t *buffer_sem = &_engine.pass[i].semantics.cbuffers[j]; - - if (buffer_sem->stage_mask && buffer_sem->uniforms) { + + if (buffer_sem->stage_mask && buffer_sem->uniforms) + { void *data = buffer.contents; uniform_sem_t *uniform = buffer_sem->uniforms; - - while (uniform->size) { + + while (uniform->size) + { if (uniform->data) memcpy((uint8_t *)data + uniform->offset, uniform->data, uniform->size); uniform++; } - + if (buffer_sem->stage_mask & SLANG_STAGE_VERTEX_MASK) [rce setVertexBuffer:buffer offset:0 atIndex:buffer_sem->binding]; - + if (buffer_sem->stage_mask & SLANG_STAGE_FRAGMENT_MASK) [rce setFragmentBuffer:buffer offset:0 atIndex:buffer_sem->binding]; [buffer didModifyRange:NSMakeRange(0, buffer.length)]; } } - + __unsafe_unretained id textures[SLANG_NUM_BINDINGS] = {NULL}; id samplers[SLANG_NUM_BINDINGS] = {NULL}; - + texture_sem_t *texture_sem = _engine.pass[i].semantics.textures; - while (texture_sem->stage_mask) { + while (texture_sem->stage_mask) + { int binding = texture_sem->binding; id tex = (__bridge id)*(void **)texture_sem->texture_data; textures[binding] = tex; samplers[binding] = _samplers[texture_sem->filter][texture_sem->wrap]; texture_sem++; } - - if (backBuffer) { + + if (backBuffer) + { [rce setViewport:_engine.frame.viewport]; } - else { + else + { [rce setViewport:_engine.pass[i].viewport]; } - + [rce setFragmentTextures:textures withRange:NSMakeRange(0, SLANG_NUM_BINDINGS)]; [rce setFragmentSamplerStates:samplers withRange:NSMakeRange(0, SLANG_NUM_BINDINGS)]; [rce setVertexBytes:vertex_bytes length:sizeof(vertex_bytes) atIndex:4]; [rce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4]; - [rce updateFence:_fence afterStages:MTLRenderStageFragment]; [rce endEncoding]; _texture = _engine.pass[i].rt.view; } - if (_texture == nil) { + if (_texture == nil) + { _drawState = ViewDrawStateContext; - } else { + } + else + { _drawState = ViewDrawStateAll; } } @@ -642,67 +975,74 @@ static vertex_t vertex_bytes[] = { - (void)_updateRenderTargets { if (!_shader || !resize_render_targets) return; - + // release existing targets - for (int i = 0; i < _shader->passes; i++) { + for (int i = 0; i < _shader->passes; i++) + { STRUCT_ASSIGN(_engine.pass[i].rt.view, nil); STRUCT_ASSIGN(_engine.pass[i].feedback.view, nil); memset(&_engine.pass[i].rt, 0, sizeof(_engine.pass[i].rt)); memset(&_engine.pass[i].feedback, 0, sizeof(_engine.pass[i].feedback)); } - + NSUInteger width = (NSUInteger)_size.width, height = (NSUInteger)_size.height; - - for (unsigned i = 0; i < _shader->passes; i++) { + + for (unsigned i = 0; i < _shader->passes; i++) + { struct video_shader_pass *shader_pass = &_shader->pass[i]; - - if (shader_pass->fbo.valid) { - switch (shader_pass->fbo.type_x) { + + if (shader_pass->fbo.valid) + { + switch (shader_pass->fbo.type_x) + { case RARCH_SCALE_INPUT: width *= shader_pass->fbo.scale_x; break; - + case RARCH_SCALE_VIEWPORT: width = (NSUInteger)(_viewport->width * shader_pass->fbo.scale_x); break; - + case RARCH_SCALE_ABSOLUTE: width = shader_pass->fbo.abs_x; break; - + default: break; } - + if (!width) width = _viewport->width; - - switch (shader_pass->fbo.type_y) { + + switch (shader_pass->fbo.type_y) + { case RARCH_SCALE_INPUT: height *= shader_pass->fbo.scale_y; break; - + case RARCH_SCALE_VIEWPORT: height = (NSUInteger)(_viewport->height * shader_pass->fbo.scale_y); break; - + case RARCH_SCALE_ABSOLUTE: height = shader_pass->fbo.abs_y; break; - + default: break; } - + if (!height) height = _viewport->height; } - else if (i == (_shader->passes - 1)) { + else if (i == (_shader->passes - 1)) + { width = _viewport->width; height = _viewport->height; } - + RARCH_LOG("[Metal]: Updating framebuffer size %u x %u.\n", width, height); + MTLPixelFormat fmt = SelectOptimalPixelFormat(glslang_format_to_metal(_engine.pass[i].semantics.format)); if ((i != (_shader->passes - 1)) || (width != _viewport->width) || (height != _viewport->height) || @@ -712,26 +1052,29 @@ static vertex_t vertex_bytes[] = { _engine.pass[i].viewport.height = height; _engine.pass[i].viewport.znear = 0.0; _engine.pass[i].viewport.zfar = 1.0; - + MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:fmt width:width height:height mipmapped:false]; + td.storageMode = MTLStorageModePrivate; td.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget; [self _initTexture:&_engine.pass[i].rt withDescriptor:td]; - - if (shader_pass->feedback) { + + if (shader_pass->feedback) + { [self _initTexture:&_engine.pass[i].feedback withDescriptor:td]; } } - else { + else + { _engine.pass[i].rt.size_data.x = width; _engine.pass[i].rt.size_data.y = height; _engine.pass[i].rt.size_data.z = 1.0f / width; _engine.pass[i].rt.size_data.w = 1.0f / height; } } - + resize_render_targets = NO; } @@ -739,67 +1082,71 @@ static vertex_t vertex_bytes[] = { { if (!shader) return; - - for (int i = 0; i < GFX_MAX_SHADERS; i++) { + + for (int i = 0; i < GFX_MAX_SHADERS; i++) + { STRUCT_ASSIGN(_engine.pass[i].rt.view, nil); STRUCT_ASSIGN(_engine.pass[i].feedback.view, nil); memset(&_engine.pass[i].rt, 0, sizeof(_engine.pass[i].rt)); memset(&_engine.pass[i].feedback, 0, sizeof(_engine.pass[i].feedback)); - + STRUCT_ASSIGN(_engine.pass[i]._state, nil); - - for (unsigned j = 0; j < SLANG_CBUFFER_MAX; j++) { + + for (unsigned j = 0; j < SLANG_CBUFFER_MAX; j++) + { STRUCT_ASSIGN(_engine.pass[i].buffers[j], nil); } } - - for (int i = 0; i < GFX_MAX_TEXTURES; i++) { + + for (int i = 0; i < GFX_MAX_TEXTURES; i++) + { STRUCT_ASSIGN(_engine.luts[i].view, nil); } - + free(shader); - _fence = nil; } - (BOOL)setShaderFromPath:(NSString *)path { [self _freeVideoShader:_shader]; _shader = nil; - + config_file_t *conf = config_file_new(path.UTF8String); struct video_shader *shader = (struct video_shader *)calloc(1, sizeof(*shader)); - - @try { + + @try + { if (!video_shader_read_conf_cgp(conf, shader)) return NO; - + video_shader_resolve_relative(shader, path.UTF8String); - + texture_t *source = &_engine.frame.texture[0]; - for (unsigned i = 0; i < shader->passes; source = &_engine.pass[i++].rt) { + for (unsigned i = 0; i < shader->passes; source = &_engine.pass[i++].rt) + { /* clang-format off */ semantics_map_t semantics_map = { { /* Original */ {&_engine.frame.texture[0].view, 0, &_engine.frame.texture[0].size_data, 0}, - + /* Source */ {&source->view, 0, &source->size_data, 0}, - + /* OriginalHistory */ {&_engine.frame.texture[0].view, sizeof(*_engine.frame.texture), &_engine.frame.texture[0].size_data, sizeof(*_engine.frame.texture)}, - + /* PassOutput */ {&_engine.pass[0].rt.view, sizeof(*_engine.pass), &_engine.pass[0].rt.size_data, sizeof(*_engine.pass)}, - + /* PassFeedback */ {&_engine.pass[0].feedback.view, sizeof(*_engine.pass), &_engine.pass[0].feedback.size_data, sizeof(*_engine.pass)}, - + /* User */ {&_engine.luts[0].view, sizeof(*_engine.luts), &_engine.luts[0].size_data, sizeof(*_engine.luts)}, @@ -812,20 +1159,21 @@ static vertex_t vertex_bytes[] = { } }; /* clang-format on */ - + if (!slang_process(shader, i, RARCH_SHADER_METAL, 20000, &semantics_map, &_engine.pass[i].semantics)) return NO; #ifdef DEBUG - bool save_msl = true; + bool save_msl = true; #else bool save_msl = false; #endif NSString *vs_src = [NSString stringWithUTF8String:shader->pass[i].source.string.vertex]; NSString *fs_src = [NSString stringWithUTF8String:shader->pass[i].source.string.fragment]; - + // vertex descriptor - @try { + @try + { MTLVertexDescriptor *vd = [MTLVertexDescriptor new]; vd.attributes[0].offset = offsetof(vertex_t, pos); vd.attributes[0].format = MTLVertexFormatFloat4; @@ -835,28 +1183,30 @@ static vertex_t vertex_bytes[] = { vd.attributes[1].bufferIndex = 4; vd.layouts[4].stride = sizeof(vertex_t); vd.layouts[4].stepFunction = MTLVertexStepFunctionPerVertex; - + MTLRenderPipelineDescriptor *psd = [MTLRenderPipelineDescriptor new]; psd.label = [NSString stringWithFormat:@"pass %d", i]; - + MTLRenderPipelineColorAttachmentDescriptor *ca = psd.colorAttachments[0]; ca.pixelFormat = SelectOptimalPixelFormat(glslang_format_to_metal(_engine.pass[i].semantics.format)); - + // TODO(sgc): confirm we never need blending for render passes ca.blendingEnabled = NO; ca.sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha; ca.sourceRGBBlendFactor = MTLBlendFactorSourceAlpha; ca.destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha; ca.destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha; - + psd.sampleCount = 1; psd.vertexDescriptor = vd; - + NSError *err; id lib = [_context.device newLibraryWithSource:vs_src options:nil error:&err]; - if (err != nil) { - if (lib == nil) { + if (err != nil) + { + if (lib == nil) + { save_msl = true; RARCH_ERR("Metal]: unable to compile vertex shader: %s\n", err.localizedDescription.UTF8String); return NO; @@ -865,12 +1215,14 @@ static vertex_t vertex_bytes[] = { RARCH_WARN("[Metal]: warnings compiling vertex shader: %s\n", err.localizedDescription.UTF8String); #endif } - + psd.vertexFunction = [lib newFunctionWithName:@"main0"]; - + lib = [_context.device newLibraryWithSource:fs_src options:nil error:&err]; - if (err != nil) { - if (lib == nil) { + if (err != nil) + { + if (lib == nil) + { save_msl = true; RARCH_ERR("Metal]: unable to compile fragment shader: %s\n", err.localizedDescription.UTF8String); return NO; @@ -880,35 +1232,41 @@ static vertex_t vertex_bytes[] = { #endif } psd.fragmentFunction = [lib newFunctionWithName:@"main0"]; - + STRUCT_ASSIGN(_engine.pass[i]._state, [_context.device newRenderPipelineStateWithDescriptor:psd error:&err]); - if (err != nil) { + if (err != nil) + { save_msl = true; RARCH_ERR("error creating pipeline state: %s", err.localizedDescription.UTF8String); return NO; } - - for (unsigned j = 0; j < SLANG_CBUFFER_MAX; j++) { + + for (unsigned j = 0; j < SLANG_CBUFFER_MAX; j++) + { unsigned int size = _engine.pass[i].semantics.cbuffers[j].size; - if (size == 0) { + if (size == 0) + { continue; } - + id buf = [_context.device newBufferWithLength:size options:MTLResourceStorageModeManaged]; STRUCT_ASSIGN(_engine.pass[i].buffers[j], buf); } - } @finally { - if (save_msl) { + } @finally + { + if (save_msl) + { RARCH_LOG("[Metal]: saving metal shader files\n"); - + NSError *err = nil; NSString *basePath = [[NSString stringWithUTF8String:shader->pass[i].source.path] stringByDeletingPathExtension]; [vs_src writeToFile:[basePath stringByAppendingPathExtension:@"vs.metal"] atomically:NO encoding:NSStringEncodingConversionAllowLossy error:&err]; - if (err != nil) { + if (err != nil) + { RARCH_ERR("[Metal]: unable to save vertex shader source: %s\n", err.localizedDescription.UTF8String); } @@ -917,60 +1275,65 @@ static vertex_t vertex_bytes[] = { atomically:NO encoding:NSStringEncodingConversionAllowLossy error:&err]; - if (err != nil) { - RARCH_ERR("[Metal]: unable to save fragment shader source: %s\n", err.localizedDescription.UTF8String); + if (err != nil) + { + RARCH_ERR("[Metal]: unable to save fragment shader source: %s\n", + err.localizedDescription.UTF8String); } } - + free(shader->pass[i].source.string.vertex); free(shader->pass[i].source.string.fragment); - + shader->pass[i].source.string.vertex = NULL; shader->pass[i].source.string.fragment = NULL; } } - - for (unsigned i = 0; i < shader->luts; i++) { + + for (unsigned i = 0; i < shader->luts; i++) + { struct texture_image image = {0}; image.supports_rgba = true; - + if (!image_texture_load(&image, shader->lut[i].path)) return NO; - + MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm width:image.width height:image.height mipmapped:shader->lut[i].mipmap]; td.usage = MTLTextureUsageShaderRead; [self _initTexture:&_engine.luts[i] withDescriptor:td]; - + [_engine.luts[i].view replaceRegion:MTLRegionMake2D(0, 0, image.width, image.height) mipmapLevel:0 withBytes:image.pixels bytesPerRow:4 * image.width]; - + // TODO(sgc): generate mip maps image_texture_free(&image); } - + video_shader_resolve_current_parameters(conf, shader); _shader = shader; shader = nil; - _fence = [_context.device newFence]; } - @finally { - if (shader) { + @finally + { + if (shader) + { [self _freeVideoShader:shader]; } - - if (conf) { + + if (conf) + { config_file_free(conf); conf = nil; } } - + resize_render_targets = YES; init_history = YES; - + return YES; } @@ -979,44 +1342,44 @@ static vertex_t vertex_bytes[] = { MTLPixelFormat glslang_format_to_metal(glslang_format fmt) { #undef FMT2 -#define FMT2(x,y) case SLANG_FORMAT_##x: return MTLPixelFormat##y +#define FMT2(x, y) case SLANG_FORMAT_##x: return MTLPixelFormat##y switch (fmt) { - FMT2(R8_UNORM, R8Unorm); - FMT2(R8_SINT, R8Sint); - FMT2(R8_UINT, R8Uint); - FMT2(R8G8_UNORM, RG8Unorm); - FMT2(R8G8_SINT, RG8Sint); - FMT2(R8G8_UINT, RG8Uint); - FMT2(R8G8B8A8_UNORM, RGBA8Unorm); - FMT2(R8G8B8A8_SINT, RGBA8Sint); - FMT2(R8G8B8A8_UINT, RGBA8Uint); - FMT2(R8G8B8A8_SRGB, RGBA8Unorm_sRGB); - - FMT2(A2B10G10R10_UNORM_PACK32, RGB10A2Unorm); - FMT2(A2B10G10R10_UINT_PACK32, RGB10A2Uint); - - FMT2(R16_UINT, R16Uint); - FMT2(R16_SINT, R16Sint); - FMT2(R16_SFLOAT, R16Float); - FMT2(R16G16_UINT, RG16Uint); - FMT2(R16G16_SINT, RG16Sint); - FMT2(R16G16_SFLOAT, RG16Float); - FMT2(R16G16B16A16_UINT, RGBA16Uint); - FMT2(R16G16B16A16_SINT, RGBA16Sint); - FMT2(R16G16B16A16_SFLOAT, RGBA16Float); - - FMT2(R32_UINT, R32Uint); - FMT2(R32_SINT, R32Sint); - FMT2(R32_SFLOAT, R32Float); - FMT2(R32G32_UINT, RG32Uint); - FMT2(R32G32_SINT, RG32Sint); - FMT2(R32G32_SFLOAT, RG32Float); - FMT2(R32G32B32A32_UINT, RGBA32Uint); - FMT2(R32G32B32A32_SINT, RGBA32Sint); - FMT2(R32G32B32A32_SFLOAT, RGBA32Float); - + FMT2(R8_UNORM, R8Unorm); + FMT2(R8_SINT, R8Sint); + FMT2(R8_UINT, R8Uint); + FMT2(R8G8_UNORM, RG8Unorm); + FMT2(R8G8_SINT, RG8Sint); + FMT2(R8G8_UINT, RG8Uint); + FMT2(R8G8B8A8_UNORM, RGBA8Unorm); + FMT2(R8G8B8A8_SINT, RGBA8Sint); + FMT2(R8G8B8A8_UINT, RGBA8Uint); + FMT2(R8G8B8A8_SRGB, RGBA8Unorm_sRGB); + + FMT2(A2B10G10R10_UNORM_PACK32, RGB10A2Unorm); + FMT2(A2B10G10R10_UINT_PACK32, RGB10A2Uint); + + FMT2(R16_UINT, R16Uint); + FMT2(R16_SINT, R16Sint); + FMT2(R16_SFLOAT, R16Float); + FMT2(R16G16_UINT, RG16Uint); + FMT2(R16G16_SINT, RG16Sint); + FMT2(R16G16_SFLOAT, RG16Float); + FMT2(R16G16B16A16_UINT, RGBA16Uint); + FMT2(R16G16B16A16_SINT, RGBA16Sint); + FMT2(R16G16B16A16_SFLOAT, RGBA16Float); + + FMT2(R32_UINT, R32Uint); + FMT2(R32_SINT, R32Sint); + FMT2(R32_SFLOAT, R32Float); + FMT2(R32G32_UINT, RG32Uint); + FMT2(R32G32_SINT, RG32Sint); + FMT2(R32G32_SFLOAT, RG32Float); + FMT2(R32G32B32A32_UINT, RGBA32Uint); + FMT2(R32G32B32A32_SINT, RGBA32Sint); + FMT2(R32G32B32A32_SFLOAT, RGBA32Float); + case SLANG_FORMAT_UNKNOWN: default: break; @@ -1031,10 +1394,10 @@ MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt) { case MTLPixelFormatRGBA8Unorm: return MTLPixelFormatBGRA8Unorm; - + case MTLPixelFormatRGBA8Unorm_sRGB: return MTLPixelFormatBGRA8Unorm_sRGB; - + default: return fmt; } diff --git a/gfx/drivers/metal.m b/gfx/drivers/metal.m index eb18c55670..8ba2ae7b5b 100644 --- a/gfx/drivers/metal.m +++ b/gfx/drivers/metal.m @@ -50,32 +50,13 @@ static void *metal_init(const video_info_t *video, const input_driver_t **input, void **input_data) { - gfx_ctx_mode_t mode; - [apple_platform setViewType:APPLE_VIEW_TYPE_METAL]; - MetalDriver *md = [MetalDriver new]; - if (md == nil) { + + MetalDriver *md = [[MetalDriver alloc] initWithVideo:video input:input inputData:input_data]; + if (md == nil) + { return NULL; } - MetalView *view = (MetalView *)apple_platform.renderView; - view.delegate = md; - - md.keepAspect = video->force_aspect; - - RARCH_LOG("[Metal]: Detecting screen resolution %ux%u.\n", video->width, video->height); - - mode.width = video->width; - mode.height = video->height; - mode.fullscreen = video->fullscreen; - - [md setVideo:video]; - [apple_platform setVideoMode:mode]; - - *input = NULL; - *input_data = NULL; - - font_driver_init_osd((__bridge_retained void *)md, false, video->is_threaded, FONT_DRIVER_RENDER_METAL_API); - return (__bridge_retained void *)md; } @@ -85,42 +66,13 @@ static bool metal_frame(void *data, const void *frame, unsigned pitch, const char *msg, video_frame_info_t *video_info) { MetalDriver *md = (__bridge MetalDriver *)data; - @autoreleasepool { - [md beginFrame]; - - FrameView *v = md.frameView; - v.frameCount = frame_count; - v.size = CGSizeMake(frame_width, frame_height); - [v updateFrame:frame pitch:pitch]; - - #if defined(HAVE_MENU) - if (md.menu.enabled) { - menu_driver_frame(video_info); - } - #endif - - [md drawViews]; - - if (video_info->statistics_show) - { - struct font_params* osd_params = (struct font_params*)&video_info->osd_stat_params; - - if (osd_params) - { - font_driver_render_msg(video_info, NULL, video_info->stat_text, - (const struct font_params*)&video_info->osd_stat_params); - } - } - - if (msg && *msg) - { - font_driver_render_msg(video_info, NULL, msg, NULL); - } - - [md endFrame]; - } - - return YES; + return [md renderFrame:frame + width:frame_width + height:frame_height + frameCount:frame_count + pitch:pitch + msg:msg + info:video_info]; } static void metal_set_nonblock_state(void *data, bool state) @@ -141,7 +93,7 @@ static bool metal_suppress_screensaver(void *data, bool enable) { bool enabled = enable; (void)data; - + return video_context_driver_suppress_screensaver(&enabled); } @@ -154,12 +106,13 @@ static bool metal_set_shader(void *data, return false; if (!path) return true; - - if (type != RARCH_SHADER_SLANG) { + + if (type != RARCH_SHADER_SLANG) + { RARCH_WARN("[Metal] Only .slang or .slangp shaders are supported. Falling back to stock.\n"); return false; } - + return [md.frameView setShaderFromPath:[NSString stringWithUTF8String:path]]; #else return false; @@ -170,13 +123,12 @@ static void metal_free(void *data) { MetalDriver *md = (__bridge_transfer MetalDriver *)data; md = nil; - font_driver_free_osd(); } static void metal_set_viewport(void *data, unsigned viewport_width, unsigned viewport_height, bool force_full, bool allow_rotate) { - RARCH_LOG("[Metal]: set_viewport\n"); + //RARCH_LOG("[Metal]: set_viewport %dx%d\n", viewport_width, viewport_height); } static void metal_set_rotation(void *data, unsigned rotation) @@ -197,12 +149,12 @@ static bool metal_read_viewport(void *data, uint8_t *buffer, bool is_idle) #ifdef HAVE_OVERLAY static const video_overlay_interface_t metal_overlay_interface = { -// metal_overlay_enable, -// metal_overlay_load, -// metal_overlay_tex_geom, -// metal_overlay_vertex_geom, -// metal_overlay_full_screen, -// metal_overlay_set_alpha, + // metal_overlay_enable, + // metal_overlay_load, + // metal_overlay_tex_geom, + // metal_overlay_vertex_geom, + // metal_overlay_full_screen, + // metal_overlay_set_alpha, }; static void metal_get_overlay_interface(void *data, @@ -218,36 +170,23 @@ static uintptr_t metal_load_texture(void *video_data, void *data, bool threaded, enum texture_filter_type filter_type) { MetalDriver *md = (__bridge MetalDriver *)video_data; - struct texture_image *image = (struct texture_image *)data; - if (!image) + struct texture_image *img = (struct texture_image *)data; + if (!img) return 0; - - if (!image->pixels && !image->width && !image->height) { - /* Create a dummy texture instead. */ -#define T0 0xff000000u -#define T1 0xffffffffu - static const uint32_t checkerboard[] = { - T0, T1, T0, T1, T0, T1, T0, T1, - T1, T0, T1, T0, T1, T0, T1, T0, - T0, T1, T0, T1, T0, T1, T0, T1, - T1, T0, T1, T0, T1, T0, T1, T0, - T0, T1, T0, T1, T0, T1, T0, T1, - T1, T0, T1, T0, T1, T0, T1, T0, - T0, T1, T0, T1, T0, T1, T0, T1, - T1, T0, T1, T0, T1, T0, T1, T0, - }; -#undef T0 -#undef T1 - - } - else { - } - - return 0; + + struct texture_image image = *img; + Texture *t = [md.context newTexture:image filter:filter_type]; + return (uintptr_t)(__bridge_retained void *)(t); } static void metal_unload_texture(void *data, uintptr_t handle) { + if (!handle) + { + return; + } + Texture *t = (__bridge_transfer Texture *)(void *)handle; + t = nil; } static void metal_set_video_mode(void *data, @@ -260,7 +199,7 @@ static void metal_set_video_mode(void *data, .height = height, .fullscreen = fullscreen, }; - + //[md setVideoMode:mode]; } @@ -268,7 +207,7 @@ static float metal_get_refresh_rate(void *data) { MetalDriver *md = (__bridge MetalDriver *)data; (void)md; - + return 0.0f; } @@ -281,27 +220,28 @@ static void metal_set_filtering(void *data, unsigned index, bool smooth) static void metal_set_aspect_ratio(void *data, unsigned aspect_ratio_idx) { MetalDriver *md = (__bridge MetalDriver *)data; - - switch (aspect_ratio_idx) { + + switch (aspect_ratio_idx) + { case ASPECT_RATIO_SQUARE: video_driver_set_viewport_square_pixel(); break; - + case ASPECT_RATIO_CORE: video_driver_set_viewport_core(); break; - + case ASPECT_RATIO_CONFIG: video_driver_set_viewport_config(); break; - + default: break; } - + video_driver_set_aspect_ratio_value( aspectratio_lut[aspect_ratio_idx].value); - + md.keepAspect = YES; [md setNeedsResize]; } @@ -318,7 +258,7 @@ static void metal_set_texture_frame(void *data, const void *frame, { MetalDriver *md = (__bridge MetalDriver *)data; settings_t *settings = config_get_ptr(); - + [md.menu updateWidth:width height:height format:rgb32 ? RPixelFormatBGRA8Unorm : RPixelFormatBGRA4Unorm @@ -332,23 +272,30 @@ static void metal_set_texture_enable(void *data, bool state, bool full_screen) MetalDriver *md = (__bridge MetalDriver *)data; if (!md) return; - + md.menu.enabled = state; //md.menu.fullScreen = full_screen; } +static void metal_set_osd_msg(void *data, + video_frame_info_t *video_info, + const char *msg, + const void *params, void *font) +{ + font_driver_render_msg(video_info, font, msg, (const struct font_params *)params); +} static void metal_show_mouse(void *data, bool state) { [apple_platform setCursorVisible:state]; } -static struct video_shader* metal_get_current_shader(void* data) +static struct video_shader *metal_get_current_shader(void *data) { MetalDriver *md = (__bridge MetalDriver *)data; if (!md) return NULL; - + return md.frameView.shader; } @@ -356,11 +303,11 @@ static struct video_shader* metal_get_current_shader(void* data) static uint32_t metal_get_flags(void *data) { uint32_t flags = 0; - + BIT32_SET(flags, GFX_CTX_FLAGS_CUSTOMIZABLE_SWAPCHAIN_IMAGES); BIT32_SET(flags, GFX_CTX_FLAGS_BLACK_FRAME_INSERTION); BIT32_SET(flags, GFX_CTX_FLAGS_MENU_FRAME_FILTERING); - + return flags; } @@ -375,6 +322,7 @@ static const video_poke_interface_t metal_poke_interface = { .apply_state_changes = metal_apply_state_changes, .set_texture_frame = metal_set_texture_frame, .set_texture_enable = metal_set_texture_enable, + .set_osd_msg = metal_set_osd_msg, .show_mouse = metal_show_mouse, .get_current_shader = metal_get_current_shader, }; diff --git a/gfx/drivers_font/metal_raster_font.m b/gfx/drivers_font/metal_raster_font.m index f04ccb4b74..9123af24dc 100644 --- a/gfx/drivers_font/metal_raster_font.m +++ b/gfx/drivers_font/metal_raster_font.m @@ -40,10 +40,12 @@ Uniforms _uniforms; id _vert; + unsigned _capacity; + unsigned _offset; unsigned _vertices; } -@property (readwrite) MetalDriver *metal; +@property (weak, readwrite) MetalDriver *metal; @property (readonly) struct font_atlas *atlas; @property (readwrite) bool needsUpdate; @@ -55,26 +57,54 @@ @implementation MetalRaster +/* macOS requires constants in a buffer to have a 256 byte alignment. */ +#ifdef TARGET_OS_MAC +static const NSUInteger kConstantAlignment = 256; +#else +static const NSUInteger kConstantAlignment = 4; +#endif + +#define ALIGN_CONSTANTS(size) ((size + kConstantAlignment - 1) & (~(kConstantAlignment - 1))) + - (instancetype)initWithDriver:(MetalDriver *)metal fontPath:(const char *)font_path fontSize:(unsigned)font_size { - if (self = [super init]) { + if (self = [super init]) + { if (metal == nil) return nil; _metal = metal; _context = metal.context; if (!font_renderer_create_default((const void **)&_font_driver, - &_font_data, font_path, font_size)) { + &_font_data, font_path, font_size)) + { RARCH_WARN("Couldn't initialize font renderer.\n"); return nil; } _uniforms.projectionMatrix = matrix_proj_ortho(0, 1, 0, 1); _atlas = _font_driver->get_atlas(_font_data); - _stride = _atlas->width; - _buffer = [_context.device newBufferWithBytes:_atlas->buffer - length:(NSUInteger)(_atlas->width * _atlas->height) - options:MTLResourceStorageModeManaged]; + _stride = ALIGN_CONSTANTS(_atlas->width); + if (_stride == _atlas->width) + { + _buffer = [_context.device newBufferWithBytes:_atlas->buffer + length:(NSUInteger)(_stride * _atlas->height) + options:MTLResourceStorageModeManaged]; + } + else + { + _buffer = [_context.device newBufferWithLength:(NSUInteger)(_stride * _atlas->height) + options:MTLResourceStorageModeManaged]; + void *dst = _buffer.contents; + void *src = _atlas->buffer; + for (unsigned i = 0; i < _atlas->height; i++) + { + memcpy(dst, src, _atlas->width); + dst += _stride; + src += _atlas->width; + } + [_buffer didModifyRange:NSMakeRange(0, _buffer.length)]; + } MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatR8Unorm width:_atlas->width @@ -83,9 +113,12 @@ _texture = [_buffer newTextureWithDescriptor:td offset:0 bytesPerRow:_stride]; - _vert = [_context.device newBufferWithLength:sizeof(FontVertex) * 500 options:MTLResourceStorageModeManaged]; + _capacity = 12000; + _vert = [_context.device newBufferWithLength:sizeof(SpriteVertex) * + _capacity options:MTLResourceStorageModeManaged]; _needsUpdate = true; - if (![self _initializeState]) { + if (![self _initializeState]) + { return nil; } } @@ -98,11 +131,11 @@ MTLVertexDescriptor *vd = [MTLVertexDescriptor new]; vd.attributes[0].offset = 0; vd.attributes[0].format = MTLVertexFormatFloat2; - vd.attributes[1].offset = offsetof(FontVertex, texCoord); + vd.attributes[1].offset = offsetof(SpriteVertex, texCoord); vd.attributes[1].format = MTLVertexFormatFloat2; - vd.attributes[2].offset = offsetof(FontVertex, color); + vd.attributes[2].offset = offsetof(SpriteVertex, color); vd.attributes[2].format = MTLVertexFormatFloat4; - vd.layouts[0].stride = sizeof(FontVertex); + vd.layouts[0].stride = sizeof(SpriteVertex); vd.layouts[0].stepFunction = MTLVertexStepFunctionPerVertex; MTLRenderPipelineDescriptor *psd = [MTLRenderPipelineDescriptor new]; @@ -118,12 +151,13 @@ psd.sampleCount = 1; psd.vertexDescriptor = vd; - psd.vertexFunction = [_context.library newFunctionWithName:@"font_vertex"]; - psd.fragmentFunction = [_context.library newFunctionWithName:@"font_fragment"]; + psd.vertexFunction = [_context.library newFunctionWithName:@"sprite_vertex"]; + psd.fragmentFunction = [_context.library newFunctionWithName:@"sprite_fragment_a8"]; NSError *err; _state = [_context.device newRenderPipelineStateWithDescriptor:psd error:&err]; - if (err != nil) { + if (err != nil) + { RARCH_ERR("[MetalRaster]: error creating pipeline state: %s\n", err.localizedDescription.UTF8String); return NO; } @@ -146,9 +180,11 @@ - (void)updateGlyph:(const struct font_glyph *)glyph { - if (_atlas->dirty) { + if (_atlas->dirty) + { unsigned row; - for (row = glyph->atlas_offset_y; row < (glyph->atlas_offset_y + glyph->height); row++) { + for (row = glyph->atlas_offset_y; row < (glyph->atlas_offset_y + glyph->height); row++) + { uint8_t *src = _atlas->buffer + row * _atlas->width + glyph->atlas_offset_x; uint8_t *dst = (uint8_t *)_buffer.contents + row * _stride + glyph->atlas_offset_x; memcpy(dst, src, glyph->width); @@ -167,19 +203,21 @@ { int delta_x = 0; - for (unsigned i = 0; i < length; i++) { + for (unsigned i = 0; i < length; i++) + { const struct font_glyph *glyph = _font_driver->get_glyph(_font_data, (uint8_t)msg[i]); if (!glyph) /* Do something smarter here ... */ glyph = _font_driver->get_glyph(_font_data, '?'); - if (glyph) { + if (glyph) + { [self updateGlyph:glyph]; delta_x += glyph->advance_x; } } - return delta_x * scale; + return (int)(delta_x * scale); } - (const struct font_glyph *)getGlyph:(uint32_t)code @@ -188,22 +226,18 @@ return NULL; const struct font_glyph *glyph = _font_driver->get_glyph((void *)_font_driver, code); - if (glyph) { + if (glyph) + { [self updateGlyph:glyph]; } return glyph; } -typedef struct color -{ - float r, g, b, a; -} color_t; - -static INLINE void write_quad(FontVertex *pv, - float x, float y, float width, float height, - float tex_x, float tex_y, float tex_width, float tex_height, - const vector_float4 *color) +static INLINE void write_quad6(SpriteVertex *pv, + float x, float y, float width, float height, + float tex_x, float tex_y, float tex_width, float tex_height, + const vector_float4 *color) { unsigned i; static const float strip[2 * 6] = { @@ -215,11 +249,12 @@ static INLINE void write_quad(FontVertex *pv, 0.0f, 1.0f, }; - for (i = 0; i < 6; i++) { - pv[i].position.x = x + strip[2 * i + 0] * width; - pv[i].position.y = y + strip[2 * i + 1] * height; - pv[i].texCoord.x = tex_x + strip[2 * i + 0] * tex_width; - pv[i].texCoord.y = tex_y + strip[2 * i + 1] * tex_height; + for (i = 0; i < 6; i++) + { + pv[i].position = simd_make_float2(x + strip[2 * i + 0] * width, + y + strip[2 * i + 1] * height); + pv[i].texCoord = simd_make_float2(tex_x + strip[2 * i + 0] * tex_width, + tex_y + strip[2 * i + 1] * tex_height); pv[i].color = *color; } } @@ -233,33 +268,36 @@ static INLINE void write_quad(FontVertex *pv, posY:(float)posY aligned:(unsigned)aligned { - const char* msg_end = msg + length; - int x = roundf(posX * _metal.viewport->width); - int y = roundf((1.0f - posY) * _metal.viewport->height); - int delta_x = 0; - int delta_y = 0; + const char *msg_end = msg + length; + int x = roundf(posX * _metal.viewport->width); + int y = roundf((1.0f - posY) * _metal.viewport->height); + int delta_x = 0; + int delta_y = 0; float inv_tex_size_x = 1.0f / _texture.width; float inv_tex_size_y = 1.0f / _texture.height; - float inv_win_width = 1.0f / _metal.viewport->width; + float inv_win_width = 1.0f / _metal.viewport->width; float inv_win_height = 1.0f / _metal.viewport->height; - switch (aligned) { + switch (aligned) + { case TEXT_ALIGN_RIGHT: x -= [self getWidthForMessage:msg length:length scale:scale]; break; - + case TEXT_ALIGN_CENTER: x -= [self getWidthForMessage:msg length:length scale:scale] / 2; break; - + default: break; } - FontVertex *v = (FontVertex *)_vert.contents; + SpriteVertex *v = (SpriteVertex *)_vert.contents; + v += _offset + _vertices; - while (msg < msg_end) { - unsigned code = utf8_walk(&msg); + while (msg < msg_end) + { + unsigned code = utf8_walk(&msg); const struct font_glyph *glyph = _font_driver->get_glyph(_font_data, code); if (!glyph) /* Do something smarter here ... */ @@ -271,33 +309,36 @@ static INLINE void write_quad(FontVertex *pv, [self updateGlyph:glyph]; int off_x, off_y, tex_x, tex_y, width, height; - off_x = glyph->draw_offset_x; - off_y = glyph->draw_offset_y; - tex_x = glyph->atlas_offset_x; - tex_y = glyph->atlas_offset_y; - width = glyph->width; + off_x = glyph->draw_offset_x; + off_y = glyph->draw_offset_y; + tex_x = glyph->atlas_offset_x; + tex_y = glyph->atlas_offset_y; + width = glyph->width; height = glyph->height; - write_quad(v + _vertices, - (x + off_x + delta_x * scale) * inv_win_width, - (y + off_y + delta_y * scale) * inv_win_height, - width * scale * inv_win_width, - height * scale * inv_win_height, - tex_x * inv_tex_size_x, - tex_y * inv_tex_size_y, - width * inv_tex_size_x, - height * inv_tex_size_y, - &color); + write_quad6(v, + (x + off_x + delta_x * scale) * inv_win_width, + (y + off_y + delta_y * scale) * inv_win_height, + width * scale * inv_win_width, + height * scale * inv_win_height, + tex_x * inv_tex_size_x, + tex_y * inv_tex_size_y, + width * inv_tex_size_x, + height * inv_tex_size_y, + &color); _vertices += 6; + v += 6; - delta_x += glyph->advance_x; - delta_y += glyph->advance_y; + delta_x += glyph->advance_x; + delta_y += glyph->advance_y; } } -- (void)_flush { - [_vert didModifyRange:NSMakeRange(0, sizeof(FontVertex)*_vertices)]; +- (void)_flush +{ + NSUInteger start = _offset * sizeof(SpriteVertex); + [_vert didModifyRange:NSMakeRange(start, sizeof(SpriteVertex) * _vertices)]; _rpd.colorAttachments[0].texture = _context.nextDrawable.texture; id cb = _context.commandBuffer; @@ -305,12 +346,14 @@ static INLINE void write_quad(FontVertex *pv, [rce pushDebugGroup:@"render fonts"]; [rce setRenderPipelineState:_state]; [rce setVertexBytes:&_uniforms length:sizeof(_uniforms) atIndex:BufferIndexUniforms]; - [rce setVertexBuffer:_vert offset:0 atIndex:BufferIndexPositions]; + [rce setVertexBuffer:_vert offset:start atIndex:BufferIndexPositions]; [rce setFragmentTexture:_texture atIndex:TextureIndexColor]; [rce setFragmentSamplerState:_sampler atIndex:SamplerIndexDraw]; [rce drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:_vertices]; [rce popDebugGroup]; [rce endEncoding]; + + _offset += _vertices; _vertices = 0; } @@ -323,7 +366,8 @@ static INLINE void write_quad(FontVertex *pv, aligned:(unsigned)aligned { /* If the font height is not supported just draw as usual */ - if (!_font_driver->get_line_height) { + if (!_font_driver->get_line_height) + { [self _renderLine:msg video:video length:strlen(msg) scale:scale color:color posX:posX posY:posY aligned:aligned]; return; } @@ -331,11 +375,13 @@ static INLINE void write_quad(FontVertex *pv, int lines = 0; float line_height = _font_driver->get_line_height(_font_data) * scale / video->height; - for (;;) { + for (;;) + { const char *delim = strchr(msg, '\n'); /* Draw the line */ - if (delim) { + if (delim) + { unsigned msg_len = delim - msg; [self _renderLine:msg video:video @@ -348,7 +394,8 @@ static INLINE void write_quad(FontVertex *pv, msg += msg_len + 1; lines++; } - else { + else + { unsigned msg_len = strlen(msg); [self _renderLine:msg video:video @@ -378,7 +425,8 @@ static INLINE void write_quad(FontVertex *pv, unsigned width = video->width; unsigned height = video->height; - if (params) { + if (params) + { x = params->x; y = params->y; scale = params->scale; @@ -387,21 +435,26 @@ static INLINE void write_quad(FontVertex *pv, drop_y = params->drop_y; drop_mod = params->drop_mod; drop_alpha = params->drop_alpha; - color.x = FONT_COLOR_GET_RED(params->color) / 255.0f; - color.y = FONT_COLOR_GET_GREEN(params->color) / 255.0f; - color.z = FONT_COLOR_GET_BLUE(params->color) / 255.0f; - color.w = FONT_COLOR_GET_ALPHA(params->color) / 255.0f; + + color = simd_make_float4( + FONT_COLOR_GET_RED(params->color) / 255.0f, + FONT_COLOR_GET_GREEN(params->color) / 255.0f, + FONT_COLOR_GET_BLUE(params->color) / 255.0f, + FONT_COLOR_GET_ALPHA(params->color) / 255.0f); + } - else { + else + { x = video->font_msg_pos_x; y = video->font_msg_pos_y; scale = 1.0f; text_align = TEXT_ALIGN_LEFT; - color.x = video->font_msg_color_r; - color.y = video->font_msg_color_g; - color.z = video->font_msg_color_b; - color.w = 1.0; + color = simd_make_float4( + video->font_msg_color_r, + video->font_msg_color_g, + video->font_msg_color_b, + 1.0f); drop_x = -2; drop_y = -2; @@ -409,19 +462,20 @@ static INLINE void write_quad(FontVertex *pv, drop_alpha = 1.0f; } - @autoreleasepool { + @autoreleasepool + { NSUInteger max_glyphs = strlen(msg); if (drop_x || drop_y) max_glyphs *= 2; - NSUInteger needed = sizeof(FontVertex) * max_glyphs * 6; - if (_vert.length < needed) + if (max_glyphs * 6 + _offset > _capacity) { - _vert = [_context.device newBufferWithLength:needed options:MTLResourceStorageModeManaged]; + _offset = 0; } - if (drop_x || drop_y) { + if (drop_x || drop_y) + { color_dark.x = color.x * drop_mod; color_dark.y = color.y * drop_mod; color_dark.z = color.z * drop_mod; @@ -456,7 +510,7 @@ static void *metal_raster_font_init_font(void *data, const char *font_path, float font_size, bool is_threaded) { - MetalRaster *r = [[MetalRaster alloc] initWithDriver:(__bridge_transfer MetalDriver *)data fontPath:font_path fontSize:(unsigned)font_size]; + MetalRaster *r = [[MetalRaster alloc] initWithDriver:(__bridge MetalDriver *)data fontPath:font_path fontSize:(unsigned)font_size]; if (!r) return NULL; @@ -478,16 +532,16 @@ static int metal_get_message_width(void *data, const char *msg, } static void metal_raster_font_render_msg( - video_frame_info_t *video_info, - void *data, const char *msg, - const struct font_params *params) + video_frame_info_t *video_info, + void *data, const char *msg, + const struct font_params *params) { MetalRaster *r = (__bridge MetalRaster *)data; [r renderMessage:msg video:video_info params:params]; } static const struct font_glyph *metal_raster_font_get_glyph( - void *data, uint32_t code) + void *data, uint32_t code) { MetalRaster *r = (__bridge MetalRaster *)data; return [r getGlyph:code]; diff --git a/griffin/griffin_objc.m b/griffin/griffin_objc.m index c009622d53..1393d61bf1 100644 --- a/griffin/griffin_objc.m +++ b/griffin/griffin_objc.m @@ -60,11 +60,10 @@ #ifdef HAVE_METAL #import "../gfx/common/metal/Context.m" #import "../gfx/common/metal/Filter.m" -#import "../gfx/common/metal/PixelConverter.m" -#import "../gfx/common/metal/Renderer.m" #import "../gfx/common/metal/RendererCommon.m" #import "../gfx/common/metal/View.m" #import "../gfx/common/metal/TexturedView.m" +#import "../gfx/common/metal/MenuDisplay.m" #import "../gfx/common/metal_common.m" #import "../gfx/drivers/metal.m" #import "../menu/drivers_display/menu_display_metal.m" diff --git a/menu/drivers_display/menu_display_metal.m b/menu/drivers_display/menu_display_metal.m index 9e92e9b744..5184aefa51 100644 --- a/menu/drivers_display/menu_display_metal.m +++ b/menu/drivers_display/menu_display_metal.m @@ -17,31 +17,60 @@ #include "../../gfx/video_driver.h" #import "../../gfx/common/metal_common.h" +#define GET_DRIVER(video_info) (video_info ? (__bridge MetalDriver *)video_info->userdata : NULL); + +static const float *menu_display_metal_get_default_vertices(void) +{ + return [MenuDisplay defaultVertices]; +} + +static const float *menu_display_metal_get_default_tex_coords(void) +{ + return [MenuDisplay defaultTexCoords]; +} + static void *menu_display_metal_get_default_mvp(video_frame_info_t *video_info) { - return NULL; + MetalDriver *md = GET_DRIVER(video_info); + if (!md) + return NULL; + return (void *)md.viewportMVP; } static void menu_display_metal_blend_begin(video_frame_info_t *video_info) { + MetalDriver *md = GET_DRIVER(video_info); + if (!md) + return; + + md.display.blend = YES; } static void menu_display_metal_blend_end(video_frame_info_t *video_info) { + MetalDriver *md = GET_DRIVER(video_info); + if (!md) + return; + + md.display.blend = NO; } static void menu_display_metal_draw(menu_display_ctx_draw_t *draw, - video_frame_info_t *video_info) + video_frame_info_t *video_info) { + MetalDriver *md = GET_DRIVER(video_info); + if (!md || !draw) + return; + + [md.display draw:draw video:video_info]; } -static void menu_display_metal_draw_pipeline( - menu_display_ctx_draw_t *draw, video_frame_info_t *video_info) +static void menu_display_metal_draw_pipeline(menu_display_ctx_draw_t *draw, video_frame_info_t *video_info) { } static void menu_display_metal_viewport(menu_display_ctx_draw_t *draw, - video_frame_info_t *video_info) + video_frame_info_t *video_info) { } @@ -49,56 +78,47 @@ static void menu_display_metal_restore_clear_color(void) { } -static void menu_display_metal_clear_color( - menu_display_ctx_clearcolor_t *clearcolor, - video_frame_info_t *video_info) +static void menu_display_metal_clear_color(menu_display_ctx_clearcolor_t *clearcolor, + video_frame_info_t *video_info) { - (void)clearcolor; + MetalDriver *md = GET_DRIVER(video_info); + if (!md) + return; + + md.display.clearColor = MTLClearColorMake(clearcolor->r, clearcolor->g, clearcolor->b, clearcolor->a); } static bool menu_display_metal_font_init_first( - void **font_handle, void *video_data, - const char *font_path, float font_size, - bool is_threaded) + void **font_handle, void *video_data, + const char *font_path, float font_size, + bool is_threaded) { - font_data_t **handle = (font_data_t**)font_handle; + font_data_t **handle = (font_data_t **)font_handle; *handle = font_driver_init_first(video_data, font_path, font_size, true, is_threaded, FONT_DRIVER_RENDER_METAL_API); - + if (*handle) return true; - + return false; } -static const float *menu_display_metal_get_default_vertices(void) -{ - static float dummy[16] = {0.0f}; - return &dummy[0]; -} - -static const float *menu_display_metal_get_default_tex_coords(void) -{ - static float dummy[16] = {0.0f}; - return &dummy[0]; -} - menu_display_ctx_driver_t menu_display_ctx_metal = { - menu_display_metal_draw, - menu_display_metal_draw_pipeline, - menu_display_metal_viewport, - menu_display_metal_blend_begin, - menu_display_metal_blend_end, - menu_display_metal_restore_clear_color, - menu_display_metal_clear_color, - menu_display_metal_get_default_mvp, - menu_display_metal_get_default_vertices, - menu_display_metal_get_default_tex_coords, - menu_display_metal_font_init_first, - MENU_VIDEO_DRIVER_GENERIC, - "menu_display_metal", - false + .draw = menu_display_metal_draw, + .draw_pipeline = menu_display_metal_draw_pipeline, + .viewport = menu_display_metal_viewport, + .blend_begin = menu_display_metal_blend_begin, + .blend_end = menu_display_metal_blend_end, + .restore_clear_color = menu_display_metal_restore_clear_color, + .clear_color = menu_display_metal_clear_color, + .get_default_mvp = menu_display_metal_get_default_mvp, + .get_default_vertices = menu_display_metal_get_default_vertices, + .get_default_tex_coords = menu_display_metal_get_default_tex_coords, + .font_init_first = menu_display_metal_font_init_first, + .type = MENU_VIDEO_DRIVER_METAL, + .ident = "menu_display_metal", + .handles_transform = NO, }; diff --git a/pkg/apple/RetroArch_Metal.xcodeproj/project.pbxproj b/pkg/apple/RetroArch_Metal.xcodeproj/project.pbxproj index 47e0a24049..8fd0f4dee9 100644 --- a/pkg/apple/RetroArch_Metal.xcodeproj/project.pbxproj +++ b/pkg/apple/RetroArch_Metal.xcodeproj/project.pbxproj @@ -9,7 +9,6 @@ /* Begin PBXBuildFile section */ 05269A6220ABF20500C29F1E /* MetalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05269A6120ABF20500C29F1E /* MetalKit.framework */; }; 05A8C7B420DB75A500FF7857 /* Shaders.metal in Sources */ = {isa = PBXBuildFile; fileRef = 05A8C74E20DB72F100FF7857 /* Shaders.metal */; }; - 05A8C7B520DB75A800FF7857 /* PixelConverter.metal in Sources */ = {isa = PBXBuildFile; fileRef = 05A8C75920DB72F100FF7857 /* PixelConverter.metal */; }; 05A8E23820A63CB40084ABDA /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A8E23720A63CB40084ABDA /* Metal.framework */; }; 05A8E23A20A63CED0084ABDA /* IOSurface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A8E23920A63CED0084ABDA /* IOSurface.framework */; }; 05A8E23C20A63CF50084ABDA /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A8E23B20A63CF50084ABDA /* QuartzCore.framework */; }; @@ -60,6 +59,17 @@ 055312AE20DE130A00C4D7F4 /* pixconv.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = pixconv.c; sourceTree = ""; }; 055312AF20DE130A00C4D7F4 /* scaler_filter.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = scaler_filter.c; sourceTree = ""; }; 055312B020DE130A00C4D7F4 /* scaler.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = scaler.c; sourceTree = ""; }; + 0566C78420E49E6800BC768F /* video_frame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = video_frame.h; sourceTree = ""; }; + 0566C78620E49E6800BC768F /* vector_4.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vector_4.h; sourceTree = ""; }; + 0566C78720E49E6800BC768F /* vector_3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vector_3.h; sourceTree = ""; }; + 0566C78820E49E6800BC768F /* vector_2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vector_2.h; sourceTree = ""; }; + 0566C78920E49E6800BC768F /* matrix_3x3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = matrix_3x3.h; sourceTree = ""; }; + 0566C78A20E49E6800BC768F /* matrix_4x4.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = matrix_4x4.h; sourceTree = ""; }; + 0566C78C20E49E6800BC768F /* scaler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = scaler.h; sourceTree = ""; }; + 0566C78D20E49E6800BC768F /* pixconv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pixconv.h; sourceTree = ""; }; + 0566C78E20E49E6800BC768F /* scaler_int.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = scaler_int.h; sourceTree = ""; }; + 0566C78F20E49E6800BC768F /* filter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = filter.h; sourceTree = ""; }; + 0566C79020E49E6800BC768F /* gl_capabilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gl_capabilities.h; sourceTree = ""; }; 05A8C51B20DB72F000FF7857 /* menu_shader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = menu_shader.h; sourceTree = ""; }; 05A8C51D20DB72F000FF7857 /* menu_cbs_get_value.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = menu_cbs_get_value.c; sourceTree = ""; }; 05A8C51E20DB72F000FF7857 /* menu_cbs_sublabel.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = menu_cbs_sublabel.c; sourceTree = ""; }; @@ -279,26 +289,19 @@ 05A8C73920DB72F100FF7857 /* font_driver.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = font_driver.c; sourceTree = ""; }; 05A8C73C20DB72F100FF7857 /* vulkan_common.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = vulkan_common.c; sourceTree = ""; }; 05A8C74420DB72F100FF7857 /* metal_common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = metal_common.h; sourceTree = ""; }; - 05A8C74620DB72F100FF7857 /* MetalRenderer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MetalRenderer.h; sourceTree = ""; }; - 05A8C74720DB72F100FF7857 /* PixelConverter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PixelConverter.h; sourceTree = ""; }; 05A8C74820DB72F100FF7857 /* metal_common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = metal_common.h; sourceTree = ""; }; 05A8C74920DB72F100FF7857 /* TexturedView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TexturedView.h; sourceTree = ""; }; - 05A8C74A20DB72F100FF7857 /* Renderer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Renderer.h; sourceTree = ""; }; 05A8C74B20DB72F100FF7857 /* Context.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Context.h; sourceTree = ""; }; 05A8C74C20DB72F100FF7857 /* RendererCommon.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RendererCommon.h; sourceTree = ""; }; - 05A8C74D20DB72F100FF7857 /* PixelConverter+private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PixelConverter+private.h"; sourceTree = ""; }; 05A8C74E20DB72F100FF7857 /* Shaders.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = Shaders.metal; sourceTree = ""; }; 05A8C74F20DB72F100FF7857 /* View.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = View.h; sourceTree = ""; }; 05A8C75020DB72F100FF7857 /* Filter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Filter.m; sourceTree = ""; }; 05A8C75120DB72F100FF7857 /* ShaderTypes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ShaderTypes.h; sourceTree = ""; }; - 05A8C75220DB72F100FF7857 /* PixelConverter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PixelConverter.m; sourceTree = ""; }; 05A8C75320DB72F100FF7857 /* Context.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Context.m; sourceTree = ""; }; 05A8C75420DB72F100FF7857 /* RendererCommon.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RendererCommon.m; sourceTree = ""; }; - 05A8C75520DB72F100FF7857 /* Renderer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Renderer.m; sourceTree = ""; }; 05A8C75620DB72F100FF7857 /* TexturedView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TexturedView.m; sourceTree = ""; }; 05A8C75720DB72F100FF7857 /* Filter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Filter.h; sourceTree = ""; }; 05A8C75820DB72F100FF7857 /* View.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = View.m; sourceTree = ""; }; - 05A8C75920DB72F100FF7857 /* PixelConverter.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = PixelConverter.metal; sourceTree = ""; }; 05A8C75D20DB72F100FF7857 /* gl_common.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = gl_common.c; sourceTree = ""; }; 05A8C75E20DB72F100FF7857 /* d3d_common.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = d3d_common.c; sourceTree = ""; }; 05A8C76320DB72F100FF7857 /* d3d10_common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = d3d10_common.h; sourceTree = ""; }; @@ -352,6 +355,59 @@ 05A8E23720A63CB40084ABDA /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; }; 05A8E23920A63CED0084ABDA /* IOSurface.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOSurface.framework; path = System/Library/Frameworks/IOSurface.framework; sourceTree = SDKROOT; }; 05A8E23B20A63CF50084ABDA /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + 05C5D53320E3DD0900654EE4 /* input_types.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = input_types.h; sourceTree = ""; }; + 05C5D53420E3DD0900654EE4 /* input_remote.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = input_remote.c; sourceTree = ""; }; + 05C5D53720E3DD0900654EE4 /* nullinput.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = nullinput.c; sourceTree = ""; }; + 05C5D53820E3DD0900654EE4 /* cocoa_input.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cocoa_input.h; sourceTree = ""; }; + 05C5D54120E3DD0900654EE4 /* sdl_input.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = sdl_input.c; sourceTree = ""; }; + 05C5D54220E3DD0900654EE4 /* cocoa_input.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = cocoa_input.c; sourceTree = ""; }; + 05C5D54C20E3DD0900654EE4 /* input_keymaps.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = input_keymaps.h; sourceTree = ""; }; + 05C5D54E20E3DD0900654EE4 /* blissbox.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = blissbox.h; sourceTree = ""; }; + 05C5D55420E3DD0900654EE4 /* GCExtendedGamepadSnapshot.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCExtendedGamepadSnapshot.h; sourceTree = ""; }; + 05C5D55520E3DD0900654EE4 /* GCControllerButtonInput.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCControllerButtonInput.h; sourceTree = ""; }; + 05C5D55620E3DD0900654EE4 /* GCGamepad.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCGamepad.h; sourceTree = ""; }; + 05C5D55720E3DD0900654EE4 /* GCExtendedGamepad.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCExtendedGamepad.h; sourceTree = ""; }; + 05C5D55820E3DD0900654EE4 /* GCGamepadSnapshot.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCGamepadSnapshot.h; sourceTree = ""; }; + 05C5D55920E3DD0900654EE4 /* GCControllerAxisInput.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCControllerAxisInput.h; sourceTree = ""; }; + 05C5D55A20E3DD0900654EE4 /* GCControllerDirectionPad.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCControllerDirectionPad.h; sourceTree = ""; }; + 05C5D55B20E3DD0900654EE4 /* GameController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GameController.h; sourceTree = ""; }; + 05C5D55C20E3DD0900654EE4 /* GCController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCController.h; sourceTree = ""; }; + 05C5D55D20E3DD0900654EE4 /* GCControllerElement.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCControllerElement.h; sourceTree = ""; }; + 05C5D55E20E3DD0900654EE4 /* hid_types.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = hid_types.h; sourceTree = ""; }; + 05C5D55F20E3DD0900654EE4 /* hid_driver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = hid_driver.h; sourceTree = ""; }; + 05C5D56020E3DD0900654EE4 /* gamepad.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = gamepad.h; sourceTree = ""; }; + 05C5D56120E3DD0900654EE4 /* input_driver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = input_driver.h; sourceTree = ""; }; + 05C5D56320E3DD0900654EE4 /* keyboard_event_apple.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = keyboard_event_apple.c; sourceTree = ""; }; + 05C5D56620E3DD0900654EE4 /* keyboard_event_apple.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = keyboard_event_apple.h; sourceTree = ""; }; + 05C5D56A20E3DD0900654EE4 /* input_remapping.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = input_remapping.h; sourceTree = ""; }; + 05C5D56B20E3DD0900654EE4 /* input_mapper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = input_mapper.h; sourceTree = ""; }; + 05C5D56C20E3DD0900654EE4 /* input_overlay.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = input_overlay.h; sourceTree = ""; }; + 05C5D56D20E3DD0900654EE4 /* input_defines.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = input_defines.h; sourceTree = ""; }; + 05C5D56F20E3DD0900654EE4 /* btstack_hid.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = btstack_hid.c; sourceTree = ""; }; + 05C5D57020E3DD0900654EE4 /* null_hid.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = null_hid.c; sourceTree = ""; }; + 05C5D57320E3DD0900654EE4 /* iohidmanager_hid.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = iohidmanager_hid.c; sourceTree = ""; }; + 05C5D57620E3DD0900654EE4 /* input_common.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = input_common.c; sourceTree = ""; }; + 05C5D57720E3DD0900654EE4 /* input_x11_common.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = input_x11_common.c; sourceTree = ""; }; + 05C5D57820E3DD0900654EE4 /* linux_common.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = linux_common.c; sourceTree = ""; }; + 05C5D57A20E3DD0900654EE4 /* hid_device_driver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = hid_device_driver.h; sourceTree = ""; }; + 05C5D57B20E3DD0900654EE4 /* device_ds3.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = device_ds3.c; sourceTree = ""; }; + 05C5D57C20E3DD0900654EE4 /* device_ds4.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = device_ds4.c; sourceTree = ""; }; + 05C5D57D20E3DD0900654EE4 /* hid_device_driver.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = hid_device_driver.c; sourceTree = ""; }; + 05C5D57E20E3DD0900654EE4 /* device_wiiu_gca.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = device_wiiu_gca.c; sourceTree = ""; }; + 05C5D57F20E3DD0900654EE4 /* device_null.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = device_null.c; sourceTree = ""; }; + 05C5D58020E3DD0900654EE4 /* linux_common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = linux_common.h; sourceTree = ""; }; + 05C5D58120E3DD0900654EE4 /* input_x11_common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = input_x11_common.h; sourceTree = ""; }; + 05C5D58220E3DD0900654EE4 /* input_autodetect_builtin.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = input_autodetect_builtin.c; sourceTree = ""; }; + 05C5D58320E3DD0900654EE4 /* input_keymaps.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = input_keymaps.c; sourceTree = ""; }; + 05C5D58720E3DD0900654EE4 /* sdl_joypad.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = sdl_joypad.c; sourceTree = ""; }; + 05C5D58820E3DD0900654EE4 /* null_joypad.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = null_joypad.c; sourceTree = ""; }; + 05C5D58D20E3DD0900654EE4 /* mfi_joypad.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = mfi_joypad.m; sourceTree = ""; }; + 05C5D59820E3DD0A00654EE4 /* hid_joypad.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = hid_joypad.c; sourceTree = ""; }; + 05C5D59F20E3DD0A00654EE4 /* input_remote.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = input_remote.h; sourceTree = ""; }; + 05C5D5A020E3DD0A00654EE4 /* input_overlay.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = input_overlay.c; sourceTree = ""; }; + 05C5D5A120E3DD0A00654EE4 /* input_mapper.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = input_mapper.c; sourceTree = ""; }; + 05C5D5A220E3DD0A00654EE4 /* input_remapping.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = input_remapping.c; sourceTree = ""; }; + 05C5D5A320E3DD0A00654EE4 /* input_driver.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = input_driver.c; sourceTree = ""; }; 05D7753120A55D2700646447 /* BaseConfig.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = BaseConfig.xcconfig; sourceTree = ""; }; 05D7753320A5678300646447 /* griffin_cpp.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = griffin_cpp.cpp; path = ../../griffin/griffin_cpp.cpp; sourceTree = ""; }; 05D7753420A5678400646447 /* griffin_glslang.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = griffin_glslang.cpp; path = ../../griffin/griffin_glslang.cpp; sourceTree = ""; }; @@ -373,6 +429,8 @@ 84DD5EB71A89F1C7007336C1 /* retroarch.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = retroarch.icns; path = ../../media/retroarch.icns; sourceTree = ""; }; 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = OSX/Info.plist; sourceTree = ""; }; 8D1107320486CEB800E47090 /* RetroArch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RetroArch.app; sourceTree = BUILT_PRODUCTS_DIR; }; + A902040DE66D42F9EE47BFE3 /* MenuDisplay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MenuDisplay.h; sourceTree = ""; }; + A902070F2C43F222FD56A95A /* MenuDisplay.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MenuDisplay.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -410,6 +468,7 @@ 0538875020DE11A800769232 /* include */ = { isa = PBXGroup; children = ( + 0566C78320E49E6800BC768F /* gfx */, 0538875920DE11D300769232 /* libretro.h */, 0538875720DE11D300769232 /* retro_assert.h */, 0538875120DE11D200769232 /* retro_common_api.h */, @@ -443,6 +502,40 @@ path = scaler; sourceTree = ""; }; + 0566C78320E49E6800BC768F /* gfx */ = { + isa = PBXGroup; + children = ( + 0566C78520E49E6800BC768F /* math */, + 0566C78B20E49E6800BC768F /* scaler */, + 0566C78420E49E6800BC768F /* video_frame.h */, + 0566C79020E49E6800BC768F /* gl_capabilities.h */, + ); + path = gfx; + sourceTree = ""; + }; + 0566C78520E49E6800BC768F /* math */ = { + isa = PBXGroup; + children = ( + 0566C78620E49E6800BC768F /* vector_4.h */, + 0566C78720E49E6800BC768F /* vector_3.h */, + 0566C78820E49E6800BC768F /* vector_2.h */, + 0566C78920E49E6800BC768F /* matrix_3x3.h */, + 0566C78A20E49E6800BC768F /* matrix_4x4.h */, + ); + path = math; + sourceTree = ""; + }; + 0566C78B20E49E6800BC768F /* scaler */ = { + isa = PBXGroup; + children = ( + 0566C78C20E49E6800BC768F /* scaler.h */, + 0566C78D20E49E6800BC768F /* pixconv.h */, + 0566C78E20E49E6800BC768F /* scaler_int.h */, + 0566C78F20E49E6800BC768F /* filter.h */, + ); + path = scaler; + sourceTree = ""; + }; 05A8C51920DB72C200FF7857 /* Sources */ = { isa = PBXGroup; children = ( @@ -450,6 +543,7 @@ 05A8C59520DB72F000FF7857 /* frontend */, 05A8C5D420DB72F000FF7857 /* gfx */, 05A8C57120DB72F000FF7857 /* intl */, + 05C5D53220E3DD0900654EE4 /* input */, 05A8C51A20DB72F000FF7857 /* menu */, 05A8C5AD20DB72F000FF7857 /* ui */, ); @@ -861,13 +955,8 @@ 05A8C75720DB72F100FF7857 /* Filter.h */, 05A8C75020DB72F100FF7857 /* Filter.m */, 05A8C74820DB72F100FF7857 /* metal_common.h */, - 05A8C74620DB72F100FF7857 /* MetalRenderer.h */, - 05A8C74720DB72F100FF7857 /* PixelConverter.h */, - 05A8C75220DB72F100FF7857 /* PixelConverter.m */, - 05A8C75920DB72F100FF7857 /* PixelConverter.metal */, - 05A8C74D20DB72F100FF7857 /* PixelConverter+private.h */, - 05A8C74A20DB72F100FF7857 /* Renderer.h */, - 05A8C75520DB72F100FF7857 /* Renderer.m */, + A902040DE66D42F9EE47BFE3 /* MenuDisplay.h */, + A902070F2C43F222FD56A95A /* MenuDisplay.m */, 05A8C74C20DB72F100FF7857 /* RendererCommon.h */, 05A8C75420DB72F100FF7857 /* RendererCommon.m */, 05A8C74E20DB72F100FF7857 /* Shaders.metal */, @@ -947,6 +1036,131 @@ path = metal_shaders; sourceTree = ""; }; + 05C5D53220E3DD0900654EE4 /* input */ = { + isa = PBXGroup; + children = ( + 05C5D57520E3DD0900654EE4 /* common */, + 05C5D53520E3DD0900654EE4 /* drivers */, + 05C5D56E20E3DD0900654EE4 /* drivers_hid */, + 05C5D58420E3DD0900654EE4 /* drivers_joypad */, + 05C5D56220E3DD0900654EE4 /* drivers_keyboard */, + 05C5D54D20E3DD0900654EE4 /* include */, + 05C5D58220E3DD0900654EE4 /* input_autodetect_builtin.c */, + 05C5D56D20E3DD0900654EE4 /* input_defines.h */, + 05C5D5A320E3DD0A00654EE4 /* input_driver.c */, + 05C5D56120E3DD0900654EE4 /* input_driver.h */, + 05C5D58320E3DD0900654EE4 /* input_keymaps.c */, + 05C5D54C20E3DD0900654EE4 /* input_keymaps.h */, + 05C5D5A120E3DD0A00654EE4 /* input_mapper.c */, + 05C5D56B20E3DD0900654EE4 /* input_mapper.h */, + 05C5D5A020E3DD0A00654EE4 /* input_overlay.c */, + 05C5D56C20E3DD0900654EE4 /* input_overlay.h */, + 05C5D5A220E3DD0A00654EE4 /* input_remapping.c */, + 05C5D56A20E3DD0900654EE4 /* input_remapping.h */, + 05C5D53420E3DD0900654EE4 /* input_remote.c */, + 05C5D59F20E3DD0A00654EE4 /* input_remote.h */, + 05C5D53320E3DD0900654EE4 /* input_types.h */, + ); + name = input; + path = ../../input; + sourceTree = ""; + }; + 05C5D53520E3DD0900654EE4 /* drivers */ = { + isa = PBXGroup; + children = ( + 05C5D54220E3DD0900654EE4 /* cocoa_input.c */, + 05C5D53820E3DD0900654EE4 /* cocoa_input.h */, + 05C5D53720E3DD0900654EE4 /* nullinput.c */, + 05C5D54120E3DD0900654EE4 /* sdl_input.c */, + ); + path = drivers; + sourceTree = ""; + }; + 05C5D54D20E3DD0900654EE4 /* include */ = { + isa = PBXGroup; + children = ( + 05C5D55320E3DD0900654EE4 /* GameController */, + 05C5D54E20E3DD0900654EE4 /* blissbox.h */, + 05C5D56020E3DD0900654EE4 /* gamepad.h */, + 05C5D55F20E3DD0900654EE4 /* hid_driver.h */, + 05C5D55E20E3DD0900654EE4 /* hid_types.h */, + ); + path = include; + sourceTree = ""; + }; + 05C5D55320E3DD0900654EE4 /* GameController */ = { + isa = PBXGroup; + children = ( + 05C5D55B20E3DD0900654EE4 /* GameController.h */, + 05C5D55C20E3DD0900654EE4 /* GCController.h */, + 05C5D55920E3DD0900654EE4 /* GCControllerAxisInput.h */, + 05C5D55520E3DD0900654EE4 /* GCControllerButtonInput.h */, + 05C5D55A20E3DD0900654EE4 /* GCControllerDirectionPad.h */, + 05C5D55D20E3DD0900654EE4 /* GCControllerElement.h */, + 05C5D55720E3DD0900654EE4 /* GCExtendedGamepad.h */, + 05C5D55420E3DD0900654EE4 /* GCExtendedGamepadSnapshot.h */, + 05C5D55620E3DD0900654EE4 /* GCGamepad.h */, + 05C5D55820E3DD0900654EE4 /* GCGamepadSnapshot.h */, + ); + path = GameController; + sourceTree = ""; + }; + 05C5D56220E3DD0900654EE4 /* drivers_keyboard */ = { + isa = PBXGroup; + children = ( + 05C5D56320E3DD0900654EE4 /* keyboard_event_apple.c */, + 05C5D56620E3DD0900654EE4 /* keyboard_event_apple.h */, + ); + path = drivers_keyboard; + sourceTree = ""; + }; + 05C5D56E20E3DD0900654EE4 /* drivers_hid */ = { + isa = PBXGroup; + children = ( + 05C5D56F20E3DD0900654EE4 /* btstack_hid.c */, + 05C5D57020E3DD0900654EE4 /* null_hid.c */, + 05C5D57320E3DD0900654EE4 /* iohidmanager_hid.c */, + ); + path = drivers_hid; + sourceTree = ""; + }; + 05C5D57520E3DD0900654EE4 /* common */ = { + isa = PBXGroup; + children = ( + 05C5D57920E3DD0900654EE4 /* hid */, + 05C5D57620E3DD0900654EE4 /* input_common.c */, + 05C5D57720E3DD0900654EE4 /* input_x11_common.c */, + 05C5D58120E3DD0900654EE4 /* input_x11_common.h */, + 05C5D57820E3DD0900654EE4 /* linux_common.c */, + 05C5D58020E3DD0900654EE4 /* linux_common.h */, + ); + path = common; + sourceTree = ""; + }; + 05C5D57920E3DD0900654EE4 /* hid */ = { + isa = PBXGroup; + children = ( + 05C5D57B20E3DD0900654EE4 /* device_ds3.c */, + 05C5D57C20E3DD0900654EE4 /* device_ds4.c */, + 05C5D57F20E3DD0900654EE4 /* device_null.c */, + 05C5D57E20E3DD0900654EE4 /* device_wiiu_gca.c */, + 05C5D57D20E3DD0900654EE4 /* hid_device_driver.c */, + 05C5D57A20E3DD0900654EE4 /* hid_device_driver.h */, + ); + path = hid; + sourceTree = ""; + }; + 05C5D58420E3DD0900654EE4 /* drivers_joypad */ = { + isa = PBXGroup; + children = ( + 05C5D59820E3DD0A00654EE4 /* hid_joypad.c */, + 05C5D58D20E3DD0900654EE4 /* mfi_joypad.m */, + 05C5D58820E3DD0900654EE4 /* null_joypad.c */, + 05C5D58720E3DD0900654EE4 /* sdl_joypad.c */, + ); + path = drivers_joypad; + sourceTree = ""; + }; 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = { isa = PBXGroup; children = ( @@ -1104,7 +1318,6 @@ buildActionMask = 2147483647; files = ( 05D7753720A567A700646447 /* griffin_glslang.cpp in Sources */, - 05A8C7B520DB75A800FF7857 /* PixelConverter.metal in Sources */, 05D7753520A567A400646447 /* griffin_cpp.cpp in Sources */, 509F0C9D1AA23AFC00619ECC /* griffin_objc.m in Sources */, 840222FC1A889EE2009AB261 /* griffin.c in Sources */, @@ -1146,6 +1359,7 @@ GCC_OPTIMIZATION_LEVEL = 0; INFOPLIST_FILE = "$(SRCROOT)/OSX/Info.plist"; INSTALL_PATH = "$(HOME)/Applications"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = YES; PRODUCT_BUNDLE_IDENTIFIER = "libretro.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = RetroArch;