diff --git a/configuration.c b/configuration.c index 13da1997f1..953f4b4524 100644 --- a/configuration.c +++ b/configuration.c @@ -125,6 +125,7 @@ enum video_driver_enum { VIDEO_GL = 0, VIDEO_VULKAN, + VIDEO_METAL, VIDEO_DRM, VIDEO_XVIDEO, VIDEO_SDL, @@ -291,6 +292,8 @@ enum midi_driver_enum #if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES) || defined(__CELLOS_LV2__) static enum video_driver_enum VIDEO_DEFAULT_DRIVER = VIDEO_GL; +#elif defined(HAVE_METAL) +static enum video_driver_enum VIDEO_DEFAULT_DRIVER = VIDEO_METAL; #elif defined(GEKKO) static enum video_driver_enum VIDEO_DEFAULT_DRIVER = VIDEO_WII; #elif defined(WIIU) @@ -711,6 +714,8 @@ const char *config_get_default_video(void) return "gl"; case VIDEO_VULKAN: return "vulkan"; + case VIDEO_METAL: + return "metal"; case VIDEO_DRM: return "drm"; case VIDEO_WII: @@ -2357,6 +2362,7 @@ static bool check_shader_compatibility(enum file_path_enum enum_idx) settings_t *settings = config_get_ptr(); if (string_is_equal(settings->arrays.video_driver, "vulkan") || + string_is_equal(settings->arrays.video_driver, "metal") || string_is_equal(settings->arrays.video_driver, "d3d11") || string_is_equal(settings->arrays.video_driver, "d3d12") || string_is_equal(settings->arrays.video_driver, "gx2")) diff --git a/gfx/common/metal/Context.h b/gfx/common/metal/Context.h index 5b5b380cf8..497da8e44d 100644 --- a/gfx/common/metal/Context.h +++ b/gfx/common/metal/Context.h @@ -28,12 +28,14 @@ typedef struct @property (nonatomic, readonly) id device; @property (nonatomic, readonly) id library; @property (nonatomic, readwrite) MTLClearColor clearColor; +@property (nonatomic, readwrite) video_viewport_t *viewport; +@property (nonatomic, readonly) Uniforms *uniforms; /*! @brief Specifies whether rendering is synchronized with the display */ @property (nonatomic, readwrite) bool displaySyncEnabled; /*! @brief Returns the command buffer used for pre-render work, - * such as mip maps for applying filters + * such as mip maps and shader effects * */ @property (nonatomic, readonly) id blitCommandBuffer; @@ -51,6 +53,14 @@ typedef struct - (Texture *)newTexture:(struct texture_image)image filter:(enum texture_filter_type)filter; - (id)newTexture:(struct texture_image)image mipmapped:(bool)mipmapped; - (void)convertFormat:(RPixelFormat)fmt from:(id)src to:(id)dst; +- (id)getStockShader:(int)index blend:(bool)blend; + +/*! @brief resets the viewport for the main render encoder to the drawable size */ +- (void)resetRenderViewport; + +/*! @brief draws a quad at the specified position (normalized coordinates) using the main render encoder */ +- (void)drawQuadX:(float)x y:(float)y w:(float)w h:(float)h + r:(float)r g:(float)g b:(float)b a:(float)a; - (bool)allocRange:(BufferRange *)range length:(NSUInteger)length; diff --git a/gfx/common/metal/Context.m b/gfx/common/metal/Context.m index 59066fccf6..f610320c24 100644 --- a/gfx/common/metal/Context.m +++ b/gfx/common/metal/Context.m @@ -38,6 +38,7 @@ id _commandQueue; CAMetalLayer *_layer; id _drawable; + video_viewport_t _viewport; id _samplers[TEXTURE_FILTER_MIPMAP_NEAREST + 1]; Filter *_filters[RPixelFormatCount]; // convert to bgra8888 @@ -49,6 +50,10 @@ NSUInteger _currentChain; BufferChain *_chain[CHAIN_LENGTH]; MTLClearColor _clearColor; + + id _states[GFX_MAX_SHADERS][2]; + id _clearState; + Uniforms _uniforms; } - (instancetype)initWithDevice:(id)d @@ -64,6 +69,7 @@ _library = l; _commandQueue = [_device newCommandQueue]; _clearColor = MTLClearColorMake(0, 0, 0, 1); + _uniforms.projectionMatrix = matrix_proj_ortho(0, 1, 0, 1); { MTLSamplerDescriptor *sd = [MTLSamplerDescriptor new]; @@ -89,7 +95,10 @@ if (![self _initConversionFilters]) return nil; - if (![self _initMainState]) + if (![self _initClearState]) + return nil; + + if (![self _initMenuStates]) return nil; for (int i = 0; i < CHAIN_LENGTH; i++) @@ -100,21 +109,241 @@ return self; } +- (video_viewport_t *)viewport +{ + return &_viewport; +} + +- (void)setViewport:(video_viewport_t *)viewport +{ + _viewport = *viewport; + _uniforms.outputSize = simd_make_float2(_viewport.full_width, _viewport.full_height); +} + +- (Uniforms *)uniforms +{ + return &_uniforms; +} + - (void)setDisplaySyncEnabled:(bool)displaySyncEnabled { _layer.displaySyncEnabled = displaySyncEnabled; } +#pragma mark - shaders + +- (id)getStockShader:(int)index blend:(bool)blend +{ + assert(index > 0 && index < GFX_MAX_SHADERS); + + switch (index) + { + case VIDEO_SHADER_STOCK_BLEND: + case VIDEO_SHADER_MENU: + case VIDEO_SHADER_MENU_2: + case VIDEO_SHADER_MENU_3: + case VIDEO_SHADER_MENU_4: + case VIDEO_SHADER_MENU_5: + case VIDEO_SHADER_MENU_6: + break; + default: + index = VIDEO_SHADER_STOCK_BLEND; + break; + } + + return _states[index][blend ? 1 : 0]; +} + - (bool)displaySyncEnabled { return _layer.displaySyncEnabled; } -- (bool)_initMainState +- (MTLVertexDescriptor *)_spriteVertexDescriptor { + 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); + return vd; +} + +- (bool)_initClearState +{ + MTLVertexDescriptor *vd = [self _spriteVertexDescriptor]; + MTLRenderPipelineDescriptor *psd = [MTLRenderPipelineDescriptor new]; + psd.label = @"clear_state"; + + MTLRenderPipelineColorAttachmentDescriptor *ca = psd.colorAttachments[0]; + ca.pixelFormat = _layer.pixelFormat; + + psd.vertexDescriptor = vd; + psd.vertexFunction = [_library newFunctionWithName:@"stock_vertex"]; + psd.fragmentFunction = [_library newFunctionWithName:@"stock_fragment_color"]; + + NSError *err; + _clearState = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; + if (err != nil) + { + RARCH_ERR("[Metal]: error creating clear pipeline state %s\n", err.localizedDescription.UTF8String); + return NO; + } + return YES; } +- (bool)_initMenuStates +{ + MTLVertexDescriptor *vd = [self _spriteVertexDescriptor]; + MTLRenderPipelineDescriptor *psd = [MTLRenderPipelineDescriptor new]; + psd.label = @"stock"; + + MTLRenderPipelineColorAttachmentDescriptor *ca = psd.colorAttachments[0]; + ca.pixelFormat = _layer.pixelFormat; + ca.blendingEnabled = NO; + ca.sourceRGBBlendFactor = MTLBlendFactorSourceAlpha; + ca.destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha; + ca.sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha; + ca.destinationAlphaBlendFactor = 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; + } + + psd.label = @"stock_blend"; + 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; + } + + MTLFunctionConstantValues *vals; + + psd.label = @"snow_simple"; + ca.blendingEnabled = YES; + { + vals = [MTLFunctionConstantValues new]; + float values[3] = { + 1.25f, // baseScale + 0.50f, // density + 0.15f, // speed + }; + [vals setConstantValue:&values[0] type:MTLDataTypeFloat withName:@"snowBaseScale"]; + [vals setConstantValue:&values[1] type:MTLDataTypeFloat withName:@"snowDensity"]; + [vals setConstantValue:&values[2] type:MTLDataTypeFloat withName:@"snowSpeed"]; + } + psd.fragmentFunction = [_library newFunctionWithName:@"snow_fragment" constantValues:vals error:&err]; + _states[VIDEO_SHADER_MENU_3][1] = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; + if (err != nil) + { + RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String); + return NO; + } + + psd.label = @"snow"; + ca.blendingEnabled = YES; + { + vals = [MTLFunctionConstantValues new]; + float values[3] = { + 3.50f, // baseScale + 0.70f, // density + 0.25f, // speed + }; + [vals setConstantValue:&values[0] type:MTLDataTypeFloat withName:@"snowBaseScale"]; + [vals setConstantValue:&values[1] type:MTLDataTypeFloat withName:@"snowDensity"]; + [vals setConstantValue:&values[2] type:MTLDataTypeFloat withName:@"snowSpeed"]; + } + psd.fragmentFunction = [_library newFunctionWithName:@"snow_fragment" constantValues:vals error:&err]; + _states[VIDEO_SHADER_MENU_4][1] = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; + if (err != nil) + { + RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String); + return NO; + } + + psd.label = @"bokeh"; + ca.blendingEnabled = YES; + psd.fragmentFunction = [_library newFunctionWithName:@"bokeh_fragment"]; + _states[VIDEO_SHADER_MENU_5][1] = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; + if (err != nil) + { + RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String); + return NO; + } + + psd.label = @"snowflake"; + ca.blendingEnabled = YES; + psd.fragmentFunction = [_library newFunctionWithName:@"snowflake_fragment"]; + _states[VIDEO_SHADER_MENU_6][1] = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; + if (err != nil) + { + RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String); + return NO; + } + + psd.label = @"ribbon"; + ca.blendingEnabled = NO; + psd.vertexFunction = [_library newFunctionWithName:@"ribbon_vertex"]; + psd.fragmentFunction = [_library newFunctionWithName:@"ribbon_fragment"]; + _states[VIDEO_SHADER_MENU][0] = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; + if (err != nil) + { + RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String); + return NO; + } + + psd.label = @"ribbon_blend"; + ca.blendingEnabled = YES; + ca.sourceRGBBlendFactor = MTLBlendFactorOne; + ca.destinationRGBBlendFactor = MTLBlendFactorOne; + _states[VIDEO_SHADER_MENU][1] = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; + if (err != nil) + { + RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String); + return NO; + } + + psd.label = @"ribbon_simple"; + ca.blendingEnabled = NO; + psd.vertexFunction = [_library newFunctionWithName:@"ribbon_simple_vertex"]; + psd.fragmentFunction = [_library newFunctionWithName:@"ribbon_simple_fragment"]; + _states[VIDEO_SHADER_MENU_2][0] = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; + if (err != nil) + { + RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String); + return NO; + } + + psd.label = @"ribbon_simple_blend"; + ca.blendingEnabled = YES; + ca.sourceRGBBlendFactor = MTLBlendFactorOne; + ca.destinationRGBBlendFactor = MTLBlendFactorOne; + _states[VIDEO_SHADER_MENU_2][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; +} + + - (bool)_initConversionFilters { NSError *err = nil; @@ -255,6 +484,41 @@ return _rce; } +- (void)resetRenderViewport +{ + MTLViewport vp = { + .originX = 0, + .originY = 0, + .width = _viewport.full_width, + .height = _viewport.full_height, + .znear = 0, + .zfar = 1, + }; + [self.rce setViewport:vp]; +} + +- (void)drawQuadX:(float)x y:(float)y w:(float)w h:(float)h + r:(float)r g:(float)g b:(float)b a:(float)a +{ + SpriteVertex v[4]; + v[0].position = simd_make_float2(x, y); + v[1].position = simd_make_float2(x + w, y); + v[2].position = simd_make_float2(x, y + h); + v[3].position = simd_make_float2(x + w, y + h); + + simd_float4 color = simd_make_float4(r, g, b, a); + v[0].color = color; + v[1].color = color; + v[2].color = color; + v[3].color = color; + + id rce = self.rce; + [rce setRenderPipelineState:_clearState]; + [rce setVertexBytes:&v length:sizeof(v) atIndex:BufferIndexPositions]; + [rce setVertexBytes:&_uniforms length:sizeof(_uniforms) atIndex:BufferIndexUniforms]; + [rce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4]; +} + - (void)end { assert(_commandBuffer != nil); @@ -263,7 +527,7 @@ if (_blitCommandBuffer) { - // pending blits for mipmaps + // pending blits for mipmaps or render passes for slang shaders [_blitCommandBuffer commit]; [_blitCommandBuffer waitUntilCompleted]; _blitCommandBuffer = nil; diff --git a/gfx/common/metal/MenuDisplay.h b/gfx/common/metal/MenuDisplay.h index f467029d6e..12d5bbd6fd 100644 --- a/gfx/common/metal/MenuDisplay.h +++ b/gfx/common/metal/MenuDisplay.h @@ -3,17 +3,15 @@ // #import -#import "ShaderTypes.h" @class Context; -@class MetalDriver; @interface MenuDisplay : NSObject @property (nonatomic, readwrite) BOOL blend; @property (nonatomic, readwrite) MTLClearColor clearColor; -- (instancetype)initWithDriver:(MetalDriver *)driver; +- (instancetype)initWithContext:(Context *)context; - (void)drawPipeline:(menu_display_ctx_draw_t *)draw video:(video_frame_info_t *)video; - (void)draw:(menu_display_ctx_draw_t *)draw video:(video_frame_info_t *)video; @@ -23,5 +21,4 @@ + (const float *)defaultTexCoords; + (const float *)defaultColor; - @end diff --git a/gfx/common/metal/MenuDisplay.m b/gfx/common/metal/MenuDisplay.m index 7581631fb7..69a299c3c7 100644 --- a/gfx/common/metal/MenuDisplay.m +++ b/gfx/common/metal/MenuDisplay.m @@ -12,19 +12,17 @@ @implementation MenuDisplay { - __weak MetalDriver *_driver; Context *_context; MTLClearColor _clearColor; bool _clearNextRender; Uniforms _uniforms; } -- (instancetype)initWithDriver:(MetalDriver *)driver +- (instancetype)initWithContext:(Context *)context { if (self = [super init]) { - _driver = driver; - _context = driver.context; + _context = context; _clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 1.0); _uniforms.projectionMatrix = matrix_proj_ortho(0, 1, 0, 1); } @@ -45,10 +43,10 @@ + (const float *)defaultTexCoords { static float dummy[] = { - 0.0f, 0.0f, - 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 0.0f, }; return &dummy[0]; } @@ -97,7 +95,7 @@ draw->y = 0; draw->matrix_data = NULL; - _uniforms.outputSize = simd_make_float2(_driver.viewport->full_width, _driver.viewport->full_height); + _uniforms.outputSize = simd_make_float2(_context.viewport->full_width, _context.viewport->full_height); draw->pipeline.backend_data = &_uniforms; draw->pipeline.backend_data_size = sizeof(_uniforms); @@ -147,7 +145,7 @@ SpriteVertex *pv = (SpriteVertex *)range.data; for (unsigned i = 0; i < draw->coords->vertices; i++, pv++) { - pv->position = simd_make_float2(vertex[0], 1.0f - vertex[1]); + pv->position = simd_make_float2(vertex[0], vertex[1]); vertex += 2; pv->texCoord = simd_make_float2(tex_coord[0], tex_coord[1]); @@ -158,9 +156,24 @@ } id rce = _context.rce; + if (_clearNextRender) + { + [_context resetRenderViewport]; + [_context drawQuadX:0 + y:0 + w:1 + h:1 + r:(float)_clearColor.red + g:(float)_clearColor.green + b:(float)_clearColor.blue + a:(float)_clearColor.alpha + ]; + _clearNextRender = NO; + } + MTLViewport vp = { .originX = draw->x, - .originY = _driver.viewport->full_height - draw->y - draw->height, + .originY = _context.viewport->full_height - draw->y - draw->height, .width = draw->width, .height = draw->height, .znear = 0, @@ -177,7 +190,7 @@ case VIDEO_SHADER_MENU_4: case VIDEO_SHADER_MENU_5: case VIDEO_SHADER_MENU_6: - [rce setRenderPipelineState:[_driver getStockShader:draw->pipeline.id blend:_blend]]; + [rce setRenderPipelineState:[_context getStockShader:draw->pipeline.id blend:_blend]]; [rce setVertexBytes:draw->pipeline.backend_data length:draw->pipeline.backend_data_size atIndex:BufferIndexUniforms]; [rce setVertexBuffer:range.buffer offset:range.offset atIndex:BufferIndexPositions]; [rce setFragmentBytes:draw->pipeline.backend_data length:draw->pipeline.backend_data_size atIndex:BufferIndexUniforms]; @@ -192,13 +205,7 @@ if (tex == nil) return; - if (_clearNextRender) - { - // TODO(sgc): draw quad to clear - _clearNextRender = NO; - } - - [rce setRenderPipelineState:[_driver getStockShader:VIDEO_SHADER_STOCK_BLEND blend:_blend]]; + [rce setRenderPipelineState:[_context getStockShader:VIDEO_SHADER_STOCK_BLEND blend:_blend]]; Uniforms uniforms = { .projectionMatrix = draw->matrix_data ? make_matrix_float4x4((const float *)draw->matrix_data) diff --git a/gfx/common/metal/RendererCommon.h b/gfx/common/metal/RendererCommon.h index 320a9cc8b0..977cf7b607 100644 --- a/gfx/common/metal/RendererCommon.h +++ b/gfx/common/metal/RendererCommon.h @@ -10,6 +10,7 @@ #define RendererCommon_h #import +#import "ShaderTypes.h" // TODO(sgc): implement triple buffering /*! @brief maximum inflight frames */ diff --git a/gfx/common/metal/RendererCommon.m b/gfx/common/metal/RendererCommon.m index dce930c162..c3be58dc86 100644 --- a/gfx/common/metal/RendererCommon.m +++ b/gfx/common/metal/RendererCommon.m @@ -55,11 +55,11 @@ NSString *NSStringFromRPixelFormat(RPixelFormat format) matrix_float4x4 make_matrix_float4x4(const float *v) { simd_float4 P = simd_make_float4(v[0], v[1], v[2], v[3]); - v+=4; + v += 4; simd_float4 Q = simd_make_float4(v[0], v[1], v[2], v[3]); - v+=4; + v += 4; simd_float4 R = simd_make_float4(v[0], v[1], v[2], v[3]); - v+=4; + v += 4; simd_float4 S = simd_make_float4(v[0], v[1], v[2], v[3]); matrix_float4x4 mat = {P, Q, R, S}; diff --git a/gfx/common/metal/Shaders.metal b/gfx/common/metal/Shaders.metal index 619442f50b..c5449b2989 100644 --- a/gfx/common/metal/Shaders.metal +++ b/gfx/common/metal/Shaders.metal @@ -74,6 +74,11 @@ fragment float4 stock_fragment(FontFragmentIn in [[ stage_in ]], return colorSample * in.color; } +fragment half4 stock_fragment_color(FontFragmentIn in [[ stage_in ]]) +{ + return half4(in.color); +} + #pragma mark - filter kernels kernel void convert_bgra4444_to_bgra8888(device uint16_t * in [[ buffer(0) ]], diff --git a/gfx/common/metal/TexturedView.m b/gfx/common/metal/TexturedView.m index 22ff31cf31..f89346a654 100644 --- a/gfx/common/metal/TexturedView.m +++ b/gfx/common/metal/TexturedView.m @@ -7,8 +7,6 @@ #import "View.h" #import "Filter.h" -#import "ShaderTypes.h" - @implementation TexturedView { Context *_context; diff --git a/gfx/common/metal_common.h b/gfx/common/metal_common.h index e14902649b..c7271cee01 100644 --- a/gfx/common/metal_common.h +++ b/gfx/common/metal_common.h @@ -79,7 +79,6 @@ extern MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt); @property (nonatomic, readonly) Overlay *overlay; @property (nonatomic, readonly) Context *context; @property (nonatomic, readonly) Uniforms *viewportMVP; -@property (nonatomic, readonly) Uniforms *viewportMVPNormalized; - (instancetype)initWithVideo:(const video_info_t *)video input:(const input_driver_t **)input @@ -94,8 +93,6 @@ extern MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt); msg:(const char *)msg info:(video_frame_info_t *)video_info; -- (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 0777a51bb7..efdb0853d2 100644 --- a/gfx/common/metal_common.m +++ b/gfx/common/metal_common.m @@ -71,13 +71,9 @@ id _samplerStateLinear; id _samplerStateNearest; - // - id _states[GFX_MAX_SHADERS][2]; - // other state Uniforms _uniforms; Uniforms _viewportMVP; - Uniforms _viewportMVPNormalized; } - (instancetype)initWithVideo:(const video_info_t *)video @@ -97,13 +93,9 @@ return nil; } - if (![self _initStates]) - { - return nil; - } - _video = *video; _viewport = (video_viewport_t *)calloc(1, sizeof(video_viewport_t)); + _viewportMVP.projectionMatrix = matrix_proj_ortho(0, 1, 0, 1); _keepAspect = _video.force_aspect; @@ -118,7 +110,7 @@ *inputData = NULL; // menu display - _display = [[MenuDisplay alloc] initWithDriver:self]; + _display = [[MenuDisplay alloc] initWithContext:_context]; // menu view _menu = [[MetalMenu alloc] initWithContext:_context]; @@ -214,162 +206,6 @@ 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"; - - MTLRenderPipelineColorAttachmentDescriptor *ca = psd.colorAttachments[0]; - ca.pixelFormat = _layer.pixelFormat; - ca.blendingEnabled = NO; - ca.sourceRGBBlendFactor = MTLBlendFactorSourceAlpha; - ca.destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha; - ca.sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha; - ca.destinationAlphaBlendFactor = 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; - } - - psd.label = @"stock_blend"; - 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; - } - - MTLFunctionConstantValues *vals; - - psd.label = @"snow_simple"; - ca.blendingEnabled = YES; - { - vals = [MTLFunctionConstantValues new]; - float values[3] = { - 1.25f, // baseScale - 0.50f, // density - 0.15f, // speed - }; - [vals setConstantValue:&values[0] type:MTLDataTypeFloat withName:@"snowBaseScale"]; - [vals setConstantValue:&values[1] type:MTLDataTypeFloat withName:@"snowDensity"]; - [vals setConstantValue:&values[2] type:MTLDataTypeFloat withName:@"snowSpeed"]; - } - psd.fragmentFunction = [_library newFunctionWithName:@"snow_fragment" constantValues:vals error:&err]; - _states[VIDEO_SHADER_MENU_3][1] = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; - if (err != nil) - { - RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String); - return NO; - } - - psd.label = @"snow"; - ca.blendingEnabled = YES; - { - vals = [MTLFunctionConstantValues new]; - float values[3] = { - 3.50f, // baseScale - 0.70f, // density - 0.25f, // speed - }; - [vals setConstantValue:&values[0] type:MTLDataTypeFloat withName:@"snowBaseScale"]; - [vals setConstantValue:&values[1] type:MTLDataTypeFloat withName:@"snowDensity"]; - [vals setConstantValue:&values[2] type:MTLDataTypeFloat withName:@"snowSpeed"]; - } - psd.fragmentFunction = [_library newFunctionWithName:@"snow_fragment" constantValues:vals error:&err]; - _states[VIDEO_SHADER_MENU_4][1] = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; - if (err != nil) - { - RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String); - return NO; - } - - psd.label = @"bokeh"; - ca.blendingEnabled = YES; - psd.fragmentFunction = [_library newFunctionWithName:@"bokeh_fragment"]; - _states[VIDEO_SHADER_MENU_5][1] = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; - if (err != nil) - { - RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String); - return NO; - } - - psd.label = @"snowflake"; - ca.blendingEnabled = YES; - psd.fragmentFunction = [_library newFunctionWithName:@"snowflake_fragment"]; - _states[VIDEO_SHADER_MENU_6][1] = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; - if (err != nil) - { - RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String); - return NO; - } - - psd.label = @"ribbon"; - ca.blendingEnabled = NO; - psd.vertexFunction = [_library newFunctionWithName:@"ribbon_vertex"]; - psd.fragmentFunction = [_library newFunctionWithName:@"ribbon_fragment"]; - _states[VIDEO_SHADER_MENU][0] = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; - if (err != nil) - { - RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String); - return NO; - } - - psd.label = @"ribbon_blend"; - ca.blendingEnabled = YES; - ca.sourceRGBBlendFactor = MTLBlendFactorOne; - ca.destinationRGBBlendFactor = MTLBlendFactorOne; - _states[VIDEO_SHADER_MENU][1] = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; - if (err != nil) - { - RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String); - return NO; - } - - psd.label = @"ribbon_simple"; - ca.blendingEnabled = NO; - psd.vertexFunction = [_library newFunctionWithName:@"ribbon_simple_vertex"]; - psd.fragmentFunction = [_library newFunctionWithName:@"ribbon_simple_fragment"]; - _states[VIDEO_SHADER_MENU_2][0] = [_device newRenderPipelineStateWithDescriptor:psd error:&err]; - if (err != nil) - { - RARCH_ERR("[Metal]: error creating pipeline state %s\n", err.localizedDescription.UTF8String); - return NO; - } - - psd.label = @"ribbon_simple_blend"; - ca.blendingEnabled = YES; - ca.sourceRGBBlendFactor = MTLBlendFactorOne; - ca.destinationRGBBlendFactor = MTLBlendFactorOne; - _states[VIDEO_SHADER_MENU_2][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); @@ -383,36 +219,9 @@ _layer.drawableSize = size; video_driver_update_viewport(_viewport, NO, _keepAspect); + _context.viewport = _viewport; + _viewportMVP.outputSize = simd_make_float2(_viewport->full_width, _viewport->full_height); - _viewportMVP.projectionMatrix = matrix_proj_ortho(0, _viewport->full_width, _viewport->full_height, 0); - _viewportMVP.projectionMatrix = matrix_proj_ortho(0, _viewport->full_width, 0, _viewport->full_height); - - _viewportMVPNormalized.outputSize = simd_make_float2(_viewport->full_width, _viewport->full_height); - _viewportMVPNormalized.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); - - switch (index) - { - case VIDEO_SHADER_STOCK_BLEND: - case VIDEO_SHADER_MENU: - case VIDEO_SHADER_MENU_2: - case VIDEO_SHADER_MENU_3: - case VIDEO_SHADER_MENU_4: - case VIDEO_SHADER_MENU_5: - case VIDEO_SHADER_MENU_6: - break; - default: - index = VIDEO_SHADER_STOCK_BLEND; - break; - } - - return _states[index][blend ? 1 : 0]; } #pragma mark - video @@ -455,7 +264,7 @@ { id rce = _context.rce; [rce pushDebugGroup:@"overlay"]; - [rce setRenderPipelineState:[self getStockShader:VIDEO_SHADER_STOCK_BLEND blend:YES]]; + [rce setRenderPipelineState:[_context getStockShader:VIDEO_SHADER_STOCK_BLEND blend:YES]]; [rce setVertexBytes:&_uniforms length:sizeof(_uniforms) atIndex:BufferIndexUniforms]; [rce setFragmentSamplerState:_samplerStateLinear atIndex:SamplerIndexDraw]; [_overlay drawWithEncoder:rce]; @@ -464,9 +273,7 @@ #endif if (msg && *msg) - { - font_driver_render_msg(video_info, NULL, msg, NULL); - } + [self _renderMessage:msg info:video_info]; [self _endFrame]; } @@ -474,6 +281,43 @@ return YES; } +- (void)_renderMessage:(const char *)msg + info:(video_frame_info_t *)video_info +{ + settings_t *settings = config_get_ptr(); + if (settings && settings->bools.video_msg_bgcolor_enable) + { + int msg_width = + font_driver_get_message_width(NULL, msg, (unsigned)strlen(msg), 1.0f); + + float x = video_info->font_msg_pos_x; + float y = 1.0f - video_info->font_msg_pos_y; + float width = msg_width / (float)_viewport->full_width; + float height = + settings->floats.video_font_size / (float)_viewport->full_height; + + y -= height; + + + float x2 = 0.005f; /* extend background around text */ + float y2 = 0.005f; + + x -= x2; + y -= y2; + width += x2; + height += y2; + + float r = settings->uints.video_msg_bgcolor_red / 255.0f; + float g = settings->uints.video_msg_bgcolor_green / 255.0f; + float b = settings->uints.video_msg_bgcolor_blue / 255.0f; + float a = settings->floats.video_msg_bgcolor_opacity; + [_context resetRenderViewport]; + [_context drawQuadX:x y:y w:width h:height r:r g:g b:b a:a]; + } + + font_driver_render_msg(video_info, NULL, msg, NULL); +} + - (void)_beginFrame { video_driver_update_viewport(_viewport, NO, _keepAspect); @@ -555,11 +399,6 @@ return &_viewportMVP; } -- (Uniforms *)viewportMVPNormalized -{ - return &_viewportMVPNormalized; -} - #pragma mark - MTKViewDelegate - (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size diff --git a/gfx/drivers/metal.m b/gfx/drivers/metal.m index ef99b59030..d788d09bfa 100644 --- a/gfx/drivers/metal.m +++ b/gfx/drivers/metal.m @@ -196,14 +196,6 @@ static void metal_set_video_mode(void *data, RARCH_LOG("[Metal]: set_video_mode res=%dx%d fullscreen=%s\n", width, height, fullscreen ? "YES" : "NO"); - MetalDriver *md = (__bridge MetalDriver *)data; - gfx_ctx_mode_t mode = { - .width = width, - .height = height, - .fullscreen = fullscreen, - }; - - //[md setVideoMode:mode]; } static float metal_get_refresh_rate(void *data) diff --git a/gfx/drivers_font/metal_raster_font.m b/gfx/drivers_font/metal_raster_font.m index 7ebf92f276..c5c7fa94bd 100644 --- a/gfx/drivers_font/metal_raster_font.m +++ b/gfx/drivers_font/metal_raster_font.m @@ -323,17 +323,8 @@ static INLINE void write_quad6(SpriteVertex *pv, id rce = _context.rce; [rce pushDebugGroup:@"render fonts"]; - - MTLViewport vp = { - .originX = 0, - .originY = 0, - .width = _driver.viewport->full_width, - .height = _driver.viewport->full_height, - .znear = 0, - .zfar = 1, - }; - [rce setViewport:vp]; - + + [_context resetRenderViewport]; [rce setRenderPipelineState:_state]; [rce setVertexBytes:&_uniforms length:sizeof(Uniforms) atIndex:BufferIndexUniforms]; [rce setVertexBuffer:_vert offset:start atIndex:BufferIndexPositions]; diff --git a/griffin/griffin.c b/griffin/griffin.c index d2700ce1bf..77584d9839 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -1235,7 +1235,7 @@ MENU #include "../menu/drivers/rgui.c" #endif -#if defined(HAVE_OPENGL) || defined(HAVE_VITA2D) || defined(_3DS) || defined(_MSC_VER) || defined(__wiiu__) +#if defined(HAVE_OPENGL) || defined(HAVE_VITA2D) || defined(_3DS) || defined(_MSC_VER) || defined(__wiiu__) || defined(HAVE_METAL) #ifdef HAVE_XMB #include "../menu/drivers/xmb.c" #endif diff --git a/libretro-common/file/file_path.c b/libretro-common/file/file_path.c index 2d7b2e23ec..1b4bcbb2b2 100644 --- a/libretro-common/file/file_path.c +++ b/libretro-common/file/file_path.c @@ -921,7 +921,12 @@ void fill_pathname_join_noext(char *out_path, void fill_pathname_join_delim(char *out_path, const char *dir, const char *path, const char delim, size_t size) { - size_t copied = strlcpy(out_path, dir, size); + size_t copied; + // behavior of strlcpy is undefined if dst and src overlap + if (out_path == dir) + copied = strlen(dir); + else + copied = strlcpy(out_path, dir, size); out_path[copied] = delim; out_path[copied+1] = '\0'; diff --git a/menu/drivers_display/menu_display_metal.m b/menu/drivers_display/menu_display_metal.m index beb57553ee..7ff5a5bfa7 100644 --- a/menu/drivers_display/menu_display_metal.m +++ b/menu/drivers_display/menu_display_metal.m @@ -35,7 +35,7 @@ static void *menu_display_metal_get_default_mvp(video_frame_info_t *video_info) if (!md) return NULL; - return (void *)&md.viewportMVPNormalized->projectionMatrix; + return (void *)&md.viewportMVP->projectionMatrix; } static void menu_display_metal_blend_begin(video_frame_info_t *video_info) diff --git a/pkg/apple/IntelliJ.xml b/pkg/apple/Project.xml similarity index 92% rename from pkg/apple/IntelliJ.xml rename to pkg/apple/Project.xml index 60c16db2de..54b041b724 100644 --- a/pkg/apple/IntelliJ.xml +++ b/pkg/apple/Project.xml @@ -4,6 +4,7 @@