From 5d2a75d78264030c3d8b183ccf974c120f160cf1 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Sun, 21 Mar 2021 18:06:00 +0100 Subject: [PATCH] (Apple/Metal) Combine various Metal files into one --- Makefile.common | 16 +- gfx/common/metal/Context.h | 90 --- gfx/common/metal/Filter.h | 26 - gfx/common/metal/Filter.m | 92 --- gfx/common/metal/MenuDisplay.h | 28 - gfx/common/metal/MenuDisplay.m | 240 ------- gfx/common/metal/RendererCommon.h | 61 -- gfx/common/metal/RendererCommon.m | 104 --- gfx/common/metal/Shaders.metal | 28 +- gfx/common/metal/TexturedView.h | 23 - gfx/common/metal/TexturedView.m | 149 ----- gfx/common/metal/View.h | 28 - gfx/common/metal/View.m | 36 - gfx/common/metal/menu_pipeline.metal | 23 +- gfx/common/metal/metal_common.h | 232 ++++++- .../metal/{Context.m => metal_renderer.m} | 618 +++++++++++++++++- .../{ShaderTypes.h => metal_shader_types.h} | 35 +- gfx/common/metal_common.m | 1 - griffin/griffin_objc.m | 7 +- .../RetroArch_Metal.xcodeproj/project.pbxproj | 30 +- 20 files changed, 896 insertions(+), 971 deletions(-) delete mode 100644 gfx/common/metal/Context.h delete mode 100644 gfx/common/metal/Filter.h delete mode 100644 gfx/common/metal/Filter.m delete mode 100644 gfx/common/metal/MenuDisplay.h delete mode 100644 gfx/common/metal/MenuDisplay.m delete mode 100644 gfx/common/metal/RendererCommon.h delete mode 100644 gfx/common/metal/RendererCommon.m delete mode 100644 gfx/common/metal/TexturedView.h delete mode 100644 gfx/common/metal/TexturedView.m delete mode 100644 gfx/common/metal/View.h delete mode 100644 gfx/common/metal/View.m rename gfx/common/metal/{Context.m => metal_renderer.m} (58%) rename gfx/common/metal/{ShaderTypes.h => metal_shader_types.h} (63%) diff --git a/Makefile.common b/Makefile.common index a18e5838b8..8ff7e1c777 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1524,16 +1524,12 @@ ifeq ($(HAVE_METAL), 1) LIBS += -framework Metal -framework MetalKit # Metal code relies on ARC (Automatic Reference Counting), enable it DEF_FLAGS += -fobjc-arc - OBJ += gfx/common/metal/Context.o \ - gfx/common/metal/Filter.o \ - gfx/common/metal/RendererCommon.o \ - gfx/common/metal/View.o \ - gfx/common/metal/TexturedView.o \ - gfx/common/metal/MenuDisplay.o \ - gfx/common/metal_common.o \ - gfx/drivers/metal.o \ - gfx/drivers_font/metal_raster_font.o \ - gfx/drivers_display/gfx_display_metal.o + OBJ += \ + gfx/common/metal/metal_renderer.o \ + gfx/common/metal_common.o \ + gfx/drivers/metal.o \ + gfx/drivers_font/metal_raster_font.o \ + gfx/drivers_display/gfx_display_metal.o endif ifeq ($(HAVE_EGL), 1) diff --git a/gfx/common/metal/Context.h b/gfx/common/metal/Context.h deleted file mode 100644 index 0a6e9b2148..0000000000 --- a/gfx/common/metal/Context.h +++ /dev/null @@ -1,90 +0,0 @@ -// -// Context.h -// MetalRenderer -// -// Created by Stuart Carnie on 6/9/18. -// Copyright © 2018 Stuart Carnie. All rights reserved. -// - -#import -#import -#import -#import "RendererCommon.h" - -#include "../../../retroarch.h" - -@interface Texture : NSObject -@property (nonatomic, readonly) id texture; -@property (nonatomic, readonly) id sampler; -@end - -typedef struct -{ - void *data; - NSUInteger offset; - __unsafe_unretained id buffer; -} BufferRange; - -typedef NS_ENUM(NSUInteger, ViewportResetMode) { - kFullscreenViewport, - kVideoViewport -}; - -/*! @brief Context contains the render state used by various components */ -@interface Context : NSObject - -@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 captureEnabled allows previous frames to be read */ -@property (nonatomic, readwrite) bool captureEnabled; - -/*! @brief Returns the command buffer used for pre-render work, - * such as mip maps and shader effects - * */ -@property (nonatomic, readonly) id blitCommandBuffer; - -/*! @brief Returns the command buffer for the current frame */ -@property (nonatomic, readonly) id commandBuffer; -@property (nonatomic, readonly) id nextDrawable; - -/*! @brief Main render encoder to back buffer */ -@property (nonatomic, readonly) id rce; - -- (instancetype)initWithDevice:(id)d - layer:(CAMetalLayer *)layer - 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; -- (id)getStockShader:(int)index blend:(bool)blend; - -/*! @brief resets the viewport for the main render encoder to \a mode */ -- (void)resetRenderViewport:(ViewportResetMode)mode; - -/*! @brief resets the scissor rect for the main render encoder to the drawable size */ -- (void)resetScissorRect; - -/*! @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; - -/*! @brief begin marks the beginning of a frame */ -- (void)begin; - -/*! @brief end commits the command buffer */ -- (void)end; - -- (void)setRotation:(unsigned)rotation; -- (bool)readBackBuffer:(uint8_t *)buffer; - -@end diff --git a/gfx/common/metal/Filter.h b/gfx/common/metal/Filter.h deleted file mode 100644 index 82094ad67f..0000000000 --- a/gfx/common/metal/Filter.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// Filter.h -// MetalByExampleObjC -// -// Created by Stuart Carnie on 5/15/18. -// Copyright © 2018 Stuart Carnie. All rights reserved. -// - -#import -#import - -@protocol FilterDelegate -- (void)configure:(id)encoder; -@end - -@interface Filter : NSObject - -@property (nonatomic, readwrite) id delegate; -@property (nonatomic, readonly) id sampler; - -- (void)apply:(id)cb in:(id)tin out:(id)tout; -- (void)apply:(id)cb inBuf:(id)tin outTex:(id)tout; - -+ (instancetype)newFilterWithFunctionName:(NSString *)name device:(id)device library:(id)library error:(NSError **)error; - -@end diff --git a/gfx/common/metal/Filter.m b/gfx/common/metal/Filter.m deleted file mode 100644 index aca78b2eea..0000000000 --- a/gfx/common/metal/Filter.m +++ /dev/null @@ -1,92 +0,0 @@ -// -// Filter.m -// MetalByExampleObjC -// -// Created by Stuart Carnie on 5/15/18. -// Copyright © 2018 Stuart Carnie. All rights reserved. -// - -#import "Filter.h" -#import - -@interface Filter() -- (instancetype)initWithKernel:(id)kernel sampler:(id)sampler; -@end - -@implementation Filter -{ - id _kernel; -} - -+ (instancetype)newFilterWithFunctionName:(NSString *)name device:(id)device library:(id)library error:(NSError **)error -{ - id function = [library newFunctionWithName:name]; - id kernel = [device newComputePipelineStateWithFunction:function error:error]; - if (*error != nil) - { - return nil; - } - - MTLSamplerDescriptor *sd = [MTLSamplerDescriptor new]; - sd.minFilter = MTLSamplerMinMagFilterNearest; - sd.magFilter = MTLSamplerMinMagFilterNearest; - sd.sAddressMode = MTLSamplerAddressModeClampToEdge; - sd.tAddressMode = MTLSamplerAddressModeClampToEdge; - sd.mipFilter = MTLSamplerMipFilterNotMipmapped; - id sampler = [device newSamplerStateWithDescriptor:sd]; - - return [[Filter alloc] initWithKernel:kernel sampler:sampler]; -} - -- (instancetype)initWithKernel:(id)kernel sampler:(id)sampler -{ - if (self = [super init]) - { - _kernel = kernel; - _sampler = sampler; - } - return self; -} - -- (void)apply:(id)cb in:(id)tin out:(id)tout -{ - id ce = [cb computeCommandEncoder]; - ce.label = @"filter kernel"; - - [ce setComputePipelineState:_kernel]; - - [ce setTexture:tin atIndex:0]; - [ce setTexture:tout atIndex:1]; - - [self.delegate configure:ce]; - - MTLSize size = MTLSizeMake(16, 16, 1); - MTLSize count = MTLSizeMake((tin.width + size.width + 1) / size.width, (tin.height + size.height + 1) / size.height, - 1); - - [ce dispatchThreadgroups:count threadsPerThreadgroup:size]; - - [ce endEncoding]; -} - -- (void)apply:(id)cb inBuf:(id)tin outTex:(id)tout -{ - id ce = [cb computeCommandEncoder]; - ce.label = @"filter kernel"; - - [ce setComputePipelineState:_kernel]; - - [ce setBuffer:tin offset:0 atIndex:0]; - [ce setTexture:tout atIndex:0]; - - [self.delegate configure:ce]; - - MTLSize size = MTLSizeMake(32, 1, 1); - MTLSize count = MTLSizeMake((tin.length + 00) / 32, 1, 1); - - [ce dispatchThreadgroups:count threadsPerThreadgroup:size]; - - [ce endEncoding]; -} - -@end diff --git a/gfx/common/metal/MenuDisplay.h b/gfx/common/metal/MenuDisplay.h deleted file mode 100644 index c2a86d41f6..0000000000 --- a/gfx/common/metal/MenuDisplay.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Created by Stuart Carnie on 6/24/18. - */ - -#import - -#include "../../gfx_display.h" - -@class Context; - -@interface MenuDisplay : NSObject - -@property (nonatomic, readwrite) BOOL blend; -@property (nonatomic, readwrite) MTLClearColor clearColor; - -- (instancetype)initWithContext:(Context *)context; -- (void)drawPipeline:(gfx_display_ctx_draw_t *)draw; -- (void)draw:(gfx_display_ctx_draw_t *)draw; -- (void)setScissorRect:(MTLScissorRect)rect; -- (void)clearScissorRect; - -#pragma mark - static methods - -+ (const float *)defaultVertices; -+ (const float *)defaultTexCoords; -+ (const float *)defaultColor; - -@end diff --git a/gfx/common/metal/MenuDisplay.m b/gfx/common/metal/MenuDisplay.m deleted file mode 100644 index 29d2b4e479..0000000000 --- a/gfx/common/metal/MenuDisplay.m +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Created by Stuart Carnie on 6/24/18. - */ - -#import - -#import "Context.h" -#import "MenuDisplay.h" -#import "ShaderTypes.h" - -#include "../../../menu/menu_driver.h" - -@implementation MenuDisplay -{ - Context *_context; - MTLClearColor _clearColor; - MTLScissorRect _scissorRect; - BOOL _useScissorRect; - Uniforms _uniforms; - bool _clearNextRender; -} - -- (instancetype)initWithContext:(Context *)context -{ - if (self = [super init]) - { - _context = context; - _clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 1.0); - _uniforms.projectionMatrix = matrix_proj_ortho(0, 1, 0, 1); - _useScissorRect = NO; - } - return self; -} - -+ (const float *)defaultVertices -{ - static float dummy[8] = { - 0.0f, 0.0f, - 1.0f, 0.0f, - 0.0f, 1.0f, - 1.0f, 1.0f, - }; - return &dummy[0]; -} - -+ (const float *)defaultTexCoords -{ - static float dummy[8] = { - 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[16] = { - 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]; -} - -- (void)setClearColor:(MTLClearColor)clearColor -{ - _clearColor = clearColor; - _clearNextRender = YES; -} - -- (MTLClearColor)clearColor -{ - return _clearColor; -} - -- (void)setScissorRect:(MTLScissorRect)rect -{ - _scissorRect = rect; - _useScissorRect = YES; -} - -- (void)clearScissorRect -{ - _useScissorRect = NO; - [_context resetScissorRect]; -} - -- (MTLPrimitiveType)_toPrimitiveType:(enum gfx_display_prim_type)prim -{ - switch (prim) - { - case GFX_DISPLAY_PRIM_TRIANGLESTRIP: - return MTLPrimitiveTypeTriangleStrip; - case GFX_DISPLAY_PRIM_TRIANGLES: - default: - /* Unexpected primitive type, defaulting to triangle */ - break; - } - - return MTLPrimitiveTypeTriangle; -} - -- (void)drawPipeline:(gfx_display_ctx_draw_t *)draw -{ - static struct video_coords blank_coords; - - draw->x = 0; - draw->y = 0; - draw->matrix_data = NULL; - - _uniforms.outputSize = simd_make_float2(_context.viewport->full_width, _context.viewport->full_height); - - draw->backend_data = &_uniforms; - draw->backend_data_size = sizeof(_uniforms); - - switch (draw->pipeline_id) - { - /* ribbon */ - default: - case VIDEO_SHADER_MENU: - case VIDEO_SHADER_MENU_2: - { - gfx_display_t *p_disp = disp_get_ptr(); - video_coord_array_t *ca = &p_disp->dispca; - draw->coords = (struct video_coords *)&ca->coords; - break; - } - - case VIDEO_SHADER_MENU_3: - case VIDEO_SHADER_MENU_4: - case VIDEO_SHADER_MENU_5: - case VIDEO_SHADER_MENU_6: - { - draw->coords = &blank_coords; - blank_coords.vertices = 4; - draw->prim_type = GFX_DISPLAY_PRIM_TRIANGLESTRIP; - break; - } - } - - _uniforms.time += 0.01; -} - -- (void)draw:(gfx_display_ctx_draw_t *)draw -{ - unsigned i; - BufferRange range; - NSUInteger vertex_count; - SpriteVertex *pv; - const float *vertex = draw->coords->vertex ?: MenuDisplay.defaultVertices; - const float *tex_coord = draw->coords->tex_coord ?: MenuDisplay.defaultTexCoords; - const float *color = draw->coords->color ?: MenuDisplay.defaultColor; - NSUInteger needed = draw->coords->vertices * sizeof(SpriteVertex); - if (![_context allocRange:&range length:needed]) - return; - - vertex_count = draw->coords->vertices; - pv = (SpriteVertex *)range.data; - - for (i = 0; i < draw->coords->vertices; i++, pv++) - { - pv->position = simd_make_float2(vertex[0], 1.0f - 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; - } - - id rce = _context.rce; - if (_clearNextRender) - { - [_context resetRenderViewport:kFullscreenViewport]; - [_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 = _context.viewport->full_height - draw->y - draw->height, - .width = draw->width, - .height = draw->height, - .znear = 0, - .zfar = 1, - }; - [rce setViewport:vp]; - - if (_useScissorRect) - [rce setScissorRect:_scissorRect]; - - switch (draw->pipeline_id) - { -#if HAVE_SHADERPIPELINE - 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: - [rce setRenderPipelineState:[_context getStockShader:draw->pipeline_id blend:_blend]]; - [rce setVertexBytes:draw->backend_data length:draw->backend_data_size atIndex:BufferIndexUniforms]; - [rce setVertexBuffer:range.buffer offset:range.offset atIndex:BufferIndexPositions]; - [rce setFragmentBytes:draw->backend_data length:draw->backend_data_size atIndex:BufferIndexUniforms]; - [rce drawPrimitives:[self _toPrimitiveType:draw->prim_type] vertexStart:0 vertexCount:vertex_count]; - return; -#endif - default: - break; - } - - Texture *tex = (__bridge Texture *)(void *)draw->texture; - if (tex == nil) - return; - - [rce setRenderPipelineState:[_context getStockShader:VIDEO_SHADER_STOCK_BLEND blend:_blend]]; - - Uniforms uniforms = { - .projectionMatrix = draw->matrix_data ? make_matrix_float4x4((const float *)draw->matrix_data) - : _uniforms.projectionMatrix - }; - [rce setVertexBytes:&uniforms length:sizeof(uniforms) atIndex:BufferIndexUniforms]; - [rce setVertexBuffer:range.buffer offset:range.offset atIndex:BufferIndexPositions]; - [rce setFragmentTexture:tex.texture atIndex:TextureIndexColor]; - [rce setFragmentSamplerState:tex.sampler atIndex:SamplerIndexDraw]; - [rce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:vertex_count]; -} -@end diff --git a/gfx/common/metal/RendererCommon.h b/gfx/common/metal/RendererCommon.h deleted file mode 100644 index ae47daf814..0000000000 --- a/gfx/common/metal/RendererCommon.h +++ /dev/null @@ -1,61 +0,0 @@ -// -// RendererCommon.h -// MetalRenderer -// -// Created by Stuart Carnie on 6/3/18. -// Copyright © 2018 Stuart Carnie. All rights reserved. -// - -#ifndef RendererCommon_h -#define RendererCommon_h - -#import -#import "ShaderTypes.h" - -// TODO(sgc): implement triple buffering -/*! @brief maximum inflight frames */ -#define MAX_INFLIGHT 1 -#define CHAIN_LENGTH 3 - -/* macOS requires constants in a buffer to have a 256 byte alignment. */ -#ifdef TARGET_OS_MAC -#define kMetalBufferAlignment 256 -#else -#define kMetalBufferAlignment 4 -#endif - -#define MTL_ALIGN_BUFFER(size) ((size + kMetalBufferAlignment - 1) & (~(kMetalBufferAlignment - 1))) - -#pragma mark - Pixel Formats - -typedef NS_ENUM(NSUInteger, RPixelFormat) -{ - - RPixelFormatInvalid, - - /* 16-bit formats */ - RPixelFormatBGRA4Unorm, - RPixelFormatB5G6R5Unorm, - - RPixelFormatBGRA8Unorm, - RPixelFormatBGRX8Unorm, // RetroArch XRGB - - RPixelFormatCount, -}; - -extern NSUInteger RPixelFormatToBPP(RPixelFormat format); -extern NSString *NSStringFromRPixelFormat(RPixelFormat format); - -typedef NS_ENUM(NSUInteger, RTextureFilter) -{ - RTextureFilterNearest, - RTextureFilterLinear, - - RTextureFilterCount, -}; - -extern matrix_float4x4 matrix_proj_ortho(float left, float right, float top, float bottom); -extern matrix_float4x4 matrix_rotate_z(float rot); -extern matrix_float4x4 make_matrix_float4x4(const float *v); - -#endif /* RendererCommon_h */ diff --git a/gfx/common/metal/RendererCommon.m b/gfx/common/metal/RendererCommon.m deleted file mode 100644 index d43f5b498a..0000000000 --- a/gfx/common/metal/RendererCommon.m +++ /dev/null @@ -1,104 +0,0 @@ -// -// RendererCommon.m -// MetalRenderer -// -// Created by Stuart Carnie on 6/3/18. -// Copyright © 2018 Stuart Carnie. All rights reserved. -// - -#import "RendererCommon.h" -#import - -#include "../../verbosity.h" - -NSUInteger RPixelFormatToBPP(RPixelFormat format) -{ - 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]; - -NSString *NSStringFromRPixelFormat(RPixelFormat format) -{ - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - -#define STRING(literal) RPixelStrings[literal] = @#literal - STRING(RPixelFormatInvalid); - STRING(RPixelFormatB5G6R5Unorm); - STRING(RPixelFormatBGRA4Unorm); - STRING(RPixelFormatBGRA8Unorm); - STRING(RPixelFormatBGRX8Unorm); -#undef STRING - - }); - - if (format >= RPixelFormatCount) - { - format = RPixelFormatInvalid; - } - - return RPixelStrings[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; - simd_float4 Q = simd_make_float4(v[0], v[1], v[2], v[3]); - v += 4; - simd_float4 R = simd_make_float4(v[0], v[1], v[2], v[3]); - v += 4; - simd_float4 S = simd_make_float4(v[0], v[1], v[2], v[3]); - - matrix_float4x4 mat = {P, Q, R, S}; - return mat; -} - -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); - - simd_float4 P = simd_make_float4(sx, 0, 0, 0); - simd_float4 Q = simd_make_float4(0, sy, 0, 0); - simd_float4 R = simd_make_float4(0, 0, sz, 0); - simd_float4 S = simd_make_float4(tx, ty, tz, 1); - - matrix_float4x4 mat = {P, Q, R, S}; - return mat; -} - -matrix_float4x4 matrix_rotate_z(float rot) -{ - float cz, sz; - __sincosf(rot, &sz, &cz); - - simd_float4 P = simd_make_float4(cz, -sz, 0, 0); - simd_float4 Q = simd_make_float4(sz, cz, 0, 0); - simd_float4 R = simd_make_float4( 0, 0, 1, 0); - simd_float4 S = simd_make_float4( 0, 0, 0, 1); - - matrix_float4x4 mat = {P, Q, R, S}; - return mat; -} diff --git a/gfx/common/metal/Shaders.metal b/gfx/common/metal/Shaders.metal index def08fb2f0..075a52fcf3 100644 --- a/gfx/common/metal/Shaders.metal +++ b/gfx/common/metal/Shaders.metal @@ -1,18 +1,26 @@ -// -// Shaders.metal -// MetalRenderer -// -// Created by Stuart Carnie on 5/31/18. -// Copyright © 2018 Stuart Carnie. All rights reserved. -// +/* RetroArch - A frontend for libretro. + * Copyright (C) 2018 - Stuart Carnie + * copyright (c) 2011-2021 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ -// File for Metal kernel and shader functions +/* File for Metal kernel and shader functions */ #include #include -// Including header shared between this Metal shader code and Swift/C code executing Metal API commands -#import "ShaderTypes.h" +/* Including header shared between this Metal shader code and Swift/C code executing Metal API commands */ +#import "metal_shader_types.h" using namespace metal; diff --git a/gfx/common/metal/TexturedView.h b/gfx/common/metal/TexturedView.h deleted file mode 100644 index 9c512c7ad9..0000000000 --- a/gfx/common/metal/TexturedView.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// Created by Stuart Carnie on 6/16/18. -// - -#import "Context.h" -#import "View.h" - -@interface TexturedView : NSObject - -@property (nonatomic, readonly) RPixelFormat format; -@property (nonatomic, readonly) RTextureFilter filter; -@property (nonatomic, readwrite) BOOL visible; -@property (nonatomic, readwrite) CGRect frame; -@property (nonatomic, readwrite) CGSize size; -@property (nonatomic, readonly) ViewDrawState drawState; - -- (instancetype)initWithDescriptor:(ViewDescriptor *)td context:(Context *)c; - -- (void)drawWithContext:(Context *)ctx; -- (void)drawWithEncoder:(id)rce; -- (void)updateFrame:(void const *)src pitch:(NSUInteger)pitch; - -@end diff --git a/gfx/common/metal/TexturedView.m b/gfx/common/metal/TexturedView.m deleted file mode 100644 index 50df1a8ce0..0000000000 --- a/gfx/common/metal/TexturedView.m +++ /dev/null @@ -1,149 +0,0 @@ -// -// Created by Stuart Carnie on 6/16/18. -// - -#import "TexturedView.h" -#import "RendererCommon.h" -#import "View.h" -#import "Filter.h" - -@implementation TexturedView -{ - Context *_context; - id _texture; // optimal render texture - Vertex _v[4]; - CGSize _size; // size of view in pixels - CGRect _frame; - NSUInteger _bpp; - - id _src; // source texture - bool _srcDirty; -} - -- (instancetype)initWithDescriptor:(ViewDescriptor *)d context:(Context *)c -{ - self = [super init]; - if (self) - { - _format = d.format; - _bpp = RPixelFormatToBPP(_format); - _filter = d.filter; - _context = c; - _visible = YES; - if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) - { - _drawState = ViewDrawStateEncoder; - } - else - { - _drawState = ViewDrawStateAll; - } - self.size = d.size; - self.frame = CGRectMake(0, 0, 1, 1); - } - return self; -} - -- (void)setSize:(CGSize)size -{ - if (CGSizeEqualToSize(_size, size)) - { - return; - } - - _size = size; - - { - MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm - width:(NSUInteger)size.width - height:(NSUInteger)size.height - mipmapped:NO]; - td.usage = MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite; - _texture = [_context.device newTextureWithDescriptor:td]; - } - - if (_format != RPixelFormatBGRA8Unorm && _format != RPixelFormatBGRX8Unorm) - { - MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatR16Uint - width:(NSUInteger)size.width - height:(NSUInteger)size.height - mipmapped:NO]; - _src = [_context.device newTextureWithDescriptor:td]; - } -} - -- (CGSize)size -{ - return _size; -} - -- (void)setFrame:(CGRect)frame -{ - if (CGRectEqualToRect(_frame, frame)) - { - return; - } - - _frame = frame; - - float l = (float)CGRectGetMinX(frame); - float t = (float)CGRectGetMinY(frame); - float r = (float)CGRectGetMaxX(frame); - float b = (float)CGRectGetMaxY(frame); - - Vertex v[4] = { - {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)); -} - -- (CGRect)frame -{ - return _frame; -} - -- (void)_convertFormat -{ - if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) - return; - - if (!_srcDirty) - return; - - [_context convertFormat:_format from:_src to:_texture]; - _srcDirty = NO; -} - -- (void)drawWithContext:(Context *)ctx -{ - [self _convertFormat]; -} - -- (void)drawWithEncoder:(id)rce -{ - [rce setVertexBytes:&_v length:sizeof(_v) atIndex:BufferIndexPositions]; - [rce setFragmentTexture:_texture atIndex:TextureIndexColor]; - [rce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4]; -} - -- (void)updateFrame:(void const *)src pitch:(NSUInteger)pitch -{ - if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) - { - [_texture replaceRegion:MTLRegionMake2D(0, 0, (NSUInteger)_size.width, (NSUInteger)_size.height) - mipmapLevel:0 withBytes:src - bytesPerRow:(NSUInteger)(4 * pitch)]; - } - else - { - [_src replaceRegion:MTLRegionMake2D(0, 0, (NSUInteger)_size.width, (NSUInteger)_size.height) - mipmapLevel:0 withBytes:src - bytesPerRow:(NSUInteger)(pitch)]; - _srcDirty = YES; - } -} - -@end diff --git a/gfx/common/metal/View.h b/gfx/common/metal/View.h deleted file mode 100644 index cd8fe06779..0000000000 --- a/gfx/common/metal/View.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// RView.h -// MetalRenderer -// -// Created by Stuart Carnie on 5/31/18. -// Copyright © 2018 Stuart Carnie. All rights reserved. -// - -#import "RendererCommon.h" -#import -#import - -typedef NS_ENUM(NSInteger, ViewDrawState) -{ - ViewDrawStateNone = 0x00, - ViewDrawStateContext = 0x01, - ViewDrawStateEncoder = 0x02, - - ViewDrawStateAll = 0x03, -}; - -@interface ViewDescriptor : NSObject -@property (nonatomic, readwrite) RPixelFormat format; -@property (nonatomic, readwrite) RTextureFilter filter; -@property (nonatomic, readwrite) CGSize size; - -- (instancetype)init; -@end diff --git a/gfx/common/metal/View.m b/gfx/common/metal/View.m deleted file mode 100644 index 7c6bfd9f10..0000000000 --- a/gfx/common/metal/View.m +++ /dev/null @@ -1,36 +0,0 @@ -// -// RView.m -// MetalRenderer -// -// Created by Stuart Carnie on 5/31/18. -// Copyright © 2018 Stuart Carnie. All rights reserved. -// - -#import "View.h" -#import "RendererCommon.h" - -@implementation ViewDescriptor - -- (instancetype)init -{ - self = [super init]; - if (self) - { - _format = RPixelFormatBGRA8Unorm; - } - return self; -} - -- (NSString *)debugDescription -{ -#if defined(HAVE_COCOATOUCH) - NSString *sizeDesc = [NSString stringWithFormat:@"width: %f, height: %f",_size.width,_size.height]; -#else - NSString *sizeDesc = NSStringFromSize(_size); -#endif - return [NSString stringWithFormat:@"( format = %@, frame = %@ )", - NSStringFromRPixelFormat(_format), - sizeDesc]; -} - -@end diff --git a/gfx/common/metal/menu_pipeline.metal b/gfx/common/metal/menu_pipeline.metal index 1119972957..4cfa884e08 100644 --- a/gfx/common/metal/menu_pipeline.metal +++ b/gfx/common/metal/menu_pipeline.metal @@ -1,13 +1,22 @@ -// -// pipeline_ribbon.metal -// RetroArch -// -// Created by Stuart Carnie on 6/30/18. -// +/* RetroArch - A frontend for libretro. + * Copyright (C) 2018 - Stuart Carnie + * copyright (c) 2011-2021 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ #include -#import "ShaderTypes.h" +#import "metal_shader_types.h" using namespace metal; diff --git a/gfx/common/metal/metal_common.h b/gfx/common/metal/metal_common.h index 45232b236d..3b216a619a 100644 --- a/gfx/common/metal/metal_common.h +++ b/gfx/common/metal/metal_common.h @@ -1,12 +1,222 @@ -// -// metal_common.h -// RetroArch_Metal -// -// Created by Stuart Carnie on 6/15/18. -// +/* RetroArch - A frontend for libretro. + * Copyright (C) 2018 - Stuart Carnie + * copyright (c) 2011-2021 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ -#import "RendererCommon.h" -#import "Context.h" -#import "View.h" -#import "TexturedView.h" -#import "MenuDisplay.h" +#ifndef METAL_RENDERER_H +#define METAL_RENDERER_H + +#import +#import +#import + +#include "metal_shader_types.h" + +#include "../../gfx_display.h" +#include "../../../retroarch.h" + +/* TODO/FIXME: implement triple buffering */ +/*! @brief maximum inflight frames */ +#define MAX_INFLIGHT 1 +#define CHAIN_LENGTH 3 + +/* macOS requires constants in a buffer to have a 256 byte alignment. */ +#ifdef TARGET_OS_MAC +#define kMetalBufferAlignment 256 +#else +#define kMetalBufferAlignment 4 +#endif + +#define MTL_ALIGN_BUFFER(size) ((size + kMetalBufferAlignment - 1) & (~(kMetalBufferAlignment - 1))) + +#pragma mark - Pixel Formats + +typedef NS_ENUM(NSUInteger, RPixelFormat) +{ + + RPixelFormatInvalid, + + /* 16-bit formats */ + RPixelFormatBGRA4Unorm, + RPixelFormatB5G6R5Unorm, + + RPixelFormatBGRA8Unorm, + RPixelFormatBGRX8Unorm, // RetroArch XRGB + + RPixelFormatCount, +}; + +extern NSUInteger RPixelFormatToBPP(RPixelFormat format); +extern NSString *NSStringFromRPixelFormat(RPixelFormat format); + +typedef NS_ENUM(NSUInteger, RTextureFilter) +{ + RTextureFilterNearest, + RTextureFilterLinear, + + RTextureFilterCount, +}; + +extern matrix_float4x4 matrix_proj_ortho(float left, float right, float top, float bottom); +extern matrix_float4x4 matrix_rotate_z(float rot); +extern matrix_float4x4 make_matrix_float4x4(const float *v); + + + +@interface Texture : NSObject +@property (nonatomic, readonly) id texture; +@property (nonatomic, readonly) id sampler; +@end + +typedef struct +{ + void *data; + NSUInteger offset; + __unsafe_unretained id buffer; +} BufferRange; + +typedef NS_ENUM(NSUInteger, ViewportResetMode) { + kFullscreenViewport, + kVideoViewport +}; + +/*! @brief Context contains the render state used by various components */ +@interface Context : NSObject + +@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 captureEnabled allows previous frames to be read */ +@property (nonatomic, readwrite) bool captureEnabled; + +/*! @brief Returns the command buffer used for pre-render work, + * such as mip maps and shader effects + * */ +@property (nonatomic, readonly) id blitCommandBuffer; + +/*! @brief Returns the command buffer for the current frame */ +@property (nonatomic, readonly) id commandBuffer; +@property (nonatomic, readonly) id nextDrawable; + +/*! @brief Main render encoder to back buffer */ +@property (nonatomic, readonly) id rce; + +- (instancetype)initWithDevice:(id)d + layer:(CAMetalLayer *)layer + 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; +- (id)getStockShader:(int)index blend:(bool)blend; + +/*! @brief resets the viewport for the main render encoder to \a mode */ +- (void)resetRenderViewport:(ViewportResetMode)mode; + +/*! @brief resets the scissor rect for the main render encoder to the drawable size */ +- (void)resetScissorRect; + +/*! @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; + +/*! @brief begin marks the beginning of a frame */ +- (void)begin; + +/*! @brief end commits the command buffer */ +- (void)end; + +- (void)setRotation:(unsigned)rotation; +- (bool)readBackBuffer:(uint8_t *)buffer; + +@end + +@protocol FilterDelegate +- (void)configure:(id)encoder; +@end + +@interface Filter : NSObject + +@property (nonatomic, readwrite) id delegate; +@property (nonatomic, readonly) id sampler; + +- (void)apply:(id)cb in:(id)tin out:(id)tout; +- (void)apply:(id)cb inBuf:(id)tin outTex:(id)tout; + ++ (instancetype)newFilterWithFunctionName:(NSString *)name device:(id)device library:(id)library error:(NSError **)error; + +@end + +@interface MenuDisplay : NSObject + +@property (nonatomic, readwrite) BOOL blend; +@property (nonatomic, readwrite) MTLClearColor clearColor; + +- (instancetype)initWithContext:(Context *)context; +- (void)drawPipeline:(gfx_display_ctx_draw_t *)draw; +- (void)draw:(gfx_display_ctx_draw_t *)draw; +- (void)setScissorRect:(MTLScissorRect)rect; +- (void)clearScissorRect; + +#pragma mark - static methods + ++ (const float *)defaultVertices; ++ (const float *)defaultTexCoords; ++ (const float *)defaultColor; + +@end + +typedef NS_ENUM(NSInteger, ViewDrawState) +{ + ViewDrawStateNone = 0x00, + ViewDrawStateContext = 0x01, + ViewDrawStateEncoder = 0x02, + + ViewDrawStateAll = 0x03, +}; + +@interface ViewDescriptor : NSObject +@property (nonatomic, readwrite) RPixelFormat format; +@property (nonatomic, readwrite) RTextureFilter filter; +@property (nonatomic, readwrite) CGSize size; + +- (instancetype)init; +@end + +@interface TexturedView : NSObject + +@property (nonatomic, readonly) RPixelFormat format; +@property (nonatomic, readonly) RTextureFilter filter; +@property (nonatomic, readwrite) BOOL visible; +@property (nonatomic, readwrite) CGRect frame; +@property (nonatomic, readwrite) CGSize size; +@property (nonatomic, readonly) ViewDrawState drawState; + +- (instancetype)initWithDescriptor:(ViewDescriptor *)td context:(Context *)c; + +- (void)drawWithContext:(Context *)ctx; +- (void)drawWithEncoder:(id)rce; +- (void)updateFrame:(void const *)src pitch:(NSUInteger)pitch; + +@end + +#endif diff --git a/gfx/common/metal/Context.m b/gfx/common/metal/metal_renderer.m similarity index 58% rename from gfx/common/metal/Context.m rename to gfx/common/metal/metal_renderer.m index 033bb219a1..9562976eb9 100644 --- a/gfx/common/metal/Context.m +++ b/gfx/common/metal/metal_renderer.m @@ -1,20 +1,134 @@ -// -// Context.m -// MetalRenderer -// -// Created by Stuart Carnie on 6/9/18. -// Copyright © 2018 Stuart Carnie. All rights reserved. -// +/* RetroArch - A frontend for libretro. + * Copyright (C) 2018 - Stuart Carnie + * copyright (c) 2011-2021 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ #include -#import "Context.h" -#import "Filter.h" +#import #import -#import "../metal_common.h" +#import "metal_common.h" +#import "metal_shader_types.h" + +#ifdef HAVE_MENU +#include "../../../menu/menu_driver.h" +#endif + +#import "../metal_common.h" #include "../../verbosity.h" +/* + * COMMON + */ + +NSUInteger RPixelFormatToBPP(RPixelFormat format) +{ + 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]; + +NSString *NSStringFromRPixelFormat(RPixelFormat format) +{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + +#define STRING(literal) RPixelStrings[literal] = @#literal + STRING(RPixelFormatInvalid); + STRING(RPixelFormatB5G6R5Unorm); + STRING(RPixelFormatBGRA4Unorm); + STRING(RPixelFormatBGRA8Unorm); + STRING(RPixelFormatBGRX8Unorm); +#undef STRING + + }); + + if (format >= RPixelFormatCount) + { + format = RPixelFormatInvalid; + } + + return RPixelStrings[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; + simd_float4 Q = simd_make_float4(v[0], v[1], v[2], v[3]); + v += 4; + simd_float4 R = simd_make_float4(v[0], v[1], v[2], v[3]); + v += 4; + simd_float4 S = simd_make_float4(v[0], v[1], v[2], v[3]); + + matrix_float4x4 mat = {P, Q, R, S}; + return mat; +} + +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); + + simd_float4 P = simd_make_float4(sx, 0, 0, 0); + simd_float4 Q = simd_make_float4(0, sy, 0, 0); + simd_float4 R = simd_make_float4(0, 0, sz, 0); + simd_float4 S = simd_make_float4(tx, ty, tz, 1); + + matrix_float4x4 mat = {P, Q, R, S}; + return mat; +} + +matrix_float4x4 matrix_rotate_z(float rot) +{ + float cz, sz; + __sincosf(rot, &sz, &cz); + + simd_float4 P = simd_make_float4(cz, -sz, 0, 0); + simd_float4 Q = simd_make_float4(sz, cz, 0, 0); + simd_float4 R = simd_make_float4( 0, 0, 1, 0); + simd_float4 S = simd_make_float4( 0, 0, 0, 1); + + matrix_float4x4 mat = {P, Q, R, S}; + return mat; +} + +/* + * CONTEXT + */ + @interface BufferNode : NSObject @property (nonatomic, readonly) id src; @property (nonatomic, readwrite) NSUInteger allocated; @@ -824,3 +938,487 @@ static const NSUInteger kConstantAlignment = 4; } @end + +/* + * FILTER + */ + +@interface Filter() +- (instancetype)initWithKernel:(id)kernel sampler:(id)sampler; +@end + +@implementation Filter +{ + id _kernel; +} + ++ (instancetype)newFilterWithFunctionName:(NSString *)name device:(id)device library:(id)library error:(NSError **)error +{ + id function = [library newFunctionWithName:name]; + id kernel = [device newComputePipelineStateWithFunction:function error:error]; + if (*error != nil) + { + return nil; + } + + MTLSamplerDescriptor *sd = [MTLSamplerDescriptor new]; + sd.minFilter = MTLSamplerMinMagFilterNearest; + sd.magFilter = MTLSamplerMinMagFilterNearest; + sd.sAddressMode = MTLSamplerAddressModeClampToEdge; + sd.tAddressMode = MTLSamplerAddressModeClampToEdge; + sd.mipFilter = MTLSamplerMipFilterNotMipmapped; + id sampler = [device newSamplerStateWithDescriptor:sd]; + + return [[Filter alloc] initWithKernel:kernel sampler:sampler]; +} + +- (instancetype)initWithKernel:(id)kernel sampler:(id)sampler +{ + if (self = [super init]) + { + _kernel = kernel; + _sampler = sampler; + } + return self; +} + +- (void)apply:(id)cb in:(id)tin out:(id)tout +{ + id ce = [cb computeCommandEncoder]; + ce.label = @"filter kernel"; + + [ce setComputePipelineState:_kernel]; + + [ce setTexture:tin atIndex:0]; + [ce setTexture:tout atIndex:1]; + + [self.delegate configure:ce]; + + MTLSize size = MTLSizeMake(16, 16, 1); + MTLSize count = MTLSizeMake((tin.width + size.width + 1) / size.width, (tin.height + size.height + 1) / size.height, + 1); + + [ce dispatchThreadgroups:count threadsPerThreadgroup:size]; + + [ce endEncoding]; +} + +- (void)apply:(id)cb inBuf:(id)tin outTex:(id)tout +{ + id ce = [cb computeCommandEncoder]; + ce.label = @"filter kernel"; + + [ce setComputePipelineState:_kernel]; + + [ce setBuffer:tin offset:0 atIndex:0]; + [ce setTexture:tout atIndex:0]; + + [self.delegate configure:ce]; + + MTLSize size = MTLSizeMake(32, 1, 1); + MTLSize count = MTLSizeMake((tin.length + 00) / 32, 1, 1); + + [ce dispatchThreadgroups:count threadsPerThreadgroup:size]; + + [ce endEncoding]; +} + +@end + +#ifdef HAVE_MENU +@implementation MenuDisplay +{ + Context *_context; + MTLClearColor _clearColor; + MTLScissorRect _scissorRect; + BOOL _useScissorRect; + Uniforms _uniforms; + bool _clearNextRender; +} + +- (instancetype)initWithContext:(Context *)context +{ + if (self = [super init]) + { + _context = context; + _clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 1.0); + _uniforms.projectionMatrix = matrix_proj_ortho(0, 1, 0, 1); + _useScissorRect = NO; + } + return self; +} + ++ (const float *)defaultVertices +{ + static float dummy[8] = { + 0.0f, 0.0f, + 1.0f, 0.0f, + 0.0f, 1.0f, + 1.0f, 1.0f, + }; + return &dummy[0]; +} + ++ (const float *)defaultTexCoords +{ + static float dummy[8] = { + 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[16] = { + 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]; +} + +- (void)setClearColor:(MTLClearColor)clearColor +{ + _clearColor = clearColor; + _clearNextRender = YES; +} + +- (MTLClearColor)clearColor +{ + return _clearColor; +} + +- (void)setScissorRect:(MTLScissorRect)rect +{ + _scissorRect = rect; + _useScissorRect = YES; +} + +- (void)clearScissorRect +{ + _useScissorRect = NO; + [_context resetScissorRect]; +} + +- (MTLPrimitiveType)_toPrimitiveType:(enum gfx_display_prim_type)prim +{ + switch (prim) + { + case GFX_DISPLAY_PRIM_TRIANGLESTRIP: + return MTLPrimitiveTypeTriangleStrip; + case GFX_DISPLAY_PRIM_TRIANGLES: + default: + /* Unexpected primitive type, defaulting to triangle */ + break; + } + + return MTLPrimitiveTypeTriangle; +} + +- (void)drawPipeline:(gfx_display_ctx_draw_t *)draw +{ + static struct video_coords blank_coords; + + draw->x = 0; + draw->y = 0; + draw->matrix_data = NULL; + + _uniforms.outputSize = simd_make_float2(_context.viewport->full_width, _context.viewport->full_height); + + draw->backend_data = &_uniforms; + draw->backend_data_size = sizeof(_uniforms); + + switch (draw->pipeline_id) + { + /* ribbon */ + default: + case VIDEO_SHADER_MENU: + case VIDEO_SHADER_MENU_2: + { + gfx_display_t *p_disp = disp_get_ptr(); + video_coord_array_t *ca = &p_disp->dispca; + draw->coords = (struct video_coords *)&ca->coords; + break; + } + + case VIDEO_SHADER_MENU_3: + case VIDEO_SHADER_MENU_4: + case VIDEO_SHADER_MENU_5: + case VIDEO_SHADER_MENU_6: + { + draw->coords = &blank_coords; + blank_coords.vertices = 4; + draw->prim_type = GFX_DISPLAY_PRIM_TRIANGLESTRIP; + break; + } + } + + _uniforms.time += 0.01; +} + +- (void)draw:(gfx_display_ctx_draw_t *)draw +{ + unsigned i; + BufferRange range; + NSUInteger vertex_count; + SpriteVertex *pv; + const float *vertex = draw->coords->vertex ?: MenuDisplay.defaultVertices; + const float *tex_coord = draw->coords->tex_coord ?: MenuDisplay.defaultTexCoords; + const float *color = draw->coords->color ?: MenuDisplay.defaultColor; + NSUInteger needed = draw->coords->vertices * sizeof(SpriteVertex); + if (![_context allocRange:&range length:needed]) + return; + + vertex_count = draw->coords->vertices; + pv = (SpriteVertex *)range.data; + + for (i = 0; i < draw->coords->vertices; i++, pv++) + { + pv->position = simd_make_float2(vertex[0], 1.0f - 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; + } + + id rce = _context.rce; + if (_clearNextRender) + { + [_context resetRenderViewport:kFullscreenViewport]; + [_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 = _context.viewport->full_height - draw->y - draw->height, + .width = draw->width, + .height = draw->height, + .znear = 0, + .zfar = 1, + }; + [rce setViewport:vp]; + + if (_useScissorRect) + [rce setScissorRect:_scissorRect]; + + switch (draw->pipeline_id) + { +#if HAVE_SHADERPIPELINE + 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: + [rce setRenderPipelineState:[_context getStockShader:draw->pipeline_id blend:_blend]]; + [rce setVertexBytes:draw->backend_data length:draw->backend_data_size atIndex:BufferIndexUniforms]; + [rce setVertexBuffer:range.buffer offset:range.offset atIndex:BufferIndexPositions]; + [rce setFragmentBytes:draw->backend_data length:draw->backend_data_size atIndex:BufferIndexUniforms]; + [rce drawPrimitives:[self _toPrimitiveType:draw->prim_type] vertexStart:0 vertexCount:vertex_count]; + return; +#endif + default: + break; + } + + Texture *tex = (__bridge Texture *)(void *)draw->texture; + if (tex == nil) + return; + + [rce setRenderPipelineState:[_context getStockShader:VIDEO_SHADER_STOCK_BLEND blend:_blend]]; + + Uniforms uniforms = { + .projectionMatrix = draw->matrix_data ? make_matrix_float4x4((const float *)draw->matrix_data) + : _uniforms.projectionMatrix + }; + [rce setVertexBytes:&uniforms length:sizeof(uniforms) atIndex:BufferIndexUniforms]; + [rce setVertexBuffer:range.buffer offset:range.offset atIndex:BufferIndexPositions]; + [rce setFragmentTexture:tex.texture atIndex:TextureIndexColor]; + [rce setFragmentSamplerState:tex.sampler atIndex:SamplerIndexDraw]; + [rce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:vertex_count]; +} +@end +#endif + +@implementation ViewDescriptor + +- (instancetype)init +{ + self = [super init]; + if (self) + { + _format = RPixelFormatBGRA8Unorm; + } + return self; +} + +- (NSString *)debugDescription +{ +#if defined(HAVE_COCOATOUCH) + NSString *sizeDesc = [NSString stringWithFormat:@"width: %f, height: %f",_size.width,_size.height]; +#else + NSString *sizeDesc = NSStringFromSize(_size); +#endif + return [NSString stringWithFormat:@"( format = %@, frame = %@ )", + NSStringFromRPixelFormat(_format), + sizeDesc]; +} + +@end + +@implementation TexturedView +{ + Context *_context; + id _texture; // optimal render texture + Vertex _v[4]; + CGSize _size; // size of view in pixels + CGRect _frame; + NSUInteger _bpp; + + id _src; // source texture + bool _srcDirty; +} + +- (instancetype)initWithDescriptor:(ViewDescriptor *)d context:(Context *)c +{ + self = [super init]; + if (self) + { + _format = d.format; + _bpp = RPixelFormatToBPP(_format); + _filter = d.filter; + _context = c; + _visible = YES; + if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) + { + _drawState = ViewDrawStateEncoder; + } + else + { + _drawState = ViewDrawStateAll; + } + self.size = d.size; + self.frame = CGRectMake(0, 0, 1, 1); + } + return self; +} + +- (void)setSize:(CGSize)size +{ + if (CGSizeEqualToSize(_size, size)) + { + return; + } + + _size = size; + + { + MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm + width:(NSUInteger)size.width + height:(NSUInteger)size.height + mipmapped:NO]; + td.usage = MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite; + _texture = [_context.device newTextureWithDescriptor:td]; + } + + if (_format != RPixelFormatBGRA8Unorm && _format != RPixelFormatBGRX8Unorm) + { + MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatR16Uint + width:(NSUInteger)size.width + height:(NSUInteger)size.height + mipmapped:NO]; + _src = [_context.device newTextureWithDescriptor:td]; + } +} + +- (CGSize)size +{ + return _size; +} + +- (void)setFrame:(CGRect)frame +{ + if (CGRectEqualToRect(_frame, frame)) + { + return; + } + + _frame = frame; + + float l = (float)CGRectGetMinX(frame); + float t = (float)CGRectGetMinY(frame); + float r = (float)CGRectGetMaxX(frame); + float b = (float)CGRectGetMaxY(frame); + + Vertex v[4] = { + {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)); +} + +- (CGRect)frame +{ + return _frame; +} + +- (void)_convertFormat +{ + if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) + return; + + if (!_srcDirty) + return; + + [_context convertFormat:_format from:_src to:_texture]; + _srcDirty = NO; +} + +- (void)drawWithContext:(Context *)ctx +{ + [self _convertFormat]; +} + +- (void)drawWithEncoder:(id)rce +{ + [rce setVertexBytes:&_v length:sizeof(_v) atIndex:BufferIndexPositions]; + [rce setFragmentTexture:_texture atIndex:TextureIndexColor]; + [rce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4]; +} + +- (void)updateFrame:(void const *)src pitch:(NSUInteger)pitch +{ + if (_format == RPixelFormatBGRA8Unorm || _format == RPixelFormatBGRX8Unorm) + { + [_texture replaceRegion:MTLRegionMake2D(0, 0, (NSUInteger)_size.width, (NSUInteger)_size.height) + mipmapLevel:0 withBytes:src + bytesPerRow:(NSUInteger)(4 * pitch)]; + } + else + { + [_src replaceRegion:MTLRegionMake2D(0, 0, (NSUInteger)_size.width, (NSUInteger)_size.height) + mipmapLevel:0 withBytes:src + bytesPerRow:(NSUInteger)(pitch)]; + _srcDirty = YES; + } +} + +@end diff --git a/gfx/common/metal/ShaderTypes.h b/gfx/common/metal/metal_shader_types.h similarity index 63% rename from gfx/common/metal/ShaderTypes.h rename to gfx/common/metal/metal_shader_types.h index db93f4ad86..24672d1f94 100644 --- a/gfx/common/metal/ShaderTypes.h +++ b/gfx/common/metal/metal_shader_types.h @@ -1,16 +1,25 @@ -// -// ShaderTypes.h -// MetalRenderer -// -// Created by Stuart Carnie on 5/31/18. -// Copyright © 2018 Stuart Carnie. All rights reserved. -// +/* RetroArch - A frontend for libretro. + * Copyright (C) 2018 - Stuart Carnie + * copyright (c) 2011-2021 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ -// -// Header containing types and enum constants shared between Metal shaders and Swift/ObjC source -// -#ifndef ShaderTypes_h -#define ShaderTypes_h +/* + * Header containing types and enum constants shared between Metal shaders and Swift/ObjC source + */ + +#ifndef METAL_SHADER_TYPES_H +#define METAL_SHADER_TYPES_H #ifdef __METAL_VERSION__ #define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type @@ -88,4 +97,4 @@ typedef struct vector_float4 color; } FontFragmentIn; -#endif /* ShaderTypes_h */ +#endif diff --git a/gfx/common/metal_common.m b/gfx/common/metal_common.m index 2cf53961bd..25d3dcd128 100644 --- a/gfx/common/metal_common.m +++ b/gfx/common/metal_common.m @@ -25,7 +25,6 @@ #import #import "metal_common.h" -#include "metal/Context.h" #include "../../ui/drivers/cocoa/apple_platform.h" #include "../../ui/drivers/cocoa/cocoa_common.h" diff --git a/griffin/griffin_objc.m b/griffin/griffin_objc.m index dc33123dcb..db490b03fe 100644 --- a/griffin/griffin_objc.m +++ b/griffin/griffin_objc.m @@ -58,12 +58,7 @@ #endif #ifdef HAVE_METAL -#import "../gfx/common/metal/Context.m" -#import "../gfx/common/metal/Filter.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/metal_renderer.m" #import "../gfx/common/metal_common.m" #import "../gfx/drivers/metal.m" #import "../gfx/drivers_display/gfx_display_metal.m" diff --git a/pkg/apple/RetroArch_Metal.xcodeproj/project.pbxproj b/pkg/apple/RetroArch_Metal.xcodeproj/project.pbxproj index ad6cd6bfa6..e3a45637c4 100644 --- a/pkg/apple/RetroArch_Metal.xcodeproj/project.pbxproj +++ b/pkg/apple/RetroArch_Metal.xcodeproj/project.pbxproj @@ -362,18 +362,9 @@ 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 = ""; }; 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 = ""; }; - 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 = ""; }; 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 = ""; }; - 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 = ""; }; - 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 = ""; }; + 05A8C75120DB72F100FF7857 /* metal_shader_types.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = metal_shader_types.h; sourceTree = ""; }; + 05A8C75420DB72F100FF7857 /* metal_renderer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = metal_renderer.m; 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 = ""; }; @@ -529,10 +520,8 @@ 8D1107320486CEB800E47090 /* RetroArch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RetroArch.app; sourceTree = BUILT_PRODUCTS_DIR; }; 9254B2E625F5516A00A1E0DA /* GitLabCI.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = GitLabCI.xcconfig; sourceTree = ""; }; 9254B33025FA0BA300A1E0DA /* assets.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; name = assets.zip; path = OSX/assets.zip; sourceTree = ""; }; - A902040DE66D42F9EE47BFE3 /* MenuDisplay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MenuDisplay.h; sourceTree = ""; }; A90205FD4D5979BD8B7E4DD6 /* Info_Metal.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.info; name = Info_Metal.plist; path = OSX/Info_Metal.plist; sourceTree = ""; }; A902065A41AEBECE594908C7 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = OSX/en.lproj/MainMenu_Metal.xib; sourceTree = ""; }; - A902070F2C43F222FD56A95A /* MenuDisplay.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MenuDisplay.m; sourceTree = ""; }; A90207489289602F593626D5 /* QTConfig.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = QTConfig.xcconfig; sourceTree = ""; }; A90208A05A895029CEC32C14 /* ui_cocoa_msg_window_metal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ui_cocoa_msg_window_metal.m; sourceTree = ""; }; A9020AF14B9A73364C6CDDA5 /* cocoa_common_metal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = cocoa_common_metal.m; sourceTree = ""; }; @@ -1111,22 +1100,11 @@ 05A8C74520DB72F100FF7857 /* metal */ = { isa = PBXGroup; children = ( - 05A8C74B20DB72F100FF7857 /* Context.h */, - 05A8C75320DB72F100FF7857 /* Context.m */, - 05A8C75720DB72F100FF7857 /* Filter.h */, - 05A8C75020DB72F100FF7857 /* Filter.m */, 05A8C74820DB72F100FF7857 /* metal_common.h */, - A902040DE66D42F9EE47BFE3 /* MenuDisplay.h */, - A902070F2C43F222FD56A95A /* MenuDisplay.m */, - 05A8C74C20DB72F100FF7857 /* RendererCommon.h */, - 05A8C75420DB72F100FF7857 /* RendererCommon.m */, + 05A8C75420DB72F100FF7857 /* metal_renderer.m */, 05A8C74E20DB72F100FF7857 /* Shaders.metal */, 05770B9820E805160013DABC /* menu_pipeline.metal */, - 05A8C75120DB72F100FF7857 /* ShaderTypes.h */, - 05A8C74920DB72F100FF7857 /* TexturedView.h */, - 05A8C75620DB72F100FF7857 /* TexturedView.m */, - 05A8C74F20DB72F100FF7857 /* View.h */, - 05A8C75820DB72F100FF7857 /* View.m */, + 05A8C75120DB72F100FF7857 /* metal_shader_types.h */, ); path = metal; sourceTree = "";