diff --git a/gfx/common/metal/Context.h b/gfx/common/metal/Context.h index eeb687fc10..5b5b380cf8 100644 --- a/gfx/common/metal/Context.h +++ b/gfx/common/metal/Context.h @@ -49,6 +49,7 @@ typedef struct library:(id)l; - (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; - (bool)allocRange:(BufferRange *)range length:(NSUInteger)length; diff --git a/gfx/common/metal/Context.m b/gfx/common/metal/Context.m index 91d5ac4c27..5e3fae6012 100644 --- a/gfx/common/metal/Context.m +++ b/gfx/common/metal/Context.m @@ -170,9 +170,17 @@ image.height = 8; } - // TODO(sgc): mipmapping is not working BOOL mipmapped = filter == TEXTURE_FILTER_MIPMAP_LINEAR || filter == TEXTURE_FILTER_MIPMAP_NEAREST; + Texture *tex = [Texture new]; + tex.texture = [self newTexture:image mipmapped:mipmapped]; + tex.sampler = _samplers[filter]; + + return tex; +} + +- (id)newTexture:(struct texture_image)image mipmapped:(bool)mipmapped +{ MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm width:image.width height:image.height @@ -192,11 +200,7 @@ [bce endEncoding]; } - Texture *tex = [Texture new]; - tex.texture = t; - tex.sampler = _samplers[filter]; - - return tex; + return t; } - (id)nextDrawable diff --git a/gfx/common/metal_common.h b/gfx/common/metal_common.h index 1ba4d73c77..e14902649b 100644 --- a/gfx/common/metal_common.h +++ b/gfx/common/metal_common.h @@ -58,6 +58,17 @@ extern MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt); filter:(RTextureFilter)filter; @end + +@interface Overlay : NSObject +@property (nonatomic, readwrite) bool enabled; +@property (nonatomic, readwrite) bool fullscreen; + +- (bool)loadImages:(const struct texture_image *)images count:(NSUInteger)count; +- (void)updateVertexX:(float)x y:(float)y w:(float)w h:(float)h index:(NSUInteger)index; +- (void)updateTextureCoordsX:(float)x y:(float)y w:(float)w h:(float)h index:(NSUInteger)index; +- (void)updateAlpha:(float)alpha index:(NSUInteger)index; +@end + @interface MetalDriver : NSObject @property (nonatomic, readonly) video_viewport_t *viewport; @@ -65,6 +76,7 @@ extern MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt); @property (nonatomic, readonly) MetalMenu *menu; @property (nonatomic, readonly) FrameView *frameView; @property (nonatomic, readonly) MenuDisplay *display; +@property (nonatomic, readonly) Overlay *overlay; @property (nonatomic, readonly) Context *context; @property (nonatomic, readonly) Uniforms *viewportMVP; @property (nonatomic, readonly) Uniforms *viewportMVPNormalized; diff --git a/gfx/common/metal_common.m b/gfx/common/metal_common.m index 0a3b3a3836..0777a51bb7 100644 --- a/gfx/common/metal_common.m +++ b/gfx/common/metal_common.m @@ -45,10 +45,16 @@ - (instancetype)initWithContext:(Context *)context; @end +@interface Overlay() +- (instancetype)initWithContext:(Context *)context; +- (void)drawWithEncoder:(id)rce; +@end + @implementation MetalDriver { FrameView *_frameView; MetalMenu *_menu; + Overlay *_overlay; video_info_t _video; @@ -128,6 +134,9 @@ [_frameView setFilteringIndex:0 smooth:video->smooth]; } + // overlay view + _overlay = [[Overlay alloc] initWithContext:_context]; + font_driver_init_osd((__bridge void *)self, false, video->is_threaded, FONT_DRIVER_RENDER_METAL_API); } return self; @@ -440,6 +449,19 @@ font_driver_render_msg(video_info, NULL, video_info->stat_text, osd_params); } } + +#ifdef HAVE_OVERLAY + if (_overlay.enabled) + { + id rce = _context.rce; + [rce pushDebugGroup:@"overlay"]; + [rce setRenderPipelineState:[self getStockShader:VIDEO_SHADER_STOCK_BLEND blend:YES]]; + [rce setVertexBytes:&_uniforms length:sizeof(_uniforms) atIndex:BufferIndexUniforms]; + [rce setFragmentSamplerState:_samplerStateLinear atIndex:SamplerIndexDraw]; + [_overlay drawWithEncoder:rce]; + [rce popDebugGroup]; + } +#endif if (msg && *msg) { @@ -1462,6 +1484,115 @@ static vertex_t vertex_bytes[] = { @end +@implementation Overlay +{ + Context *_context; + NSMutableArray> *_images; + id _vert; + bool _vertDirty; +} + +- (instancetype)initWithContext:(Context *)context +{ + if (self = [super init]) + { + _context = context; + } + return self; +} + +- (bool)loadImages:(const struct texture_image *)images count:(NSUInteger)count +{ + [self _freeImages]; + + _images = [NSMutableArray arrayWithCapacity:count]; + + NSUInteger needed = sizeof(SpriteVertex) * count * 4; + if (!_vert || _vert.length < needed) + { + _vert = [_context.device newBufferWithLength:needed options:MTLResourceStorageModeManaged]; + } + + for (NSUInteger i = 0; i < count; i++) + { + _images[i] = [_context newTexture:images[i] mipmapped:NO]; + [self updateVertexX:0 y:0 w:1 h:1 index:i]; + [self updateTextureCoordsX:0 y:0 w:1 h:1 index:i]; + [self _updateColorRed:1.0 green:1.0 blue:1.0 alpha:1.0 index:i]; + } + + _vertDirty = YES; + + return YES; +} + +- (void)drawWithEncoder:(id)rce +{ + if (_vertDirty) + { + [_vert didModifyRange:NSMakeRange(0, _vert.length)]; + _vertDirty = NO; + } + + NSUInteger count = _images.count; + for (NSUInteger i = 0; i < count; ++i) + { + NSUInteger offset = sizeof(SpriteVertex) * 4 * i; + [rce setVertexBuffer:_vert offset:offset atIndex:BufferIndexPositions]; + [rce setFragmentTexture:_images[i] atIndex:TextureIndexColor]; + [rce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4]; + } +} + +- (SpriteVertex *)_getForIndex:(NSUInteger)index +{ + SpriteVertex *pv = (SpriteVertex *)_vert.contents; + return &pv[index * 4]; +} + +- (void)_updateColorRed:(float)r green:(float)g blue:(float)b alpha:(float)a index:(NSUInteger)index +{ + simd_float4 color = simd_make_float4(r, g, b, a); + SpriteVertex *pv = [self _getForIndex:index]; + pv[0].color = color; + pv[1].color = color; + pv[2].color = color; + pv[3].color = color; + _vertDirty = YES; +} + +- (void)updateAlpha:(float)alpha index:(NSUInteger)index +{ + [self _updateColorRed:1.0 green:1.0 blue:1.0 alpha:alpha index:index]; +} + +- (void)updateVertexX:(float)x y:(float)y w:(float)w h:(float)h index:(NSUInteger)index +{ + SpriteVertex *pv = [self _getForIndex:index]; + pv[0].position = simd_make_float2(x, y); + pv[1].position = simd_make_float2(x + w, y); + pv[2].position = simd_make_float2(x, y + h); + pv[3].position = simd_make_float2(x + w, y + h); + _vertDirty = YES; +} + +- (void)updateTextureCoordsX:(float)x y:(float)y w:(float)w h:(float)h index:(NSUInteger)index +{ + SpriteVertex *pv = [self _getForIndex:index]; + pv[0].texCoord = simd_make_float2(x, y); + pv[1].texCoord = simd_make_float2(x + w, y); + pv[2].texCoord = simd_make_float2(x, y + h); + pv[3].texCoord = simd_make_float2(x + w, y + h); + _vertDirty = YES; +} + +- (void)_freeImages +{ + _images = nil; +} + +@end + MTLPixelFormat glslang_format_to_metal(glslang_format fmt) { #undef FMT2 diff --git a/gfx/drivers/metal.m b/gfx/drivers/metal.m index 773848546e..fbfb3c48e4 100644 --- a/gfx/drivers/metal.m +++ b/gfx/drivers/metal.m @@ -92,12 +92,10 @@ static bool metal_focus(void *data) return apple_platform.hasFocus; } -static bool metal_suppress_screensaver(void *data, bool enable) +static bool metal_suppress_screensaver(void *data, bool disable) { - bool enabled = enable; - (void)data; - - return video_context_driver_suppress_screensaver(&enabled); + RARCH_LOG("[Metal]: suppress screen saver: %s\n", disable ? "YES" : "NO"); + return [apple_platform setDisableDisplaySleep:disable]; } static bool metal_set_shader(void *data, @@ -149,26 +147,6 @@ static bool metal_read_viewport(void *data, uint8_t *buffer, bool is_idle) return true; } -#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, -}; - -static void metal_get_overlay_interface(void *data, - const video_overlay_interface_t **iface) -{ - (void)data; - *iface = &metal_overlay_interface; -} - -#endif - static uintptr_t metal_load_texture(void *video_data, void *data, bool threaded, enum texture_filter_type filter_type) { @@ -337,6 +315,82 @@ static void metal_get_poke_interface(void *data, *iface = &metal_poke_interface; } +#ifdef HAVE_OVERLAY + +static void metal_overlay_enable(void *data, bool state) +{ + MetalDriver *md = (__bridge MetalDriver *)data; + if (!md) + return; + md.overlay.enabled = state; +} + +static bool metal_overlay_load(void *data, + const void *images, unsigned num_images) +{ + MetalDriver *md = (__bridge MetalDriver *)data; + if (!md) + return NO; + + return [md.overlay loadImages:(const struct texture_image *)images count:num_images]; +} + +static void metal_overlay_tex_geom(void *data, unsigned index, + float x, float y, float w, float h) +{ + MetalDriver *md = (__bridge MetalDriver *)data; + if (!md) + return; + + [md.overlay updateTextureCoordsX:x y:y w:w h:h index:index]; +} + +static void metal_overlay_vertex_geom(void *data, unsigned index, + float x, float y, float w, float h) +{ + MetalDriver *md = (__bridge MetalDriver *)data; + if (!md) + return; + + [md.overlay updateVertexX:x y:y w:w h:h index:index]; +} + +static void metal_overlay_full_screen(void *data, bool enable) +{ + MetalDriver *md = (__bridge MetalDriver *)data; + if (!md) + return; + + md.overlay.fullscreen = enable; +} + +static void metal_overlay_set_alpha(void *data, unsigned index, float mod) +{ + MetalDriver *md = (__bridge MetalDriver *)data; + if (!md) + return; + + [md.overlay updateAlpha:mod index:index]; +} + +static const video_overlay_interface_t metal_overlay_interface = { + .enable = metal_overlay_enable, + .load = metal_overlay_load, + .tex_geom = metal_overlay_tex_geom, + .vertex_geom = metal_overlay_vertex_geom, + .full_screen = metal_overlay_full_screen, + .set_alpha = metal_overlay_set_alpha, +}; + +static void metal_get_overlay_interface(void *data, + const video_overlay_interface_t **iface) +{ + (void)data; + *iface = &metal_overlay_interface; +} + +#endif + video_driver_t video_metal = { .init = metal_init, diff --git a/ui/drivers/cocoa/cocoa_common.h b/ui/drivers/cocoa/cocoa_common.h index 8e0cc1a0dd..a291b769b3 100644 --- a/ui/drivers/cocoa/cocoa_common.h +++ b/ui/drivers/cocoa/cocoa_common.h @@ -60,6 +60,10 @@ typedef enum apple_view_type { /*! @brief setCursorVisible specifies whether the cursor is visible */ - (void)setCursorVisible:(bool)v; +/*! @brief controls whether the screen saver should be disabled and + * the displays should not sleep. + */ +- (bool)setDisableDisplaySleep:(bool)disable; @end extern id apple_platform; diff --git a/ui/drivers/ui_cocoa.m b/ui/drivers/ui_cocoa.m index 24540d2e70..4070cfe873 100644 --- a/ui/drivers/ui_cocoa.m +++ b/ui/drivers/ui_cocoa.m @@ -53,6 +53,8 @@ id apple_platform; NSWindow* _window; apple_view_type_t _vt; NSView* _renderView; + id _sleepActivity; + } @property (nonatomic, retain) NSWindow IBOutlet* window; @@ -337,6 +339,26 @@ static char** waiting_argv; [NSCursor hide]; } +- (bool)setDisableDisplaySleep:(bool)disable +{ +#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_9 + if (disable && _sleepActivity == nil) + { + _sleepActivity = [NSProcessInfo.processInfo beginActivityWithOptions:NSActivityIdleDisplaySleepDisabled reason:@"disable screen saver"]; + } + else if (!disable && _sleepActivity != nil) + { + [NSProcessInfo.processInfo endActivity:_sleepActivity]; + _sleepActivity = nil; + } + return YES; +#else + return NO; +#endif + +} + + - (void) rarch_main { do