mirror of
https://github.com/libretro/RetroArch
synced 2025-02-21 09:39:56 +00:00
feat(Metal): font rendering
* FPS and other OSD messages render correctly * add autoreleasepool around entire render loop to avoid memory leaks
This commit is contained in:
parent
c10aab9830
commit
367c15f488
@ -23,7 +23,8 @@
|
|||||||
- (void)drawableSizeWillChange:(CGSize)size;
|
- (void)drawableSizeWillChange:(CGSize)size;
|
||||||
|
|
||||||
- (void)beginFrame;
|
- (void)beginFrame;
|
||||||
- (void)drawFrame;
|
- (void)drawViews;
|
||||||
|
- (void)endFrame;
|
||||||
|
|
||||||
#pragma mark - view management
|
#pragma mark - view management
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
|
|
||||||
// other state
|
// other state
|
||||||
Uniforms _uniforms;
|
Uniforms _uniforms;
|
||||||
BOOL _begin;
|
BOOL _begin, _end;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithDevice:(id<MTLDevice>)device layer:(CAMetalLayer *)layer
|
- (instancetype)initWithDevice:(id<MTLDevice>)device layer:(CAMetalLayer *)layer
|
||||||
@ -57,6 +57,7 @@
|
|||||||
|
|
||||||
_conv = [[PixelConverter alloc] initWithContext:_context];
|
_conv = [[PixelConverter alloc] initWithContext:_context];
|
||||||
_begin = NO;
|
_begin = NO;
|
||||||
|
_end = NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@ -137,78 +138,19 @@
|
|||||||
|
|
||||||
- (void)beginFrame
|
- (void)beginFrame
|
||||||
{
|
{
|
||||||
assert(!_begin);
|
assert(!_begin && !_end);
|
||||||
_begin = YES;
|
_begin = YES;
|
||||||
dispatch_semaphore_wait(_inflightSemaphore, DISPATCH_TIME_FOREVER);
|
dispatch_semaphore_wait(_inflightSemaphore, DISPATCH_TIME_FOREVER);
|
||||||
[_context begin];
|
[_context begin];
|
||||||
[self _updateUniforms];
|
[self _updateUniforms];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)drawFrame
|
- (void)endFrame
|
||||||
{
|
{
|
||||||
@autoreleasepool {
|
assert(!_begin && _end);
|
||||||
[self _render];
|
_end = NO;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_render
|
|
||||||
{
|
|
||||||
assert(_begin);
|
|
||||||
_begin = NO;
|
|
||||||
|
|
||||||
id<MTLCommandBuffer> cb = _context.commandBuffer;
|
id<MTLCommandBuffer> cb = _context.commandBuffer;
|
||||||
cb.label = @"renderer cb";
|
|
||||||
|
|
||||||
for (id<View> v in _views) {
|
|
||||||
if (!v.visible) continue;
|
|
||||||
if ([v respondsToSelector:@selector(drawWithContext:)]) {
|
|
||||||
[v drawWithContext:_context];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL pendingDraws = NO;
|
|
||||||
for (id<View> v in _views) {
|
|
||||||
if (v.visible && (v.drawState & ViewDrawStateEncoder) != 0) {
|
|
||||||
pendingDraws = YES;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pendingDraws) {
|
|
||||||
id<CAMetalDrawable> drawable = _context.nextDrawable;
|
|
||||||
_t_rpd.colorAttachments[0].texture = drawable.texture;
|
|
||||||
|
|
||||||
id<MTLRenderCommandEncoder> rce = [cb renderCommandEncoderWithDescriptor:_t_rpd];
|
|
||||||
[rce setVertexBytes:&_uniforms length:sizeof(_uniforms) atIndex:BufferIndexUniforms];
|
|
||||||
|
|
||||||
for (id<View> v in _views) {
|
|
||||||
if (!v.visible ||
|
|
||||||
![v respondsToSelector:@selector(drawWithEncoder:)] ||
|
|
||||||
(v.drawState & ViewDrawStateEncoder) == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set view state
|
|
||||||
if (v.format == RPixelFormatBGRX8Unorm || v.format == RPixelFormatB5G6R5Unorm) {
|
|
||||||
[rce setRenderPipelineState:_t_pipelineStateNoAlpha];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[rce setRenderPipelineState:_t_pipelineState];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (v.filter == RTextureFilterNearest) {
|
|
||||||
[rce setFragmentSamplerState:_samplerStateNearest atIndex:SamplerIndexDraw];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[rce setFragmentSamplerState:_samplerStateLinear atIndex:SamplerIndexDraw];
|
|
||||||
}
|
|
||||||
|
|
||||||
[v drawWithEncoder:rce];
|
|
||||||
}
|
|
||||||
|
|
||||||
[rce endEncoding];
|
|
||||||
}
|
|
||||||
|
|
||||||
__block dispatch_semaphore_t inflight = _inflightSemaphore;
|
__block dispatch_semaphore_t inflight = _inflightSemaphore;
|
||||||
[cb addCompletedHandler:^(id<MTLCommandBuffer> _) {
|
[cb addCompletedHandler:^(id<MTLCommandBuffer> _) {
|
||||||
dispatch_semaphore_signal(inflight);
|
dispatch_semaphore_signal(inflight);
|
||||||
@ -218,6 +160,68 @@
|
|||||||
[_context end];
|
[_context end];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)drawViews
|
||||||
|
{
|
||||||
|
@autoreleasepool {
|
||||||
|
assert(_begin && !_end);
|
||||||
|
_begin = NO;
|
||||||
|
_end = YES;
|
||||||
|
|
||||||
|
id<MTLCommandBuffer> cb = _context.commandBuffer;
|
||||||
|
cb.label = @"renderer cb";
|
||||||
|
|
||||||
|
for (id<View> v in _views) {
|
||||||
|
if (!v.visible) continue;
|
||||||
|
if ([v respondsToSelector:@selector(drawWithContext:)]) {
|
||||||
|
[v drawWithContext:_context];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL pendingDraws = NO;
|
||||||
|
for (id<View> v in _views) {
|
||||||
|
if (v.visible && (v.drawState & ViewDrawStateEncoder) != 0) {
|
||||||
|
pendingDraws = YES;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pendingDraws) {
|
||||||
|
id<CAMetalDrawable> drawable = _context.nextDrawable;
|
||||||
|
_t_rpd.colorAttachments[0].texture = drawable.texture;
|
||||||
|
|
||||||
|
id<MTLRenderCommandEncoder> rce = [cb renderCommandEncoderWithDescriptor:_t_rpd];
|
||||||
|
[rce setVertexBytes:&_uniforms length:sizeof(_uniforms) atIndex:BufferIndexUniforms];
|
||||||
|
|
||||||
|
for (id<View> v in _views) {
|
||||||
|
if (!v.visible ||
|
||||||
|
![v respondsToSelector:@selector(drawWithEncoder:)] ||
|
||||||
|
(v.drawState & ViewDrawStateEncoder) == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set view state
|
||||||
|
if (v.format == RPixelFormatBGRX8Unorm || v.format == RPixelFormatB5G6R5Unorm) {
|
||||||
|
[rce setRenderPipelineState:_t_pipelineStateNoAlpha];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
[rce setRenderPipelineState:_t_pipelineState];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (v.filter == RTextureFilterNearest) {
|
||||||
|
[rce setFragmentSamplerState:_samplerStateNearest atIndex:SamplerIndexDraw];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
[rce setFragmentSamplerState:_samplerStateLinear atIndex:SamplerIndexDraw];
|
||||||
|
}
|
||||||
|
|
||||||
|
[v drawWithEncoder:rce];
|
||||||
|
}
|
||||||
|
|
||||||
|
[rce endEncoding];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark - view APIs
|
#pragma mark - view APIs
|
||||||
|
|
||||||
- (void)bringViewToFront:(id<View>)view
|
- (void)bringViewToFront:(id<View>)view
|
||||||
@ -256,27 +260,4 @@
|
|||||||
_layer.drawableSize = size;
|
_layer.drawableSize = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark Matrix Math Utilities
|
|
||||||
|
|
||||||
extern inline matrix_float4x4 matrix_proj_ortho(float left, float right, float top, float bottom)
|
|
||||||
{
|
|
||||||
float near = 0;
|
|
||||||
float far = 1;
|
|
||||||
|
|
||||||
float sx = 2 / (right - left);
|
|
||||||
float sy = 2 / (top - bottom);
|
|
||||||
float sz = 1 / (far - near);
|
|
||||||
float tx = (right + left) / (left - right);
|
|
||||||
float ty = (top + bottom) / (bottom - top);
|
|
||||||
float tz = near / (far - near);
|
|
||||||
|
|
||||||
vector_float4 P = {sx, 0, 0, 0};
|
|
||||||
vector_float4 Q = {0, sy, 0, 0};
|
|
||||||
vector_float4 R = {0, 0, sz, 0};
|
|
||||||
vector_float4 S = {tx, ty, tz, 1};
|
|
||||||
|
|
||||||
matrix_float4x4 mat = {P, Q, R, S};
|
|
||||||
return mat;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -41,4 +41,6 @@ typedef NS_ENUM(NSUInteger, RTextureFilter) {
|
|||||||
RTextureFilterCount,
|
RTextureFilterCount,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern matrix_float4x4 matrix_proj_ortho(float left, float right, float top, float bottom);
|
||||||
|
|
||||||
#endif /* RendererCommon_h */
|
#endif /* RendererCommon_h */
|
||||||
|
@ -52,4 +52,23 @@ NSString *NSStringFromRPixelFormat(RPixelFormat format)
|
|||||||
return RPixelStrings[format];
|
return RPixelStrings[format];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
matrix_float4x4 matrix_proj_ortho(float left, float right, float top, float bottom)
|
||||||
|
{
|
||||||
|
float near = 0;
|
||||||
|
float far = 1;
|
||||||
|
|
||||||
|
float sx = 2 / (right - left);
|
||||||
|
float sy = 2 / (top - bottom);
|
||||||
|
float sz = 1 / (far - near);
|
||||||
|
float tx = (right + left) / (left - right);
|
||||||
|
float ty = (top + bottom) / (bottom - top);
|
||||||
|
float tz = near / (far - near);
|
||||||
|
|
||||||
|
vector_float4 P = {sx, 0, 0, 0};
|
||||||
|
vector_float4 Q = {0, sy, 0, 0};
|
||||||
|
vector_float4 R = {0, 0, sz, 0};
|
||||||
|
vector_float4 S = {tx, ty, tz, 1};
|
||||||
|
|
||||||
|
matrix_float4x4 mat = {P, Q, R, S};
|
||||||
|
return mat;
|
||||||
|
}
|
||||||
|
@ -41,6 +41,7 @@ typedef NS_ENUM(NSInteger, VertexAttribute)
|
|||||||
{
|
{
|
||||||
VertexAttributePosition = 0,
|
VertexAttributePosition = 0,
|
||||||
VertexAttributeTexcoord = 1,
|
VertexAttributeTexcoord = 1,
|
||||||
|
VertexAttributeColor = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef NS_ENUM(NSInteger, TextureIndex)
|
typedef NS_ENUM(NSInteger, TextureIndex)
|
||||||
@ -70,5 +71,17 @@ typedef struct
|
|||||||
matrix_float4x4 projectionMatrix;
|
matrix_float4x4 projectionMatrix;
|
||||||
} Uniforms;
|
} Uniforms;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
vector_float2 position METAL_ATTRIBUTE(VertexAttributePosition);
|
||||||
|
vector_float2 texCoord METAL_ATTRIBUTE(VertexAttributeTexcoord);
|
||||||
|
vector_float4 color METAL_ATTRIBUTE(VertexAttributeColor);
|
||||||
|
} FontVertex;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
vector_float4 position METAL_POSITION;
|
||||||
|
vector_float2 texCoord;
|
||||||
|
vector_float4 color;
|
||||||
|
} FontFragmentIn;
|
||||||
|
|
||||||
#endif /* ShaderTypes_h */
|
#endif /* ShaderTypes_h */
|
||||||
|
|
||||||
|
@ -53,3 +53,22 @@ fragment float4 basic_fragment_ndc_tex(ColorInOut in [[stage_in]],
|
|||||||
half4 colorSample = tex.sample(samp, in.texCoord.xy);
|
half4 colorSample = tex.sample(samp, in.texCoord.xy);
|
||||||
return float4(colorSample);
|
return float4(colorSample);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark - functions for rendering fonts
|
||||||
|
|
||||||
|
vertex FontFragmentIn font_vertex(const FontVertex in [[ stage_in ]], const device Uniforms &uniforms [[ buffer(BufferIndexUniforms) ]])
|
||||||
|
{
|
||||||
|
FontFragmentIn out;
|
||||||
|
out.position = uniforms.projectionMatrix * float4(in.position, 0, 1);
|
||||||
|
out.texCoord = in.texCoord;
|
||||||
|
out.color = in.color;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment float4 font_fragment(FontFragmentIn in [[ stage_in ]],
|
||||||
|
texture2d<half> tex [[ texture(TextureIndexColor) ]],
|
||||||
|
sampler samp [[ sampler(SamplerIndexDraw) ]])
|
||||||
|
{
|
||||||
|
half4 colorSample = tex.sample(samp, in.texCoord.xy);
|
||||||
|
return float4(in.color.rgb, in.color.a * colorSample.r);
|
||||||
|
}
|
||||||
|
@ -65,12 +65,14 @@ extern MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt);
|
|||||||
@property (readonly) MetalMenu* menu;
|
@property (readonly) MetalMenu* menu;
|
||||||
@property (readwrite) uint64_t frameCount;
|
@property (readwrite) uint64_t frameCount;
|
||||||
@property (readonly) FrameView* frameView;
|
@property (readonly) FrameView* frameView;
|
||||||
|
@property (readonly) Context* context;
|
||||||
|
|
||||||
- (instancetype)init NS_DESIGNATED_INITIALIZER;
|
- (instancetype)init NS_DESIGNATED_INITIALIZER;
|
||||||
|
|
||||||
- (void)setVideo:(const video_info_t *)video;
|
- (void)setVideo:(const video_info_t *)video;
|
||||||
|
|
||||||
- (void)beginFrame;
|
- (void)beginFrame;
|
||||||
|
- (void)drawViews;
|
||||||
- (void)endFrame;
|
- (void)endFrame;
|
||||||
|
|
||||||
/*! @brief setNeedsResize triggers a display resize */
|
/*! @brief setNeedsResize triggers a display resize */
|
||||||
|
@ -51,7 +51,6 @@
|
|||||||
FrameView *_frameView;
|
FrameView *_frameView;
|
||||||
|
|
||||||
video_info_t _video;
|
video_info_t _video;
|
||||||
bool resize_chain;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)init
|
- (instancetype)init
|
||||||
@ -66,12 +65,17 @@
|
|||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
|
RARCH_LOG("[MetalDriver]: destroyed\n");
|
||||||
if (_viewport) {
|
if (_viewport) {
|
||||||
free(_viewport);
|
free(_viewport);
|
||||||
_viewport = nil;
|
_viewport = nil;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (Context *)context {
|
||||||
|
return _renderer.context;
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark - video
|
#pragma mark - video
|
||||||
|
|
||||||
- (void)setVideo:(const video_info_t *)video
|
- (void)setVideo:(const video_info_t *)video
|
||||||
@ -100,8 +104,6 @@
|
|||||||
[_renderer sendViewToBack:_frameView];
|
[_renderer sendViewToBack:_frameView];
|
||||||
[_frameView setFilteringIndex:0 smooth:video->smooth];
|
[_frameView setFilteringIndex:0 smooth:video->smooth];
|
||||||
}
|
}
|
||||||
|
|
||||||
resize_chain = YES;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)beginFrame
|
- (void)beginFrame
|
||||||
@ -111,9 +113,13 @@
|
|||||||
[_renderer beginFrame];
|
[_renderer beginFrame];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)drawViews {
|
||||||
|
[_renderer drawViews];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)endFrame
|
- (void)endFrame
|
||||||
{
|
{
|
||||||
[_renderer drawFrame];
|
[_renderer endFrame];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setNeedsResize
|
- (void)setNeedsResize
|
||||||
@ -136,27 +142,6 @@
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern inline matrix_float4x4 matrix_proj_ortho1(float left, float right, float top, float bottom)
|
|
||||||
{
|
|
||||||
float near = 0;
|
|
||||||
float far = 1;
|
|
||||||
|
|
||||||
float sx = 2 / (right - left);
|
|
||||||
float sy = 2 / (top - bottom);
|
|
||||||
float sz = 1 / (far - near);
|
|
||||||
float tx = (right + left) / (left - right);
|
|
||||||
float ty = (top + bottom) / (bottom - top);
|
|
||||||
float tz = near / (far - near);
|
|
||||||
|
|
||||||
vector_float4 P = {sx, 0, 0, 0};
|
|
||||||
vector_float4 Q = {0, sy, 0, 0};
|
|
||||||
vector_float4 R = {0, 0, sz, 0};
|
|
||||||
vector_float4 S = {tx, ty, tz, 1};
|
|
||||||
|
|
||||||
matrix_float4x4 mat = {P, Q, R, S};
|
|
||||||
return mat;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation MetalMenu
|
@implementation MetalMenu
|
||||||
@ -295,7 +280,7 @@ typedef struct ALIGN(16)
|
|||||||
_drawState = ViewDrawStateAll;
|
_drawState = ViewDrawStateAll;
|
||||||
}
|
}
|
||||||
_visible = YES;
|
_visible = YES;
|
||||||
_engine.mvp = matrix_proj_ortho1(0, 1, 0, 1);
|
_engine.mvp = matrix_proj_ortho(0, 1, 0, 1);
|
||||||
[self _initSamplers];
|
[self _initSamplers];
|
||||||
|
|
||||||
self.size = d.size;
|
self.size = d.size;
|
||||||
|
@ -73,6 +73,8 @@ static void *metal_init(const video_info_t *video,
|
|||||||
|
|
||||||
*input = NULL;
|
*input = NULL;
|
||||||
*input_data = NULL;
|
*input_data = NULL;
|
||||||
|
|
||||||
|
font_driver_init_osd((__bridge_retained void *)md, false, video->is_threaded, FONT_DRIVER_RENDER_METAL_API);
|
||||||
|
|
||||||
return (__bridge_retained void *)md;
|
return (__bridge_retained void *)md;
|
||||||
}
|
}
|
||||||
@ -83,20 +85,40 @@ static bool metal_frame(void *data, const void *frame,
|
|||||||
unsigned pitch, const char *msg, video_frame_info_t *video_info)
|
unsigned pitch, const char *msg, video_frame_info_t *video_info)
|
||||||
{
|
{
|
||||||
MetalDriver *md = (__bridge MetalDriver *)data;
|
MetalDriver *md = (__bridge MetalDriver *)data;
|
||||||
[md beginFrame];
|
@autoreleasepool {
|
||||||
|
[md beginFrame];
|
||||||
|
|
||||||
FrameView *v = md.frameView;
|
FrameView *v = md.frameView;
|
||||||
v.frameCount = frame_count;
|
v.frameCount = frame_count;
|
||||||
v.size = CGSizeMake(frame_width, frame_height);
|
v.size = CGSizeMake(frame_width, frame_height);
|
||||||
[v updateFrame:frame pitch:pitch];
|
[v updateFrame:frame pitch:pitch];
|
||||||
|
|
||||||
#if defined(HAVE_MENU)
|
#if defined(HAVE_MENU)
|
||||||
if (md.menu.enabled) {
|
if (md.menu.enabled) {
|
||||||
menu_driver_frame(video_info);
|
menu_driver_frame(video_info);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
[md drawViews];
|
||||||
|
|
||||||
|
if (video_info->statistics_show)
|
||||||
|
{
|
||||||
|
struct font_params* osd_params = (struct font_params*)&video_info->osd_stat_params;
|
||||||
|
|
||||||
|
if (osd_params)
|
||||||
|
{
|
||||||
|
font_driver_render_msg(video_info, NULL, video_info->stat_text,
|
||||||
|
(const struct font_params*)&video_info->osd_stat_params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg && *msg)
|
||||||
|
{
|
||||||
|
font_driver_render_msg(video_info, NULL, msg, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
[md endFrame];
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
[md endFrame];
|
|
||||||
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
@ -148,6 +170,7 @@ static void metal_free(void *data)
|
|||||||
{
|
{
|
||||||
MetalDriver *md = (__bridge_transfer MetalDriver *)data;
|
MetalDriver *md = (__bridge_transfer MetalDriver *)data;
|
||||||
md = nil;
|
md = nil;
|
||||||
|
font_driver_free_osd();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void metal_set_viewport(void *data, unsigned viewport_width,
|
static void metal_set_viewport(void *data, unsigned viewport_width,
|
||||||
|
@ -22,248 +22,479 @@
|
|||||||
|
|
||||||
#include "../font_driver.h"
|
#include "../font_driver.h"
|
||||||
|
|
||||||
typedef struct {
|
@interface MetalRaster : NSObject
|
||||||
int stride;
|
|
||||||
void * mapped;
|
|
||||||
} metal_texture_t;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
{
|
||||||
const font_renderer_driver_t *font_driver;
|
const font_renderer_driver_t *_font_driver;
|
||||||
void *font_data;
|
void *_font_data;
|
||||||
metal_texture_t texture;
|
struct font_atlas *_atlas;
|
||||||
struct font_atlas *atlas;
|
|
||||||
} font_ctx_t;
|
NSUInteger _stride;
|
||||||
|
id<MTLBuffer> _buffer;
|
||||||
@interface MetalRaster: NSObject {
|
id<MTLTexture> _texture;
|
||||||
font_ctx_t *_font;
|
|
||||||
|
MTLRenderPassDescriptor *_rpd;
|
||||||
|
id<MTLRenderPipelineState> _state;
|
||||||
|
id<MTLSamplerState> _sampler;
|
||||||
|
|
||||||
|
Context *_context;
|
||||||
|
|
||||||
|
Uniforms _uniforms;
|
||||||
|
id<MTLBuffer> _vert;
|
||||||
|
unsigned _vertices;
|
||||||
}
|
}
|
||||||
|
|
||||||
@property (readwrite) MetalDriver *metal;
|
@property (readwrite) MetalDriver *metal;
|
||||||
@property (readwrite) font_ctx_t *font;
|
@property (readonly) struct font_atlas *atlas;
|
||||||
@property (readwrite) bool needsUpdate;
|
@property (readwrite) bool needsUpdate;
|
||||||
|
|
||||||
- (instancetype)initWithDriver:(MetalDriver *)metal fontPath:(const char *)font_path fontSize:(unsigned)font_size;
|
- (instancetype)initWithDriver:(MetalDriver *)metal fontPath:(const char *)font_path fontSize:(unsigned)font_size;
|
||||||
|
|
||||||
|
- (int)getWidthForMessage:(const char *)msg length:(unsigned int)length scale:(float)scale;
|
||||||
|
- (const struct font_glyph *)getGlyph:(uint32_t)code;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation MetalRaster
|
@implementation MetalRaster
|
||||||
|
|
||||||
- (instancetype)initWithDriver:(MetalDriver *)metal fontPath:(const char *)font_path fontSize:(unsigned)font_size {
|
- (instancetype)initWithDriver:(MetalDriver *)metal fontPath:(const char *)font_path fontSize:(unsigned)font_size
|
||||||
if (self = [super init])
|
{
|
||||||
{
|
if (self = [super init]) {
|
||||||
if (metal == nil)
|
if (metal == nil)
|
||||||
return nil;
|
return nil;
|
||||||
|
|
||||||
_metal = metal;
|
_metal = metal;
|
||||||
_font = (font_ctx_t *)calloc(1, sizeof(font_ctx_t));
|
_context = metal.context;
|
||||||
if (!font_renderer_create_default((const void**)&_font->font_driver,
|
if (!font_renderer_create_default((const void **)&_font_driver,
|
||||||
&_font->font_data, font_path, font_size))
|
&_font_data, font_path, font_size)) {
|
||||||
{
|
|
||||||
RARCH_WARN("Couldn't initialize font renderer.\n");
|
RARCH_WARN("Couldn't initialize font renderer.\n");
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
_font->atlas = _font->font_driver->get_atlas(_font->font_data);
|
_uniforms.projectionMatrix = matrix_proj_ortho(0, 1, 0, 1);
|
||||||
|
_atlas = _font_driver->get_atlas(_font_data);
|
||||||
// font->texture = vulkan_create_texture(font->vk, NULL,
|
_stride = _atlas->width;
|
||||||
// font->atlas->width, font->atlas->height, VK_FORMAT_R8_UNORM, font->atlas->buffer,
|
_buffer = [_context.device newBufferWithBytes:_atlas->buffer
|
||||||
// NULL /*&swizzle*/, VULKAN_TEXTURE_STAGING);
|
length:(NSUInteger)(_atlas->width * _atlas->height)
|
||||||
//
|
options:MTLResourceStorageModeManaged];
|
||||||
// vulkan_map_persistent_texture(
|
|
||||||
// font->vk->context->device, &font->texture);
|
MTLTextureDescriptor *td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatR8Unorm
|
||||||
//
|
width:_atlas->width
|
||||||
// font->texture_optimal = vulkan_create_texture(font->vk, NULL,
|
height:_atlas->height
|
||||||
// font->atlas->width, font->atlas->height, VK_FORMAT_R8_UNORM, NULL,
|
mipmapped:NO];
|
||||||
// NULL /*&swizzle*/, VULKAN_TEXTURE_DYNAMIC);
|
|
||||||
//
|
_texture = [_buffer newTextureWithDescriptor:td offset:0 bytesPerRow:_stride];
|
||||||
|
|
||||||
|
_vert = [_context.device newBufferWithLength:sizeof(FontVertex) * 500 options:MTLResourceStorageModeManaged];
|
||||||
_needsUpdate = true;
|
_needsUpdate = true;
|
||||||
|
if (![self _initializeState]) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc {
|
- (bool)_initializeState
|
||||||
if (_font) {
|
|
||||||
if (_font->font_driver && _font->font_data) {
|
|
||||||
_font->font_driver->free(_font->font_data);
|
|
||||||
_font->font_data = NULL;
|
|
||||||
_font->font_driver = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(_font);
|
|
||||||
_font = nil;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void metal_raster_font_free_font(void *data, bool is_threaded);
|
|
||||||
|
|
||||||
static void *metal_raster_font_init_font(void *data,
|
|
||||||
const char *font_path, float font_size,
|
|
||||||
bool is_threaded)
|
|
||||||
{
|
{
|
||||||
MetalRaster *r = [[MetalRaster alloc] initWithDriver:(__bridge MetalDriver *)data fontPath:font_path fontSize:font_size];
|
|
||||||
|
|
||||||
if (!r)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return (__bridge_retained void *)r;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void metal_raster_font_free_font(void *data, bool is_threaded)
|
|
||||||
{
|
|
||||||
MetalRaster * r = (__bridge_transfer MetalRaster *)data;
|
|
||||||
r = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
static INLINE void metal_raster_font_update_glyph(MetalRaster *r, const struct font_glyph *glyph)
|
|
||||||
{
|
|
||||||
font_ctx_t * font = r.font;
|
|
||||||
|
|
||||||
if(font->atlas->dirty)
|
|
||||||
{
|
{
|
||||||
|
MTLVertexDescriptor *vd = [MTLVertexDescriptor new];
|
||||||
|
vd.attributes[0].offset = 0;
|
||||||
|
vd.attributes[0].format = MTLVertexFormatFloat2;
|
||||||
|
vd.attributes[1].offset = offsetof(FontVertex, texCoord);
|
||||||
|
vd.attributes[1].format = MTLVertexFormatFloat2;
|
||||||
|
vd.attributes[2].offset = offsetof(FontVertex, color);
|
||||||
|
vd.attributes[2].format = MTLVertexFormatFloat4;
|
||||||
|
vd.layouts[0].stride = sizeof(FontVertex);
|
||||||
|
vd.layouts[0].stepFunction = MTLVertexStepFunctionPerVertex;
|
||||||
|
|
||||||
|
MTLRenderPipelineDescriptor *psd = [MTLRenderPipelineDescriptor new];
|
||||||
|
psd.label = @"font pipeline";
|
||||||
|
|
||||||
|
MTLRenderPipelineColorAttachmentDescriptor *ca = psd.colorAttachments[0];
|
||||||
|
ca.pixelFormat = MTLPixelFormatBGRA8Unorm;
|
||||||
|
ca.blendingEnabled = YES;
|
||||||
|
ca.sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha;
|
||||||
|
ca.sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
|
||||||
|
ca.destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
|
||||||
|
ca.destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
|
||||||
|
|
||||||
|
psd.sampleCount = 1;
|
||||||
|
psd.vertexDescriptor = vd;
|
||||||
|
psd.vertexFunction = [_context.library newFunctionWithName:@"font_vertex"];
|
||||||
|
psd.fragmentFunction = [_context.library newFunctionWithName:@"font_fragment"];
|
||||||
|
|
||||||
|
NSError *err;
|
||||||
|
_state = [_context.device newRenderPipelineStateWithDescriptor:psd error:&err];
|
||||||
|
if (err != nil) {
|
||||||
|
RARCH_ERR("[MetalRaster]: error creating pipeline state: %s\n", err.localizedDescription.UTF8String);
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
_rpd = [MTLRenderPassDescriptor renderPassDescriptor];
|
||||||
|
_rpd.colorAttachments[0].loadAction = MTLLoadActionDontCare;
|
||||||
|
_rpd.colorAttachments[0].storeAction = MTLStoreActionStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
MTLSamplerDescriptor *sd = [MTLSamplerDescriptor new];
|
||||||
|
sd.minFilter = MTLSamplerMinMagFilterLinear;
|
||||||
|
sd.magFilter = MTLSamplerMinMagFilterLinear;
|
||||||
|
_sampler = [_context.device newSamplerStateWithDescriptor:sd];
|
||||||
|
}
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)updateGlyph:(const struct font_glyph *)glyph
|
||||||
|
{
|
||||||
|
if (_atlas->dirty) {
|
||||||
unsigned row;
|
unsigned row;
|
||||||
for (row = glyph->atlas_offset_y; row < (glyph->atlas_offset_y + glyph->height); row++)
|
for (row = glyph->atlas_offset_y; row < (glyph->atlas_offset_y + glyph->height); row++) {
|
||||||
{
|
uint8_t *src = _atlas->buffer + row * _atlas->width + glyph->atlas_offset_x;
|
||||||
uint8_t *src = font->atlas->buffer + row * font->atlas->width + glyph->atlas_offset_x;
|
uint8_t *dst = (uint8_t *)_buffer.contents + row * _stride + glyph->atlas_offset_x;
|
||||||
uint8_t *dst = (uint8_t*)font->texture.mapped + row * font->texture.stride + glyph->atlas_offset_x;
|
|
||||||
memcpy(dst, src, glyph->width);
|
memcpy(dst, src, glyph->width);
|
||||||
}
|
}
|
||||||
|
|
||||||
font->atlas->dirty = false;
|
NSUInteger offset = glyph->atlas_offset_y;
|
||||||
r.needsUpdate = true;
|
NSUInteger len = glyph->height * _stride;
|
||||||
|
[_buffer didModifyRange:NSMakeRange(offset, len)];
|
||||||
|
|
||||||
|
_atlas->dirty = false;
|
||||||
|
_needsUpdate = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int metal_get_message_width(void *data, const char *msg,
|
- (int)getWidthForMessage:(const char *)msg length:(unsigned int)length scale:(float)scale
|
||||||
unsigned msg_len, float scale)
|
|
||||||
{
|
{
|
||||||
MetalRaster * r = (__bridge MetalRaster *)data;
|
|
||||||
font_ctx_t *font = r.font;
|
|
||||||
|
|
||||||
unsigned i;
|
|
||||||
int delta_x = 0;
|
int delta_x = 0;
|
||||||
|
|
||||||
if (!font)
|
for (unsigned i = 0; i < length; i++) {
|
||||||
return 0;
|
const struct font_glyph *glyph = _font_driver->get_glyph(_font_data, (uint8_t)msg[i]);
|
||||||
|
|
||||||
for (i = 0; i < msg_len; i++)
|
|
||||||
{
|
|
||||||
const struct font_glyph *glyph =
|
|
||||||
font->font_driver->get_glyph(font->font_data, (uint8_t)msg[i]);
|
|
||||||
if (!glyph) /* Do something smarter here ... */
|
if (!glyph) /* Do something smarter here ... */
|
||||||
glyph = font->font_driver->get_glyph(font->font_data, '?');
|
glyph = _font_driver->get_glyph(_font_data, '?');
|
||||||
|
|
||||||
|
|
||||||
if (glyph)
|
if (glyph) {
|
||||||
{
|
[self updateGlyph:glyph];
|
||||||
metal_raster_font_update_glyph(r, glyph);
|
|
||||||
delta_x += glyph->advance_x;
|
delta_x += glyph->advance_x;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return delta_x * scale;
|
return delta_x * scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void metal_raster_font_render_line(
|
- (const struct font_glyph *)getGlyph:(uint32_t)code
|
||||||
MetalRaster *r, const char *msg, unsigned msg_len,
|
|
||||||
float scale, const float color[4], float pos_x,
|
|
||||||
float pos_y, unsigned text_align)
|
|
||||||
{
|
{
|
||||||
|
if (!_font_driver->ident)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
const struct font_glyph *glyph = _font_driver->get_glyph((void *)_font_driver, code);
|
||||||
|
if (glyph) {
|
||||||
|
[self updateGlyph:glyph];
|
||||||
|
}
|
||||||
|
|
||||||
|
return glyph;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void metal_raster_font_render_message(
|
typedef struct color
|
||||||
MetalRaster *r, const char *msg, float scale,
|
|
||||||
const float color[4], float pos_x, float pos_y,
|
|
||||||
unsigned text_align)
|
|
||||||
{
|
{
|
||||||
font_ctx_t *font = r.font;
|
float r, g, b, a;
|
||||||
|
} color_t;
|
||||||
|
|
||||||
int lines = 0;
|
static INLINE void write_quad(FontVertex *pv,
|
||||||
float line_height;
|
float x, float y, float width, float height,
|
||||||
|
float tex_x, float tex_y, float tex_width, float tex_height,
|
||||||
|
const vector_float4 *color)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
static const float strip[2 * 6] = {
|
||||||
|
0.0f, 0.0f,
|
||||||
|
0.0f, 1.0f,
|
||||||
|
1.0f, 0.0f,
|
||||||
|
1.0f, 1.0f,
|
||||||
|
1.0f, 0.0f,
|
||||||
|
0.0f, 1.0f,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (i = 0; i < 6; i++) {
|
||||||
|
pv[i].position.x = x + strip[2 * i + 0] * width;
|
||||||
|
pv[i].position.y = y + strip[2 * i + 1] * height;
|
||||||
|
pv[i].texCoord.x = tex_x + strip[2 * i + 0] * tex_width;
|
||||||
|
pv[i].texCoord.y = tex_y + strip[2 * i + 1] * tex_height;
|
||||||
|
pv[i].color = *color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!msg || !*msg || !r.metal)
|
- (void)_renderLine:(const char *)msg
|
||||||
return;
|
video:(video_frame_info_t *)video
|
||||||
|
length:(NSUInteger)length
|
||||||
|
scale:(float)scale
|
||||||
|
color:(vector_float4)color
|
||||||
|
posX:(float)posX
|
||||||
|
posY:(float)posY
|
||||||
|
aligned:(unsigned)aligned
|
||||||
|
{
|
||||||
|
const char* msg_end = msg + length;
|
||||||
|
int x = roundf(posX * _metal.viewport->width);
|
||||||
|
int y = roundf((1.0f - posY) * _metal.viewport->height);
|
||||||
|
int delta_x = 0;
|
||||||
|
int delta_y = 0;
|
||||||
|
float inv_tex_size_x = 1.0f / _texture.width;
|
||||||
|
float inv_tex_size_y = 1.0f / _texture.height;
|
||||||
|
float inv_win_width = 1.0f / _metal.viewport->width;
|
||||||
|
float inv_win_height = 1.0f / _metal.viewport->height;
|
||||||
|
|
||||||
|
switch (aligned) {
|
||||||
|
case TEXT_ALIGN_RIGHT:
|
||||||
|
x -= [self getWidthForMessage:msg length:length scale:scale];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TEXT_ALIGN_CENTER:
|
||||||
|
x -= [self getWidthForMessage:msg length:length scale:scale] / 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
FontVertex *v = (FontVertex *)_vert.contents;
|
||||||
|
|
||||||
|
while (msg < msg_end) {
|
||||||
|
unsigned code = utf8_walk(&msg);
|
||||||
|
const struct font_glyph *glyph = _font_driver->get_glyph(_font_data, code);
|
||||||
|
|
||||||
|
if (!glyph) /* Do something smarter here ... */
|
||||||
|
glyph = _font_driver->get_glyph(_font_data, '?');
|
||||||
|
|
||||||
|
if (!glyph)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
[self updateGlyph:glyph];
|
||||||
|
|
||||||
|
int off_x, off_y, tex_x, tex_y, width, height;
|
||||||
|
off_x = glyph->draw_offset_x;
|
||||||
|
off_y = glyph->draw_offset_y;
|
||||||
|
tex_x = glyph->atlas_offset_x;
|
||||||
|
tex_y = glyph->atlas_offset_y;
|
||||||
|
width = glyph->width;
|
||||||
|
height = glyph->height;
|
||||||
|
|
||||||
|
write_quad(v + _vertices,
|
||||||
|
(x + off_x + delta_x * scale) * inv_win_width,
|
||||||
|
(y + off_y + delta_y * scale) * inv_win_height,
|
||||||
|
width * scale * inv_win_width,
|
||||||
|
height * scale * inv_win_height,
|
||||||
|
tex_x * inv_tex_size_x,
|
||||||
|
tex_y * inv_tex_size_y,
|
||||||
|
width * inv_tex_size_x,
|
||||||
|
height * inv_tex_size_y,
|
||||||
|
&color);
|
||||||
|
|
||||||
|
_vertices += 6;
|
||||||
|
|
||||||
|
delta_x += glyph->advance_x;
|
||||||
|
delta_y += glyph->advance_y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)_flush {
|
||||||
|
[_vert didModifyRange:NSMakeRange(0, sizeof(FontVertex)*_vertices)];
|
||||||
|
_rpd.colorAttachments[0].texture = _context.nextDrawable.texture;
|
||||||
|
|
||||||
|
id<MTLCommandBuffer> cb = _context.commandBuffer;
|
||||||
|
id<MTLRenderCommandEncoder> rce = [cb renderCommandEncoderWithDescriptor:_rpd];
|
||||||
|
[rce pushDebugGroup:@"render fonts"];
|
||||||
|
[rce setRenderPipelineState:_state];
|
||||||
|
[rce setVertexBytes:&_uniforms length:sizeof(_uniforms) atIndex:BufferIndexUniforms];
|
||||||
|
[rce setVertexBuffer:_vert offset:0 atIndex:BufferIndexPositions];
|
||||||
|
[rce setFragmentTexture:_texture atIndex:TextureIndexColor];
|
||||||
|
[rce setFragmentSamplerState:_sampler atIndex:SamplerIndexDraw];
|
||||||
|
[rce drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:_vertices];
|
||||||
|
[rce popDebugGroup];
|
||||||
|
[rce endEncoding];
|
||||||
|
_vertices = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)renderMessage:(const char *)msg
|
||||||
|
video:(video_frame_info_t *)video
|
||||||
|
scale:(float)scale
|
||||||
|
color:(vector_float4)color
|
||||||
|
posX:(float)posX
|
||||||
|
posY:(float)posY
|
||||||
|
aligned:(unsigned)aligned
|
||||||
|
{
|
||||||
/* If the font height is not supported just draw as usual */
|
/* If the font height is not supported just draw as usual */
|
||||||
if (!font->font_driver->get_line_height)
|
if (!_font_driver->get_line_height) {
|
||||||
{
|
[self _renderLine:msg video:video length:strlen(msg) scale:scale color:color posX:posX posY:posY aligned:aligned];
|
||||||
if (r.metal)
|
|
||||||
metal_raster_font_render_line(r, msg, (unsigned)strlen(msg),
|
|
||||||
scale, color, pos_x, pos_y, text_align);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
line_height = (float) font->font_driver->get_line_height(font->font_data) *
|
int lines = 0;
|
||||||
scale / r.metal.viewport->height;
|
float line_height = _font_driver->get_line_height(_font_data) * scale / video->height;
|
||||||
|
|
||||||
for (;;)
|
for (;;) {
|
||||||
{
|
|
||||||
const char *delim = strchr(msg, '\n');
|
const char *delim = strchr(msg, '\n');
|
||||||
|
|
||||||
/* Draw the line */
|
/* Draw the line */
|
||||||
if (delim)
|
if (delim) {
|
||||||
{
|
unsigned msg_len = delim - msg;
|
||||||
unsigned msg_len = (unsigned)(delim - msg);
|
[self _renderLine:msg
|
||||||
if (r.metal)
|
video:video
|
||||||
metal_raster_font_render_line(r, msg, msg_len,
|
length:msg_len
|
||||||
scale, color, pos_x, pos_y - (float)lines * line_height,
|
scale:scale
|
||||||
text_align);
|
color:color
|
||||||
|
posX:posX
|
||||||
|
posY:posY - (float)lines * line_height
|
||||||
|
aligned:aligned];
|
||||||
msg += msg_len + 1;
|
msg += msg_len + 1;
|
||||||
lines++;
|
lines++;
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
unsigned msg_len = strlen(msg);
|
||||||
unsigned msg_len = (unsigned)strlen(msg);
|
[self _renderLine:msg
|
||||||
if (r.metal)
|
video:video
|
||||||
metal_raster_font_render_line(r, msg, msg_len,
|
length:msg_len
|
||||||
scale, color, pos_x, pos_y - (float)lines * line_height,
|
scale:scale
|
||||||
text_align);
|
color:color
|
||||||
|
posX:posX
|
||||||
|
posY:posY - (float)lines * line_height
|
||||||
|
aligned:aligned];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void metal_raster_font_render_msg(
|
- (void)renderMessage:(const char *)msg
|
||||||
video_frame_info_t *video_info,
|
video:(video_frame_info_t *)video
|
||||||
void *data, const char *msg,
|
params:(const struct font_params *)params
|
||||||
const struct font_params *params)
|
|
||||||
{
|
{
|
||||||
MetalRaster *r = (__bridge MetalRaster *)data;
|
|
||||||
|
|
||||||
if (!r || !msg || !*msg)
|
if (!msg || !*msg)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
float x, y, scale, drop_mod, drop_alpha;
|
||||||
|
int drop_x, drop_y;
|
||||||
|
enum text_alignment text_align;
|
||||||
|
vector_float4 color, color_dark;
|
||||||
|
unsigned width = video->width;
|
||||||
|
unsigned height = video->height;
|
||||||
|
|
||||||
|
if (params) {
|
||||||
|
x = params->x;
|
||||||
|
y = params->y;
|
||||||
|
scale = params->scale;
|
||||||
|
text_align = params->text_align;
|
||||||
|
drop_x = params->drop_x;
|
||||||
|
drop_y = params->drop_y;
|
||||||
|
drop_mod = params->drop_mod;
|
||||||
|
drop_alpha = params->drop_alpha;
|
||||||
|
color.x = FONT_COLOR_GET_RED(params->color) / 255.0f;
|
||||||
|
color.y = FONT_COLOR_GET_GREEN(params->color) / 255.0f;
|
||||||
|
color.z = FONT_COLOR_GET_BLUE(params->color) / 255.0f;
|
||||||
|
color.w = FONT_COLOR_GET_ALPHA(params->color) / 255.0f;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
x = video->font_msg_pos_x;
|
||||||
|
y = video->font_msg_pos_y;
|
||||||
|
scale = 1.0f;
|
||||||
|
text_align = TEXT_ALIGN_LEFT;
|
||||||
|
|
||||||
|
color.x = video->font_msg_color_r;
|
||||||
|
color.y = video->font_msg_color_g;
|
||||||
|
color.z = video->font_msg_color_b;
|
||||||
|
color.w = 1.0;
|
||||||
|
|
||||||
|
drop_x = -2;
|
||||||
|
drop_y = -2;
|
||||||
|
drop_mod = 0.3f;
|
||||||
|
drop_alpha = 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
@autoreleasepool {
|
||||||
|
|
||||||
|
NSUInteger max_glyphs = strlen(msg);
|
||||||
|
if (drop_x || drop_y)
|
||||||
|
max_glyphs *= 2;
|
||||||
|
|
||||||
|
NSUInteger needed = sizeof(FontVertex) * max_glyphs * 6;
|
||||||
|
if (_vert.length < needed)
|
||||||
|
{
|
||||||
|
_vert = [_context.device newBufferWithLength:needed options:MTLResourceStorageModeManaged];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (drop_x || drop_y) {
|
||||||
|
color_dark.x = color.x * drop_mod;
|
||||||
|
color_dark.y = color.y * drop_mod;
|
||||||
|
color_dark.z = color.z * drop_mod;
|
||||||
|
color_dark.w = color.w * drop_alpha;
|
||||||
|
|
||||||
|
[self renderMessage:msg
|
||||||
|
video:video
|
||||||
|
scale:scale
|
||||||
|
color:color_dark
|
||||||
|
posX:x + scale * drop_x / width
|
||||||
|
posY:y + scale * drop_y / height
|
||||||
|
aligned:text_align];
|
||||||
|
}
|
||||||
|
|
||||||
|
[self renderMessage:msg
|
||||||
|
video:video
|
||||||
|
scale:scale
|
||||||
|
color:color
|
||||||
|
posX:x
|
||||||
|
posY:y
|
||||||
|
aligned:text_align];
|
||||||
|
|
||||||
|
[self _flush];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
static void metal_raster_font_free_font(void *data, bool is_threaded);
|
||||||
|
|
||||||
|
static void *metal_raster_font_init_font(void *data,
|
||||||
|
const char *font_path, float font_size,
|
||||||
|
bool is_threaded)
|
||||||
|
{
|
||||||
|
MetalRaster *r = [[MetalRaster alloc] initWithDriver:(__bridge_transfer MetalDriver *)data fontPath:font_path fontSize:(unsigned)font_size];
|
||||||
|
|
||||||
|
if (!r)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return (__bridge_retained void *)r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void metal_raster_font_free_font(void *data, bool is_threaded)
|
||||||
|
{
|
||||||
|
MetalRaster *r = (__bridge_transfer MetalRaster *)data;
|
||||||
|
r = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int metal_get_message_width(void *data, const char *msg,
|
||||||
|
unsigned msg_len, float scale)
|
||||||
|
{
|
||||||
|
MetalRaster *r = (__bridge MetalRaster *)data;
|
||||||
|
return [r getWidthForMessage:msg length:msg_len scale:scale];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void metal_raster_font_render_msg(
|
||||||
|
video_frame_info_t *video_info,
|
||||||
|
void *data, const char *msg,
|
||||||
|
const struct font_params *params)
|
||||||
|
{
|
||||||
|
MetalRaster *r = (__bridge MetalRaster *)data;
|
||||||
|
[r renderMessage:msg video:video_info params:params];
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct font_glyph *metal_raster_font_get_glyph(
|
static const struct font_glyph *metal_raster_font_get_glyph(
|
||||||
void *data, uint32_t code)
|
void *data, uint32_t code)
|
||||||
{
|
{
|
||||||
const struct font_glyph* glyph;
|
MetalRaster *r = (__bridge MetalRaster *)data;
|
||||||
MetalRaster * r = (__bridge MetalRaster *)data;
|
return [r getGlyph:code];
|
||||||
font_ctx_t *font = r.font;
|
|
||||||
|
|
||||||
if (!font || !font->font_driver)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (!font->font_driver->ident)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
glyph = font->font_driver->get_glyph((void*)font->font_driver, code);
|
|
||||||
|
|
||||||
if(glyph)
|
|
||||||
metal_raster_font_update_glyph(r, glyph);
|
|
||||||
|
|
||||||
return glyph;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void metal_raster_font_flush_block(unsigned width, unsigned height,
|
static void metal_raster_font_flush_block(unsigned width, unsigned height,
|
||||||
void *data, video_frame_info_t *video_info)
|
void *data, video_frame_info_t *video_info)
|
||||||
{
|
{
|
||||||
(void)data;
|
(void)data;
|
||||||
}
|
}
|
||||||
@ -274,12 +505,12 @@ static void metal_raster_font_bind_block(void *data, void *userdata)
|
|||||||
}
|
}
|
||||||
|
|
||||||
font_renderer_t metal_raster_font = {
|
font_renderer_t metal_raster_font = {
|
||||||
metal_raster_font_init_font,
|
.init = metal_raster_font_init_font,
|
||||||
metal_raster_font_free_font,
|
.free = metal_raster_font_free_font,
|
||||||
metal_raster_font_render_msg,
|
.render_msg = metal_raster_font_render_msg,
|
||||||
"Metal raster",
|
.ident = "Metal raster",
|
||||||
metal_raster_font_get_glyph,
|
.get_glyph = metal_raster_font_get_glyph,
|
||||||
metal_raster_font_bind_block,
|
.bind_block = metal_raster_font_bind_block,
|
||||||
metal_raster_font_flush_block,
|
.flush = metal_raster_font_flush_block,
|
||||||
metal_get_message_width
|
.get_message_width = metal_get_message_width
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user