I discovered Sokol a few days ago, and notice that i dont need to use SDL/GLFW with it because of sokol_app, since it already does what i normally want with sdl (windows/mouse/input).
My question is, how far will I go with sokol alone without hitting some problem that will make me rethink the use of SDL ? Looks like i dont need SDL anymore (which is great, single dependency :P ), but i麓m missing something obvious here?
I don't know, I guess it depends on your use case :)
If you want to release a "professional" application, I would definitely go for SDL. If it's just a small tool for a limited audience, or mainly a WebAssembly app running in the browser, sokol_app.h should be "good enough".
For instance: SDL has a lot of compatibility fixes especially on Linux which sokol_app.h doesn't have.
OTH, using sokol_app.h can reduce binary size a lot vs SDL (about 1 MByte), which might be important with WebAssembly.
Switching from SDL/GLFW to sokol_app.h and back shouldn't affect too many lines of code, so switching back to SDL or GFLW later shouldn't be too much of a hassle if sokol_app.h wasn't the right choice (you can use the other sokol-headers with or without sokol_app.h, e.g. use sokol_gfx.h together with SDL or GLFW works, but this might limit the choice of 3D backend to OpenGL).
Using SDL with Metal is pretty simple. I haven't gotten around to setting up DX SDL yet but all the metal code you need to get going is here:
// in your normal init/setup code
SDL_SetHint(SDL_HINT_RENDER_DRIVER, 'metal')
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)
window_flags := SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI
state.window = SDL_CreateWindow("SDL2 + Metal + Sokol demo", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 512, 384, window_flags)
mu_create_metal_layerl(state.window)
sg_setup(&sg_desc {
mtl_device: mu_get_metal_device()
mtl_renderpass_descriptor_cb: mu_get_render_pass_descriptor
mtl_drawable_cb: mu_get_drawable
})
// metal_util.h
#if defined(SOKOL_METAL)
#import <Cocoa/Cocoa.h>
#import <Metal/Metal.h>
#import <MetalKit/MetalKit.h>
#import <SDL.h>
static void* _window;
static CAMetalLayer* _metal_layer;
static id<CAMetalDrawable> _drawable;
static MTLRenderPassDescriptor* _render_pass_descriptor;
static CGSize _fb_size;
#endif
#if defined(SOKOL_METAL)
CGSize _mu_calculate_drawable_size() {
static float content_scale = 0;
if (content_scale < 0)
content_scale = _metal_layer.contentsScale;
int width, height;
SDL_GetWindowSize(_window, &width, &height);
return CGSizeMake(width * _metal_layer.contentsScale, height * _metal_layer.contentsScale);
}
void mu_create_metal_layer(void* window) {
_window = window;
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC);
_metal_layer = (__bridge __typeof__ (CAMetalLayer*))SDL_RenderGetMetalLayer(renderer);
SDL_DestroyRenderer(renderer);
_metal_layer.framebufferOnly = YES;
}
const void* mu_get_metal_device() {
return (__bridge const void*)_metal_layer.device;
}
const void* mu_get_render_pass_descriptor() {
// todo: do we need to set the drawableSize? doesnt seem like we do...
//_metal_layer.drawableSize = _mu_calculate_drawable_size();
_drawable = [_metal_layer nextDrawable];
_fb_size = _metal_layer.drawableSize;
_render_pass_descriptor = NULL;
_render_pass_descriptor = [[MTLRenderPassDescriptor alloc] init];
_render_pass_descriptor.colorAttachments[0].texture = _drawable.texture;
return (__bridge const void*)_render_pass_descriptor;
}
const void* mu_get_drawable() {
return (__bridge const void*)_drawable;
}
float mu_dpi_scale() {
return _metal_layer.contentsScale;
}
float mu_width() {
return _fb_size.width;
}
float mu_height() {
return _fb_size.height;
}
void mu_set_framebuffer_only(bool framebuffer_only) {
_metal_layer.framebufferOnly = framebuffer_only;
}
void mu_set_drawable_size(int width, int height) {
_metal_layer.drawableSize = CGSizeMake(width, height);
}
void mu_set_display_sync_enabled(bool enabled) {
_metal_layer.displaySyncEnabled = enabled;
}
#else
void mu_create_metal_layer(void* window) {}
const void* mu_get_metal_device() { return 0; }
const void* mu_get_render_pass_descriptor() { return 0; }
const void* mu_get_drawable() { return 0; }
void mu_set_framebuffer_only(bool framebuffer_only) {}
void mu_set_drawable_size(int width, int height) {}
void mu_set_display_sync_enabled(bool enabled) {}
#endif
You can go pretty far with just sokol depending on your needs. SDL does have a more fully featured OOTB experience and provides things that sokol does not (at least not yet), for example (just to name a few):
It is possible to use SDL for the the sokol_app part, and use other sokol libs for other stuff, like using sokol_gfx with SDL. There is a basic example I found here for OpenGL (that should work in the browser too I think, but haven't tested it): https://gist.github.com/sherjilozair/c0fa81250c1b8f5e4234b1588e755bca?signup=true
NOTE: the example is just something I found, I take no credit for it.
As a side note, I started trying to work on some "glue library" to use sokol_gfx with SDL that will support not just OpenGL but also DX11 and Metal, by creating a window through SDL and extracting all necessary parameters/pointers for Metal/DX11 and pass those to sokol_gfx, but I have not gotten very far and kinda abandoned it, but the example above seems like part of what I had in mind.
To add to this, the next SDL release has a new method to create and fetch the CAMetalLayer directly. That will let you skip creating and destroying the Renderer like i did in the example code above.
Most helpful comment
Using SDL with Metal is pretty simple. I haven't gotten around to setting up DX SDL yet but all the metal code you need to get going is here: