/************************************************************************* * * Orca * Copyright 2023 Martin Fouilleul and the Orca project contributors * See LICENSE.txt for licensing information * **************************************************************************/ #include #include #define _USE_MATH_DEFINES //NOTE: necessary for MSVC #include #include "orca.h" #include "graphics/mtl_surface.h" #import #import #include "vertex.h" static const my_vertex triangle[3] = { { { 250, -250 }, { 1, 0, 0, 1 } }, { { -250, -250 }, { 0, 1, 0, 1 } }, { { 0, 250 }, { 0, 0, 1, 1 } } }; int main() { oc_init(); oc_rect rect = { .x = 100, .y = 100, .w = 800, .h = 600 }; oc_window window = oc_window_create(rect, OC_STR8("test"), 0); //NOTE: create surface oc_surface surface = oc_surface_create_for_window(window, OC_METAL); //NOTE(martin): load the library id device = MTLCreateSystemDefaultDevice(); oc_arena_scope scratch = oc_scratch_begin(); oc_str8 shaderPath = oc_path_executable_relative(scratch.arena, OC_STR8("triangle_shader.metallib")); const char* shaderPathCString = oc_str8_to_cstring(scratch.arena, shaderPath); NSString* metalFileName = [[NSString alloc] initWithCString:shaderPathCString encoding:NSUTF8StringEncoding]; NSError* err = 0; id library = [device newLibraryWithFile:metalFileName error:&err]; if(err != nil) { const char* errStr = [[err localizedDescription] UTF8String]; printf("error : %s\n", errStr); return (-1); } id vertexFunction = [library newFunctionWithName:@"VertexShader"]; id fragmentFunction = [library newFunctionWithName:@"FragmentShader"]; //NOTE(martin): create a render pipeline MTLRenderPipelineDescriptor* pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init]; pipelineStateDescriptor.label = @"My simple pipeline"; pipelineStateDescriptor.vertexFunction = vertexFunction; pipelineStateDescriptor.fragmentFunction = fragmentFunction; CAMetalLayer* layer = oc_mtl_surface_layer(surface); pipelineStateDescriptor.colorAttachments[0].pixelFormat = layer.pixelFormat; id pipelineState = [device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&err]; if(err != nil) { const char* errStr = [[err localizedDescription] UTF8String]; printf("error : %s\n", errStr); return (-1); } oc_scratch_end(scrathc); // start app oc_window_bring_to_front(window); oc_window_focus(window); while(!oc_should_quit()) { scratch = oc_scratch_begin(); oc_pump_events(0); oc_event* event = 0; while((event = oc_next_event(scratch.arena)) != 0) { switch(event->type) { case OC_EVENT_WINDOW_CLOSE: { oc_request_quit(); } break; default: break; } } vector_uint2 viewportSize; viewportSize.x = 800; viewportSize.y = 600; oc_surface_select(surface); id drawable = oc_mtl_surface_drawable(surface); id commandBuffer = oc_mtl_surface_command_buffer(surface); MTLRenderPassDescriptor* renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor]; renderPassDescriptor.colorAttachments[0].texture = drawable.texture; renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore; id encoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; //Set the pipeline state [encoder setRenderPipelineState:pipelineState]; //Send data to the shader and add draw call [encoder setVertexBytes:triangle length:sizeof(triangle) atIndex:vertexInputIndexVertices]; [encoder setVertexBytes:&viewportSize length:sizeof(viewportSize) atIndex:vertexInputIndexViewportSize]; [encoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:3]; [encoder endEncoding]; oc_surface_present(surface); oc_scratch_end(scratch); } oc_terminate(); return (0); }