diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 78642f73..5d850da3 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -41,6 +41,13 @@ add_executable(wave WIN32 MACOSX_BUNDLE wave.c ${ICON} ${GLAD}) target_link_libraries(particles "${CMAKE_THREAD_LIBS_INIT}" "${RT_LIBRARY}") +if (APPLE) + add_executable(metal MACOSX_BUNDLE metal.m ${ICON}) + target_compile_options(metal PUBLIC "-fobjc-arc") + target_link_libraries(metal "-framework Metal" "-framework QuartzCore") + set_target_properties(metal PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Metal") +endif() + set(WINDOWS_BINARIES boing gears heightmap particles simple splitview wave) set(CONSOLE_BINARIES offscreen) @@ -57,6 +64,7 @@ if (APPLE) set_target_properties(boing PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Boing") set_target_properties(gears PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Gears") set_target_properties(heightmap PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Heightmap") + set_target_properties(metal PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Metal") set_target_properties(particles PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Particles") set_target_properties(simple PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Simple") set_target_properties(splitview PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "SplitView") diff --git a/examples/metal.m b/examples/metal.m new file mode 100644 index 00000000..de66ed00 --- /dev/null +++ b/examples/metal.m @@ -0,0 +1,167 @@ +//======================================================================== +// 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] + +#import +#import +#import + +#define GLFW_INCLUDE_NONE +#import +#define GLFW_EXPOSE_NATIVE_COCOA +#define GLFW_EXPOSE_NATIVE_NSGL +#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); + } + + GLFWwindow* window; + + glfwSetErrorCallback(error_callback); + + if (!glfwInit()) + exit(EXIT_FAILURE); + + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + 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]