From b52f1cee246a37ed5850df7cc08af3230237e20f Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Sun, 5 Jan 2020 03:13:47 +0200 Subject: [PATCH] Metal projection matrix test. --- bin/4ed_build.cpp | 11 +- metal/4ed_metal_render.mm | 245 +++++++++++++++++++++++++++++++++- platform_mac/mac_4ed_metal.mm | 8 +- 3 files changed, 250 insertions(+), 14 deletions(-) diff --git a/bin/4ed_build.cpp b/bin/4ed_build.cpp index 0a98abdb..8640443b 100644 --- a/bin/4ed_build.cpp +++ b/bin/4ed_build.cpp @@ -380,11 +380,12 @@ build(Arena *arena, u32 flags, u32 arch, char *code_path, char **code_files, cha #if OS_MAC -# define CLANG_OPTS \ -"-Wno-write-strings -Wno-deprecated-declarations " \ -"-Wno-comment -Wno-switch -Wno-null-dereference " \ -"-Wno-tautological-compare " \ -"-Wno-unused-result -Wno-missing-declarations -std=c++11 " +# define CLANG_OPTS \ +"-Wno-write-strings -Wno-deprecated-declarations " \ +"-Wno-comment -Wno-switch -Wno-null-dereference " \ +"-Wno-tautological-compare -Wno-unused-result " \ +"-Wno-missing-declarations -Wno-nullability-completeness " \ +"-std=c++11 " #define CLANG_LIBS_COMMON \ "-framework Cocoa -framework QuartzCore " \ diff --git a/metal/4ed_metal_render.mm b/metal/4ed_metal_render.mm index e715a8fe..442a3e50 100644 --- a/metal/4ed_metal_render.mm +++ b/metal/4ed_metal_render.mm @@ -5,12 +5,88 @@ #import #import -// 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" - +#include "AAPLShaderTypes.h" #define function static -#define clamp(a,x,b) clamp_((a),(x),(b)) + +struct Metal_Renderer{ + MTKView *view; + + id device; + id pipeline_state; + id command_queue; + id buffer; +}; + +global_const u32 metal_max_vertices = (1<<16); + +global_const char *metal__shaders_source = R"( +#include +#include + +using namespace metal; + +// 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; + +// 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 float4x4 &projMatrix[[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; + + // 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 = float4(pixelSpacePosition, 0.0, 1.0) * projMatrix; + + // 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; +} +)"; @interface FCoderMetalRenderer : NSObject - (nonnull instancetype)initWithMetalKitView:(nonnull MTKView *)mtkView; @@ -133,4 +209,161 @@ // Finalize rendering here & push the command buffer to the GPU. [commandBuffer commit]; } -@end \ No newline at end of file +@end + +function b32 +metal_init(Metal_Renderer *renderer, MTKView *view){ + NSError *error = nil; + + renderer->view = view; + renderer->device = view.device; + + // NOTE(yuval): Compile the shaders + id vertex_function = nil; + id fragment_function = nil; + { + NSString *shaders_source_str = [NSString stringWithUTF8String:metal__shaders_source]; + + MTLCompileOptions *options = [[MTLCompileOptions alloc] init]; + options.fastMathEnabled = YES; + + id shader_library = [renderer->device newLibraryWithSource:shaders_source_str + options:options error:&error]; + vertex_function = [shader_library newFunctionWithName:@"vertexShader"]; + fragment_function = [shader_library newFunctionWithName:@"fragmentShader"]; + + [options release]; + } + + if (error != nil){ + return(false); + } + + // NOTE(yuval): Configure the pipeline descriptor + { + MTLRenderPipelineDescriptor *pipeline_state_descriptor = [[MTLRenderPipelineDescriptor alloc] init]; + pipeline_state_descriptor.label = @"4coder Metal Renderer Pipeline"; + pipeline_state_descriptor.vertexFunction = vertex_function; + pipeline_state_descriptor.fragmentFunction = fragment_function; + pipeline_state_descriptor.colorAttachments[0].pixelFormat = view.colorPixelFormat; + + renderer->pipeline_state = [renderer->device newRenderPipelineStateWithDescriptor:pipeline_state_descriptor + error:&error]; + } + + if (error != nil){ + return(false); + } + + // NOTE(yuval): Create the command queue + renderer->command_queue = [renderer->device newCommandQueue]; + + // NOTE(yuval): Create the vertex buffer + { + u32 buffer_size = (metal_max_vertices * sizeof(Render_Vertex)); + MTLResourceOptions options = MTLCPUCacheModeWriteCombined|MTLResourceStorageModeManaged; + renderer->buffer = [renderer->device newBufferWithLength:buffer_size + options:options]; + } + + return(true); +} + +function void +metal_render(Metal_Renderer *renderer, Render_Target *t){ + static const AAPLVertex triangleVertices[] = { + // 2D positions, RGBA colors + { { 200, 100 }, { 1, 0, 0, 1 } }, + { { 100, 100 }, { 0, 1, 0, 1 } }, + { { 150, 200 }, { 0, 0, 1, 1 } }, + }; + + // NOTE(yuval): Create the command buffer + id command_buffer = [renderer->command_queue commandBuffer]; + command_buffer.label = @"4coder Metal Render Command"; + + // NOTE(yuval): Obtain the render pass descriptor from the renderer's view + MTLRenderPassDescriptor *render_pass_descriptor = renderer->view.currentRenderPassDescriptor; + if (render_pass_descriptor != nil){ + // NOTE(yuval): Create the render command encoder + id render_encoder + = [command_buffer renderCommandEncoderWithDescriptor:render_pass_descriptor]; + render_encoder.label = @"4coder Render Encoder"; + + // NOTE(yuval): Set the region of the drawable to draw into + [render_encoder setViewport:(MTLViewport){0.0, 0.0, (double)t->width, (double)t->height, 0.0, 1.0}]; + + // NOTE(yuval): Set the render pipeline to use for drawing + [render_encoder setRenderPipelineState:renderer->pipeline_state]; + + // NOTE(yuval): Pass in the parameter data + [render_encoder setVertexBytes:triangleVertices + length:sizeof(triangleVertices) + atIndex:AAPLVertexInputIndexVertices]; + +#if 0 + vector_uint2 viewport_size = {(u32)t->width, (u32)t->height}; + [render_encoder setVertexBytes:&viewport_size + length:sizeof(viewport_size) + atIndex:AAPLVertexInputIndexViewportSize]; +#else + float left = 0, right = (float)t->width; + float bottom = 0, top = (float)t->height; + float near_depth = -1.0f, far_depth = 1.0f; + float m[16] = { + 2.0f / (right - left), 0.0f, 0.0f, 0.0f, + 0.0f, 2.0f / (top - bottom), 0.0f, 0.0f, + 0.0f, 0.0f, -1.0f / (far_depth - near_depth), 0.0f, + -((right + left) / (right - left)), -((top + bottom) / (top - bottom)), + (-near_depth) * (far_depth - near_depth), 1.0f + }; + + float sLength = 1.0f / (right - left); + float sHeight = 1.0f / (top - bottom); + float sDepth = 1.0f / (far_depth - near_depth); + + simd::float4 P; + simd::float4 Q; + simd::float4 R; + simd::float4 S; + + P.x = 2.0f * sLength; + P.y = 0.0f; + P.z = 0.0f; + P.w = -((right + left) / (right - left)); + + Q.x = 0.0f; + Q.y = 2.0f * sHeight; + Q.z = 0.0f; + Q.w = -((top + bottom) / (top - bottom)); + + R.x = 0.0f; + R.y = 0.0f; + R.z = sDepth; + R.w = -near_depth * sDepth; + + S.x = 0.0f; + S.y = 0.0f; + S.z = 0.0f; + S.w = 1.0f; + + simd_float4x4 proj = simd::float4x4(P, Q, R, S); + + [render_encoder setVertexBytes:&proj + length:sizeof(proj) + atIndex:AAPLVertexInputIndexViewportSize]; +#endif + + // NOTE(yuval): Draw the triangle + [render_encoder drawPrimitives:MTLPrimitiveTypeTriangle + vertexStart:0 + vertexCount:3]; + + [render_encoder endEncoding]; + + // NOTE(yuval): Schedule a present once the framebuffer is complete using the current drawable + [command_buffer presentDrawable:renderer->view.currentDrawable]; + } + + [command_buffer commit]; +} \ No newline at end of file diff --git a/platform_mac/mac_4ed_metal.mm b/platform_mac/mac_4ed_metal.mm index 05ed482e..ff69d8d3 100644 --- a/platform_mac/mac_4ed_metal.mm +++ b/platform_mac/mac_4ed_metal.mm @@ -1,7 +1,7 @@ #import "metal/4ed_metal_render.mm" +global Metal_Renderer metal_renderer; global MTKView *metal_view; -global FCoderMetalRenderer *metal_renderer; function void mac_metal_init(NSWindow *window){ @@ -17,13 +17,15 @@ mac_metal_init(NSWindow *window){ [content_view addSubview:metal_view]; // NOTE(yuval): Create the Metal renderer - metal_renderer = [[FCoderMetalRenderer alloc] initWithMetalKitView:metal_view]; + //metal_renderer = [[FCoderMetalRenderer alloc] initWithMetalKitView:metal_view]; + metal_init(&metal_renderer, metal_view); } function void mac_metal_render(Render_Target* target){ u64 begin_time = system_now_time(); - [metal_renderer drawInMTKView:metal_view]; + //[metal_renderer drawInMTKView:metal_view]; + metal_render(&metal_renderer, target); u64 end_time = system_now_time(); printf("Metal Render Time: %fs\n\n", mac_get_time_diff_sec(begin_time, end_time)); } \ No newline at end of file