Skip to content

Instantly share code, notes, and snippets.

@gcatlin
Last active March 23, 2024 18:13
Show Gist options
  • Star 29 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save gcatlin/987be74e2d58da96093a7598f3fbfb27 to your computer and use it in GitHub Desktop.
Save gcatlin/987be74e2d58da96093a7598f3fbfb27 to your computer and use it in GitHub Desktop.
Minimal C GLFW Metal example
//
// cc glfw-metal-example.m `pkg-config --cflags --libs glfw3` -framework AppKit -framework Metal -framework QuartzCore
//
#define GLFW_INCLUDE_NONE
#define GLFW_EXPOSE_NATIVE_COCOA
#include <GLFW/glfw3.h>
#include <GLFW/glfw3native.h>
#import <Metal/Metal.h>
#import <QuartzCore/CAMetalLayer.h>
static void quit(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)
{
const id<MTLDevice> gpu = MTLCreateSystemDefaultDevice();
const id<MTLCommandQueue> queue = [gpu newCommandQueue];
CAMetalLayer *swapchain = [CAMetalLayer layer];
swapchain.device = gpu;
swapchain.opaque = YES;
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
GLFWwindow *window = glfwCreateWindow(640, 480, "GLFW Metal", NULL, NULL);
NSWindow *nswindow = glfwGetCocoaWindow(window);
nswindow.contentView.layer = swapchain;
nswindow.contentView.wantsLayer = YES;
glfwSetKeyCallback(window, quit);
MTLClearColor color = MTLClearColorMake(0, 0, 0, 1);
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
@autoreleasepool {
color.red = (color.red > 1.0) ? 0 : color.red + 0.01;
id<CAMetalDrawable> surface = [swapchain nextDrawable];
MTLRenderPassDescriptor *pass = [MTLRenderPassDescriptor renderPassDescriptor];
pass.colorAttachments[0].clearColor = color;
pass.colorAttachments[0].loadAction = MTLLoadActionClear;
pass.colorAttachments[0].storeAction = MTLStoreActionStore;
pass.colorAttachments[0].texture = surface.texture;
id<MTLCommandBuffer> buffer = [queue commandBuffer];
id<MTLRenderCommandEncoder> encoder = [buffer renderCommandEncoderWithDescriptor:pass];
[encoder endEncoding];
[buffer presentDrawable:surface];
[buffer commit];
}
}
glfwDestroyWindow(window);
glfwTerminate();
return 0;
}
@vmilea
Copy link

vmilea commented Jul 10, 2023

Thanks for sharing this! To handle window resizing, the CAMetalLayer.drawableSize should be updated so it matches glfwGetFramebufferSize() before fetching the nextDrawable.

@truedat101
Copy link

Not sure if pkg-config is generally available in OS X builds. From brew, lib and include needs to be specified.

cc glfw-metal-example.m -L/opt/homebrew/Cellar/glfw/3.3.9/lib -I/opt/homebrew/Cellar/glfw/3.3.9/include -lglfw -framework AppKit -framework Metal -framework QuartzCore

@louwers
Copy link

louwers commented Mar 23, 2024

If you don't want to use pkg-config, here is how I built it with Bazel: https://github.com/louwers/metal-glfw

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment