Skip to content

Instantly share code, notes, and snippets.

@gcatlin
Last active March 1, 2024 13:13
Show Gist options
  • Star 32 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save gcatlin/b89e0efed78dd91364609ca4095da346 to your computer and use it in GitHub Desktop.
Save gcatlin/b89e0efed78dd91364609ca4095da346 to your computer and use it in GitHub Desktop.
Minimal C SDL2 Metal example
//
// cc sdl-metal-example.m `sdl2-config --cflags --libs` -framework Metal -framework QuartzCore && ./a.out
//
#include <SDL.h>
#import <Metal/Metal.h>
#import <QuartzCore/CAMetalLayer.h>
int main (int argc, char *args[])
{
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "metal");
SDL_InitSubSystem(SDL_INIT_VIDEO);
SDL_Window *window = SDL_CreateWindow("SDL Metal", -1, -1, 640, 480, SDL_WINDOW_ALLOW_HIGHDPI);
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC);
const CAMetalLayer *swapchain = (__bridge CAMetalLayer *)SDL_RenderGetMetalLayer(renderer);
const id<MTLDevice> gpu = swapchain.device;
const id<MTLCommandQueue> queue = [gpu newCommandQueue];
MTLClearColor color = MTLClearColorMake(0, 0, 0, 1);
bool quit = false;
SDL_Event e;
while (!quit) {
while (SDL_PollEvent(&e) != 0) {
switch (e.type) {
case SDL_QUIT: quit = true; break;
}
}
@autoreleasepool {
id<CAMetalDrawable> surface = [swapchain nextDrawable];
color.red = (color.red > 1.0) ? 0 : color.red + 0.01;
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];
}
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
@sherjilozair
Copy link

This is interesting use of SDL renderer only to get the metal device. Do you know of a small example where the metal device is obtained without using SDL2? Thanks for putting this up, btw.

@gcatlin
Copy link
Author

gcatlin commented Aug 9, 2019

@Reak45
Copy link

Reak45 commented Jun 12, 2020

It does not work :( All I get is a black screen. Please help!

@adilPatel
Copy link

For Reak45, this code is meant to draw a black screen or very dark red. Change the clear colour in the main function to white or something bright, and remove the colour.red line in the render loop, then report your results.

@slightlybeige
Copy link

For anyone who is stuck here with a black screen, you need to remove the "SDL_DestroyRenderer(renderer);" on line 17. This function in SDL destroys the View as well as the renderer which will prevent you from having anything draw to the screen. SDL_RenderGetMetalLayer will flush all the buffers before returning so there shouldn't be any conflicts.
I haven't tested on iOS, I'm guessing perhaps the UIKit based renderer on iOS behaves differently hence the author adding the destroy?

@gcatlin
Copy link
Author

gcatlin commented Jul 14, 2020

@slightlybeige thanks for figuring out the issue. It used to work as is -- I'm not sure what changed, maybe something in SDL. I moved SDL_DestroyRenderer below the loop and updated the gist.

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