unify a little bit more iOS & MacOS

This commit is contained in:
Carlos Aragones 2020-05-17 19:48:59 +02:00
parent 75de20e4cc
commit 80a7a33b7c
9 changed files with 146 additions and 131 deletions

View File

@ -11,7 +11,7 @@
SWindowData * SWindowData *
create_window_data(unsigned width, unsigned height) { create_window_data(unsigned width, unsigned height) {
SWindowData *window_data; SWindowData *window_data;
window_data = malloc(sizeof(SWindowData)); window_data = malloc(sizeof(SWindowData));
if(window_data == 0x0) { if(window_data == 0x0) {
NSLog(@"Cannot allocate window data"); NSLog(@"Cannot allocate window data");
@ -48,7 +48,7 @@ create_window_data(unsigned width, unsigned height) {
NSLog(@"Unable to create draw buffer"); NSLog(@"Unable to create draw buffer");
return 0x0; return 0x0;
} }
return window_data; return window_data;
} }
@ -67,20 +67,11 @@ mfb_open_ex(const char *title, unsigned width, unsigned height, unsigned flags)
kUnused(title); kUnused(title);
kUnused(flags); kUnused(flags);
SWindowData *window_data = create_window_data(width, height); SWindowData *window_data = create_window_data(width, height);
if (window_data == 0x0) { if (window_data == 0x0) {
return 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]; windows = [[UIApplication sharedApplication] windows];
numWindows = [windows count]; numWindows = [windows count];
@ -94,7 +85,7 @@ mfb_open_ex(const char *title, unsigned width, unsigned height, unsigned flags)
window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
NSLog(@"UIApplication has no window. We create one (%f, %f).", [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height); NSLog(@"UIApplication has no window. We create one (%f, %f).", [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height);
} }
if([window.rootViewController isKindOfClass:[iOSViewController class]] == false) { if([window.rootViewController isKindOfClass:[iOSViewController class]] == false) {
iOSViewController *controller = [[iOSViewController alloc] initWithWindowData:window_data]; iOSViewController *controller = [[iOSViewController alloc] initWithWindowData:window_data];
[window setRootViewController:controller]; [window setRootViewController:controller];
@ -216,7 +207,7 @@ mfb_timer_tick() {
if (timebase.denom == 0) { if (timebase.denom == 0) {
(void) mach_timebase_info(&timebase); (void) mach_timebase_info(&timebase);
} }
uint64_t time = mach_absolute_time(); uint64_t time = mach_absolute_time();
//return (time * s_timebase_info.numer) / s_timebase_info.denom; //return (time * s_timebase_info.numer) / s_timebase_info.denom;
@ -226,7 +217,7 @@ mfb_timer_tick() {
uint64_t highRem = ((high % timebase.denom) << 32) / timebase.denom; uint64_t highRem = ((high % timebase.denom) << 32) / timebase.denom;
uint64_t low = (time & 0xFFFFFFFFull) * timebase.numer / timebase.denom; uint64_t low = (time & 0xFFFFFFFFull) * timebase.numer / timebase.denom;
high /= timebase.denom; high /= timebase.denom;
return (high << 32) + highRem + low; return (high << 32) + highRem + low;
} }

View File

@ -8,15 +8,12 @@
#import <MetalKit/MetalKit.h> #import <MetalKit/MetalKit.h>
#include "WindowData.h" #include "WindowData.h"
#include "WindowData_IOS.h"
// Our platform independent renderer class. // Our platform independent renderer class.
// Implements the MTKViewDelegate protocol which allows it to accept per-frame // Implements the MTKViewDelegate protocol which allows it to accept per-frame
// update and drawable resize callbacks. // update and drawable resize callbacks.
@interface iOSViewDelegate : NSObject <MTKViewDelegate> @interface iOSViewDelegate : NSObject <MTKViewDelegate>
{ {
@public SWindowData *window_data;
@public SWindowData_IOS *window_data_ios;
} }
-(nonnull instancetype) initWithMetalKitView:(nonnull MTKView *) view windowData:(nonnull SWindowData *) windowData; -(nonnull instancetype) initWithMetalKitView:(nonnull MTKView *) view windowData:(nonnull SWindowData *) windowData;

View File

@ -21,25 +21,23 @@
//------------------------------------- //-------------------------------------
enum { MaxBuffersInFlight = 3 }; // Number of textures in flight (tripple buffered) 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( NSString *g_shader_src = kShader(
"#include <metal_stdlib>\n", "#include <metal_stdlib>\n",
using namespace metal; using namespace metal;
//--------------------- //-------------
struct VertexOutput { struct VertexOutput {
float4 pos [[position]]; float4 pos [[position]];
float2 texcoord; float2 texcoord;
}; };
//-------------
struct Vertex { struct Vertex {
float4 position [[position]]; float4 position [[position]];
}; };
//--------------------- //-------------
vertex VertexOutput vertex VertexOutput
vertFunc(unsigned int vID[[vertex_id]], const device Vertex *pos [[ buffer(0) ]]) { vertFunc(unsigned int vID[[vertex_id]], const device Vertex *pos [[ buffer(0) ]]) {
VertexOutput out; VertexOutput out;
@ -52,7 +50,7 @@ NSString *g_shader_src = kShader(
return out; return out;
} }
//--------------------- //-------------
fragment float4 fragment float4
fragFunc(VertexOutput input [[stage_in]], texture2d<half> colorTexture [[ texture(0) ]]) { fragFunc(VertexOutput input [[stage_in]], texture2d<half> colorTexture [[ texture(0) ]]) {
constexpr sampler textureSampler(mag_filter::nearest, min_filter::nearest); constexpr sampler textureSampler(mag_filter::nearest, min_filter::nearest);
@ -67,29 +65,38 @@ NSString *g_shader_src = kShader(
//------------------------------------- //-------------------------------------
@implementation iOSViewDelegate { @implementation iOSViewDelegate {
dispatch_semaphore_t m_semaphore; SWindowData *window_data;
id <MTLCommandQueue> m_command_queue; SWindowData_IOS *window_data_ios;
id<MTLDevice> metal_device;
id<MTLLibrary> metal_library;
id <MTLRenderPipelineState> m_pipeline_state; dispatch_semaphore_t semaphore;
id <MTLTexture> m_texture_buffer; id<MTLCommandQueue> command_queue;
uint8_t m_current_buffer; id<MTLRenderPipelineState> pipeline_state;
id<MTLTexture> texture_buffer;
uint8_t current_buffer;
} }
//------------------------------------- //-------------------------------------
-(nonnull instancetype) initWithMetalKitView:(nonnull MTKView *) view windowData:(nonnull SWindowData *) windowData { -(nonnull instancetype) initWithMetalKitView:(nonnull MTKView *) view windowData:(nonnull SWindowData *) windowData {
self = [super init]; self = [super init];
if (self) { if (self) {
self->window_data = windowData; window_data = windowData;
self->window_data_ios = (SWindowData_IOS *) windowData->specific; window_data_ios = (SWindowData_IOS *) windowData->specific;
view.colorPixelFormat = MTLPixelFormatBGRA8Unorm; view.colorPixelFormat = MTLPixelFormatBGRA8Unorm;
view.sampleCount = 1; view.sampleCount = 1;
g_metal_device = view.device; metal_device = view.device;
m_semaphore = dispatch_semaphore_create(MaxBuffersInFlight); // Used for syncing the CPU and GPU
m_command_queue = [g_metal_device newCommandQueue]; semaphore = dispatch_semaphore_create(MaxBuffersInFlight);
// Setup command queue
command_queue = [metal_device newCommandQueue];
[self _createShaders]; [self _createShaders];
[self _createAssets]; [self _createAssets];
@ -100,19 +107,19 @@ NSString *g_shader_src = kShader(
//------------------------------------- //-------------------------------------
- (bool) _createShaders { - (bool) _createShaders {
NSError *error = nil; NSError *error = 0x0;
g_library = [g_metal_device newLibraryWithSource:g_shader_src metal_library = [metal_device newLibraryWithSource:g_shader_src
options:[[MTLCompileOptions alloc] init] options:[[MTLCompileOptions alloc] init]
error:&error error:&error
]; ];
if (error || !g_library) { if (error || !metal_library) {
NSLog(@"Unable to create shaders %@", error); NSLog(@"Unable to create shaders %@", error);
return false; return false;
} }
id<MTLFunction> vertex_shader_func = [g_library newFunctionWithName:@"vertFunc"]; id<MTLFunction> vertex_shader_func = [metal_library newFunctionWithName:@"vertFunc"];
id<MTLFunction> fragment_shader_func = [g_library newFunctionWithName:@"fragFunc"]; id<MTLFunction> fragment_shader_func = [metal_library newFunctionWithName:@"fragFunc"];
if (!vertex_shader_func) { if (!vertex_shader_func) {
NSLog(@"Unable to get vertFunc!\n"); NSLog(@"Unable to get vertFunc!\n");
@ -124,14 +131,15 @@ NSString *g_shader_src = kShader(
return false; return false;
} }
// Create a reusable pipeline state
MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init]; MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
pipelineStateDescriptor.label = @"MiniFB_pipeline"; pipelineStateDescriptor.label = @"MiniFB_pipeline";
pipelineStateDescriptor.vertexFunction = vertex_shader_func; pipelineStateDescriptor.vertexFunction = vertex_shader_func;
pipelineStateDescriptor.fragmentFunction = fragment_shader_func; pipelineStateDescriptor.fragmentFunction = fragment_shader_func;
pipelineStateDescriptor.colorAttachments[0].pixelFormat = 80; //bgra8Unorm; pipelineStateDescriptor.colorAttachments[0].pixelFormat = 80; //bgra8Unorm;
m_pipeline_state = [g_metal_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&error]; pipeline_state = [metal_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&error];
if (!m_pipeline_state) { if (!pipeline_state) {
NSLog(@"Failed to created pipeline state, error %@", error); NSLog(@"Failed to created pipeline state, error %@", error);
return false; return false;
} }
@ -141,26 +149,42 @@ NSString *g_shader_src = kShader(
//------------------------------------- //-------------------------------------
- (void) _createAssets { - (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; MTLTextureDescriptor *td;
td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
width:window_data->buffer_width width:window_data->buffer_width
height:window_data->buffer_height height:window_data->buffer_height
mipmapped:false]; 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 { - (void) drawInMTKView:(nonnull MTKView *) view {
// Per frame updates here // Wait to ensure only MaxBuffersInFlight number of frames are getting proccessed
dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER); // 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"; 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) { [commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer) {
(void)buffer; (void)buffer;
dispatch_semaphore_signal(block_sema); dispatch_semaphore_signal(block_sema);
@ -168,7 +192,7 @@ NSString *g_shader_src = kShader(
// Copy the bytes from our data object into the texture // Copy the bytes from our data object into the texture
MTLRegion region = { { 0, 0, 0 }, { window_data->buffer_width, window_data->buffer_height, 1 } }; 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 // Delay getting the currentRenderPassDescriptor until absolutely needed. This avoids
// holding onto the drawable and blocking the display pipeline any longer than necessary // 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"; renderEncoder.label = @"minifb_command_encoder";
// Set render command encoder state // 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 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:texture_buffers[current_buffer] atIndex:0];
[renderEncoder setFragmentTexture:m_texture_buffer atIndex:0]; [renderEncoder setFragmentTexture:texture_buffer atIndex:0];
// Draw the vertices of our quads // Draw the vertices of our quads
[renderEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4]; [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];

View File

@ -132,7 +132,7 @@ mfb_open_ex(const char *title, unsigned width, unsigned height, unsigned flags)
OSXViewController *viewController = [[OSXViewController alloc] initWithWindowData:window_data]; OSXViewController *viewController = [[OSXViewController alloc] initWithWindowData:window_data];
MTKView* view = [[MTKView alloc] initWithFrame:rectangle]; MTKView* view = [[MTKView alloc] initWithFrame:rectangle];
view.device = viewController->metal_device; view.device = viewController->metal_device;
view.delegate = viewController; view.delegate = viewController;
view.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; view.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
[window_data_osx->window.contentView addSubview:view]; [window_data_osx->window.contentView addSubview:view];
@ -169,13 +169,13 @@ mfb_open_ex(const char *title, unsigned width, unsigned height, unsigned flags)
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static void static void
destroy_window_data(SWindowData *window_data) { destroy_window_data(SWindowData *window_data) {
if(window_data == 0x0) if(window_data == 0x0)
return; return;
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
SWindowData_OSX *window_data_osx = (SWindowData_OSX *) window_data->specific; SWindowData_OSX *window_data_osx = (SWindowData_OSX *) window_data->specific;
if(window_data_osx != 0x0) { if(window_data_osx != 0x0) {
OSXWindow *window = window_data_osx->window; OSXWindow *window = window_data_osx->window;
@ -201,7 +201,7 @@ destroy_window_data(SWindowData *window_data) {
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static void static void
update_events(SWindowData *window_data) { update_events(SWindowData *window_data) {
NSEvent* event; NSEvent* event;
@ -218,7 +218,7 @@ update_events(SWindowData *window_data) {
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
mfb_update_state mfb_update_state
mfb_update(struct mfb_window *window, void *buffer) { mfb_update(struct mfb_window *window, void *buffer) {
if(window == 0x0) { if(window == 0x0) {
return STATE_INVALID_WINDOW; return STATE_INVALID_WINDOW;
@ -254,7 +254,7 @@ mfb_update(struct mfb_window *window, void *buffer) {
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
mfb_update_state mfb_update_state
mfb_update_events(struct mfb_window *window) { mfb_update_events(struct mfb_window *window) {
if(window == 0x0) { if(window == 0x0) {
return STATE_INVALID_WINDOW; return STATE_INVALID_WINDOW;
@ -310,7 +310,7 @@ mfb_wait_sync(struct mfb_window *window) {
if (event) { if (event) {
[NSApp sendEvent:event]; [NSApp sendEvent:event];
} }
if(window_data->close) { if(window_data->close) {
destroy_window_data(window_data); destroy_window_data(window_data);
return false; return false;
@ -324,7 +324,7 @@ mfb_wait_sync(struct mfb_window *window) {
else if(current >= g_time_for_frame * 0.8) { else if(current >= g_time_for_frame * 0.8) {
millis = 0; millis = 0;
} }
usleep(millis * 1000); usleep(millis * 1000);
//sched_yield(); //sched_yield();
} }
@ -336,7 +336,7 @@ mfb_wait_sync(struct mfb_window *window) {
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool bool
mfb_set_viewport(struct mfb_window *window, unsigned offset_x, unsigned offset_y, unsigned width, unsigned height) { mfb_set_viewport(struct mfb_window *window, unsigned offset_x, unsigned offset_y, unsigned width, unsigned height) {
if(window == 0x0) { if(window == 0x0) {
return false; return false;
@ -384,10 +384,10 @@ mfb_set_viewport(struct mfb_window *window, unsigned offset_x, unsigned offset_y
extern short int g_keycodes[512]; extern short int g_keycodes[512];
void void
init_keycodes() { init_keycodes() {
// Clear keys // Clear keys
for (unsigned int i = 0; i < sizeof(g_keycodes) / sizeof(g_keycodes[0]); ++i) for (unsigned int i = 0; i < sizeof(g_keycodes) / sizeof(g_keycodes[0]); ++i)
g_keycodes[i] = 0; g_keycodes[i] = 0;
g_keycodes[0x1D] = KB_KEY_0; g_keycodes[0x1D] = KB_KEY_0;
@ -426,7 +426,7 @@ init_keycodes() {
g_keycodes[0x07] = KB_KEY_X; g_keycodes[0x07] = KB_KEY_X;
g_keycodes[0x10] = KB_KEY_Y; g_keycodes[0x10] = KB_KEY_Y;
g_keycodes[0x06] = KB_KEY_Z; g_keycodes[0x06] = KB_KEY_Z;
g_keycodes[0x27] = KB_KEY_APOSTROPHE; g_keycodes[0x27] = KB_KEY_APOSTROPHE;
g_keycodes[0x2A] = KB_KEY_BACKSLASH; g_keycodes[0x2A] = KB_KEY_BACKSLASH;
g_keycodes[0x2B] = KB_KEY_COMMA; g_keycodes[0x2B] = KB_KEY_COMMA;
@ -439,7 +439,7 @@ init_keycodes() {
g_keycodes[0x29] = KB_KEY_SEMICOLON; g_keycodes[0x29] = KB_KEY_SEMICOLON;
g_keycodes[0x2C] = KB_KEY_SLASH; g_keycodes[0x2C] = KB_KEY_SLASH;
g_keycodes[0x0A] = KB_KEY_WORLD_1; g_keycodes[0x0A] = KB_KEY_WORLD_1;
g_keycodes[0x33] = KB_KEY_BACKSPACE; g_keycodes[0x33] = KB_KEY_BACKSPACE;
g_keycodes[0x39] = KB_KEY_CAPS_LOCK; g_keycodes[0x39] = KB_KEY_CAPS_LOCK;
g_keycodes[0x75] = KB_KEY_DELETE; g_keycodes[0x75] = KB_KEY_DELETE;
@ -486,7 +486,7 @@ init_keycodes() {
g_keycodes[0x31] = KB_KEY_SPACE; g_keycodes[0x31] = KB_KEY_SPACE;
g_keycodes[0x30] = KB_KEY_TAB; g_keycodes[0x30] = KB_KEY_TAB;
g_keycodes[0x7E] = KB_KEY_UP; g_keycodes[0x7E] = KB_KEY_UP;
g_keycodes[0x52] = KB_KEY_KP_0; g_keycodes[0x52] = KB_KEY_KP_0;
g_keycodes[0x53] = KB_KEY_KP_1; g_keycodes[0x53] = KB_KEY_KP_1;
g_keycodes[0x54] = KB_KEY_KP_2; g_keycodes[0x54] = KB_KEY_KP_2;
@ -511,14 +511,14 @@ init_keycodes() {
extern double g_timer_frequency; extern double g_timer_frequency;
extern double g_timer_resolution; extern double g_timer_resolution;
uint64_t uint64_t
mfb_timer_tick() { mfb_timer_tick() {
static mach_timebase_info_data_t timebase = { 0 }; static mach_timebase_info_data_t timebase = { 0 };
if (timebase.denom == 0) { if (timebase.denom == 0) {
(void) mach_timebase_info(&timebase); (void) mach_timebase_info(&timebase);
} }
uint64_t time = mach_absolute_time(); uint64_t time = mach_absolute_time();
//return (time * s_timebase_info.numer) / s_timebase_info.denom; //return (time * s_timebase_info.numer) / s_timebase_info.denom;
@ -528,11 +528,11 @@ mfb_timer_tick() {
uint64_t highRem = ((high % timebase.denom) << 32) / timebase.denom; uint64_t highRem = ((high % timebase.denom) << 32) / timebase.denom;
uint64_t low = (time & 0xFFFFFFFFull) * timebase.numer / timebase.denom; uint64_t low = (time & 0xFFFFFFFFull) * timebase.numer / timebase.denom;
high /= timebase.denom; high /= timebase.denom;
return (high << 32) + highRem + low; return (high << 32) + highRem + low;
} }
void void
mfb_timer_init() { mfb_timer_init() {
g_timer_frequency = 1e+9; g_timer_frequency = 1e+9;
g_timer_resolution = 1.0 / g_timer_frequency; g_timer_resolution = 1.0 / g_timer_frequency;

View File

@ -1,5 +1,6 @@
#import "OSXView.h" #import "OSXView.h"
#import "OSXWindow.h" #import "OSXWindow.h"
#import "WindowData_OSX.h"
#include <MiniFB_internal.h> #include <MiniFB_internal.h>
//------------------------------------- //-------------------------------------
@ -22,13 +23,13 @@
[self addTrackingArea:tracking_area]; [self addTrackingArea:tracking_area];
} }
#else #else
//------------------------------------- //-------------------------------------
- (NSRect)resizeRect { - (NSRect)resizeRect {
const CGFloat resizeBoxSize = 16.0; const CGFloat resizeBoxSize = 16.0;
const CGFloat contentViewPadding = 5.5; const CGFloat contentViewPadding = 5.5;
NSRect contentViewRect = [[self window] contentRectForFrameRect:[[self window] frame]]; NSRect contentViewRect = [[self window] contentRectForFrameRect:[[self window] frame]];
NSRect resizeRect = NSMakeRect( NSRect resizeRect = NSMakeRect(
NSMaxX(contentViewRect) + contentViewPadding, NSMaxX(contentViewRect) + contentViewPadding,
@ -36,7 +37,7 @@
resizeBoxSize, resizeBoxSize,
resizeBoxSize resizeBoxSize
); );
return resizeRect; return resizeRect;
} }
@ -47,16 +48,16 @@
if(window_data == 0x0) if(window_data == 0x0)
return; return;
SWindowData_OSX *window_data_osx = (SWindowData_OSX *) window_data->specific; SWindowData_OSX *window_data_osx = (SWindowData_OSX *) window_data->specific;
if (!window_data_osx || !window_data_osx->window || !window_data->draw_buffer) if (!window_data_osx || !window_data_osx->window || !window_data->draw_buffer)
return; return;
CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort]; CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB(); CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
CGDataProviderRef provider = CGDataProviderCreateWithData(0x0, window_data->draw_buffer, window_data->buffer_width * window_data->buffer_height * 4, 0x0); CGDataProviderRef provider = CGDataProviderCreateWithData(0x0, window_data->draw_buffer, window_data->buffer_width * window_data->buffer_height * 4, 0x0);
CGImageRef img = CGImageCreate(window_data->buffer_width, window_data->buffer_height, 8, 32, window_data->buffer_width * 4, space, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little, CGImageRef img = CGImageCreate(window_data->buffer_width, window_data->buffer_height, 8, 32, window_data->buffer_width * 4, space, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little,
provider, 0x0, false, kCGRenderingIntentDefault); provider, 0x0, false, kCGRenderingIntentDefault);
const CGFloat components[] = {0.0f, 0.0f, 0.0f, 1.0f}; const CGFloat components[] = {0.0f, 0.0f, 0.0f, 1.0f};
@ -69,7 +70,7 @@
CGContextSetFillColorWithColor(context, black); CGContextSetFillColorWithColor(context, black);
CGContextFillRect(context, CGRectMake(0, 0, window_data->window_width, window_data->window_height)); CGContextFillRect(context, CGRectMake(0, 0, window_data->window_width, window_data->window_height));
} }
CGContextDrawImage(context, CGRectMake(window_data->dst_offset_x, window_data->dst_offset_y, window_data->dst_width, window_data->dst_height), img); CGContextDrawImage(context, CGRectMake(window_data->dst_offset_x, window_data->dst_offset_y, window_data->dst_width, window_data->dst_height), img);
CGImageRelease(img); CGImageRelease(img);

View File

@ -1,27 +1,34 @@
#pragma once #pragma once
#if defined(USE_METAL_API) #if defined(USE_METAL_API)
#include "WindowData_OSX.h"
#import <MetalKit/MetalKit.h> #import <MetalKit/MetalKit.h>
#include "WindowData_OSX.h"
// Number of textures in flight (tripple buffered) // Number of textures in flight (tripple buffered)
enum { MaxBuffersInFlight = 3 }; enum { MaxBuffersInFlight = 3 };
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@interface OSXViewController : NSViewController<MTKViewDelegate> @interface OSXViewController : NSViewController<MTKViewDelegate>
{ {
@public SWindowData *window_data; @public SWindowData *window_data;
@public SWindowData_OSX *window_data_osx; @public SWindowData_OSX *window_data_osx;
@public id<MTLDevice> metal_device; id<MTLDevice> metal_device;
@public id<MTLLibrary> metal_library; id<MTLLibrary> metal_library;
@public id<MTLTexture> texture_buffers[MaxBuffersInFlight];
@public int current_buffer; dispatch_semaphore_t semaphore; // Used for syncing with CPU/GPU
@public 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; - (id) initWithWindowData:(SWindowData *) windowData;
@end @end
#endif #endif

View File

@ -1,6 +1,7 @@
#include "OSXViewController.h" #include "OSXViewController.h"
#if defined(USE_METAL_API) #if defined(USE_METAL_API)
#import <MetalKit/MetalKit.h> #import <MetalKit/MetalKit.h>
//------------------------------------- //-------------------------------------
@ -63,28 +64,28 @@ NSString *g_shader_src = kShader(
return 0x0; return 0x0;
} }
[self _create_shaders];
[self _createAssets];
// Used for syncing the CPU and GPU // Used for syncing the CPU and GPU
semaphore = dispatch_semaphore_create(MaxBuffersInFlight); semaphore = dispatch_semaphore_create(MaxBuffersInFlight);
// Setup command queue // Setup command queue
window_data_osx->metal.command_queue = [metal_device newCommandQueue]; command_queue = [metal_device newCommandQueue];
[self _createShaders];
[self _createAssets];
} }
return self; return self;
} }
//------------------------------------- //-------------------------------------
- (bool) _create_shaders { - (bool) _createShaders {
NSError *error = 0x0; NSError *error = 0x0;
metal_library = [metal_device newLibraryWithSource:g_shader_src metal_library = [metal_device newLibraryWithSource:g_shader_src
options:[[MTLCompileOptions alloc] init] options:[[MTLCompileOptions alloc] init]
error:&error error:&error
]; ];
if (error || !metal_library) { if (error || !metal_library) {
NSLog(@"Unable to create shaders %@", error); NSLog(@"Unable to create shaders %@", error);
return false; return false;
} }
@ -108,9 +109,10 @@ NSString *g_shader_src = kShader(
pipelineStateDescriptor.fragmentFunction = fragment_shader_func; pipelineStateDescriptor.fragmentFunction = fragment_shader_func;
pipelineStateDescriptor.colorAttachments[0].pixelFormat = 80; //bgra8Unorm; pipelineStateDescriptor.colorAttachments[0].pixelFormat = 80; //bgra8Unorm;
window_data_osx->metal.pipeline_state = [metal_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&error]; pipeline_state = [metal_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&error];
if (!window_data_osx->metal.pipeline_state) { if (!pipeline_state) {
NSLog(@"Failed to created pipeline state, error %@", error); NSLog(@"Failed to created pipeline state, error %@", error);
return false;
} }
return true; return true;
@ -126,8 +128,6 @@ NSString *g_shader_src = kShader(
}; };
memcpy(window_data_osx->metal.vertices, s_vertices, sizeof(s_vertices)); 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; MTLTextureDescriptor *td;
td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
width:window_data->buffer_width 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 // 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); 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; 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 // 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"; commandBuffer.label = @"minifb_command_buffer";
// Add completion hander which signals _inFlightSemaphore when Metal and the GPU has fully // 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 // 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 // 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 // be needed by Metal and the GPU, meaning we can overwrite the buffer contents without
// corrupting the rendering. // corrupting the rendering.
__block dispatch_semaphore_t block_sema = semaphore; __block dispatch_semaphore_t block_sema = semaphore;
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer) [commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer) {
{ (void)buffer;
(void)buffer;
dispatch_semaphore_signal(block_sema); 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; MTLRenderPassDescriptor* renderPassDescriptor = view.currentRenderPassDescriptor;
if (renderPassDescriptor != nil) { if (renderPassDescriptor != nil) {
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 1.0); 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"; renderEncoder.label = @"minifb_command_encoder";
// Set render command encoder state // 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 setVertexBytes:window_data_osx->metal.vertices length:sizeof(window_data_osx->metal.vertices) atIndex:0];
[renderEncoder setFragmentTexture:texture_buffers[current_buffer] atIndex:0]; [renderEncoder setFragmentTexture:texture_buffers[current_buffer] atIndex:0];
@ -209,4 +206,4 @@ NSString *g_shader_src = kShader(
@end @end
#endif #endif

View File

@ -24,9 +24,9 @@
{ {
[self setOpaque:YES]; [self setOpaque:YES];
[self setBackgroundColor:[NSColor clearColor]]; [self setBackgroundColor:[NSColor clearColor]];
self.delegate = self; self.delegate = self;
self->window_data = windowData; self->window_data = windowData;
OSXView *view = (OSXView *) self->childContentView.superview; OSXView *view = (OSXView *) self->childContentView.superview;
view->window_data = windowData; view->window_data = windowData;
@ -59,12 +59,12 @@
NSSize childBoundsSize = [childContentView bounds].size; NSSize childBoundsSize = [childContentView bounds].size;
sizeDelta.width -= childBoundsSize.width; sizeDelta.width -= childBoundsSize.width;
sizeDelta.height -= childBoundsSize.height; sizeDelta.height -= childBoundsSize.height;
OSXView *frameView = [super contentView]; OSXView *frameView = [super contentView];
NSSize newFrameSize = [frameView bounds].size; NSSize newFrameSize = [frameView bounds].size;
newFrameSize.width += sizeDelta.width; newFrameSize.width += sizeDelta.width;
newFrameSize.height += sizeDelta.height; newFrameSize.height += sizeDelta.height;
[super setContentSize:newFrameSize]; [super setContentSize:newFrameSize];
} }
@ -164,7 +164,7 @@
if(window_data != 0x0) { if(window_data != 0x0) {
NSString *characters; NSString *characters;
NSUInteger length; NSUInteger length;
if ([string isKindOfClass:[NSAttributedString class]]) if ([string isKindOfClass:[NSAttributedString class]])
characters = [string string]; characters = [string string];
@ -212,10 +212,10 @@
if (!frameView) if (!frameView)
{ {
frameView = [[[OSXView alloc] initWithFrame:bounds] autorelease]; frameView = [[[OSXView alloc] initWithFrame:bounds] autorelease];
[super setContentView:frameView]; [super setContentView:frameView];
} }
if (childContentView) if (childContentView)
{ {
[childContentView removeFromSuperview]; [childContentView removeFromSuperview];

View File

@ -16,12 +16,10 @@ typedef struct Vertex {
typedef struct { typedef struct {
OSXWindow *window; OSXWindow *window;
struct mfb_timer *timer; struct mfb_timer *timer;
#if defined(USE_METAL_API) #if defined(USE_METAL_API)
struct { struct {
id<MTLCommandQueue> command_queue;
id<MTLRenderPipelineState> pipeline_state;
Vertex vertices[4]; Vertex vertices[4];
} metal; } metal;
#endif #endif
} SWindowData_OSX; } SWindowData_OSX;