refactor OSX to be more similar to iOS
This commit is contained in:
parent
2be0d5a7e0
commit
6254705203
@ -38,6 +38,8 @@ set(SrcMacOSX
|
|||||||
src/macosx/OSXWindow.m
|
src/macosx/OSXWindow.m
|
||||||
src/macosx/OSXWindowFrameView.h
|
src/macosx/OSXWindowFrameView.h
|
||||||
src/macosx/OSXWindowFrameView.m
|
src/macosx/OSXWindowFrameView.m
|
||||||
|
src/macosx/OSXViewController.h
|
||||||
|
src/macosx/OSXViewController.m
|
||||||
src/macosx/WindowData_OSX.h
|
src/macosx/WindowData_OSX.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "OSXWindow.h"
|
#include "OSXWindow.h"
|
||||||
#include "OSXWindowFrameView.h"
|
#include "OSXWindowFrameView.h"
|
||||||
|
#include "OSXViewController.h"
|
||||||
#include "WindowData_OSX.h"
|
#include "WindowData_OSX.h"
|
||||||
#include <MiniFB.h>
|
#include <MiniFB.h>
|
||||||
#include <MiniFB_enums.h>
|
#include <MiniFB_enums.h>
|
||||||
@ -15,102 +16,6 @@
|
|||||||
|
|
||||||
void init_keycodes();
|
void init_keycodes();
|
||||||
|
|
||||||
//-------------------------------------
|
|
||||||
#if defined(USE_METAL_API)
|
|
||||||
|
|
||||||
id<MTLDevice> g_metal_device = nil;
|
|
||||||
id<MTLLibrary> g_library = nil;
|
|
||||||
|
|
||||||
//-------------------------------------
|
|
||||||
#define kShader(inc, src) @inc#src
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
out.pos = pos[vID].position;
|
|
||||||
|
|
||||||
out.texcoord.x = (float) (vID / 2);
|
|
||||||
out.texcoord.y = 1.0 - (float) (vID % 2);
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
//---------------------
|
|
||||||
fragment float4
|
|
||||||
fragFunc(VertexOutput input [[stage_in]], texture2d<half> colorTexture [[ texture(0) ]]) {
|
|
||||||
constexpr sampler textureSampler(mag_filter::nearest, min_filter::nearest);
|
|
||||||
|
|
||||||
// Sample the texture to obtain a color
|
|
||||||
const half4 colorSample = colorTexture.sample(textureSampler, input.texcoord);
|
|
||||||
|
|
||||||
// We return the color of the texture
|
|
||||||
return float4(colorSample);
|
|
||||||
};
|
|
||||||
);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#if defined(USE_METAL_API)
|
|
||||||
static bool
|
|
||||||
create_shaders(SWindowData_OSX *window_data_osx) {
|
|
||||||
NSError *error = 0x0;
|
|
||||||
|
|
||||||
id<MTLLibrary> g_library = [g_metal_device newLibraryWithSource:g_shader_src
|
|
||||||
options:[[MTLCompileOptions alloc] init]
|
|
||||||
error:&error
|
|
||||||
];
|
|
||||||
if (error || !g_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"];
|
|
||||||
|
|
||||||
if (!vertex_shader_func) {
|
|
||||||
NSLog(@"Unable to get vertFunc!\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fragment_shader_func) {
|
|
||||||
NSLog(@"Unable to get fragFunc!\n");
|
|
||||||
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;
|
|
||||||
|
|
||||||
window_data_osx->metal.pipeline_state = [g_metal_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&error];
|
|
||||||
if (!window_data_osx->metal.pipeline_state) {
|
|
||||||
NSLog(@"Failed to created pipeline state, error %@", error);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
struct mfb_window *
|
struct mfb_window *
|
||||||
@ -200,47 +105,10 @@ mfb_open_ex(const char *title, unsigned width, unsigned height, unsigned flags)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(USE_METAL_API)
|
#if defined(USE_METAL_API)
|
||||||
g_metal_device = MTLCreateSystemDefaultDevice();
|
OSXViewController *viewController = [[OSXViewController alloc] initWithWindowData:window_data];
|
||||||
if (!g_metal_device) {
|
|
||||||
NSLog(@"Metal is not supported on this device");
|
|
||||||
return 0x0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!create_shaders((SWindowData_OSX *) window_data->specific)) {
|
|
||||||
return 0x0;
|
|
||||||
}
|
|
||||||
|
|
||||||
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_osx->metal.vertices, s_vertices, sizeof(s_vertices));
|
|
||||||
|
|
||||||
// Setup command queue
|
|
||||||
window_data_osx->metal.command_queue = [g_metal_device newCommandQueue];
|
|
||||||
|
|
||||||
WindowViewController* viewController = [WindowViewController new];
|
|
||||||
|
|
||||||
// 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:width
|
|
||||||
height:height
|
|
||||||
mipmapped:false];
|
|
||||||
|
|
||||||
// Create the texture from the device by using the descriptor
|
|
||||||
for (size_t i = 0; i < MaxBuffersInFlight; ++i) {
|
|
||||||
viewController->texture_buffers[i] = [g_metal_device newTextureWithDescriptor:td];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used for syncing the CPU and GPU
|
|
||||||
viewController->semaphore = dispatch_semaphore_create(MaxBuffersInFlight);
|
|
||||||
|
|
||||||
MTKView* view = [[MTKView alloc] initWithFrame:rectangle];
|
MTKView* view = [[MTKView alloc] initWithFrame:rectangle];
|
||||||
view.device = g_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];
|
||||||
|
27
src/macosx/OSXViewController.h
Normal file
27
src/macosx/OSXViewController.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if defined(USE_METAL_API)
|
||||||
|
#include "WindowData_OSX.h"
|
||||||
|
#import <MetalKit/MetalKit.h>
|
||||||
|
|
||||||
|
// Number of textures in flight (tripple buffered)
|
||||||
|
enum { MaxBuffersInFlight = 3 };
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@interface OSXViewController : NSViewController<MTKViewDelegate>
|
||||||
|
{
|
||||||
|
@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) initWithWindowData:(SWindowData *) windowData;
|
||||||
|
|
||||||
|
@end
|
||||||
|
#endif
|
212
src/macosx/OSXViewController.m
Normal file
212
src/macosx/OSXViewController.m
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
#include "OSXViewController.h"
|
||||||
|
|
||||||
|
#if defined(USE_METAL_API)
|
||||||
|
#import <MetalKit/MetalKit.h>
|
||||||
|
|
||||||
|
//-------------------------------------
|
||||||
|
#define kShader(inc, src) @inc#src
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
out.pos = pos[vID].position;
|
||||||
|
|
||||||
|
out.texcoord.x = (float) (vID / 2);
|
||||||
|
out.texcoord.y = 1.0 - (float) (vID % 2);
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------
|
||||||
|
fragment float4
|
||||||
|
fragFunc(VertexOutput input [[stage_in]], texture2d<half> colorTexture [[ texture(0) ]]) {
|
||||||
|
constexpr sampler textureSampler(mag_filter::nearest, min_filter::nearest);
|
||||||
|
|
||||||
|
// Sample the texture to obtain a color
|
||||||
|
const half4 colorSample = colorTexture.sample(textureSampler, input.texcoord);
|
||||||
|
|
||||||
|
// We return the color of the texture
|
||||||
|
return float4(colorSample);
|
||||||
|
};
|
||||||
|
);
|
||||||
|
|
||||||
|
//-------------------------------------
|
||||||
|
@implementation OSXViewController
|
||||||
|
|
||||||
|
//-------------------------------------
|
||||||
|
- (id) initWithWindowData:(SWindowData *) windowData {
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
window_data = windowData;
|
||||||
|
window_data_osx = (SWindowData_OSX *) windowData->specific;
|
||||||
|
|
||||||
|
metal_device = MTLCreateSystemDefaultDevice();
|
||||||
|
if (!metal_device) {
|
||||||
|
NSLog(@"Metal is not supported on this device");
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------
|
||||||
|
- (bool) _create_shaders {
|
||||||
|
NSError *error = 0x0;
|
||||||
|
|
||||||
|
metal_library = [metal_device newLibraryWithSource:g_shader_src
|
||||||
|
options:[[MTLCompileOptions alloc] init]
|
||||||
|
error:&error
|
||||||
|
];
|
||||||
|
if (error || !metal_library) {
|
||||||
|
NSLog(@"Unable to create shaders %@", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fragment_shader_func) {
|
||||||
|
NSLog(@"Unable to get fragFunc!\n");
|
||||||
|
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;
|
||||||
|
|
||||||
|
window_data_osx->metal.pipeline_state = [metal_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&error];
|
||||||
|
if (!window_data_osx->metal.pipeline_state) {
|
||||||
|
NSLog(@"Failed to created pipeline state, error %@", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------
|
||||||
|
- (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_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
|
||||||
|
height:window_data->buffer_height
|
||||||
|
mipmapped:false];
|
||||||
|
|
||||||
|
// Create the texture from the device by using the descriptor
|
||||||
|
for (size_t i = 0; i < MaxBuffersInFlight; ++i) {
|
||||||
|
texture_buffers[i] = [metal_device newTextureWithDescriptor:td];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------
|
||||||
|
- (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)
|
||||||
|
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];
|
||||||
|
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.
|
||||||
|
__block dispatch_semaphore_t block_sema = semaphore;
|
||||||
|
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer)
|
||||||
|
{
|
||||||
|
(void)buffer;
|
||||||
|
dispatch_semaphore_signal(block_sema);
|
||||||
|
}];
|
||||||
|
|
||||||
|
MTLRenderPassDescriptor* renderPassDescriptor = view.currentRenderPassDescriptor;
|
||||||
|
if (renderPassDescriptor != nil) {
|
||||||
|
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 1.0);
|
||||||
|
|
||||||
|
// Create a render command encoder so we can render into something
|
||||||
|
id<MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
|
||||||
|
renderEncoder.label = @"minifb_command_encoder";
|
||||||
|
|
||||||
|
// Set render command encoder state
|
||||||
|
[renderEncoder setRenderPipelineState:window_data_osx->metal.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];
|
||||||
|
|
||||||
|
// Draw the vertices of our quads
|
||||||
|
[renderEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
|
||||||
|
|
||||||
|
// We're done encoding commands
|
||||||
|
[renderEncoder endEncoding];
|
||||||
|
|
||||||
|
// Schedule a present once the framebuffer is complete using the current drawable
|
||||||
|
[commandBuffer presentDrawable:view.currentDrawable];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finalize rendering here & push the command buffer to the GPU
|
||||||
|
[commandBuffer commit];
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------
|
||||||
|
- (void) mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size {
|
||||||
|
(void)view;
|
||||||
|
(void)size;
|
||||||
|
// resize
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#endif
|
@ -2,26 +2,6 @@
|
|||||||
|
|
||||||
#include "WindowData.h"
|
#include "WindowData.h"
|
||||||
|
|
||||||
#if defined(USE_METAL_API)
|
|
||||||
#import <MetalKit/MetalKit.h>
|
|
||||||
|
|
||||||
// Number of textures in flight (tripple buffered)
|
|
||||||
enum { MaxBuffersInFlight = 3 };
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
@interface WindowViewController : NSViewController<MTKViewDelegate>
|
|
||||||
{
|
|
||||||
@public id<MTLTexture> texture_buffers[MaxBuffersInFlight];
|
|
||||||
@public int current_buffer;
|
|
||||||
@public dispatch_semaphore_t semaphore; // Used for syncing with CPU/GPU
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
#endif
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
@interface OSXWindowFrameView : NSView
|
@interface OSXWindowFrameView : NSView
|
||||||
{
|
{
|
||||||
@public SWindowData *window_data;
|
@public SWindowData *window_data;
|
||||||
|
@ -1,98 +1,14 @@
|
|||||||
#import "OSXWindowFrameView.h"
|
#import "OSXWindowFrameView.h"
|
||||||
#import "OSXWindow.h"
|
#import "OSXWindow.h"
|
||||||
#include "WindowData_OSX.h"
|
|
||||||
#include <MiniFB_internal.h>
|
#include <MiniFB_internal.h>
|
||||||
|
|
||||||
#if defined(USE_METAL_API)
|
//-------------------------------------
|
||||||
#import <MetalKit/MetalKit.h>
|
|
||||||
|
|
||||||
@implementation WindowViewController
|
|
||||||
|
|
||||||
- (void) drawInMTKView:(nonnull MTKView *)view {
|
|
||||||
OSXWindow *window = (OSXWindow *) view.window;
|
|
||||||
SWindowData *window_data = window->window_data;
|
|
||||||
if(window_data == 0x0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
// 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
|
|
||||||
SWindowData_OSX *window_data_osx = (SWindowData_OSX *) window->window_data->specific;
|
|
||||||
id<MTLCommandBuffer> commandBuffer = [window_data_osx->metal.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.
|
|
||||||
__block dispatch_semaphore_t block_sema = semaphore;
|
|
||||||
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer)
|
|
||||||
{
|
|
||||||
(void)buffer;
|
|
||||||
dispatch_semaphore_signal(block_sema);
|
|
||||||
}];
|
|
||||||
|
|
||||||
MTLRenderPassDescriptor* renderPassDescriptor = view.currentRenderPassDescriptor;
|
|
||||||
if (renderPassDescriptor != nil) {
|
|
||||||
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 1.0);
|
|
||||||
|
|
||||||
// Create a render command encoder so we can render into something
|
|
||||||
id<MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
|
|
||||||
renderEncoder.label = @"minifb_command_encoder";
|
|
||||||
|
|
||||||
// Set render command encoder state
|
|
||||||
OSXWindow *window = (OSXWindow *) view.window;
|
|
||||||
SWindowData_OSX *window_data_osx = (SWindowData_OSX *) window->window_data->specific;
|
|
||||||
[renderEncoder setRenderPipelineState:window_data_osx->metal.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];
|
|
||||||
|
|
||||||
// Draw the vertices of our quads
|
|
||||||
[renderEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
|
|
||||||
|
|
||||||
// We're done encoding commands
|
|
||||||
[renderEncoder endEncoding];
|
|
||||||
|
|
||||||
// Schedule a present once the framebuffer is complete using the current drawable
|
|
||||||
[commandBuffer presentDrawable:view.currentDrawable];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finalize rendering here & push the command buffer to the GPU
|
|
||||||
[commandBuffer commit];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size {
|
|
||||||
(void)view;
|
|
||||||
(void)size;
|
|
||||||
// resize
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@implementation OSXWindowFrameView
|
@implementation OSXWindowFrameView
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#if defined(USE_METAL_API)
|
#if defined(USE_METAL_API)
|
||||||
|
|
||||||
- (void)updateTrackingAreas
|
//-------------------------------------
|
||||||
{
|
- (void)updateTrackingAreas {
|
||||||
if(tracking_area != nil) {
|
if(tracking_area != nil) {
|
||||||
[self removeTrackingArea:tracking_area];
|
[self removeTrackingArea:tracking_area];
|
||||||
[tracking_area release];
|
[tracking_area release];
|
||||||
@ -108,10 +24,8 @@
|
|||||||
|
|
||||||
#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;
|
||||||
|
|
||||||
@ -126,10 +40,8 @@
|
|||||||
return resizeRect;
|
return resizeRect;
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
//-------------------------------------
|
||||||
|
- (void)drawRect:(NSRect)rect {
|
||||||
- (void)drawRect:(NSRect)rect
|
|
||||||
{
|
|
||||||
(void)rect;
|
(void)rect;
|
||||||
|
|
||||||
if(window_data == 0x0)
|
if(window_data == 0x0)
|
||||||
@ -162,104 +74,87 @@
|
|||||||
|
|
||||||
CGImageRelease(img);
|
CGImageRelease(img);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
//-------------------------------------
|
||||||
|
- (BOOL)acceptsFirstMouse:(NSEvent *)event {
|
||||||
- (BOOL)acceptsFirstMouse:(NSEvent *)event
|
|
||||||
{
|
|
||||||
(void)event;
|
(void)event;
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
//-------------------------------------
|
||||||
|
- (void)mouseDown:(NSEvent*)event {
|
||||||
- (void)mouseDown:(NSEvent*)event
|
|
||||||
{
|
|
||||||
(void)event;
|
(void)event;
|
||||||
if(window_data != 0x0) {
|
if(window_data != 0x0) {
|
||||||
kCall(mouse_btn_func, MOUSE_BTN_1, window_data->mod_keys, true);
|
kCall(mouse_btn_func, MOUSE_BTN_1, window_data->mod_keys, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
//-------------------------------------
|
||||||
|
- (void)mouseUp:(NSEvent*)event {
|
||||||
- (void)mouseUp:(NSEvent*)event
|
|
||||||
{
|
|
||||||
(void)event;
|
(void)event;
|
||||||
if(window_data != 0x0) {
|
if(window_data != 0x0) {
|
||||||
kCall(mouse_btn_func, MOUSE_BTN_1, window_data->mod_keys, false);
|
kCall(mouse_btn_func, MOUSE_BTN_1, window_data->mod_keys, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
//-------------------------------------
|
||||||
|
- (void)rightMouseDown:(NSEvent*)event {
|
||||||
- (void)rightMouseDown:(NSEvent*)event
|
|
||||||
{
|
|
||||||
(void)event;
|
(void)event;
|
||||||
if(window_data != 0x0) {
|
if(window_data != 0x0) {
|
||||||
kCall(mouse_btn_func, MOUSE_BTN_2, window_data->mod_keys, true);
|
kCall(mouse_btn_func, MOUSE_BTN_2, window_data->mod_keys, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
//-------------------------------------
|
||||||
|
- (void)rightMouseUp:(NSEvent*)event {
|
||||||
- (void)rightMouseUp:(NSEvent*)event
|
|
||||||
{
|
|
||||||
(void)event;
|
(void)event;
|
||||||
if(window_data != 0x0) {
|
if(window_data != 0x0) {
|
||||||
kCall(mouse_btn_func, MOUSE_BTN_1, window_data->mod_keys, false);
|
kCall(mouse_btn_func, MOUSE_BTN_1, window_data->mod_keys, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
//-------------------------------------
|
||||||
|
- (void)otherMouseDown:(NSEvent *)event {
|
||||||
- (void)otherMouseDown:(NSEvent *)event
|
|
||||||
{
|
|
||||||
(void)event;
|
(void)event;
|
||||||
if(window_data != 0x0) {
|
if(window_data != 0x0) {
|
||||||
kCall(mouse_btn_func, [event buttonNumber], window_data->mod_keys, true);
|
kCall(mouse_btn_func, [event buttonNumber], window_data->mod_keys, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
//-------------------------------------
|
||||||
|
- (void)otherMouseUp:(NSEvent *)event {
|
||||||
- (void)otherMouseUp:(NSEvent *)event
|
|
||||||
{
|
|
||||||
(void)event;
|
(void)event;
|
||||||
if(window_data != 0x0) {
|
if(window_data != 0x0) {
|
||||||
kCall(mouse_btn_func, [event buttonNumber], window_data->mod_keys, false);
|
kCall(mouse_btn_func, [event buttonNumber], window_data->mod_keys, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
//-------------------------------------
|
||||||
|
- (void)scrollWheel:(NSEvent *)event {
|
||||||
- (void)scrollWheel:(NSEvent *)event
|
|
||||||
{
|
|
||||||
if(window_data != 0x0) {
|
if(window_data != 0x0) {
|
||||||
kCall(mouse_wheel_func, window_data->mod_keys, [event deltaX], [event deltaY]);
|
kCall(mouse_wheel_func, window_data->mod_keys, [event deltaX], [event deltaY]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
//-------------------------------------
|
||||||
|
- (void)mouseDragged:(NSEvent *)event {
|
||||||
- (void)mouseDragged:(NSEvent *)event
|
|
||||||
{
|
|
||||||
[self mouseMoved:event];
|
[self mouseMoved:event];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)rightMouseDragged:(NSEvent *)event
|
//-------------------------------------
|
||||||
{
|
- (void)rightMouseDragged:(NSEvent *)event {
|
||||||
[self mouseMoved:event];
|
[self mouseMoved:event];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)otherMouseDragged:(NSEvent *)event
|
//-------------------------------------
|
||||||
{
|
- (void)otherMouseDragged:(NSEvent *)event {
|
||||||
[self mouseMoved:event];
|
[self mouseMoved:event];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mouseMoved:(NSEvent *)event
|
//-------------------------------------
|
||||||
{
|
- (void)mouseMoved:(NSEvent *)event {
|
||||||
if(window_data != 0x0) {
|
if(window_data != 0x0) {
|
||||||
NSPoint point = [event locationInWindow];
|
NSPoint point = [event locationInWindow];
|
||||||
//NSPoint localPoint = [self convertPoint:point fromView:nil];
|
//NSPoint localPoint = [self convertPoint:point fromView:nil];
|
||||||
@ -269,60 +164,44 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
//-------------------------------------
|
||||||
|
- (void)mouseExited:(NSEvent *)event {
|
||||||
- (void)mouseExited:(NSEvent *)event
|
|
||||||
{
|
|
||||||
(void)event;
|
(void)event;
|
||||||
//printf("mouse exit\n");
|
//printf("mouse exit\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
//-------------------------------------
|
||||||
|
- (void)mouseEntered:(NSEvent *)event {
|
||||||
- (void)mouseEntered:(NSEvent *)event
|
|
||||||
{
|
|
||||||
(void)event;
|
(void)event;
|
||||||
//printf("mouse enter\n");
|
//printf("mouse enter\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
//-------------------------------------
|
||||||
|
- (BOOL)canBecomeKeyView {
|
||||||
- (BOOL)canBecomeKeyView
|
|
||||||
{
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
//-------------------------------------
|
||||||
|
- (NSView *)nextValidKeyView {
|
||||||
- (NSView *)nextValidKeyView
|
|
||||||
{
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
//-------------------------------------
|
||||||
|
- (NSView *)previousValidKeyView {
|
||||||
- (NSView *)previousValidKeyView
|
|
||||||
{
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
//-------------------------------------
|
||||||
|
- (BOOL)acceptsFirstResponder {
|
||||||
- (BOOL)acceptsFirstResponder
|
|
||||||
{
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
//-------------------------------------
|
||||||
|
- (void)viewDidMoveToWindow {
|
||||||
- (void)viewDidMoveToWindow
|
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
//-------------------------------------
|
||||||
|
- (void)dealloc {
|
||||||
- (void)dealloc
|
|
||||||
{
|
|
||||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||||
[super dealloc];
|
[super dealloc];
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user