Basic metal renderer (only rendering a triangle as of right now).
This commit is contained in:
parent
4e0549f270
commit
0fceec19a9
|
@ -389,7 +389,7 @@ build(Arena *arena, u32 flags, u32 arch, char *code_path, char **code_files, cha
|
|||
#define CLANG_LIBS_COMMON \
|
||||
"-framework Cocoa -framework QuartzCore " \
|
||||
"-framework CoreServices " \
|
||||
"-framework OpenGL -framework IOKit "
|
||||
"-framework OpenGL -framework IOKit -framework Metal -framework MetalKit "
|
||||
|
||||
#define CLANG_LIBS_X64 CLANG_LIBS_COMMON \
|
||||
FOREIGN "/x64/libfreetype-mac.a"
|
||||
|
@ -398,7 +398,7 @@ FOREIGN "/x64/libfreetype-mac.a"
|
|||
FOREIGN "/x86/libfreetype-mac.a"
|
||||
|
||||
#else
|
||||
# error gcc options not set for this platform
|
||||
# error clang options not set for this platform
|
||||
#endif
|
||||
|
||||
internal void
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
/* 4coder Metal render implementation */
|
||||
|
||||
#undef clamp
|
||||
#undef function
|
||||
#import <simd/simd.h>
|
||||
#import <MetalKit/MetalKit.h>
|
||||
|
||||
// Header shared between C code here, which executes Metal API commands, and .metal files, which
|
||||
// uses these types as inputs to the shaders.
|
||||
#import "AAPLShaderTypes.h"
|
||||
|
||||
#define function static
|
||||
#define clamp(a,x,b) clamp_((a),(x),(b))
|
||||
|
||||
@interface FCoderMetalRenderer : NSObject<MTKViewDelegate>
|
||||
- (nonnull instancetype)initWithMetalKitView:(nonnull MTKView *)mtkView;
|
||||
@end
|
||||
|
||||
@implementation FCoderMetalRenderer{
|
||||
id<MTLDevice> _device;
|
||||
|
||||
// The render pipeline generated from the vertex and fragment shaders in the .metal shader file.
|
||||
id<MTLRenderPipelineState> _pipelineState;
|
||||
|
||||
// The command queue used to pass commands to the device.
|
||||
id<MTLCommandQueue> _commandQueue;
|
||||
|
||||
// The current size of the view, used as an input to the vertex shader.
|
||||
vector_uint2 _viewportSize;
|
||||
}
|
||||
|
||||
- (nonnull instancetype)initWithMetalKitView:(nonnull MTKView *)mtkView{
|
||||
self = [super init];
|
||||
if(self)
|
||||
{
|
||||
NSError *error = nil;
|
||||
|
||||
_device = mtkView.device;
|
||||
|
||||
// Load all the shader files with a .metal file extension in the project.
|
||||
id<MTLLibrary> defaultLibrary = [_device newLibraryWithFile:@"shaders/AAPLShaders.metallib"
|
||||
error:&error];
|
||||
Assert(error == nil);
|
||||
|
||||
id<MTLFunction> vertexFunction = [defaultLibrary newFunctionWithName:@"vertexShader"];
|
||||
id<MTLFunction> fragmentFunction = [defaultLibrary newFunctionWithName:@"fragmentShader"];
|
||||
|
||||
// Configure a pipeline descriptor that is used to create a pipeline state.
|
||||
MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
|
||||
pipelineStateDescriptor.label = @"Simple Pipeline";
|
||||
pipelineStateDescriptor.vertexFunction = vertexFunction;
|
||||
pipelineStateDescriptor.fragmentFunction = fragmentFunction;
|
||||
pipelineStateDescriptor.colorAttachments[0].pixelFormat = mtkView.colorPixelFormat;
|
||||
|
||||
_pipelineState = [_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor
|
||||
error:&error];
|
||||
|
||||
// Pipeline State creation could fail if the pipeline descriptor isn't set up properly.
|
||||
// If the Metal API validation is enabled, you can find out more information about what
|
||||
// went wrong. (Metal API validation is enabled by default when a debug build is run
|
||||
// from Xcode.)
|
||||
NSAssert(_pipelineState, @"Failed to created pipeline state: %@", error);
|
||||
|
||||
// Create the command queue
|
||||
_commandQueue = [_device newCommandQueue];
|
||||
|
||||
u32 max_buffer_size = (u32)[_device maxBufferLength];
|
||||
printf("Max Buffer Size: %u - Which is %lu vertices\n", max_buffer_size, (max_buffer_size / sizeof(Render_Vertex)));
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/// Called whenever view changes orientation or is resized
|
||||
- (void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size{
|
||||
// Save the size of the drawable to pass to the vertex shader.
|
||||
|
||||
}
|
||||
|
||||
/// Called whenever the view needs to render a frame.
|
||||
- (void)drawInMTKView:(nonnull MTKView *)view{
|
||||
CGSize size = [view drawableSize];
|
||||
_viewportSize.x = size.width;
|
||||
_viewportSize.y = size.height;
|
||||
|
||||
static const AAPLVertex triangleVertices[] =
|
||||
{
|
||||
// 2D positions, RGBA colors
|
||||
{ { 250, -250 }, { 1, 0, 0, 1 } },
|
||||
{ { -250, -250 }, { 0, 1, 0, 1 } },
|
||||
{ { 0, 250 }, { 0, 0, 1, 1 } },
|
||||
};
|
||||
|
||||
// Create a new command buffer for each render pass to the current drawable.
|
||||
id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
|
||||
commandBuffer.label = @"MyCommand";
|
||||
|
||||
// Obtain a renderPassDescriptor generated from the view's drawable textures.
|
||||
MTLRenderPassDescriptor *renderPassDescriptor = view.currentRenderPassDescriptor;
|
||||
|
||||
if(renderPassDescriptor != nil)
|
||||
{
|
||||
// Create a render command encoder.
|
||||
id<MTLRenderCommandEncoder> renderEncoder =
|
||||
[commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
|
||||
renderEncoder.label = @"MyRenderEncoder";
|
||||
|
||||
// Set the region of the drawable to draw into.
|
||||
[renderEncoder setViewport:(MTLViewport){0.0, 0.0, (double)_viewportSize.x, (double)_viewportSize.y, 0.0, 1.0 }];
|
||||
|
||||
[renderEncoder setRenderPipelineState:_pipelineState];
|
||||
|
||||
// Pass in the parameter data.
|
||||
[renderEncoder setVertexBytes:triangleVertices
|
||||
length:sizeof(triangleVertices)
|
||||
atIndex:AAPLVertexInputIndexVertices];
|
||||
|
||||
[renderEncoder setVertexBytes:&_viewportSize
|
||||
length:sizeof(_viewportSize)
|
||||
atIndex:AAPLVertexInputIndexViewportSize];
|
||||
|
||||
// Draw the triangle.
|
||||
[renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle
|
||||
vertexStart:0
|
||||
vertexCount:3];
|
||||
|
||||
[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];
|
||||
}
|
||||
@end
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
See LICENSE folder for this sample’s licensing information.
|
||||
|
||||
Abstract:
|
||||
Header containing types and enum constants shared between Metal shaders and C/ObjC source
|
||||
*/
|
||||
|
||||
#ifndef AAPLShaderTypes_h
|
||||
#define AAPLShaderTypes_h
|
||||
|
||||
#undef clamp
|
||||
#include <simd/simd.h>
|
||||
#define clamp(a,x,b) clamp_((a),(x),(b))
|
||||
|
||||
// Buffer index values shared between shader and C code to ensure Metal shader buffer inputs
|
||||
// match Metal API buffer set calls.
|
||||
typedef enum AAPLVertexInputIndex
|
||||
{
|
||||
AAPLVertexInputIndexVertices = 0,
|
||||
AAPLVertexInputIndexViewportSize = 1,
|
||||
} AAPLVertexInputIndex;
|
||||
|
||||
// This structure defines the layout of vertices sent to the vertex
|
||||
// shader. This header is shared between the .metal shader and C code, to guarantee that
|
||||
// the layout of the vertex array in the C code matches the layout that the .metal
|
||||
// vertex shader expects.
|
||||
typedef struct
|
||||
{
|
||||
vector_float2 position;
|
||||
vector_float4 color;
|
||||
} AAPLVertex;
|
||||
|
||||
#endif /* AAPLShaderTypes_h */
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
See LICENSE folder for this sample’s licensing information.
|
||||
|
||||
Abstract:
|
||||
Metal shaders used for this sample
|
||||
*/
|
||||
|
||||
#include <metal_stdlib>
|
||||
#include <simd/simd.h>
|
||||
|
||||
using namespace metal;
|
||||
|
||||
// Include header shared between this Metal shader code and C code executing Metal API commands.
|
||||
#import "AAPLShaderTypes.h"
|
||||
|
||||
// Vertex shader outputs and fragment shader inputs
|
||||
typedef struct
|
||||
{
|
||||
// The [[position]] attribute of this member indicates that this value
|
||||
// is the clip space position of the vertex when this structure is
|
||||
// returned from the vertex function.
|
||||
float4 position [[position]];
|
||||
|
||||
// Since this member does not have a special attribute, the rasterizer
|
||||
// interpolates its value with the values of the other triangle vertices
|
||||
// and then passes the interpolated value to the fragment shader for each
|
||||
// fragment in the triangle.
|
||||
float4 color;
|
||||
|
||||
} RasterizerData;
|
||||
|
||||
vertex RasterizerData
|
||||
vertexShader(uint vertexID [[vertex_id]],
|
||||
constant AAPLVertex *vertices [[buffer(AAPLVertexInputIndexVertices)]],
|
||||
constant vector_uint2 *viewportSizePointer [[buffer(AAPLVertexInputIndexViewportSize)]])
|
||||
{
|
||||
RasterizerData out;
|
||||
|
||||
// Index into the array of positions to get the current vertex.
|
||||
// The positions are specified in pixel dimensions (i.e. a value of 100
|
||||
// is 100 pixels from the origin).
|
||||
float2 pixelSpacePosition = vertices[vertexID].position.xy;
|
||||
|
||||
// Get the viewport size and cast to float.
|
||||
vector_float2 viewportSize = vector_float2(*viewportSizePointer);
|
||||
|
||||
|
||||
// To convert from positions in pixel space to positions in clip-space,
|
||||
// divide the pixel coordinates by half the size of the viewport.
|
||||
out.position = vector_float4(0.0, 0.0, 0.0, 1.0);
|
||||
out.position.xy = pixelSpacePosition / (viewportSize / 2.0);
|
||||
|
||||
// Pass the input color directly to the rasterizer.
|
||||
out.color = vertices[vertexID].color;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
fragment float4 fragmentShader(RasterizerData in [[stage_in]])
|
||||
{
|
||||
// Return the interpolated color.
|
||||
return in.color;
|
||||
}
|
||||
|
|
@ -285,6 +285,9 @@ gl_render(Render_Target *t){
|
|||
t->free_texture_first = 0;
|
||||
t->free_texture_last = 0;
|
||||
|
||||
u32 all_vertex_count = 0;
|
||||
|
||||
u64 begin_draw = system_now_time();
|
||||
for (Render_Group *group = t->group_first;
|
||||
group != 0;
|
||||
group = group->next){
|
||||
|
@ -339,9 +342,18 @@ gl_render(Render_Target *t){
|
|||
glDisableVertexAttribArray(gpu_program.vertex_c);
|
||||
glDisableVertexAttribArray(gpu_program.vertex_ht);
|
||||
}
|
||||
|
||||
all_vertex_count += vertex_count;
|
||||
}
|
||||
u64 end_draw = system_now_time();
|
||||
printf("Draw time: %fs\n", mac_get_time_diff_sec(begin_draw, end_draw));
|
||||
|
||||
u64 begin_flush = system_now_time();
|
||||
glFlush();
|
||||
u64 end_flush = system_now_time();
|
||||
printf("Flush time: %fs\n", mac_get_time_diff_sec(begin_flush, end_flush));
|
||||
|
||||
printf("Drawn %d Vertices\n", all_vertex_count);
|
||||
}
|
||||
|
||||
// BOTTOM
|
||||
|
|
|
@ -273,11 +273,24 @@ mac_to_object(Plat_Handle handle){
|
|||
#include <OpenGL/gl.h>
|
||||
#import "mac_4ed_opengl.mm"
|
||||
|
||||
#import "mac_4ed_metal.mm"
|
||||
|
||||
#include "4ed_font_provider_freetype.h"
|
||||
#include "4ed_font_provider_freetype.cpp"
|
||||
|
||||
#import "mac_4ed_functions.mm"
|
||||
|
||||
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
global Key_Code keycode_lookup_table[255];
|
||||
|
||||
function void
|
||||
mac_key_code_init(void){
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
function void
|
||||
|
@ -326,8 +339,6 @@ mac_resize(NSWindow *window){
|
|||
|
||||
////////////////////////////////
|
||||
|
||||
// TODO(yuval): mac_resize(bounds.size.width, bounds.size.height);
|
||||
|
||||
@implementation FCoderAppDelegate
|
||||
- (void)applicationDidFinishLaunching:(id)sender{
|
||||
}
|
||||
|
@ -376,6 +387,13 @@ mac_resize(NSWindow *window){
|
|||
}
|
||||
|
||||
- (void)drawRect:(NSRect)bounds{
|
||||
/* NOTE(yuval): Force the graphics context to clear to black so we don't
|
||||
get a flash of white until the app is ready to draw. In practice on modern macOS,
|
||||
this only gets called for window creation and other extraordinary events.
|
||||
(Taken From SDL) */
|
||||
[[NSColor blackColor] setFill];
|
||||
NSRectFill(bounds);
|
||||
|
||||
// NOTE(yuval): Read comment in win32_4ed.cpp's main loop
|
||||
system_mutex_release(mac_vars.global_frame_mutex);
|
||||
|
||||
|
@ -422,7 +440,8 @@ mac_resize(NSWindow *window){
|
|||
[NSApp terminate:nil];
|
||||
}
|
||||
|
||||
mac_gl_render(&target);
|
||||
// mac_gl_render(&target);
|
||||
mac_metal_render(&target);
|
||||
|
||||
mac_vars.first = false;
|
||||
|
||||
|
@ -670,17 +689,19 @@ main(int arg_count, char **args){
|
|||
|
||||
NSView* content_view = [mac_vars.window contentView];
|
||||
|
||||
// NOTE(yuval): Initialize the renderer
|
||||
mac_gl_init(mac_vars.window);
|
||||
|
||||
// NOTE(yuval): Create the 4coder view
|
||||
mac_vars.view = [[FCoderView alloc] init];
|
||||
[mac_vars.view setFrame:[content_view bounds]];
|
||||
[mac_vars.view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
|
||||
|
||||
// NOTE(yuval): Display window and view
|
||||
[content_view addSubview:mac_vars.view];
|
||||
[mac_vars.window makeKeyAndOrderFront:nil];
|
||||
|
||||
// NOTE(yuval): Initialize the renderer
|
||||
mac_gl_init(mac_vars.window);
|
||||
mac_metal_init(mac_vars.window);
|
||||
|
||||
mac_resize(w, h);
|
||||
|
||||
//
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
#import "metal/4ed_metal_render.mm"
|
||||
|
||||
global MTKView *metal_view;
|
||||
global FCoderMetalRenderer *metal_renderer;
|
||||
|
||||
function void
|
||||
mac_metal_init(NSWindow *window){
|
||||
// NOTE(yuval): Create Metal view
|
||||
NSView *content_view = [window contentView];
|
||||
|
||||
metal_view = [[MTKView alloc] initWithFrame:[content_view bounds]];
|
||||
[metal_view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
|
||||
|
||||
metal_view.device = MTLCreateSystemDefaultDevice();
|
||||
|
||||
// NOTE(yuval): Add the Metal view as a subview of the window
|
||||
[content_view addSubview:metal_view];
|
||||
|
||||
// NOTE(yuval): Create the Metal renderer
|
||||
metal_renderer = [[FCoderMetalRenderer alloc] initWithMetalKitView:metal_view];
|
||||
}
|
||||
|
||||
function void
|
||||
mac_metal_render(Render_Target* target){
|
||||
u64 begin_time = system_now_time();
|
||||
[metal_renderer drawInMTKView:metal_view];
|
||||
u64 end_time = system_now_time();
|
||||
printf("Metal Render Time: %fs\n\n", mac_get_time_diff_sec(begin_time, end_time));
|
||||
}
|
|
@ -5,6 +5,11 @@
|
|||
#define GL_FUNC(N,R,P) typedef R (CALL_CONVENTION N##_Function)P; N##_Function *N = 0;
|
||||
#include "mac_4ed_opengl_funcs.h"
|
||||
|
||||
f64 mac_get_time_diff_sec(u64 begin, u64 end){
|
||||
f64 result = ((end - begin) / 1000000.0);
|
||||
return result;
|
||||
}
|
||||
|
||||
#include "opengl/4ed_opengl_render.cpp"
|
||||
|
||||
@interface OpenGLView : NSOpenGLView
|
||||
|
@ -12,8 +17,6 @@
|
|||
- (void)render:(Render_Target*)target;
|
||||
@end
|
||||
|
||||
global OpenGLView *opengl_view;
|
||||
|
||||
@implementation OpenGLView{
|
||||
b32 glIsInitialized;
|
||||
}
|
||||
|
@ -95,14 +98,35 @@ global OpenGLView *opengl_view;
|
|||
- (void)render:(Render_Target*)target{
|
||||
Assert(glIsInitialized);
|
||||
|
||||
u64 context_lock_begin = system_now_time();
|
||||
CGLLockContext([[self openGLContext] CGLContextObj]);
|
||||
u64 context_lock_end = system_now_time();
|
||||
printf("Context lock time: %fs\n", mac_get_time_diff_sec(context_lock_begin, context_lock_end));
|
||||
|
||||
u64 make_current_context_begin = system_now_time();
|
||||
[[self openGLContext] makeCurrentContext];
|
||||
u64 make_current_context_end = system_now_time();
|
||||
printf("Make current context time: %fs\n", mac_get_time_diff_sec(make_current_context_begin, make_current_context_end));
|
||||
|
||||
u64 gl_render_begin = system_now_time();
|
||||
gl_render(target);
|
||||
u64 gl_render_end = system_now_time();
|
||||
printf("GL render time: %fs\n", mac_get_time_diff_sec(gl_render_begin, gl_render_end));
|
||||
|
||||
u64 gl_flush_buffer_begin = system_now_time();
|
||||
[[self openGLContext] flushBuffer];
|
||||
u64 gl_flush_buffer_end = system_now_time();
|
||||
printf("GL flush buffer time: %fs\n", mac_get_time_diff_sec(gl_flush_buffer_begin, gl_flush_buffer_end));
|
||||
|
||||
u64 context_unlock_begin = system_now_time();
|
||||
CGLUnlockContext([[self openGLContext] CGLContextObj]);
|
||||
u64 context_unlock_end = system_now_time();
|
||||
printf("Context unlock time: %fs\n", mac_get_time_diff_sec(context_unlock_begin, context_unlock_end));
|
||||
}
|
||||
@end
|
||||
|
||||
global OpenGLView *opengl_view;
|
||||
|
||||
function void
|
||||
mac_gl_init(NSWindow *window){
|
||||
// NOTE(yuval): Create OpenGLView
|
||||
|
@ -125,8 +149,8 @@ mac_gl_init(NSWindow *window){
|
|||
|
||||
function void
|
||||
mac_gl_render(Render_Target* target){
|
||||
f64 begin_time = system_now_time() / 1000000.0;
|
||||
u64 begin_time = system_now_time();
|
||||
[opengl_view render:target];
|
||||
f64 end_time = system_now_time() / 1000000.0;
|
||||
printf("Render Time: %fs\n", (end_time - begin_time));
|
||||
u64 end_time = system_now_time();
|
||||
printf("Render Time: %fs\n\n", mac_get_time_diff_sec(begin_time, end_time));
|
||||
}
|
Loading…
Reference in New Issue