unify a little bit more iOS & MacOS
This commit is contained in:
parent
75de20e4cc
commit
80a7a33b7c
@ -72,15 +72,6 @@ mfb_open_ex(const char *title, unsigned width, unsigned height, unsigned flags)
|
||||
if (window_data == 0x0) {
|
||||
return 0x0;
|
||||
}
|
||||
SWindowData_IOS *window_data_ios = (SWindowData_IOS *) window_data->specific;
|
||||
|
||||
static Vertex s_vertices[4] = {
|
||||
{-1.0, -1.0, 0, 1},
|
||||
{-1.0, 1.0, 0, 1},
|
||||
{ 1.0, -1.0, 0, 1},
|
||||
{ 1.0, 1.0, 0, 1},
|
||||
};
|
||||
memcpy(window_data_ios->vertices, s_vertices, sizeof(s_vertices));
|
||||
|
||||
windows = [[UIApplication sharedApplication] windows];
|
||||
numWindows = [windows count];
|
||||
|
@ -8,15 +8,12 @@
|
||||
|
||||
#import <MetalKit/MetalKit.h>
|
||||
#include "WindowData.h"
|
||||
#include "WindowData_IOS.h"
|
||||
|
||||
// Our platform independent renderer class.
|
||||
// Implements the MTKViewDelegate protocol which allows it to accept per-frame
|
||||
// update and drawable resize callbacks.
|
||||
@interface iOSViewDelegate : NSObject <MTKViewDelegate>
|
||||
{
|
||||
@public SWindowData *window_data;
|
||||
@public SWindowData_IOS *window_data_ios;
|
||||
}
|
||||
|
||||
-(nonnull instancetype) initWithMetalKitView:(nonnull MTKView *) view windowData:(nonnull SWindowData *) windowData;
|
||||
|
@ -21,25 +21,23 @@
|
||||
//-------------------------------------
|
||||
enum { MaxBuffersInFlight = 3 }; // Number of textures in flight (tripple buffered)
|
||||
|
||||
id<MTLDevice> g_metal_device = nil;
|
||||
id<MTLLibrary> g_library = nil;
|
||||
|
||||
//--
|
||||
NSString *g_shader_src = kShader(
|
||||
"#include <metal_stdlib>\n",
|
||||
using namespace metal;
|
||||
|
||||
//---------------------
|
||||
//-------------
|
||||
struct VertexOutput {
|
||||
float4 pos [[position]];
|
||||
float2 texcoord;
|
||||
};
|
||||
|
||||
//-------------
|
||||
struct Vertex {
|
||||
float4 position [[position]];
|
||||
};
|
||||
|
||||
//---------------------
|
||||
//-------------
|
||||
vertex VertexOutput
|
||||
vertFunc(unsigned int vID[[vertex_id]], const device Vertex *pos [[ buffer(0) ]]) {
|
||||
VertexOutput out;
|
||||
@ -52,7 +50,7 @@ NSString *g_shader_src = kShader(
|
||||
return out;
|
||||
}
|
||||
|
||||
//---------------------
|
||||
//-------------
|
||||
fragment float4
|
||||
fragFunc(VertexOutput input [[stage_in]], texture2d<half> colorTexture [[ texture(0) ]]) {
|
||||
constexpr sampler textureSampler(mag_filter::nearest, min_filter::nearest);
|
||||
@ -67,29 +65,38 @@ NSString *g_shader_src = kShader(
|
||||
|
||||
//-------------------------------------
|
||||
@implementation iOSViewDelegate {
|
||||
dispatch_semaphore_t m_semaphore;
|
||||
id <MTLCommandQueue> m_command_queue;
|
||||
SWindowData *window_data;
|
||||
SWindowData_IOS *window_data_ios;
|
||||
|
||||
id <MTLRenderPipelineState> m_pipeline_state;
|
||||
id <MTLTexture> m_texture_buffer;
|
||||
id<MTLDevice> metal_device;
|
||||
id<MTLLibrary> metal_library;
|
||||
|
||||
uint8_t m_current_buffer;
|
||||
dispatch_semaphore_t semaphore;
|
||||
id<MTLCommandQueue> command_queue;
|
||||
|
||||
id<MTLRenderPipelineState> pipeline_state;
|
||||
id<MTLTexture> texture_buffer;
|
||||
|
||||
uint8_t current_buffer;
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
-(nonnull instancetype) initWithMetalKitView:(nonnull MTKView *) view windowData:(nonnull SWindowData *) windowData {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self->window_data = windowData;
|
||||
self->window_data_ios = (SWindowData_IOS *) windowData->specific;
|
||||
window_data = windowData;
|
||||
window_data_ios = (SWindowData_IOS *) windowData->specific;
|
||||
|
||||
view.colorPixelFormat = MTLPixelFormatBGRA8Unorm;
|
||||
view.sampleCount = 1;
|
||||
|
||||
g_metal_device = view.device;
|
||||
metal_device = view.device;
|
||||
|
||||
m_semaphore = dispatch_semaphore_create(MaxBuffersInFlight);
|
||||
m_command_queue = [g_metal_device newCommandQueue];
|
||||
// Used for syncing the CPU and GPU
|
||||
semaphore = dispatch_semaphore_create(MaxBuffersInFlight);
|
||||
|
||||
// Setup command queue
|
||||
command_queue = [metal_device newCommandQueue];
|
||||
|
||||
[self _createShaders];
|
||||
[self _createAssets];
|
||||
@ -100,19 +107,19 @@ NSString *g_shader_src = kShader(
|
||||
|
||||
//-------------------------------------
|
||||
- (bool) _createShaders {
|
||||
NSError *error = nil;
|
||||
NSError *error = 0x0;
|
||||
|
||||
g_library = [g_metal_device newLibraryWithSource:g_shader_src
|
||||
options:[[MTLCompileOptions alloc] init]
|
||||
error:&error
|
||||
metal_library = [metal_device newLibraryWithSource:g_shader_src
|
||||
options:[[MTLCompileOptions alloc] init]
|
||||
error:&error
|
||||
];
|
||||
if (error || !g_library) {
|
||||
if (error || !metal_library) {
|
||||
NSLog(@"Unable to create shaders %@", error);
|
||||
return false;
|
||||
}
|
||||
|
||||
id<MTLFunction> vertex_shader_func = [g_library newFunctionWithName:@"vertFunc"];
|
||||
id<MTLFunction> fragment_shader_func = [g_library newFunctionWithName:@"fragFunc"];
|
||||
id<MTLFunction> vertex_shader_func = [metal_library newFunctionWithName:@"vertFunc"];
|
||||
id<MTLFunction> fragment_shader_func = [metal_library newFunctionWithName:@"fragFunc"];
|
||||
|
||||
if (!vertex_shader_func) {
|
||||
NSLog(@"Unable to get vertFunc!\n");
|
||||
@ -124,14 +131,15 @@ NSString *g_shader_src = kShader(
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a reusable pipeline state
|
||||
MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
|
||||
pipelineStateDescriptor.label = @"MiniFB_pipeline";
|
||||
pipelineStateDescriptor.vertexFunction = vertex_shader_func;
|
||||
pipelineStateDescriptor.fragmentFunction = fragment_shader_func;
|
||||
pipelineStateDescriptor.colorAttachments[0].pixelFormat = 80; //bgra8Unorm;
|
||||
|
||||
m_pipeline_state = [g_metal_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&error];
|
||||
if (!m_pipeline_state) {
|
||||
pipeline_state = [metal_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&error];
|
||||
if (!pipeline_state) {
|
||||
NSLog(@"Failed to created pipeline state, error %@", error);
|
||||
return false;
|
||||
}
|
||||
@ -141,26 +149,42 @@ NSString *g_shader_src = kShader(
|
||||
|
||||
//-------------------------------------
|
||||
- (void) _createAssets {
|
||||
static Vertex s_vertices[4] = {
|
||||
{-1.0, -1.0, 0, 1},
|
||||
{-1.0, 1.0, 0, 1},
|
||||
{ 1.0, -1.0, 0, 1},
|
||||
{ 1.0, 1.0, 0, 1},
|
||||
};
|
||||
memcpy(window_data_ios->vertices, s_vertices, sizeof(s_vertices));
|
||||
|
||||
MTLTextureDescriptor *td;
|
||||
td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
|
||||
width:window_data->buffer_width
|
||||
height:window_data->buffer_height
|
||||
mipmapped:false];
|
||||
|
||||
m_texture_buffer = [g_metal_device newTextureWithDescriptor:td];
|
||||
// Create the texture from the device by using the descriptor
|
||||
texture_buffer = [metal_device newTextureWithDescriptor:td];
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
- (void) drawInMTKView:(nonnull MTKView *) view {
|
||||
// Per frame updates here
|
||||
dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER);
|
||||
// Wait to ensure only MaxBuffersInFlight number of frames are getting proccessed
|
||||
// by any stage in the Metal pipeline (App, Metal, Drivers, GPU, etc)
|
||||
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
|
||||
|
||||
m_current_buffer = (m_current_buffer + 1) % MaxBuffersInFlight;
|
||||
current_buffer = (current_buffer + 1) % MaxBuffersInFlight;
|
||||
|
||||
id <MTLCommandBuffer> commandBuffer = [m_command_queue commandBuffer];
|
||||
// Create a new command buffer for each render pass to the current drawable
|
||||
id<MTLCommandBuffer> commandBuffer = [command_queue commandBuffer];
|
||||
commandBuffer.label = @"minifb_command_buffer";
|
||||
|
||||
__block dispatch_semaphore_t block_sema = m_semaphore;
|
||||
// Add completion hander which signals semaphore when Metal and the GPU has fully
|
||||
// finished processing the commands we're encoding this frame. This indicates when the
|
||||
// dynamic buffers filled with our vertices, that we're writing to this frame, will no longer
|
||||
// be needed by Metal and the GPU, meaning we can overwrite the buffer contents without
|
||||
// corrupting the rendering.
|
||||
__block dispatch_semaphore_t block_sema = semaphore;
|
||||
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer) {
|
||||
(void)buffer;
|
||||
dispatch_semaphore_signal(block_sema);
|
||||
@ -168,7 +192,7 @@ NSString *g_shader_src = kShader(
|
||||
|
||||
// Copy the bytes from our data object into the texture
|
||||
MTLRegion region = { { 0, 0, 0 }, { window_data->buffer_width, window_data->buffer_height, 1 } };
|
||||
[m_texture_buffer replaceRegion:region mipmapLevel:0 withBytes:window_data->draw_buffer bytesPerRow:window_data->buffer_stride];
|
||||
[texture_buffer replaceRegion:region mipmapLevel:0 withBytes:window_data->draw_buffer bytesPerRow:window_data->buffer_stride];
|
||||
|
||||
// Delay getting the currentRenderPassDescriptor until absolutely needed. This avoids
|
||||
// holding onto the drawable and blocking the display pipeline any longer than necessary
|
||||
@ -181,11 +205,11 @@ NSString *g_shader_src = kShader(
|
||||
renderEncoder.label = @"minifb_command_encoder";
|
||||
|
||||
// Set render command encoder state
|
||||
[renderEncoder setRenderPipelineState:m_pipeline_state];
|
||||
[renderEncoder setRenderPipelineState:pipeline_state];
|
||||
[renderEncoder setVertexBytes:window_data_ios->vertices length:sizeof(window_data_ios->vertices) atIndex:0];
|
||||
|
||||
//[renderEncoder setFragmentTexture:m_texture_buffers[m_current_buffer] atIndex:0];
|
||||
[renderEncoder setFragmentTexture:m_texture_buffer atIndex:0];
|
||||
//[renderEncoder setFragmentTexture:texture_buffers[current_buffer] atIndex:0];
|
||||
[renderEncoder setFragmentTexture:texture_buffer atIndex:0];
|
||||
|
||||
// Draw the vertices of our quads
|
||||
[renderEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
|
||||
|
@ -1,5 +1,6 @@
|
||||
#import "OSXView.h"
|
||||
#import "OSXWindow.h"
|
||||
#import "WindowData_OSX.h"
|
||||
#include <MiniFB_internal.h>
|
||||
|
||||
//-------------------------------------
|
||||
|
@ -1,8 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(USE_METAL_API)
|
||||
#include "WindowData_OSX.h"
|
||||
|
||||
#import <MetalKit/MetalKit.h>
|
||||
#include "WindowData_OSX.h"
|
||||
|
||||
// Number of textures in flight (tripple buffered)
|
||||
enum { MaxBuffersInFlight = 3 };
|
||||
@ -11,17 +12,23 @@ enum { MaxBuffersInFlight = 3 };
|
||||
|
||||
@interface OSXViewController : NSViewController<MTKViewDelegate>
|
||||
{
|
||||
@public SWindowData *window_data;
|
||||
@public SWindowData_OSX *window_data_osx;
|
||||
@public SWindowData *window_data;
|
||||
@public SWindowData_OSX *window_data_osx;
|
||||
|
||||
@public id<MTLDevice> metal_device;
|
||||
@public id<MTLLibrary> metal_library;
|
||||
@public id<MTLTexture> texture_buffers[MaxBuffersInFlight];
|
||||
@public int current_buffer;
|
||||
@public dispatch_semaphore_t semaphore; // Used for syncing with CPU/GPU
|
||||
id<MTLDevice> metal_device;
|
||||
id<MTLLibrary> metal_library;
|
||||
|
||||
dispatch_semaphore_t semaphore; // Used for syncing with CPU/GPU
|
||||
id<MTLCommandQueue> command_queue;
|
||||
|
||||
id<MTLRenderPipelineState> pipeline_state;
|
||||
id<MTLTexture> texture_buffers[MaxBuffersInFlight];
|
||||
|
||||
int current_buffer;
|
||||
}
|
||||
|
||||
- (id) initWithWindowData:(SWindowData *) windowData;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "OSXViewController.h"
|
||||
|
||||
#if defined(USE_METAL_API)
|
||||
|
||||
#import <MetalKit/MetalKit.h>
|
||||
|
||||
//-------------------------------------
|
||||
@ -63,20 +64,20 @@ NSString *g_shader_src = kShader(
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
[self _create_shaders];
|
||||
[self _createAssets];
|
||||
|
||||
// Used for syncing the CPU and GPU
|
||||
semaphore = dispatch_semaphore_create(MaxBuffersInFlight);
|
||||
|
||||
// Setup command queue
|
||||
window_data_osx->metal.command_queue = [metal_device newCommandQueue];
|
||||
command_queue = [metal_device newCommandQueue];
|
||||
|
||||
[self _createShaders];
|
||||
[self _createAssets];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
- (bool) _create_shaders {
|
||||
- (bool) _createShaders {
|
||||
NSError *error = 0x0;
|
||||
|
||||
metal_library = [metal_device newLibraryWithSource:g_shader_src
|
||||
@ -108,9 +109,10 @@ NSString *g_shader_src = kShader(
|
||||
pipelineStateDescriptor.fragmentFunction = fragment_shader_func;
|
||||
pipelineStateDescriptor.colorAttachments[0].pixelFormat = 80; //bgra8Unorm;
|
||||
|
||||
window_data_osx->metal.pipeline_state = [metal_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&error];
|
||||
if (!window_data_osx->metal.pipeline_state) {
|
||||
pipeline_state = [metal_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&error];
|
||||
if (!pipeline_state) {
|
||||
NSLog(@"Failed to created pipeline state, error %@", error);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -126,8 +128,6 @@ NSString *g_shader_src = kShader(
|
||||
};
|
||||
memcpy(window_data_osx->metal.vertices, s_vertices, sizeof(s_vertices));
|
||||
|
||||
// Indicate that each pixel has a blue, green, red, and alpha channel, where each channel is
|
||||
// an 8-bit unsigned normalized value (i.e. 0 maps to 0.0 and 255 maps to 1.0)
|
||||
MTLTextureDescriptor *td;
|
||||
td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
|
||||
width:window_data->buffer_width
|
||||
@ -141,36 +141,34 @@ NSString *g_shader_src = kShader(
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
- (void) drawInMTKView:(nonnull MTKView *)view {
|
||||
- (void) drawInMTKView:(nonnull MTKView *) view {
|
||||
// Wait to ensure only MaxBuffersInFlight number of frames are getting proccessed
|
||||
// by any stage in the Metal pipeline (App, Metal, Drivers, GPU, etc)
|
||||
// by any stage in the Metal pipeline (App, Metal, Drivers, GPU, etc)
|
||||
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
|
||||
|
||||
// Iterate through our Metal buffers, and cycle back to the first when we've written to MaxBuffersInFlight
|
||||
current_buffer = (current_buffer + 1) % MaxBuffersInFlight;
|
||||
|
||||
// Calculate the number of bytes per row of our image.
|
||||
MTLRegion region = { { 0, 0, 0 }, { window_data->buffer_width, window_data->buffer_height, 1 } };
|
||||
|
||||
// Copy the bytes from our data object into the texture
|
||||
[texture_buffers[current_buffer] replaceRegion:region mipmapLevel:0 withBytes:window_data->draw_buffer bytesPerRow:window_data->buffer_stride];
|
||||
|
||||
// Create a new command buffer for each render pass to the current drawable
|
||||
id<MTLCommandBuffer> commandBuffer = [window_data_osx->metal.command_queue commandBuffer];
|
||||
id<MTLCommandBuffer> commandBuffer = [command_queue commandBuffer];
|
||||
commandBuffer.label = @"minifb_command_buffer";
|
||||
|
||||
// Add completion hander which signals _inFlightSemaphore when Metal and the GPU has fully
|
||||
// finished processing the commands we're encoding this frame. This indicates when the
|
||||
// dynamic buffers filled with our vertices, that we're writing to this frame, will no longer
|
||||
// be needed by Metal and the GPU, meaning we can overwrite the buffer contents without
|
||||
// corrupting the rendering.
|
||||
// Add completion hander which signals semaphore when Metal and the GPU has fully
|
||||
// finished processing the commands we're encoding this frame. This indicates when the
|
||||
// dynamic buffers filled with our vertices, that we're writing to this frame, will no longer
|
||||
// be needed by Metal and the GPU, meaning we can overwrite the buffer contents without
|
||||
// corrupting the rendering.
|
||||
__block dispatch_semaphore_t block_sema = semaphore;
|
||||
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer)
|
||||
{
|
||||
(void)buffer;
|
||||
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer) {
|
||||
(void)buffer;
|
||||
dispatch_semaphore_signal(block_sema);
|
||||
}];
|
||||
|
||||
// Copy the bytes from our data object into the texture
|
||||
MTLRegion region = { { 0, 0, 0 }, { window_data->buffer_width, window_data->buffer_height, 1 } };
|
||||
[texture_buffers[current_buffer] replaceRegion:region mipmapLevel:0 withBytes:window_data->draw_buffer bytesPerRow:window_data->buffer_stride];
|
||||
|
||||
// Delay getting the currentRenderPassDescriptor until absolutely needed. This avoids
|
||||
// holding onto the drawable and blocking the display pipeline any longer than necessary
|
||||
MTLRenderPassDescriptor* renderPassDescriptor = view.currentRenderPassDescriptor;
|
||||
if (renderPassDescriptor != nil) {
|
||||
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 1.0);
|
||||
@ -180,8 +178,7 @@ NSString *g_shader_src = kShader(
|
||||
renderEncoder.label = @"minifb_command_encoder";
|
||||
|
||||
// Set render command encoder state
|
||||
[renderEncoder setRenderPipelineState:window_data_osx->metal.pipeline_state];
|
||||
|
||||
[renderEncoder setRenderPipelineState:pipeline_state];
|
||||
[renderEncoder setVertexBytes:window_data_osx->metal.vertices length:sizeof(window_data_osx->metal.vertices) atIndex:0];
|
||||
|
||||
[renderEncoder setFragmentTexture:texture_buffers[current_buffer] atIndex:0];
|
||||
|
@ -19,8 +19,6 @@ typedef struct {
|
||||
|
||||
#if defined(USE_METAL_API)
|
||||
struct {
|
||||
id<MTLCommandQueue> command_queue;
|
||||
id<MTLRenderPipelineState> pipeline_state;
|
||||
Vertex vertices[4];
|
||||
} metal;
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user