test 1
This commit is contained in:
15
src/ios/WindowData_IOS.h
Normal file
15
src/ios/WindowData_IOS.h
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <MiniFB_enums.h>
|
||||
#include <WindowData.h>
|
||||
|
||||
#include <MetalKit/MetalKit.h>
|
||||
|
||||
typedef struct Vertex {
|
||||
float x, y, z, w;
|
||||
} Vertex;
|
||||
|
||||
typedef struct {
|
||||
id<MTLCommandQueue> command_queue;
|
||||
id<MTLRenderPipelineState> pipeline_state;
|
||||
} SWindowData_IOS;
|
170
src/ios/iOSMiniFB.m
Normal file
170
src/ios/iOSMiniFB.m
Normal file
@ -0,0 +1,170 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#include <mach/mach_time.h>
|
||||
#include <MiniFB.h>
|
||||
#include "MiniFB_internal.h"
|
||||
#include "WindowData.h"
|
||||
#include "WindowData_IOS.h"
|
||||
|
||||
//-------------------------------------
|
||||
struct mfb_window *
|
||||
mfb_open(const char *title, unsigned width, unsigned height) {
|
||||
return mfb_open_ex(title, width, height, 0);
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
struct mfb_window *
|
||||
mfb_open_ex(const char *title, unsigned width, unsigned height, unsigned flags) {
|
||||
kUnused(title);
|
||||
kUnused(flags);
|
||||
SWindowData *window_data;
|
||||
|
||||
window_data = malloc(sizeof(SWindowData));
|
||||
memset(window_data, 0, sizeof(SWindowData));
|
||||
|
||||
SWindowData_IOS *window_data_ios = malloc(sizeof(SWindowData_IOS));
|
||||
memset((void *) window_data_ios, 0, sizeof(SWindowData_IOS));
|
||||
window_data->specific = window_data_ios;
|
||||
|
||||
window_data->window_width = width;
|
||||
window_data->window_height = height;
|
||||
|
||||
window_data->dst_width = width;
|
||||
window_data->dst_height = height;
|
||||
|
||||
window_data->buffer_width = width;
|
||||
window_data->buffer_height = height;
|
||||
window_data->buffer_stride = width * 4;
|
||||
|
||||
window_data->draw_buffer = malloc(width * height * 4);
|
||||
if (!window_data->draw_buffer) {
|
||||
NSLog(@"Unable to create draw buffer");
|
||||
}
|
||||
|
||||
return (struct mfb_window *) window_data;
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
static void
|
||||
destroy_window_data(SWindowData *window_data) {
|
||||
if(window_data == 0x0)
|
||||
return;
|
||||
|
||||
@autoreleasepool {
|
||||
SWindowData_IOS *window_data_ios = (SWindowData_IOS *) window_data->specific;
|
||||
if(window_data_ios != 0x0) {
|
||||
memset((void *) window_data_ios, 0, sizeof(SWindowData_IOS));
|
||||
free(window_data_ios);
|
||||
}
|
||||
memset(window_data, 0, sizeof(SWindowData));
|
||||
free(window_data);
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
mfb_update_state
|
||||
mfb_update(struct mfb_window *window, void *buffer) {
|
||||
if(window == 0x0) {
|
||||
return STATE_INVALID_WINDOW;
|
||||
}
|
||||
|
||||
SWindowData *window_data = (SWindowData *) window;
|
||||
if(window_data->close) {
|
||||
destroy_window_data(window_data);
|
||||
return STATE_EXIT;
|
||||
}
|
||||
|
||||
if(buffer == 0x0) {
|
||||
return STATE_INVALID_BUFFER;
|
||||
}
|
||||
|
||||
memcpy(window_data->draw_buffer, buffer, window_data->buffer_width * window_data->buffer_height * 4);
|
||||
|
||||
return STATE_OK;
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
mfb_update_state
|
||||
mfb_update_events(struct mfb_window *window) {
|
||||
return STATE_OK;
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
extern double g_time_for_frame;
|
||||
|
||||
bool
|
||||
mfb_wait_sync(struct mfb_window *window) {
|
||||
return true;
|
||||
}
|
||||
|
||||
extern Vertex g_vertices[4];
|
||||
|
||||
//-------------------------------------
|
||||
bool
|
||||
mfb_set_viewport(struct mfb_window *window, unsigned offset_x, unsigned offset_y, unsigned width, unsigned height) {
|
||||
SWindowData *window_data = (SWindowData *) window;
|
||||
|
||||
if(offset_x + width > window_data->window_width) {
|
||||
return false;
|
||||
}
|
||||
if(offset_y + height > window_data->window_height) {
|
||||
return false;
|
||||
}
|
||||
|
||||
window_data->dst_offset_x = offset_x;
|
||||
window_data->dst_offset_y = offset_y;
|
||||
window_data->dst_width = width;
|
||||
window_data->dst_height = height;
|
||||
|
||||
float x1 = ((float) offset_x / window_data->window_width) * 2.0f - 1.0f;
|
||||
float x2 = (((float) offset_x + width) / window_data->window_width) * 2.0f - 1.0f;
|
||||
float y1 = ((float) offset_y / window_data->window_height) * 2.0f - 1.0f;
|
||||
float y2 = (((float) offset_y + height) / window_data->window_height) * 2.0f - 1.0f;
|
||||
|
||||
g_vertices[0].x = x1;
|
||||
g_vertices[0].y = y1;
|
||||
|
||||
g_vertices[1].x = x1;
|
||||
g_vertices[1].y = y2;
|
||||
|
||||
g_vertices[2].x = x2;
|
||||
g_vertices[2].y = y1;
|
||||
|
||||
g_vertices[3].x = x2;
|
||||
g_vertices[3].y = y2;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
extern double g_timer_frequency;
|
||||
extern double g_timer_resolution;
|
||||
|
||||
uint64_t
|
||||
mfb_timer_tick() {
|
||||
static mach_timebase_info_data_t timebase = { 0 };
|
||||
|
||||
if (timebase.denom == 0) {
|
||||
(void) mach_timebase_info(&timebase);
|
||||
}
|
||||
|
||||
uint64_t time = mach_absolute_time();
|
||||
|
||||
//return (time * s_timebase_info.numer) / s_timebase_info.denom;
|
||||
|
||||
// Perform the arithmetic at 128-bit precision to avoid the overflow!
|
||||
uint64_t high = (time >> 32) * timebase.numer;
|
||||
uint64_t highRem = ((high % timebase.denom) << 32) / timebase.denom;
|
||||
uint64_t low = (time & 0xFFFFFFFFull) * timebase.numer / timebase.denom;
|
||||
high /= timebase.denom;
|
||||
|
||||
return (high << 32) + highRem + low;
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
void
|
||||
mfb_timer_init() {
|
||||
g_timer_frequency = 1e+9;
|
||||
g_timer_resolution = 1.0 / g_timer_frequency;
|
||||
}
|
||||
|
14
src/ios/iOSViewController.h
Normal file
14
src/ios/iOSViewController.h
Normal file
@ -0,0 +1,14 @@
|
||||
//
|
||||
// iOSViewController.h
|
||||
// MiniFB
|
||||
//
|
||||
// Created by Carlos Aragones on 22/04/2020.
|
||||
// Copyright © 2020 Carlos Aragones. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#include "iOSViewDelegate.h"
|
||||
|
||||
@interface iOSViewController : UIViewController
|
||||
|
||||
@end
|
49
src/ios/iOSViewController.m
Normal file
49
src/ios/iOSViewController.m
Normal file
@ -0,0 +1,49 @@
|
||||
//
|
||||
// iOSViewController.m
|
||||
// MiniFB
|
||||
//
|
||||
// Created by Carlos Aragones on 22/04/2020.
|
||||
// Copyright © 2020 Carlos Aragones. All rights reserved.
|
||||
//
|
||||
|
||||
#import "iOSViewController.h"
|
||||
#import <Metal/Metal.h>
|
||||
#import <MetalKit/MetalKit.h>
|
||||
#import "iOSViewDelegate.h"
|
||||
|
||||
//-------------------------------------
|
||||
@implementation iOSViewController
|
||||
{
|
||||
MTKView *_view;
|
||||
iOSViewDelegate *_renderer;
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
- (void) loadView {
|
||||
UIView *view = [[MTKView alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||
[self setView:view];
|
||||
[view release];
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
- (void) viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
_view = (MTKView *)self.view;
|
||||
_view.device = MTLCreateSystemDefaultDevice();
|
||||
_view.backgroundColor = UIColor.blackColor;
|
||||
|
||||
if(!_view.device) {
|
||||
NSLog(@"Metal is not supported on this device");
|
||||
self.view = [[UIView alloc] initWithFrame:self.view.frame];
|
||||
return;
|
||||
}
|
||||
|
||||
_renderer = [[iOSViewDelegate alloc] initWithMetalKitView:_view];
|
||||
[_renderer mtkView:_view drawableSizeWillChange:_view.bounds.size];
|
||||
|
||||
_view.delegate = _renderer;
|
||||
}
|
||||
|
||||
@end
|
23
src/ios/iOSViewDelegate.h
Normal file
23
src/ios/iOSViewDelegate.h
Normal file
@ -0,0 +1,23 @@
|
||||
//
|
||||
// Renderer.h
|
||||
// MiniFB
|
||||
//
|
||||
// Created by Carlos Aragones on 22/04/2020.
|
||||
// Copyright © 2020 Carlos Aragones. All rights reserved.
|
||||
//
|
||||
|
||||
#import <MetalKit/MetalKit.h>
|
||||
#include "WindowData.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 *m_window_data;
|
||||
}
|
||||
|
||||
-(nonnull instancetype)initWithMetalKitView:(nonnull MTKView *)view;
|
||||
|
||||
@end
|
||||
|
222
src/ios/iOSViewDelegate.m
Normal file
222
src/ios/iOSViewDelegate.m
Normal file
@ -0,0 +1,222 @@
|
||||
//
|
||||
// Renderer.m
|
||||
// MiniFB
|
||||
//
|
||||
// Created by Carlos Aragones on 22/04/2020.
|
||||
// Copyright © 2020 Carlos Aragones. All rights reserved.
|
||||
//
|
||||
|
||||
#import <simd/simd.h>
|
||||
#import <ModelIO/ModelIO.h>
|
||||
|
||||
#import "iOSViewDelegate.h"
|
||||
#include "WindowData_IOS.h"
|
||||
#include <MiniFB.h>
|
||||
#include <MiniFB_ios.h>
|
||||
|
||||
//-------------------------------------
|
||||
#define kShader(inc, src) @inc#src
|
||||
|
||||
//-------------------------------------
|
||||
enum { MaxBuffersInFlight = 3 }; // Number of textures in flight (tripple buffered)
|
||||
|
||||
id<MTLDevice> g_metal_device = nil;
|
||||
id<MTLLibrary> g_library = nil;
|
||||
|
||||
//--
|
||||
Vertex g_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},
|
||||
};
|
||||
|
||||
//--
|
||||
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 iOSViewDelegate {
|
||||
dispatch_semaphore_t m_semaphore;
|
||||
id <MTLCommandQueue> m_command_queue;
|
||||
|
||||
id <MTLRenderPipelineState> m_pipeline_state;
|
||||
id <MTLTexture> m_texture_buffer;
|
||||
|
||||
uint8_t m_current_buffer;
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
-(nonnull instancetype) initWithMetalKitView:(nonnull MTKView *) view {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
g_metal_device = view.device;
|
||||
|
||||
view.colorPixelFormat = MTLPixelFormatBGRA8Unorm;
|
||||
view.sampleCount = 1;
|
||||
|
||||
uint32_t width = view.bounds.size.width;
|
||||
uint32_t height = view.bounds.size.height;
|
||||
|
||||
m_window_data = (SWindowData *) mfb_open_ex("", width, height, 0);
|
||||
|
||||
m_semaphore = dispatch_semaphore_create(MaxBuffersInFlight);
|
||||
m_command_queue = [g_metal_device newCommandQueue];
|
||||
|
||||
[self _createShaders];
|
||||
[self _createAssets];
|
||||
|
||||
user_implemented_init((struct mfb_window *) m_window_data);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
- (bool) _createShaders {
|
||||
NSError *error = nil;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
NSLog(@"Failed to created pipeline state, error %@", error);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
- (void) _createAssets {
|
||||
MTLTextureDescriptor *td;
|
||||
td = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
|
||||
width:m_window_data->window_width
|
||||
height:m_window_data->window_height
|
||||
mipmapped:false];
|
||||
|
||||
m_texture_buffer = [g_metal_device newTextureWithDescriptor:td];
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
- (void) drawInMTKView:(nonnull MTKView *) view
|
||||
{
|
||||
// Per frame updates here
|
||||
user_implemented_update((struct mfb_window *) m_window_data);
|
||||
|
||||
dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER);
|
||||
|
||||
m_current_buffer = (m_current_buffer + 1) % MaxBuffersInFlight;
|
||||
|
||||
id <MTLCommandBuffer> commandBuffer = [m_command_queue commandBuffer];
|
||||
commandBuffer.label = @"minifb_command_buffer";
|
||||
|
||||
__block dispatch_semaphore_t block_sema = m_semaphore;
|
||||
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer) {
|
||||
dispatch_semaphore_signal(block_sema);
|
||||
}];
|
||||
|
||||
// Copy the bytes from our data object into the texture
|
||||
MTLRegion region = { { 0, 0, 0 }, { m_window_data->window_width, m_window_data->window_height, 1 } };
|
||||
[m_texture_buffer replaceRegion:region mipmapLevel:0 withBytes:m_window_data->draw_buffer bytesPerRow:m_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);
|
||||
|
||||
// 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:m_pipeline_state];
|
||||
[renderEncoder setVertexBytes:g_vertices length:sizeof(g_vertices) atIndex:0];
|
||||
|
||||
//[renderEncoder setFragmentTexture:m_texture_buffers[m_current_buffer] atIndex:0];
|
||||
[renderEncoder setFragmentTexture:m_texture_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 {
|
||||
// Respond to drawable size or orientation changes here
|
||||
}
|
||||
|
||||
@end
|
Reference in New Issue
Block a user