mirror of
https://github.com/CTCaer/RetroArch.git
synced 2025-02-18 18:40:44 +00:00
(Apple/Metal) Combine various Metal files into one
This commit is contained in:
parent
e5ca68a518
commit
5d2a75d782
@ -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)
|
||||
|
@ -1,90 +0,0 @@
|
||||
//
|
||||
// Context.h
|
||||
// MetalRenderer
|
||||
//
|
||||
// Created by Stuart Carnie on 6/9/18.
|
||||
// Copyright © 2018 Stuart Carnie. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Metal/Metal.h>
|
||||
#import <QuartzCore/CAMetalLayer.h>
|
||||
#import "RendererCommon.h"
|
||||
|
||||
#include "../../../retroarch.h"
|
||||
|
||||
@interface Texture : NSObject
|
||||
@property (nonatomic, readonly) id<MTLTexture> texture;
|
||||
@property (nonatomic, readonly) id<MTLSamplerState> sampler;
|
||||
@end
|
||||
|
||||
typedef struct
|
||||
{
|
||||
void *data;
|
||||
NSUInteger offset;
|
||||
__unsafe_unretained id<MTLBuffer> 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<MTLDevice> device;
|
||||
@property (nonatomic, readonly) id<MTLLibrary> 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<MTLCommandBuffer> blitCommandBuffer;
|
||||
|
||||
/*! @brief Returns the command buffer for the current frame */
|
||||
@property (nonatomic, readonly) id<MTLCommandBuffer> commandBuffer;
|
||||
@property (nonatomic, readonly) id<CAMetalDrawable> nextDrawable;
|
||||
|
||||
/*! @brief Main render encoder to back buffer */
|
||||
@property (nonatomic, readonly) id<MTLRenderCommandEncoder> rce;
|
||||
|
||||
- (instancetype)initWithDevice:(id<MTLDevice>)d
|
||||
layer:(CAMetalLayer *)layer
|
||||
library:(id<MTLLibrary>)l;
|
||||
|
||||
- (Texture *)newTexture:(struct texture_image)image filter:(enum texture_filter_type)filter;
|
||||
- (id<MTLTexture>)newTexture:(struct texture_image)image mipmapped:(bool)mipmapped;
|
||||
- (void)convertFormat:(RPixelFormat)fmt from:(id<MTLTexture>)src to:(id<MTLTexture>)dst;
|
||||
- (id<MTLRenderPipelineState>)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
|
@ -1,26 +0,0 @@
|
||||
//
|
||||
// Filter.h
|
||||
// MetalByExampleObjC
|
||||
//
|
||||
// Created by Stuart Carnie on 5/15/18.
|
||||
// Copyright © 2018 Stuart Carnie. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Metal/Metal.h>
|
||||
|
||||
@protocol FilterDelegate
|
||||
- (void)configure:(id<MTLCommandEncoder>)encoder;
|
||||
@end
|
||||
|
||||
@interface Filter : NSObject
|
||||
|
||||
@property (nonatomic, readwrite) id<FilterDelegate> delegate;
|
||||
@property (nonatomic, readonly) id<MTLSamplerState> sampler;
|
||||
|
||||
- (void)apply:(id<MTLCommandBuffer>)cb in:(id<MTLTexture>)tin out:(id<MTLTexture>)tout;
|
||||
- (void)apply:(id<MTLCommandBuffer>)cb inBuf:(id<MTLBuffer>)tin outTex:(id<MTLTexture>)tout;
|
||||
|
||||
+ (instancetype)newFilterWithFunctionName:(NSString *)name device:(id<MTLDevice>)device library:(id<MTLLibrary>)library error:(NSError **)error;
|
||||
|
||||
@end
|
@ -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 <Metal/Metal.h>
|
||||
|
||||
@interface Filter()
|
||||
- (instancetype)initWithKernel:(id<MTLComputePipelineState>)kernel sampler:(id<MTLSamplerState>)sampler;
|
||||
@end
|
||||
|
||||
@implementation Filter
|
||||
{
|
||||
id<MTLComputePipelineState> _kernel;
|
||||
}
|
||||
|
||||
+ (instancetype)newFilterWithFunctionName:(NSString *)name device:(id<MTLDevice>)device library:(id<MTLLibrary>)library error:(NSError **)error
|
||||
{
|
||||
id<MTLFunction> function = [library newFunctionWithName:name];
|
||||
id<MTLComputePipelineState> 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<MTLSamplerState> sampler = [device newSamplerStateWithDescriptor:sd];
|
||||
|
||||
return [[Filter alloc] initWithKernel:kernel sampler:sampler];
|
||||
}
|
||||
|
||||
- (instancetype)initWithKernel:(id<MTLComputePipelineState>)kernel sampler:(id<MTLSamplerState>)sampler
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_kernel = kernel;
|
||||
_sampler = sampler;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)apply:(id<MTLCommandBuffer>)cb in:(id<MTLTexture>)tin out:(id<MTLTexture>)tout
|
||||
{
|
||||
id<MTLComputeCommandEncoder> 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<MTLCommandBuffer>)cb inBuf:(id<MTLBuffer>)tin outTex:(id<MTLTexture>)tout
|
||||
{
|
||||
id<MTLComputeCommandEncoder> 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
|
@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Created by Stuart Carnie on 6/24/18.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#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
|
@ -1,240 +0,0 @@
|
||||
/*
|
||||
* Created by Stuart Carnie on 6/24/18.
|
||||
*/
|
||||
|
||||
#import <Metal/Metal.h>
|
||||
|
||||
#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<MTLRenderCommandEncoder> 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
|
@ -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 <Foundation/Foundation.h>
|
||||
#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 */
|
@ -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 <Metal/Metal.h>
|
||||
|
||||
#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;
|
||||
}
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// File for Metal kernel and shader functions
|
||||
/* File for Metal kernel and shader functions */
|
||||
|
||||
#include <metal_stdlib>
|
||||
#include <simd/simd.h>
|
||||
|
||||
// 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;
|
||||
|
||||
|
@ -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<MTLRenderCommandEncoder>)rce;
|
||||
- (void)updateFrame:(void const *)src pitch:(NSUInteger)pitch;
|
||||
|
||||
@end
|
@ -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<MTLTexture> _texture; // optimal render texture
|
||||
Vertex _v[4];
|
||||
CGSize _size; // size of view in pixels
|
||||
CGRect _frame;
|
||||
NSUInteger _bpp;
|
||||
|
||||
id<MTLTexture> _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<MTLRenderCommandEncoder>)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
|
@ -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 <Foundation/Foundation.h>
|
||||
#import <Metal/Metal.h>
|
||||
|
||||
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
|
@ -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
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <metal_stdlib>
|
||||
|
||||
#import "ShaderTypes.h"
|
||||
#import "metal_shader_types.h"
|
||||
|
||||
using namespace metal;
|
||||
|
||||
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#import "RendererCommon.h"
|
||||
#import "Context.h"
|
||||
#import "View.h"
|
||||
#import "TexturedView.h"
|
||||
#import "MenuDisplay.h"
|
||||
#ifndef METAL_RENDERER_H
|
||||
#define METAL_RENDERER_H
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Metal/Metal.h>
|
||||
#import <QuartzCore/CAMetalLayer.h>
|
||||
|
||||
#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<MTLTexture> texture;
|
||||
@property (nonatomic, readonly) id<MTLSamplerState> sampler;
|
||||
@end
|
||||
|
||||
typedef struct
|
||||
{
|
||||
void *data;
|
||||
NSUInteger offset;
|
||||
__unsafe_unretained id<MTLBuffer> 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<MTLDevice> device;
|
||||
@property (nonatomic, readonly) id<MTLLibrary> 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<MTLCommandBuffer> blitCommandBuffer;
|
||||
|
||||
/*! @brief Returns the command buffer for the current frame */
|
||||
@property (nonatomic, readonly) id<MTLCommandBuffer> commandBuffer;
|
||||
@property (nonatomic, readonly) id<CAMetalDrawable> nextDrawable;
|
||||
|
||||
/*! @brief Main render encoder to back buffer */
|
||||
@property (nonatomic, readonly) id<MTLRenderCommandEncoder> rce;
|
||||
|
||||
- (instancetype)initWithDevice:(id<MTLDevice>)d
|
||||
layer:(CAMetalLayer *)layer
|
||||
library:(id<MTLLibrary>)l;
|
||||
|
||||
- (Texture *)newTexture:(struct texture_image)image filter:(enum texture_filter_type)filter;
|
||||
- (id<MTLTexture>)newTexture:(struct texture_image)image mipmapped:(bool)mipmapped;
|
||||
- (void)convertFormat:(RPixelFormat)fmt from:(id<MTLTexture>)src to:(id<MTLTexture>)dst;
|
||||
- (id<MTLRenderPipelineState>)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<MTLCommandEncoder>)encoder;
|
||||
@end
|
||||
|
||||
@interface Filter : NSObject
|
||||
|
||||
@property (nonatomic, readwrite) id<FilterDelegate> delegate;
|
||||
@property (nonatomic, readonly) id<MTLSamplerState> sampler;
|
||||
|
||||
- (void)apply:(id<MTLCommandBuffer>)cb in:(id<MTLTexture>)tin out:(id<MTLTexture>)tout;
|
||||
- (void)apply:(id<MTLCommandBuffer>)cb inBuf:(id<MTLBuffer>)tin outTex:(id<MTLTexture>)tout;
|
||||
|
||||
+ (instancetype)newFilterWithFunctionName:(NSString *)name device:(id<MTLDevice>)device library:(id<MTLLibrary>)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<MTLRenderCommandEncoder>)rce;
|
||||
- (void)updateFrame:(void const *)src pitch:(NSUInteger)pitch;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <retro_assert.h>
|
||||
|
||||
#import "Context.h"
|
||||
#import "Filter.h"
|
||||
#import <Metal/Metal.h>
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
#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<MTLBuffer> src;
|
||||
@property (nonatomic, readwrite) NSUInteger allocated;
|
||||
@ -824,3 +938,487 @@ static const NSUInteger kConstantAlignment = 4;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/*
|
||||
* FILTER
|
||||
*/
|
||||
|
||||
@interface Filter()
|
||||
- (instancetype)initWithKernel:(id<MTLComputePipelineState>)kernel sampler:(id<MTLSamplerState>)sampler;
|
||||
@end
|
||||
|
||||
@implementation Filter
|
||||
{
|
||||
id<MTLComputePipelineState> _kernel;
|
||||
}
|
||||
|
||||
+ (instancetype)newFilterWithFunctionName:(NSString *)name device:(id<MTLDevice>)device library:(id<MTLLibrary>)library error:(NSError **)error
|
||||
{
|
||||
id<MTLFunction> function = [library newFunctionWithName:name];
|
||||
id<MTLComputePipelineState> 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<MTLSamplerState> sampler = [device newSamplerStateWithDescriptor:sd];
|
||||
|
||||
return [[Filter alloc] initWithKernel:kernel sampler:sampler];
|
||||
}
|
||||
|
||||
- (instancetype)initWithKernel:(id<MTLComputePipelineState>)kernel sampler:(id<MTLSamplerState>)sampler
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_kernel = kernel;
|
||||
_sampler = sampler;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)apply:(id<MTLCommandBuffer>)cb in:(id<MTLTexture>)tin out:(id<MTLTexture>)tout
|
||||
{
|
||||
id<MTLComputeCommandEncoder> 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<MTLCommandBuffer>)cb inBuf:(id<MTLBuffer>)tin outTex:(id<MTLTexture>)tout
|
||||
{
|
||||
id<MTLComputeCommandEncoder> 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<MTLRenderCommandEncoder> 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<MTLTexture> _texture; // optimal render texture
|
||||
Vertex _v[4];
|
||||
CGSize _size; // size of view in pixels
|
||||
CGRect _frame;
|
||||
NSUInteger _bpp;
|
||||
|
||||
id<MTLTexture> _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<MTLRenderCommandEncoder>)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
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//
|
||||
// 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
|
@ -25,7 +25,6 @@
|
||||
#import <gfx/video_frame.h>
|
||||
|
||||
#import "metal_common.h"
|
||||
#include "metal/Context.h"
|
||||
|
||||
#include "../../ui/drivers/cocoa/apple_platform.h"
|
||||
#include "../../ui/drivers/cocoa/cocoa_common.h"
|
||||
|
@ -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"
|
||||
|
@ -362,18 +362,9 @@
|
||||
05A8C73C20DB72F100FF7857 /* vulkan_common.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = vulkan_common.c; sourceTree = "<group>"; };
|
||||
05A8C74420DB72F100FF7857 /* metal_common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = metal_common.h; sourceTree = "<group>"; };
|
||||
05A8C74820DB72F100FF7857 /* metal_common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = metal_common.h; sourceTree = "<group>"; };
|
||||
05A8C74920DB72F100FF7857 /* TexturedView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TexturedView.h; sourceTree = "<group>"; };
|
||||
05A8C74B20DB72F100FF7857 /* Context.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Context.h; sourceTree = "<group>"; };
|
||||
05A8C74C20DB72F100FF7857 /* RendererCommon.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RendererCommon.h; sourceTree = "<group>"; };
|
||||
05A8C74E20DB72F100FF7857 /* Shaders.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = Shaders.metal; sourceTree = "<group>"; };
|
||||
05A8C74F20DB72F100FF7857 /* View.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = View.h; sourceTree = "<group>"; };
|
||||
05A8C75020DB72F100FF7857 /* Filter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Filter.m; sourceTree = "<group>"; };
|
||||
05A8C75120DB72F100FF7857 /* ShaderTypes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ShaderTypes.h; sourceTree = "<group>"; };
|
||||
05A8C75320DB72F100FF7857 /* Context.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Context.m; sourceTree = "<group>"; };
|
||||
05A8C75420DB72F100FF7857 /* RendererCommon.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RendererCommon.m; sourceTree = "<group>"; };
|
||||
05A8C75620DB72F100FF7857 /* TexturedView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TexturedView.m; sourceTree = "<group>"; };
|
||||
05A8C75720DB72F100FF7857 /* Filter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Filter.h; sourceTree = "<group>"; };
|
||||
05A8C75820DB72F100FF7857 /* View.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = View.m; sourceTree = "<group>"; };
|
||||
05A8C75120DB72F100FF7857 /* metal_shader_types.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = metal_shader_types.h; sourceTree = "<group>"; };
|
||||
05A8C75420DB72F100FF7857 /* metal_renderer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = metal_renderer.m; sourceTree = "<group>"; };
|
||||
05A8C75D20DB72F100FF7857 /* gl_common.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = gl_common.c; sourceTree = "<group>"; };
|
||||
05A8C75E20DB72F100FF7857 /* d3d_common.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = d3d_common.c; sourceTree = "<group>"; };
|
||||
05A8C76320DB72F100FF7857 /* d3d10_common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = d3d10_common.h; sourceTree = "<group>"; };
|
||||
@ -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 = "<group>"; };
|
||||
9254B33025FA0BA300A1E0DA /* assets.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; name = assets.zip; path = OSX/assets.zip; sourceTree = "<group>"; };
|
||||
A902040DE66D42F9EE47BFE3 /* MenuDisplay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MenuDisplay.h; sourceTree = "<group>"; };
|
||||
A90205FD4D5979BD8B7E4DD6 /* Info_Metal.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.info; name = Info_Metal.plist; path = OSX/Info_Metal.plist; sourceTree = "<group>"; };
|
||||
A902065A41AEBECE594908C7 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = OSX/en.lproj/MainMenu_Metal.xib; sourceTree = "<group>"; };
|
||||
A902070F2C43F222FD56A95A /* MenuDisplay.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MenuDisplay.m; sourceTree = "<group>"; };
|
||||
A90207489289602F593626D5 /* QTConfig.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = QTConfig.xcconfig; sourceTree = "<group>"; };
|
||||
A90208A05A895029CEC32C14 /* ui_cocoa_msg_window_metal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ui_cocoa_msg_window_metal.m; sourceTree = "<group>"; };
|
||||
A9020AF14B9A73364C6CDDA5 /* cocoa_common_metal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = cocoa_common_metal.m; sourceTree = "<group>"; };
|
||||
@ -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 = "<group>";
|
||||
|
Loading…
x
Reference in New Issue
Block a user