mirror of
https://github.com/libretro/RetroArch.git
synced 2024-10-08 23:23:27 +00:00
Merge pull request #7176 from stuartcarnie/sgc-metal
Metal: Add support for screen shots
This commit is contained in:
commit
9c53dc3797
@ -34,6 +34,9 @@ typedef struct
|
|||||||
/*! @brief Specifies whether rendering is synchronized with the display */
|
/*! @brief Specifies whether rendering is synchronized with the display */
|
||||||
@property (nonatomic, readwrite) bool displaySyncEnabled;
|
@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,
|
/*! @brief Returns the command buffer used for pre-render work,
|
||||||
* such as mip maps and shader effects
|
* such as mip maps and shader effects
|
||||||
* */
|
* */
|
||||||
@ -70,4 +73,6 @@ typedef struct
|
|||||||
/*! @brief end commits the command buffer */
|
/*! @brief end commits the command buffer */
|
||||||
- (void)end;
|
- (void)end;
|
||||||
|
|
||||||
|
- (bool)readBackBuffer:(uint8_t *)buffer;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -54,6 +54,9 @@
|
|||||||
id<MTLRenderPipelineState> _states[GFX_MAX_SHADERS][2];
|
id<MTLRenderPipelineState> _states[GFX_MAX_SHADERS][2];
|
||||||
id<MTLRenderPipelineState> _clearState;
|
id<MTLRenderPipelineState> _clearState;
|
||||||
Uniforms _uniforms;
|
Uniforms _uniforms;
|
||||||
|
|
||||||
|
bool _captureEnabled;
|
||||||
|
id<MTLTexture> _backBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithDevice:(id<MTLDevice>)d
|
- (instancetype)initWithDevice:(id<MTLDevice>)d
|
||||||
@ -470,11 +473,65 @@
|
|||||||
[_chain[_currentChain] discard];
|
[_chain[_currentChain] discard];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setCaptureEnabled:(bool)captureEnabled
|
||||||
|
{
|
||||||
|
if (_captureEnabled == captureEnabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_captureEnabled = captureEnabled;
|
||||||
|
//_layer.framebufferOnly = !captureEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (bool)captureEnabled
|
||||||
|
{
|
||||||
|
return _captureEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (bool)readBackBuffer:(uint8_t *)buffer
|
||||||
|
{
|
||||||
|
if (!_captureEnabled || _backBuffer == nil)
|
||||||
|
return NO;
|
||||||
|
|
||||||
|
if (_backBuffer.pixelFormat != MTLPixelFormatBGRA8Unorm)
|
||||||
|
{
|
||||||
|
RARCH_WARN("[Metal]: unexpected pixel format %d\n", _backBuffer.pixelFormat);
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *tmp = malloc(_backBuffer.width * _backBuffer.height * 4);
|
||||||
|
|
||||||
|
[_backBuffer getBytes:tmp
|
||||||
|
bytesPerRow:4 * _backBuffer.width
|
||||||
|
fromRegion:MTLRegionMake2D(0, 0, _backBuffer.width, _backBuffer.height)
|
||||||
|
mipmapLevel:0];
|
||||||
|
|
||||||
|
NSUInteger srcStride = _backBuffer.width * 4;
|
||||||
|
uint8_t const *src = tmp + (_viewport.y * srcStride);
|
||||||
|
|
||||||
|
NSUInteger dstStride = _viewport.width * 3;
|
||||||
|
uint8_t *dst = buffer + (_viewport.height - 1) * dstStride;
|
||||||
|
|
||||||
|
for (int y = _viewport.y; y < _viewport.height; y++, src += srcStride, dst -= dstStride)
|
||||||
|
{
|
||||||
|
for (int x = _viewport.x; x < _viewport.width; x++)
|
||||||
|
{
|
||||||
|
dst[3 * x + 0] = src[4 * x + 0];
|
||||||
|
dst[3 * x + 1] = src[4 * x + 1];
|
||||||
|
dst[3 * x + 2] = src[4 * x + 2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(tmp);
|
||||||
|
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)begin
|
- (void)begin
|
||||||
{
|
{
|
||||||
assert(_commandBuffer == nil);
|
assert(_commandBuffer == nil);
|
||||||
dispatch_semaphore_wait(_inflightSemaphore, DISPATCH_TIME_FOREVER);
|
dispatch_semaphore_wait(_inflightSemaphore, DISPATCH_TIME_FOREVER);
|
||||||
_commandBuffer = [_commandQueue commandBuffer];
|
_commandBuffer = [_commandQueue commandBuffer];
|
||||||
|
_backBuffer = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id<MTLRenderCommandEncoder>)rce
|
- (id<MTLRenderCommandEncoder>)rce
|
||||||
@ -486,6 +543,10 @@
|
|||||||
rpd.colorAttachments[0].clearColor = _clearColor;
|
rpd.colorAttachments[0].clearColor = _clearColor;
|
||||||
rpd.colorAttachments[0].loadAction = MTLLoadActionClear;
|
rpd.colorAttachments[0].loadAction = MTLLoadActionClear;
|
||||||
rpd.colorAttachments[0].texture = self.nextDrawable.texture;
|
rpd.colorAttachments[0].texture = self.nextDrawable.texture;
|
||||||
|
if (_captureEnabled)
|
||||||
|
{
|
||||||
|
_backBuffer = self.nextDrawable.texture;
|
||||||
|
}
|
||||||
_rce = [_commandBuffer renderCommandEncoderWithDescriptor:rpd];
|
_rce = [_commandBuffer renderCommandEncoderWithDescriptor:rpd];
|
||||||
}
|
}
|
||||||
return _rce;
|
return _rce;
|
||||||
|
@ -55,6 +55,12 @@ typedef struct
|
|||||||
vector_float2 texCoord METAL_ATTRIBUTE(VertexAttributeTexcoord);
|
vector_float2 texCoord METAL_ATTRIBUTE(VertexAttributeTexcoord);
|
||||||
} Vertex;
|
} Vertex;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
vector_float4 position;
|
||||||
|
vector_float2 texCoord;
|
||||||
|
} VertexSlang;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
vector_float4 position METAL_POSITION;
|
vector_float4 position METAL_POSITION;
|
||||||
|
@ -40,6 +40,7 @@ extern MTLPixelFormat SelectOptimalPixelFormat(MTLPixelFormat fmt);
|
|||||||
- (void)setFilteringIndex:(int)index smooth:(bool)smooth;
|
- (void)setFilteringIndex:(int)index smooth:(bool)smooth;
|
||||||
- (BOOL)setShaderFromPath:(NSString *)path;
|
- (BOOL)setShaderFromPath:(NSString *)path;
|
||||||
- (void)updateFrame:(void const *)src pitch:(NSUInteger)pitch;
|
- (void)updateFrame:(void const *)src pitch:(NSUInteger)pitch;
|
||||||
|
- (bool)readViewport:(uint8_t *)buffer isIdle:(bool)isIdle;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@ -546,6 +546,7 @@ typedef struct MTLALIGN(16)
|
|||||||
Context *_context;
|
Context *_context;
|
||||||
id<MTLTexture> _texture; // final render texture
|
id<MTLTexture> _texture; // final render texture
|
||||||
Vertex _v[4];
|
Vertex _v[4];
|
||||||
|
VertexSlang _vertex[4];
|
||||||
CGSize _size; // size of view in pixels
|
CGSize _size; // size of view in pixels
|
||||||
CGRect _frame;
|
CGRect _frame;
|
||||||
NSUInteger _bpp;
|
NSUInteger _bpp;
|
||||||
@ -587,6 +588,15 @@ typedef struct MTLALIGN(16)
|
|||||||
self.size = d.size;
|
self.size = d.size;
|
||||||
self.frame = CGRectMake(0, 0, 1, 1);
|
self.frame = CGRectMake(0, 0, 1, 1);
|
||||||
resize_render_targets = YES;
|
resize_render_targets = YES;
|
||||||
|
|
||||||
|
// init slang vertex buffer
|
||||||
|
VertexSlang v[4] = {
|
||||||
|
{simd_make_float4(0, 1, 0, 1), simd_make_float2(0, 1)},
|
||||||
|
{simd_make_float4(1, 1, 0, 1), simd_make_float2(1, 1)},
|
||||||
|
{simd_make_float4(0, 0, 0, 1), simd_make_float2(0, 0)},
|
||||||
|
{simd_make_float4(1, 0, 0, 1), simd_make_float2(1, 0)},
|
||||||
|
};
|
||||||
|
memcpy(_vertex, v, sizeof(_vertex));
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
@ -750,6 +760,24 @@ typedef struct MTLALIGN(16)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (bool)readViewport:(uint8_t *)buffer isIdle:(bool)isIdle
|
||||||
|
{
|
||||||
|
RARCH_LOG("[Metal]: readViewport is_idle = %s\n", isIdle ? "YES" : "NO");
|
||||||
|
|
||||||
|
bool enabled = _context.captureEnabled;
|
||||||
|
if (!enabled)
|
||||||
|
_context.captureEnabled = YES;
|
||||||
|
|
||||||
|
video_driver_cached_frame();
|
||||||
|
|
||||||
|
bool res = [_context readBackBuffer:buffer];
|
||||||
|
|
||||||
|
if (!enabled)
|
||||||
|
_context.captureEnabled = NO;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)updateFrame:(void const *)src pitch:(NSUInteger)pitch
|
- (void)updateFrame:(void const *)src pitch:(NSUInteger)pitch
|
||||||
{
|
{
|
||||||
if (_shader && (_engine.frame.output_size.x != _viewport->width ||
|
if (_shader && (_engine.frame.output_size.x != _viewport->width ||
|
||||||
@ -816,19 +844,6 @@ typedef struct MTLALIGN(16)
|
|||||||
init_history = NO;
|
init_history = NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct vertex
|
|
||||||
{
|
|
||||||
simd_float4 pos;
|
|
||||||
simd_float2 tex;
|
|
||||||
} vertex_t;
|
|
||||||
|
|
||||||
static vertex_t vertex_bytes[] = {
|
|
||||||
{{0, 1, 0, 1}, {0, 1}},
|
|
||||||
{{1, 1, 0, 1}, {1, 1}},
|
|
||||||
{{0, 0, 0, 1}, {0, 0}},
|
|
||||||
{{1, 0, 0, 1}, {1, 0}},
|
|
||||||
};
|
|
||||||
|
|
||||||
- (void)drawWithEncoder:(id<MTLRenderCommandEncoder>)rce
|
- (void)drawWithEncoder:(id<MTLRenderCommandEncoder>)rce
|
||||||
{
|
{
|
||||||
if (_texture)
|
if (_texture)
|
||||||
@ -942,7 +957,7 @@ static vertex_t vertex_bytes[] = {
|
|||||||
|
|
||||||
[rce setFragmentTextures:textures withRange:NSMakeRange(0, SLANG_NUM_BINDINGS)];
|
[rce setFragmentTextures:textures withRange:NSMakeRange(0, SLANG_NUM_BINDINGS)];
|
||||||
[rce setFragmentSamplerStates:samplers withRange:NSMakeRange(0, SLANG_NUM_BINDINGS)];
|
[rce setFragmentSamplerStates:samplers withRange:NSMakeRange(0, SLANG_NUM_BINDINGS)];
|
||||||
[rce setVertexBytes:vertex_bytes length:sizeof(vertex_bytes) atIndex:4];
|
[rce setVertexBytes:_vertex length:sizeof(_vertex) atIndex:4];
|
||||||
[rce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
|
[rce drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
|
||||||
|
|
||||||
if (!backBuffer)
|
if (!backBuffer)
|
||||||
@ -1162,13 +1177,13 @@ static vertex_t vertex_bytes[] = {
|
|||||||
@try
|
@try
|
||||||
{
|
{
|
||||||
MTLVertexDescriptor *vd = [MTLVertexDescriptor new];
|
MTLVertexDescriptor *vd = [MTLVertexDescriptor new];
|
||||||
vd.attributes[0].offset = offsetof(vertex_t, pos);
|
vd.attributes[0].offset = offsetof(VertexSlang, position);
|
||||||
vd.attributes[0].format = MTLVertexFormatFloat4;
|
vd.attributes[0].format = MTLVertexFormatFloat4;
|
||||||
vd.attributes[0].bufferIndex = 4;
|
vd.attributes[0].bufferIndex = 4;
|
||||||
vd.attributes[1].offset = offsetof(vertex_t, tex);
|
vd.attributes[1].offset = offsetof(VertexSlang, texCoord);
|
||||||
vd.attributes[1].format = MTLVertexFormatFloat2;
|
vd.attributes[1].format = MTLVertexFormatFloat2;
|
||||||
vd.attributes[1].bufferIndex = 4;
|
vd.attributes[1].bufferIndex = 4;
|
||||||
vd.layouts[4].stride = sizeof(vertex_t);
|
vd.layouts[4].stride = sizeof(VertexSlang);
|
||||||
vd.layouts[4].stepFunction = MTLVertexStepFunctionPerVertex;
|
vd.layouts[4].stepFunction = MTLVertexStepFunctionPerVertex;
|
||||||
|
|
||||||
MTLRenderPipelineDescriptor *psd = [MTLRenderPipelineDescriptor new];
|
MTLRenderPipelineDescriptor *psd = [MTLRenderPipelineDescriptor new];
|
||||||
|
@ -163,7 +163,8 @@ static void metal_viewport_info(void *data, struct video_viewport *vp)
|
|||||||
|
|
||||||
static bool metal_read_viewport(void *data, uint8_t *buffer, bool is_idle)
|
static bool metal_read_viewport(void *data, uint8_t *buffer, bool is_idle)
|
||||||
{
|
{
|
||||||
return true;
|
MetalDriver *md = (__bridge MetalDriver *)data;
|
||||||
|
return [md.frameView readViewport:buffer isIdle:is_idle];
|
||||||
}
|
}
|
||||||
|
|
||||||
static uintptr_t metal_load_texture(void *video_data, void *data,
|
static uintptr_t metal_load_texture(void *video_data, void *data,
|
||||||
|
Loading…
Reference in New Issue
Block a user