Sokol: Q: How far I go without SDL/GLFW

Created on 17 Dec 2019  路  4Comments  路  Source: floooh/sokol

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?

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:

// 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

All 4 comments

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):

  • Listing and selecting audio devices and formats
  • File I/O including writing, and getting paths to certain important folders
  • More control over input events including Joystick and controllers + heptic, and more mouse related features

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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

pjako picture pjako  路  3Comments

datgame picture datgame  路  6Comments

amerkoleci picture amerkoleci  路  4Comments

sherjilozair picture sherjilozair  路  7Comments

serkonda7 picture serkonda7  路  4Comments