Update sokol

This commit is contained in:
John Alanbrook 2023-11-01 20:33:22 +00:00
parent 05a596746e
commit 71d5ae7619
9 changed files with 3757 additions and 2013 deletions

View file

@ -171,7 +171,7 @@ tools/libcdb.a:
mv $(CDB)/libcdb.a tools
DOCOS = Sound gameobject Game Window physics Profile Time Player Mouse IO timer Log ColorMap sprite
DOCOS = Sound gameobject Game Window physics Profile Time Player Mouse IO Log ColorMap sprite SpriteAnim
DOCHTML := $(addsuffix .api.html, $(DOCOS))
DOCMD := $(addsuffix .api.md, $(DOCOS))

View file

@ -122,8 +122,49 @@ sprite.inputs.kp2 = function() { this.pos = [-0.5,-1]; };
sprite.inputs.kp1 = function() { this.pos = [-1,-1]; };
Object.seal(sprite);
var aseframeset2anim = function(frameset, meta)
{
var SpriteAnim = {
gif(path) {
var anim = {};
anim.frames = [];
anim.path = path;
var frames = cmd(139,path);
var yslice = 1/frames;
for (var f = 0; f < frames; f++) {
var frame = {};
frame.rect = {
s0: 0,
s1: 1,
t0: yslice*f,
t1: yslice*(f+1)
};
frame.time = 0.05;
anim.frames.push(frame);
}
anim.loop = true;
var dim = cmd(64,path);
dim.y /= frames;
anim.dim = dim;
return anim;
},
strip(path, frames) {
var anim = {};
anim.frames = [];
anim.path = path;
var xslice = 1/frames;
for (var f = 0; f < frames; f++) {
var frame = {};
frame.rect = {s0:xslice*f, s1: xslice*(f+1), t0:0, t1:1};
frame.time = 0.05;
anim.frames.push(frame);
}
anim.dim = cmd(64,path);
anim.dim.x /= frames;
return anim;
},
aseprite(path) {
function aseframeset2anim(frameset, meta) {
var anim = {};
anim.frames = [];
anim.path = meta.image;
@ -146,18 +187,8 @@ var aseframeset2anim = function(frameset, meta)
anim.dim = [frameset[0].sourceSize.x, frameset[0].sourceSize.y];
anim.loop = true;
return anim;
}
};
var ase2anim = function(ase)
{
var json = IO.slurp(ase);
json = JSON.parse(json);
var frames = Array.isArray(json.frames) ? json.frames : Object.values(json.frames);
return aseframeset2anim(json.frames, json.meta);
}
var ase2anims = function(ase)
{
var json = IO.slurp(ase);
json = JSON.parse(json);
var anims = {};
@ -166,56 +197,20 @@ var ase2anims = function(ase)
anims[tag.name] = aseframeset2anim(frames.slice(tag.from, tag.to+1), json.meta);
return anims;
}
},
};
var gif2anim = function(gif)
{
var anim = {};
anim.frames = [];
anim.path = gif;
var frames = cmd(139,gif);
var yslice = 1/frames;
for (var f = 0; f < frames; f++) {
var frame = {};
frame.rect = {
s0: 0,
s1: 1,
t0: yslice*f,
t1: yslice*(f+1)
};
frame.time = 0.05;
anim.frames.push(frame);
}
anim.loop = true;
var dim = cmd(64,gif);
dim.y /= frames;
anim.dim = dim;
return anim;
}
var strip2anim = function(strip)
{
var anim = {};
anim.frames = [];
anim.path = strip;
var frames = 8;
var xslice = 1/frames;
for (var f = 0; f < frames; f++) {
var frame = {};
frame.rect = {s0:xslice*f, s1: slice*(f+1), t0:0, t1:1};
frame.time = 0.05;
anim.frames.push(frame);
}
anim.dim = cmd(64,strip);
anim.dim.x /= frames;
return anim;
}
SpriteAnim.doc = 'Functions to create Primum animations from varying sources.';
SpriteAnim.gif.doc = 'Convert a gif.';
SpriteAnim.strip.doc = 'Given a path and number of frames, converts a horizontal strip animation, where each cell is the same width.'
SpriteAnim.aseprite.doc = 'Given an aseprite json metadata, returns an object of animations defined in the aseprite file.';
/* Container to play sprites and anim2ds */
component.char2d = Object.copy(sprite, {
component.char2d = Object.copy(component, {
get enabled() { return cmd(114,this.id); },
set enabled(x) { cmd(20,this.id,x); },
set color(x) { cmd(96,this.id,x); },
get pos() { return cmd(111, this.id); },
set pos(x) { cmd(37,this.id,x); },
set layer(x) { cmd(60, this.id, x); },
@ -235,18 +230,15 @@ component.char2d = Object.copy(sprite, {
cmd(12,this.id,this.path,this.rect);
},
kill() { cmd(9,this.id); },
ur: {
},
make(go) {
Log.say('creating animation');
var char = Object.create(this);
char.gameobject = go;
Object.assign(char, make_sprite(go.body));
char.frame = 0;
char.timer = timer.make(char.advance.bind(char), 1);
char.timer.loop = true;
Object.hide(char, 'timer');
return char;
},
@ -308,6 +300,17 @@ component.char2d = Object.copy(sprite, {
},
});
component.char2d.impl = {
get enabled() { return cmd(114,this.id); },
set enabled(x) { cmd(20,this.id,x); },
set color(x) { cmd(96,this.id,x); },
get pos() { return cmd(111, this.id); },
set pos(x) { cmd(37,this.id,x); },
set layer(x) { cmd(60, this.id, x); },
get layer() { return this.gameobject.draw_layer; },
};
/* Returns points specifying this geometry, with ccw */
var Geometry = {
box(w, h) {

View file

@ -35,7 +35,7 @@ static FILE *sout;
void log_init()
{
consolelog = malloc(CONSOLE_BUF+1);
sout = fdopen(dup(stdout),"w");
sout = fdopen(dup(1),"w");
sout = stdout;
}

View file

@ -951,9 +951,11 @@
OPTIONAL: DON'T HIJACK main() (#define SOKOL_NO_ENTRY)
======================================================
NOTE: SOKOL_NO_ENTRY and sapp_run() is currently not supported on Android.
In its default configuration, sokol_app.h "hijacks" the platform's
standard main() function. This was done because different platforms
have different main functions which are not compatible with
have different entry point conventions which are not compatible with
C's main() (for instance WinMain on Windows has completely different
arguments). However, this "main hijacking" posed a problem for
usage scenarios like integrating sokol_app.h with other languages than
@ -965,12 +967,30 @@
- instead provide the standard main() function of the platform
- from the main function, call the function ```sapp_run()``` which
takes a pointer to an ```sapp_desc``` structure.
- ```sapp_run()``` takes over control and calls the provided init-, frame-,
shutdown- and event-callbacks just like in the default model, it
will only return when the application quits (or not at all on some
platforms, like emscripten)
- from here on```sapp_run()``` takes over control and calls the provided
init-, frame-, event- and cleanup-callbacks just like in the default model.
NOTE: SOKOL_NO_ENTRY is currently not supported on Android.
sapp_run() behaves differently across platforms:
- on some platforms, sapp_run() will return when the application quits
- on other platforms, sapp_run() will never return, even when the
application quits (the operating system is free to simply terminate
the application at any time)
- on Emscripten specifically, sapp_run() will return immediately while
the frame callback keeps being called
This different behaviour of sapp_run() essentially means that there shouldn't
be any code *after* sapp_run(), because that may either never be called, or in
case of Emscripten will be called at an unexpected time (at application start).
An application also should not depend on the cleanup-callback being called
when cross-platform compatibility is required.
Since sapp_run() returns immediately on Emscripten you shouldn't activate
the 'EXIT_RUNTIME' linker option (this is disabled by default when compiling
for the browser target), since the C/C++ exit runtime would be called immediately at
application start, causing any global objects to be destroyed and global
variables to be zeroed.
WINDOWS CONSOLE OUTPUT
======================
@ -1019,8 +1039,8 @@
return (sapp_desc){
// ...
.allocator = {
.alloc = my_alloc,
.free = my_free,
.alloc_fn = my_alloc,
.free_fn = my_free,
.user_data = ...,
}
};
@ -1473,12 +1493,12 @@ typedef struct sapp_icon_desc {
Used in sapp_desc to provide custom memory-alloc and -free functions
to sokol_app.h. If memory management should be overridden, both the
alloc and free function must be provided (e.g. it's not valid to
alloc_fn and free_fn function must be provided (e.g. it's not valid to
override one function but not the other).
*/
typedef struct sapp_allocator {
void* (*alloc)(size_t size, void* user_data);
void (*free)(void* ptr, void* user_data);
void* (*alloc_fn)(size_t size, void* user_data);
void (*free_fn)(void* ptr, void* user_data);
void* user_data;
} sapp_allocator;
@ -1573,6 +1593,18 @@ typedef struct sapp_allocator {
_SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONCREATE, "NativeActivity onCreate") \
_SAPP_LOGITEM_XMACRO(ANDROID_CREATE_THREAD_PIPE_FAILED, "failed to create thread pipe") \
_SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_CREATE_SUCCESS, "NativeActivity sucessfully created") \
_SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_CREATE_SURFACE_FAILED, "wgpu: failed to create surface for swapchain") \
_SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_CREATE_SWAPCHAIN_FAILED, "wgpu: failed to create swapchain object") \
_SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_CREATE_DEPTH_STENCIL_TEXTURE_FAILED, "wgpu: failed to create depth-stencil texture for swapchain") \
_SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_CREATE_DEPTH_STENCIL_VIEW_FAILED, "wgpu: failed to create view object for swapchain depth-stencil texture") \
_SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_CREATE_MSAA_TEXTURE_FAILED, "wgpu: failed to create msaa texture for swapchain") \
_SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_CREATE_MSAA_VIEW_FAILED, "wgpu: failed to create view object for swapchain msaa texture") \
_SAPP_LOGITEM_XMACRO(WGPU_REQUEST_DEVICE_STATUS_ERROR, "wgpu: requesting device failed with status 'error'") \
_SAPP_LOGITEM_XMACRO(WGPU_REQUEST_DEVICE_STATUS_UNKNOWN, "wgpu: requesting device failed with status 'unknown'") \
_SAPP_LOGITEM_XMACRO(WGPU_REQUEST_ADAPTER_STATUS_UNAVAILABLE, "wgpu: requesting adapter failed with 'unavailable'") \
_SAPP_LOGITEM_XMACRO(WGPU_REQUEST_ADAPTER_STATUS_ERROR, "wgpu: requesting adapter failed with status 'error'") \
_SAPP_LOGITEM_XMACRO(WGPU_REQUEST_ADAPTER_STATUS_UNKNOWN, "wgpu: requesting adapter failed with status 'unknown'") \
_SAPP_LOGITEM_XMACRO(WGPU_CREATE_INSTANCE_FAILED, "wgpu: failed to create instance") \
_SAPP_LOGITEM_XMACRO(IMAGE_DATA_SIZE_MISMATCH, "image data size mismatch (must be width*height*4 bytes)") \
_SAPP_LOGITEM_XMACRO(DROPPED_FILE_PATH_TOO_LONG, "dropped file path too long (sapp_desc.max_dropped_filed_path_length)") \
_SAPP_LOGITEM_XMACRO(CLIPBOARD_STRING_TOO_BIG, "clipboard string didn't fit into clipboard buffer") \
@ -1852,7 +1884,31 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); }
#include <stddef.h> // size_t
#include <math.h> // roundf
/* check if the config defines are alright */
// helper macros
#define _sapp_def(val, def) (((val) == 0) ? (def) : (val))
#define _sapp_absf(a) (((a)<0.0f)?-(a):(a))
#define _SAPP_MAX_TITLE_LENGTH (128)
#define _SAPP_FALLBACK_DEFAULT_WINDOW_WIDTH (640)
#define _SAPP_FALLBACK_DEFAULT_WINDOW_HEIGHT (480)
// NOTE: the pixel format values *must* be compatible with sg_pixel_format
#define _SAPP_PIXELFORMAT_RGBA8 (23)
#define _SAPP_PIXELFORMAT_BGRA8 (28)
#define _SAPP_PIXELFORMAT_DEPTH (42)
#define _SAPP_PIXELFORMAT_DEPTH_STENCIL (43)
#if defined(_SAPP_MACOS) || defined(_SAPP_IOS)
// this is ARC compatible
#if defined(__cplusplus)
#define _SAPP_CLEAR_ARC_STRUCT(type, item) { item = type(); }
#else
#define _SAPP_CLEAR_ARC_STRUCT(type, item) { item = (type) { 0 }; }
#endif
#else
#define _SAPP_CLEAR_ARC_STRUCT(type, item) { _sapp_clear(&item, sizeof(item)); }
#endif
// check if the config defines are alright
#if defined(__APPLE__)
// see https://clang.llvm.org/docs/LanguageExtensions.html#automatic-reference-counting
#if !defined(__cplusplus)
@ -2339,15 +2395,18 @@ typedef struct {
#if defined(SOKOL_WGPU)
typedef struct {
int state;
WGPUInstance instance;
WGPUAdapter adapter;
WGPUDevice device;
WGPUSwapChain swapchain;
WGPUTextureFormat render_format;
WGPUSurface surface;
WGPUSwapChain swapchain;
WGPUTexture msaa_tex;
WGPUTexture depth_stencil_tex;
WGPUTextureView swapchain_view;
WGPUTextureView msaa_view;
WGPUTexture depth_stencil_tex;
WGPUTextureView depth_stencil_view;
WGPUTextureView swapchain_view;
bool async_init_done;
} _sapp_wgpu_t;
#endif
@ -2357,9 +2416,6 @@ typedef struct {
bool wants_hide_keyboard;
bool mouse_lock_requested;
uint16_t mouse_buttons;
#if defined(SOKOL_WGPU)
_sapp_wgpu_t wgpu;
#endif
} _sapp_emsc_t;
#endif // _SAPP_EMSCRIPTEN
@ -2396,7 +2452,7 @@ typedef enum MONITOR_DPI_TYPE {
MDT_RAW_DPI = 2,
MDT_DEFAULT = MDT_EFFECTIVE_DPI
} MONITOR_DPI_TYPE;
#endif /*DPI_ENUMS_DECLARED*/
#endif // DPI_ENUMS_DECLARED
typedef struct {
bool aware;
@ -2683,30 +2739,6 @@ typedef struct {
#endif // _SAPP_LINUX
/* helper macros */
#define _sapp_def(val, def) (((val) == 0) ? (def) : (val))
#define _sapp_absf(a) (((a)<0.0f)?-(a):(a))
#define _SAPP_MAX_TITLE_LENGTH (128)
#define _SAPP_FALLBACK_DEFAULT_WINDOW_WIDTH (640)
#define _SAPP_FALLBACK_DEFAULT_WINDOW_HEIGHT (480)
/* NOTE: the pixel format values *must* be compatible with sg_pixel_format */
#define _SAPP_PIXELFORMAT_RGBA8 (23)
#define _SAPP_PIXELFORMAT_BGRA8 (28)
#define _SAPP_PIXELFORMAT_DEPTH (42)
#define _SAPP_PIXELFORMAT_DEPTH_STENCIL (43)
#if defined(_SAPP_MACOS) || defined(_SAPP_IOS)
// this is ARC compatible
#if defined(__cplusplus)
#define _SAPP_CLEAR_ARC_STRUCT(type, item) { item = type(); }
#else
#define _SAPP_CLEAR_ARC_STRUCT(type, item) { item = (type) { 0 }; }
#endif
#else
#define _SAPP_CLEAR_ARC_STRUCT(type, item) { _sapp_clear(&item, sizeof(item)); }
#endif
typedef struct {
bool enabled;
int buf_size;
@ -2764,6 +2796,9 @@ typedef struct {
_sapp_ios_t ios;
#elif defined(_SAPP_EMSCRIPTEN)
_sapp_emsc_t emsc;
#if defined(SOKOL_WGPU)
_sapp_wgpu_t wgpu;
#endif
#elif defined(_SAPP_WIN32)
_sapp_win32_t win32;
#if defined(SOKOL_D3D11)
@ -2782,8 +2817,8 @@ typedef struct {
#endif
#endif
char html5_canvas_selector[_SAPP_MAX_TITLE_LENGTH];
char window_title[_SAPP_MAX_TITLE_LENGTH]; /* UTF-8 */
wchar_t window_title_wide[_SAPP_MAX_TITLE_LENGTH]; /* UTF-32 or UCS-2 */
char window_title[_SAPP_MAX_TITLE_LENGTH]; // UTF-8
wchar_t window_title_wide[_SAPP_MAX_TITLE_LENGTH]; // UTF-32 or UCS-2 */
sapp_keycode keycodes[SAPP_MAX_KEYCODES];
} _sapp_t;
static _sapp_t _sapp;
@ -2842,10 +2877,9 @@ _SOKOL_PRIVATE void _sapp_clear(void* ptr, size_t size) {
_SOKOL_PRIVATE void* _sapp_malloc(size_t size) {
SOKOL_ASSERT(size > 0);
void* ptr;
if (_sapp.desc.allocator.alloc) {
ptr = _sapp.desc.allocator.alloc(size, _sapp.desc.allocator.user_data);
}
else {
if (_sapp.desc.allocator.alloc_fn) {
ptr = _sapp.desc.allocator.alloc_fn(size, _sapp.desc.allocator.user_data);
} else {
ptr = malloc(size);
}
if (0 == ptr) {
@ -2861,8 +2895,8 @@ _SOKOL_PRIVATE void* _sapp_malloc_clear(size_t size) {
}
_SOKOL_PRIVATE void _sapp_free(void* ptr) {
if (_sapp.desc.allocator.free) {
_sapp.desc.allocator.free(ptr, _sapp.desc.allocator.user_data);
if (_sapp.desc.allocator.free_fn) {
_sapp.desc.allocator.free_fn(ptr, _sapp.desc.allocator.user_data);
}
else {
free(ptr);
@ -2966,7 +3000,7 @@ _SOKOL_PRIVATE bool _sapp_strcpy(const char* src, char* dst, int max_len) {
}
_SOKOL_PRIVATE sapp_desc _sapp_desc_defaults(const sapp_desc* desc) {
SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free));
SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn));
sapp_desc res = *desc;
res.sample_count = _sapp_def(res.sample_count, 1);
res.swap_interval = _sapp_def(res.swap_interval, 1);
@ -3437,50 +3471,6 @@ _SOKOL_PRIVATE void _sapp_macos_run(const sapp_desc* desc) {
// set the application dock icon as early as possible, otherwise
// the dummy icon will be visible for a short time
sapp_set_icon(&_sapp.desc.icon);
NSMenu* menu_bar = [[NSMenu alloc] init];
NSMenuItem* app_menu_item = [[NSMenuItem alloc] init];
[menu_bar addItem:app_menu_item];
NSApp.mainMenu = menu_bar;
NSMenu* app_menu = [[NSMenu alloc] init];
NSString* window_title_as_nsstring = [NSString stringWithUTF8String:desc->window_title];
NSString* quit_title = [@"Quit " stringByAppendingString:window_title_as_nsstring];
NSMenuItem* quit_menu_item = [[NSMenuItem alloc]
initWithTitle:quit_title
action:@selector(terminate:)
keyEquivalent:@"q"];
NSString* hide_title = [@"Hide " stringByAppendingString:window_title_as_nsstring];
NSMenuItem* hide_menu_item = [[NSMenuItem alloc]
initWithTitle:hide_title
action:@selector(hide:)
keyEquivalent:@"h"];
NSMenuItem* hide_others_item = [[NSMenuItem alloc]
initWithTitle:@"Hide Others"
action:@selector(hideOtherApplications:)
keyEquivalent:@"h"];
[hide_others_item setKeyEquivalentModifierMask: NSEventModifierFlagOption];
NSMenuItem* show_all_item = [[NSMenuItem alloc]
initWithTitle:@"Show All"
action:@selector(unhideAllApplications:)
keyEquivalent:@""];
[app_menu addItem:hide_menu_item];
[app_menu addItem:hide_others_item];
[app_menu addItem:show_all_item];
[app_menu addItem:[NSMenuItem separatorItem]];
[app_menu addItem:quit_menu_item];
app_menu_item.submenu = app_menu;
_SAPP_OBJC_RELEASE(window_title_as_nsstring);
_SAPP_OBJC_RELEASE(app_menu);
_SAPP_OBJC_RELEASE(app_menu_item);
_SAPP_OBJC_RELEASE(menu_bar);
_sapp.macos.app_dlg = [[_sapp_macos_app_delegate alloc] init];
NSApp.delegate = _sapp.macos.app_dlg;
@ -5075,11 +5065,6 @@ _SOKOL_PRIVATE void _sapp_emsc_set_icon(const sapp_icon_desc* icon_desc, int num
sapp_js_set_favicon(img_desc->width, img_desc->height, (const uint8_t*) img_desc->pixels.ptr);
}
#if defined(SOKOL_WGPU)
_SOKOL_PRIVATE void _sapp_emsc_wgpu_surfaces_create(void);
_SOKOL_PRIVATE void _sapp_emsc_wgpu_surfaces_discard(void);
#endif
_SOKOL_PRIVATE uint32_t _sapp_emsc_mouse_button_mods(uint16_t buttons) {
uint32_t m = 0;
if (0 != (buttons & (1<<0))) { m |= SAPP_MODIFIER_LMB; }
@ -5118,6 +5103,10 @@ _SOKOL_PRIVATE uint32_t _sapp_emsc_touch_event_mods(const EmscriptenTouchEvent*
return m;
}
#if defined(SOKOL_WGPU)
_SOKOL_PRIVATE void _sapp_emsc_wgpu_size_changed(void);
#endif
_SOKOL_PRIVATE EM_BOOL _sapp_emsc_size_changed(int event_type, const EmscriptenUiEvent* ui_event, void* user_data) {
_SOKOL_UNUSED(event_type);
_SOKOL_UNUSED(user_data);
@ -5163,9 +5152,8 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_size_changed(int event_type, const EmscriptenU
SOKOL_ASSERT((_sapp.framebuffer_width > 0) && (_sapp.framebuffer_height > 0));
emscripten_set_canvas_element_size(_sapp.html5_canvas_selector, _sapp.framebuffer_width, _sapp.framebuffer_height);
#if defined(SOKOL_WGPU)
/* on WebGPU: recreate size-dependent rendering surfaces */
_sapp_emsc_wgpu_surfaces_discard();
_sapp_emsc_wgpu_surfaces_create();
// on WebGPU: recreate size-dependent rendering surfaces
_sapp_emsc_wgpu_size_changed();
#endif
if (_sapp_events_enabled()) {
_sapp_init_event(SAPP_EVENTTYPE_RESIZED);
@ -5437,11 +5425,10 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboard
_sapp.event.key_code = _sapp_emsc_translate_key(emsc_event->key);
}
/* Special hack for macOS: if the Super key is pressed, macOS doesn't
send keyUp events. As a workaround, to prevent keys from
"sticking", we'll send a keyup event following a keydown
when the SUPER key is pressed
*/
// Special hack for macOS: if the Super key is pressed, macOS doesn't
// send keyUp events. As a workaround, to prevent keys from
// "sticking", we'll send a keyup event following a keydown
// when the SUPER key is pressed
if ((type == SAPP_EVENTTYPE_KEY_DOWN) &&
(_sapp.event.key_code != SAPP_KEYCODE_LEFT_SUPER) &&
(_sapp.event.key_code != SAPP_KEYCODE_RIGHT_SUPER) &&
@ -5636,114 +5623,184 @@ _SOKOL_PRIVATE void _sapp_emsc_webgl_init(void) {
#endif
#if defined(SOKOL_WGPU)
#define _SAPP_EMSC_WGPU_STATE_INITIAL (0)
#define _SAPP_EMSC_WGPU_STATE_READY (1)
#define _SAPP_EMSC_WGPU_STATE_RUNNING (2)
#if defined(__cplusplus)
extern "C" {
#endif
/* called when the asynchronous WebGPU device + swapchain init code in JS has finished */
EMSCRIPTEN_KEEPALIVE void _sapp_emsc_wgpu_ready(int device_id, int swapchain_id, int swapchain_fmt) {
SOKOL_ASSERT(0 == _sapp.emsc.wgpu.device);
_sapp.emsc.wgpu.device = (WGPUDevice) device_id;
_sapp.emsc.wgpu.swapchain = (WGPUSwapChain) swapchain_id;
_sapp.emsc.wgpu.render_format = (WGPUTextureFormat) swapchain_fmt;
_sapp.emsc.wgpu.state = _SAPP_EMSC_WGPU_STATE_READY;
}
#if defined(__cplusplus)
} // extern "C"
#endif
_SOKOL_PRIVATE void _sapp_emsc_wgpu_create_swapchain(void) {
SOKOL_ASSERT(_sapp.wgpu.instance);
SOKOL_ASSERT(_sapp.wgpu.device);
SOKOL_ASSERT(0 == _sapp.wgpu.surface);
SOKOL_ASSERT(0 == _sapp.wgpu.swapchain);
SOKOL_ASSERT(0 == _sapp.wgpu.msaa_tex);
SOKOL_ASSERT(0 == _sapp.wgpu.msaa_view);
SOKOL_ASSERT(0 == _sapp.wgpu.depth_stencil_tex);
SOKOL_ASSERT(0 == _sapp.wgpu.depth_stencil_view);
SOKOL_ASSERT(0 == _sapp.wgpu.swapchain_view);
/* embedded JS function to handle all the asynchronous WebGPU setup */
EM_JS(void, sapp_js_wgpu_init, (), {
WebGPU.initManagers();
// FIXME: the extension activation must be more clever here
navigator.gpu.requestAdapter().then((adapter) => {
console.log("wgpu adapter extensions: " + adapter.extensions);
adapter.requestDevice({ extensions: ["textureCompressionBC"]}).then((device) => {
var gpuContext = document.getElementById("canvas").getContext("gpupresent");
console.log("wgpu device extensions: " + adapter.extensions);
gpuContext.getSwapChainPreferredFormat(device).then((fmt) => {
const swapChainDescriptor = { device: device, format: fmt };
const swapChain = gpuContext.configureSwapChain(swapChainDescriptor);
const deviceId = WebGPU.mgrDevice.create(device);
const swapChainId = WebGPU.mgrSwapChain.create(swapChain);
const fmtId = WebGPU.TextureFormat.findIndex(function(elm) { return elm==fmt; });
console.log("wgpu device: " + device);
console.log("wgpu swap chain: " + swapChain);
console.log("wgpu preferred format: " + fmt + " (" + fmtId + ")");
__sapp_emsc_wgpu_ready(deviceId, swapChainId, fmtId);
});
});
});
});
WGPUSurfaceDescriptorFromCanvasHTMLSelector canvas_desc;
_sapp_clear(&canvas_desc, sizeof(canvas_desc));
canvas_desc.chain.sType = WGPUSType_SurfaceDescriptorFromCanvasHTMLSelector;
canvas_desc.selector = _sapp.html5_canvas_selector;
WGPUSurfaceDescriptor surf_desc;
_sapp_clear(&surf_desc, sizeof(surf_desc));
surf_desc.nextInChain = &canvas_desc.chain;
_sapp.wgpu.surface = wgpuInstanceCreateSurface(_sapp.wgpu.instance, &surf_desc);
if (0 == _sapp.wgpu.surface) {
_SAPP_PANIC(WGPU_SWAPCHAIN_CREATE_SURFACE_FAILED);
}
_sapp.wgpu.render_format = wgpuSurfaceGetPreferredFormat(_sapp.wgpu.surface, _sapp.wgpu.adapter);
_SOKOL_PRIVATE void _sapp_emsc_wgpu_surfaces_create(void) {
SOKOL_ASSERT(_sapp.emsc.wgpu.device);
SOKOL_ASSERT(_sapp.emsc.wgpu.swapchain);
SOKOL_ASSERT(0 == _sapp.emsc.wgpu.depth_stencil_tex);
SOKOL_ASSERT(0 == _sapp.emsc.wgpu.depth_stencil_view);
SOKOL_ASSERT(0 == _sapp.emsc.wgpu.msaa_tex);
SOKOL_ASSERT(0 == _sapp.emsc.wgpu.msaa_view);
WGPUSwapChainDescriptor sc_desc;
_sapp_clear(&sc_desc, sizeof(sc_desc));
sc_desc.usage = WGPUTextureUsage_RenderAttachment;
sc_desc.format = _sapp.wgpu.render_format;
sc_desc.width = (uint32_t)_sapp.framebuffer_width;
sc_desc.height = (uint32_t)_sapp.framebuffer_height;
sc_desc.presentMode = WGPUPresentMode_Fifo;
_sapp.wgpu.swapchain = wgpuDeviceCreateSwapChain(_sapp.wgpu.device, _sapp.wgpu.surface, &sc_desc);
if (0 == _sapp.wgpu.swapchain) {
_SAPP_PANIC(WGPU_SWAPCHAIN_CREATE_SWAPCHAIN_FAILED);
}
WGPUTextureDescriptor ds_desc;
_sapp_clear(&ds_desc, sizeof(ds_desc));
ds_desc.usage = WGPUTextureUsage_OutputAttachment;
ds_desc.usage = WGPUTextureUsage_RenderAttachment;
ds_desc.dimension = WGPUTextureDimension_2D;
ds_desc.size.width = (uint32_t) _sapp.framebuffer_width;
ds_desc.size.height = (uint32_t) _sapp.framebuffer_height;
ds_desc.size.depth = 1;
ds_desc.arrayLayerCount = 1;
ds_desc.format = WGPUTextureFormat_Depth24PlusStencil8;
ds_desc.size.width = (uint32_t)_sapp.framebuffer_width;
ds_desc.size.height = (uint32_t)_sapp.framebuffer_height;
ds_desc.size.depthOrArrayLayers = 1;
ds_desc.format = WGPUTextureFormat_Depth32FloatStencil8;
ds_desc.mipLevelCount = 1;
ds_desc.sampleCount = _sapp.sample_count;
_sapp.emsc.wgpu.depth_stencil_tex = wgpuDeviceCreateTexture(_sapp.emsc.wgpu.device, &ds_desc);
_sapp.emsc.wgpu.depth_stencil_view = wgpuTextureCreateView(_sapp.emsc.wgpu.depth_stencil_tex, 0);
ds_desc.sampleCount = (uint32_t)_sapp.sample_count;
_sapp.wgpu.depth_stencil_tex = wgpuDeviceCreateTexture(_sapp.wgpu.device, &ds_desc);
if (0 == _sapp.wgpu.depth_stencil_tex) {
_SAPP_PANIC(WGPU_SWAPCHAIN_CREATE_DEPTH_STENCIL_TEXTURE_FAILED);
}
_sapp.wgpu.depth_stencil_view = wgpuTextureCreateView(_sapp.wgpu.depth_stencil_tex, 0);
if (0 == _sapp.wgpu.depth_stencil_view) {
_SAPP_PANIC(WGPU_SWAPCHAIN_CREATE_DEPTH_STENCIL_VIEW_FAILED);
}
if (_sapp.sample_count > 1) {
WGPUTextureDescriptor msaa_desc;
_sapp_clear(&msaa_desc, sizeof(msaa_desc));
msaa_desc.usage = WGPUTextureUsage_OutputAttachment;
msaa_desc.usage = WGPUTextureUsage_RenderAttachment;
msaa_desc.dimension = WGPUTextureDimension_2D;
msaa_desc.size.width = (uint32_t) _sapp.framebuffer_width;
msaa_desc.size.height = (uint32_t) _sapp.framebuffer_height;
msaa_desc.size.depth = 1;
msaa_desc.arrayLayerCount = 1;
msaa_desc.format = _sapp.emsc.wgpu.render_format;
msaa_desc.size.width = (uint32_t)_sapp.framebuffer_width;
msaa_desc.size.height = (uint32_t)_sapp.framebuffer_height;
msaa_desc.size.depthOrArrayLayers = 1;
msaa_desc.format = _sapp.wgpu.render_format;
msaa_desc.mipLevelCount = 1;
msaa_desc.sampleCount = _sapp.sample_count;
_sapp.emsc.wgpu.msaa_tex = wgpuDeviceCreateTexture(_sapp.emsc.wgpu.device, &msaa_desc);
_sapp.emsc.wgpu.msaa_view = wgpuTextureCreateView(_sapp.emsc.wgpu.msaa_tex, 0);
msaa_desc.sampleCount = (uint32_t)_sapp.sample_count;
_sapp.wgpu.msaa_tex = wgpuDeviceCreateTexture(_sapp.wgpu.device, &msaa_desc);
if (0 == _sapp.wgpu.msaa_tex) {
_SAPP_PANIC(WGPU_SWAPCHAIN_CREATE_MSAA_TEXTURE_FAILED);
}
_sapp.wgpu.msaa_view = wgpuTextureCreateView(_sapp.wgpu.msaa_tex, 0);
if (0 == _sapp.wgpu.msaa_view) {
_SAPP_PANIC(WGPU_SWAPCHAIN_CREATE_MSAA_VIEW_FAILED);
}
}
}
_SOKOL_PRIVATE void _sapp_emsc_wgpu_surfaces_discard(void) {
if (_sapp.emsc.wgpu.msaa_tex) {
wgpuTextureRelease(_sapp.emsc.wgpu.msaa_tex);
_sapp.emsc.wgpu.msaa_tex = 0;
_SOKOL_PRIVATE void _sapp_emsc_wgpu_discard_swapchain(void) {
if (_sapp.wgpu.msaa_view) {
wgpuTextureViewRelease(_sapp.wgpu.msaa_view);
_sapp.wgpu.msaa_view = 0;
}
if (_sapp.emsc.wgpu.msaa_view) {
wgpuTextureViewRelease(_sapp.emsc.wgpu.msaa_view);
_sapp.emsc.wgpu.msaa_view = 0;
if (_sapp.wgpu.msaa_tex) {
wgpuTextureRelease(_sapp.wgpu.msaa_tex);
_sapp.wgpu.msaa_tex = 0;
}
if (_sapp.emsc.wgpu.depth_stencil_tex) {
wgpuTextureRelease(_sapp.emsc.wgpu.depth_stencil_tex);
_sapp.emsc.wgpu.depth_stencil_tex = 0;
if (_sapp.wgpu.depth_stencil_view) {
wgpuTextureViewRelease(_sapp.wgpu.depth_stencil_view);
_sapp.wgpu.depth_stencil_view = 0;
}
if (_sapp.emsc.wgpu.depth_stencil_view) {
wgpuTextureViewRelease(_sapp.emsc.wgpu.depth_stencil_view);
_sapp.emsc.wgpu.depth_stencil_view = 0;
if (_sapp.wgpu.depth_stencil_tex) {
wgpuTextureRelease(_sapp.wgpu.depth_stencil_tex);
_sapp.wgpu.depth_stencil_tex = 0;
}
if (_sapp.wgpu.swapchain) {
wgpuSwapChainRelease(_sapp.wgpu.swapchain);
_sapp.wgpu.swapchain = 0;
}
if (_sapp.wgpu.surface) {
wgpuSurfaceRelease(_sapp.wgpu.surface);
_sapp.wgpu.surface = 0;
}
}
_SOKOL_PRIVATE void _sapp_emsc_wgpu_next_frame(void) {
if (_sapp.emsc.wgpu.swapchain_view) {
wgpuTextureViewRelease(_sapp.emsc.wgpu.swapchain_view);
}
_sapp.emsc.wgpu.swapchain_view = wgpuSwapChainGetCurrentTextureView(_sapp.emsc.wgpu.swapchain);
_SOKOL_PRIVATE void _sapp_emsc_wgpu_size_changed(void) {
_sapp_emsc_wgpu_discard_swapchain();
_sapp_emsc_wgpu_create_swapchain();
}
#endif
_SOKOL_PRIVATE void _sapp_emsc_wgpu_request_device_cb(WGPURequestDeviceStatus status, WGPUDevice device, const char* msg, void* userdata) {
_SOKOL_UNUSED(msg);
_SOKOL_UNUSED(userdata);
SOKOL_ASSERT(!_sapp.wgpu.async_init_done);
if (status != WGPURequestDeviceStatus_Success) {
if (status == WGPURequestDeviceStatus_Error) {
_SAPP_PANIC(WGPU_REQUEST_DEVICE_STATUS_ERROR);
} else {
_SAPP_PANIC(WGPU_REQUEST_DEVICE_STATUS_UNKNOWN);
}
}
SOKOL_ASSERT(device);
_sapp.wgpu.device = device;
_sapp_emsc_wgpu_create_swapchain();
_sapp.wgpu.async_init_done = true;
}
_SOKOL_PRIVATE void _sapp_emsc_wgpu_request_adapter_cb(WGPURequestAdapterStatus status, WGPUAdapter adapter, const char* msg, void* userdata) {
_SOKOL_UNUSED(msg);
_SOKOL_UNUSED(userdata);
if (status != WGPURequestAdapterStatus_Success) {
switch (status) {
case WGPURequestAdapterStatus_Unavailable: _SAPP_PANIC(WGPU_REQUEST_ADAPTER_STATUS_UNAVAILABLE); break;
case WGPURequestAdapterStatus_Error: _SAPP_PANIC(WGPU_REQUEST_ADAPTER_STATUS_ERROR); break;
default: _SAPP_PANIC(WGPU_REQUEST_ADAPTER_STATUS_UNKNOWN); break;
}
}
SOKOL_ASSERT(adapter);
_sapp.wgpu.adapter = adapter;
size_t cur_feature_index = 1;
WGPUFeatureName requiredFeatures[8] = {
WGPUFeatureName_Depth32FloatStencil8,
};
// check for optional features we're interested in
// FIXME: ASTC texture compression
if (wgpuAdapterHasFeature(adapter, WGPUFeatureName_TextureCompressionBC)) {
requiredFeatures[cur_feature_index++] = WGPUFeatureName_TextureCompressionBC;
} else if (wgpuAdapterHasFeature(adapter, WGPUFeatureName_TextureCompressionETC2)) {
requiredFeatures[cur_feature_index++] = WGPUFeatureName_TextureCompressionETC2;
}
WGPUDeviceDescriptor dev_desc;
_sapp_clear(&dev_desc, sizeof(dev_desc));
dev_desc.requiredFeaturesCount = cur_feature_index;
dev_desc.requiredFeatures = requiredFeatures,
wgpuAdapterRequestDevice(adapter, &dev_desc, _sapp_emsc_wgpu_request_device_cb, 0);
}
_SOKOL_PRIVATE void _sapp_emsc_wgpu_init(void) {
SOKOL_ASSERT(0 == _sapp.wgpu.instance);
SOKOL_ASSERT(!_sapp.wgpu.async_init_done);
_sapp.wgpu.instance = wgpuCreateInstance(0);
if (0 == _sapp.wgpu.instance) {
_SAPP_PANIC(WGPU_CREATE_INSTANCE_FAILED);
}
// FIXME: power preference?
wgpuInstanceRequestAdapter(_sapp.wgpu.instance, 0, _sapp_emsc_wgpu_request_adapter_cb, 0);
}
_SOKOL_PRIVATE void _sapp_emsc_wgpu_frame(void) {
if (_sapp.wgpu.async_init_done) {
_sapp.wgpu.swapchain_view = wgpuSwapChainGetCurrentTextureView(_sapp.wgpu.swapchain);
_sapp_frame();
wgpuTextureViewRelease(_sapp.wgpu.swapchain_view);
_sapp.wgpu.swapchain_view = 0;
}
}
#endif // SOKOL_WGPU
_SOKOL_PRIVATE void _sapp_emsc_register_eventhandlers(void) {
emscripten_set_mousedown_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb);
@ -5812,32 +5869,12 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_frame(double time, void* userData) {
_sapp_timing_external(&_sapp.timing, time / 1000.0);
#if defined(SOKOL_WGPU)
/*
on WebGPU, the emscripten frame callback will already be called while
the asynchronous WebGPU device and swapchain initialization is still
in progress
*/
switch (_sapp.emsc.wgpu.state) {
case _SAPP_EMSC_WGPU_STATE_INITIAL:
/* async JS init hasn't finished yet */
break;
case _SAPP_EMSC_WGPU_STATE_READY:
/* perform post-async init stuff */
_sapp_emsc_wgpu_surfaces_create();
_sapp.emsc.wgpu.state = _SAPP_EMSC_WGPU_STATE_RUNNING;
break;
case _SAPP_EMSC_WGPU_STATE_RUNNING:
/* a regular frame */
_sapp_emsc_wgpu_next_frame();
_sapp_frame();
break;
}
_sapp_emsc_wgpu_frame();
#else
/* WebGL code path */
_sapp_frame();
#endif
/* quit-handling */
// quit-handling
if (_sapp.quit_requested) {
_sapp_init_event(SAPP_EVENTTYPE_QUIT_REQUESTED);
_sapp_call_event(&_sapp.event);
@ -5877,18 +5914,17 @@ _SOKOL_PRIVATE void _sapp_emsc_run(const sapp_desc* desc) {
#if defined(SOKOL_GLES3)
_sapp_emsc_webgl_init();
#elif defined(SOKOL_WGPU)
sapp_js_wgpu_init();
_sapp_emsc_wgpu_init();
#endif
_sapp.valid = true;
_sapp_emsc_register_eventhandlers();
sapp_set_icon(&desc->icon);
/* start the frame loop */
// start the frame loop
emscripten_request_animation_frame_loop(_sapp_emsc_frame, 0);
/* NOT A BUG: do not call _sapp_discard_state() here, instead this is
called in _sapp_emsc_frame() when the application is ordered to quit
*/
// NOT A BUG: do not call _sapp_discard_state() here, instead this is
// called in _sapp_emsc_frame() when the application is ordered to quit
}
#if !defined(SOKOL_NO_ENTRY)
@ -5932,6 +5968,99 @@ _SOKOL_PRIVATE void _sapp_gl_init_fbconfig(_sapp_gl_fbconfig* fbconfig) {
fbconfig->samples = -1;
}
typedef struct {
int least_missing;
int least_color_diff;
int least_extra_diff;
bool best_match;
} _sapp_gl_fbselect;
_SOKOL_PRIVATE void _sapp_gl_init_fbselect(_sapp_gl_fbselect* fbselect) {
_sapp_clear(fbselect, sizeof(_sapp_gl_fbselect));
fbselect->least_missing = 1000000;
fbselect->least_color_diff = 10000000;
fbselect->least_extra_diff = 10000000;
fbselect->best_match = false;
}
// NOTE: this is used only in the WGL code path
_SOKOL_PRIVATE bool _sapp_gl_select_fbconfig(_sapp_gl_fbselect* fbselect, const _sapp_gl_fbconfig* desired, const _sapp_gl_fbconfig* current) {
int missing = 0;
if (desired->doublebuffer != current->doublebuffer) {
return false;
}
if ((desired->alpha_bits > 0) && (current->alpha_bits == 0)) {
missing++;
}
if ((desired->depth_bits > 0) && (current->depth_bits == 0)) {
missing++;
}
if ((desired->stencil_bits > 0) && (current->stencil_bits == 0)) {
missing++;
}
if ((desired->samples > 0) && (current->samples == 0)) {
/* Technically, several multisampling buffers could be
involved, but that's a lower level implementation detail and
not important to us here, so we count them as one
*/
missing++;
}
/* These polynomials make many small channel size differences matter
less than one large channel size difference
Calculate color channel size difference value
*/
int color_diff = 0;
if (desired->red_bits != -1) {
color_diff += (desired->red_bits - current->red_bits) * (desired->red_bits - current->red_bits);
}
if (desired->green_bits != -1) {
color_diff += (desired->green_bits - current->green_bits) * (desired->green_bits - current->green_bits);
}
if (desired->blue_bits != -1) {
color_diff += (desired->blue_bits - current->blue_bits) * (desired->blue_bits - current->blue_bits);
}
/* Calculate non-color channel size difference value */
int extra_diff = 0;
if (desired->alpha_bits != -1) {
extra_diff += (desired->alpha_bits - current->alpha_bits) * (desired->alpha_bits - current->alpha_bits);
}
if (desired->depth_bits != -1) {
extra_diff += (desired->depth_bits - current->depth_bits) * (desired->depth_bits - current->depth_bits);
}
if (desired->stencil_bits != -1) {
extra_diff += (desired->stencil_bits - current->stencil_bits) * (desired->stencil_bits - current->stencil_bits);
}
if (desired->samples != -1) {
extra_diff += (desired->samples - current->samples) * (desired->samples - current->samples);
}
/* Figure out if the current one is better than the best one found so far
Least number of missing buffers is the most important heuristic,
then color buffer size match and lastly size match for other buffers
*/
bool new_closest = false;
if (missing < fbselect->least_missing) {
new_closest = true;
} else if (missing == fbselect->least_missing) {
if ((color_diff < fbselect->least_color_diff) ||
((color_diff == fbselect->least_color_diff) && (extra_diff < fbselect->least_extra_diff)))
{
new_closest = true;
}
}
if (new_closest) {
fbselect->least_missing = missing;
fbselect->least_color_diff = color_diff;
fbselect->least_extra_diff = extra_diff;
fbselect->best_match = (missing | color_diff | extra_diff) == 0;
}
return new_closest;
}
// NOTE: this is used only in the GLX code path
_SOKOL_PRIVATE const _sapp_gl_fbconfig* _sapp_gl_choose_fbconfig(const _sapp_gl_fbconfig* desired, const _sapp_gl_fbconfig* alternatives, int count) {
int missing, least_missing = 1000000;
int color_diff, least_color_diff = 10000000;
@ -6609,44 +6738,55 @@ _SOKOL_PRIVATE int _sapp_wgl_attrib(int pixel_format, int attrib) {
return value;
}
_SOKOL_PRIVATE void _sapp_wgl_attribiv(int pixel_format, int num_attribs, const int* attribs, int* results) {
SOKOL_ASSERT(_sapp.wgl.arb_pixel_format);
if (!_sapp.wgl.GetPixelFormatAttribivARB(_sapp.win32.dc, pixel_format, 0, num_attribs, attribs, results)) {
_SAPP_PANIC(WIN32_GET_PIXELFORMAT_ATTRIB_FAILED);
}
}
_SOKOL_PRIVATE int _sapp_wgl_find_pixel_format(void) {
SOKOL_ASSERT(_sapp.win32.dc);
SOKOL_ASSERT(_sapp.wgl.arb_pixel_format);
const _sapp_gl_fbconfig* closest;
#define _sapp_wgl_num_query_tags (12)
const int query_tags[_sapp_wgl_num_query_tags] = {
WGL_SUPPORT_OPENGL_ARB,
WGL_DRAW_TO_WINDOW_ARB,
WGL_PIXEL_TYPE_ARB,
WGL_ACCELERATION_ARB,
WGL_DOUBLE_BUFFER_ARB,
WGL_RED_BITS_ARB,
WGL_GREEN_BITS_ARB,
WGL_BLUE_BITS_ARB,
WGL_ALPHA_BITS_ARB,
WGL_DEPTH_BITS_ARB,
WGL_STENCIL_BITS_ARB,
WGL_SAMPLES_ARB,
};
const int result_support_opengl_index = 0;
const int result_draw_to_window_index = 1;
const int result_pixel_type_index = 2;
const int result_acceleration_index = 3;
const int result_double_buffer_index = 4;
const int result_red_bits_index = 5;
const int result_green_bits_index = 6;
const int result_blue_bits_index = 7;
const int result_alpha_bits_index = 8;
const int result_depth_bits_index = 9;
const int result_stencil_bits_index = 10;
const int result_samples_index = 11;
int query_results[_sapp_wgl_num_query_tags] = {0};
// Drop the last item if multisample extension is not supported.
// If in future querying with multiple extensions, will have to shuffle index values to have active extensions on the end.
int query_count = _sapp_wgl_num_query_tags;
if (!_sapp.wgl.arb_multisample) {
query_count = _sapp_wgl_num_query_tags - 1;
}
int native_count = _sapp_wgl_attrib(1, WGL_NUMBER_PIXEL_FORMATS_ARB);
_sapp_gl_fbconfig* usable_configs = (_sapp_gl_fbconfig*) _sapp_malloc_clear((size_t)native_count * sizeof(_sapp_gl_fbconfig));
SOKOL_ASSERT(usable_configs);
int usable_count = 0;
for (int i = 0; i < native_count; i++) {
const int n = i + 1;
_sapp_gl_fbconfig* u = usable_configs + usable_count;
_sapp_gl_init_fbconfig(u);
if (!_sapp_wgl_attrib(n, WGL_SUPPORT_OPENGL_ARB) || !_sapp_wgl_attrib(n, WGL_DRAW_TO_WINDOW_ARB)) {
continue;
}
if (_sapp_wgl_attrib(n, WGL_PIXEL_TYPE_ARB) != WGL_TYPE_RGBA_ARB) {
continue;
}
if (_sapp_wgl_attrib(n, WGL_ACCELERATION_ARB) == WGL_NO_ACCELERATION_ARB) {
continue;
}
u->red_bits = _sapp_wgl_attrib(n, WGL_RED_BITS_ARB);
u->green_bits = _sapp_wgl_attrib(n, WGL_GREEN_BITS_ARB);
u->blue_bits = _sapp_wgl_attrib(n, WGL_BLUE_BITS_ARB);
u->alpha_bits = _sapp_wgl_attrib(n, WGL_ALPHA_BITS_ARB);
u->depth_bits = _sapp_wgl_attrib(n, WGL_DEPTH_BITS_ARB);
u->stencil_bits = _sapp_wgl_attrib(n, WGL_STENCIL_BITS_ARB);
if (_sapp_wgl_attrib(n, WGL_DOUBLE_BUFFER_ARB)) {
u->doublebuffer = true;
}
if (_sapp.wgl.arb_multisample) {
u->samples = _sapp_wgl_attrib(n, WGL_SAMPLES_ARB);
}
u->handle = (uintptr_t)n;
usable_count++;
}
SOKOL_ASSERT(usable_count > 0);
_sapp_gl_fbconfig desired;
_sapp_gl_init_fbconfig(&desired);
desired.red_bits = 8;
@ -6656,13 +6796,46 @@ _SOKOL_PRIVATE int _sapp_wgl_find_pixel_format(void) {
desired.depth_bits = 24;
desired.stencil_bits = 8;
desired.doublebuffer = true;
desired.samples = _sapp.sample_count > 1 ? _sapp.sample_count : 0;
closest = _sapp_gl_choose_fbconfig(&desired, usable_configs, usable_count);
desired.samples = (_sapp.sample_count > 1) ? _sapp.sample_count : 0;
int pixel_format = 0;
if (closest) {
pixel_format = (int) closest->handle;
_sapp_gl_fbselect fbselect;
_sapp_gl_init_fbselect(&fbselect);
for (int i = 0; i < native_count; i++) {
const int n = i + 1;
_sapp_wgl_attribiv(n, query_count, query_tags, query_results);
if (query_results[result_support_opengl_index] == 0
|| query_results[result_draw_to_window_index] == 0
|| query_results[result_pixel_type_index] != WGL_TYPE_RGBA_ARB
|| query_results[result_acceleration_index] == WGL_NO_ACCELERATION_ARB)
{
continue;
}
_sapp_free(usable_configs);
_sapp_gl_fbconfig u;
_sapp_clear(&u, sizeof(u));
u.red_bits = query_results[result_red_bits_index];
u.green_bits = query_results[result_green_bits_index];
u.blue_bits = query_results[result_blue_bits_index];
u.alpha_bits = query_results[result_alpha_bits_index];
u.depth_bits = query_results[result_depth_bits_index];
u.stencil_bits = query_results[result_stencil_bits_index];
u.doublebuffer = 0 != query_results[result_double_buffer_index];
u.samples = query_results[result_samples_index]; // NOTE: If arb_multisample is not supported - just takes the default 0
// Test if this pixel format is better than the previous one
if (_sapp_gl_select_fbconfig(&fbselect, &desired, &u)) {
pixel_format = (uintptr_t)n;
// Early exit if matching as good as possible
if (fbselect.best_match) {
break;
}
}
}
return pixel_format;
}
@ -11074,7 +11247,7 @@ SOKOL_API_IMPL float sapp_heightf(void) {
SOKOL_API_IMPL int sapp_color_format(void) {
#if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU)
switch (_sapp.emsc.wgpu.render_format) {
switch (_sapp.wgpu.render_format) {
case WGPUTextureFormat_RGBA8Unorm:
return _SAPP_PIXELFORMAT_RGBA8;
case WGPUTextureFormat_BGRA8Unorm:
@ -11502,7 +11675,7 @@ SOKOL_API_IMPL const void* sapp_win32_get_hwnd(void) {
SOKOL_API_IMPL const void* sapp_wgpu_get_device(void) {
SOKOL_ASSERT(_sapp.valid);
#if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU)
return (const void*) _sapp.emsc.wgpu.device;
return (const void*) _sapp.wgpu.device;
#else
return 0;
#endif
@ -11512,10 +11685,10 @@ SOKOL_API_IMPL const void* sapp_wgpu_get_render_view(void) {
SOKOL_ASSERT(_sapp.valid);
#if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU)
if (_sapp.sample_count > 1) {
return (const void*) _sapp.emsc.wgpu.msaa_view;
return (const void*) _sapp.wgpu.msaa_view;
}
else {
return (const void*) _sapp.emsc.wgpu.swapchain_view;
return (const void*) _sapp.wgpu.swapchain_view;
}
#else
return 0;
@ -11526,7 +11699,7 @@ SOKOL_API_IMPL const void* sapp_wgpu_get_resolve_view(void) {
SOKOL_ASSERT(_sapp.valid);
#if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU)
if (_sapp.sample_count > 1) {
return (const void*) _sapp.emsc.wgpu.swapchain_view;
return (const void*) _sapp.wgpu.swapchain_view;
}
else {
return 0;
@ -11539,7 +11712,7 @@ SOKOL_API_IMPL const void* sapp_wgpu_get_resolve_view(void) {
SOKOL_API_IMPL const void* sapp_wgpu_get_depth_stencil_view(void) {
SOKOL_ASSERT(_sapp.valid);
#if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU)
return (const void*) _sapp.emsc.wgpu.depth_stencil_view;
return (const void*) _sapp.wgpu.depth_stencil_view;
#else
return 0;
#endif

View file

@ -41,6 +41,17 @@
kc85 type=kc85_3 mod=m022 snapshot=kc85/jungle.kcc
You can also use standalone keys without value:
https://floooh.github.io/tiny8bit/kc85.html?bla&blub
On the command line:
kc85 bla blub
Such value-less keys are reported as the value being an empty string, but they
can be tested with `sapp_exists("bla")` or `sapp_boolean("blub")`.
ARGUMENT FORMATTING
===================
On the web platform, arguments must be formatted as a valid URL query string
@ -57,6 +68,12 @@
key=value
or
key
When a key has no value, the value will be assigned an empty string.
Key/value pairs are separated by 'whitespace', valid whitespace
characters are space and tab.
@ -71,9 +88,6 @@
The 'key' string must be a simple string without escape sequences or whitespace.
Currently 'single keys' without values are not allowed, but may be
in the future.
The 'value' string can be quoted, and quoted value strings can contain
whitespace:
@ -123,7 +137,7 @@
...
}
// check if a key's value is "true", "yes" or "on"
// check if a key's value is "true", "yes" or "on" or if this is a standalone key
if (sargs_boolean("joystick_enabled")) {
...
}
@ -183,23 +197,23 @@
Return true between sargs_setup() and sargs_shutdown()
bool sargs_exists(const char* key)
Test if a key arg exists.
Test if an argument exists by its key name.
const char* sargs_value(const char* key)
Return value associated with key. Returns an empty
string ("") if the key doesn't exist.
Return value associated with key. Returns an empty string ("") if the
key doesn't exist, or if the key doesn't have a value.
const char* sargs_value_def(const char* key, const char* default)
Return value associated with key, or the provided default
value if the value doesn't exist.
Return value associated with key, or the provided default value if the
key doesn't exist, or this is a value-less key.
bool sargs_equals(const char* key, const char* val);
Return true if the value associated with key matches
the 'val' argument.
bool sargs_boolean(const char* key)
Return true if the value string of 'key' is one
of 'true', 'yes', 'on'.
Return true if the value string of 'key' is one of 'true', 'yes', 'on',
or this is a key without value.
int sargs_find(const char* key)
Find argument by key name and return its index, or -1 if not found.
@ -213,7 +227,7 @@
const char* sargs_value_at(int index)
Return the value of argument at index. Returns empty string
if index is outside range.
if the key at index has no value, or the index is out-of-range.
MEMORY ALLOCATION OVERRIDE
@ -233,8 +247,8 @@
sargs_setup(&(sargs_desc){
// ...
.allocator = {
.alloc = my_alloc,
.free = my_free,
.alloc_fn = my_alloc,
.free_fn = my_free,
.user_data = ...,
}
});
@ -302,12 +316,12 @@ extern "C" {
Used in sargs_desc to provide custom memory-alloc and -free functions
to sokol_args.h. If memory management should be overridden, both the
alloc and free function must be provided (e.g. it's not valid to
alloc_fn and free_fn function must be provided (e.g. it's not valid to
override one function but not the other).
*/
typedef struct sargs_allocator {
void* (*alloc)(size_t size, void* user_data);
void (*free)(void* ptr, void* user_data);
void* (*alloc_fn)(size_t size, void* user_data);
void (*free_fn)(void* ptr, void* user_data);
void* user_data;
} sargs_allocator;
@ -327,13 +341,13 @@ SOKOL_ARGS_API_DECL void sargs_shutdown(void);
SOKOL_ARGS_API_DECL bool sargs_isvalid(void);
/* test if an argument exists by key name */
SOKOL_ARGS_API_DECL bool sargs_exists(const char* key);
/* get value by key name, return empty string if key doesn't exist */
/* get value by key name, return empty string if key doesn't exist or an existing key has no value */
SOKOL_ARGS_API_DECL const char* sargs_value(const char* key);
/* get value by key name, return provided default if key doesn't exist */
/* get value by key name, return provided default if key doesn't exist or has no value */
SOKOL_ARGS_API_DECL const char* sargs_value_def(const char* key, const char* def);
/* return true if val arg matches the value associated with key */
SOKOL_ARGS_API_DECL bool sargs_equals(const char* key, const char* val);
/* return true if key's value is "true", "yes" or "on" */
/* return true if key's value is "true", "yes", "on" or an existing key has no value */
SOKOL_ARGS_API_DECL bool sargs_boolean(const char* key);
/* get index of arg by key name, return -1 if not exists */
SOKOL_ARGS_API_DECL int sargs_find(const char* key);
@ -433,10 +447,9 @@ _SOKOL_PRIVATE void _sargs_clear(void* ptr, size_t size) {
_SOKOL_PRIVATE void* _sargs_malloc(size_t size) {
SOKOL_ASSERT(size > 0);
void* ptr;
if (_sargs.allocator.alloc) {
ptr = _sargs.allocator.alloc(size, _sargs.allocator.user_data);
}
else {
if (_sargs.allocator.alloc_fn) {
ptr = _sargs.allocator.alloc_fn(size, _sargs.allocator.user_data);
} else {
ptr = malloc(size);
}
SOKOL_ASSERT(ptr);
@ -450,10 +463,9 @@ _SOKOL_PRIVATE void* _sargs_malloc_clear(size_t size) {
}
_SOKOL_PRIVATE void _sargs_free(void* ptr) {
if (_sargs.allocator.free) {
_sargs.allocator.free(ptr, _sargs.allocator.user_data);
}
else {
if (_sargs.allocator.free_fn) {
_sargs.allocator.free_fn(ptr, _sargs.allocator.user_data);
} else {
free(ptr);
}
}
@ -486,8 +498,8 @@ _SOKOL_PRIVATE bool _sargs_val_expected(void) {
return 0 != (_sargs.parse_state & _SARGS_EXPECT_VAL);
}
_SOKOL_PRIVATE void _sargs_expect_sep(void) {
_sargs.parse_state = _SARGS_EXPECT_SEP;
_SOKOL_PRIVATE void _sargs_expect_sep_or_key(void) {
_sargs.parse_state = _SARGS_EXPECT_SEP | _SARGS_EXPECT_KEY;
}
_SOKOL_PRIVATE bool _sargs_any_expected(void) {
@ -524,14 +536,17 @@ _SOKOL_PRIVATE bool _sargs_is_whitespace(char c) {
}
_SOKOL_PRIVATE void _sargs_start_key(void) {
SOKOL_ASSERT(_sargs.num_args < _sargs.max_args);
SOKOL_ASSERT((_sargs.num_args >= 0) && (_sargs.num_args < _sargs.max_args));
_sargs.parse_state = _SARGS_PARSING_KEY;
_sargs.args[_sargs.num_args].key = _sargs.buf_pos;
}
_SOKOL_PRIVATE void _sargs_end_key(void) {
SOKOL_ASSERT(_sargs.num_args < _sargs.max_args);
SOKOL_ASSERT((_sargs.num_args >= 0) && (_sargs.num_args < _sargs.max_args));
_sargs_putc(0);
// declare val as empty string in case this is a key-only arg
_sargs.args[_sargs.num_args].val = _sargs.buf_pos - 1;
_sargs.num_args++;
_sargs.parse_state = 0;
}
@ -540,15 +555,13 @@ _SOKOL_PRIVATE bool _sargs_parsing_key(void) {
}
_SOKOL_PRIVATE void _sargs_start_val(void) {
SOKOL_ASSERT(_sargs.num_args < _sargs.max_args);
SOKOL_ASSERT((_sargs.num_args > 0) && (_sargs.num_args <= _sargs.max_args));
_sargs.parse_state = _SARGS_PARSING_VAL;
_sargs.args[_sargs.num_args].val = _sargs.buf_pos;
_sargs.args[_sargs.num_args - 1].val = _sargs.buf_pos;
}
_SOKOL_PRIVATE void _sargs_end_val(void) {
SOKOL_ASSERT(_sargs.num_args < _sargs.max_args);
_sargs_putc(0);
_sargs.num_args++;
_sargs.parse_state = 0;
}
@ -596,7 +609,12 @@ _SOKOL_PRIVATE bool _sargs_parse_carg(const char* src) {
if (_sargs_any_expected()) {
if (!_sargs_is_whitespace(c)) {
/* start of key, value or separator */
if (_sargs_key_expected()) {
if (_sargs_is_separator(c)) {
/* skip separator and expect value */
_sargs_expect_val();
continue;
}
else if (_sargs_key_expected()) {
/* start of new key */
_sargs_start_key();
}
@ -608,13 +626,6 @@ _SOKOL_PRIVATE bool _sargs_parse_carg(const char* src) {
}
_sargs_start_val();
}
else {
/* separator */
if (_sargs_is_separator(c)) {
_sargs_expect_val();
continue;
}
}
}
else {
/* skip white space */
@ -629,7 +640,7 @@ _SOKOL_PRIVATE bool _sargs_parse_carg(const char* src) {
_sargs_expect_val();
}
else {
_sargs_expect_sep();
_sargs_expect_sep_or_key();
}
continue;
}
@ -657,7 +668,7 @@ _SOKOL_PRIVATE bool _sargs_parse_carg(const char* src) {
}
if (_sargs_parsing_key()) {
_sargs_end_key();
_sargs_expect_sep();
_sargs_expect_sep_or_key();
}
else if (_sargs_parsing_val() && !_sargs_in_quotes()) {
_sargs_end_val();
@ -823,7 +834,13 @@ SOKOL_API_IMPL const char* sargs_value_def(const char* key, const char* def) {
SOKOL_ASSERT(_sargs.valid && key && def);
int arg_index = sargs_find(key);
if (-1 != arg_index) {
return sargs_value_at(arg_index);
const char* res = sargs_value_at(arg_index);
SOKOL_ASSERT(res);
if (res[0] == 0) {
return def;
} else {
return res;
}
}
else {
return def;
@ -836,10 +853,15 @@ SOKOL_API_IMPL bool sargs_equals(const char* key, const char* val) {
}
SOKOL_API_IMPL bool sargs_boolean(const char* key) {
if (sargs_exists(key)) {
const char* val = sargs_value(key);
return (0 == strcmp("true", val)) ||
(0 == strcmp("yes", val)) ||
(0 == strcmp("on", val));
(0 == strcmp("on", val)) ||
(0 == strcmp("", val));
} else {
return false;
}
}
#endif /* SOKOL_ARGS_IMPL */

View file

@ -397,8 +397,8 @@
saudio_setup(&(saudio_desc){
// ...
.allocator = {
.alloc = my_alloc,
.free = my_free,
.alloc_fn = my_alloc,
.free_fn = my_free,
.user_data = ...,
}
});
@ -575,12 +575,12 @@ typedef struct saudio_logger {
Used in saudio_desc to provide custom memory-alloc and -free functions
to sokol_audio.h. If memory management should be overridden, both the
alloc and free function must be provided (e.g. it's not valid to
alloc_fn and free_fn function must be provided (e.g. it's not valid to
override one function but not the other).
*/
typedef struct saudio_allocator {
void* (*alloc)(size_t size, void* user_data);
void (*free)(void* ptr, void* user_data);
void* (*alloc_fn)(size_t size, void* user_data);
void (*free_fn)(void* ptr, void* user_data);
void* user_data;
} saudio_allocator;
@ -1146,10 +1146,9 @@ _SOKOL_PRIVATE void _saudio_clear(void* ptr, size_t size) {
_SOKOL_PRIVATE void* _saudio_malloc(size_t size) {
SOKOL_ASSERT(size > 0);
void* ptr;
if (_saudio.desc.allocator.alloc) {
ptr = _saudio.desc.allocator.alloc(size, _saudio.desc.allocator.user_data);
}
else {
if (_saudio.desc.allocator.alloc_fn) {
ptr = _saudio.desc.allocator.alloc_fn(size, _saudio.desc.allocator.user_data);
} else {
ptr = malloc(size);
}
if (0 == ptr) {
@ -1165,10 +1164,9 @@ _SOKOL_PRIVATE void* _saudio_malloc_clear(size_t size) {
}
_SOKOL_PRIVATE void _saudio_free(void* ptr) {
if (_saudio.desc.allocator.free) {
_saudio.desc.allocator.free(ptr, _saudio.desc.allocator.user_data);
}
else {
if (_saudio.desc.allocator.free_fn) {
_saudio.desc.allocator.free_fn(ptr, _saudio.desc.allocator.user_data);
} else {
free(ptr);
}
}
@ -1519,15 +1517,13 @@ _SOKOL_PRIVATE bool _saudio_alsa_backend_init(void) {
_SAUDIO_ERROR(ALSA_PTHREAD_CREATE_FAILED);
goto error;
}
snd_config_update_free_global();
return true;
error:
if (_saudio.backend.device) {
snd_pcm_close(_saudio.backend.device);
_saudio.backend.device = 0;
}
snd_config_update_free_global();
return false;
};
@ -1537,7 +1533,6 @@ _SOKOL_PRIVATE void _saudio_alsa_backend_shutdown(void) {
pthread_join(_saudio.backend.thread, 0);
snd_pcm_drain(_saudio.backend.device);
snd_pcm_close(_saudio.backend.device);
snd_config_update_free_global();
_saudio_free(_saudio.backend.buffer);
};
@ -2488,7 +2483,7 @@ void _saudio_backend_shutdown(void) {
SOKOL_API_IMPL void saudio_setup(const saudio_desc* desc) {
SOKOL_ASSERT(!_saudio.valid);
SOKOL_ASSERT(desc);
SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free));
SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn));
_saudio_clear(&_saudio, sizeof(_saudio));
_saudio.desc = *desc;
_saudio.stream_cb = desc->stream_cb;

View file

@ -820,8 +820,8 @@
sfetch_setup(&(sfetch_desc_t){
// ...
.allocator = {
.alloc = my_alloc,
.free = my_free,
.alloc_fn = my_alloc,
.free_fn = my_free,
.user_data = ...,
}
});
@ -1024,8 +1024,8 @@ typedef struct sfetch_range_t {
override one function but not the other).
*/
typedef struct sfetch_allocator_t {
void* (*alloc)(size_t size, void* user_data);
void (*free)(void* ptr, void* user_data);
void* (*alloc_fn)(size_t size, void* user_data);
void (*free_fn)(void* ptr, void* user_data);
void* user_data;
} sfetch_allocator_t;
@ -1424,10 +1424,9 @@ _SOKOL_PRIVATE void _sfetch_clear(void* ptr, size_t size) {
_SOKOL_PRIVATE void* _sfetch_malloc_with_allocator(const sfetch_allocator_t* allocator, size_t size) {
SOKOL_ASSERT(size > 0);
void* ptr;
if (allocator->alloc) {
ptr = allocator->alloc(size, allocator->user_data);
}
else {
if (allocator->alloc_fn) {
ptr = allocator->alloc_fn(size, allocator->user_data);
} else {
ptr = malloc(size);
}
if (0 == ptr) {
@ -1447,10 +1446,9 @@ _SOKOL_PRIVATE void* _sfetch_malloc_clear(size_t size) {
}
_SOKOL_PRIVATE void _sfetch_free(void* ptr) {
if (_sfetch->desc.allocator.free) {
_sfetch->desc.allocator.free(ptr, _sfetch->desc.allocator.user_data);
}
else {
if (_sfetch->desc.allocator.free_fn) {
_sfetch->desc.allocator.free_fn(ptr, _sfetch->desc.allocator.user_data);
} else {
free(ptr);
}
}
@ -2457,6 +2455,12 @@ _SOKOL_PRIVATE void _sfetch_invoke_response_callback(_sfetch_item_t* item) {
item->callback(&response);
}
_SOKOL_PRIVATE void _sfetch_cancel_item(_sfetch_item_t* item) {
item->state = _SFETCH_STATE_FAILED;
item->user.finished = true;
item->user.error_code = SFETCH_ERROR_CANCELLED;
}
/* per-frame channel stuff: move requests in and out of the IO threads, call response callbacks */
_SOKOL_PRIVATE void _sfetch_channel_dowork(_sfetch_channel_t* chn, _sfetch_pool_t* pool) {
@ -2469,9 +2473,16 @@ _SOKOL_PRIVATE void _sfetch_channel_dowork(_sfetch_channel_t* chn, _sfetch_pool_
_sfetch_item_t* item = _sfetch_pool_item_lookup(pool, slot_id);
SOKOL_ASSERT(item);
SOKOL_ASSERT(item->state == _SFETCH_STATE_ALLOCATED);
// if the item was cancelled early, kick it out immediately
if (item->user.cancel) {
_sfetch_cancel_item(item);
_sfetch_invoke_response_callback(item);
_sfetch_pool_item_free(pool, slot_id);
continue;
}
item->state = _SFETCH_STATE_DISPATCHED;
item->lane = _sfetch_ring_dequeue(&chn->free_lanes);
/* if no buffer provided yet, invoke response callback to do so */
// if no buffer provided yet, invoke response callback to do so
if (0 == item->buffer.ptr) {
_sfetch_invoke_response_callback(item);
}
@ -2498,8 +2509,7 @@ _SOKOL_PRIVATE void _sfetch_channel_dowork(_sfetch_channel_t* chn, _sfetch_pool_
item->user.cont = false;
}
if (item->user.cancel) {
item->state = _SFETCH_STATE_FAILED;
item->user.finished = true;
_sfetch_cancel_item(item);
}
switch (item->state) {
case _SFETCH_STATE_DISPATCHED:
@ -2541,7 +2551,7 @@ _SOKOL_PRIVATE void _sfetch_channel_dowork(_sfetch_channel_t* chn, _sfetch_pool_
item->user.fetched_offset = item->thread.fetched_offset;
item->user.fetched_size = item->thread.fetched_size;
if (item->user.cancel) {
item->user.error_code = SFETCH_ERROR_CANCELLED;
_sfetch_cancel_item(item);
}
else {
item->user.error_code = item->thread.error_code;
@ -2558,7 +2568,7 @@ _SOKOL_PRIVATE void _sfetch_channel_dowork(_sfetch_channel_t* chn, _sfetch_pool_
}
_sfetch_invoke_response_callback(item);
/* when the request is finish, free the lane for another request,
/* when the request is finished, free the lane for another request,
otherwise feed it back into the incoming queue
*/
if (item->user.finished) {
@ -2608,7 +2618,7 @@ _SOKOL_PRIVATE bool _sfetch_validate_request(_sfetch_t* ctx, const sfetch_reques
}
_SOKOL_PRIVATE sfetch_desc_t _sfetch_desc_defaults(const sfetch_desc_t* desc) {
SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free));
SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn));
sfetch_desc_t res = *desc;
res.max_requests = _sfetch_def(desc->max_requests, 128);
res.num_channels = _sfetch_def(desc->num_channels, 1);

File diff suppressed because it is too large Load diff

View file

@ -27,8 +27,8 @@
sg_setup(&(sg_desc){
//...
.allocator = {
.alloc = smemtrack_alloc,
.free = smemtrack_free,
.alloc_fn = smemtrack_alloc,
.free_fn = smemtrack_free,
}
});