//======================================================================== // Simple GLFW+Metal example // Copyright (c) Camilla Berglund // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== //! [code] #define GLFW_INCLUDE_NONE #import #define GLFW_EXPOSE_NATIVE_COCOA #import #import #import #import #include #include #include static void error_callback(int error, const char* description) { fputs(description, stderr); } static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) { if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) glfwSetWindowShouldClose(window, GLFW_TRUE); } int main(void) { id device = MTLCreateSystemDefaultDevice(); if (!device) exit(EXIT_FAILURE); glfwSetErrorCallback(error_callback); if (!glfwInit()) exit(EXIT_FAILURE); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); GLFWwindow* window = glfwCreateWindow(640, 480, "Metal Example", NULL, NULL); if (!window) { glfwTerminate(); exit(EXIT_FAILURE); } NSWindow* nswin = glfwGetCocoaWindow(window); CAMetalLayer* layer = [CAMetalLayer layer]; layer.device = device; layer.pixelFormat = MTLPixelFormatBGRA8Unorm; nswin.contentView.layer = layer; nswin.contentView.wantsLayer = YES; MTLCompileOptions* compileOptions = [MTLCompileOptions new]; compileOptions.languageVersion = MTLLanguageVersion1_1; NSError* compileError; id lib = [device newLibraryWithSource: @"#include \n" "using namespace metal;\n" "vertex float4 v_simple(\n" " constant float4* in [[buffer(0)]],\n" " uint vid [[vertex_id]])\n" "{\n" " return in[vid];\n" "}\n" "fragment float4 f_simple(\n" " float4 in [[stage_in]])\n" "{\n" " return float4(1, 0, 0, 1);\n" "}\n" options:compileOptions error:&compileError]; if (!lib) { NSLog(@"can't create library: %@", compileError); glfwTerminate(); exit(EXIT_FAILURE); } id vs = [lib newFunctionWithName:@"v_simple"]; assert(vs); id fs = [lib newFunctionWithName:@"f_simple"]; assert(fs); id cq = [device newCommandQueue]; assert(cq); MTLRenderPipelineDescriptor* rpd = [MTLRenderPipelineDescriptor new]; rpd.vertexFunction = vs; rpd.fragmentFunction = fs; rpd.colorAttachments[0].pixelFormat = layer.pixelFormat; id rps = [device newRenderPipelineStateWithDescriptor:rpd error:NULL]; assert(rps); glfwSetKeyCallback(window, key_callback); while (!glfwWindowShouldClose(window)) { float ratio; int width, height; glfwGetFramebufferSize(window, &width, &height); ratio = width / (float) height; layer.drawableSize = CGSizeMake(width, height); id drawable = [layer nextDrawable]; assert(drawable); id cb = [cq commandBuffer]; MTLRenderPassDescriptor* rpd = [MTLRenderPassDescriptor new]; MTLRenderPassColorAttachmentDescriptor* cd = rpd.colorAttachments[0]; cd.texture = drawable.texture; cd.loadAction = MTLLoadActionClear; cd.clearColor = MTLClearColorMake(1.0, 1.0, 1.0, 1.0); cd.storeAction = MTLStoreActionStore; id rce = [cb renderCommandEncoderWithDescriptor:rpd]; [rce setRenderPipelineState:rps]; [rce setVertexBytes:(vector_float4[]){ { 0, 0, 0, 1 }, { -1, 1, 0, 1 }, { 1, 1, 0, 1 }, } length:3 * sizeof(vector_float4) atIndex:0]; [rce drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:3]; [rce endEncoding]; [cb presentDrawable:drawable]; [cb commit]; glfwPollEvents(); } glfwDestroyWindow(window); glfwTerminate(); exit(EXIT_SUCCESS); } //! [code]