diff --git a/thirdparty/fontstash/stb_truetype.h b/thirdparty/fontstash/stb_truetype.h index c4010f557d..62595a15fd 100644 --- a/thirdparty/fontstash/stb_truetype.h +++ b/thirdparty/fontstash/stb_truetype.h @@ -1,5 +1,14 @@ -// stb_truetype.h - v1.21 - public domain -// authored from 2009-2016 by Sean Barrett / RAD Game Tools +// stb_truetype.h - v1.24 - public domain +// authored from 2009-2020 by Sean Barrett / RAD Game Tools +// +// ======================================================================= +// +// NO SECURITY GUARANTEE -- DO NOT USE THIS ON UNTRUSTED FONT FILES +// +// This library does no range checking of the offsets found in the file, +// meaning an attacker can use it to read arbitrary memory. +// +// ======================================================================= // // This library processes TrueType files: // parse files @@ -32,11 +41,11 @@ // Daniel Ribeiro Maciel // // Bug/warning reports/fixes: -// "Zer" on mollyrocket Fabian "ryg" Giesen -// Cass Everitt Martins Mozeiko -// stoiko (Haemimont Games) Cap Petschulat -// Brian Hook Omar Cornut -// Walter van Niftrik github:aloucks +// "Zer" on mollyrocket Fabian "ryg" Giesen github:NiLuJe +// Cass Everitt Martins Mozeiko github:aloucks +// stoiko (Haemimont Games) Cap Petschulat github:oyvindjam +// Brian Hook Omar Cornut github:vassvik +// Walter van Niftrik Ryan Griege // David Gow Peter LaValle // David Given Sergey Popov // Ivan-Assen Ivanov Giumo X. Clanjor @@ -44,11 +53,14 @@ // Johan Duparc Thomas Fields // Hou Qiming Derek Vinyard // Rob Loach Cort Stratton -// Kenney Phillis Jr. github:oyvindjam -// Brian Costabile github:vassvik +// Kenney Phillis Jr. Brian Costabile +// Ken Voskuil (kaesve) // // VERSION HISTORY // +// 1.24 (2020-02-05) fix warning +// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS) +// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined // 1.21 (2019-02-25) fix warning // 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() // 1.19 (2018-02-11) GPOS kerning, STBTT_fmod @@ -702,7 +714,7 @@ struct stbtt_fontinfo int numGlyphs; // number of glyphs, needed for range checking - int loca,head,glyf,hhea,hmtx,kern,gpos; // table locations as offset from start of .ttf + int loca,head,glyf,hhea,hmtx,kern,gpos,svg; // table locations as offset from start of .ttf int index_map; // a cmap mapping for our chosen character encoding int indexToLocFormat; // format needed to map from glyph index to glyph @@ -785,6 +797,18 @@ STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); // as above, but takes one or more glyph indices for greater efficiency +typedef struct stbtt_kerningentry +{ + int glyph1; // use stbtt_FindGlyphIndex + int glyph2; + int advance; +} stbtt_kerningentry; + +STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info); +STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length); +// Retrieves a complete list of all of the kerning pairs provided by the font +// stbtt_GetKerningTable never writes more than table_length entries and returns how many entries it did write. +// The table will be sorted by (a.glyph1 == b.glyph1)?(a.glyph2 < b.glyph2):(a.glyph1 < b.glyph1) ////////////////////////////////////////////////////////////////////////////// // @@ -829,6 +853,11 @@ STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, s STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); // frees the data allocated above +STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg); +STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg); +// fills svg with the character's SVG data. +// returns data size or 0 if SVG not found. + ////////////////////////////////////////////////////////////////////////////// // // BITMAP RENDERING @@ -1330,6 +1359,22 @@ static stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict) return stbtt__cff_get_index(&cff); } +// since most people won't use this, find this table the first time it's needed +static int stbtt__get_svg(stbtt_fontinfo *info) +{ + stbtt_uint32 t; + if (info->svg < 0) { + t = stbtt__find_table(info->data, info->fontstart, "SVG "); + if (t) { + stbtt_uint32 offset = ttULONG(info->data + t + 2); + info->svg = t + offset; + } else { + info->svg = 0; + } + } + return info->svg; +} + static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart) { stbtt_uint32 cmap, t; @@ -1409,6 +1454,8 @@ static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, in else info->numGlyphs = 0xffff; + info->svg = -1; + // find a cmap encoding table we understand *now* to avoid searching // later. (todo: could make this installable) // the same regardless of glyph. @@ -1757,7 +1804,7 @@ static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, s } } num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); - } else if (numberOfContours == -1) { + } else if (numberOfContours < 0) { // Compound shapes. int more = 1; stbtt_uint8 *comp = data + g + 10; @@ -1834,9 +1881,6 @@ static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, s // More components ? more = flags & (1<<5); } - } else if (numberOfContours < 0) { - // @TODO other compound variations? - STBTT_assert(0); } else { // numberOfCounters == 0, do nothing } @@ -2265,6 +2309,48 @@ STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_inde } } +STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info) +{ + stbtt_uint8 *data = info->data + info->kern; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + return ttUSHORT(data+10); +} + +STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length) +{ + stbtt_uint8 *data = info->data + info->kern; + int k, length; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + length = ttUSHORT(data+10); + if (table_length < length) + length = table_length; + + for (k = 0; k < length; k++) + { + table[k].glyph1 = ttUSHORT(data+18+(k*6)); + table[k].glyph2 = ttUSHORT(data+20+(k*6)); + table[k].advance = ttSHORT(data+22+(k*6)); + } + + return length; +} + static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) { stbtt_uint8 *data = info->data + info->kern; @@ -2540,8 +2626,7 @@ STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int if (info->gpos) xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2); - - if (info->kern) + else if (info->kern) xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2); return xAdvance; @@ -2602,6 +2687,45 @@ STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) STBTT_free(v, info->userdata); } +STBTT_DEF stbtt_uint8 *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl) +{ + int i; + stbtt_uint8 *data = info->data; + stbtt_uint8 *svg_doc_list = data + stbtt__get_svg((stbtt_fontinfo *) info); + + int numEntries = ttUSHORT(svg_doc_list); + stbtt_uint8 *svg_docs = svg_doc_list + 2; + + for(i=0; i= ttUSHORT(svg_doc)) && (gl <= ttUSHORT(svg_doc + 2))) + return svg_doc; + } + return 0; +} + +STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg) +{ + stbtt_uint8 *data = info->data; + stbtt_uint8 *svg_doc; + + if (info->svg == 0) + return 0; + + svg_doc = stbtt_FindSVGDoc(info, gl); + if (svg_doc != NULL) { + *svg = (char *) data + info->svg + ttULONG(svg_doc + 4); + return ttULONG(svg_doc + 8); + } else { + return 0; + } +} + +STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg) +{ + return stbtt_GetGlyphSVG(info, stbtt_FindGlyphIndex(info, unicode_codepoint), svg); +} + ////////////////////////////////////////////////////////////////////////////// // // antialiasing software rasterizer @@ -2978,7 +3102,7 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, float x_top, x_bottom; float sy0,sy1; float dy = e->fdy; -// STBTT_assert(e->sy <= y_bottom && e->ey >= y_top); + STBTT_assert(e->sy <= y_bottom && e->ey >= y_top); // compute endpoints of line segment clipped to this scanline (if the // line segment starts on this scanline. x0 is the intersection of the @@ -3968,6 +4092,7 @@ static float stbtt__oversample_shift(int oversample) STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) { int i,j,k; + int missing_glyph_added = 0; k=0; for (i=0; i < num_ranges; ++i) { @@ -3979,7 +4104,7 @@ STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stb int x0,y0,x1,y1; int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; int glyph = stbtt_FindGlyphIndex(info, codepoint); - if (glyph == 0 && spc->skip_missing) { + if (glyph == 0 && (spc->skip_missing || missing_glyph_added)) { rects[k].w = rects[k].h = 0; } else { stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, @@ -3989,6 +4114,8 @@ STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stb &x0,&y0,&x1,&y1); rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); + if (glyph == 0) + missing_glyph_added = 1; } ++k; } @@ -4023,7 +4150,7 @@ STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info // rects array must be big enough to accommodate all characters in the given ranges STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) { - int i,j,k, return_value = 1; + int i,j,k, missing_glyph = -1, return_value = 1; // save current values int old_h_over = spc->h_oversample; @@ -4088,6 +4215,13 @@ STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const bc->yoff = (float) y0 * recip_v + sub_y; bc->xoff2 = (x0 + r->w) * recip_h + sub_x; bc->yoff2 = (y0 + r->h) * recip_v + sub_y; + + if (glyph == 0) + missing_glyph = j; + } else if (spc->skip_missing) { + return_value = 0; + } else if (r->was_packed && r->w == 0 && r->h == 0 && missing_glyph >= 0) { + ranges[i].chardata_for_range[j] = ranges[i].chardata_for_range[missing_glyph]; } else { return_value = 0; // if any fail, report failure } @@ -4389,12 +4523,7 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc int w,h; unsigned char *data; - // if one scale is 0, use same scale for both - if (scale_x == 0) scale_x = scale_y; - if (scale_y == 0) { - if (scale_x == 0) return NULL; // if both scales are 0, return NULL - scale_y = scale_x; - } + if (scale == 0) return NULL; stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1); diff --git a/thirdparty/sokol/sokol_app.h b/thirdparty/sokol/sokol_app.h index b89148217c..254a44b16e 100755 --- a/thirdparty/sokol/sokol_app.h +++ b/thirdparty/sokol/sokol_app.h @@ -9,37 +9,44 @@ before you include this file in *one* C or C++ file to create the implementation. + In the same place define one of the following to select the 3D-API + which should be initialized by sokol_app.h (this must also match + the backend selected for sokol_gfx.h if both are used in the same + project): + + #define SOKOL_GLCORE33 + #define SOKOL_GLES2 + #define SOKOL_GLES3 + #define SOKOL_D3D11 + #define SOKOL_METAL + #define SOKOL_WGPU + Optionally provide the following defines with your own implementations: - SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) - SOKOL_LOG(msg) - your own logging function (default: puts(msg)) - SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) - SOKOL_ABORT() - called after an unrecoverable error (default: abort()) - SOKOL_WIN32_FORCE_MAIN - define this on Win32 to use a main() entry point instead of WinMain - SOKOL_NO_ENTRY - define this if sokol_app.h shouldn't "hijack" the main() function - SOKOL_API_DECL - public function declaration prefix (default: extern) - SOKOL_API_IMPL - public function implementation prefix (default: -) - SOKOL_CALLOC - your own calloc function (default: calloc(n, s)) - SOKOL_FREE - your own free function (default: free(p)) + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_LOG(msg) - your own logging function (default: puts(msg)) + SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) + SOKOL_ABORT() - called after an unrecoverable error (default: abort()) + SOKOL_WIN32_FORCE_MAIN - define this on Win32 to use a main() entry point instead of WinMain + SOKOL_NO_ENTRY - define this if sokol_app.h shouldn't "hijack" the main() function + SOKOL_API_DECL - public function declaration prefix (default: extern) + SOKOL_API_IMPL - public function implementation prefix (default: -) + SOKOL_CALLOC - your own calloc function (default: calloc(n, s)) + SOKOL_FREE - your own free function (default: free(p)) Optionally define the following to force debug checks and validations even in release mode: - SOKOL_DEBUG - by default this is defined if _DEBUG is defined + SOKOL_DEBUG - by default this is defined if _DEBUG is defined If sokol_app.h is compiled as a DLL, define the following before including the declaration or implementation: - SOKOL_DLL + SOKOL_DLL On Windows, SOKOL_DLL will define SOKOL_API_DECL as __declspec(dllexport) or __declspec(dllimport) as needed. - Portions of the Windows and Linux GL initialization and event code have been - taken from GLFW (http://www.glfw.org/) - - iOS onscreen keyboard support 'inspired' by libgdx. - If you use sokol_app.h together with sokol_gfx.h, include both headers in the implementation source file, and include sokol_app.h before sokol_gfx.h since sokol_app.h will also include the required 3D-API @@ -50,8 +57,26 @@ GL header-generator/loader instead, define SOKOL_WIN32_NO_GL_LOADER before including the implementation part of sokol_app.h. + To make use of the integrated GL loader, simply include the sokol_app.h + implementation before the sokol_gfx.h implementation. + For example code, see https://github.com/floooh/sokol-samples/tree/master/sapp + Portions of the Windows and Linux GL initialization and event code have been + taken from GLFW (http://www.glfw.org/) + + iOS onscreen keyboard support 'inspired' by libgdx. + + Link with the following system libraries: + + - on macOS with Metal: Cocoa, QuartzCore, Metal, MetalKit + - on macOS with GL: Cocoa, QuartzCore, OpenGL + - on iOS with Metal: UIKit, Metal, MetalKit + - on iOS with GL: UIKit, OpenGLES, GLKit + - on Linux: X11, Xi, Xcursor, GL, dl, m(?) + - on Android: GLESv3, EGL, log, android + - on Windows: no action needed, libs are defined in-source via pragma-comment-lib + FEATURE OVERVIEW ================ sokol_app.h provides a minimalistic cross-platform API which @@ -61,7 +86,7 @@ - creates a window and 3D-API context/device with a 'default framebuffer' - makes the rendered frame visible - provides keyboard-, mouse- and low-level touch-events - - platforms: MacOS, iOS, HTML5, Win32, Linux, Android (RaspberryPi) + - platforms: MacOS, iOS, HTML5, Win32, Linux, Android (TODO: RaspberryPi) - 3D-APIs: Metal, D3D11, GL3.2, GLES2, GLES3, WebGL, WebGL2 FEATURE/PLATFORM MATRIX @@ -91,13 +116,14 @@ RESTORED | YES | YES | YES | --- | --- | --- | --- SUSPENDED | --- | --- | --- | YES | YES | --- | TODO RESUMED | --- | --- | --- | YES | YES | --- | TODO - QUIT_REQUESTED | YES | YES | YES | --- | --- | TODO | --- + QUIT_REQUESTED | YES | YES | YES | --- | --- | TODO | YES UPDATE_CURSOR | YES | YES | TODO | --- | --- | --- | TODO IME | TODO | TODO? | TODO | ??? | TODO | ??? | ??? key repeat flag | YES | YES | YES | --- | --- | TODO | YES windowed | YES | YES | YES | --- | --- | TODO | YES - fullscreen | YES | YES | TODO | YES | YES | TODO | --- - pointer lock | TODO | TODO | TODO | --- | --- | TODO | TODO + fullscreen | YES | YES | YES | YES | YES | TODO | --- + mouse hide | YES | YES | YES | --- | --- | TODO | TODO + mouse lock | YES | YES | YES | --- | --- | TODO | YES screen keyboard | --- | --- | --- | YES | TODO | --- | YES swap interval | YES | YES | YES | YES | TODO | TODO | YES high-dpi | YES | YES | TODO | YES | YES | TODO | YES @@ -105,8 +131,8 @@ TODO ==== - - Linux clipboard support - - document sapp_consume_event() + - Linux: + - clipboard support - sapp_consume_event() on non-web platforms? STEP BY STEP @@ -158,10 +184,10 @@ The cleanup callback is called once right before the application quits. .event_cb (void (*)(const sapp_event* event)) - The event callback is mainly for input handling, but in the - future may also be used to communicate other types of events - to the application. Keep the event_cb struct member zero-initialized - if your application doesn't require event handling. + The event callback is mainly for input handling, but is also + used to communicate other types of events to the application. Keep the + event_cb struct member zero-initialized if your application doesn't require + event handling. .fail_cb (void (*)(const char* msg)) The fail callback is called when a fatal error is encountered during start which doesn't allow the program to continue. @@ -170,9 +196,9 @@ As you can see, those 'standard callbacks' don't have a user_data argument, so any data that needs to be preserved between callbacks - must live in global variables. If you're allergic to global variables - or cannot use them for other reasons, an alternative set of callbacks - can be defined in sapp_desc, together with a user_data pointer: + must live in global variables. If keeping state in global variables + is not an option, there's an alternative set of callbacks with + an additional user_data pointer argument: .user_data (void*) The user-data argument for the callbacks below @@ -188,7 +214,7 @@ The function sapp_userdata() can be used to query the user_data pointer provided in the sapp_desc struct. - You can call sapp_query_desc() to get a copy of the + You can also call sapp_query_desc() to get a copy of the original sapp_desc structure. NOTE that there's also an alternative compile mode where sokol_app.h @@ -201,10 +227,25 @@ at this point are: int sapp_width(void) - Returns the current width of the default framebuffer, this may change - from one frame to the next. int sapp_height(void) - Likewise, returns the current height of the default framebuffer. + Returns the current width and height of the default framebuffer in pixels, + this may change from one frame to the next, and it may be different + from the initial size provided in the sapp_desc struct. + + int sapp_color_format(void) + int sapp_depth_format(void) + The color and depth-stencil pixelformats of the default framebuffer, + as integer values which are compatible with sokol-gfx's + sg_pixel_format enum (so that they can be plugged directly in places + where sg_pixel_format is expected). Possible values are: + + 23 == SG_PIXELFORMAT_RGBA8 + 27 == SG_PIXELFORMAT_BGRA8 + 41 == SG_PIXELFORMAT_DEPTH + 42 == SG_PIXELFORMAT_DEPTH_STENCIL + + int sapp_sample_count(void) + Return the MSAA sample count of the default framebuffer. bool sapp_gles2(void) Returns true if a GLES2 or WebGL context has been created. This @@ -217,8 +258,8 @@ If the Metal backend has been selected, these functions return pointers to various Metal API objects required for rendering, otherwise they return a null pointer. These void pointers are actually - Objective-C ids converted with an ARC __bridge cast so that - they ids can be tunnel through C code. Also note that the returned + Objective-C ids converted with a (ARC) __bridge cast so that + the ids can be tunnel through C code. Also note that the returned pointers to the renderpass-descriptor and drawable may change from one frame to the next, only the Metal device object is guaranteed to stay the same. @@ -226,22 +267,22 @@ const void* sapp_macos_get_window(void) On macOS, get the NSWindow object pointer, otherwise a null pointer. Before being used as Objective-C object, the void* must be converted - back with an ARC __bridge cast. + back with a (ARC) __bridge cast. const void* sapp_ios_get_window(void) On iOS, get the UIWindow object pointer, otherwise a null pointer. Before being used as Objective-C object, the void* must be converted - back with an ARC __bridge cast. + back with a (ARC) __bridge cast. const void* sapp_win32_get_hwnd(void) On Windows, get the window's HWND, otherwise a null pointer. The HWND has been cast to a void pointer in order to be tunneled through code which doesn't include Windows.h. - const void* sapp_d3d11_get_device(void); - const void* sapp_d3d11_get_device_context(void); - const void* sapp_d3d11_get_render_target_view(void); - const void* sapp_d3d11_get_depth_stencil_view(void); + const void* sapp_d3d11_get_device(void) + const void* sapp_d3d11_get_device_context(void) + const void* sapp_d3d11_get_render_target_view(void) + const void* sapp_d3d11_get_depth_stencil_view(void) Similar to the sapp_metal_* functions, the sapp_d3d11_* functions return pointers to D3D11 API objects required for rendering, only if the D3D11 backend has been selected. Otherwise they @@ -249,6 +290,14 @@ render-target-view and depth-stencil-view may change from one frame to the next! + const void* sapp_wgpu_get_device(void) + const void* sapp_wgpu_get_render_view(void) + const void* sapp_wgpu_get_resolve_view(void) + const void* sapp_wgpu_get_depth_stencil_view(void) + These are the WebGPU-specific functions to get the WebGPU + objects and values required for rendering. If sokol_app.h + is not compiled with SOKOL_WGPU, these functions return null. + const void* sapp_android_get_native_activity(void); On Android, get the native activity ANativeActivity pointer, otherwise a null pointer. @@ -272,13 +321,118 @@ - the application window was resized, iconified or restored - the application was suspended or restored (on mobile platforms) - the user or application code has asked to quit the application + - a string was pasted to the system clipboard + + To explicitly 'consume' an event and prevent that the event is + forwarded for further handling to the operating system, call + sapp_consume_event() from inside the event handler (NOTE that + this behaviour is currently only implemented for some HTML5 + events, support for other platforms and event types will + be added as needed, please open a github ticket and/or provide + a PR if needed). + + NOTE: Do *not* call any 3D API rendering functions in the event + callback function, since the 3D API context may not be active when the + event callback is called (it may work on some platforms and 3D APIs, + but not others, and the exact behaviour may change between + sokol-app versions). --- Implement the cleanup-callback function, this is called once after the user quits the application (see the section "APPLICATION QUIT" for detailed information on quitting - behaviour, and how to intercept a pending quit (for instance to show a + behaviour, and how to intercept a pending quit - for instance to show a "Really Quit?" dialog box). Note that the cleanup-callback isn't - called on the web and mobile platforms. + guaranteed to be called on the web and mobile platforms. + + MOUSE LOCK (AKA POINTER LOCK, AKA MOUSE CAPTURE) + ================================================ + In normal mouse mode, no mouse movement events are reported when the + mouse leaves the windows client area or hits the screen border (whether + it's one or the other depends on the platform), and the mouse move events + (SAPP_EVENTTYPE_MOUSE_MOVE) contain absolute mouse positions in + framebuffer pixels in the sapp_event items mouse_x and mouse_y, and + relative movement in framebuffer pixels in the sapp_event items mouse_dx + and mouse_dy. + + To get continuous mouse movement (also when the mouse leaves the window + client area or hits the screen border), activate mouse-lock mode + by calling: + + sapp_lock_mouse(true) + + When mouse lock is activated, the mouse pointer is hidden, the + reported absolute mouse position (sapp_event.mouse_x/y) appears + frozen, and the relative mouse movement in sapp_event.mouse_dx/dy + no longer has a direct relation to framebuffer pixels but instead + uses "raw mouse input" (what "raw mouse input" exactly means also + differs by platform). + + To deactivate mouse lock and return to normal mouse mode, call + + sapp_lock_mouse(false) + + And finally, to check if mouse lock is currently active, call + + if (sapp_mouse_locked()) { ... } + + On native platforms, the sapp_lock_mouse() and sapp_mouse_locked() + functions work as expected (mouse lock is activated or deactivated + immediately when sapp_lock_mouse() is called, and sapp_mouse_locked() + also immediately returns the new state after sapp_lock_mouse() + is called. + + On the web platform, sapp_lock_mouse() and sapp_mouse_locked() behave + differently, as dictated by the limitations of the HTML5 Pointer Lock API: + + - sapp_lock_mouse(true) can be called at any time, but it will + only take effect in a 'short-lived input event handler of a specific + type', meaning when one of the following events happens: + - SAPP_EVENTTYPE_MOUSE_DOWN + - SAPP_EVENTTYPE_MOUSE_UP + - SAPP_EVENTTYPE_MOUSE_SCROLL + - SAPP_EVENTYTPE_KEY_UP + - SAPP_EVENTTYPE_KEY_DOWN + - The mouse lock/unlock action on the web platform is asynchronous, + this means that sapp_mouse_locked() won't immediately return + the new status after calling sapp_lock_mouse(), instead the + reported status will only change when the pointer lock has actually + been activated or deactivated in the browser. + - On the web, mouse lock can be deactivated by the user at any time + by pressing the Esc key. When this happens, sokol_app.h behaves + the same as if sapp_lock_mouse(false) is called. + + For things like camera manipulation it's most straightforward to lock + and unlock the mouse right from the sokol_app.h event handler, for + instance the following code enters and leaves mouse lock when the + left mouse button is pressed and released, and then uses the relative + movement information to manipulate a camera (taken from the + cgltf-sapp.c sample in the sokol-samples repository + at https://github.com/floooh/sokol-samples): + + static void input(const sapp_event* ev) { + switch (ev->type) { + case SAPP_EVENTTYPE_MOUSE_DOWN: + if (ev->mouse_button == SAPP_MOUSEBUTTON_LEFT) { + sapp_lock_mouse(true); + } + break; + + case SAPP_EVENTTYPE_MOUSE_UP: + if (ev->mouse_button == SAPP_MOUSEBUTTON_LEFT) { + sapp_lock_mouse(false); + } + break; + + case SAPP_EVENTTYPE_MOUSE_MOVE: + if (sapp_mouse_locked()) { + cam_orbit(&state.camera, ev->mouse_dx * 0.25f, ev->mouse_dy * 0.25f); + } + break; + + default: + break; + } + } CLIPBOARD SUPPORT ================= @@ -292,7 +446,7 @@ Enabling the clipboard will dynamically allocate a clipboard buffer for UTF-8 encoded text data of the requested size in bytes, the default - size if 8 KBytes. Strings that don't fit into the clipboard buffer + size is 8 KBytes. Strings that don't fit into the clipboard buffer (including the terminating zero) will be silently clipped, so it's important that you provide a big enough clipboard size for your use case. @@ -309,7 +463,7 @@ To get data from the clipboard, check for the SAPP_EVENTTYPE_CLIPBOARD_PASTED event in your event handler function, and then call sapp_get_clipboard_string() - to obtain the updated UTF-8 encoded text. + to obtain the pasted UTF-8 encoded text. NOTE that behaviour of sapp_get_clipboard_string() is slightly different depending on platform: @@ -373,36 +527,16 @@ APPLICATION QUIT ================ - Without special quit handling, a sokol_app.h application will exist - 'gracefully' when the user clicks the window close-button. 'Graceful - exit' means that the application-provided cleanup callback will be - called. + Without special quit handling, a sokol_app.h application will quit + 'gracefully' when the user clicks the window close-button unless a + platform's application model prevents this (e.g. on web or mobile). + 'Graceful exit' means that the application-provided cleanup callback will + be called before the application quits. - This 'graceful exit' is only supported on native desktop platforms, on - the web and mobile platforms an application may be terminated at any time - by the user or browser/OS runtime environment without a chance to run - custom shutdown code. - - On the web platform, you can call the following function to let the - browser open a standard popup dialog before the user wants to leave a site: - - sapp_html5_ask_leave_site(bool ask); - - The initial state of the associated internal flag can be provided - at startup via sapp_desc.html5_ask_leave_site. - - This feature should only be used sparingly in critical situations - for - instance when the user would loose data - since popping up modal dialog - boxes is considered quite rude in the web world. Note that there's no way - to customize the content of this dialog box or run any code as a result - of the user's decision. Also note that the user must have interacted with - the site before the dialog box will appear. These are all security measures - to prevent fishing. - - On native desktop platforms, sokol_app.h provides more control over the + On native desktop platforms sokol_app.h provides more control over the application-quit-process. It's possible to initiate a 'programmatic quit' - from the application code, and a quit initiated by the application user - can be intercepted (for instance to show a custom dialog box). + from the application code, and a quit initiated by the application user can + be intercepted (for instance to show a custom dialog box). This 'programmatic quit protocol' is implemented trough 3 functions and 1 event: @@ -429,6 +563,37 @@ this event by calling sapp_cancel_quit() to cancel the quit. If the event is ignored, the application will quit as usual. + On the web platform, the quit behaviour differs from native platforms, + because of web-specific restrictions: + + A `programmatic quit` initiated by calling sapp_quit() or + sapp_request_quit() will work as described above: the cleanup callback is + called, platform-specific cleanup is performed (on the web + this means that JS event handlers are unregisters), and then + the request-animation-loop will be exited. However that's all. The + web page itself will continue to exist (e.g. it's not possible to + programmatically close the browser tab). + + On the web it's also not possible to run custom code when the user + closes a brower tab, so it's not possible to prevent this with a + fancy custom dialog box. + + Instead the standard "Leave Site?" dialog box can be activated (or + deactivated) with the following function: + + sapp_html5_ask_leave_site(bool ask); + + The initial state of the associated internal flag can be provided + at startup via sapp_desc.html5_ask_leave_site. + + This feature should only be used sparingly in critical situations - for + instance when the user would loose data - since popping up modal dialog + boxes is considered quite rude in the web world. Note that there's no way + to customize the content of this dialog box or run any code as a result + of the user's decision. Also note that the user must have interacted with + the site before the dialog box will appear. These are all security measures + to prevent fishing. + The Dear ImGui HighDPI sample contains example code of how to implement a 'Really Quit?' dialog box with Dear ImGui (native desktop platforms only), and for showing the hardwired "Leave Site?" dialog box @@ -454,6 +619,11 @@ might still be used for the non-fullscreen window, in case the user can switch back from fullscreen- to windowed-mode). + To toggle fullscreen mode programmatically, call sapp_toggle_fullscreen(). + + To check if the application window is currently in fullscreen mode, + call sapp_is_fullscreen(). + ONSCREEN KEYBOARD ================= On some platforms which don't provide a physical keyboard, sokol-app @@ -503,7 +673,6 @@ - sapp_desc needs a bool whether to initialize depth-stencil surface - GL context initialization needs more control (at least what GL version to initialize) - application icon - - mouse pointer visibility(?) - the UPDATE_CURSOR event currently behaves differently between Win32 and OSX (Win32 sends the event each frame when the mouse moves and is inside the window client area, OSX sends it only once when the mouse enters the client area) @@ -512,8 +681,9 @@ on Android Honeycomb and later (it can't be done at the moment as the app may be started again after onStop and the sokol lifecycle does not yet handle context teardown/bringup) - FIXME: ERROR HANDLING (this will need an error callback function) + LICENSE + ======= zlib/libpng license Copyright (c) 2018 Andre Weissflog @@ -744,6 +914,8 @@ typedef struct sapp_event { sapp_mousebutton mouse_button; float mouse_x; float mouse_y; + float mouse_dx; + float mouse_dy; float scroll_x; float scroll_y; int num_touches; @@ -798,18 +970,32 @@ SOKOL_API_DECL bool sapp_isvalid(void); SOKOL_API_DECL int sapp_width(void); /* returns the current framebuffer height in pixels */ SOKOL_API_DECL int sapp_height(void); +/* get default framebuffer color pixel format */ +SOKOL_API_DECL int sapp_color_format(void); +/* get default framebuffer depth pixel format */ +SOKOL_API_DECL int sapp_depth_format(void); +/* get default framebuffer sample count */ +SOKOL_API_DECL int sapp_sample_count(void); /* returns true when high_dpi was requested and actually running in a high-dpi scenario */ SOKOL_API_DECL bool sapp_high_dpi(void); /* returns the dpi scaling factor (window pixels to framebuffer pixels) */ SOKOL_API_DECL float sapp_dpi_scale(void); /* show or hide the mobile device onscreen keyboard */ -SOKOL_API_DECL void sapp_show_keyboard(bool visible); +SOKOL_API_DECL void sapp_show_keyboard(bool show); /* return true if the mobile device onscreen keyboard is currently shown */ SOKOL_API_DECL bool sapp_keyboard_shown(void); +/* query fullscreen mode */ +SOKOL_API_DECL bool sapp_is_fullscreen(void); +/* toggle fullscreen mode */ +SOKOL_API_DECL void sapp_toggle_fullscreen(void); /* show or hide the mouse cursor */ -SOKOL_API_DECL void sapp_show_mouse(bool visible); +SOKOL_API_DECL void sapp_show_mouse(bool show); /* show or hide the mouse cursor */ SOKOL_API_DECL bool sapp_mouse_shown(); +/* enable/disable mouse-pointer-lock mode */ +SOKOL_API_DECL void sapp_lock_mouse(bool lock); +/* return true if in mouse-pointer-lock mode (this may toggle a few frames later) */ +SOKOL_API_DECL bool sapp_mouse_locked(void); /* return the userdata pointer optionally provided in sapp_desc */ SOKOL_API_DECL void* sapp_userdata(void); /* return a copy of the sapp_desc structure */ @@ -818,7 +1004,7 @@ SOKOL_API_DECL sapp_desc sapp_query_desc(void); SOKOL_API_DECL void sapp_request_quit(void); /* cancel a pending quit (when SAPP_EVENTTYPE_QUIT_REQUESTED has been received) */ SOKOL_API_DECL void sapp_cancel_quit(void); -/* intiate a "hard quit" (quit application without sending SAPP_EVENTTYPE_QUIT_REQUSTED) */ +/* initiate a "hard quit" (quit application without sending SAPP_EVENTTYPE_QUIT_REQUSTED) */ SOKOL_API_DECL void sapp_quit(void); /* call from inside event callback to consume the current event (don't forward to platform) */ SOKOL_API_DECL void sapp_consume_event(void); @@ -838,16 +1024,17 @@ SOKOL_API_DECL bool sapp_gles2(void); /* HTML5: enable or disable the hardwired "Leave Site?" dialog box */ SOKOL_API_DECL void sapp_html5_ask_leave_site(bool ask); -/* Metal: get ARC-bridged pointer to Metal device object */ +/* Metal: get bridged pointer to Metal device object */ SOKOL_API_DECL const void* sapp_metal_get_device(void); -/* Metal: get ARC-bridged pointer to this frame's renderpass descriptor */ +/* Metal: get bridged pointer to this frame's renderpass descriptor */ SOKOL_API_DECL const void* sapp_metal_get_renderpass_descriptor(void); -/* Metal: get ARC-bridged pointer to current drawable */ +/* Metal: get bridged pointer to current drawable */ SOKOL_API_DECL const void* sapp_metal_get_drawable(void); -/* macOS: get ARC-bridged pointer to macOS NSWindow */ +/* macOS: get bridged pointer to macOS NSWindow */ SOKOL_API_DECL const void* sapp_macos_get_window(void); +/* macOS: set window title */ SOKOL_API_DECL void sapp_macos_set_title(const char* title); -/* iOS: get ARC-bridged pointer to iOS UIWindow */ +/* iOS: get bridged pointer to iOS UIWindow */ SOKOL_API_DECL const void* sapp_ios_get_window(void); /* D3D11: get pointer to ID3D11Device object */ @@ -861,11 +1048,24 @@ SOKOL_API_DECL const void* sapp_d3d11_get_depth_stencil_view(void); /* Win32: get the HWND window handle */ SOKOL_API_DECL const void* sapp_win32_get_hwnd(void); +/* WebGPU: get WGPUDevice handle */ +SOKOL_API_DECL const void* sapp_wgpu_get_device(void); +/* WebGPU: get swapchain's WGPUTextureView handle for rendering */ +SOKOL_API_DECL const void* sapp_wgpu_get_render_view(void); +/* WebGPU: get swapchain's MSAA-resolve WGPUTextureView (may return null) */ +SOKOL_API_DECL const void* sapp_wgpu_get_resolve_view(void); +/* WebGPU: get swapchain's WGPUTextureView for the depth-stencil surface */ +SOKOL_API_DECL const void* sapp_wgpu_get_depth_stencil_view(void); + /* Android: get native activity handle */ SOKOL_API_DECL const void* sapp_android_get_native_activity(void); #ifdef __cplusplus } /* extern "C" */ + +/* reference-based equivalents for C++ */ +inline int sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } + #endif #endif // SOKOL_APP_INCLUDED @@ -873,47 +1073,46 @@ SOKOL_API_DECL const void* sapp_android_get_native_activity(void); #ifdef SOKOL_IMPL #define SOKOL_APP_IMPL_INCLUDED (1) -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ -#pragma warning(disable:4115) /* named type definition in parentheses */ -#pragma warning(disable:4054) /* 'type cast': from function pointer */ -#pragma warning(disable:4055) /* 'type cast': from data pointer */ -#pragma warning(disable:4505) /* unreferenced local function has been removed */ -#pragma warning(disable:4115) /* /W4: 'ID3D11ModuleInstance': named type definition in parentheses (in d3d11.h) */ -#endif - #include /* memset */ /* check if the config defines are alright */ #if defined(__APPLE__) - #if !__has_feature(objc_arc) - #error "sokol_app.h requires ARC (Automatic Reference Counting) on MacOS and iOS" - #endif - #include - #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE - /* iOS */ - #if !defined(SOKOL_METAL) && !defined(SOKOL_GLES3) - #error("sokol_app.h: unknown 3D API selected for iOS, must be SOKOL_METAL or SOKOL_GLES3") + // see https://clang.llvm.org/docs/LanguageExtensions.html#automatic-reference-counting + #if !defined(__cplusplus) + #if __has_feature(objc_arc) && !__has_feature(objc_arc_fields) + #error "sokol_app.h requires __has_feature(objc_arc_field) if ARC is enabled (use a more recent compiler version)" #endif - #else + #endif + #define _SAPP_APPLE (1) + #include + #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE /* MacOS */ + #define _SAPP_MACOS (1) #if !defined(SOKOL_METAL) && !defined(SOKOL_GLCORE33) #error("sokol_app.h: unknown 3D API selected for MacOS, must be SOKOL_METAL or SOKOL_GLCORE33") #endif + #else + /* iOS or iOS Simulator */ + #define _SAPP_IOS (1) + #if !defined(SOKOL_METAL) && !defined(SOKOL_GLES3) + #error("sokol_app.h: unknown 3D API selected for iOS, must be SOKOL_METAL or SOKOL_GLES3") + #endif #endif #elif defined(__EMSCRIPTEN__) /* emscripten (asm.js or wasm) */ - #if !defined(SOKOL_GLES3) && !defined(SOKOL_GLES2) - #error("sokol_app.h: unknown 3D API selected for emscripten, must be SOKOL_GLES3 or SOKOL_GLES2") + #define _SAPP_EMSCRIPTEN (1) + #if !defined(SOKOL_GLES3) && !defined(SOKOL_GLES2) && !defined(SOKOL_WGPU) + #error("sokol_app.h: unknown 3D API selected for emscripten, must be SOKOL_GLES3, SOKOL_GLES2 or SOKOL_WGPU") #endif #elif defined(_WIN32) /* Windows (D3D11 or GL) */ + #define _SAPP_WIN32 (1) #if !defined(SOKOL_D3D11) && !defined(SOKOL_GLCORE33) #error("sokol_app.h: unknown 3D API selected for Win32, must be SOKOL_D3D11 or SOKOL_GLCORE33") #endif #elif defined(__ANDROID__) /* Android */ + #define _SAPP_ANDROID (1) #if !defined(SOKOL_GLES3) && !defined(SOKOL_GLES2) #error("sokol_app.h: unknown 3D API selected for Android, must be SOKOL_GLES3 or SOKOL_GLES2") #endif @@ -922,6 +1121,7 @@ SOKOL_API_DECL const void* sapp_android_get_native_activity(void); #endif #elif defined(__linux__) || defined(__unix__) /* Linux */ + #define _SAPP_LINUX (1) #if !defined(SOKOL_GLCORE33) #error("sokol_app.h: unknown 3D API selected for Linux, must be SOKOL_GLCORE33") #endif @@ -941,6 +1141,9 @@ SOKOL_API_DECL const void* sapp_android_get_native_activity(void); #include #define SOKOL_ASSERT(c) assert(c) #endif +#ifndef SOKOL_UNREACHABLE + #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) +#endif #if !defined(SOKOL_CALLOC) || !defined(SOKOL_FREE) #include #endif @@ -968,7 +1171,7 @@ SOKOL_API_DECL const void* sapp_android_get_native_activity(void); #define SOKOL_ABORT() abort() #endif #ifndef _SOKOL_PRIVATE - #if defined(__GNUC__) + #if defined(__GNUC__) || defined(__clang__) #define _SOKOL_PRIVATE __attribute__((unused)) static #else #define _SOKOL_PRIVATE static @@ -978,835 +1181,155 @@ SOKOL_API_DECL const void* sapp_android_get_native_activity(void); #define _SOKOL_UNUSED(x) (void)(x) #endif -/* helper macros */ -#define _sapp_def(val, def) (((val) == 0) ? (def) : (val)) -#define _sapp_absf(a) (((a)<0.0f)?-(a):(a)) - -enum { - _SAPP_MAX_TITLE_LENGTH = 128, -}; - -typedef struct { - bool valid; - int window_width; - int window_height; - int framebuffer_width; - int framebuffer_height; - int sample_count; - int swap_interval; - float dpi_scale; - bool gles2_fallback; - bool first_frame; - bool init_called; - bool cleanup_called; - bool quit_requested; - bool quit_ordered; - bool event_consumed; - const char* html5_canvas_name; - bool html5_ask_leave_site; - char window_title[_SAPP_MAX_TITLE_LENGTH]; /* UTF-8 */ - wchar_t window_title_wide[_SAPP_MAX_TITLE_LENGTH]; /* UTF-32 or UCS-2 */ - uint64_t frame_count; - float mouse_x; - float mouse_y; - bool win32_mouse_tracked; - bool onscreen_keyboard_shown; - sapp_event event; - sapp_desc desc; - sapp_keycode keycodes[SAPP_MAX_KEYCODES]; - bool clipboard_enabled; - int clipboard_size; - char* clipboard; -} _sapp_state; -static _sapp_state _sapp; - -_SOKOL_PRIVATE void _sapp_fail(const char* msg) { - if (_sapp.desc.fail_cb) { - _sapp.desc.fail_cb(msg); - } - else if (_sapp.desc.fail_userdata_cb) { - _sapp.desc.fail_userdata_cb(msg, _sapp.desc.user_data); - } - else { - SOKOL_LOG(msg); - } - SOKOL_ABORT(); -} - -_SOKOL_PRIVATE void _sapp_call_init(void) { - if (_sapp.desc.init_cb) { - _sapp.desc.init_cb(); - } - else if (_sapp.desc.init_userdata_cb) { - _sapp.desc.init_userdata_cb(_sapp.desc.user_data); - } - _sapp.init_called = true; -} - -_SOKOL_PRIVATE void _sapp_call_frame(void) { - if (_sapp.init_called && !_sapp.cleanup_called) { - if (_sapp.desc.frame_cb) { - _sapp.desc.frame_cb(); - } - else if (_sapp.desc.frame_userdata_cb) { - _sapp.desc.frame_userdata_cb(_sapp.desc.user_data); - } - } -} - -_SOKOL_PRIVATE void _sapp_call_cleanup(void) { - if (!_sapp.cleanup_called) { - if (_sapp.desc.cleanup_cb) { - _sapp.desc.cleanup_cb(); - } - else if (_sapp.desc.cleanup_userdata_cb) { - _sapp.desc.cleanup_userdata_cb(_sapp.desc.user_data); - } - _sapp.cleanup_called = true; - } -} - -_SOKOL_PRIVATE bool _sapp_call_event(const sapp_event* e) { - if (!_sapp.cleanup_called) { - if (_sapp.desc.event_cb) { - _sapp.desc.event_cb(e); - } - else if (_sapp.desc.event_userdata_cb) { - _sapp.desc.event_userdata_cb(e, _sapp.desc.user_data); - } - } - if (_sapp.event_consumed) { - _sapp.event_consumed = false; - return true; - } - else { - return false; - } -} - -_SOKOL_PRIVATE void _sapp_strcpy(const char* src, char* dst, int max_len) { - SOKOL_ASSERT(src && dst && (max_len > 0)); - char* const end = &(dst[max_len-1]); - char c = 0; - for (int i = 0; i < max_len; i++) { - c = *src; - if (c != 0) { - src++; - } - *dst++ = c; - } - /* truncated? */ - if (c != 0) { - *end = 0; - } -} - -_SOKOL_PRIVATE void _sapp_init_state(const sapp_desc* desc) { - memset(&_sapp, 0, sizeof(_sapp)); - _sapp.desc = *desc; - _sapp.first_frame = true; - _sapp.window_width = _sapp_def(_sapp.desc.width, 640); - _sapp.window_height = _sapp_def(_sapp.desc.height, 480); - _sapp.framebuffer_width = _sapp.window_width; - _sapp.framebuffer_height = _sapp.window_height; - _sapp.sample_count = _sapp_def(_sapp.desc.sample_count, 1); - _sapp.swap_interval = _sapp_def(_sapp.desc.swap_interval, 1); - _sapp.html5_canvas_name = _sapp_def(_sapp.desc.html5_canvas_name, "canvas"); - _sapp.html5_ask_leave_site = _sapp.desc.html5_ask_leave_site; - _sapp.clipboard_enabled = _sapp.desc.enable_clipboard; - if (_sapp.clipboard_enabled) { - _sapp.clipboard_size = _sapp_def(_sapp.desc.clipboard_size, 8192); - _sapp.clipboard = (char*) SOKOL_CALLOC(1, _sapp.clipboard_size); - } - if (_sapp.desc.window_title) { - _sapp_strcpy(_sapp.desc.window_title, _sapp.window_title, sizeof(_sapp.window_title)); - } - else { - _sapp_strcpy("sokol_app", _sapp.window_title, sizeof(_sapp.window_title)); - } - _sapp.dpi_scale = 1.0f; -} - -_SOKOL_PRIVATE void _sapp_discard_state(void) { - if (_sapp.clipboard_enabled) { - SOKOL_ASSERT(_sapp.clipboard); - SOKOL_FREE((void*)_sapp.clipboard); - } - memset(&_sapp, 0, sizeof(_sapp)); -} - -_SOKOL_PRIVATE void _sapp_init_event(sapp_event_type type) { - memset(&_sapp.event, 0, sizeof(_sapp.event)); - _sapp.event.type = type; - _sapp.event.frame_count = _sapp.frame_count; - _sapp.event.mouse_button = SAPP_MOUSEBUTTON_INVALID; - _sapp.event.window_width = _sapp.window_width; - _sapp.event.window_height = _sapp.window_height; - _sapp.event.framebuffer_width = _sapp.framebuffer_width; - _sapp.event.framebuffer_height = _sapp.framebuffer_height; -} - -_SOKOL_PRIVATE bool _sapp_events_enabled(void) { - /* only send events when an event callback is set, and the init function was called */ - return (_sapp.desc.event_cb || _sapp.desc.event_userdata_cb) && _sapp.init_called; -} - -_SOKOL_PRIVATE sapp_keycode _sapp_translate_key(int scan_code) { - if ((scan_code >= 0) && (scan_code < SAPP_MAX_KEYCODES)) { - return _sapp.keycodes[scan_code]; - } - else { - return SAPP_KEYCODE_INVALID; - } -} - -_SOKOL_PRIVATE void _sapp_frame(void) { - if (_sapp.first_frame) { - _sapp.first_frame = false; - _sapp_call_init(); - } - _sapp_call_frame(); - _sapp.frame_count++; -} - -/*== MacOS/iOS ===============================================================*/ - -#if defined(__APPLE__) - -/*== MacOS ===================================================================*/ -#if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE - -#if defined(SOKOL_METAL) -#import -#import -#elif defined(SOKOL_GLCORE33) -#ifndef GL_SILENCE_DEPRECATION -#define GL_SILENCE_DEPRECATION -#endif -#include -#include +/*== PLATFORM SPECIFIC INCLUDES AND DEFINES ==================================*/ +#if defined(_SAPP_APPLE) + #if defined(SOKOL_METAL) + #import + #import + #endif + #if defined(_SAPP_MACOS) + #if !defined(SOKOL_METAL) + #ifndef GL_SILENCE_DEPRECATION + #define GL_SILENCE_DEPRECATION + #endif + #include + #include + #endif + #elif defined(_SAPP_IOS) + #import + #if !defined(SOKOL_METAL) + #import + #include + #include + #endif + #endif +#elif defined(_SAPP_EMSCRIPTEN) + #if defined(SOKOL_GLES3) + #include + #elif defined(SOKOL_GLES2) + #ifndef GL_EXT_PROTOTYPES + #define GL_GLEXT_PROTOTYPES + #endif + #include + #include + #elif defined(SOKOL_WGPU) + #include + #endif + #include + #include +#elif defined(_SAPP_WIN32) + #ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ + #pragma warning(disable:4204) /* nonstandard extension used: non-constant aggregate initializer */ + #pragma warning(disable:4054) /* 'type cast': from function pointer */ + #pragma warning(disable:4055) /* 'type cast': from data pointer */ + #pragma warning(disable:4505) /* unreferenced local function has been removed */ + #pragma warning(disable:4115) /* /W4: 'ID3D11ModuleInstance': named type definition in parentheses (in d3d11.h) */ + #endif + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #ifndef NOMINMAX + #define NOMINMAX + #endif + #include + #include + #include + #pragma comment (lib, "Shell32.lib") + #if !defined(SOKOL_WIN32_FORCE_MAIN) + #pragma comment (linker, "/subsystem:windows") + #endif + #if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) + #pragma comment (lib, "WindowsApp.lib") + #else + #pragma comment (lib, "user32.lib") + #if defined(SOKOL_D3D11) + #pragma comment (lib, "dxgi.lib") + #pragma comment (lib, "d3d11.lib") + #pragma comment (lib, "dxguid.lib") + #endif + #if defined(SOKOL_GLCORE33) + #pragma comment (lib, "gdi32.lib") + #endif + #endif + #if defined(SOKOL_D3D11) + #ifndef D3D11_NO_HELPERS + #define D3D11_NO_HELPERS + #endif + #ifndef CINTERFACE + #define CINTERFACE + #endif + #ifndef COBJMACROS + #define COBJMACROS + #endif + #include + #include + #endif + #ifndef WM_MOUSEHWHEEL /* see https://github.com/floooh/sokol/issues/138 */ + #define WM_MOUSEHWHEEL (0x020E) + #endif +#elif defined(_SAPP_ANDROID) + #include + #include + #include + #include + #include + #if defined(SOKOL_GLES3) + #include + #else + #ifndef GL_EXT_PROTOTYPES + #define GL_GLEXT_PROTOTYPES + #endif + #include + #include + #endif +#elif defined(_SAPP_LINUX) + #define GL_GLEXT_PROTOTYPES + #include + #include + #include + #include + #include + #include + #include + #include /* CARD32 */ + #include + #include /* dlopen, dlsym, dlclose */ + #include /* LONG_MAX */ #endif +/*== MACOS DECLARATIONS ======================================================*/ +#if defined(_SAPP_MACOS) @interface _sapp_macos_app_delegate : NSObject @end @interface _sapp_macos_window_delegate : NSObject @end #if defined(SOKOL_METAL) -@interface _sapp_macos_mtk_view_dlg : NSObject -@end -@interface _sapp_macos_view : MTKView -{ - NSTrackingArea* trackingArea; -} -@end + @interface _sapp_macos_view : MTKView + @end #elif defined(SOKOL_GLCORE33) -@interface _sapp_macos_view : NSOpenGLView -{ - NSTrackingArea* trackingArea; -} -- (void)timerFired:(id)sender; -- (void)prepareOpenGL; -- (void)drawRect:(NSRect)bounds; -@end -#endif + @interface _sapp_macos_view : NSOpenGLView + - (void)timerFired:(id)sender; + @end +#endif // SOKOL_GLCORE33 -@interface MyWindow : NSWindow { -} -@end - -@implementation MyWindow -- (BOOL)canBecomeKeyWindow { return YES;} -@end - - -static MyWindow* _sapp_macos_window_obj; -static _sapp_macos_window_delegate* _sapp_macos_win_dlg_obj; -static _sapp_macos_app_delegate* _sapp_macos_app_dlg_obj; -static _sapp_macos_view* _sapp_view_obj; -#if defined(SOKOL_METAL) -static _sapp_macos_mtk_view_dlg* _sapp_macos_mtk_view_dlg_obj; -static id _sapp_mtl_device_obj; -#elif defined(SOKOL_GLCORE33) -static NSOpenGLPixelFormat* _sapp_macos_glpixelformat_obj; -static NSTimer* _sapp_macos_timer_obj; -#endif -static uint32_t _sapp_macos_flags_changed_store; - -_SOKOL_PRIVATE void _sapp_macos_init_keytable(void) { - _sapp.keycodes[0x1D] = SAPP_KEYCODE_0; - _sapp.keycodes[0x12] = SAPP_KEYCODE_1; - _sapp.keycodes[0x13] = SAPP_KEYCODE_2; - _sapp.keycodes[0x14] = SAPP_KEYCODE_3; - _sapp.keycodes[0x15] = SAPP_KEYCODE_4; - _sapp.keycodes[0x17] = SAPP_KEYCODE_5; - _sapp.keycodes[0x16] = SAPP_KEYCODE_6; - _sapp.keycodes[0x1A] = SAPP_KEYCODE_7; - _sapp.keycodes[0x1C] = SAPP_KEYCODE_8; - _sapp.keycodes[0x19] = SAPP_KEYCODE_9; - _sapp.keycodes[0x00] = SAPP_KEYCODE_A; - _sapp.keycodes[0x0B] = SAPP_KEYCODE_B; - _sapp.keycodes[0x08] = SAPP_KEYCODE_C; - _sapp.keycodes[0x02] = SAPP_KEYCODE_D; - _sapp.keycodes[0x0E] = SAPP_KEYCODE_E; - _sapp.keycodes[0x03] = SAPP_KEYCODE_F; - _sapp.keycodes[0x05] = SAPP_KEYCODE_G; - _sapp.keycodes[0x04] = SAPP_KEYCODE_H; - _sapp.keycodes[0x22] = SAPP_KEYCODE_I; - _sapp.keycodes[0x26] = SAPP_KEYCODE_J; - _sapp.keycodes[0x28] = SAPP_KEYCODE_K; - _sapp.keycodes[0x25] = SAPP_KEYCODE_L; - _sapp.keycodes[0x2E] = SAPP_KEYCODE_M; - _sapp.keycodes[0x2D] = SAPP_KEYCODE_N; - _sapp.keycodes[0x1F] = SAPP_KEYCODE_O; - _sapp.keycodes[0x23] = SAPP_KEYCODE_P; - _sapp.keycodes[0x0C] = SAPP_KEYCODE_Q; - _sapp.keycodes[0x0F] = SAPP_KEYCODE_R; - _sapp.keycodes[0x01] = SAPP_KEYCODE_S; - _sapp.keycodes[0x11] = SAPP_KEYCODE_T; - _sapp.keycodes[0x20] = SAPP_KEYCODE_U; - _sapp.keycodes[0x09] = SAPP_KEYCODE_V; - _sapp.keycodes[0x0D] = SAPP_KEYCODE_W; - _sapp.keycodes[0x07] = SAPP_KEYCODE_X; - _sapp.keycodes[0x10] = SAPP_KEYCODE_Y; - _sapp.keycodes[0x06] = SAPP_KEYCODE_Z; - _sapp.keycodes[0x27] = SAPP_KEYCODE_APOSTROPHE; - _sapp.keycodes[0x2A] = SAPP_KEYCODE_BACKSLASH; - _sapp.keycodes[0x2B] = SAPP_KEYCODE_COMMA; - _sapp.keycodes[0x18] = SAPP_KEYCODE_EQUAL; - _sapp.keycodes[0x32] = SAPP_KEYCODE_GRAVE_ACCENT; - _sapp.keycodes[0x21] = SAPP_KEYCODE_LEFT_BRACKET; - _sapp.keycodes[0x1B] = SAPP_KEYCODE_MINUS; - _sapp.keycodes[0x2F] = SAPP_KEYCODE_PERIOD; - _sapp.keycodes[0x1E] = SAPP_KEYCODE_RIGHT_BRACKET; - _sapp.keycodes[0x29] = SAPP_KEYCODE_SEMICOLON; - _sapp.keycodes[0x2C] = SAPP_KEYCODE_SLASH; - _sapp.keycodes[0x0A] = SAPP_KEYCODE_WORLD_1; - _sapp.keycodes[0x33] = SAPP_KEYCODE_BACKSPACE; - _sapp.keycodes[0x39] = SAPP_KEYCODE_CAPS_LOCK; - _sapp.keycodes[0x75] = SAPP_KEYCODE_DELETE; - _sapp.keycodes[0x7D] = SAPP_KEYCODE_DOWN; - _sapp.keycodes[0x77] = SAPP_KEYCODE_END; - _sapp.keycodes[0x24] = SAPP_KEYCODE_ENTER; - _sapp.keycodes[0x35] = SAPP_KEYCODE_ESCAPE; - _sapp.keycodes[0x7A] = SAPP_KEYCODE_F1; - _sapp.keycodes[0x78] = SAPP_KEYCODE_F2; - _sapp.keycodes[0x63] = SAPP_KEYCODE_F3; - _sapp.keycodes[0x76] = SAPP_KEYCODE_F4; - _sapp.keycodes[0x60] = SAPP_KEYCODE_F5; - _sapp.keycodes[0x61] = SAPP_KEYCODE_F6; - _sapp.keycodes[0x62] = SAPP_KEYCODE_F7; - _sapp.keycodes[0x64] = SAPP_KEYCODE_F8; - _sapp.keycodes[0x65] = SAPP_KEYCODE_F9; - _sapp.keycodes[0x6D] = SAPP_KEYCODE_F10; - _sapp.keycodes[0x67] = SAPP_KEYCODE_F11; - _sapp.keycodes[0x6F] = SAPP_KEYCODE_F12; - _sapp.keycodes[0x69] = SAPP_KEYCODE_F13; - _sapp.keycodes[0x6B] = SAPP_KEYCODE_F14; - _sapp.keycodes[0x71] = SAPP_KEYCODE_F15; - _sapp.keycodes[0x6A] = SAPP_KEYCODE_F16; - _sapp.keycodes[0x40] = SAPP_KEYCODE_F17; - _sapp.keycodes[0x4F] = SAPP_KEYCODE_F18; - _sapp.keycodes[0x50] = SAPP_KEYCODE_F19; - _sapp.keycodes[0x5A] = SAPP_KEYCODE_F20; - _sapp.keycodes[0x73] = SAPP_KEYCODE_HOME; - _sapp.keycodes[0x72] = SAPP_KEYCODE_INSERT; - _sapp.keycodes[0x7B] = SAPP_KEYCODE_LEFT; - _sapp.keycodes[0x3A] = SAPP_KEYCODE_LEFT_ALT; - _sapp.keycodes[0x3B] = SAPP_KEYCODE_LEFT_CONTROL; - _sapp.keycodes[0x38] = SAPP_KEYCODE_LEFT_SHIFT; - _sapp.keycodes[0x37] = SAPP_KEYCODE_LEFT_SUPER; - _sapp.keycodes[0x6E] = SAPP_KEYCODE_MENU; - _sapp.keycodes[0x47] = SAPP_KEYCODE_NUM_LOCK; - _sapp.keycodes[0x79] = SAPP_KEYCODE_PAGE_DOWN; - _sapp.keycodes[0x74] = SAPP_KEYCODE_PAGE_UP; - _sapp.keycodes[0x7C] = SAPP_KEYCODE_RIGHT; - _sapp.keycodes[0x3D] = SAPP_KEYCODE_RIGHT_ALT; - _sapp.keycodes[0x3E] = SAPP_KEYCODE_RIGHT_CONTROL; - _sapp.keycodes[0x3C] = SAPP_KEYCODE_RIGHT_SHIFT; - _sapp.keycodes[0x36] = SAPP_KEYCODE_RIGHT_SUPER; - _sapp.keycodes[0x31] = SAPP_KEYCODE_SPACE; - _sapp.keycodes[0x30] = SAPP_KEYCODE_TAB; - _sapp.keycodes[0x7E] = SAPP_KEYCODE_UP; - _sapp.keycodes[0x52] = SAPP_KEYCODE_KP_0; - _sapp.keycodes[0x53] = SAPP_KEYCODE_KP_1; - _sapp.keycodes[0x54] = SAPP_KEYCODE_KP_2; - _sapp.keycodes[0x55] = SAPP_KEYCODE_KP_3; - _sapp.keycodes[0x56] = SAPP_KEYCODE_KP_4; - _sapp.keycodes[0x57] = SAPP_KEYCODE_KP_5; - _sapp.keycodes[0x58] = SAPP_KEYCODE_KP_6; - _sapp.keycodes[0x59] = SAPP_KEYCODE_KP_7; - _sapp.keycodes[0x5B] = SAPP_KEYCODE_KP_8; - _sapp.keycodes[0x5C] = SAPP_KEYCODE_KP_9; - _sapp.keycodes[0x45] = SAPP_KEYCODE_KP_ADD; - _sapp.keycodes[0x41] = SAPP_KEYCODE_KP_DECIMAL; - _sapp.keycodes[0x4B] = SAPP_KEYCODE_KP_DIVIDE; - _sapp.keycodes[0x4C] = SAPP_KEYCODE_KP_ENTER; - _sapp.keycodes[0x51] = SAPP_KEYCODE_KP_EQUAL; - _sapp.keycodes[0x43] = SAPP_KEYCODE_KP_MULTIPLY; - _sapp.keycodes[0x4E] = SAPP_KEYCODE_KP_SUBTRACT; -} - -_SOKOL_PRIVATE void _sapp_run(const sapp_desc* desc) { - //puts("Sokol run()"); - _sapp_init_state(desc); - _sapp_macos_init_keytable(); - [NSApplication sharedApplication]; - NSApp.activationPolicy = NSApplicationActivationPolicyRegular; - _sapp_macos_app_dlg_obj = [[_sapp_macos_app_delegate alloc] init]; - NSApp.delegate = _sapp_macos_app_dlg_obj; - [NSApp activateIgnoringOtherApps:YES]; - [NSApp run]; - _sapp_discard_state(); -} - -/* MacOS entry function */ -#if !defined(SOKOL_NO_ENTRY) -int main(int argc, char* argv[]) { - sapp_desc desc = sokol_main(argc, argv); - _sapp_run(&desc); - return 0; -} -#endif /* SOKOL_NO_ENTRY */ - -_SOKOL_PRIVATE void _sapp_macos_update_dimensions(void) { +typedef struct { + uint32_t flags_changed_store; + NSWindow* window; + NSTrackingArea* tracking_area; + _sapp_macos_app_delegate* app_dlg; + _sapp_macos_window_delegate* win_dlg; + _sapp_macos_view* view; #if defined(SOKOL_METAL) - const CGSize fb_size = [_sapp_view_obj drawableSize]; - _sapp.framebuffer_width = fb_size.width; - _sapp.framebuffer_height = fb_size.height; - #elif defined(SOKOL_GLCORE33) - const NSRect fb_rect = [_sapp_view_obj convertRectToBacking:[_sapp_view_obj frame]]; - _sapp.framebuffer_width = fb_rect.size.width; - _sapp.framebuffer_height = fb_rect.size.height; + id mtl_device; #endif - const NSRect bounds = [_sapp_view_obj bounds]; - _sapp.window_width = bounds.size.width; - _sapp.window_height = bounds.size.height; - SOKOL_ASSERT((_sapp.framebuffer_width > 0) && (_sapp.framebuffer_height > 0)); - _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float)_sapp.window_width; -} +} _sapp_macos_t; -_SOKOL_PRIVATE void _sapp_macos_frame(void) { - const NSPoint mouse_pos = [_sapp_macos_window_obj mouseLocationOutsideOfEventStream]; - _sapp.mouse_x = mouse_pos.x * _sapp.dpi_scale; - _sapp.mouse_y = _sapp.framebuffer_height - (mouse_pos.y * _sapp.dpi_scale) - 1; - _sapp_frame(); - if (_sapp.quit_requested || _sapp.quit_ordered) { - [_sapp_macos_window_obj performClose:nil]; - } -} +#endif // _SAPP_MACOS -@implementation _sapp_macos_app_delegate -- (void)applicationDidFinishLaunching:(NSNotification*)aNotification { - if (_sapp.desc.fullscreen) { - NSRect screen_rect = NSScreen.mainScreen.frame; - _sapp.window_width = screen_rect.size.width; - _sapp.window_height = screen_rect.size.height; - if (_sapp.desc.high_dpi) { - _sapp.framebuffer_width = 2 * _sapp.window_width; - _sapp.framebuffer_height = 2 * _sapp.window_height; - } - else { - _sapp.framebuffer_width = _sapp.window_width; - _sapp.framebuffer_height = _sapp.window_height; - } - _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float) _sapp.window_width; - } - NSUInteger style = NSWindowStyleMaskBorderless; - if (!_sapp.desc.fullscreen) { - style = - NSWindowStyleMaskTitled | - NSWindowStyleMaskClosable | - NSWindowStyleMaskMiniaturizable | - NSWindowStyleMaskResizable; - } - - NSRect window_rect = NSMakeRect(0, 0, _sapp.window_width, _sapp.window_height); - _sapp_macos_window_obj = [[MyWindow alloc] - initWithContentRect:window_rect - styleMask:style - backing:NSBackingStoreBuffered - defer:NO]; - _sapp_macos_window_obj.title = [NSString stringWithUTF8String:_sapp.window_title]; - _sapp_macos_window_obj.acceptsMouseMovedEvents = YES; - _sapp_macos_window_obj.restorable = YES; - _sapp_macos_win_dlg_obj = [[_sapp_macos_window_delegate alloc] init]; - _sapp_macos_window_obj.delegate = _sapp_macos_win_dlg_obj; - #if defined(SOKOL_METAL) - _sapp_mtl_device_obj = MTLCreateSystemDefaultDevice(); - _sapp_macos_mtk_view_dlg_obj = [[_sapp_macos_mtk_view_dlg alloc] init]; - _sapp_view_obj = [[_sapp_macos_view alloc] init]; - [_sapp_view_obj updateTrackingAreas]; - _sapp_view_obj.preferredFramesPerSecond = 60 / _sapp.swap_interval; - _sapp_view_obj.delegate = _sapp_macos_mtk_view_dlg_obj; - _sapp_view_obj.device = _sapp_mtl_device_obj; - _sapp_view_obj.colorPixelFormat = MTLPixelFormatBGRA8Unorm; - _sapp_view_obj.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8; - _sapp_view_obj.sampleCount = _sapp.sample_count; - _sapp_macos_window_obj.contentView = _sapp_view_obj; - [_sapp_macos_window_obj makeFirstResponder:_sapp_view_obj]; - if (!_sapp.desc.high_dpi) { - CGSize drawable_size = { (CGFloat) _sapp.framebuffer_width, (CGFloat) _sapp.framebuffer_height }; - _sapp_view_obj.drawableSize = drawable_size; - } - _sapp_macos_update_dimensions(); - _sapp_view_obj.layer.magnificationFilter = kCAFilterNearest; - #elif defined(SOKOL_GLCORE33) - NSOpenGLPixelFormatAttribute attrs[32]; - int i = 0; - attrs[i++] = NSOpenGLPFAAccelerated; - attrs[i++] = NSOpenGLPFADoubleBuffer; - attrs[i++] = NSOpenGLPFAOpenGLProfile; attrs[i++] = NSOpenGLProfileVersion3_2Core; - attrs[i++] = NSOpenGLPFAColorSize; attrs[i++] = 24; - attrs[i++] = NSOpenGLPFAAlphaSize; attrs[i++] = 8; - attrs[i++] = NSOpenGLPFADepthSize; attrs[i++] = 24; - attrs[i++] = NSOpenGLPFAStencilSize; attrs[i++] = 8; - if (_sapp.sample_count > 1) { - attrs[i++] = NSOpenGLPFAMultisample; - attrs[i++] = NSOpenGLPFASampleBuffers; attrs[i++] = 1; - attrs[i++] = NSOpenGLPFASamples; attrs[i++] = _sapp.sample_count; - } - else { - attrs[i++] = NSOpenGLPFASampleBuffers; attrs[i++] = 0; - } - attrs[i++] = 0; - _sapp_macos_glpixelformat_obj = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs]; - SOKOL_ASSERT(_sapp_macos_glpixelformat_obj != nil); - - _sapp_view_obj = [[_sapp_macos_view alloc] - initWithFrame:window_rect - pixelFormat:_sapp_macos_glpixelformat_obj]; - [_sapp_view_obj updateTrackingAreas]; - if (_sapp.desc.high_dpi) { - [_sapp_view_obj setWantsBestResolutionOpenGLSurface:YES]; - } - else { - [_sapp_view_obj setWantsBestResolutionOpenGLSurface:NO]; - } - - _sapp_macos_window_obj.contentView = _sapp_view_obj; - [_sapp_macos_window_obj makeFirstResponder:_sapp_view_obj]; - - _sapp_macos_timer_obj = [NSTimer timerWithTimeInterval:0.001 - target:_sapp_view_obj - selector:@selector(timerFired:) - userInfo:nil - repeats:YES]; - [[NSRunLoop currentRunLoop] addTimer:_sapp_macos_timer_obj forMode:NSDefaultRunLoopMode]; - #endif - _sapp.valid = true; - if (_sapp.desc.fullscreen && false) { - /* on GL, this already toggles a rendered frame, so set the valid flag before */ - [_sapp_macos_window_obj toggleFullScreen:self]; - } - else { - [_sapp_macos_window_obj center]; - } - [_sapp_macos_window_obj makeKeyAndOrderFront:nil]; -} - -- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)sender { - return YES; -} -@end - -_SOKOL_PRIVATE uint32_t _sapp_macos_mod(NSEventModifierFlags f) { - uint32_t m = 0; - if (f & NSEventModifierFlagShift) { - m |= SAPP_MODIFIER_SHIFT; - } - if (f & NSEventModifierFlagControl) { - m |= SAPP_MODIFIER_CTRL; - } - if (f & NSEventModifierFlagOption) { - m |= SAPP_MODIFIER_ALT; - } - if (f & NSEventModifierFlagCommand) { - m |= SAPP_MODIFIER_SUPER; - } - return m; -} - -_SOKOL_PRIVATE void _sapp_macos_mouse_event(sapp_event_type type, sapp_mousebutton btn, uint32_t mod) { - if (_sapp_events_enabled()) { - _sapp_init_event(type); - _sapp.event.mouse_button = btn; - _sapp.event.modifiers = mod; - _sapp.event.mouse_x = _sapp.mouse_x; - _sapp.event.mouse_y = _sapp.mouse_y; - _sapp_call_event(&_sapp.event); - } -} - -_SOKOL_PRIVATE void _sapp_macos_key_event(sapp_event_type type, sapp_keycode key, bool repeat, uint32_t mod) { - if (_sapp_events_enabled()) { - _sapp_init_event(type); - _sapp.event.key_code = key; - _sapp.event.key_repeat = repeat; - _sapp.event.modifiers = mod; - _sapp_call_event(&_sapp.event); - } -} - -_SOKOL_PRIVATE void _sapp_macos_app_event(sapp_event_type type) { - if (_sapp_events_enabled()) { - _sapp_init_event(type); - _sapp_call_event(&_sapp.event); - } -} - -@implementation _sapp_macos_window_delegate -- (BOOL)windowShouldClose:(id)sender { - /* only give user-code a chance to intervene when sapp_quit() wasn't already called */ - if (!_sapp.quit_ordered) { - /* if window should be closed and event handling is enabled, give user code - a chance to intervene via sapp_cancel_quit() - */ - _sapp.quit_requested = true; - _sapp_macos_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED); - /* user code hasn't intervened, quit the app */ - if (_sapp.quit_requested) { - _sapp.quit_ordered = true; - } - } - if (_sapp.quit_ordered) { - _sapp_call_cleanup(); - return YES; - } - else { - return NO; - } -} - -- (void)windowDidResize:(NSNotification*)notification { - _sapp_macos_update_dimensions(); - _sapp_macos_app_event(SAPP_EVENTTYPE_RESIZED); -} - -- (void)windowDidMiniaturize:(NSNotification*)notification { - _sapp_macos_app_event(SAPP_EVENTTYPE_ICONIFIED); -} - -- (void)windowDidDeminiaturize:(NSNotification*)notification { - _sapp_macos_app_event(SAPP_EVENTTYPE_RESTORED); -} -@end - -#if defined(SOKOL_METAL) -@implementation _sapp_macos_mtk_view_dlg -- (void)drawInMTKView:(MTKView*)view { - @autoreleasepool { - _sapp_macos_frame(); - } -} -- (void)mtkView:(MTKView*)view drawableSizeWillChange:(CGSize)size { - /* this is required by the protocol, but we can't do anything useful here */ -} -@end -#endif - -@implementation _sapp_macos_view -#if defined(SOKOL_GLCORE33) -- (void)timerFired:(id)sender { - [self setNeedsDisplay:YES]; -} -- (void)prepareOpenGL { - [super prepareOpenGL]; - GLint swapInt = 1; - NSOpenGLContext* ctx = [_sapp_view_obj openGLContext]; - [ctx setValues:&swapInt forParameter:NSOpenGLContextParameterSwapInterval]; - [ctx makeCurrentContext]; -} -- (void)drawRect:(NSRect)bound { - _sapp_macos_frame(); - [[_sapp_view_obj openGLContext] flushBuffer]; -} -#endif - -- (BOOL)isOpaque { - return YES; -} -- (BOOL)canBecomeKey { - return YES; -} -- (BOOL)acceptsFirstResponder { - return YES; -} - -- (void)updateTrackingAreas { - if (trackingArea != nil) { - [self removeTrackingArea:trackingArea]; - trackingArea = nil; - } - const NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | - NSTrackingActiveInKeyWindow | - NSTrackingEnabledDuringMouseDrag | - NSTrackingCursorUpdate | - NSTrackingInVisibleRect | - NSTrackingAssumeInside; - trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] options:options owner:self userInfo:nil]; - [self addTrackingArea:trackingArea]; - [super updateTrackingAreas]; -} -- (void)mouseEntered:(NSEvent*)event { - _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mod(event.modifierFlags)); -} -- (void)mouseExited:(NSEvent*)event { - _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mod(event.modifierFlags)); -} -- (void)mouseDown:(NSEvent*)event { - _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_LEFT, _sapp_macos_mod(event.modifierFlags)); -} -- (void)mouseUp:(NSEvent*)event { - _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_LEFT, _sapp_macos_mod(event.modifierFlags)); -} -- (void)rightMouseDown:(NSEvent*)event { - _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_RIGHT, _sapp_macos_mod(event.modifierFlags)); -} -- (void)rightMouseUp:(NSEvent*)event { - _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_RIGHT, _sapp_macos_mod(event.modifierFlags)); -} -- (void)mouseMoved:(NSEvent*)event { - _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID , _sapp_macos_mod(event.modifierFlags)); -} -- (void)mouseDragged:(NSEvent*)event { - _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID , _sapp_macos_mod(event.modifierFlags)); -} -- (void)rightMouseDragged:(NSEvent*)event { - _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mod(event.modifierFlags)); -} -- (void)scrollWheel:(NSEvent*)event { - if (_sapp_events_enabled()) { - float dx = (float) event.scrollingDeltaX; - float dy = (float) event.scrollingDeltaY; - if (event.hasPreciseScrollingDeltas) { - dx *= 0.1; - dy *= 0.1; - } - if ((_sapp_absf(dx) > 0.0f) || (_sapp_absf(dy) > 0.0f)) { - _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); - _sapp.event.modifiers = _sapp_macos_mod(event.modifierFlags); - _sapp.event.mouse_x = _sapp.mouse_x; - _sapp.event.mouse_y = _sapp.mouse_y; - _sapp.event.scroll_x = dx; - _sapp.event.scroll_y = dy; - _sapp_call_event(&_sapp.event); - } - } -} -- (void)keyDown:(NSEvent*)event { - if (_sapp_events_enabled()) { - const uint32_t mods = _sapp_macos_mod(event.modifierFlags); - /* NOTE: macOS doesn't send keyUp events while the Cmd key is pressed, - as a workaround, to prevent key presses from sticking we'll send - a keyup event following right after the keydown if SUPER is also pressed - */ - const sapp_keycode key_code = _sapp_translate_key(event.keyCode); - _sapp_macos_key_event(SAPP_EVENTTYPE_KEY_DOWN, key_code, event.isARepeat, mods); - if (0 != (mods & SAPP_MODIFIER_SUPER)) { - _sapp_macos_key_event(SAPP_EVENTTYPE_KEY_UP, key_code, event.isARepeat, mods); - } - const NSString* chars = event.characters; - const NSUInteger len = chars.length; - if (len > 0) { - _sapp_init_event(SAPP_EVENTTYPE_CHAR); - _sapp.event.modifiers = mods; - for (NSUInteger i = 0; i < len; i++) { - const unichar codepoint = [chars characterAtIndex:i]; - if ((codepoint & 0xFF00) == 0xF700) { - continue; - } - _sapp.event.char_code = codepoint; - _sapp.event.key_repeat = event.isARepeat; - _sapp_call_event(&_sapp.event); - } - } - /* if this is a Cmd+V (paste), also send a CLIPBOARD_PASTE event */ - if (_sapp.clipboard_enabled && (mods == SAPP_MODIFIER_SUPER) && (key_code == SAPP_KEYCODE_V)) { - _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED); - _sapp_call_event(&_sapp.event); - } - } -} -- (void)keyUp:(NSEvent*)event { - _sapp_macos_key_event(SAPP_EVENTTYPE_KEY_UP, - _sapp_translate_key(event.keyCode), - event.isARepeat, - _sapp_macos_mod(event.modifierFlags)); -} -- (void)flagsChanged:(NSEvent*)event { - const uint32_t old_f = _sapp_macos_flags_changed_store; - const uint32_t new_f = event.modifierFlags; - _sapp_macos_flags_changed_store = new_f; - sapp_keycode key_code = SAPP_KEYCODE_INVALID; - bool down = false; - if ((new_f ^ old_f) & NSEventModifierFlagShift) { - key_code = SAPP_KEYCODE_LEFT_SHIFT; - down = 0 != (new_f & NSEventModifierFlagShift); - } - if ((new_f ^ old_f) & NSEventModifierFlagControl) { - key_code = SAPP_KEYCODE_LEFT_CONTROL; - down = 0 != (new_f & NSEventModifierFlagControl); - } - if ((new_f ^ old_f) & NSEventModifierFlagOption) { - key_code = SAPP_KEYCODE_LEFT_ALT; - down = 0 != (new_f & NSEventModifierFlagOption); - } - if ((new_f ^ old_f) & NSEventModifierFlagCommand) { - key_code = SAPP_KEYCODE_LEFT_SUPER; - down = 0 != (new_f & NSEventModifierFlagCommand); - } - if (key_code != SAPP_KEYCODE_INVALID) { - _sapp_macos_key_event(down ? SAPP_EVENTTYPE_KEY_DOWN : SAPP_EVENTTYPE_KEY_UP, - key_code, - false, - _sapp_macos_mod(event.modifierFlags)); - } -} -- (void)cursorUpdate:(NSEvent*)event { - if (_sapp.desc.user_cursor) { - _sapp_macos_app_event(SAPP_EVENTTYPE_UPDATE_CURSOR); - } -} -@end - -void _sapp_macos_set_clipboard_string(const char* str) { - @autoreleasepool { - NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; - [pasteboard declareTypes:@[NSPasteboardTypeString] owner:nil]; - [pasteboard setString:@(str) forType:NSPasteboardTypeString]; - } -} - -const char* _sapp_macos_get_clipboard_string(void) { - SOKOL_ASSERT(_sapp.clipboard); - @autoreleasepool { - _sapp.clipboard[0] = 0; - NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; - if (![[pasteboard types] containsObject:NSPasteboardTypeString]) { - return _sapp.clipboard; - } - NSString* str = [pasteboard stringForType:NSPasteboardTypeString]; - if (!str) { - return _sapp.clipboard; - } - _sapp_strcpy([str UTF8String], _sapp.clipboard, _sapp.clipboard_size); - } - return _sapp.clipboard; -} - -#endif /* MacOS */ - -/*== iOS =====================================================================*/ -#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE -#import -#if defined(SOKOL_METAL) -#import -#import -#else -#import -#include -#include -#endif +/*== IOS DECLARATIONS ========================================================*/ +#if defined(_SAPP_IOS) @interface _sapp_app_delegate : NSObject @end @@ -1816,1181 +1339,60 @@ const char* _sapp_macos_get_clipboard_string(void) { - (void)keyboardDidChangeFrame:(NSNotification*)notif; @end #if defined(SOKOL_METAL) -@interface _sapp_ios_mtk_view_dlg : NSObject -@end -@interface _sapp_ios_view : MTKView; -@end + @interface _sapp_ios_view : MTKView; + @end #else -@interface _sapp_ios_glk_view_dlg : NSObject -@end -@interface _sapp_ios_view : GLKView -@end + @interface _sapp_ios_view : GLKView + @end #endif -static bool _sapp_ios_suspended; -static UIWindow* _sapp_ios_window_obj; -static _sapp_ios_view* _sapp_view_obj; -static UITextField* _sapp_ios_textfield_obj; -static _sapp_textfield_dlg* _sapp_ios_textfield_dlg_obj; -#if defined(SOKOL_METAL) -static _sapp_ios_mtk_view_dlg* _sapp_ios_mtk_view_dlg_obj; -static UIViewController* _sapp_ios_view_ctrl_obj; -static id _sapp_mtl_device_obj; -#else -static EAGLContext* _sapp_ios_eagl_ctx_obj; -static _sapp_ios_glk_view_dlg* _sapp_ios_glk_view_dlg_obj; -static GLKViewController* _sapp_ios_view_ctrl_obj; -#endif - -_SOKOL_PRIVATE void _sapp_run(const sapp_desc* desc) { - _sapp_init_state(desc); - static int argc = 1; - static char* argv[] = { (char*)"sokol_app" }; - UIApplicationMain(argc, argv, nil, NSStringFromClass([_sapp_app_delegate class])); - _sapp_discard_state(); -} - -/* iOS entry function */ -#if !defined(SOKOL_NO_ENTRY) -int main(int argc, char* argv[]) { - sapp_desc desc = sokol_main(argc, argv); - _sapp_run(&desc); - return 0; -} -#endif /* SOKOL_NO_ENTRY */ - -_SOKOL_PRIVATE void _sapp_ios_app_event(sapp_event_type type) { - if (_sapp_events_enabled()) { - _sapp_init_event(type); - _sapp_call_event(&_sapp.event); - } -} - -_SOKOL_PRIVATE void _sapp_ios_update_dimensions(void) { - CGRect screen_rect = UIScreen.mainScreen.bounds; - _sapp.window_width = (int) screen_rect.size.width; - _sapp.window_height = (int) screen_rect.size.height; - int cur_fb_width, cur_fb_height; - #if defined(SOKOL_METAL) - const CGSize fb_size = _sapp_view_obj.drawableSize; - cur_fb_width = (int) fb_size.width; - cur_fb_height = (int) fb_size.height; - #else - cur_fb_width = (int) _sapp_view_obj.drawableWidth; - cur_fb_height = (int) _sapp_view_obj.drawableHeight; - #endif - const bool dim_changed = - (_sapp.framebuffer_width != cur_fb_width) || - (_sapp.framebuffer_height != cur_fb_height); - _sapp.framebuffer_width = cur_fb_width; - _sapp.framebuffer_height = cur_fb_height; - SOKOL_ASSERT((_sapp.framebuffer_width > 0) && (_sapp.framebuffer_height > 0)); - _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float) _sapp.window_width; - if (dim_changed) { - _sapp_ios_app_event(SAPP_EVENTTYPE_RESIZED); - } -} - -_SOKOL_PRIVATE void _sapp_ios_frame(void) { - _sapp_ios_update_dimensions(); - _sapp_frame(); -} - -_SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) { - /* if not happened yet, create an invisible text field */ - if (nil == _sapp_ios_textfield_obj) { - _sapp_ios_textfield_dlg_obj = [[_sapp_textfield_dlg alloc] init]; - _sapp_ios_textfield_obj = [[UITextField alloc] initWithFrame:CGRectMake(10, 10, 100, 50)]; - _sapp_ios_textfield_obj.keyboardType = UIKeyboardTypeDefault; - _sapp_ios_textfield_obj.returnKeyType = UIReturnKeyDefault; - _sapp_ios_textfield_obj.autocapitalizationType = UITextAutocapitalizationTypeNone; - _sapp_ios_textfield_obj.autocorrectionType = UITextAutocorrectionTypeNo; - _sapp_ios_textfield_obj.spellCheckingType = UITextSpellCheckingTypeNo; - _sapp_ios_textfield_obj.hidden = YES; - _sapp_ios_textfield_obj.text = @"x"; - _sapp_ios_textfield_obj.delegate = _sapp_ios_textfield_dlg_obj; - [_sapp_ios_view_ctrl_obj.view addSubview:_sapp_ios_textfield_obj]; - - [[NSNotificationCenter defaultCenter] addObserver:_sapp_ios_textfield_dlg_obj - selector:@selector(keyboardWasShown:) - name:UIKeyboardDidShowNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:_sapp_ios_textfield_dlg_obj - selector:@selector(keyboardWillBeHidden:) - name:UIKeyboardWillHideNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:_sapp_ios_textfield_dlg_obj - selector:@selector(keyboardDidChangeFrame:) - name:UIKeyboardDidChangeFrameNotification object:nil]; - } - if (shown) { - /* setting the text field as first responder brings up the onscreen keyboard */ - [_sapp_ios_textfield_obj becomeFirstResponder]; - } - else { - [_sapp_ios_textfield_obj resignFirstResponder]; - } -} - -@implementation _sapp_app_delegate -- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { - CGRect screen_rect = UIScreen.mainScreen.bounds; - _sapp_ios_window_obj = [[UIWindow alloc] initWithFrame:screen_rect]; - _sapp.window_width = screen_rect.size.width; - _sapp.window_height = screen_rect.size.height; - if (_sapp.desc.high_dpi) { - _sapp.framebuffer_width = 2 * _sapp.window_width; - _sapp.framebuffer_height = 2 * _sapp.window_height; - } - else { - _sapp.framebuffer_width = _sapp.window_width; - _sapp.framebuffer_height = _sapp.window_height; - } - _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float) _sapp.window_width; - #if defined(SOKOL_METAL) - _sapp_mtl_device_obj = MTLCreateSystemDefaultDevice(); - _sapp_ios_mtk_view_dlg_obj = [[_sapp_ios_mtk_view_dlg alloc] init]; - _sapp_view_obj = [[_sapp_ios_view alloc] init]; - _sapp_view_obj.preferredFramesPerSecond = 60 / _sapp.swap_interval; - _sapp_view_obj.delegate = _sapp_ios_mtk_view_dlg_obj; - _sapp_view_obj.device = _sapp_mtl_device_obj; - _sapp_view_obj.colorPixelFormat = MTLPixelFormatBGRA8Unorm; - _sapp_view_obj.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8; - _sapp_view_obj.sampleCount = _sapp.sample_count; - if (_sapp.desc.high_dpi) { - _sapp_view_obj.contentScaleFactor = 2.0; - } - else { - _sapp_view_obj.contentScaleFactor = 1.0; - } - _sapp_view_obj.userInteractionEnabled = YES; - _sapp_view_obj.multipleTouchEnabled = YES; - [_sapp_ios_window_obj addSubview:_sapp_view_obj]; - _sapp_ios_view_ctrl_obj = [[UIViewController alloc] init]; - _sapp_ios_view_ctrl_obj.view = _sapp_view_obj; - _sapp_ios_window_obj.rootViewController = _sapp_ios_view_ctrl_obj; - #else - if (_sapp.desc.gl_force_gles2) { - _sapp_ios_eagl_ctx_obj = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; - _sapp.gles2_fallback = true; - } - else { - _sapp_ios_eagl_ctx_obj = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; - if (_sapp_ios_eagl_ctx_obj == nil) { - _sapp_ios_eagl_ctx_obj = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; - _sapp.gles2_fallback = true; - } - } - _sapp_ios_glk_view_dlg_obj = [[_sapp_ios_glk_view_dlg alloc] init]; - _sapp_view_obj = [[_sapp_ios_view alloc] initWithFrame:screen_rect]; - _sapp_view_obj.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888; - _sapp_view_obj.drawableDepthFormat = GLKViewDrawableDepthFormat24; - _sapp_view_obj.drawableStencilFormat = GLKViewDrawableStencilFormatNone; - _sapp_view_obj.drawableMultisample = GLKViewDrawableMultisampleNone; /* FIXME */ - _sapp_view_obj.context = _sapp_ios_eagl_ctx_obj; - _sapp_view_obj.delegate = _sapp_ios_glk_view_dlg_obj; - _sapp_view_obj.enableSetNeedsDisplay = NO; - _sapp_view_obj.userInteractionEnabled = YES; - _sapp_view_obj.multipleTouchEnabled = YES; - if (_sapp.desc.high_dpi) { - _sapp_view_obj.contentScaleFactor = 2.0; - } - else { - _sapp_view_obj.contentScaleFactor = 1.0; - } - [_sapp_ios_window_obj addSubview:_sapp_view_obj]; - _sapp_ios_view_ctrl_obj = [[GLKViewController alloc] init]; - _sapp_ios_view_ctrl_obj.view = _sapp_view_obj; - _sapp_ios_view_ctrl_obj.preferredFramesPerSecond = 60 / _sapp.swap_interval; - _sapp_ios_window_obj.rootViewController = _sapp_ios_view_ctrl_obj; - #endif - [_sapp_ios_window_obj makeKeyAndVisible]; - - _sapp.valid = true; - return YES; -} - -- (void)applicationWillResignActive:(UIApplication *)application { - if (!_sapp_ios_suspended) { - _sapp_ios_suspended = true; - _sapp_ios_app_event(SAPP_EVENTTYPE_SUSPENDED); - } -} - -- (void)applicationDidBecomeActive:(UIApplication *)application { - if (_sapp_ios_suspended) { - _sapp_ios_suspended = false; - _sapp_ios_app_event(SAPP_EVENTTYPE_RESUMED); - } -} -@end - -@implementation _sapp_textfield_dlg -- (void)keyboardWasShown:(NSNotification*)notif { - _sapp.onscreen_keyboard_shown = true; - /* query the keyboard's size, and modify the content view's size */ - if (_sapp.desc.ios_keyboard_resizes_canvas) { - NSDictionary* info = notif.userInfo; - CGFloat kbd_h = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height; - CGRect view_frame = UIScreen.mainScreen.bounds; - view_frame.size.height -= kbd_h; - _sapp_view_obj.frame = view_frame; - } -} -- (void)keyboardWillBeHidden:(NSNotification*)notif { - _sapp.onscreen_keyboard_shown = false; - if (_sapp.desc.ios_keyboard_resizes_canvas) { - _sapp_view_obj.frame = UIScreen.mainScreen.bounds; - } -} -- (void)keyboardDidChangeFrame:(NSNotification*)notif { - /* this is for the case when the screen rotation changes while the keyboard is open */ - if (_sapp.onscreen_keyboard_shown && _sapp.desc.ios_keyboard_resizes_canvas) { - NSDictionary* info = notif.userInfo; - CGFloat kbd_h = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height; - CGRect view_frame = UIScreen.mainScreen.bounds; - view_frame.size.height -= kbd_h; - _sapp_view_obj.frame = view_frame; - } -} -- (BOOL)textField:(UITextField*)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString*)string { - if (_sapp_events_enabled()) { - const NSUInteger len = string.length; - if (len > 0) { - for (NSUInteger i = 0; i < len; i++) { - unichar c = [string characterAtIndex:i]; - if (c >= 32) { - /* ignore surrogates for now */ - if ((c < 0xD800) || (c > 0xDFFF)) { - _sapp_init_event(SAPP_EVENTTYPE_CHAR); - _sapp.event.char_code = c; - _sapp_call_event(&_sapp.event); - } - } - if (c <= 32) { - sapp_keycode k = SAPP_KEYCODE_INVALID; - switch (c) { - case 10: k = SAPP_KEYCODE_ENTER; break; - case 32: k = SAPP_KEYCODE_SPACE; break; - default: break; - } - if (k != SAPP_KEYCODE_INVALID) { - _sapp_init_event(SAPP_EVENTTYPE_KEY_DOWN); - _sapp.event.key_code = k; - _sapp_call_event(&_sapp.event); - _sapp_init_event(SAPP_EVENTTYPE_KEY_UP); - _sapp.event.key_code = k; - _sapp_call_event(&_sapp.event); - } - } - } - } - else { - /* this was a backspace */ - _sapp_init_event(SAPP_EVENTTYPE_KEY_DOWN); - _sapp.event.key_code = SAPP_KEYCODE_BACKSPACE; - _sapp_call_event(&_sapp.event); - _sapp_init_event(SAPP_EVENTTYPE_KEY_UP); - _sapp.event.key_code = SAPP_KEYCODE_BACKSPACE; - _sapp_call_event(&_sapp.event); - } - } - return NO; -} -@end - -#if defined(SOKOL_METAL) -@implementation _sapp_ios_mtk_view_dlg -- (void)drawInMTKView:(MTKView*)view { - @autoreleasepool { - _sapp_ios_frame(); - } -} - -- (void)mtkView:(MTKView*)view drawableSizeWillChange:(CGSize)size { - /* this is required by the protocol, but we can't do anything useful here */ -} -@end -#else -@implementation _sapp_ios_glk_view_dlg -- (void)glkView:(GLKView*)view drawInRect:(CGRect)rect { - @autoreleasepool { - _sapp_ios_frame(); - } -} -@end -#endif - -_SOKOL_PRIVATE void _sapp_ios_touch_event(sapp_event_type type, NSSet* touches, UIEvent* event) { - if (_sapp_events_enabled()) { - _sapp_init_event(type); - NSEnumerator* enumerator = event.allTouches.objectEnumerator; - UITouch* ios_touch; - while ((ios_touch = [enumerator nextObject])) { - if ((_sapp.event.num_touches + 1) < SAPP_MAX_TOUCHPOINTS) { - CGPoint ios_pos = [ios_touch locationInView:_sapp_view_obj]; - sapp_touchpoint* cur_point = &_sapp.event.touches[_sapp.event.num_touches++]; - cur_point->identifier = (uintptr_t) ios_touch; - cur_point->pos_x = ios_pos.x * _sapp.dpi_scale; - cur_point->pos_y = ios_pos.y * _sapp.dpi_scale; - cur_point->changed = [touches containsObject:ios_touch]; - } - } - if (_sapp.event.num_touches > 0) { - _sapp_call_event(&_sapp.event); - } - } -} - -@implementation _sapp_ios_view -- (BOOL) isOpaque { - return YES; -} -- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent*)event { - _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_BEGAN, touches, event); -} -- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent*)event { - _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_MOVED, touches, event); -} -- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent*)event { - _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_ENDED, touches, event); -} -- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent*)event { - _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_CANCELLED, touches, event); -} -@end -#endif /* TARGET_OS_IPHONE */ - -#endif /* __APPLE__ */ - -/*== EMSCRIPTEN ==============================================================*/ -#if defined(__EMSCRIPTEN__) -#if defined(SOKOL_GLES3) -#include -#else -#ifndef GL_EXT_PROTOTYPES -#define GL_GLEXT_PROTOTYPES -#endif -#include -#include -#endif -#include -#include - -static bool _sapp_emsc_input_created; -static bool _sapp_emsc_wants_show_keyboard; -static bool _sapp_emsc_wants_hide_keyboard; - -/* this function is called from a JS event handler when the user hides - the onscreen keyboard pressing the 'dismiss keyboard key' -*/ -#ifdef __cplusplus -extern "C" { -#endif -EMSCRIPTEN_KEEPALIVE void _sapp_emsc_notify_keyboard_hidden(void) { - _sapp.onscreen_keyboard_shown = false; -} -#ifdef __cplusplus -} /* extern "C" */ -#endif - -/* Javascript helper functions for mobile virtual keyboard input */ -EM_JS(void, sapp_js_create_textfield, (void), { - var _sapp_inp = document.createElement("input"); - _sapp_inp.type = "text"; - _sapp_inp.id = "_sokol_app_input_element"; - _sapp_inp.autocapitalize = "none"; - _sapp_inp.addEventListener("focusout", function(_sapp_event) { - __sapp_emsc_notify_keyboard_hidden() - - }); - document.body.append(_sapp_inp); -}); - -EM_JS(void, sapp_js_focus_textfield, (void), { - document.getElementById("_sokol_app_input_element").focus(); -}); - -EM_JS(void, sapp_js_unfocus_textfield, (void), { - document.getElementById("_sokol_app_input_element").blur(); -}); - -EMSCRIPTEN_KEEPALIVE void _sapp_emsc_onpaste(const char* str) { - if (_sapp.clipboard_enabled) { - _sapp_strcpy(str, _sapp.clipboard, _sapp.clipboard_size); - if (_sapp_events_enabled()) { - _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED); - _sapp_call_event(&_sapp.event); - } - } -} - -/* https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeunload */ -EMSCRIPTEN_KEEPALIVE int _sapp_html5_get_ask_leave_site(void) { - return _sapp.html5_ask_leave_site ? 1 : 0; -} - -EM_JS(void, sapp_js_hook_beforeunload, (void), { - window.addEventListener('beforeunload', function(_sapp_event) { - if (__sapp_html5_get_ask_leave_site() != 0) { - _sapp_event.preventDefault(); - _sapp_event.returnValue = ' '; - } - }); -}); - -EM_JS(void, sapp_js_init_clipboard, (void), { - window.addEventListener('paste', function(event) { - var pasted_str = event.clipboardData.getData('text'); - ccall('_sapp_emsc_onpaste', 'void', ['string'], [pasted_str]); - }); -}); - -EM_JS(void, sapp_js_write_clipboard, (const char* c_str), { - var str = UTF8ToString(c_str); - var ta = document.createElement('textarea'); - ta.setAttribute('autocomplete', 'off'); - ta.setAttribute('autocorrect', 'off'); - ta.setAttribute('autocapitalize', 'off'); - ta.setAttribute('spellcheck', 'false'); - ta.style.left = -100 + 'px'; - ta.style.top = -100 + 'px'; - ta.style.height = 1; - ta.style.width = 1; - ta.value = str; - document.body.appendChild(ta); - ta.select(); - document.execCommand('copy'); - document.body.removeChild(ta); -}); - -_SOKOL_PRIVATE void _sapp_emsc_set_clipboard_string(const char* str) { - sapp_js_write_clipboard(str); -} - -/* called from the emscripten event handler to update the keyboard visibility - state, this must happen from an JS input event handler, otherwise - the request will be ignored by the browser -*/ -_SOKOL_PRIVATE void _sapp_emsc_update_keyboard_state(void) { - if (_sapp_emsc_wants_show_keyboard) { - /* create input text field on demand */ - if (!_sapp_emsc_input_created) { - _sapp_emsc_input_created = true; - sapp_js_create_textfield(); - } - /* focus the text input field, this will bring up the keyboard */ - _sapp.onscreen_keyboard_shown = true; - _sapp_emsc_wants_show_keyboard = false; - sapp_js_focus_textfield(); - } - if (_sapp_emsc_wants_hide_keyboard) { - /* unfocus the text input field */ - if (_sapp_emsc_input_created) { - _sapp.onscreen_keyboard_shown = false; - _sapp_emsc_wants_hide_keyboard = false; - sapp_js_unfocus_textfield(); - } - } -} - -/* actually showing the onscreen keyboard must be initiated from a JS - input event handler, so we'll just keep track of the desired - state, and the actual state change will happen with the next input event -*/ -_SOKOL_PRIVATE void _sapp_emsc_show_keyboard(bool show) { - if (show) { - _sapp_emsc_wants_show_keyboard = true; - } - else { - _sapp_emsc_wants_hide_keyboard = true; - } -} - -_SOKOL_PRIVATE EM_BOOL _sapp_emsc_size_changed(int event_type, const EmscriptenUiEvent* ui_event, void* user_data) { - double w, h; - emscripten_get_element_css_size(_sapp.html5_canvas_name, &w, &h); - /* The above method might report zero when toggling HTML5 fullscreen, - in that case use the window's inner width reported by the - emscripten event. This works ok when toggling *into* fullscreen - but doesn't properly restore the previous canvas size when switching - back from fullscreen. - - In general, due to the HTML5's fullscreen API's flaky nature it is - recommended to use 'soft fullscreen' (stretching the WebGL canvas - over the browser windows client rect) with a CSS definition like this: - - position: absolute; - top: 0px; - left: 0px; - margin: 0px; - border: 0; - width: 100%; - height: 100%; - overflow: hidden; - display: block; - */ - if (w < 1.0) { - w = ui_event->windowInnerWidth; - } - else { - _sapp.window_width = (int) w; - } - if (h < 1.0) { - h = ui_event->windowInnerHeight; - } - else { - _sapp.window_height = (int) h; - } - if (_sapp.desc.high_dpi) { - _sapp.dpi_scale = emscripten_get_device_pixel_ratio(); - } - _sapp.framebuffer_width = (int) (w * _sapp.dpi_scale); - _sapp.framebuffer_height = (int) (h * _sapp.dpi_scale); - SOKOL_ASSERT((_sapp.framebuffer_width > 0) && (_sapp.framebuffer_height > 0)); - emscripten_set_canvas_element_size(_sapp.html5_canvas_name, _sapp.framebuffer_width, _sapp.framebuffer_height); - if (_sapp_events_enabled()) { - _sapp_init_event(SAPP_EVENTTYPE_RESIZED); - _sapp_call_event(&_sapp.event); - } - return true; -} - -_SOKOL_PRIVATE EM_BOOL _sapp_emsc_frame(double time, void* userData) { - _SOKOL_UNUSED(time); - _SOKOL_UNUSED(userData); - _sapp_frame(); - return EM_TRUE; -} - -_SOKOL_PRIVATE EM_BOOL _sapp_emsc_context_cb(int emsc_type, const void* reserved, void* user_data) { - sapp_event_type type; - switch (emsc_type) { - case EMSCRIPTEN_EVENT_WEBGLCONTEXTLOST: type = SAPP_EVENTTYPE_SUSPENDED; break; - case EMSCRIPTEN_EVENT_WEBGLCONTEXTRESTORED: type = SAPP_EVENTTYPE_RESUMED; break; - default: type = SAPP_EVENTTYPE_INVALID; break; - } - if (_sapp_events_enabled() && (SAPP_EVENTTYPE_INVALID != type)) { - _sapp_init_event(type); - _sapp_call_event(&_sapp.event); - } - return true; -} - -_SOKOL_PRIVATE EM_BOOL _sapp_emsc_mouse_cb(int emsc_type, const EmscriptenMouseEvent* emsc_event, void* user_data) { - _sapp.mouse_x = (emsc_event->targetX * _sapp.dpi_scale); - _sapp.mouse_y = (emsc_event->targetY * _sapp.dpi_scale); - if (_sapp_events_enabled() && (emsc_event->button >= 0) && (emsc_event->button < SAPP_MAX_MOUSEBUTTONS)) { - sapp_event_type type; - bool is_button_event = false; - switch (emsc_type) { - case EMSCRIPTEN_EVENT_MOUSEDOWN: - type = SAPP_EVENTTYPE_MOUSE_DOWN; - is_button_event = true; - break; - case EMSCRIPTEN_EVENT_MOUSEUP: - type = SAPP_EVENTTYPE_MOUSE_UP; - is_button_event = true; - break; - case EMSCRIPTEN_EVENT_MOUSEMOVE: - type = SAPP_EVENTTYPE_MOUSE_MOVE; - break; - case EMSCRIPTEN_EVENT_MOUSEENTER: - type = SAPP_EVENTTYPE_MOUSE_ENTER; - break; - case EMSCRIPTEN_EVENT_MOUSELEAVE: - type = SAPP_EVENTTYPE_MOUSE_LEAVE; - break; - default: - type = SAPP_EVENTTYPE_INVALID; - break; - } - if (type != SAPP_EVENTTYPE_INVALID) { - _sapp_init_event(type); - if (emsc_event->ctrlKey) { - _sapp.event.modifiers |= SAPP_MODIFIER_CTRL; - } - if (emsc_event->shiftKey) { - _sapp.event.modifiers |= SAPP_MODIFIER_SHIFT; - } - if (emsc_event->altKey) { - _sapp.event.modifiers |= SAPP_MODIFIER_ALT; - } - if (emsc_event->metaKey) { - _sapp.event.modifiers |= SAPP_MODIFIER_SUPER; - } - if (is_button_event) { - switch (emsc_event->button) { - case 0: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_LEFT; break; - case 1: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_MIDDLE; break; - case 2: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_RIGHT; break; - default: _sapp.event.mouse_button = (sapp_mousebutton)emsc_event->button; break; - } - } - else { - _sapp.event.mouse_button = SAPP_MOUSEBUTTON_INVALID; - } - _sapp.event.mouse_x = _sapp.mouse_x; - _sapp.event.mouse_y = _sapp.mouse_y; - _sapp_call_event(&_sapp.event); - } - } - _sapp_emsc_update_keyboard_state(); - return true; -} - -_SOKOL_PRIVATE EM_BOOL _sapp_emsc_wheel_cb(int emsc_type, const EmscriptenWheelEvent* emsc_event, void* user_data) { - if (_sapp_events_enabled()) { - _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); - if (emsc_event->mouse.ctrlKey) { - _sapp.event.modifiers |= SAPP_MODIFIER_CTRL; - } - if (emsc_event->mouse.shiftKey) { - _sapp.event.modifiers |= SAPP_MODIFIER_SHIFT; - } - if (emsc_event->mouse.altKey) { - _sapp.event.modifiers |= SAPP_MODIFIER_ALT; - } - if (emsc_event->mouse.metaKey) { - _sapp.event.modifiers |= SAPP_MODIFIER_SUPER; - } - _sapp.event.scroll_x = -0.1 * (float)emsc_event->deltaX; - _sapp.event.scroll_y = -0.1 * (float)emsc_event->deltaY; - _sapp_call_event(&_sapp.event); - } - _sapp_emsc_update_keyboard_state(); - return true; -} - -_SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboardEvent* emsc_event, void* user_data) { - bool retval = true; - if (_sapp_events_enabled()) { - sapp_event_type type; - switch (emsc_type) { - case EMSCRIPTEN_EVENT_KEYDOWN: - type = SAPP_EVENTTYPE_KEY_DOWN; - break; - case EMSCRIPTEN_EVENT_KEYUP: - type = SAPP_EVENTTYPE_KEY_UP; - break; - case EMSCRIPTEN_EVENT_KEYPRESS: - type = SAPP_EVENTTYPE_CHAR; - break; - default: - type = SAPP_EVENTTYPE_INVALID; - break; - } - if (type != SAPP_EVENTTYPE_INVALID) { - bool send_keyup_followup = false; - _sapp_init_event(type); - _sapp.event.key_repeat = emsc_event->repeat; - if (emsc_event->ctrlKey) { - _sapp.event.modifiers |= SAPP_MODIFIER_CTRL; - } - if (emsc_event->shiftKey) { - _sapp.event.modifiers |= SAPP_MODIFIER_SHIFT; - } - if (emsc_event->altKey) { - _sapp.event.modifiers |= SAPP_MODIFIER_ALT; - } - if (emsc_event->metaKey) { - _sapp.event.modifiers |= SAPP_MODIFIER_SUPER; - } - if (type == SAPP_EVENTTYPE_CHAR) { - _sapp.event.char_code = emsc_event->charCode; - /* workaround to make Cmd+V work on Safari */ - if ((emsc_event->metaKey) && (emsc_event->charCode == 118)) { - retval = false; - } - } - else { - _sapp.event.key_code = _sapp_translate_key(emsc_event->keyCode); - /* 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) && - (_sapp.event.modifiers & SAPP_MODIFIER_SUPER)) - { - send_keyup_followup = true; - } - /* only forward a certain key ranges to the browser */ - switch (_sapp.event.key_code) { - case SAPP_KEYCODE_WORLD_1: - case SAPP_KEYCODE_WORLD_2: - case SAPP_KEYCODE_ESCAPE: - case SAPP_KEYCODE_ENTER: - case SAPP_KEYCODE_TAB: - case SAPP_KEYCODE_BACKSPACE: - case SAPP_KEYCODE_INSERT: - case SAPP_KEYCODE_DELETE: - case SAPP_KEYCODE_RIGHT: - case SAPP_KEYCODE_LEFT: - case SAPP_KEYCODE_DOWN: - case SAPP_KEYCODE_UP: - case SAPP_KEYCODE_PAGE_UP: - case SAPP_KEYCODE_PAGE_DOWN: - case SAPP_KEYCODE_HOME: - case SAPP_KEYCODE_END: - case SAPP_KEYCODE_CAPS_LOCK: - case SAPP_KEYCODE_SCROLL_LOCK: - case SAPP_KEYCODE_NUM_LOCK: - case SAPP_KEYCODE_PRINT_SCREEN: - case SAPP_KEYCODE_PAUSE: - case SAPP_KEYCODE_F1: - case SAPP_KEYCODE_F2: - case SAPP_KEYCODE_F3: - case SAPP_KEYCODE_F4: - case SAPP_KEYCODE_F5: - case SAPP_KEYCODE_F6: - case SAPP_KEYCODE_F7: - case SAPP_KEYCODE_F8: - case SAPP_KEYCODE_F9: - case SAPP_KEYCODE_F10: - case SAPP_KEYCODE_F11: - case SAPP_KEYCODE_F12: - case SAPP_KEYCODE_F13: - case SAPP_KEYCODE_F14: - case SAPP_KEYCODE_F15: - case SAPP_KEYCODE_F16: - case SAPP_KEYCODE_F17: - case SAPP_KEYCODE_F18: - case SAPP_KEYCODE_F19: - case SAPP_KEYCODE_F20: - case SAPP_KEYCODE_F21: - case SAPP_KEYCODE_F22: - case SAPP_KEYCODE_F23: - case SAPP_KEYCODE_F24: - case SAPP_KEYCODE_F25: - case SAPP_KEYCODE_LEFT_SHIFT: - case SAPP_KEYCODE_LEFT_CONTROL: - case SAPP_KEYCODE_LEFT_ALT: - case SAPP_KEYCODE_LEFT_SUPER: - case SAPP_KEYCODE_RIGHT_SHIFT: - case SAPP_KEYCODE_RIGHT_CONTROL: - case SAPP_KEYCODE_RIGHT_ALT: - case SAPP_KEYCODE_RIGHT_SUPER: - case SAPP_KEYCODE_MENU: - /* consume the event */ - break; - default: - /* forward key to browser */ - retval = false; - break; - } - } - if (_sapp_call_event(&_sapp.event)) { - /* consume event via sapp_consume_event() */ - retval = true; - } - if (send_keyup_followup) { - _sapp.event.type = SAPP_EVENTTYPE_KEY_UP; - if (_sapp_call_event(&_sapp.event)) { - retval = true; - } - } - } - } - _sapp_emsc_update_keyboard_state(); - return retval; -} - -_SOKOL_PRIVATE EM_BOOL _sapp_emsc_touch_cb(int emsc_type, const EmscriptenTouchEvent* emsc_event, void* user_data) { - bool retval = true; - if (_sapp_events_enabled()) { - sapp_event_type type; - switch (emsc_type) { - case EMSCRIPTEN_EVENT_TOUCHSTART: - type = SAPP_EVENTTYPE_TOUCHES_BEGAN; - break; - case EMSCRIPTEN_EVENT_TOUCHMOVE: - type = SAPP_EVENTTYPE_TOUCHES_MOVED; - break; - case EMSCRIPTEN_EVENT_TOUCHEND: - type = SAPP_EVENTTYPE_TOUCHES_ENDED; - break; - case EMSCRIPTEN_EVENT_TOUCHCANCEL: - type = SAPP_EVENTTYPE_TOUCHES_CANCELLED; - break; - default: - type = SAPP_EVENTTYPE_INVALID; - retval = false; - break; - } - if (type != SAPP_EVENTTYPE_INVALID) { - _sapp_init_event(type); - if (emsc_event->ctrlKey) { - _sapp.event.modifiers |= SAPP_MODIFIER_CTRL; - } - if (emsc_event->shiftKey) { - _sapp.event.modifiers |= SAPP_MODIFIER_SHIFT; - } - if (emsc_event->altKey) { - _sapp.event.modifiers |= SAPP_MODIFIER_ALT; - } - if (emsc_event->metaKey) { - _sapp.event.modifiers |= SAPP_MODIFIER_SUPER; - } - _sapp.event.num_touches = emsc_event->numTouches; - if (_sapp.event.num_touches > SAPP_MAX_TOUCHPOINTS) { - _sapp.event.num_touches = SAPP_MAX_TOUCHPOINTS; - } - for (int i = 0; i < _sapp.event.num_touches; i++) { - const EmscriptenTouchPoint* src = &emsc_event->touches[i]; - sapp_touchpoint* dst = &_sapp.event.touches[i]; - dst->identifier = src->identifier; - dst->pos_x = src->targetX * _sapp.dpi_scale; - dst->pos_y = src->targetY * _sapp.dpi_scale; - dst->changed = src->isChanged; - } - _sapp_call_event(&_sapp.event); - } - } - _sapp_emsc_update_keyboard_state(); - return retval; -} - -_SOKOL_PRIVATE void _sapp_emsc_init_keytable(void) { - _sapp.keycodes[8] = SAPP_KEYCODE_BACKSPACE; - _sapp.keycodes[9] = SAPP_KEYCODE_TAB; - _sapp.keycodes[13] = SAPP_KEYCODE_ENTER; - _sapp.keycodes[16] = SAPP_KEYCODE_LEFT_SHIFT; - _sapp.keycodes[17] = SAPP_KEYCODE_LEFT_CONTROL; - _sapp.keycodes[18] = SAPP_KEYCODE_LEFT_ALT; - _sapp.keycodes[19] = SAPP_KEYCODE_PAUSE; - _sapp.keycodes[27] = SAPP_KEYCODE_ESCAPE; - _sapp.keycodes[32] = SAPP_KEYCODE_SPACE; - _sapp.keycodes[33] = SAPP_KEYCODE_PAGE_UP; - _sapp.keycodes[34] = SAPP_KEYCODE_PAGE_DOWN; - _sapp.keycodes[35] = SAPP_KEYCODE_END; - _sapp.keycodes[36] = SAPP_KEYCODE_HOME; - _sapp.keycodes[37] = SAPP_KEYCODE_LEFT; - _sapp.keycodes[38] = SAPP_KEYCODE_UP; - _sapp.keycodes[39] = SAPP_KEYCODE_RIGHT; - _sapp.keycodes[40] = SAPP_KEYCODE_DOWN; - _sapp.keycodes[45] = SAPP_KEYCODE_INSERT; - _sapp.keycodes[46] = SAPP_KEYCODE_DELETE; - _sapp.keycodes[48] = SAPP_KEYCODE_0; - _sapp.keycodes[49] = SAPP_KEYCODE_1; - _sapp.keycodes[50] = SAPP_KEYCODE_2; - _sapp.keycodes[51] = SAPP_KEYCODE_3; - _sapp.keycodes[52] = SAPP_KEYCODE_4; - _sapp.keycodes[53] = SAPP_KEYCODE_5; - _sapp.keycodes[54] = SAPP_KEYCODE_6; - _sapp.keycodes[55] = SAPP_KEYCODE_7; - _sapp.keycodes[56] = SAPP_KEYCODE_8; - _sapp.keycodes[57] = SAPP_KEYCODE_9; - _sapp.keycodes[59] = SAPP_KEYCODE_SEMICOLON; - _sapp.keycodes[64] = SAPP_KEYCODE_EQUAL; - _sapp.keycodes[65] = SAPP_KEYCODE_A; - _sapp.keycodes[66] = SAPP_KEYCODE_B; - _sapp.keycodes[67] = SAPP_KEYCODE_C; - _sapp.keycodes[68] = SAPP_KEYCODE_D; - _sapp.keycodes[69] = SAPP_KEYCODE_E; - _sapp.keycodes[70] = SAPP_KEYCODE_F; - _sapp.keycodes[71] = SAPP_KEYCODE_G; - _sapp.keycodes[72] = SAPP_KEYCODE_H; - _sapp.keycodes[73] = SAPP_KEYCODE_I; - _sapp.keycodes[74] = SAPP_KEYCODE_J; - _sapp.keycodes[75] = SAPP_KEYCODE_K; - _sapp.keycodes[76] = SAPP_KEYCODE_L; - _sapp.keycodes[77] = SAPP_KEYCODE_M; - _sapp.keycodes[78] = SAPP_KEYCODE_N; - _sapp.keycodes[79] = SAPP_KEYCODE_O; - _sapp.keycodes[80] = SAPP_KEYCODE_P; - _sapp.keycodes[81] = SAPP_KEYCODE_Q; - _sapp.keycodes[82] = SAPP_KEYCODE_R; - _sapp.keycodes[83] = SAPP_KEYCODE_S; - _sapp.keycodes[84] = SAPP_KEYCODE_T; - _sapp.keycodes[85] = SAPP_KEYCODE_U; - _sapp.keycodes[86] = SAPP_KEYCODE_V; - _sapp.keycodes[87] = SAPP_KEYCODE_W; - _sapp.keycodes[88] = SAPP_KEYCODE_X; - _sapp.keycodes[89] = SAPP_KEYCODE_Y; - _sapp.keycodes[90] = SAPP_KEYCODE_Z; - _sapp.keycodes[91] = SAPP_KEYCODE_LEFT_SUPER; - _sapp.keycodes[93] = SAPP_KEYCODE_MENU; - _sapp.keycodes[96] = SAPP_KEYCODE_KP_0; - _sapp.keycodes[97] = SAPP_KEYCODE_KP_1; - _sapp.keycodes[98] = SAPP_KEYCODE_KP_2; - _sapp.keycodes[99] = SAPP_KEYCODE_KP_3; - _sapp.keycodes[100] = SAPP_KEYCODE_KP_4; - _sapp.keycodes[101] = SAPP_KEYCODE_KP_5; - _sapp.keycodes[102] = SAPP_KEYCODE_KP_6; - _sapp.keycodes[103] = SAPP_KEYCODE_KP_7; - _sapp.keycodes[104] = SAPP_KEYCODE_KP_8; - _sapp.keycodes[105] = SAPP_KEYCODE_KP_9; - _sapp.keycodes[106] = SAPP_KEYCODE_KP_MULTIPLY; - _sapp.keycodes[107] = SAPP_KEYCODE_KP_ADD; - _sapp.keycodes[109] = SAPP_KEYCODE_KP_SUBTRACT; - _sapp.keycodes[110] = SAPP_KEYCODE_KP_DECIMAL; - _sapp.keycodes[111] = SAPP_KEYCODE_KP_DIVIDE; - _sapp.keycodes[112] = SAPP_KEYCODE_F1; - _sapp.keycodes[113] = SAPP_KEYCODE_F2; - _sapp.keycodes[114] = SAPP_KEYCODE_F3; - _sapp.keycodes[115] = SAPP_KEYCODE_F4; - _sapp.keycodes[116] = SAPP_KEYCODE_F5; - _sapp.keycodes[117] = SAPP_KEYCODE_F6; - _sapp.keycodes[118] = SAPP_KEYCODE_F7; - _sapp.keycodes[119] = SAPP_KEYCODE_F8; - _sapp.keycodes[120] = SAPP_KEYCODE_F9; - _sapp.keycodes[121] = SAPP_KEYCODE_F10; - _sapp.keycodes[122] = SAPP_KEYCODE_F11; - _sapp.keycodes[123] = SAPP_KEYCODE_F12; - _sapp.keycodes[144] = SAPP_KEYCODE_NUM_LOCK; - _sapp.keycodes[145] = SAPP_KEYCODE_SCROLL_LOCK; - _sapp.keycodes[173] = SAPP_KEYCODE_MINUS; - _sapp.keycodes[186] = SAPP_KEYCODE_SEMICOLON; - _sapp.keycodes[187] = SAPP_KEYCODE_EQUAL; - _sapp.keycodes[188] = SAPP_KEYCODE_COMMA; - _sapp.keycodes[189] = SAPP_KEYCODE_MINUS; - _sapp.keycodes[190] = SAPP_KEYCODE_PERIOD; - _sapp.keycodes[191] = SAPP_KEYCODE_SLASH; - _sapp.keycodes[192] = SAPP_KEYCODE_GRAVE_ACCENT; - _sapp.keycodes[219] = SAPP_KEYCODE_LEFT_BRACKET; - _sapp.keycodes[220] = SAPP_KEYCODE_BACKSLASH; - _sapp.keycodes[221] = SAPP_KEYCODE_RIGHT_BRACKET; - _sapp.keycodes[222] = SAPP_KEYCODE_APOSTROPHE; - _sapp.keycodes[224] = SAPP_KEYCODE_LEFT_SUPER; -} - -_SOKOL_PRIVATE void _sapp_emsc_init_clipboard(void) { - sapp_js_init_clipboard(); -} - -_SOKOL_PRIVATE void _sapp_run(const sapp_desc* desc) { - _sapp_init_state(desc); - _sapp_emsc_init_keytable(); - if (_sapp.clipboard_enabled) { - _sapp_emsc_init_clipboard(); - } - double w, h; - if (_sapp.desc.html5_canvas_resize) { - w = (double) _sapp.desc.width; - h = (double) _sapp.desc.height; - } - else { - emscripten_get_element_css_size(_sapp.html5_canvas_name, &w, &h); - emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, false, _sapp_emsc_size_changed); - } - if (_sapp.desc.high_dpi) { - _sapp.dpi_scale = emscripten_get_device_pixel_ratio(); - } - _sapp.window_width = (int) w; - _sapp.window_height = (int) h; - _sapp.framebuffer_width = (int) (w * _sapp.dpi_scale); - _sapp.framebuffer_height = (int) (h * _sapp.dpi_scale); - emscripten_set_canvas_element_size(_sapp.html5_canvas_name, _sapp.framebuffer_width, _sapp.framebuffer_height); - - EmscriptenWebGLContextAttributes attrs; - emscripten_webgl_init_context_attributes(&attrs); - attrs.alpha = _sapp.desc.alpha; - attrs.depth = true; - attrs.stencil = true; - attrs.antialias = _sapp.sample_count > 1; - attrs.premultipliedAlpha = _sapp.desc.html5_premultiplied_alpha; - attrs.preserveDrawingBuffer = _sapp.desc.html5_preserve_drawing_buffer; - attrs.enableExtensionsByDefault = true; - #if defined(SOKOL_GLES3) - if (_sapp.desc.gl_force_gles2) { - attrs.majorVersion = 1; - _sapp.gles2_fallback = true; - } - else { - attrs.majorVersion = 2; - } - #endif - EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(_sapp.html5_canvas_name, &attrs); - if (!ctx) { - attrs.majorVersion = 1; - ctx = emscripten_webgl_create_context(_sapp.html5_canvas_name, &attrs); - _sapp.gles2_fallback = true; - } - emscripten_webgl_make_context_current(ctx); - - /* some WebGL extension are not enabled automatically by emscripten */ - emscripten_webgl_enable_extension(ctx, "WEBKIT_WEBGL_compressed_texture_pvrtc"); - - _sapp.valid = true; - emscripten_set_mousedown_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_mouse_cb); - emscripten_set_mouseup_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_mouse_cb); - emscripten_set_mousemove_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_mouse_cb); - emscripten_set_mouseenter_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_mouse_cb); - emscripten_set_mouseleave_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_mouse_cb); - emscripten_set_wheel_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_wheel_cb); - emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb); - emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb); - emscripten_set_keypress_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb); - emscripten_set_touchstart_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_touch_cb); - emscripten_set_touchmove_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_touch_cb); - emscripten_set_touchend_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_touch_cb); - emscripten_set_touchcancel_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_touch_cb); - emscripten_set_webglcontextlost_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_context_cb); - emscripten_set_webglcontextrestored_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_context_cb); - emscripten_request_animation_frame_loop(_sapp_emsc_frame, 0); - - sapp_js_hook_beforeunload(); - - // NOT A BUG: do not call _sapp_discard_state() -} - -#if !defined(SOKOL_NO_ENTRY) -int main(int argc, char* argv[]) { - sapp_desc desc = sokol_main(argc, argv); - _sapp_run(&desc); - return 0; -} -#endif /* SOKOL_NO_ENTRY */ -#endif /* __EMSCRIPTEN__ */ - -/*== MISC GL SUPPORT FUNCTIONS ================================================*/ -#if defined(SOKOL_GLCORE33) typedef struct { - int red_bits; - int green_bits; - int blue_bits; - int alpha_bits; - int depth_bits; - int stencil_bits; - int samples; - bool doublebuffer; - uintptr_t handle; -} _sapp_gl_fbconfig; + UIWindow* window; + _sapp_ios_view* view; + UITextField* textfield; + _sapp_textfield_dlg* textfield_dlg; + #if defined(SOKOL_METAL) + UIViewController* view_ctrl; + id mtl_device; + #else + GLKViewController* view_ctrl; + EAGLContext* eagl_ctx; + #endif + bool suspended; +} _sapp_ios_t; -_SOKOL_PRIVATE void _sapp_gl_init_fbconfig(_sapp_gl_fbconfig* fbconfig) { - memset(fbconfig, 0, sizeof(_sapp_gl_fbconfig)); - /* -1 means "don't care" */ - fbconfig->red_bits = -1; - fbconfig->green_bits = -1; - fbconfig->blue_bits = -1; - fbconfig->alpha_bits = -1; - fbconfig->depth_bits = -1; - fbconfig->stencil_bits = -1; - fbconfig->samples = -1; -} +#endif // _SAPP_IOS -_SOKOL_PRIVATE const _sapp_gl_fbconfig* _sapp_gl_choose_fbconfig(const _sapp_gl_fbconfig* desired, const _sapp_gl_fbconfig* alternatives, unsigned int count) { - unsigned int i; - unsigned int missing, least_missing = 1000000; - unsigned int color_diff, least_color_diff = 10000000; - unsigned int extra_diff, least_extra_diff = 10000000; - const _sapp_gl_fbconfig* current; - const _sapp_gl_fbconfig* closest = NULL; - for (i = 0; i < count; i++) { - current = alternatives + i; - if (desired->doublebuffer != current->doublebuffer) { - continue; - } - missing = 0; - 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++; - } +/*== EMSCRIPTEN DECLARATIONS =================================================*/ +#if defined(_SAPP_EMSCRIPTEN) - /* These polynomials make many small channel size differences matter - less than one large channel size difference - Calculate color channel size difference value - */ - 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 */ - 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 - */ - if (missing < least_missing) { - closest = current; - } - else if (missing == least_missing) { - if ((color_diff < least_color_diff) || - (color_diff == least_color_diff && extra_diff < least_extra_diff)) - { - closest = current; - } - } - if (current == closest) { - least_missing = missing; - least_color_diff = color_diff; - least_extra_diff = extra_diff; - } - } - return closest; -} +#if defined(SOKOL_WGPU) +typedef struct { + int state; + WGPUDevice device; + WGPUSwapChain swapchain; + WGPUTextureFormat render_format; + WGPUTexture msaa_tex; + WGPUTexture depth_stencil_tex; + WGPUTextureView swapchain_view; + WGPUTextureView msaa_view; + WGPUTextureView depth_stencil_view; +} _sapp_wgpu_t; #endif -/*== WINDOWS ==================================================================*/ -#if defined(_WIN32) -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include -#include -#include -#pragma comment (lib, "Shell32") +typedef struct { + bool textfield_created; + bool wants_show_keyboard; + bool wants_hide_keyboard; + bool mouse_lock_requested; + #if defined(SOKOL_WGPU) + _sapp_wgpu_t wgpu; + #endif +} _sapp_emsc_t; +#endif // _SAPP_EMSCRIPTEN -#if defined(SOKOL_D3D11) -#ifndef D3D11_NO_HELPERS -#define D3D11_NO_HELPERS -#endif -#ifndef CINTERFACE -#define CINTERFACE -#endif -#ifndef COBJMACROS -#define COBJMACROS -#endif -#include -#include -#include -#if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) -#pragma comment (lib, "WindowsApp.lib") -#else -#pragma comment (lib, "user32.lib") -#pragma comment (lib, "dxgi.lib") -#pragma comment (lib, "d3d11.lib") -#pragma comment (lib, "dxguid.lib") -#endif -#endif - -/* see https://github.com/floooh/sokol/issues/138 */ -#ifndef WM_MOUSEHWHEEL -#define WM_MOUSEHWHEEL (0x020E) -#endif +/*== WIN32 DECLARATIONS ======================================================*/ +#if defined(_SAPP_WIN32) #ifndef DPI_ENUMS_DECLARED typedef enum PROCESS_DPI_AWARENESS @@ -3007,30 +1409,41 @@ typedef enum MONITOR_DPI_TYPE { } MONITOR_DPI_TYPE; #endif /*DPI_ENUMS_DECLARED*/ -static HWND _sapp_win32_hwnd; -static HDC _sapp_win32_dc; -static bool _sapp_win32_in_create_window; -static bool _sapp_win32_dpi_aware; -static float _sapp_win32_content_scale; -static float _sapp_win32_window_scale; -static float _sapp_win32_mouse_scale; -static bool _sapp_win32_iconified; -typedef BOOL(WINAPI * SETPROCESSDPIAWARE_T)(void); -typedef HRESULT(WINAPI * SETPROCESSDPIAWARENESS_T)(PROCESS_DPI_AWARENESS); -typedef HRESULT(WINAPI * GETDPIFORMONITOR_T)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*); -static SETPROCESSDPIAWARE_T _sapp_win32_setprocessdpiaware; -static SETPROCESSDPIAWARENESS_T _sapp_win32_setprocessdpiawareness; -static GETDPIFORMONITOR_T _sapp_win32_getdpiformonitor; +typedef struct { + bool aware; + float content_scale; + float window_scale; + float mouse_scale; +} _sapp_win32_dpi_t; + +typedef struct { + HWND hwnd; + HDC dc; + LONG mouse_locked_x, mouse_locked_y; + bool in_create_window; + bool iconified; + bool mouse_tracked; + _sapp_win32_dpi_t dpi; + bool raw_input_mousepos_valid; + LONG raw_input_mousepos_x; + LONG raw_input_mousepos_y; + uint8_t raw_input_data[256]; +} _sapp_win32_t; + #if defined(SOKOL_D3D11) -static ID3D11Device* _sapp_d3d11_device; -static ID3D11DeviceContext* _sapp_d3d11_device_context; -static DXGI_SWAP_CHAIN_DESC _sapp_dxgi_swap_chain_desc; -static IDXGISwapChain* _sapp_dxgi_swap_chain; -static ID3D11Texture2D* _sapp_d3d11_rt; -static ID3D11RenderTargetView* _sapp_d3d11_rtv; -static ID3D11Texture2D* _sapp_d3d11_ds; -static ID3D11DepthStencilView* _sapp_d3d11_dsv; +typedef struct { + ID3D11Device* device; + ID3D11DeviceContext* device_context; + ID3D11Texture2D* rt; + ID3D11RenderTargetView* rtv; + ID3D11Texture2D* ds; + ID3D11DepthStencilView* dsv; + DXGI_SWAP_CHAIN_DESC swap_chain_desc; + IDXGISwapChain* swap_chain; +} _sapp_d3d11_t; #endif + +#if defined(SOKOL_GLCORE33) #define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 #define WGL_SUPPORT_OPENGL_ARB 0x2010 #define WGL_DRAW_TO_WINDOW_ARB 0x2001 @@ -3039,42 +1452,19 @@ static ID3D11DepthStencilView* _sapp_d3d11_dsv; #define WGL_ACCELERATION_ARB 0x2003 #define WGL_NO_ACCELERATION_ARB 0x2025 #define WGL_RED_BITS_ARB 0x2015 -#define WGL_RED_SHIFT_ARB 0x2016 #define WGL_GREEN_BITS_ARB 0x2017 -#define WGL_GREEN_SHIFT_ARB 0x2018 #define WGL_BLUE_BITS_ARB 0x2019 -#define WGL_BLUE_SHIFT_ARB 0x201a #define WGL_ALPHA_BITS_ARB 0x201b -#define WGL_ALPHA_SHIFT_ARB 0x201c -#define WGL_ACCUM_BITS_ARB 0x201d -#define WGL_ACCUM_RED_BITS_ARB 0x201e -#define WGL_ACCUM_GREEN_BITS_ARB 0x201f -#define WGL_ACCUM_BLUE_BITS_ARB 0x2020 -#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021 #define WGL_DEPTH_BITS_ARB 0x2022 #define WGL_STENCIL_BITS_ARB 0x2023 -#define WGL_AUX_BUFFERS_ARB 0x2024 -#define WGL_STEREO_ARB 0x2012 #define WGL_DOUBLE_BUFFER_ARB 0x2011 #define WGL_SAMPLES_ARB 0x2042 -#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9 -#define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001 #define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 #define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 #define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 -#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 #define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 #define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 #define WGL_CONTEXT_FLAGS_ARB 0x2094 -#define WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004 -#define WGL_LOSE_CONTEXT_ON_RESET_ARB 0x8252 -#define WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 -#define WGL_NO_RESET_NOTIFICATION_ARB 0x8261 -#define WGL_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 -#define WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0 -#define WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098 -#define WGL_COLORSPACE_EXT 0x309d -#define WGL_COLORSPACE_SRGB_EXT 0x3089 #define ERROR_INVALID_VERSION_ARB 0x2095 #define ERROR_INVALID_PROFILE_ARB 0x2096 #define ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB 0x2054 @@ -3088,31 +1478,286 @@ typedef BOOL (WINAPI * PFN_wglDeleteContext)(HGLRC); typedef PROC (WINAPI * PFN_wglGetProcAddress)(LPCSTR); typedef HDC (WINAPI * PFN_wglGetCurrentDC)(void); typedef BOOL (WINAPI * PFN_wglMakeCurrent)(HDC,HGLRC); -static HINSTANCE _sapp_opengl32; -static HGLRC _sapp_gl_ctx; -static PFN_wglCreateContext _sapp_wglCreateContext; -static PFN_wglDeleteContext _sapp_wglDeleteContext; -static PFN_wglGetProcAddress _sapp_wglGetProcAddress; -static PFN_wglGetCurrentDC _sapp_wglGetCurrentDC; -static PFN_wglMakeCurrent _sapp_wglMakeCurrent; -static PFNWGLSWAPINTERVALEXTPROC _sapp_SwapIntervalEXT; -static PFNWGLGETPIXELFORMATATTRIBIVARBPROC _sapp_GetPixelFormatAttribivARB; -static PFNWGLGETEXTENSIONSSTRINGEXTPROC _sapp_GetExtensionsStringEXT; -static PFNWGLGETEXTENSIONSSTRINGARBPROC _sapp_GetExtensionsStringARB; -static PFNWGLCREATECONTEXTATTRIBSARBPROC _sapp_CreateContextAttribsARB; -static bool _sapp_ext_swap_control; -static bool _sapp_arb_multisample; -static bool _sapp_arb_pixel_format; -static bool _sapp_arb_create_context; -static bool _sapp_arb_create_context_profile; -static HWND _sapp_win32_msg_hwnd; -static HDC _sapp_win32_msg_dc; -/* NOTE: the optional GL loader only contains the GL constants and functions required for sokol_gfx.h, if you need -more, you'll need to use you own gl header-generator/loader -*/ -#if !defined(SOKOL_WIN32_NO_GL_LOADER) -#if defined(SOKOL_GLCORE33) +typedef struct { + HINSTANCE opengl32; + HGLRC gl_ctx; + PFN_wglCreateContext CreateContext; + PFN_wglDeleteContext DeleteContext; + PFN_wglGetProcAddress GetProcAddress; + PFN_wglGetCurrentDC GetCurrentDC; + PFN_wglMakeCurrent MakeCurrent; + PFNWGLSWAPINTERVALEXTPROC SwapIntervalEXT; + PFNWGLGETPIXELFORMATATTRIBIVARBPROC GetPixelFormatAttribivARB; + PFNWGLGETEXTENSIONSSTRINGEXTPROC GetExtensionsStringEXT; + PFNWGLGETEXTENSIONSSTRINGARBPROC GetExtensionsStringARB; + PFNWGLCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB; + bool ext_swap_control; + bool arb_multisample; + bool arb_pixel_format; + bool arb_create_context; + bool arb_create_context_profile; + HWND msg_hwnd; + HDC msg_dc; +} _sapp_wgl_t; +#endif // SOKOL_GLCORE33 + +#endif // _SAPP_WIN32 + +/*== ANDROID DECLARATIONS ====================================================*/ + +#if defined(_SAPP_ANDROID) +typedef enum { + _SOKOL_ANDROID_MSG_CREATE, + _SOKOL_ANDROID_MSG_RESUME, + _SOKOL_ANDROID_MSG_PAUSE, + _SOKOL_ANDROID_MSG_FOCUS, + _SOKOL_ANDROID_MSG_NO_FOCUS, + _SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW, + _SOKOL_ANDROID_MSG_SET_INPUT_QUEUE, + _SOKOL_ANDROID_MSG_DESTROY, +} _sapp_android_msg_t; + +typedef struct { + pthread_t thread; + pthread_mutex_t mutex; + pthread_cond_t cond; + int read_from_main_fd; + int write_from_main_fd; +} _sapp_android_pt_t; + +typedef struct { + ANativeWindow* window; + AInputQueue* input; +} _sapp_android_resources_t; + +typedef struct { + ANativeActivity* activity; + _sapp_android_pt_t pt; + _sapp_android_resources_t pending; + _sapp_android_resources_t current; + ALooper* looper; + bool is_thread_started; + bool is_thread_stopping; + bool is_thread_stopped; + bool has_created; + bool has_resumed; + bool has_focus; + EGLConfig config; + EGLDisplay display; + EGLContext context; + EGLSurface surface; +} _sapp_android_t; + +#endif // _SAPP_ANDROID + +/*== LINUX DECLARATIONS ======================================================*/ +#if defined(_SAPP_LINUX) + +#define GLX_VENDOR 1 +#define GLX_RGBA_BIT 0x00000001 +#define GLX_WINDOW_BIT 0x00000001 +#define GLX_DRAWABLE_TYPE 0x8010 +#define GLX_RENDER_TYPE 0x8011 +#define GLX_DOUBLEBUFFER 5 +#define GLX_RED_SIZE 8 +#define GLX_GREEN_SIZE 9 +#define GLX_BLUE_SIZE 10 +#define GLX_ALPHA_SIZE 11 +#define GLX_DEPTH_SIZE 12 +#define GLX_STENCIL_SIZE 13 +#define GLX_SAMPLES 0x186a1 +#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 +#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define GLX_CONTEXT_FLAGS_ARB 0x2094 + +typedef XID GLXWindow; +typedef XID GLXDrawable; +typedef struct __GLXFBConfig* GLXFBConfig; +typedef struct __GLXcontext* GLXContext; +typedef void (*__GLXextproc)(void); + +typedef int (*PFNGLXGETFBCONFIGATTRIBPROC)(Display*,GLXFBConfig,int,int*); +typedef const char* (*PFNGLXGETCLIENTSTRINGPROC)(Display*,int); +typedef Bool (*PFNGLXQUERYEXTENSIONPROC)(Display*,int*,int*); +typedef Bool (*PFNGLXQUERYVERSIONPROC)(Display*,int*,int*); +typedef void (*PFNGLXDESTROYCONTEXTPROC)(Display*,GLXContext); +typedef Bool (*PFNGLXMAKECURRENTPROC)(Display*,GLXDrawable,GLXContext); +typedef void (*PFNGLXSWAPBUFFERSPROC)(Display*,GLXDrawable); +typedef const char* (*PFNGLXQUERYEXTENSIONSSTRINGPROC)(Display*,int); +typedef GLXFBConfig* (*PFNGLXGETFBCONFIGSPROC)(Display*,int,int*); +typedef __GLXextproc (* PFNGLXGETPROCADDRESSPROC)(const GLubyte *procName); +typedef void (*PFNGLXSWAPINTERVALEXTPROC)(Display*,GLXDrawable,int); +typedef XVisualInfo* (*PFNGLXGETVISUALFROMFBCONFIGPROC)(Display*,GLXFBConfig); +typedef GLXWindow (*PFNGLXCREATEWINDOWPROC)(Display*,GLXFBConfig,Window,const int*); +typedef void (*PFNGLXDESTROYWINDOWPROC)(Display*,GLXWindow); + +typedef int (*PFNGLXSWAPINTERVALMESAPROC)(int); +typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display*,GLXFBConfig,GLXContext,Bool,const int*); + +typedef struct { + bool available; + int major_opcode; + int event_base; + int error_base; + int major; + int minor; +} _sapp_xi_t; + +typedef struct { + Display* display; + int screen; + Window root; + Colormap colormap; + Window window; + Cursor hidden_cursor; + int window_state; + float dpi; + unsigned char error_code; + Atom UTF8_STRING; + Atom WM_PROTOCOLS; + Atom WM_DELETE_WINDOW; + Atom WM_STATE; + Atom NET_WM_NAME; + Atom NET_WM_ICON_NAME; + Atom NET_WM_STATE; + Atom NET_WM_STATE_FULLSCREEN; + _sapp_xi_t xi; +} _sapp_x11_t; + +typedef struct { + void* libgl; + int major; + int minor; + int event_base; + int error_base; + GLXContext ctx; + GLXWindow window; + + // GLX 1.3 functions + PFNGLXGETFBCONFIGSPROC GetFBConfigs; + PFNGLXGETFBCONFIGATTRIBPROC GetFBConfigAttrib; + PFNGLXGETCLIENTSTRINGPROC GetClientString; + PFNGLXQUERYEXTENSIONPROC QueryExtension; + PFNGLXQUERYVERSIONPROC QueryVersion; + PFNGLXDESTROYCONTEXTPROC DestroyContext; + PFNGLXMAKECURRENTPROC MakeCurrent; + PFNGLXSWAPBUFFERSPROC SwapBuffers; + PFNGLXQUERYEXTENSIONSSTRINGPROC QueryExtensionsString; + PFNGLXGETVISUALFROMFBCONFIGPROC GetVisualFromFBConfig; + PFNGLXCREATEWINDOWPROC CreateWindow; + PFNGLXDESTROYWINDOWPROC DestroyWindow; + + // GLX 1.4 and extension functions + PFNGLXGETPROCADDRESSPROC GetProcAddress; + PFNGLXGETPROCADDRESSPROC GetProcAddressARB; + PFNGLXSWAPINTERVALEXTPROC SwapIntervalEXT; + PFNGLXSWAPINTERVALMESAPROC SwapIntervalMESA; + PFNGLXCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB; + + // extension availability + bool EXT_swap_control; + bool MESA_swap_control; + bool ARB_multisample; + bool ARB_create_context; + bool ARB_create_context_profile; +} _sapp_glx_t; + +#endif // _SAPP_LINUX + +/*== COMMON DECLARATIONS =====================================================*/ + +/* 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) +/* NOTE: the pixel format values *must* be compatible with sg_pixel_format */ +#define _SAPP_PIXELFORMAT_RGBA8 (23) +#define _SAPP_PIXELFORMAT_BGRA8 (27) +#define _SAPP_PIXELFORMAT_DEPTH (41) +#define _SAPP_PIXELFORMAT_DEPTH_STENCIL (42) + +#if defined(_SAPP_MACOS) || defined(_SAPP_IOS) + // this is ARC compatible + #if defined(__cplusplus) + #define _SAPP_CLEAR(type, item) { item = { }; } + #else + #define _SAPP_CLEAR(type, item) { item = (type) { 0 }; } + #endif +#else + #define _SAPP_CLEAR(type, item) { memset(&item, 0, sizeof(item)); } +#endif + +typedef struct { + bool enabled; + int buf_size; + char* buffer; +} _sapp_clipboard_t; + +typedef struct { + float x, y; + float dx, dy; + bool shown; + bool locked; + bool pos_valid; +} _sapp_mouse_t; + +typedef struct { + sapp_desc desc; + bool valid; + bool fullscreen; + bool gles2_fallback; + bool first_frame; + bool init_called; + bool cleanup_called; + bool quit_requested; + bool quit_ordered; + bool event_consumed; + bool html5_ask_leave_site; + bool onscreen_keyboard_shown; + int window_width; + int window_height; + int framebuffer_width; + int framebuffer_height; + int sample_count; + int swap_interval; + float dpi_scale; + uint64_t frame_count; + sapp_event event; + _sapp_mouse_t mouse; + _sapp_clipboard_t clipboard; + #if defined(_SAPP_MACOS) + _sapp_macos_t macos; + #elif defined(_SAPP_IOS) + _sapp_ios_t ios; + #elif defined(_SAPP_EMSCRIPTEN) + _sapp_emsc_t emsc; + #elif defined(_SAPP_WIN32) + _sapp_win32_t win32; + #if defined(SOKOL_D3D11) + _sapp_d3d11_t d3d11; + #elif defined(SOKOL_GLCORE33) + _sapp_wgl_t wgl; + #endif + #elif defined(_SAPP_ANDROID) + _sapp_android_t android; + #elif defined(_SAPP_LINUX) + _sapp_x11_t x11; + _sapp_glx_t glx; + #endif + char html5_canvas_name[_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 */ + sapp_keycode keycodes[SAPP_MAX_KEYCODES]; +} _sapp_t; +static _sapp_t _sapp; + +/*=== OPTIONAL MINI GL LOADER FOR WIN32/WGL ==================================*/ +#if defined(_SAPP_WIN32) && defined(SOKOL_GLCORE33) && !defined(SOKOL_WIN32_NO_GL_LOADER) #define __gl_h_ 1 #define __gl32_h_ 1 #define __gl31_h_ 1 @@ -3342,414 +1987,2422 @@ typedef int GLint; #define GL_MAX_VERTEX_ATTRIBS 0x8869 #define GL_CLAMP_TO_BORDER 0x812D #define GL_TEXTURE_BORDER_COLOR 0x1004 +#define GL_CURRENT_PROGRAM 0x8B8D -typedef void (GL_APIENTRY *PFN_glBindVertexArray)(GLuint array); -static PFN_glBindVertexArray _sapp_glBindVertexArray; -typedef void (GL_APIENTRY *PFN_glFramebufferTextureLayer)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); -static PFN_glFramebufferTextureLayer _sapp_glFramebufferTextureLayer; -typedef void (GL_APIENTRY *PFN_glGenFramebuffers)(GLsizei n, GLuint * framebuffers); -static PFN_glGenFramebuffers _sapp_glGenFramebuffers; -typedef void (GL_APIENTRY *PFN_glBindFramebuffer)(GLenum target, GLuint framebuffer); -static PFN_glBindFramebuffer _sapp_glBindFramebuffer; -typedef void (GL_APIENTRY *PFN_glBindRenderbuffer)(GLenum target, GLuint renderbuffer); -static PFN_glBindRenderbuffer _sapp_glBindRenderbuffer; -typedef const GLubyte * (GL_APIENTRY *PFN_glGetStringi)(GLenum name, GLuint index); -static PFN_glGetStringi _sapp_glGetStringi; -typedef void (GL_APIENTRY *PFN_glClearBufferfi)(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); -static PFN_glClearBufferfi _sapp_glClearBufferfi; -typedef void (GL_APIENTRY *PFN_glClearBufferfv)(GLenum buffer, GLint drawbuffer, const GLfloat * value); -static PFN_glClearBufferfv _sapp_glClearBufferfv; -typedef void (GL_APIENTRY *PFN_glClearBufferuiv)(GLenum buffer, GLint drawbuffer, const GLuint * value); -static PFN_glClearBufferuiv _sapp_glClearBufferuiv; -typedef void (GL_APIENTRY *PFN_glDeleteRenderbuffers)(GLsizei n, const GLuint * renderbuffers); -static PFN_glDeleteRenderbuffers _sapp_glDeleteRenderbuffers; -typedef void (GL_APIENTRY *PFN_glUniform4fv)(GLint location, GLsizei count, const GLfloat * value); -static PFN_glUniform4fv _sapp_glUniform4fv; -typedef void (GL_APIENTRY *PFN_glUniform2fv)(GLint location, GLsizei count, const GLfloat * value); -static PFN_glUniform2fv _sapp_glUniform2fv; -typedef void (GL_APIENTRY *PFN_glUseProgram)(GLuint program); -static PFN_glUseProgram _sapp_glUseProgram; -typedef void (GL_APIENTRY *PFN_glShaderSource)(GLuint shader, GLsizei count, const GLchar *const* string, const GLint * length); -static PFN_glShaderSource _sapp_glShaderSource; -typedef void (GL_APIENTRY *PFN_glLinkProgram)(GLuint program); -static PFN_glLinkProgram _sapp_glLinkProgram; -typedef GLint (GL_APIENTRY *PFN_glGetUniformLocation)(GLuint program, const GLchar * name); -static PFN_glGetUniformLocation _sapp_glGetUniformLocation; -typedef void (GL_APIENTRY *PFN_glGetShaderiv)(GLuint shader, GLenum pname, GLint * params); -static PFN_glGetShaderiv _sapp_glGetShaderiv; -typedef void (GL_APIENTRY *PFN_glGetProgramInfoLog)(GLuint program, GLsizei bufSize, GLsizei * length, GLchar * infoLog); -static PFN_glGetProgramInfoLog _sapp_glGetProgramInfoLog; -typedef GLint (GL_APIENTRY *PFN_glGetAttribLocation)(GLuint program, const GLchar * name); -static PFN_glGetAttribLocation _sapp_glGetAttribLocation; -typedef void (GL_APIENTRY *PFN_glDisableVertexAttribArray)(GLuint index); -static PFN_glDisableVertexAttribArray _sapp_glDisableVertexAttribArray; -typedef void (GL_APIENTRY *PFN_glDeleteShader)(GLuint shader); -static PFN_glDeleteShader _sapp_glDeleteShader; -typedef void (GL_APIENTRY *PFN_glDeleteProgram)(GLuint program); -static PFN_glDeleteProgram _sapp_glDeleteProgram; -typedef void (GL_APIENTRY *PFN_glCompileShader)(GLuint shader); -static PFN_glCompileShader _sapp_glCompileShader; -typedef void (GL_APIENTRY *PFN_glStencilFuncSeparate)(GLenum face, GLenum func, GLint ref, GLuint mask); -static PFN_glStencilFuncSeparate _sapp_glStencilFuncSeparate; -typedef void (GL_APIENTRY *PFN_glStencilOpSeparate)(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); -static PFN_glStencilOpSeparate _sapp_glStencilOpSeparate; -typedef void (GL_APIENTRY *PFN_glRenderbufferStorageMultisample)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); -static PFN_glRenderbufferStorageMultisample _sapp_glRenderbufferStorageMultisample; -typedef void (GL_APIENTRY *PFN_glDrawBuffers)(GLsizei n, const GLenum * bufs); -static PFN_glDrawBuffers _sapp_glDrawBuffers; -typedef void (GL_APIENTRY *PFN_glVertexAttribDivisor)(GLuint index, GLuint divisor); -static PFN_glVertexAttribDivisor _sapp_glVertexAttribDivisor; -typedef void (GL_APIENTRY *PFN_glBufferSubData)(GLenum target, GLintptr offset, GLsizeiptr size, const void * data); -static PFN_glBufferSubData _sapp_glBufferSubData; -typedef void (GL_APIENTRY *PFN_glGenBuffers)(GLsizei n, GLuint * buffers); -static PFN_glGenBuffers _sapp_glGenBuffers; -typedef GLenum (GL_APIENTRY *PFN_glCheckFramebufferStatus)(GLenum target); -static PFN_glCheckFramebufferStatus _sapp_glCheckFramebufferStatus; -typedef void (GL_APIENTRY *PFN_glFramebufferRenderbuffer)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); -static PFN_glFramebufferRenderbuffer _sapp_glFramebufferRenderbuffer; -typedef void (GL_APIENTRY *PFN_glCompressedTexImage2D)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void * data); -static PFN_glCompressedTexImage2D _sapp_glCompressedTexImage2D; -typedef void (GL_APIENTRY *PFN_glCompressedTexImage3D)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void * data); -static PFN_glCompressedTexImage3D _sapp_glCompressedTexImage3D; -typedef void (GL_APIENTRY *PFN_glActiveTexture)(GLenum texture); -static PFN_glActiveTexture _sapp_glActiveTexture; -typedef void (GL_APIENTRY *PFN_glTexSubImage3D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * pixels); -static PFN_glTexSubImage3D _sapp_glTexSubImage3D; -typedef void (GL_APIENTRY *PFN_glUniformMatrix4fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); -static PFN_glUniformMatrix4fv _sapp_glUniformMatrix4fv; -typedef void (GL_APIENTRY *PFN_glRenderbufferStorage)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); -static PFN_glRenderbufferStorage _sapp_glRenderbufferStorage; -typedef void (GL_APIENTRY *PFN_glGenTextures)(GLsizei n, GLuint * textures); -static PFN_glGenTextures _sapp_glGenTextures; -typedef void (GL_APIENTRY *PFN_glPolygonOffset)(GLfloat factor, GLfloat units); -static PFN_glPolygonOffset _sapp_glPolygonOffset; -typedef void (GL_APIENTRY *PFN_glDrawElements)(GLenum mode, GLsizei count, GLenum type, const void * indices); -static PFN_glDrawElements _sapp_glDrawElements; -typedef void (GL_APIENTRY *PFN_glDeleteFramebuffers)(GLsizei n, const GLuint * framebuffers); -static PFN_glDeleteFramebuffers _sapp_glDeleteFramebuffers; -typedef void (GL_APIENTRY *PFN_glBlendEquationSeparate)(GLenum modeRGB, GLenum modeAlpha); -static PFN_glBlendEquationSeparate _sapp_glBlendEquationSeparate; -typedef void (GL_APIENTRY *PFN_glDeleteTextures)(GLsizei n, const GLuint * textures); -static PFN_glDeleteTextures _sapp_glDeleteTextures; -typedef void (GL_APIENTRY *PFN_glGetProgramiv)(GLuint program, GLenum pname, GLint * params); -static PFN_glGetProgramiv _sapp_glGetProgramiv; -typedef void (GL_APIENTRY *PFN_glBindTexture)(GLenum target, GLuint texture); -static PFN_glBindTexture _sapp_glBindTexture; -typedef void (GL_APIENTRY *PFN_glTexImage3D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void * pixels); -static PFN_glTexImage3D _sapp_glTexImage3D; -typedef GLuint (GL_APIENTRY *PFN_glCreateShader)(GLenum type); -static PFN_glCreateShader _sapp_glCreateShader; -typedef void (GL_APIENTRY *PFN_glTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels); -static PFN_glTexSubImage2D _sapp_glTexSubImage2D; -typedef void (GL_APIENTRY *PFN_glClearDepth)(GLdouble depth); -static PFN_glClearDepth _sapp_glClearDepth; -typedef void (GL_APIENTRY *PFN_glFramebufferTexture2D)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); -static PFN_glFramebufferTexture2D _sapp_glFramebufferTexture2D; -typedef GLuint (GL_APIENTRY *PFN_glCreateProgram)(); -static PFN_glCreateProgram _sapp_glCreateProgram; -typedef void (GL_APIENTRY *PFN_glViewport)(GLint x, GLint y, GLsizei width, GLsizei height); -static PFN_glViewport _sapp_glViewport; -typedef void (GL_APIENTRY *PFN_glDeleteBuffers)(GLsizei n, const GLuint * buffers); -static PFN_glDeleteBuffers _sapp_glDeleteBuffers; -typedef void (GL_APIENTRY *PFN_glDrawArrays)(GLenum mode, GLint first, GLsizei count); -static PFN_glDrawArrays _sapp_glDrawArrays; -typedef void (GL_APIENTRY *PFN_glDrawElementsInstanced)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount); -static PFN_glDrawElementsInstanced _sapp_glDrawElementsInstanced; -typedef void (GL_APIENTRY *PFN_glVertexAttribPointer)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void * pointer); -static PFN_glVertexAttribPointer _sapp_glVertexAttribPointer; -typedef void (GL_APIENTRY *PFN_glUniform1i)(GLint location, GLint v0); -static PFN_glUniform1i _sapp_glUniform1i; -typedef void (GL_APIENTRY *PFN_glDisable)(GLenum cap); -static PFN_glDisable _sapp_glDisable; -typedef void (GL_APIENTRY *PFN_glColorMask)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); -static PFN_glColorMask _sapp_glColorMask; -typedef void (GL_APIENTRY *PFN_glBindBuffer)(GLenum target, GLuint buffer); -static PFN_glBindBuffer _sapp_glBindBuffer; -typedef void (GL_APIENTRY *PFN_glDeleteVertexArrays)(GLsizei n, const GLuint * arrays); -static PFN_glDeleteVertexArrays _sapp_glDeleteVertexArrays; -typedef void (GL_APIENTRY *PFN_glDepthMask)(GLboolean flag); -static PFN_glDepthMask _sapp_glDepthMask; -typedef void (GL_APIENTRY *PFN_glDrawArraysInstanced)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount); -static PFN_glDrawArraysInstanced _sapp_glDrawArraysInstanced; -typedef void (GL_APIENTRY *PFN_glClearStencil)(GLint s); -static PFN_glClearStencil _sapp_glClearStencil; -typedef void (GL_APIENTRY *PFN_glScissor)(GLint x, GLint y, GLsizei width, GLsizei height); -static PFN_glScissor _sapp_glScissor; -typedef void (GL_APIENTRY *PFN_glUniform3fv)(GLint location, GLsizei count, const GLfloat * value); -static PFN_glUniform3fv _sapp_glUniform3fv; -typedef void (GL_APIENTRY *PFN_glGenRenderbuffers)(GLsizei n, GLuint * renderbuffers); -static PFN_glGenRenderbuffers _sapp_glGenRenderbuffers; -typedef void (GL_APIENTRY *PFN_glBufferData)(GLenum target, GLsizeiptr size, const void * data, GLenum usage); -static PFN_glBufferData _sapp_glBufferData; -typedef void (GL_APIENTRY *PFN_glBlendFuncSeparate)(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); -static PFN_glBlendFuncSeparate _sapp_glBlendFuncSeparate; -typedef void (GL_APIENTRY *PFN_glTexParameteri)(GLenum target, GLenum pname, GLint param); -static PFN_glTexParameteri _sapp_glTexParameteri; -typedef void (GL_APIENTRY *PFN_glGetIntegerv)(GLenum pname, GLint * data); -static PFN_glGetIntegerv _sapp_glGetIntegerv; -typedef void (GL_APIENTRY *PFN_glEnable)(GLenum cap); -static PFN_glEnable _sapp_glEnable; -typedef void (GL_APIENTRY *PFN_glBlitFramebuffer)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); -static PFN_glBlitFramebuffer _sapp_glBlitFramebuffer; -typedef void (GL_APIENTRY *PFN_glStencilMask)(GLuint mask); -static PFN_glStencilMask _sapp_glStencilMask; -typedef void (GL_APIENTRY *PFN_glAttachShader)(GLuint program, GLuint shader); -static PFN_glAttachShader _sapp_glAttachShader; -typedef GLenum (GL_APIENTRY *PFN_glGetError)(); -static PFN_glGetError _sapp_glGetError; -typedef void (GL_APIENTRY *PFN_glClearColor)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); -static PFN_glClearColor _sapp_glClearColor; -typedef void (GL_APIENTRY *PFN_glBlendColor)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); -static PFN_glBlendColor _sapp_glBlendColor; -typedef void (GL_APIENTRY *PFN_glTexParameterf)(GLenum target, GLenum pname, GLfloat param); -static PFN_glTexParameterf _sapp_glTexParameterf; -typedef void (GL_APIENTRY *PFN_glTexParameterfv)(GLenum target, GLenum pname, GLfloat* params); -static PFN_glTexParameterfv _sapp_glTexParameterfv; -typedef void (GL_APIENTRY *PFN_glGetShaderInfoLog)(GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * infoLog); -static PFN_glGetShaderInfoLog _sapp_glGetShaderInfoLog; -typedef void (GL_APIENTRY *PFN_glDepthFunc)(GLenum func); -static PFN_glDepthFunc _sapp_glDepthFunc; -typedef void (GL_APIENTRY *PFN_glStencilOp)(GLenum fail, GLenum zfail, GLenum zpass); -static PFN_glStencilOp _sapp_glStencilOp; -typedef void (GL_APIENTRY *PFN_glStencilFunc)(GLenum func, GLint ref, GLuint mask); -static PFN_glStencilFunc _sapp_glStencilFunc; -typedef void (GL_APIENTRY *PFN_glEnableVertexAttribArray)(GLuint index); -static PFN_glEnableVertexAttribArray _sapp_glEnableVertexAttribArray; -typedef void (GL_APIENTRY *PFN_glBlendFunc)(GLenum sfactor, GLenum dfactor); -static PFN_glBlendFunc _sapp_glBlendFunc; -typedef void (GL_APIENTRY *PFN_glUniform1fv)(GLint location, GLsizei count, const GLfloat * value); -static PFN_glUniform1fv _sapp_glUniform1fv; -typedef void (GL_APIENTRY *PFN_glReadBuffer)(GLenum src); -static PFN_glReadBuffer _sapp_glReadBuffer; -typedef void (GL_APIENTRY *PFN_glClear)(GLbitfield mask); -static PFN_glClear _sapp_glClear; -typedef void (GL_APIENTRY *PFN_glTexImage2D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * pixels); -static PFN_glTexImage2D _sapp_glTexImage2D; -typedef void (GL_APIENTRY *PFN_glGenVertexArrays)(GLsizei n, GLuint * arrays); -static PFN_glGenVertexArrays _sapp_glGenVertexArrays; -typedef void (GL_APIENTRY *PFN_glFrontFace)(GLenum mode); -static PFN_glFrontFace _sapp_glFrontFace; -typedef void (GL_APIENTRY *PFN_glCullFace)(GLenum mode); -static PFN_glCullFace _sapp_glCullFace; +// X Macro list of GL function names and signatures +#define _SAPP_GL_FUNCS \ + _SAPP_XMACRO(glBindVertexArray, void, (GLuint array)) \ + _SAPP_XMACRO(glFramebufferTextureLayer, void, (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer)) \ + _SAPP_XMACRO(glGenFramebuffers, void, (GLsizei n, GLuint * framebuffers)) \ + _SAPP_XMACRO(glBindFramebuffer, void, (GLenum target, GLuint framebuffer)) \ + _SAPP_XMACRO(glBindRenderbuffer, void, (GLenum target, GLuint renderbuffer)) \ + _SAPP_XMACRO(glGetStringi, const GLubyte *, (GLenum name, GLuint index)) \ + _SAPP_XMACRO(glClearBufferfi, void, (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil)) \ + _SAPP_XMACRO(glClearBufferfv, void, (GLenum buffer, GLint drawbuffer, const GLfloat * value)) \ + _SAPP_XMACRO(glClearBufferuiv, void, (GLenum buffer, GLint drawbuffer, const GLuint * value)) \ + _SAPP_XMACRO(glClearBufferiv, void, (GLenum buffer, GLint drawbuffer, const GLint * value)) \ + _SAPP_XMACRO(glDeleteRenderbuffers, void, (GLsizei n, const GLuint * renderbuffers)) \ + _SAPP_XMACRO(glUniform4fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ + _SAPP_XMACRO(glUniform2fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ + _SAPP_XMACRO(glUseProgram, void, (GLuint program)) \ + _SAPP_XMACRO(glShaderSource, void, (GLuint shader, GLsizei count, const GLchar *const* string, const GLint * length)) \ + _SAPP_XMACRO(glLinkProgram, void, (GLuint program)) \ + _SAPP_XMACRO(glGetUniformLocation, GLint, (GLuint program, const GLchar * name)) \ + _SAPP_XMACRO(glGetShaderiv, void, (GLuint shader, GLenum pname, GLint * params)) \ + _SAPP_XMACRO(glGetProgramInfoLog, void, (GLuint program, GLsizei bufSize, GLsizei * length, GLchar * infoLog)) \ + _SAPP_XMACRO(glGetAttribLocation, GLint, (GLuint program, const GLchar * name)) \ + _SAPP_XMACRO(glDisableVertexAttribArray, void, (GLuint index)) \ + _SAPP_XMACRO(glDeleteShader, void, (GLuint shader)) \ + _SAPP_XMACRO(glDeleteProgram, void, (GLuint program)) \ + _SAPP_XMACRO(glCompileShader, void, (GLuint shader)) \ + _SAPP_XMACRO(glStencilFuncSeparate, void, (GLenum face, GLenum func, GLint ref, GLuint mask)) \ + _SAPP_XMACRO(glStencilOpSeparate, void, (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass)) \ + _SAPP_XMACRO(glRenderbufferStorageMultisample, void, (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height)) \ + _SAPP_XMACRO(glDrawBuffers, void, (GLsizei n, const GLenum * bufs)) \ + _SAPP_XMACRO(glVertexAttribDivisor, void, (GLuint index, GLuint divisor)) \ + _SAPP_XMACRO(glBufferSubData, void, (GLenum target, GLintptr offset, GLsizeiptr size, const void * data)) \ + _SAPP_XMACRO(glGenBuffers, void, (GLsizei n, GLuint * buffers)) \ + _SAPP_XMACRO(glCheckFramebufferStatus, GLenum, (GLenum target)) \ + _SAPP_XMACRO(glFramebufferRenderbuffer, void, (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)) \ + _SAPP_XMACRO(glCompressedTexImage2D, void, (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void * data)) \ + _SAPP_XMACRO(glCompressedTexImage3D, void, (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void * data)) \ + _SAPP_XMACRO(glActiveTexture, void, (GLenum texture)) \ + _SAPP_XMACRO(glTexSubImage3D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * pixels)) \ + _SAPP_XMACRO(glUniformMatrix4fv, void, (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value)) \ + _SAPP_XMACRO(glRenderbufferStorage, void, (GLenum target, GLenum internalformat, GLsizei width, GLsizei height)) \ + _SAPP_XMACRO(glGenTextures, void, (GLsizei n, GLuint * textures)) \ + _SAPP_XMACRO(glPolygonOffset, void, (GLfloat factor, GLfloat units)) \ + _SAPP_XMACRO(glDrawElements, void, (GLenum mode, GLsizei count, GLenum type, const void * indices)) \ + _SAPP_XMACRO(glDeleteFramebuffers, void, (GLsizei n, const GLuint * framebuffers)) \ + _SAPP_XMACRO(glBlendEquationSeparate, void, (GLenum modeRGB, GLenum modeAlpha)) \ + _SAPP_XMACRO(glDeleteTextures, void, (GLsizei n, const GLuint * textures)) \ + _SAPP_XMACRO(glGetProgramiv, void, (GLuint program, GLenum pname, GLint * params)) \ + _SAPP_XMACRO(glBindTexture, void, (GLenum target, GLuint texture)) \ + _SAPP_XMACRO(glTexImage3D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void * pixels)) \ + _SAPP_XMACRO(glCreateShader, GLuint, (GLenum type)) \ + _SAPP_XMACRO(glTexSubImage2D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels)) \ + _SAPP_XMACRO(glClearDepth, void, (GLdouble depth)) \ + _SAPP_XMACRO(glFramebufferTexture2D, void, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)) \ + _SAPP_XMACRO(glCreateProgram, GLuint, (void)) \ + _SAPP_XMACRO(glViewport, void, (GLint x, GLint y, GLsizei width, GLsizei height)) \ + _SAPP_XMACRO(glDeleteBuffers, void, (GLsizei n, const GLuint * buffers)) \ + _SAPP_XMACRO(glDrawArrays, void, (GLenum mode, GLint first, GLsizei count)) \ + _SAPP_XMACRO(glDrawElementsInstanced, void, (GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount)) \ + _SAPP_XMACRO(glVertexAttribPointer, void, (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void * pointer)) \ + _SAPP_XMACRO(glUniform1i, void, (GLint location, GLint v0)) \ + _SAPP_XMACRO(glDisable, void, (GLenum cap)) \ + _SAPP_XMACRO(glColorMask, void, (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha)) \ + _SAPP_XMACRO(glBindBuffer, void, (GLenum target, GLuint buffer)) \ + _SAPP_XMACRO(glDeleteVertexArrays, void, (GLsizei n, const GLuint * arrays)) \ + _SAPP_XMACRO(glDepthMask, void, (GLboolean flag)) \ + _SAPP_XMACRO(glDrawArraysInstanced, void, (GLenum mode, GLint first, GLsizei count, GLsizei instancecount)) \ + _SAPP_XMACRO(glClearStencil, void, (GLint s)) \ + _SAPP_XMACRO(glScissor, void, (GLint x, GLint y, GLsizei width, GLsizei height)) \ + _SAPP_XMACRO(glUniform3fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ + _SAPP_XMACRO(glGenRenderbuffers, void, (GLsizei n, GLuint * renderbuffers)) \ + _SAPP_XMACRO(glBufferData, void, (GLenum target, GLsizeiptr size, const void * data, GLenum usage)) \ + _SAPP_XMACRO(glBlendFuncSeparate, void, (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha)) \ + _SAPP_XMACRO(glTexParameteri, void, (GLenum target, GLenum pname, GLint param)) \ + _SAPP_XMACRO(glGetIntegerv, void, (GLenum pname, GLint * data)) \ + _SAPP_XMACRO(glEnable, void, (GLenum cap)) \ + _SAPP_XMACRO(glBlitFramebuffer, void, (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)) \ + _SAPP_XMACRO(glStencilMask, void, (GLuint mask)) \ + _SAPP_XMACRO(glAttachShader, void, (GLuint program, GLuint shader)) \ + _SAPP_XMACRO(glGetError, GLenum, (void)) \ + _SAPP_XMACRO(glClearColor, void, (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)) \ + _SAPP_XMACRO(glBlendColor, void, (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)) \ + _SAPP_XMACRO(glTexParameterf, void, (GLenum target, GLenum pname, GLfloat param)) \ + _SAPP_XMACRO(glTexParameterfv, void, (GLenum target, GLenum pname, GLfloat* params)) \ + _SAPP_XMACRO(glGetShaderInfoLog, void, (GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * infoLog)) \ + _SAPP_XMACRO(glDepthFunc, void, (GLenum func)) \ + _SAPP_XMACRO(glStencilOp , void, (GLenum fail, GLenum zfail, GLenum zpass)) \ + _SAPP_XMACRO(glStencilFunc, void, (GLenum func, GLint ref, GLuint mask)) \ + _SAPP_XMACRO(glEnableVertexAttribArray, void, (GLuint index)) \ + _SAPP_XMACRO(glBlendFunc, void, (GLenum sfactor, GLenum dfactor)) \ + _SAPP_XMACRO(glUniform1fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ + _SAPP_XMACRO(glReadBuffer, void, (GLenum src)) \ + _SAPP_XMACRO(glClear, void, (GLbitfield mask)) \ + _SAPP_XMACRO(glTexImage2D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * pixels)) \ + _SAPP_XMACRO(glGenVertexArrays, void, (GLsizei n, GLuint * arrays)) \ + _SAPP_XMACRO(glFrontFace, void, (GLenum mode)) \ + _SAPP_XMACRO(glCullFace, void, (GLenum mode)) +// generate GL function pointer typedefs +#define _SAPP_XMACRO(name, ret, args) typedef ret (GL_APIENTRY* PFN_ ## name) args; +_SAPP_GL_FUNCS +#undef _SAPP_XMACRO + +// generate GL function pointers +#define _SAPP_XMACRO(name, ret, args) static PFN_ ## name name; +_SAPP_GL_FUNCS +#undef _SAPP_XMACRO + +// helper function to lookup GL functions in GL DLL _SOKOL_PRIVATE void* _sapp_win32_glgetprocaddr(const char* name) { - void* proc_addr = (void*) _sapp_wglGetProcAddress(name); + void* proc_addr = (void*) _sapp.wgl.GetProcAddress(name); if (0 == proc_addr) { - proc_addr = (void*) GetProcAddress(_sapp_opengl32, name); + proc_addr = (void*) GetProcAddress(_sapp.wgl.opengl32, name); } SOKOL_ASSERT(proc_addr); return proc_addr; } -#define _SAPP_GLPROC(name) _sapp_ ## name = (PFN_ ## name) _sapp_win32_glgetprocaddr(#name) - +// populate GL function pointers _SOKOL_PRIVATE void _sapp_win32_gl_loadfuncs(void) { - SOKOL_ASSERT(_sapp_wglGetProcAddress); - SOKOL_ASSERT(_sapp_opengl32); - _SAPP_GLPROC(glBindVertexArray); - _SAPP_GLPROC(glFramebufferTextureLayer); - _SAPP_GLPROC(glGenFramebuffers); - _SAPP_GLPROC(glBindFramebuffer); - _SAPP_GLPROC(glBindRenderbuffer); - _SAPP_GLPROC(glGetStringi); - _SAPP_GLPROC(glClearBufferfi); - _SAPP_GLPROC(glClearBufferfv); - _SAPP_GLPROC(glClearBufferuiv); - _SAPP_GLPROC(glDeleteRenderbuffers); - _SAPP_GLPROC(glUniform4fv); - _SAPP_GLPROC(glUniform2fv); - _SAPP_GLPROC(glUseProgram); - _SAPP_GLPROC(glShaderSource); - _SAPP_GLPROC(glLinkProgram); - _SAPP_GLPROC(glGetUniformLocation); - _SAPP_GLPROC(glGetShaderiv); - _SAPP_GLPROC(glGetProgramInfoLog); - _SAPP_GLPROC(glGetAttribLocation); - _SAPP_GLPROC(glDisableVertexAttribArray); - _SAPP_GLPROC(glDeleteShader); - _SAPP_GLPROC(glDeleteProgram); - _SAPP_GLPROC(glCompileShader); - _SAPP_GLPROC(glStencilFuncSeparate); - _SAPP_GLPROC(glStencilOpSeparate); - _SAPP_GLPROC(glRenderbufferStorageMultisample); - _SAPP_GLPROC(glDrawBuffers); - _SAPP_GLPROC(glVertexAttribDivisor); - _SAPP_GLPROC(glBufferSubData); - _SAPP_GLPROC(glGenBuffers); - _SAPP_GLPROC(glCheckFramebufferStatus); - _SAPP_GLPROC(glFramebufferRenderbuffer); - _SAPP_GLPROC(glCompressedTexImage2D); - _SAPP_GLPROC(glCompressedTexImage3D); - _SAPP_GLPROC(glActiveTexture); - _SAPP_GLPROC(glTexSubImage3D); - _SAPP_GLPROC(glUniformMatrix4fv); - _SAPP_GLPROC(glRenderbufferStorage); - _SAPP_GLPROC(glGenTextures); - _SAPP_GLPROC(glPolygonOffset); - _SAPP_GLPROC(glDrawElements); - _SAPP_GLPROC(glDeleteFramebuffers); - _SAPP_GLPROC(glBlendEquationSeparate); - _SAPP_GLPROC(glDeleteTextures); - _SAPP_GLPROC(glGetProgramiv); - _SAPP_GLPROC(glBindTexture); - _SAPP_GLPROC(glTexImage3D); - _SAPP_GLPROC(glCreateShader); - _SAPP_GLPROC(glTexSubImage2D); - _SAPP_GLPROC(glClearDepth); - _SAPP_GLPROC(glFramebufferTexture2D); - _SAPP_GLPROC(glCreateProgram); - _SAPP_GLPROC(glViewport); - _SAPP_GLPROC(glDeleteBuffers); - _SAPP_GLPROC(glDrawArrays); - _SAPP_GLPROC(glDrawElementsInstanced); - _SAPP_GLPROC(glVertexAttribPointer); - _SAPP_GLPROC(glUniform1i); - _SAPP_GLPROC(glDisable); - _SAPP_GLPROC(glColorMask); - _SAPP_GLPROC(glBindBuffer); - _SAPP_GLPROC(glDeleteVertexArrays); - _SAPP_GLPROC(glDepthMask); - _SAPP_GLPROC(glDrawArraysInstanced); - _SAPP_GLPROC(glClearStencil); - _SAPP_GLPROC(glScissor); - _SAPP_GLPROC(glUniform3fv); - _SAPP_GLPROC(glGenRenderbuffers); - _SAPP_GLPROC(glBufferData); - _SAPP_GLPROC(glBlendFuncSeparate); - _SAPP_GLPROC(glTexParameteri); - _SAPP_GLPROC(glGetIntegerv); - _SAPP_GLPROC(glEnable); - _SAPP_GLPROC(glBlitFramebuffer); - _SAPP_GLPROC(glStencilMask); - _SAPP_GLPROC(glAttachShader); - _SAPP_GLPROC(glGetError); - _SAPP_GLPROC(glClearColor); - _SAPP_GLPROC(glBlendColor); - _SAPP_GLPROC(glTexParameterf); - _SAPP_GLPROC(glTexParameterfv); - _SAPP_GLPROC(glGetShaderInfoLog); - _SAPP_GLPROC(glDepthFunc); - _SAPP_GLPROC(glStencilOp); - _SAPP_GLPROC(glStencilFunc); - _SAPP_GLPROC(glEnableVertexAttribArray); - _SAPP_GLPROC(glBlendFunc); - _SAPP_GLPROC(glUniform1fv); - _SAPP_GLPROC(glReadBuffer); - _SAPP_GLPROC(glClear); - _SAPP_GLPROC(glTexImage2D); - _SAPP_GLPROC(glGenVertexArrays); - _SAPP_GLPROC(glFrontFace); - _SAPP_GLPROC(glCullFace); + SOKOL_ASSERT(_sapp.wgl.GetProcAddress); + SOKOL_ASSERT(_sapp.wgl.opengl32); + #define _SAPP_XMACRO(name, ret, args) name = (PFN_ ## name) _sapp_win32_glgetprocaddr(#name); + _SAPP_GL_FUNCS + #undef _SAPP_XMACRO } -#define glBindVertexArray _sapp_glBindVertexArray -#define glFramebufferTextureLayer _sapp_glFramebufferTextureLayer -#define glGenFramebuffers _sapp_glGenFramebuffers -#define glBindFramebuffer _sapp_glBindFramebuffer -#define glBindRenderbuffer _sapp_glBindRenderbuffer -#define glGetStringi _sapp_glGetStringi -#define glClearBufferfi _sapp_glClearBufferfi -#define glClearBufferfv _sapp_glClearBufferfv -#define glClearBufferuiv _sapp_glClearBufferuiv -#define glDeleteRenderbuffers _sapp_glDeleteRenderbuffers -#define glUniform4fv _sapp_glUniform4fv -#define glUniform2fv _sapp_glUniform2fv -#define glUseProgram _sapp_glUseProgram -#define glShaderSource _sapp_glShaderSource -#define glLinkProgram _sapp_glLinkProgram -#define glGetUniformLocation _sapp_glGetUniformLocation -#define glGetShaderiv _sapp_glGetShaderiv -#define glGetProgramInfoLog _sapp_glGetProgramInfoLog -#define glGetAttribLocation _sapp_glGetAttribLocation -#define glDisableVertexAttribArray _sapp_glDisableVertexAttribArray -#define glDeleteShader _sapp_glDeleteShader -#define glDeleteProgram _sapp_glDeleteProgram -#define glCompileShader _sapp_glCompileShader -#define glStencilFuncSeparate _sapp_glStencilFuncSeparate -#define glStencilOpSeparate _sapp_glStencilOpSeparate -#define glRenderbufferStorageMultisample _sapp_glRenderbufferStorageMultisample -#define glDrawBuffers _sapp_glDrawBuffers -#define glVertexAttribDivisor _sapp_glVertexAttribDivisor -#define glBufferSubData _sapp_glBufferSubData -#define glGenBuffers _sapp_glGenBuffers -#define glCheckFramebufferStatus _sapp_glCheckFramebufferStatus -#define glFramebufferRenderbuffer _sapp_glFramebufferRenderbuffer -#define glCompressedTexImage2D _sapp_glCompressedTexImage2D -#define glCompressedTexImage3D _sapp_glCompressedTexImage3D -#define glActiveTexture _sapp_glActiveTexture -#define glTexSubImage3D _sapp_glTexSubImage3D -#define glUniformMatrix4fv _sapp_glUniformMatrix4fv -#define glRenderbufferStorage _sapp_glRenderbufferStorage -#define glGenTextures _sapp_glGenTextures -#define glPolygonOffset _sapp_glPolygonOffset -#define glDrawElements _sapp_glDrawElements -#define glDeleteFramebuffers _sapp_glDeleteFramebuffers -#define glBlendEquationSeparate _sapp_glBlendEquationSeparate -#define glDeleteTextures _sapp_glDeleteTextures -#define glGetProgramiv _sapp_glGetProgramiv -#define glBindTexture _sapp_glBindTexture -#define glTexImage3D _sapp_glTexImage3D -#define glCreateShader _sapp_glCreateShader -#define glTexSubImage2D _sapp_glTexSubImage2D -#define glClearDepth _sapp_glClearDepth -#define glFramebufferTexture2D _sapp_glFramebufferTexture2D -#define glCreateProgram _sapp_glCreateProgram -#define glViewport _sapp_glViewport -#define glDeleteBuffers _sapp_glDeleteBuffers -#define glDrawArrays _sapp_glDrawArrays -#define glDrawElementsInstanced _sapp_glDrawElementsInstanced -#define glVertexAttribPointer _sapp_glVertexAttribPointer -#define glUniform1i _sapp_glUniform1i -#define glDisable _sapp_glDisable -#define glColorMask _sapp_glColorMask -#define glBindBuffer _sapp_glBindBuffer -#define glDeleteVertexArrays _sapp_glDeleteVertexArrays -#define glDepthMask _sapp_glDepthMask -#define glDrawArraysInstanced _sapp_glDrawArraysInstanced -#define glClearStencil _sapp_glClearStencil -#define glScissor _sapp_glScissor -#define glUniform3fv _sapp_glUniform3fv -#define glGenRenderbuffers _sapp_glGenRenderbuffers -#define glBufferData _sapp_glBufferData -#define glBlendFuncSeparate _sapp_glBlendFuncSeparate -#define glTexParameteri _sapp_glTexParameteri -#define glGetIntegerv _sapp_glGetIntegerv -#define glEnable _sapp_glEnable -#define glBlitFramebuffer _sapp_glBlitFramebuffer -#define glStencilMask _sapp_glStencilMask -#define glAttachShader _sapp_glAttachShader -#define glGetError _sapp_glGetError -#define glClearColor _sapp_glClearColor -#define glBlendColor _sapp_glBlendColor -#define glTexParameterf _sapp_glTexParameterf -#define glTexParameterfv _sapp_glTexParameterfv -#define glGetShaderInfoLog _sapp_glGetShaderInfoLog -#define glDepthFunc _sapp_glDepthFunc -#define glStencilOp _sapp_glStencilOp -#define glStencilFunc _sapp_glStencilFunc -#define glEnableVertexAttribArray _sapp_glEnableVertexAttribArray -#define glBlendFunc _sapp_glBlendFunc -#define glUniform1fv _sapp_glUniform1fv -#define glReadBuffer _sapp_glReadBuffer -#define glClear _sapp_glClear -#define glTexImage2D _sapp_glTexImage2D -#define glGenVertexArrays _sapp_glGenVertexArrays -#define glFrontFace _sapp_glFrontFace -#define glCullFace _sapp_glCullFace -#endif /* SOKOL_WIN32_NO_GL_LOADER */ +#endif // _SAPP_WIN32 && SOKOL_GLCORE33 && !SOKOL_WIN32_NO_GL_LOADER -#endif /* SOKOL_GLCORE33 */ +/*=== PRIVATE HELPER FUNCTIONS ===============================================*/ +_SOKOL_PRIVATE void _sapp_fail(const char* msg) { + if (_sapp.desc.fail_cb) { + _sapp.desc.fail_cb(msg); + } + else if (_sapp.desc.fail_userdata_cb) { + _sapp.desc.fail_userdata_cb(msg, _sapp.desc.user_data); + } + else { + SOKOL_LOG(msg); + } + SOKOL_ABORT(); +} + +_SOKOL_PRIVATE void _sapp_call_init(void) { + if (_sapp.desc.init_cb) { + _sapp.desc.init_cb(); + } + else if (_sapp.desc.init_userdata_cb) { + _sapp.desc.init_userdata_cb(_sapp.desc.user_data); + } + _sapp.init_called = true; +} + +_SOKOL_PRIVATE void _sapp_call_frame(void) { + if (_sapp.init_called && !_sapp.cleanup_called) { + if (_sapp.desc.frame_cb) { + _sapp.desc.frame_cb(); + } + else if (_sapp.desc.frame_userdata_cb) { + _sapp.desc.frame_userdata_cb(_sapp.desc.user_data); + } + } +} + +_SOKOL_PRIVATE void _sapp_call_cleanup(void) { + if (!_sapp.cleanup_called) { + if (_sapp.desc.cleanup_cb) { + _sapp.desc.cleanup_cb(); + } + else if (_sapp.desc.cleanup_userdata_cb) { + _sapp.desc.cleanup_userdata_cb(_sapp.desc.user_data); + } + _sapp.cleanup_called = true; + } +} + +_SOKOL_PRIVATE bool _sapp_call_event(const sapp_event* e) { + if (!_sapp.cleanup_called) { + if (_sapp.desc.event_cb) { + _sapp.desc.event_cb(e); + } + else if (_sapp.desc.event_userdata_cb) { + _sapp.desc.event_userdata_cb(e, _sapp.desc.user_data); + } + } + if (_sapp.event_consumed) { + _sapp.event_consumed = false; + return true; + } + else { + return false; + } +} + +_SOKOL_PRIVATE void _sapp_strcpy(const char* src, char* dst, int max_len) { + SOKOL_ASSERT(src && dst && (max_len > 0)); + char* const end = &(dst[max_len-1]); + char c = 0; + for (int i = 0; i < max_len; i++) { + c = *src; + if (c != 0) { + src++; + } + *dst++ = c; + } + /* truncated? */ + if (c != 0) { + *end = 0; + } +} + +_SOKOL_PRIVATE sapp_desc _sapp_desc_defaults(const sapp_desc* in_desc) { + sapp_desc desc = *in_desc; + desc.width = _sapp_def(desc.width, 640); + desc.height = _sapp_def(desc.height, 480); + desc.sample_count = _sapp_def(desc.sample_count, 1); + desc.swap_interval = _sapp_def(desc.swap_interval, 1); + desc.html5_canvas_name = _sapp_def(desc.html5_canvas_name, "canvas"); + desc.clipboard_size = _sapp_def(desc.clipboard_size, 8192); + desc.window_title = _sapp_def(desc.window_title, "sokol_app"); + return desc; +} + +_SOKOL_PRIVATE void _sapp_init_state(const sapp_desc* desc) { + _SAPP_CLEAR(_sapp_t, _sapp); + _sapp.desc = _sapp_desc_defaults(desc); + _sapp.first_frame = true; + _sapp.window_width = _sapp.desc.width; + _sapp.window_height = _sapp.desc.height; + _sapp.framebuffer_width = _sapp.window_width; + _sapp.framebuffer_height = _sapp.window_height; + _sapp.sample_count = _sapp.desc.sample_count; + _sapp.swap_interval = _sapp.desc.swap_interval; + _sapp_strcpy(_sapp.desc.html5_canvas_name, _sapp.html5_canvas_name, sizeof(_sapp.html5_canvas_name)); + _sapp.desc.html5_canvas_name = _sapp.html5_canvas_name; + _sapp.html5_ask_leave_site = _sapp.desc.html5_ask_leave_site; + _sapp.clipboard.enabled = _sapp.desc.enable_clipboard; + if (_sapp.clipboard.enabled) { + _sapp.clipboard.buf_size = _sapp.desc.clipboard_size; + _sapp.clipboard.buffer = (char*) SOKOL_CALLOC(1, _sapp.clipboard.buf_size); + } + _sapp_strcpy(_sapp.desc.window_title, _sapp.window_title, sizeof(_sapp.window_title)); + _sapp.desc.window_title = _sapp.window_title; + _sapp.dpi_scale = 1.0f; + _sapp.fullscreen = _sapp.desc.fullscreen; + _sapp.mouse.shown = true; +} + +_SOKOL_PRIVATE void _sapp_discard_state(void) { + if (_sapp.clipboard.enabled) { + SOKOL_ASSERT(_sapp.clipboard.buffer); + SOKOL_FREE((void*)_sapp.clipboard.buffer); + } + _SAPP_CLEAR(_sapp_t, _sapp); +} + +_SOKOL_PRIVATE void _sapp_init_event(sapp_event_type type) { + memset(&_sapp.event, 0, sizeof(_sapp.event)); + _sapp.event.type = type; + _sapp.event.frame_count = _sapp.frame_count; + _sapp.event.mouse_button = SAPP_MOUSEBUTTON_INVALID; + _sapp.event.window_width = _sapp.window_width; + _sapp.event.window_height = _sapp.window_height; + _sapp.event.framebuffer_width = _sapp.framebuffer_width; + _sapp.event.framebuffer_height = _sapp.framebuffer_height; + _sapp.event.mouse_x = _sapp.mouse.x; + _sapp.event.mouse_y = _sapp.mouse.y; + _sapp.event.mouse_dx = _sapp.mouse.dx; + _sapp.event.mouse_dy = _sapp.mouse.dy; +} + +_SOKOL_PRIVATE bool _sapp_events_enabled(void) { + /* only send events when an event callback is set, and the init function was called */ + return (_sapp.desc.event_cb || _sapp.desc.event_userdata_cb) && _sapp.init_called; +} + +_SOKOL_PRIVATE sapp_keycode _sapp_translate_key(int scan_code) { + if ((scan_code >= 0) && (scan_code < SAPP_MAX_KEYCODES)) { + return _sapp.keycodes[scan_code]; + } + else { + return SAPP_KEYCODE_INVALID; + } +} + +_SOKOL_PRIVATE void _sapp_frame(void) { + if (_sapp.first_frame) { + _sapp.first_frame = false; + _sapp_call_init(); + } + _sapp_call_frame(); + _sapp.frame_count++; +} + +/*== MacOS/iOS ===============================================================*/ +#if defined(_SAPP_APPLE) + +#if __has_feature(objc_arc) +#define _SAPP_OBJC_RELEASE(obj) { obj = nil; } +#else +#define _SAPP_OBJC_RELEASE(obj) { [obj release]; obj = nil; } +#endif + +/*== MacOS ===================================================================*/ +#if defined(_SAPP_MACOS) + +_SOKOL_PRIVATE void _sapp_macos_init_keytable(void) { + _sapp.keycodes[0x1D] = SAPP_KEYCODE_0; + _sapp.keycodes[0x12] = SAPP_KEYCODE_1; + _sapp.keycodes[0x13] = SAPP_KEYCODE_2; + _sapp.keycodes[0x14] = SAPP_KEYCODE_3; + _sapp.keycodes[0x15] = SAPP_KEYCODE_4; + _sapp.keycodes[0x17] = SAPP_KEYCODE_5; + _sapp.keycodes[0x16] = SAPP_KEYCODE_6; + _sapp.keycodes[0x1A] = SAPP_KEYCODE_7; + _sapp.keycodes[0x1C] = SAPP_KEYCODE_8; + _sapp.keycodes[0x19] = SAPP_KEYCODE_9; + _sapp.keycodes[0x00] = SAPP_KEYCODE_A; + _sapp.keycodes[0x0B] = SAPP_KEYCODE_B; + _sapp.keycodes[0x08] = SAPP_KEYCODE_C; + _sapp.keycodes[0x02] = SAPP_KEYCODE_D; + _sapp.keycodes[0x0E] = SAPP_KEYCODE_E; + _sapp.keycodes[0x03] = SAPP_KEYCODE_F; + _sapp.keycodes[0x05] = SAPP_KEYCODE_G; + _sapp.keycodes[0x04] = SAPP_KEYCODE_H; + _sapp.keycodes[0x22] = SAPP_KEYCODE_I; + _sapp.keycodes[0x26] = SAPP_KEYCODE_J; + _sapp.keycodes[0x28] = SAPP_KEYCODE_K; + _sapp.keycodes[0x25] = SAPP_KEYCODE_L; + _sapp.keycodes[0x2E] = SAPP_KEYCODE_M; + _sapp.keycodes[0x2D] = SAPP_KEYCODE_N; + _sapp.keycodes[0x1F] = SAPP_KEYCODE_O; + _sapp.keycodes[0x23] = SAPP_KEYCODE_P; + _sapp.keycodes[0x0C] = SAPP_KEYCODE_Q; + _sapp.keycodes[0x0F] = SAPP_KEYCODE_R; + _sapp.keycodes[0x01] = SAPP_KEYCODE_S; + _sapp.keycodes[0x11] = SAPP_KEYCODE_T; + _sapp.keycodes[0x20] = SAPP_KEYCODE_U; + _sapp.keycodes[0x09] = SAPP_KEYCODE_V; + _sapp.keycodes[0x0D] = SAPP_KEYCODE_W; + _sapp.keycodes[0x07] = SAPP_KEYCODE_X; + _sapp.keycodes[0x10] = SAPP_KEYCODE_Y; + _sapp.keycodes[0x06] = SAPP_KEYCODE_Z; + _sapp.keycodes[0x27] = SAPP_KEYCODE_APOSTROPHE; + _sapp.keycodes[0x2A] = SAPP_KEYCODE_BACKSLASH; + _sapp.keycodes[0x2B] = SAPP_KEYCODE_COMMA; + _sapp.keycodes[0x18] = SAPP_KEYCODE_EQUAL; + _sapp.keycodes[0x32] = SAPP_KEYCODE_GRAVE_ACCENT; + _sapp.keycodes[0x21] = SAPP_KEYCODE_LEFT_BRACKET; + _sapp.keycodes[0x1B] = SAPP_KEYCODE_MINUS; + _sapp.keycodes[0x2F] = SAPP_KEYCODE_PERIOD; + _sapp.keycodes[0x1E] = SAPP_KEYCODE_RIGHT_BRACKET; + _sapp.keycodes[0x29] = SAPP_KEYCODE_SEMICOLON; + _sapp.keycodes[0x2C] = SAPP_KEYCODE_SLASH; + _sapp.keycodes[0x0A] = SAPP_KEYCODE_WORLD_1; + _sapp.keycodes[0x33] = SAPP_KEYCODE_BACKSPACE; + _sapp.keycodes[0x39] = SAPP_KEYCODE_CAPS_LOCK; + _sapp.keycodes[0x75] = SAPP_KEYCODE_DELETE; + _sapp.keycodes[0x7D] = SAPP_KEYCODE_DOWN; + _sapp.keycodes[0x77] = SAPP_KEYCODE_END; + _sapp.keycodes[0x24] = SAPP_KEYCODE_ENTER; + _sapp.keycodes[0x35] = SAPP_KEYCODE_ESCAPE; + _sapp.keycodes[0x7A] = SAPP_KEYCODE_F1; + _sapp.keycodes[0x78] = SAPP_KEYCODE_F2; + _sapp.keycodes[0x63] = SAPP_KEYCODE_F3; + _sapp.keycodes[0x76] = SAPP_KEYCODE_F4; + _sapp.keycodes[0x60] = SAPP_KEYCODE_F5; + _sapp.keycodes[0x61] = SAPP_KEYCODE_F6; + _sapp.keycodes[0x62] = SAPP_KEYCODE_F7; + _sapp.keycodes[0x64] = SAPP_KEYCODE_F8; + _sapp.keycodes[0x65] = SAPP_KEYCODE_F9; + _sapp.keycodes[0x6D] = SAPP_KEYCODE_F10; + _sapp.keycodes[0x67] = SAPP_KEYCODE_F11; + _sapp.keycodes[0x6F] = SAPP_KEYCODE_F12; + _sapp.keycodes[0x69] = SAPP_KEYCODE_F13; + _sapp.keycodes[0x6B] = SAPP_KEYCODE_F14; + _sapp.keycodes[0x71] = SAPP_KEYCODE_F15; + _sapp.keycodes[0x6A] = SAPP_KEYCODE_F16; + _sapp.keycodes[0x40] = SAPP_KEYCODE_F17; + _sapp.keycodes[0x4F] = SAPP_KEYCODE_F18; + _sapp.keycodes[0x50] = SAPP_KEYCODE_F19; + _sapp.keycodes[0x5A] = SAPP_KEYCODE_F20; + _sapp.keycodes[0x73] = SAPP_KEYCODE_HOME; + _sapp.keycodes[0x72] = SAPP_KEYCODE_INSERT; + _sapp.keycodes[0x7B] = SAPP_KEYCODE_LEFT; + _sapp.keycodes[0x3A] = SAPP_KEYCODE_LEFT_ALT; + _sapp.keycodes[0x3B] = SAPP_KEYCODE_LEFT_CONTROL; + _sapp.keycodes[0x38] = SAPP_KEYCODE_LEFT_SHIFT; + _sapp.keycodes[0x37] = SAPP_KEYCODE_LEFT_SUPER; + _sapp.keycodes[0x6E] = SAPP_KEYCODE_MENU; + _sapp.keycodes[0x47] = SAPP_KEYCODE_NUM_LOCK; + _sapp.keycodes[0x79] = SAPP_KEYCODE_PAGE_DOWN; + _sapp.keycodes[0x74] = SAPP_KEYCODE_PAGE_UP; + _sapp.keycodes[0x7C] = SAPP_KEYCODE_RIGHT; + _sapp.keycodes[0x3D] = SAPP_KEYCODE_RIGHT_ALT; + _sapp.keycodes[0x3E] = SAPP_KEYCODE_RIGHT_CONTROL; + _sapp.keycodes[0x3C] = SAPP_KEYCODE_RIGHT_SHIFT; + _sapp.keycodes[0x36] = SAPP_KEYCODE_RIGHT_SUPER; + _sapp.keycodes[0x31] = SAPP_KEYCODE_SPACE; + _sapp.keycodes[0x30] = SAPP_KEYCODE_TAB; + _sapp.keycodes[0x7E] = SAPP_KEYCODE_UP; + _sapp.keycodes[0x52] = SAPP_KEYCODE_KP_0; + _sapp.keycodes[0x53] = SAPP_KEYCODE_KP_1; + _sapp.keycodes[0x54] = SAPP_KEYCODE_KP_2; + _sapp.keycodes[0x55] = SAPP_KEYCODE_KP_3; + _sapp.keycodes[0x56] = SAPP_KEYCODE_KP_4; + _sapp.keycodes[0x57] = SAPP_KEYCODE_KP_5; + _sapp.keycodes[0x58] = SAPP_KEYCODE_KP_6; + _sapp.keycodes[0x59] = SAPP_KEYCODE_KP_7; + _sapp.keycodes[0x5B] = SAPP_KEYCODE_KP_8; + _sapp.keycodes[0x5C] = SAPP_KEYCODE_KP_9; + _sapp.keycodes[0x45] = SAPP_KEYCODE_KP_ADD; + _sapp.keycodes[0x41] = SAPP_KEYCODE_KP_DECIMAL; + _sapp.keycodes[0x4B] = SAPP_KEYCODE_KP_DIVIDE; + _sapp.keycodes[0x4C] = SAPP_KEYCODE_KP_ENTER; + _sapp.keycodes[0x51] = SAPP_KEYCODE_KP_EQUAL; + _sapp.keycodes[0x43] = SAPP_KEYCODE_KP_MULTIPLY; + _sapp.keycodes[0x4E] = SAPP_KEYCODE_KP_SUBTRACT; +} + +_SOKOL_PRIVATE void _sapp_macos_discard_state(void) { + // NOTE: it's safe to call [release] on a nil object + _SAPP_OBJC_RELEASE(_sapp.macos.tracking_area); + _SAPP_OBJC_RELEASE(_sapp.macos.app_dlg); + _SAPP_OBJC_RELEASE(_sapp.macos.win_dlg); + _SAPP_OBJC_RELEASE(_sapp.macos.view); + #if defined(SOKOL_METAL) + _SAPP_OBJC_RELEASE(_sapp.macos.mtl_device); + #endif + _SAPP_OBJC_RELEASE(_sapp.macos.window); +} + +_SOKOL_PRIVATE void _sapp_macos_run(const sapp_desc* desc) { + _sapp_init_state(desc); + _sapp_macos_init_keytable(); + [NSApplication sharedApplication]; + NSApp.activationPolicy = NSApplicationActivationPolicyRegular; + _sapp.macos.app_dlg = [[_sapp_macos_app_delegate alloc] init]; + NSApp.delegate = _sapp.macos.app_dlg; + [NSApp activateIgnoringOtherApps:YES]; + [NSApp run]; + // NOTE: [NSApp run] never returns, instead cleanup code + // must be put into applicationWillTerminate +} + +/* MacOS entry function */ +#if !defined(SOKOL_NO_ENTRY) +int main(int argc, char* argv[]) { + sapp_desc desc = sokol_main(argc, argv); + _sapp_macos_run(&desc); + return 0; +} +#endif /* SOKOL_NO_ENTRY */ + +_SOKOL_PRIVATE uint32_t _sapp_macos_mod(NSEventModifierFlags f) { + uint32_t m = 0; + if (f & NSEventModifierFlagShift) { + m |= SAPP_MODIFIER_SHIFT; + } + if (f & NSEventModifierFlagControl) { + m |= SAPP_MODIFIER_CTRL; + } + if (f & NSEventModifierFlagOption) { + m |= SAPP_MODIFIER_ALT; + } + if (f & NSEventModifierFlagCommand) { + m |= SAPP_MODIFIER_SUPER; + } + return m; +} + +_SOKOL_PRIVATE void _sapp_macos_mouse_event(sapp_event_type type, sapp_mousebutton btn, uint32_t mod) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + _sapp.event.mouse_button = btn; + _sapp.event.modifiers = mod; + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_macos_key_event(sapp_event_type type, sapp_keycode key, bool repeat, uint32_t mod) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + _sapp.event.key_code = key; + _sapp.event.key_repeat = repeat; + _sapp.event.modifiers = mod; + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_macos_app_event(sapp_event_type type) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_macos_update_dimensions(void) { + #if defined(SOKOL_METAL) + const CGSize fb_size = [_sapp.macos.view drawableSize]; + _sapp.framebuffer_width = fb_size.width; + _sapp.framebuffer_height = fb_size.height; + #elif defined(SOKOL_GLCORE33) + const NSRect fb_rect = [_sapp.macos.view convertRectToBacking:[_sapp.macos.view frame]]; + _sapp.framebuffer_width = fb_rect.size.width; + _sapp.framebuffer_height = fb_rect.size.height; + #endif + const NSRect bounds = [_sapp.macos.view bounds]; + _sapp.window_width = bounds.size.width; + _sapp.window_height = bounds.size.height; + if (_sapp.framebuffer_width == 0) { + _sapp.framebuffer_width = 1; + } + if (_sapp.framebuffer_height == 0) { + _sapp.framebuffer_height = 1; + } + if (_sapp.window_width == 0) { + _sapp.window_width = 1; + } + if (_sapp.window_height == 0) { + _sapp.window_height = 1; + } + _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float)_sapp.window_width; +} + +_SOKOL_PRIVATE void _sapp_macos_toggle_fullscreen(void) { + /* NOTE: the _sapp.fullscreen flag is also notified by the + windowDidEnterFullscreen / windowDidExitFullscreen + event handlers + */ + _sapp.fullscreen = !_sapp.fullscreen; + [_sapp.macos.window toggleFullScreen:nil]; +} + +_SOKOL_PRIVATE void _sapp_macos_set_clipboard_string(const char* str) { + @autoreleasepool { + NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; + [pasteboard declareTypes:@[NSPasteboardTypeString] owner:nil]; + [pasteboard setString:@(str) forType:NSPasteboardTypeString]; + } +} + +_SOKOL_PRIVATE const char* _sapp_macos_get_clipboard_string(void) { + SOKOL_ASSERT(_sapp.clipboard.buffer); + @autoreleasepool { + _sapp.clipboard.buffer[0] = 0; + NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; + if (![[pasteboard types] containsObject:NSPasteboardTypeString]) { + return _sapp.clipboard.buffer; + } + NSString* str = [pasteboard stringForType:NSPasteboardTypeString]; + if (!str) { + return _sapp.clipboard.buffer; + } + _sapp_strcpy([str UTF8String], _sapp.clipboard.buffer, _sapp.clipboard.buf_size); + } + return _sapp.clipboard.buffer; +} + +_SOKOL_PRIVATE void _sapp_macos_update_mouse(void) { + if (!_sapp.mouse.locked) { + const NSPoint mouse_pos = [_sapp.macos.window mouseLocationOutsideOfEventStream]; + float new_x = mouse_pos.x * _sapp.dpi_scale; + float new_y = _sapp.framebuffer_height - (mouse_pos.y * _sapp.dpi_scale) - 1; + /* don't update dx/dy in the very first update */ + if (_sapp.mouse.pos_valid) { + _sapp.mouse.dx = new_x - _sapp.mouse.x; + _sapp.mouse.dy = new_y - _sapp.mouse.y; + } + _sapp.mouse.x = new_x; + _sapp.mouse.y = new_y; + _sapp.mouse.pos_valid = true; + } +} + +_SOKOL_PRIVATE void _sapp_macos_show_mouse(bool visible) { + /* NOTE: this function is only called when the mouse visibility actually changes */ + if (visible) { + CGDisplayShowCursor(kCGDirectMainDisplay); + } + else { + CGDisplayHideCursor(kCGDirectMainDisplay); + } +} + +_SOKOL_PRIVATE void _sapp_macos_lock_mouse(bool lock) { + if (lock == _sapp.mouse.locked) { + return; + } + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; + _sapp.mouse.locked = lock; + /* + NOTE that this code doesn't warp the mouse cursor to the window + center as everybody else does it. This lead to a spike in the + *second* mouse-moved event after the warp happened. The + mouse centering doesn't seem to be required (mouse-moved events + are reported correctly even when the cursor is at an edge of the screen). + + NOTE also that the hide/show of the mouse cursor should properly + stack with calls to sapp_show_mouse() + */ + if (_sapp.mouse.locked) { + [NSEvent setMouseCoalescingEnabled:NO]; + CGAssociateMouseAndMouseCursorPosition(NO); + CGDisplayHideCursor(kCGDirectMainDisplay); + } + else { + CGDisplayShowCursor(kCGDirectMainDisplay); + CGAssociateMouseAndMouseCursorPosition(YES); + [NSEvent setMouseCoalescingEnabled:YES]; + } +} + +_SOKOL_PRIVATE void _sapp_macos_frame(void) { + _sapp_macos_update_mouse(); + _sapp_frame(); + if (_sapp.quit_requested || _sapp.quit_ordered) { + [_sapp.macos.window performClose:nil]; + } +} + +@implementation _sapp_macos_app_delegate +- (void)applicationDidFinishLaunching:(NSNotification*)aNotification { + _SOKOL_UNUSED(aNotification); + if (_sapp.fullscreen) { + NSRect screen_rect = NSScreen.mainScreen.frame; + _sapp.window_width = screen_rect.size.width; + _sapp.window_height = screen_rect.size.height; + if (_sapp.desc.high_dpi) { + _sapp.framebuffer_width = 2 * _sapp.window_width; + _sapp.framebuffer_height = 2 * _sapp.window_height; + } + else { + _sapp.framebuffer_width = _sapp.window_width; + _sapp.framebuffer_height = _sapp.window_height; + } + _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float) _sapp.window_width; + } + const NSUInteger style = + NSWindowStyleMaskTitled | + NSWindowStyleMaskClosable | + NSWindowStyleMaskMiniaturizable | + NSWindowStyleMaskResizable; + NSRect window_rect = NSMakeRect(0, 0, _sapp.window_width, _sapp.window_height); + _sapp.macos.window = [[NSWindow alloc] + initWithContentRect:window_rect + styleMask:style + backing:NSBackingStoreBuffered + defer:NO]; + _sapp.macos.window.releasedWhenClosed = NO; // this is necessary for proper cleanup in applicationWillTerminate + _sapp.macos.window.title = [NSString stringWithUTF8String:_sapp.window_title]; + _sapp.macos.window.acceptsMouseMovedEvents = YES; + _sapp.macos.window.restorable = YES; + + _sapp.macos.win_dlg = [[_sapp_macos_window_delegate alloc] init]; + _sapp.macos.window.delegate = _sapp.macos.win_dlg; + #if defined(SOKOL_METAL) + _sapp.macos.mtl_device = MTLCreateSystemDefaultDevice(); + _sapp.macos.view = [[_sapp_macos_view alloc] init]; + [_sapp.macos.view updateTrackingAreas]; + _sapp.macos.view.preferredFramesPerSecond = 60 / _sapp.swap_interval; + _sapp.macos.view.device = _sapp.macos.mtl_device; + _sapp.macos.view.colorPixelFormat = MTLPixelFormatBGRA8Unorm; + _sapp.macos.view.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8; + _sapp.macos.view.sampleCount = _sapp.sample_count; + _sapp.macos.window.contentView = _sapp.macos.view; + [_sapp.macos.window makeFirstResponder:_sapp.macos.view]; + if (!_sapp.desc.high_dpi) { + CGSize drawable_size = { (CGFloat) _sapp.framebuffer_width, (CGFloat) _sapp.framebuffer_height }; + _sapp.macos.view.drawableSize = drawable_size; + } + _sapp.macos.view.layer.magnificationFilter = kCAFilterNearest; + #elif defined(SOKOL_GLCORE33) + NSOpenGLPixelFormatAttribute attrs[32]; + int i = 0; + attrs[i++] = NSOpenGLPFAAccelerated; + attrs[i++] = NSOpenGLPFADoubleBuffer; + attrs[i++] = NSOpenGLPFAOpenGLProfile; attrs[i++] = NSOpenGLProfileVersion3_2Core; + attrs[i++] = NSOpenGLPFAColorSize; attrs[i++] = 24; + attrs[i++] = NSOpenGLPFAAlphaSize; attrs[i++] = 8; + attrs[i++] = NSOpenGLPFADepthSize; attrs[i++] = 24; + attrs[i++] = NSOpenGLPFAStencilSize; attrs[i++] = 8; + if (_sapp.sample_count > 1) { + attrs[i++] = NSOpenGLPFAMultisample; + attrs[i++] = NSOpenGLPFASampleBuffers; attrs[i++] = 1; + attrs[i++] = NSOpenGLPFASamples; attrs[i++] = _sapp.sample_count; + } + else { + attrs[i++] = NSOpenGLPFASampleBuffers; attrs[i++] = 0; + } + attrs[i++] = 0; + NSOpenGLPixelFormat* glpixelformat_obj = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs]; + SOKOL_ASSERT(glpixelformat_obj != nil); + + _sapp.macos.view = [[_sapp_macos_view alloc] + initWithFrame:window_rect + pixelFormat:glpixelformat_obj]; + _SAPP_OBJC_RELEASE(glpixelformat_obj); + [_sapp.macos.view updateTrackingAreas]; + if (_sapp.desc.high_dpi) { + [_sapp.macos.view setWantsBestResolutionOpenGLSurface:YES]; + } + else { + [_sapp.macos.view setWantsBestResolutionOpenGLSurface:NO]; + } + + _sapp.macos.window.contentView = _sapp.macos.view; + [_sapp.macos.window makeFirstResponder:_sapp.macos.view]; + + NSTimer* timer_obj = [NSTimer timerWithTimeInterval:0.001 + target:_sapp.macos.view + selector:@selector(timerFired:) + userInfo:nil + repeats:YES]; + [[NSRunLoop currentRunLoop] addTimer:timer_obj forMode:NSDefaultRunLoopMode]; + timer_obj = nil; + #endif + _sapp.valid = true; + if (_sapp.fullscreen) { + /* on GL, this already toggles a rendered frame, so set the valid flag before */ + [_sapp.macos.window toggleFullScreen:self]; + } + else { + [_sapp.macos.window center]; + } + [_sapp.macos.window makeKeyAndOrderFront:nil]; + _sapp_macos_update_dimensions(); +} + +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)sender { + _SOKOL_UNUSED(sender); + return YES; +} + +- (void)applicationWillTerminate:(NSNotification*)notification { + _SOKOL_UNUSED(notification); + _sapp_call_cleanup(); + _sapp_macos_discard_state(); + _sapp_discard_state(); +} +@end + +@implementation _sapp_macos_window_delegate +- (BOOL)windowShouldClose:(id)sender { + _SOKOL_UNUSED(sender); + /* only give user-code a chance to intervene when sapp_quit() wasn't already called */ + if (!_sapp.quit_ordered) { + /* if window should be closed and event handling is enabled, give user code + a chance to intervene via sapp_cancel_quit() + */ + _sapp.quit_requested = true; + _sapp_macos_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED); + /* user code hasn't intervened, quit the app */ + if (_sapp.quit_requested) { + _sapp.quit_ordered = true; + } + } + if (_sapp.quit_ordered) { + return YES; + } + else { + return NO; + } +} + +- (void)windowDidResize:(NSNotification*)notification { + _SOKOL_UNUSED(notification); + _sapp_macos_update_dimensions(); + if (!_sapp.first_frame) { + _sapp_macos_app_event(SAPP_EVENTTYPE_RESIZED); + } +} + +- (void)windowDidMiniaturize:(NSNotification*)notification { + _SOKOL_UNUSED(notification); + _sapp_macos_app_event(SAPP_EVENTTYPE_ICONIFIED); +} + +- (void)windowDidDeminiaturize:(NSNotification*)notification { + _SOKOL_UNUSED(notification); + _sapp_macos_app_event(SAPP_EVENTTYPE_RESTORED); +} + +- (void)windowDidEnterFullScreen:(NSNotification*)notification { + _SOKOL_UNUSED(notification); + _sapp.fullscreen = true; +} + +- (void)windowDidExitFullScreen:(NSNotification*)notification { + _SOKOL_UNUSED(notification); + _sapp.fullscreen = false; +} +@end + +@implementation _sapp_macos_view +#if defined(SOKOL_GLCORE33) +/* NOTE: this is a hack/fix when the initial window size has been clipped by + macOS because it didn't fit on the screen, in that case the + frame size of the window is reported wrong if low-dpi rendering + was requested (instead the high-dpi dimensions are returned) + until the window is resized for the first time. + + Hooking into reshape and getting the frame dimensions seems to report + the correct dimensions. +*/ +- (void)reshape { + _sapp_macos_update_dimensions(); + [super reshape]; +} +- (void)timerFired:(id)sender { + _SOKOL_UNUSED(sender); + [self setNeedsDisplay:YES]; +} +- (void)prepareOpenGL { + [super prepareOpenGL]; + GLint swapInt = 1; + NSOpenGLContext* ctx = [_sapp.macos.view openGLContext]; + [ctx setValues:&swapInt forParameter:NSOpenGLContextParameterSwapInterval]; + [ctx makeCurrentContext]; +} +#endif + +- (void)drawRect:(NSRect)rect { + _SOKOL_UNUSED(rect); + _sapp_macos_frame(); + #if !defined(SOKOL_METAL) + [[_sapp.macos.view openGLContext] flushBuffer]; + #endif +} + +- (BOOL)isOpaque { + return YES; +} +- (BOOL)canBecomeKeyView { + return YES; +} +- (BOOL)acceptsFirstResponder { + return YES; +} +- (void)updateTrackingAreas { + if (_sapp.macos.tracking_area != nil) { + [self removeTrackingArea:_sapp.macos.tracking_area]; + _SAPP_OBJC_RELEASE(_sapp.macos.tracking_area); + } + const NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | + NSTrackingActiveInKeyWindow | + NSTrackingEnabledDuringMouseDrag | + NSTrackingCursorUpdate | + NSTrackingInVisibleRect | + NSTrackingAssumeInside; + _sapp.macos.tracking_area = [[NSTrackingArea alloc] initWithRect:[self bounds] options:options owner:self userInfo:nil]; + [self addTrackingArea:_sapp.macos.tracking_area]; + [super updateTrackingAreas]; +} +- (void)mouseEntered:(NSEvent*)event { + _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mod(event.modifierFlags)); +} +- (void)mouseExited:(NSEvent*)event { + _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mod(event.modifierFlags)); +} +- (void)mouseDown:(NSEvent*)event { + _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_LEFT, _sapp_macos_mod(event.modifierFlags)); +} +- (void)mouseUp:(NSEvent*)event { + _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_LEFT, _sapp_macos_mod(event.modifierFlags)); +} +- (void)rightMouseDown:(NSEvent*)event { + _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_RIGHT, _sapp_macos_mod(event.modifierFlags)); +} +- (void)rightMouseUp:(NSEvent*)event { + _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_RIGHT, _sapp_macos_mod(event.modifierFlags)); +} +- (void)otherMouseDown:(NSEvent*)event { + if (2 == event.buttonNumber) { + _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_MIDDLE, _sapp_macos_mod(event.modifierFlags)); + } +} +- (void)otherMouseUp:(NSEvent*)event { + if (2 == event.buttonNumber) { + _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_MIDDLE, _sapp_macos_mod(event.modifierFlags)); + } +} +// FIXME: otherMouseDragged? +- (void)mouseMoved:(NSEvent*)event { + if (_sapp.mouse.locked) { + _sapp.mouse.dx = [event deltaX]; + _sapp.mouse.dy = [event deltaY]; + } + _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID , _sapp_macos_mod(event.modifierFlags)); +} +- (void)mouseDragged:(NSEvent*)event { + if (_sapp.mouse.locked) { + _sapp.mouse.dx = [event deltaX]; + _sapp.mouse.dy = [event deltaY]; + } + _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID , _sapp_macos_mod(event.modifierFlags)); +} +- (void)rightMouseDragged:(NSEvent*)event { + if (_sapp.mouse.locked) { + _sapp.mouse.dx = [event deltaX]; + _sapp.mouse.dy = [event deltaY]; + } + _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mod(event.modifierFlags)); +} +- (void)scrollWheel:(NSEvent*)event { + if (_sapp_events_enabled()) { + float dx = (float) event.scrollingDeltaX; + float dy = (float) event.scrollingDeltaY; + if (event.hasPreciseScrollingDeltas) { + dx *= 0.1; + dy *= 0.1; + } + if ((_sapp_absf(dx) > 0.0f) || (_sapp_absf(dy) > 0.0f)) { + _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); + _sapp.event.modifiers = _sapp_macos_mod(event.modifierFlags); + _sapp.event.scroll_x = dx; + _sapp.event.scroll_y = dy; + _sapp_call_event(&_sapp.event); + } + } +} +- (void)keyDown:(NSEvent*)event { + if (_sapp_events_enabled()) { + const uint32_t mods = _sapp_macos_mod(event.modifierFlags); + /* NOTE: macOS doesn't send keyUp events while the Cmd key is pressed, + as a workaround, to prevent key presses from sticking we'll send + a keyup event following right after the keydown if SUPER is also pressed + */ + const sapp_keycode key_code = _sapp_translate_key(event.keyCode); + _sapp_macos_key_event(SAPP_EVENTTYPE_KEY_DOWN, key_code, event.isARepeat, mods); + if (0 != (mods & SAPP_MODIFIER_SUPER)) { + _sapp_macos_key_event(SAPP_EVENTTYPE_KEY_UP, key_code, event.isARepeat, mods); + } + const NSString* chars = event.characters; + const NSUInteger len = chars.length; + if (len > 0) { + _sapp_init_event(SAPP_EVENTTYPE_CHAR); + _sapp.event.modifiers = mods; + for (NSUInteger i = 0; i < len; i++) { + const unichar codepoint = [chars characterAtIndex:i]; + if ((codepoint & 0xFF00) == 0xF700) { + continue; + } + _sapp.event.char_code = codepoint; + _sapp.event.key_repeat = event.isARepeat; + _sapp_call_event(&_sapp.event); + } + } + /* if this is a Cmd+V (paste), also send a CLIPBOARD_PASTE event */ + if (_sapp.clipboard.enabled && (mods == SAPP_MODIFIER_SUPER) && (key_code == SAPP_KEYCODE_V)) { + _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED); + _sapp_call_event(&_sapp.event); + } + } +} +- (void)keyUp:(NSEvent*)event { + _sapp_macos_key_event(SAPP_EVENTTYPE_KEY_UP, + _sapp_translate_key(event.keyCode), + event.isARepeat, + _sapp_macos_mod(event.modifierFlags)); +} +- (void)flagsChanged:(NSEvent*)event { + const uint32_t old_f = _sapp.macos.flags_changed_store; + const uint32_t new_f = event.modifierFlags; + _sapp.macos.flags_changed_store = new_f; + sapp_keycode key_code = SAPP_KEYCODE_INVALID; + bool down = false; + if ((new_f ^ old_f) & NSEventModifierFlagShift) { + key_code = SAPP_KEYCODE_LEFT_SHIFT; + down = 0 != (new_f & NSEventModifierFlagShift); + } + if ((new_f ^ old_f) & NSEventModifierFlagControl) { + key_code = SAPP_KEYCODE_LEFT_CONTROL; + down = 0 != (new_f & NSEventModifierFlagControl); + } + if ((new_f ^ old_f) & NSEventModifierFlagOption) { + key_code = SAPP_KEYCODE_LEFT_ALT; + down = 0 != (new_f & NSEventModifierFlagOption); + } + if ((new_f ^ old_f) & NSEventModifierFlagCommand) { + key_code = SAPP_KEYCODE_LEFT_SUPER; + down = 0 != (new_f & NSEventModifierFlagCommand); + } + if (key_code != SAPP_KEYCODE_INVALID) { + _sapp_macos_key_event(down ? SAPP_EVENTTYPE_KEY_DOWN : SAPP_EVENTTYPE_KEY_UP, + key_code, + false, + _sapp_macos_mod(event.modifierFlags)); + } +} +- (void)cursorUpdate:(NSEvent*)event { + _SOKOL_UNUSED(event); + if (_sapp.desc.user_cursor) { + _sapp_macos_app_event(SAPP_EVENTTYPE_UPDATE_CURSOR); + } +} +@end + +#endif /* MacOS */ + +/*== iOS =====================================================================*/ +#if defined(_SAPP_IOS) + +_SOKOL_PRIVATE void _sapp_ios_discard_state(void) { + // NOTE: it's safe to call [release] on a nil object + _SAPP_OBJC_RELEASE(_sapp.ios.textfield_dlg); + _SAPP_OBJC_RELEASE(_sapp.ios.textfield); + #if defined(SOKOL_METAL) + _SAPP_OBJC_RELEASE(_sapp.ios.view_ctrl); + _SAPP_OBJC_RELEASE(_sapp.ios.mtl_device); + #else + _SAPP_OBJC_RELEASE(_sapp.ios.view_ctrl); + _SAPP_OBJC_RELEASE(_sapp.ios.eagl_ctx); + #endif + _SAPP_OBJC_RELEASE(_sapp.ios.view); + _SAPP_OBJC_RELEASE(_sapp.ios.window); +} + +_SOKOL_PRIVATE void _sapp_ios_run(const sapp_desc* desc) { + _sapp_init_state(desc); + static int argc = 1; + static char* argv[] = { (char*)"sokol_app" }; + UIApplicationMain(argc, argv, nil, NSStringFromClass([_sapp_app_delegate class])); +} + +/* iOS entry function */ +#if !defined(SOKOL_NO_ENTRY) +int main(int argc, char* argv[]) { + sapp_desc desc = sokol_main(argc, argv); + _sapp_ios_run(&desc); + return 0; +} +#endif /* SOKOL_NO_ENTRY */ + +_SOKOL_PRIVATE void _sapp_ios_app_event(sapp_event_type type) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_ios_touch_event(sapp_event_type type, NSSet* touches, UIEvent* event) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + NSEnumerator* enumerator = event.allTouches.objectEnumerator; + UITouch* ios_touch; + while ((ios_touch = [enumerator nextObject])) { + if ((_sapp.event.num_touches + 1) < SAPP_MAX_TOUCHPOINTS) { + CGPoint ios_pos = [ios_touch locationInView:_sapp.ios.view]; + sapp_touchpoint* cur_point = &_sapp.event.touches[_sapp.event.num_touches++]; + cur_point->identifier = (uintptr_t) ios_touch; + cur_point->pos_x = ios_pos.x * _sapp.dpi_scale; + cur_point->pos_y = ios_pos.y * _sapp.dpi_scale; + cur_point->changed = [touches containsObject:ios_touch]; + } + } + if (_sapp.event.num_touches > 0) { + _sapp_call_event(&_sapp.event); + } + } +} + +_SOKOL_PRIVATE void _sapp_ios_update_dimensions(void) { + CGRect screen_rect = UIScreen.mainScreen.bounds; + _sapp.window_width = (int) screen_rect.size.width; + _sapp.window_height = (int) screen_rect.size.height; + int cur_fb_width, cur_fb_height; + #if defined(SOKOL_METAL) + const CGSize fb_size = _sapp.ios.view.drawableSize; + cur_fb_width = (int) fb_size.width; + cur_fb_height = (int) fb_size.height; + #else + cur_fb_width = (int) _sapp.ios.view.drawableWidth; + cur_fb_height = (int) _sapp.ios.view.drawableHeight; + #endif + const bool dim_changed = + (_sapp.framebuffer_width != cur_fb_width) || + (_sapp.framebuffer_height != cur_fb_height); + _sapp.framebuffer_width = cur_fb_width; + _sapp.framebuffer_height = cur_fb_height; + SOKOL_ASSERT((_sapp.framebuffer_width > 0) && (_sapp.framebuffer_height > 0)); + _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float) _sapp.window_width; + if (dim_changed && !_sapp.first_frame) { + _sapp_ios_app_event(SAPP_EVENTTYPE_RESIZED); + } +} + +_SOKOL_PRIVATE void _sapp_ios_frame(void) { + _sapp_ios_update_dimensions(); + _sapp_frame(); +} + +_SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) { + /* if not happened yet, create an invisible text field */ + if (nil == _sapp.ios.textfield) { + _sapp.ios.textfield_dlg = [[_sapp_textfield_dlg alloc] init]; + _sapp.ios.textfield = [[UITextField alloc] initWithFrame:CGRectMake(10, 10, 100, 50)]; + _sapp.ios.textfield.keyboardType = UIKeyboardTypeDefault; + _sapp.ios.textfield.returnKeyType = UIReturnKeyDefault; + _sapp.ios.textfield.autocapitalizationType = UITextAutocapitalizationTypeNone; + _sapp.ios.textfield.autocorrectionType = UITextAutocorrectionTypeNo; + _sapp.ios.textfield.spellCheckingType = UITextSpellCheckingTypeNo; + _sapp.ios.textfield.hidden = YES; + _sapp.ios.textfield.text = @"x"; + _sapp.ios.textfield.delegate = _sapp.ios.textfield_dlg; + [_sapp.ios.view_ctrl.view addSubview:_sapp.ios.textfield]; + + [[NSNotificationCenter defaultCenter] addObserver:_sapp.ios.textfield_dlg + selector:@selector(keyboardWasShown:) + name:UIKeyboardDidShowNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:_sapp.ios.textfield_dlg + selector:@selector(keyboardWillBeHidden:) + name:UIKeyboardWillHideNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:_sapp.ios.textfield_dlg + selector:@selector(keyboardDidChangeFrame:) + name:UIKeyboardDidChangeFrameNotification object:nil]; + } + if (shown) { + /* setting the text field as first responder brings up the onscreen keyboard */ + [_sapp.ios.textfield becomeFirstResponder]; + } + else { + [_sapp.ios.textfield resignFirstResponder]; + } +} + +@implementation _sapp_app_delegate +- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { + CGRect screen_rect = UIScreen.mainScreen.bounds; + _sapp.ios.window = [[UIWindow alloc] initWithFrame:screen_rect]; + _sapp.window_width = screen_rect.size.width; + _sapp.window_height = screen_rect.size.height; + if (_sapp.desc.high_dpi) { + _sapp.framebuffer_width = 2 * _sapp.window_width; + _sapp.framebuffer_height = 2 * _sapp.window_height; + } + else { + _sapp.framebuffer_width = _sapp.window_width; + _sapp.framebuffer_height = _sapp.window_height; + } + _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float) _sapp.window_width; + #if defined(SOKOL_METAL) + _sapp.ios.mtl_device = MTLCreateSystemDefaultDevice(); + _sapp.ios.view = [[_sapp_ios_view alloc] init]; + _sapp.ios.view.preferredFramesPerSecond = 60 / _sapp.swap_interval; + _sapp.ios.view.device = _sapp.ios.mtl_device; + _sapp.ios.view.colorPixelFormat = MTLPixelFormatBGRA8Unorm; + _sapp.ios.view.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8; + _sapp.ios.view.sampleCount = _sapp.sample_count; + if (_sapp.desc.high_dpi) { + _sapp.ios.view.contentScaleFactor = 2.0; + } + else { + _sapp.ios.view.contentScaleFactor = 1.0; + } + _sapp.ios.view.userInteractionEnabled = YES; + _sapp.ios.view.multipleTouchEnabled = YES; + _sapp.ios.view_ctrl = [[UIViewController alloc] init]; + _sapp.ios.view_ctrl.modalPresentationStyle = UIModalPresentationFullScreen; + _sapp.ios.view_ctrl.view = _sapp.ios.view; + _sapp.ios.window.rootViewController = _sapp.ios.view_ctrl; + #else + if (_sapp.desc.gl_force_gles2) { + _sapp.ios.eagl_ctx = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; + _sapp.gles2_fallback = true; + } + else { + _sapp.ios.eagl_ctx = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; + if (_sapp.ios.eagl_ctx == nil) { + _sapp.ios.eagl_ctx = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; + _sapp.gles2_fallback = true; + } + } + _sapp.ios.view = [[_sapp_ios_view alloc] initWithFrame:screen_rect]; + _sapp.ios.view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888; + _sapp.ios.view.drawableDepthFormat = GLKViewDrawableDepthFormat24; + _sapp.ios.view.drawableStencilFormat = GLKViewDrawableStencilFormatNone; + _sapp.ios.view.drawableMultisample = GLKViewDrawableMultisampleNone; /* FIXME */ + _sapp.ios.view.context = _sapp.ios.eagl_ctx; + _sapp.ios.view.enableSetNeedsDisplay = NO; + _sapp.ios.view.userInteractionEnabled = YES; + _sapp.ios.view.multipleTouchEnabled = YES; + if (_sapp.desc.high_dpi) { + _sapp.ios.view.contentScaleFactor = 2.0; + } + else { + _sapp.ios.view.contentScaleFactor = 1.0; + } + _sapp.ios.view_ctrl = [[GLKViewController alloc] init]; + _sapp.ios.view_ctrl.view = _sapp.ios.view; + _sapp.ios.view_ctrl.preferredFramesPerSecond = 60 / _sapp.swap_interval; + _sapp.ios.window.rootViewController = _sapp.ios.view_ctrl; + #endif + [_sapp.ios.window makeKeyAndVisible]; + + _sapp.valid = true; + return YES; +} + +- (void)applicationWillResignActive:(UIApplication *)application { + if (!_sapp.ios.suspended) { + _sapp.ios.suspended = true; + _sapp_ios_app_event(SAPP_EVENTTYPE_SUSPENDED); + } +} + +- (void)applicationDidBecomeActive:(UIApplication *)application { + if (_sapp.ios.suspended) { + _sapp.ios.suspended = false; + _sapp_ios_app_event(SAPP_EVENTTYPE_RESUMED); + } +} + +/* NOTE: this method will rarely ever be called, iOS application + which are terminated by the user are usually killed via signal 9 + by the operating system. +*/ +- (void)applicationWillTerminate:(UIApplication *)application { + _SOKOL_UNUSED(application); + _sapp_call_cleanup(); + _sapp_ios_discard_state(); + _sapp_discard_state(); +} +@end + +@implementation _sapp_textfield_dlg +- (void)keyboardWasShown:(NSNotification*)notif { + _sapp.onscreen_keyboard_shown = true; + /* query the keyboard's size, and modify the content view's size */ + if (_sapp.desc.ios_keyboard_resizes_canvas) { + NSDictionary* info = notif.userInfo; + CGFloat kbd_h = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height; + CGRect view_frame = UIScreen.mainScreen.bounds; + view_frame.size.height -= kbd_h; + _sapp.ios.view.frame = view_frame; + } +} +- (void)keyboardWillBeHidden:(NSNotification*)notif { + _sapp.onscreen_keyboard_shown = false; + if (_sapp.desc.ios_keyboard_resizes_canvas) { + _sapp.ios.view.frame = UIScreen.mainScreen.bounds; + } +} +- (void)keyboardDidChangeFrame:(NSNotification*)notif { + /* this is for the case when the screen rotation changes while the keyboard is open */ + if (_sapp.onscreen_keyboard_shown && _sapp.desc.ios_keyboard_resizes_canvas) { + NSDictionary* info = notif.userInfo; + CGFloat kbd_h = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height; + CGRect view_frame = UIScreen.mainScreen.bounds; + view_frame.size.height -= kbd_h; + _sapp.ios.view.frame = view_frame; + } +} +- (BOOL)textField:(UITextField*)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString*)string { + if (_sapp_events_enabled()) { + const NSUInteger len = string.length; + if (len > 0) { + for (NSUInteger i = 0; i < len; i++) { + unichar c = [string characterAtIndex:i]; + if (c >= 32) { + /* ignore surrogates for now */ + if ((c < 0xD800) || (c > 0xDFFF)) { + _sapp_init_event(SAPP_EVENTTYPE_CHAR); + _sapp.event.char_code = c; + _sapp_call_event(&_sapp.event); + } + } + if (c <= 32) { + sapp_keycode k = SAPP_KEYCODE_INVALID; + switch (c) { + case 10: k = SAPP_KEYCODE_ENTER; break; + case 32: k = SAPP_KEYCODE_SPACE; break; + default: break; + } + if (k != SAPP_KEYCODE_INVALID) { + _sapp_init_event(SAPP_EVENTTYPE_KEY_DOWN); + _sapp.event.key_code = k; + _sapp_call_event(&_sapp.event); + _sapp_init_event(SAPP_EVENTTYPE_KEY_UP); + _sapp.event.key_code = k; + _sapp_call_event(&_sapp.event); + } + } + } + } + else { + /* this was a backspace */ + _sapp_init_event(SAPP_EVENTTYPE_KEY_DOWN); + _sapp.event.key_code = SAPP_KEYCODE_BACKSPACE; + _sapp_call_event(&_sapp.event); + _sapp_init_event(SAPP_EVENTTYPE_KEY_UP); + _sapp.event.key_code = SAPP_KEYCODE_BACKSPACE; + _sapp_call_event(&_sapp.event); + } + } + return NO; +} +@end + +@implementation _sapp_ios_view +- (void)drawRect:(CGRect)rect { + _SOKOL_UNUSED(rect); + _sapp_ios_frame(); +} +- (BOOL)isOpaque { + return YES; +} +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent*)event { + _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_BEGAN, touches, event); +} +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent*)event { + _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_MOVED, touches, event); +} +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent*)event { + _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_ENDED, touches, event); +} +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent*)event { + _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_CANCELLED, touches, event); +} +@end +#endif /* TARGET_OS_IPHONE */ + +#endif /* _SAPP_APPLE */ + +/*== EMSCRIPTEN ==============================================================*/ +#if defined(_SAPP_EMSCRIPTEN) + +/* this function is called from a JS event handler when the user hides + the onscreen keyboard pressing the 'dismiss keyboard key' +*/ +#ifdef __cplusplus +extern "C" { +#endif +EMSCRIPTEN_KEEPALIVE void _sapp_emsc_notify_keyboard_hidden(void) { + _sapp.onscreen_keyboard_shown = false; +} +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/* Javascript helper functions for mobile virtual keyboard input */ +EM_JS(void, sapp_js_create_textfield, (void), { + var _sapp_inp = document.createElement("input"); + _sapp_inp.type = "text"; + _sapp_inp.id = "_sokol_app_input_element"; + _sapp_inp.autocapitalize = "none"; + _sapp_inp.addEventListener("focusout", function(_sapp_event) { + __sapp_emsc_notify_keyboard_hidden() + + }); + document.body.append(_sapp_inp); +}); + +EM_JS(void, sapp_js_focus_textfield, (void), { + document.getElementById("_sokol_app_input_element").focus(); +}); + +EM_JS(void, sapp_js_unfocus_textfield, (void), { + document.getElementById("_sokol_app_input_element").blur(); +}); + +EMSCRIPTEN_KEEPALIVE void _sapp_emsc_onpaste(const char* str) { + if (_sapp.clipboard.enabled) { + _sapp_strcpy(str, _sapp.clipboard.buffer, _sapp.clipboard.buf_size); + if (_sapp_events_enabled()) { + _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED); + _sapp_call_event(&_sapp.event); + } + } +} + +/* https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeunload */ +EMSCRIPTEN_KEEPALIVE int _sapp_html5_get_ask_leave_site(void) { + return _sapp.html5_ask_leave_site ? 1 : 0; +} + +EM_JS(void, sapp_add_js_hook_beforeunload, (void), { + Module.sokol_beforeunload = function(_sapp_event) { + if (__sapp_html5_get_ask_leave_site() != 0) { + _sapp_event.preventDefault(); + _sapp_event.returnValue = ' '; + } + }; + window.addEventListener('beforeunload', Module.sokol_beforeunload); +}); + +EM_JS(void, sapp_remove_js_hook_beforeunload, (void), { + window.removeEventListener('beforeunload', Module.sokol_beforeunload); +}); + +EM_JS(void, sapp_add_js_hook_clipboard, (void), { + Module.sokol_paste = function(event) { + var pasted_str = event.clipboardData.getData('text'); + ccall('_sapp_emsc_onpaste', 'void', ['string'], [pasted_str]); + }; + window.addEventListener('paste', Module.sokol_paste); +}); + +EM_JS(void, sapp_remove_js_hook_clipboard, (void), { + window.removeEventListener('paste', Module.sokol_paste); +}); + +EM_JS(void, sapp_js_write_clipboard, (const char* c_str), { + var str = UTF8ToString(c_str); + var ta = document.createElement('textarea'); + ta.setAttribute('autocomplete', 'off'); + ta.setAttribute('autocorrect', 'off'); + ta.setAttribute('autocapitalize', 'off'); + ta.setAttribute('spellcheck', 'false'); + ta.style.left = -100 + 'px'; + ta.style.top = -100 + 'px'; + ta.style.height = 1; + ta.style.width = 1; + ta.value = str; + document.body.appendChild(ta); + ta.select(); + document.execCommand('copy'); + document.body.removeChild(ta); +}); + +_SOKOL_PRIVATE void _sapp_emsc_set_clipboard_string(const char* str) { + sapp_js_write_clipboard(str); +} + +/* called from the emscripten event handler to update the keyboard visibility + state, this must happen from an JS input event handler, otherwise + the request will be ignored by the browser +*/ +_SOKOL_PRIVATE void _sapp_emsc_update_keyboard_state(void) { + if (_sapp.emsc.wants_show_keyboard) { + /* create input text field on demand */ + if (!_sapp.emsc.textfield_created) { + _sapp.emsc.textfield_created = true; + sapp_js_create_textfield(); + } + /* focus the text input field, this will bring up the keyboard */ + _sapp.onscreen_keyboard_shown = true; + _sapp.emsc.wants_show_keyboard = false; + sapp_js_focus_textfield(); + } + if (_sapp.emsc.wants_hide_keyboard) { + /* unfocus the text input field */ + if (_sapp.emsc.textfield_created) { + _sapp.onscreen_keyboard_shown = false; + _sapp.emsc.wants_hide_keyboard = false; + sapp_js_unfocus_textfield(); + } + } +} + +/* actually showing the onscreen keyboard must be initiated from a JS + input event handler, so we'll just keep track of the desired + state, and the actual state change will happen with the next input event +*/ +_SOKOL_PRIVATE void _sapp_emsc_show_keyboard(bool show) { + if (show) { + _sapp.emsc.wants_show_keyboard = true; + } + else { + _sapp.emsc.wants_hide_keyboard = true; + } +} + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_pointerlockchange_cb(int emsc_type, const EmscriptenPointerlockChangeEvent* emsc_event, void* user_data) { + _SOKOL_UNUSED(emsc_type); + _SOKOL_UNUSED(user_data); + _sapp.mouse.locked = emsc_event->isActive; + return EM_TRUE; +} + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_pointerlockerror_cb(int emsc_type, const void* reserved, void* user_data) { + _SOKOL_UNUSED(emsc_type); + _SOKOL_UNUSED(reserved); + _SOKOL_UNUSED(user_data); + _sapp.mouse.locked = false; + _sapp.emsc.mouse_lock_requested = false; + return true; +} + +EM_JS(void, _sapp_emsc_request_pointerlock, (const char* c_str_target), { + var target_str = UTF8ToString(c_str_target); + var target = document.getElementById(target_str); + if (!target) { + console.log("sokol_app.h: invalid target:" + target_str); + return; + } + if (!target.requestPointerLock) { + console.log("sokol_app.h: target doesn't doesn't support pointer lock:" + target_str); + return; + } + target.requestPointerLock(); +}); + +EM_JS(void, _sapp_emsc_exit_pointerlock, (void), { + if (document.exitPointerLock) { + document.exitPointerLock(); + } +}); + +_SOKOL_PRIVATE void _sapp_emsc_lock_mouse(bool lock) { + if (lock) { + /* request mouse-lock during event handler invocation (see _sapp_emsc_update_mouse_lock_state) */ + _sapp.emsc.mouse_lock_requested = true; + } + else { + /* NOTE: the _sapp.mouse_locked state will be set in the pointerlockchange callback */ + _sapp.emsc.mouse_lock_requested = false; + _sapp_emsc_exit_pointerlock(); + } +} + +/* called from inside event handlers to check if mouse lock had been requested, + and if yes, actually enter mouse lock. +*/ +_SOKOL_PRIVATE void _sapp_emsc_update_mouse_lock_state(void) { + if (_sapp.emsc.mouse_lock_requested) { + _sapp.emsc.mouse_lock_requested = false; + _sapp_emsc_request_pointerlock(_sapp.html5_canvas_name); + } +} + +#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 EM_BOOL _sapp_emsc_size_changed(int event_type, const EmscriptenUiEvent* ui_event, void* user_data) { + _SOKOL_UNUSED(event_type); + _SOKOL_UNUSED(user_data); + double w, h; + emscripten_get_element_css_size(_sapp.html5_canvas_name, &w, &h); + /* The above method might report zero when toggling HTML5 fullscreen, + in that case use the window's inner width reported by the + emscripten event. This works ok when toggling *into* fullscreen + but doesn't properly restore the previous canvas size when switching + back from fullscreen. + + In general, due to the HTML5's fullscreen API's flaky nature it is + recommended to use 'soft fullscreen' (stretching the WebGL canvas + over the browser windows client rect) with a CSS definition like this: + + position: absolute; + top: 0px; + left: 0px; + margin: 0px; + border: 0; + width: 100%; + height: 100%; + overflow: hidden; + display: block; + */ + if (w < 1.0) { + w = ui_event->windowInnerWidth; + } + else { + _sapp.window_width = (int) w; + } + if (h < 1.0) { + h = ui_event->windowInnerHeight; + } + else { + _sapp.window_height = (int) h; + } + if (_sapp.desc.high_dpi) { + _sapp.dpi_scale = emscripten_get_device_pixel_ratio(); + } + _sapp.framebuffer_width = (int) (w * _sapp.dpi_scale); + _sapp.framebuffer_height = (int) (h * _sapp.dpi_scale); + SOKOL_ASSERT((_sapp.framebuffer_width > 0) && (_sapp.framebuffer_height > 0)); + emscripten_set_canvas_element_size(_sapp.html5_canvas_name, _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(); + #endif + if (_sapp_events_enabled()) { + _sapp_init_event(SAPP_EVENTTYPE_RESIZED); + _sapp_call_event(&_sapp.event); + } + return true; +} + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_mouse_cb(int emsc_type, const EmscriptenMouseEvent* emsc_event, void* user_data) { + _SOKOL_UNUSED(user_data); + if (_sapp.mouse.locked) { + _sapp.mouse.dx = (float) emsc_event->movementX; + _sapp.mouse.dy = (float) emsc_event->movementY; + } + else { + float new_x = emsc_event->targetX * _sapp.dpi_scale; + float new_y = emsc_event->targetY * _sapp.dpi_scale; + if (_sapp.mouse.pos_valid) { + _sapp.mouse.dx = new_x - _sapp.mouse.x; + _sapp.mouse.dy = new_y - _sapp.mouse.y; + } + _sapp.mouse.x = new_x; + _sapp.mouse.y = new_y; + _sapp.mouse.pos_valid = true; + } + if (_sapp_events_enabled() && (emsc_event->button >= 0) && (emsc_event->button < SAPP_MAX_MOUSEBUTTONS)) { + sapp_event_type type; + bool is_button_event = false; + switch (emsc_type) { + case EMSCRIPTEN_EVENT_MOUSEDOWN: + type = SAPP_EVENTTYPE_MOUSE_DOWN; + is_button_event = true; + break; + case EMSCRIPTEN_EVENT_MOUSEUP: + type = SAPP_EVENTTYPE_MOUSE_UP; + is_button_event = true; + break; + case EMSCRIPTEN_EVENT_MOUSEMOVE: + type = SAPP_EVENTTYPE_MOUSE_MOVE; + break; + case EMSCRIPTEN_EVENT_MOUSEENTER: + type = SAPP_EVENTTYPE_MOUSE_ENTER; + break; + case EMSCRIPTEN_EVENT_MOUSELEAVE: + type = SAPP_EVENTTYPE_MOUSE_LEAVE; + break; + default: + type = SAPP_EVENTTYPE_INVALID; + break; + } + if (type != SAPP_EVENTTYPE_INVALID) { + _sapp_init_event(type); + if (emsc_event->ctrlKey) { + _sapp.event.modifiers |= SAPP_MODIFIER_CTRL; + } + if (emsc_event->shiftKey) { + _sapp.event.modifiers |= SAPP_MODIFIER_SHIFT; + } + if (emsc_event->altKey) { + _sapp.event.modifiers |= SAPP_MODIFIER_ALT; + } + if (emsc_event->metaKey) { + _sapp.event.modifiers |= SAPP_MODIFIER_SUPER; + } + if (is_button_event) { + switch (emsc_event->button) { + case 0: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_LEFT; break; + case 1: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_MIDDLE; break; + case 2: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_RIGHT; break; + default: _sapp.event.mouse_button = (sapp_mousebutton)emsc_event->button; break; + } + } + else { + _sapp.event.mouse_button = SAPP_MOUSEBUTTON_INVALID; + } + _sapp_call_event(&_sapp.event); + } + /* mouse lock can only be activated in mouse button events (not in move, enter or leave) */ + if (is_button_event) { + _sapp_emsc_update_mouse_lock_state(); + } + } + _sapp_emsc_update_keyboard_state(); + return true; +} + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_wheel_cb(int emsc_type, const EmscriptenWheelEvent* emsc_event, void* user_data) { + _SOKOL_UNUSED(emsc_type); + _SOKOL_UNUSED(user_data); + if (_sapp_events_enabled()) { + _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); + if (emsc_event->mouse.ctrlKey) { + _sapp.event.modifiers |= SAPP_MODIFIER_CTRL; + } + if (emsc_event->mouse.shiftKey) { + _sapp.event.modifiers |= SAPP_MODIFIER_SHIFT; + } + if (emsc_event->mouse.altKey) { + _sapp.event.modifiers |= SAPP_MODIFIER_ALT; + } + if (emsc_event->mouse.metaKey) { + _sapp.event.modifiers |= SAPP_MODIFIER_SUPER; + } + /* see https://github.com/floooh/sokol/issues/339 */ + float scale; + switch (emsc_event->deltaMode) { + case DOM_DELTA_PIXEL: scale = -0.04f; break; + case DOM_DELTA_LINE: scale = -1.33f; break; + case DOM_DELTA_PAGE: scale = -10.0f; break; // FIXME: this is a guess + default: scale = -0.1f; break; // shouldn't happen + } + _sapp.event.scroll_x = scale * (float)emsc_event->deltaX; + _sapp.event.scroll_y = scale * (float)emsc_event->deltaY; + _sapp_call_event(&_sapp.event); + } + _sapp_emsc_update_keyboard_state(); + _sapp_emsc_update_mouse_lock_state(); + return true; +} + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboardEvent* emsc_event, void* user_data) { + _SOKOL_UNUSED(user_data); + bool retval = true; + if (_sapp_events_enabled()) { + sapp_event_type type; + switch (emsc_type) { + case EMSCRIPTEN_EVENT_KEYDOWN: + type = SAPP_EVENTTYPE_KEY_DOWN; + break; + case EMSCRIPTEN_EVENT_KEYUP: + type = SAPP_EVENTTYPE_KEY_UP; + break; + case EMSCRIPTEN_EVENT_KEYPRESS: + type = SAPP_EVENTTYPE_CHAR; + break; + default: + type = SAPP_EVENTTYPE_INVALID; + break; + } + if (type != SAPP_EVENTTYPE_INVALID) { + bool send_keyup_followup = false; + _sapp_init_event(type); + _sapp.event.key_repeat = emsc_event->repeat; + if (emsc_event->ctrlKey) { + _sapp.event.modifiers |= SAPP_MODIFIER_CTRL; + } + if (emsc_event->shiftKey) { + _sapp.event.modifiers |= SAPP_MODIFIER_SHIFT; + } + if (emsc_event->altKey) { + _sapp.event.modifiers |= SAPP_MODIFIER_ALT; + } + if (emsc_event->metaKey) { + _sapp.event.modifiers |= SAPP_MODIFIER_SUPER; + } + if (type == SAPP_EVENTTYPE_CHAR) { + _sapp.event.char_code = emsc_event->charCode; + /* workaround to make Cmd+V work on Safari */ + if ((emsc_event->metaKey) && (emsc_event->charCode == 118)) { + retval = false; + } + } + else { + _sapp.event.key_code = _sapp_translate_key(emsc_event->keyCode); + /* 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) && + (_sapp.event.modifiers & SAPP_MODIFIER_SUPER)) + { + send_keyup_followup = true; + } + /* only forward a certain key ranges to the browser */ + switch (_sapp.event.key_code) { + case SAPP_KEYCODE_WORLD_1: + case SAPP_KEYCODE_WORLD_2: + case SAPP_KEYCODE_ESCAPE: + case SAPP_KEYCODE_ENTER: + case SAPP_KEYCODE_TAB: + case SAPP_KEYCODE_BACKSPACE: + case SAPP_KEYCODE_INSERT: + case SAPP_KEYCODE_DELETE: + case SAPP_KEYCODE_RIGHT: + case SAPP_KEYCODE_LEFT: + case SAPP_KEYCODE_DOWN: + case SAPP_KEYCODE_UP: + case SAPP_KEYCODE_PAGE_UP: + case SAPP_KEYCODE_PAGE_DOWN: + case SAPP_KEYCODE_HOME: + case SAPP_KEYCODE_END: + case SAPP_KEYCODE_CAPS_LOCK: + case SAPP_KEYCODE_SCROLL_LOCK: + case SAPP_KEYCODE_NUM_LOCK: + case SAPP_KEYCODE_PRINT_SCREEN: + case SAPP_KEYCODE_PAUSE: + case SAPP_KEYCODE_F1: + case SAPP_KEYCODE_F2: + case SAPP_KEYCODE_F3: + case SAPP_KEYCODE_F4: + case SAPP_KEYCODE_F5: + case SAPP_KEYCODE_F6: + case SAPP_KEYCODE_F7: + case SAPP_KEYCODE_F8: + case SAPP_KEYCODE_F9: + case SAPP_KEYCODE_F10: + case SAPP_KEYCODE_F11: + case SAPP_KEYCODE_F12: + case SAPP_KEYCODE_F13: + case SAPP_KEYCODE_F14: + case SAPP_KEYCODE_F15: + case SAPP_KEYCODE_F16: + case SAPP_KEYCODE_F17: + case SAPP_KEYCODE_F18: + case SAPP_KEYCODE_F19: + case SAPP_KEYCODE_F20: + case SAPP_KEYCODE_F21: + case SAPP_KEYCODE_F22: + case SAPP_KEYCODE_F23: + case SAPP_KEYCODE_F24: + case SAPP_KEYCODE_F25: + case SAPP_KEYCODE_LEFT_SHIFT: + case SAPP_KEYCODE_LEFT_CONTROL: + case SAPP_KEYCODE_LEFT_ALT: + case SAPP_KEYCODE_LEFT_SUPER: + case SAPP_KEYCODE_RIGHT_SHIFT: + case SAPP_KEYCODE_RIGHT_CONTROL: + case SAPP_KEYCODE_RIGHT_ALT: + case SAPP_KEYCODE_RIGHT_SUPER: + case SAPP_KEYCODE_MENU: + /* consume the event */ + break; + default: + /* forward key to browser */ + retval = false; + break; + } + } + if (_sapp_call_event(&_sapp.event)) { + /* consume event via sapp_consume_event() */ + retval = true; + } + if (send_keyup_followup) { + _sapp.event.type = SAPP_EVENTTYPE_KEY_UP; + if (_sapp_call_event(&_sapp.event)) { + retval = true; + } + } + } + } + _sapp_emsc_update_keyboard_state(); + _sapp_emsc_update_mouse_lock_state(); + return retval; +} + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_touch_cb(int emsc_type, const EmscriptenTouchEvent* emsc_event, void* user_data) { + _SOKOL_UNUSED(user_data); + bool retval = true; + if (_sapp_events_enabled()) { + sapp_event_type type; + switch (emsc_type) { + case EMSCRIPTEN_EVENT_TOUCHSTART: + type = SAPP_EVENTTYPE_TOUCHES_BEGAN; + break; + case EMSCRIPTEN_EVENT_TOUCHMOVE: + type = SAPP_EVENTTYPE_TOUCHES_MOVED; + break; + case EMSCRIPTEN_EVENT_TOUCHEND: + type = SAPP_EVENTTYPE_TOUCHES_ENDED; + break; + case EMSCRIPTEN_EVENT_TOUCHCANCEL: + type = SAPP_EVENTTYPE_TOUCHES_CANCELLED; + break; + default: + type = SAPP_EVENTTYPE_INVALID; + retval = false; + break; + } + if (type != SAPP_EVENTTYPE_INVALID) { + _sapp_init_event(type); + if (emsc_event->ctrlKey) { + _sapp.event.modifiers |= SAPP_MODIFIER_CTRL; + } + if (emsc_event->shiftKey) { + _sapp.event.modifiers |= SAPP_MODIFIER_SHIFT; + } + if (emsc_event->altKey) { + _sapp.event.modifiers |= SAPP_MODIFIER_ALT; + } + if (emsc_event->metaKey) { + _sapp.event.modifiers |= SAPP_MODIFIER_SUPER; + } + _sapp.event.num_touches = emsc_event->numTouches; + if (_sapp.event.num_touches > SAPP_MAX_TOUCHPOINTS) { + _sapp.event.num_touches = SAPP_MAX_TOUCHPOINTS; + } + for (int i = 0; i < _sapp.event.num_touches; i++) { + const EmscriptenTouchPoint* src = &emsc_event->touches[i]; + sapp_touchpoint* dst = &_sapp.event.touches[i]; + dst->identifier = src->identifier; + dst->pos_x = src->targetX * _sapp.dpi_scale; + dst->pos_y = src->targetY * _sapp.dpi_scale; + dst->changed = src->isChanged; + } + _sapp_call_event(&_sapp.event); + } + } + _sapp_emsc_update_keyboard_state(); + return retval; +} + +_SOKOL_PRIVATE void _sapp_emsc_keytable_init(void) { + _sapp.keycodes[8] = SAPP_KEYCODE_BACKSPACE; + _sapp.keycodes[9] = SAPP_KEYCODE_TAB; + _sapp.keycodes[13] = SAPP_KEYCODE_ENTER; + _sapp.keycodes[16] = SAPP_KEYCODE_LEFT_SHIFT; + _sapp.keycodes[17] = SAPP_KEYCODE_LEFT_CONTROL; + _sapp.keycodes[18] = SAPP_KEYCODE_LEFT_ALT; + _sapp.keycodes[19] = SAPP_KEYCODE_PAUSE; + _sapp.keycodes[27] = SAPP_KEYCODE_ESCAPE; + _sapp.keycodes[32] = SAPP_KEYCODE_SPACE; + _sapp.keycodes[33] = SAPP_KEYCODE_PAGE_UP; + _sapp.keycodes[34] = SAPP_KEYCODE_PAGE_DOWN; + _sapp.keycodes[35] = SAPP_KEYCODE_END; + _sapp.keycodes[36] = SAPP_KEYCODE_HOME; + _sapp.keycodes[37] = SAPP_KEYCODE_LEFT; + _sapp.keycodes[38] = SAPP_KEYCODE_UP; + _sapp.keycodes[39] = SAPP_KEYCODE_RIGHT; + _sapp.keycodes[40] = SAPP_KEYCODE_DOWN; + _sapp.keycodes[45] = SAPP_KEYCODE_INSERT; + _sapp.keycodes[46] = SAPP_KEYCODE_DELETE; + _sapp.keycodes[48] = SAPP_KEYCODE_0; + _sapp.keycodes[49] = SAPP_KEYCODE_1; + _sapp.keycodes[50] = SAPP_KEYCODE_2; + _sapp.keycodes[51] = SAPP_KEYCODE_3; + _sapp.keycodes[52] = SAPP_KEYCODE_4; + _sapp.keycodes[53] = SAPP_KEYCODE_5; + _sapp.keycodes[54] = SAPP_KEYCODE_6; + _sapp.keycodes[55] = SAPP_KEYCODE_7; + _sapp.keycodes[56] = SAPP_KEYCODE_8; + _sapp.keycodes[57] = SAPP_KEYCODE_9; + _sapp.keycodes[59] = SAPP_KEYCODE_SEMICOLON; + _sapp.keycodes[64] = SAPP_KEYCODE_EQUAL; + _sapp.keycodes[65] = SAPP_KEYCODE_A; + _sapp.keycodes[66] = SAPP_KEYCODE_B; + _sapp.keycodes[67] = SAPP_KEYCODE_C; + _sapp.keycodes[68] = SAPP_KEYCODE_D; + _sapp.keycodes[69] = SAPP_KEYCODE_E; + _sapp.keycodes[70] = SAPP_KEYCODE_F; + _sapp.keycodes[71] = SAPP_KEYCODE_G; + _sapp.keycodes[72] = SAPP_KEYCODE_H; + _sapp.keycodes[73] = SAPP_KEYCODE_I; + _sapp.keycodes[74] = SAPP_KEYCODE_J; + _sapp.keycodes[75] = SAPP_KEYCODE_K; + _sapp.keycodes[76] = SAPP_KEYCODE_L; + _sapp.keycodes[77] = SAPP_KEYCODE_M; + _sapp.keycodes[78] = SAPP_KEYCODE_N; + _sapp.keycodes[79] = SAPP_KEYCODE_O; + _sapp.keycodes[80] = SAPP_KEYCODE_P; + _sapp.keycodes[81] = SAPP_KEYCODE_Q; + _sapp.keycodes[82] = SAPP_KEYCODE_R; + _sapp.keycodes[83] = SAPP_KEYCODE_S; + _sapp.keycodes[84] = SAPP_KEYCODE_T; + _sapp.keycodes[85] = SAPP_KEYCODE_U; + _sapp.keycodes[86] = SAPP_KEYCODE_V; + _sapp.keycodes[87] = SAPP_KEYCODE_W; + _sapp.keycodes[88] = SAPP_KEYCODE_X; + _sapp.keycodes[89] = SAPP_KEYCODE_Y; + _sapp.keycodes[90] = SAPP_KEYCODE_Z; + _sapp.keycodes[91] = SAPP_KEYCODE_LEFT_SUPER; + _sapp.keycodes[93] = SAPP_KEYCODE_MENU; + _sapp.keycodes[96] = SAPP_KEYCODE_KP_0; + _sapp.keycodes[97] = SAPP_KEYCODE_KP_1; + _sapp.keycodes[98] = SAPP_KEYCODE_KP_2; + _sapp.keycodes[99] = SAPP_KEYCODE_KP_3; + _sapp.keycodes[100] = SAPP_KEYCODE_KP_4; + _sapp.keycodes[101] = SAPP_KEYCODE_KP_5; + _sapp.keycodes[102] = SAPP_KEYCODE_KP_6; + _sapp.keycodes[103] = SAPP_KEYCODE_KP_7; + _sapp.keycodes[104] = SAPP_KEYCODE_KP_8; + _sapp.keycodes[105] = SAPP_KEYCODE_KP_9; + _sapp.keycodes[106] = SAPP_KEYCODE_KP_MULTIPLY; + _sapp.keycodes[107] = SAPP_KEYCODE_KP_ADD; + _sapp.keycodes[109] = SAPP_KEYCODE_KP_SUBTRACT; + _sapp.keycodes[110] = SAPP_KEYCODE_KP_DECIMAL; + _sapp.keycodes[111] = SAPP_KEYCODE_KP_DIVIDE; + _sapp.keycodes[112] = SAPP_KEYCODE_F1; + _sapp.keycodes[113] = SAPP_KEYCODE_F2; + _sapp.keycodes[114] = SAPP_KEYCODE_F3; + _sapp.keycodes[115] = SAPP_KEYCODE_F4; + _sapp.keycodes[116] = SAPP_KEYCODE_F5; + _sapp.keycodes[117] = SAPP_KEYCODE_F6; + _sapp.keycodes[118] = SAPP_KEYCODE_F7; + _sapp.keycodes[119] = SAPP_KEYCODE_F8; + _sapp.keycodes[120] = SAPP_KEYCODE_F9; + _sapp.keycodes[121] = SAPP_KEYCODE_F10; + _sapp.keycodes[122] = SAPP_KEYCODE_F11; + _sapp.keycodes[123] = SAPP_KEYCODE_F12; + _sapp.keycodes[144] = SAPP_KEYCODE_NUM_LOCK; + _sapp.keycodes[145] = SAPP_KEYCODE_SCROLL_LOCK; + _sapp.keycodes[173] = SAPP_KEYCODE_MINUS; + _sapp.keycodes[186] = SAPP_KEYCODE_SEMICOLON; + _sapp.keycodes[187] = SAPP_KEYCODE_EQUAL; + _sapp.keycodes[188] = SAPP_KEYCODE_COMMA; + _sapp.keycodes[189] = SAPP_KEYCODE_MINUS; + _sapp.keycodes[190] = SAPP_KEYCODE_PERIOD; + _sapp.keycodes[191] = SAPP_KEYCODE_SLASH; + _sapp.keycodes[192] = SAPP_KEYCODE_GRAVE_ACCENT; + _sapp.keycodes[219] = SAPP_KEYCODE_LEFT_BRACKET; + _sapp.keycodes[220] = SAPP_KEYCODE_BACKSLASH; + _sapp.keycodes[221] = SAPP_KEYCODE_RIGHT_BRACKET; + _sapp.keycodes[222] = SAPP_KEYCODE_APOSTROPHE; + _sapp.keycodes[224] = SAPP_KEYCODE_LEFT_SUPER; +} + +#if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_webgl_context_cb(int emsc_type, const void* reserved, void* user_data) { + _SOKOL_UNUSED(reserved); + _SOKOL_UNUSED(user_data); + sapp_event_type type; + switch (emsc_type) { + case EMSCRIPTEN_EVENT_WEBGLCONTEXTLOST: type = SAPP_EVENTTYPE_SUSPENDED; break; + case EMSCRIPTEN_EVENT_WEBGLCONTEXTRESTORED: type = SAPP_EVENTTYPE_RESUMED; break; + default: type = SAPP_EVENTTYPE_INVALID; break; + } + if (_sapp_events_enabled() && (SAPP_EVENTTYPE_INVALID != type)) { + _sapp_init_event(type); + _sapp_call_event(&_sapp.event); + } + return true; +} + +_SOKOL_PRIVATE void _sapp_emsc_webgl_init(void) { + EmscriptenWebGLContextAttributes attrs; + emscripten_webgl_init_context_attributes(&attrs); + attrs.alpha = _sapp.desc.alpha; + attrs.depth = true; + attrs.stencil = true; + attrs.antialias = _sapp.sample_count > 1; + attrs.premultipliedAlpha = _sapp.desc.html5_premultiplied_alpha; + attrs.preserveDrawingBuffer = _sapp.desc.html5_preserve_drawing_buffer; + attrs.enableExtensionsByDefault = true; + #if defined(SOKOL_GLES3) + if (_sapp.desc.gl_force_gles2) { + attrs.majorVersion = 1; + _sapp.gles2_fallback = true; + } + else { + attrs.majorVersion = 2; + } + #endif + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(_sapp.html5_canvas_name, &attrs); + if (!ctx) { + attrs.majorVersion = 1; + ctx = emscripten_webgl_create_context(_sapp.html5_canvas_name, &attrs); + _sapp.gles2_fallback = true; + } + emscripten_webgl_make_context_current(ctx); + + /* some WebGL extension are not enabled automatically by emscripten */ + emscripten_webgl_enable_extension(ctx, "WEBKIT_WEBGL_compressed_texture_pvrtc"); +} +#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) + +/* 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; +} + +/* embedded JS function to handle all the asynchronous WebGPU setup */ +EM_JS(void, _sapp_emsc_wgpu_init, (), { + WebGPU.initManagers(); + // FIXME: the extension activation must be more clever here + navigator.gpu.requestAdapter().then(function(adapter) { + console.log("wgpu adapter extensions: " + adapter.extensions); + adapter.requestDevice({ extensions: ["textureCompressionBC"]}).then(function(device) { + var gpuContext = document.getElementById("canvas").getContext("gpupresent"); + console.log("wgpu device extensions: " + adapter.extensions); + gpuContext.getSwapChainPreferredFormat(device).then(function(fmt) { + var swapChainDescriptor = { device: device, format: fmt }; + var swapChain = gpuContext.configureSwapChain(swapChainDescriptor); + var deviceId = WebGPU.mgrDevice.create(device); + var swapChainId = WebGPU.mgrSwapChain.create(swapChain); + var 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); + }); + }); + }); +}); + +_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); + + WGPUTextureDescriptor ds_desc; + memset(&ds_desc, 0, sizeof(ds_desc)); + ds_desc.usage = WGPUTextureUsage_OutputAttachment; + 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.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); + + if (_sapp.sample_count > 1) { + WGPUTextureDescriptor msaa_desc; + memset(&msaa_desc, 0, sizeof(msaa_desc)); + msaa_desc.usage = WGPUTextureUsage_OutputAttachment; + 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.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); + } +} + +_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; + } + if (_sapp.emsc.wgpu.msaa_view) { + wgpuTextureViewRelease(_sapp.emsc.wgpu.msaa_view); + _sapp.emsc.wgpu.msaa_view = 0; + } + if (_sapp.emsc.wgpu.depth_stencil_tex) { + wgpuTextureRelease(_sapp.emsc.wgpu.depth_stencil_tex); + _sapp.emsc.wgpu.depth_stencil_tex = 0; + } + if (_sapp.emsc.wgpu.depth_stencil_view) { + wgpuTextureViewRelease(_sapp.emsc.wgpu.depth_stencil_view); + _sapp.emsc.wgpu.depth_stencil_view = 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); +} +#endif + +_SOKOL_PRIVATE void _sapp_emsc_register_eventhandlers(void) { + emscripten_set_mousedown_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_mouse_cb); + emscripten_set_mouseup_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_mouse_cb); + emscripten_set_mousemove_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_mouse_cb); + emscripten_set_mouseenter_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_mouse_cb); + emscripten_set_mouseleave_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_mouse_cb); + emscripten_set_wheel_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_wheel_cb); + emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb); + emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb); + emscripten_set_keypress_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb); + emscripten_set_touchstart_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_touch_cb); + emscripten_set_touchmove_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_touch_cb); + emscripten_set_touchend_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_touch_cb); + emscripten_set_touchcancel_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_touch_cb); + emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, _sapp_emsc_pointerlockchange_cb); + emscripten_set_pointerlockerror_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, _sapp_emsc_pointerlockerror_cb); + sapp_add_js_hook_beforeunload(); + if (_sapp.clipboard.enabled) { + sapp_add_js_hook_clipboard(); + } + #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + emscripten_set_webglcontextlost_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_webgl_context_cb); + emscripten_set_webglcontextrestored_callback(_sapp.html5_canvas_name, 0, true, _sapp_emsc_webgl_context_cb); + #endif +} + +_SOKOL_PRIVATE void _sapp_emsc_unregister_eventhandlers() { + emscripten_set_mousedown_callback(_sapp.html5_canvas_name, 0, true, 0); + emscripten_set_mouseup_callback(_sapp.html5_canvas_name, 0, true, 0); + emscripten_set_mousemove_callback(_sapp.html5_canvas_name, 0, true, 0); + emscripten_set_mouseenter_callback(_sapp.html5_canvas_name, 0, true, 0); + emscripten_set_mouseleave_callback(_sapp.html5_canvas_name, 0, true, 0); + emscripten_set_wheel_callback(_sapp.html5_canvas_name, 0, true, 0); + emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0); + emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0); + emscripten_set_keypress_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0); + emscripten_set_touchstart_callback(_sapp.html5_canvas_name, 0, true, 0); + emscripten_set_touchmove_callback(_sapp.html5_canvas_name, 0, true, 0); + emscripten_set_touchend_callback(_sapp.html5_canvas_name, 0, true, 0); + emscripten_set_touchcancel_callback(_sapp.html5_canvas_name, 0, true, 0); + emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, 0); + emscripten_set_pointerlockerror_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, 0); + sapp_remove_js_hook_beforeunload(); + if (_sapp.clipboard.enabled) { + sapp_remove_js_hook_clipboard(); + } + #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + emscripten_set_webglcontextlost_callback(_sapp.html5_canvas_name, 0, true, 0); + emscripten_set_webglcontextrestored_callback(_sapp.html5_canvas_name, 0, true, 0); + #endif +} + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_frame(double time, void* userData) { + _SOKOL_UNUSED(time); + _SOKOL_UNUSED(userData); + + #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; + } + #else + /* WebGL code path */ + _sapp_frame(); + #endif + + /* quit-handling */ + if (_sapp.quit_requested) { + _sapp_init_event(SAPP_EVENTTYPE_QUIT_REQUESTED); + _sapp_call_event(&_sapp.event); + if (_sapp.quit_requested) { + _sapp.quit_ordered = true; + } + } + if (_sapp.quit_ordered) { + _sapp_emsc_unregister_eventhandlers(); + _sapp_call_cleanup(); + _sapp_discard_state(); + return EM_FALSE; + } + return EM_TRUE; +} + +_SOKOL_PRIVATE void _sapp_emsc_run(const sapp_desc* desc) { + _sapp_init_state(desc); + _sapp_emsc_keytable_init(); + double w, h; + if (_sapp.desc.html5_canvas_resize) { + w = (double) _sapp.desc.width; + h = (double) _sapp.desc.height; + } + else { + emscripten_get_element_css_size(_sapp.html5_canvas_name, &w, &h); + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, false, _sapp_emsc_size_changed); + } + if (_sapp.desc.high_dpi) { + _sapp.dpi_scale = emscripten_get_device_pixel_ratio(); + } + _sapp.window_width = (int) w; + _sapp.window_height = (int) h; + _sapp.framebuffer_width = (int) (w * _sapp.dpi_scale); + _sapp.framebuffer_height = (int) (h * _sapp.dpi_scale); + emscripten_set_canvas_element_size(_sapp.html5_canvas_name, _sapp.framebuffer_width, _sapp.framebuffer_height); + #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + _sapp_emsc_webgl_init(); + #elif defined(SOKOL_WGPU) + _sapp_emsc_wgpu_init(); + #endif + _sapp.valid = true; + _sapp_emsc_register_eventhandlers(); + + /* 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 + */ +} + +#if !defined(SOKOL_NO_ENTRY) +int main(int argc, char* argv[]) { + sapp_desc desc = sokol_main(argc, argv); + _sapp_emsc_run(&desc); + return 0; +} +#endif /* SOKOL_NO_ENTRY */ +#endif /* _SAPP_EMSCRIPTEN */ + +/*== MISC GL SUPPORT FUNCTIONS ================================================*/ +#if defined(SOKOL_GLCORE33) +typedef struct { + int red_bits; + int green_bits; + int blue_bits; + int alpha_bits; + int depth_bits; + int stencil_bits; + int samples; + bool doublebuffer; + uintptr_t handle; +} _sapp_gl_fbconfig; + +_SOKOL_PRIVATE void _sapp_gl_init_fbconfig(_sapp_gl_fbconfig* fbconfig) { + memset(fbconfig, 0, sizeof(_sapp_gl_fbconfig)); + /* -1 means "don't care" */ + fbconfig->red_bits = -1; + fbconfig->green_bits = -1; + fbconfig->blue_bits = -1; + fbconfig->alpha_bits = -1; + fbconfig->depth_bits = -1; + fbconfig->stencil_bits = -1; + fbconfig->samples = -1; +} + +_SOKOL_PRIVATE const _sapp_gl_fbconfig* _sapp_gl_choose_fbconfig(const _sapp_gl_fbconfig* desired, const _sapp_gl_fbconfig* alternatives, unsigned int count) { + unsigned int i; + unsigned int missing, least_missing = 1000000; + unsigned int color_diff, least_color_diff = 10000000; + unsigned int extra_diff, least_extra_diff = 10000000; + const _sapp_gl_fbconfig* current; + const _sapp_gl_fbconfig* closest = NULL; + for (i = 0; i < count; i++) { + current = alternatives + i; + if (desired->doublebuffer != current->doublebuffer) { + continue; + } + missing = 0; + 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 + */ + 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 */ + 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 + */ + if (missing < least_missing) { + closest = current; + } + else if (missing == least_missing) { + if ((color_diff < least_color_diff) || + (color_diff == least_color_diff && extra_diff < least_extra_diff)) + { + closest = current; + } + } + if (current == closest) { + least_missing = missing; + least_color_diff = color_diff; + least_extra_diff = extra_diff; + } + } + return closest; +} +#endif + +/*== WINDOWS ==================================================================*/ +#if defined(_SAPP_WIN32) #if defined(SOKOL_D3D11) #define _SAPP_SAFE_RELEASE(class, obj) if (obj) { class##_Release(obj); obj=0; } + _SOKOL_PRIVATE void _sapp_d3d11_create_device_and_swapchain(void) { - DXGI_SWAP_CHAIN_DESC* sc_desc = &_sapp_dxgi_swap_chain_desc; + DXGI_SWAP_CHAIN_DESC* sc_desc = &_sapp.d3d11.swap_chain_desc; sc_desc->BufferDesc.Width = _sapp.framebuffer_width; sc_desc->BufferDesc.Height = _sapp.framebuffer_height; sc_desc->BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; sc_desc->BufferDesc.RefreshRate.Numerator = 60; sc_desc->BufferDesc.RefreshRate.Denominator = 1; - sc_desc->OutputWindow = _sapp_win32_hwnd; + sc_desc->OutputWindow = _sapp.win32.hwnd; sc_desc->Windowed = true; sc_desc->SwapEffect = DXGI_SWAP_EFFECT_DISCARD; sc_desc->BufferCount = 1; @@ -3770,30 +4423,30 @@ _SOKOL_PRIVATE void _sapp_d3d11_create_device_and_swapchain(void) { 0, /* FeatureLevels */ D3D11_SDK_VERSION, /* SDKVersion */ sc_desc, /* pSwapChainDesc */ - &_sapp_dxgi_swap_chain, /* ppSwapChain */ - &_sapp_d3d11_device, /* ppDevice */ + &_sapp.d3d11.swap_chain, /* ppSwapChain */ + &_sapp.d3d11.device, /* ppDevice */ &feature_level, /* pFeatureLevel */ - &_sapp_d3d11_device_context); /* ppImmediateContext */ + &_sapp.d3d11.device_context); /* ppImmediateContext */ _SOKOL_UNUSED(hr); - SOKOL_ASSERT(SUCCEEDED(hr) && _sapp_dxgi_swap_chain && _sapp_d3d11_device && _sapp_d3d11_device_context); + SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.swap_chain && _sapp.d3d11.device && _sapp.d3d11.device_context); } _SOKOL_PRIVATE void _sapp_d3d11_destroy_device_and_swapchain(void) { - _SAPP_SAFE_RELEASE(IDXGISwapChain, _sapp_dxgi_swap_chain); - _SAPP_SAFE_RELEASE(ID3D11DeviceContext, _sapp_d3d11_device_context); - _SAPP_SAFE_RELEASE(ID3D11Device, _sapp_d3d11_device); + _SAPP_SAFE_RELEASE(IDXGISwapChain, _sapp.d3d11.swap_chain); + _SAPP_SAFE_RELEASE(ID3D11DeviceContext, _sapp.d3d11.device_context); + _SAPP_SAFE_RELEASE(ID3D11Device, _sapp.d3d11.device); } _SOKOL_PRIVATE void _sapp_d3d11_create_default_render_target(void) { HRESULT hr; #ifdef __cplusplus - hr = IDXGISwapChain_GetBuffer(_sapp_dxgi_swap_chain, 0, IID_ID3D11Texture2D, (void**)&_sapp_d3d11_rt); + hr = IDXGISwapChain_GetBuffer(_sapp.d3d11.swap_chain, 0, IID_ID3D11Texture2D, (void**)&_sapp.d3d11.rt); #else - hr = IDXGISwapChain_GetBuffer(_sapp_dxgi_swap_chain, 0, &IID_ID3D11Texture2D, (void**)&_sapp_d3d11_rt); + hr = IDXGISwapChain_GetBuffer(_sapp.d3d11.swap_chain, 0, &IID_ID3D11Texture2D, (void**)&_sapp.d3d11.rt); #endif - SOKOL_ASSERT(SUCCEEDED(hr) && _sapp_d3d11_rt); - hr = ID3D11Device_CreateRenderTargetView(_sapp_d3d11_device, (ID3D11Resource*)_sapp_d3d11_rt, NULL, &_sapp_d3d11_rtv); - SOKOL_ASSERT(SUCCEEDED(hr) && _sapp_d3d11_rtv); + SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.rt); + hr = ID3D11Device_CreateRenderTargetView(_sapp.d3d11.device, (ID3D11Resource*)_sapp.d3d11.rt, NULL, &_sapp.d3d11.rtv); + SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.rtv); D3D11_TEXTURE2D_DESC ds_desc; memset(&ds_desc, 0, sizeof(ds_desc)); ds_desc.Width = _sapp.framebuffer_width; @@ -3801,54 +4454,54 @@ _SOKOL_PRIVATE void _sapp_d3d11_create_default_render_target(void) { ds_desc.MipLevels = 1; ds_desc.ArraySize = 1; ds_desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; - ds_desc.SampleDesc = _sapp_dxgi_swap_chain_desc.SampleDesc; + ds_desc.SampleDesc = _sapp.d3d11.swap_chain_desc.SampleDesc; ds_desc.Usage = D3D11_USAGE_DEFAULT; ds_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; - hr = ID3D11Device_CreateTexture2D(_sapp_d3d11_device, &ds_desc, NULL, &_sapp_d3d11_ds); - SOKOL_ASSERT(SUCCEEDED(hr) && _sapp_d3d11_ds); + hr = ID3D11Device_CreateTexture2D(_sapp.d3d11.device, &ds_desc, NULL, &_sapp.d3d11.ds); + SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.ds); D3D11_DEPTH_STENCIL_VIEW_DESC dsv_desc; memset(&dsv_desc, 0, sizeof(dsv_desc)); dsv_desc.Format = ds_desc.Format; dsv_desc.ViewDimension = _sapp.sample_count > 1 ? D3D11_DSV_DIMENSION_TEXTURE2DMS : D3D11_DSV_DIMENSION_TEXTURE2D; - hr = ID3D11Device_CreateDepthStencilView(_sapp_d3d11_device, (ID3D11Resource*)_sapp_d3d11_ds, &dsv_desc, &_sapp_d3d11_dsv); - SOKOL_ASSERT(SUCCEEDED(hr) && _sapp_d3d11_dsv); + hr = ID3D11Device_CreateDepthStencilView(_sapp.d3d11.device, (ID3D11Resource*)_sapp.d3d11.ds, &dsv_desc, &_sapp.d3d11.dsv); + SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.dsv); } _SOKOL_PRIVATE void _sapp_d3d11_destroy_default_render_target(void) { - _SAPP_SAFE_RELEASE(ID3D11Texture2D, _sapp_d3d11_rt); - _SAPP_SAFE_RELEASE(ID3D11RenderTargetView, _sapp_d3d11_rtv); - _SAPP_SAFE_RELEASE(ID3D11Texture2D, _sapp_d3d11_ds); - _SAPP_SAFE_RELEASE(ID3D11DepthStencilView, _sapp_d3d11_dsv); + _SAPP_SAFE_RELEASE(ID3D11Texture2D, _sapp.d3d11.rt); + _SAPP_SAFE_RELEASE(ID3D11RenderTargetView, _sapp.d3d11.rtv); + _SAPP_SAFE_RELEASE(ID3D11Texture2D, _sapp.d3d11.ds); + _SAPP_SAFE_RELEASE(ID3D11DepthStencilView, _sapp.d3d11.dsv); } _SOKOL_PRIVATE void _sapp_d3d11_resize_default_render_target(void) { - if (_sapp_dxgi_swap_chain) { + if (_sapp.d3d11.swap_chain) { _sapp_d3d11_destroy_default_render_target(); - IDXGISwapChain_ResizeBuffers(_sapp_dxgi_swap_chain, 1, _sapp.framebuffer_width, _sapp.framebuffer_height, DXGI_FORMAT_B8G8R8A8_UNORM, 0); + IDXGISwapChain_ResizeBuffers(_sapp.d3d11.swap_chain, 1, _sapp.framebuffer_width, _sapp.framebuffer_height, DXGI_FORMAT_B8G8R8A8_UNORM, 0); _sapp_d3d11_create_default_render_target(); } } -#endif +#endif /* SOKOL_D3D11 */ #if defined(SOKOL_GLCORE33) _SOKOL_PRIVATE void _sapp_wgl_init(void) { - _sapp_opengl32 = LoadLibraryA("opengl32.dll"); - if (!_sapp_opengl32) { + _sapp.wgl.opengl32 = LoadLibraryA("opengl32.dll"); + if (!_sapp.wgl.opengl32) { _sapp_fail("Failed to load opengl32.dll\n"); } - SOKOL_ASSERT(_sapp_opengl32); - _sapp_wglCreateContext = (PFN_wglCreateContext) GetProcAddress(_sapp_opengl32, "wglCreateContext"); - SOKOL_ASSERT(_sapp_wglCreateContext); - _sapp_wglDeleteContext = (PFN_wglDeleteContext) GetProcAddress(_sapp_opengl32, "wglDeleteContext"); - SOKOL_ASSERT(_sapp_wglDeleteContext); - _sapp_wglGetProcAddress = (PFN_wglGetProcAddress) GetProcAddress(_sapp_opengl32, "wglGetProcAddress"); - SOKOL_ASSERT(_sapp_wglGetProcAddress); - _sapp_wglGetCurrentDC = (PFN_wglGetCurrentDC) GetProcAddress(_sapp_opengl32, "wglGetCurrentDC"); - SOKOL_ASSERT(_sapp_wglGetCurrentDC); - _sapp_wglMakeCurrent = (PFN_wglMakeCurrent) GetProcAddress(_sapp_opengl32, "wglMakeCurrent"); - SOKOL_ASSERT(_sapp_wglMakeCurrent); + SOKOL_ASSERT(_sapp.wgl.opengl32); + _sapp.wgl.CreateContext = (PFN_wglCreateContext) GetProcAddress(_sapp.wgl.opengl32, "wglCreateContext"); + SOKOL_ASSERT(_sapp.wgl.CreateContext); + _sapp.wgl.DeleteContext = (PFN_wglDeleteContext) GetProcAddress(_sapp.wgl.opengl32, "wglDeleteContext"); + SOKOL_ASSERT(_sapp.wgl.DeleteContext); + _sapp.wgl.GetProcAddress = (PFN_wglGetProcAddress) GetProcAddress(_sapp.wgl.opengl32, "wglGetProcAddress"); + SOKOL_ASSERT(_sapp.wgl.GetProcAddress); + _sapp.wgl.GetCurrentDC = (PFN_wglGetCurrentDC) GetProcAddress(_sapp.wgl.opengl32, "wglGetCurrentDC"); + SOKOL_ASSERT(_sapp.wgl.GetCurrentDC); + _sapp.wgl.MakeCurrent = (PFN_wglMakeCurrent) GetProcAddress(_sapp.wgl.opengl32, "wglMakeCurrent"); + SOKOL_ASSERT(_sapp.wgl.MakeCurrent); - _sapp_win32_msg_hwnd = CreateWindowExW(WS_EX_OVERLAPPEDWINDOW, + _sapp.wgl.msg_hwnd = CreateWindowExW(WS_EX_OVERLAPPEDWINDOW, L"SOKOLAPP", L"sokol-app message window", WS_CLIPSIBLINGS|WS_CLIPCHILDREN, @@ -3856,25 +4509,25 @@ _SOKOL_PRIVATE void _sapp_wgl_init(void) { NULL, NULL, GetModuleHandleW(NULL), NULL); - if (!_sapp_win32_msg_hwnd) { + if (!_sapp.wgl.msg_hwnd) { _sapp_fail("Win32: failed to create helper window!\n"); } - ShowWindow(_sapp_win32_msg_hwnd, SW_HIDE); + ShowWindow(_sapp.wgl.msg_hwnd, SW_HIDE); MSG msg; - while (PeekMessageW(&msg, _sapp_win32_msg_hwnd, 0, 0, PM_REMOVE)) { + while (PeekMessageW(&msg, _sapp.wgl.msg_hwnd, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessageW(&msg); } - _sapp_win32_msg_dc = GetDC(_sapp_win32_msg_hwnd); - if (!_sapp_win32_msg_dc) { + _sapp.wgl.msg_dc = GetDC(_sapp.wgl.msg_hwnd); + if (!_sapp.wgl.msg_dc) { _sapp_fail("Win32: failed to obtain helper window DC!\n"); } } _SOKOL_PRIVATE void _sapp_wgl_shutdown(void) { - SOKOL_ASSERT(_sapp_opengl32 && _sapp_win32_msg_hwnd); - DestroyWindow(_sapp_win32_msg_hwnd); _sapp_win32_msg_hwnd = 0; - FreeLibrary(_sapp_opengl32); _sapp_opengl32 = 0; + SOKOL_ASSERT(_sapp.wgl.opengl32 && _sapp.wgl.msg_hwnd); + DestroyWindow(_sapp.wgl.msg_hwnd); _sapp.wgl.msg_hwnd = 0; + FreeLibrary(_sapp.wgl.opengl32); _sapp.wgl.opengl32 = 0; } _SOKOL_PRIVATE bool _sapp_wgl_has_ext(const char* ext, const char* extensions) { @@ -3898,16 +4551,16 @@ _SOKOL_PRIVATE bool _sapp_wgl_has_ext(const char* ext, const char* extensions) { _SOKOL_PRIVATE bool _sapp_wgl_ext_supported(const char* ext) { SOKOL_ASSERT(ext); - if (_sapp_GetExtensionsStringEXT) { - const char* extensions = _sapp_GetExtensionsStringEXT(); + if (_sapp.wgl.GetExtensionsStringEXT) { + const char* extensions = _sapp.wgl.GetExtensionsStringEXT(); if (extensions) { if (_sapp_wgl_has_ext(ext, extensions)) { return true; } } } - if (_sapp_GetExtensionsStringARB) { - const char* extensions = _sapp_GetExtensionsStringARB(_sapp_wglGetCurrentDC()); + if (_sapp.wgl.GetExtensionsStringARB) { + const char* extensions = _sapp.wgl.GetExtensionsStringARB(_sapp.wgl.GetCurrentDC()); if (extensions) { if (_sapp_wgl_has_ext(ext, extensions)) { return true; @@ -3918,7 +4571,7 @@ _SOKOL_PRIVATE bool _sapp_wgl_ext_supported(const char* ext) { } _SOKOL_PRIVATE void _sapp_wgl_load_extensions(void) { - SOKOL_ASSERT(_sapp_win32_msg_dc); + SOKOL_ASSERT(_sapp.wgl.msg_dc); PIXELFORMATDESCRIPTOR pfd; memset(&pfd, 0, sizeof(pfd)); pfd.nSize = sizeof(pfd); @@ -3926,42 +4579,42 @@ _SOKOL_PRIVATE void _sapp_wgl_load_extensions(void) { pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; pfd.iPixelType = PFD_TYPE_RGBA; pfd.cColorBits = 24; - if (!SetPixelFormat(_sapp_win32_msg_dc, ChoosePixelFormat(_sapp_win32_msg_dc, &pfd), &pfd)) { + if (!SetPixelFormat(_sapp.wgl.msg_dc, ChoosePixelFormat(_sapp.wgl.msg_dc, &pfd), &pfd)) { _sapp_fail("WGL: failed to set pixel format for dummy context\n"); } - HGLRC rc = _sapp_wglCreateContext(_sapp_win32_msg_dc); + HGLRC rc = _sapp.wgl.CreateContext(_sapp.wgl.msg_dc); if (!rc) { _sapp_fail("WGL: Failed to create dummy context\n"); } - if (!_sapp_wglMakeCurrent(_sapp_win32_msg_dc, rc)) { + if (!_sapp.wgl.MakeCurrent(_sapp.wgl.msg_dc, rc)) { _sapp_fail("WGL: Failed to make context current\n"); } - _sapp_GetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC) _sapp_wglGetProcAddress("wglGetExtensionsStringEXT"); - _sapp_GetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC) _sapp_wglGetProcAddress("wglGetExtensionsStringARB"); - _sapp_CreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) _sapp_wglGetProcAddress("wglCreateContextAttribsARB"); - _sapp_SwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) _sapp_wglGetProcAddress("wglSwapIntervalEXT"); - _sapp_GetPixelFormatAttribivARB = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC) _sapp_wglGetProcAddress("wglGetPixelFormatAttribivARB"); - _sapp_arb_multisample = _sapp_wgl_ext_supported("WGL_ARB_multisample"); - _sapp_arb_create_context = _sapp_wgl_ext_supported("WGL_ARB_create_context"); - _sapp_arb_create_context_profile = _sapp_wgl_ext_supported("WGL_ARB_create_context_profile"); - _sapp_ext_swap_control = _sapp_wgl_ext_supported("WGL_EXT_swap_control"); - _sapp_arb_pixel_format = _sapp_wgl_ext_supported("WGL_ARB_pixel_format"); - _sapp_wglMakeCurrent(_sapp_win32_msg_dc, 0); - _sapp_wglDeleteContext(rc); + _sapp.wgl.GetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC) _sapp.wgl.GetProcAddress("wglGetExtensionsStringEXT"); + _sapp.wgl.GetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC) _sapp.wgl.GetProcAddress("wglGetExtensionsStringARB"); + _sapp.wgl.CreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) _sapp.wgl.GetProcAddress("wglCreateContextAttribsARB"); + _sapp.wgl.SwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) _sapp.wgl.GetProcAddress("wglSwapIntervalEXT"); + _sapp.wgl.GetPixelFormatAttribivARB = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC) _sapp.wgl.GetProcAddress("wglGetPixelFormatAttribivARB"); + _sapp.wgl.arb_multisample = _sapp_wgl_ext_supported("WGL_ARB_multisample"); + _sapp.wgl.arb_create_context = _sapp_wgl_ext_supported("WGL_ARB_create_context"); + _sapp.wgl.arb_create_context_profile = _sapp_wgl_ext_supported("WGL_ARB_create_context_profile"); + _sapp.wgl.ext_swap_control = _sapp_wgl_ext_supported("WGL_EXT_swap_control"); + _sapp.wgl.arb_pixel_format = _sapp_wgl_ext_supported("WGL_ARB_pixel_format"); + _sapp.wgl.MakeCurrent(_sapp.wgl.msg_dc, 0); + _sapp.wgl.DeleteContext(rc); } _SOKOL_PRIVATE int _sapp_wgl_attrib(int pixel_format, int attrib) { - SOKOL_ASSERT(_sapp_arb_pixel_format); + SOKOL_ASSERT(_sapp.wgl.arb_pixel_format); int value = 0; - if (!_sapp_GetPixelFormatAttribivARB(_sapp_win32_dc, pixel_format, 0, 1, &attrib, &value)) { + if (!_sapp.wgl.GetPixelFormatAttribivARB(_sapp.win32.dc, pixel_format, 0, 1, &attrib, &value)) { _sapp_fail("WGL: Failed to retrieve pixel format attribute\n"); } return value; } _SOKOL_PRIVATE int _sapp_wgl_find_pixel_format(void) { - SOKOL_ASSERT(_sapp_win32_dc); - SOKOL_ASSERT(_sapp_arb_pixel_format); + SOKOL_ASSERT(_sapp.win32.dc); + SOKOL_ASSERT(_sapp.wgl.arb_pixel_format); const _sapp_gl_fbconfig* closest; int native_count = _sapp_wgl_attrib(1, WGL_NUMBER_PIXEL_FORMATS_ARB); @@ -3989,7 +4642,7 @@ _SOKOL_PRIVATE int _sapp_wgl_find_pixel_format(void) { if (_sapp_wgl_attrib(n, WGL_DOUBLE_BUFFER_ARB)) { u->doublebuffer = true; } - if (_sapp_arb_multisample) { + if (_sapp.wgl.arb_multisample) { u->samples = _sapp_wgl_attrib(n, WGL_SAMPLES_ARB); } u->handle = n; @@ -4021,16 +4674,16 @@ _SOKOL_PRIVATE void _sapp_wgl_create_context(void) { _sapp_fail("WGL: Didn't find matching pixel format.\n"); } PIXELFORMATDESCRIPTOR pfd; - if (!DescribePixelFormat(_sapp_win32_dc, pixel_format, sizeof(pfd), &pfd)) { + if (!DescribePixelFormat(_sapp.win32.dc, pixel_format, sizeof(pfd), &pfd)) { _sapp_fail("WGL: Failed to retrieve PFD for selected pixel format!\n"); } - if (!SetPixelFormat(_sapp_win32_dc, pixel_format, &pfd)) { + if (!SetPixelFormat(_sapp.win32.dc, pixel_format, &pfd)) { _sapp_fail("WGL: Failed to set selected pixel format!\n"); } - if (!_sapp_arb_create_context) { + if (!_sapp.wgl.arb_create_context) { _sapp_fail("WGL: ARB_create_context required!\n"); } - if (!_sapp_arb_create_context_profile) { + if (!_sapp.wgl.arb_create_context_profile) { _sapp_fail("WGL: ARB_create_context_profile required!\n"); } const int attrs[] = { @@ -4040,8 +4693,8 @@ _SOKOL_PRIVATE void _sapp_wgl_create_context(void) { WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, 0, 0 }; - _sapp_gl_ctx = _sapp_CreateContextAttribsARB(_sapp_win32_dc, 0, attrs); - if (!_sapp_gl_ctx) { + _sapp.wgl.gl_ctx = _sapp.wgl.CreateContextAttribsARB(_sapp.win32.dc, 0, attrs); + if (!_sapp.wgl.gl_ctx) { const DWORD err = GetLastError(); if (err == (0xc0070000 | ERROR_INVALID_VERSION_ARB)) { _sapp_fail("WGL: Driver does not support OpenGL version 3.3\n"); @@ -4056,25 +4709,25 @@ _SOKOL_PRIVATE void _sapp_wgl_create_context(void) { _sapp_fail("WGL: Failed to create OpenGL context"); } } - _sapp_wglMakeCurrent(_sapp_win32_dc, _sapp_gl_ctx); - if (_sapp_ext_swap_control) { + _sapp.wgl.MakeCurrent(_sapp.win32.dc, _sapp.wgl.gl_ctx); + if (_sapp.wgl.ext_swap_control) { /* FIXME: DwmIsCompositionEnabled() (see GLFW) */ - _sapp_SwapIntervalEXT(_sapp.swap_interval); + _sapp.wgl.SwapIntervalEXT(_sapp.swap_interval); } } _SOKOL_PRIVATE void _sapp_wgl_destroy_context(void) { - SOKOL_ASSERT(_sapp_gl_ctx); - _sapp_wglDeleteContext(_sapp_gl_ctx); - _sapp_gl_ctx = 0; + SOKOL_ASSERT(_sapp.wgl.gl_ctx); + _sapp.wgl.DeleteContext(_sapp.wgl.gl_ctx); + _sapp.wgl.gl_ctx = 0; } _SOKOL_PRIVATE void _sapp_wgl_swap_buffers(void) { - SOKOL_ASSERT(_sapp_win32_dc); + SOKOL_ASSERT(_sapp.win32.dc); /* FIXME: DwmIsCompositionEnabled? (see GLFW) */ - SwapBuffers(_sapp_win32_dc); + SwapBuffers(_sapp.win32.dc); } -#endif +#endif /* SOKOL_GLCORE33 */ _SOKOL_PRIVATE bool _sapp_win32_utf8_to_wide(const char* src, wchar_t* dst, int dst_num_bytes) { SOKOL_ASSERT(src && dst && (dst_num_bytes > 1)); @@ -4097,16 +4750,108 @@ _SOKOL_PRIVATE bool _sapp_win32_wide_to_utf8(const wchar_t* src, char* dst, int return 0 != WideCharToMultiByte(CP_UTF8, 0, src, -1, dst, dst_num_bytes, NULL, NULL); } -_SOKOL_PRIVATE void _sapp_win32_show_mouse(bool shown) { - ShowCursor((BOOL)shown); +_SOKOL_PRIVATE void _sapp_win32_toggle_fullscreen(void) { + HMONITOR monitor = MonitorFromWindow(_sapp.win32.hwnd, MONITOR_DEFAULTTONEAREST); + MONITORINFO minfo; + memset(&minfo, 0, sizeof(minfo)); + minfo.cbSize = sizeof(MONITORINFO); + GetMonitorInfo(monitor, &minfo); + const RECT mr = minfo.rcMonitor; + const int monitor_w = mr.right - mr.left; + const int monitor_h = mr.bottom - mr.top; + + const DWORD win_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; + DWORD win_style; + RECT rect = { 0, 0, 0, 0 }; + + _sapp.fullscreen = !_sapp.fullscreen; + if (!_sapp.fullscreen) { + win_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SIZEBOX; + rect.right = (int) ((float)_sapp.desc.width * _sapp.win32.dpi.window_scale); + rect.bottom = (int) ((float)_sapp.desc.height * _sapp.win32.dpi.window_scale); + } + else { + win_style = WS_POPUP | WS_SYSMENU | WS_VISIBLE; + rect.right = monitor_w; + rect.bottom = monitor_h; + } + AdjustWindowRectEx(&rect, win_style, FALSE, win_ex_style); + int win_width = rect.right - rect.left; + int win_height = rect.bottom - rect.top; + if (!_sapp.fullscreen) { + rect.left = (monitor_w - win_width) / 2; + rect.top = (monitor_h - win_height) / 2; + } + + SetWindowLongPtr(_sapp.win32.hwnd, GWL_STYLE, win_style); + SetWindowPos(_sapp.win32.hwnd, HWND_TOP, mr.left + rect.left, mr.top + rect.top, win_width, win_height, SWP_SHOWWINDOW | SWP_FRAMECHANGED); } -_SOKOL_PRIVATE bool _sapp_win32_mouse_shown(void) { - CURSORINFO cursor_info; - memset(&cursor_info, 0, sizeof(CURSORINFO)); - cursor_info.cbSize = sizeof(CURSORINFO); - GetCursorInfo(&cursor_info); - return (cursor_info.flags & CURSOR_SHOWING) != 0; +_SOKOL_PRIVATE void _sapp_win32_show_mouse(bool visible) { + /* NOTE: this function is only called when the mouse visibility actually changes */ + ShowCursor((BOOL)visible); +} + +_SOKOL_PRIVATE void _sapp_win32_lock_mouse(bool lock) { + if (lock == _sapp.mouse.locked) { + return; + } + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; + _sapp.mouse.locked = lock; + if (_sapp.mouse.locked) { + /* store the current mouse position, so it can be restored when unlocked */ + POINT pos; + BOOL res = GetCursorPos(&pos); + SOKOL_ASSERT(res); _SOKOL_UNUSED(res); + _sapp.win32.mouse_locked_x = pos.x; + _sapp.win32.mouse_locked_y = pos.y; + + /* while the mouse is locked, make the mouse cursor invisible and + confine the mouse movement to a small rectangle inside our window + (so that we dont miss any mouse up events) + */ + RECT client_rect = { + _sapp.win32.mouse_locked_x, + _sapp.win32.mouse_locked_y, + _sapp.win32.mouse_locked_x, + _sapp.win32.mouse_locked_y + }; + ClipCursor(&client_rect); + + /* make the mouse cursor invisible, this will stack with sapp_show_mouse() */ + ShowCursor(FALSE); + + /* enable raw input for mouse, starts sending WM_INPUT messages to WinProc (see GLFW) */ + const RAWINPUTDEVICE rid = { + 0x01, // usUsagePage: HID_USAGE_PAGE_GENERIC + 0x02, // usUsage: HID_USAGE_GENERIC_MOUSE + 0, // dwFlags + _sapp.win32.hwnd // hwndTarget + }; + if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) { + SOKOL_LOG("RegisterRawInputDevices() failed (on mouse lock).\n"); + } + /* in case the raw mouse device only supports absolute position reporting, + we need to skip the dx/dy compution for the first WM_INPUT event + */ + _sapp.win32.raw_input_mousepos_valid = false; + } + else { + /* disable raw input for mouse */ + const RAWINPUTDEVICE rid = { 0x01, 0x02, RIDEV_REMOVE, NULL }; + if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) { + SOKOL_LOG("RegisterRawInputDevices() failed (on mouse unlock).\n"); + } + + /* let the mouse roam freely again */ + ClipCursor(NULL); + ShowCursor(TRUE); + + /* restore the 'pre-locked' mouse position */ + BOOL res = SetCursorPos(_sapp.win32.mouse_locked_x, _sapp.win32.mouse_locked_y); + SOKOL_ASSERT(res); _SOKOL_UNUSED(res); + } } _SOKOL_PRIVATE void _sapp_win32_init_keytable(void) { @@ -4234,14 +4979,14 @@ _SOKOL_PRIVATE void _sapp_win32_init_keytable(void) { /* updates current window and framebuffer size from the window's client rect, returns true if size has changed */ _SOKOL_PRIVATE bool _sapp_win32_update_dimensions(void) { RECT rect; - if (GetClientRect(_sapp_win32_hwnd, &rect)) { - _sapp.window_width = (int)((float)(rect.right - rect.left) / _sapp_win32_window_scale); - _sapp.window_height = (int)((float)(rect.bottom - rect.top) / _sapp_win32_window_scale); - const int fb_width = (int)((float)_sapp.window_width * _sapp_win32_content_scale); - const int fb_height = (int)((float)_sapp.window_height * _sapp_win32_content_scale); + if (GetClientRect(_sapp.win32.hwnd, &rect)) { + _sapp.window_width = (int)((float)(rect.right - rect.left) / _sapp.win32.dpi.window_scale); + _sapp.window_height = (int)((float)(rect.bottom - rect.top) / _sapp.win32.dpi.window_scale); + const int fb_width = (int)((float)_sapp.window_width * _sapp.win32.dpi.content_scale); + const int fb_height = (int)((float)_sapp.window_height * _sapp.win32.dpi.content_scale); if ((fb_width != _sapp.framebuffer_width) || (fb_height != _sapp.framebuffer_height)) { - _sapp.framebuffer_width = (int)((float)_sapp.window_width * _sapp_win32_content_scale); - _sapp.framebuffer_height = (int)((float)_sapp.window_height * _sapp_win32_content_scale); + _sapp.framebuffer_width = fb_width; + _sapp.framebuffer_height = fb_height; /* prevent a framebuffer size of 0 when window is minimized */ if (_sapp.framebuffer_width == 0) { _sapp.framebuffer_width = 1; @@ -4261,16 +5006,16 @@ _SOKOL_PRIVATE bool _sapp_win32_update_dimensions(void) { _SOKOL_PRIVATE uint32_t _sapp_win32_mods(void) { uint32_t mods = 0; - if (GetKeyState(VK_SHIFT) & (1<<31)) { + if (GetKeyState(VK_SHIFT) & (1<<15)) { mods |= SAPP_MODIFIER_SHIFT; } - if (GetKeyState(VK_CONTROL) & (1<<31)) { + if (GetKeyState(VK_CONTROL) & (1<<15)) { mods |= SAPP_MODIFIER_CTRL; } - if (GetKeyState(VK_MENU) & (1<<31)) { + if (GetKeyState(VK_MENU) & (1<<15)) { mods |= SAPP_MODIFIER_ALT; } - if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & (1<<31)) { + if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & (1<<15)) { mods |= SAPP_MODIFIER_SUPER; } return mods; @@ -4281,8 +5026,6 @@ _SOKOL_PRIVATE void _sapp_win32_mouse_event(sapp_event_type type, sapp_mousebutt _sapp_init_event(type); _sapp.event.modifiers = _sapp_win32_mods(); _sapp.event.mouse_button = btn; - _sapp.event.mouse_x = _sapp.mouse_x; - _sapp.event.mouse_y = _sapp.mouse_y; _sapp_call_event(&_sapp.event); } } @@ -4305,7 +5048,7 @@ _SOKOL_PRIVATE void _sapp_win32_key_event(sapp_event_type type, int vk, bool rep _sapp.event.key_repeat = repeat; _sapp_call_event(&_sapp.event); /* check if a CLIPBOARD_PASTED event must be sent too */ - if (_sapp.clipboard_enabled && + if (_sapp.clipboard.enabled && (type == SAPP_EVENTTYPE_KEY_DOWN) && (_sapp.event.modifiers == SAPP_MODIFIER_CTRL) && (_sapp.event.key_code == SAPP_KEYCODE_V)) @@ -4335,7 +5078,7 @@ _SOKOL_PRIVATE void _sapp_win32_app_event(sapp_event_type type) { _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { /* FIXME: refresh rendering during resize with a WM_TIMER event */ - if (!_sapp_win32_in_create_window) { + if (!_sapp.win32.in_create_window) { switch (uMsg) { case WM_CLOSE: /* only give user a chance to intervene when sapp_quit() wasn't already called */ @@ -4358,7 +5101,7 @@ _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM switch (wParam & 0xFFF0) { case SC_SCREENSAVE: case SC_MONITORPOWER: - if (_sapp.desc.fullscreen) { + if (_sapp.fullscreen) { /* disable screen saver and blanking in fullscreen mode */ return 0; } @@ -4373,8 +5116,8 @@ _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM case WM_SIZE: { const bool iconified = wParam == SIZE_MINIMIZED; - if (iconified != _sapp_win32_iconified) { - _sapp_win32_iconified = iconified; + if (iconified != _sapp.win32.iconified) { + _sapp.win32.iconified = iconified; if (iconified) { _sapp_win32_app_event(SAPP_EVENTTYPE_ICONIFIED); } @@ -4411,23 +5154,70 @@ _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_MIDDLE); break; case WM_MOUSEMOVE: - _sapp.mouse_x = (float)GET_X_LPARAM(lParam) * _sapp_win32_mouse_scale; - _sapp.mouse_y = (float)GET_Y_LPARAM(lParam) * _sapp_win32_mouse_scale; - if (!_sapp.win32_mouse_tracked) { - _sapp.win32_mouse_tracked = true; - TRACKMOUSEEVENT tme; - memset(&tme, 0, sizeof(tme)); - tme.cbSize = sizeof(tme); - tme.dwFlags = TME_LEAVE; - tme.hwndTrack = _sapp_win32_hwnd; - TrackMouseEvent(&tme); - _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID); + if (!_sapp.mouse.locked) { + const float new_x = (float)GET_X_LPARAM(lParam) * _sapp.win32.dpi.mouse_scale; + const float new_y = (float)GET_Y_LPARAM(lParam) * _sapp.win32.dpi.mouse_scale; + /* don't update dx/dy in the very first event */ + if (_sapp.mouse.pos_valid) { + _sapp.mouse.dx = new_x - _sapp.mouse.x; + _sapp.mouse.dy = new_y - _sapp.mouse.y; + } + _sapp.mouse.x = new_x; + _sapp.mouse.y = new_y; + _sapp.mouse.pos_valid = true; + if (!_sapp.win32.mouse_tracked) { + _sapp.win32.mouse_tracked = true; + TRACKMOUSEEVENT tme; + memset(&tme, 0, sizeof(tme)); + tme.cbSize = sizeof(tme); + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = _sapp.win32.hwnd; + TrackMouseEvent(&tme); + _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID); + } + _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID); } - _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID); break; + case WM_INPUT: + /* raw mouse input during mouse-lock */ + if (_sapp.mouse.locked) { + HRAWINPUT ri = (HRAWINPUT) lParam; + UINT size = sizeof(_sapp.win32.raw_input_data); + if (-1 == GetRawInputData(ri, RID_INPUT, &_sapp.win32.raw_input_data, &size, sizeof(RAWINPUTHEADER))) { + SOKOL_LOG("GetRawInputData() failed\n"); + break; + } + const RAWINPUT* raw_mouse_data = (const RAWINPUT*) &_sapp.win32.raw_input_data; + if (raw_mouse_data->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) { + /* mouse only reports absolute position + NOTE: THIS IS UNTESTED, it's unclear from reading the + Win32 RawInput docs under which circumstances absolute + positions are sent. + */ + if (_sapp.win32.raw_input_mousepos_valid) { + LONG new_x = raw_mouse_data->data.mouse.lLastX; + LONG new_y = raw_mouse_data->data.mouse.lLastY; + _sapp.mouse.dx = (float) (new_x - _sapp.win32.raw_input_mousepos_x); + _sapp.mouse.dy = (float) (new_y - _sapp.win32.raw_input_mousepos_y); + _sapp.win32.raw_input_mousepos_x = new_x; + _sapp.win32.raw_input_mousepos_y = new_y; + _sapp.win32.raw_input_mousepos_valid = true; + } + } + else { + /* mouse reports movement delta (this seems to be the common case) */ + _sapp.mouse.dx = (float) raw_mouse_data->data.mouse.lLastX; + _sapp.mouse.dy = (float) raw_mouse_data->data.mouse.lLastY; + } + _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID); + } + break; + case WM_MOUSELEAVE: - _sapp.win32_mouse_tracked = false; - _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID); + if (!_sapp.mouse.locked) { + _sapp.win32.mouse_tracked = false; + _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID); + } break; case WM_MOUSEWHEEL: _sapp_win32_scroll_event(0.0f, (float)((SHORT)HIWORD(wParam))); @@ -4467,21 +5257,21 @@ _SOKOL_PRIVATE void _sapp_win32_create_window(void) { DWORD win_style; const DWORD win_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; RECT rect = { 0, 0, 0, 0 }; - if (_sapp.desc.fullscreen) { + if (_sapp.fullscreen) { win_style = WS_POPUP | WS_SYSMENU | WS_VISIBLE; rect.right = GetSystemMetrics(SM_CXSCREEN); rect.bottom = GetSystemMetrics(SM_CYSCREEN); } else { win_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SIZEBOX; - rect.right = (int) ((float)_sapp.window_width * _sapp_win32_window_scale); - rect.bottom = (int) ((float)_sapp.window_height * _sapp_win32_window_scale); + rect.right = (int) ((float)_sapp.window_width * _sapp.win32.dpi.window_scale); + rect.bottom = (int) ((float)_sapp.window_height * _sapp.win32.dpi.window_scale); } AdjustWindowRectEx(&rect, win_style, FALSE, win_ex_style); const int win_width = rect.right - rect.left; const int win_height = rect.bottom - rect.top; - _sapp_win32_in_create_window = true; - _sapp_win32_hwnd = CreateWindowExW( + _sapp.win32.in_create_window = true; + _sapp.win32.hwnd = CreateWindowExW( win_ex_style, /* dwExStyle */ L"SOKOLAPP", /* lpClassName */ _sapp.window_title_wide, /* lpWindowName */ @@ -4494,68 +5284,73 @@ _SOKOL_PRIVATE void _sapp_win32_create_window(void) { NULL, /* hMenu */ GetModuleHandle(NULL), /* hInstance */ NULL); /* lParam */ - ShowWindow(_sapp_win32_hwnd, SW_SHOW); - _sapp_win32_in_create_window = false; - _sapp_win32_dc = GetDC(_sapp_win32_hwnd); - SOKOL_ASSERT(_sapp_win32_dc); + ShowWindow(_sapp.win32.hwnd, SW_SHOW); + _sapp.win32.in_create_window = false; + _sapp.win32.dc = GetDC(_sapp.win32.hwnd); + SOKOL_ASSERT(_sapp.win32.dc); _sapp_win32_update_dimensions(); } _SOKOL_PRIVATE void _sapp_win32_destroy_window(void) { - DestroyWindow(_sapp_win32_hwnd); _sapp_win32_hwnd = 0; + DestroyWindow(_sapp.win32.hwnd); _sapp.win32.hwnd = 0; UnregisterClassW(L"SOKOLAPP", GetModuleHandleW(NULL)); } _SOKOL_PRIVATE void _sapp_win32_init_dpi(void) { - SOKOL_ASSERT(0 == _sapp_win32_setprocessdpiaware); - SOKOL_ASSERT(0 == _sapp_win32_setprocessdpiawareness); - SOKOL_ASSERT(0 == _sapp_win32_getdpiformonitor); + + typedef BOOL(WINAPI * SETPROCESSDPIAWARE_T)(void); + typedef HRESULT(WINAPI * SETPROCESSDPIAWARENESS_T)(PROCESS_DPI_AWARENESS); + typedef HRESULT(WINAPI * GETDPIFORMONITOR_T)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*); + + SETPROCESSDPIAWARE_T fn_setprocessdpiaware = 0; + SETPROCESSDPIAWARENESS_T fn_setprocessdpiawareness = 0; + GETDPIFORMONITOR_T fn_getdpiformonitor = 0; HINSTANCE user32 = LoadLibraryA("user32.dll"); if (user32) { - _sapp_win32_setprocessdpiaware = (SETPROCESSDPIAWARE_T) GetProcAddress(user32, "SetProcessDPIAware"); + fn_setprocessdpiaware = (SETPROCESSDPIAWARE_T) GetProcAddress(user32, "SetProcessDPIAware"); } HINSTANCE shcore = LoadLibraryA("shcore.dll"); if (shcore) { - _sapp_win32_setprocessdpiawareness = (SETPROCESSDPIAWARENESS_T) GetProcAddress(shcore, "SetProcessDpiAwareness"); - _sapp_win32_getdpiformonitor = (GETDPIFORMONITOR_T) GetProcAddress(shcore, "GetDpiForMonitor"); + fn_setprocessdpiawareness = (SETPROCESSDPIAWARENESS_T) GetProcAddress(shcore, "SetProcessDpiAwareness"); + fn_getdpiformonitor = (GETDPIFORMONITOR_T) GetProcAddress(shcore, "GetDpiForMonitor"); } - if (_sapp_win32_setprocessdpiawareness) { + if (fn_setprocessdpiawareness) { /* if the app didn't request HighDPI rendering, let Windows do the upscaling */ PROCESS_DPI_AWARENESS process_dpi_awareness = PROCESS_SYSTEM_DPI_AWARE; - _sapp_win32_dpi_aware = true; + _sapp.win32.dpi.aware = true; if (!_sapp.desc.high_dpi) { process_dpi_awareness = PROCESS_DPI_UNAWARE; - _sapp_win32_dpi_aware = false; + _sapp.win32.dpi.aware = false; } - _sapp_win32_setprocessdpiawareness(process_dpi_awareness); + fn_setprocessdpiawareness(process_dpi_awareness); } - else if (_sapp_win32_setprocessdpiaware) { - _sapp_win32_setprocessdpiaware(); - _sapp_win32_dpi_aware = true; + else if (fn_setprocessdpiaware) { + fn_setprocessdpiaware(); + _sapp.win32.dpi.aware = true; } /* get dpi scale factor for main monitor */ - if (_sapp_win32_getdpiformonitor && _sapp_win32_dpi_aware) { + if (fn_getdpiformonitor && _sapp.win32.dpi.aware) { POINT pt = { 1, 1 }; HMONITOR hm = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); UINT dpix, dpiy; - HRESULT hr = _sapp_win32_getdpiformonitor(hm, MDT_EFFECTIVE_DPI, &dpix, &dpiy); + HRESULT hr = fn_getdpiformonitor(hm, MDT_EFFECTIVE_DPI, &dpix, &dpiy); _SOKOL_UNUSED(hr); SOKOL_ASSERT(SUCCEEDED(hr)); /* clamp window scale to an integer factor */ - _sapp_win32_window_scale = (float)dpix / 96.0f; + _sapp.win32.dpi.window_scale = (float)dpix / 96.0f; } else { - _sapp_win32_window_scale = 1.0f; + _sapp.win32.dpi.window_scale = 1.0f; } if (_sapp.desc.high_dpi) { - _sapp_win32_content_scale = _sapp_win32_window_scale; - _sapp_win32_mouse_scale = 1.0f; + _sapp.win32.dpi.content_scale = _sapp.win32.dpi.window_scale; + _sapp.win32.dpi.mouse_scale = 1.0f; } else { - _sapp_win32_content_scale = 1.0f; - _sapp_win32_mouse_scale = 1.0f / _sapp_win32_window_scale; + _sapp.win32.dpi.content_scale = 1.0f; + _sapp.win32.dpi.mouse_scale = 1.0f / _sapp.win32.dpi.window_scale; } - _sapp.dpi_scale = _sapp_win32_content_scale; + _sapp.dpi_scale = _sapp.win32.dpi.content_scale; if (user32) { FreeLibrary(user32); } @@ -4566,11 +5361,11 @@ _SOKOL_PRIVATE void _sapp_win32_init_dpi(void) { _SOKOL_PRIVATE bool _sapp_win32_set_clipboard_string(const char* str) { SOKOL_ASSERT(str); - SOKOL_ASSERT(_sapp_win32_hwnd); - SOKOL_ASSERT(_sapp.clipboard_enabled && (_sapp.clipboard_size > 0)); + SOKOL_ASSERT(_sapp.win32.hwnd); + SOKOL_ASSERT(_sapp.clipboard.enabled && (_sapp.clipboard.buf_size > 0)); wchar_t* wchar_buf = 0; - const int wchar_buf_size = _sapp.clipboard_size * sizeof(wchar_t); + const int wchar_buf_size = _sapp.clipboard.buf_size * sizeof(wchar_t); HANDLE object = GlobalAlloc(GMEM_MOVEABLE, wchar_buf_size); if (!object) { goto error; @@ -4584,7 +5379,7 @@ _SOKOL_PRIVATE bool _sapp_win32_set_clipboard_string(const char* str) { } GlobalUnlock(wchar_buf); wchar_buf = 0; - if (!OpenClipboard(_sapp_win32_hwnd)) { + if (!OpenClipboard(_sapp.win32.hwnd)) { goto error; } EmptyClipboard(); @@ -4603,31 +5398,31 @@ error: } _SOKOL_PRIVATE const char* _sapp_win32_get_clipboard_string(void) { - SOKOL_ASSERT(_sapp.clipboard_enabled && _sapp.clipboard); - SOKOL_ASSERT(_sapp_win32_hwnd); - if (!OpenClipboard(_sapp_win32_hwnd)) { + SOKOL_ASSERT(_sapp.clipboard.enabled && _sapp.clipboard.buffer); + SOKOL_ASSERT(_sapp.win32.hwnd); + if (!OpenClipboard(_sapp.win32.hwnd)) { /* silently ignore any errors and just return the current content of the local clipboard buffer */ - return _sapp.clipboard; + return _sapp.clipboard.buffer; } HANDLE object = GetClipboardData(CF_UNICODETEXT); if (!object) { CloseClipboard(); - return _sapp.clipboard; + return _sapp.clipboard.buffer; } const wchar_t* wchar_buf = (const wchar_t*) GlobalLock(object); if (!wchar_buf) { CloseClipboard(); - return _sapp.clipboard; + return _sapp.clipboard.buffer; } - _sapp_win32_wide_to_utf8(wchar_buf, _sapp.clipboard, _sapp.clipboard_size); + _sapp_win32_wide_to_utf8(wchar_buf, _sapp.clipboard.buffer, _sapp.clipboard.buf_size); GlobalUnlock(object); CloseClipboard(); - return _sapp.clipboard; + return _sapp.clipboard.buffer; } -_SOKOL_PRIVATE void _sapp_run(const sapp_desc* desc) { +_SOKOL_PRIVATE void _sapp_win32_run(const sapp_desc* desc) { _sapp_init_state(desc); _sapp_win32_init_keytable(); _sapp_win32_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide)); @@ -4662,8 +5457,8 @@ _SOKOL_PRIVATE void _sapp_run(const sapp_desc* desc) { } _sapp_frame(); #if defined(SOKOL_D3D11) - IDXGISwapChain_Present(_sapp_dxgi_swap_chain, _sapp.swap_interval, 0); - if (IsIconic(_sapp_win32_hwnd)) { + IDXGISwapChain_Present(_sapp.d3d11.swap_chain, _sapp.swap_interval, 0); + if (IsIconic(_sapp.win32.hwnd)) { Sleep(16 * _sapp.swap_interval); } #endif @@ -4678,7 +5473,7 @@ _SOKOL_PRIVATE void _sapp_run(const sapp_desc* desc) { _sapp_win32_app_event(SAPP_EVENTTYPE_RESIZED); } if (_sapp.quit_requested) { - PostMessage(_sapp_win32_hwnd, WM_CLOSE, 0, 0); + PostMessage(_sapp.win32.hwnd, WM_CLOSE, 0, 0); } } _sapp_call_cleanup(); @@ -4694,7 +5489,7 @@ _SOKOL_PRIVATE void _sapp_run(const sapp_desc* desc) { _sapp_discard_state(); } -static char** _sapp_win32_command_line_to_utf8_argv(LPWSTR w_command_line, int* o_argc) { +_SOKOL_PRIVATE char** _sapp_win32_command_line_to_utf8_argv(LPWSTR w_command_line, int* o_argc) { int argc = 0; char** argv = 0; char* args; @@ -4727,7 +5522,7 @@ static char** _sapp_win32_command_line_to_utf8_argv(LPWSTR w_command_line, int* #if defined(SOKOL_WIN32_FORCE_MAIN) int main(int argc, char* argv[]) { sapp_desc desc = sokol_main(argc, argv); - _sapp_run(&desc); + _sapp_win32_run(&desc); return 0; } #else @@ -4739,82 +5534,26 @@ int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _ int argc_utf8 = 0; char** argv_utf8 = _sapp_win32_command_line_to_utf8_argv(GetCommandLineW(), &argc_utf8); sapp_desc desc = sokol_main(argc_utf8, argv_utf8); - _sapp_run(&desc); + _sapp_win32_run(&desc); SOKOL_FREE(argv_utf8); return 0; } #endif /* SOKOL_WIN32_FORCE_MAIN */ #endif /* SOKOL_NO_ENTRY */ -#undef _SAPP_SAFE_RELEASE -#endif /* WINDOWS */ -/*== Android ================================================================*/ -#if defined(__ANDROID__) -#include -#include -#include -#include - -#include -#if defined(SOKOL_GLES3) - #include -#else - #ifndef GL_EXT_PROTOTYPES - #define GL_GLEXT_PROTOTYPES - #endif - #include - #include +#ifdef _MSC_VER + #pragma warning(pop) #endif -typedef struct { - pthread_t thread; - pthread_mutex_t mutex; - pthread_cond_t cond; - int read_from_main_fd; - int write_from_main_fd; -} _sapp_android_pt_t; +#endif /* _SAPP_WIN32 */ -typedef struct { - ANativeWindow* window; - AInputQueue* input; -} _sapp_android_resources_t; - -typedef enum { - _SOKOL_ANDROID_MSG_CREATE, - _SOKOL_ANDROID_MSG_RESUME, - _SOKOL_ANDROID_MSG_PAUSE, - _SOKOL_ANDROID_MSG_FOCUS, - _SOKOL_ANDROID_MSG_NO_FOCUS, - _SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW, - _SOKOL_ANDROID_MSG_SET_INPUT_QUEUE, - _SOKOL_ANDROID_MSG_DESTROY, -} _sapp_android_msg_t; - -typedef struct { - ANativeActivity* activity; - _sapp_android_pt_t pt; - _sapp_android_resources_t pending; - _sapp_android_resources_t current; - ALooper* looper; - bool is_thread_started; - bool is_thread_stopping; - bool is_thread_stopped; - bool has_created; - bool has_resumed; - bool has_focus; - EGLConfig config; - EGLDisplay display; - EGLContext context; - EGLSurface surface; -} _sapp_android_state_t; - -static _sapp_android_state_t _sapp_android_state; +/*== Android ================================================================*/ +#if defined(_SAPP_ANDROID) /* android loop thread */ _SOKOL_PRIVATE bool _sapp_android_init_egl(void) { - _sapp_android_state_t* state = &_sapp_android_state; - SOKOL_ASSERT(state->display == EGL_NO_DISPLAY); - SOKOL_ASSERT(state->context == EGL_NO_CONTEXT); + SOKOL_ASSERT(_sapp.android.display == EGL_NO_DISPLAY); + SOKOL_ASSERT(_sapp.android.context == EGL_NO_CONTEXT); EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); if (display == EGL_NO_DISPLAY) { @@ -4875,63 +5614,60 @@ _SOKOL_PRIVATE bool _sapp_android_init_egl(void) { return false; } - state->config = config; - state->display = display; - state->context = context; + _sapp.android.config = config; + _sapp.android.display = display; + _sapp.android.context = context; return true; } _SOKOL_PRIVATE void _sapp_android_cleanup_egl(void) { - _sapp_android_state_t* state = &_sapp_android_state; - if (state->display != EGL_NO_DISPLAY) { - eglMakeCurrent(state->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - if (state->surface != EGL_NO_SURFACE) { + if (_sapp.android.display != EGL_NO_DISPLAY) { + eglMakeCurrent(_sapp.android.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (_sapp.android.surface != EGL_NO_SURFACE) { SOKOL_LOG("Destroying egl surface"); - eglDestroySurface(state->display, state->surface); - state->surface = EGL_NO_SURFACE; + eglDestroySurface(_sapp.android.display, _sapp.android.surface); + _sapp.android.surface = EGL_NO_SURFACE; } - if (state->context != EGL_NO_CONTEXT) { + if (_sapp.android.context != EGL_NO_CONTEXT) { SOKOL_LOG("Destroying egl context"); - eglDestroyContext(state->display, state->context); - state->context = EGL_NO_CONTEXT; + eglDestroyContext(_sapp.android.display, _sapp.android.context); + _sapp.android.context = EGL_NO_CONTEXT; } SOKOL_LOG("Terminating egl display"); - eglTerminate(state->display); - state->display = EGL_NO_DISPLAY; + eglTerminate(_sapp.android.display); + _sapp.android.display = EGL_NO_DISPLAY; } } _SOKOL_PRIVATE bool _sapp_android_init_egl_surface(ANativeWindow* window) { - _sapp_android_state_t* state = &_sapp_android_state; - SOKOL_ASSERT(state->display != EGL_NO_DISPLAY); - SOKOL_ASSERT(state->context != EGL_NO_CONTEXT); - SOKOL_ASSERT(state->surface == EGL_NO_SURFACE); + SOKOL_ASSERT(_sapp.android.display != EGL_NO_DISPLAY); + SOKOL_ASSERT(_sapp.android.context != EGL_NO_CONTEXT); + SOKOL_ASSERT(_sapp.android.surface == EGL_NO_SURFACE); SOKOL_ASSERT(window); /* TODO: set window flags */ /* ANativeActivity_setWindowFlags(activity, AWINDOW_FLAG_KEEP_SCREEN_ON, 0); */ /* create egl surface and make it current */ - EGLSurface surface = eglCreateWindowSurface(state->display, state->config, window, NULL); + EGLSurface surface = eglCreateWindowSurface(_sapp.android.display, _sapp.android.config, window, NULL); if (surface == EGL_NO_SURFACE) { return false; } - if (eglMakeCurrent(state->display, surface, surface, state->context) == EGL_FALSE) { + if (eglMakeCurrent(_sapp.android.display, surface, surface, _sapp.android.context) == EGL_FALSE) { return false; } - state->surface = surface; + _sapp.android.surface = surface; return true; } _SOKOL_PRIVATE void _sapp_android_cleanup_egl_surface(void) { - _sapp_android_state_t* state = &_sapp_android_state; - if (state->display == EGL_NO_DISPLAY) { + if (_sapp.android.display == EGL_NO_DISPLAY) { return; } - eglMakeCurrent(state->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - if (state->surface != EGL_NO_SURFACE) { - eglDestroySurface(state->display, state->surface); - state->surface = EGL_NO_SURFACE; + eglMakeCurrent(_sapp.android.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (_sapp.android.surface != EGL_NO_SURFACE) { + eglDestroySurface(_sapp.android.display, _sapp.android.surface); + _sapp.android.surface = EGL_NO_SURFACE; } } @@ -4944,10 +5680,9 @@ _SOKOL_PRIVATE void _sapp_android_app_event(sapp_event_type type) { } _SOKOL_PRIVATE void _sapp_android_update_dimensions(ANativeWindow* window, bool force_update) { - _sapp_android_state_t* state = &_sapp_android_state; - SOKOL_ASSERT(state->display != EGL_NO_DISPLAY); - SOKOL_ASSERT(state->context != EGL_NO_CONTEXT); - SOKOL_ASSERT(state->surface != EGL_NO_SURFACE); + SOKOL_ASSERT(_sapp.android.display != EGL_NO_DISPLAY); + SOKOL_ASSERT(_sapp.android.context != EGL_NO_CONTEXT); + SOKOL_ASSERT(_sapp.android.surface != EGL_NO_SURFACE); SOKOL_ASSERT(window); const int32_t win_w = ANativeWindow_getWidth(window); @@ -4961,7 +5696,7 @@ _SOKOL_PRIVATE void _sapp_android_update_dimensions(ANativeWindow* window, bool const int32_t buf_w = win_w / 2; const int32_t buf_h = win_h / 2; EGLint format; - EGLBoolean egl_result = eglGetConfigAttrib(state->display, state->config, EGL_NATIVE_VISUAL_ID, &format); + EGLBoolean egl_result = eglGetConfigAttrib(_sapp.android.display, _sapp.android.config, EGL_NATIVE_VISUAL_ID, &format); SOKOL_ASSERT(egl_result == EGL_TRUE); /* NOTE: calling ANativeWindow_setBuffersGeometry() with the same dimensions as the ANativeWindow size results in weird display artefacts, that's @@ -4975,8 +5710,8 @@ _SOKOL_PRIVATE void _sapp_android_update_dimensions(ANativeWindow* window, bool /* query surface size */ EGLint fb_w, fb_h; - EGLBoolean egl_result_w = eglQuerySurface(state->display, state->surface, EGL_WIDTH, &fb_w); - EGLBoolean egl_result_h = eglQuerySurface(state->display, state->surface, EGL_HEIGHT, &fb_h); + EGLBoolean egl_result_w = eglQuerySurface(_sapp.android.display, _sapp.android.surface, EGL_WIDTH, &fb_w); + EGLBoolean egl_result_h = eglQuerySurface(_sapp.android.display, _sapp.android.surface, EGL_HEIGHT, &fb_h); SOKOL_ASSERT(egl_result_w == EGL_TRUE); SOKOL_ASSERT(egl_result_h == EGL_TRUE); const bool fb_changed = (fb_w != _sapp.framebuffer_width) || (fb_h != _sapp.framebuffer_height); @@ -4992,9 +5727,8 @@ _SOKOL_PRIVATE void _sapp_android_update_dimensions(ANativeWindow* window, bool } _SOKOL_PRIVATE void _sapp_android_cleanup(void) { - _sapp_android_state_t* state = &_sapp_android_state; SOKOL_LOG("Cleaning up"); - if (state->surface != EGL_NO_SURFACE) { + if (_sapp.android.surface != EGL_NO_SURFACE) { /* egl context is bound, cleanup gracefully */ if (_sapp.init_called && !_sapp.cleanup_called) { SOKOL_LOG("cleanup_cb()"); @@ -5009,17 +5743,16 @@ _SOKOL_PRIVATE void _sapp_android_shutdown(void) { /* try to cleanup while we still have a surface and can call cleanup_cb() */ _sapp_android_cleanup(); /* request exit */ - ANativeActivity_finish(_sapp_android_state.activity); + ANativeActivity_finish(_sapp.android.activity); } _SOKOL_PRIVATE void _sapp_android_frame(void) { - _sapp_android_state_t* state = &_sapp_android_state; - SOKOL_ASSERT(state->display != EGL_NO_DISPLAY); - SOKOL_ASSERT(state->context != EGL_NO_CONTEXT); - SOKOL_ASSERT(state->surface != EGL_NO_SURFACE); - _sapp_android_update_dimensions(state->current.window, false); + SOKOL_ASSERT(_sapp.android.display != EGL_NO_DISPLAY); + SOKOL_ASSERT(_sapp.android.context != EGL_NO_CONTEXT); + SOKOL_ASSERT(_sapp.android.surface != EGL_NO_SURFACE); + _sapp_android_update_dimensions(_sapp.android.current.window, false); _sapp_frame(); - eglSwapBuffers(state->display, _sapp_android_state.surface); + eglSwapBuffers(_sapp.android.display, _sapp.android.surface); } _SOKOL_PRIVATE bool _sapp_android_touch_event(const AInputEvent* e) { @@ -5101,18 +5834,17 @@ _SOKOL_PRIVATE int _sapp_android_input_cb(int fd, int events, void* data) { SOKOL_LOG("_sapp_android_input_cb() encountered unsupported event"); return 1; } - _sapp_android_state_t* state = &_sapp_android_state;; - SOKOL_ASSERT(state->current.input); + SOKOL_ASSERT(_sapp.android.current.input); AInputEvent* event = NULL; - while (AInputQueue_getEvent(state->current.input, &event) >= 0) { - if (AInputQueue_preDispatchEvent(state->current.input, event) != 0) { + while (AInputQueue_getEvent(_sapp.android.current.input, &event) >= 0) { + if (AInputQueue_preDispatchEvent(_sapp.android.current.input, event) != 0) { continue; } int32_t handled = 0; if (_sapp_android_touch_event(event) || _sapp_android_key_event(event)) { handled = 1; } - AInputQueue_finishEvent(state->current.input, event, handled); + AInputQueue_finishEvent(_sapp.android.current.input, event, handled); } return 1; } @@ -5122,7 +5854,6 @@ _SOKOL_PRIVATE int _sapp_android_main_cb(int fd, int events, void* data) { SOKOL_LOG("_sapp_android_main_cb() encountered unsupported event"); return 1; } - _sapp_android_state_t* state = &_sapp_android_state; _sapp_android_msg_t msg; if (read(fd, &msg, sizeof(msg)) != sizeof(msg)) { @@ -5130,7 +5861,7 @@ _SOKOL_PRIVATE int _sapp_android_main_cb(int fd, int events, void* data) { return 1; } - pthread_mutex_lock(&state->pt.mutex); + pthread_mutex_lock(&_sapp.android.pt.mutex); switch (msg) { case _SOKOL_ANDROID_MSG_CREATE: { @@ -5139,81 +5870,81 @@ _SOKOL_PRIVATE int _sapp_android_main_cb(int fd, int events, void* data) { bool result = _sapp_android_init_egl(); SOKOL_ASSERT(result); _sapp.valid = true; - state->has_created = true; + _sapp.android.has_created = true; } break; case _SOKOL_ANDROID_MSG_RESUME: SOKOL_LOG("MSG_RESUME"); - state->has_resumed = true; + _sapp.android.has_resumed = true; _sapp_android_app_event(SAPP_EVENTTYPE_RESUMED); break; case _SOKOL_ANDROID_MSG_PAUSE: SOKOL_LOG("MSG_PAUSE"); - state->has_resumed = false; + _sapp.android.has_resumed = false; _sapp_android_app_event(SAPP_EVENTTYPE_SUSPENDED); break; case _SOKOL_ANDROID_MSG_FOCUS: SOKOL_LOG("MSG_FOCUS"); - state->has_focus = true; + _sapp.android.has_focus = true; break; case _SOKOL_ANDROID_MSG_NO_FOCUS: SOKOL_LOG("MSG_NO_FOCUS"); - state->has_focus = false; + _sapp.android.has_focus = false; break; case _SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW: SOKOL_LOG("MSG_SET_NATIVE_WINDOW"); - if (state->current.window != state->pending.window) { - if (state->current.window != NULL) { + if (_sapp.android.current.window != _sapp.android.pending.window) { + if (_sapp.android.current.window != NULL) { _sapp_android_cleanup_egl_surface(); } - if (state->pending.window != NULL) { + if (_sapp.android.pending.window != NULL) { SOKOL_LOG("Creating egl surface ..."); - if (_sapp_android_init_egl_surface(state->pending.window)) { + if (_sapp_android_init_egl_surface(_sapp.android.pending.window)) { SOKOL_LOG("... ok!"); - _sapp_android_update_dimensions(state->pending.window, true); + _sapp_android_update_dimensions(_sapp.android.pending.window, true); } else { SOKOL_LOG("... failed!"); _sapp_android_shutdown(); } } } - state->current.window = state->pending.window; + _sapp.android.current.window = _sapp.android.pending.window; break; case _SOKOL_ANDROID_MSG_SET_INPUT_QUEUE: SOKOL_LOG("MSG_SET_INPUT_QUEUE"); - if (state->current.input != state->pending.input) { - if (state->current.input != NULL) { - AInputQueue_detachLooper(state->current.input); + if (_sapp.android.current.input != _sapp.android.pending.input) { + if (_sapp.android.current.input != NULL) { + AInputQueue_detachLooper(_sapp.android.current.input); } - if (state->pending.input != NULL) { + if (_sapp.android.pending.input != NULL) { AInputQueue_attachLooper( - state->pending.input, - state->looper, + _sapp.android.pending.input, + _sapp.android.looper, ALOOPER_POLL_CALLBACK, _sapp_android_input_cb, NULL); /* data */ } } - state->current.input = state->pending.input; + _sapp.android.current.input = _sapp.android.pending.input; break; case _SOKOL_ANDROID_MSG_DESTROY: SOKOL_LOG("MSG_DESTROY"); _sapp_android_cleanup(); _sapp.valid = false; - state->is_thread_stopping = true; + _sapp.android.is_thread_stopping = true; break; default: SOKOL_LOG("Unknown msg type received"); break; } - pthread_cond_broadcast(&state->pt.cond); /* signal "received" */ - pthread_mutex_unlock(&state->pt.mutex); + pthread_cond_broadcast(&_sapp.android.pt.cond); /* signal "received" */ + pthread_mutex_unlock(&_sapp.android.pt.mutex); return 1; } _SOKOL_PRIVATE bool _sapp_android_should_update(void) { - bool is_in_front = _sapp_android_state.has_resumed && _sapp_android_state.has_focus; - bool has_surface = _sapp_android_state.surface != EGL_NO_SURFACE; + bool is_in_front = _sapp.android.has_resumed && _sapp.android.has_focus; + bool has_surface = _sapp.android.surface != EGL_NO_SURFACE; return is_in_front && has_surface; } @@ -5222,33 +5953,33 @@ _SOKOL_PRIVATE void _sapp_android_show_keyboard(bool shown) { /* This seems to be broken in the NDK, but there is (a very cumbersome) workaround... */ if (shown) { SOKOL_LOG("Showing keyboard"); - ANativeActivity_showSoftInput(_sapp_android_state.activity, ANATIVEACTIVITY_SHOW_SOFT_INPUT_FORCED); + ANativeActivity_showSoftInput(_sapp.android.activity, ANATIVEACTIVITY_SHOW_SOFT_INPUT_FORCED); } else { SOKOL_LOG("Hiding keyboard"); - ANativeActivity_hideSoftInput(_sapp_android_state.activity, ANATIVEACTIVITY_HIDE_SOFT_INPUT_NOT_ALWAYS); + ANativeActivity_hideSoftInput(_sapp.android.activity, ANATIVEACTIVITY_HIDE_SOFT_INPUT_NOT_ALWAYS); } } -_SOKOL_PRIVATE void* _sapp_android_loop(void* obj) { +_SOKOL_PRIVATE void* _sapp_android_loop(void* arg) { + _SOKOL_UNUSED(arg); SOKOL_LOG("Loop thread started"); - _sapp_android_state_t* state = (_sapp_android_state_t*)obj; - state->looper = ALooper_prepare(0 /* or ALOOPER_PREPARE_ALLOW_NON_CALLBACKS*/); - ALooper_addFd(state->looper, - state->pt.read_from_main_fd, + _sapp.android.looper = ALooper_prepare(0 /* or ALOOPER_PREPARE_ALLOW_NON_CALLBACKS*/); + ALooper_addFd(_sapp.android.looper, + _sapp.android.pt.read_from_main_fd, ALOOPER_POLL_CALLBACK, ALOOPER_EVENT_INPUT, _sapp_android_main_cb, NULL); /* data */ /* signal start to main thread */ - pthread_mutex_lock(&state->pt.mutex); - state->is_thread_started = true; - pthread_cond_broadcast(&state->pt.cond); - pthread_mutex_unlock(&state->pt.mutex); + pthread_mutex_lock(&_sapp.android.pt.mutex); + _sapp.android.is_thread_started = true; + pthread_cond_broadcast(&_sapp.android.pt.cond); + pthread_mutex_unlock(&_sapp.android.pt.mutex); /* main loop */ - while (!state->is_thread_stopping) { + while (!_sapp.android.is_thread_stopping) { /* sokol frame */ if (_sapp_android_should_update()) { _sapp_android_frame(); @@ -5256,33 +5987,33 @@ _SOKOL_PRIVATE void* _sapp_android_loop(void* obj) { /* process all events (or stop early if app is requested to quit) */ bool process_events = true; - while (process_events && !state->is_thread_stopping) { - bool block_until_event = !state->is_thread_stopping && !_sapp_android_should_update(); + while (process_events && !_sapp.android.is_thread_stopping) { + bool block_until_event = !_sapp.android.is_thread_stopping && !_sapp_android_should_update(); process_events = ALooper_pollOnce(block_until_event ? -1 : 0, NULL, NULL, NULL) == ALOOPER_POLL_CALLBACK; } } /* cleanup thread */ - if (state->current.input != NULL) { - AInputQueue_detachLooper(state->current.input); + if (_sapp.android.current.input != NULL) { + AInputQueue_detachLooper(_sapp.android.current.input); } /* the following causes heap corruption on exit, why?? - ALooper_removeFd(state->looper, state->pt.read_from_main_fd); - ALooper_release(state->looper);*/ + ALooper_removeFd(_sapp.android.looper, _sapp.android.pt.read_from_main_fd); + ALooper_release(_sapp.android.looper);*/ /* signal "destroyed" */ - pthread_mutex_lock(&state->pt.mutex); - state->is_thread_stopped = true; - pthread_cond_broadcast(&state->pt.cond); - pthread_mutex_unlock(&state->pt.mutex); + pthread_mutex_lock(&_sapp.android.pt.mutex); + _sapp.android.is_thread_stopped = true; + pthread_cond_broadcast(&_sapp.android.pt.cond); + pthread_mutex_unlock(&_sapp.android.pt.mutex); SOKOL_LOG("Loop thread done"); return NULL; } /* android main/ui thread */ -_SOKOL_PRIVATE void _sapp_android_msg(_sapp_android_state_t* state, _sapp_android_msg_t msg) { - if (write(state->pt.write_from_main_fd, &msg, sizeof(msg)) != sizeof(msg)) { +_SOKOL_PRIVATE void _sapp_android_msg(_sapp_android_msg_t msg) { + if (write(_sapp.android.pt.write_from_main_fd, &msg, sizeof(msg)) != sizeof(msg)) { SOKOL_LOG("Could not write to write_from_main_fd"); } } @@ -5293,7 +6024,7 @@ _SOKOL_PRIVATE void _sapp_android_on_start(ANativeActivity* activity) { _SOKOL_PRIVATE void _sapp_android_on_resume(ANativeActivity* activity) { SOKOL_LOG("NativeActivity onResume()"); - _sapp_android_msg(&_sapp_android_state, _SOKOL_ANDROID_MSG_RESUME); + _sapp_android_msg(_SOKOL_ANDROID_MSG_RESUME); } _SOKOL_PRIVATE void* _sapp_android_on_save_instance_state(ANativeActivity* activity, size_t* out_size) { @@ -5305,59 +6036,59 @@ _SOKOL_PRIVATE void* _sapp_android_on_save_instance_state(ANativeActivity* activ _SOKOL_PRIVATE void _sapp_android_on_window_focus_changed(ANativeActivity* activity, int has_focus) { SOKOL_LOG("NativeActivity onWindowFocusChanged()"); if (has_focus) { - _sapp_android_msg(&_sapp_android_state, _SOKOL_ANDROID_MSG_FOCUS); + _sapp_android_msg(_SOKOL_ANDROID_MSG_FOCUS); } else { - _sapp_android_msg(&_sapp_android_state, _SOKOL_ANDROID_MSG_NO_FOCUS); + _sapp_android_msg(_SOKOL_ANDROID_MSG_NO_FOCUS); } } _SOKOL_PRIVATE void _sapp_android_on_pause(ANativeActivity* activity) { SOKOL_LOG("NativeActivity onPause()"); - _sapp_android_msg(&_sapp_android_state, _SOKOL_ANDROID_MSG_PAUSE); + _sapp_android_msg(_SOKOL_ANDROID_MSG_PAUSE); } _SOKOL_PRIVATE void _sapp_android_on_stop(ANativeActivity* activity) { SOKOL_LOG("NativeActivity onStop()"); } -_SOKOL_PRIVATE void _sapp_android_msg_set_native_window(_sapp_android_state_t* state, ANativeWindow* window) { - pthread_mutex_lock(&state->pt.mutex); - state->pending.window = window; - _sapp_android_msg(state, _SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW); - while (state->current.window != window) { - pthread_cond_wait(&state->pt.cond, &state->pt.mutex); +_SOKOL_PRIVATE void _sapp_android_msg_set_native_window(ANativeWindow* window) { + pthread_mutex_lock(&_sapp.android.pt.mutex); + _sapp.android.pending.window = window; + _sapp_android_msg(_SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW); + while (_sapp.android.current.window != window) { + pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex); } - pthread_mutex_unlock(&state->pt.mutex); + pthread_mutex_unlock(&_sapp.android.pt.mutex); } _SOKOL_PRIVATE void _sapp_android_on_native_window_created(ANativeActivity* activity, ANativeWindow* window) { SOKOL_LOG("NativeActivity onNativeWindowCreated()"); - _sapp_android_msg_set_native_window(&_sapp_android_state, window); + _sapp_android_msg_set_native_window(window); } _SOKOL_PRIVATE void _sapp_android_on_native_window_destroyed(ANativeActivity* activity, ANativeWindow* window) { SOKOL_LOG("NativeActivity onNativeWindowDestroyed()"); - _sapp_android_msg_set_native_window(&_sapp_android_state, NULL); + _sapp_android_msg_set_native_window(NULL); } -_SOKOL_PRIVATE void _sapp_android_msg_set_input_queue(_sapp_android_state_t* state, AInputQueue* input) { - pthread_mutex_lock(&state->pt.mutex); - state->pending.input = input; - _sapp_android_msg(state, _SOKOL_ANDROID_MSG_SET_INPUT_QUEUE); - while (state->current.input != input) { - pthread_cond_wait(&state->pt.cond, &state->pt.mutex); +_SOKOL_PRIVATE void _sapp_android_msg_set_input_queue(AInputQueue* input) { + pthread_mutex_lock(&_sapp.android.pt.mutex); + _sapp.android.pending.input = input; + _sapp_android_msg(_SOKOL_ANDROID_MSG_SET_INPUT_QUEUE); + while (_sapp.android.current.input != input) { + pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex); } - pthread_mutex_unlock(&state->pt.mutex); + pthread_mutex_unlock(&_sapp.android.pt.mutex); } _SOKOL_PRIVATE void _sapp_android_on_input_queue_created(ANativeActivity* activity, AInputQueue* queue) { SOKOL_LOG("NativeActivity onInputQueueCreated()"); - _sapp_android_msg_set_input_queue(&_sapp_android_state, queue); + _sapp_android_msg_set_input_queue(queue); } _SOKOL_PRIVATE void _sapp_android_on_input_queue_destroyed(ANativeActivity* activity, AInputQueue* queue) { SOKOL_LOG("NativeActivity onInputQueueDestroyed()"); - _sapp_android_msg_set_input_queue(&_sapp_android_state, NULL); + _sapp_android_msg_set_input_queue(NULL); } _SOKOL_PRIVATE void _sapp_android_on_config_changed(ANativeActivity* activity) { @@ -5379,22 +6110,21 @@ _SOKOL_PRIVATE void _sapp_android_on_destroy(ANativeActivity* activity) { * _sapp_android_on_stop(), the crash disappears. Is this a bug in NativeActivity? */ SOKOL_LOG("NativeActivity onDestroy()"); - _sapp_android_state_t* state = &_sapp_android_state; /* send destroy msg */ - pthread_mutex_lock(&state->pt.mutex); - _sapp_android_msg(state, _SOKOL_ANDROID_MSG_DESTROY); - while (!_sapp_android_state.is_thread_stopped) { - pthread_cond_wait(&state->pt.cond, &state->pt.mutex); + pthread_mutex_lock(&_sapp.android.pt.mutex); + _sapp_android_msg(_SOKOL_ANDROID_MSG_DESTROY); + while (!_sapp.android.is_thread_stopped) { + pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex); } - pthread_mutex_unlock(&state->pt.mutex); + pthread_mutex_unlock(&_sapp.android.pt.mutex); /* clean up main thread */ - pthread_cond_destroy(&state->pt.cond); - pthread_mutex_destroy(&state->pt.mutex); + pthread_cond_destroy(&_sapp.android.pt.cond); + pthread_mutex_destroy(&_sapp.android.pt.mutex); - close(state->pt.read_from_main_fd); - close(state->pt.write_from_main_fd); + close(_sapp.android.pt.read_from_main_fd); + close(_sapp.android.pt.write_from_main_fd); SOKOL_LOG("NativeActivity done"); @@ -5410,45 +6140,41 @@ void ANativeActivity_onCreate(ANativeActivity* activity, void* saved_state, size _sapp_init_state(&desc); /* start loop thread */ - _sapp_android_state = (_sapp_android_state_t){0}; - _sapp_android_state_t* state = &_sapp_android_state; - - state->activity = activity; + _sapp.android.activity = activity; int pipe_fd[2]; if (pipe(pipe_fd) != 0) { SOKOL_LOG("Could not create thread pipe"); return; } - state->pt.read_from_main_fd = pipe_fd[0]; - state->pt.write_from_main_fd = pipe_fd[1]; + _sapp.android.pt.read_from_main_fd = pipe_fd[0]; + _sapp.android.pt.write_from_main_fd = pipe_fd[1]; - pthread_mutex_init(&state->pt.mutex, NULL); - pthread_cond_init(&state->pt.cond, NULL); + pthread_mutex_init(&_sapp.android.pt.mutex, NULL); + pthread_cond_init(&_sapp.android.pt.cond, NULL); pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - pthread_create(&state->pt.thread, &attr, _sapp_android_loop, state); + pthread_create(&_sapp.android.pt.thread, &attr, _sapp_android_loop, 0); pthread_attr_destroy(&attr); /* wait until main loop has started */ - pthread_mutex_lock(&state->pt.mutex); - while (!state->is_thread_started) { - pthread_cond_wait(&state->pt.cond, &state->pt.mutex); + pthread_mutex_lock(&_sapp.android.pt.mutex); + while (!_sapp.android.is_thread_started) { + pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex); } - pthread_mutex_unlock(&state->pt.mutex); + pthread_mutex_unlock(&_sapp.android.pt.mutex); /* send create msg */ - pthread_mutex_lock(&state->pt.mutex); - _sapp_android_msg(state, _SOKOL_ANDROID_MSG_CREATE); - while (!state->has_created) { - pthread_cond_wait(&state->pt.cond, &state->pt.mutex); + pthread_mutex_lock(&_sapp.android.pt.mutex); + _sapp_android_msg(_SOKOL_ANDROID_MSG_CREATE); + while (!_sapp.android.has_created) { + pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex); } - pthread_mutex_unlock(&state->pt.mutex); + pthread_mutex_unlock(&_sapp.android.pt.mutex); /* register for callbacks */ - activity->instance = state; activity->callbacks->onStart = _sapp_android_on_start; activity->callbacks->onResume = _sapp_android_on_resume; activity->callbacks->onSaveInstanceState = _sapp_android_on_save_instance_state; @@ -5471,134 +6197,10 @@ void ANativeActivity_onCreate(ANativeActivity* activity, void* saved_state, size /* NOT A BUG: do NOT call sapp_discard_state() */ } -#endif /* Android */ +#endif /* _SAPP_ANDROID */ /*== LINUX ==================================================================*/ -#if (defined(__linux__) || defined(__unix__)) && !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) -#define GL_GLEXT_PROTOTYPES -#include -#include -#include -#include -#include -#include /* CARD32 */ -#include -#include /* dlopen, dlsym, dlclose */ -#include /* LONG_MAX */ - -#define GLX_VENDOR 1 -#define GLX_RGBA_BIT 0x00000001 -#define GLX_WINDOW_BIT 0x00000001 -#define GLX_DRAWABLE_TYPE 0x8010 -#define GLX_RENDER_TYPE 0x8011 -#define GLX_RGBA_TYPE 0x8014 -#define GLX_DOUBLEBUFFER 5 -#define GLX_STEREO 6 -#define GLX_AUX_BUFFERS 7 -#define GLX_RED_SIZE 8 -#define GLX_GREEN_SIZE 9 -#define GLX_BLUE_SIZE 10 -#define GLX_ALPHA_SIZE 11 -#define GLX_DEPTH_SIZE 12 -#define GLX_STENCIL_SIZE 13 -#define GLX_ACCUM_RED_SIZE 14 -#define GLX_ACCUM_GREEN_SIZE 15 -#define GLX_ACCUM_BLUE_SIZE 16 -#define GLX_ACCUM_ALPHA_SIZE 17 -#define GLX_SAMPLES 0x186a1 -#define GLX_VISUAL_ID 0x800b - -#define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20b2 -#define GLX_CONTEXT_DEBUG_BIT_ARB 0x00000001 -#define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 -#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 -#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126 -#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 -#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 -#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 -#define GLX_CONTEXT_FLAGS_ARB 0x2094 -#define GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004 -#define GLX_LOSE_CONTEXT_ON_RESET_ARB 0x8252 -#define GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 -#define GLX_NO_RESET_NOTIFICATION_ARB 0x8261 -#define GLX_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 -#define GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0 -#define GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098 - -typedef XID GLXWindow; -typedef XID GLXDrawable; -typedef struct __GLXFBConfig* GLXFBConfig; -typedef struct __GLXcontext* GLXContext; -typedef void (*__GLXextproc)(void); - -typedef int (*PFNGLXGETFBCONFIGATTRIBPROC)(Display*,GLXFBConfig,int,int*); -typedef const char* (*PFNGLXGETCLIENTSTRINGPROC)(Display*,int); -typedef Bool (*PFNGLXQUERYEXTENSIONPROC)(Display*,int*,int*); -typedef Bool (*PFNGLXQUERYVERSIONPROC)(Display*,int*,int*); -typedef void (*PFNGLXDESTROYCONTEXTPROC)(Display*,GLXContext); -typedef Bool (*PFNGLXMAKECURRENTPROC)(Display*,GLXDrawable,GLXContext); -typedef void (*PFNGLXSWAPBUFFERSPROC)(Display*,GLXDrawable); -typedef const char* (*PFNGLXQUERYEXTENSIONSSTRINGPROC)(Display*,int); -typedef GLXFBConfig* (*PFNGLXGETFBCONFIGSPROC)(Display*,int,int*); -typedef GLXContext (*PFNGLXCREATENEWCONTEXTPROC)(Display*,GLXFBConfig,int,GLXContext,Bool); -typedef __GLXextproc (* PFNGLXGETPROCADDRESSPROC)(const GLubyte *procName); -typedef void (*PFNGLXSWAPINTERVALEXTPROC)(Display*,GLXDrawable,int); -typedef XVisualInfo* (*PFNGLXGETVISUALFROMFBCONFIGPROC)(Display*,GLXFBConfig); -typedef GLXWindow (*PFNGLXCREATEWINDOWPROC)(Display*,GLXFBConfig,Window,const int*); -typedef void (*PFNGLXDESTROYWINDOWPROC)(Display*,GLXWindow); - -typedef int (*PFNGLXSWAPINTERVALMESAPROC)(int); -typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display*,GLXFBConfig,GLXContext,Bool,const int*); - -static Display* _sapp_x11_display; -static int _sapp_x11_screen; -static Window _sapp_x11_root; -static Colormap _sapp_x11_colormap; -static Window _sapp_x11_window; -static float _sapp_x11_dpi; -static int _sapp_x11_window_state; -static unsigned char _sapp_x11_error_code; -static void* _sapp_glx_libgl; -static int _sapp_glx_major; -static int _sapp_glx_minor; -static int _sapp_glx_eventbase; -static int _sapp_glx_errorbase; -static GLXContext _sapp_glx_ctx; -static GLXWindow _sapp_glx_window; -static Atom _sapp_x11_UTF8_STRING; -static Atom _sapp_x11_WM_PROTOCOLS; -static Atom _sapp_x11_WM_DELETE_WINDOW; -static Atom _sapp_x11_WM_STATE; -static Atom _sapp_x11_NET_WM_NAME; -static Atom _sapp_x11_NET_WM_ICON_NAME; -// GLX 1.3 functions -static PFNGLXGETFBCONFIGSPROC _sapp_glx_GetFBConfigs; -static PFNGLXGETFBCONFIGATTRIBPROC _sapp_glx_GetFBConfigAttrib; -static PFNGLXGETCLIENTSTRINGPROC _sapp_glx_GetClientString; -static PFNGLXQUERYEXTENSIONPROC _sapp_glx_QueryExtension; -static PFNGLXQUERYVERSIONPROC _sapp_glx_QueryVersion; -static PFNGLXDESTROYCONTEXTPROC _sapp_glx_DestroyContext; -static PFNGLXMAKECURRENTPROC _sapp_glx_MakeCurrent; -static PFNGLXSWAPBUFFERSPROC _sapp_glx_SwapBuffers; -static PFNGLXQUERYEXTENSIONSSTRINGPROC _sapp_glx_QueryExtensionsString; -static PFNGLXCREATENEWCONTEXTPROC _sapp_glx_CreateNewContext; -static PFNGLXGETVISUALFROMFBCONFIGPROC _sapp_glx_GetVisualFromFBConfig; -static PFNGLXCREATEWINDOWPROC _sapp_glx_CreateWindow; -static PFNGLXDESTROYWINDOWPROC _sapp_glx_DestroyWindow; - -// GLX 1.4 and extension functions -static PFNGLXGETPROCADDRESSPROC _sapp_glx_GetProcAddress; -static PFNGLXGETPROCADDRESSPROC _sapp_glx_GetProcAddressARB; -static PFNGLXSWAPINTERVALEXTPROC _sapp_glx_SwapIntervalEXT; -static PFNGLXSWAPINTERVALMESAPROC _sapp_glx_SwapIntervalMESA; -static PFNGLXCREATECONTEXTATTRIBSARBPROC _sapp_glx_CreateContextAttribsARB; -static bool _sapp_glx_EXT_swap_control; -static bool _sapp_glx_MESA_swap_control; -static bool _sapp_glx_ARB_multisample; -static bool _sapp_glx_ARB_framebuffer_sRGB; -static bool _sapp_glx_EXT_framebuffer_sRGB; -static bool _sapp_glx_ARB_create_context; -static bool _sapp_glx_ARB_create_context_profile; +#if defined(_SAPP_LINUX) /* see GLFW's xkb_unicode.c */ static const struct _sapp_x11_codepair { @@ -6436,27 +7038,39 @@ static const struct _sapp_x11_codepair { }; _SOKOL_PRIVATE int _sapp_x11_error_handler(Display* display, XErrorEvent* event) { - _sapp_x11_error_code = event->error_code; + _SOKOL_UNUSED(display); + _sapp.x11.error_code = event->error_code; return 0; } _SOKOL_PRIVATE void _sapp_x11_grab_error_handler(void) { - _sapp_x11_error_code = Success; + _sapp.x11.error_code = Success; XSetErrorHandler(_sapp_x11_error_handler); } _SOKOL_PRIVATE void _sapp_x11_release_error_handler(void) { - XSync(_sapp_x11_display, False); + XSync(_sapp.x11.display, False); XSetErrorHandler(NULL); } _SOKOL_PRIVATE void _sapp_x11_init_extensions(void) { - _sapp_x11_UTF8_STRING = XInternAtom(_sapp_x11_display, "UTF8_STRING", False); - _sapp_x11_WM_PROTOCOLS = XInternAtom(_sapp_x11_display, "WM_PROTOCOLS", False); - _sapp_x11_WM_DELETE_WINDOW = XInternAtom(_sapp_x11_display, "WM_DELETE_WINDOW", False); - _sapp_x11_WM_STATE = XInternAtom(_sapp_x11_display, "WM_STATE", False); - _sapp_x11_NET_WM_NAME = XInternAtom(_sapp_x11_display, "_NET_WM_NAME", False); - _sapp_x11_NET_WM_ICON_NAME = XInternAtom(_sapp_x11_display, "_NET_WM_ICON_NAME", False); + _sapp.x11.UTF8_STRING = XInternAtom(_sapp.x11.display, "UTF8_STRING", False); + _sapp.x11.WM_PROTOCOLS = XInternAtom(_sapp.x11.display, "WM_PROTOCOLS", False); + _sapp.x11.WM_DELETE_WINDOW = XInternAtom(_sapp.x11.display, "WM_DELETE_WINDOW", False); + _sapp.x11.WM_STATE = XInternAtom(_sapp.x11.display, "WM_STATE", False); + _sapp.x11.NET_WM_NAME = XInternAtom(_sapp.x11.display, "_NET_WM_NAME", False); + _sapp.x11.NET_WM_ICON_NAME = XInternAtom(_sapp.x11.display, "_NET_WM_ICON_NAME", False); + _sapp.x11.NET_WM_STATE = XInternAtom(_sapp.x11.display, "_NET_WM_STATE", False); + _sapp.x11.NET_WM_STATE_FULLSCREEN = XInternAtom(_sapp.x11.display, "_NET_WM_STATE_FULLSCREEN", False); + + /* check Xi extension for raw mouse input */ + if (XQueryExtension(_sapp.x11.display, "XInputExtension", &_sapp.x11.xi.major_opcode, &_sapp.x11.xi.event_base, &_sapp.x11.xi.error_base)) { + _sapp.x11.xi.major = 2; + _sapp.x11.xi.minor = 0; + if (XIQueryVersion(_sapp.x11.display, &_sapp.x11.xi.major, &_sapp.x11.xi.minor) == Success) { + _sapp.x11.xi.available = true; + } + } } _SOKOL_PRIVATE void _sapp_x11_query_system_dpi(void) { @@ -6464,14 +7078,15 @@ _SOKOL_PRIVATE void _sapp_x11_query_system_dpi(void) { NOTE: Default to the display-wide DPI as we don't currently have a policy for which monitor a window is considered to be on - _sapp_x11_dpi = DisplayWidth(_sapp_x11_display, _sapp_x11_screen) * - 25.4f / DisplayWidthMM(_sapp_x11_display, _sapp_x11_screen); + + _sapp.x11.dpi = DisplayWidth(_sapp.x11.display, _sapp.x11.screen) * + 25.4f / DisplayWidthMM(_sapp.x11.display, _sapp.x11.screen); NOTE: Basing the scale on Xft.dpi where available should provide the most consistent user experience (matches Qt, Gtk, etc), although not always the most accurate one */ - char* rms = XResourceManagerString(_sapp_x11_display); + char* rms = XResourceManagerString(_sapp.x11.display); if (rms) { XrmDatabase db = XrmGetStringDatabase(rms); if (db) { @@ -6479,7 +7094,7 @@ _SOKOL_PRIVATE void _sapp_x11_query_system_dpi(void) { char* type = NULL; if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value)) { if (type && strcmp(type, "String") == 0) { - _sapp_x11_dpi = atof(value.addr); + _sapp.x11.dpi = atof(value.addr); } } XrmDestroyDatabase(db); @@ -6517,96 +7132,89 @@ _SOKOL_PRIVATE bool _sapp_glx_extsupported(const char* ext, const char* extensio _SOKOL_PRIVATE void* _sapp_glx_getprocaddr(const char* procname) { - if (_sapp_glx_GetProcAddress) { - return (void*) _sapp_glx_GetProcAddress((const GLubyte*) procname); + if (_sapp.glx.GetProcAddress) { + return (void*) _sapp.glx.GetProcAddress((const GLubyte*) procname); } - else if (_sapp_glx_GetProcAddressARB) { - return (void*) _sapp_glx_GetProcAddressARB((const GLubyte*) procname); + else if (_sapp.glx.GetProcAddressARB) { + return (void*) _sapp.glx.GetProcAddressARB((const GLubyte*) procname); } else { - return dlsym(_sapp_glx_libgl, procname); + return dlsym(_sapp.glx.libgl, procname); } } _SOKOL_PRIVATE void _sapp_glx_init() { const char* sonames[] = { "libGL.so.1", "libGL.so", 0 }; for (int i = 0; sonames[i]; i++) { - _sapp_glx_libgl = dlopen(sonames[i], RTLD_LAZY|RTLD_GLOBAL); - if (_sapp_glx_libgl) { + _sapp.glx.libgl = dlopen(sonames[i], RTLD_LAZY|RTLD_GLOBAL); + if (_sapp.glx.libgl) { break; } } - if (!_sapp_glx_libgl) { + if (!_sapp.glx.libgl) { _sapp_fail("GLX: failed to load libGL"); } - _sapp_glx_GetFBConfigs = (PFNGLXGETFBCONFIGSPROC) dlsym(_sapp_glx_libgl, "glXGetFBConfigs"); - _sapp_glx_GetFBConfigAttrib = (PFNGLXGETFBCONFIGATTRIBPROC) dlsym(_sapp_glx_libgl, "glXGetFBConfigAttrib"); - _sapp_glx_GetClientString = (PFNGLXGETCLIENTSTRINGPROC) dlsym(_sapp_glx_libgl, "glXGetClientString"); - _sapp_glx_QueryExtension = (PFNGLXQUERYEXTENSIONPROC) dlsym(_sapp_glx_libgl, "glXQueryExtension"); - _sapp_glx_QueryVersion = (PFNGLXQUERYVERSIONPROC) dlsym(_sapp_glx_libgl, "glXQueryVersion"); - _sapp_glx_DestroyContext = (PFNGLXDESTROYCONTEXTPROC) dlsym(_sapp_glx_libgl, "glXDestroyContext"); - _sapp_glx_MakeCurrent = (PFNGLXMAKECURRENTPROC) dlsym(_sapp_glx_libgl, "glXMakeCurrent"); - _sapp_glx_SwapBuffers = (PFNGLXSWAPBUFFERSPROC) dlsym(_sapp_glx_libgl, "glXSwapBuffers"); - _sapp_glx_QueryExtensionsString = (PFNGLXQUERYEXTENSIONSSTRINGPROC) dlsym(_sapp_glx_libgl, "glXQueryExtensionsString"); - _sapp_glx_CreateNewContext = (PFNGLXCREATENEWCONTEXTPROC) dlsym(_sapp_glx_libgl, "glXCreateNewContext"); - _sapp_glx_CreateWindow = (PFNGLXCREATEWINDOWPROC) dlsym(_sapp_glx_libgl, "glXCreateWindow"); - _sapp_glx_DestroyWindow = (PFNGLXDESTROYWINDOWPROC) dlsym(_sapp_glx_libgl, "glXDestroyWindow"); - _sapp_glx_GetProcAddress = (PFNGLXGETPROCADDRESSPROC) dlsym(_sapp_glx_libgl, "glXGetProcAddress"); - _sapp_glx_GetProcAddressARB = (PFNGLXGETPROCADDRESSPROC) dlsym(_sapp_glx_libgl, "glXGetProcAddressARB"); - _sapp_glx_GetVisualFromFBConfig = (PFNGLXGETVISUALFROMFBCONFIGPROC) dlsym(_sapp_glx_libgl, "glXGetVisualFromFBConfig"); - if (!_sapp_glx_GetFBConfigs || - !_sapp_glx_GetFBConfigAttrib || - !_sapp_glx_GetClientString || - !_sapp_glx_QueryExtension || - !_sapp_glx_QueryVersion || - !_sapp_glx_DestroyContext || - !_sapp_glx_MakeCurrent || - !_sapp_glx_SwapBuffers || - !_sapp_glx_QueryExtensionsString || - !_sapp_glx_CreateNewContext || - !_sapp_glx_CreateWindow || - !_sapp_glx_DestroyWindow || - !_sapp_glx_GetProcAddress || - !_sapp_glx_GetProcAddressARB || - !_sapp_glx_GetVisualFromFBConfig) + _sapp.glx.GetFBConfigs = (PFNGLXGETFBCONFIGSPROC) dlsym(_sapp.glx.libgl, "glXGetFBConfigs"); + _sapp.glx.GetFBConfigAttrib = (PFNGLXGETFBCONFIGATTRIBPROC) dlsym(_sapp.glx.libgl, "glXGetFBConfigAttrib"); + _sapp.glx.GetClientString = (PFNGLXGETCLIENTSTRINGPROC) dlsym(_sapp.glx.libgl, "glXGetClientString"); + _sapp.glx.QueryExtension = (PFNGLXQUERYEXTENSIONPROC) dlsym(_sapp.glx.libgl, "glXQueryExtension"); + _sapp.glx.QueryVersion = (PFNGLXQUERYVERSIONPROC) dlsym(_sapp.glx.libgl, "glXQueryVersion"); + _sapp.glx.DestroyContext = (PFNGLXDESTROYCONTEXTPROC) dlsym(_sapp.glx.libgl, "glXDestroyContext"); + _sapp.glx.MakeCurrent = (PFNGLXMAKECURRENTPROC) dlsym(_sapp.glx.libgl, "glXMakeCurrent"); + _sapp.glx.SwapBuffers = (PFNGLXSWAPBUFFERSPROC) dlsym(_sapp.glx.libgl, "glXSwapBuffers"); + _sapp.glx.QueryExtensionsString = (PFNGLXQUERYEXTENSIONSSTRINGPROC) dlsym(_sapp.glx.libgl, "glXQueryExtensionsString"); + _sapp.glx.CreateWindow = (PFNGLXCREATEWINDOWPROC) dlsym(_sapp.glx.libgl, "glXCreateWindow"); + _sapp.glx.DestroyWindow = (PFNGLXDESTROYWINDOWPROC) dlsym(_sapp.glx.libgl, "glXDestroyWindow"); + _sapp.glx.GetProcAddress = (PFNGLXGETPROCADDRESSPROC) dlsym(_sapp.glx.libgl, "glXGetProcAddress"); + _sapp.glx.GetProcAddressARB = (PFNGLXGETPROCADDRESSPROC) dlsym(_sapp.glx.libgl, "glXGetProcAddressARB"); + _sapp.glx.GetVisualFromFBConfig = (PFNGLXGETVISUALFROMFBCONFIGPROC) dlsym(_sapp.glx.libgl, "glXGetVisualFromFBConfig"); + if (!_sapp.glx.GetFBConfigs || + !_sapp.glx.GetFBConfigAttrib || + !_sapp.glx.GetClientString || + !_sapp.glx.QueryExtension || + !_sapp.glx.QueryVersion || + !_sapp.glx.DestroyContext || + !_sapp.glx.MakeCurrent || + !_sapp.glx.SwapBuffers || + !_sapp.glx.QueryExtensionsString || + !_sapp.glx.CreateWindow || + !_sapp.glx.DestroyWindow || + !_sapp.glx.GetProcAddress || + !_sapp.glx.GetProcAddressARB || + !_sapp.glx.GetVisualFromFBConfig) { _sapp_fail("GLX: failed to load required entry points"); } - if (!_sapp_glx_QueryExtension(_sapp_x11_display, - &_sapp_glx_errorbase, - &_sapp_glx_eventbase)) - { + if (!_sapp.glx.QueryExtension(_sapp.x11.display, &_sapp.glx.error_base, &_sapp.glx.event_base)) { _sapp_fail("GLX: GLX extension not found"); } - if (!_sapp_glx_QueryVersion(_sapp_x11_display, &_sapp_glx_major, &_sapp_glx_minor)) { + if (!_sapp.glx.QueryVersion(_sapp.x11.display, &_sapp.glx.major, &_sapp.glx.minor)) { _sapp_fail("GLX: Failed to query GLX version"); } - if (_sapp_glx_major == 1 && _sapp_glx_minor < 3) { + if (_sapp.glx.major == 1 && _sapp.glx.minor < 3) { _sapp_fail("GLX: GLX version 1.3 is required"); } - const char* exts = _sapp_glx_QueryExtensionsString(_sapp_x11_display, _sapp_x11_screen); + const char* exts = _sapp.glx.QueryExtensionsString(_sapp.x11.display, _sapp.x11.screen); if (_sapp_glx_extsupported("GLX_EXT_swap_control", exts)) { - _sapp_glx_SwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC) _sapp_glx_getprocaddr("glXSwapIntervalEXT"); - _sapp_glx_EXT_swap_control = 0 != _sapp_glx_SwapIntervalEXT; + _sapp.glx.SwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC) _sapp_glx_getprocaddr("glXSwapIntervalEXT"); + _sapp.glx.EXT_swap_control = 0 != _sapp.glx.SwapIntervalEXT; } if (_sapp_glx_extsupported("GLX_MESA_swap_control", exts)) { - _sapp_glx_SwapIntervalMESA = (PFNGLXSWAPINTERVALMESAPROC) _sapp_glx_getprocaddr("glXSwapIntervalMESA"); - _sapp_glx_MESA_swap_control = 0 != _sapp_glx_SwapIntervalMESA; + _sapp.glx.SwapIntervalMESA = (PFNGLXSWAPINTERVALMESAPROC) _sapp_glx_getprocaddr("glXSwapIntervalMESA"); + _sapp.glx.MESA_swap_control = 0 != _sapp.glx.SwapIntervalMESA; } - _sapp_glx_ARB_multisample = _sapp_glx_extsupported("GLX_ARB_multisample", exts); - _sapp_glx_ARB_framebuffer_sRGB = _sapp_glx_extsupported("GLX_ARB_framebuffer_sRGB", exts); - _sapp_glx_EXT_framebuffer_sRGB = _sapp_glx_extsupported("GLX_EXT_framebuffer_sRGB", exts); + _sapp.glx.ARB_multisample = _sapp_glx_extsupported("GLX_ARB_multisample", exts); if (_sapp_glx_extsupported("GLX_ARB_create_context", exts)) { - _sapp_glx_CreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC) _sapp_glx_getprocaddr("glXCreateContextAttribsARB"); - _sapp_glx_ARB_create_context = 0 != _sapp_glx_CreateContextAttribsARB; + _sapp.glx.CreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC) _sapp_glx_getprocaddr("glXCreateContextAttribsARB"); + _sapp.glx.ARB_create_context = 0 != _sapp.glx.CreateContextAttribsARB; } - _sapp_glx_ARB_create_context_profile = _sapp_glx_extsupported("GLX_ARB_create_context_profile", exts); + _sapp.glx.ARB_create_context_profile = _sapp_glx_extsupported("GLX_ARB_create_context_profile", exts); } _SOKOL_PRIVATE int _sapp_glx_attrib(GLXFBConfig fbconfig, int attrib) { int value; - _sapp_glx_GetFBConfigAttrib(_sapp_x11_display, fbconfig, attrib, &value); + _sapp.glx.GetFBConfigAttrib(_sapp.x11.display, fbconfig, attrib, &value); return value; } @@ -6621,12 +7229,12 @@ _SOKOL_PRIVATE GLXFBConfig _sapp_glx_choosefbconfig() { /* HACK: This is a (hopefully temporary) workaround for Chromium (VirtualBox GL) not setting the window bit on any GLXFBConfigs */ - vendor = _sapp_glx_GetClientString(_sapp_x11_display, GLX_VENDOR); + vendor = _sapp.glx.GetClientString(_sapp.x11.display, GLX_VENDOR); if (vendor && strcmp(vendor, "Chromium") == 0) { trust_window_bit = false; } - native_configs = _sapp_glx_GetFBConfigs(_sapp_x11_display, _sapp_x11_screen, &native_count); + native_configs = _sapp.glx.GetFBConfigs(_sapp.x11.display, _sapp.x11.screen, &native_count); if (!native_configs || !native_count) { _sapp_fail("GLX: No GLXFBConfigs returned"); } @@ -6657,7 +7265,7 @@ _SOKOL_PRIVATE GLXFBConfig _sapp_glx_choosefbconfig() { if (_sapp_glx_attrib(n, GLX_DOUBLEBUFFER)) { u->doublebuffer = true; } - if (_sapp_glx_ARB_multisample) { + if (_sapp.glx.ARB_multisample) { u->samples = _sapp_glx_attrib(n, GLX_SAMPLES); } u->handle = (uintptr_t) n; @@ -6688,7 +7296,7 @@ _SOKOL_PRIVATE void _sapp_glx_choose_visual(Visual** visual, int* depth) { if (0 == native) { _sapp_fail("GLX: Failed to find a suitable GLXFBConfig"); } - XVisualInfo* result = _sapp_glx_GetVisualFromFBConfig(_sapp_x11_display, native); + XVisualInfo* result = _sapp.glx.GetVisualFromFBConfig(_sapp.x11.display, native); if (!result) { _sapp_fail("GLX: Failed to retrieve Visual for GLXFBConfig"); } @@ -6702,7 +7310,7 @@ _SOKOL_PRIVATE void _sapp_glx_create_context(void) { if (0 == native){ _sapp_fail("GLX: Failed to find a suitable GLXFBConfig (2)"); } - if (!(_sapp_glx_ARB_create_context && _sapp_glx_ARB_create_context_profile)) { + if (!(_sapp.glx.ARB_create_context && _sapp.glx.ARB_create_context_profile)) { _sapp_fail("GLX: ARB_create_context and ARB_create_context_profile required"); } _sapp_x11_grab_error_handler(); @@ -6713,92 +7321,199 @@ _SOKOL_PRIVATE void _sapp_glx_create_context(void) { GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, 0, 0 }; - _sapp_glx_ctx = _sapp_glx_CreateContextAttribsARB(_sapp_x11_display, native, NULL, True, attribs); - if (!_sapp_glx_ctx) { + _sapp.glx.ctx = _sapp.glx.CreateContextAttribsARB(_sapp.x11.display, native, NULL, True, attribs); + if (!_sapp.glx.ctx) { _sapp_fail("GLX: failed to create GL context"); } _sapp_x11_release_error_handler(); - _sapp_glx_window = _sapp_glx_CreateWindow(_sapp_x11_display, native, _sapp_x11_window, NULL); - if (!_sapp_glx_window) { + _sapp.glx.window = _sapp.glx.CreateWindow(_sapp.x11.display, native, _sapp.x11.window, NULL); + if (!_sapp.glx.window) { _sapp_fail("GLX: failed to create window"); } } _SOKOL_PRIVATE void _sapp_glx_destroy_context(void) { - if (_sapp_glx_window) { - _sapp_glx_DestroyWindow(_sapp_x11_display, _sapp_glx_window); - _sapp_glx_window = 0; + if (_sapp.glx.window) { + _sapp.glx.DestroyWindow(_sapp.x11.display, _sapp.glx.window); + _sapp.glx.window = 0; } - if (_sapp_glx_ctx) { - _sapp_glx_DestroyContext(_sapp_x11_display, _sapp_glx_ctx); - _sapp_glx_ctx = 0; + if (_sapp.glx.ctx) { + _sapp.glx.DestroyContext(_sapp.x11.display, _sapp.glx.ctx); + _sapp.glx.ctx = 0; } } _SOKOL_PRIVATE void _sapp_glx_make_current(void) { - _sapp_glx_MakeCurrent(_sapp_x11_display, _sapp_glx_window, _sapp_glx_ctx); + _sapp.glx.MakeCurrent(_sapp.x11.display, _sapp.glx.window, _sapp.glx.ctx); } _SOKOL_PRIVATE void _sapp_glx_swap_buffers(void) { - _sapp_glx_SwapBuffers(_sapp_x11_display, _sapp_glx_window); + _sapp.glx.SwapBuffers(_sapp.x11.display, _sapp.glx.window); } _SOKOL_PRIVATE void _sapp_glx_swapinterval(int interval) { _sapp_glx_make_current(); - if (_sapp_glx_EXT_swap_control) { - _sapp_glx_SwapIntervalEXT(_sapp_x11_display, _sapp_glx_window, interval); + if (_sapp.glx.EXT_swap_control) { + _sapp.glx.SwapIntervalEXT(_sapp.x11.display, _sapp.glx.window, interval); } - else if (_sapp_glx_MESA_swap_control) { - _sapp_glx_SwapIntervalMESA(interval); + else if (_sapp.glx.MESA_swap_control) { + _sapp.glx.SwapIntervalMESA(interval); } } -_SOKOL_PRIVATE void _sapp_x11_update_window_title(void) { - Xutf8SetWMProperties(_sapp_x11_display, - _sapp_x11_window, - _sapp.window_title, _sapp.window_title, - NULL, 0, NULL, NULL, NULL); - XChangeProperty(_sapp_x11_display, _sapp_x11_window, - _sapp_x11_NET_WM_NAME, _sapp_x11_UTF8_STRING, 8, - PropModeReplace, - (unsigned char*)_sapp.window_title, - strlen(_sapp.window_title)); - XChangeProperty(_sapp_x11_display, _sapp_x11_window, - _sapp_x11_NET_WM_ICON_NAME, _sapp_x11_UTF8_STRING, 8, - PropModeReplace, - (unsigned char*)_sapp.window_title, - strlen(_sapp.window_title)); - XFlush(_sapp_x11_display); +_SOKOL_PRIVATE void _sapp_x11_send_event(Atom type, int a, int b, int c, int d, int e) { + XEvent event; + memset(&event, 0, sizeof(event)); + + event.type = ClientMessage; + event.xclient.window = _sapp.x11.window; + event.xclient.format = 32; + event.xclient.message_type = type; + event.xclient.data.l[0] = a; + event.xclient.data.l[1] = b; + event.xclient.data.l[2] = c; + event.xclient.data.l[3] = d; + event.xclient.data.l[4] = e; + + XSendEvent(_sapp.x11.display, _sapp.x11.root, + False, + SubstructureNotifyMask | SubstructureRedirectMask, + &event); } _SOKOL_PRIVATE void _sapp_x11_query_window_size(void) { XWindowAttributes attribs; - XGetWindowAttributes(_sapp_x11_display, _sapp_x11_window, &attribs); + XGetWindowAttributes(_sapp.x11.display, _sapp.x11.window, &attribs); _sapp.window_width = attribs.width; _sapp.window_height = attribs.height; _sapp.framebuffer_width = _sapp.window_width; - _sapp.framebuffer_height = _sapp.framebuffer_height; + _sapp.framebuffer_height = _sapp.window_height; +} + +_SOKOL_PRIVATE void _sapp_x11_set_fullscreen(bool enable) { + /* NOTE: this function must be called after XMapWindow (which happens in _sapp_x11_show_window()) */ + if (_sapp.x11.NET_WM_STATE && _sapp.x11.NET_WM_STATE_FULLSCREEN) { + if (enable) { + const int _NET_WM_STATE_ADD = 1; + _sapp_x11_send_event(_sapp.x11.NET_WM_STATE, + _NET_WM_STATE_ADD, + _sapp.x11.NET_WM_STATE_FULLSCREEN, + 0, 1, 0); + } + else { + const int _NET_WM_STATE_REMOVE = 0; + _sapp_x11_send_event(_sapp.x11.NET_WM_STATE, + _NET_WM_STATE_REMOVE, + _sapp.x11.NET_WM_STATE_FULLSCREEN, + 0, 1, 0); + } + } + XFlush(_sapp.x11.display); +} + +_SOKOL_PRIVATE void _sapp_x11_create_hidden_cursor(void) { + SOKOL_ASSERT(0 == _sapp.x11.hidden_cursor); + const int w = 16; + const int h = 16; + XcursorImage* img = XcursorImageCreate(w, h); + SOKOL_ASSERT(img && (img->width == 16) && (img->height == 16) && img->pixels); + img->xhot = 0; + img->yhot = 0; + const size_t num_bytes = w * h * sizeof(XcursorPixel); + memset(img->pixels, 0, num_bytes); + _sapp.x11.hidden_cursor = XcursorImageLoadCursor(_sapp.x11.display, img); + XcursorImageDestroy(img); +} + +_SOKOL_PRIVATE void _sapp_x11_toggle_fullscreen(void) { + _sapp.fullscreen = !_sapp.fullscreen; + _sapp_x11_set_fullscreen(_sapp.fullscreen); + _sapp_x11_query_window_size(); +} + +_SOKOL_PRIVATE void _sapp_x11_show_mouse(bool show) { + if (show) { + XUndefineCursor(_sapp.x11.display, _sapp.x11.window); + } + else { + XDefineCursor(_sapp.x11.display, _sapp.x11.window, _sapp.x11.hidden_cursor); + } +} + +_SOKOL_PRIVATE void _sapp_x11_lock_mouse(bool lock) { + if (lock == _sapp.mouse.locked) { + return; + } + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; + _sapp.mouse.locked = lock; + if (_sapp.mouse.locked) { + if (_sapp.x11.xi.available) { + XIEventMask em; + unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 }; // XIMaskLen is a macro + em.deviceid = XIAllMasterDevices; + em.mask_len = sizeof(mask); + em.mask = mask; + XISetMask(mask, XI_RawMotion); + XISelectEvents(_sapp.x11.display, _sapp.x11.root, &em, 1); + } + XGrabPointer(_sapp.x11.display, // display + _sapp.x11.window, // grab_window + True, // owner_events + ButtonPressMask | ButtonReleaseMask | PointerMotionMask, // event_mask + GrabModeAsync, // pointer_mode + GrabModeAsync, // keyboard_mode + _sapp.x11.window, // confine_to + _sapp.x11.hidden_cursor, // cursor + CurrentTime); // time + } + else { + if (_sapp.x11.xi.available) { + XIEventMask em; + unsigned char mask[] = { 0 }; + em.deviceid = XIAllMasterDevices; + em.mask_len = sizeof(mask); + em.mask = mask; + XISelectEvents(_sapp.x11.display, _sapp.x11.root, &em, 1); + } + XWarpPointer(_sapp.x11.display, None, _sapp.x11.window, 0, 0, 0, 0, (int) _sapp.mouse.x, _sapp.mouse.y); + XUngrabPointer(_sapp.x11.display, CurrentTime); + } + XFlush(_sapp.x11.display); +} + +_SOKOL_PRIVATE void _sapp_x11_update_window_title(void) { + Xutf8SetWMProperties(_sapp.x11.display, + _sapp.x11.window, + _sapp.window_title, _sapp.window_title, + NULL, 0, NULL, NULL, NULL); + XChangeProperty(_sapp.x11.display, _sapp.x11.window, + _sapp.x11.NET_WM_NAME, _sapp.x11.UTF8_STRING, 8, + PropModeReplace, + (unsigned char*)_sapp.window_title, + strlen(_sapp.window_title)); + XChangeProperty(_sapp.x11.display, _sapp.x11.window, + _sapp.x11.NET_WM_ICON_NAME, _sapp.x11.UTF8_STRING, 8, + PropModeReplace, + (unsigned char*)_sapp.window_title, + strlen(_sapp.window_title)); + XFlush(_sapp.x11.display); } _SOKOL_PRIVATE void _sapp_x11_create_window(Visual* visual, int depth) { - _sapp_x11_colormap = XCreateColormap(_sapp_x11_display, _sapp_x11_root, visual, AllocNone); - - - - + _sapp.x11.colormap = XCreateColormap(_sapp.x11.display, _sapp.x11.root, visual, AllocNone); XSetWindowAttributes wa; memset(&wa, 0, sizeof(wa)); - //wa.override_redirect = 1; const uint32_t wamask = CWBorderPixel | CWColormap | CWEventMask; - wa.colormap = _sapp_x11_colormap; + wa.colormap = _sapp.x11.colormap; wa.border_pixel = 0; wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask | ExposureMask | FocusChangeMask | VisibilityChangeMask | EnterWindowMask | LeaveWindowMask | PropertyChangeMask; _sapp_x11_grab_error_handler(); - _sapp_x11_window = XCreateWindow(_sapp_x11_display, - _sapp_x11_root, + _sapp.x11.window = XCreateWindow(_sapp.x11.display, + _sapp.x11.root, 0, 0, _sapp.window_width, _sapp.window_height, @@ -6809,83 +7524,61 @@ _SOKOL_PRIVATE void _sapp_x11_create_window(Visual* visual, int depth) { wamask, &wa); _sapp_x11_release_error_handler(); - if (!_sapp_x11_window) { + if (!_sapp.x11.window) { _sapp_fail("X11: Failed to create window"); } - - if (_sapp.desc.fullscreen) { - Atom wm_state = XInternAtom(_sapp_x11_display, "_NET_WM_STATE", False); - Atom fullscreen = XInternAtom(_sapp_x11_display, "_NET_WM_STATE_FULLSCREEN", False); - - XEvent xev; - memset(&xev, 0, sizeof(xev)); - xev.type = ClientMessage; - xev.xclient.window = _sapp_x11_window; - xev.xclient.message_type = wm_state; - xev.xclient.format = 32; - xev.xclient.data.l[0] = 1; - xev.xclient.data.l[1] = fullscreen; - xev.xclient.data.l[2] = 0; - - XMapWindow(_sapp_x11_display, _sapp_x11_window); - - XSendEvent (_sapp_x11_display, DefaultRootWindow(_sapp_x11_display), False, - SubstructureRedirectMask | SubstructureNotifyMask, &xev); - } - Atom protocols[] = { - _sapp_x11_WM_DELETE_WINDOW + _sapp.x11.WM_DELETE_WINDOW }; - XSetWMProtocols(_sapp_x11_display, _sapp_x11_window, protocols, 1); + XSetWMProtocols(_sapp.x11.display, _sapp.x11.window, protocols, 1); XSizeHints* hints = XAllocSizeHints(); hints->flags |= PWinGravity; hints->win_gravity = StaticGravity; - XSetWMNormalHints(_sapp_x11_display, _sapp_x11_window, hints); + XSetWMNormalHints(_sapp.x11.display, _sapp.x11.window, hints); XFree(hints); _sapp_x11_update_window_title(); - _sapp_x11_query_window_size(); } _SOKOL_PRIVATE void _sapp_x11_destroy_window(void) { - if (_sapp_x11_window) { - XUnmapWindow(_sapp_x11_display, _sapp_x11_window); - XDestroyWindow(_sapp_x11_display, _sapp_x11_window); - _sapp_x11_window = 0; + if (_sapp.x11.window) { + XUnmapWindow(_sapp.x11.display, _sapp.x11.window); + XDestroyWindow(_sapp.x11.display, _sapp.x11.window); + _sapp.x11.window = 0; } - if (_sapp_x11_colormap) { - XFreeColormap(_sapp_x11_display, _sapp_x11_colormap); - _sapp_x11_colormap = 0; + if (_sapp.x11.colormap) { + XFreeColormap(_sapp.x11.display, _sapp.x11.colormap); + _sapp.x11.colormap = 0; } - XFlush(_sapp_x11_display); + XFlush(_sapp.x11.display); } _SOKOL_PRIVATE bool _sapp_x11_window_visible(void) { XWindowAttributes wa; - XGetWindowAttributes(_sapp_x11_display, _sapp_x11_window, &wa); + XGetWindowAttributes(_sapp.x11.display, _sapp.x11.window, &wa); return wa.map_state == IsViewable; } _SOKOL_PRIVATE void _sapp_x11_show_window(void) { if (!_sapp_x11_window_visible()) { - XMapWindow(_sapp_x11_display, _sapp_x11_window); - XRaiseWindow(_sapp_x11_display, _sapp_x11_window); - XFlush(_sapp_x11_display); + XMapWindow(_sapp.x11.display, _sapp.x11.window); + XRaiseWindow(_sapp.x11.display, _sapp.x11.window); + XFlush(_sapp.x11.display); } } _SOKOL_PRIVATE void _sapp_x11_hide_window(void) { - XUnmapWindow(_sapp_x11_display, _sapp_x11_window); - XFlush(_sapp_x11_display); + XUnmapWindow(_sapp.x11.display, _sapp.x11.window); + XFlush(_sapp.x11.display); } _SOKOL_PRIVATE unsigned long _sapp_x11_get_window_property(Atom property, Atom type, unsigned char** value) { Atom actualType; int actualFormat; unsigned long itemCount, bytesAfter; - XGetWindowProperty(_sapp_x11_display, - _sapp_x11_window, + XGetWindowProperty(_sapp.x11.display, + _sapp.x11.window, property, 0, LONG_MAX, @@ -6906,7 +7599,7 @@ _SOKOL_PRIVATE int _sapp_x11_get_window_state(void) { Window icon; } *state = NULL; - if (_sapp_x11_get_window_property(_sapp_x11_WM_STATE, _sapp_x11_WM_STATE, (unsigned char**)&state) >= 2) { + if (_sapp_x11_get_window_property(_sapp.x11.WM_STATE, _sapp.x11.WM_STATE, (unsigned char**)&state) >= 2) { result = state->state; } if (state) { @@ -6953,8 +7646,6 @@ _SOKOL_PRIVATE void _sapp_x11_mouse_event(sapp_event_type type, sapp_mousebutton _sapp_init_event(type); _sapp.event.mouse_button = btn; _sapp.event.modifiers = mods; - _sapp.event.mouse_x = _sapp.mouse_x; - _sapp.event.mouse_y = _sapp.mouse_y; _sapp_call_event(&_sapp.event); } } @@ -6977,7 +7668,7 @@ _SOKOL_PRIVATE void _sapp_x11_key_event(sapp_event_type type, sapp_keycode key, _sapp.event.modifiers = mods; _sapp_call_event(&_sapp.event); /* check if a CLIPBOARD_PASTED event must be sent too */ - if (_sapp.clipboard_enabled && + if (_sapp.clipboard.enabled && (type == SAPP_EVENTTYPE_KEY_DOWN) && (_sapp.event.modifiers == SAPP_MODIFIER_CTRL) && (_sapp.event.key_code == SAPP_KEYCODE_V)) @@ -7000,7 +7691,7 @@ _SOKOL_PRIVATE void _sapp_x11_char_event(uint32_t chr, bool repeat, uint32_t mod _SOKOL_PRIVATE sapp_keycode _sapp_x11_translate_key(int scancode) { int dummy; - KeySym* keysyms = XGetKeyboardMapping(_sapp_x11_display, scancode, 1, &dummy); + KeySym* keysyms = XGetKeyboardMapping(_sapp.x11.display, scancode, 1, &dummy); SOKOL_ASSERT(keysyms); KeySym keysym = keysyms[0]; XFree(keysyms); @@ -7176,6 +7867,35 @@ static bool _sapp_x11_keycodes[256]; _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { switch (event->type) { + case GenericEvent: + if (_sapp.mouse.locked && _sapp.x11.xi.available) { + if (event->xcookie.extension == _sapp.x11.xi.major_opcode) { + if (XGetEventData(_sapp.x11.display, &event->xcookie)) { + if (event->xcookie.evtype == XI_RawMotion) { + XIRawEvent* re = (XIRawEvent*) event->xcookie.data; + if (re->valuators.mask_len) { + const double* values = re->raw_values; + if (XIMaskIsSet(re->valuators.mask, 0)) { + _sapp.mouse.dx = (float) *values; + values++; + } + if (XIMaskIsSet(re->valuators.mask, 1)) { + _sapp.mouse.dy = (float) *values; + } + _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mod(event->xmotion.state)); + } + } + XFreeEventData(_sapp.x11.display, &event->xcookie); + } + } + } + break; + case FocusOut: + /* if focus is lost for any reason, and we're in mouse locked mode, disable mouse lock */ + if (_sapp.mouse.locked) { + _sapp_x11_lock_mouse(false); + } + break; case KeyPress: { int keycode = event->xkey.keycode; @@ -7238,9 +7958,18 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mod(event->xcrossing.state)); break; case MotionNotify: - _sapp.mouse_x = event->xmotion.x; - _sapp.mouse_y = event->xmotion.y; - _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mod(event->xmotion.state)); + if (!_sapp.mouse.locked) { + const float new_x = (float) event->xmotion.x; + const float new_y = (float) event->xmotion.y; + if (_sapp.mouse.pos_valid) { + _sapp.mouse.dx = new_x - _sapp.mouse.x; + _sapp.mouse.dy = new_y - _sapp.mouse.y; + } + _sapp.mouse.x = new_x; + _sapp.mouse.y = new_y; + _sapp.mouse.pos_valid = true; + _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mod(event->xmotion.state)); + } break; case ConfigureNotify: if ((event->xconfigure.width != _sapp.window_width) || (event->xconfigure.height != _sapp.window_height)) { @@ -7253,10 +7982,10 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { break; case PropertyNotify: if (event->xproperty.state == PropertyNewValue) { - if (event->xproperty.atom == _sapp_x11_WM_STATE) { + if (event->xproperty.atom == _sapp.x11.WM_STATE) { const int state = _sapp_x11_get_window_state(); - if (state != _sapp_x11_window_state) { - _sapp_x11_window_state = state; + if (state != _sapp.x11.window_state) { + _sapp.x11.window_state = state; if (state == IconicState) { _sapp_x11_app_event(SAPP_EVENTTYPE_ICONIFIED); } @@ -7268,9 +7997,9 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { } break; case ClientMessage: - if (event->xclient.message_type == _sapp_x11_WM_PROTOCOLS) { + if (event->xclient.message_type == _sapp.x11.WM_PROTOCOLS) { const Atom protocol = event->xclient.data.l[0]; - if (protocol == _sapp_x11_WM_DELETE_WINDOW) { + if (protocol == _sapp.x11.WM_DELETE_WINDOW) { _sapp.quit_requested = true; } } @@ -7280,22 +8009,23 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { } } -_SOKOL_PRIVATE void _sapp_run(const sapp_desc* desc) { +_SOKOL_PRIVATE void _sapp_linux_run(const sapp_desc* desc) { _sapp_init_state(desc); - _sapp_x11_window_state = NormalState; + _sapp.x11.window_state = NormalState; XInitThreads(); XrmInitialize(); - _sapp_x11_display = XOpenDisplay(NULL); - if (!_sapp_x11_display) { + _sapp.x11.display = XOpenDisplay(NULL); + if (!_sapp.x11.display) { _sapp_fail("XOpenDisplay() failed!\n"); } - _sapp_x11_screen = DefaultScreen(_sapp_x11_display); - _sapp_x11_root = DefaultRootWindow(_sapp_x11_display); - XkbSetDetectableAutoRepeat(_sapp_x11_display, true, NULL); + _sapp.x11.screen = DefaultScreen(_sapp.x11.display); + _sapp.x11.root = DefaultRootWindow(_sapp.x11.display); + XkbSetDetectableAutoRepeat(_sapp.x11.display, true, NULL); _sapp_x11_query_system_dpi(); - _sapp.dpi_scale = _sapp_x11_dpi / 96.0f; + _sapp.dpi_scale = _sapp.x11.dpi / 96.0f; _sapp_x11_init_extensions(); + _sapp_x11_create_hidden_cursor(); _sapp_glx_init(); Visual* visual = 0; int depth = 0; @@ -7304,19 +8034,23 @@ _SOKOL_PRIVATE void _sapp_run(const sapp_desc* desc) { _sapp_glx_create_context(); _sapp.valid = true; _sapp_x11_show_window(); + if (_sapp.fullscreen) { + _sapp_x11_set_fullscreen(true); + } + _sapp_x11_query_window_size(); _sapp_glx_swapinterval(_sapp.swap_interval); - XFlush(_sapp_x11_display); + XFlush(_sapp.x11.display); while (!_sapp.quit_ordered) { _sapp_glx_make_current(); - int count = XPending(_sapp_x11_display); + int count = XPending(_sapp.x11.display); while (count--) { XEvent event; - XNextEvent(_sapp_x11_display, &event); + XNextEvent(_sapp.x11.display, &event); _sapp_x11_process_event(&event); } _sapp_frame(); _sapp_glx_swap_buffers(); - XFlush(_sapp_x11_display); + XFlush(_sapp.x11.display); /* handle quit-requested, either from window or from sapp_request_quit() */ if (_sapp.quit_requested && !_sapp.quit_ordered) { /* give user code a chance to intervene */ @@ -7330,24 +8064,37 @@ _SOKOL_PRIVATE void _sapp_run(const sapp_desc* desc) { _sapp_call_cleanup(); _sapp_glx_destroy_context(); _sapp_x11_destroy_window(); - XCloseDisplay(_sapp_x11_display); + XCloseDisplay(_sapp.x11.display); _sapp_discard_state(); } #if !defined(SOKOL_NO_ENTRY) int main(int argc, char* argv[]) { sapp_desc desc = sokol_main(argc, argv); - _sapp_run(&desc); + _sapp_linux_run(&desc); return 0; } #endif /* SOKOL_NO_ENTRY */ -#endif /* LINUX */ +#endif /* _SAPP_LINUX */ /*== PUBLIC API FUNCTIONS ====================================================*/ #if defined(SOKOL_NO_ENTRY) SOKOL_API_IMPL int sapp_run(const sapp_desc* desc) { SOKOL_ASSERT(desc); - _sapp_run(desc); + #if defined(_SAPP_MACOS) + _sapp_macos_run(desc); + #elif defined(_SAPP_IOS) + _sapp_ios_run(desc); + #elif defined(_SAPP_EMSCRIPTEN) + _sapp_emsc_run(desc); + #elif defined(_SAPP_WIN32) + _sapp_win32_run(desc); + #elif defined(_SAPP_LINUX) + _sapp_linux_run(desc); + #else + // calling sapp_run() directly is not supported on Android) + _sapp_fail("sapp_run() not supported on this platform!"); + #endif return 0; } @@ -7387,12 +8134,38 @@ SOKOL_API_IMPL int sapp_width(void) { return (_sapp.framebuffer_width > 0) ? _sapp.framebuffer_width : 1; } +SOKOL_API_IMPL int sapp_color_format(void) { + #if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU) + switch (_sapp.emsc.wgpu.render_format) { + case WGPUTextureFormat_RGBA8Unorm: + return _SAPP_PIXELFORMAT_RGBA8; + case WGPUTextureFormat_BGRA8Unorm: + return _SAPP_PIXELFORMAT_BGRA8; + default: + SOKOL_UNREACHABLE; + return 0; + } + #elif defined(SOKOL_METAL) || defined(SOKOL_D3D11) + return _SAPP_PIXELFORMAT_BGRA8; + #else + return _SAPP_PIXELFORMAT_RGBA8; + #endif +} + +SOKOL_API_IMPL int sapp_depth_format(void) { + return _SAPP_PIXELFORMAT_DEPTH_STENCIL; +} + +SOKOL_API_IMPL int sapp_sample_count(void) { + return _sapp.sample_count; +} + SOKOL_API_IMPL int sapp_height(void) { return (_sapp.framebuffer_height > 0) ? _sapp.framebuffer_height : 1; } SOKOL_API_IMPL bool sapp_high_dpi(void) { - return _sapp.desc.high_dpi && (_sapp.dpi_scale > 1.5f); + return _sapp.desc.high_dpi && (_sapp.dpi_scale >= 1.5f); } SOKOL_API_IMPL float sapp_dpi_scale(void) { @@ -7403,15 +8176,15 @@ SOKOL_API_IMPL bool sapp_gles2(void) { return _sapp.gles2_fallback; } -SOKOL_API_IMPL void sapp_show_keyboard(bool shown) { - #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE - _sapp_ios_show_keyboard(shown); - #elif defined(__EMSCRIPTEN__) - _sapp_emsc_show_keyboard(shown); - #elif defined(__ANDROID__) - _sapp_android_show_keyboard(shown); +SOKOL_API_IMPL void sapp_show_keyboard(bool show) { + #if defined(_SAPP_IOS) + _sapp_ios_show_keyboard(show); + #elif defined(_SAPP_EMSCRIPTEN) + _sapp_emsc_show_keyboard(show); + #elif defined(_SAPP_ANDROID) + _sapp_android_show_keyboard(show); #else - _SOKOL_UNUSED(shown); + _SOKOL_UNUSED(show); #endif } @@ -7419,22 +8192,56 @@ SOKOL_API_IMPL bool sapp_keyboard_shown(void) { return _sapp.onscreen_keyboard_shown; } -SOKOL_API_IMPL void sapp_show_mouse(bool shown) { - #if defined(_WIN32) - _sapp_win32_show_mouse(shown); - #else - _SOKOL_UNUSED(shown); +SOKOL_API_DECL bool sapp_is_fullscreen(void) { + return _sapp.fullscreen; +} + +SOKOL_API_DECL void sapp_toggle_fullscreen(void) { + #if defined(_SAPP_MACOS) + _sapp_macos_toggle_fullscreen(); + #elif defined(_SAPP_WIN32) + _sapp_win32_toggle_fullscreen(); + #elif defined(_SAPP_LINUX) + _sapp_x11_toggle_fullscreen(); #endif } +/* NOTE that sapp_show_mouse() does not "stack" like the Win32 or macOS API functions! */ +SOKOL_API_IMPL void sapp_show_mouse(bool show) { + if (_sapp.mouse.shown != show) { + #if defined(_SAPP_MACOS) + _sapp_macos_show_mouse(show); + #elif defined(_SAPP_WIN32) + _sapp_win32_show_mouse(show); + #elif defined(_SAPP_LINUX) + _sapp_x11_show_mouse(show); + #endif + _sapp.mouse.shown = show; + } +} + SOKOL_API_IMPL bool sapp_mouse_shown(void) { - #if defined(_WIN32) - return _sapp_win32_mouse_shown(); + return _sapp.mouse.shown; +} + +SOKOL_API_IMPL void sapp_lock_mouse(bool lock) { + #if defined(_SAPP_MACOS) + _sapp_macos_lock_mouse(lock); + #elif defined(_SAPP_EMSCRIPTEN) + _sapp_emsc_lock_mouse(lock); + #elif defined(_SAPP_WIN32) + _sapp_win32_lock_mouse(lock); + #elif defined(_SAPP_LINUX) + _sapp_x11_lock_mouse(lock); #else - return false; + _sapp.mouse.locked = lock; #endif } +SOKOL_API_IMPL bool sapp_mouse_locked(void) { + return _sapp.mouse.locked; +} + SOKOL_API_IMPL void sapp_request_quit(void) { _sapp.quit_requested = true; } @@ -7453,42 +8260,46 @@ SOKOL_API_IMPL void sapp_consume_event(void) { /* NOTE: on HTML5, sapp_set_clipboard_string() must be called from within event handler! */ SOKOL_API_IMPL void sapp_set_clipboard_string(const char* str) { - if (!_sapp.clipboard_enabled) { + if (!_sapp.clipboard.enabled) { return; } SOKOL_ASSERT(str); - #if defined(__APPLE__) && defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE + #if defined(_SAPP_MACOS) _sapp_macos_set_clipboard_string(str); - #elif defined(__EMSCRIPTEN__) + #elif defined(_SAPP_EMSCRIPTEN) _sapp_emsc_set_clipboard_string(str); - #elif defined(_WIN32) + #elif defined(_SAPP_WIN32) _sapp_win32_set_clipboard_string(str); #else /* not implemented */ #endif - _sapp_strcpy(str, _sapp.clipboard, _sapp.clipboard_size); + _sapp_strcpy(str, _sapp.clipboard.buffer, _sapp.clipboard.buf_size); } SOKOL_API_IMPL const char* sapp_get_clipboard_string(void) { - if (!_sapp.clipboard_enabled) { + if (!_sapp.clipboard.enabled) { return ""; } - #if defined(__APPLE__) && defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE + #if defined(_SAPP_MACOS) return _sapp_macos_get_clipboard_string(); - #elif defined(__EMSCRIPTEN__) - return _sapp.clipboard; - #elif defined(_WIN32) + #elif defined(_SAPP_EMSCRIPTEN) + return _sapp.clipboard.buffer; + #elif defined(_SAPP_WIN32) return _sapp_win32_get_clipboard_string(); #else /* not implemented */ - return _sapp.clipboard; + return _sapp.clipboard.buffer; #endif } SOKOL_API_IMPL const void* sapp_metal_get_device(void) { SOKOL_ASSERT(_sapp.valid); #if defined(SOKOL_METAL) - const void* obj = (__bridge const void*) _sapp_mtl_device_obj; + #if defined(_SAPP_MACOS) + const void* obj = (__bridge const void*) _sapp.macos.mtl_device; + #else + const void* obj = (__bridge const void*) _sapp.ios.mtl_device; + #endif SOKOL_ASSERT(obj); return obj; #else @@ -7499,7 +8310,11 @@ SOKOL_API_IMPL const void* sapp_metal_get_device(void) { SOKOL_API_IMPL const void* sapp_metal_get_renderpass_descriptor(void) { SOKOL_ASSERT(_sapp.valid); #if defined(SOKOL_METAL) - const void* obj = (__bridge const void*) [_sapp_view_obj currentRenderPassDescriptor]; + #if defined(_SAPP_MACOS) + const void* obj = (__bridge const void*) [_sapp.macos.view currentRenderPassDescriptor]; + #else + const void* obj = (__bridge const void*) [_sapp.ios.view currentRenderPassDescriptor]; + #endif SOKOL_ASSERT(obj); return obj; #else @@ -7510,7 +8325,11 @@ SOKOL_API_IMPL const void* sapp_metal_get_renderpass_descriptor(void) { SOKOL_API_IMPL const void* sapp_metal_get_drawable(void) { SOKOL_ASSERT(_sapp.valid); #if defined(SOKOL_METAL) - const void* obj = (__bridge const void*) [_sapp_view_obj currentDrawable]; + #if defined(_SAPP_MACOS) + const void* obj = (__bridge const void*) [_sapp.macos.view currentDrawable]; + #else + const void* obj = (__bridge const void*) [_sapp.ios.view currentDrawable]; + #endif SOKOL_ASSERT(obj); return obj; #else @@ -7519,8 +8338,8 @@ SOKOL_API_IMPL const void* sapp_metal_get_drawable(void) { } SOKOL_API_IMPL const void* sapp_macos_get_window(void) { - #if defined(__APPLE__) && !TARGET_OS_IPHONE - const void* obj = (__bridge const void*) _sapp_macos_window_obj; + #if defined(_SAPP_MACOS) + const void* obj = (__bridge const void*) _sapp.macos.window; SOKOL_ASSERT(obj); return obj; #else @@ -7530,13 +8349,13 @@ SOKOL_API_IMPL const void* sapp_macos_get_window(void) { SOKOL_API_IMPL void sapp_macos_set_title(const char* title) { #if defined(__APPLE__) && !TARGET_OS_IPHONE - [_sapp_macos_window_obj setTitle: [NSString stringWithUTF8String:title]]; + [_sapp.macos.window setTitle: [NSString stringWithUTF8String:title]]; #endif } SOKOL_API_IMPL const void* sapp_ios_get_window(void) { - #if defined(__APPLE__) && TARGET_OS_IPHONE - const void* obj = (__bridge const void*) _sapp_ios_window_obj; + #if defined(_SAPP_IOS) + const void* obj = (__bridge const void*) _sapp.ios.window; SOKOL_ASSERT(obj); return obj; #else @@ -7548,7 +8367,7 @@ SOKOL_API_IMPL const void* sapp_ios_get_window(void) { SOKOL_API_IMPL const void* sapp_d3d11_get_device(void) { SOKOL_ASSERT(_sapp.valid); #if defined(SOKOL_D3D11) - return _sapp_d3d11_device; + return _sapp.d3d11.device; #else return 0; #endif @@ -7557,7 +8376,7 @@ SOKOL_API_IMPL const void* sapp_d3d11_get_device(void) { SOKOL_API_IMPL const void* sapp_d3d11_get_device_context(void) { SOKOL_ASSERT(_sapp.valid); #if defined(SOKOL_D3D11) - return _sapp_d3d11_device_context; + return _sapp.d3d11.device_context; #else return 0; #endif @@ -7566,7 +8385,7 @@ SOKOL_API_IMPL const void* sapp_d3d11_get_device_context(void) { SOKOL_API_IMPL const void* sapp_d3d11_get_render_target_view(void) { SOKOL_ASSERT(_sapp.valid); #if defined(SOKOL_D3D11) - return _sapp_d3d11_rtv; + return _sapp.d3d11.rtv; #else return 0; #endif @@ -7575,7 +8394,7 @@ SOKOL_API_IMPL const void* sapp_d3d11_get_render_target_view(void) { SOKOL_API_IMPL const void* sapp_d3d11_get_depth_stencil_view(void) { SOKOL_ASSERT(_sapp.valid); #if defined(SOKOL_D3D11) - return _sapp_d3d11_dsv; + return _sapp.d3d11.dsv; #else return 0; #endif @@ -7583,8 +8402,54 @@ SOKOL_API_IMPL const void* sapp_d3d11_get_depth_stencil_view(void) { SOKOL_API_IMPL const void* sapp_win32_get_hwnd(void) { SOKOL_ASSERT(_sapp.valid); - #if defined(_WIN32) - return _sapp_win32_hwnd; + #if defined(_SAPP_WIN32) + return _sapp.win32.hwnd; + #else + return 0; + #endif +} + +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; + #else + return 0; + #endif +} + +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; + } + else { + return (const void*) _sapp.emsc.wgpu.swapchain_view; + } + #else + return 0; + #endif +} + +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; + } + else { + return 0; + } + #else + return 0; + #endif +} + +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; #else return 0; #endif @@ -7592,8 +8457,8 @@ SOKOL_API_IMPL const void* sapp_win32_get_hwnd(void) { SOKOL_API_IMPL const void* sapp_android_get_native_activity(void) { SOKOL_ASSERT(_sapp.valid); - #if defined(__ANDROID__) - return (void*)_sapp_android_state.activity; + #if defined(_SAPP_ANDROID) + return (void*)_sapp.android.activity; #else return 0; #endif @@ -7603,10 +8468,4 @@ SOKOL_API_IMPL void sapp_html5_ask_leave_site(bool ask) { _sapp.html5_ask_leave_site = ask; } -#undef _sapp_def - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - #endif /* SOKOL_IMPL */ diff --git a/thirdparty/sokol/sokol_gfx.h b/thirdparty/sokol/sokol_gfx.h index 6a89094dfb..bcbf072301 100755 --- a/thirdparty/sokol/sokol_gfx.h +++ b/thirdparty/sokol/sokol_gfx.h @@ -16,6 +16,7 @@ #define SOKOL_GLES3 #define SOKOL_D3D11 #define SOKOL_METAL + #define SOKOL_WGPU #define SOKOL_DUMMY_BACKEND I.e. for the GL 3.3 Core Profile it should look like this: @@ -135,6 +136,11 @@ sg_draw(int base_element, int num_elements, int num_instances) + In the case of no instancing: num_instances should be set to 1 and base_element/num_elements are + amounts of vertices. In the case of instancing (meaning num_instances > 1), num elements is the + number of vertices in one instance, while base_element remains unchanged. base_element is the index + of the first vertex to begin drawing from. + --- finish the current rendering pass with: sg_end_pass() @@ -228,6 +234,14 @@ bool sg_query_buffer_overflow(sg_buffer buf) + NOTE: Due to restrictions in underlying 3D-APIs, appended chunks of + data will be 4-byte aligned in the destination buffer. This means + that there will be gaps in index buffers containing 16-bit indices + when the number of indices in a call to sg_append_buffer() is + odd. This isn't a problem when each call to sg_append_buffer() + is associated with one draw call, but will be problematic when + a single indexed draw call spans several appended chunks of indices. + --- to check at runtime for optional features, limits and pixelformat support, call: @@ -275,6 +289,40 @@ will be replaced with their concrete values in the returned desc struct. + ON INITIALIZATION: + ================== + When calling sg_setup(), a pointer to an sg_desc struct must be provided + which contains initialization options. These options provide two types + of information to sokol-gfx: + + (1) upper bounds and limits needed to allocate various internal + data structures: + - the max number of resources of each type that can + be alive at the same time, this is used for allocating + internal pools + - the max overall size of uniform data that can be + updated per frame, including a worst-case alignment + per uniform update (this worst-case alignment is 256 bytes) + - the max size of all dynamic resource updates (sg_update_buffer, + sg_append_buffer and sg_update_image) per frame + - the max number of entries in the texture sampler cache + (how many unique texture sampler can exist at the same time) + Not all of those limit values are used by all backends, but it is + good practice to provide them none-the-less. + + (2) 3D-API "context information" (sometimes also called "bindings"): + sokol_gfx.h doesn't create or initialize 3D API objects which are + closely related to the presentation layer (this includes the "rendering + device", the swapchain, and any objects which depend on the + swapchain). These API objects (or callback functions to obtain + them, if those objects might change between frames), must + be provided in a nested sg_context_desc struct inside the + sg_desc struct. If sokol_gfx.h is used together with + sokol_app.h, have a look at the sokol_glue.h header which provides + a convenience function to get a sg_context_desc struct filled out + with context information provided by sokol_app.h + + See the documention block of the sg_desc struct below for more information. BACKEND-SPECIFIC TOPICS: ======================== @@ -342,6 +390,25 @@ The provided semantic information will be used later in sg_create_pipeline() to match the vertex layout to vertex shader inputs. + --- on D3D11, and when passing HLSL source code (instead of byte code) to shader + creation, you can optionally define the shader model targets on the vertex + stage: + + sg_shader_Desc desc = { + .vs = { + ... + .d3d11_target = "vs_5_0" + }, + .fs = { + ... + .d3d11_target = "ps_5_0" + } + }; + + The default targets are "ps_4_0" and "fs_4_0". Note that those target names + are only used when compiling shaders from source. They are ignored when + creating a shader from bytecode. + --- on Metal, GL 3.3 or GLES3/WebGL2, you don't need to provide an attribute name or semantic name, since vertex attributes can be bound by their slot index (this is mandatory in Metal, and optional in GL): @@ -446,10 +513,9 @@ - SG_VERTEXFORMAT_SHORT4N - SG_VERTEXFORMAT_USHORT4N - D3D11 will not convert *non-normalized* vertex formats - to floating point vertex shader inputs, those can - only use the ivecn formats when D3D11 is used - as backend (GL and should Metal can use both formats) + D3D11 will not convert *non-normalized* vertex formats to floating point + vertex shader inputs, those can only be uses with the *ivecn* vertex shader + input types when D3D11 is used as backend (GL and Metal can use both formats) - SG_VERTEXFORMAT_BYTE4, - SG_VERTEXFORMAT_UBYTE4 @@ -581,7 +647,7 @@ enum { The active 3D-API backend, use the function sg_query_backend() to get the currently active backend. - For returned value corresponds with the compile-time define to select + The returned value corresponds with the compile-time define to select a backend, with the only exception of SOKOL_GLES3: this may return SG_BACKEND_GLES2 if the backend has to fallback to GLES2 mode because GLES3 isn't supported. @@ -594,6 +660,7 @@ typedef enum sg_backend { SG_BACKEND_METAL_IOS, SG_BACKEND_METAL_MACOS, SG_BACKEND_METAL_SIMULATOR, + SG_BACKEND_WGPU, SG_BACKEND_DUMMY, } sg_backend; @@ -627,7 +694,7 @@ typedef enum sg_backend { - render: the pixelformat can be used for render targets - blend: blending is supported when using the pixelformat for render targets - - msaa: multisample-antiliasing is supported when using the + - msaa: multisample-antialiasing is supported when using the pixelformat for render targets - depth: the pixelformat can be used for depth-stencil attachments @@ -740,13 +807,13 @@ typedef struct sg_pixelformat_info { returned by sg_query_features() */ typedef struct sg_features { - bool instancing; - bool origin_top_left; - bool multiple_render_targets; - bool msaa_render_targets; - bool imagetype_3d; /* creation of SG_IMAGETYPE_3D images is supported */ - bool imagetype_array; /* creation of SG_IMAGETYPE_ARRAY images is supported */ - bool image_clamp_to_border; /* border color and clamp-to-border UV-wrap mode is supported */ + bool instancing; /* hardware instancing supported */ + bool origin_top_left; /* framebuffer and texture origin is in top left corner */ + bool multiple_render_targets; /* offscreen render passes can have multiple render targets attached */ + bool msaa_render_targets; /* offscreen render passes support MSAA antialiasing */ + bool imagetype_3d; /* creation of SG_IMAGETYPE_3D images is supported */ + bool imagetype_array; /* creation of SG_IMAGETYPE_ARRAY images is supported */ + bool image_clamp_to_border; /* border color and clamp-to-border UV-wrap mode is supported */ } sg_features; /* @@ -756,8 +823,8 @@ typedef struct sg_limits { uint32_t max_image_size_2d; /* max width/height of SG_IMAGETYPE_2D images */ uint32_t max_image_size_cube; /* max width/height of SG_IMAGETYPE_CUBE images */ uint32_t max_image_size_3d; /* max width/height/depth of SG_IMAGETYPE_3D images */ - uint32_t max_image_size_array; - uint32_t max_image_array_layers; + uint32_t max_image_size_array; /* max width/height pf SG_IMAGETYPE_ARRAY images */ + uint32_t max_image_array_layers; /* max number of layers in SG_IMAGETYPE_ARRAY images */ uint32_t max_vertex_attrs; /* <= SG_MAX_VERTEX_ATTRIBUTES (only on some GLES2 impls) */ } sg_limits; @@ -797,7 +864,7 @@ typedef enum sg_resource_state { and images: SG_USAGE_IMMUTABLE: the resource will never be updated with - new data, instead the data content of the + new data, instead the content of the resource must be provided on creation SG_USAGE_DYNAMIC: the resource will be updated infrequently with new data (this could range from "once @@ -810,13 +877,15 @@ typedef enum sg_resource_state { CPU needs to wait for the GPU when attempting to update a resource that might be currently accessed by the GPU. - Resource content is updated with the function sg_update_buffer() for - buffer objects, and sg_update_image() for image objects. Only - one update is allowed per frame and resource object. The - application must update all data required for rendering (this - means that the update data can be smaller than the resource size, - if only a part of the overall resource size is used for rendering, - you only need to make sure that the data that *is* used is valid. + Resource content is updated with the functions sg_update_buffer() or + sg_append_buffer() for buffer objects, and sg_update_image() for image + objects. For the sg_update_*() functions, only one update is allowed per + frame and resource object, while sg_append_buffer() can be called + multiple times per frame on the same buffer. The application must update + all data required for rendering (this means that the update data can be + smaller than the resource size, if only a part of the overall resource + size is used for rendering, you only need to make sure that the data that + *is* used is valid). The default usage is SG_USAGE_IMMUTABLE. */ @@ -867,10 +936,12 @@ typedef enum sg_index_type { /* sg_image_type - Indicates the basic image type (2D-texture, cubemap, 3D-texture - or 2D-array-texture). 3D- and array-textures are not supported - on the GLES2/WebGL backend. The image type is used in the - sg_image_desc.type member when creating an image. + Indicates the basic type of an image object (2D-texture, cubemap, + 3D-texture or 2D-array-texture). 3D- and array-textures are not supported + on the GLES2/WebGL backend (use sg_query_features().imagetype_3d and + sg_query_features().imagetype_array to check for support). The image type + is used in the sg_image_desc.type member when creating an image, and + in sg_shader_image_desc when describing a shader's texture sampler binding. The default image type when creating an image is SG_IMAGETYPE_2D. */ @@ -884,6 +955,23 @@ typedef enum sg_image_type { _SG_IMAGETYPE_FORCE_U32 = 0x7FFFFFFF } sg_image_type; +/* + sg_sampler_type + + Indicates the basic data type of a shader's texture sampler which + can be float , unsigned integer or signed integer. The sampler + type is used in the sg_shader_image_desc to describe the + sampler type of a shader's texture sampler binding. + + The default sampler type is SG_SAMPLERTYPE_FLOAT. +*/ +typedef enum sg_sampler_type { + _SG_SAMPLERTYPE_DEFAULT, /* value 0 reserved for default-init */ + SG_SAMPLERTYPE_FLOAT, + SG_SAMPLERTYPE_SINT, + SG_SAMPLERTYPE_UINT, +} sg_sampler_type; + /* sg_cube_face @@ -1244,6 +1332,10 @@ typedef enum sg_blend_op { sg_pipeline_desc.blend.color_write_mask when creating a pipeline object. The default colormask is SG_COLORMASK_RGBA (write all colors channels) + + NOTE: since the color mask value 0 is reserved for the default value + (SG_COLORMASK_RGBA), use SG_COLORMASK_NONE if all color channels + should be disabled. */ typedef enum sg_color_mask { _SG_COLORMASK_DEFAULT = 0, /* value 0 reserved for default-init */ @@ -1346,8 +1438,8 @@ typedef struct sg_pass_action { are defined by the SG_MAX_SHADERSTAGE_BUFFERS and SG_MAX_SHADERSTAGE_IMAGES configuration constants. - The optional buffer offsets can be used to group different chunks - of vertex- and/or index-data into the same buffer objects. + The optional buffer offsets can be used to put different unrelated + chunks of vertex- and/or index-data into the same buffer objects. */ typedef struct sg_bindings { uint32_t _start_canary; @@ -1374,9 +1466,9 @@ typedef struct sg_bindings { .content 0 .label 0 (optional string label for trace hooks) - The dbg_label will be ignored by sokol_gfx.h, it is only useful + The label will be ignored by sokol_gfx.h, it is only useful when hooking into sg_make_buffer() or sg_init_buffer() via - the sg_install_trace_hook + the sg_install_trace_hooks() function. ADVANCED TOPIC: Injecting native 3D-API buffers: @@ -1414,6 +1506,8 @@ typedef struct sg_buffer_desc { const void* mtl_buffers[SG_NUM_INFLIGHT_FRAMES]; /* D3D11 specific */ const void* d3d11_buffer; + /* WebGPU specific */ + const void* wgpu_buffer; uint32_t _end_canary; } sg_buffer_desc; @@ -1461,9 +1555,8 @@ typedef struct sg_image_content { .depth/.layers: 1 .num_mipmaps: 1 .usage: SG_USAGE_IMMUTABLE - .pixel_format: SG_PIXELFORMAT_RGBA8 for textures, backend-dependent - for render targets (RGBA8 or BGRA8) - .sample_count: 1 (only used in render_targets) + .pixel_format: SG_PIXELFORMAT_RGBA8 for textures, or sg_desc.context.color_format for render targets + .sample_count: 1 for textures, or sg_desc.context.sample_count for render target .min_filter: SG_FILTER_NEAREST .mag_filter: SG_FILTER_NEAREST .wrap_u: SG_WRAP_REPEAT @@ -1476,6 +1569,17 @@ typedef struct sg_image_content { .content an sg_image_content struct to define the initial content .label 0 (optional string label for trace hooks) + Q: Why is the default sample_count for render targets identical with the + "default sample count" from sg_desc.context.sample_count? + + A: So that it matches the default sample count in pipeline objects. Even + though it is a bit strange/confusing that offscreen render targets by default + get the same sample count as the default framebuffer, but it's better that + an offscreen render target created with default parameters matches + a pipeline object created with default parameters. + + NOTE: + SG_IMAGETYPE_ARRAY and SG_IMAGETYPE_3D are not supported on WebGL/GLES2, use sg_query_features().imagetype_array and sg_query_features().imagetype_3d at runtime to check @@ -1528,6 +1632,8 @@ typedef struct sg_image_desc { const void* mtl_textures[SG_NUM_INFLIGHT_FRAMES]; /* D3D11 specific */ const void* d3d11_texture; + /* WebGPU specific */ + const void* wgpu_texture; uint32_t _end_canary; } sg_image_desc; @@ -1540,9 +1646,11 @@ typedef struct sg_image_desc { - reflection information for vertex attributes (vertex shader inputs): - vertex attribute name (required for GLES2, optional for GLES3 and GL) - a semantic name and index (required for D3D11) - - for each vertex- and fragment-shader-stage: + - for each shader-stage (vertex and fragment): - the shader source or bytecode - an optional entry function name + - an optional compile target (only for D3D11 when source is provided, + defaults are "vs_4_0" and "ps_4_0") - reflection info for each uniform block used by the shader stage: - the size of the uniform block in bytes - reflection info for each uniform block member (only required for GL backends): @@ -1551,13 +1659,17 @@ typedef struct sg_image_desc { - if the member is an array, the number of array items - reflection info for the texture images used by the shader stage: - the image type (SG_IMAGETYPE_xxx) + - the sampler type (SG_SAMPLERTYPE_xxx, default is SG_SAMPLERTYPE_FLOAT) - the name of the texture sampler (required for GLES2, optional everywhere else) For all GL backends, shader source-code must be provided. For D3D11 and Metal, either shader source-code or byte-code can be provided. For D3D11, if source code is provided, the d3dcompiler_47.dll will be loaded - on demand. If this fails, shader creation will fail. + on demand. If this fails, shader creation will fail. When compiling HLSL + source code, you can provide an optional target string via + sg_shader_stage_desc.d3d11_target, the default target is "vs_4_0" for the + vertex shader stage and "ps_4_0" for the pixel shader stage. */ typedef struct sg_shader_attr_desc { const char* name; /* GLSL vertex attribute name (only required for GLES2) */ @@ -1578,7 +1690,8 @@ typedef struct sg_shader_uniform_block_desc { typedef struct sg_shader_image_desc { const char* name; - sg_image_type type; + sg_image_type type; /* FIXME: should this be renamed to 'image_type'? */ + sg_sampler_type sampler_type; } sg_shader_image_desc; typedef struct sg_shader_stage_desc { @@ -1586,6 +1699,7 @@ typedef struct sg_shader_stage_desc { const uint8_t* byte_code; int byte_code_size; const char* entry; + const char* d3d11_target; sg_shader_uniform_block_desc uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; sg_shader_image_desc images[SG_MAX_SHADERSTAGE_IMAGES]; } sg_shader_stage_desc; @@ -1616,9 +1730,9 @@ typedef struct sg_shader_desc { If the vertex data has no gaps between vertex components, you can omit the .layout.buffers[].stride and layout.attrs[].offset items (leave them - default-initialized to 0), sokol will then compute the offsets and strides - from the vertex component formats (.layout.attrs[].offset). Please note - that ALL vertex attribute offsets must be 0 in order for the the + default-initialized to 0), sokol-gfx will then compute the offsets and strides + from the vertex component formats (.layout.attrs[].format). Please note + that ALL vertex attribute offsets must be 0 in order for the automatic offset computation to kick in. The default configuration is as follows: @@ -1632,7 +1746,7 @@ typedef struct sg_shader_desc { .buffer_index 0 the vertex buffer bind slot .offset 0 (offsets can be omitted if the vertex layout has no gaps) .format SG_VERTEXFORMAT_INVALID (must be initialized!) - .shader: 0 (must be intilized with a valid sg_shader id!) + .shader: 0 (must be initialized with a valid sg_shader id!) .primitive_type: SG_PRIMITIVETYPE_TRIANGLES .index_type: SG_INDEXTYPE_NONE .depth_stencil: @@ -1664,7 +1778,7 @@ typedef struct sg_shader_desc { .alpha_to_coverage_enabled: false .cull_mode: SG_CULLMODE_NONE .face_winding: SG_FACEWINDING_CW - .sample_count: 1 + .sample_count: sg_desc.context.sample_count .depth_bias: 0.0f .depth_bias_slope_scale: 0.0f .depth_bias_clamp: 0.0f @@ -1752,7 +1866,7 @@ typedef struct sg_pipeline_desc { A pass object contains 1..4 color-attachments and none, or one, depth-stencil-attachment. Each attachment consists of an image, and two additional indices describing - which subimage the pass will render: one mipmap index, and + which subimage the pass will render to: one mipmap index, and if the image is a cubemap, array-texture or 3D-texture, the face-index, array-layer or depth-slice. @@ -1763,8 +1877,7 @@ typedef struct sg_pipeline_desc { - the same size - the same sample count - In addition, all color-attachment images must have the same - pixel format. + In addition, all color-attachment images must have the same pixel format. */ typedef struct sg_attachment_desc { sg_image image; @@ -1787,7 +1900,7 @@ typedef struct sg_pass_desc { /* sg_trace_hooks - Installable callback functions to keep track of the sokol_gfx calls, + Installable callback functions to keep track of the sokol-gfx calls, this is useful for debugging, or keeping track of resource creation and destruction. @@ -1892,6 +2005,8 @@ typedef struct sg_image_info { uint32_t upd_frame_index; /* frame index of last sg_update_image() */ int num_slots; /* number of renaming-slots for dynamically updated images */ int active_slot; /* currently active write-slot for dynamically updated images */ + int width; /* image width */ + int height; /* image height */ } sg_image_info; typedef struct sg_shader_info { @@ -1912,20 +2027,32 @@ typedef struct sg_pass_info { The sg_desc struct contains configuration values for sokol_gfx, it is used as parameter to the sg_setup() call. + FIXME: explain the various configuration options + The default configuration is: - .buffer_pool_size: 128 - .image_pool_size: 128 - .shader_pool_size: 32 - .pipeline_pool_size: 64 - .pass_pool_size: 16 - .context_pool_size: 16 + .buffer_pool_size 128 + .image_pool_size 128 + .shader_pool_size 32 + .pipeline_pool_size 64 + .pass_pool_size 16 + .context_pool_size 16 + .sampler_cache_size 64 + .uniform_buffer_size 4 MB (4*1024*1024) + .staging_buffer_size 8 MB (8*1024*1024) + + .context.color_format: default value depends on selected backend: + all GL backends: SG_PIXELFORMAT_RGBA8 + Metal and D3D11: SG_PIXELFORMAT_BGRA8 + WGPU: *no default* (must be queried from WGPU swapchain) + .context.depth_format SG_PIXELFORMAT_DEPTH_STENCIL + .context.sample_count 1 GL specific: - .gl_force_gles2 - if this is true the GL backend will act in "GLES2 fallback mode" even - when compiled with SOKOL_GLES3, this is useful to fall back - to traditional WebGL if a browser doesn't support a WebGL2 context + .context.gl.force_gles2 + if this is true the GL backend will act in "GLES2 fallback mode" even + when compiled with SOKOL_GLES3, this is useful to fall back + to traditional WebGL if a browser doesn't support a WebGL2 context Metal specific: (NOTE: All Objective-C object references are transferred through @@ -1935,42 +2062,89 @@ typedef struct sg_pass_info { must hold a strong reference to the Objective-C object for the duration of the sokol_gfx call! - .mtl_device - a pointer to the MTLDevice object - .mtl_renderpass_descriptor_cb - a C callback function to obtain the MTLRenderPassDescriptor for the - current frame when rendering to the default framebuffer, will be called - in sg_begin_default_pass() - .mtl_drawable_cb - a C callback function to obtain a MTLDrawable for the current - frame when rendering to the default framebuffer, will be called in - sg_end_pass() of the default pass - .mtl_global_uniform_buffer_size - the size of the global uniform buffer in bytes, this must be big - enough to hold all uniform block updates for a single frame, - the default value is 4 MByte (4 * 1024 * 1024) - .mtl_sampler_cache_size - the number of slots in the sampler cache, the Metal backend - will share texture samplers with the same state in this - cache, the default value is 64 + .context.metal.device + a pointer to the MTLDevice object + .context.metal.renderpass_descriptor_cb + a C callback function to obtain the MTLRenderPassDescriptor for the + current frame when rendering to the default framebuffer, will be called + in sg_begin_default_pass() + .context.metal.drawable_cb + a C callback function to obtain a MTLDrawable for the current + frame when rendering to the default framebuffer, will be called in + sg_end_pass() of the default pass D3D11 specific: - .d3d11_device - a pointer to the ID3D11Device object, this must have been created - before sg_setup() is called - .d3d11_device_context - a pointer to the ID3D11DeviceContext object - .d3d11_render_target_view_cb - a C callback function to obtain a pointer to the current - ID3D11RenderTargetView object of the default framebuffer, - this function will be called in sg_begin_pass() when rendering - to the default framebuffer - .d3d11_depth_stencil_view_cb - a C callback function to obtain a pointer to the current - ID3D11DepthStencilView object of the default framebuffer, - this function will be called in sg_begin_pass() when rendering - to the default framebuffer + .context.d3d11.device + a pointer to the ID3D11Device object, this must have been created + before sg_setup() is called + .context.d3d11.device_context + a pointer to the ID3D11DeviceContext object + .context.d3d11.render_target_view_cb + a C callback function to obtain a pointer to the current + ID3D11RenderTargetView object of the default framebuffer, + this function will be called in sg_begin_pass() when rendering + to the default framebuffer + .context.d3d11.depth_stencil_view_cb + a C callback function to obtain a pointer to the current + ID3D11DepthStencilView object of the default framebuffer, + this function will be called in sg_begin_pass() when rendering + to the default framebuffer + + WebGPU specific: + .context.wgpu.device + a WGPUDevice handle + .context.wgpu.render_format + WGPUTextureFormat of the swap chain surface + .context.wgpu.render_view_cb + callback to get the current WGPUTextureView of the swapchain's + rendering attachment (may be an MSAA surface) + .context.wgpu.resolve_view_cb + callback to get the current WGPUTextureView of the swapchain's + MSAA-resolve-target surface, must return 0 if not MSAA rendering + .context.wgpu.depth_stencil_view_cb + callback to get current default-pass depth-stencil-surface WGPUTextureView + the pixel format of the default WGPUTextureView must be WGPUTextureFormat_Depth24Plus8 + + When using sokol_gfx.h and sokol_app.h together, consider using the + helper function sapp_sgcontext() in the sokol_glue.h header to + initialize the sg_desc.context nested struct. sapp_sgcontext() returns + a completely initialized sg_context_desc struct with information + provided by sokol_app.h. */ +typedef struct sg_gl_context_desc { + bool force_gles2; +} sg_gl_context_desc; + +typedef struct sg_mtl_context_desc { + const void* device; + const void* (*renderpass_descriptor_cb)(void); + const void* (*drawable_cb)(void); +} sg_metal_context_desc; + +typedef struct sg_d3d11_context_desc { + const void* device; + const void* device_context; + const void* (*render_target_view_cb)(void); + const void* (*depth_stencil_view_cb)(void); +} sg_d3d11_context_desc; + +typedef struct sg_wgpu_context_desc { + const void* device; /* WGPUDevice */ + const void* (*render_view_cb)(void); /* returns WGPUTextureView */ + const void* (*resolve_view_cb)(void); /* returns WGPUTextureView */ + const void* (*depth_stencil_view_cb)(void); /* returns WGPUTextureView, must be WGPUTextureFormat_Depth24Plus8 */ +} sg_wgpu_context_desc; + +typedef struct sg_context_desc { + sg_pixel_format color_format; + sg_pixel_format depth_format; + int sample_count; + sg_gl_context_desc gl; + sg_metal_context_desc metal; + sg_d3d11_context_desc d3d11; + sg_wgpu_context_desc wgpu; +} sg_context_desc; + typedef struct sg_desc { uint32_t _start_canary; int buffer_pool_size; @@ -1979,19 +2153,10 @@ typedef struct sg_desc { int pipeline_pool_size; int pass_pool_size; int context_pool_size; - /* GL specific */ - bool gl_force_gles2; - /* Metal-specific */ - const void* mtl_device; - const void* (*mtl_renderpass_descriptor_cb)(void); - const void* (*mtl_drawable_cb)(void); - int mtl_global_uniform_buffer_size; - int mtl_sampler_cache_size; - /* D3D11-specific */ - const void* d3d11_device; - const void* d3d11_device_context; - const void* (*d3d11_render_target_view_cb)(void); - const void* (*d3d11_depth_stencil_view_cb)(void); + int uniform_buffer_size; + int staging_buffer_size; + int sampler_cache_size; + sg_context_desc context; uint32_t _end_canary; } sg_desc; @@ -2079,11 +2244,47 @@ SOKOL_API_DECL sg_context sg_setup_context(void); SOKOL_API_DECL void sg_activate_context(sg_context ctx_id); SOKOL_API_DECL void sg_discard_context(sg_context ctx_id); +/* Backend-specific helper functions, these may come in handy for mixing + sokol-gfx rendering with 'native backend' rendering functions. + + This group of functions will be expanded as needed. +*/ + +/* Metal: return __bridge-casted MTLRenderCommandEncoder in current pass (or zero if outside pass) */ +SOKOL_API_DECL const void* sg_mtl_render_command_encoder(void); + #ifdef _MSC_VER #pragma warning(pop) #endif #ifdef __cplusplus } /* extern "C" */ + +/* reference-based equivalents for c++ */ +inline void sg_setup(const sg_desc& desc) { return sg_setup(&desc); } + +inline sg_buffer sg_make_buffer(const sg_buffer_desc& desc) { return sg_make_buffer(&desc); } +inline sg_image sg_make_image(const sg_image_desc& desc) { return sg_make_image(&desc); } +inline sg_shader sg_make_shader(const sg_shader_desc& desc) { return sg_make_shader(&desc); } +inline sg_pipeline sg_make_pipeline(const sg_pipeline_desc& desc) { return sg_make_pipeline(&desc); } +inline sg_pass sg_make_pass(const sg_pass_desc& desc) { return sg_make_pass(&desc); } +inline void sg_update_image(sg_image img, const sg_image_content& data) { return sg_update_image(img, &data); } + +inline void sg_begin_default_pass(const sg_pass_action& pass_action, int width, int height) { return sg_begin_default_pass(&pass_action, width, height); } +inline void sg_begin_pass(sg_pass pass, const sg_pass_action& pass_action) { return sg_begin_pass(pass, &pass_action); } +inline void sg_apply_bindings(const sg_bindings& bindings) { return sg_apply_bindings(&bindings); } + +inline sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc& desc) { return sg_query_buffer_defaults(&desc); } +inline sg_image_desc sg_query_image_defaults(const sg_image_desc& desc) { return sg_query_image_defaults(&desc); } +inline sg_shader_desc sg_query_shader_defaults(const sg_shader_desc& desc) { return sg_query_shader_defaults(&desc); } +inline sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc& desc) { return sg_query_pipeline_defaults(&desc); } +inline sg_pass_desc sg_query_pass_defaults(const sg_pass_desc& desc) { return sg_query_pass_defaults(&desc); } + +inline void sg_init_buffer(sg_buffer buf_id, const sg_buffer_desc& desc) { return sg_init_buffer(buf_id, &desc); } +inline void sg_init_image(sg_image img_id, const sg_image_desc& desc) { return sg_init_image(img_id, &desc); } +inline void sg_init_shader(sg_shader shd_id, const sg_shader_desc& desc) { return sg_init_shader(shd_id, &desc); } +inline void sg_init_pipeline(sg_pipeline pip_id, const sg_pipeline_desc& desc) { return sg_init_pipeline(pip_id, &desc); } +inline void sg_init_pass(sg_pass pass_id, const sg_pass_desc& desc) { return sg_init_pass(pass_id, &desc); } + #endif #endif // SOKOL_GFX_INCLUDED @@ -2091,8 +2292,8 @@ SOKOL_API_DECL void sg_discard_context(sg_context ctx_id); #ifdef SOKOL_IMPL #define SOKOL_GFX_IMPL_INCLUDED (1) -#if !(defined(SOKOL_GLCORE33)||defined(SOKOL_GLES2)||defined(SOKOL_GLES3)||defined(SOKOL_D3D11)||defined(SOKOL_METAL)||defined(SOKOL_DUMMY_BACKEND)) -#error "Please select a backend with SOKOL_GLCORE33, SOKOL_GLES2, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL or SOKOL_DUMMY_BACKEND" +#if !(defined(SOKOL_GLCORE33)||defined(SOKOL_GLES2)||defined(SOKOL_GLES3)||defined(SOKOL_D3D11)||defined(SOKOL_METAL)||defined(SOKOL_WGPU)||defined(SOKOL_DUMMY_BACKEND)) +#error "Please select a backend with SOKOL_GLCORE33, SOKOL_GLES2, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND" #endif #include /* memset */ #include /* FLT_MAX */ @@ -2136,7 +2337,7 @@ SOKOL_API_DECL void sg_discard_context(sg_context ctx_id); #endif #ifndef _SOKOL_PRIVATE - #if defined(__GNUC__) + #if defined(__GNUC__) || defined(__clang__) #define _SOKOL_PRIVATE __attribute__((unused)) static #else #define _SOKOL_PRIVATE static @@ -2183,6 +2384,8 @@ SOKOL_API_DECL void sg_discard_context(sg_context ctx_id); #endif #if defined(SOKOL_GLCORE33) || defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + #define _SOKOL_ANY_GL (1) + #ifndef GL_UNSIGNED_INT_2_10_10_10_REV #define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 #endif @@ -2303,9 +2506,12 @@ SOKOL_API_DECL void sg_discard_context(sg_context ctx_id); #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif - #include + #ifndef NOMINMAX + #define NOMINMAX + #endif #include #include + #ifdef _MSC_VER #if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) #pragma comment (lib, "WindowsApp.lib") #else @@ -2314,9 +2520,13 @@ SOKOL_API_DECL void sg_discard_context(sg_context ctx_id); #pragma comment (lib, "d3d11.lib") #pragma comment (lib, "dxguid.lib") #endif + #endif #elif defined(SOKOL_METAL) - #if !__has_feature(objc_arc) - #error "Please enable ARC when using the Metal backend" + // see https://clang.llvm.org/docs/LanguageExtensions.html#automatic-reference-counting + #if !defined(__cplusplus) + #if __has_feature(objc_arc) && !__has_feature(objc_arc_fields) + #error "sokol_app.h requires __has_feature(objc_arc_field) if ARC is enabled (use a more recent compiler version)" + #endif #endif #include #import @@ -2328,9 +2538,15 @@ SOKOL_API_DECL void sg_discard_context(sg_context ctx_id); #define _SG_TARGET_IOS_SIMULATOR (1) #endif #endif +#elif defined(SOKOL_WGPU) + #if defined(__EMSCRIPTEN__) + #include + #else + #include + #endif #endif -/*=== PRIVATE DECLS ==========================================================*/ +/*=== COMMON BACKEND STUFF ===================================================*/ /* resource pool slots */ typedef struct { @@ -2351,8 +2567,9 @@ enum { _SG_DEFAULT_PIPELINE_POOL_SIZE = 64, _SG_DEFAULT_PASS_POOL_SIZE = 16, _SG_DEFAULT_CONTEXT_POOL_SIZE = 16, - _SG_MTL_DEFAULT_UB_SIZE = 4 * 1024 * 1024, - _SG_MTL_DEFAULT_SAMPLER_CACHE_CAPACITY = 64, + _SG_DEFAULT_SAMPLER_CACHE_CAPACITY = 64, + _SG_DEFAULT_UB_SIZE = 4 * 1024 * 1024, + _SG_DEFAULT_STAGING_SIZE = 8 * 1024 * 1024, }; /* fixed-size string */ @@ -2368,10 +2585,7 @@ typedef struct { #define _sg_clamp(v,v0,v1) ((vv1)?(v1):(v))) #define _sg_fequal(val,cmp,delta) (((val-cmp)> -delta)&&((val-cmp)size = desc->size; + cmn->append_pos = 0; + cmn->append_overflow = false; + cmn->type = desc->type; + cmn->usage = desc->usage; + cmn->update_frame_index = 0; + cmn->append_frame_index = 0; + cmn->num_slots = (cmn->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; + cmn->active_slot = 0; +} typedef struct { - _sg_slot_t slot; - sg_image_type type; - bool render_target; - int width; - int height; - int depth; - int num_mipmaps; - sg_usage usage; - sg_pixel_format pixel_format; - int sample_count; - sg_filter min_filter; - sg_filter mag_filter; - sg_wrap wrap_u; - sg_wrap wrap_v; - sg_wrap wrap_w; - uint32_t max_anisotropy; - uint32_t upd_frame_index; - int num_slots; - int active_slot; -} _sg_image_t; - -typedef struct { - int size; -} _sg_uniform_block_t; - -typedef struct { - sg_image_type type; -} _sg_shader_image_t; - -typedef struct { - int num_uniform_blocks; - int num_images; - _sg_uniform_block_t uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; - _sg_shader_image_t images[SG_MAX_SHADERSTAGE_IMAGES]; -} _sg_shader_stage_t; - -typedef struct { - _sg_slot_t slot; - _sg_shader_stage_t stage[SG_NUM_SHADER_STAGES]; -} _sg_shader_t; - -typedef struct { - _sg_slot_t slot; - _sg_shader_t* shader; - sg_shader shader_id; - bool vertex_layout_valid[SG_MAX_SHADERSTAGE_BUFFERS]; - int color_attachment_count; - sg_pixel_format color_format; - sg_pixel_format depth_format; - int sample_count; - float depth_bias; - float depth_bias_slope_scale; - float depth_bias_clamp; - sg_index_type index_type; - float blend_color[4]; -} _sg_pipeline_t; - -typedef struct { - _sg_image_t* image; - sg_image image_id; - int mip_level; - int slice; -} _sg_attachment_t; - -typedef struct { - _sg_slot_t slot; - int num_color_atts; - _sg_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; - _sg_attachment_t ds_att; -} _sg_pass_t; - -typedef struct { - _sg_slot_t slot; -} _sg_context_t; - -/*== GL BACKEND DECLARATIONS =================================================*/ -#elif defined(SOKOL_GLCORE33) || defined(SOKOL_GLES2) || defined(SOKOL_GLES3) -typedef struct { - _sg_slot_t slot; - int size; - int append_pos; - bool append_overflow; - sg_buffer_type type; - sg_usage usage; - uint32_t update_frame_index; - uint32_t append_frame_index; - int num_slots; - int active_slot; - GLuint gl_buf[SG_NUM_INFLIGHT_FRAMES]; - bool ext_buffers; /* if true, external buffers were injected with sg_buffer_desc.gl_buffers */ -} _sg_buffer_t; - -typedef struct { - _sg_slot_t slot; sg_image_type type; bool render_target; int width; @@ -2494,39 +2626,42 @@ typedef struct { sg_wrap wrap_w; sg_border_color border_color; uint32_t max_anisotropy; - GLenum gl_target; - GLuint gl_depth_render_buffer; - GLuint gl_msaa_render_buffer; uint32_t upd_frame_index; int num_slots; int active_slot; - GLuint gl_tex[SG_NUM_INFLIGHT_FRAMES]; - bool ext_textures; /* if true, external textures were injected with sg_image_desc.gl_textures */ -} _sg_image_t; +} _sg_image_common_t; -typedef struct { - GLint gl_loc; - sg_uniform_type type; - uint8_t count; - uint16_t offset; -} _sg_uniform_t; +_SOKOL_PRIVATE void _sg_image_common_init(_sg_image_common_t* cmn, const sg_image_desc* desc) { + cmn->type = desc->type; + cmn->render_target = desc->render_target; + cmn->width = desc->width; + cmn->height = desc->height; + cmn->depth = desc->depth; + cmn->num_mipmaps = desc->num_mipmaps; + cmn->usage = desc->usage; + cmn->pixel_format = desc->pixel_format; + cmn->sample_count = desc->sample_count; + cmn->min_filter = desc->min_filter; + cmn->mag_filter = desc->mag_filter; + cmn->wrap_u = desc->wrap_u; + cmn->wrap_v = desc->wrap_v; + cmn->wrap_w = desc->wrap_w; + cmn->border_color = desc->border_color; + cmn->max_anisotropy = desc->max_anisotropy; + cmn->upd_frame_index = 0; + cmn->num_slots = (cmn->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; + cmn->active_slot = 0; +} typedef struct { int size; - int num_uniforms; - _sg_uniform_t uniforms[SG_MAX_UB_MEMBERS]; } _sg_uniform_block_t; typedef struct { sg_image_type type; - GLint gl_loc; - int gl_tex_slot; + sg_sampler_type sampler_type; } _sg_shader_image_t; -typedef struct { - _sg_str_t name; -} _sg_shader_attr_t; - typedef struct { int num_uniform_blocks; int num_images; @@ -2535,11 +2670,307 @@ typedef struct { } _sg_shader_stage_t; typedef struct { - _sg_slot_t slot; - GLuint gl_prog; - _sg_shader_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES]; _sg_shader_stage_t stage[SG_NUM_SHADER_STAGES]; -} _sg_shader_t; +} _sg_shader_common_t; + +_SOKOL_PRIVATE void _sg_shader_common_init(_sg_shader_common_t* cmn, const sg_shader_desc* desc) { + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS) ? &desc->vs : &desc->fs; + _sg_shader_stage_t* stage = &cmn->stage[stage_index]; + SOKOL_ASSERT(stage->num_uniform_blocks == 0); + for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { + const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; + if (0 == ub_desc->size) { + break; + } + stage->uniform_blocks[ub_index].size = ub_desc->size; + stage->num_uniform_blocks++; + } + SOKOL_ASSERT(stage->num_images == 0); + for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { + const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; + if (img_desc->type == _SG_IMAGETYPE_DEFAULT) { + break; + } + stage->images[img_index].type = img_desc->type; + stage->images[img_index].sampler_type = img_desc->sampler_type; + stage->num_images++; + } + } +} + +typedef struct { + sg_shader shader_id; + sg_index_type index_type; + bool vertex_layout_valid[SG_MAX_SHADERSTAGE_BUFFERS]; + int color_attachment_count; + sg_pixel_format color_format; + sg_pixel_format depth_format; + int sample_count; + float depth_bias; + float depth_bias_slope_scale; + float depth_bias_clamp; + float blend_color[4]; +} _sg_pipeline_common_t; + +_SOKOL_PRIVATE void _sg_pipeline_common_init(_sg_pipeline_common_t* cmn, const sg_pipeline_desc* desc) { + cmn->shader_id = desc->shader; + cmn->index_type = desc->index_type; + for (int i = 0; i < SG_MAX_SHADERSTAGE_BUFFERS; i++) { + cmn->vertex_layout_valid[i] = false; + } + cmn->color_attachment_count = desc->blend.color_attachment_count; + cmn->color_format = desc->blend.color_format; + cmn->depth_format = desc->blend.depth_format; + cmn->sample_count = desc->rasterizer.sample_count; + cmn->depth_bias = desc->rasterizer.depth_bias; + cmn->depth_bias_slope_scale = desc->rasterizer.depth_bias_slope_scale; + cmn->depth_bias_clamp = desc->rasterizer.depth_bias_clamp; + for (int i = 0; i < 4; i++) { + cmn->blend_color[i] = desc->blend.blend_color[i]; + } +} + +typedef struct { + sg_image image_id; + int mip_level; + int slice; +} _sg_attachment_common_t; + +typedef struct { + int num_color_atts; + _sg_attachment_common_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; + _sg_attachment_common_t ds_att; +} _sg_pass_common_t; + +_SOKOL_PRIVATE void _sg_pass_common_init(_sg_pass_common_t* cmn, const sg_pass_desc* desc) { + const sg_attachment_desc* att_desc; + _sg_attachment_common_t* att; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + att_desc = &desc->color_attachments[i]; + if (att_desc->image.id != SG_INVALID_ID) { + cmn->num_color_atts++; + att = &cmn->color_atts[i]; + att->image_id = att_desc->image; + att->mip_level = att_desc->mip_level; + att->slice = att_desc->slice; + } + } + att_desc = &desc->depth_stencil_attachment; + if (att_desc->image.id != SG_INVALID_ID) { + att = &cmn->ds_att; + att->image_id = att_desc->image; + att->mip_level = att_desc->mip_level; + att->slice = att_desc->slice; + } +} + +/*=== GENERIC SAMPLER CACHE ==================================================*/ + +/* + this is used by the Metal and WGPU backends to reduce the + number of sampler state objects created through the backend API +*/ +typedef struct { + sg_filter min_filter; + sg_filter mag_filter; + sg_wrap wrap_u; + sg_wrap wrap_v; + sg_wrap wrap_w; + sg_border_color border_color; + uint32_t max_anisotropy; + int min_lod; /* orig min/max_lod is float, this is int(min/max_lod*1000.0) */ + int max_lod; + uintptr_t sampler_handle; +} _sg_sampler_cache_item_t; + +typedef struct { + int capacity; + int num_items; + _sg_sampler_cache_item_t* items; +} _sg_sampler_cache_t; + +_SOKOL_PRIVATE void _sg_smpcache_init(_sg_sampler_cache_t* cache, int capacity) { + SOKOL_ASSERT(cache && (capacity > 0)); + memset(cache, 0, sizeof(_sg_sampler_cache_t)); + cache->capacity = capacity; + const int size = cache->capacity * sizeof(_sg_sampler_cache_item_t); + cache->items = (_sg_sampler_cache_item_t*) SOKOL_MALLOC(size); + memset(cache->items, 0, size); +} + +_SOKOL_PRIVATE void _sg_smpcache_discard(_sg_sampler_cache_t* cache) { + SOKOL_ASSERT(cache && cache->items); + SOKOL_FREE(cache->items); + cache->items = 0; + cache->num_items = 0; + cache->capacity = 0; +} + +_SOKOL_PRIVATE int _sg_smpcache_minlod_int(float min_lod) { + return (int) (min_lod * 1000.0f); +} + +_SOKOL_PRIVATE int _sg_smpcache_maxlod_int(float max_lod) { + return (int) (_sg_clamp(max_lod, 0.0f, 1000.0f) * 1000.0f); +} + +_SOKOL_PRIVATE int _sg_smpcache_find_item(const _sg_sampler_cache_t* cache, const sg_image_desc* img_desc) { + /* return matching sampler cache item index or -1 */ + SOKOL_ASSERT(cache && cache->items); + SOKOL_ASSERT(img_desc); + const int min_lod = _sg_smpcache_minlod_int(img_desc->min_lod); + const int max_lod = _sg_smpcache_maxlod_int(img_desc->max_lod); + for (int i = 0; i < cache->num_items; i++) { + const _sg_sampler_cache_item_t* item = &cache->items[i]; + if ((img_desc->min_filter == item->min_filter) && + (img_desc->mag_filter == item->mag_filter) && + (img_desc->wrap_u == item->wrap_u) && + (img_desc->wrap_v == item->wrap_v) && + (img_desc->wrap_w == item->wrap_w) && + (img_desc->max_anisotropy == item->max_anisotropy) && + (img_desc->border_color == item->border_color) && + (min_lod == item->min_lod) && + (max_lod == item->max_lod)) + { + return i; + } + } + /* fallthrough: no matching cache item found */ + return -1; +} + +_SOKOL_PRIVATE void _sg_smpcache_add_item(_sg_sampler_cache_t* cache, const sg_image_desc* img_desc, uintptr_t sampler_handle) { + SOKOL_ASSERT(cache && cache->items); + SOKOL_ASSERT(img_desc); + SOKOL_ASSERT(cache->num_items < cache->capacity); + const int item_index = cache->num_items++; + _sg_sampler_cache_item_t* item = &cache->items[item_index]; + item->min_filter = img_desc->min_filter; + item->mag_filter = img_desc->mag_filter; + item->wrap_u = img_desc->wrap_u; + item->wrap_v = img_desc->wrap_v; + item->wrap_w = img_desc->wrap_w; + item->border_color = img_desc->border_color; + item->max_anisotropy = img_desc->max_anisotropy; + item->min_lod = _sg_smpcache_minlod_int(img_desc->min_lod); + item->max_lod = _sg_smpcache_maxlod_int(img_desc->max_lod); + item->sampler_handle = sampler_handle; +} + +_SOKOL_PRIVATE uintptr_t _sg_smpcache_sampler(_sg_sampler_cache_t* cache, int item_index) { + SOKOL_ASSERT(cache && cache->items); + SOKOL_ASSERT((item_index >= 0) && (item_index < cache->num_items)); + return cache->items[item_index].sampler_handle; +} + +/*=== DUMMY BACKEND DECLARATIONS =============================================*/ +#if defined(SOKOL_DUMMY_BACKEND) +typedef struct { + _sg_slot_t slot; + _sg_buffer_common_t cmn; +} _sg_dummy_buffer_t; +typedef _sg_dummy_buffer_t _sg_buffer_t; + +typedef struct { + _sg_slot_t slot; + _sg_image_common_t cmn; +} _sg_dummy_image_t; +typedef _sg_dummy_image_t _sg_image_t; + +typedef struct { + _sg_slot_t slot; + _sg_shader_common_t cmn; +} _sg_dummy_shader_t; +typedef _sg_dummy_shader_t _sg_shader_t; + +typedef struct { + _sg_slot_t slot; + _sg_shader_t* shader; + _sg_pipeline_common_t cmn; +} _sg_dummy_pipeline_t; +typedef _sg_dummy_pipeline_t _sg_pipeline_t; + +typedef struct { + _sg_image_t* image; +} _sg_dummy_attachment_t; + +typedef struct { + _sg_slot_t slot; + _sg_pass_common_t cmn; + struct { + _sg_dummy_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; + _sg_dummy_attachment_t ds_att; + } dmy; +} _sg_dummy_pass_t; +typedef _sg_dummy_pass_t _sg_pass_t; +typedef _sg_attachment_common_t _sg_attachment_t; + +typedef struct { + _sg_slot_t slot; +} _sg_dummy_context_t; +typedef _sg_dummy_context_t _sg_context_t; + +/*== GL BACKEND DECLARATIONS =================================================*/ +#elif defined(_SOKOL_ANY_GL) +typedef struct { + _sg_slot_t slot; + _sg_buffer_common_t cmn; + struct { + GLuint buf[SG_NUM_INFLIGHT_FRAMES]; + bool ext_buffers; /* if true, external buffers were injected with sg_buffer_desc.gl_buffers */ + } gl; +} _sg_gl_buffer_t; +typedef _sg_gl_buffer_t _sg_buffer_t; + +typedef struct { + _sg_slot_t slot; + _sg_image_common_t cmn; + struct { + GLenum target; + GLuint depth_render_buffer; + GLuint msaa_render_buffer; + GLuint tex[SG_NUM_INFLIGHT_FRAMES]; + bool ext_textures; /* if true, external textures were injected with sg_image_desc.gl_textures */ + } gl; +} _sg_gl_image_t; +typedef _sg_gl_image_t _sg_image_t; + +typedef struct { + GLint gl_loc; + sg_uniform_type type; + uint8_t count; + uint16_t offset; +} _sg_gl_uniform_t; + +typedef struct { + int num_uniforms; + _sg_gl_uniform_t uniforms[SG_MAX_UB_MEMBERS]; +} _sg_gl_uniform_block_t; + +typedef struct { + int gl_tex_slot; +} _sg_gl_shader_image_t; + +typedef struct { + _sg_str_t name; +} _sg_gl_shader_attr_t; + +typedef struct { + _sg_gl_uniform_block_t uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; + _sg_gl_shader_image_t images[SG_MAX_SHADERSTAGE_IMAGES]; +} _sg_gl_shader_stage_t; + +typedef struct { + _sg_slot_t slot; + _sg_shader_common_t cmn; + struct { + GLuint prog; + _sg_gl_shader_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES]; + _sg_gl_shader_stage_t stage[SG_NUM_SHADER_STAGES]; + } gl; +} _sg_gl_shader_t; +typedef _sg_gl_shader_t _sg_shader_t; typedef struct { int8_t vb_index; /* -1 if attr is not enabled */ @@ -2553,36 +2984,34 @@ typedef struct { typedef struct { _sg_slot_t slot; + _sg_pipeline_common_t cmn; _sg_shader_t* shader; - sg_shader shader_id; - sg_primitive_type primitive_type; - sg_index_type index_type; - bool vertex_layout_valid[SG_MAX_SHADERSTAGE_BUFFERS]; - int color_attachment_count; - sg_pixel_format color_format; - sg_pixel_format depth_format; - int sample_count; - _sg_gl_attr_t gl_attrs[SG_MAX_VERTEX_ATTRIBUTES]; - sg_depth_stencil_state depth_stencil; - sg_blend_state blend; - sg_rasterizer_state rast; -} _sg_pipeline_t; + struct { + _sg_gl_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES]; + sg_depth_stencil_state depth_stencil; + sg_primitive_type primitive_type; + sg_blend_state blend; + sg_rasterizer_state rast; + } gl; +} _sg_gl_pipeline_t; +typedef _sg_gl_pipeline_t _sg_pipeline_t; typedef struct { _sg_image_t* image; - sg_image image_id; - int mip_level; - int slice; GLuint gl_msaa_resolve_buffer; -} _sg_attachment_t; +} _sg_gl_attachment_t; typedef struct { _sg_slot_t slot; - GLuint gl_fb; - int num_color_atts; - _sg_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; - _sg_attachment_t ds_att; -} _sg_pass_t; + _sg_pass_common_t cmn; + struct { + GLuint fb; + _sg_gl_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; + _sg_gl_attachment_t ds_att; + } gl; +} _sg_gl_pass_t; +typedef _sg_gl_pass_t _sg_pass_t; +typedef _sg_attachment_common_t _sg_attachment_t; typedef struct { _sg_slot_t slot; @@ -2590,7 +3019,8 @@ typedef struct { GLuint vao; #endif GLuint default_framebuffer; -} _sg_context_t; +} _sg_gl_context_t; +typedef _sg_gl_context_t _sg_context_t; typedef struct { _sg_gl_attr_t gl_attr; @@ -2612,11 +3042,13 @@ typedef struct { GLuint index_buffer; GLuint stored_vertex_buffer; GLuint stored_index_buffer; + GLuint prog; _sg_gl_texture_bind_slot textures[SG_MAX_SHADERSTAGE_IMAGES]; _sg_gl_texture_bind_slot stored_texture; int cur_ib_offset; GLenum cur_primitive_type; GLenum cur_index_type; + GLenum cur_active_texture; _sg_pipeline_t* cur_pipeline; sg_pipeline cur_pipeline_id; } _sg_gl_state_cache_t; @@ -2641,116 +3073,93 @@ typedef struct { typedef struct { _sg_slot_t slot; - int size; - int append_pos; - bool append_overflow; - sg_buffer_type type; - sg_usage usage; - uint32_t update_frame_index; - uint32_t append_frame_index; - ID3D11Buffer* d3d11_buf; -} _sg_buffer_t; + _sg_buffer_common_t cmn; + struct { + ID3D11Buffer* buf; + } d3d11; +} _sg_d3d11_buffer_t; +typedef _sg_d3d11_buffer_t _sg_buffer_t; typedef struct { _sg_slot_t slot; - sg_image_type type; - bool render_target; - int width; - int height; - int depth; - int num_mipmaps; - sg_usage usage; - sg_pixel_format pixel_format; - int sample_count; - sg_filter min_filter; - sg_filter mag_filter; - sg_wrap wrap_u; - sg_wrap wrap_v; - sg_wrap wrap_w; - sg_border_color border_color; - uint32_t max_anisotropy; - uint32_t upd_frame_index; - DXGI_FORMAT d3d11_format; - ID3D11Texture2D* d3d11_tex2d; - ID3D11Texture3D* d3d11_tex3d; - ID3D11Texture2D* d3d11_texds; - ID3D11Texture2D* d3d11_texmsaa; - ID3D11ShaderResourceView* d3d11_srv; - ID3D11SamplerState* d3d11_smp; -} _sg_image_t; - -typedef struct { - int size; -} _sg_uniform_block_t; - -typedef struct { - sg_image_type type; -} _sg_shader_image_t; + _sg_image_common_t cmn; + struct { + DXGI_FORMAT format; + ID3D11Texture2D* tex2d; + ID3D11Texture3D* tex3d; + ID3D11Texture2D* texds; + ID3D11Texture2D* texmsaa; + ID3D11ShaderResourceView* srv; + ID3D11SamplerState* smp; + } d3d11; +} _sg_d3d11_image_t; +typedef _sg_d3d11_image_t _sg_image_t; typedef struct { _sg_str_t sem_name; int sem_index; -} _sg_shader_attr_t; +} _sg_d3d11_shader_attr_t; typedef struct { - int num_uniform_blocks; - int num_images; - _sg_shader_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES]; - _sg_uniform_block_t uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; - _sg_shader_image_t images[SG_MAX_SHADERSTAGE_IMAGES]; - ID3D11Buffer* d3d11_cbs[SG_MAX_SHADERSTAGE_UBS]; -} _sg_shader_stage_t; + ID3D11Buffer* cbufs[SG_MAX_SHADERSTAGE_UBS]; +} _sg_d3d11_shader_stage_t; typedef struct { _sg_slot_t slot; - _sg_shader_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES]; - _sg_shader_stage_t stage[SG_NUM_SHADER_STAGES]; - ID3D11VertexShader* d3d11_vs; - ID3D11PixelShader* d3d11_fs; - void* d3d11_vs_blob; - int d3d11_vs_blob_length; -} _sg_shader_t; + _sg_shader_common_t cmn; + struct { + _sg_d3d11_shader_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES]; + _sg_d3d11_shader_stage_t stage[SG_NUM_SHADER_STAGES]; + ID3D11VertexShader* vs; + ID3D11PixelShader* fs; + void* vs_blob; + int vs_blob_length; + } d3d11; +} _sg_d3d11_shader_t; +typedef _sg_d3d11_shader_t _sg_shader_t; typedef struct { _sg_slot_t slot; + _sg_pipeline_common_t cmn; _sg_shader_t* shader; - sg_shader shader_id; - sg_index_type index_type; - bool vertex_layout_valid[SG_MAX_SHADERSTAGE_BUFFERS]; - int color_attachment_count; - sg_pixel_format color_format; - sg_pixel_format depth_format; - int sample_count; - float blend_color[4]; - UINT d3d11_stencil_ref; - UINT d3d11_vb_strides[SG_MAX_SHADERSTAGE_BUFFERS]; - D3D_PRIMITIVE_TOPOLOGY d3d11_topology; - DXGI_FORMAT d3d11_index_format; - ID3D11InputLayout* d3d11_il; - ID3D11RasterizerState* d3d11_rs; - ID3D11DepthStencilState* d3d11_dss; - ID3D11BlendState* d3d11_bs; -} _sg_pipeline_t; + struct { + UINT stencil_ref; + UINT vb_strides[SG_MAX_SHADERSTAGE_BUFFERS]; + D3D_PRIMITIVE_TOPOLOGY topology; + DXGI_FORMAT index_format; + ID3D11InputLayout* il; + ID3D11RasterizerState* rs; + ID3D11DepthStencilState* dss; + ID3D11BlendState* bs; + } d3d11; +} _sg_d3d11_pipeline_t; +typedef _sg_d3d11_pipeline_t _sg_pipeline_t; typedef struct { _sg_image_t* image; - sg_image image_id; - int mip_level; - int slice; -} _sg_attachment_t; + ID3D11RenderTargetView* rtv; +} _sg_d3d11_color_attachment_t; + +typedef struct { + _sg_image_t* image; + ID3D11DepthStencilView* dsv; +} _sg_d3d11_ds_attachment_t; typedef struct { _sg_slot_t slot; - int num_color_atts; - _sg_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; - _sg_attachment_t ds_att; - ID3D11RenderTargetView* d3d11_rtvs[SG_MAX_COLOR_ATTACHMENTS]; - ID3D11DepthStencilView* d3d11_dsv; -} _sg_pass_t; + _sg_pass_common_t cmn; + struct { + _sg_d3d11_color_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; + _sg_d3d11_ds_attachment_t ds_att; + } d3d11; +} _sg_d3d11_pass_t; +typedef _sg_d3d11_pass_t _sg_pass_t; +typedef _sg_attachment_common_t _sg_attachment_t; typedef struct { _sg_slot_t slot; -} _sg_context_t; +} _sg_d3d11_context_t; +typedef _sg_d3d11_context_t _sg_context_t; typedef struct { bool valid; @@ -2788,24 +3197,20 @@ typedef struct { /*=== METAL BACKEND DECLARATIONS =============================================*/ #elif defined(SOKOL_METAL) -enum { - #if defined(_SG_TARGET_MACOS) - _SG_MTL_UB_ALIGN = 256, - #else - _SG_MTL_UB_ALIGN = 16, - #endif - _SG_MTL_INVALID_SLOT_INDEX = 0 -}; +#if defined(_SG_TARGET_MACOS) || defined(_SG_TARGET_IOS_SIMULATOR) +#define _SG_MTL_UB_ALIGN (256) +#else +#define _SG_MTL_UB_ALIGN (16) +#endif +#define _SG_MTL_INVALID_SLOT_INDEX (0) -/* note that there's a free-standing _sg_mtl_idpool NSMutableArray, - this can't be part of a C struct before Xcode10.x -*/ typedef struct { uint32_t frame_index; /* frame index at which it is safe to release this resource */ uint32_t slot_index; } _sg_mtl_release_item_t; typedef struct { + NSMutableArray* pool; uint32_t num_slots; uint32_t free_queue_top; uint32_t* free_queue; @@ -2814,129 +3219,77 @@ typedef struct { _sg_mtl_release_item_t* release_queue; } _sg_mtl_idpool_t; -/* Metal sampler cache */ typedef struct { - sg_filter min_filter; - sg_filter mag_filter; - sg_wrap wrap_u; - sg_wrap wrap_v; - sg_wrap wrap_w; - sg_border_color border_color; - uint32_t max_anisotropy; - int min_lod; /* orig min/max_lod is float, this is int(min/max_lod*1000.0) */ - int max_lod; - uint32_t mtl_sampler_state; -} _sg_mtl_sampler_cache_item_t; - -typedef struct { - int capacity; - int num_items; - _sg_mtl_sampler_cache_item_t* items; -} _sg_mtl_sampler_cache_t; + _sg_slot_t slot; + _sg_buffer_common_t cmn; + struct { + uint32_t buf[SG_NUM_INFLIGHT_FRAMES]; /* index into _sg_mtl_pool */ + } mtl; +} _sg_mtl_buffer_t; +typedef _sg_mtl_buffer_t _sg_buffer_t; typedef struct { _sg_slot_t slot; - int size; - int append_pos; - bool append_overflow; - sg_buffer_type type; - sg_usage usage; - uint32_t update_frame_index; - uint32_t append_frame_index; - int num_slots; - int active_slot; - uint32_t mtl_buf[SG_NUM_INFLIGHT_FRAMES]; /* index intp _sg_mtl_pool */ -} _sg_buffer_t; + _sg_image_common_t cmn; + struct { + uint32_t tex[SG_NUM_INFLIGHT_FRAMES]; + uint32_t depth_tex; + uint32_t msaa_tex; + uint32_t sampler_state; + } mtl; +} _sg_mtl_image_t; +typedef _sg_mtl_image_t _sg_image_t; typedef struct { - _sg_slot_t slot; - sg_image_type type; - bool render_target; - int width; - int height; - int depth; - int num_mipmaps; - sg_usage usage; - sg_pixel_format pixel_format; - int sample_count; - sg_filter min_filter; - sg_filter mag_filter; - sg_wrap wrap_u; - sg_wrap wrap_v; - sg_wrap wrap_w; - uint32_t max_anisotropy; - uint32_t upd_frame_index; - int num_slots; - int active_slot; - uint32_t mtl_tex[SG_NUM_INFLIGHT_FRAMES]; - uint32_t mtl_depth_tex; - uint32_t mtl_msaa_tex; - uint32_t mtl_sampler_state; -} _sg_image_t; - -typedef struct { - int size; -} _sg_uniform_block_t; - -typedef struct { - sg_image_type type; -} _sg_shader_image_t; - -typedef struct { - int num_uniform_blocks; - int num_images; - _sg_uniform_block_t uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; - _sg_shader_image_t images[SG_MAX_SHADERSTAGE_IMAGES]; uint32_t mtl_lib; uint32_t mtl_func; -} _sg_shader_stage_t; +} _sg_mtl_shader_stage_t; typedef struct { _sg_slot_t slot; - _sg_shader_stage_t stage[SG_NUM_SHADER_STAGES]; -} _sg_shader_t; + _sg_shader_common_t cmn; + struct { + _sg_mtl_shader_stage_t stage[SG_NUM_SHADER_STAGES]; + } mtl; +} _sg_mtl_shader_t; +typedef _sg_mtl_shader_t _sg_shader_t; typedef struct { _sg_slot_t slot; + _sg_pipeline_common_t cmn; _sg_shader_t* shader; - sg_shader shader_id; - bool vertex_layout_valid[SG_MAX_SHADERSTAGE_BUFFERS]; - int color_attachment_count; - sg_pixel_format color_format; - sg_pixel_format depth_format; - int sample_count; - float depth_bias; - float depth_bias_slope_scale; - float depth_bias_clamp; - MTLPrimitiveType mtl_prim_type; - sg_index_type index_type; - NSUInteger mtl_index_size; - MTLIndexType mtl_index_type; - MTLCullMode mtl_cull_mode; - MTLWinding mtl_winding; - float blend_color[4]; - uint32_t mtl_stencil_ref; - uint32_t mtl_rps; - uint32_t mtl_dss; -} _sg_pipeline_t; + struct { + MTLPrimitiveType prim_type; + NSUInteger index_size; + MTLIndexType index_type; + MTLCullMode cull_mode; + MTLWinding winding; + uint32_t stencil_ref; + uint32_t rps; + uint32_t dss; + } mtl; +} _sg_mtl_pipeline_t; +typedef _sg_mtl_pipeline_t _sg_pipeline_t; typedef struct { _sg_image_t* image; - sg_image image_id; - int mip_level; - int slice; -} _sg_attachment_t; +} _sg_mtl_attachment_t; typedef struct { _sg_slot_t slot; - int num_color_atts; - _sg_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; - _sg_attachment_t ds_att; -} _sg_pass_t; + _sg_pass_common_t cmn; + struct { + _sg_mtl_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; + _sg_mtl_attachment_t ds_att; + } mtl; +} _sg_mtl_pass_t; +typedef _sg_mtl_pass_t _sg_pass_t; +typedef _sg_attachment_common_t _sg_attachment_t; typedef struct { _sg_slot_t slot; -} _sg_context_t; +} _sg_mtl_context_t; +typedef _sg_mtl_context_t _sg_context_t; /* resouce binding state cache */ typedef struct { @@ -2968,20 +3321,143 @@ typedef struct { int cur_width; int cur_height; _sg_mtl_state_cache_t state_cache; - _sg_mtl_sampler_cache_t sampler_cache; + _sg_sampler_cache_t sampler_cache; _sg_mtl_idpool_t idpool; + dispatch_semaphore_t sem; + id device; + id cmd_queue; + id cmd_buffer; + id cmd_encoder; + id uniform_buffers[SG_NUM_INFLIGHT_FRAMES]; } _sg_mtl_backend_t; -/* keep Objective-C 'smart data' in a separate static objects, these can't be in a C struct until Xcode10 or so */ -static NSMutableArray* _sg_mtl_idpool; -static id _sg_mtl_device; -static id _sg_mtl_cmd_queue; -static id _sg_mtl_cmd_buffer; -static id _sg_mtl_uniform_buffers[SG_NUM_INFLIGHT_FRAMES]; -static id _sg_mtl_cmd_encoder; -static dispatch_semaphore_t _sg_mtl_sem; +/*=== WGPU BACKEND DECLARATIONS ==============================================*/ +#elif defined(SOKOL_WGPU) -#endif /* SOKOL_METAL */ +#define _SG_WGPU_STAGING_ALIGN (256) +#define _SG_WGPU_STAGING_PIPELINE_SIZE (8) +#define _SG_WGPU_ROWPITCH_ALIGN (256) +#define _SG_WGPU_MAX_SHADERSTAGE_IMAGES (8) +#define _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE (1<<16) + +typedef struct { + _sg_slot_t slot; + _sg_buffer_common_t cmn; + struct { + WGPUBuffer buf; + } wgpu; +} _sg_wgpu_buffer_t; +typedef _sg_wgpu_buffer_t _sg_buffer_t; + +typedef struct { + _sg_slot_t slot; + _sg_image_common_t cmn; + struct { + WGPUTexture tex; + WGPUTextureView tex_view; + WGPUTexture msaa_tex; + WGPUSampler sampler; + } wgpu; +} _sg_wgpu_image_t; +typedef _sg_wgpu_image_t _sg_image_t; + +typedef struct { + WGPUShaderModule module; + WGPUBindGroupLayout bind_group_layout; + _sg_str_t entry; +} _sg_wgpu_shader_stage_t; + +typedef struct { + _sg_slot_t slot; + _sg_shader_common_t cmn; + struct { + _sg_wgpu_shader_stage_t stage[SG_NUM_SHADER_STAGES]; + } wgpu; +} _sg_wgpu_shader_t; +typedef _sg_wgpu_shader_t _sg_shader_t; + +typedef struct { + _sg_slot_t slot; + _sg_pipeline_common_t cmn; + _sg_shader_t* shader; + struct { + WGPURenderPipeline pip; + uint32_t stencil_ref; + } wgpu; +} _sg_wgpu_pipeline_t; +typedef _sg_wgpu_pipeline_t _sg_pipeline_t; + +typedef struct { + _sg_image_t* image; + WGPUTextureView render_tex_view; + WGPUTextureView resolve_tex_view; +} _sg_wgpu_attachment_t; + +typedef struct { + _sg_slot_t slot; + _sg_pass_common_t cmn; + struct { + _sg_wgpu_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; + _sg_wgpu_attachment_t ds_att; + } wgpu; +} _sg_wgpu_pass_t; +typedef _sg_wgpu_pass_t _sg_pass_t; +typedef _sg_attachment_common_t _sg_attachment_t; + +typedef struct { + _sg_slot_t slot; +} _sg_wgpu_context_t; +typedef _sg_wgpu_context_t _sg_context_t; + +/* a pool of per-frame uniform buffers */ +typedef struct { + WGPUBindGroupLayout bindgroup_layout; + uint32_t num_bytes; + uint32_t offset; /* current offset into current frame's mapped uniform buffer */ + uint32_t bind_offsets[SG_NUM_SHADER_STAGES][SG_MAX_SHADERSTAGE_UBS]; + WGPUBuffer buf; /* the GPU-side uniform buffer */ + WGPUBindGroup bindgroup; + struct { + int num; + int cur; + WGPUBuffer buf[_SG_WGPU_STAGING_PIPELINE_SIZE]; /* CPU-side staging buffers */ + uint8_t* ptr[_SG_WGPU_STAGING_PIPELINE_SIZE]; /* if != 0, staging buffer currently mapped */ + } stage; +} _sg_wgpu_ubpool_t; + +/* ...a similar pool (like uniform buffer pool) of dynamic-resource staging buffers */ +typedef struct { + uint32_t num_bytes; + uint32_t offset; /* current offset into current frame's staging buffer */ + int num; /* number of staging buffers */ + int cur; /* this frame's staging buffer */ + WGPUBuffer buf[_SG_WGPU_STAGING_PIPELINE_SIZE]; /* CPU-side staging buffers */ + uint8_t* ptr[_SG_WGPU_STAGING_PIPELINE_SIZE]; /* if != 0, staging buffer currently mapped */ +} _sg_wgpu_stagingpool_t; + +/* the WGPU backend state */ +typedef struct { + bool valid; + bool in_pass; + bool draw_indexed; + int cur_width; + int cur_height; + WGPUDevice dev; + WGPUTextureView (*render_view_cb)(void); + WGPUTextureView (*resolve_view_cb)(void); + WGPUTextureView (*depth_stencil_view_cb)(void); + WGPUQueue queue; + WGPUCommandEncoder render_cmd_enc; + WGPUCommandEncoder staging_cmd_enc; + WGPURenderPassEncoder pass_enc; + WGPUBindGroup empty_bind_group; + const _sg_pipeline_t* cur_pipeline; + sg_pipeline cur_pipeline_id; + _sg_sampler_cache_t sampler_cache; + _sg_wgpu_ubpool_t ub; + _sg_wgpu_stagingpool_t staging; +} _sg_wgpu_backend_t; +#endif /*=== RESOURCE POOL DECLARATIONS =============================================*/ @@ -3154,12 +3630,14 @@ typedef struct { sg_features features; sg_limits limits; sg_pixelformat_info formats[_SG_PIXELFORMAT_NUM]; - #if defined(SOKOL_GLCORE33) || defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + #if defined(_SOKOL_ANY_GL) _sg_gl_backend_t gl; #elif defined(SOKOL_METAL) _sg_mtl_backend_t mtl; #elif defined(SOKOL_D3D11) _sg_d3d11_backend_t d3d11; + #elif defined(SOKOL_WGPU) + _sg_wgpu_backend_t wgpu; #endif #if defined(SOKOL_TRACE_HOOKS) sg_trace_hooks hooks; @@ -3232,19 +3710,6 @@ _SOKOL_PRIVATE int _sg_uniform_size(sg_uniform_type type, int count) { } } -/* the default color pixelformat for render targets */ -_SOKOL_PRIVATE sg_pixel_format _sg_default_rendertarget_colorformat(void) { - #if defined(SOKOL_METAL) || defined(SOKOL_D3D11) - return SG_PIXELFORMAT_BGRA8; - #else - return SG_PIXELFORMAT_RGBA8; - #endif -} - -_SOKOL_PRIVATE sg_pixel_format _sg_default_rendertarget_depthformat(void) { - return SG_PIXELFORMAT_DEPTH_STENCIL; -} - /* return true if pixel format is a compressed format */ _SOKOL_PRIVATE bool _sg_is_compressed_pixel_format(sg_pixel_format fmt) { switch (fmt) { @@ -3350,11 +3815,13 @@ _SOKOL_PRIVATE int _sg_pixelformat_bytesize(sg_pixel_format fmt) { } } +#define _sg_roundup(val, round_to) (((val)+((round_to)-1))&~((round_to)-1)) + /* return row pitch for an image see ComputePitch in https://github.com/microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexUtil.cpp */ -_SOKOL_PRIVATE int _sg_row_pitch(sg_pixel_format fmt, int width) { - int pitch; +_SOKOL_PRIVATE uint32_t _sg_row_pitch(sg_pixel_format fmt, uint32_t width, uint32_t row_align) { + uint32_t pitch; switch (fmt) { case SG_PIXELFORMAT_BC1_RGBA: case SG_PIXELFORMAT_BC4_R: @@ -3401,14 +3868,13 @@ _SOKOL_PRIVATE int _sg_row_pitch(sg_pixel_format fmt, int width) { pitch = width * _sg_pixelformat_bytesize(fmt); break; } + pitch = _sg_roundup(pitch, row_align); return pitch; } -/* return pitch of a 2D subimage / texture slice - see ComputePitch in https://github.com/microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexUtil.cpp -*/ -_SOKOL_PRIVATE int _sg_surface_pitch(sg_pixel_format fmt, int width, int height) { - int num_rows = 0; +/* compute the number of rows in a surface depending on pixel format */ +_SOKOL_PRIVATE int _sg_num_rows(sg_pixel_format fmt, int height) { + int num_rows; switch (fmt) { case SG_PIXELFORMAT_BC1_RGBA: case SG_PIXELFORMAT_BC4_R: @@ -3438,7 +3904,15 @@ _SOKOL_PRIVATE int _sg_surface_pitch(sg_pixel_format fmt, int width, int height) if (num_rows < 1) { num_rows = 1; } - return num_rows * _sg_row_pitch(fmt, width); + return num_rows; +} + +/* return pitch of a 2D subimage / texture slice + see ComputePitch in https://github.com/microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexUtil.cpp +*/ +_SOKOL_PRIVATE uint32_t _sg_surface_pitch(sg_pixel_format fmt, uint32_t width, uint32_t height, uint32_t row_align) { + int num_rows = _sg_num_rows(fmt, height); + return num_rows * _sg_row_pitch(fmt, width, row_align); } /* capability table pixel format helper functions */ @@ -3529,7 +4003,7 @@ _SOKOL_PRIVATE void _sg_resolve_default_pass_action(const sg_pass_action* from, /*== DUMMY BACKEND IMPL ======================================================*/ #if defined(SOKOL_DUMMY_BACKEND) -_SOKOL_PRIVATE void _sg_setup_backend(const sg_desc* desc) { +_SOKOL_PRIVATE void _sg_dummy_setup_backend(const sg_desc* desc) { SOKOL_ASSERT(desc); _SOKOL_UNUSED(desc); _sg.backend = SG_BACKEND_DUMMY; @@ -3544,185 +4018,128 @@ _SOKOL_PRIVATE void _sg_setup_backend(const sg_desc* desc) { _sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL].depth = true; } -_SOKOL_PRIVATE void _sg_discard_backend(void) { +_SOKOL_PRIVATE void _sg_dummy_discard_backend(void) { /* empty */ } -_SOKOL_PRIVATE void _sg_reset_state_cache(void) { +_SOKOL_PRIVATE void _sg_dummy_reset_state_cache(void) { /* empty*/ } -_SOKOL_PRIVATE sg_resource_state _sg_create_context(_sg_context_t* ctx) { +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_context(_sg_context_t* ctx) { SOKOL_ASSERT(ctx); _SOKOL_UNUSED(ctx); return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_destroy_context(_sg_context_t* ctx) { +_SOKOL_PRIVATE void _sg_dummy_destroy_context(_sg_context_t* ctx) { SOKOL_ASSERT(ctx); _SOKOL_UNUSED(ctx); } -_SOKOL_PRIVATE void _sg_activate_context(_sg_context_t* ctx) { +_SOKOL_PRIVATE void _sg_dummy_activate_context(_sg_context_t* ctx) { SOKOL_ASSERT(ctx); _SOKOL_UNUSED(ctx); } -_SOKOL_PRIVATE sg_resource_state _sg_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { SOKOL_ASSERT(buf && desc); - buf->size = desc->size; - buf->append_pos = 0; - buf->append_overflow = false; - buf->type = desc->type; - buf->usage = desc->usage; - buf->update_frame_index = 0; - buf->append_frame_index = 0; - buf->num_slots = (buf->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; - buf->active_slot = 0; + _sg_buffer_common_init(&buf->cmn, desc); return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_destroy_buffer(_sg_buffer_t* buf) { +_SOKOL_PRIVATE void _sg_dummy_destroy_buffer(_sg_buffer_t* buf) { SOKOL_ASSERT(buf); _SOKOL_UNUSED(buf); } -_SOKOL_PRIVATE sg_resource_state _sg_create_image(_sg_image_t* img, const sg_image_desc* desc) { +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_image(_sg_image_t* img, const sg_image_desc* desc) { SOKOL_ASSERT(img && desc); - img->type = desc->type; - img->render_target = desc->render_target; - img->width = desc->width; - img->height = desc->height; - img->depth = desc->depth; - img->num_mipmaps = desc->num_mipmaps; - img->usage = desc->usage; - img->pixel_format = desc->pixel_format; - img->sample_count = desc->sample_count; - img->min_filter = desc->min_filter; - img->mag_filter = desc->mag_filter; - img->wrap_u = desc->wrap_u; - img->wrap_v = desc->wrap_v; - img->wrap_w = desc->wrap_w; - img->max_anisotropy = desc->max_anisotropy; - img->upd_frame_index = 0; - img->num_slots = (img->usage == SG_USAGE_IMMUTABLE) ? 1 :SG_NUM_INFLIGHT_FRAMES; - img->active_slot = 0; + _sg_image_common_init(&img->cmn, desc); return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_destroy_image(_sg_image_t* img) { +_SOKOL_PRIVATE void _sg_dummy_destroy_image(_sg_image_t* img) { SOKOL_ASSERT(img); _SOKOL_UNUSED(img); } -_SOKOL_PRIVATE sg_resource_state _sg_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { SOKOL_ASSERT(shd && desc); - /* uniform block sizes and image types */ - for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { - const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS) ? &desc->vs : &desc->fs; - _sg_shader_stage_t* stage = &shd->stage[stage_index]; - SOKOL_ASSERT(stage->num_uniform_blocks == 0); - for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { - const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; - if (0 == ub_desc->size) { - break; - } - _sg_uniform_block_t* ub = &stage->uniform_blocks[ub_index]; - ub->size = ub_desc->size; - stage->num_uniform_blocks++; - } - SOKOL_ASSERT(stage->num_images == 0); - for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { - const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; - if (img_desc->type == _SG_IMAGETYPE_DEFAULT) { - break; - } - stage->images[img_index].type = img_desc->type; - stage->num_images++; - } - } + _sg_shader_common_init(&shd->cmn, desc); return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_destroy_shader(_sg_shader_t* shd) { +_SOKOL_PRIVATE void _sg_dummy_destroy_shader(_sg_shader_t* shd) { SOKOL_ASSERT(shd); _SOKOL_UNUSED(shd); } -_SOKOL_PRIVATE sg_resource_state _sg_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { SOKOL_ASSERT(pip && desc); pip->shader = shd; - pip->shader_id = desc->shader; + _sg_pipeline_common_init(&pip->cmn, desc); for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; if (a_desc->format == SG_VERTEXFORMAT_INVALID) { break; } SOKOL_ASSERT((a_desc->buffer_index >= 0) && (a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS)); - pip->vertex_layout_valid[a_desc->buffer_index] = true; - } - pip->color_attachment_count = desc->blend.color_attachment_count; - pip->color_format = desc->blend.color_format; - pip->depth_format = desc->blend.depth_format; - pip->sample_count = desc->rasterizer.sample_count; - pip->depth_bias = desc->rasterizer.depth_bias; - pip->depth_bias_slope_scale = desc->rasterizer.depth_bias_slope_scale; - pip->depth_bias_clamp = desc->rasterizer.depth_bias_clamp; - pip->index_type = desc->index_type; - for (int i = 0; i < 4; i++) { - pip->blend_color[i] = desc->blend.blend_color[i]; + pip->cmn.vertex_layout_valid[a_desc->buffer_index] = true; } return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_destroy_pipeline(_sg_pipeline_t* pip) { +_SOKOL_PRIVATE void _sg_dummy_destroy_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); _SOKOL_UNUSED(pip); } -_SOKOL_PRIVATE sg_resource_state _sg_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { SOKOL_ASSERT(pass && desc); SOKOL_ASSERT(att_images && att_images[0]); - /* copy image pointers and desc attributes */ + + _sg_pass_common_init(&pass->cmn, desc); + const sg_attachment_desc* att_desc; - _sg_attachment_t* att; - for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - SOKOL_ASSERT(0 == pass->color_atts[i].image); + for (int i = 0; i < pass->cmn.num_color_atts; i++) { att_desc = &desc->color_attachments[i]; - if (att_desc->image.id != SG_INVALID_ID) { - pass->num_color_atts++; - SOKOL_ASSERT(att_images[i] && (att_images[i]->slot.id == att_desc->image.id)); - SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(att_images[i]->pixel_format)); - att = &pass->color_atts[i]; - SOKOL_ASSERT((att->image == 0) && (att->image_id.id == SG_INVALID_ID)); - att->image = att_images[i]; - att->image_id = att_desc->image; - att->mip_level = att_desc->mip_level; - att->slice = att_desc->slice; - } + SOKOL_ASSERT(att_desc->image.id != SG_INVALID_ID); + SOKOL_ASSERT(0 == pass->dmy.color_atts[i].image); + SOKOL_ASSERT(att_images[i] && (att_images[i]->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(att_images[i]->cmn.pixel_format)); + pass->dmy.color_atts[i].image = att_images[i]; } - SOKOL_ASSERT(0 == pass->ds_att.image); + + SOKOL_ASSERT(0 == pass->dmy.ds_att.image); att_desc = &desc->depth_stencil_attachment; - const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; if (att_desc->image.id != SG_INVALID_ID) { + const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; SOKOL_ASSERT(att_images[ds_img_index] && (att_images[ds_img_index]->slot.id == att_desc->image.id)); - SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_images[ds_img_index]->pixel_format)); - att = &pass->ds_att; - SOKOL_ASSERT((att->image == 0) && (att->image_id.id == SG_INVALID_ID)); - att->image = att_images[ds_img_index]; - att->image_id = att_desc->image; - att->mip_level = att_desc->mip_level; - att->slice = att_desc->slice; + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_images[ds_img_index]->cmn.pixel_format)); + pass->dmy.ds_att.image = att_images[ds_img_index]; } return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_destroy_pass(_sg_pass_t* pass) { +_SOKOL_PRIVATE void _sg_dummy_destroy_pass(_sg_pass_t* pass) { SOKOL_ASSERT(pass); _SOKOL_UNUSED(pass); } -_SOKOL_PRIVATE void _sg_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { +_SOKOL_PRIVATE _sg_image_t* _sg_dummy_pass_color_image(const _sg_pass_t* pass, int index) { + SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + /* NOTE: may return null */ + return pass->dmy.color_atts[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_dummy_pass_ds_image(const _sg_pass_t* pass) { + /* NOTE: may return null */ + SOKOL_ASSERT(pass); + return pass->dmy.ds_att.image; +} + +_SOKOL_PRIVATE void _sg_dummy_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { SOKOL_ASSERT(action); _SOKOL_UNUSED(pass); _SOKOL_UNUSED(action); @@ -3730,15 +4147,15 @@ _SOKOL_PRIVATE void _sg_begin_pass(_sg_pass_t* pass, const sg_pass_action* actio _SOKOL_UNUSED(h); } -_SOKOL_PRIVATE void _sg_end_pass(void) { +_SOKOL_PRIVATE void _sg_dummy_end_pass(void) { /* empty */ } -_SOKOL_PRIVATE void _sg_commit(void) { +_SOKOL_PRIVATE void _sg_dummy_commit(void) { /* empty */ } -_SOKOL_PRIVATE void _sg_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { +_SOKOL_PRIVATE void _sg_dummy_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { _SOKOL_UNUSED(x); _SOKOL_UNUSED(y); _SOKOL_UNUSED(w); @@ -3746,7 +4163,7 @@ _SOKOL_PRIVATE void _sg_apply_viewport(int x, int y, int w, int h, bool origin_t _SOKOL_UNUSED(origin_top_left); } -_SOKOL_PRIVATE void _sg_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { +_SOKOL_PRIVATE void _sg_dummy_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { _SOKOL_UNUSED(x); _SOKOL_UNUSED(y); _SOKOL_UNUSED(w); @@ -3754,12 +4171,12 @@ _SOKOL_PRIVATE void _sg_apply_scissor_rect(int x, int y, int w, int h, bool orig _SOKOL_UNUSED(origin_top_left); } -_SOKOL_PRIVATE void _sg_apply_pipeline(_sg_pipeline_t* pip) { +_SOKOL_PRIVATE void _sg_dummy_apply_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); _SOKOL_UNUSED(pip); } -_SOKOL_PRIVATE void _sg_apply_bindings( +_SOKOL_PRIVATE void _sg_dummy_apply_bindings( _sg_pipeline_t* pip, _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, _sg_buffer_t* ib, int ib_offset, @@ -3777,7 +4194,7 @@ _SOKOL_PRIVATE void _sg_apply_bindings( _SOKOL_UNUSED(fs_imgs); _SOKOL_UNUSED(num_fs_imgs); } -_SOKOL_PRIVATE void _sg_apply_uniforms(sg_shader_stage stage_index, int ub_index, const void* data, int num_bytes) { +_SOKOL_PRIVATE void _sg_dummy_apply_uniforms(sg_shader_stage stage_index, int ub_index, const void* data, int num_bytes) { SOKOL_ASSERT(data && (num_bytes > 0)); SOKOL_ASSERT((stage_index >= 0) && ((int)stage_index < SG_NUM_SHADER_STAGES)); SOKOL_ASSERT((ub_index >= 0) && (ub_index < SG_MAX_SHADERSTAGE_UBS)); @@ -3787,42 +4204,44 @@ _SOKOL_PRIVATE void _sg_apply_uniforms(sg_shader_stage stage_index, int ub_index _SOKOL_UNUSED(num_bytes); } -_SOKOL_PRIVATE void _sg_draw(int base_element, int num_elements, int num_instances) { +_SOKOL_PRIVATE void _sg_dummy_draw(int base_element, int num_elements, int num_instances) { _SOKOL_UNUSED(base_element); _SOKOL_UNUSED(num_elements); _SOKOL_UNUSED(num_instances); } -_SOKOL_PRIVATE void _sg_update_buffer(_sg_buffer_t* buf, const void* data, int data_size) { +_SOKOL_PRIVATE void _sg_dummy_update_buffer(_sg_buffer_t* buf, const void* data, uint32_t data_size) { SOKOL_ASSERT(buf && data && (data_size > 0)); _SOKOL_UNUSED(data); _SOKOL_UNUSED(data_size); - if (++buf->active_slot >= buf->num_slots) { - buf->active_slot = 0; + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; } } -_SOKOL_PRIVATE void _sg_append_buffer(_sg_buffer_t* buf, const void* data, int data_size, bool new_frame) { +_SOKOL_PRIVATE uint32_t _sg_dummy_append_buffer(_sg_buffer_t* buf, const void* data, uint32_t data_size, bool new_frame) { SOKOL_ASSERT(buf && data && (data_size > 0)); _SOKOL_UNUSED(data); _SOKOL_UNUSED(data_size); if (new_frame) { - if (++buf->active_slot >= buf->num_slots) { - buf->active_slot = 0; + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; } } + /* NOTE: this is a requirement from WebGPU, but we want identical behaviour across all backend */ + return _sg_roundup(data_size, 4); } -_SOKOL_PRIVATE void _sg_update_image(_sg_image_t* img, const sg_image_content* data) { +_SOKOL_PRIVATE void _sg_dummy_update_image(_sg_image_t* img, const sg_image_content* data) { SOKOL_ASSERT(img && data); _SOKOL_UNUSED(data); - if (++img->active_slot >= img->num_slots) { - img->active_slot = 0; + if (++img->cmn.active_slot >= img->cmn.num_slots) { + img->cmn.active_slot = 0; } } /*== GL BACKEND ==============================================================*/ -#elif defined(SOKOL_GLCORE33) || defined(SOKOL_GLES2) || defined(SOKOL_GLES3) +#elif defined(_SOKOL_ANY_GL) /*-- type translation --------------------------------------------------------*/ _SOKOL_PRIVATE GLenum _sg_gl_buffer_target(sg_buffer_type t) { @@ -4720,6 +5139,9 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_gles3(void) { else if (strstr(ext, "_texture_compression_pvrtc")) { has_pvrtc = true; } + else if (strstr(ext, "_compressed_texture_pvrtc")) { + has_pvrtc = true; + } else if (strstr(ext, "_compressed_texture_etc")) { has_etc2 = true; } @@ -4787,7 +5209,7 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_gles2(void) { has_s3tc = strstr(ext, "_texture_compression_s3tc") || strstr(ext, "_compressed_texture_s3tc"); has_rgtc = strstr(ext, "_texture_compression_rgtc"); has_bptc = strstr(ext, "_texture_compression_bptc"); - has_pvrtc = strstr(ext, "_texture_compression_pvrtc"); + has_pvrtc = strstr(ext, "_texture_compression_pvrtc") || strstr(ext, "_compressed_texture_pvrtc"); has_etc2 = strstr(ext, "_compressed_texture_etc"); has_texture_float = strstr(ext, "_texture_float"); has_texture_float_linear = strstr(ext, "_texture_float_linear"); @@ -4850,7 +5272,7 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_gles2(void) { #endif /*-- state cache implementation ----------------------------------------------*/ -_SOKOL_PRIVATE void _sg_gl_clear_buffer_bindings(bool force) { +_SOKOL_PRIVATE void _sg_gl_cache_clear_buffer_bindings(bool force) { if (force || (_sg.gl.cache.vertex_buffer != 0)) { glBindBuffer(GL_ARRAY_BUFFER, 0); _sg.gl.cache.vertex_buffer = 0; @@ -4861,7 +5283,7 @@ _SOKOL_PRIVATE void _sg_gl_clear_buffer_bindings(bool force) { } } -_SOKOL_PRIVATE void _sg_gl_bind_buffer(GLenum target, GLuint buffer) { +_SOKOL_PRIVATE void _sg_gl_cache_bind_buffer(GLenum target, GLuint buffer) { SOKOL_ASSERT((GL_ARRAY_BUFFER == target) || (GL_ELEMENT_ARRAY_BUFFER == target)); if (target == GL_ARRAY_BUFFER) { if (_sg.gl.cache.vertex_buffer != buffer) { @@ -4877,7 +5299,7 @@ _SOKOL_PRIVATE void _sg_gl_bind_buffer(GLenum target, GLuint buffer) { } } -_SOKOL_PRIVATE void _sg_gl_store_buffer_binding(GLenum target) { +_SOKOL_PRIVATE void _sg_gl_cache_store_buffer_binding(GLenum target) { if (target == GL_ARRAY_BUFFER) { _sg.gl.cache.stored_vertex_buffer = _sg.gl.cache.vertex_buffer; } @@ -4886,19 +5308,58 @@ _SOKOL_PRIVATE void _sg_gl_store_buffer_binding(GLenum target) { } } -_SOKOL_PRIVATE void _sg_gl_restore_buffer_binding(GLenum target) { +_SOKOL_PRIVATE void _sg_gl_cache_restore_buffer_binding(GLenum target) { if (target == GL_ARRAY_BUFFER) { - _sg_gl_bind_buffer(target, _sg.gl.cache.stored_vertex_buffer); + if (_sg.gl.cache.stored_vertex_buffer != 0) { + /* we only care restoring valid ids */ + _sg_gl_cache_bind_buffer(target, _sg.gl.cache.stored_vertex_buffer); + _sg.gl.cache.stored_vertex_buffer = 0; + } } else { - _sg_gl_bind_buffer(target, _sg.gl.cache.stored_index_buffer); + if (_sg.gl.cache.stored_index_buffer != 0) { + /* we only care restoring valid ids */ + _sg_gl_cache_bind_buffer(target, _sg.gl.cache.stored_index_buffer); + _sg.gl.cache.stored_index_buffer = 0; + } } } -_SOKOL_PRIVATE void _sg_gl_clear_texture_bindings(bool force) { +/* called when from _sg_gl_destroy_buffer() */ +_SOKOL_PRIVATE void _sg_gl_cache_invalidate_buffer(GLuint buf) { + if (buf == _sg.gl.cache.vertex_buffer) { + _sg.gl.cache.vertex_buffer = 0; + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + if (buf == _sg.gl.cache.index_buffer) { + _sg.gl.cache.index_buffer = 0; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + if (buf == _sg.gl.cache.stored_vertex_buffer) { + _sg.gl.cache.stored_vertex_buffer = 0; + } + if (buf == _sg.gl.cache.stored_index_buffer) { + _sg.gl.cache.stored_index_buffer = 0; + } + for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { + if (buf == _sg.gl.cache.attrs[i].gl_vbuf) { + _sg.gl.cache.attrs[i].gl_vbuf = 0; + } + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_active_texture(GLenum texture) { + if (_sg.gl.cache.cur_active_texture != texture) { + _sg.gl.cache.cur_active_texture = texture; + glActiveTexture(texture); + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_clear_texture_bindings(bool force) { for (int i = 0; (i < SG_MAX_SHADERSTAGE_IMAGES) && (i < _sg.gl.max_combined_texture_image_units); i++) { if (force || (_sg.gl.cache.textures[i].texture != 0)) { - glActiveTexture(GL_TEXTURE0 + i); + GLenum gl_texture_slot = GL_TEXTURE0 + i; + glActiveTexture(gl_texture_slot); glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_CUBE_MAP, 0); #if !defined(SOKOL_GLES2) @@ -4909,11 +5370,12 @@ _SOKOL_PRIVATE void _sg_gl_clear_texture_bindings(bool force) { #endif _sg.gl.cache.textures[i].target = 0; _sg.gl.cache.textures[i].texture = 0; + _sg.gl.cache.cur_active_texture = gl_texture_slot; } } } -_SOKOL_PRIVATE void _sg_gl_bind_texture(int slot_index, GLenum target, GLuint texture) { +_SOKOL_PRIVATE void _sg_gl_cache_bind_texture(int slot_index, GLenum target, GLuint texture) { /* it's valid to call this function with target=0 and/or texture=0 target=0 will unbind the previous binding, texture=0 will clear the new binding @@ -4924,7 +5386,7 @@ _SOKOL_PRIVATE void _sg_gl_bind_texture(int slot_index, GLenum target, GLuint te } _sg_gl_texture_bind_slot* slot = &_sg.gl.cache.textures[slot_index]; if ((slot->target != target) || (slot->texture != texture)) { - glActiveTexture(GL_TEXTURE0 + slot_index); + _sg_gl_cache_active_texture(GL_TEXTURE0 + slot_index); /* if the target has changed, clear the previous binding on that target */ if ((target != slot->target) && (slot->target != 0)) { glBindTexture(slot->target, 0); @@ -4938,73 +5400,114 @@ _SOKOL_PRIVATE void _sg_gl_bind_texture(int slot_index, GLenum target, GLuint te } } -_SOKOL_PRIVATE void _sg_gl_store_texture_binding(int slot_index) { +_SOKOL_PRIVATE void _sg_gl_cache_store_texture_binding(int slot_index) { SOKOL_ASSERT(slot_index < SG_MAX_SHADERSTAGE_IMAGES); _sg.gl.cache.stored_texture = _sg.gl.cache.textures[slot_index]; } -_SOKOL_PRIVATE void _sg_gl_restore_texture_binding(int slot_index) { +_SOKOL_PRIVATE void _sg_gl_cache_restore_texture_binding(int slot_index) { SOKOL_ASSERT(slot_index < SG_MAX_SHADERSTAGE_IMAGES); - const _sg_gl_texture_bind_slot* slot = &_sg.gl.cache.stored_texture; - _sg_gl_bind_texture(slot_index, slot->target, slot->texture); + _sg_gl_texture_bind_slot* slot = &_sg.gl.cache.stored_texture; + if (slot->texture != 0) { + /* we only care restoring valid ids */ + SOKOL_ASSERT(slot->target != 0); + _sg_gl_cache_bind_texture(slot_index, slot->target, slot->texture); + slot->target = 0; + slot->texture = 0; + } +} + +/* called from _sg_gl_destroy_texture() */ +_SOKOL_PRIVATE void _sg_gl_cache_invalidate_texture(GLuint tex) { + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { + _sg_gl_texture_bind_slot* slot = &_sg.gl.cache.textures[i]; + if (tex == slot->texture) { + _sg_gl_cache_active_texture(GL_TEXTURE0 + i); + glBindTexture(slot->target, 0); + slot->target = 0; + slot->texture = 0; + } + } + if (tex == _sg.gl.cache.stored_texture.texture) { + _sg.gl.cache.stored_texture.target = 0; + _sg.gl.cache.stored_texture.texture = 0; + } +} + +/* called from _sg_gl_destroy_shader() */ +_SOKOL_PRIVATE void _sg_gl_cache_invalidate_program(GLuint prog) { + if (prog == _sg.gl.cache.prog) { + _sg.gl.cache.prog = 0; + glUseProgram(0); + } } _SOKOL_PRIVATE void _sg_gl_reset_state_cache(void) { - _SG_GL_CHECK_ERROR(); - memset(&_sg.gl.cache, 0, sizeof(_sg.gl.cache)); - _sg_gl_clear_buffer_bindings(true); - _SG_GL_CHECK_ERROR(); - _sg_gl_clear_texture_bindings(true); - _SG_GL_CHECK_ERROR(); - for (uint32_t i = 0; i < _sg.limits.max_vertex_attrs; i++) { - _sg_gl_init_attr(&_sg.gl.cache.attrs[i].gl_attr); - glDisableVertexAttribArray(i); + if (_sg.gl.cur_context) { _SG_GL_CHECK_ERROR(); + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + glBindVertexArray(_sg.gl.cur_context->vao); + _SG_GL_CHECK_ERROR(); + } + #endif + memset(&_sg.gl.cache, 0, sizeof(_sg.gl.cache)); + _sg_gl_cache_clear_buffer_bindings(true); + _SG_GL_CHECK_ERROR(); + _sg_gl_cache_clear_texture_bindings(true); + _SG_GL_CHECK_ERROR(); + for (uint32_t i = 0; i < _sg.limits.max_vertex_attrs; i++) { + _sg_gl_init_attr(&_sg.gl.cache.attrs[i].gl_attr); + glDisableVertexAttribArray(i); + _SG_GL_CHECK_ERROR(); + } + _sg.gl.cache.cur_primitive_type = GL_TRIANGLES; + + /* shader program */ + glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&_sg.gl.cache.prog); + _SG_GL_CHECK_ERROR(); + + /* depth-stencil state */ + _sg_gl_init_depth_stencil_state(&_sg.gl.cache.ds); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_ALWAYS); + glDepthMask(GL_FALSE); + glDisable(GL_STENCIL_TEST); + glStencilFunc(GL_ALWAYS, 0, 0); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glStencilMask(0); + + /* blend state */ + _sg_gl_init_blend_state(&_sg.gl.cache.blend); + glDisable(GL_BLEND); + glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO); + glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glBlendColor(0.0f, 0.0f, 0.0f, 0.0f); + + /* rasterizer state */ + _sg_gl_init_rasterizer_state(&_sg.gl.cache.rast); + glPolygonOffset(0.0f, 0.0f); + glDisable(GL_POLYGON_OFFSET_FILL); + glDisable(GL_CULL_FACE); + glFrontFace(GL_CW); + glCullFace(GL_BACK); + glEnable(GL_SCISSOR_TEST); + glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); + glEnable(GL_DITHER); + glDisable(GL_POLYGON_OFFSET_FILL); + #if defined(SOKOL_GLCORE33) + glEnable(GL_MULTISAMPLE); + glEnable(GL_PROGRAM_POINT_SIZE); + #endif } - _sg.gl.cache.cur_primitive_type = GL_TRIANGLES; - - /* depth-stencil state */ - _sg_gl_init_depth_stencil_state(&_sg.gl.cache.ds); - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_ALWAYS); - glDepthMask(GL_FALSE); - glDisable(GL_STENCIL_TEST); - glStencilFunc(GL_ALWAYS, 0, 0); - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - glStencilMask(0); - - /* blend state */ - _sg_gl_init_blend_state(&_sg.gl.cache.blend); - glDisable(GL_BLEND); - glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO); - glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - glBlendColor(0.0f, 0.0f, 0.0f, 0.0f); - - /* rasterizer state */ - _sg_gl_init_rasterizer_state(&_sg.gl.cache.rast); - glPolygonOffset(0.0f, 0.0f); - glDisable(GL_POLYGON_OFFSET_FILL); - glDisable(GL_CULL_FACE); - glFrontFace(GL_CW); - glCullFace(GL_BACK); - glEnable(GL_SCISSOR_TEST); - glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); - glEnable(GL_DITHER); - glDisable(GL_POLYGON_OFFSET_FILL); - #if defined(SOKOL_GLCORE33) - glEnable(GL_MULTISAMPLE); - glEnable(GL_PROGRAM_POINT_SIZE); - #endif } -/*-- main GL backend state and functions -------------------------------------*/ - -_SOKOL_PRIVATE void _sg_setup_backend(const sg_desc* desc) { +_SOKOL_PRIVATE void _sg_gl_setup_backend(const sg_desc* desc) { /* assumes that _sg.gl is already zero-initialized */ _sg.gl.valid = true; #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) - _sg.gl.gles2 = desc->gl_force_gles2; + _sg.gl.gles2 = desc->context.gl.force_gles2; #else _SOKOL_UNUSED(desc); _sg.gl.gles2 = false; @@ -5028,33 +5531,20 @@ _SOKOL_PRIVATE void _sg_setup_backend(const sg_desc* desc) { #endif } -_SOKOL_PRIVATE void _sg_discard_backend(void) { +_SOKOL_PRIVATE void _sg_gl_discard_backend(void) { SOKOL_ASSERT(_sg.gl.valid); _sg.gl.valid = false; } -_SOKOL_PRIVATE void _sg_reset_state_cache(void) { - if (_sg.gl.cur_context) { - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - _SG_GL_CHECK_ERROR(); - glBindVertexArray(_sg.gl.cur_context->vao); - _SG_GL_CHECK_ERROR(); - } - #endif - _sg_gl_reset_state_cache(); - } -} - -_SOKOL_PRIVATE void _sg_activate_context(_sg_context_t* ctx) { +_SOKOL_PRIVATE void _sg_gl_activate_context(_sg_context_t* ctx) { SOKOL_ASSERT(_sg.gl.valid); /* NOTE: ctx can be 0 to unset the current context */ _sg.gl.cur_context = ctx; - _sg_reset_state_cache(); + _sg_gl_reset_state_cache(); } /*-- GL backend resource creation and destruction ----------------------------*/ -_SOKOL_PRIVATE sg_resource_state _sg_create_context(_sg_context_t* ctx) { +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_context(_sg_context_t* ctx) { SOKOL_ASSERT(ctx); SOKOL_ASSERT(0 == ctx->default_framebuffer); _SG_GL_CHECK_ERROR(); @@ -5071,7 +5561,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_context(_sg_context_t* ctx) { return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_destroy_context(_sg_context_t* ctx) { +_SOKOL_PRIVATE void _sg_gl_destroy_context(_sg_context_t* ctx) { SOKOL_ASSERT(ctx); #if !defined(SOKOL_GLES2) if (!_sg.gl.gles2) { @@ -5080,58 +5570,53 @@ _SOKOL_PRIVATE void _sg_destroy_context(_sg_context_t* ctx) { } _SG_GL_CHECK_ERROR(); } + #else + _SOKOL_UNUSED(ctx); #endif } -_SOKOL_PRIVATE sg_resource_state _sg_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { SOKOL_ASSERT(buf && desc); _SG_GL_CHECK_ERROR(); - buf->size = desc->size; - buf->append_pos = 0; - buf->append_overflow = false; - buf->type = desc->type; - buf->usage = desc->usage; - buf->update_frame_index = 0; - buf->append_frame_index = 0; - buf->num_slots = (buf->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; - buf->active_slot = 0; - buf->ext_buffers = (0 != desc->gl_buffers[0]); - GLenum gl_target = _sg_gl_buffer_target(buf->type); - GLenum gl_usage = _sg_gl_usage(buf->usage); - for (int slot = 0; slot < buf->num_slots; slot++) { + _sg_buffer_common_init(&buf->cmn, desc); + buf->gl.ext_buffers = (0 != desc->gl_buffers[0]); + GLenum gl_target = _sg_gl_buffer_target(buf->cmn.type); + GLenum gl_usage = _sg_gl_usage(buf->cmn.usage); + for (int slot = 0; slot < buf->cmn.num_slots; slot++) { GLuint gl_buf = 0; - if (buf->ext_buffers) { + if (buf->gl.ext_buffers) { SOKOL_ASSERT(desc->gl_buffers[slot]); gl_buf = desc->gl_buffers[slot]; } else { glGenBuffers(1, &gl_buf); - _sg_gl_store_buffer_binding(gl_target); - _sg_gl_bind_buffer(gl_target, gl_buf); - glBufferData(gl_target, buf->size, 0, gl_usage); - if (buf->usage == SG_USAGE_IMMUTABLE) { + _sg_gl_cache_store_buffer_binding(gl_target); + _sg_gl_cache_bind_buffer(gl_target, gl_buf); + glBufferData(gl_target, buf->cmn.size, 0, gl_usage); + if (buf->cmn.usage == SG_USAGE_IMMUTABLE) { SOKOL_ASSERT(desc->content); - glBufferSubData(gl_target, 0, buf->size, desc->content); + glBufferSubData(gl_target, 0, buf->cmn.size, desc->content); } - _sg_gl_restore_buffer_binding(gl_target); + _sg_gl_cache_restore_buffer_binding(gl_target); } - buf->gl_buf[slot] = gl_buf; + buf->gl.buf[slot] = gl_buf; } _SG_GL_CHECK_ERROR(); return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_destroy_buffer(_sg_buffer_t* buf) { +_SOKOL_PRIVATE void _sg_gl_destroy_buffer(_sg_buffer_t* buf) { SOKOL_ASSERT(buf); _SG_GL_CHECK_ERROR(); - if (!buf->ext_buffers) { - for (int slot = 0; slot < buf->num_slots; slot++) { - if (buf->gl_buf[slot]) { - glDeleteBuffers(1, &buf->gl_buf[slot]); + for (int slot = 0; slot < buf->cmn.num_slots; slot++) { + if (buf->gl.buf[slot]) { + _sg_gl_cache_invalidate_buffer(buf->gl.buf[slot]); + if (!buf->gl.ext_buffers) { + glDeleteBuffers(1, &buf->gl.buf[slot]); } } - _SG_GL_CHECK_ERROR(); } + _SG_GL_CHECK_ERROR(); } _SOKOL_PRIVATE bool _sg_gl_supported_texture_format(sg_pixel_format fmt) { @@ -5140,126 +5625,106 @@ _SOKOL_PRIVATE bool _sg_gl_supported_texture_format(sg_pixel_format fmt) { return _sg.formats[fmt_index].sample; } -_SOKOL_PRIVATE sg_resource_state _sg_create_image(_sg_image_t* img, const sg_image_desc* desc) { +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_image(_sg_image_t* img, const sg_image_desc* desc) { SOKOL_ASSERT(img && desc); _SG_GL_CHECK_ERROR(); - img->type = desc->type; - img->render_target = desc->render_target; - img->width = desc->width; - img->height = desc->height; - img->depth = desc->depth; - img->num_mipmaps = desc->num_mipmaps; - img->usage = desc->usage; - img->pixel_format = desc->pixel_format; - img->sample_count = desc->sample_count; - img->min_filter = desc->min_filter; - img->mag_filter = desc->mag_filter; - img->wrap_u = desc->wrap_u; - img->wrap_v = desc->wrap_v; - img->wrap_w = desc->wrap_w; - img->border_color = desc->border_color; - img->max_anisotropy = desc->max_anisotropy; - img->upd_frame_index = 0; + _sg_image_common_init(&img->cmn, desc); + img->gl.ext_textures = (0 != desc->gl_textures[0]); /* check if texture format is support */ - if (!_sg_gl_supported_texture_format(img->pixel_format)) { + if (!_sg_gl_supported_texture_format(img->cmn.pixel_format)) { SOKOL_LOG("texture format not supported by GL context\n"); return SG_RESOURCESTATE_FAILED; } /* check for optional texture types */ - if ((img->type == SG_IMAGETYPE_3D) && !_sg.features.imagetype_3d) { + if ((img->cmn.type == SG_IMAGETYPE_3D) && !_sg.features.imagetype_3d) { SOKOL_LOG("3D textures not supported by GL context\n"); return SG_RESOURCESTATE_FAILED; } - if ((img->type == SG_IMAGETYPE_ARRAY) && !_sg.features.imagetype_array) { + if ((img->cmn.type == SG_IMAGETYPE_ARRAY) && !_sg.features.imagetype_array) { SOKOL_LOG("array textures not supported by GL context\n"); return SG_RESOURCESTATE_FAILED; } - /* create 1 or 2 GL textures, depending on requested update strategy */ - img->num_slots = (img->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; - img->active_slot = 0; - img->ext_textures = (0 != desc->gl_textures[0]); - #if !defined(SOKOL_GLES2) bool msaa = false; if (!_sg.gl.gles2) { - msaa = (img->sample_count > 1) && (_sg.features.msaa_render_targets); + msaa = (img->cmn.sample_count > 1) && (_sg.features.msaa_render_targets); } #endif - if (_sg_is_valid_rendertarget_depth_format(img->pixel_format)) { + if (_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format)) { /* special case depth-stencil-buffer? */ - SOKOL_ASSERT((img->usage == SG_USAGE_IMMUTABLE) && (img->num_slots == 1)); - SOKOL_ASSERT(!img->ext_textures); /* cannot provide external texture for depth images */ - glGenRenderbuffers(1, &img->gl_depth_render_buffer); - glBindRenderbuffer(GL_RENDERBUFFER, img->gl_depth_render_buffer); - GLenum gl_depth_format = _sg_gl_depth_attachment_format(img->pixel_format); + SOKOL_ASSERT((img->cmn.usage == SG_USAGE_IMMUTABLE) && (img->cmn.num_slots == 1)); + SOKOL_ASSERT(!img->gl.ext_textures); /* cannot provide external texture for depth images */ + glGenRenderbuffers(1, &img->gl.depth_render_buffer); + glBindRenderbuffer(GL_RENDERBUFFER, img->gl.depth_render_buffer); + GLenum gl_depth_format = _sg_gl_depth_attachment_format(img->cmn.pixel_format); #if !defined(SOKOL_GLES2) if (!_sg.gl.gles2 && msaa) { - glRenderbufferStorageMultisample(GL_RENDERBUFFER, img->sample_count, gl_depth_format, img->width, img->height); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, img->cmn.sample_count, gl_depth_format, img->cmn.width, img->cmn.height); } else #endif { - glRenderbufferStorage(GL_RENDERBUFFER, gl_depth_format, img->width, img->height); + glRenderbufferStorage(GL_RENDERBUFFER, gl_depth_format, img->cmn.width, img->cmn.height); } } else { /* regular color texture */ - img->gl_target = _sg_gl_texture_target(img->type); - const GLenum gl_internal_format = _sg_gl_teximage_internal_format(img->pixel_format); + img->gl.target = _sg_gl_texture_target(img->cmn.type); + const GLenum gl_internal_format = _sg_gl_teximage_internal_format(img->cmn.pixel_format); /* if this is a MSAA render target, need to create a separate render buffer */ #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2 && img->render_target && msaa) { - glGenRenderbuffers(1, &img->gl_msaa_render_buffer); - glBindRenderbuffer(GL_RENDERBUFFER, img->gl_msaa_render_buffer); - glRenderbufferStorageMultisample(GL_RENDERBUFFER, img->sample_count, gl_internal_format, img->width, img->height); + if (!_sg.gl.gles2 && img->cmn.render_target && msaa) { + glGenRenderbuffers(1, &img->gl.msaa_render_buffer); + glBindRenderbuffer(GL_RENDERBUFFER, img->gl.msaa_render_buffer); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, img->cmn.sample_count, gl_internal_format, img->cmn.width, img->cmn.height); } #endif - if (img->ext_textures) { + if (img->gl.ext_textures) { /* inject externally GL textures */ - for (int slot = 0; slot < img->num_slots; slot++) { + for (int slot = 0; slot < img->cmn.num_slots; slot++) { SOKOL_ASSERT(desc->gl_textures[slot]); - img->gl_tex[slot] = desc->gl_textures[slot]; + img->gl.tex[slot] = desc->gl_textures[slot]; } } else { /* create our own GL texture(s) */ - const GLenum gl_format = _sg_gl_teximage_format(img->pixel_format); - const bool is_compressed = _sg_is_compressed_pixel_format(img->pixel_format); - for (int slot = 0; slot < img->num_slots; slot++) { - glGenTextures(1, &img->gl_tex[slot]); - _sg_gl_store_texture_binding(0); - _sg_gl_bind_texture(0, img->gl_target, img->gl_tex[slot]); - GLenum gl_min_filter = _sg_gl_filter(img->min_filter); - GLenum gl_mag_filter = _sg_gl_filter(img->mag_filter); - glTexParameteri(img->gl_target, GL_TEXTURE_MIN_FILTER, gl_min_filter); - glTexParameteri(img->gl_target, GL_TEXTURE_MAG_FILTER, gl_mag_filter); - if (_sg.gl.ext_anisotropic && (img->max_anisotropy > 1)) { - GLint max_aniso = (GLint) img->max_anisotropy; + const GLenum gl_format = _sg_gl_teximage_format(img->cmn.pixel_format); + const bool is_compressed = _sg_is_compressed_pixel_format(img->cmn.pixel_format); + for (int slot = 0; slot < img->cmn.num_slots; slot++) { + glGenTextures(1, &img->gl.tex[slot]); + _sg_gl_cache_store_texture_binding(0); + _sg_gl_cache_bind_texture(0, img->gl.target, img->gl.tex[slot]); + GLenum gl_min_filter = _sg_gl_filter(img->cmn.min_filter); + GLenum gl_mag_filter = _sg_gl_filter(img->cmn.mag_filter); + glTexParameteri(img->gl.target, GL_TEXTURE_MIN_FILTER, gl_min_filter); + glTexParameteri(img->gl.target, GL_TEXTURE_MAG_FILTER, gl_mag_filter); + if (_sg.gl.ext_anisotropic && (img->cmn.max_anisotropy > 1)) { + GLint max_aniso = (GLint) img->cmn.max_anisotropy; if (max_aniso > _sg.gl.max_anisotropy) { max_aniso = _sg.gl.max_anisotropy; } - glTexParameteri(img->gl_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_aniso); + glTexParameteri(img->gl.target, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_aniso); } - if (img->type == SG_IMAGETYPE_CUBE) { - glTexParameteri(img->gl_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(img->gl_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + if (img->cmn.type == SG_IMAGETYPE_CUBE) { + glTexParameteri(img->gl.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(img->gl.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } else { - glTexParameteri(img->gl_target, GL_TEXTURE_WRAP_S, _sg_gl_wrap(img->wrap_u)); - glTexParameteri(img->gl_target, GL_TEXTURE_WRAP_T, _sg_gl_wrap(img->wrap_v)); + glTexParameteri(img->gl.target, GL_TEXTURE_WRAP_S, _sg_gl_wrap(img->cmn.wrap_u)); + glTexParameteri(img->gl.target, GL_TEXTURE_WRAP_T, _sg_gl_wrap(img->cmn.wrap_v)); #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2 && (img->type == SG_IMAGETYPE_3D)) { - glTexParameteri(img->gl_target, GL_TEXTURE_WRAP_R, _sg_gl_wrap(img->wrap_w)); + if (!_sg.gl.gles2 && (img->cmn.type == SG_IMAGETYPE_3D)) { + glTexParameteri(img->gl.target, GL_TEXTURE_WRAP_R, _sg_gl_wrap(img->cmn.wrap_w)); } #endif #if defined(SOKOL_GLCORE33) float border[4]; - switch (img->border_color) { + switch (img->cmn.border_color) { case SG_BORDERCOLOR_TRANSPARENT_BLACK: border[0] = 0.0f; border[1] = 0.0f; border[2] = 0.0f; border[3] = 0.0f; break; @@ -5270,7 +5735,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_image(_sg_image_t* img, const sg_ima border[0] = 0.0f; border[1] = 0.0f; border[2] = 0.0f; border[3] = 1.0f; break; } - glTexParameterfv(img->gl_target, GL_TEXTURE_BORDER_COLOR, border); + glTexParameterfv(img->gl.target, GL_TEXTURE_BORDER_COLOR, border); #endif } #if !defined(SOKOL_GLES2) @@ -5278,43 +5743,43 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_image(_sg_image_t* img, const sg_ima /* GL spec has strange defaults for mipmap min/max lod: -1000 to +1000 */ const float min_lod = _sg_clamp(desc->min_lod, 0.0f, 1000.0f); const float max_lod = _sg_clamp(desc->max_lod, 0.0f, 1000.0f); - glTexParameterf(img->gl_target, GL_TEXTURE_MIN_LOD, min_lod); - glTexParameterf(img->gl_target, GL_TEXTURE_MAX_LOD, max_lod); + glTexParameterf(img->gl.target, GL_TEXTURE_MIN_LOD, min_lod); + glTexParameterf(img->gl.target, GL_TEXTURE_MAX_LOD, max_lod); } #endif - const int num_faces = img->type == SG_IMAGETYPE_CUBE ? 6 : 1; + const int num_faces = img->cmn.type == SG_IMAGETYPE_CUBE ? 6 : 1; int data_index = 0; for (int face_index = 0; face_index < num_faces; face_index++) { - for (int mip_index = 0; mip_index < img->num_mipmaps; mip_index++, data_index++) { - GLenum gl_img_target = img->gl_target; - if (SG_IMAGETYPE_CUBE == img->type) { + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++, data_index++) { + GLenum gl_img_target = img->gl.target; + if (SG_IMAGETYPE_CUBE == img->cmn.type) { gl_img_target = _sg_gl_cubeface_target(face_index); } const GLvoid* data_ptr = desc->content.subimage[face_index][mip_index].ptr; const int data_size = desc->content.subimage[face_index][mip_index].size; - int mip_width = img->width >> mip_index; + int mip_width = img->cmn.width >> mip_index; if (mip_width == 0) { mip_width = 1; } - int mip_height = img->height >> mip_index; + int mip_height = img->cmn.height >> mip_index; if (mip_height == 0) { mip_height = 1; } - if ((SG_IMAGETYPE_2D == img->type) || (SG_IMAGETYPE_CUBE == img->type)) { + if ((SG_IMAGETYPE_2D == img->cmn.type) || (SG_IMAGETYPE_CUBE == img->cmn.type)) { if (is_compressed) { glCompressedTexImage2D(gl_img_target, mip_index, gl_internal_format, mip_width, mip_height, 0, data_size, data_ptr); } else { - const GLenum gl_type = _sg_gl_teximage_type(img->pixel_format); + const GLenum gl_type = _sg_gl_teximage_type(img->cmn.pixel_format); glTexImage2D(gl_img_target, mip_index, gl_internal_format, mip_width, mip_height, 0, gl_format, gl_type, data_ptr); } } #if !defined(SOKOL_GLES2) - else if (!_sg.gl.gles2 && ((SG_IMAGETYPE_3D == img->type) || (SG_IMAGETYPE_ARRAY == img->type))) { - int mip_depth = img->depth; - if (SG_IMAGETYPE_3D == img->type) { + else if (!_sg.gl.gles2 && ((SG_IMAGETYPE_3D == img->cmn.type) || (SG_IMAGETYPE_ARRAY == img->cmn.type))) { + int mip_depth = img->cmn.depth; + if (SG_IMAGETYPE_3D == img->cmn.type) { mip_depth >>= mip_index; } if (mip_depth == 0) { @@ -5325,7 +5790,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_image(_sg_image_t* img, const sg_ima mip_width, mip_height, mip_depth, 0, data_size, data_ptr); } else { - const GLenum gl_type = _sg_gl_teximage_type(img->pixel_format); + const GLenum gl_type = _sg_gl_teximage_type(img->cmn.pixel_format); glTexImage3D(gl_img_target, mip_index, gl_internal_format, mip_width, mip_height, mip_depth, 0, gl_format, gl_type, data_ptr); } @@ -5333,7 +5798,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_image(_sg_image_t* img, const sg_ima #endif } } - _sg_gl_restore_texture_binding(0); + _sg_gl_cache_restore_texture_binding(0); } } } @@ -5341,21 +5806,22 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_image(_sg_image_t* img, const sg_ima return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_destroy_image(_sg_image_t* img) { +_SOKOL_PRIVATE void _sg_gl_destroy_image(_sg_image_t* img) { SOKOL_ASSERT(img); _SG_GL_CHECK_ERROR(); - if (!img->ext_textures) { - for (int slot = 0; slot < img->num_slots; slot++) { - if (img->gl_tex[slot]) { - glDeleteTextures(1, &img->gl_tex[slot]); + for (int slot = 0; slot < img->cmn.num_slots; slot++) { + if (img->gl.tex[slot]) { + _sg_gl_cache_invalidate_texture(img->gl.tex[slot]); + if (!img->gl.ext_textures) { + glDeleteTextures(1, &img->gl.tex[slot]); } } } - if (img->gl_depth_render_buffer) { - glDeleteRenderbuffers(1, &img->gl_depth_render_buffer); + if (img->gl.depth_render_buffer) { + glDeleteRenderbuffers(1, &img->gl.depth_render_buffer); } - if (img->gl_msaa_render_buffer) { - glDeleteRenderbuffers(1, &img->gl_msaa_render_buffer); + if (img->gl.msaa_render_buffer) { + glDeleteRenderbuffers(1, &img->gl.msaa_render_buffer); } _SG_GL_CHECK_ERROR(); } @@ -5385,14 +5851,16 @@ _SOKOL_PRIVATE GLuint _sg_gl_compile_shader(sg_shader_stage stage, const char* s return gl_shd; } -_SOKOL_PRIVATE sg_resource_state _sg_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { SOKOL_ASSERT(shd && desc); - SOKOL_ASSERT(!shd->gl_prog); + SOKOL_ASSERT(!shd->gl.prog); _SG_GL_CHECK_ERROR(); + _sg_shader_common_init(&shd->cmn, desc); + /* copy vertex attribute names over, these are required for GLES2, and optional for GLES3 and GL3.x */ for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { - _sg_strcpy(&shd->attrs[i].name, desc->attrs[i].name); + _sg_strcpy(&shd->gl.attrs[i].name, desc->attrs[i].name); } GLuint gl_vs = _sg_gl_compile_shader(SG_SHADERSTAGE_VS, desc->vs.source); @@ -5422,21 +5890,17 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_shader(_sg_shader_t* shd, const sg_s glDeleteProgram(gl_prog); return SG_RESOURCESTATE_FAILED; } - shd->gl_prog = gl_prog; + shd->gl.prog = gl_prog; /* resolve uniforms */ _SG_GL_CHECK_ERROR(); for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS)? &desc->vs : &desc->fs; - _sg_shader_stage_t* stage = &shd->stage[stage_index]; - SOKOL_ASSERT(stage->num_uniform_blocks == 0); - for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { + _sg_gl_shader_stage_t* gl_stage = &shd->gl.stage[stage_index]; + for (int ub_index = 0; ub_index < shd->cmn.stage[stage_index].num_uniform_blocks; ub_index++) { const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; - if (0 == ub_desc->size) { - break; - } - _sg_uniform_block_t* ub = &stage->uniform_blocks[ub_index]; - ub->size = ub_desc->size; + SOKOL_ASSERT(ub_desc->size > 0); + _sg_gl_uniform_block_t* ub = &gl_stage->uniform_blocks[ub_index]; SOKOL_ASSERT(ub->num_uniforms == 0); int cur_uniform_offset = 0; for (int u_index = 0; u_index < SG_MAX_UB_MEMBERS; u_index++) { @@ -5444,7 +5908,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_shader(_sg_shader_t* shd, const sg_s if (u_desc->type == SG_UNIFORMTYPE_INVALID) { break; } - _sg_uniform_t* u = &ub->uniforms[u_index]; + _sg_gl_uniform_t* u = &ub->uniforms[u_index]; u->type = u_desc->type; u->count = (uint8_t) u_desc->array_count; u->offset = (uint16_t) cur_uniform_offset; @@ -5458,70 +5922,66 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_shader(_sg_shader_t* shd, const sg_s ub->num_uniforms++; } SOKOL_ASSERT(ub_desc->size == cur_uniform_offset); - stage->num_uniform_blocks++; } } /* resolve image locations */ _SG_GL_CHECK_ERROR(); + GLuint cur_prog = 0; + glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&cur_prog); + glUseProgram(gl_prog); int gl_tex_slot = 0; for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS)? &desc->vs : &desc->fs; - _sg_shader_stage_t* stage = &shd->stage[stage_index]; - SOKOL_ASSERT(stage->num_images == 0); - for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { + _sg_gl_shader_stage_t* gl_stage = &shd->gl.stage[stage_index]; + for (int img_index = 0; img_index < shd->cmn.stage[stage_index].num_images; img_index++) { const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; - if (img_desc->type == _SG_IMAGETYPE_DEFAULT) { - break; - } - _sg_shader_image_t* img = &stage->images[img_index]; - img->type = img_desc->type; - img->gl_loc = img_index; + SOKOL_ASSERT(img_desc->type != _SG_IMAGETYPE_DEFAULT); + _sg_gl_shader_image_t* gl_img = &gl_stage->images[img_index]; + GLint gl_loc = img_index; if (img_desc->name) { - img->gl_loc = glGetUniformLocation(gl_prog, img_desc->name); + gl_loc = glGetUniformLocation(gl_prog, img_desc->name); } - if (img->gl_loc != -1) { - img->gl_tex_slot = gl_tex_slot++; + if (gl_loc != -1) { + gl_img->gl_tex_slot = gl_tex_slot++; + glUniform1i(gl_loc, gl_img->gl_tex_slot); } else { - img->gl_tex_slot = -1; + gl_img->gl_tex_slot = -1; } - stage->num_images++; } } + /* it's legal to call glUseProgram with 0 */ + glUseProgram(cur_prog); _SG_GL_CHECK_ERROR(); return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_destroy_shader(_sg_shader_t* shd) { +_SOKOL_PRIVATE void _sg_gl_destroy_shader(_sg_shader_t* shd) { SOKOL_ASSERT(shd); _SG_GL_CHECK_ERROR(); - if (shd->gl_prog) { - glDeleteProgram(shd->gl_prog); + if (shd->gl.prog) { + _sg_gl_cache_invalidate_program(shd->gl.prog); + glDeleteProgram(shd->gl.prog); } _SG_GL_CHECK_ERROR(); } -_SOKOL_PRIVATE sg_resource_state _sg_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { SOKOL_ASSERT(pip && shd && desc); - SOKOL_ASSERT(!pip->shader && pip->shader_id.id == SG_INVALID_ID); + SOKOL_ASSERT(!pip->shader && pip->cmn.shader_id.id == SG_INVALID_ID); SOKOL_ASSERT(desc->shader.id == shd->slot.id); - SOKOL_ASSERT(shd->gl_prog); + SOKOL_ASSERT(shd->gl.prog); pip->shader = shd; - pip->shader_id = desc->shader; - pip->primitive_type = desc->primitive_type; - pip->index_type = desc->index_type; - pip->color_attachment_count = desc->blend.color_attachment_count; - pip->color_format = desc->blend.color_format; - pip->depth_format = desc->blend.depth_format; - pip->sample_count = desc->rasterizer.sample_count; - pip->depth_stencil = desc->depth_stencil; - pip->blend = desc->blend; - pip->rast = desc->rasterizer; + _sg_pipeline_common_init(&pip->cmn, desc); + pip->gl.primitive_type = desc->primitive_type; + pip->gl.depth_stencil = desc->depth_stencil; + pip->gl.blend = desc->blend; + pip->gl.rast = desc->rasterizer; /* resolve vertex attributes */ for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { - pip->gl_attrs[attr_index].vb_index = -1; + pip->gl.attrs[attr_index].vb_index = -1; } for (uint32_t attr_index = 0; attr_index < _sg.limits.max_vertex_attrs; attr_index++) { const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; @@ -5533,12 +5993,12 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_pipeline(_sg_pipeline_t* pip, _sg_sh const sg_vertex_step step_func = l_desc->step_func; const int step_rate = l_desc->step_rate; GLint attr_loc = attr_index; - if (!_sg_strempty(&shd->attrs[attr_index].name)) { - attr_loc = glGetAttribLocation(pip->shader->gl_prog, _sg_strptr(&shd->attrs[attr_index].name)); + if (!_sg_strempty(&shd->gl.attrs[attr_index].name)) { + attr_loc = glGetAttribLocation(pip->shader->gl.prog, _sg_strptr(&shd->gl.attrs[attr_index].name)); } SOKOL_ASSERT(attr_loc < (GLint)_sg.limits.max_vertex_attrs); if (attr_loc != -1) { - _sg_gl_attr_t* gl_attr = &pip->gl_attrs[attr_loc]; + _sg_gl_attr_t* gl_attr = &pip->gl.attrs[attr_loc]; SOKOL_ASSERT(gl_attr->vb_index == -1); gl_attr->vb_index = (int8_t) a_desc->buffer_index; if (step_func == SG_VERTEXSTEP_PER_VERTEX) { @@ -5553,18 +6013,19 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_pipeline(_sg_pipeline_t* pip, _sg_sh gl_attr->size = (uint8_t) _sg_gl_vertexformat_size(a_desc->format); gl_attr->type = _sg_gl_vertexformat_type(a_desc->format); gl_attr->normalized = _sg_gl_vertexformat_normalized(a_desc->format); - pip->vertex_layout_valid[a_desc->buffer_index] = true; + pip->cmn.vertex_layout_valid[a_desc->buffer_index] = true; } else { SOKOL_LOG("Vertex attribute not found in shader: "); - SOKOL_LOG(_sg_strptr(&shd->attrs[attr_index].name)); + SOKOL_LOG(_sg_strptr(&shd->gl.attrs[attr_index].name)); } } return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_destroy_pipeline(_sg_pipeline_t* pip) { +_SOKOL_PRIVATE void _sg_gl_destroy_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); + _SOKOL_UNUSED(pip); /* empty */ } @@ -5575,41 +6036,30 @@ _SOKOL_PRIVATE void _sg_destroy_pipeline(_sg_pipeline_t* pip) { first entries are the color attachment images (or nullptr), last entry is the depth-stencil image (or nullptr). */ -_SOKOL_PRIVATE sg_resource_state _sg_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { SOKOL_ASSERT(pass && att_images && desc); SOKOL_ASSERT(att_images && att_images[0]); _SG_GL_CHECK_ERROR(); - /* copy image pointers and desc attributes */ + _sg_pass_common_init(&pass->cmn, desc); + + /* copy image pointers */ const sg_attachment_desc* att_desc; - _sg_attachment_t* att; - for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - SOKOL_ASSERT(0 == pass->color_atts[i].image); + for (int i = 0; i < pass->cmn.num_color_atts; i++) { att_desc = &desc->color_attachments[i]; - if (att_desc->image.id != SG_INVALID_ID) { - pass->num_color_atts++; - SOKOL_ASSERT(att_images[i] && (att_images[i]->slot.id == att_desc->image.id)); - SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(att_images[i]->pixel_format)); - att = &pass->color_atts[i]; - SOKOL_ASSERT((att->image == 0) && (att->image_id.id == SG_INVALID_ID)); - att->image = att_images[i]; - att->image_id = att_desc->image; - att->mip_level = att_desc->mip_level; - att->slice = att_desc->slice; - } + SOKOL_ASSERT(att_desc->image.id != SG_INVALID_ID); + SOKOL_ASSERT(0 == pass->gl.color_atts[i].image); + SOKOL_ASSERT(att_images[i] && (att_images[i]->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(att_images[i]->cmn.pixel_format)); + pass->gl.color_atts[i].image = att_images[i]; } - SOKOL_ASSERT(0 == pass->ds_att.image); + SOKOL_ASSERT(0 == pass->gl.ds_att.image); att_desc = &desc->depth_stencil_attachment; - const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; if (att_desc->image.id != SG_INVALID_ID) { + const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; SOKOL_ASSERT(att_images[ds_img_index] && (att_images[ds_img_index]->slot.id == att_desc->image.id)); - SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_images[ds_img_index]->pixel_format)); - att = &pass->ds_att; - SOKOL_ASSERT((att->image == 0) && (att->image_id.id == SG_INVALID_ID)); - att->image = att_images[ds_img_index]; - att->image_id = att_desc->image; - att->mip_level = att_desc->mip_level; - att->slice = att_desc->slice; + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_images[ds_img_index]->cmn.pixel_format)); + pass->gl.ds_att.image = att_images[ds_img_index]; } /* store current framebuffer binding (restored at end of function) */ @@ -5617,16 +6067,16 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_pass(_sg_pass_t* pass, _sg_image_t** glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&gl_orig_fb); /* create a framebuffer object */ - glGenFramebuffers(1, &pass->gl_fb); - glBindFramebuffer(GL_FRAMEBUFFER, pass->gl_fb); + glGenFramebuffers(1, &pass->gl.fb); + glBindFramebuffer(GL_FRAMEBUFFER, pass->gl.fb); /* attach msaa render buffer or textures */ - const bool is_msaa = (0 != att_images[0]->gl_msaa_render_buffer); + const bool is_msaa = (0 != att_images[0]->gl.msaa_render_buffer); if (is_msaa) { for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - const _sg_image_t* att_img = pass->color_atts[i].image; + const _sg_image_t* att_img = pass->gl.color_atts[i].image; if (att_img) { - const GLuint gl_render_buffer = att_img->gl_msaa_render_buffer; + const GLuint gl_render_buffer = att_img->gl.msaa_render_buffer; SOKOL_ASSERT(gl_render_buffer); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+i, GL_RENDERBUFFER, gl_render_buffer); } @@ -5634,14 +6084,14 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_pass(_sg_pass_t* pass, _sg_image_t** } else { for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - const _sg_image_t* att_img = pass->color_atts[i].image; - const int mip_level = pass->color_atts[i].mip_level; - const int slice = pass->color_atts[i].slice; + const _sg_image_t* att_img = pass->gl.color_atts[i].image; + const int mip_level = pass->cmn.color_atts[i].mip_level; + const int slice = pass->cmn.color_atts[i].slice; if (att_img) { - const GLuint gl_tex = att_img->gl_tex[0]; + const GLuint gl_tex = att_img->gl.tex[0]; SOKOL_ASSERT(gl_tex); const GLenum gl_att = GL_COLOR_ATTACHMENT0 + i; - switch (att_img->type) { + switch (att_img->cmn.type) { case SG_IMAGETYPE_2D: glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att, GL_TEXTURE_2D, gl_tex, mip_level); break; @@ -5662,11 +6112,11 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_pass(_sg_pass_t* pass, _sg_image_t** } /* attach depth-stencil buffer to framebuffer */ - if (pass->ds_att.image) { - const GLuint gl_render_buffer = pass->ds_att.image->gl_depth_render_buffer; + if (pass->gl.ds_att.image) { + const GLuint gl_render_buffer = pass->gl.ds_att.image->gl.depth_render_buffer; SOKOL_ASSERT(gl_render_buffer); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, gl_render_buffer); - if (_sg_is_depth_stencil_format(pass->ds_att.image->pixel_format)) { + if (_sg_is_depth_stencil_format(pass->gl.ds_att.image->cmn.pixel_format)) { glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, gl_render_buffer); } } @@ -5677,29 +6127,43 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_pass(_sg_pass_t* pass, _sg_image_t** return SG_RESOURCESTATE_FAILED; } + /* setup color attachments for the framebuffer */ + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + GLenum att[SG_MAX_COLOR_ATTACHMENTS] = { + GL_COLOR_ATTACHMENT0, + GL_COLOR_ATTACHMENT1, + GL_COLOR_ATTACHMENT2, + GL_COLOR_ATTACHMENT3 + }; + glDrawBuffers(pass->cmn.num_color_atts, att); + } + #endif + /* create MSAA resolve framebuffers if necessary */ if (is_msaa) { for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - att = &pass->color_atts[i]; - if (att->image) { - SOKOL_ASSERT(0 == att->gl_msaa_resolve_buffer); - glGenFramebuffers(1, &att->gl_msaa_resolve_buffer); - glBindFramebuffer(GL_FRAMEBUFFER, att->gl_msaa_resolve_buffer); - const GLuint gl_tex = att->image->gl_tex[0]; + _sg_gl_attachment_t* gl_att = &pass->gl.color_atts[i]; + _sg_attachment_t* cmn_att = &pass->cmn.color_atts[i]; + if (gl_att->image) { + SOKOL_ASSERT(0 == gl_att->gl_msaa_resolve_buffer); + glGenFramebuffers(1, &gl_att->gl_msaa_resolve_buffer); + glBindFramebuffer(GL_FRAMEBUFFER, gl_att->gl_msaa_resolve_buffer); + const GLuint gl_tex = gl_att->image->gl.tex[0]; SOKOL_ASSERT(gl_tex); - switch (att->image->type) { + switch (gl_att->image->cmn.type) { case SG_IMAGETYPE_2D: glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, gl_tex, att->mip_level); + GL_TEXTURE_2D, gl_tex, cmn_att->mip_level); break; case SG_IMAGETYPE_CUBE: glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - _sg_gl_cubeface_target(att->slice), gl_tex, att->mip_level); + _sg_gl_cubeface_target(cmn_att->slice), gl_tex, cmn_att->mip_level); break; default: #if !defined(SOKOL_GLES2) if (!_sg.gl.gles2) { - glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, gl_tex, att->mip_level, att->slice); + glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, gl_tex, cmn_att->mip_level, cmn_att->slice); } #endif break; @@ -5709,6 +6173,13 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_pass(_sg_pass_t* pass, _sg_image_t** SOKOL_LOG("Framebuffer completeness check failed (msaa resolve buffer)!\n"); return SG_RESOURCESTATE_FAILED; } + /* setup color attachments for the framebuffer */ + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + const GLenum gl_draw_bufs = GL_COLOR_ATTACHMENT0; + glDrawBuffers(1, &gl_draw_bufs); + } + #endif } } } @@ -5719,25 +6190,36 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_pass(_sg_pass_t* pass, _sg_image_t** return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_destroy_pass(_sg_pass_t* pass) { +_SOKOL_PRIVATE void _sg_gl_destroy_pass(_sg_pass_t* pass) { SOKOL_ASSERT(pass); _SG_GL_CHECK_ERROR(); - if (0 != pass->gl_fb) { - glDeleteFramebuffers(1, &pass->gl_fb); + if (0 != pass->gl.fb) { + glDeleteFramebuffers(1, &pass->gl.fb); } for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - if (pass->color_atts[i].gl_msaa_resolve_buffer) { - glDeleteFramebuffers(1, &pass->color_atts[i].gl_msaa_resolve_buffer); + if (pass->gl.color_atts[i].gl_msaa_resolve_buffer) { + glDeleteFramebuffers(1, &pass->gl.color_atts[i].gl_msaa_resolve_buffer); } } - if (pass->ds_att.gl_msaa_resolve_buffer) { - glDeleteFramebuffers(1, &pass->ds_att.gl_msaa_resolve_buffer); + if (pass->gl.ds_att.gl_msaa_resolve_buffer) { + glDeleteFramebuffers(1, &pass->gl.ds_att.gl_msaa_resolve_buffer); } _SG_GL_CHECK_ERROR(); } -/*-- GL backend rendering functions ------------------------------------------*/ -_SOKOL_PRIVATE void _sg_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { +_SOKOL_PRIVATE _sg_image_t* _sg_gl_pass_color_image(const _sg_pass_t* pass, int index) { + SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + /* NOTE: may return null */ + return pass->gl.color_atts[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_gl_pass_ds_image(const _sg_pass_t* pass) { + /* NOTE: may return null */ + SOKOL_ASSERT(pass); + return pass->gl.ds_att.image; +} + +_SOKOL_PRIVATE void _sg_gl_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { /* FIXME: what if a texture used as render target is still bound, should we unbind all currently bound textures in begin pass? */ SOKOL_ASSERT(action); @@ -5753,30 +6235,15 @@ _SOKOL_PRIVATE void _sg_begin_pass(_sg_pass_t* pass, const sg_pass_action* actio } _sg.gl.cur_pass_width = w; _sg.gl.cur_pass_height = h; + + /* number of color attachments */ + const int num_color_atts = pass ? pass->cmn.num_color_atts : 1; + + /* bind the render pass framebuffer */ if (pass) { /* offscreen pass */ - SOKOL_ASSERT(pass->gl_fb); - glBindFramebuffer(GL_FRAMEBUFFER, pass->gl_fb); - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - GLenum att[SG_MAX_COLOR_ATTACHMENTS] = { - GL_COLOR_ATTACHMENT0, - GL_COLOR_ATTACHMENT1, - GL_COLOR_ATTACHMENT2, - GL_COLOR_ATTACHMENT3 - }; - int num_attrs = 0; - for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - if (pass->color_atts[num_attrs].image) { - num_attrs++; - } - else { - break; - } - } - glDrawBuffers(num_attrs, att); - } - #endif + SOKOL_ASSERT(pass->gl.fb); + glBindFramebuffer(GL_FRAMEBUFFER, pass->gl.fb); } else { /* default pass */ @@ -5785,26 +6252,44 @@ _SOKOL_PRIVATE void _sg_begin_pass(_sg_pass_t* pass, const sg_pass_action* actio } glViewport(0, 0, w, h); glScissor(0, 0, w, h); + + /* clear color and depth-stencil attachments if needed */ + bool clear_color = false; + for (int i = 0; i < num_color_atts; i++) { + if (SG_ACTION_CLEAR == action->colors[i].action) { + clear_color = true; + break; + } + } + const bool clear_depth = (action->depth.action == SG_ACTION_CLEAR); + const bool clear_stencil = (action->stencil.action == SG_ACTION_CLEAR); + bool need_pip_cache_flush = false; - if (_sg.gl.cache.blend.color_write_mask != SG_COLORMASK_RGBA) { - need_pip_cache_flush = true; - _sg.gl.cache.blend.color_write_mask = SG_COLORMASK_RGBA; - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + if (clear_color) { + if (_sg.gl.cache.blend.color_write_mask != SG_COLORMASK_RGBA) { + need_pip_cache_flush = true; + _sg.gl.cache.blend.color_write_mask = SG_COLORMASK_RGBA; + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } } - if (!_sg.gl.cache.ds.depth_write_enabled) { - need_pip_cache_flush = true; - _sg.gl.cache.ds.depth_write_enabled = true; - glDepthMask(GL_TRUE); + if (clear_depth) { + if (!_sg.gl.cache.ds.depth_write_enabled) { + need_pip_cache_flush = true; + _sg.gl.cache.ds.depth_write_enabled = true; + glDepthMask(GL_TRUE); + } + if (_sg.gl.cache.ds.depth_compare_func != SG_COMPAREFUNC_ALWAYS) { + need_pip_cache_flush = true; + _sg.gl.cache.ds.depth_compare_func = SG_COMPAREFUNC_ALWAYS; + glDepthFunc(GL_ALWAYS); + } } - if (_sg.gl.cache.ds.depth_compare_func != SG_COMPAREFUNC_ALWAYS) { - need_pip_cache_flush = true; - _sg.gl.cache.ds.depth_compare_func = SG_COMPAREFUNC_ALWAYS; - glDepthFunc(GL_ALWAYS); - } - if (_sg.gl.cache.ds.stencil_write_mask != 0xFF) { - need_pip_cache_flush = true; - _sg.gl.cache.ds.stencil_write_mask = 0xFF; - glStencilMask(0xFF); + if (clear_stencil) { + if (_sg.gl.cache.ds.stencil_write_mask != 0xFF) { + need_pip_cache_flush = true; + _sg.gl.cache.ds.stencil_write_mask = 0xFF; + glStencilMask(0xFF); + } } if (need_pip_cache_flush) { /* we messed with the state cache directly, need to clear cached @@ -5822,12 +6307,12 @@ _SOKOL_PRIVATE void _sg_begin_pass(_sg_pass_t* pass, const sg_pass_action* actio #endif if (!use_mrt_clear) { GLbitfield clear_mask = 0; - if (action->colors[0].action == SG_ACTION_CLEAR) { + if (clear_color) { clear_mask |= GL_COLOR_BUFFER_BIT; const float* c = action->colors[0].val; glClearColor(c[0], c[1], c[2], c[3]); } - if (action->depth.action == SG_ACTION_CLEAR) { + if (clear_depth) { clear_mask |= GL_DEPTH_BUFFER_BIT; #ifdef SOKOL_GLCORE33 glClearDepth(action->depth.val); @@ -5835,7 +6320,7 @@ _SOKOL_PRIVATE void _sg_begin_pass(_sg_pass_t* pass, const sg_pass_action* actio glClearDepthf(action->depth.val); #endif } - if (action->stencil.action == SG_ACTION_CLEAR) { + if (clear_stencil) { clear_mask |= GL_STENCIL_BUFFER_BIT; glClearStencil(action->stencil.val); } @@ -5846,26 +6331,21 @@ _SOKOL_PRIVATE void _sg_begin_pass(_sg_pass_t* pass, const sg_pass_action* actio #if !defined SOKOL_GLES2 else { SOKOL_ASSERT(pass); - for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - if (pass->color_atts[i].image) { - if (action->colors[i].action == SG_ACTION_CLEAR) { - glClearBufferfv(GL_COLOR, i, action->colors[i].val); - } - } - else { - break; + for (int i = 0; i < num_color_atts; i++) { + if (action->colors[i].action == SG_ACTION_CLEAR) { + glClearBufferfv(GL_COLOR, i, action->colors[i].val); } } - if (pass->ds_att.image) { - if ((action->depth.action == SG_ACTION_CLEAR) && (action->stencil.action == SG_ACTION_CLEAR)) { + if (pass->gl.ds_att.image) { + if (clear_depth && clear_stencil) { glClearBufferfi(GL_DEPTH_STENCIL, 0, action->depth.val, action->stencil.val); } - else if (action->depth.action == SG_ACTION_CLEAR) { + else if (clear_depth) { glClearBufferfv(GL_DEPTH, 0, &action->depth.val); } - else if (action->stencil.action == SG_ACTION_CLEAR) { - GLuint val = action->stencil.val; - glClearBufferuiv(GL_STENCIL, 0, &val); + else if (clear_stencil) { + GLint val = (GLint) action->stencil.val; + glClearBufferiv(GL_STENCIL, 0, &val); } } } @@ -5873,7 +6353,7 @@ _SOKOL_PRIVATE void _sg_begin_pass(_sg_pass_t* pass, const sg_pass_action* actio _SG_GL_CHECK_ERROR(); } -_SOKOL_PRIVATE void _sg_end_pass(void) { +_SOKOL_PRIVATE void _sg_gl_end_pass(void) { SOKOL_ASSERT(_sg.gl.in_pass); _SG_GL_CHECK_ERROR(); @@ -5884,21 +6364,19 @@ _SOKOL_PRIVATE void _sg_end_pass(void) { /* check if the pass object is still valid */ const _sg_pass_t* pass = _sg.gl.cur_pass; SOKOL_ASSERT(pass->slot.id == _sg.gl.cur_pass_id.id); - bool is_msaa = (0 != _sg.gl.cur_pass->color_atts[0].gl_msaa_resolve_buffer); + bool is_msaa = (0 != _sg.gl.cur_pass->gl.color_atts[0].gl_msaa_resolve_buffer); if (is_msaa) { - SOKOL_ASSERT(pass->gl_fb); - glBindFramebuffer(GL_READ_FRAMEBUFFER, pass->gl_fb); - SOKOL_ASSERT(pass->color_atts[0].image); - const int w = pass->color_atts[0].image->width; - const int h = pass->color_atts[0].image->height; + SOKOL_ASSERT(pass->gl.fb); + glBindFramebuffer(GL_READ_FRAMEBUFFER, pass->gl.fb); + SOKOL_ASSERT(pass->gl.color_atts[0].image); + const int w = pass->gl.color_atts[0].image->cmn.width; + const int h = pass->gl.color_atts[0].image->cmn.height; for (int att_index = 0; att_index < SG_MAX_COLOR_ATTACHMENTS; att_index++) { - const _sg_attachment_t* att = &pass->color_atts[att_index]; - if (att->image) { - SOKOL_ASSERT(att->gl_msaa_resolve_buffer); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, att->gl_msaa_resolve_buffer); + const _sg_gl_attachment_t* gl_att = &pass->gl.color_atts[att_index]; + if (gl_att->image) { + SOKOL_ASSERT(gl_att->gl_msaa_resolve_buffer); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gl_att->gl_msaa_resolve_buffer); glReadBuffer(GL_COLOR_ATTACHMENT0 + att_index); - const GLenum gl_att = GL_COLOR_ATTACHMENT0; - glDrawBuffers(1, &gl_att); glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST); } else { @@ -5919,30 +6397,30 @@ _SOKOL_PRIVATE void _sg_end_pass(void) { _SG_GL_CHECK_ERROR(); } -_SOKOL_PRIVATE void _sg_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { +_SOKOL_PRIVATE void _sg_gl_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { SOKOL_ASSERT(_sg.gl.in_pass); y = origin_top_left ? (_sg.gl.cur_pass_height - (y+h)) : y; glViewport(x, y, w, h); } -_SOKOL_PRIVATE void _sg_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { +_SOKOL_PRIVATE void _sg_gl_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { SOKOL_ASSERT(_sg.gl.in_pass); y = origin_top_left ? (_sg.gl.cur_pass_height - (y+h)) : y; glScissor(x, y, w, h); } -_SOKOL_PRIVATE void _sg_apply_pipeline(_sg_pipeline_t* pip) { +_SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); - SOKOL_ASSERT(pip->shader); + SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); _SG_GL_CHECK_ERROR(); if ((_sg.gl.cache.cur_pipeline != pip) || (_sg.gl.cache.cur_pipeline_id.id != pip->slot.id)) { _sg.gl.cache.cur_pipeline = pip; _sg.gl.cache.cur_pipeline_id.id = pip->slot.id; - _sg.gl.cache.cur_primitive_type = _sg_gl_primitive_type(pip->primitive_type); - _sg.gl.cache.cur_index_type = _sg_gl_index_type(pip->index_type); + _sg.gl.cache.cur_primitive_type = _sg_gl_primitive_type(pip->gl.primitive_type); + _sg.gl.cache.cur_index_type = _sg_gl_index_type(pip->cmn.index_type); /* update depth-stencil state */ - const sg_depth_stencil_state* new_ds = &pip->depth_stencil; + const sg_depth_stencil_state* new_ds = &pip->gl.depth_stencil; sg_depth_stencil_state* cache_ds = &_sg.gl.cache.ds; if (new_ds->depth_compare_func != cache_ds->depth_compare_func) { cache_ds->depth_compare_func = new_ds->depth_compare_func; @@ -5992,7 +6470,7 @@ _SOKOL_PRIVATE void _sg_apply_pipeline(_sg_pipeline_t* pip) { cache_ds->stencil_ref = new_ds->stencil_ref; /* update blend state */ - const sg_blend_state* new_b = &pip->blend; + const sg_blend_state* new_b = &pip->gl.blend; sg_blend_state* cache_b = &_sg.gl.cache.blend; if (new_b->enabled != cache_b->enabled) { cache_b->enabled = new_b->enabled; @@ -6038,7 +6516,7 @@ _SOKOL_PRIVATE void _sg_apply_pipeline(_sg_pipeline_t* pip) { } /* update rasterizer state */ - const sg_rasterizer_state* new_r = &pip->rast; + const sg_rasterizer_state* new_r = &pip->gl.rast; sg_rasterizer_state* cache_r = &_sg.gl.cache.rast; if (new_r->cull_mode != cache_r->cull_mode) { cache_r->cull_mode = new_r->cull_mode; @@ -6093,11 +6571,14 @@ _SOKOL_PRIVATE void _sg_apply_pipeline(_sg_pipeline_t* pip) { } /* bind shader program */ - glUseProgram(pip->shader->gl_prog); + if (pip->shader->gl.prog != _sg.gl.cache.prog) { + _sg.gl.cache.prog = pip->shader->gl.prog; + glUseProgram(pip->shader->gl.prog); + } } } -_SOKOL_PRIVATE void _sg_apply_bindings( +_SOKOL_PRIVATE void _sg_gl_apply_bindings( _sg_pipeline_t* pip, _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, _sg_buffer_t* ib, int ib_offset, @@ -6113,31 +6594,31 @@ _SOKOL_PRIVATE void _sg_apply_bindings( /* bind textures */ _SG_GL_CHECK_ERROR(); for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { - const _sg_shader_stage_t* stage = &pip->shader->stage[stage_index]; + const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[stage_index]; + const _sg_gl_shader_stage_t* gl_stage = &pip->shader->gl.stage[stage_index]; _sg_image_t** imgs = (stage_index == SG_SHADERSTAGE_VS)? vs_imgs : fs_imgs; SOKOL_ASSERT(((stage_index == SG_SHADERSTAGE_VS)? num_vs_imgs : num_fs_imgs) == stage->num_images); for (int img_index = 0; img_index < stage->num_images; img_index++) { - const _sg_shader_image_t* shd_img = &stage->images[img_index]; - if (shd_img->gl_loc != -1) { + const _sg_gl_shader_image_t* gl_shd_img = &gl_stage->images[img_index]; + if (gl_shd_img->gl_tex_slot != -1) { _sg_image_t* img = imgs[img_index]; - const GLuint gl_tex = img->gl_tex[img->active_slot]; - SOKOL_ASSERT(img && img->gl_target); - SOKOL_ASSERT((shd_img->gl_tex_slot != -1) && gl_tex); - glUniform1i(shd_img->gl_loc, shd_img->gl_tex_slot); - _sg_gl_bind_texture(shd_img->gl_tex_slot, img->gl_target, gl_tex); + const GLuint gl_tex = img->gl.tex[img->cmn.active_slot]; + SOKOL_ASSERT(img && img->gl.target); + SOKOL_ASSERT((gl_shd_img->gl_tex_slot != -1) && gl_tex); + _sg_gl_cache_bind_texture(gl_shd_img->gl_tex_slot, img->gl.target, gl_tex); } } } _SG_GL_CHECK_ERROR(); /* index buffer (can be 0) */ - const GLuint gl_ib = ib ? ib->gl_buf[ib->active_slot] : 0; - _sg_gl_bind_buffer(GL_ELEMENT_ARRAY_BUFFER, gl_ib); + const GLuint gl_ib = ib ? ib->gl.buf[ib->cmn.active_slot] : 0; + _sg_gl_cache_bind_buffer(GL_ELEMENT_ARRAY_BUFFER, gl_ib); _sg.gl.cache.cur_ib_offset = ib_offset; /* vertex attributes */ for (uint32_t attr_index = 0; attr_index < _sg.limits.max_vertex_attrs; attr_index++) { - _sg_gl_attr_t* attr = &pip->gl_attrs[attr_index]; + _sg_gl_attr_t* attr = &pip->gl.attrs[attr_index]; _sg_gl_cache_attr_t* cache_attr = &_sg.gl.cache.attrs[attr_index]; bool cache_attr_dirty = false; int vb_offset = 0; @@ -6147,7 +6628,7 @@ _SOKOL_PRIVATE void _sg_apply_bindings( SOKOL_ASSERT(attr->vb_index < num_vbs); _sg_buffer_t* vb = vbs[attr->vb_index]; SOKOL_ASSERT(vb); - gl_vb = vb->gl_buf[vb->active_slot]; + gl_vb = vb->gl.buf[vb->cmn.active_slot]; vb_offset = vb_offsets[attr->vb_index] + attr->offset; if ((gl_vb != cache_attr->gl_vbuf) || (attr->size != cache_attr->gl_attr.size) || @@ -6157,7 +6638,7 @@ _SOKOL_PRIVATE void _sg_apply_bindings( (vb_offset != cache_attr->gl_attr.offset) || (cache_attr->gl_attr.divisor != attr->divisor)) { - _sg_gl_bind_buffer(GL_ARRAY_BUFFER, gl_vb); + _sg_gl_cache_bind_buffer(GL_ARRAY_BUFFER, gl_vb); glVertexAttribPointer(attr_index, attr->size, attr->type, attr->normalized, attr->stride, (const GLvoid*)(GLintptr)vb_offset); @@ -6189,19 +6670,19 @@ _SOKOL_PRIVATE void _sg_apply_bindings( _SG_GL_CHECK_ERROR(); } -_SOKOL_PRIVATE void _sg_apply_uniforms(sg_shader_stage stage_index, int ub_index, const void* data, int num_bytes) { +_SOKOL_PRIVATE void _sg_gl_apply_uniforms(sg_shader_stage stage_index, int ub_index, const void* data, int num_bytes) { _SOKOL_UNUSED(num_bytes); SOKOL_ASSERT(data && (num_bytes > 0)); SOKOL_ASSERT((stage_index >= 0) && ((int)stage_index < SG_NUM_SHADER_STAGES)); SOKOL_ASSERT(_sg.gl.cache.cur_pipeline); SOKOL_ASSERT(_sg.gl.cache.cur_pipeline->slot.id == _sg.gl.cache.cur_pipeline_id.id); - SOKOL_ASSERT(_sg.gl.cache.cur_pipeline->shader->slot.id == _sg.gl.cache.cur_pipeline->shader_id.id); - _sg_shader_stage_t* stage = &_sg.gl.cache.cur_pipeline->shader->stage[stage_index]; - SOKOL_ASSERT(ub_index < stage->num_uniform_blocks); - _sg_uniform_block_t* ub = &stage->uniform_blocks[ub_index]; - SOKOL_ASSERT(ub->size == num_bytes); - for (int u_index = 0; u_index < ub->num_uniforms; u_index++) { - _sg_uniform_t* u = &ub->uniforms[u_index]; + SOKOL_ASSERT(_sg.gl.cache.cur_pipeline->shader->slot.id == _sg.gl.cache.cur_pipeline->cmn.shader_id.id); + SOKOL_ASSERT(_sg.gl.cache.cur_pipeline->shader->cmn.stage[stage_index].num_uniform_blocks > ub_index); + SOKOL_ASSERT(_sg.gl.cache.cur_pipeline->shader->cmn.stage[stage_index].uniform_blocks[ub_index].size == num_bytes); + const _sg_gl_shader_stage_t* gl_stage = &_sg.gl.cache.cur_pipeline->shader->gl.stage[stage_index]; + const _sg_gl_uniform_block_t* gl_ub = &gl_stage->uniform_blocks[ub_index]; + for (int u_index = 0; u_index < gl_ub->num_uniforms; u_index++) { + const _sg_gl_uniform_t* u = &gl_ub->uniforms[u_index]; SOKOL_ASSERT(u->type != SG_UNIFORMTYPE_INVALID); if (u->gl_loc == -1) { continue; @@ -6232,7 +6713,7 @@ _SOKOL_PRIVATE void _sg_apply_uniforms(sg_shader_stage stage_index, int ub_index } } -_SOKOL_PRIVATE void _sg_draw(int base_element, int num_elements, int num_instances) { +_SOKOL_PRIVATE void _sg_gl_draw(int base_element, int num_elements, int num_instances) { const GLenum i_type = _sg.gl.cache.cur_index_type; const GLenum p_type = _sg.gl.cache.cur_primitive_type; if (0 != i_type) { @@ -6262,80 +6743,82 @@ _SOKOL_PRIVATE void _sg_draw(int base_element, int num_elements, int num_instanc } } -_SOKOL_PRIVATE void _sg_commit(void) { +_SOKOL_PRIVATE void _sg_gl_commit(void) { SOKOL_ASSERT(!_sg.gl.in_pass); /* "soft" clear bindings (only those that are actually bound) */ - _sg_gl_clear_buffer_bindings(false); - _sg_gl_clear_texture_bindings(false); + _sg_gl_cache_clear_buffer_bindings(false); + _sg_gl_cache_clear_texture_bindings(false); } -_SOKOL_PRIVATE void _sg_update_buffer(_sg_buffer_t* buf, const void* data_ptr, int data_size) { +_SOKOL_PRIVATE void _sg_gl_update_buffer(_sg_buffer_t* buf, const void* data_ptr, uint32_t data_size) { SOKOL_ASSERT(buf && data_ptr && (data_size > 0)); /* only one update per buffer per frame allowed */ - if (++buf->active_slot >= buf->num_slots) { - buf->active_slot = 0; + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; } - GLenum gl_tgt = _sg_gl_buffer_target(buf->type); - SOKOL_ASSERT(buf->active_slot < SG_NUM_INFLIGHT_FRAMES); - GLuint gl_buf = buf->gl_buf[buf->active_slot]; + GLenum gl_tgt = _sg_gl_buffer_target(buf->cmn.type); + SOKOL_ASSERT(buf->cmn.active_slot < SG_NUM_INFLIGHT_FRAMES); + GLuint gl_buf = buf->gl.buf[buf->cmn.active_slot]; SOKOL_ASSERT(gl_buf); _SG_GL_CHECK_ERROR(); - _sg_gl_store_buffer_binding(gl_tgt); - _sg_gl_bind_buffer(gl_tgt, gl_buf); + _sg_gl_cache_store_buffer_binding(gl_tgt); + _sg_gl_cache_bind_buffer(gl_tgt, gl_buf); glBufferSubData(gl_tgt, 0, data_size, data_ptr); - _sg_gl_restore_buffer_binding(gl_tgt); + _sg_gl_cache_restore_buffer_binding(gl_tgt); _SG_GL_CHECK_ERROR(); } -_SOKOL_PRIVATE void _sg_append_buffer(_sg_buffer_t* buf, const void* data_ptr, int data_size, bool new_frame) { +_SOKOL_PRIVATE uint32_t _sg_gl_append_buffer(_sg_buffer_t* buf, const void* data_ptr, uint32_t data_size, bool new_frame) { SOKOL_ASSERT(buf && data_ptr && (data_size > 0)); if (new_frame) { - if (++buf->active_slot >= buf->num_slots) { - buf->active_slot = 0; + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; } } - GLenum gl_tgt = _sg_gl_buffer_target(buf->type); - SOKOL_ASSERT(buf->active_slot < SG_NUM_INFLIGHT_FRAMES); - GLuint gl_buf = buf->gl_buf[buf->active_slot]; + GLenum gl_tgt = _sg_gl_buffer_target(buf->cmn.type); + SOKOL_ASSERT(buf->cmn.active_slot < SG_NUM_INFLIGHT_FRAMES); + GLuint gl_buf = buf->gl.buf[buf->cmn.active_slot]; SOKOL_ASSERT(gl_buf); _SG_GL_CHECK_ERROR(); - _sg_gl_store_buffer_binding(gl_tgt); - _sg_gl_bind_buffer(gl_tgt, gl_buf); - glBufferSubData(gl_tgt, buf->append_pos, data_size, data_ptr); - _sg_gl_restore_buffer_binding(gl_tgt); + _sg_gl_cache_store_buffer_binding(gl_tgt); + _sg_gl_cache_bind_buffer(gl_tgt, gl_buf); + glBufferSubData(gl_tgt, buf->cmn.append_pos, data_size, data_ptr); + _sg_gl_cache_restore_buffer_binding(gl_tgt); _SG_GL_CHECK_ERROR(); + /* NOTE: this is a requirement from WebGPU, but we want identical behaviour across all backend */ + return _sg_roundup(data_size, 4); } -_SOKOL_PRIVATE void _sg_update_image(_sg_image_t* img, const sg_image_content* data) { +_SOKOL_PRIVATE void _sg_gl_update_image(_sg_image_t* img, const sg_image_content* data) { SOKOL_ASSERT(img && data); /* only one update per image per frame allowed */ - if (++img->active_slot >= img->num_slots) { - img->active_slot = 0; + if (++img->cmn.active_slot >= img->cmn.num_slots) { + img->cmn.active_slot = 0; } - SOKOL_ASSERT(img->active_slot < SG_NUM_INFLIGHT_FRAMES); - SOKOL_ASSERT(0 != img->gl_tex[img->active_slot]); - _sg_gl_store_texture_binding(0); - _sg_gl_bind_texture(0, img->gl_target, img->gl_tex[img->active_slot]); - const GLenum gl_img_format = _sg_gl_teximage_format(img->pixel_format); - const GLenum gl_img_type = _sg_gl_teximage_type(img->pixel_format); - const int num_faces = img->type == SG_IMAGETYPE_CUBE ? 6 : 1; - const int num_mips = img->num_mipmaps; + SOKOL_ASSERT(img->cmn.active_slot < SG_NUM_INFLIGHT_FRAMES); + SOKOL_ASSERT(0 != img->gl.tex[img->cmn.active_slot]); + _sg_gl_cache_store_texture_binding(0); + _sg_gl_cache_bind_texture(0, img->gl.target, img->gl.tex[img->cmn.active_slot]); + const GLenum gl_img_format = _sg_gl_teximage_format(img->cmn.pixel_format); + const GLenum gl_img_type = _sg_gl_teximage_type(img->cmn.pixel_format); + const int num_faces = img->cmn.type == SG_IMAGETYPE_CUBE ? 6 : 1; + const int num_mips = img->cmn.num_mipmaps; for (int face_index = 0; face_index < num_faces; face_index++) { for (int mip_index = 0; mip_index < num_mips; mip_index++) { - GLenum gl_img_target = img->gl_target; - if (SG_IMAGETYPE_CUBE == img->type) { + GLenum gl_img_target = img->gl.target; + if (SG_IMAGETYPE_CUBE == img->cmn.type) { gl_img_target = _sg_gl_cubeface_target(face_index); } const GLvoid* data_ptr = data->subimage[face_index][mip_index].ptr; - int mip_width = img->width >> mip_index; + int mip_width = img->cmn.width >> mip_index; if (mip_width == 0) { mip_width = 1; } - int mip_height = img->height >> mip_index; + int mip_height = img->cmn.height >> mip_index; if (mip_height == 0) { mip_height = 1; } - if ((SG_IMAGETYPE_2D == img->type) || (SG_IMAGETYPE_CUBE == img->type)) { + if ((SG_IMAGETYPE_2D == img->cmn.type) || (SG_IMAGETYPE_CUBE == img->cmn.type)) { glTexSubImage2D(gl_img_target, mip_index, 0, 0, mip_width, mip_height, @@ -6343,8 +6826,8 @@ _SOKOL_PRIVATE void _sg_update_image(_sg_image_t* img, const sg_image_content* d data_ptr); } #if !defined(SOKOL_GLES2) - else if (!_sg.gl.gles2 && ((SG_IMAGETYPE_3D == img->type) || (SG_IMAGETYPE_ARRAY == img->type))) { - int mip_depth = img->depth >> mip_index; + else if (!_sg.gl.gles2 && ((SG_IMAGETYPE_3D == img->cmn.type) || (SG_IMAGETYPE_ARRAY == img->cmn.type))) { + int mip_depth = img->cmn.depth >> mip_index; if (mip_depth == 0) { mip_depth = 1; } @@ -6358,7 +6841,7 @@ _SOKOL_PRIVATE void _sg_update_image(_sg_image_t* img, const sg_image_content* d #endif } } - _sg_gl_restore_texture_binding(0); + _sg_gl_cache_restore_texture_binding(0); } /*== D3D11 BACKEND IMPLEMENTATION ============================================*/ @@ -6652,11 +7135,16 @@ _SOKOL_PRIVATE void _sg_d3d11_init_caps(void) { _sg.limits.max_vertex_attrs = SG_MAX_VERTEX_ATTRIBUTES; /* see: https://docs.microsoft.com/en-us/windows/win32/api/d3d11/ne-d3d11-d3d11_format_support */ - UINT dxgi_fmt_caps = 0; for (int fmt = (SG_PIXELFORMAT_NONE+1); fmt < _SG_PIXELFORMAT_NUM; fmt++) { - DXGI_FORMAT dxgi_fmt = _sg_d3d11_pixel_format((sg_pixel_format)fmt); - HRESULT hr = ID3D11Device_CheckFormatSupport(_sg.d3d11.dev, dxgi_fmt, &dxgi_fmt_caps); - SOKOL_ASSERT(SUCCEEDED(hr)); + UINT dxgi_fmt_caps = 0; + const DXGI_FORMAT dxgi_fmt = _sg_d3d11_pixel_format((sg_pixel_format)fmt); + if (dxgi_fmt != DXGI_FORMAT_UNKNOWN) { + HRESULT hr = ID3D11Device_CheckFormatSupport(_sg.d3d11.dev, dxgi_fmt, &dxgi_fmt_caps); + SOKOL_ASSERT(SUCCEEDED(hr) || (E_FAIL == hr)); + if (!SUCCEEDED(hr)) { + dxgi_fmt_caps = 0; + } + } sg_pixelformat_info* info = &_sg.formats[fmt]; info->sample = 0 != (dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_TEXTURE2D); info->filter = 0 != (dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_SHADER_SAMPLE); @@ -6670,23 +7158,23 @@ _SOKOL_PRIVATE void _sg_d3d11_init_caps(void) { } } -_SOKOL_PRIVATE void _sg_setup_backend(const sg_desc* desc) { +_SOKOL_PRIVATE void _sg_d3d11_setup_backend(const sg_desc* desc) { /* assume _sg.d3d11 already is zero-initialized */ SOKOL_ASSERT(desc); - SOKOL_ASSERT(desc->d3d11_device); - SOKOL_ASSERT(desc->d3d11_device_context); - SOKOL_ASSERT(desc->d3d11_render_target_view_cb); - SOKOL_ASSERT(desc->d3d11_depth_stencil_view_cb); - SOKOL_ASSERT(desc->d3d11_render_target_view_cb != desc->d3d11_depth_stencil_view_cb); + SOKOL_ASSERT(desc->context.d3d11.device); + SOKOL_ASSERT(desc->context.d3d11.device_context); + SOKOL_ASSERT(desc->context.d3d11.render_target_view_cb); + SOKOL_ASSERT(desc->context.d3d11.depth_stencil_view_cb); + SOKOL_ASSERT(desc->context.d3d11.render_target_view_cb != desc->context.d3d11.depth_stencil_view_cb); _sg.d3d11.valid = true; - _sg.d3d11.dev = (ID3D11Device*) desc->d3d11_device; - _sg.d3d11.ctx = (ID3D11DeviceContext*) desc->d3d11_device_context; - _sg.d3d11.rtv_cb = desc->d3d11_render_target_view_cb; - _sg.d3d11.dsv_cb = desc->d3d11_depth_stencil_view_cb; + _sg.d3d11.dev = (ID3D11Device*) desc->context.d3d11.device; + _sg.d3d11.ctx = (ID3D11DeviceContext*) desc->context.d3d11.device_context; + _sg.d3d11.rtv_cb = desc->context.d3d11.render_target_view_cb; + _sg.d3d11.dsv_cb = desc->context.d3d11.depth_stencil_view_cb; _sg_d3d11_init_caps(); } -_SOKOL_PRIVATE void _sg_discard_backend(void) { +_SOKOL_PRIVATE void _sg_d3d11_discard_backend(void) { SOKOL_ASSERT(_sg.d3d11.valid); _sg.d3d11.valid = false; } @@ -6710,92 +7198,86 @@ _SOKOL_PRIVATE void _sg_d3d11_clear_state(void) { ID3D11DeviceContext_PSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, _sg.d3d11.zero_smps); } -_SOKOL_PRIVATE void _sg_reset_state_cache(void) { +_SOKOL_PRIVATE void _sg_d3d11_reset_state_cache(void) { /* just clear the d3d11 device context state */ _sg_d3d11_clear_state(); } -_SOKOL_PRIVATE void _sg_activate_context(_sg_context_t* ctx) { +_SOKOL_PRIVATE void _sg_d3d11_activate_context(_sg_context_t* ctx) { _SOKOL_UNUSED(ctx); - _sg_reset_state_cache(); + _sg_d3d11_clear_state(); } -_SOKOL_PRIVATE sg_resource_state _sg_create_context(_sg_context_t* ctx) { +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_context(_sg_context_t* ctx) { SOKOL_ASSERT(ctx); _SOKOL_UNUSED(ctx); return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_destroy_context(_sg_context_t* ctx) { +_SOKOL_PRIVATE void _sg_d3d11_destroy_context(_sg_context_t* ctx) { SOKOL_ASSERT(ctx); _SOKOL_UNUSED(ctx); /* empty */ } -_SOKOL_PRIVATE sg_resource_state _sg_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { SOKOL_ASSERT(buf && desc); - SOKOL_ASSERT(!buf->d3d11_buf); - buf->size = desc->size; - buf->append_pos = 0; - buf->append_overflow = false; - buf->type = desc->type; - buf->usage = desc->usage; - buf->update_frame_index = 0; - buf->append_frame_index = 0; + SOKOL_ASSERT(!buf->d3d11.buf); + _sg_buffer_common_init(&buf->cmn, desc); const bool injected = (0 != desc->d3d11_buffer); if (injected) { - buf->d3d11_buf = (ID3D11Buffer*) desc->d3d11_buffer; - ID3D11Buffer_AddRef(buf->d3d11_buf); + buf->d3d11.buf = (ID3D11Buffer*) desc->d3d11_buffer; + ID3D11Buffer_AddRef(buf->d3d11.buf); } else { D3D11_BUFFER_DESC d3d11_desc; memset(&d3d11_desc, 0, sizeof(d3d11_desc)); - d3d11_desc.ByteWidth = buf->size; - d3d11_desc.Usage = _sg_d3d11_usage(buf->usage); - d3d11_desc.BindFlags = buf->type == SG_BUFFERTYPE_VERTEXBUFFER ? D3D11_BIND_VERTEX_BUFFER : D3D11_BIND_INDEX_BUFFER; - d3d11_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(buf->usage); + d3d11_desc.ByteWidth = buf->cmn.size; + d3d11_desc.Usage = _sg_d3d11_usage(buf->cmn.usage); + d3d11_desc.BindFlags = buf->cmn.type == SG_BUFFERTYPE_VERTEXBUFFER ? D3D11_BIND_VERTEX_BUFFER : D3D11_BIND_INDEX_BUFFER; + d3d11_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(buf->cmn.usage); D3D11_SUBRESOURCE_DATA* init_data_ptr = 0; D3D11_SUBRESOURCE_DATA init_data; memset(&init_data, 0, sizeof(init_data)); - if (buf->usage == SG_USAGE_IMMUTABLE) { + if (buf->cmn.usage == SG_USAGE_IMMUTABLE) { SOKOL_ASSERT(desc->content); init_data.pSysMem = desc->content; init_data_ptr = &init_data; } - HRESULT hr = ID3D11Device_CreateBuffer(_sg.d3d11.dev, &d3d11_desc, init_data_ptr, &buf->d3d11_buf); + HRESULT hr = ID3D11Device_CreateBuffer(_sg.d3d11.dev, &d3d11_desc, init_data_ptr, &buf->d3d11.buf); _SOKOL_UNUSED(hr); - SOKOL_ASSERT(SUCCEEDED(hr) && buf->d3d11_buf); + SOKOL_ASSERT(SUCCEEDED(hr) && buf->d3d11.buf); } return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_destroy_buffer(_sg_buffer_t* buf) { +_SOKOL_PRIVATE void _sg_d3d11_destroy_buffer(_sg_buffer_t* buf) { SOKOL_ASSERT(buf); - if (buf->d3d11_buf) { - ID3D11Buffer_Release(buf->d3d11_buf); + if (buf->d3d11.buf) { + ID3D11Buffer_Release(buf->d3d11.buf); } } _SOKOL_PRIVATE void _sg_d3d11_fill_subres_data(const _sg_image_t* img, const sg_image_content* content) { - const int num_faces = (img->type == SG_IMAGETYPE_CUBE) ? 6:1; - const int num_slices = (img->type == SG_IMAGETYPE_ARRAY) ? img->depth:1; + const int num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6:1; + const int num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.depth:1; int subres_index = 0; for (int face_index = 0; face_index < num_faces; face_index++) { for (int slice_index = 0; slice_index < num_slices; slice_index++) { - for (int mip_index = 0; mip_index < img->num_mipmaps; mip_index++, subres_index++) { + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++, subres_index++) { SOKOL_ASSERT(subres_index < (SG_MAX_MIPMAPS * SG_MAX_TEXTUREARRAY_LAYERS)); D3D11_SUBRESOURCE_DATA* subres_data = &_sg.d3d11.subres_data[subres_index]; - const int mip_width = ((img->width>>mip_index)>0) ? img->width>>mip_index : 1; - const int mip_height = ((img->height>>mip_index)>0) ? img->height>>mip_index : 1; + const int mip_width = ((img->cmn.width>>mip_index)>0) ? img->cmn.width>>mip_index : 1; + const int mip_height = ((img->cmn.height>>mip_index)>0) ? img->cmn.height>>mip_index : 1; const sg_subimage_content* subimg_content = &(content->subimage[face_index][mip_index]); const int slice_size = subimg_content->size / num_slices; const int slice_offset = slice_size * slice_index; const uint8_t* ptr = (const uint8_t*) subimg_content->ptr; subres_data->pSysMem = ptr + slice_offset; - subres_data->SysMemPitch = _sg_row_pitch(img->pixel_format, mip_width); - if (img->type == SG_IMAGETYPE_3D) { + subres_data->SysMemPitch = _sg_row_pitch(img->cmn.pixel_format, mip_width, 1); + if (img->cmn.type == SG_IMAGETYPE_3D) { /* FIXME? const int mip_depth = ((img->depth>>mip_index)>0) ? img->depth>>mip_index : 1; */ - subres_data->SysMemSlicePitch = _sg_surface_pitch(img->pixel_format, mip_width, mip_height); + subres_data->SysMemSlicePitch = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1); } else { subres_data->SysMemSlicePitch = 0; @@ -6805,81 +7287,66 @@ _SOKOL_PRIVATE void _sg_d3d11_fill_subres_data(const _sg_image_t* img, const sg_ } } -_SOKOL_PRIVATE sg_resource_state _sg_create_image(_sg_image_t* img, const sg_image_desc* desc) { +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const sg_image_desc* desc) { SOKOL_ASSERT(img && desc); - SOKOL_ASSERT(!img->d3d11_tex2d && !img->d3d11_tex3d && !img->d3d11_texds && !img->d3d11_texmsaa); - SOKOL_ASSERT(!img->d3d11_srv && !img->d3d11_smp); + SOKOL_ASSERT(!img->d3d11.tex2d && !img->d3d11.tex3d && !img->d3d11.texds && !img->d3d11.texmsaa); + SOKOL_ASSERT(!img->d3d11.srv && !img->d3d11.smp); HRESULT hr; + _SOKOL_UNUSED(hr); - img->type = desc->type; - img->render_target = desc->render_target; - img->width = desc->width; - img->height = desc->height; - img->depth = desc->depth; - img->num_mipmaps = desc->num_mipmaps; - img->usage = desc->usage; - img->pixel_format = desc->pixel_format; - img->sample_count = desc->sample_count; - img->min_filter = desc->min_filter; - img->mag_filter = desc->mag_filter; - img->wrap_u = desc->wrap_u; - img->wrap_v = desc->wrap_v; - img->wrap_w = desc->wrap_w; - img->border_color = desc->border_color; - img->max_anisotropy = desc->max_anisotropy; - img->upd_frame_index = 0; + _sg_image_common_init(&img->cmn, desc); const bool injected = (0 != desc->d3d11_texture); - const bool msaa = (img->sample_count > 1); + const bool msaa = (img->cmn.sample_count > 1); /* special case depth-stencil buffer? */ - if (_sg_is_valid_rendertarget_depth_format(img->pixel_format)) { + if (_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format)) { /* create only a depth-texture */ SOKOL_ASSERT(!injected); - img->d3d11_format = _sg_d3d11_pixel_format(img->pixel_format); - if (img->d3d11_format == DXGI_FORMAT_UNKNOWN) { + img->d3d11.format = _sg_d3d11_pixel_format(img->cmn.pixel_format); + if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) { SOKOL_LOG("trying to create a D3D11 depth-texture with unsupported pixel format\n"); return SG_RESOURCESTATE_FAILED; } D3D11_TEXTURE2D_DESC d3d11_desc; memset(&d3d11_desc, 0, sizeof(d3d11_desc)); - d3d11_desc.Width = img->width; - d3d11_desc.Height = img->height; + d3d11_desc.Width = img->cmn.width; + d3d11_desc.Height = img->cmn.height; d3d11_desc.MipLevels = 1; d3d11_desc.ArraySize = 1; - d3d11_desc.Format = img->d3d11_format; + d3d11_desc.Format = img->d3d11.format; d3d11_desc.Usage = D3D11_USAGE_DEFAULT; d3d11_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; - d3d11_desc.SampleDesc.Count = img->sample_count; + d3d11_desc.SampleDesc.Count = img->cmn.sample_count; d3d11_desc.SampleDesc.Quality = msaa ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0; - hr = ID3D11Device_CreateTexture2D(_sg.d3d11.dev, &d3d11_desc, NULL, &img->d3d11_texds); - SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11_texds); + hr = ID3D11Device_CreateTexture2D(_sg.d3d11.dev, &d3d11_desc, NULL, &img->d3d11.texds); + SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11.texds); } else { /* create (or inject) color texture */ /* prepare initial content pointers */ D3D11_SUBRESOURCE_DATA* init_data = 0; - if (!injected && (img->usage == SG_USAGE_IMMUTABLE) && !img->render_target) { + if (!injected && (img->cmn.usage == SG_USAGE_IMMUTABLE) && !img->cmn.render_target) { _sg_d3d11_fill_subres_data(img, &desc->content); init_data = _sg.d3d11.subres_data; } - if (img->type != SG_IMAGETYPE_3D) { + if (img->cmn.type != SG_IMAGETYPE_3D) { /* 2D-, cube- or array-texture */ /* if this is an MSAA render target, the following texture will be the 'resolve-texture' */ D3D11_TEXTURE2D_DESC d3d11_tex_desc; memset(&d3d11_tex_desc, 0, sizeof(d3d11_tex_desc)); - d3d11_tex_desc.Width = img->width; - d3d11_tex_desc.Height = img->height; - d3d11_tex_desc.MipLevels = img->num_mipmaps; - switch (img->type) { - case SG_IMAGETYPE_ARRAY: d3d11_tex_desc.ArraySize = img->depth; break; + d3d11_tex_desc.Width = img->cmn.width; + d3d11_tex_desc.Height = img->cmn.height; + d3d11_tex_desc.MipLevels = img->cmn.num_mipmaps; + switch (img->cmn.type) { + case SG_IMAGETYPE_ARRAY: d3d11_tex_desc.ArraySize = img->cmn.depth; break; case SG_IMAGETYPE_CUBE: d3d11_tex_desc.ArraySize = 6; break; default: d3d11_tex_desc.ArraySize = 1; break; } d3d11_tex_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; - if (img->render_target) { - img->d3d11_format = _sg_d3d11_pixel_format(img->pixel_format); - d3d11_tex_desc.Format = img->d3d11_format; + if (img->cmn.render_target) { + img->d3d11.format = _sg_d3d11_pixel_format(img->cmn.pixel_format); + d3d11_tex_desc.Format = img->d3d11.format; d3d11_tex_desc.Usage = D3D11_USAGE_DEFAULT; if (!msaa) { d3d11_tex_desc.BindFlags |= D3D11_BIND_RENDER_TARGET; @@ -6887,64 +7354,64 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_image(_sg_image_t* img, const sg_ima d3d11_tex_desc.CPUAccessFlags = 0; } else { - img->d3d11_format = _sg_d3d11_pixel_format(img->pixel_format); - d3d11_tex_desc.Format = img->d3d11_format; - d3d11_tex_desc.Usage = _sg_d3d11_usage(img->usage); - d3d11_tex_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(img->usage); + img->d3d11.format = _sg_d3d11_pixel_format(img->cmn.pixel_format); + d3d11_tex_desc.Format = img->d3d11.format; + d3d11_tex_desc.Usage = _sg_d3d11_usage(img->cmn.usage); + d3d11_tex_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(img->cmn.usage); } - if (img->d3d11_format == DXGI_FORMAT_UNKNOWN) { + if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) { /* trying to create a texture format that's not supported by D3D */ SOKOL_LOG("trying to create a D3D11 texture with unsupported pixel format\n"); return SG_RESOURCESTATE_FAILED; } d3d11_tex_desc.SampleDesc.Count = 1; d3d11_tex_desc.SampleDesc.Quality = 0; - d3d11_tex_desc.MiscFlags = (img->type == SG_IMAGETYPE_CUBE) ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0; + d3d11_tex_desc.MiscFlags = (img->cmn.type == SG_IMAGETYPE_CUBE) ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0; if (injected) { - img->d3d11_tex2d = (ID3D11Texture2D*) desc->d3d11_texture; - ID3D11Texture2D_AddRef(img->d3d11_tex2d); + img->d3d11.tex2d = (ID3D11Texture2D*) desc->d3d11_texture; + ID3D11Texture2D_AddRef(img->d3d11.tex2d); } else { - hr = ID3D11Device_CreateTexture2D(_sg.d3d11.dev, &d3d11_tex_desc, init_data, &img->d3d11_tex2d); - SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11_tex2d); + hr = ID3D11Device_CreateTexture2D(_sg.d3d11.dev, &d3d11_tex_desc, init_data, &img->d3d11.tex2d); + SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11.tex2d); } /* shader-resource-view */ D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; memset(&d3d11_srv_desc, 0, sizeof(d3d11_srv_desc)); d3d11_srv_desc.Format = d3d11_tex_desc.Format; - switch (img->type) { + switch (img->cmn.type) { case SG_IMAGETYPE_2D: d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; - d3d11_srv_desc.Texture2D.MipLevels = img->num_mipmaps; + d3d11_srv_desc.Texture2D.MipLevels = img->cmn.num_mipmaps; break; case SG_IMAGETYPE_CUBE: d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; - d3d11_srv_desc.TextureCube.MipLevels = img->num_mipmaps; + d3d11_srv_desc.TextureCube.MipLevels = img->cmn.num_mipmaps; break; case SG_IMAGETYPE_ARRAY: d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; - d3d11_srv_desc.Texture2DArray.MipLevels = img->num_mipmaps; - d3d11_srv_desc.Texture2DArray.ArraySize = img->depth; + d3d11_srv_desc.Texture2DArray.MipLevels = img->cmn.num_mipmaps; + d3d11_srv_desc.Texture2DArray.ArraySize = img->cmn.depth; break; default: SOKOL_UNREACHABLE; break; } - hr = ID3D11Device_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)img->d3d11_tex2d, &d3d11_srv_desc, &img->d3d11_srv); - SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11_srv); + hr = ID3D11Device_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)img->d3d11.tex2d, &d3d11_srv_desc, &img->d3d11.srv); + SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11.srv); } else { /* 3D texture */ D3D11_TEXTURE3D_DESC d3d11_tex_desc; memset(&d3d11_tex_desc, 0, sizeof(d3d11_tex_desc)); - d3d11_tex_desc.Width = img->width; - d3d11_tex_desc.Height = img->height; - d3d11_tex_desc.Depth = img->depth; - d3d11_tex_desc.MipLevels = img->num_mipmaps; + d3d11_tex_desc.Width = img->cmn.width; + d3d11_tex_desc.Height = img->cmn.height; + d3d11_tex_desc.Depth = img->cmn.depth; + d3d11_tex_desc.MipLevels = img->cmn.num_mipmaps; d3d11_tex_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; - if (img->render_target) { - img->d3d11_format = _sg_d3d11_pixel_format(img->pixel_format); - d3d11_tex_desc.Format = img->d3d11_format; + if (img->cmn.render_target) { + img->d3d11.format = _sg_d3d11_pixel_format(img->cmn.pixel_format); + d3d11_tex_desc.Format = img->d3d11.format; d3d11_tex_desc.Usage = D3D11_USAGE_DEFAULT; if (!msaa) { d3d11_tex_desc.BindFlags |= D3D11_BIND_RENDER_TARGET; @@ -6952,23 +7419,23 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_image(_sg_image_t* img, const sg_ima d3d11_tex_desc.CPUAccessFlags = 0; } else { - img->d3d11_format = _sg_d3d11_pixel_format(img->pixel_format); - d3d11_tex_desc.Format = img->d3d11_format; - d3d11_tex_desc.Usage = _sg_d3d11_usage(img->usage); - d3d11_tex_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(img->usage); + img->d3d11.format = _sg_d3d11_pixel_format(img->cmn.pixel_format); + d3d11_tex_desc.Format = img->d3d11.format; + d3d11_tex_desc.Usage = _sg_d3d11_usage(img->cmn.usage); + d3d11_tex_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(img->cmn.usage); } - if (img->d3d11_format == DXGI_FORMAT_UNKNOWN) { + if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) { /* trying to create a texture format that's not supported by D3D */ SOKOL_LOG("trying to create a D3D11 texture with unsupported pixel format\n"); return SG_RESOURCESTATE_FAILED; } if (injected) { - img->d3d11_tex3d = (ID3D11Texture3D*) desc->d3d11_texture; - ID3D11Texture3D_AddRef(img->d3d11_tex3d); + img->d3d11.tex3d = (ID3D11Texture3D*) desc->d3d11_texture; + ID3D11Texture3D_AddRef(img->d3d11.tex3d); } else { - hr = ID3D11Device_CreateTexture3D(_sg.d3d11.dev, &d3d11_tex_desc, init_data, &img->d3d11_tex3d); - SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11_tex3d); + hr = ID3D11Device_CreateTexture3D(_sg.d3d11.dev, &d3d11_tex_desc, init_data, &img->d3d11.tex3d); + SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11.tex3d); } /* shader resource view for 3d texture */ @@ -6976,37 +7443,37 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_image(_sg_image_t* img, const sg_ima memset(&d3d11_srv_desc, 0, sizeof(d3d11_srv_desc)); d3d11_srv_desc.Format = d3d11_tex_desc.Format; d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; - d3d11_srv_desc.Texture3D.MipLevels = img->num_mipmaps; - hr = ID3D11Device_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)img->d3d11_tex3d, &d3d11_srv_desc, &img->d3d11_srv); - SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11_srv); + d3d11_srv_desc.Texture3D.MipLevels = img->cmn.num_mipmaps; + hr = ID3D11Device_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)img->d3d11.tex3d, &d3d11_srv_desc, &img->d3d11.srv); + SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11.srv); } /* also need to create a separate MSAA render target texture? */ if (msaa) { D3D11_TEXTURE2D_DESC d3d11_tex_desc; memset(&d3d11_tex_desc, 0, sizeof(d3d11_tex_desc)); - d3d11_tex_desc.Width = img->width; - d3d11_tex_desc.Height = img->height; + d3d11_tex_desc.Width = img->cmn.width; + d3d11_tex_desc.Height = img->cmn.height; d3d11_tex_desc.MipLevels = 1; d3d11_tex_desc.ArraySize = 1; - d3d11_tex_desc.Format = img->d3d11_format; + d3d11_tex_desc.Format = img->d3d11.format; d3d11_tex_desc.Usage = D3D11_USAGE_DEFAULT; d3d11_tex_desc.BindFlags = D3D11_BIND_RENDER_TARGET; d3d11_tex_desc.CPUAccessFlags = 0; - d3d11_tex_desc.SampleDesc.Count = img->sample_count; + d3d11_tex_desc.SampleDesc.Count = img->cmn.sample_count; d3d11_tex_desc.SampleDesc.Quality = (UINT)D3D11_STANDARD_MULTISAMPLE_PATTERN; - hr = ID3D11Device_CreateTexture2D(_sg.d3d11.dev, &d3d11_tex_desc, NULL, &img->d3d11_texmsaa); - SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11_texmsaa); + hr = ID3D11Device_CreateTexture2D(_sg.d3d11.dev, &d3d11_tex_desc, NULL, &img->d3d11.texmsaa); + SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11.texmsaa); } /* sampler state object, note D3D11 implements an internal shared-pool for sampler objects */ D3D11_SAMPLER_DESC d3d11_smp_desc; memset(&d3d11_smp_desc, 0, sizeof(d3d11_smp_desc)); - d3d11_smp_desc.Filter = _sg_d3d11_filter(img->min_filter, img->mag_filter, img->max_anisotropy); - d3d11_smp_desc.AddressU = _sg_d3d11_address_mode(img->wrap_u); - d3d11_smp_desc.AddressV = _sg_d3d11_address_mode(img->wrap_v); - d3d11_smp_desc.AddressW = _sg_d3d11_address_mode(img->wrap_w); - switch (img->border_color) { + d3d11_smp_desc.Filter = _sg_d3d11_filter(img->cmn.min_filter, img->cmn.mag_filter, img->cmn.max_anisotropy); + d3d11_smp_desc.AddressU = _sg_d3d11_address_mode(img->cmn.wrap_u); + d3d11_smp_desc.AddressV = _sg_d3d11_address_mode(img->cmn.wrap_v); + d3d11_smp_desc.AddressW = _sg_d3d11_address_mode(img->cmn.wrap_w); + switch (img->cmn.border_color) { case SG_BORDERCOLOR_TRANSPARENT_BLACK: /* all 0.0f */ break; @@ -7020,35 +7487,35 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_image(_sg_image_t* img, const sg_ima d3d11_smp_desc.BorderColor[3] = 1.0f; break; } - d3d11_smp_desc.MaxAnisotropy = img->max_anisotropy; + d3d11_smp_desc.MaxAnisotropy = img->cmn.max_anisotropy; d3d11_smp_desc.ComparisonFunc = D3D11_COMPARISON_NEVER; d3d11_smp_desc.MinLOD = desc->min_lod; d3d11_smp_desc.MaxLOD = desc->max_lod; - hr = ID3D11Device_CreateSamplerState(_sg.d3d11.dev, &d3d11_smp_desc, &img->d3d11_smp); - SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11_smp); + hr = ID3D11Device_CreateSamplerState(_sg.d3d11.dev, &d3d11_smp_desc, &img->d3d11.smp); + SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11.smp); } return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_destroy_image(_sg_image_t* img) { +_SOKOL_PRIVATE void _sg_d3d11_destroy_image(_sg_image_t* img) { SOKOL_ASSERT(img); - if (img->d3d11_tex2d) { - ID3D11Texture2D_Release(img->d3d11_tex2d); + if (img->d3d11.tex2d) { + ID3D11Texture2D_Release(img->d3d11.tex2d); } - if (img->d3d11_tex3d) { - ID3D11Texture3D_Release(img->d3d11_tex3d); + if (img->d3d11.tex3d) { + ID3D11Texture3D_Release(img->d3d11.tex3d); } - if (img->d3d11_texds) { - ID3D11Texture2D_Release(img->d3d11_texds); + if (img->d3d11.texds) { + ID3D11Texture2D_Release(img->d3d11.texds); } - if (img->d3d11_texmsaa) { - ID3D11Texture2D_Release(img->d3d11_texmsaa); + if (img->d3d11.texmsaa) { + ID3D11Texture2D_Release(img->d3d11.texmsaa); } - if (img->d3d11_srv) { - ID3D11ShaderResourceView_Release(img->d3d11_srv); + if (img->d3d11.srv) { + ID3D11ShaderResourceView_Release(img->d3d11.srv); } - if (img->d3d11_smp) { - ID3D11SamplerState_Release(img->d3d11_smp); + if (img->d3d11.smp) { + ID3D11SamplerState_Release(img->d3d11.smp); } } @@ -7080,79 +7547,71 @@ _SOKOL_PRIVATE bool _sg_d3d11_load_d3dcompiler_dll(void) { #define _sg_d3d11_D3DCompile _sg.d3d11.D3DCompile_func #endif -_SOKOL_PRIVATE ID3DBlob* _sg_d3d11_compile_shader(const sg_shader_stage_desc* stage_desc, const char* target) { +_SOKOL_PRIVATE ID3DBlob* _sg_d3d11_compile_shader(const sg_shader_stage_desc* stage_desc) { if (!_sg_d3d11_load_d3dcompiler_dll()) { return NULL; } + SOKOL_ASSERT(stage_desc->d3d11_target); ID3DBlob* output = NULL; - ID3DBlob* errors = NULL; - _sg_d3d11_D3DCompile( + ID3DBlob* errors_or_warnings = NULL; + HRESULT hr = _sg_d3d11_D3DCompile( stage_desc->source, /* pSrcData */ strlen(stage_desc->source), /* SrcDataSize */ NULL, /* pSourceName */ NULL, /* pDefines */ NULL, /* pInclude */ stage_desc->entry ? stage_desc->entry : "main", /* pEntryPoint */ - target, /* pTarget (vs_5_0 or ps_5_0) */ + stage_desc->d3d11_target, /* pTarget (vs_5_0 or ps_5_0) */ D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR | D3DCOMPILE_OPTIMIZATION_LEVEL3, /* Flags1 */ 0, /* Flags2 */ &output, /* ppCode */ - &errors); /* ppErrorMsgs */ - if (errors) { - SOKOL_LOG((LPCSTR)ID3D10Blob_GetBufferPointer(errors)); - ID3D10Blob_Release(errors); errors = NULL; - return NULL; + &errors_or_warnings); /* ppErrorMsgs */ + if (errors_or_warnings) { + SOKOL_LOG((LPCSTR)ID3D10Blob_GetBufferPointer(errors_or_warnings)); + ID3D10Blob_Release(errors_or_warnings); errors_or_warnings = NULL; + } + if (FAILED(hr)) { + /* just in case, usually output is NULL here */ + if (output) { + ID3D10Blob_Release(output); + output = NULL; + } } return output; } #define _sg_d3d11_roundup(val, round_to) (((val)+((round_to)-1))&~((round_to)-1)) -_SOKOL_PRIVATE sg_resource_state _sg_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { SOKOL_ASSERT(shd && desc); - SOKOL_ASSERT(!shd->d3d11_vs && !shd->d3d11_fs && !shd->d3d11_vs_blob); + SOKOL_ASSERT(!shd->d3d11.vs && !shd->d3d11.fs && !shd->d3d11.vs_blob); HRESULT hr; - sg_resource_state result = SG_RESOURCESTATE_FAILED; + _SOKOL_UNUSED(hr); + + _sg_shader_common_init(&shd->cmn, desc); /* copy vertex attribute semantic names and indices */ for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { - _sg_strcpy(&shd->attrs[i].sem_name, desc->attrs[i].sem_name); - shd->attrs[i].sem_index = desc->attrs[i].sem_index; + _sg_strcpy(&shd->d3d11.attrs[i].sem_name, desc->attrs[i].sem_name); + shd->d3d11.attrs[i].sem_index = desc->attrs[i].sem_index; } /* shader stage uniform blocks and image slots */ for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { - const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS) ? &desc->vs : &desc->fs; - _sg_shader_stage_t* stage = &shd->stage[stage_index]; - SOKOL_ASSERT(stage->num_uniform_blocks == 0); - for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { - const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; - if (0 == ub_desc->size) { - break; - } - _sg_uniform_block_t* ub = &stage->uniform_blocks[ub_index]; - ub->size = ub_desc->size; + _sg_shader_stage_t* cmn_stage = &shd->cmn.stage[stage_index]; + _sg_d3d11_shader_stage_t* d3d11_stage = &shd->d3d11.stage[stage_index]; + for (int ub_index = 0; ub_index < cmn_stage->num_uniform_blocks; ub_index++) { + const _sg_uniform_block_t* ub = &cmn_stage->uniform_blocks[ub_index]; - /* create a D3D constant buffer */ - SOKOL_ASSERT(!stage->d3d11_cbs[ub_index]); + /* create a D3D constant buffer for each uniform block */ + SOKOL_ASSERT(0 == d3d11_stage->cbufs[ub_index]); D3D11_BUFFER_DESC cb_desc; memset(&cb_desc, 0, sizeof(cb_desc)); cb_desc.ByteWidth = _sg_d3d11_roundup(ub->size, 16); cb_desc.Usage = D3D11_USAGE_DEFAULT; cb_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; - hr = ID3D11Device_CreateBuffer(_sg.d3d11.dev, &cb_desc, NULL, &stage->d3d11_cbs[ub_index]); - SOKOL_ASSERT(SUCCEEDED(hr) && stage->d3d11_cbs[ub_index]); - - stage->num_uniform_blocks++; - } - SOKOL_ASSERT(stage->num_images == 0); - for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { - const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; - if (img_desc->type == _SG_IMAGETYPE_DEFAULT) { - break; - } - stage->images[img_index].type = img_desc->type; - stage->num_images++; + hr = ID3D11Device_CreateBuffer(_sg.d3d11.dev, &cb_desc, NULL, &d3d11_stage->cbufs[ub_index]); + SOKOL_ASSERT(SUCCEEDED(hr) && d3d11_stage->cbufs[ub_index]); } } @@ -7160,16 +7619,16 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_shader(_sg_shader_t* shd, const sg_s SIZE_T vs_length = 0, fs_length = 0; ID3DBlob* vs_blob = 0, *fs_blob = 0; if (desc->vs.byte_code && desc->fs.byte_code) { - /* create from byte code */ + /* create from shader byte code */ vs_ptr = desc->vs.byte_code; fs_ptr = desc->fs.byte_code; vs_length = desc->vs.byte_code_size; fs_length = desc->fs.byte_code_size; } else { - /* compile shader code */ - vs_blob = _sg_d3d11_compile_shader(&desc->vs, "vs_5_0"); - fs_blob = _sg_d3d11_compile_shader(&desc->fs, "ps_5_0"); + /* compile from shader source code */ + vs_blob = _sg_d3d11_compile_shader(&desc->vs); + fs_blob = _sg_d3d11_compile_shader(&desc->fs); if (vs_blob && fs_blob) { vs_ptr = ID3D10Blob_GetBufferPointer(vs_blob); vs_length = ID3D10Blob_GetBufferSize(vs_blob); @@ -7177,20 +7636,22 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_shader(_sg_shader_t* shd, const sg_s fs_length = ID3D10Blob_GetBufferSize(fs_blob); } } + sg_resource_state result = SG_RESOURCESTATE_FAILED; if (vs_ptr && fs_ptr && (vs_length > 0) && (fs_length > 0)) { /* create the D3D vertex- and pixel-shader objects */ - hr = ID3D11Device_CreateVertexShader(_sg.d3d11.dev, vs_ptr, vs_length, NULL, &shd->d3d11_vs); - SOKOL_ASSERT(SUCCEEDED(hr) && shd->d3d11_vs); - hr = ID3D11Device_CreatePixelShader(_sg.d3d11.dev, fs_ptr, fs_length, NULL, &shd->d3d11_fs); - SOKOL_ASSERT(SUCCEEDED(hr) && shd->d3d11_fs); + hr = ID3D11Device_CreateVertexShader(_sg.d3d11.dev, vs_ptr, vs_length, NULL, &shd->d3d11.vs); + bool vs_succeeded = SUCCEEDED(hr) && shd->d3d11.vs; + hr = ID3D11Device_CreatePixelShader(_sg.d3d11.dev, fs_ptr, fs_length, NULL, &shd->d3d11.fs); + bool fs_succeeded = SUCCEEDED(hr) && shd->d3d11.fs; /* need to store the vertex shader byte code, this is needed later in sg_create_pipeline */ - shd->d3d11_vs_blob_length = (int)vs_length; - shd->d3d11_vs_blob = SOKOL_MALLOC((int)vs_length); - SOKOL_ASSERT(shd->d3d11_vs_blob); - memcpy(shd->d3d11_vs_blob, vs_ptr, vs_length); - - result = SG_RESOURCESTATE_VALID; + if (vs_succeeded && fs_succeeded) { + shd->d3d11.vs_blob_length = (int)vs_length; + shd->d3d11.vs_blob = SOKOL_MALLOC((int)vs_length); + SOKOL_ASSERT(shd->d3d11.vs_blob); + memcpy(shd->d3d11.vs_blob, vs_ptr, vs_length); + result = SG_RESOURCESTATE_VALID; + } } if (vs_blob) { ID3D10Blob_Release(vs_blob); vs_blob = 0; @@ -7201,50 +7662,44 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_shader(_sg_shader_t* shd, const sg_s return result; } -_SOKOL_PRIVATE void _sg_destroy_shader(_sg_shader_t* shd) { +_SOKOL_PRIVATE void _sg_d3d11_destroy_shader(_sg_shader_t* shd) { SOKOL_ASSERT(shd); - if (shd->d3d11_vs) { - ID3D11VertexShader_Release(shd->d3d11_vs); + if (shd->d3d11.vs) { + ID3D11VertexShader_Release(shd->d3d11.vs); } - if (shd->d3d11_fs) { - ID3D11PixelShader_Release(shd->d3d11_fs); + if (shd->d3d11.fs) { + ID3D11PixelShader_Release(shd->d3d11.fs); } - if (shd->d3d11_vs_blob) { - SOKOL_FREE(shd->d3d11_vs_blob); + if (shd->d3d11.vs_blob) { + SOKOL_FREE(shd->d3d11.vs_blob); } for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { - _sg_shader_stage_t* stage = &shd->stage[stage_index]; - for (int ub_index = 0; ub_index < stage->num_uniform_blocks; ub_index++) { - if (stage->d3d11_cbs[ub_index]) { - ID3D11Buffer_Release(stage->d3d11_cbs[ub_index]); + _sg_shader_stage_t* cmn_stage = &shd->cmn.stage[stage_index]; + _sg_d3d11_shader_stage_t* d3d11_stage = &shd->d3d11.stage[stage_index]; + for (int ub_index = 0; ub_index < cmn_stage->num_uniform_blocks; ub_index++) { + if (d3d11_stage->cbufs[ub_index]) { + ID3D11Buffer_Release(d3d11_stage->cbufs[ub_index]); } } } } -_SOKOL_PRIVATE sg_resource_state _sg_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { SOKOL_ASSERT(pip && shd && desc); SOKOL_ASSERT(desc->shader.id == shd->slot.id); SOKOL_ASSERT(shd->slot.state == SG_RESOURCESTATE_VALID); - SOKOL_ASSERT(shd->d3d11_vs_blob && shd->d3d11_vs_blob_length > 0); - SOKOL_ASSERT(!pip->d3d11_il && !pip->d3d11_rs && !pip->d3d11_dss && !pip->d3d11_bs); - HRESULT hr; + SOKOL_ASSERT(shd->d3d11.vs_blob && shd->d3d11.vs_blob_length > 0); + SOKOL_ASSERT(!pip->d3d11.il && !pip->d3d11.rs && !pip->d3d11.dss && !pip->d3d11.bs); pip->shader = shd; - pip->shader_id = desc->shader; - pip->index_type = desc->index_type; - pip->color_attachment_count = desc->blend.color_attachment_count; - pip->color_format = desc->blend.color_format; - pip->depth_format = desc->blend.depth_format; - pip->sample_count = desc->rasterizer.sample_count; - pip->d3d11_index_format = _sg_d3d11_index_format(pip->index_type); - pip->d3d11_topology = _sg_d3d11_primitive_topology(desc->primitive_type); - for (int i = 0; i < 4; i++) { - pip->blend_color[i] = desc->blend.blend_color[i]; - } - pip->d3d11_stencil_ref = desc->depth_stencil.stencil_ref; + _sg_pipeline_common_init(&pip->cmn, desc); + pip->d3d11.index_format = _sg_d3d11_index_format(pip->cmn.index_type); + pip->d3d11.topology = _sg_d3d11_primitive_topology(desc->primitive_type); + pip->d3d11.stencil_ref = desc->depth_stencil.stencil_ref; /* create input layout object */ + HRESULT hr; + _SOKOL_UNUSED(hr); D3D11_INPUT_ELEMENT_DESC d3d11_comps[SG_MAX_VERTEX_ATTRIBUTES]; memset(d3d11_comps, 0, sizeof(d3d11_comps)); int attr_index = 0; @@ -7258,8 +7713,8 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_pipeline(_sg_pipeline_t* pip, _sg_sh const sg_vertex_step step_func = l_desc->step_func; const int step_rate = l_desc->step_rate; D3D11_INPUT_ELEMENT_DESC* d3d11_comp = &d3d11_comps[attr_index]; - d3d11_comp->SemanticName = _sg_strptr(&shd->attrs[attr_index].sem_name); - d3d11_comp->SemanticIndex = shd->attrs[attr_index].sem_index; + d3d11_comp->SemanticName = _sg_strptr(&shd->d3d11.attrs[attr_index].sem_name); + d3d11_comp->SemanticIndex = shd->d3d11.attrs[attr_index].sem_index; d3d11_comp->Format = _sg_d3d11_vertex_format(a_desc->format); d3d11_comp->InputSlot = a_desc->buffer_index; d3d11_comp->AlignedByteOffset = a_desc->offset; @@ -7267,25 +7722,25 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_pipeline(_sg_pipeline_t* pip, _sg_sh if (SG_VERTEXSTEP_PER_INSTANCE == step_func) { d3d11_comp->InstanceDataStepRate = step_rate; } - pip->vertex_layout_valid[a_desc->buffer_index] = true; + pip->cmn.vertex_layout_valid[a_desc->buffer_index] = true; } for (int layout_index = 0; layout_index < SG_MAX_SHADERSTAGE_BUFFERS; layout_index++) { - if (pip->vertex_layout_valid[layout_index]) { + if (pip->cmn.vertex_layout_valid[layout_index]) { const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[layout_index]; SOKOL_ASSERT(l_desc->stride > 0); - pip->d3d11_vb_strides[layout_index] = l_desc->stride; + pip->d3d11.vb_strides[layout_index] = l_desc->stride; } else { - pip->d3d11_vb_strides[layout_index] = 0; + pip->d3d11.vb_strides[layout_index] = 0; } } hr = ID3D11Device_CreateInputLayout(_sg.d3d11.dev, d3d11_comps, /* pInputElementDesc */ attr_index, /* NumElements */ - shd->d3d11_vs_blob, /* pShaderByteCodeWithInputSignature */ - shd->d3d11_vs_blob_length, /* BytecodeLength */ - &pip->d3d11_il); - SOKOL_ASSERT(SUCCEEDED(hr) && pip->d3d11_il); + shd->d3d11.vs_blob, /* pShaderByteCodeWithInputSignature */ + shd->d3d11.vs_blob_length, /* BytecodeLength */ + &pip->d3d11.il); + SOKOL_ASSERT(SUCCEEDED(hr) && pip->d3d11.il); /* create rasterizer state */ D3D11_RASTERIZER_DESC rs_desc; @@ -7293,15 +7748,15 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_pipeline(_sg_pipeline_t* pip, _sg_sh rs_desc.FillMode = D3D11_FILL_SOLID; rs_desc.CullMode = _sg_d3d11_cull_mode(desc->rasterizer.cull_mode); rs_desc.FrontCounterClockwise = desc->rasterizer.face_winding == SG_FACEWINDING_CCW; - rs_desc.DepthBias = (INT) desc->rasterizer.depth_bias; - rs_desc.DepthBiasClamp = desc->rasterizer.depth_bias_clamp; - rs_desc.SlopeScaledDepthBias = desc->rasterizer.depth_bias_slope_scale; + rs_desc.DepthBias = (INT) pip->cmn.depth_bias; + rs_desc.DepthBiasClamp = pip->cmn.depth_bias_clamp; + rs_desc.SlopeScaledDepthBias = pip->cmn.depth_bias_slope_scale; rs_desc.DepthClipEnable = TRUE; rs_desc.ScissorEnable = TRUE; rs_desc.MultisampleEnable = desc->rasterizer.sample_count > 1; rs_desc.AntialiasedLineEnable = FALSE; - hr = ID3D11Device_CreateRasterizerState(_sg.d3d11.dev, &rs_desc, &pip->d3d11_rs); - SOKOL_ASSERT(SUCCEEDED(hr) && pip->d3d11_rs); + hr = ID3D11Device_CreateRasterizerState(_sg.d3d11.dev, &rs_desc, &pip->d3d11.rs); + SOKOL_ASSERT(SUCCEEDED(hr) && pip->d3d11.rs); /* create depth-stencil state */ D3D11_DEPTH_STENCIL_DESC dss_desc; @@ -7322,8 +7777,8 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_pipeline(_sg_pipeline_t* pip, _sg_sh dss_desc.BackFace.StencilDepthFailOp = _sg_d3d11_stencil_op(sb->depth_fail_op); dss_desc.BackFace.StencilPassOp = _sg_d3d11_stencil_op(sb->pass_op); dss_desc.BackFace.StencilFunc = _sg_d3d11_compare_func(sb->compare_func); - hr = ID3D11Device_CreateDepthStencilState(_sg.d3d11.dev, &dss_desc, &pip->d3d11_dss); - SOKOL_ASSERT(SUCCEEDED(hr) && pip->d3d11_dss); + hr = ID3D11Device_CreateDepthStencilState(_sg.d3d11.dev, &dss_desc, &pip->d3d11.dss); + SOKOL_ASSERT(SUCCEEDED(hr) && pip->d3d11.dss); /* create blend state */ D3D11_BLEND_DESC bs_desc; @@ -7338,137 +7793,143 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_pipeline(_sg_pipeline_t* pip, _sg_sh bs_desc.RenderTarget[0].DestBlendAlpha = _sg_d3d11_blend_factor(desc->blend.dst_factor_alpha); bs_desc.RenderTarget[0].BlendOpAlpha = _sg_d3d11_blend_op(desc->blend.op_alpha); bs_desc.RenderTarget[0].RenderTargetWriteMask = _sg_d3d11_color_write_mask((sg_color_mask)desc->blend.color_write_mask); - hr = ID3D11Device_CreateBlendState(_sg.d3d11.dev, &bs_desc, &pip->d3d11_bs); - SOKOL_ASSERT(SUCCEEDED(hr) && pip->d3d11_bs); + hr = ID3D11Device_CreateBlendState(_sg.d3d11.dev, &bs_desc, &pip->d3d11.bs); + SOKOL_ASSERT(SUCCEEDED(hr) && pip->d3d11.bs); return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_destroy_pipeline(_sg_pipeline_t* pip) { +_SOKOL_PRIVATE void _sg_d3d11_destroy_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); - if (pip->d3d11_il) { - ID3D11InputLayout_Release(pip->d3d11_il); + if (pip->d3d11.il) { + ID3D11InputLayout_Release(pip->d3d11.il); } - if (pip->d3d11_rs) { - ID3D11RasterizerState_Release(pip->d3d11_rs); + if (pip->d3d11.rs) { + ID3D11RasterizerState_Release(pip->d3d11.rs); } - if (pip->d3d11_dss) { - ID3D11DepthStencilState_Release(pip->d3d11_dss); + if (pip->d3d11.dss) { + ID3D11DepthStencilState_Release(pip->d3d11.dss); } - if (pip->d3d11_bs) { - ID3D11BlendState_Release(pip->d3d11_bs); + if (pip->d3d11.bs) { + ID3D11BlendState_Release(pip->d3d11.bs); } } -_SOKOL_PRIVATE sg_resource_state _sg_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { SOKOL_ASSERT(pass && desc); SOKOL_ASSERT(att_images && att_images[0]); SOKOL_ASSERT(_sg.d3d11.dev); - const sg_attachment_desc* att_desc; - _sg_attachment_t* att; - for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - SOKOL_ASSERT(0 == pass->color_atts[i].image); - SOKOL_ASSERT(pass->d3d11_rtvs[i] == 0); - att_desc = &desc->color_attachments[i]; - if (att_desc->image.id != SG_INVALID_ID) { - pass->num_color_atts++; - SOKOL_ASSERT(att_images[i] && (att_images[i]->slot.id == att_desc->image.id)); - SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(att_images[i]->pixel_format)); - att = &pass->color_atts[i]; - SOKOL_ASSERT((att->image == 0) && (att->image_id.id == SG_INVALID_ID)); - att->image = att_images[i]; - att->image_id = att_desc->image; - att->mip_level = att_desc->mip_level; - att->slice = att_desc->slice; + _sg_pass_common_init(&pass->cmn, desc); - /* create D3D11 render-target-view */ - ID3D11Resource* d3d11_res = 0; - const bool is_msaa = att->image->sample_count > 1; - D3D11_RENDER_TARGET_VIEW_DESC d3d11_rtv_desc; - memset(&d3d11_rtv_desc, 0, sizeof(d3d11_rtv_desc)); - d3d11_rtv_desc.Format = att->image->d3d11_format; - if ((att->image->type == SG_IMAGETYPE_2D) || is_msaa) { - if (is_msaa) { - d3d11_res = (ID3D11Resource*) att->image->d3d11_texmsaa; - d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS; - } - else { - d3d11_res = (ID3D11Resource*) att->image->d3d11_tex2d; - d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; - d3d11_rtv_desc.Texture2D.MipSlice = att->mip_level; - } - } - else if ((att->image->type == SG_IMAGETYPE_CUBE) || (att->image->type == SG_IMAGETYPE_ARRAY)) { - d3d11_res = (ID3D11Resource*) att->image->d3d11_tex2d; - d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY; - d3d11_rtv_desc.Texture2DArray.MipSlice = att->mip_level; - d3d11_rtv_desc.Texture2DArray.FirstArraySlice = att->slice; - d3d11_rtv_desc.Texture2DArray.ArraySize = 1; + for (int i = 0; i < pass->cmn.num_color_atts; i++) { + const sg_attachment_desc* att_desc = &desc->color_attachments[i]; + _SOKOL_UNUSED(att_desc); + SOKOL_ASSERT(att_desc->image.id != SG_INVALID_ID); + _sg_image_t* att_img = att_images[i]; + SOKOL_ASSERT(att_img && (att_img->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(att_img->cmn.pixel_format)); + SOKOL_ASSERT(0 == pass->d3d11.color_atts[i].image); + pass->d3d11.color_atts[i].image = att_img; + + /* create D3D11 render-target-view */ + const _sg_attachment_t* cmn_att = &pass->cmn.color_atts[i]; + SOKOL_ASSERT(0 == pass->d3d11.color_atts[i].rtv); + ID3D11Resource* d3d11_res = 0; + const bool is_msaa = att_img->cmn.sample_count > 1; + D3D11_RENDER_TARGET_VIEW_DESC d3d11_rtv_desc; + memset(&d3d11_rtv_desc, 0, sizeof(d3d11_rtv_desc)); + d3d11_rtv_desc.Format = att_img->d3d11.format; + if ((att_img->cmn.type == SG_IMAGETYPE_2D) || is_msaa) { + if (is_msaa) { + d3d11_res = (ID3D11Resource*) att_img->d3d11.texmsaa; + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS; } else { - SOKOL_ASSERT(att->image->type == SG_IMAGETYPE_3D); - d3d11_res = (ID3D11Resource*) att->image->d3d11_tex3d; - d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D; - d3d11_rtv_desc.Texture3D.MipSlice = att->mip_level; - d3d11_rtv_desc.Texture3D.FirstWSlice = att->slice; - d3d11_rtv_desc.Texture3D.WSize = 1; + d3d11_res = (ID3D11Resource*) att_img->d3d11.tex2d; + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + d3d11_rtv_desc.Texture2D.MipSlice = cmn_att->mip_level; } - SOKOL_ASSERT(d3d11_res); - HRESULT hr = ID3D11Device_CreateRenderTargetView(_sg.d3d11.dev, d3d11_res, &d3d11_rtv_desc, &pass->d3d11_rtvs[i]); - _SOKOL_UNUSED(hr); - SOKOL_ASSERT(SUCCEEDED(hr) && pass->d3d11_rtvs[i]); } + else if ((att_img->cmn.type == SG_IMAGETYPE_CUBE) || (att_img->cmn.type == SG_IMAGETYPE_ARRAY)) { + d3d11_res = (ID3D11Resource*) att_img->d3d11.tex2d; + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY; + d3d11_rtv_desc.Texture2DArray.MipSlice = cmn_att->mip_level; + d3d11_rtv_desc.Texture2DArray.FirstArraySlice = cmn_att->slice; + d3d11_rtv_desc.Texture2DArray.ArraySize = 1; + } + else { + SOKOL_ASSERT(att_img->cmn.type == SG_IMAGETYPE_3D); + d3d11_res = (ID3D11Resource*) att_img->d3d11.tex3d; + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D; + d3d11_rtv_desc.Texture3D.MipSlice = cmn_att->mip_level; + d3d11_rtv_desc.Texture3D.FirstWSlice = cmn_att->slice; + d3d11_rtv_desc.Texture3D.WSize = 1; + } + SOKOL_ASSERT(d3d11_res); + HRESULT hr = ID3D11Device_CreateRenderTargetView(_sg.d3d11.dev, d3d11_res, &d3d11_rtv_desc, &pass->d3d11.color_atts[i].rtv); + _SOKOL_UNUSED(hr); + SOKOL_ASSERT(SUCCEEDED(hr) && pass->d3d11.color_atts[i].rtv); } /* optional depth-stencil image */ - SOKOL_ASSERT(0 == pass->ds_att.image); - SOKOL_ASSERT(pass->d3d11_dsv == 0); - att_desc = &desc->depth_stencil_attachment; - const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; - if (att_desc->image.id != SG_INVALID_ID) { - SOKOL_ASSERT(att_images[ds_img_index] && (att_images[ds_img_index]->slot.id == att_desc->image.id)); - SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_images[ds_img_index]->pixel_format)); - att = &pass->ds_att; - SOKOL_ASSERT((att->image == 0) && (att->image_id.id == SG_INVALID_ID)); - att->image = att_images[ds_img_index]; - att->image_id = att_desc->image; - att->mip_level = att_desc->mip_level; - att->slice = att_desc->slice; + SOKOL_ASSERT(0 == pass->d3d11.ds_att.image); + SOKOL_ASSERT(0 == pass->d3d11.ds_att.dsv); + if (desc->depth_stencil_attachment.image.id != SG_INVALID_ID) { + const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; + const sg_attachment_desc* att_desc = &desc->depth_stencil_attachment; + _SOKOL_UNUSED(att_desc); + _sg_image_t* att_img = att_images[ds_img_index]; + SOKOL_ASSERT(att_img && (att_img->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_img->cmn.pixel_format)); + SOKOL_ASSERT(0 == pass->d3d11.ds_att.image); + pass->d3d11.ds_att.image = att_img; /* create D3D11 depth-stencil-view */ D3D11_DEPTH_STENCIL_VIEW_DESC d3d11_dsv_desc; memset(&d3d11_dsv_desc, 0, sizeof(d3d11_dsv_desc)); - d3d11_dsv_desc.Format = att->image->d3d11_format; - const bool is_msaa = att->image->sample_count > 1; + d3d11_dsv_desc.Format = att_img->d3d11.format; + const bool is_msaa = att_img->cmn.sample_count > 1; if (is_msaa) { d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS; } else { d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; } - ID3D11Resource* d3d11_res = (ID3D11Resource*) att->image->d3d11_texds; + ID3D11Resource* d3d11_res = (ID3D11Resource*) att_img->d3d11.texds; SOKOL_ASSERT(d3d11_res); - HRESULT hr = ID3D11Device_CreateDepthStencilView(_sg.d3d11.dev, d3d11_res, &d3d11_dsv_desc, &pass->d3d11_dsv); + HRESULT hr = ID3D11Device_CreateDepthStencilView(_sg.d3d11.dev, d3d11_res, &d3d11_dsv_desc, &pass->d3d11.ds_att.dsv); _SOKOL_UNUSED(hr); - SOKOL_ASSERT(SUCCEEDED(hr) && pass->d3d11_dsv); + SOKOL_ASSERT(SUCCEEDED(hr) && pass->d3d11.ds_att.dsv); } return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_destroy_pass(_sg_pass_t* pass) { +_SOKOL_PRIVATE void _sg_d3d11_destroy_pass(_sg_pass_t* pass) { SOKOL_ASSERT(pass); for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - if (pass->d3d11_rtvs[i]) { - ID3D11RenderTargetView_Release(pass->d3d11_rtvs[i]); + if (pass->d3d11.color_atts[i].rtv) { + ID3D11RenderTargetView_Release(pass->d3d11.color_atts[i].rtv); } } - if (pass->d3d11_dsv) { - ID3D11DepthStencilView_Release(pass->d3d11_dsv); + if (pass->d3d11.ds_att.dsv) { + ID3D11DepthStencilView_Release(pass->d3d11.ds_att.dsv); } } -_SOKOL_PRIVATE void _sg_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { +_SOKOL_PRIVATE _sg_image_t* _sg_d3d11_pass_color_image(const _sg_pass_t* pass, int index) { + SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + /* NOTE: may return null */ + return pass->d3d11.color_atts[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_d3d11_pass_ds_image(const _sg_pass_t* pass) { + /* NOTE: may return null */ + SOKOL_ASSERT(pass); + return pass->d3d11.ds_att.image; +} + +_SOKOL_PRIVATE void _sg_d3d11_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { SOKOL_ASSERT(action); SOKOL_ASSERT(!_sg.d3d11.in_pass); _sg.d3d11.in_pass = true; @@ -7479,12 +7940,12 @@ _SOKOL_PRIVATE void _sg_begin_pass(_sg_pass_t* pass, const sg_pass_action* actio _sg.d3d11.cur_pass_id.id = pass->slot.id; _sg.d3d11.num_rtvs = 0; for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - _sg.d3d11.cur_rtvs[i] = pass->d3d11_rtvs[i]; + _sg.d3d11.cur_rtvs[i] = pass->d3d11.color_atts[i].rtv; if (_sg.d3d11.cur_rtvs[i]) { _sg.d3d11.num_rtvs++; } } - _sg.d3d11.cur_dsv = pass->d3d11_dsv; + _sg.d3d11.cur_dsv = pass->d3d11.ds_att.dsv; } else { /* render to default frame buffer */ @@ -7538,7 +7999,7 @@ _SOKOL_PRIVATE UINT _sg_d3d11_calcsubresource(UINT mip_slice, UINT array_slice, return mip_slice + array_slice * mip_levels; } -_SOKOL_PRIVATE void _sg_end_pass(void) { +_SOKOL_PRIVATE void _sg_d3d11_end_pass(void) { SOKOL_ASSERT(_sg.d3d11.in_pass && _sg.d3d11.ctx); _sg.d3d11.in_pass = false; @@ -7546,20 +8007,20 @@ _SOKOL_PRIVATE void _sg_end_pass(void) { if (_sg.d3d11.cur_pass) { SOKOL_ASSERT(_sg.d3d11.cur_pass->slot.id == _sg.d3d11.cur_pass_id.id); for (int i = 0; i < _sg.d3d11.num_rtvs; i++) { - _sg_attachment_t* att = &_sg.d3d11.cur_pass->color_atts[i]; - SOKOL_ASSERT(att->image && (att->image->slot.id == att->image_id.id)); - if (att->image->sample_count > 1) { + _sg_attachment_t* cmn_att = &_sg.d3d11.cur_pass->cmn.color_atts[i]; + _sg_image_t* att_img = _sg.d3d11.cur_pass->d3d11.color_atts[i].image; + SOKOL_ASSERT(att_img && (att_img->slot.id == cmn_att->image_id.id)); + if (att_img->cmn.sample_count > 1) { /* FIXME: support MSAA resolve into 3D texture */ - SOKOL_ASSERT(att->image->d3d11_tex2d && att->image->d3d11_texmsaa && !att->image->d3d11_tex3d); - SOKOL_ASSERT(DXGI_FORMAT_UNKNOWN != att->image->d3d11_format); - const _sg_image_t* img = att->image; - UINT dst_subres = _sg_d3d11_calcsubresource(att->mip_level, att->slice, img->num_mipmaps); + SOKOL_ASSERT(att_img->d3d11.tex2d && att_img->d3d11.texmsaa && !att_img->d3d11.tex3d); + SOKOL_ASSERT(DXGI_FORMAT_UNKNOWN != att_img->d3d11.format); + UINT dst_subres = _sg_d3d11_calcsubresource(cmn_att->mip_level, cmn_att->slice, att_img->cmn.num_mipmaps); ID3D11DeviceContext_ResolveSubresource(_sg.d3d11.ctx, - (ID3D11Resource*) img->d3d11_tex2d, /* pDstResource */ - dst_subres, /* DstSubresource */ - (ID3D11Resource*) img->d3d11_texmsaa, /* pSrcResource */ - 0, /* SrcSubresource */ - img->d3d11_format); + (ID3D11Resource*) att_img->d3d11.tex2d, /* pDstResource */ + dst_subres, /* DstSubresource */ + (ID3D11Resource*) att_img->d3d11.texmsaa, /* pSrcResource */ + 0, /* SrcSubresource */ + att_img->d3d11.format); } } } @@ -7574,7 +8035,7 @@ _SOKOL_PRIVATE void _sg_end_pass(void) { _sg_d3d11_clear_state(); } -_SOKOL_PRIVATE void _sg_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { +_SOKOL_PRIVATE void _sg_d3d11_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { SOKOL_ASSERT(_sg.d3d11.ctx); SOKOL_ASSERT(_sg.d3d11.in_pass); D3D11_VIEWPORT vp; @@ -7587,7 +8048,7 @@ _SOKOL_PRIVATE void _sg_apply_viewport(int x, int y, int w, int h, bool origin_t ID3D11DeviceContext_RSSetViewports(_sg.d3d11.ctx, 1, &vp); } -_SOKOL_PRIVATE void _sg_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { +_SOKOL_PRIVATE void _sg_d3d11_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { SOKOL_ASSERT(_sg.d3d11.ctx); SOKOL_ASSERT(_sg.d3d11.in_pass); D3D11_RECT rect; @@ -7598,29 +8059,29 @@ _SOKOL_PRIVATE void _sg_apply_scissor_rect(int x, int y, int w, int h, bool orig ID3D11DeviceContext_RSSetScissorRects(_sg.d3d11.ctx, 1, &rect); } -_SOKOL_PRIVATE void _sg_apply_pipeline(_sg_pipeline_t* pip) { +_SOKOL_PRIVATE void _sg_d3d11_apply_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); - SOKOL_ASSERT(pip->shader); + SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); SOKOL_ASSERT(_sg.d3d11.ctx); SOKOL_ASSERT(_sg.d3d11.in_pass); - SOKOL_ASSERT(pip->d3d11_rs && pip->d3d11_bs && pip->d3d11_dss && pip->d3d11_il); + SOKOL_ASSERT(pip->d3d11.rs && pip->d3d11.bs && pip->d3d11.dss && pip->d3d11.il); _sg.d3d11.cur_pipeline = pip; _sg.d3d11.cur_pipeline_id.id = pip->slot.id; - _sg.d3d11.use_indexed_draw = (pip->d3d11_index_format != DXGI_FORMAT_UNKNOWN); + _sg.d3d11.use_indexed_draw = (pip->d3d11.index_format != DXGI_FORMAT_UNKNOWN); - ID3D11DeviceContext_RSSetState(_sg.d3d11.ctx, pip->d3d11_rs); - ID3D11DeviceContext_OMSetDepthStencilState(_sg.d3d11.ctx, pip->d3d11_dss, pip->d3d11_stencil_ref); - ID3D11DeviceContext_OMSetBlendState(_sg.d3d11.ctx, pip->d3d11_bs, pip->blend_color, 0xFFFFFFFF); - ID3D11DeviceContext_IASetPrimitiveTopology(_sg.d3d11.ctx, pip->d3d11_topology); - ID3D11DeviceContext_IASetInputLayout(_sg.d3d11.ctx, pip->d3d11_il); - ID3D11DeviceContext_VSSetShader(_sg.d3d11.ctx, pip->shader->d3d11_vs, NULL, 0); - ID3D11DeviceContext_VSSetConstantBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_UBS, pip->shader->stage[SG_SHADERSTAGE_VS].d3d11_cbs); - ID3D11DeviceContext_PSSetShader(_sg.d3d11.ctx, pip->shader->d3d11_fs, NULL, 0); - ID3D11DeviceContext_PSSetConstantBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_UBS, pip->shader->stage[SG_SHADERSTAGE_FS].d3d11_cbs); + ID3D11DeviceContext_RSSetState(_sg.d3d11.ctx, pip->d3d11.rs); + ID3D11DeviceContext_OMSetDepthStencilState(_sg.d3d11.ctx, pip->d3d11.dss, pip->d3d11.stencil_ref); + ID3D11DeviceContext_OMSetBlendState(_sg.d3d11.ctx, pip->d3d11.bs, pip->cmn.blend_color, 0xFFFFFFFF); + ID3D11DeviceContext_IASetPrimitiveTopology(_sg.d3d11.ctx, pip->d3d11.topology); + ID3D11DeviceContext_IASetInputLayout(_sg.d3d11.ctx, pip->d3d11.il); + ID3D11DeviceContext_VSSetShader(_sg.d3d11.ctx, pip->shader->d3d11.vs, NULL, 0); + ID3D11DeviceContext_VSSetConstantBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_UBS, pip->shader->d3d11.stage[SG_SHADERSTAGE_VS].cbufs); + ID3D11DeviceContext_PSSetShader(_sg.d3d11.ctx, pip->shader->d3d11.fs, NULL, 0); + ID3D11DeviceContext_PSSetConstantBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_UBS, pip->shader->d3d11.stage[SG_SHADERSTAGE_FS].cbufs); } -_SOKOL_PRIVATE void _sg_apply_bindings( +_SOKOL_PRIVATE void _sg_d3d11_apply_bindings( _sg_pipeline_t* pip, _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, _sg_buffer_t* ib, int ib_offset, @@ -7632,7 +8093,7 @@ _SOKOL_PRIVATE void _sg_apply_bindings( SOKOL_ASSERT(_sg.d3d11.in_pass); /* gather all the D3D11 resources into arrays */ - ID3D11Buffer* d3d11_ib = ib ? ib->d3d11_buf : 0; + ID3D11Buffer* d3d11_ib = ib ? ib->d3d11.buf : 0; ID3D11Buffer* d3d11_vbs[SG_MAX_SHADERSTAGE_BUFFERS]; UINT d3d11_vb_offsets[SG_MAX_SHADERSTAGE_BUFFERS]; ID3D11ShaderResourceView* d3d11_vs_srvs[SG_MAX_SHADERSTAGE_IMAGES]; @@ -7641,8 +8102,8 @@ _SOKOL_PRIVATE void _sg_apply_bindings( ID3D11SamplerState* d3d11_fs_smps[SG_MAX_SHADERSTAGE_IMAGES]; int i; for (i = 0; i < num_vbs; i++) { - SOKOL_ASSERT(vbs[i]->d3d11_buf); - d3d11_vbs[i] = vbs[i]->d3d11_buf; + SOKOL_ASSERT(vbs[i]->d3d11.buf); + d3d11_vbs[i] = vbs[i]->d3d11.buf; d3d11_vb_offsets[i] = vb_offsets[i]; } for (; i < SG_MAX_SHADERSTAGE_BUFFERS; i++) { @@ -7650,50 +8111,50 @@ _SOKOL_PRIVATE void _sg_apply_bindings( d3d11_vb_offsets[i] = 0; } for (i = 0; i < num_vs_imgs; i++) { - SOKOL_ASSERT(vs_imgs[i]->d3d11_srv); - SOKOL_ASSERT(vs_imgs[i]->d3d11_smp); - d3d11_vs_srvs[i] = vs_imgs[i]->d3d11_srv; - d3d11_vs_smps[i] = vs_imgs[i]->d3d11_smp; + SOKOL_ASSERT(vs_imgs[i]->d3d11.srv); + SOKOL_ASSERT(vs_imgs[i]->d3d11.smp); + d3d11_vs_srvs[i] = vs_imgs[i]->d3d11.srv; + d3d11_vs_smps[i] = vs_imgs[i]->d3d11.smp; } for (; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { d3d11_vs_srvs[i] = 0; d3d11_vs_smps[i] = 0; } for (i = 0; i < num_fs_imgs; i++) { - SOKOL_ASSERT(fs_imgs[i]->d3d11_srv); - SOKOL_ASSERT(fs_imgs[i]->d3d11_smp); - d3d11_fs_srvs[i] = fs_imgs[i]->d3d11_srv; - d3d11_fs_smps[i] = fs_imgs[i]->d3d11_smp; + SOKOL_ASSERT(fs_imgs[i]->d3d11.srv); + SOKOL_ASSERT(fs_imgs[i]->d3d11.smp); + d3d11_fs_srvs[i] = fs_imgs[i]->d3d11.srv; + d3d11_fs_smps[i] = fs_imgs[i]->d3d11.smp; } for (; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { d3d11_fs_srvs[i] = 0; d3d11_fs_smps[i] = 0; } - ID3D11DeviceContext_IASetVertexBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_BUFFERS, d3d11_vbs, pip->d3d11_vb_strides, d3d11_vb_offsets); - ID3D11DeviceContext_IASetIndexBuffer(_sg.d3d11.ctx, d3d11_ib, pip->d3d11_index_format, ib_offset); + ID3D11DeviceContext_IASetVertexBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_BUFFERS, d3d11_vbs, pip->d3d11.vb_strides, d3d11_vb_offsets); + ID3D11DeviceContext_IASetIndexBuffer(_sg.d3d11.ctx, d3d11_ib, pip->d3d11.index_format, ib_offset); ID3D11DeviceContext_VSSetShaderResources(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_vs_srvs); ID3D11DeviceContext_VSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_vs_smps); ID3D11DeviceContext_PSSetShaderResources(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_fs_srvs); ID3D11DeviceContext_PSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_fs_smps); } -_SOKOL_PRIVATE void _sg_apply_uniforms(sg_shader_stage stage_index, int ub_index, const void* data, int num_bytes) { +_SOKOL_PRIVATE void _sg_d3d11_apply_uniforms(sg_shader_stage stage_index, int ub_index, const void* data, int num_bytes) { _SOKOL_UNUSED(num_bytes); SOKOL_ASSERT(_sg.d3d11.ctx && _sg.d3d11.in_pass); SOKOL_ASSERT(data && (num_bytes > 0)); SOKOL_ASSERT((stage_index >= 0) && ((int)stage_index < SG_NUM_SHADER_STAGES)); SOKOL_ASSERT((ub_index >= 0) && (ub_index < SG_MAX_SHADERSTAGE_UBS)); SOKOL_ASSERT(_sg.d3d11.cur_pipeline && _sg.d3d11.cur_pipeline->slot.id == _sg.d3d11.cur_pipeline_id.id); - SOKOL_ASSERT(_sg.d3d11.cur_pipeline->shader && _sg.d3d11.cur_pipeline->shader->slot.id == _sg.d3d11.cur_pipeline->shader_id.id); - SOKOL_ASSERT(ub_index < _sg.d3d11.cur_pipeline->shader->stage[stage_index].num_uniform_blocks); - SOKOL_ASSERT(num_bytes == _sg.d3d11.cur_pipeline->shader->stage[stage_index].uniform_blocks[ub_index].size); - ID3D11Buffer* cb = _sg.d3d11.cur_pipeline->shader->stage[stage_index].d3d11_cbs[ub_index]; + SOKOL_ASSERT(_sg.d3d11.cur_pipeline->shader && _sg.d3d11.cur_pipeline->shader->slot.id == _sg.d3d11.cur_pipeline->cmn.shader_id.id); + SOKOL_ASSERT(ub_index < _sg.d3d11.cur_pipeline->shader->cmn.stage[stage_index].num_uniform_blocks); + SOKOL_ASSERT(num_bytes == _sg.d3d11.cur_pipeline->shader->cmn.stage[stage_index].uniform_blocks[ub_index].size); + ID3D11Buffer* cb = _sg.d3d11.cur_pipeline->shader->d3d11.stage[stage_index].cbufs[ub_index]; SOKOL_ASSERT(cb); ID3D11DeviceContext_UpdateSubresource(_sg.d3d11.ctx, (ID3D11Resource*)cb, 0, NULL, data, 0, 0); } -_SOKOL_PRIVATE void _sg_draw(int base_element, int num_elements, int num_instances) { +_SOKOL_PRIVATE void _sg_d3d11_draw(int base_element, int num_elements, int num_instances) { SOKOL_ASSERT(_sg.d3d11.in_pass); if (_sg.d3d11.use_indexed_draw) { if (1 == num_instances) { @@ -7713,60 +8174,63 @@ _SOKOL_PRIVATE void _sg_draw(int base_element, int num_elements, int num_instanc } } -_SOKOL_PRIVATE void _sg_commit(void) { +_SOKOL_PRIVATE void _sg_d3d11_commit(void) { SOKOL_ASSERT(!_sg.d3d11.in_pass); } -_SOKOL_PRIVATE void _sg_update_buffer(_sg_buffer_t* buf, const void* data_ptr, int data_size) { +_SOKOL_PRIVATE void _sg_d3d11_update_buffer(_sg_buffer_t* buf, const void* data_ptr, uint32_t data_size) { SOKOL_ASSERT(buf && data_ptr && (data_size > 0)); SOKOL_ASSERT(_sg.d3d11.ctx); - SOKOL_ASSERT(buf->d3d11_buf); + SOKOL_ASSERT(buf->d3d11.buf); D3D11_MAPPED_SUBRESOURCE d3d11_msr; - HRESULT hr = ID3D11DeviceContext_Map(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11_buf, 0, D3D11_MAP_WRITE_DISCARD, 0, &d3d11_msr); + HRESULT hr = ID3D11DeviceContext_Map(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0, D3D11_MAP_WRITE_DISCARD, 0, &d3d11_msr); _SOKOL_UNUSED(hr); SOKOL_ASSERT(SUCCEEDED(hr)); memcpy(d3d11_msr.pData, data_ptr, data_size); - ID3D11DeviceContext_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11_buf, 0); + ID3D11DeviceContext_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0); } -_SOKOL_PRIVATE void _sg_append_buffer(_sg_buffer_t* buf, const void* data_ptr, int data_size, bool new_frame) { +_SOKOL_PRIVATE uint32_t _sg_d3d11_append_buffer(_sg_buffer_t* buf, const void* data_ptr, uint32_t data_size, bool new_frame) { SOKOL_ASSERT(buf && data_ptr && (data_size > 0)); SOKOL_ASSERT(_sg.d3d11.ctx); - SOKOL_ASSERT(buf->d3d11_buf); + SOKOL_ASSERT(buf->d3d11.buf); D3D11_MAP map_type = new_frame ? D3D11_MAP_WRITE_DISCARD : D3D11_MAP_WRITE_NO_OVERWRITE; D3D11_MAPPED_SUBRESOURCE d3d11_msr; - HRESULT hr = ID3D11DeviceContext_Map(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11_buf, 0, map_type, 0, &d3d11_msr); + HRESULT hr = ID3D11DeviceContext_Map(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0, map_type, 0, &d3d11_msr); _SOKOL_UNUSED(hr); SOKOL_ASSERT(SUCCEEDED(hr)); - uint8_t* dst_ptr = (uint8_t*)d3d11_msr.pData + buf->append_pos; + uint8_t* dst_ptr = (uint8_t*)d3d11_msr.pData + buf->cmn.append_pos; memcpy(dst_ptr, data_ptr, data_size); - ID3D11DeviceContext_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11_buf, 0); + ID3D11DeviceContext_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0); + /* NOTE: this is a requirement from WebGPU, but we want identical behaviour across all backend */ + return _sg_roundup(data_size, 4); } -_SOKOL_PRIVATE void _sg_update_image(_sg_image_t* img, const sg_image_content* data) { +_SOKOL_PRIVATE void _sg_d3d11_update_image(_sg_image_t* img, const sg_image_content* data) { SOKOL_ASSERT(img && data); SOKOL_ASSERT(_sg.d3d11.ctx); - SOKOL_ASSERT(img->d3d11_tex2d || img->d3d11_tex3d); + SOKOL_ASSERT(img->d3d11.tex2d || img->d3d11.tex3d); ID3D11Resource* d3d11_res = 0; - if (img->d3d11_tex3d) { - d3d11_res = (ID3D11Resource*) img->d3d11_tex3d; + if (img->d3d11.tex3d) { + d3d11_res = (ID3D11Resource*) img->d3d11.tex3d; } else { - d3d11_res = (ID3D11Resource*) img->d3d11_tex2d; + d3d11_res = (ID3D11Resource*) img->d3d11.tex2d; } SOKOL_ASSERT(d3d11_res); - const int num_faces = (img->type == SG_IMAGETYPE_CUBE) ? 6:1; - const int num_slices = (img->type == SG_IMAGETYPE_ARRAY) ? img->depth:1; + const int num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6:1; + const int num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.depth:1; int subres_index = 0; HRESULT hr; + _SOKOL_UNUSED(hr); D3D11_MAPPED_SUBRESOURCE d3d11_msr; for (int face_index = 0; face_index < num_faces; face_index++) { for (int slice_index = 0; slice_index < num_slices; slice_index++) { - for (int mip_index = 0; mip_index < img->num_mipmaps; mip_index++, subres_index++) { + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++, subres_index++) { SOKOL_ASSERT(subres_index < (SG_MAX_MIPMAPS * SG_MAX_TEXTUREARRAY_LAYERS)); - const int mip_width = ((img->width>>mip_index)>0) ? img->width>>mip_index : 1; - const int mip_height = ((img->height>>mip_index)>0) ? img->height>>mip_index : 1; - const int src_pitch = _sg_row_pitch(img->pixel_format, mip_width); + const int mip_width = ((img->cmn.width>>mip_index)>0) ? img->cmn.width>>mip_index : 1; + const int mip_height = ((img->cmn.height>>mip_index)>0) ? img->cmn.height>>mip_index : 1; + const int src_pitch = _sg_row_pitch(img->cmn.pixel_format, mip_width, 1); const sg_subimage_content* subimg_content = &(data->subimage[face_index][mip_index]); const int slice_size = subimg_content->size / num_slices; const int slice_offset = slice_size * slice_index; @@ -7796,6 +8260,16 @@ _SOKOL_PRIVATE void _sg_update_image(_sg_image_t* img, const sg_image_content* d /*== METAL BACKEND IMPLEMENTATION ============================================*/ #elif defined(SOKOL_METAL) +#if __has_feature(objc_arc) +#define _SG_OBJC_RETAIN(obj) { } +#define _SG_OBJC_RELEASE(obj) { obj = nil; } +#define _SG_OBJC_RELEASE_WITH_NULL(obj) { obj = [NSNull null]; } +#else +#define _SG_OBJC_RETAIN(obj) { [obj retain]; } +#define _SG_OBJC_RELEASE(obj) { [obj release]; obj = nil; } +#define _SG_OBJC_RELEASE_WITH_NULL(obj) { [obj release]; obj = [NSNull null]; } +#endif + /*-- enum translation functions ----------------------------------------------*/ _SOKOL_PRIVATE MTLLoadAction _sg_mtl_load_action(sg_action a) { switch (a) { @@ -8131,12 +8605,13 @@ _SOKOL_PRIVATE void _sg_mtl_init_pool(const sg_desc* desc) { 2 * desc->pipeline_pool_size + desc->pass_pool_size ); - _sg_mtl_idpool = [NSMutableArray arrayWithCapacity:_sg.mtl.idpool.num_slots]; + _sg.mtl.idpool.pool = [NSMutableArray arrayWithCapacity:_sg.mtl.idpool.num_slots]; + _SG_OBJC_RETAIN(_sg.mtl.idpool.pool); NSNull* null = [NSNull null]; for (uint32_t i = 0; i < _sg.mtl.idpool.num_slots; i++) { - [_sg_mtl_idpool addObject:null]; + [_sg.mtl.idpool.pool addObject:null]; } - SOKOL_ASSERT([_sg_mtl_idpool count] == _sg.mtl.idpool.num_slots); + SOKOL_ASSERT([_sg.mtl.idpool.pool count] == _sg.mtl.idpool.num_slots); /* a queue of currently free slot indices */ _sg.mtl.idpool.free_queue_top = 0; _sg.mtl.idpool.free_queue = (uint32_t*)SOKOL_MALLOC(_sg.mtl.idpool.num_slots * sizeof(uint32_t)); @@ -8160,7 +8635,7 @@ _SOKOL_PRIVATE void _sg_mtl_init_pool(const sg_desc* desc) { _SOKOL_PRIVATE void _sg_mtl_destroy_pool(void) { SOKOL_FREE(_sg.mtl.idpool.release_queue); _sg.mtl.idpool.release_queue = 0; SOKOL_FREE(_sg.mtl.idpool.free_queue); _sg.mtl.idpool.free_queue = 0; - _sg_mtl_idpool = nil; + _SG_OBJC_RELEASE(_sg.mtl.idpool.pool); } /* get a new free resource pool slot */ @@ -8184,8 +8659,8 @@ _SOKOL_PRIVATE uint32_t _sg_mtl_add_resource(id res) { return _SG_MTL_INVALID_SLOT_INDEX; } const uint32_t slot_index = _sg_mtl_alloc_pool_slot(); - SOKOL_ASSERT([NSNull null] == _sg_mtl_idpool[slot_index]); - _sg_mtl_idpool[slot_index] = res; + SOKOL_ASSERT([NSNull null] == _sg.mtl.idpool.pool[slot_index]); + _sg.mtl.idpool.pool[slot_index] = res; return slot_index; } @@ -8199,7 +8674,7 @@ _SOKOL_PRIVATE void _sg_mtl_release_resource(uint32_t frame_index, uint32_t slot return; } SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); - SOKOL_ASSERT([NSNull null] != _sg_mtl_idpool[slot_index]); + SOKOL_ASSERT([NSNull null] != _sg.mtl.idpool.pool[slot_index]); int release_index = _sg.mtl.idpool.release_queue_front++; if (_sg.mtl.idpool.release_queue_front >= _sg.mtl.idpool.num_slots) { /* wrap-around */ @@ -8223,8 +8698,8 @@ _SOKOL_PRIVATE void _sg_mtl_garbage_collect(uint32_t frame_index) { /* safe to release this resource */ const uint32_t slot_index = _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].slot_index; SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); - SOKOL_ASSERT(_sg_mtl_idpool[slot_index] != [NSNull null]); - _sg_mtl_idpool[slot_index] = [NSNull null]; + SOKOL_ASSERT(_sg.mtl.idpool.pool[slot_index] != [NSNull null]); + _SG_OBJC_RELEASE_WITH_NULL(_sg.mtl.idpool.pool[slot_index]); /* put the now free pool index back on the free queue */ _sg_mtl_free_pool_slot(slot_index); /* reset the release queue slot and advance the back index */ @@ -8238,20 +8713,13 @@ _SOKOL_PRIVATE void _sg_mtl_garbage_collect(uint32_t frame_index) { } } -/*-- a very simple sampler cache ----------------------------------------------- +_SOKOL_PRIVATE id _sg_mtl_id(uint32_t slot_index) { + return _sg.mtl.idpool.pool[slot_index]; +} - since there's only a small number of different samplers, sampler objects - will never be deleted (except on shutdown), and searching an identical - sampler is a simple linear search -*/ -/* initialize the sampler cache */ _SOKOL_PRIVATE void _sg_mtl_init_sampler_cache(const sg_desc* desc) { - SOKOL_ASSERT(desc->mtl_sampler_cache_size > 0); - _sg.mtl.sampler_cache.capacity = desc->mtl_sampler_cache_size; - _sg.mtl.sampler_cache.num_items = 0; - const int size = _sg.mtl.sampler_cache.capacity * sizeof(_sg_mtl_sampler_cache_item_t); - _sg.mtl.sampler_cache.items = (_sg_mtl_sampler_cache_item_t*)SOKOL_MALLOC(size); - memset(_sg.mtl.sampler_cache.items, 0, size); + SOKOL_ASSERT(desc->sampler_cache_size > 0); + _sg_smpcache_init(&_sg.mtl.sampler_cache, desc->sampler_cache_size); } /* destroy the sampler cache, and release all sampler objects */ @@ -8259,11 +8727,9 @@ _SOKOL_PRIVATE void _sg_mtl_destroy_sampler_cache(uint32_t frame_index) { SOKOL_ASSERT(_sg.mtl.sampler_cache.items); SOKOL_ASSERT(_sg.mtl.sampler_cache.num_items <= _sg.mtl.sampler_cache.capacity); for (int i = 0; i < _sg.mtl.sampler_cache.num_items; i++) { - _sg_mtl_release_resource(frame_index, _sg.mtl.sampler_cache.items[i].mtl_sampler_state); + _sg_mtl_release_resource(frame_index, (uint32_t)_sg_smpcache_sampler(&_sg.mtl.sampler_cache, i)); } - SOKOL_FREE(_sg.mtl.sampler_cache.items); _sg.mtl.sampler_cache.items = 0; - _sg.mtl.sampler_cache.num_items = 0; - _sg.mtl.sampler_cache.capacity = 0; + _sg_smpcache_discard(&_sg.mtl.sampler_cache); } /* @@ -8272,65 +8738,35 @@ _SOKOL_PRIVATE void _sg_mtl_destroy_sampler_cache(uint32_t frame_index) { */ _SOKOL_PRIVATE uint32_t _sg_mtl_create_sampler(id mtl_device, const sg_image_desc* img_desc) { SOKOL_ASSERT(img_desc); - SOKOL_ASSERT(_sg.mtl.sampler_cache.items); - /* sampler state cache is full */ - const sg_filter min_filter = img_desc->min_filter; - const sg_filter mag_filter = img_desc->mag_filter; - const sg_wrap wrap_u = img_desc->wrap_u; - const sg_wrap wrap_v = img_desc->wrap_v; - const sg_wrap wrap_w = img_desc->wrap_w; - const sg_border_color border_color = img_desc->border_color; - const uint32_t max_anisotropy = img_desc->max_anisotropy; - /* convert floats to valid int for proper comparison */ - const int min_lod = (int)(img_desc->min_lod * 1000.0f); - const int max_lod = (int)(_sg_clamp(img_desc->max_lod, 0.0f, 1000.0f) * 1000.0f); - /* first try to find identical sampler, number of samplers will be small, so linear search is ok */ - for (int i = 0; i < _sg.mtl.sampler_cache.num_items; i++) { - _sg_mtl_sampler_cache_item_t* item = &_sg.mtl.sampler_cache.items[i]; - if ((min_filter == item->min_filter) && - (mag_filter == item->mag_filter) && - (wrap_u == item->wrap_u) && - (wrap_v == item->wrap_v) && - (wrap_w == item->wrap_w) && - (max_anisotropy == item->max_anisotropy) && - (border_color == item->border_color) && - (min_lod == item->min_lod) && - (max_lod == item->max_lod)) - { - return item->mtl_sampler_state; + int index = _sg_smpcache_find_item(&_sg.mtl.sampler_cache, img_desc); + if (index >= 0) { + /* reuse existing sampler */ + return (uint32_t) _sg_smpcache_sampler(&_sg.mtl.sampler_cache, index); + } + else { + /* create a new Metal sampler state object and add to sampler cache */ + MTLSamplerDescriptor* mtl_desc = [[MTLSamplerDescriptor alloc] init]; + mtl_desc.sAddressMode = _sg_mtl_address_mode(img_desc->wrap_u); + mtl_desc.tAddressMode = _sg_mtl_address_mode(img_desc->wrap_v); + if (SG_IMAGETYPE_3D == img_desc->type) { + mtl_desc.rAddressMode = _sg_mtl_address_mode(img_desc->wrap_w); } + #if defined(_SG_TARGET_MACOS) + mtl_desc.borderColor = _sg_mtl_border_color(img_desc->border_color); + #endif + mtl_desc.minFilter = _sg_mtl_minmag_filter(img_desc->min_filter); + mtl_desc.magFilter = _sg_mtl_minmag_filter(img_desc->mag_filter); + mtl_desc.mipFilter = _sg_mtl_mip_filter(img_desc->min_filter); + mtl_desc.lodMinClamp = img_desc->min_lod; + mtl_desc.lodMaxClamp = img_desc->max_lod; + mtl_desc.maxAnisotropy = img_desc->max_anisotropy; + mtl_desc.normalizedCoordinates = YES; + id mtl_sampler = [mtl_device newSamplerStateWithDescriptor:mtl_desc]; + _SG_OBJC_RELEASE(mtl_desc); + uint32_t sampler_handle = _sg_mtl_add_resource(mtl_sampler); + _sg_smpcache_add_item(&_sg.mtl.sampler_cache, img_desc, sampler_handle); + return sampler_handle; } - /* fallthrough: need to create a new MTLSamplerState object */ - SOKOL_ASSERT(_sg.mtl.sampler_cache.num_items < _sg.mtl.sampler_cache.capacity); - _sg_mtl_sampler_cache_item_t* new_item = &_sg.mtl.sampler_cache.items[_sg.mtl.sampler_cache.num_items++]; - new_item->min_filter = min_filter; - new_item->mag_filter = mag_filter; - new_item->wrap_u = wrap_u; - new_item->wrap_v = wrap_v; - new_item->wrap_w = wrap_w; - new_item->min_lod = min_lod; - new_item->max_lod = max_lod; - new_item->max_anisotropy = max_anisotropy; - new_item->border_color = border_color; - MTLSamplerDescriptor* mtl_desc = [[MTLSamplerDescriptor alloc] init]; - mtl_desc.sAddressMode = _sg_mtl_address_mode(wrap_u); - mtl_desc.tAddressMode = _sg_mtl_address_mode(wrap_v); - if (SG_IMAGETYPE_3D == img_desc->type) { - mtl_desc.rAddressMode = _sg_mtl_address_mode(wrap_w); - } - #if defined(_SG_TARGET_MACOS) - mtl_desc.borderColor = _sg_mtl_border_color(border_color); - #endif - mtl_desc.minFilter = _sg_mtl_minmag_filter(min_filter); - mtl_desc.magFilter = _sg_mtl_minmag_filter(mag_filter); - mtl_desc.mipFilter = _sg_mtl_mip_filter(min_filter); - mtl_desc.lodMinClamp = img_desc->min_lod; - mtl_desc.lodMaxClamp = img_desc->max_lod; - mtl_desc.maxAnisotropy = max_anisotropy; - mtl_desc.normalizedCoordinates = YES; - id mtl_sampler = [mtl_device newSamplerStateWithDescriptor:mtl_desc]; - new_item->mtl_sampler_state = _sg_mtl_add_resource(mtl_sampler); - return new_item->mtl_sampler_state; } _SOKOL_PRIVATE void _sg_mtl_clear_state_cache(void) { @@ -8476,31 +8912,30 @@ _SOKOL_PRIVATE void _sg_mtl_init_caps(void) { } /*-- main Metal backend state and functions ----------------------------------*/ -_SOKOL_PRIVATE void _sg_setup_backend(const sg_desc* desc) { - puts("Sokol backend: Metal"); +_SOKOL_PRIVATE void _sg_mtl_setup_backend(const sg_desc* desc) { /* assume already zero-initialized */ SOKOL_ASSERT(desc); - SOKOL_ASSERT(desc->mtl_device); - SOKOL_ASSERT(desc->mtl_renderpass_descriptor_cb); - SOKOL_ASSERT(desc->mtl_drawable_cb); - SOKOL_ASSERT(desc->mtl_global_uniform_buffer_size > 0); + SOKOL_ASSERT(desc->context.metal.device); + SOKOL_ASSERT(desc->context.metal.renderpass_descriptor_cb); + SOKOL_ASSERT(desc->context.metal.drawable_cb); + SOKOL_ASSERT(desc->uniform_buffer_size > 0); _sg_mtl_init_pool(desc); _sg_mtl_init_sampler_cache(desc); _sg_mtl_clear_state_cache(); _sg.mtl.valid = true; - _sg.mtl.renderpass_descriptor_cb = desc->mtl_renderpass_descriptor_cb; - _sg.mtl.drawable_cb = desc->mtl_drawable_cb; + _sg.mtl.renderpass_descriptor_cb = desc->context.metal.renderpass_descriptor_cb; + _sg.mtl.drawable_cb = desc->context.metal.drawable_cb; _sg.mtl.frame_index = 1; - _sg.mtl.ub_size = desc->mtl_global_uniform_buffer_size; - _sg_mtl_sem = dispatch_semaphore_create(SG_NUM_INFLIGHT_FRAMES); - _sg_mtl_device = (__bridge id) desc->mtl_device; - _sg_mtl_cmd_queue = [_sg_mtl_device newCommandQueue]; + _sg.mtl.ub_size = desc->uniform_buffer_size; + _sg.mtl.sem = dispatch_semaphore_create(SG_NUM_INFLIGHT_FRAMES); + _sg.mtl.device = (__bridge id) desc->context.metal.device; + _sg.mtl.cmd_queue = [_sg.mtl.device newCommandQueue]; MTLResourceOptions res_opts = MTLResourceCPUCacheModeWriteCombined; #if defined(_SG_TARGET_MACOS) res_opts |= MTLResourceStorageModeManaged; #endif for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { - _sg_mtl_uniform_buffers[i] = [_sg_mtl_device + _sg.mtl.uniform_buffers[i] = [_sg.mtl.device newBufferWithLength:_sg.mtl.ub_size options:res_opts ]; @@ -8508,105 +8943,126 @@ _SOKOL_PRIVATE void _sg_setup_backend(const sg_desc* desc) { _sg_mtl_init_caps(); } -_SOKOL_PRIVATE void _sg_discard_backend(void) { +_SOKOL_PRIVATE void _sg_mtl_discard_backend(void) { SOKOL_ASSERT(_sg.mtl.valid); /* wait for the last frame to finish */ for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { - dispatch_semaphore_wait(_sg_mtl_sem, DISPATCH_TIME_FOREVER); + dispatch_semaphore_wait(_sg.mtl.sem, DISPATCH_TIME_FOREVER); + } + /* semaphore must be "relinquished" before destruction */ + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + dispatch_semaphore_signal(_sg.mtl.sem); } _sg_mtl_destroy_sampler_cache(_sg.mtl.frame_index); _sg_mtl_garbage_collect(_sg.mtl.frame_index + SG_NUM_INFLIGHT_FRAMES + 2); _sg_mtl_destroy_pool(); _sg.mtl.valid = false; - _sg_mtl_cmd_encoder = nil; - _sg_mtl_cmd_buffer = nil; - _sg_mtl_cmd_queue = nil; + + _SG_OBJC_RELEASE(_sg.mtl.sem); + _SG_OBJC_RELEASE(_sg.mtl.device); + _SG_OBJC_RELEASE(_sg.mtl.cmd_queue); for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { - _sg_mtl_uniform_buffers[i] = nil; + _SG_OBJC_RELEASE(_sg.mtl.uniform_buffers[i]); } - _sg_mtl_device = nil; + /* NOTE: MTLCommandBuffer and MTLRenderCommandEncoder are auto-released */ + _sg.mtl.cmd_buffer = nil; + _sg.mtl.cmd_encoder = nil; } -_SOKOL_PRIVATE void _sg_reset_state_cache(void) { +_SOKOL_PRIVATE void _sg_mtl_bind_uniform_buffers(void) { + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); + for (int slot = 0; slot < SG_MAX_SHADERSTAGE_UBS; slot++) { + [_sg.mtl.cmd_encoder + setVertexBuffer:_sg.mtl.uniform_buffers[_sg.mtl.cur_frame_rotate_index] + offset:0 + atIndex:slot]; + [_sg.mtl.cmd_encoder + setFragmentBuffer:_sg.mtl.uniform_buffers[_sg.mtl.cur_frame_rotate_index] + offset:0 + atIndex:slot]; + } +} + +_SOKOL_PRIVATE void _sg_mtl_reset_state_cache(void) { _sg_mtl_clear_state_cache(); + + /* need to restore the uniform buffer binding (normally happens in + _sg_mtl_begin_pass() + */ + if (nil != _sg.mtl.cmd_encoder) { + _sg_mtl_bind_uniform_buffers(); + } } -_SOKOL_PRIVATE sg_resource_state _sg_create_context(_sg_context_t* ctx) { +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_context(_sg_context_t* ctx) { SOKOL_ASSERT(ctx); _SOKOL_UNUSED(ctx); return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_destroy_context(_sg_context_t* ctx) { +_SOKOL_PRIVATE void _sg_mtl_destroy_context(_sg_context_t* ctx) { SOKOL_ASSERT(ctx); _SOKOL_UNUSED(ctx); /* empty */ } -_SOKOL_PRIVATE void _sg_activate_context(_sg_context_t* ctx) { - _sg_reset_state_cache(); +_SOKOL_PRIVATE void _sg_mtl_activate_context(_sg_context_t* ctx) { + _SOKOL_UNUSED(ctx); + _sg_mtl_clear_state_cache(); } -_SOKOL_PRIVATE sg_resource_state _sg_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { SOKOL_ASSERT(buf && desc); - buf->size = desc->size; - buf->append_pos = 0; - buf->append_overflow = false; - buf->type = desc->type; - buf->usage = desc->usage; - buf->update_frame_index = 0; - buf->append_frame_index = 0; - buf->num_slots = (buf->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; - buf->active_slot = 0; + _sg_buffer_common_init(&buf->cmn, desc); const bool injected = (0 != desc->mtl_buffers[0]); - MTLResourceOptions mtl_options = _sg_mtl_buffer_resource_options(buf->usage); - for (int slot = 0; slot < buf->num_slots; slot++) { + MTLResourceOptions mtl_options = _sg_mtl_buffer_resource_options(buf->cmn.usage); + for (int slot = 0; slot < buf->cmn.num_slots; slot++) { id mtl_buf; if (injected) { SOKOL_ASSERT(desc->mtl_buffers[slot]); mtl_buf = (__bridge id) desc->mtl_buffers[slot]; } else { - if (buf->usage == SG_USAGE_IMMUTABLE) { + if (buf->cmn.usage == SG_USAGE_IMMUTABLE) { SOKOL_ASSERT(desc->content); - mtl_buf = [_sg_mtl_device newBufferWithBytes:desc->content length:buf->size options:mtl_options]; + mtl_buf = [_sg.mtl.device newBufferWithBytes:desc->content length:buf->cmn.size options:mtl_options]; } else { - mtl_buf = [_sg_mtl_device newBufferWithLength:buf->size options:mtl_options]; + mtl_buf = [_sg.mtl.device newBufferWithLength:buf->cmn.size options:mtl_options]; } } - buf->mtl_buf[slot] = _sg_mtl_add_resource(mtl_buf); + buf->mtl.buf[slot] = _sg_mtl_add_resource(mtl_buf); } return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_destroy_buffer(_sg_buffer_t* buf) { +_SOKOL_PRIVATE void _sg_mtl_destroy_buffer(_sg_buffer_t* buf) { SOKOL_ASSERT(buf); - for (int slot = 0; slot < buf->num_slots; slot++) { + for (int slot = 0; slot < buf->cmn.num_slots; slot++) { /* it's valid to call release resource with '0' */ - _sg_mtl_release_resource(_sg.mtl.frame_index, buf->mtl_buf[slot]); + _sg_mtl_release_resource(_sg.mtl.frame_index, buf->mtl.buf[slot]); } } _SOKOL_PRIVATE void _sg_mtl_copy_image_content(const _sg_image_t* img, __unsafe_unretained id mtl_tex, const sg_image_content* content) { - const int num_faces = (img->type == SG_IMAGETYPE_CUBE) ? 6:1; - const int num_slices = (img->type == SG_IMAGETYPE_ARRAY) ? img->depth : 1; + const int num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6:1; + const int num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.depth : 1; for (int face_index = 0; face_index < num_faces; face_index++) { - for (int mip_index = 0; mip_index < img->num_mipmaps; mip_index++) { + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++) { SOKOL_ASSERT(content->subimage[face_index][mip_index].ptr); SOKOL_ASSERT(content->subimage[face_index][mip_index].size > 0); const uint8_t* data_ptr = (const uint8_t*)content->subimage[face_index][mip_index].ptr; - const int mip_width = _sg_max(img->width >> mip_index, 1); - const int mip_height = _sg_max(img->height >> mip_index, 1); + const int mip_width = _sg_max(img->cmn.width >> mip_index, 1); + const int mip_height = _sg_max(img->cmn.height >> mip_index, 1); /* special case PVRTC formats: bytePerRow must be 0 */ int bytes_per_row = 0; - int bytes_per_slice = _sg_surface_pitch(img->pixel_format, mip_width, mip_height); - if (!_sg_mtl_is_pvrtc(img->pixel_format)) { - bytes_per_row = _sg_row_pitch(img->pixel_format, mip_width); + int bytes_per_slice = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1); + if (!_sg_mtl_is_pvrtc(img->cmn.pixel_format)) { + bytes_per_row = _sg_row_pitch(img->cmn.pixel_format, mip_width, 1); } MTLRegion region; - if (img->type == SG_IMAGETYPE_3D) { - const int mip_depth = _sg_max(img->depth >> mip_index, 1); + if (img->cmn.type == SG_IMAGETYPE_3D) { + const int mip_depth = _sg_max(img->cmn.depth >> mip_index, 1); region = MTLRegionMake3D(0, 0, 0, mip_width, mip_height, mip_depth); /* FIXME: apparently the minimal bytes_per_image size for 3D texture is 4 KByte... somehow need to handle this */ @@ -8615,7 +9071,7 @@ _SOKOL_PRIVATE void _sg_mtl_copy_image_content(const _sg_image_t* img, __unsafe_ region = MTLRegionMake2D(0, 0, mip_width, mip_height); } for (int slice_index = 0; slice_index < num_slices; slice_index++) { - const int mtl_slice_index = (img->type == SG_IMAGETYPE_CUBE) ? face_index : slice_index; + const int mtl_slice_index = (img->cmn.type == SG_IMAGETYPE_CUBE) ? face_index : slice_index; const int slice_offset = slice_index * bytes_per_slice; SOKOL_ASSERT((slice_offset + bytes_per_slice) <= (int)content->subimage[face_index][mip_index].size); [mtl_tex replaceRegion:region @@ -8646,29 +9102,29 @@ _SOKOL_PRIVATE void _sg_mtl_copy_image_content(const _sg_image_t* img, __unsafe_ /* initialize MTLTextureDescritor with common attributes */ _SOKOL_PRIVATE bool _sg_mtl_init_texdesc_common(MTLTextureDescriptor* mtl_desc, _sg_image_t* img) { - mtl_desc.textureType = _sg_mtl_texture_type(img->type); - mtl_desc.pixelFormat = _sg_mtl_pixel_format(img->pixel_format); + mtl_desc.textureType = _sg_mtl_texture_type(img->cmn.type); + mtl_desc.pixelFormat = _sg_mtl_pixel_format(img->cmn.pixel_format); if (MTLPixelFormatInvalid == mtl_desc.pixelFormat) { SOKOL_LOG("Unsupported texture pixel format!\n"); return false; } - mtl_desc.width = img->width; - mtl_desc.height = img->height; - if (SG_IMAGETYPE_3D == img->type) { - mtl_desc.depth = img->depth; + mtl_desc.width = img->cmn.width; + mtl_desc.height = img->cmn.height; + if (SG_IMAGETYPE_3D == img->cmn.type) { + mtl_desc.depth = img->cmn.depth; } else { mtl_desc.depth = 1; } - mtl_desc.mipmapLevelCount = img->num_mipmaps; - if (SG_IMAGETYPE_ARRAY == img->type) { - mtl_desc.arrayLength = img->depth; + mtl_desc.mipmapLevelCount = img->cmn.num_mipmaps; + if (SG_IMAGETYPE_ARRAY == img->cmn.type) { + mtl_desc.arrayLength = img->cmn.depth; } else { mtl_desc.arrayLength = 1; } mtl_desc.usage = MTLTextureUsageShaderRead; - if (img->usage != SG_USAGE_IMMUTABLE) { + if (img->cmn.usage != SG_USAGE_IMMUTABLE) { mtl_desc.cpuCacheMode = MTLCPUCacheModeWriteCombined; } #if defined(_SG_TARGET_MACOS) @@ -8685,7 +9141,8 @@ _SOKOL_PRIVATE bool _sg_mtl_init_texdesc_common(MTLTextureDescriptor* mtl_desc, /* initialize MTLTextureDescritor with rendertarget attributes */ _SOKOL_PRIVATE void _sg_mtl_init_texdesc_rt(MTLTextureDescriptor* mtl_desc, _sg_image_t* img) { - SOKOL_ASSERT(img->render_target); + SOKOL_ASSERT(img->cmn.render_target); + _SOKOL_UNUSED(img); /* reset the cpuCacheMode to 'default' */ mtl_desc.cpuCacheMode = MTLCPUCacheModeDefaultCache; /* render targets are only visible to the GPU */ @@ -8697,7 +9154,7 @@ _SOKOL_PRIVATE void _sg_mtl_init_texdesc_rt(MTLTextureDescriptor* mtl_desc, _sg_ /* initialize MTLTextureDescritor with MSAA attributes */ _SOKOL_PRIVATE void _sg_mtl_init_texdesc_rt_msaa(MTLTextureDescriptor* mtl_desc, _sg_image_t* img) { - SOKOL_ASSERT(img->sample_count > 1); + SOKOL_ASSERT(img->cmn.sample_count > 1); /* reset the cpuCacheMode to 'default' */ mtl_desc.cpuCacheMode = MTLCPUCacheModeDefaultCache; /* render targets are only visible to the GPU */ @@ -8709,52 +9166,36 @@ _SOKOL_PRIVATE void _sg_mtl_init_texdesc_rt_msaa(MTLTextureDescriptor* mtl_desc, mtl_desc.depth = 1; mtl_desc.arrayLength = 1; mtl_desc.mipmapLevelCount = 1; - mtl_desc.sampleCount = img->sample_count; + mtl_desc.sampleCount = img->cmn.sample_count; } -_SOKOL_PRIVATE sg_resource_state _sg_create_image(_sg_image_t* img, const sg_image_desc* desc) { +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_image(_sg_image_t* img, const sg_image_desc* desc) { SOKOL_ASSERT(img && desc); - img->type = desc->type; - img->render_target = desc->render_target; - img->width = desc->width; - img->height = desc->height; - img->depth = desc->depth; - img->num_mipmaps = desc->num_mipmaps; - img->usage = desc->usage; - img->pixel_format = desc->pixel_format; - img->sample_count = desc->sample_count; - img->min_filter = desc->min_filter; - img->mag_filter = desc->mag_filter; - img->wrap_u = desc->wrap_u; - img->wrap_v = desc->wrap_v; - img->wrap_w = desc->wrap_w; - img->max_anisotropy = desc->max_anisotropy; - img->upd_frame_index = 0; - img->num_slots = (img->usage == SG_USAGE_IMMUTABLE) ? 1 :SG_NUM_INFLIGHT_FRAMES; - img->active_slot = 0; + _sg_image_common_init(&img->cmn, desc); const bool injected = (0 != desc->mtl_textures[0]); - const bool msaa = (img->sample_count > 1); + const bool msaa = (img->cmn.sample_count > 1); /* first initialize all Metal resource pool slots to 'empty' */ for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { - img->mtl_tex[i] = _sg_mtl_add_resource(nil); + img->mtl.tex[i] = _sg_mtl_add_resource(nil); } - img->mtl_sampler_state = _sg_mtl_add_resource(nil); - img->mtl_depth_tex = _sg_mtl_add_resource(nil); - img->mtl_msaa_tex = _sg_mtl_add_resource(nil); + img->mtl.sampler_state = _sg_mtl_add_resource(nil); + img->mtl.depth_tex = _sg_mtl_add_resource(nil); + img->mtl.msaa_tex = _sg_mtl_add_resource(nil); /* initialize a Metal texture descriptor with common attributes */ MTLTextureDescriptor* mtl_desc = [[MTLTextureDescriptor alloc] init]; if (!_sg_mtl_init_texdesc_common(mtl_desc, img)) { + _SG_OBJC_RELEASE(mtl_desc); return SG_RESOURCESTATE_FAILED; } /* special case depth-stencil-buffer? */ - if (_sg_is_valid_rendertarget_depth_format(img->pixel_format)) { + if (_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format)) { /* depth-stencil buffer texture must always be a render target */ - SOKOL_ASSERT(img->render_target); - SOKOL_ASSERT(img->type == SG_IMAGETYPE_2D); - SOKOL_ASSERT(img->num_mipmaps == 1); + SOKOL_ASSERT(img->cmn.render_target); + SOKOL_ASSERT(img->cmn.type == SG_IMAGETYPE_2D); + SOKOL_ASSERT(img->cmn.num_mipmaps == 1); SOKOL_ASSERT(!injected); if (msaa) { _sg_mtl_init_texdesc_rt_msaa(mtl_desc, img); @@ -8762,9 +9203,9 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_image(_sg_image_t* img, const sg_ima else { _sg_mtl_init_texdesc_rt(mtl_desc, img); } - id tex = [_sg_mtl_device newTextureWithDescriptor:mtl_desc]; + id tex = [_sg.mtl.device newTextureWithDescriptor:mtl_desc]; SOKOL_ASSERT(nil != tex); - img->mtl_depth_tex = _sg_mtl_add_resource(tex); + img->mtl.depth_tex = _sg_mtl_add_resource(tex); } else { /* create the color texture @@ -8775,51 +9216,52 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_image(_sg_image_t* img, const sg_ima will go into a separate render target texture of type MTLTextureType2DMultisample. */ - if (img->render_target && !msaa) { + if (img->cmn.render_target && !msaa) { _sg_mtl_init_texdesc_rt(mtl_desc, img); } - for (int slot = 0; slot < img->num_slots; slot++) { + for (int slot = 0; slot < img->cmn.num_slots; slot++) { id tex; if (injected) { SOKOL_ASSERT(desc->mtl_textures[slot]); tex = (__bridge id) desc->mtl_textures[slot]; } else { - tex = [_sg_mtl_device newTextureWithDescriptor:mtl_desc]; - if ((img->usage == SG_USAGE_IMMUTABLE) && !img->render_target) { + tex = [_sg.mtl.device newTextureWithDescriptor:mtl_desc]; + if ((img->cmn.usage == SG_USAGE_IMMUTABLE) && !img->cmn.render_target) { _sg_mtl_copy_image_content(img, tex, &desc->content); } } - img->mtl_tex[slot] = _sg_mtl_add_resource(tex); + img->mtl.tex[slot] = _sg_mtl_add_resource(tex); } /* if MSAA color render target, create an additional MSAA render-surface texture */ - if (img->render_target && msaa) { + if (img->cmn.render_target && msaa) { _sg_mtl_init_texdesc_rt_msaa(mtl_desc, img); - id tex = [_sg_mtl_device newTextureWithDescriptor:mtl_desc]; - img->mtl_msaa_tex = _sg_mtl_add_resource(tex); + id tex = [_sg.mtl.device newTextureWithDescriptor:mtl_desc]; + img->mtl.msaa_tex = _sg_mtl_add_resource(tex); } /* create (possibly shared) sampler state */ - img->mtl_sampler_state = _sg_mtl_create_sampler(_sg_mtl_device, desc); + img->mtl.sampler_state = _sg_mtl_create_sampler(_sg.mtl.device, desc); } + _SG_OBJC_RELEASE(mtl_desc); return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_destroy_image(_sg_image_t* img) { +_SOKOL_PRIVATE void _sg_mtl_destroy_image(_sg_image_t* img) { SOKOL_ASSERT(img); /* it's valid to call release resource with a 'null resource' */ - for (int slot = 0; slot < img->num_slots; slot++) { - _sg_mtl_release_resource(_sg.mtl.frame_index, img->mtl_tex[slot]); + for (int slot = 0; slot < img->cmn.num_slots; slot++) { + _sg_mtl_release_resource(_sg.mtl.frame_index, img->mtl.tex[slot]); } - _sg_mtl_release_resource(_sg.mtl.frame_index, img->mtl_depth_tex); - _sg_mtl_release_resource(_sg.mtl.frame_index, img->mtl_msaa_tex); + _sg_mtl_release_resource(_sg.mtl.frame_index, img->mtl.depth_tex); + _sg_mtl_release_resource(_sg.mtl.frame_index, img->mtl.msaa_tex); /* NOTE: sampler state objects are shared and not released until shutdown */ } _SOKOL_PRIVATE id _sg_mtl_compile_library(const char* src) { NSError* err = NULL; - id lib = [_sg_mtl_device + id lib = [_sg.mtl.device newLibraryWithSource:[NSString stringWithUTF8String:src] options:nil error:&err @@ -8833,41 +9275,18 @@ _SOKOL_PRIVATE id _sg_mtl_compile_library(const char* src) { _SOKOL_PRIVATE id _sg_mtl_library_from_bytecode(const uint8_t* ptr, int num_bytes) { NSError* err = NULL; dispatch_data_t lib_data = dispatch_data_create(ptr, num_bytes, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT); - id lib = [_sg_mtl_device newLibraryWithData:lib_data error:&err]; + id lib = [_sg.mtl.device newLibraryWithData:lib_data error:&err]; if (err) { SOKOL_LOG([err.localizedDescription UTF8String]); } + _SG_OBJC_RELEASE(lib_data); return lib; } -_SOKOL_PRIVATE sg_resource_state _sg_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { - puts("sokol: create Metal shader"); +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { SOKOL_ASSERT(shd && desc); - /* uniform block sizes and image types */ - for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { - const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS) ? &desc->vs : &desc->fs; - _sg_shader_stage_t* stage = &shd->stage[stage_index]; - SOKOL_ASSERT(stage->num_uniform_blocks == 0); - for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { - const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; - if (0 == ub_desc->size) { - break; - } - _sg_uniform_block_t* ub = &stage->uniform_blocks[ub_index]; - ub->size = ub_desc->size; - stage->num_uniform_blocks++; - } - SOKOL_ASSERT(stage->num_images == 0); - for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { - const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; - if (img_desc->type == _SG_IMAGETYPE_DEFAULT) { - break; - } - stage->images[img_index].type = img_desc->type; - stage->num_images++; - } - } + _sg_shader_common_init(&shd->cmn, desc); /* create metal libray objects and lookup entry functions */ id vs_lib; @@ -8908,48 +9327,38 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_shader(_sg_shader_t* shd, const sg_s return SG_RESOURCESTATE_FAILED; } /* it is legal to call _sg_mtl_add_resource with a nil value, this will return a special 0xFFFFFFFF index */ - shd->stage[SG_SHADERSTAGE_VS].mtl_lib = _sg_mtl_add_resource(vs_lib); - shd->stage[SG_SHADERSTAGE_FS].mtl_lib = _sg_mtl_add_resource(fs_lib); - shd->stage[SG_SHADERSTAGE_VS].mtl_func = _sg_mtl_add_resource(vs_func); - shd->stage[SG_SHADERSTAGE_FS].mtl_func = _sg_mtl_add_resource(fs_func); + shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_lib = _sg_mtl_add_resource(vs_lib); + shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_lib = _sg_mtl_add_resource(fs_lib); + shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func = _sg_mtl_add_resource(vs_func); + shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func = _sg_mtl_add_resource(fs_func); return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_destroy_shader(_sg_shader_t* shd) { +_SOKOL_PRIVATE void _sg_mtl_destroy_shader(_sg_shader_t* shd) { SOKOL_ASSERT(shd); /* it is valid to call _sg_mtl_release_resource with a 'null resource' */ - _sg_mtl_release_resource(_sg.mtl.frame_index, shd->stage[SG_SHADERSTAGE_VS].mtl_func); - _sg_mtl_release_resource(_sg.mtl.frame_index, shd->stage[SG_SHADERSTAGE_VS].mtl_lib); - _sg_mtl_release_resource(_sg.mtl.frame_index, shd->stage[SG_SHADERSTAGE_FS].mtl_func); - _sg_mtl_release_resource(_sg.mtl.frame_index, shd->stage[SG_SHADERSTAGE_FS].mtl_lib); + _sg_mtl_release_resource(_sg.mtl.frame_index, shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func); + _sg_mtl_release_resource(_sg.mtl.frame_index, shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_lib); + _sg_mtl_release_resource(_sg.mtl.frame_index, shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func); + _sg_mtl_release_resource(_sg.mtl.frame_index, shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_lib); } -_SOKOL_PRIVATE sg_resource_state _sg_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { SOKOL_ASSERT(pip && shd && desc); SOKOL_ASSERT(desc->shader.id == shd->slot.id); pip->shader = shd; - pip->shader_id = desc->shader; - pip->color_attachment_count = desc->blend.color_attachment_count; - pip->color_format = desc->blend.color_format; - pip->depth_format = desc->blend.depth_format; - pip->sample_count = desc->rasterizer.sample_count; - pip->depth_bias = desc->rasterizer.depth_bias; - pip->depth_bias_slope_scale = desc->rasterizer.depth_bias_slope_scale; - pip->depth_bias_clamp = desc->rasterizer.depth_bias_clamp; + _sg_pipeline_common_init(&pip->cmn, desc); + sg_primitive_type prim_type = desc->primitive_type; - pip->mtl_prim_type = _sg_mtl_primitive_type(prim_type); - pip->index_type = desc->index_type; - pip->mtl_index_size = _sg_mtl_index_size(pip->index_type); - if (SG_INDEXTYPE_NONE != pip->index_type) { - pip->mtl_index_type = _sg_mtl_index_type(pip->index_type); - } - pip->mtl_cull_mode = _sg_mtl_cull_mode(desc->rasterizer.cull_mode); - pip->mtl_winding = _sg_mtl_winding(desc->rasterizer.face_winding); - pip->mtl_stencil_ref = desc->depth_stencil.stencil_ref; - for (int i = 0; i < 4; i++) { - pip->blend_color[i] = desc->blend.blend_color[i]; + pip->mtl.prim_type = _sg_mtl_primitive_type(prim_type); + pip->mtl.index_size = _sg_mtl_index_size(pip->cmn.index_type); + if (SG_INDEXTYPE_NONE != pip->cmn.index_type) { + pip->mtl.index_type = _sg_mtl_index_type(pip->cmn.index_type); } + pip->mtl.cull_mode = _sg_mtl_cull_mode(desc->rasterizer.cull_mode); + pip->mtl.winding = _sg_mtl_winding(desc->rasterizer.face_winding); + pip->mtl.stencil_ref = desc->depth_stencil.stencil_ref; /* create vertex-descriptor */ MTLVertexDescriptor* vtx_desc = [MTLVertexDescriptor vertexDescriptor]; @@ -8962,10 +9371,10 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_pipeline(_sg_pipeline_t* pip, _sg_sh vtx_desc.attributes[attr_index].format = _sg_mtl_vertex_format(a_desc->format); vtx_desc.attributes[attr_index].offset = a_desc->offset; vtx_desc.attributes[attr_index].bufferIndex = a_desc->buffer_index + SG_MAX_SHADERSTAGE_UBS; - pip->vertex_layout_valid[a_desc->buffer_index] = true; + pip->cmn.vertex_layout_valid[a_desc->buffer_index] = true; } for (int layout_index = 0; layout_index < SG_MAX_SHADERSTAGE_BUFFERS; layout_index++) { - if (pip->vertex_layout_valid[layout_index]) { + if (pip->cmn.vertex_layout_valid[layout_index]) { const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[layout_index]; const int mtl_vb_slot = layout_index + SG_MAX_SHADERSTAGE_UBS; SOKOL_ASSERT(l_desc->stride > 0); @@ -8978,10 +9387,10 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_pipeline(_sg_pipeline_t* pip, _sg_sh /* render-pipeline descriptor */ MTLRenderPipelineDescriptor* rp_desc = [[MTLRenderPipelineDescriptor alloc] init]; rp_desc.vertexDescriptor = vtx_desc; - SOKOL_ASSERT(shd->stage[SG_SHADERSTAGE_VS].mtl_func != _SG_MTL_INVALID_SLOT_INDEX); - rp_desc.vertexFunction = _sg_mtl_idpool[shd->stage[SG_SHADERSTAGE_VS].mtl_func]; - SOKOL_ASSERT(shd->stage[SG_SHADERSTAGE_FS].mtl_func != _SG_MTL_INVALID_SLOT_INDEX); - rp_desc.fragmentFunction = _sg_mtl_idpool[shd->stage[SG_SHADERSTAGE_FS].mtl_func]; + SOKOL_ASSERT(shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func != _SG_MTL_INVALID_SLOT_INDEX); + rp_desc.vertexFunction = _sg_mtl_id(shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func); + SOKOL_ASSERT(shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func != _SG_MTL_INVALID_SLOT_INDEX); + rp_desc.fragmentFunction = _sg_mtl_id(shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func); rp_desc.sampleCount = desc->rasterizer.sample_count; rp_desc.alphaToCoverageEnabled = desc->rasterizer.alpha_to_coverage_enabled; rp_desc.alphaToOneEnabled = NO; @@ -9011,7 +9420,8 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_pipeline(_sg_pipeline_t* pip, _sg_sh rp_desc.colorAttachments[i].sourceRGBBlendFactor = _sg_mtl_blend_factor(desc->blend.src_factor_rgb); } NSError* err = NULL; - id mtl_rps = [_sg_mtl_device newRenderPipelineStateWithDescriptor:rp_desc error:&err]; + id mtl_rps = [_sg.mtl.device newRenderPipelineStateWithDescriptor:rp_desc error:&err]; + _SG_OBJC_RELEASE(rp_desc); if (nil == mtl_rps) { SOKOL_ASSERT(err); SOKOL_LOG([err.localizedDescription UTF8String]); @@ -9040,68 +9450,71 @@ _SOKOL_PRIVATE sg_resource_state _sg_create_pipeline(_sg_pipeline_t* pip, _sg_sh ds_desc.frontFaceStencil.readMask = desc->depth_stencil.stencil_read_mask; ds_desc.frontFaceStencil.writeMask = desc->depth_stencil.stencil_write_mask; } - id mtl_dss = [_sg_mtl_device newDepthStencilStateWithDescriptor:ds_desc]; - - pip->mtl_rps = _sg_mtl_add_resource(mtl_rps); - pip->mtl_dss = _sg_mtl_add_resource(mtl_dss); + id mtl_dss = [_sg.mtl.device newDepthStencilStateWithDescriptor:ds_desc]; + _SG_OBJC_RELEASE(ds_desc); + pip->mtl.rps = _sg_mtl_add_resource(mtl_rps); + pip->mtl.dss = _sg_mtl_add_resource(mtl_dss); return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_destroy_pipeline(_sg_pipeline_t* pip) { +_SOKOL_PRIVATE void _sg_mtl_destroy_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); /* it's valid to call release resource with a 'null resource' */ - _sg_mtl_release_resource(_sg.mtl.frame_index, pip->mtl_rps); - _sg_mtl_release_resource(_sg.mtl.frame_index, pip->mtl_dss); + _sg_mtl_release_resource(_sg.mtl.frame_index, pip->mtl.rps); + _sg_mtl_release_resource(_sg.mtl.frame_index, pip->mtl.dss); } -_SOKOL_PRIVATE sg_resource_state _sg_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { SOKOL_ASSERT(pass && desc); SOKOL_ASSERT(att_images && att_images[0]); - /* copy image pointers and desc attributes */ + _sg_pass_common_init(&pass->cmn, desc); + + /* copy image pointers */ const sg_attachment_desc* att_desc; - _sg_attachment_t* att; - for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - SOKOL_ASSERT(0 == pass->color_atts[i].image); + for (int i = 0; i < pass->cmn.num_color_atts; i++) { att_desc = &desc->color_attachments[i]; if (att_desc->image.id != SG_INVALID_ID) { - pass->num_color_atts++; + SOKOL_ASSERT(att_desc->image.id != SG_INVALID_ID); + SOKOL_ASSERT(0 == pass->mtl.color_atts[i].image); SOKOL_ASSERT(att_images[i] && (att_images[i]->slot.id == att_desc->image.id)); - SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(att_images[i]->pixel_format)); - att = &pass->color_atts[i]; - SOKOL_ASSERT((att->image == 0) && (att->image_id.id == SG_INVALID_ID)); - att->image = att_images[i]; - att->image_id = att_desc->image; - att->mip_level = att_desc->mip_level; - att->slice = att_desc->slice; + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(att_images[i]->cmn.pixel_format)); + pass->mtl.color_atts[i].image = att_images[i]; } } - SOKOL_ASSERT(0 == pass->ds_att.image); + SOKOL_ASSERT(0 == pass->mtl.ds_att.image); att_desc = &desc->depth_stencil_attachment; - const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; if (att_desc->image.id != SG_INVALID_ID) { + const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; SOKOL_ASSERT(att_images[ds_img_index] && (att_images[ds_img_index]->slot.id == att_desc->image.id)); - SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_images[ds_img_index]->pixel_format)); - att = &pass->ds_att; - SOKOL_ASSERT((att->image == 0) && (att->image_id.id == SG_INVALID_ID)); - att->image = att_images[ds_img_index]; - att->image_id = att_desc->image; - att->mip_level = att_desc->mip_level; - att->slice = att_desc->slice; + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_images[ds_img_index]->cmn.pixel_format)); + pass->mtl.ds_att.image = att_images[ds_img_index]; } return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_destroy_pass(_sg_pass_t* pass) { +_SOKOL_PRIVATE void _sg_mtl_destroy_pass(_sg_pass_t* pass) { SOKOL_ASSERT(pass); _SOKOL_UNUSED(pass); } -_SOKOL_PRIVATE void _sg_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { +_SOKOL_PRIVATE _sg_image_t* _sg_mtl_pass_color_image(const _sg_pass_t* pass, int index) { + SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + /* NOTE: may return null */ + return pass->mtl.color_atts[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_mtl_pass_ds_image(const _sg_pass_t* pass) { + /* NOTE: may return null */ + SOKOL_ASSERT(pass); + return pass->mtl.ds_att.image; +} + +_SOKOL_PRIVATE void _sg_mtl_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { SOKOL_ASSERT(action); SOKOL_ASSERT(!_sg.mtl.in_pass); - SOKOL_ASSERT(_sg_mtl_cmd_queue); - SOKOL_ASSERT(!_sg_mtl_cmd_encoder); + SOKOL_ASSERT(_sg.mtl.cmd_queue); + SOKOL_ASSERT(nil == _sg.mtl.cmd_encoder); SOKOL_ASSERT(_sg.mtl.renderpass_descriptor_cb); _sg.mtl.in_pass = true; _sg.mtl.cur_width = w; @@ -9109,15 +9522,15 @@ _SOKOL_PRIVATE void _sg_begin_pass(_sg_pass_t* pass, const sg_pass_action* actio _sg_mtl_clear_state_cache(); /* if this is the first pass in the frame, create a command buffer */ - if (nil == _sg_mtl_cmd_buffer) { + if (nil == _sg.mtl.cmd_buffer) { /* block until the oldest frame in flight has finished */ - dispatch_semaphore_wait(_sg_mtl_sem, DISPATCH_TIME_FOREVER); - _sg_mtl_cmd_buffer = [_sg_mtl_cmd_queue commandBufferWithUnretainedReferences]; + dispatch_semaphore_wait(_sg.mtl.sem, DISPATCH_TIME_FOREVER); + _sg.mtl.cmd_buffer = [_sg.mtl.cmd_queue commandBufferWithUnretainedReferences]; } /* if this is first pass in frame, get uniform buffer base pointer */ if (0 == _sg.mtl.cur_ub_base_ptr) { - _sg.mtl.cur_ub_base_ptr = (uint8_t*)[_sg_mtl_uniform_buffers[_sg.mtl.cur_frame_rotate_index] contents]; + _sg.mtl.cur_ub_base_ptr = (uint8_t*)[_sg.mtl.uniform_buffers[_sg.mtl.cur_frame_rotate_index] contents]; } /* initialize a render pass descriptor */ @@ -9143,61 +9556,60 @@ _SOKOL_PRIVATE void _sg_begin_pass(_sg_pass_t* pass, const sg_pass_action* actio if (pass) { /* setup pass descriptor for offscreen rendering */ SOKOL_ASSERT(pass->slot.state == SG_RESOURCESTATE_VALID); - for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - const _sg_attachment_t* att = &pass->color_atts[i]; - if (0 == att->image) { - break; - } - SOKOL_ASSERT(att->image->slot.state == SG_RESOURCESTATE_VALID); - SOKOL_ASSERT(att->image->slot.id == att->image_id.id); - const bool is_msaa = (att->image->sample_count > 1); + for (int i = 0; i < pass->cmn.num_color_atts; i++) { + const _sg_attachment_t* cmn_att = &pass->cmn.color_atts[i]; + const _sg_mtl_attachment_t* mtl_att = &pass->mtl.color_atts[i]; + const _sg_image_t* att_img = mtl_att->image; + SOKOL_ASSERT(att_img->slot.state == SG_RESOURCESTATE_VALID); + SOKOL_ASSERT(att_img->slot.id == cmn_att->image_id.id); + const bool is_msaa = (att_img->cmn.sample_count > 1); pass_desc.colorAttachments[i].loadAction = _sg_mtl_load_action(action->colors[i].action); pass_desc.colorAttachments[i].storeAction = is_msaa ? MTLStoreActionMultisampleResolve : MTLStoreActionStore; const float* c = &(action->colors[i].val[0]); pass_desc.colorAttachments[i].clearColor = MTLClearColorMake(c[0], c[1], c[2], c[3]); if (is_msaa) { - SOKOL_ASSERT(att->image->mtl_msaa_tex != _SG_MTL_INVALID_SLOT_INDEX); - SOKOL_ASSERT(att->image->mtl_tex[att->image->active_slot] != _SG_MTL_INVALID_SLOT_INDEX); - pass_desc.colorAttachments[i].texture = _sg_mtl_idpool[att->image->mtl_msaa_tex]; - pass_desc.colorAttachments[i].resolveTexture = _sg_mtl_idpool[att->image->mtl_tex[att->image->active_slot]]; - pass_desc.colorAttachments[i].resolveLevel = att->mip_level; - switch (att->image->type) { + SOKOL_ASSERT(att_img->mtl.msaa_tex != _SG_MTL_INVALID_SLOT_INDEX); + SOKOL_ASSERT(att_img->mtl.tex[mtl_att->image->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + pass_desc.colorAttachments[i].texture = _sg_mtl_id(att_img->mtl.msaa_tex); + pass_desc.colorAttachments[i].resolveTexture = _sg_mtl_id(att_img->mtl.tex[att_img->cmn.active_slot]); + pass_desc.colorAttachments[i].resolveLevel = cmn_att->mip_level; + switch (att_img->cmn.type) { case SG_IMAGETYPE_CUBE: case SG_IMAGETYPE_ARRAY: - pass_desc.colorAttachments[i].resolveSlice = att->slice; + pass_desc.colorAttachments[i].resolveSlice = cmn_att->slice; break; case SG_IMAGETYPE_3D: - pass_desc.colorAttachments[i].resolveDepthPlane = att->slice; + pass_desc.colorAttachments[i].resolveDepthPlane = cmn_att->slice; break; default: break; } } else { - SOKOL_ASSERT(att->image->mtl_tex[att->image->active_slot] != _SG_MTL_INVALID_SLOT_INDEX); - pass_desc.colorAttachments[i].texture = _sg_mtl_idpool[att->image->mtl_tex[att->image->active_slot]]; - pass_desc.colorAttachments[i].level = att->mip_level; - switch (att->image->type) { + SOKOL_ASSERT(att_img->mtl.tex[att_img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + pass_desc.colorAttachments[i].texture = _sg_mtl_id(att_img->mtl.tex[att_img->cmn.active_slot]); + pass_desc.colorAttachments[i].level = cmn_att->mip_level; + switch (att_img->cmn.type) { case SG_IMAGETYPE_CUBE: case SG_IMAGETYPE_ARRAY: - pass_desc.colorAttachments[i].slice = att->slice; + pass_desc.colorAttachments[i].slice = cmn_att->slice; break; case SG_IMAGETYPE_3D: - pass_desc.colorAttachments[i].depthPlane = att->slice; + pass_desc.colorAttachments[i].depthPlane = cmn_att->slice; break; default: break; } } } - if (0 != pass->ds_att.image) { - const _sg_attachment_t* att = &pass->ds_att; - SOKOL_ASSERT(att->image->slot.state == SG_RESOURCESTATE_VALID); - SOKOL_ASSERT(att->image->slot.id == att->image_id.id); - SOKOL_ASSERT(att->image->mtl_depth_tex != _SG_MTL_INVALID_SLOT_INDEX); - pass_desc.depthAttachment.texture = _sg_mtl_idpool[att->image->mtl_depth_tex]; + const _sg_image_t* ds_att_img = pass->mtl.ds_att.image; + if (0 != ds_att_img) { + SOKOL_ASSERT(ds_att_img->slot.state == SG_RESOURCESTATE_VALID); + SOKOL_ASSERT(ds_att_img->slot.id == pass->cmn.ds_att.image_id.id); + SOKOL_ASSERT(ds_att_img->mtl.depth_tex != _SG_MTL_INVALID_SLOT_INDEX); + pass_desc.depthAttachment.texture = _sg_mtl_id(ds_att_img->mtl.depth_tex); pass_desc.depthAttachment.loadAction = _sg_mtl_load_action(action->depth.action); pass_desc.depthAttachment.clearDepth = action->depth.val; - if (_sg_is_depth_stencil_format(att->image->pixel_format)) { - pass_desc.stencilAttachment.texture = _sg_mtl_idpool[att->image->mtl_depth_tex]; + if (_sg_is_depth_stencil_format(ds_att_img->cmn.pixel_format)) { + pass_desc.stencilAttachment.texture = _sg_mtl_id(ds_att_img->mtl.depth_tex); pass_desc.stencilAttachment.loadAction = _sg_mtl_load_action(action->stencil.action); pass_desc.stencilAttachment.clearStencil = action->stencil.val; } @@ -9215,53 +9627,46 @@ _SOKOL_PRIVATE void _sg_begin_pass(_sg_pass_t* pass, const sg_pass_action* actio } /* create a render command encoder, this might return nil if window is minimized */ - _sg_mtl_cmd_encoder = [_sg_mtl_cmd_buffer renderCommandEncoderWithDescriptor:pass_desc]; - if (_sg_mtl_cmd_encoder == nil) { + _sg.mtl.cmd_encoder = [_sg.mtl.cmd_buffer renderCommandEncoderWithDescriptor:pass_desc]; + if (nil == _sg.mtl.cmd_encoder) { _sg.mtl.pass_valid = false; return; } /* bind the global uniform buffer, this only happens once per pass */ - for (int slot = 0; slot < SG_MAX_SHADERSTAGE_UBS; slot++) { - [_sg_mtl_cmd_encoder - setVertexBuffer:_sg_mtl_uniform_buffers[_sg.mtl.cur_frame_rotate_index] - offset:0 - atIndex:slot]; - [_sg_mtl_cmd_encoder - setFragmentBuffer:_sg_mtl_uniform_buffers[_sg.mtl.cur_frame_rotate_index] - offset:0 - atIndex:slot]; - } + _sg_mtl_bind_uniform_buffers(); } -_SOKOL_PRIVATE void _sg_end_pass(void) { +_SOKOL_PRIVATE void _sg_mtl_end_pass(void) { SOKOL_ASSERT(_sg.mtl.in_pass); _sg.mtl.in_pass = false; _sg.mtl.pass_valid = false; - if (nil != _sg_mtl_cmd_encoder) { - [_sg_mtl_cmd_encoder endEncoding]; - _sg_mtl_cmd_encoder = nil; + if (nil != _sg.mtl.cmd_encoder) { + [_sg.mtl.cmd_encoder endEncoding]; + /* NOTE: MTLRenderCommandEncoder is autoreleased */ + _sg.mtl.cmd_encoder = nil; } } -_SOKOL_PRIVATE void _sg_commit(void) { +_SOKOL_PRIVATE void _sg_mtl_commit(void) { SOKOL_ASSERT(!_sg.mtl.in_pass); SOKOL_ASSERT(!_sg.mtl.pass_valid); SOKOL_ASSERT(_sg.mtl.drawable_cb); - SOKOL_ASSERT(nil == _sg_mtl_cmd_encoder); - SOKOL_ASSERT(nil != _sg_mtl_cmd_buffer); + SOKOL_ASSERT(nil == _sg.mtl.cmd_encoder); + SOKOL_ASSERT(nil != _sg.mtl.cmd_buffer); #if defined(_SG_TARGET_MACOS) - [_sg_mtl_uniform_buffers[_sg.mtl.cur_frame_rotate_index] didModifyRange:NSMakeRange(0, _sg.mtl.cur_ub_offset)]; + [_sg.mtl.uniform_buffers[_sg.mtl.cur_frame_rotate_index] didModifyRange:NSMakeRange(0, _sg.mtl.cur_ub_offset)]; #endif /* present, commit and signal semaphore when done */ id cur_drawable = (__bridge id) _sg.mtl.drawable_cb(); - [_sg_mtl_cmd_buffer presentDrawable:cur_drawable]; - [_sg_mtl_cmd_buffer addCompletedHandler:^(id cmd_buffer) { - dispatch_semaphore_signal(_sg_mtl_sem); + [_sg.mtl.cmd_buffer presentDrawable:cur_drawable]; + [_sg.mtl.cmd_buffer addCompletedHandler:^(id cmd_buffer) { + _SOKOL_UNUSED(cmd_buffer); + dispatch_semaphore_signal(_sg.mtl.sem); }]; - [_sg_mtl_cmd_buffer commit]; + [_sg.mtl.cmd_buffer commit]; /* garbage-collect resources pending for release */ _sg_mtl_garbage_collect(_sg.mtl.frame_index); @@ -9273,15 +9678,16 @@ _SOKOL_PRIVATE void _sg_commit(void) { _sg.mtl.frame_index++; _sg.mtl.cur_ub_offset = 0; _sg.mtl.cur_ub_base_ptr = 0; - _sg_mtl_cmd_buffer = nil; + /* NOTE: MTLCommandBuffer is autoreleased */ + _sg.mtl.cmd_buffer = nil; } -_SOKOL_PRIVATE void _sg_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { +_SOKOL_PRIVATE void _sg_mtl_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { SOKOL_ASSERT(_sg.mtl.in_pass); if (!_sg.mtl.pass_valid) { return; } - SOKOL_ASSERT(_sg_mtl_cmd_encoder); + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); MTLViewport vp; vp.originX = (double) x; vp.originY = (double) (origin_top_left ? y : (_sg.mtl.cur_height - (y + h))); @@ -9289,15 +9695,15 @@ _SOKOL_PRIVATE void _sg_apply_viewport(int x, int y, int w, int h, bool origin_t vp.height = (double) h; vp.znear = 0.0; vp.zfar = 1.0; - [_sg_mtl_cmd_encoder setViewport:vp]; + [_sg.mtl.cmd_encoder setViewport:vp]; } -_SOKOL_PRIVATE void _sg_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { +_SOKOL_PRIVATE void _sg_mtl_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { SOKOL_ASSERT(_sg.mtl.in_pass); if (!_sg.mtl.pass_valid) { return; } - SOKOL_ASSERT(_sg_mtl_cmd_encoder); + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); /* clip against framebuffer rect */ x = _sg_min(_sg_max(0, x), _sg.mtl.cur_width-1); y = _sg_min(_sg_max(0, y), _sg.mtl.cur_height-1); @@ -9315,56 +9721,57 @@ _SOKOL_PRIVATE void _sg_apply_scissor_rect(int x, int y, int w, int h, bool orig r.y = origin_top_left ? y : (_sg.mtl.cur_height - (y + h)); r.width = w; r.height = h; - [_sg_mtl_cmd_encoder setScissorRect:r]; + [_sg.mtl.cmd_encoder setScissorRect:r]; } -_SOKOL_PRIVATE void _sg_apply_pipeline(_sg_pipeline_t* pip) { +_SOKOL_PRIVATE void _sg_mtl_apply_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); - SOKOL_ASSERT(pip->shader); + SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); SOKOL_ASSERT(_sg.mtl.in_pass); if (!_sg.mtl.pass_valid) { return; } - SOKOL_ASSERT(_sg_mtl_cmd_encoder); + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); if ((_sg.mtl.state_cache.cur_pipeline != pip) || (_sg.mtl.state_cache.cur_pipeline_id.id != pip->slot.id)) { _sg.mtl.state_cache.cur_pipeline = pip; _sg.mtl.state_cache.cur_pipeline_id.id = pip->slot.id; - const float* c = pip->blend_color; - [_sg_mtl_cmd_encoder setBlendColorRed:c[0] green:c[1] blue:c[2] alpha:c[3]]; - [_sg_mtl_cmd_encoder setCullMode:pip->mtl_cull_mode]; - [_sg_mtl_cmd_encoder setFrontFacingWinding:pip->mtl_winding]; - [_sg_mtl_cmd_encoder setStencilReferenceValue:pip->mtl_stencil_ref]; - [_sg_mtl_cmd_encoder setDepthBias:pip->depth_bias slopeScale:pip->depth_bias_slope_scale clamp:pip->depth_bias_clamp]; - SOKOL_ASSERT(pip->mtl_rps != _SG_MTL_INVALID_SLOT_INDEX); - [_sg_mtl_cmd_encoder setRenderPipelineState:_sg_mtl_idpool[pip->mtl_rps]]; - SOKOL_ASSERT(pip->mtl_dss != _SG_MTL_INVALID_SLOT_INDEX); - [_sg_mtl_cmd_encoder setDepthStencilState:_sg_mtl_idpool[pip->mtl_dss]]; + const float* c = pip->cmn.blend_color; + [_sg.mtl.cmd_encoder setBlendColorRed:c[0] green:c[1] blue:c[2] alpha:c[3]]; + [_sg.mtl.cmd_encoder setCullMode:pip->mtl.cull_mode]; + [_sg.mtl.cmd_encoder setFrontFacingWinding:pip->mtl.winding]; + [_sg.mtl.cmd_encoder setStencilReferenceValue:pip->mtl.stencil_ref]; + [_sg.mtl.cmd_encoder setDepthBias:pip->cmn.depth_bias slopeScale:pip->cmn.depth_bias_slope_scale clamp:pip->cmn.depth_bias_clamp]; + SOKOL_ASSERT(pip->mtl.rps != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setRenderPipelineState:_sg_mtl_id(pip->mtl.rps)]; + SOKOL_ASSERT(pip->mtl.dss != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setDepthStencilState:_sg_mtl_id(pip->mtl.dss)]; } } -_SOKOL_PRIVATE void _sg_apply_bindings( +_SOKOL_PRIVATE void _sg_mtl_apply_bindings( _sg_pipeline_t* pip, _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, _sg_buffer_t* ib, int ib_offset, _sg_image_t** vs_imgs, int num_vs_imgs, _sg_image_t** fs_imgs, int num_fs_imgs) { + _SOKOL_UNUSED(pip); SOKOL_ASSERT(_sg.mtl.in_pass); if (!_sg.mtl.pass_valid) { return; } - SOKOL_ASSERT(_sg_mtl_cmd_encoder); + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); /* store index buffer binding, this will be needed later in sg_draw() */ _sg.mtl.state_cache.cur_indexbuffer = ib; _sg.mtl.state_cache.cur_indexbuffer_offset = ib_offset; if (ib) { - SOKOL_ASSERT(pip->index_type != SG_INDEXTYPE_NONE); + SOKOL_ASSERT(pip->cmn.index_type != SG_INDEXTYPE_NONE); _sg.mtl.state_cache.cur_indexbuffer_id.id = ib->slot.id; } else { - SOKOL_ASSERT(pip->index_type == SG_INDEXTYPE_NONE); + SOKOL_ASSERT(pip->cmn.index_type == SG_INDEXTYPE_NONE); _sg.mtl.state_cache.cur_indexbuffer_id.id = SG_INVALID_ID; } @@ -9380,8 +9787,8 @@ _SOKOL_PRIVATE void _sg_apply_bindings( _sg.mtl.state_cache.cur_vertexbuffer_offsets[slot] = vb_offsets[slot]; _sg.mtl.state_cache.cur_vertexbuffer_ids[slot].id = vb->slot.id; const NSUInteger mtl_slot = SG_MAX_SHADERSTAGE_UBS + slot; - SOKOL_ASSERT(vb->mtl_buf[vb->active_slot] != _SG_MTL_INVALID_SLOT_INDEX); - [_sg_mtl_cmd_encoder setVertexBuffer:_sg_mtl_idpool[vb->mtl_buf[vb->active_slot]] + SOKOL_ASSERT(vb->mtl.buf[vb->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setVertexBuffer:_sg_mtl_id(vb->mtl.buf[vb->cmn.active_slot]) offset:vb_offsets[slot] atIndex:mtl_slot]; } @@ -9393,10 +9800,10 @@ _SOKOL_PRIVATE void _sg_apply_bindings( if ((_sg.mtl.state_cache.cur_vs_images[slot] != img) || (_sg.mtl.state_cache.cur_vs_image_ids[slot].id != img->slot.id)) { _sg.mtl.state_cache.cur_vs_images[slot] = img; _sg.mtl.state_cache.cur_vs_image_ids[slot].id = img->slot.id; - SOKOL_ASSERT(img->mtl_tex[img->active_slot] != _SG_MTL_INVALID_SLOT_INDEX); - [_sg_mtl_cmd_encoder setVertexTexture:_sg_mtl_idpool[img->mtl_tex[img->active_slot]] atIndex:slot]; - SOKOL_ASSERT(img->mtl_sampler_state != _SG_MTL_INVALID_SLOT_INDEX); - [_sg_mtl_cmd_encoder setVertexSamplerState:_sg_mtl_idpool[img->mtl_sampler_state] atIndex:slot]; + SOKOL_ASSERT(img->mtl.tex[img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setVertexTexture:_sg_mtl_id(img->mtl.tex[img->cmn.active_slot]) atIndex:slot]; + SOKOL_ASSERT(img->mtl.sampler_state != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setVertexSamplerState:_sg_mtl_id(img->mtl.sampler_state) atIndex:slot]; } } @@ -9406,22 +9813,20 @@ _SOKOL_PRIVATE void _sg_apply_bindings( if ((_sg.mtl.state_cache.cur_fs_images[slot] != img) || (_sg.mtl.state_cache.cur_fs_image_ids[slot].id != img->slot.id)) { _sg.mtl.state_cache.cur_fs_images[slot] = img; _sg.mtl.state_cache.cur_fs_image_ids[slot].id = img->slot.id; - SOKOL_ASSERT(img->mtl_tex[img->active_slot] != _SG_MTL_INVALID_SLOT_INDEX); - [_sg_mtl_cmd_encoder setFragmentTexture:_sg_mtl_idpool[img->mtl_tex[img->active_slot]] atIndex:slot]; - SOKOL_ASSERT(img->mtl_sampler_state != _SG_MTL_INVALID_SLOT_INDEX); - [_sg_mtl_cmd_encoder setFragmentSamplerState:_sg_mtl_idpool[img->mtl_sampler_state] atIndex:slot]; + SOKOL_ASSERT(img->mtl.tex[img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setFragmentTexture:_sg_mtl_id(img->mtl.tex[img->cmn.active_slot]) atIndex:slot]; + SOKOL_ASSERT(img->mtl.sampler_state != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setFragmentSamplerState:_sg_mtl_id(img->mtl.sampler_state) atIndex:slot]; } } } -#define _sg_mtl_roundup(val, round_to) (((val)+((round_to)-1))&~((round_to)-1)) - -_SOKOL_PRIVATE void _sg_apply_uniforms(sg_shader_stage stage_index, int ub_index, const void* data, int num_bytes) { +_SOKOL_PRIVATE void _sg_mtl_apply_uniforms(sg_shader_stage stage_index, int ub_index, const void* data, int num_bytes) { SOKOL_ASSERT(_sg.mtl.in_pass); if (!_sg.mtl.pass_valid) { return; } - SOKOL_ASSERT(_sg_mtl_cmd_encoder); + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); SOKOL_ASSERT(data && (num_bytes > 0)); SOKOL_ASSERT((stage_index >= 0) && ((int)stage_index < SG_NUM_SHADER_STAGES)); SOKOL_ASSERT((ub_index >= 0) && (ub_index < SG_MAX_SHADERSTAGE_UBS)); @@ -9429,58 +9834,58 @@ _SOKOL_PRIVATE void _sg_apply_uniforms(sg_shader_stage stage_index, int ub_index SOKOL_ASSERT((_sg.mtl.cur_ub_offset & (_SG_MTL_UB_ALIGN-1)) == 0); SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline && _sg.mtl.state_cache.cur_pipeline->shader); SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline->slot.id == _sg.mtl.state_cache.cur_pipeline_id.id); - SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline->shader->slot.id == _sg.mtl.state_cache.cur_pipeline->shader_id.id); - SOKOL_ASSERT(ub_index < _sg.mtl.state_cache.cur_pipeline->shader->stage[stage_index].num_uniform_blocks); - SOKOL_ASSERT(num_bytes <= _sg.mtl.state_cache.cur_pipeline->shader->stage[stage_index].uniform_blocks[ub_index].size); + SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline->shader->slot.id == _sg.mtl.state_cache.cur_pipeline->cmn.shader_id.id); + SOKOL_ASSERT(ub_index < _sg.mtl.state_cache.cur_pipeline->shader->cmn.stage[stage_index].num_uniform_blocks); + SOKOL_ASSERT(num_bytes <= _sg.mtl.state_cache.cur_pipeline->shader->cmn.stage[stage_index].uniform_blocks[ub_index].size); /* copy to global uniform buffer, record offset into cmd encoder, and advance offset */ uint8_t* dst = &_sg.mtl.cur_ub_base_ptr[_sg.mtl.cur_ub_offset]; memcpy(dst, data, num_bytes); if (stage_index == SG_SHADERSTAGE_VS) { - [_sg_mtl_cmd_encoder setVertexBufferOffset:_sg.mtl.cur_ub_offset atIndex:ub_index]; + [_sg.mtl.cmd_encoder setVertexBufferOffset:_sg.mtl.cur_ub_offset atIndex:ub_index]; } else { - [_sg_mtl_cmd_encoder setFragmentBufferOffset:_sg.mtl.cur_ub_offset atIndex:ub_index]; + [_sg.mtl.cmd_encoder setFragmentBufferOffset:_sg.mtl.cur_ub_offset atIndex:ub_index]; } - _sg.mtl.cur_ub_offset = _sg_mtl_roundup(_sg.mtl.cur_ub_offset + num_bytes, _SG_MTL_UB_ALIGN); + _sg.mtl.cur_ub_offset = _sg_roundup(_sg.mtl.cur_ub_offset + num_bytes, _SG_MTL_UB_ALIGN); } -_SOKOL_PRIVATE void _sg_draw(int base_element, int num_elements, int num_instances) { +_SOKOL_PRIVATE void _sg_mtl_draw(int base_element, int num_elements, int num_instances) { SOKOL_ASSERT(_sg.mtl.in_pass); if (!_sg.mtl.pass_valid) { return; } - SOKOL_ASSERT(_sg_mtl_cmd_encoder); + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline && (_sg.mtl.state_cache.cur_pipeline->slot.id == _sg.mtl.state_cache.cur_pipeline_id.id)); - if (SG_INDEXTYPE_NONE != _sg.mtl.state_cache.cur_pipeline->index_type) { + if (SG_INDEXTYPE_NONE != _sg.mtl.state_cache.cur_pipeline->cmn.index_type) { /* indexed rendering */ SOKOL_ASSERT(_sg.mtl.state_cache.cur_indexbuffer && (_sg.mtl.state_cache.cur_indexbuffer->slot.id == _sg.mtl.state_cache.cur_indexbuffer_id.id)); const _sg_buffer_t* ib = _sg.mtl.state_cache.cur_indexbuffer; - SOKOL_ASSERT(ib->mtl_buf[ib->active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + SOKOL_ASSERT(ib->mtl.buf[ib->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); const NSUInteger index_buffer_offset = _sg.mtl.state_cache.cur_indexbuffer_offset + - base_element * _sg.mtl.state_cache.cur_pipeline->mtl_index_size; - [_sg_mtl_cmd_encoder drawIndexedPrimitives:_sg.mtl.state_cache.cur_pipeline->mtl_prim_type + base_element * _sg.mtl.state_cache.cur_pipeline->mtl.index_size; + [_sg.mtl.cmd_encoder drawIndexedPrimitives:_sg.mtl.state_cache.cur_pipeline->mtl.prim_type indexCount:num_elements - indexType:_sg.mtl.state_cache.cur_pipeline->mtl_index_type - indexBuffer:_sg_mtl_idpool[ib->mtl_buf[ib->active_slot]] + indexType:_sg.mtl.state_cache.cur_pipeline->mtl.index_type + indexBuffer:_sg_mtl_id(ib->mtl.buf[ib->cmn.active_slot]) indexBufferOffset:index_buffer_offset instanceCount:num_instances]; } else { /* non-indexed rendering */ - [_sg_mtl_cmd_encoder drawPrimitives:_sg.mtl.state_cache.cur_pipeline->mtl_prim_type + [_sg.mtl.cmd_encoder drawPrimitives:_sg.mtl.state_cache.cur_pipeline->mtl.prim_type vertexStart:base_element vertexCount:num_elements instanceCount:num_instances]; } } -_SOKOL_PRIVATE void _sg_update_buffer(_sg_buffer_t* buf, const void* data, int data_size) { +_SOKOL_PRIVATE void _sg_mtl_update_buffer(_sg_buffer_t* buf, const void* data, uint32_t data_size) { SOKOL_ASSERT(buf && data && (data_size > 0)); - if (++buf->active_slot >= buf->num_slots) { - buf->active_slot = 0; + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; } - __unsafe_unretained id mtl_buf = _sg_mtl_idpool[buf->mtl_buf[buf->active_slot]]; + __unsafe_unretained id mtl_buf = _sg_mtl_id(buf->mtl.buf[buf->cmn.active_slot]); void* dst_ptr = [mtl_buf contents]; memcpy(dst_ptr, data, data_size); #if defined(_SG_TARGET_MACOS) @@ -9488,33 +9893,2236 @@ _SOKOL_PRIVATE void _sg_update_buffer(_sg_buffer_t* buf, const void* data, int d #endif } -_SOKOL_PRIVATE void _sg_append_buffer(_sg_buffer_t* buf, const void* data, int data_size, bool new_frame) { +_SOKOL_PRIVATE uint32_t _sg_mtl_append_buffer(_sg_buffer_t* buf, const void* data, uint32_t data_size, bool new_frame) { SOKOL_ASSERT(buf && data && (data_size > 0)); if (new_frame) { - if (++buf->active_slot >= buf->num_slots) { - buf->active_slot = 0; + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; } } - __unsafe_unretained id mtl_buf = _sg_mtl_idpool[buf->mtl_buf[buf->active_slot]]; + __unsafe_unretained id mtl_buf = _sg_mtl_id(buf->mtl.buf[buf->cmn.active_slot]); uint8_t* dst_ptr = (uint8_t*) [mtl_buf contents]; - dst_ptr += buf->append_pos; + dst_ptr += buf->cmn.append_pos; memcpy(dst_ptr, data, data_size); #if defined(_SG_TARGET_MACOS) - [mtl_buf didModifyRange:NSMakeRange(buf->append_pos, data_size)]; + [mtl_buf didModifyRange:NSMakeRange(buf->cmn.append_pos, data_size)]; #endif + /* NOTE: this is a requirement from WebGPU, but we want identical behaviour across all backend */ + return _sg_roundup(data_size, 4); } -_SOKOL_PRIVATE void _sg_update_image(_sg_image_t* img, const sg_image_content* data) { +_SOKOL_PRIVATE void _sg_mtl_update_image(_sg_image_t* img, const sg_image_content* data) { SOKOL_ASSERT(img && data); - if (++img->active_slot >= img->num_slots) { - img->active_slot = 0; + if (++img->cmn.active_slot >= img->cmn.num_slots) { + img->cmn.active_slot = 0; } - __unsafe_unretained id mtl_tex = _sg_mtl_idpool[img->mtl_tex[img->active_slot]]; + __unsafe_unretained id mtl_tex = _sg_mtl_id(img->mtl.tex[img->cmn.active_slot]); _sg_mtl_copy_image_content(img, mtl_tex, data); } +/*== WEBGPU BACKEND IMPLEMENTATION ===========================================*/ +#elif defined(SOKOL_WGPU) + +_SOKOL_PRIVATE WGPUBufferUsageFlags _sg_wgpu_buffer_usage(sg_buffer_type t, sg_usage u) { + WGPUBufferUsageFlags res = 0; + if (SG_BUFFERTYPE_VERTEXBUFFER == t) { + res |= WGPUBufferUsage_Vertex; + } + else { + res |= WGPUBufferUsage_Index; + } + if (SG_USAGE_IMMUTABLE != u) { + res |= WGPUBufferUsage_CopyDst; + } + return res; +} + +_SOKOL_PRIVATE WGPULoadOp _sg_wgpu_load_op(sg_action a) { + switch (a) { + case SG_ACTION_CLEAR: + case SG_ACTION_DONTCARE: + return WGPULoadOp_Clear; + case SG_ACTION_LOAD: + return WGPULoadOp_Load; + default: + SOKOL_UNREACHABLE; + return (WGPULoadOp)0; + } +} + +_SOKOL_PRIVATE WGPUTextureViewDimension _sg_wgpu_tex_viewdim(sg_image_type t) { + switch (t) { + case SG_IMAGETYPE_2D: return WGPUTextureViewDimension_2D; + case SG_IMAGETYPE_CUBE: return WGPUTextureViewDimension_Cube; + case SG_IMAGETYPE_3D: return WGPUTextureViewDimension_3D; + case SG_IMAGETYPE_ARRAY: return WGPUTextureViewDimension_2DArray; + default: SOKOL_UNREACHABLE; return WGPUTextureViewDimension_Force32; + } +} + +_SOKOL_PRIVATE WGPUTextureComponentType _sg_wgpu_tex_comptype(sg_sampler_type t) { + switch (t) { + case SG_SAMPLERTYPE_FLOAT: return WGPUTextureComponentType_Float; + case SG_SAMPLERTYPE_SINT: return WGPUTextureComponentType_Sint; + case SG_SAMPLERTYPE_UINT: return WGPUTextureComponentType_Uint; + default: SOKOL_UNREACHABLE; return WGPUTextureComponentType_Force32; + } +} + +_SOKOL_PRIVATE WGPUTextureDimension _sg_wgpu_tex_dim(sg_image_type t) { + if (SG_IMAGETYPE_3D == t) { + return WGPUTextureDimension_3D; + } + else { + return WGPUTextureDimension_2D; + } +} + +_SOKOL_PRIVATE WGPUAddressMode _sg_wgpu_sampler_addrmode(sg_wrap m) { + switch (m) { + case SG_WRAP_REPEAT: + return WGPUAddressMode_Repeat; + case SG_WRAP_CLAMP_TO_EDGE: + case SG_WRAP_CLAMP_TO_BORDER: + return WGPUAddressMode_ClampToEdge; + case SG_WRAP_MIRRORED_REPEAT: + return WGPUAddressMode_MirrorRepeat; + default: + SOKOL_UNREACHABLE; + return WGPUAddressMode_Force32; + } +} + +_SOKOL_PRIVATE WGPUFilterMode _sg_wgpu_sampler_minmagfilter(sg_filter f) { + switch (f) { + case SG_FILTER_NEAREST: + case SG_FILTER_NEAREST_MIPMAP_NEAREST: + case SG_FILTER_NEAREST_MIPMAP_LINEAR: + return WGPUFilterMode_Nearest; + case SG_FILTER_LINEAR: + case SG_FILTER_LINEAR_MIPMAP_NEAREST: + case SG_FILTER_LINEAR_MIPMAP_LINEAR: + return WGPUFilterMode_Linear; + default: + SOKOL_UNREACHABLE; + return WGPUFilterMode_Force32; + } +} + +_SOKOL_PRIVATE WGPUFilterMode _sg_wgpu_sampler_mipfilter(sg_filter f) { + switch (f) { + case SG_FILTER_NEAREST: + case SG_FILTER_LINEAR: + case SG_FILTER_NEAREST_MIPMAP_NEAREST: + case SG_FILTER_LINEAR_MIPMAP_NEAREST: + return WGPUFilterMode_Nearest; + case SG_FILTER_NEAREST_MIPMAP_LINEAR: + case SG_FILTER_LINEAR_MIPMAP_LINEAR: + return WGPUFilterMode_Linear; + default: + SOKOL_UNREACHABLE; + return WGPUFilterMode_Force32; + } +} + +_SOKOL_PRIVATE WGPUIndexFormat _sg_wgpu_indexformat(sg_index_type t) { + /* NOTE: there's no WGPUIndexFormat_None */ + return (t == SG_INDEXTYPE_UINT16) ? WGPUIndexFormat_Uint16 : WGPUIndexFormat_Uint32; +} + +_SOKOL_PRIVATE WGPUInputStepMode _sg_wgpu_stepmode(sg_vertex_step s) { + return (s == SG_VERTEXSTEP_PER_VERTEX) ? WGPUInputStepMode_Vertex : WGPUInputStepMode_Instance; +} + +_SOKOL_PRIVATE WGPUVertexFormat _sg_wgpu_vertexformat(sg_vertex_format f) { + switch (f) { + case SG_VERTEXFORMAT_FLOAT: return WGPUVertexFormat_Float; + case SG_VERTEXFORMAT_FLOAT2: return WGPUVertexFormat_Float2; + case SG_VERTEXFORMAT_FLOAT3: return WGPUVertexFormat_Float3; + case SG_VERTEXFORMAT_FLOAT4: return WGPUVertexFormat_Float4; + case SG_VERTEXFORMAT_BYTE4: return WGPUVertexFormat_Char4; + case SG_VERTEXFORMAT_BYTE4N: return WGPUVertexFormat_Char4Norm; + case SG_VERTEXFORMAT_UBYTE4: return WGPUVertexFormat_UChar4; + case SG_VERTEXFORMAT_UBYTE4N: return WGPUVertexFormat_UChar4Norm; + case SG_VERTEXFORMAT_SHORT2: return WGPUVertexFormat_Short2; + case SG_VERTEXFORMAT_SHORT2N: return WGPUVertexFormat_Short2Norm; + case SG_VERTEXFORMAT_USHORT2N: return WGPUVertexFormat_UShort2Norm; + case SG_VERTEXFORMAT_SHORT4: return WGPUVertexFormat_Short4; + case SG_VERTEXFORMAT_SHORT4N: return WGPUVertexFormat_Short4Norm; + case SG_VERTEXFORMAT_USHORT4N: return WGPUVertexFormat_UShort4Norm; + /* FIXME! UINT10_N2 */ + case SG_VERTEXFORMAT_UINT10_N2: + default: + SOKOL_UNREACHABLE; + return WGPUVertexFormat_Force32; + } +} + +_SOKOL_PRIVATE WGPUPrimitiveTopology _sg_wgpu_topology(sg_primitive_type t) { + switch (t) { + case SG_PRIMITIVETYPE_POINTS: return WGPUPrimitiveTopology_PointList; + case SG_PRIMITIVETYPE_LINES: return WGPUPrimitiveTopology_LineList; + case SG_PRIMITIVETYPE_LINE_STRIP: return WGPUPrimitiveTopology_LineStrip; + case SG_PRIMITIVETYPE_TRIANGLES: return WGPUPrimitiveTopology_TriangleList; + case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return WGPUPrimitiveTopology_TriangleStrip; + default: SOKOL_UNREACHABLE; return WGPUPrimitiveTopology_Force32; + } +} + +_SOKOL_PRIVATE WGPUFrontFace _sg_wgpu_frontface(sg_face_winding fw) { + return (fw == SG_FACEWINDING_CCW) ? WGPUFrontFace_CCW : WGPUFrontFace_CW; +} + +_SOKOL_PRIVATE WGPUCullMode _sg_wgpu_cullmode(sg_cull_mode cm) { + switch (cm) { + case SG_CULLMODE_NONE: return WGPUCullMode_None; + case SG_CULLMODE_FRONT: return WGPUCullMode_Front; + case SG_CULLMODE_BACK: return WGPUCullMode_Back; + default: SOKOL_UNREACHABLE; return WGPUCullMode_Force32; + } +} + +_SOKOL_PRIVATE WGPUTextureFormat _sg_wgpu_textureformat(sg_pixel_format p) { + switch (p) { + case SG_PIXELFORMAT_NONE: return WGPUTextureFormat_Undefined; + case SG_PIXELFORMAT_R8: return WGPUTextureFormat_R8Unorm; + case SG_PIXELFORMAT_R8SN: return WGPUTextureFormat_R8Snorm; + case SG_PIXELFORMAT_R8UI: return WGPUTextureFormat_R8Uint; + case SG_PIXELFORMAT_R8SI: return WGPUTextureFormat_R8Sint; + case SG_PIXELFORMAT_R16UI: return WGPUTextureFormat_R16Uint; + case SG_PIXELFORMAT_R16SI: return WGPUTextureFormat_R16Sint; + case SG_PIXELFORMAT_R16F: return WGPUTextureFormat_R16Float; + case SG_PIXELFORMAT_RG8: return WGPUTextureFormat_RG8Unorm; + case SG_PIXELFORMAT_RG8SN: return WGPUTextureFormat_RG8Snorm; + case SG_PIXELFORMAT_RG8UI: return WGPUTextureFormat_RG8Uint; + case SG_PIXELFORMAT_RG8SI: return WGPUTextureFormat_RG8Sint; + case SG_PIXELFORMAT_R32UI: return WGPUTextureFormat_R32Uint; + case SG_PIXELFORMAT_R32SI: return WGPUTextureFormat_R32Sint; + case SG_PIXELFORMAT_R32F: return WGPUTextureFormat_R32Float; + case SG_PIXELFORMAT_RG16UI: return WGPUTextureFormat_RG16Uint; + case SG_PIXELFORMAT_RG16SI: return WGPUTextureFormat_RG16Sint; + case SG_PIXELFORMAT_RG16F: return WGPUTextureFormat_RG16Float; + case SG_PIXELFORMAT_RGBA8: return WGPUTextureFormat_RGBA8Unorm; + case SG_PIXELFORMAT_RGBA8SN: return WGPUTextureFormat_RGBA8Snorm; + case SG_PIXELFORMAT_RGBA8UI: return WGPUTextureFormat_RGBA8Uint; + case SG_PIXELFORMAT_RGBA8SI: return WGPUTextureFormat_RGBA8Sint; + case SG_PIXELFORMAT_BGRA8: return WGPUTextureFormat_BGRA8Unorm; + case SG_PIXELFORMAT_RGB10A2: return WGPUTextureFormat_RGB10A2Unorm; + case SG_PIXELFORMAT_RG11B10F: return WGPUTextureFormat_RG11B10Float; + case SG_PIXELFORMAT_RG32UI: return WGPUTextureFormat_RG32Uint; + case SG_PIXELFORMAT_RG32SI: return WGPUTextureFormat_RG32Sint; + case SG_PIXELFORMAT_RG32F: return WGPUTextureFormat_RG32Float; + case SG_PIXELFORMAT_RGBA16UI: return WGPUTextureFormat_RGBA16Uint; + case SG_PIXELFORMAT_RGBA16SI: return WGPUTextureFormat_RGBA16Sint; + case SG_PIXELFORMAT_RGBA16F: return WGPUTextureFormat_RGBA16Float; + case SG_PIXELFORMAT_RGBA32UI: return WGPUTextureFormat_RGBA32Uint; + case SG_PIXELFORMAT_RGBA32SI: return WGPUTextureFormat_RGBA32Sint; + case SG_PIXELFORMAT_RGBA32F: return WGPUTextureFormat_RGBA32Float; + case SG_PIXELFORMAT_DEPTH: return WGPUTextureFormat_Depth24Plus; + case SG_PIXELFORMAT_DEPTH_STENCIL: return WGPUTextureFormat_Depth24PlusStencil8; + case SG_PIXELFORMAT_BC1_RGBA: return WGPUTextureFormat_BC1RGBAUnorm; + case SG_PIXELFORMAT_BC2_RGBA: return WGPUTextureFormat_BC2RGBAUnorm; + case SG_PIXELFORMAT_BC3_RGBA: return WGPUTextureFormat_BC3RGBAUnorm; + case SG_PIXELFORMAT_BC4_R: return WGPUTextureFormat_BC4RUnorm; + case SG_PIXELFORMAT_BC4_RSN: return WGPUTextureFormat_BC4RSnorm; + case SG_PIXELFORMAT_BC5_RG: return WGPUTextureFormat_BC5RGUnorm; + case SG_PIXELFORMAT_BC5_RGSN: return WGPUTextureFormat_BC5RGSnorm; + case SG_PIXELFORMAT_BC6H_RGBF: return WGPUTextureFormat_BC6HRGBSfloat; + case SG_PIXELFORMAT_BC6H_RGBUF: return WGPUTextureFormat_BC6HRGBUfloat; + case SG_PIXELFORMAT_BC7_RGBA: return WGPUTextureFormat_BC7RGBAUnorm; + + /* NOT SUPPORTED */ + case SG_PIXELFORMAT_R16: + case SG_PIXELFORMAT_R16SN: + case SG_PIXELFORMAT_RG16: + case SG_PIXELFORMAT_RG16SN: + case SG_PIXELFORMAT_RGBA16: + case SG_PIXELFORMAT_RGBA16SN: + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: + case SG_PIXELFORMAT_ETC2_RGB8: + case SG_PIXELFORMAT_ETC2_RGB8A1: + case SG_PIXELFORMAT_ETC2_RGBA8: + case SG_PIXELFORMAT_ETC2_RG11: + case SG_PIXELFORMAT_ETC2_RG11SN: + default: + SOKOL_UNREACHABLE; + return WGPUTextureFormat_Force32; + } +} + +/* +FIXME ??? this isn't needed anywhere? +_SOKOL_PRIVATE WGPUTextureAspect _sg_wgpu_texture_aspect(sg_pixel_format fmt) { + if (_sg_is_valid_rendertarget_depth_format(fmt)) { + if (!_sg_is_depth_stencil_format(fmt)) { + return WGPUTextureAspect_DepthOnly; + } + } + return WGPUTextureAspect_All; +} +*/ + +_SOKOL_PRIVATE WGPUCompareFunction _sg_wgpu_comparefunc(sg_compare_func f) { + switch (f) { + case SG_COMPAREFUNC_NEVER: return WGPUCompareFunction_Never; + case SG_COMPAREFUNC_LESS: return WGPUCompareFunction_Less; + case SG_COMPAREFUNC_EQUAL: return WGPUCompareFunction_Equal; + case SG_COMPAREFUNC_LESS_EQUAL: return WGPUCompareFunction_LessEqual; + case SG_COMPAREFUNC_GREATER: return WGPUCompareFunction_Greater; + case SG_COMPAREFUNC_NOT_EQUAL: return WGPUCompareFunction_NotEqual; + case SG_COMPAREFUNC_GREATER_EQUAL: return WGPUCompareFunction_GreaterEqual; + case SG_COMPAREFUNC_ALWAYS: return WGPUCompareFunction_Always; + default: SOKOL_UNREACHABLE; return WGPUCompareFunction_Force32; + } +} + +_SOKOL_PRIVATE WGPUStencilOperation _sg_wgpu_stencilop(sg_stencil_op op) { + switch (op) { + case SG_STENCILOP_KEEP: return WGPUStencilOperation_Keep; + case SG_STENCILOP_ZERO: return WGPUStencilOperation_Zero; + case SG_STENCILOP_REPLACE: return WGPUStencilOperation_Replace; + case SG_STENCILOP_INCR_CLAMP: return WGPUStencilOperation_IncrementClamp; + case SG_STENCILOP_DECR_CLAMP: return WGPUStencilOperation_DecrementClamp; + case SG_STENCILOP_INVERT: return WGPUStencilOperation_Invert; + case SG_STENCILOP_INCR_WRAP: return WGPUStencilOperation_IncrementWrap; + case SG_STENCILOP_DECR_WRAP: return WGPUStencilOperation_DecrementWrap; + default: SOKOL_UNREACHABLE; return WGPUStencilOperation_Force32; + } +} + +_SOKOL_PRIVATE WGPUBlendOperation _sg_wgpu_blendop(sg_blend_op op) { + switch (op) { + case SG_BLENDOP_ADD: return WGPUBlendOperation_Add; + case SG_BLENDOP_SUBTRACT: return WGPUBlendOperation_Subtract; + case SG_BLENDOP_REVERSE_SUBTRACT: return WGPUBlendOperation_ReverseSubtract; + default: SOKOL_UNREACHABLE; return WGPUBlendOperation_Force32; + } +} + +_SOKOL_PRIVATE WGPUBlendFactor _sg_wgpu_blendfactor(sg_blend_factor f) { + switch (f) { + case SG_BLENDFACTOR_ZERO: return WGPUBlendFactor_Zero; + case SG_BLENDFACTOR_ONE: return WGPUBlendFactor_One; + case SG_BLENDFACTOR_SRC_COLOR: return WGPUBlendFactor_SrcColor; + case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return WGPUBlendFactor_OneMinusSrcColor; + case SG_BLENDFACTOR_SRC_ALPHA: return WGPUBlendFactor_SrcAlpha; + case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return WGPUBlendFactor_OneMinusSrcAlpha; + case SG_BLENDFACTOR_DST_COLOR: return WGPUBlendFactor_DstColor; + case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return WGPUBlendFactor_OneMinusDstColor; + case SG_BLENDFACTOR_DST_ALPHA: return WGPUBlendFactor_DstAlpha; + case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return WGPUBlendFactor_OneMinusDstAlpha; + case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return WGPUBlendFactor_SrcAlphaSaturated; + case SG_BLENDFACTOR_BLEND_COLOR: return WGPUBlendFactor_BlendColor; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return WGPUBlendFactor_OneMinusBlendColor; + /* FIXME: separate blend alpha value not supported? */ + case SG_BLENDFACTOR_BLEND_ALPHA: return WGPUBlendFactor_BlendColor; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return WGPUBlendFactor_OneMinusBlendColor; + default: + SOKOL_UNREACHABLE; return WGPUBlendFactor_Force32; + } +} + +_SOKOL_PRIVATE WGPUColorWriteMaskFlags _sg_wgpu_colorwritemask(uint8_t m) { + WGPUColorWriteMaskFlags res = 0; + if (0 != (m & SG_COLORMASK_R)) { + res |= WGPUColorWriteMask_Red; + } + if (0 != (m & SG_COLORMASK_G)) { + res |= WGPUColorWriteMask_Green; + } + if (0 != (m & SG_COLORMASK_B)) { + res |= WGPUColorWriteMask_Blue; + } + if (0 != (m & SG_COLORMASK_A)) { + res |= WGPUColorWriteMask_Alpha; + } + return res; +} + +_SOKOL_PRIVATE void _sg_wgpu_init_caps(void) { + _sg.backend = SG_BACKEND_WGPU; + _sg.features.instancing = true; + _sg.features.origin_top_left = true; + _sg.features.multiple_render_targets = true; + _sg.features.msaa_render_targets = true; + _sg.features.imagetype_3d = true; + _sg.features.imagetype_array = true; + _sg.features.image_clamp_to_border = false; + + /* FIXME: max images size??? */ + _sg.limits.max_image_size_2d = 8 * 1024; + _sg.limits.max_image_size_cube = 8 * 1024; + _sg.limits.max_image_size_3d = 2 * 1024; + _sg.limits.max_image_size_array = 8 * 1024; + _sg.limits.max_image_array_layers = 2 * 1024; + _sg.limits.max_vertex_attrs = SG_MAX_VERTEX_ATTRIBUTES; + + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8SI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32SI]); + _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_BGRA8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB10A2]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32SI]); + _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH]); + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL]); + + /* FIXME FIXME FIXME: need to check if BC texture compression is + actually supported, currently the WebGPU C-API doesn't allow this + */ + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC1_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC2_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_R]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_RSN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RG]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RGSN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBUF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_RGBA]); +} + +/* + WGPU uniform buffer pool implementation: + + At start of frame, a mapped buffer is grabbed from the pool, + or a new buffer is created if there is no mapped buffer available. + + At end of frame, the current buffer is unmapped before queue submit, + and async-mapped immediately again. + + UNIFORM BUFFER FIXME: + + - As per WebGPU spec, it should be possible to create a Uniform|MapWrite + buffer, but this isn't currently allowed in Dawn. +*/ +_SOKOL_PRIVATE void _sg_wgpu_ubpool_init(const sg_desc* desc) { + + /* Add the max-uniform-update size (64 KB) to the requested buffer size, + this is to prevent validation errors in the WebGPU implementation + if the entire buffer size is used per frame. 64 KB is the allowed + max uniform update size on NVIDIA + */ + _sg.wgpu.ub.num_bytes = desc->uniform_buffer_size + _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE; + + WGPUBufferDescriptor ub_desc; + memset(&ub_desc, 0, sizeof(ub_desc)); + ub_desc.size = _sg.wgpu.ub.num_bytes; + ub_desc.usage = WGPUBufferUsage_Uniform|WGPUBufferUsage_CopyDst; + _sg.wgpu.ub.buf = wgpuDeviceCreateBuffer(_sg.wgpu.dev, &ub_desc); + SOKOL_ASSERT(_sg.wgpu.ub.buf); + + WGPUBindGroupLayoutBinding ub_bglb_desc[SG_NUM_SHADER_STAGES][SG_MAX_SHADERSTAGE_UBS]; + memset(ub_bglb_desc, 0, sizeof(ub_bglb_desc)); + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + WGPUShaderStage vis = (stage_index == SG_SHADERSTAGE_VS) ? WGPUShaderStage_Vertex : WGPUShaderStage_Fragment; + for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { + int bind_index = stage_index * SG_MAX_SHADERSTAGE_UBS + ub_index; + ub_bglb_desc[stage_index][ub_index].binding = bind_index; + ub_bglb_desc[stage_index][ub_index].visibility = vis; + ub_bglb_desc[stage_index][ub_index].type = WGPUBindingType_UniformBuffer; + ub_bglb_desc[stage_index][ub_index].hasDynamicOffset = true; + } + } + + WGPUBindGroupLayoutDescriptor ub_bgl_desc; + memset(&ub_bgl_desc, 0, sizeof(ub_bgl_desc)); + ub_bgl_desc.bindingCount = SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS; + ub_bgl_desc.bindings = &ub_bglb_desc[0][0]; + _sg.wgpu.ub.bindgroup_layout = wgpuDeviceCreateBindGroupLayout(_sg.wgpu.dev, &ub_bgl_desc); + SOKOL_ASSERT(_sg.wgpu.ub.bindgroup_layout); + + WGPUBindGroupBinding ub_bgb[SG_NUM_SHADER_STAGES][SG_MAX_SHADERSTAGE_UBS]; + memset(ub_bgb, 0, sizeof(ub_bgb)); + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { + int bind_index = stage_index * SG_MAX_SHADERSTAGE_UBS + ub_index; + ub_bgb[stage_index][ub_index].binding = bind_index; + ub_bgb[stage_index][ub_index].buffer = _sg.wgpu.ub.buf; + // FIXME FIXME FIXME FIXME: HACK FOR VALIDATION BUG IN DAWN + ub_bgb[stage_index][ub_index].size = (1<<16); + } + } + WGPUBindGroupDescriptor bg_desc; + memset(&bg_desc, 0, sizeof(bg_desc)); + bg_desc.layout = _sg.wgpu.ub.bindgroup_layout; + bg_desc.bindingCount = SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS; + bg_desc.bindings = &ub_bgb[0][0]; + _sg.wgpu.ub.bindgroup = wgpuDeviceCreateBindGroup(_sg.wgpu.dev, &bg_desc); + SOKOL_ASSERT(_sg.wgpu.ub.bindgroup); +} + +_SOKOL_PRIVATE void _sg_wgpu_ubpool_discard(void) { + if (_sg.wgpu.ub.buf) { + wgpuBufferRelease(_sg.wgpu.ub.buf); + _sg.wgpu.ub.buf = 0; + } + if (_sg.wgpu.ub.bindgroup) { + wgpuBindGroupRelease(_sg.wgpu.ub.bindgroup); + _sg.wgpu.ub.bindgroup = 0; + } + if (_sg.wgpu.ub.bindgroup_layout) { + wgpuBindGroupLayoutRelease(_sg.wgpu.ub.bindgroup_layout); + _sg.wgpu.ub.bindgroup_layout = 0; + } + for (int i = 0; i < _sg.wgpu.ub.stage.num; i++) { + if (_sg.wgpu.ub.stage.buf[i]) { + wgpuBufferRelease(_sg.wgpu.ub.stage.buf[i]); + _sg.wgpu.ub.stage.buf[i] = 0; + _sg.wgpu.ub.stage.ptr[i] = 0; + } + } +} + +_SOKOL_PRIVATE void _sg_wgpu_ubpool_mapped_callback(WGPUBufferMapAsyncStatus status, void* data, uint64_t data_len, void* user_data) { + if (!_sg.wgpu.valid) { + return; + } + /* FIXME: better handling for this */ + if (WGPUBufferMapAsyncStatus_Success != status) { + SOKOL_LOG("Mapping uniform buffer failed!\n"); + SOKOL_ASSERT(false); + } + SOKOL_ASSERT(data && (data_len == _sg.wgpu.ub.num_bytes)); + int index = (int)(intptr_t) user_data; + SOKOL_ASSERT(index < _sg.wgpu.ub.stage.num); + SOKOL_ASSERT(0 == _sg.wgpu.ub.stage.ptr[index]); + _sg.wgpu.ub.stage.ptr[index] = (uint8_t*) data; +} + +_SOKOL_PRIVATE void _sg_wgpu_ubpool_next_frame(bool first_frame) { + + /* immediately request a new mapping for the last frame's current staging buffer */ + if (!first_frame) { + WGPUBuffer ub_src = _sg.wgpu.ub.stage.buf[_sg.wgpu.ub.stage.cur]; + wgpuBufferMapWriteAsync(ub_src, _sg_wgpu_ubpool_mapped_callback, (void*)(intptr_t)_sg.wgpu.ub.stage.cur); + } + + /* rewind per-frame offsets */ + _sg.wgpu.ub.offset = 0; + memset(&_sg.wgpu.ub.bind_offsets, 0, sizeof(_sg.wgpu.ub.bind_offsets)); + + /* check if a mapped staging buffer is available, otherwise create one */ + for (int i = 0; i < _sg.wgpu.ub.stage.num; i++) { + if (_sg.wgpu.ub.stage.ptr[i]) { + _sg.wgpu.ub.stage.cur = i; + return; + } + } + + /* no mapped uniform buffer available, create one */ + SOKOL_ASSERT(_sg.wgpu.ub.stage.num < _SG_WGPU_STAGING_PIPELINE_SIZE); + _sg.wgpu.ub.stage.cur = _sg.wgpu.ub.stage.num++; + const int cur = _sg.wgpu.ub.stage.cur; + + WGPUBufferDescriptor desc; + memset(&desc, 0, sizeof(desc)); + desc.size = _sg.wgpu.ub.num_bytes; + desc.usage = WGPUBufferUsage_CopySrc|WGPUBufferUsage_MapWrite; + WGPUCreateBufferMappedResult res = wgpuDeviceCreateBufferMapped(_sg.wgpu.dev, &desc); + _sg.wgpu.ub.stage.buf[cur] = res.buffer; + _sg.wgpu.ub.stage.ptr[cur] = (uint8_t*) res.data; + SOKOL_ASSERT(_sg.wgpu.ub.stage.buf[cur]); + SOKOL_ASSERT(_sg.wgpu.ub.stage.ptr[cur]); + SOKOL_ASSERT(res.dataLength == _sg.wgpu.ub.num_bytes); +} + +_SOKOL_PRIVATE void _sg_wgpu_ubpool_flush(void) { + /* unmap staging buffer and copy to uniform buffer */ + const int cur = _sg.wgpu.ub.stage.cur; + SOKOL_ASSERT(_sg.wgpu.ub.stage.ptr[cur]); + _sg.wgpu.ub.stage.ptr[cur] = 0; + WGPUBuffer src_buf = _sg.wgpu.ub.stage.buf[cur]; + wgpuBufferUnmap(src_buf); + if (_sg.wgpu.ub.offset > 0) { + WGPUBuffer dst_buf = _sg.wgpu.ub.buf; + wgpuCommandEncoderCopyBufferToBuffer(_sg.wgpu.render_cmd_enc, src_buf, 0, dst_buf, 0, _sg.wgpu.ub.offset); + } +} + +/* helper function to compute number of bytes needed in staging buffer to copy image data */ +_SOKOL_PRIVATE uint32_t _sg_wgpu_image_content_buffer_size(const _sg_image_t* img, const sg_image_content* content) { + uint32_t num_bytes = 0; + const uint32_t num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6:1; + const uint32_t num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.depth : 1; + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++) { + const uint32_t mip_width = _sg_max(img->cmn.width >> mip_index, 1); + const uint32_t mip_height = _sg_max(img->cmn.height >> mip_index, 1); + /* row-pitch must be 256-aligend */ + const uint32_t bytes_per_slice = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, _SG_WGPU_ROWPITCH_ALIGN); + num_bytes += bytes_per_slice * num_slices * num_faces; + } + return num_bytes; +} + +/* helper function to copy image data into a texture via a staging buffer, returns number of + bytes copied +*/ +_SOKOL_PRIVATE uint32_t _sg_wgpu_copy_image_content(WGPUBuffer stg_buf, uint8_t* stg_base_ptr, uint32_t stg_base_offset, _sg_image_t* img, const sg_image_content* content) { + SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); + SOKOL_ASSERT(stg_buf && stg_base_ptr); + SOKOL_ASSERT(img); + SOKOL_ASSERT(content); + uint32_t stg_offset = stg_base_offset; + const uint32_t num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6:1; + const uint32_t num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.depth : 1; + const sg_pixel_format fmt = img->cmn.pixel_format; + WGPUBufferCopyView src_view; + memset(&src_view, 0, sizeof(src_view)); + src_view.buffer = stg_buf; + WGPUTextureCopyView dst_view; + memset(&dst_view, 0, sizeof(dst_view)); + dst_view.texture = img->wgpu.tex; + WGPUExtent3D extent; + memset(&extent, 0, sizeof(extent)); + + for (uint32_t face_index = 0; face_index < num_faces; face_index++) { + for (uint32_t mip_index = 0; mip_index < (uint32_t)img->cmn.num_mipmaps; mip_index++) { + SOKOL_ASSERT(content->subimage[face_index][mip_index].ptr); + SOKOL_ASSERT(content->subimage[face_index][mip_index].size > 0); + const uint8_t* src_base_ptr = (const uint8_t*)content->subimage[face_index][mip_index].ptr; + SOKOL_ASSERT(src_base_ptr); + uint8_t* dst_base_ptr = stg_base_ptr + stg_offset; + + const uint32_t mip_width = _sg_max(img->cmn.width >> mip_index, 1); + const uint32_t mip_height = _sg_max(img->cmn.height >> mip_index, 1); + const uint32_t mip_depth = (img->cmn.type == SG_IMAGETYPE_3D) ? _sg_max(img->cmn.depth >> mip_index, 1) : 1; + const uint32_t num_rows = _sg_num_rows(fmt, mip_height); + const uint32_t src_bytes_per_row = _sg_row_pitch(fmt, mip_width, 1); + const uint32_t dst_bytes_per_row = _sg_row_pitch(fmt, mip_width, _SG_WGPU_ROWPITCH_ALIGN); + const uint32_t src_bytes_per_slice = _sg_surface_pitch(fmt, mip_width, mip_height, 1); + const uint32_t dst_bytes_per_slice = _sg_surface_pitch(fmt, mip_width, mip_height, _SG_WGPU_ROWPITCH_ALIGN); + SOKOL_ASSERT((uint32_t)content->subimage[face_index][mip_index].size == (src_bytes_per_slice * num_slices)); + SOKOL_ASSERT(src_bytes_per_row <= dst_bytes_per_row); + SOKOL_ASSERT(src_bytes_per_slice == (src_bytes_per_row * num_rows)); + SOKOL_ASSERT(dst_bytes_per_slice == (dst_bytes_per_row * num_rows)); + _SOKOL_UNUSED(src_bytes_per_slice); + + /* copy content into mapped staging buffer */ + if (src_bytes_per_row == dst_bytes_per_row) { + /* can do a single memcpy */ + uint32_t num_bytes = content->subimage[face_index][mip_index].size; + memcpy(dst_base_ptr, src_base_ptr, num_bytes); + } + else { + /* src/dst pitch doesn't match, need to copy row by row */ + uint8_t* dst_ptr = dst_base_ptr; + const uint8_t* src_ptr = src_base_ptr; + for (uint32_t slice_index = 0; slice_index < num_slices; slice_index++) { + SOKOL_ASSERT(dst_ptr == dst_base_ptr + slice_index * dst_bytes_per_slice); + for (uint32_t row_index = 0; row_index < num_rows; row_index++) { + memcpy(dst_ptr, src_ptr, src_bytes_per_row); + src_ptr += src_bytes_per_row; + dst_ptr += dst_bytes_per_row; + } + } + } + + /* record the staging copy operation into command encoder */ + src_view.imageHeight = mip_height; + src_view.rowPitch = dst_bytes_per_row; + dst_view.mipLevel = mip_index; + extent.width = mip_width; + extent.height = mip_height; + extent.depth = mip_depth; + SOKOL_ASSERT((img->cmn.type != SG_IMAGETYPE_CUBE) || (num_slices == 1)); + for (uint32_t slice_index = 0; slice_index < num_slices; slice_index++) { + const uint32_t layer_index = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? slice_index : face_index; + src_view.offset = stg_offset; + dst_view.arrayLayer = layer_index; + wgpuCommandEncoderCopyBufferToTexture(_sg.wgpu.staging_cmd_enc, &src_view, &dst_view, &extent); + stg_offset += dst_bytes_per_slice; + SOKOL_ASSERT(stg_offset <= _sg.wgpu.staging.num_bytes); + } + } + } + SOKOL_ASSERT(stg_offset >= stg_base_offset); + return (stg_offset - stg_base_offset); +} + +/* + The WGPU staging buffer implementation: + + Very similar to the uniform buffer pool, there's a pool of big + per-frame staging buffers, each must be big enough to hold + all data uploaded to dynamic resources for one frame. + + Staging buffers are created on demand and reused, because the + 'frame pipeline depth' of WGPU isn't predictable. + + The difference to the uniform buffer system is that there isn't + a 1:1 relationship for source- and destination for the + data-copy operation. There's always one staging buffer as copy-source + per frame, but many copy-destinations (regular vertex/index buffers + or images). Instead of one big copy-operation at the end of the frame, + multiple copy-operations will be written throughout the frame. +*/ +_SOKOL_PRIVATE void _sg_wgpu_staging_init(const sg_desc* desc) { + SOKOL_ASSERT(desc && (desc->staging_buffer_size > 0)); + _sg.wgpu.staging.num_bytes = desc->staging_buffer_size; + /* there's actually nothing more to do here */ +} + +_SOKOL_PRIVATE void _sg_wgpu_staging_discard(void) { + for (int i = 0; i < _sg.wgpu.staging.num; i++) { + if (_sg.wgpu.staging.buf[i]) { + wgpuBufferRelease(_sg.wgpu.staging.buf[i]); + _sg.wgpu.staging.buf[i] = 0; + _sg.wgpu.staging.ptr[i] = 0; + } + } +} + +_SOKOL_PRIVATE void _sg_wgpu_staging_mapped_callback(WGPUBufferMapAsyncStatus status, void* data, uint64_t data_len, void* user_data) { + if (!_sg.wgpu.valid) { + return; + } + /* FIXME: better handling for this */ + if (WGPUBufferMapAsyncStatus_Success != status) { + SOKOL_ASSERT("Mapping staging buffer failed!\n"); + SOKOL_ASSERT(false); + } + SOKOL_ASSERT(data && (data_len == _sg.wgpu.staging.num_bytes)); + int index = (int)(intptr_t) user_data; + SOKOL_ASSERT(index < _sg.wgpu.staging.num); + SOKOL_ASSERT(0 == _sg.wgpu.staging.ptr[index]); + _sg.wgpu.staging.ptr[index] = (uint8_t*) data; +} + +_SOKOL_PRIVATE void _sg_wgpu_staging_next_frame(bool first_frame) { + + /* immediately request a new mapping for the last frame's current staging buffer */ + if (!first_frame) { + WGPUBuffer cur_buf = _sg.wgpu.staging.buf[_sg.wgpu.staging.cur]; + wgpuBufferMapWriteAsync(cur_buf, _sg_wgpu_staging_mapped_callback, (void*)(intptr_t)_sg.wgpu.staging.cur); + } + + /* rewind staging-buffer offset */ + _sg.wgpu.staging.offset = 0; + + /* check if mapped staging buffer is available, otherwise create one */ + for (int i = 0; i < _sg.wgpu.staging.num; i++) { + if (_sg.wgpu.staging.ptr[i]) { + _sg.wgpu.staging.cur = i; + return; + } + } + + /* no mapped buffer available, create one */ + SOKOL_ASSERT(_sg.wgpu.staging.num < _SG_WGPU_STAGING_PIPELINE_SIZE); + _sg.wgpu.staging.cur = _sg.wgpu.staging.num++; + const int cur = _sg.wgpu.staging.cur; + + WGPUBufferDescriptor desc; + memset(&desc, 0, sizeof(desc)); + desc.size = _sg.wgpu.staging.num_bytes; + desc.usage = WGPUBufferUsage_CopySrc|WGPUBufferUsage_MapWrite; + WGPUCreateBufferMappedResult res = wgpuDeviceCreateBufferMapped(_sg.wgpu.dev, &desc); + _sg.wgpu.staging.buf[cur] = res.buffer; + _sg.wgpu.staging.ptr[cur] = (uint8_t*) res.data; + SOKOL_ASSERT(_sg.wgpu.staging.buf[cur]); + SOKOL_ASSERT(_sg.wgpu.staging.ptr[cur]); + SOKOL_ASSERT(res.dataLength == _sg.wgpu.staging.num_bytes); +} + +_SOKOL_PRIVATE uint32_t _sg_wgpu_staging_copy_to_buffer(WGPUBuffer dst_buf, uint32_t dst_buf_offset, const void* data, uint32_t data_num_bytes) { + /* Copy a chunk of data into the staging buffer, and record a blit-operation into + the command encoder, bump the offset for the next data chunk, return 0 if there + was not enough room in the staging buffer, return the number of actually + copied bytes on success. + + NOTE: that the number of staging bytes to be copied must be a multiple of 4. + + */ + SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); + SOKOL_ASSERT((dst_buf_offset & 3) == 0); + SOKOL_ASSERT(data_num_bytes > 0); + uint32_t copy_num_bytes = _sg_roundup(data_num_bytes, 4); + if ((_sg.wgpu.staging.offset + copy_num_bytes) >= _sg.wgpu.staging.num_bytes) { + SOKOL_LOG("WGPU: Per frame staging buffer full (in _sg_wgpu_staging_copy_to_buffer())!\n"); + return false; + } + const int cur = _sg.wgpu.staging.cur; + SOKOL_ASSERT(_sg.wgpu.staging.ptr[cur]); + uint32_t stg_buf_offset = _sg.wgpu.staging.offset; + uint8_t* stg_ptr = _sg.wgpu.staging.ptr[cur] + stg_buf_offset; + memcpy(stg_ptr, data, data_num_bytes); + WGPUBuffer stg_buf = _sg.wgpu.staging.buf[cur]; + wgpuCommandEncoderCopyBufferToBuffer(_sg.wgpu.staging_cmd_enc, stg_buf, stg_buf_offset, dst_buf, dst_buf_offset, copy_num_bytes); + _sg.wgpu.staging.offset = stg_buf_offset + copy_num_bytes; + return copy_num_bytes; +} + +_SOKOL_PRIVATE bool _sg_wgpu_staging_copy_to_texture(_sg_image_t* img, const sg_image_content* content) { + /* similar to _sg_wgpu_staging_copy_to_buffer(), but with image data instead */ + SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); + uint32_t num_bytes = _sg_wgpu_image_content_buffer_size(img, content); + if ((_sg.wgpu.staging.offset + num_bytes) >= _sg.wgpu.staging.num_bytes) { + SOKOL_LOG("WGPU: Per frame staging buffer full (in _sg_wgpu_staging_copy_to_texture)!\n"); + return false; + } + const int cur = _sg.wgpu.staging.cur; + SOKOL_ASSERT(_sg.wgpu.staging.ptr[cur]); + uint32_t stg_offset = _sg.wgpu.staging.offset; + uint8_t* stg_ptr = _sg.wgpu.staging.ptr[cur]; + WGPUBuffer stg_buf = _sg.wgpu.staging.buf[cur]; + uint32_t bytes_copied = _sg_wgpu_copy_image_content(stg_buf, stg_ptr, stg_offset, img, content); + _SOKOL_UNUSED(bytes_copied); + SOKOL_ASSERT(bytes_copied == num_bytes); + _sg.wgpu.staging.offset = _sg_roundup(stg_offset + num_bytes, _SG_WGPU_STAGING_ALIGN); + return true; +} + +_SOKOL_PRIVATE void _sg_wgpu_staging_unmap(void) { + /* called at end of frame before queue-submit */ + const int cur = _sg.wgpu.staging.cur; + SOKOL_ASSERT(_sg.wgpu.staging.ptr[cur]); + _sg.wgpu.staging.ptr[cur] = 0; + wgpuBufferUnmap(_sg.wgpu.staging.buf[cur]); +} + +/*--- WGPU sampler cache functions ---*/ +_SOKOL_PRIVATE void _sg_wgpu_init_sampler_cache(const sg_desc* desc) { + SOKOL_ASSERT(desc->sampler_cache_size > 0); + _sg_smpcache_init(&_sg.wgpu.sampler_cache, desc->sampler_cache_size); +} + +_SOKOL_PRIVATE void _sg_wgpu_destroy_sampler_cache(void) { + SOKOL_ASSERT(_sg.wgpu.sampler_cache.items); + SOKOL_ASSERT(_sg.wgpu.sampler_cache.num_items <= _sg.wgpu.sampler_cache.capacity); + for (int i = 0; i < _sg.wgpu.sampler_cache.num_items; i++) { + wgpuSamplerRelease((WGPUSampler)_sg_smpcache_sampler(&_sg.wgpu.sampler_cache, i)); + } + _sg_smpcache_discard(&_sg.wgpu.sampler_cache); +} + +_SOKOL_PRIVATE WGPUSampler _sg_wgpu_create_sampler(const sg_image_desc* img_desc) { + SOKOL_ASSERT(img_desc); + int index = _sg_smpcache_find_item(&_sg.wgpu.sampler_cache, img_desc); + if (index >= 0) { + /* reuse existing sampler */ + return (WGPUSampler) _sg_smpcache_sampler(&_sg.wgpu.sampler_cache, index); + } + else { + /* create a new WGPU sampler and add to sampler cache */ + /* FIXME: anisotropic filtering not supported? */ + WGPUSamplerDescriptor smp_desc; + memset(&smp_desc, 0, sizeof(smp_desc)); + smp_desc.addressModeU = _sg_wgpu_sampler_addrmode(img_desc->wrap_u); + smp_desc.addressModeV = _sg_wgpu_sampler_addrmode(img_desc->wrap_v); + smp_desc.addressModeW = _sg_wgpu_sampler_addrmode(img_desc->wrap_w); + smp_desc.magFilter = _sg_wgpu_sampler_minmagfilter(img_desc->mag_filter); + smp_desc.minFilter = _sg_wgpu_sampler_minmagfilter(img_desc->min_filter); + smp_desc.mipmapFilter = _sg_wgpu_sampler_mipfilter(img_desc->min_filter); + smp_desc.lodMinClamp = img_desc->min_lod; + smp_desc.lodMaxClamp = img_desc->max_lod; + WGPUSampler smp = wgpuDeviceCreateSampler(_sg.wgpu.dev, &smp_desc); + SOKOL_ASSERT(smp); + _sg_smpcache_add_item(&_sg.wgpu.sampler_cache, img_desc, (uintptr_t)smp); + return smp; + } +} + +/*--- WGPU backend API functions ---*/ +_SOKOL_PRIVATE void _sg_wgpu_setup_backend(const sg_desc* desc) { + SOKOL_ASSERT(desc); + SOKOL_ASSERT(desc->context.wgpu.device); + SOKOL_ASSERT(desc->context.wgpu.render_view_cb); + SOKOL_ASSERT(desc->context.wgpu.resolve_view_cb); + SOKOL_ASSERT(desc->context.wgpu.depth_stencil_view_cb); + SOKOL_ASSERT(desc->uniform_buffer_size > 0); + SOKOL_ASSERT(desc->staging_buffer_size > 0); + _sg.backend = SG_BACKEND_WGPU; + _sg.wgpu.valid = true; + _sg.wgpu.dev = (WGPUDevice) desc->context.wgpu.device; + _sg.wgpu.render_view_cb = (WGPUTextureView(*)(void)) desc->context.wgpu.render_view_cb; + _sg.wgpu.resolve_view_cb = (WGPUTextureView(*)(void)) desc->context.wgpu.resolve_view_cb; + _sg.wgpu.depth_stencil_view_cb = (WGPUTextureView(*)(void)) desc->context.wgpu.depth_stencil_view_cb; + _sg.wgpu.queue = wgpuDeviceCreateQueue(_sg.wgpu.dev); + SOKOL_ASSERT(_sg.wgpu.queue); + + /* setup WebGPU features and limits */ + _sg_wgpu_init_caps(); + + /* setup the sampler cache, uniform and staging buffer pools */ + _sg_wgpu_init_sampler_cache(&_sg.desc); + _sg_wgpu_ubpool_init(desc); + _sg_wgpu_ubpool_next_frame(true); + _sg_wgpu_staging_init(desc); + _sg_wgpu_staging_next_frame(true); + + /* create an empty bind group for shader stages without bound images */ + WGPUBindGroupLayoutDescriptor bgl_desc; + memset(&bgl_desc, 0, sizeof(bgl_desc)); + WGPUBindGroupLayout empty_bgl = wgpuDeviceCreateBindGroupLayout(_sg.wgpu.dev, &bgl_desc); + SOKOL_ASSERT(empty_bgl); + WGPUBindGroupDescriptor bg_desc; + memset(&bg_desc, 0, sizeof(bg_desc)); + bg_desc.layout = empty_bgl; + _sg.wgpu.empty_bind_group = wgpuDeviceCreateBindGroup(_sg.wgpu.dev, &bg_desc); + SOKOL_ASSERT(_sg.wgpu.empty_bind_group); + wgpuBindGroupLayoutRelease(empty_bgl); + + /* create initial per-frame command encoders */ + WGPUCommandEncoderDescriptor cmd_enc_desc; + memset(&cmd_enc_desc, 0, sizeof(cmd_enc_desc)); + _sg.wgpu.render_cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); + SOKOL_ASSERT(_sg.wgpu.render_cmd_enc); + _sg.wgpu.staging_cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); + SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); +} + +_SOKOL_PRIVATE void _sg_wgpu_discard_backend(void) { + SOKOL_ASSERT(_sg.wgpu.valid); + SOKOL_ASSERT(_sg.wgpu.render_cmd_enc); + SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); + _sg.wgpu.valid = false; + _sg_wgpu_ubpool_discard(); + _sg_wgpu_staging_discard(); + _sg_wgpu_destroy_sampler_cache(); + wgpuBindGroupRelease(_sg.wgpu.empty_bind_group); + wgpuCommandEncoderRelease(_sg.wgpu.render_cmd_enc); + _sg.wgpu.render_cmd_enc = 0; + wgpuCommandEncoderRelease(_sg.wgpu.staging_cmd_enc); + _sg.wgpu.staging_cmd_enc = 0; + if (_sg.wgpu.queue) { + wgpuQueueRelease(_sg.wgpu.queue); + _sg.wgpu.queue = 0; + } +} + +_SOKOL_PRIVATE void _sg_wgpu_reset_state_cache(void) { + SOKOL_LOG("_sg_wgpu_reset_state_cache: FIXME\n"); +} + +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + _SOKOL_UNUSED(ctx); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_wgpu_destroy_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + _SOKOL_UNUSED(ctx); +} + +_SOKOL_PRIVATE void _sg_wgpu_activate_context(_sg_context_t* ctx) { + SOKOL_LOG("_sg_wgpu_activate_context: FIXME\n"); +} + +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf && desc); + const bool injected = (0 != desc->wgpu_buffer); + _sg_buffer_common_init(&buf->cmn, desc); + if (injected) { + buf->wgpu.buf = (WGPUBuffer) desc->wgpu_buffer; + wgpuBufferReference(buf->wgpu.buf); + } + else { + WGPUBufferDescriptor wgpu_buf_desc; + memset(&wgpu_buf_desc, 0, sizeof(wgpu_buf_desc)); + wgpu_buf_desc.usage = _sg_wgpu_buffer_usage(buf->cmn.type, buf->cmn.usage); + wgpu_buf_desc.size = buf->cmn.size; + if (SG_USAGE_IMMUTABLE == buf->cmn.usage) { + SOKOL_ASSERT(desc->content); + WGPUCreateBufferMappedResult res = wgpuDeviceCreateBufferMapped(_sg.wgpu.dev, &wgpu_buf_desc); + buf->wgpu.buf = res.buffer; + SOKOL_ASSERT(res.data && ((int)res.dataLength == buf->cmn.size)); + memcpy(res.data, desc->content, buf->cmn.size); + wgpuBufferUnmap(res.buffer); + } + else { + buf->wgpu.buf = wgpuDeviceCreateBuffer(_sg.wgpu.dev, &wgpu_buf_desc); + } + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_wgpu_destroy_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + WGPUBuffer wgpu_buf = buf->wgpu.buf; + if (0 != wgpu_buf) { + wgpuBufferRelease(wgpu_buf); + } +} + +_SOKOL_PRIVATE void _sg_wgpu_init_texdesc_common(WGPUTextureDescriptor* wgpu_tex_desc, const sg_image_desc* desc) { + wgpu_tex_desc->usage = WGPUTextureUsage_Sampled|WGPUTextureUsage_CopyDst; + wgpu_tex_desc->dimension = _sg_wgpu_tex_dim(desc->type); + wgpu_tex_desc->size.width = desc->width; + wgpu_tex_desc->size.height = desc->height; + if (desc->type == SG_IMAGETYPE_3D) { + wgpu_tex_desc->size.depth = desc->depth; + wgpu_tex_desc->arrayLayerCount = 1; + } + else if (desc->type == SG_IMAGETYPE_CUBE) { + wgpu_tex_desc->size.depth = 1; + wgpu_tex_desc->arrayLayerCount = 6; + } + else { + wgpu_tex_desc->size.depth = 1; + wgpu_tex_desc->arrayLayerCount = desc->layers; + } + wgpu_tex_desc->format = _sg_wgpu_textureformat(desc->pixel_format); + wgpu_tex_desc->mipLevelCount = desc->num_mipmaps; + wgpu_tex_desc->sampleCount = 1; +} + +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && desc); + SOKOL_ASSERT(_sg.wgpu.dev); + SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); + + _sg_image_common_init(&img->cmn, desc); + + const bool injected = (0 != desc->wgpu_texture); + const bool is_msaa = desc->sample_count > 1; + WGPUTextureDescriptor wgpu_tex_desc; + memset(&wgpu_tex_desc, 0, sizeof(wgpu_tex_desc)); + _sg_wgpu_init_texdesc_common(&wgpu_tex_desc, desc); + if (_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format)) { + SOKOL_ASSERT(img->cmn.render_target); + SOKOL_ASSERT(img->cmn.type == SG_IMAGETYPE_2D); + SOKOL_ASSERT(img->cmn.num_mipmaps == 1); + SOKOL_ASSERT(!injected); + /* NOTE: a depth-stencil texture will never be MSAA-resolved, so there + won't be a separate MSAA- and resolve-texture + */ + wgpu_tex_desc.usage = WGPUTextureUsage_OutputAttachment; + wgpu_tex_desc.sampleCount = desc->sample_count; + img->wgpu.tex = wgpuDeviceCreateTexture(_sg.wgpu.dev, &wgpu_tex_desc); + SOKOL_ASSERT(img->wgpu.tex); + } + else { + if (injected) { + img->wgpu.tex = (WGPUTexture) desc->wgpu_texture; + wgpuTextureReference(img->wgpu.tex); + } + else { + /* NOTE: in the MSAA-rendertarget case, both the MSAA texture *and* + the resolve texture need OutputAttachment usage + */ + if (img->cmn.render_target) { + wgpu_tex_desc.usage = WGPUTextureUsage_Sampled|WGPUTextureUsage_OutputAttachment; + } + img->wgpu.tex = wgpuDeviceCreateTexture(_sg.wgpu.dev, &wgpu_tex_desc); + SOKOL_ASSERT(img->wgpu.tex); + + /* copy content into texture via a throw-away staging buffer */ + if (desc->usage == SG_USAGE_IMMUTABLE && !desc->render_target) { + WGPUBufferDescriptor wgpu_buf_desc; + memset(&wgpu_buf_desc, 0, sizeof(wgpu_buf_desc)); + wgpu_buf_desc.size = _sg_wgpu_image_content_buffer_size(img, &desc->content); + wgpu_buf_desc.usage = WGPUBufferUsage_CopySrc|WGPUBufferUsage_CopyDst; + WGPUCreateBufferMappedResult map = wgpuDeviceCreateBufferMapped(_sg.wgpu.dev, &wgpu_buf_desc); + SOKOL_ASSERT(map.buffer && map.data); + uint32_t num_bytes = _sg_wgpu_copy_image_content(map.buffer, (uint8_t*)map.data, 0, img, &desc->content); + _SOKOL_UNUSED(num_bytes); + SOKOL_ASSERT(num_bytes == wgpu_buf_desc.size); + wgpuBufferUnmap(map.buffer); + wgpuBufferRelease(map.buffer); + } + } + + /* create texture view object */ + WGPUTextureViewDescriptor wgpu_view_desc; + memset(&wgpu_view_desc, 0, sizeof(wgpu_view_desc)); + wgpu_view_desc.dimension = _sg_wgpu_tex_viewdim(desc->type); + img->wgpu.tex_view = wgpuTextureCreateView(img->wgpu.tex, &wgpu_view_desc); + + /* if render target and MSAA, then a separate texture in MSAA format is needed + which will be resolved into the regular texture at the end of the + offscreen-render pass + */ + if (desc->render_target && is_msaa) { + wgpu_tex_desc.dimension = WGPUTextureDimension_2D; + wgpu_tex_desc.size.depth = 1; + wgpu_tex_desc.arrayLayerCount = 1; + wgpu_tex_desc.mipLevelCount = 1; + wgpu_tex_desc.usage = WGPUTextureUsage_OutputAttachment; + wgpu_tex_desc.sampleCount = desc->sample_count; + img->wgpu.msaa_tex = wgpuDeviceCreateTexture(_sg.wgpu.dev, &wgpu_tex_desc); + SOKOL_ASSERT(img->wgpu.msaa_tex); + } + + /* create sampler via shared-sampler-cache */ + img->wgpu.sampler = _sg_wgpu_create_sampler(desc); + SOKOL_ASSERT(img->wgpu.sampler); + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_wgpu_destroy_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + if (img->wgpu.tex) { + wgpuTextureRelease(img->wgpu.tex); + img->wgpu.tex = 0; + } + if (img->wgpu.tex_view) { + wgpuTextureViewRelease(img->wgpu.tex_view); + img->wgpu.tex_view = 0; + } + if (img->wgpu.msaa_tex) { + wgpuTextureRelease(img->wgpu.msaa_tex); + img->wgpu.msaa_tex = 0; + } + /* NOTE: do *not* destroy the sampler from the shared-sampler-cache */ + img->wgpu.sampler = 0; +} + +/* + How BindGroups work in WebGPU: + + - up to 4 bind groups can be bound simultanously + - up to 16 bindings per bind group + - 'binding' slots are local per bind group + - in the shader: + layout(set=0, binding=1) corresponds to bind group 0, binding 1 + + Now how to map this to sokol-gfx's bind model: + + Reduce SG_MAX_SHADERSTAGE_IMAGES to 8, then: + + 1 bind group for all 8 uniform buffers + 1 bind group for vertex shader textures + samplers + 1 bind group for fragment shader textures + samples + + Alternatively: + + 1 bind group for 8 uniform buffer slots + 1 bind group for 8 vs images + 8 vs samplers + 1 bind group for 12 fs images + 1 bind group for 12 fs samplers + + I guess this means that we need to create BindGroups on the + fly during sg_apply_bindings() :/ +*/ +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && desc); + SOKOL_ASSERT(desc->vs.byte_code && desc->fs.byte_code); + _sg_shader_common_init(&shd->cmn, desc); + + bool success = true; + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS) ? &desc->vs : &desc->fs; + SOKOL_ASSERT((stage_desc->byte_code_size & 3) == 0); + + _sg_shader_stage_t* cmn_stage = &shd->cmn.stage[stage_index]; + _sg_wgpu_shader_stage_t* wgpu_stage = &shd->wgpu.stage[stage_index]; + + _sg_strcpy(&wgpu_stage->entry, stage_desc->entry); + WGPUShaderModuleDescriptor wgpu_shdmod_desc; + memset(&wgpu_shdmod_desc, 0, sizeof(wgpu_shdmod_desc)); + wgpu_shdmod_desc.codeSize = stage_desc->byte_code_size >> 2; + wgpu_shdmod_desc.code = (const uint32_t*) stage_desc->byte_code; + wgpu_stage->module = wgpuDeviceCreateShaderModule(_sg.wgpu.dev, &wgpu_shdmod_desc); + if (0 == wgpu_stage->module) { + success = false; + } + + /* create image/sampler bind group for the shader stage */ + WGPUShaderStage vis = (stage_index == SG_SHADERSTAGE_VS) ? WGPUShaderStage_Vertex : WGPUShaderStage_Fragment; + int num_imgs = cmn_stage->num_images; + if (num_imgs > _SG_WGPU_MAX_SHADERSTAGE_IMAGES) { + num_imgs = _SG_WGPU_MAX_SHADERSTAGE_IMAGES; + } + WGPUBindGroupLayoutBinding bglb_desc[_SG_WGPU_MAX_SHADERSTAGE_IMAGES * 2]; + memset(bglb_desc, 0, sizeof(bglb_desc)); + for (int img_index = 0; img_index < num_imgs; img_index++) { + /* texture- and sampler-bindings */ + WGPUBindGroupLayoutBinding* tex_desc = &bglb_desc[img_index*2 + 0]; + WGPUBindGroupLayoutBinding* smp_desc = &bglb_desc[img_index*2 + 1]; + + tex_desc->binding = img_index; + tex_desc->visibility = vis; + tex_desc->type = WGPUBindingType_SampledTexture; + tex_desc->textureDimension = _sg_wgpu_tex_viewdim(cmn_stage->images[img_index].type); + tex_desc->textureComponentType = _sg_wgpu_tex_comptype(cmn_stage->images[img_index].sampler_type); + + smp_desc->binding = img_index + _SG_WGPU_MAX_SHADERSTAGE_IMAGES; + smp_desc->visibility = vis; + smp_desc->type = WGPUBindingType_Sampler; + } + WGPUBindGroupLayoutDescriptor img_bgl_desc; + memset(&img_bgl_desc, 0, sizeof(img_bgl_desc)); + img_bgl_desc.bindingCount = num_imgs * 2; + img_bgl_desc.bindings = &bglb_desc[0]; + wgpu_stage->bind_group_layout = wgpuDeviceCreateBindGroupLayout(_sg.wgpu.dev, &img_bgl_desc); + SOKOL_ASSERT(wgpu_stage->bind_group_layout); + } + return success ? SG_RESOURCESTATE_VALID : SG_RESOURCESTATE_FAILED; +} + +_SOKOL_PRIVATE void _sg_wgpu_destroy_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + _sg_wgpu_shader_stage_t* wgpu_stage = &shd->wgpu.stage[stage_index]; + if (wgpu_stage->module) { + wgpuShaderModuleRelease(wgpu_stage->module); + wgpu_stage->module = 0; + } + if (wgpu_stage->bind_group_layout) { + wgpuBindGroupLayoutRelease(wgpu_stage->bind_group_layout); + wgpu_stage->bind_group_layout = 0; + } + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && shd && desc); + SOKOL_ASSERT(desc->shader.id == shd->slot.id); + SOKOL_ASSERT(shd->wgpu.stage[SG_SHADERSTAGE_VS].bind_group_layout); + SOKOL_ASSERT(shd->wgpu.stage[SG_SHADERSTAGE_FS].bind_group_layout); + pip->shader = shd; + _sg_pipeline_common_init(&pip->cmn, desc); + pip->wgpu.stencil_ref = (uint32_t) desc->depth_stencil.stencil_ref; + + WGPUBindGroupLayout pip_bgl[3] = { + _sg.wgpu.ub.bindgroup_layout, + shd->wgpu.stage[SG_SHADERSTAGE_VS].bind_group_layout, + shd->wgpu.stage[SG_SHADERSTAGE_FS].bind_group_layout + }; + WGPUPipelineLayoutDescriptor pl_desc; + memset(&pl_desc, 0, sizeof(pl_desc)); + pl_desc.bindGroupLayoutCount = 3; + pl_desc.bindGroupLayouts = &pip_bgl[0]; + WGPUPipelineLayout pip_layout = wgpuDeviceCreatePipelineLayout(_sg.wgpu.dev, &pl_desc); + + WGPUVertexBufferLayoutDescriptor vb_desc[SG_MAX_SHADERSTAGE_BUFFERS]; + memset(&vb_desc, 0, sizeof(vb_desc)); + WGPUVertexAttributeDescriptor va_desc[SG_MAX_SHADERSTAGE_BUFFERS][SG_MAX_VERTEX_ATTRIBUTES]; + memset(&va_desc, 0, sizeof(va_desc)); + int vb_idx = 0; + for (; vb_idx < SG_MAX_SHADERSTAGE_BUFFERS; vb_idx++) { + const sg_buffer_layout_desc* src_vb_desc = &desc->layout.buffers[vb_idx]; + if (0 == src_vb_desc->stride) { + break; + } + vb_desc[vb_idx].arrayStride = src_vb_desc->stride; + vb_desc[vb_idx].stepMode = _sg_wgpu_stepmode(src_vb_desc->step_func); + /* NOTE: WebGPU has no support for vertex step rate (because that's + not supported by Core Vulkan + */ + int va_idx = 0; + for (int va_loc = 0; va_loc < SG_MAX_VERTEX_ATTRIBUTES; va_loc++) { + const sg_vertex_attr_desc* src_va_desc = &desc->layout.attrs[va_loc]; + if (SG_VERTEXFORMAT_INVALID == src_va_desc->format) { + break; + } + pip->cmn.vertex_layout_valid[src_va_desc->buffer_index] = true; + if (vb_idx == src_va_desc->buffer_index) { + va_desc[vb_idx][va_idx].format = _sg_wgpu_vertexformat(src_va_desc->format); + va_desc[vb_idx][va_idx].offset = src_va_desc->offset; + va_desc[vb_idx][va_idx].shaderLocation = va_loc; + va_idx++; + } + } + vb_desc[vb_idx].attributeCount = va_idx; + vb_desc[vb_idx].attributes = &va_desc[vb_idx][0]; + } + WGPUVertexStateDescriptor vx_state_desc; + memset(&vx_state_desc, 0, sizeof(vx_state_desc)); + vx_state_desc.indexFormat = _sg_wgpu_indexformat(desc->index_type); + vx_state_desc.vertexBufferCount = vb_idx; + vx_state_desc.vertexBuffers = vb_desc; + + WGPURasterizationStateDescriptor rs_desc; + memset(&rs_desc, 0, sizeof(rs_desc)); + rs_desc.frontFace = _sg_wgpu_frontface(desc->rasterizer.face_winding); + rs_desc.cullMode = _sg_wgpu_cullmode(desc->rasterizer.cull_mode); + rs_desc.depthBias = (int32_t) desc->rasterizer.depth_bias; + rs_desc.depthBiasClamp = desc->rasterizer.depth_bias_clamp; + rs_desc.depthBiasSlopeScale = desc->rasterizer.depth_bias_slope_scale; + + WGPUDepthStencilStateDescriptor ds_desc; + memset(&ds_desc, 0, sizeof(ds_desc)); + ds_desc.format = _sg_wgpu_textureformat(desc->blend.depth_format); + ds_desc.depthWriteEnabled = desc->depth_stencil.depth_write_enabled; + ds_desc.depthCompare = _sg_wgpu_comparefunc(desc->depth_stencil.depth_compare_func); + ds_desc.stencilReadMask = desc->depth_stencil.stencil_read_mask; + ds_desc.stencilWriteMask = desc->depth_stencil.stencil_write_mask; + ds_desc.stencilFront.compare = _sg_wgpu_comparefunc(desc->depth_stencil.stencil_front.compare_func); + ds_desc.stencilFront.failOp = _sg_wgpu_stencilop(desc->depth_stencil.stencil_front.fail_op); + ds_desc.stencilFront.depthFailOp = _sg_wgpu_stencilop(desc->depth_stencil.stencil_front.depth_fail_op); + ds_desc.stencilFront.passOp = _sg_wgpu_stencilop(desc->depth_stencil.stencil_front.pass_op); + ds_desc.stencilBack.compare = _sg_wgpu_comparefunc(desc->depth_stencil.stencil_back.compare_func); + ds_desc.stencilBack.failOp = _sg_wgpu_stencilop(desc->depth_stencil.stencil_back.fail_op); + ds_desc.stencilBack.depthFailOp = _sg_wgpu_stencilop(desc->depth_stencil.stencil_back.depth_fail_op); + ds_desc.stencilBack.passOp = _sg_wgpu_stencilop(desc->depth_stencil.stencil_back.pass_op); + + WGPUProgrammableStageDescriptor fs_desc; + memset(&fs_desc, 0, sizeof(fs_desc)); + fs_desc.module = shd->wgpu.stage[SG_SHADERSTAGE_FS].module; + fs_desc.entryPoint = shd->wgpu.stage[SG_SHADERSTAGE_VS].entry.buf; + + WGPUColorStateDescriptor cs_desc[SG_MAX_COLOR_ATTACHMENTS]; + memset(cs_desc, 0, sizeof(cs_desc)); + cs_desc[0].format = _sg_wgpu_textureformat(desc->blend.color_format); + cs_desc[0].colorBlend.operation = _sg_wgpu_blendop(desc->blend.op_rgb); + cs_desc[0].colorBlend.srcFactor = _sg_wgpu_blendfactor(desc->blend.src_factor_rgb); + cs_desc[0].colorBlend.dstFactor = _sg_wgpu_blendfactor(desc->blend.dst_factor_rgb); + cs_desc[0].alphaBlend.operation = _sg_wgpu_blendop(desc->blend.op_alpha); + cs_desc[0].alphaBlend.srcFactor = _sg_wgpu_blendfactor(desc->blend.src_factor_alpha); + cs_desc[0].alphaBlend.dstFactor = _sg_wgpu_blendfactor(desc->blend.dst_factor_alpha); + cs_desc[0].writeMask = _sg_wgpu_colorwritemask(desc->blend.color_write_mask); + SOKOL_ASSERT(desc->blend.color_attachment_count <= SG_MAX_COLOR_ATTACHMENTS); + for (int i = 1; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + cs_desc[i] = cs_desc[0]; + } + + WGPURenderPipelineDescriptor pip_desc; + memset(&pip_desc, 0, sizeof(pip_desc)); + pip_desc.layout = pip_layout; + pip_desc.vertexStage.module = shd->wgpu.stage[SG_SHADERSTAGE_VS].module; + pip_desc.vertexStage.entryPoint = shd->wgpu.stage[SG_SHADERSTAGE_VS].entry.buf; + pip_desc.fragmentStage = &fs_desc; + pip_desc.vertexState = &vx_state_desc; + pip_desc.primitiveTopology = _sg_wgpu_topology(desc->primitive_type); + pip_desc.rasterizationState = &rs_desc; + pip_desc.sampleCount = desc->rasterizer.sample_count; + if (SG_PIXELFORMAT_NONE != desc->blend.depth_format) { + pip_desc.depthStencilState = &ds_desc; + } + pip_desc.colorStateCount = desc->blend.color_attachment_count; + pip_desc.colorStates = cs_desc; + pip_desc.sampleMask = 0xFFFFFFFF; /* FIXME: ??? */ + pip->wgpu.pip = wgpuDeviceCreateRenderPipeline(_sg.wgpu.dev, &pip_desc); + SOKOL_ASSERT(0 != pip->wgpu.pip); + wgpuPipelineLayoutRelease(pip_layout); + + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_wgpu_destroy_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + if (pip->wgpu.pip) { + wgpuRenderPipelineRelease(pip->wgpu.pip); + pip->wgpu.pip = 0; + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { + SOKOL_ASSERT(pass && desc); + SOKOL_ASSERT(att_images && att_images[0]); + _sg_pass_common_init(&pass->cmn, desc); + + /* copy image pointers and create render-texture views */ + const sg_attachment_desc* att_desc; + for (int i = 0; i < pass->cmn.num_color_atts; i++) { + att_desc = &desc->color_attachments[i]; + if (att_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(att_desc->image.id != SG_INVALID_ID); + SOKOL_ASSERT(0 == pass->wgpu.color_atts[i].image); + _sg_image_t* img = att_images[i]; + SOKOL_ASSERT(img && (img->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(img->cmn.pixel_format)); + pass->wgpu.color_atts[i].image = img; + /* create a render-texture-view to render into the right sub-surface */ + const bool is_msaa = img->cmn.sample_count > 1; + WGPUTextureViewDescriptor view_desc; + memset(&view_desc, 0, sizeof(view_desc)); + view_desc.baseMipLevel = is_msaa ? 0 : att_desc->mip_level; + view_desc.mipLevelCount = 1; + view_desc.baseArrayLayer = is_msaa ? 0 : att_desc->slice; + view_desc.arrayLayerCount = 1; + WGPUTexture wgpu_tex = is_msaa ? img->wgpu.msaa_tex : img->wgpu.tex; + SOKOL_ASSERT(wgpu_tex); + pass->wgpu.color_atts[i].render_tex_view = wgpuTextureCreateView(wgpu_tex, &view_desc); + SOKOL_ASSERT(pass->wgpu.color_atts[i].render_tex_view); + /* ... and if needed a separate resolve texture view */ + if (is_msaa) { + view_desc.baseMipLevel = att_desc->mip_level; + view_desc.baseArrayLayer = att_desc->slice; + WGPUTexture wgpu_tex = img->wgpu.tex; + pass->wgpu.color_atts[i].resolve_tex_view = wgpuTextureCreateView(wgpu_tex, &view_desc); + SOKOL_ASSERT(pass->wgpu.color_atts[i].resolve_tex_view); + } + } + } + SOKOL_ASSERT(0 == pass->wgpu.ds_att.image); + att_desc = &desc->depth_stencil_attachment; + if (att_desc->image.id != SG_INVALID_ID) { + const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; + SOKOL_ASSERT(att_images[ds_img_index] && (att_images[ds_img_index]->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_images[ds_img_index]->cmn.pixel_format)); + _sg_image_t* ds_img = att_images[ds_img_index]; + pass->wgpu.ds_att.image = ds_img; + /* create a render-texture view */ + SOKOL_ASSERT(0 == att_desc->mip_level); + SOKOL_ASSERT(0 == att_desc->slice); + WGPUTextureViewDescriptor view_desc; + memset(&view_desc, 0, sizeof(view_desc)); + WGPUTexture wgpu_tex = ds_img->wgpu.tex; + SOKOL_ASSERT(wgpu_tex); + pass->wgpu.ds_att.render_tex_view = wgpuTextureCreateView(wgpu_tex, &view_desc); + SOKOL_ASSERT(pass->wgpu.ds_att.render_tex_view); + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_wgpu_destroy_pass(_sg_pass_t* pass) { + SOKOL_ASSERT(pass); + for (int i = 0; i < pass->cmn.num_color_atts; i++) { + if (pass->wgpu.color_atts[i].render_tex_view) { + wgpuTextureViewRelease(pass->wgpu.color_atts[i].render_tex_view); + pass->wgpu.color_atts[i].render_tex_view = 0; + } + if (pass->wgpu.color_atts[i].resolve_tex_view) { + wgpuTextureViewRelease(pass->wgpu.color_atts[i].resolve_tex_view); + pass->wgpu.color_atts[i].resolve_tex_view = 0; + } + } + if (pass->wgpu.ds_att.render_tex_view) { + wgpuTextureViewRelease(pass->wgpu.ds_att.render_tex_view); + pass->wgpu.ds_att.render_tex_view = 0; + } +} + +_SOKOL_PRIVATE _sg_image_t* _sg_wgpu_pass_color_image(const _sg_pass_t* pass, int index) { + SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + /* NOTE: may return null */ + return pass->wgpu.color_atts[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_wgpu_pass_ds_image(const _sg_pass_t* pass) { + /* NOTE: may return null */ + SOKOL_ASSERT(pass); + return pass->wgpu.ds_att.image; +} + +_SOKOL_PRIVATE void _sg_wgpu_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { + SOKOL_ASSERT(action); + SOKOL_ASSERT(!_sg.wgpu.in_pass); + SOKOL_ASSERT(_sg.wgpu.render_cmd_enc); + SOKOL_ASSERT(_sg.wgpu.dev); + SOKOL_ASSERT(_sg.wgpu.render_view_cb); + SOKOL_ASSERT(_sg.wgpu.resolve_view_cb); + SOKOL_ASSERT(_sg.wgpu.depth_stencil_view_cb); + _sg.wgpu.in_pass = true; + _sg.wgpu.cur_width = w; + _sg.wgpu.cur_height = h; + _sg.wgpu.cur_pipeline = 0; + _sg.wgpu.cur_pipeline_id.id = SG_INVALID_ID; + + SOKOL_ASSERT(_sg.wgpu.render_cmd_enc); + if (pass) { + WGPURenderPassDescriptor wgpu_pass_desc; + memset(&wgpu_pass_desc, 0, sizeof(wgpu_pass_desc)); + WGPURenderPassColorAttachmentDescriptor wgpu_color_att_desc[SG_MAX_COLOR_ATTACHMENTS]; + memset(&wgpu_color_att_desc, 0, sizeof(wgpu_color_att_desc)); + SOKOL_ASSERT(pass->slot.state == SG_RESOURCESTATE_VALID); + for (int i = 0; i < pass->cmn.num_color_atts; i++) { + const _sg_wgpu_attachment_t* wgpu_att = &pass->wgpu.color_atts[i]; + wgpu_color_att_desc[i].loadOp = _sg_wgpu_load_op(action->colors[i].action); + wgpu_color_att_desc[i].storeOp = WGPUStoreOp_Store; + wgpu_color_att_desc[i].clearColor.r = action->colors[i].val[0]; + wgpu_color_att_desc[i].clearColor.g = action->colors[i].val[1]; + wgpu_color_att_desc[i].clearColor.b = action->colors[i].val[2]; + wgpu_color_att_desc[i].clearColor.a = action->colors[i].val[3]; + wgpu_color_att_desc[i].attachment = wgpu_att->render_tex_view; + if (wgpu_att->image->cmn.sample_count > 1) { + wgpu_color_att_desc[i].resolveTarget = wgpu_att->resolve_tex_view; + } + } + wgpu_pass_desc.colorAttachmentCount = pass->cmn.num_color_atts; + wgpu_pass_desc.colorAttachments = &wgpu_color_att_desc[0]; + if (pass->wgpu.ds_att.image) { + WGPURenderPassDepthStencilAttachmentDescriptor wgpu_ds_att_desc; + memset(&wgpu_ds_att_desc, 0, sizeof(wgpu_ds_att_desc)); + wgpu_ds_att_desc.depthLoadOp = _sg_wgpu_load_op(action->depth.action); + wgpu_ds_att_desc.clearDepth = action->depth.val; + wgpu_ds_att_desc.stencilLoadOp = _sg_wgpu_load_op(action->stencil.action); + wgpu_ds_att_desc.clearStencil = action->stencil.val; + wgpu_ds_att_desc.attachment = pass->wgpu.ds_att.render_tex_view; + wgpu_pass_desc.depthStencilAttachment = &wgpu_ds_att_desc; + _sg.wgpu.pass_enc = wgpuCommandEncoderBeginRenderPass(_sg.wgpu.render_cmd_enc, &wgpu_pass_desc); + } + } + else { + /* default render pass */ + WGPUTextureView wgpu_render_view = _sg.wgpu.render_view_cb(); + WGPUTextureView wgpu_resolve_view = _sg.wgpu.resolve_view_cb(); + WGPUTextureView wgpu_depth_stencil_view = _sg.wgpu.depth_stencil_view_cb(); + + WGPURenderPassDescriptor pass_desc; + memset(&pass_desc, 0, sizeof(pass_desc)); + WGPURenderPassColorAttachmentDescriptor color_att_desc; + memset(&color_att_desc, 0, sizeof(color_att_desc)); + color_att_desc.loadOp = _sg_wgpu_load_op(action->colors[0].action); + color_att_desc.clearColor.r = action->colors[0].val[0]; + color_att_desc.clearColor.g = action->colors[0].val[1]; + color_att_desc.clearColor.b = action->colors[0].val[2]; + color_att_desc.clearColor.a = action->colors[0].val[3]; + color_att_desc.attachment = wgpu_render_view; + color_att_desc.resolveTarget = wgpu_resolve_view; /* null if no MSAA rendering */ + pass_desc.colorAttachmentCount = 1; + pass_desc.colorAttachments = &color_att_desc; + WGPURenderPassDepthStencilAttachmentDescriptor ds_att_desc; + memset(&ds_att_desc, 0, sizeof(ds_att_desc)); + ds_att_desc.attachment = wgpu_depth_stencil_view; + SOKOL_ASSERT(0 != ds_att_desc.attachment); + ds_att_desc.depthLoadOp = _sg_wgpu_load_op(action->depth.action); + ds_att_desc.clearDepth = action->depth.val; + ds_att_desc.stencilLoadOp = _sg_wgpu_load_op(action->stencil.action); + ds_att_desc.clearStencil = action->stencil.val; + pass_desc.depthStencilAttachment = &ds_att_desc; + _sg.wgpu.pass_enc = wgpuCommandEncoderBeginRenderPass(_sg.wgpu.render_cmd_enc, &pass_desc); + } + SOKOL_ASSERT(_sg.wgpu.pass_enc); + + /* initial uniform buffer binding (required even if no uniforms are set in the frame) */ + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, + 0, /* groupIndex 0 is reserved for uniform buffers */ + _sg.wgpu.ub.bindgroup, + SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS, + &_sg.wgpu.ub.bind_offsets[0][0]); +} + +_SOKOL_PRIVATE void _sg_wgpu_end_pass(void) { + SOKOL_ASSERT(_sg.wgpu.in_pass); + SOKOL_ASSERT(_sg.wgpu.pass_enc); + _sg.wgpu.in_pass = false; + wgpuRenderPassEncoderEndPass(_sg.wgpu.pass_enc); + wgpuRenderPassEncoderRelease(_sg.wgpu.pass_enc); + _sg.wgpu.pass_enc = 0; +} + +_SOKOL_PRIVATE void _sg_wgpu_commit(void) { + SOKOL_ASSERT(!_sg.wgpu.in_pass); + SOKOL_ASSERT(_sg.wgpu.queue); + SOKOL_ASSERT(_sg.wgpu.render_cmd_enc); + SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); + + /* finish and submit this frame's work */ + _sg_wgpu_ubpool_flush(); + _sg_wgpu_staging_unmap(); + + WGPUCommandBuffer cmd_bufs[2]; + + WGPUCommandBufferDescriptor cmd_buf_desc; + memset(&cmd_buf_desc, 0, sizeof(cmd_buf_desc)); + cmd_bufs[0] = wgpuCommandEncoderFinish(_sg.wgpu.staging_cmd_enc, &cmd_buf_desc); + SOKOL_ASSERT(cmd_bufs[0]); + wgpuCommandEncoderRelease(_sg.wgpu.staging_cmd_enc); + _sg.wgpu.staging_cmd_enc = 0; + + cmd_bufs[1] = wgpuCommandEncoderFinish(_sg.wgpu.render_cmd_enc, &cmd_buf_desc); + SOKOL_ASSERT(cmd_bufs[1]); + wgpuCommandEncoderRelease(_sg.wgpu.render_cmd_enc); + _sg.wgpu.render_cmd_enc = 0; + + wgpuQueueSubmit(_sg.wgpu.queue, 2, &cmd_bufs[0]); + + wgpuCommandBufferRelease(cmd_bufs[0]); + wgpuCommandBufferRelease(cmd_bufs[1]); + + /* create a new render- and staging-command-encoders for next frame */ + WGPUCommandEncoderDescriptor cmd_enc_desc; + memset(&cmd_enc_desc, 0, sizeof(cmd_enc_desc)); + _sg.wgpu.staging_cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); + _sg.wgpu.render_cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); + + /* grab new staging buffers for uniform- and vertex/image-updates */ + _sg_wgpu_ubpool_next_frame(false); + _sg_wgpu_staging_next_frame(false); +} + +_SOKOL_PRIVATE void _sg_wgpu_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.wgpu.in_pass); + SOKOL_ASSERT(_sg.wgpu.pass_enc); + float xf = (float) x; + float yf = (float) (origin_top_left ? y : (_sg.wgpu.cur_height - (y + h))); + float wf = (float) w; + float hf = (float) h; + wgpuRenderPassEncoderSetViewport(_sg.wgpu.pass_enc, xf, yf, wf, hf, 0.0f, 1.0f); +} + +_SOKOL_PRIVATE void _sg_wgpu_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.wgpu.in_pass); + SOKOL_ASSERT(_sg.wgpu.pass_enc); + SOKOL_ASSERT(_sg.wgpu.in_pass); + SOKOL_ASSERT(_sg.wgpu.pass_enc); + + /* clip against framebuffer rect */ + x = _sg_min(_sg_max(0, x), _sg.wgpu.cur_width-1); + y = _sg_min(_sg_max(0, y), _sg.wgpu.cur_height-1); + if ((x + w) > _sg.wgpu.cur_width) { + w = _sg.wgpu.cur_width - x; + } + if ((y + h) > _sg.wgpu.cur_height) { + h = _sg.wgpu.cur_height - y; + } + w = _sg_max(w, 1); + h = _sg_max(h, 1); + + uint32_t sx = (uint32_t) x; + uint32_t sy = origin_top_left ? y : (_sg.wgpu.cur_height - (y + h)); + uint32_t sw = w; + uint32_t sh = h; + wgpuRenderPassEncoderSetScissorRect(_sg.wgpu.pass_enc, sx, sy, sw, sh); +} + +_SOKOL_PRIVATE void _sg_wgpu_apply_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + SOKOL_ASSERT(pip->wgpu.pip); + SOKOL_ASSERT(_sg.wgpu.in_pass); + SOKOL_ASSERT(_sg.wgpu.pass_enc); + _sg.wgpu.draw_indexed = (pip->cmn.index_type != SG_INDEXTYPE_NONE); + _sg.wgpu.cur_pipeline = pip; + _sg.wgpu.cur_pipeline_id.id = pip->slot.id; + wgpuRenderPassEncoderSetPipeline(_sg.wgpu.pass_enc, pip->wgpu.pip); + wgpuRenderPassEncoderSetBlendColor(_sg.wgpu.pass_enc, (WGPUColor*)pip->cmn.blend_color); + wgpuRenderPassEncoderSetStencilReference(_sg.wgpu.pass_enc, pip->wgpu.stencil_ref); +} + +_SOKOL_PRIVATE WGPUBindGroup _sg_wgpu_create_images_bindgroup(WGPUBindGroupLayout bgl, _sg_image_t** imgs, int num_imgs) { + SOKOL_ASSERT(_sg.wgpu.dev); + SOKOL_ASSERT(num_imgs <= _SG_WGPU_MAX_SHADERSTAGE_IMAGES); + WGPUBindGroupBinding img_bgb[_SG_WGPU_MAX_SHADERSTAGE_IMAGES * 2]; + memset(&img_bgb, 0, sizeof(img_bgb)); + for (int img_index = 0; img_index < num_imgs; img_index++) { + WGPUBindGroupBinding* tex_bdg = &img_bgb[img_index*2 + 0]; + WGPUBindGroupBinding* smp_bdg = &img_bgb[img_index*2 + 1]; + tex_bdg->binding = img_index; + tex_bdg->textureView = imgs[img_index]->wgpu.tex_view; + smp_bdg->binding = img_index + _SG_WGPU_MAX_SHADERSTAGE_IMAGES; + smp_bdg->sampler = imgs[img_index]->wgpu.sampler; + } + WGPUBindGroupDescriptor bg_desc; + memset(&bg_desc, 0, sizeof(bg_desc)); + bg_desc.layout = bgl; + bg_desc.bindingCount = 2 * num_imgs; + bg_desc.bindings = &img_bgb[0]; + WGPUBindGroup bg = wgpuDeviceCreateBindGroup(_sg.wgpu.dev, &bg_desc); + SOKOL_ASSERT(bg); + return bg; +} + +_SOKOL_PRIVATE void _sg_wgpu_apply_bindings( + _sg_pipeline_t* pip, + _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, + _sg_buffer_t* ib, int ib_offset, + _sg_image_t** vs_imgs, int num_vs_imgs, + _sg_image_t** fs_imgs, int num_fs_imgs) +{ + SOKOL_ASSERT(_sg.wgpu.in_pass); + SOKOL_ASSERT(_sg.wgpu.pass_enc); + SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); + + /* index buffer */ + if (ib) { + wgpuRenderPassEncoderSetIndexBuffer(_sg.wgpu.pass_enc, ib->wgpu.buf, ib_offset); + } + + /* vertex buffers */ + for (uint32_t slot = 0; slot < (uint32_t)num_vbs; slot++) { + wgpuRenderPassEncoderSetVertexBuffer(_sg.wgpu.pass_enc, slot, vbs[slot]->wgpu.buf, (uint64_t)vb_offsets[slot]); + } + + /* need to create throw-away bind groups for images */ + if (num_vs_imgs > 0) { + if (num_vs_imgs > _SG_WGPU_MAX_SHADERSTAGE_IMAGES) { + num_vs_imgs = _SG_WGPU_MAX_SHADERSTAGE_IMAGES; + } + WGPUBindGroupLayout vs_bgl = pip->shader->wgpu.stage[SG_SHADERSTAGE_VS].bind_group_layout; + SOKOL_ASSERT(vs_bgl); + WGPUBindGroup vs_img_bg = _sg_wgpu_create_images_bindgroup(vs_bgl, vs_imgs, num_vs_imgs); + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, 1, vs_img_bg, 0, 0); + wgpuBindGroupRelease(vs_img_bg); + } + else { + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, 1, _sg.wgpu.empty_bind_group, 0, 0); + } + if (num_fs_imgs > 0) { + if (num_fs_imgs > _SG_WGPU_MAX_SHADERSTAGE_IMAGES) { + num_fs_imgs = _SG_WGPU_MAX_SHADERSTAGE_IMAGES; + } + WGPUBindGroupLayout fs_bgl = pip->shader->wgpu.stage[SG_SHADERSTAGE_FS].bind_group_layout; + SOKOL_ASSERT(fs_bgl); + WGPUBindGroup fs_img_bg = _sg_wgpu_create_images_bindgroup(fs_bgl, fs_imgs, num_fs_imgs); + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, 2, fs_img_bg, 0, 0); + wgpuBindGroupRelease(fs_img_bg); + } + else { + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, 2, _sg.wgpu.empty_bind_group, 0, 0); + } +} + +_SOKOL_PRIVATE void _sg_wgpu_apply_uniforms(sg_shader_stage stage_index, int ub_index, const void* data, int num_bytes) { + SOKOL_ASSERT(_sg.wgpu.in_pass); + SOKOL_ASSERT(_sg.wgpu.pass_enc); + SOKOL_ASSERT(data && (num_bytes > 0)); + SOKOL_ASSERT((stage_index >= 0) && ((int)stage_index < SG_NUM_SHADER_STAGES)); + SOKOL_ASSERT((ub_index >= 0) && (ub_index < SG_MAX_SHADERSTAGE_UBS)); + SOKOL_ASSERT((_sg.wgpu.ub.offset + num_bytes) <= _sg.wgpu.ub.num_bytes); + SOKOL_ASSERT((_sg.wgpu.ub.offset & (_SG_WGPU_STAGING_ALIGN-1)) == 0); + SOKOL_ASSERT(_sg.wgpu.cur_pipeline && _sg.wgpu.cur_pipeline->shader); + SOKOL_ASSERT(_sg.wgpu.cur_pipeline->slot.id == _sg.wgpu.cur_pipeline_id.id); + SOKOL_ASSERT(_sg.wgpu.cur_pipeline->shader->slot.id == _sg.wgpu.cur_pipeline->cmn.shader_id.id); + SOKOL_ASSERT(ub_index < _sg.wgpu.cur_pipeline->shader->cmn.stage[stage_index].num_uniform_blocks); + SOKOL_ASSERT(num_bytes <= _sg.wgpu.cur_pipeline->shader->cmn.stage[stage_index].uniform_blocks[ub_index].size); + SOKOL_ASSERT(num_bytes <= _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE); + SOKOL_ASSERT(0 != _sg.wgpu.ub.stage.ptr[_sg.wgpu.ub.stage.cur]); + + uint8_t* dst_ptr = _sg.wgpu.ub.stage.ptr[_sg.wgpu.ub.stage.cur] + _sg.wgpu.ub.offset; + memcpy(dst_ptr, data, num_bytes); + _sg.wgpu.ub.bind_offsets[stage_index][ub_index] = _sg.wgpu.ub.offset; + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, + 0, /* groupIndex 0 is reserved for uniform buffers */ + _sg.wgpu.ub.bindgroup, + SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS, + &_sg.wgpu.ub.bind_offsets[0][0]); + _sg.wgpu.ub.offset = _sg_roundup(_sg.wgpu.ub.offset + num_bytes, _SG_WGPU_STAGING_ALIGN); +} + +_SOKOL_PRIVATE void _sg_wgpu_draw(int base_element, int num_elements, int num_instances) { + SOKOL_ASSERT(_sg.wgpu.in_pass); + SOKOL_ASSERT(_sg.wgpu.pass_enc); + if (_sg.wgpu.draw_indexed) { + wgpuRenderPassEncoderDrawIndexed(_sg.wgpu.pass_enc, num_elements, num_instances, base_element, 0, 0); + } + else { + wgpuRenderPassEncoderDraw(_sg.wgpu.pass_enc, num_elements, num_instances, base_element, 0); + } +} + +_SOKOL_PRIVATE void _sg_wgpu_update_buffer(_sg_buffer_t* buf, const void* data, uint32_t num_bytes) { + SOKOL_ASSERT(buf && data && (num_bytes > 0)); + uint32_t copied_num_bytes = _sg_wgpu_staging_copy_to_buffer(buf->wgpu.buf, 0, data, (uint32_t)num_bytes); + SOKOL_ASSERT(copied_num_bytes > 0); _SOKOL_UNUSED(copied_num_bytes); +} + +_SOKOL_PRIVATE uint32_t _sg_wgpu_append_buffer(_sg_buffer_t* buf, const void* data, uint32_t num_bytes, bool new_frame) { + SOKOL_ASSERT(buf && data && (num_bytes > 0)); + _SOKOL_UNUSED(new_frame); + uint32_t copied_num_bytes = _sg_wgpu_staging_copy_to_buffer(buf->wgpu.buf, buf->cmn.append_pos, data, num_bytes); + SOKOL_ASSERT(copied_num_bytes > 0); _SOKOL_UNUSED(copied_num_bytes); + return copied_num_bytes; +} + +_SOKOL_PRIVATE void _sg_wgpu_update_image(_sg_image_t* img, const sg_image_content* data) { + SOKOL_ASSERT(img && data); + bool success = _sg_wgpu_staging_copy_to_texture(img, data); + SOKOL_ASSERT(success); + _SOKOL_UNUSED(success); +} #endif +/*== BACKEND API WRAPPERS ====================================================*/ +static inline void _sg_setup_backend(const sg_desc* desc) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_setup_backend(desc); + #elif defined(SOKOL_METAL) + _sg_mtl_setup_backend(desc); + #elif defined(SOKOL_D3D11) + _sg_d3d11_setup_backend(desc); + #elif defined(SOKOL_WGPU) + _sg_wgpu_setup_backend(desc); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_setup_backend(desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_discard_backend(void) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_discard_backend(); + #elif defined(SOKOL_METAL) + _sg_mtl_discard_backend(); + #elif defined(SOKOL_D3D11) + _sg_d3d11_discard_backend(); + #elif defined(SOKOL_WGPU) + _sg_wgpu_discard_backend(); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_discard_backend(); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_reset_state_cache(void) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_reset_state_cache(); + #elif defined(SOKOL_METAL) + _sg_mtl_reset_state_cache(); + #elif defined(SOKOL_D3D11) + _sg_d3d11_reset_state_cache(); + #elif defined(SOKOL_WGPU) + _sg_wgpu_reset_state_cache(); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_reset_state_cache(); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_activate_context(_sg_context_t* ctx) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_activate_context(ctx); + #elif defined(SOKOL_METAL) + _sg_mtl_activate_context(ctx); + #elif defined(SOKOL_D3D11) + _sg_d3d11_activate_context(ctx); + #elif defined(SOKOL_WGPU) + _sg_wgpu_activate_context(ctx); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_activate_context(ctx); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline sg_resource_state _sg_create_context(_sg_context_t* ctx) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_create_context(ctx); + #elif defined(SOKOL_METAL) + return _sg_mtl_create_context(ctx); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_create_context(ctx); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_create_context(ctx); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_context(ctx); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_destroy_context(_sg_context_t* ctx) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_destroy_context(ctx); + #elif defined(SOKOL_METAL) + _sg_mtl_destroy_context(ctx); + #elif defined(SOKOL_D3D11) + _sg_d3d11_destroy_context(ctx); + #elif defined(SOKOL_WGPU) + _sg_wgpu_destroy_context(ctx); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_destroy_context(ctx); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline sg_resource_state _sg_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_create_buffer(buf, desc); + #elif defined(SOKOL_METAL) + return _sg_mtl_create_buffer(buf, desc); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_create_buffer(buf, desc); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_create_buffer(buf, desc); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_buffer(buf, desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_destroy_buffer(_sg_buffer_t* buf) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_destroy_buffer(buf); + #elif defined(SOKOL_METAL) + _sg_mtl_destroy_buffer(buf); + #elif defined(SOKOL_D3D11) + _sg_d3d11_destroy_buffer(buf); + #elif defined(SOKOL_WGPU) + _sg_wgpu_destroy_buffer(buf); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_destroy_buffer(buf); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline sg_resource_state _sg_create_image(_sg_image_t* img, const sg_image_desc* desc) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_create_image(img, desc); + #elif defined(SOKOL_METAL) + return _sg_mtl_create_image(img, desc); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_create_image(img, desc); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_create_image(img, desc); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_image(img, desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_destroy_image(_sg_image_t* img) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_destroy_image(img); + #elif defined(SOKOL_METAL) + _sg_mtl_destroy_image(img); + #elif defined(SOKOL_D3D11) + _sg_d3d11_destroy_image(img); + #elif defined(SOKOL_WGPU) + _sg_wgpu_destroy_image(img); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_destroy_image(img); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline sg_resource_state _sg_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_create_shader(shd, desc); + #elif defined(SOKOL_METAL) + return _sg_mtl_create_shader(shd, desc); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_create_shader(shd, desc); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_create_shader(shd, desc); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_shader(shd, desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_destroy_shader(_sg_shader_t* shd) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_destroy_shader(shd); + #elif defined(SOKOL_METAL) + _sg_mtl_destroy_shader(shd); + #elif defined(SOKOL_D3D11) + _sg_d3d11_destroy_shader(shd); + #elif defined(SOKOL_WGPU) + _sg_wgpu_destroy_shader(shd); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_destroy_shader(shd); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline sg_resource_state _sg_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_create_pipeline(pip, shd, desc); + #elif defined(SOKOL_METAL) + return _sg_mtl_create_pipeline(pip, shd, desc); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_create_pipeline(pip, shd, desc); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_create_pipeline(pip, shd, desc); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_pipeline(pip, shd, desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_destroy_pipeline(_sg_pipeline_t* pip) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_destroy_pipeline(pip); + #elif defined(SOKOL_METAL) + _sg_mtl_destroy_pipeline(pip); + #elif defined(SOKOL_D3D11) + _sg_d3d11_destroy_pipeline(pip); + #elif defined(SOKOL_WGPU) + _sg_wgpu_destroy_pipeline(pip); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_destroy_pipeline(pip); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline sg_resource_state _sg_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_create_pass(pass, att_images, desc); + #elif defined(SOKOL_METAL) + return _sg_mtl_create_pass(pass, att_images, desc); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_create_pass(pass, att_images, desc); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_create_pass(pass, att_images, desc); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_pass(pass, att_images, desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_destroy_pass(_sg_pass_t* pass) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_destroy_pass(pass); + #elif defined(SOKOL_METAL) + _sg_mtl_destroy_pass(pass); + #elif defined(SOKOL_D3D11) + _sg_d3d11_destroy_pass(pass); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_destroy_pass(pass); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_destroy_pass(pass); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline _sg_image_t* _sg_pass_color_image(const _sg_pass_t* pass, int index) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_pass_color_image(pass, index); + #elif defined(SOKOL_METAL) + return _sg_mtl_pass_color_image(pass, index); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_pass_color_image(pass, index); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_pass_color_image(pass, index); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_pass_color_image(pass, index); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline _sg_image_t* _sg_pass_ds_image(const _sg_pass_t* pass) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_pass_ds_image(pass); + #elif defined(SOKOL_METAL) + return _sg_mtl_pass_ds_image(pass); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_pass_ds_image(pass); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_pass_ds_image(pass); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_pass_ds_image(pass); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_begin_pass(pass, action, w, h); + #elif defined(SOKOL_METAL) + _sg_mtl_begin_pass(pass, action, w, h); + #elif defined(SOKOL_D3D11) + _sg_d3d11_begin_pass(pass, action, w, h); + #elif defined(SOKOL_WGPU) + _sg_wgpu_begin_pass(pass, action, w, h); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_begin_pass(pass, action, w, h); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_end_pass(void) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_end_pass(); + #elif defined(SOKOL_METAL) + _sg_mtl_end_pass(); + #elif defined(SOKOL_D3D11) + _sg_d3d11_end_pass(); + #elif defined(SOKOL_WGPU) + _sg_wgpu_end_pass(); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_end_pass(); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_apply_viewport(x, y, w, h, origin_top_left); + #elif defined(SOKOL_METAL) + _sg_mtl_apply_viewport(x, y, w, h, origin_top_left); + #elif defined(SOKOL_D3D11) + _sg_d3d11_apply_viewport(x, y, w, h, origin_top_left); + #elif defined(SOKOL_WGPU) + _sg_wgpu_apply_viewport(x, y, w, h, origin_top_left); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_apply_viewport(x, y, w, h, origin_top_left); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_apply_scissor_rect(x, y, w, h, origin_top_left); + #elif defined(SOKOL_METAL) + _sg_mtl_apply_scissor_rect(x, y, w, h, origin_top_left); + #elif defined(SOKOL_D3D11) + _sg_d3d11_apply_scissor_rect(x, y, w, h, origin_top_left); + #elif defined(SOKOL_WGPU) + _sg_wgpu_apply_scissor_rect(x, y, w, h, origin_top_left); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_apply_scissor_rect(x, y, w, h, origin_top_left); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_apply_pipeline(_sg_pipeline_t* pip) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_apply_pipeline(pip); + #elif defined(SOKOL_METAL) + _sg_mtl_apply_pipeline(pip); + #elif defined(SOKOL_D3D11) + _sg_d3d11_apply_pipeline(pip); + #elif defined(SOKOL_WGPU) + _sg_wgpu_apply_pipeline(pip); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_apply_pipeline(pip); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_apply_bindings( + _sg_pipeline_t* pip, + _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, + _sg_buffer_t* ib, int ib_offset, + _sg_image_t** vs_imgs, int num_vs_imgs, + _sg_image_t** fs_imgs, int num_fs_imgs) +{ + #if defined(_SOKOL_ANY_GL) + _sg_gl_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs); + #elif defined(SOKOL_METAL) + _sg_mtl_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs); + #elif defined(SOKOL_D3D11) + _sg_d3d11_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs); + #elif defined(SOKOL_WGPU) + _sg_wgpu_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_apply_uniforms(sg_shader_stage stage_index, int ub_index, const void* data, int num_bytes) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_apply_uniforms(stage_index, ub_index, data, num_bytes); + #elif defined(SOKOL_METAL) + _sg_mtl_apply_uniforms(stage_index, ub_index, data, num_bytes); + #elif defined(SOKOL_D3D11) + _sg_d3d11_apply_uniforms(stage_index, ub_index, data, num_bytes); + #elif defined(SOKOL_WGPU) + _sg_wgpu_apply_uniforms(stage_index, ub_index, data, num_bytes); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_apply_uniforms(stage_index, ub_index, data, num_bytes); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_draw(int base_element, int num_elements, int num_instances) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_draw(base_element, num_elements, num_instances); + #elif defined(SOKOL_METAL) + _sg_mtl_draw(base_element, num_elements, num_instances); + #elif defined(SOKOL_D3D11) + _sg_d3d11_draw(base_element, num_elements, num_instances); + #elif defined(SOKOL_WGPU) + _sg_wgpu_draw(base_element, num_elements, num_instances); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_draw(base_element, num_elements, num_instances); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_commit(void) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_commit(); + #elif defined(SOKOL_METAL) + _sg_mtl_commit(); + #elif defined(SOKOL_D3D11) + _sg_d3d11_commit(); + #elif defined(SOKOL_WGPU) + _sg_wgpu_commit(); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_commit(); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_update_buffer(_sg_buffer_t* buf, const void* data_ptr, uint32_t data_size) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_update_buffer(buf, data_ptr, data_size); + #elif defined(SOKOL_METAL) + _sg_mtl_update_buffer(buf, data_ptr, data_size); + #elif defined(SOKOL_D3D11) + _sg_d3d11_update_buffer(buf, data_ptr, data_size); + #elif defined(SOKOL_WGPU) + _sg_wgpu_update_buffer(buf, data_ptr, data_size); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_update_buffer(buf, data_ptr, data_size); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline uint32_t _sg_append_buffer(_sg_buffer_t* buf, const void* data_ptr, uint32_t data_size, bool new_frame) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_append_buffer(buf, data_ptr, data_size, new_frame); + #elif defined(SOKOL_METAL) + return _sg_mtl_append_buffer(buf, data_ptr, data_size, new_frame); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_append_buffer(buf, data_ptr, data_size, new_frame); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_append_buffer(buf, data_ptr, data_size, new_frame); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_append_buffer(buf, data_ptr, data_size, new_frame); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_update_image(_sg_image_t* img, const sg_image_content* data) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_update_image(img, data); + #elif defined(SOKOL_METAL) + _sg_mtl_update_image(img, data); + #elif defined(SOKOL_D3D11) + _sg_d3d11_update_image(img, data); + #elif defined(SOKOL_WGPU) + _sg_wgpu_update_image(img, data); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_update_image(img, data); + #else + #error("INVALID BACKEND"); + #endif +} + /*== RESOURCE POOLS ==========================================================*/ _SOKOL_PRIVATE void _sg_init_pool(_sg_pool_t* pool, int num) { @@ -10021,8 +12629,11 @@ _SOKOL_PRIVATE bool _sg_validate_buffer_desc(const sg_buffer_desc* desc) { SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_BUFFERDESC_CANARY); SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_BUFFERDESC_CANARY); SOKOL_VALIDATE(desc->size > 0, _SG_VALIDATE_BUFFERDESC_SIZE); - bool ext = (0 != desc->gl_buffers[0]) || (0 != desc->mtl_buffers[0]) || (0 != desc->d3d11_buffer); - if (!ext && (desc->usage == SG_USAGE_IMMUTABLE)) { + bool injected = (0 != desc->gl_buffers[0]) || + (0 != desc->mtl_buffers[0]) || + (0 != desc->d3d11_buffer) || + (0 != desc->wgpu_buffer); + if (!injected && (desc->usage == SG_USAGE_IMMUTABLE)) { SOKOL_VALIDATE(0 != desc->content, _SG_VALIDATE_BUFFERDESC_CONTENT); } else { @@ -10045,7 +12656,10 @@ _SOKOL_PRIVATE bool _sg_validate_image_desc(const sg_image_desc* desc) { SOKOL_VALIDATE(desc->height > 0, _SG_VALIDATE_IMAGEDESC_HEIGHT); const sg_pixel_format fmt = desc->pixel_format; const sg_usage usage = desc->usage; - const bool ext = (0 != desc->gl_textures[0]) || (0 != desc->mtl_textures[0]) || (0 != desc->d3d11_texture); + const bool injected = (0 != desc->gl_textures[0]) || + (0 != desc->mtl_textures[0]) || + (0 != desc->d3d11_texture) || + (0 != desc->wgpu_texture); if (desc->render_target) { SOKOL_ASSERT(((int)fmt >= 0) && ((int)fmt < _SG_PIXELFORMAT_NUM)); SOKOL_VALIDATE(_sg.formats[fmt].render, _SG_VALIDATE_IMAGEDESC_RT_PIXELFORMAT); @@ -10067,7 +12681,7 @@ _SOKOL_PRIVATE bool _sg_validate_image_desc(const sg_image_desc* desc) { const bool valid_nonrt_fmt = !_sg_is_valid_rendertarget_depth_format(fmt); SOKOL_VALIDATE(valid_nonrt_fmt, _SG_VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT); /* FIXME: should use the same "expected size" computation as in _sg_validate_update_image() here */ - if (!ext && (usage == SG_USAGE_IMMUTABLE)) { + if (!injected && (usage == SG_USAGE_IMMUTABLE)) { const int num_faces = desc->type == SG_IMAGETYPE_CUBE ? 6:1; const int num_mips = desc->num_mipmaps; for (int face_index = 0; face_index < num_faces; face_index++) { @@ -10114,6 +12728,10 @@ _SOKOL_PRIVATE bool _sg_validate_shader_desc(const sg_shader_desc* desc) { /* on Metal or D3D11, must provide shader source code or byte code */ SOKOL_VALIDATE((0 != desc->vs.source)||(0 != desc->vs.byte_code), _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE); SOKOL_VALIDATE((0 != desc->fs.source)||(0 != desc->fs.byte_code), _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE); + #elif defined(SOKOL_WGPU) + /* on WGPU byte code must be provided */ + SOKOL_VALIDATE((0 != desc->vs.byte_code), _SG_VALIDATE_SHADERDESC_BYTECODE); + SOKOL_VALIDATE((0 != desc->fs.byte_code), _SG_VALIDATE_SHADERDESC_BYTECODE); #else /* Dummy Backend, don't require source or bytecode */ #endif @@ -10194,8 +12812,6 @@ _SOKOL_PRIVATE bool _sg_validate_pipeline_desc(const sg_pipeline_desc* desc) { SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_PIPELINEDESC_CANARY); SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_PIPELINEDESC_CANARY); SOKOL_VALIDATE(desc->shader.id != SG_INVALID_ID, _SG_VALIDATE_PIPELINEDESC_SHADER); - const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, desc->shader.id); - SOKOL_VALIDATE(shd && shd->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_PIPELINEDESC_SHADER); for (int buf_index = 0; buf_index < SG_MAX_SHADERSTAGE_BUFFERS; buf_index++) { const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[buf_index]; if (l_desc->stride == 0) { @@ -10204,22 +12820,27 @@ _SOKOL_PRIVATE bool _sg_validate_pipeline_desc(const sg_pipeline_desc* desc) { SOKOL_VALIDATE((l_desc->stride & 3) == 0, _SG_VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4); } SOKOL_VALIDATE(desc->layout.attrs[0].format != SG_VERTEXFORMAT_INVALID, _SG_VALIDATE_PIPELINEDESC_NO_ATTRS); - bool attrs_cont = true; - for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { - const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; - if (a_desc->format == SG_VERTEXFORMAT_INVALID) { - attrs_cont = false; - continue; + const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, desc->shader.id); + SOKOL_VALIDATE(shd, _SG_VALIDATE_PIPELINEDESC_SHADER); + if (shd) { + SOKOL_VALIDATE(shd->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_PIPELINEDESC_SHADER); + bool attrs_cont = true; + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; + if (a_desc->format == SG_VERTEXFORMAT_INVALID) { + attrs_cont = false; + continue; + } + SOKOL_VALIDATE(attrs_cont, _SG_VALIDATE_PIPELINEDESC_NO_ATTRS); + SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); + #if defined(SOKOL_GLES2) + /* on GLES2, vertex attribute names must be provided */ + SOKOL_VALIDATE(!_sg_strempty(&shd->gl.attrs[attr_index].name), _SG_VALIDATE_PIPELINEDESC_ATTR_NAME); + #elif defined(SOKOL_D3D11) + /* on D3D11, semantic names (and semantic indices) must be provided */ + SOKOL_VALIDATE(!_sg_strempty(&shd->d3d11.attrs[attr_index].sem_name), _SG_VALIDATE_PIPELINEDESC_ATTR_SEMANTICS); + #endif } - SOKOL_VALIDATE(attrs_cont, _SG_VALIDATE_PIPELINEDESC_NO_ATTRS); - SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); - #if defined(SOKOL_GLES2) - /* on GLES2, vertex attribute names must be provided */ - SOKOL_VALIDATE(!_sg_strempty(&shd->attrs[attr_index].name), _SG_VALIDATE_PIPELINEDESC_ATTR_NAME); - #elif defined(SOKOL_D3D11) - /* on D3D11, semantic names (and semantic indices) must be provided */ - SOKOL_VALIDATE(!_sg_strempty(&shd->attrs[attr_index].sem_name), _SG_VALIDATE_PIPELINEDESC_ATTR_SEMANTICS); - #endif } return SOKOL_VALIDATE_END(); #endif @@ -10247,50 +12868,50 @@ _SOKOL_PRIVATE bool _sg_validate_pass_desc(const sg_pass_desc* desc) { SOKOL_VALIDATE(atts_cont, _SG_VALIDATE_PASSDESC_NO_CONT_COLOR_ATTS); const _sg_image_t* img = _sg_lookup_image(&_sg.pools, att->image.id); SOKOL_VALIDATE(img && img->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_PASSDESC_IMAGE); - SOKOL_VALIDATE(att->mip_level < img->num_mipmaps, _SG_VALIDATE_PASSDESC_MIPLEVEL); - if (img->type == SG_IMAGETYPE_CUBE) { + SOKOL_VALIDATE(att->mip_level < img->cmn.num_mipmaps, _SG_VALIDATE_PASSDESC_MIPLEVEL); + if (img->cmn.type == SG_IMAGETYPE_CUBE) { SOKOL_VALIDATE(att->face < 6, _SG_VALIDATE_PASSDESC_FACE); } - else if (img->type == SG_IMAGETYPE_ARRAY) { - SOKOL_VALIDATE(att->layer < img->depth, _SG_VALIDATE_PASSDESC_LAYER); + else if (img->cmn.type == SG_IMAGETYPE_ARRAY) { + SOKOL_VALIDATE(att->layer < img->cmn.depth, _SG_VALIDATE_PASSDESC_LAYER); } - else if (img->type == SG_IMAGETYPE_3D) { - SOKOL_VALIDATE(att->slice < img->depth, _SG_VALIDATE_PASSDESC_SLICE); + else if (img->cmn.type == SG_IMAGETYPE_3D) { + SOKOL_VALIDATE(att->slice < img->cmn.depth, _SG_VALIDATE_PASSDESC_SLICE); } - SOKOL_VALIDATE(img->render_target, _SG_VALIDATE_PASSDESC_IMAGE_NO_RT); + SOKOL_VALIDATE(img->cmn.render_target, _SG_VALIDATE_PASSDESC_IMAGE_NO_RT); if (att_index == 0) { - color_fmt = img->pixel_format; - width = img->width >> att->mip_level; - height = img->height >> att->mip_level; - sample_count = img->sample_count; + color_fmt = img->cmn.pixel_format; + width = img->cmn.width >> att->mip_level; + height = img->cmn.height >> att->mip_level; + sample_count = img->cmn.sample_count; } else { - SOKOL_VALIDATE(img->pixel_format == color_fmt, _SG_VALIDATE_PASSDESC_COLOR_PIXELFORMATS); - SOKOL_VALIDATE(width == img->width >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); - SOKOL_VALIDATE(height == img->height >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); - SOKOL_VALIDATE(sample_count == img->sample_count, _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS); + SOKOL_VALIDATE(img->cmn.pixel_format == color_fmt, _SG_VALIDATE_PASSDESC_COLOR_PIXELFORMATS); + SOKOL_VALIDATE(width == img->cmn.width >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); + SOKOL_VALIDATE(height == img->cmn.height >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); + SOKOL_VALIDATE(sample_count == img->cmn.sample_count, _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS); } - SOKOL_VALIDATE(_sg_is_valid_rendertarget_color_format(img->pixel_format), _SG_VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT); + SOKOL_VALIDATE(_sg_is_valid_rendertarget_color_format(img->cmn.pixel_format), _SG_VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT); } if (desc->depth_stencil_attachment.image.id != SG_INVALID_ID) { const sg_attachment_desc* att = &desc->depth_stencil_attachment; const _sg_image_t* img = _sg_lookup_image(&_sg.pools, att->image.id); SOKOL_VALIDATE(img && img->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_PASSDESC_IMAGE); - SOKOL_VALIDATE(att->mip_level < img->num_mipmaps, _SG_VALIDATE_PASSDESC_MIPLEVEL); - if (img->type == SG_IMAGETYPE_CUBE) { + SOKOL_VALIDATE(att->mip_level < img->cmn.num_mipmaps, _SG_VALIDATE_PASSDESC_MIPLEVEL); + if (img->cmn.type == SG_IMAGETYPE_CUBE) { SOKOL_VALIDATE(att->face < 6, _SG_VALIDATE_PASSDESC_FACE); } - else if (img->type == SG_IMAGETYPE_ARRAY) { - SOKOL_VALIDATE(att->layer < img->depth, _SG_VALIDATE_PASSDESC_LAYER); + else if (img->cmn.type == SG_IMAGETYPE_ARRAY) { + SOKOL_VALIDATE(att->layer < img->cmn.depth, _SG_VALIDATE_PASSDESC_LAYER); } - else if (img->type == SG_IMAGETYPE_3D) { - SOKOL_VALIDATE(att->slice < img->depth, _SG_VALIDATE_PASSDESC_SLICE); + else if (img->cmn.type == SG_IMAGETYPE_3D) { + SOKOL_VALIDATE(att->slice < img->cmn.depth, _SG_VALIDATE_PASSDESC_SLICE); } - SOKOL_VALIDATE(img->render_target, _SG_VALIDATE_PASSDESC_IMAGE_NO_RT); - SOKOL_VALIDATE(width == img->width >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); - SOKOL_VALIDATE(height == img->height >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); - SOKOL_VALIDATE(sample_count == img->sample_count, _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS); - SOKOL_VALIDATE(_sg_is_valid_rendertarget_depth_format(img->pixel_format), _SG_VALIDATE_PASSDESC_DEPTH_INV_PIXELFORMAT); + SOKOL_VALIDATE(img->cmn.render_target, _SG_VALIDATE_PASSDESC_IMAGE_NO_RT); + SOKOL_VALIDATE(width == img->cmn.width >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); + SOKOL_VALIDATE(height == img->cmn.height >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); + SOKOL_VALIDATE(sample_count == img->cmn.sample_count, _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS); + SOKOL_VALIDATE(_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format), _SG_VALIDATE_PASSDESC_DEPTH_INV_PIXELFORMAT); } return SOKOL_VALIDATE_END(); #endif @@ -10303,17 +12924,20 @@ _SOKOL_PRIVATE bool _sg_validate_begin_pass(_sg_pass_t* pass) { #else SOKOL_VALIDATE_BEGIN(); SOKOL_VALIDATE(pass->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_BEGINPASS_PASS); + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - const _sg_attachment_t* att = &pass->color_atts[i]; - if (att->image) { - SOKOL_VALIDATE(att->image->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_BEGINPASS_IMAGE); - SOKOL_VALIDATE(att->image->slot.id == att->image_id.id, _SG_VALIDATE_BEGINPASS_IMAGE); + const _sg_attachment_t* att = &pass->cmn.color_atts[i]; + const _sg_image_t* img = _sg_pass_color_image(pass, i); + if (img) { + SOKOL_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_BEGINPASS_IMAGE); + SOKOL_VALIDATE(img->slot.id == att->image_id.id, _SG_VALIDATE_BEGINPASS_IMAGE); } } - if (pass->ds_att.image) { - const _sg_attachment_t* att = &pass->ds_att; - SOKOL_VALIDATE(att->image->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_BEGINPASS_IMAGE); - SOKOL_VALIDATE(att->image->slot.id == att->image_id.id, _SG_VALIDATE_BEGINPASS_IMAGE); + const _sg_image_t* ds_img = _sg_pass_ds_image(pass); + if (ds_img) { + const _sg_attachment_t* att = &pass->cmn.ds_att; + SOKOL_VALIDATE(ds_img->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_BEGINPASS_IMAGE); + SOKOL_VALIDATE(ds_img->slot.id == att->image_id.id, _SG_VALIDATE_BEGINPASS_IMAGE); } return SOKOL_VALIDATE_END(); #endif @@ -10335,28 +12959,30 @@ _SOKOL_PRIVATE bool _sg_validate_apply_pipeline(sg_pipeline pip_id) { SOKOL_VALIDATE(pip->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_APIP_PIPELINE_VALID); /* the pipeline's shader must be alive and valid */ SOKOL_ASSERT(pip->shader); - SOKOL_VALIDATE(pip->shader->slot.id == pip->shader_id.id, _SG_VALIDATE_APIP_SHADER_EXISTS); + SOKOL_VALIDATE(pip->shader->slot.id == pip->cmn.shader_id.id, _SG_VALIDATE_APIP_SHADER_EXISTS); SOKOL_VALIDATE(pip->shader->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_APIP_SHADER_VALID); /* check that pipeline attributes match current pass attributes */ const _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, _sg.cur_pass.id); if (pass) { /* an offscreen pass */ - SOKOL_VALIDATE(pip->color_attachment_count == pass->num_color_atts, _SG_VALIDATE_APIP_ATT_COUNT); - SOKOL_VALIDATE(pip->color_format == pass->color_atts[0].image->pixel_format, _SG_VALIDATE_APIP_COLOR_FORMAT); - SOKOL_VALIDATE(pip->sample_count == pass->color_atts[0].image->sample_count, _SG_VALIDATE_APIP_SAMPLE_COUNT); - if (pass->ds_att.image) { - SOKOL_VALIDATE(pip->depth_format == pass->ds_att.image->pixel_format, _SG_VALIDATE_APIP_DEPTH_FORMAT); + const _sg_image_t* att_img = _sg_pass_color_image(pass, 0); + SOKOL_VALIDATE(pip->cmn.color_attachment_count == pass->cmn.num_color_atts, _SG_VALIDATE_APIP_ATT_COUNT); + SOKOL_VALIDATE(pip->cmn.color_format == att_img->cmn.pixel_format, _SG_VALIDATE_APIP_COLOR_FORMAT); + SOKOL_VALIDATE(pip->cmn.sample_count == att_img->cmn.sample_count, _SG_VALIDATE_APIP_SAMPLE_COUNT); + const _sg_image_t* att_dsimg = _sg_pass_ds_image(pass); + if (att_dsimg) { + SOKOL_VALIDATE(pip->cmn.depth_format == att_dsimg->cmn.pixel_format, _SG_VALIDATE_APIP_DEPTH_FORMAT); } else { - SOKOL_VALIDATE(pip->depth_format == SG_PIXELFORMAT_NONE, _SG_VALIDATE_APIP_DEPTH_FORMAT); + SOKOL_VALIDATE(pip->cmn.depth_format == SG_PIXELFORMAT_NONE, _SG_VALIDATE_APIP_DEPTH_FORMAT); } } else { /* default pass */ - SOKOL_VALIDATE(pip->color_attachment_count == 1, _SG_VALIDATE_APIP_ATT_COUNT); - SOKOL_VALIDATE(pip->color_format == _sg_default_rendertarget_colorformat(), _SG_VALIDATE_APIP_COLOR_FORMAT); - SOKOL_VALIDATE(pip->depth_format == _sg_default_rendertarget_depthformat(), _SG_VALIDATE_APIP_DEPTH_FORMAT); - /* FIXME: hmm, we don't know if the default framebuffer is multisampled here */ + SOKOL_VALIDATE(pip->cmn.color_attachment_count == 1, _SG_VALIDATE_APIP_ATT_COUNT); + SOKOL_VALIDATE(pip->cmn.color_format == _sg.desc.context.color_format, _SG_VALIDATE_APIP_COLOR_FORMAT); + SOKOL_VALIDATE(pip->cmn.depth_format == _sg.desc.context.depth_format, _SG_VALIDATE_APIP_DEPTH_FORMAT); + SOKOL_VALIDATE(pip->cmn.sample_count == _sg.desc.context.sample_count, _SG_VALIDATE_APIP_SAMPLE_COUNT); } return SOKOL_VALIDATE_END(); #endif @@ -10377,28 +13003,28 @@ _SOKOL_PRIVATE bool _sg_validate_apply_bindings(const sg_bindings* bindings) { return SOKOL_VALIDATE_END(); } SOKOL_VALIDATE(pip->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_ABND_PIPELINE_VALID); - SOKOL_ASSERT(pip->shader); + SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); /* has expected vertex buffers, and vertex buffers still exist */ for (int i = 0; i < SG_MAX_SHADERSTAGE_BUFFERS; i++) { if (bindings->vertex_buffers[i].id != SG_INVALID_ID) { - SOKOL_VALIDATE(pip->vertex_layout_valid[i], _SG_VALIDATE_ABND_VBS); + SOKOL_VALIDATE(pip->cmn.vertex_layout_valid[i], _SG_VALIDATE_ABND_VBS); /* buffers in vertex-buffer-slots must be of type SG_BUFFERTYPE_VERTEXBUFFER */ const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, bindings->vertex_buffers[i].id); SOKOL_VALIDATE(buf != 0, _SG_VALIDATE_ABND_VB_EXISTS); if (buf && buf->slot.state == SG_RESOURCESTATE_VALID) { - SOKOL_VALIDATE(SG_BUFFERTYPE_VERTEXBUFFER == buf->type, _SG_VALIDATE_ABND_VB_TYPE); - SOKOL_VALIDATE(!buf->append_overflow, _SG_VALIDATE_ABND_VB_OVERFLOW); + SOKOL_VALIDATE(SG_BUFFERTYPE_VERTEXBUFFER == buf->cmn.type, _SG_VALIDATE_ABND_VB_TYPE); + SOKOL_VALIDATE(!buf->cmn.append_overflow, _SG_VALIDATE_ABND_VB_OVERFLOW); } } else { /* vertex buffer provided in a slot which has no vertex layout in pipeline */ - SOKOL_VALIDATE(!pip->vertex_layout_valid[i], _SG_VALIDATE_ABND_VBS); + SOKOL_VALIDATE(!pip->cmn.vertex_layout_valid[i], _SG_VALIDATE_ABND_VBS); } } /* index buffer expected or not, and index buffer still exists */ - if (pip->index_type == SG_INDEXTYPE_NONE) { + if (pip->cmn.index_type == SG_INDEXTYPE_NONE) { /* pipeline defines non-indexed rendering, but index buffer provided */ SOKOL_VALIDATE(bindings->index_buffer.id == SG_INVALID_ID, _SG_VALIDATE_ABND_IB); } @@ -10411,20 +13037,20 @@ _SOKOL_PRIVATE bool _sg_validate_apply_bindings(const sg_bindings* bindings) { const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, bindings->index_buffer.id); SOKOL_VALIDATE(buf != 0, _SG_VALIDATE_ABND_IB_EXISTS); if (buf && buf->slot.state == SG_RESOURCESTATE_VALID) { - SOKOL_VALIDATE(SG_BUFFERTYPE_INDEXBUFFER == buf->type, _SG_VALIDATE_ABND_IB_TYPE); - SOKOL_VALIDATE(!buf->append_overflow, _SG_VALIDATE_ABND_IB_OVERFLOW); + SOKOL_VALIDATE(SG_BUFFERTYPE_INDEXBUFFER == buf->cmn.type, _SG_VALIDATE_ABND_IB_TYPE); + SOKOL_VALIDATE(!buf->cmn.append_overflow, _SG_VALIDATE_ABND_IB_OVERFLOW); } } /* has expected vertex shader images */ for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { - _sg_shader_stage_t* stage = &pip->shader->stage[SG_SHADERSTAGE_VS]; + _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_VS]; if (bindings->vs_images[i].id != SG_INVALID_ID) { SOKOL_VALIDATE(i < stage->num_images, _SG_VALIDATE_ABND_VS_IMGS); const _sg_image_t* img = _sg_lookup_image(&_sg.pools, bindings->vs_images[i].id); SOKOL_VALIDATE(img != 0, _SG_VALIDATE_ABND_VS_IMG_EXISTS); if (img && img->slot.state == SG_RESOURCESTATE_VALID) { - SOKOL_VALIDATE(img->type == stage->images[i].type, _SG_VALIDATE_ABND_VS_IMG_TYPES); + SOKOL_VALIDATE(img->cmn.type == stage->images[i].type, _SG_VALIDATE_ABND_VS_IMG_TYPES); } } else { @@ -10434,13 +13060,13 @@ _SOKOL_PRIVATE bool _sg_validate_apply_bindings(const sg_bindings* bindings) { /* has expected fragment shader images */ for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { - _sg_shader_stage_t* stage = &pip->shader->stage[SG_SHADERSTAGE_FS]; + _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_FS]; if (bindings->fs_images[i].id != SG_INVALID_ID) { SOKOL_VALIDATE(i < stage->num_images, _SG_VALIDATE_ABND_FS_IMGS); const _sg_image_t* img = _sg_lookup_image(&_sg.pools, bindings->fs_images[i].id); SOKOL_VALIDATE(img != 0, _SG_VALIDATE_ABND_FS_IMG_EXISTS); if (img && img->slot.state == SG_RESOURCESTATE_VALID) { - SOKOL_VALIDATE(img->type == stage->images[i].type, _SG_VALIDATE_ABND_FS_IMG_TYPES); + SOKOL_VALIDATE(img->cmn.type == stage->images[i].type, _SG_VALIDATE_ABND_FS_IMG_TYPES); } } else { @@ -10465,10 +13091,10 @@ _SOKOL_PRIVATE bool _sg_validate_apply_uniforms(sg_shader_stage stage_index, int SOKOL_VALIDATE(_sg.cur_pipeline.id != SG_INVALID_ID, _SG_VALIDATE_AUB_NO_PIPELINE); const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, _sg.cur_pipeline.id); SOKOL_ASSERT(pip && (pip->slot.id == _sg.cur_pipeline.id)); - SOKOL_ASSERT(pip->shader && (pip->shader->slot.id == pip->shader_id.id)); + SOKOL_ASSERT(pip->shader && (pip->shader->slot.id == pip->cmn.shader_id.id)); /* check that there is a uniform block at 'stage' and 'ub_index' */ - const _sg_shader_stage_t* stage = &pip->shader->stage[stage_index]; + const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[stage_index]; SOKOL_VALIDATE(ub_index < stage->num_uniform_blocks, _SG_VALIDATE_AUB_NO_UB_AT_SLOT); /* check that the provided data size doesn't exceed the uniform block size */ @@ -10487,10 +13113,10 @@ _SOKOL_PRIVATE bool _sg_validate_update_buffer(const _sg_buffer_t* buf, const vo #else SOKOL_ASSERT(buf && data); SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(buf->usage != SG_USAGE_IMMUTABLE, _SG_VALIDATE_UPDATEBUF_USAGE); - SOKOL_VALIDATE(buf->size >= size, _SG_VALIDATE_UPDATEBUF_SIZE); - SOKOL_VALIDATE(buf->update_frame_index != _sg.frame_index, _SG_VALIDATE_UPDATEBUF_ONCE); - SOKOL_VALIDATE(buf->append_frame_index != _sg.frame_index, _SG_VALIDATE_UPDATEBUF_APPEND); + SOKOL_VALIDATE(buf->cmn.usage != SG_USAGE_IMMUTABLE, _SG_VALIDATE_UPDATEBUF_USAGE); + SOKOL_VALIDATE(buf->cmn.size >= size, _SG_VALIDATE_UPDATEBUF_SIZE); + SOKOL_VALIDATE(buf->cmn.update_frame_index != _sg.frame_index, _SG_VALIDATE_UPDATEBUF_ONCE); + SOKOL_VALIDATE(buf->cmn.append_frame_index != _sg.frame_index, _SG_VALIDATE_UPDATEBUF_APPEND); return SOKOL_VALIDATE_END(); #endif } @@ -10504,9 +13130,9 @@ _SOKOL_PRIVATE bool _sg_validate_append_buffer(const _sg_buffer_t* buf, const vo #else SOKOL_ASSERT(buf && data); SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(buf->usage != SG_USAGE_IMMUTABLE, _SG_VALIDATE_APPENDBUF_USAGE); - SOKOL_VALIDATE(buf->size >= (buf->append_pos+size), _SG_VALIDATE_APPENDBUF_SIZE); - SOKOL_VALIDATE(buf->update_frame_index != _sg.frame_index, _SG_VALIDATE_APPENDBUF_UPDATE); + SOKOL_VALIDATE(buf->cmn.usage != SG_USAGE_IMMUTABLE, _SG_VALIDATE_APPENDBUF_USAGE); + SOKOL_VALIDATE(buf->cmn.size >= (buf->cmn.append_pos+size), _SG_VALIDATE_APPENDBUF_SIZE); + SOKOL_VALIDATE(buf->cmn.update_frame_index != _sg.frame_index, _SG_VALIDATE_APPENDBUF_UPDATE); return SOKOL_VALIDATE_END(); #endif } @@ -10519,18 +13145,18 @@ _SOKOL_PRIVATE bool _sg_validate_update_image(const _sg_image_t* img, const sg_i #else SOKOL_ASSERT(img && data); SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(img->usage != SG_USAGE_IMMUTABLE, _SG_VALIDATE_UPDIMG_USAGE); - SOKOL_VALIDATE(img->upd_frame_index != _sg.frame_index, _SG_VALIDATE_UPDIMG_ONCE); - SOKOL_VALIDATE(!_sg_is_compressed_pixel_format(img->pixel_format), _SG_VALIDATE_UPDIMG_COMPRESSED); - const int num_faces = (img->type == SG_IMAGETYPE_CUBE) ? 6 : 1; - const int num_mips = img->num_mipmaps; + SOKOL_VALIDATE(img->cmn.usage != SG_USAGE_IMMUTABLE, _SG_VALIDATE_UPDIMG_USAGE); + SOKOL_VALIDATE(img->cmn.upd_frame_index != _sg.frame_index, _SG_VALIDATE_UPDIMG_ONCE); + SOKOL_VALIDATE(!_sg_is_compressed_pixel_format(img->cmn.pixel_format), _SG_VALIDATE_UPDIMG_COMPRESSED); + const int num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6 : 1; + const int num_mips = img->cmn.num_mipmaps; for (int face_index = 0; face_index < num_faces; face_index++) { for (int mip_index = 0; mip_index < num_mips; mip_index++) { SOKOL_VALIDATE(0 != data->subimage[face_index][mip_index].ptr, _SG_VALIDATE_UPDIMG_NOTENOUGHDATA); - const int mip_width = _sg_max(img->width >> mip_index, 1); - const int mip_height = _sg_max(img->height >> mip_index, 1); - const int bytes_per_slice = _sg_surface_pitch(img->pixel_format, mip_width, mip_height); - const int expected_size = bytes_per_slice * img->depth; + const int mip_width = _sg_max(img->cmn.width >> mip_index, 1); + const int mip_height = _sg_max(img->cmn.height >> mip_index, 1); + const int bytes_per_slice = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1); + const int expected_size = bytes_per_slice * img->cmn.depth; SOKOL_VALIDATE(data->subimage[face_index][mip_index].size <= expected_size, _SG_VALIDATE_UPDIMG_SIZE); } } @@ -10553,12 +13179,13 @@ _SOKOL_PRIVATE sg_image_desc _sg_image_desc_defaults(const sg_image_desc* desc) def.num_mipmaps = _sg_def(def.num_mipmaps, 1); def.usage = _sg_def(def.usage, SG_USAGE_IMMUTABLE); if (desc->render_target) { - def.pixel_format = _sg_def(def.pixel_format, _sg_default_rendertarget_colorformat()); + def.pixel_format = _sg_def(def.pixel_format, _sg.desc.context.color_format); + def.sample_count = _sg_def(def.sample_count, _sg.desc.context.sample_count); } else { def.pixel_format = _sg_def(def.pixel_format, SG_PIXELFORMAT_RGBA8); + def.sample_count = _sg_def(def.sample_count, 1); } - def.sample_count = _sg_def(def.sample_count, 1); def.min_filter = _sg_def(def.min_filter, SG_FILTER_NEAREST); def.mag_filter = _sg_def(def.mag_filter, SG_FILTER_NEAREST); def.wrap_u = _sg_def(def.wrap_u, SG_WRAP_REPEAT); @@ -10579,6 +13206,14 @@ _SOKOL_PRIVATE sg_shader_desc _sg_shader_desc_defaults(const sg_shader_desc* des def.vs.entry = _sg_def(def.vs.entry, "main"); def.fs.entry = _sg_def(def.fs.entry, "main"); #endif + #if defined(SOKOL_D3D11) + if (def.vs.source) { + def.vs.d3d11_target = _sg_def(def.vs.d3d11_target, "vs_4_0"); + } + if (def.fs.source) { + def.fs.d3d11_target = _sg_def(def.fs.d3d11_target, "ps_4_0"); + } + #endif for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS)? &def.vs : &def.fs; for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { @@ -10594,6 +13229,13 @@ _SOKOL_PRIVATE sg_shader_desc _sg_shader_desc_defaults(const sg_shader_desc* des u_desc->array_count = _sg_def(u_desc->array_count, 1); } } + for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { + sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; + if (img_desc->type == _SG_IMAGETYPE_DEFAULT) { + break; + } + img_desc->sampler_type = _sg_def(img_desc->sampler_type, SG_SAMPLERTYPE_FLOAT); + } } return def; } @@ -10627,12 +13269,12 @@ _SOKOL_PRIVATE sg_pipeline_desc _sg_pipeline_desc_defaults(const sg_pipeline_des def.blend.color_write_mask = (uint8_t) _sg_def((sg_color_mask)def.blend.color_write_mask, SG_COLORMASK_RGBA); } def.blend.color_attachment_count = _sg_def(def.blend.color_attachment_count, 1); - def.blend.color_format = _sg_def(def.blend.color_format, _sg_default_rendertarget_colorformat()); - def.blend.depth_format = _sg_def(def.blend.depth_format, _sg_default_rendertarget_depthformat()); + def.blend.color_format = _sg_def(def.blend.color_format, _sg.desc.context.color_format); + def.blend.depth_format = _sg_def(def.blend.depth_format, _sg.desc.context.depth_format); def.rasterizer.cull_mode = _sg_def(def.rasterizer.cull_mode, SG_CULLMODE_NONE); def.rasterizer.face_winding = _sg_def(def.rasterizer.face_winding, SG_FACEWINDING_CW); - def.rasterizer.sample_count = _sg_def(def.rasterizer.sample_count, 1); + def.rasterizer.sample_count = _sg_def(def.rasterizer.sample_count, _sg.desc.context.sample_count); for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { sg_vertex_attr_desc* a_desc = &def.layout.attrs[attr_index]; @@ -10798,8 +13440,12 @@ _SOKOL_PRIVATE void _sg_init_pipeline(sg_pipeline pip_id, const sg_pipeline_desc pip->slot.ctx_id = _sg.active_context.id; if (_sg_validate_pipeline_desc(desc)) { _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, desc->shader.id); - SOKOL_ASSERT(shd && shd->slot.state == SG_RESOURCESTATE_VALID); - pip->slot.state = _sg_create_pipeline(pip, shd, desc); + if (shd && (shd->slot.state == SG_RESOURCESTATE_VALID)) { + pip->slot.state = _sg_create_pipeline(pip, shd, desc); + } + else { + pip->slot.state = SG_RESOURCESTATE_FAILED; + } } else { pip->slot.state = SG_RESOURCESTATE_FAILED; @@ -10818,6 +13464,7 @@ _SOKOL_PRIVATE void _sg_init_pass(sg_pass pass_id, const sg_pass_desc* desc) { for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { if (desc->color_attachments[i].image.id) { att_imgs[i] = _sg_lookup_image(&_sg.pools, desc->color_attachments[i].image.id); + /* FIXME: this shouldn't be an assertion, but result in a SG_RESOURCESTATE_FAILED pass */ SOKOL_ASSERT(att_imgs[i] && att_imgs[i]->slot.state == SG_RESOURCESTATE_VALID); } else { @@ -10827,6 +13474,7 @@ _SOKOL_PRIVATE void _sg_init_pass(sg_pass pass_id, const sg_pass_desc* desc) { const int ds_att_index = SG_MAX_COLOR_ATTACHMENTS; if (desc->depth_stencil_attachment.image.id) { att_imgs[ds_att_index] = _sg_lookup_image(&_sg.pools, desc->depth_stencil_attachment.image.id); + /* FIXME: this shouldn't be an assertion, but result in a SG_RESOURCESTATE_FAILED pass */ SOKOL_ASSERT(att_imgs[ds_att_index] && att_imgs[ds_att_index]->slot.state == SG_RESOURCESTATE_VALID); } else { @@ -10841,21 +13489,46 @@ _SOKOL_PRIVATE void _sg_init_pass(sg_pass pass_id, const sg_pass_desc* desc) { } /*== PUBLIC API FUNCTIONS ====================================================*/ + +#if defined(SOKOL_METAL) + // this is ARC compatible + #if defined(__cplusplus) + #define _SG_CLEAR(type, item) { item = { }; } + #else + #define _SG_CLEAR(type, item) { item = (type) { 0 }; } + #endif +#else + #define _SG_CLEAR(type, item) { memset(&item, 0, sizeof(item)); } +#endif + SOKOL_API_IMPL void sg_setup(const sg_desc* desc) { SOKOL_ASSERT(desc); SOKOL_ASSERT((desc->_start_canary == 0) && (desc->_end_canary == 0)); - memset(&_sg, 0, sizeof(_sg)); + _SG_CLEAR(_sg_state_t, _sg); _sg.desc = *desc; - /* replace zero-init items with their default values */ + /* replace zero-init items with their default values + NOTE: on WebGPU, the default color pixel format MUST be provided, + cannot be a default compile-time constant. + */ + #if defined(SOKOL_WGPU) + SOKOL_ASSERT(SG_PIXELFORMAT_NONE != _sg.desc.context.color_format); + #elif defined(SOKOL_METAL) || defined(SOKOL_D3D11) + _sg.desc.context.color_format = _sg_def(_sg.desc.context.color_format, SG_PIXELFORMAT_BGRA8); + #else + _sg.desc.context.color_format = _sg_def(_sg.desc.context.color_format, SG_PIXELFORMAT_RGBA8); + #endif + _sg.desc.context.depth_format = _sg_def(_sg.desc.context.depth_format, SG_PIXELFORMAT_DEPTH_STENCIL); + _sg.desc.context.sample_count = _sg_def(_sg.desc.context.sample_count, 1); _sg.desc.buffer_pool_size = _sg_def(_sg.desc.buffer_pool_size, _SG_DEFAULT_BUFFER_POOL_SIZE); _sg.desc.image_pool_size = _sg_def(_sg.desc.image_pool_size, _SG_DEFAULT_IMAGE_POOL_SIZE); _sg.desc.shader_pool_size = _sg_def(_sg.desc.shader_pool_size, _SG_DEFAULT_SHADER_POOL_SIZE); _sg.desc.pipeline_pool_size = _sg_def(_sg.desc.pipeline_pool_size, _SG_DEFAULT_PIPELINE_POOL_SIZE); _sg.desc.pass_pool_size = _sg_def(_sg.desc.pass_pool_size, _SG_DEFAULT_PASS_POOL_SIZE); _sg.desc.context_pool_size = _sg_def(_sg.desc.context_pool_size, _SG_DEFAULT_CONTEXT_POOL_SIZE); - _sg.desc.mtl_global_uniform_buffer_size = _sg_def(_sg.desc.mtl_global_uniform_buffer_size, _SG_MTL_DEFAULT_UB_SIZE); - _sg.desc.mtl_sampler_cache_size = _sg_def(_sg.desc.mtl_sampler_cache_size, _SG_MTL_DEFAULT_SAMPLER_CACHE_CAPACITY); + _sg.desc.uniform_buffer_size = _sg_def(_sg.desc.uniform_buffer_size, _SG_DEFAULT_UB_SIZE); + _sg.desc.staging_buffer_size = _sg_def(_sg.desc.staging_buffer_size, _SG_DEFAULT_STAGING_SIZE); + _sg.desc.sampler_cache_size = _sg_def(_sg.desc.sampler_cache_size, _SG_DEFAULT_SAMPLER_CACHE_CAPACITY); _sg_setup_pools(&_sg.pools, &_sg.desc); _sg.frame_index = 1; @@ -10955,6 +13628,7 @@ SOKOL_API_IMPL void sg_activate_context(sg_context ctx_id) { SOKOL_API_IMPL sg_trace_hooks sg_install_trace_hooks(const sg_trace_hooks* trace_hooks) { SOKOL_ASSERT(_sg.valid); SOKOL_ASSERT(trace_hooks); + _SOKOL_UNUSED(trace_hooks); #if defined(SOKOL_TRACE_HOOKS) sg_trace_hooks old_hooks = _sg.hooks; _sg.hooks = *trace_hooks; @@ -11311,8 +13985,10 @@ SOKOL_API_IMPL void sg_begin_pass(sg_pass pass_id, const sg_pass_action* pass_ac _sg.pass_valid = true; sg_pass_action pa; _sg_resolve_default_pass_action(pass_action, &pa); - const int w = pass->color_atts[0].image->width; - const int h = pass->color_atts[0].image->height; + const _sg_image_t* img = _sg_pass_color_image(pass, 0); + SOKOL_ASSERT(img); + const int w = img->cmn.width; + const int h = img->cmn.height; _sg_begin_pass(pass, &pa, w, h); _SG_TRACE_ARGS(begin_pass, pass_id, pass_action); } @@ -11358,7 +14034,7 @@ SOKOL_API_IMPL void sg_apply_pipeline(sg_pipeline pip_id) { _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); SOKOL_ASSERT(pip); _sg.next_draw_valid = (SG_RESOURCESTATE_VALID == pip->slot.state); - SOKOL_ASSERT(pip->shader && (pip->shader->slot.id == pip->shader_id.id)); + SOKOL_ASSERT(pip->shader && (pip->shader->slot.id == pip->cmn.shader_id.id)); _sg_apply_pipeline(pip); _SG_TRACE_ARGS(apply_pipeline, pip_id); } @@ -11384,7 +14060,7 @@ SOKOL_API_IMPL void sg_apply_bindings(const sg_bindings* bindings) { vbs[i] = _sg_lookup_buffer(&_sg.pools, bindings->vertex_buffers[i].id); SOKOL_ASSERT(vbs[i]); _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == vbs[i]->slot.state); - _sg.next_draw_valid &= !vbs[i]->append_overflow; + _sg.next_draw_valid &= !vbs[i]->cmn.append_overflow; } else { break; @@ -11396,7 +14072,7 @@ SOKOL_API_IMPL void sg_apply_bindings(const sg_bindings* bindings) { ib = _sg_lookup_buffer(&_sg.pools, bindings->index_buffer.id); SOKOL_ASSERT(ib); _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == ib->slot.state); - _sg.next_draw_valid &= !ib->append_overflow; + _sg.next_draw_valid &= !ib->cmn.append_overflow; } _sg_image_t* vs_imgs[SG_MAX_SHADERSTAGE_IMAGES] = { 0 }; @@ -11510,13 +14186,13 @@ SOKOL_API_IMPL void sg_update_buffer(sg_buffer buf_id, const void* data, int num _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); if ((num_bytes > 0) && buf && (buf->slot.state == SG_RESOURCESTATE_VALID)) { if (_sg_validate_update_buffer(buf, data, num_bytes)) { - SOKOL_ASSERT(num_bytes <= buf->size); + SOKOL_ASSERT(num_bytes <= buf->cmn.size); /* only one update allowed per buffer and frame */ - SOKOL_ASSERT(buf->update_frame_index != _sg.frame_index); + SOKOL_ASSERT(buf->cmn.update_frame_index != _sg.frame_index); /* update and append on same buffer in same frame not allowed */ - SOKOL_ASSERT(buf->append_frame_index != _sg.frame_index); - _sg_update_buffer(buf, data, num_bytes); - buf->update_frame_index = _sg.frame_index; + SOKOL_ASSERT(buf->cmn.append_frame_index != _sg.frame_index); + _sg_update_buffer(buf, data, (uint32_t)num_bytes); + buf->cmn.update_frame_index = _sg.frame_index; } } _SG_TRACE_ARGS(update_buffer, buf_id, data, num_bytes); @@ -11528,22 +14204,22 @@ SOKOL_API_IMPL int sg_append_buffer(sg_buffer buf_id, const void* data, int num_ int result; if (buf) { /* rewind append cursor in a new frame */ - if (buf->append_frame_index != _sg.frame_index) { - buf->append_pos = 0; - buf->append_overflow = false; + if (buf->cmn.append_frame_index != _sg.frame_index) { + buf->cmn.append_pos = 0; + buf->cmn.append_overflow = false; } - if ((buf->append_pos + num_bytes) > buf->size) { - buf->append_overflow = true; + if ((buf->cmn.append_pos + _sg_roundup(num_bytes, 4)) > buf->cmn.size) { + buf->cmn.append_overflow = true; } - const int start_pos = buf->append_pos; + const int start_pos = buf->cmn.append_pos; if (buf->slot.state == SG_RESOURCESTATE_VALID) { if (_sg_validate_append_buffer(buf, data, num_bytes)) { - if (!buf->append_overflow && (num_bytes > 0)) { + if (!buf->cmn.append_overflow && (num_bytes > 0)) { /* update and append on same buffer in same frame not allowed */ - SOKOL_ASSERT(buf->update_frame_index != _sg.frame_index); - _sg_append_buffer(buf, data, num_bytes, buf->append_frame_index != _sg.frame_index); - buf->append_pos += num_bytes; - buf->append_frame_index = _sg.frame_index; + SOKOL_ASSERT(buf->cmn.update_frame_index != _sg.frame_index); + uint32_t copied_num_bytes = _sg_append_buffer(buf, data, (uint32_t)num_bytes, buf->cmn.append_frame_index != _sg.frame_index); + buf->cmn.append_pos += copied_num_bytes; + buf->cmn.append_frame_index = _sg.frame_index; } } } @@ -11560,7 +14236,7 @@ SOKOL_API_IMPL int sg_append_buffer(sg_buffer buf_id, const void* data, int num_ SOKOL_API_IMPL bool sg_query_buffer_overflow(sg_buffer buf_id) { SOKOL_ASSERT(_sg.valid); _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); - bool result = buf ? buf->append_overflow : false; + bool result = buf ? buf->cmn.append_overflow : false; return result; } @@ -11569,9 +14245,9 @@ SOKOL_API_IMPL void sg_update_image(sg_image img_id, const sg_image_content* dat _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); if (img && img->slot.state == SG_RESOURCESTATE_VALID) { if (_sg_validate_update_image(img, data)) { - SOKOL_ASSERT(img->upd_frame_index != _sg.frame_index); + SOKOL_ASSERT(img->cmn.upd_frame_index != _sg.frame_index); _sg_update_image(img, data); - img->upd_frame_index = _sg.frame_index; + img->cmn.upd_frame_index = _sg.frame_index; } } _SG_TRACE_ARGS(update_image, img_id, data); @@ -11580,6 +14256,7 @@ SOKOL_API_IMPL void sg_update_image(sg_image img_id, const sg_image_content* dat SOKOL_API_IMPL void sg_push_debug_group(const char* name) { SOKOL_ASSERT(_sg.valid); SOKOL_ASSERT(name); + _SOKOL_UNUSED(name); _SG_TRACE_ARGS(push_debug_group, name); } @@ -11597,16 +14274,16 @@ SOKOL_API_IMPL sg_buffer_info sg_query_buffer_info(sg_buffer buf_id) { info.slot.state = buf->slot.state; info.slot.res_id = buf->slot.id; info.slot.ctx_id = buf->slot.ctx_id; - info.update_frame_index = buf->update_frame_index; - info.append_frame_index = buf->append_frame_index; - info.append_pos = buf->append_pos; - info.append_overflow = buf->append_overflow; + info.update_frame_index = buf->cmn.update_frame_index; + info.append_frame_index = buf->cmn.append_frame_index; + info.append_pos = buf->cmn.append_pos; + info.append_overflow = buf->cmn.append_overflow; #if defined(SOKOL_D3D11) info.num_slots = 1; info.active_slot = 0; #else - info.num_slots = buf->num_slots; - info.active_slot = buf->active_slot; + info.num_slots = buf->cmn.num_slots; + info.active_slot = buf->cmn.active_slot; #endif } return info; @@ -11625,9 +14302,11 @@ SOKOL_API_IMPL sg_image_info sg_query_image_info(sg_image img_id) { info.num_slots = 1; info.active_slot = 0; #else - info.num_slots = img->num_slots; - info.active_slot = img->active_slot; + info.num_slots = img->cmn.num_slots; + info.active_slot = img->cmn.active_slot; #endif + info.width = img->cmn.width; + info.height = img->cmn.height; } return info; } @@ -11696,6 +14375,19 @@ SOKOL_API_IMPL sg_pass_desc sg_query_pass_defaults(const sg_pass_desc* desc) { return _sg_pass_desc_defaults(desc); } +SOKOL_API_IMPL const void* sg_mtl_render_command_encoder(void) { + #if defined(SOKOL_METAL) + if (nil != _sg.mtl.cmd_encoder) { + return (__bridge const void*) _sg.mtl.cmd_encoder; + } + else { + return 0; + } + #else + return 0; + #endif +} + #ifdef _MSC_VER #pragma warning(pop) #endif diff --git a/thirdparty/sokol/util/sokol_fontstash.h b/thirdparty/sokol/util/sokol_fontstash.h index fe348fedea..c923661b16 100755 --- a/thirdparty/sokol/util/sokol_fontstash.h +++ b/thirdparty/sokol/util/sokol_fontstash.h @@ -222,409 +222,1354 @@ SOKOL_API_DECL uint32_t sfons_rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a); #ifndef SOKOL_UNREACHABLE #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) #endif +#ifndef _SOKOL_UNUSED + #define _SOKOL_UNUSED(x) (void)(x) +#endif #if defined(SOKOL_GLCORE33) -static const char* _sfons_vs_src = - "#version 330\n" - "uniform mat4 mvp;\n" - "uniform mat4 tm;\n" - "in vec4 position;\n" - "in vec2 texcoord0;\n" - "in vec4 color0;\n" - "out vec4 uv;\n" - "out vec4 color;\n" - "void main() {\n" - " gl_Position = mvp * position;\n" - " uv = tm * vec4(texcoord0, 0.0, 1.0);\n" - " color = color0;\n" - "}\n"; -static const char* _sfons_fs_src = - "#version 330\n" - "uniform sampler2D tex;\n" - "in vec4 uv;\n" - "in vec4 color;\n" - "out vec4 frag_color;\n" - "void main() {\n" - " frag_color = vec4(1.0, 1.0, 1.0, texture(tex, uv.xy).r) * color;\n" - "}\n"; -#elif defined(SOKOL_GLES2) || defined(SOKOL_GLES3) -static const char* _sfons_vs_src = - "uniform mat4 mvp;\n" - "uniform mat4 tm;\n" - "attribute vec4 position;\n" - "attribute vec2 texcoord0;\n" - "attribute vec4 color0;\n" - "varying vec4 uv;\n" - "varying vec4 color;\n" - "void main() {\n" - " gl_Position = mvp * position;\n" - " uv = tm * vec4(texcoord0, 0.0, 1.0);\n" - " color = color0;\n" - "}\n"; -static const char* _sfons_fs_src = - "precision mediump float;\n" - "uniform sampler2D tex;\n" - "varying vec4 uv;\n" - "varying vec4 color;\n" - "void main() {\n" - " gl_FragColor = vec4(1.0, 1.0, 1.0, texture2D(tex, uv.xy).r) * color;\n" - "}\n"; -#elif defined(SOKOL_METAL) -static const char* _sfons_vs_src = - "#include \n" - "using namespace metal;\n" - "struct params_t {\n" - " float4x4 mvp;\n" - " float4x4 tm;\n" - "};\n" - "struct vs_in {\n" - " float4 pos [[attribute(0)]];\n" - " float2 uv [[attribute(1)]];\n" - " float4 color [[attribute(2)]];\n" - "};\n" - "struct vs_out {\n" - " float4 pos [[position]];\n" - " float4 uv;\n" - " float4 color;\n" - "};\n" - "vertex vs_out _main(vs_in in [[stage_in]], constant params_t& params [[buffer(0)]]) {\n" - " vs_out out;\n" - " out.pos = params.mvp * in.pos;\n" - " out.uv = params.tm * float4(in.uv, 0.0, 1.0);\n" - " out.color = in.color;\n" - " return out;\n" - "}\n"; -static const char* _sfons_fs_src = - "#include \n" - "using namespace metal;\n" - "struct fs_in {\n" - " float4 uv;\n" - " float4 color;\n" - "};\n" - "fragment float4 _main(fs_in in [[stage_in]], texture2d tex [[texture(0)]], sampler smp [[sampler(0)]]) {\n" - " return float4(1.0, 1.0, 1.0, tex.sample(smp, in.uv.xy).r) * in.color;\n" - "}\n"; -#elif defined(SOKOL_D3D11) /* - Shader blobs for D3D11, compiled with: + Embedded source code compiled with: - fxc.exe /T vs_4_0 /Fh vs.h /Gec /O3 vs.hlsl - fxc.exe /T ps_4_0 /Fh fs.h /Gec /O3 fs.hlsl + sokol-shdc -i sfons.glsl -o sfons.h -l glsl330:glsl100:hlsl4:metal_macos:metal_ios:metal_sim:wgpu -b + + (not that for Metal and D3D11 byte code, sokol-shdc must be run + on macOS and Windows) + + @vs vs + uniform vs_params { + uniform mat4 mvp; + uniform mat4 tm; + }; + in vec4 position; + in vec2 texcoord0; + in vec4 color0; + out vec4 uv; + out vec4 color; + void main() { + gl_Position = mvp * position; + uv = tm * vec4(texcoord0, 0.0, 1.0); + color = color0; + } + @end + + @fs fs + uniform sampler2D tex; + in vec4 uv; + in vec4 color; + out vec4 frag_color; + void main() { + frag_color = vec4(1.0, 1.0, 1.0, texture(tex, uv.xy).r) * color; + } + @end + + @program sfontstash vs fs +*/ +static const char _sfons_vs_source_glsl330[415] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x33,0x30,0x0a,0x0a,0x75,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65,0x63,0x34,0x20,0x76,0x73,0x5f,0x70,0x61, + 0x72,0x61,0x6d,0x73,0x5b,0x38,0x5d,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28, + 0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x69,0x6e, + 0x20,0x76,0x65,0x63,0x34,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a, + 0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x75,0x76,0x3b,0x0a,0x6c,0x61,0x79, + 0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31, + 0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f, + 0x72,0x64,0x30,0x3b,0x0a,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f, + 0x6c,0x6f,0x72,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61, + 0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x32,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63, + 0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20, + 0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f, + 0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x6d,0x61,0x74,0x34,0x28, + 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2c,0x20,0x76,0x73, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x73,0x5b,0x32,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72, + 0x61,0x6d,0x73,0x5b,0x33,0x5d,0x29,0x20,0x2a,0x20,0x70,0x6f,0x73,0x69,0x74,0x69, + 0x6f,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x6d,0x61,0x74, + 0x34,0x28,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x34,0x5d,0x2c,0x20, + 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x35,0x5d,0x2c,0x20,0x76,0x73, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x36,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x73,0x5b,0x37,0x5d,0x29,0x20,0x2a,0x20,0x76,0x65,0x63,0x34, + 0x28,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c, + 0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +static const char _sfons_fs_source_glsl330[195] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x33,0x30,0x0a,0x0a,0x75,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x32,0x44,0x20, + 0x74,0x65,0x78,0x3b,0x0a,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63, + 0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x76, + 0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a, + 0x69,0x6e,0x20,0x76,0x65,0x63,0x34,0x20,0x75,0x76,0x3b,0x0a,0x69,0x6e,0x20,0x76, + 0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64, + 0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72, + 0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x76,0x65,0x63,0x34,0x28, + 0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x2c,0x20,0x74, + 0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x74,0x65,0x78,0x2c,0x20,0x75,0x76,0x2e,0x78, + 0x79,0x29,0x2e,0x78,0x29,0x20,0x2a,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x7d, + 0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_GLES2) || defined(SOKOL_GLES3) +static const char _sfons_vs_source_glsl100[381] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x31,0x30,0x30,0x0a,0x0a,0x75,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65,0x63,0x34,0x20,0x76,0x73,0x5f,0x70,0x61, + 0x72,0x61,0x6d,0x73,0x5b,0x38,0x5d,0x3b,0x0a,0x61,0x74,0x74,0x72,0x69,0x62,0x75, + 0x74,0x65,0x20,0x76,0x65,0x63,0x34,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, + 0x3b,0x0a,0x76,0x61,0x72,0x79,0x69,0x6e,0x67,0x20,0x76,0x65,0x63,0x34,0x20,0x75, + 0x76,0x3b,0x0a,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x20,0x76,0x65,0x63, + 0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a,0x76,0x61,0x72, + 0x79,0x69,0x6e,0x67,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b, + 0x0a,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x20,0x76,0x65,0x63,0x34,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61, + 0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f, + 0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x6d,0x61,0x74,0x34,0x28,0x76,0x73, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72, + 0x61,0x6d,0x73,0x5b,0x32,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x73,0x5b,0x33,0x5d,0x29,0x20,0x2a,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, + 0x3b,0x0a,0x20,0x20,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x6d,0x61,0x74,0x34,0x28, + 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x34,0x5d,0x2c,0x20,0x76,0x73, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x35,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x73,0x5b,0x36,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72, + 0x61,0x6d,0x73,0x5b,0x37,0x5d,0x29,0x20,0x2a,0x20,0x76,0x65,0x63,0x34,0x28,0x74, + 0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x31, + 0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +static const char _sfons_fs_source_glsl100[233] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x31,0x30,0x30,0x0a,0x70,0x72,0x65, + 0x63,0x69,0x73,0x69,0x6f,0x6e,0x20,0x6d,0x65,0x64,0x69,0x75,0x6d,0x70,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x3b,0x0a,0x70,0x72,0x65,0x63,0x69,0x73,0x69,0x6f,0x6e,0x20, + 0x68,0x69,0x67,0x68,0x70,0x20,0x69,0x6e,0x74,0x3b,0x0a,0x0a,0x75,0x6e,0x69,0x66, + 0x6f,0x72,0x6d,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65, + 0x72,0x32,0x44,0x20,0x74,0x65,0x78,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x79,0x69,0x6e, + 0x67,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x34,0x20,0x75,0x76,0x3b, + 0x0a,0x76,0x61,0x72,0x79,0x69,0x6e,0x67,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76, + 0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64, + 0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c, + 0x5f,0x46,0x72,0x61,0x67,0x44,0x61,0x74,0x61,0x5b,0x30,0x5d,0x20,0x3d,0x20,0x76, + 0x65,0x63,0x34,0x28,0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e, + 0x30,0x2c,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x32,0x44,0x28,0x74,0x65,0x78, + 0x2c,0x20,0x75,0x76,0x2e,0x78,0x79,0x29,0x2e,0x78,0x29,0x20,0x2a,0x20,0x63,0x6f, + 0x6c,0x6f,0x72,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_METAL) +static const uint8_t _sfons_vs_bytecode_metal_macos[3360] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x20,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x10,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0x54,0xec,0xec,0xa8,0x23,0x83,0x8d, + 0xc7,0xcf,0x6e,0x67,0x94,0x95,0x54,0x94,0xeb,0x98,0x15,0x2a,0x2c,0x54,0xa0,0x4b, + 0x24,0x6f,0x5e,0xed,0x6e,0x76,0x45,0xb7,0x5e,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x45,0x4e,0x44,0x54,0x37,0x00,0x00, + 0x00,0x56,0x41,0x54,0x54,0x22,0x00,0x03,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x00,0x00,0x80,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x01,0x80, + 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x00,0x02,0x80,0x56,0x41,0x54,0x59,0x05,0x00,0x03, + 0x00,0x06,0x04,0x06,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54, + 0xde,0xc0,0x17,0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0xf0,0x0b,0x00,0x00, + 0xff,0xff,0xff,0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0xf9,0x02,0x00,0x00, + 0x0b,0x82,0x20,0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91, + 0x41,0xc8,0x04,0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19, + 0x1e,0x04,0x8b,0x62,0x80,0x14,0x45,0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14, + 0x38,0x08,0x18,0x49,0x0a,0x32,0x44,0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80, + 0x0c,0x19,0x21,0x72,0x24,0x07,0xc8,0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0, + 0x01,0x00,0x00,0x00,0x51,0x18,0x00,0x00,0x81,0x00,0x00,0x00,0x1b,0xc8,0x25,0xf8, + 0xff,0xff,0xff,0xff,0x01,0x90,0x80,0x8a,0x18,0x87,0x77,0x90,0x07,0x79,0x28,0x87, + 0x71,0xa0,0x07,0x76,0xc8,0x87,0x36,0x90,0x87,0x77,0xa8,0x07,0x77,0x20,0x87,0x72, + 0x20,0x87,0x36,0x20,0x87,0x74,0xb0,0x87,0x74,0x20,0x87,0x72,0x68,0x83,0x79,0x88, + 0x07,0x79,0xa0,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07, + 0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c,0x00,0x82,0x1c,0xd2,0x61,0x1e,0xc2,0x41, + 0x1c,0xd8,0xa1,0x1c,0xda,0x80,0x1e,0xc2,0x21,0x1d,0xd8,0xa1,0x0d,0xc6,0x21,0x1c, + 0xd8,0x81,0x1d,0xe6,0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07,0x80,0x60,0x87, + 0x72,0x98,0x87,0x79,0x68,0x03,0x78,0x90,0x87,0x72,0x18,0x87,0x74,0x98,0x87,0x72, + 0x68,0x03,0x73,0x80,0x87,0x76,0x08,0x07,0x72,0x00,0xcc,0x21,0x1c,0xd8,0x61,0x1e, + 0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0xc0,0x1c,0xe4,0x21,0x1c,0xda,0xa1,0x1c,0xda, + 0x00,0x1e,0xde,0x21,0x1d,0xdc,0x81,0x1e,0xca,0x41,0x1e,0xda,0xa0,0x1c,0xd8,0x21, + 0x1d,0xda,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x06,0x77,0x78,0x87,0x36,0x30, + 0x07,0x79,0x08,0x87,0x76,0x28,0x87,0x36,0x80,0x87,0x77,0x48,0x07,0x77,0xa0,0x87, + 0x72,0x90,0x87,0x36,0x28,0x07,0x76,0x48,0x87,0x76,0x68,0x03,0x77,0x78,0x07,0x77, + 0x68,0x03,0x76,0x28,0x87,0x70,0x30,0x07,0x80,0x70,0x87,0x77,0x68,0x83,0x74,0x70, + 0x07,0x73,0x98,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07, + 0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0x40,0x1d,0xea,0xa1, + 0x1d,0xe0,0xa1,0x0d,0xe8,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x1e,0x00,0x73,0x08, + 0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x77,0x78,0x87,0x36,0x70,0x87,0x70,0x70,0x87, + 0x79,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41, + 0x1e,0xea,0xa1,0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xe6,0x21,0x1d,0xce,0xc1,0x1d, + 0xca,0x81,0x1c,0xda,0x40,0x1f,0xca,0x41,0x1e,0xde,0x61,0x1e,0xda,0xc0,0x1c,0xe0, + 0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80, + 0x70,0x87,0x77,0x68,0x03,0x7a,0x90,0x87,0x70,0x80,0x07,0x78,0x48,0x07,0x77,0x38, + 0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00, + 0x62,0x1e,0xe8,0x21,0x1c,0xc6,0x61,0x1d,0xda,0x00,0x1e,0xe4,0xe1,0x1d,0xe8,0xa1, + 0x1c,0xc6,0x81,0x1e,0xde,0x41,0x1e,0xda,0x40,0x1c,0xea,0xc1,0x1c,0xcc,0xa1,0x1c, + 0xe4,0xa1,0x0d,0xe6,0x21,0x1d,0xf4,0xa1,0x1c,0x00,0x3c,0x00,0x88,0x7a,0x70,0x87, + 0x79,0x08,0x07,0x73,0x28,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a, + 0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xea,0x61,0x1e,0xca,0xa1,0x0d, + 0xe6,0xe1,0x1d,0xcc,0x81,0x1e,0xda,0xc0,0x1c,0xd8,0xe1,0x1d,0xc2,0x81,0x1e,0x00, + 0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x36,0x18,0x02,0x01,0x2c,0x40,0x05,0x00, + 0x49,0x18,0x00,0x00,0x01,0x00,0x00,0x00,0x13,0x84,0x40,0x00,0x89,0x20,0x00,0x00, + 0x23,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85,0x04,0x93,0x22,0xa4,0x84, + 0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a,0x8c,0x0b,0x84,0xa4,0x4c, + 0x10,0x44,0x33,0x00,0xc3,0x08,0x04,0x70,0x90,0x34,0x45,0x94,0x30,0xf9,0x0c,0x80, + 0x34,0xf4,0xef,0x50,0x13,0x1a,0x42,0x08,0xc3,0x08,0x02,0x90,0x04,0x61,0x26,0x6a, + 0x1e,0xe8,0x41,0x1e,0xea,0x61,0x1c,0xe8,0xc1,0x0d,0xda,0xa1,0x1c,0xe8,0x21,0x1c, + 0xd8,0x41,0x0f,0xf4,0xa0,0x1d,0xc2,0x81,0x1e,0xe4,0x21,0x1d,0xf0,0x01,0x05,0xe4, + 0x20,0x69,0x8a,0x28,0x61,0xf2,0x2b,0xe9,0x7f,0x80,0x08,0x60,0x24,0x24,0x94,0x32, + 0x88,0x60,0x08,0xa5,0x10,0x61,0x84,0x43,0x68,0x20,0x60,0x8e,0x00,0x0c,0x72,0x60, + 0xcd,0x11,0x80,0xc2,0x20,0x42,0x20,0x0c,0x23,0x10,0xcb,0x08,0x00,0x00,0x00,0x00, + 0x13,0xb2,0x70,0x48,0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60, + 0x87,0x72,0x68,0x83,0x76,0x08,0x87,0x71,0x78,0x87,0x79,0xc0,0x87,0x38,0x80,0x03, + 0x37,0x88,0x83,0x38,0x70,0x03,0x38,0xd8,0x70,0x1b,0xe5,0xd0,0x06,0xf0,0xa0,0x07, + 0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x90,0x0e,0x71, + 0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0,0x06,0xe9,0x80,0x07,0x7a,0x80,0x07,0x7a,0x80, + 0x07,0x6d,0x90,0x0e,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07, + 0x6d,0x90,0x0e,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d, + 0x90,0x0e,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60, + 0x0e,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e, + 0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x71, + 0x60,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x60,0x0f,0x72,0x40, + 0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x73,0x20,0x07, + 0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x74,0x80,0x07,0x7a, + 0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x76,0x40,0x07,0x7a,0x60, + 0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x79,0x60,0x07,0x7a,0x10,0x07, + 0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x6d,0x60,0x0f,0x71,0x20,0x07,0x78, + 0xa0,0x07,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10, + 0x07,0x79,0x20,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07, + 0x6d,0x60,0x0f,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72, + 0x50,0x07,0x76,0xd0,0x06,0xf6,0x50,0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20, + 0x07,0x7a,0x50,0x07,0x71,0x20,0x07,0x6d,0x60,0x0f,0x71,0x00,0x07,0x72,0x40,0x07, + 0x7a,0x10,0x07,0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x6d, + 0xe0,0x0e,0x78,0xa0,0x07,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0x30,0x84,0x49,0x00, + 0x00,0x08,0x00,0x00,0x00,0x00,0x00,0xc8,0x02,0x01,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x32,0x1e,0x98,0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a, + 0x25,0x30,0x02,0x50,0x04,0x05,0x18,0x50,0x80,0x02,0x85,0x50,0x10,0x65,0x50,0x20, + 0xd4,0x46,0x00,0x88,0x8d,0x35,0x28,0x0f,0x01,0x00,0x00,0x00,0x79,0x18,0x00,0x00, + 0x19,0x01,0x00,0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9, + 0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x32,0x28,0x00,0xb3,0x50,0xb9,0x1b,0x43,0x0b,0x93, + 0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62,0x2c,0x81,0x22,0x2c,0x06,0xd9,0x20,0x08,0x0e, + 0x8e,0xad,0x0c,0x84,0x89,0xc9,0xaa,0x09,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d, + 0x24,0x07,0x46,0xc6,0x25,0x86,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac, + 0x25,0x07,0x46,0xc6,0x25,0x86,0xc6,0x25,0x27,0x65,0x88,0xa0,0x10,0x43,0x8c,0x25, + 0x58,0x8e,0x45,0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x51,0x8e,0x25,0x58,0x82, + 0x45,0xe0,0x16,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56, + 0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x50, + 0x12,0x72,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61, + 0x73,0x74,0x5f,0x6d,0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x04, + 0x65,0x61,0x19,0x84,0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98, + 0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1, + 0x7d,0x91,0xa5,0xcd,0x85,0x89,0xb1,0x95,0x0d,0x11,0x94,0x86,0x61,0x10,0x96,0x26, + 0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0xe2,0x16,0x46,0x97,0x66,0x57,0xf6, + 0x45,0xf6,0x56,0x27,0xc6,0x56,0xf6,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44, + 0x50,0x1e,0x92,0x41,0x58,0x9a,0x9c,0xcb,0xd8,0x5b,0x1b,0x5c,0x1a,0x5b,0x99,0x8b, + 0x5b,0x18,0x5d,0x9a,0x5d,0xd9,0x17,0xdb,0x9b,0xdb,0xd9,0x17,0xdb,0x9b,0xdb,0xd9, + 0x17,0x59,0xda,0x5c,0x98,0x18,0x5b,0xd9,0x10,0x41,0x89,0x78,0x06,0x61,0x69,0x72, + 0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x6e,0x61,0x74,0x69,0x76,0x65,0x5f, + 0x77,0x69,0x64,0x65,0x5f,0x76,0x65,0x63,0x74,0x6f,0x72,0x73,0x5f,0x64,0x69,0x73, + 0x61,0x62,0x6c,0x65,0x43,0x04,0x65,0x62,0x14,0x96,0x26,0xe7,0x62,0x57,0x26,0x47, + 0x57,0x86,0xf7,0xf5,0x56,0x47,0x07,0x57,0x47,0xc7,0xa5,0x6e,0xae,0x4c,0x0e,0x85, + 0xed,0x6d,0xcc,0x0d,0x26,0x85,0x51,0x58,0x9a,0x9c,0x4b,0x98,0xdc,0xd9,0x17,0x5d, + 0x1e,0x5c,0xd9,0x97,0x5b,0x58,0x5b,0x19,0x0d,0x33,0xb6,0xb7,0x30,0x3a,0x1a,0x32, + 0x61,0x69,0x72,0x2e,0x61,0x72,0x67,0x5f,0x6e,0x61,0x6d,0x65,0x14,0xea,0xec,0x86, + 0x30,0x4a,0xa5,0x58,0xca,0xa5,0x60,0x4a,0xa6,0x68,0x5c,0xea,0xe6,0xca,0xe4,0x50, + 0xd8,0xde,0xc6,0xdc,0x62,0x52,0x58,0x8c,0xbd,0xb1,0xbd,0xc9,0x0d,0x61,0x94,0x4a, + 0xe1,0x94,0x4b,0xc1,0x94,0x4c,0xe9,0xc8,0x84,0xa5,0xc9,0xb9,0xc0,0xbd,0xcd,0xa5, + 0xd1,0xa5,0xbd,0xb9,0x71,0x39,0x63,0xfb,0x82,0x7a,0x9b,0x4b,0xa3,0x4b,0x7b,0x73, + 0x1b,0xa2,0x28,0x9f,0x72,0x29,0x98,0x92,0x29,0x60,0x30,0xc4,0x50,0x36,0xc5,0x53, + 0xc2,0x80,0x50,0x58,0x9a,0x9c,0x8b,0x5d,0x99,0x1c,0x5d,0x19,0xde,0x57,0x9a,0x1b, + 0x5c,0x1d,0x1d,0xa5,0xb0,0x34,0x39,0x17,0xb6,0xb7,0xb1,0x30,0xba,0xb4,0x37,0xb7, + 0xaf,0x34,0x37,0xb2,0x32,0x3c,0x66,0x67,0x65,0x6e,0x65,0x72,0x61,0x74,0x65,0x64, + 0x28,0x38,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x44,0x76,0x34,0x5f,0x66,0x29, + 0x44,0xe0,0xde,0xe6,0xd2,0xe8,0xd2,0xde,0xdc,0x86,0x50,0x8b,0xa0,0x8c,0x81,0x42, + 0x06,0x8b,0xb0,0x04,0x4a,0x19,0x28,0x97,0x82,0x29,0x99,0x62,0x06,0xd4,0xce,0xca, + 0xdc,0xca,0xe4,0xc2,0xe8,0xca,0xc8,0x50,0x72,0xe8,0xca,0xf0,0xc6,0xde,0xde,0xe4, + 0xc8,0x60,0x88,0xec,0x64,0xbe,0xcc,0x52,0x68,0x98,0xb1,0xbd,0x85,0xd1,0xc9,0x30, + 0xa1,0x2b,0xc3,0x1b,0x7b,0x7b,0x93,0x23,0x83,0x19,0x42,0x2d,0x81,0x32,0x06,0x0a, + 0x19,0x2c,0xc1,0x12,0x28,0x68,0xa0,0x5c,0x4a,0x1a,0x28,0x99,0xa2,0x06,0xbc,0xce, + 0xca,0xdc,0xca,0xe4,0xc2,0xe8,0xca,0xc8,0x50,0x6c,0xc6,0xde,0xd8,0xde,0xe4,0x60, + 0x88,0xec,0x68,0xbe,0xcc,0x52,0x68,0x8c,0xbd,0xb1,0xbd,0xc9,0xc1,0x0c,0xa1,0x96, + 0x41,0x19,0x03,0x85,0x0c,0x96,0x61,0x09,0x14,0x36,0x50,0x2e,0x05,0x53,0x32,0xa5, + 0x0d,0xa8,0x84,0xa5,0xc9,0xb9,0x88,0xd5,0x99,0x99,0x95,0xc9,0xf1,0x09,0x4b,0x93, + 0x73,0x11,0xab,0x33,0x33,0x2b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x23,0x12,0x96,0x26, + 0xe7,0x22,0x57,0x16,0x46,0x46,0x2a,0x2c,0x4d,0xce,0x65,0x8e,0x4e,0xae,0x6e,0x8c, + 0xee,0x8b,0x2e,0x0f,0xae,0xec,0x2b,0xcd,0xcd,0xec,0x8d,0x88,0x19,0xdb,0x5b,0x18, + 0x1d,0x0d,0x1e,0x0d,0x87,0x36,0x3b,0x38,0x0a,0x74,0x6d,0x43,0xa8,0x45,0x58,0x88, + 0x45,0x50,0xe6,0x40,0xa1,0x83,0x85,0x58,0x88,0x45,0x50,0xe6,0x40,0xa9,0x03,0x46, + 0x61,0x69,0x72,0x2e,0x61,0x72,0x67,0x5f,0x74,0x79,0x70,0x65,0x5f,0x73,0x69,0x7a, + 0x65,0xbc,0xc2,0xd2,0xe4,0x5c,0xc2,0xe4,0xce,0xbe,0xe8,0xf2,0xe0,0xca,0xbe,0xc2, + 0xd8,0xd2,0xce,0xdc,0xbe,0xe6,0xd2,0xf4,0xca,0x98,0xd8,0xcd,0x7d,0xc1,0x85,0xc9, + 0x85,0xb5,0xcd,0x71,0xf8,0x92,0x81,0x19,0x42,0x06,0x0b,0xa2,0xbc,0x81,0x02,0x07, + 0x4b,0xa1,0x90,0xc1,0x22,0x2c,0x81,0x12,0x07,0x8a,0x1c,0x28,0x76,0xa0,0xdc,0xc1, + 0x52,0x28,0x78,0xb0,0x24,0xca,0xa5,0xe4,0x81,0x92,0x29,0x7a,0x30,0x04,0x51,0xce, + 0x40,0x59,0x03,0xc5,0x0d,0x94,0x3d,0x18,0x62,0x24,0x80,0x22,0x06,0x0a,0x1f,0xf0, + 0x79,0x6b,0x73,0x4b,0x83,0x7b,0xa3,0x2b,0x73,0xa3,0x03,0x19,0x43,0x0b,0x93,0xe3, + 0x33,0x95,0xd6,0x06,0xc7,0x56,0x06,0x32,0xb4,0xb2,0x02,0x42,0x25,0x14,0x14,0x34, + 0x44,0x50,0xfe,0x60,0x88,0xa1,0xf8,0x81,0x02,0x0a,0x8d,0x32,0xc4,0x50,0x42,0x41, + 0x09,0x85,0x46,0x19,0x11,0xb1,0x03,0x3b,0xd8,0x43,0x3b,0xb8,0x41,0x3b,0xbc,0x03, + 0x39,0xd4,0x03,0x3b,0x94,0x83,0x1b,0x98,0x03,0x3b,0x84,0xc3,0x39,0xcc,0xc3,0x14, + 0x21,0x18,0x46,0x28,0xec,0xc0,0x0e,0xf6,0xd0,0x0e,0x6e,0x90,0x0e,0xe4,0x50,0x0e, + 0xee,0x40,0x0f,0x53,0x82,0x62,0xc4,0x12,0x0e,0xe9,0x20,0x0f,0x6e,0x60,0x0f,0xe5, + 0x20,0x0f,0xf3,0x90,0x0e,0xef,0xe0,0x0e,0x53,0x02,0x63,0x04,0x15,0x0e,0xe9,0x20, + 0x0f,0x6e,0xc0,0x0e,0xe1,0xe0,0x0e,0xe7,0x50,0x0f,0xe1,0x70,0x0e,0xe5,0xf0,0x0b, + 0xf6,0x50,0x0e,0xf2,0x30,0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x25,0x40,0x46,0x4c,0xe1, + 0x90,0x0e,0xf2,0xe0,0x06,0xe3,0xf0,0x0e,0xed,0x00,0x0f,0xe9,0xc0,0x0e,0xe5,0xf0, + 0x0b,0xef,0x00,0x0f,0xf4,0x90,0x0e,0xef,0xe0,0x0e,0xf3,0x30,0xc5,0x50,0x18,0x07, + 0x92,0xa8,0x11,0x4a,0x38,0xa4,0x83,0x3c,0xb8,0x81,0x3d,0x94,0x83,0x3c,0xd0,0x43, + 0x39,0xe0,0xc3,0x94,0xa0,0x0f,0x00,0x00,0x79,0x18,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3, + 0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10, + 0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30, + 0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03, + 0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07, + 0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e, + 0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d, + 0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b, + 0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76, + 0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90, + 0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87, + 0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e, + 0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c, + 0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca, + 0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8, + 0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82, + 0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83, + 0x74,0x68,0x07,0x78,0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f, + 0xee,0x00,0x0f,0xf2,0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec, + 0x50,0x0e,0x33,0x20,0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc, + 0x81,0x1e,0xdc,0xe0,0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0, + 0x43,0x3a,0x9c,0x83,0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60, + 0x87,0x77,0x78,0x07,0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e, + 0x6a,0x1e,0xca,0x61,0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1, + 0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43, + 0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c, + 0xc5,0x0a,0x87,0x79,0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72, + 0x00,0x00,0x00,0x00,0x71,0x20,0x00,0x00,0x02,0x00,0x00,0x00,0x06,0x50,0x30,0x00, + 0xd2,0xd0,0x00,0x00,0x61,0x20,0x00,0x00,0x3d,0x00,0x00,0x00,0x13,0x04,0x41,0x2c, + 0x10,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0xf4,0xc6,0x22,0x86,0x61,0x18,0xc6,0x22, + 0x04,0x41,0x10,0xc6,0x22,0x82,0x20,0x08,0xa8,0x95,0x40,0x19,0x14,0x01,0xbd,0x11, + 0x00,0x1a,0x33,0x00,0x24,0x66,0x00,0x28,0xcc,0x00,0x00,0x00,0xe3,0x15,0x0b,0x84, + 0x61,0x10,0x05,0x65,0x90,0x01,0x1a,0x0c,0x13,0x02,0xf9,0x8c,0x57,0x3c,0x14,0xc7, + 0x2d,0x14,0x94,0x41,0x06,0xea,0x70,0x4c,0x08,0xe4,0x63,0x41,0x01,0x9f,0xf1,0x0a, + 0x2a,0x0b,0x83,0x30,0x70,0x28,0x28,0x83,0x0c,0x19,0x43,0x99,0x10,0xc8,0xc7,0x8a, + 0x00,0x3e,0xe3,0x15,0x99,0x67,0x06,0x66,0x40,0x51,0x50,0x06,0x19,0xbc,0x48,0x33, + 0x21,0x90,0x8f,0x15,0x01,0x7c,0xc6,0x2b,0xbc,0x31,0x60,0x83,0x35,0x18,0x03,0x0a, + 0xca,0x20,0x83,0x18,0x60,0x99,0x09,0x81,0x7c,0xc6,0x2b,0xc4,0xe0,0x0c,0xe0,0xe0, + 0x0d,0x3c,0x0a,0xca,0x20,0x83,0x19,0x70,0x61,0x60,0x42,0x20,0x1f,0x0b,0x0a,0xf8, + 0x8c,0x57,0x9c,0x01,0x1b,0xd4,0x01,0x1d,0x88,0x01,0x05,0xc5,0x86,0x00,0x3e,0xb3, + 0x0d,0x61,0x10,0x00,0xb3,0x0d,0x01,0x1b,0x04,0xb3,0x0d,0xc1,0x23,0x64,0x10,0x10, + 0x03,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x5b,0x86,0x20,0x10,0x85,0x2d,0x43,0x11, + 0x88,0xc2,0x96,0x41,0x09,0x44,0x61,0xcb,0xf0,0x04,0xa2,0xb0,0x65,0xa0,0x02,0x51, + 0xd8,0x32,0x60,0x81,0x28,0x6c,0x19,0xba,0x40,0x14,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sfons_fs_bytecode_metal_macos[2941] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x7d,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd5,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xa0,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0xfd,0x3a,0x06,0x83,0x09,0xe9,0xe7, + 0xd2,0xb5,0xf9,0x1e,0x86,0x99,0xb0,0x0c,0x8c,0xbe,0xe0,0xd3,0x2d,0x9b,0x8c,0xe9, + 0x2a,0x92,0xa3,0xeb,0x0b,0xf7,0x98,0x18,0x30,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x45,0x4e,0x44,0x54,0x04,0x00,0x00, + 0x00,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17, + 0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x84,0x0a,0x00,0x00,0xff,0xff,0xff, + 0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0x9e,0x02,0x00,0x00,0x0b,0x82,0x20, + 0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04, + 0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b, + 0x62,0x80,0x14,0x45,0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18, + 0x49,0x0a,0x32,0x44,0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21, + 0x72,0x24,0x07,0xc8,0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00, + 0x00,0x51,0x18,0x00,0x00,0x89,0x00,0x00,0x00,0x1b,0xcc,0x25,0xf8,0xff,0xff,0xff, + 0xff,0x01,0x60,0x00,0x09,0xa8,0x88,0x71,0x78,0x07,0x79,0x90,0x87,0x72,0x18,0x07, + 0x7a,0x60,0x87,0x7c,0x68,0x03,0x79,0x78,0x87,0x7a,0x70,0x07,0x72,0x28,0x07,0x72, + 0x68,0x03,0x72,0x48,0x07,0x7b,0x48,0x07,0x72,0x28,0x87,0x36,0x98,0x87,0x78,0x90, + 0x07,0x7a,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xcc, + 0x21,0x1c,0xd8,0x61,0x1e,0xca,0x01,0x20,0xc8,0x21,0x1d,0xe6,0x21,0x1c,0xc4,0x81, + 0x1d,0xca,0xa1,0x0d,0xe8,0x21,0x1c,0xd2,0x81,0x1d,0xda,0x60,0x1c,0xc2,0x81,0x1d, + 0xd8,0x61,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x76,0x28,0x87, + 0x79,0x98,0x87,0x36,0x80,0x07,0x79,0x28,0x87,0x71,0x48,0x87,0x79,0x28,0x87,0x36, + 0x30,0x07,0x78,0x68,0x87,0x70,0x20,0x07,0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c, + 0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xcc,0x41,0x1e,0xc2,0xa1,0x1d,0xca,0xa1,0x0d,0xe0, + 0xe1,0x1d,0xd2,0xc1,0x1d,0xe8,0xa1,0x1c,0xe4,0xa1,0x0d,0xca,0x81,0x1d,0xd2,0xa1, + 0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x60,0x70,0x87,0x77,0x68,0x03,0x73,0x90, + 0x87,0x70,0x68,0x87,0x72,0x68,0x03,0x78,0x78,0x87,0x74,0x70,0x07,0x7a,0x28,0x07, + 0x79,0x68,0x83,0x72,0x60,0x87,0x74,0x68,0x87,0x36,0x70,0x87,0x77,0x70,0x87,0x36, + 0x60,0x87,0x72,0x08,0x07,0x73,0x00,0x08,0x77,0x78,0x87,0x36,0x48,0x07,0x77,0x30, + 0x87,0x79,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8, + 0x41,0x1e,0xea,0xa1,0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xd4,0xa1,0x1e,0xda,0x01, + 0x1e,0xda,0x80,0x1e,0xc2,0x41,0x1c,0xd8,0xa1,0x1c,0xe6,0x01,0x30,0x87,0x70,0x60, + 0x87,0x79,0x28,0x07,0x80,0x70,0x87,0x77,0x68,0x03,0x77,0x08,0x07,0x77,0x98,0x87, + 0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1, + 0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0x60,0x1e,0xd2,0xe1,0x1c,0xdc,0xa1,0x1c, + 0xc8,0xa1,0x0d,0xf4,0xa1,0x1c,0xe4,0xe1,0x1d,0xe6,0xa1,0x0d,0xcc,0x01,0x1e,0xda, + 0xa0,0x1d,0xc2,0x81,0x1e,0xd0,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x08,0x77, + 0x78,0x87,0x36,0xa0,0x07,0x79,0x08,0x07,0x78,0x80,0x87,0x74,0x70,0x87,0x73,0x68, + 0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xe6, + 0x81,0x1e,0xc2,0x61,0x1c,0xd6,0xa1,0x0d,0xe0,0x41,0x1e,0xde,0x81,0x1e,0xca,0x61, + 0x1c,0xe8,0xe1,0x1d,0xe4,0xa1,0x0d,0xc4,0xa1,0x1e,0xcc,0xc1,0x1c,0xca,0x41,0x1e, + 0xda,0x60,0x1e,0xd2,0x41,0x1f,0xca,0x01,0xc0,0x03,0x80,0xa8,0x07,0x77,0x98,0x87, + 0x70,0x30,0x87,0x72,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74, + 0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0xa2,0x1e,0xe6,0xa1,0x1c,0xda,0x60,0x1e, + 0xde,0xc1,0x1c,0xe8,0xa1,0x0d,0xcc,0x81,0x1d,0xde,0x21,0x1c,0xe8,0x01,0x30,0x87, + 0x70,0x60,0x87,0x79,0x28,0x07,0x60,0x83,0x21,0x0c,0xc0,0x02,0x54,0x1b,0x8c,0x81, + 0x00,0x16,0xa0,0xda,0x80,0x10,0xff,0xff,0xff,0xff,0x3f,0x00,0x0c,0x20,0x01,0xd5, + 0x06,0xa3,0x08,0x80,0x05,0xa8,0x36,0x18,0x86,0x00,0x2c,0x40,0x05,0x49,0x18,0x00, + 0x00,0x03,0x00,0x00,0x00,0x13,0x86,0x40,0x18,0x26,0x0c,0x44,0x61,0x00,0x00,0x00, + 0x00,0x89,0x20,0x00,0x00,0x21,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85, + 0x04,0x93,0x22,0xa4,0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a, + 0x8c,0x0b,0x84,0xa4,0x4c,0x10,0x4c,0x33,0x00,0xc3,0x08,0x04,0x70,0x90,0x34,0x45, + 0x94,0x30,0xf9,0x0c,0x80,0x34,0xf4,0xef,0x50,0x13,0x0a,0xc2,0x51,0xd2,0x14,0x51, + 0xc2,0xe4,0xff,0x13,0x71,0x4d,0x54,0x44,0xfc,0xf6,0xf0,0x4f,0x63,0x04,0xc0,0x20, + 0xc2,0x10,0x5c,0x24,0x4d,0x11,0x25,0x4c,0xfe,0x2f,0x01,0xcc,0xb3,0x10,0xd1,0x3f, + 0x8d,0x11,0x00,0x83,0x08,0x85,0x50,0x0a,0x11,0x02,0x31,0x74,0x86,0x11,0x04,0x60, + 0x8e,0x20,0x98,0x23,0x00,0x83,0x61,0x04,0x61,0x29,0x48,0x20,0x26,0x29,0xa6,0x00, + 0xb5,0x81,0x80,0x1c,0x58,0xc3,0x08,0xc4,0x32,0x02,0x00,0x00,0x00,0x13,0xb2,0x70, + 0x48,0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68, + 0x83,0x76,0x08,0x87,0x71,0x78,0x87,0x79,0xc0,0x87,0x38,0x80,0x03,0x37,0x88,0x83, + 0x38,0x70,0x03,0x38,0xd8,0x70,0x1b,0xe5,0xd0,0x06,0xf0,0xa0,0x07,0x76,0x40,0x07, + 0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x90,0x0e,0x71,0xa0,0x07,0x78, + 0xa0,0x07,0x78,0xd0,0x06,0xe9,0x80,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90, + 0x0e,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x90,0x0e, + 0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x76, + 0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0e,0x73,0x20, + 0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e,0x76,0x40,0x07, + 0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x71,0x60,0x07,0x7a, + 0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x60,0x0f,0x72,0x40,0x07,0x7a,0x30, + 0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x73,0x20,0x07,0x7a,0x30,0x07, + 0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x74,0x80,0x07,0x7a,0x60,0x07,0x74, + 0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0, + 0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x79,0x60,0x07,0x7a,0x10,0x07,0x72,0x80,0x07, + 0x7a,0x10,0x07,0x72,0x80,0x07,0x6d,0x60,0x0f,0x71,0x20,0x07,0x78,0xa0,0x07,0x71, + 0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x79,0x20, + 0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f, + 0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76, + 0xd0,0x06,0xf6,0x50,0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20,0x07,0x7a,0x50, + 0x07,0x71,0x20,0x07,0x6d,0x60,0x0f,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07, + 0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x6d,0xe0,0x0e,0x78, + 0xa0,0x07,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0x30,0x84,0x41,0x00,0x00,0x08,0x00, + 0x00,0x00,0x00,0x00,0x18,0xc2,0x38,0x40,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x64, + 0x81,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c, + 0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a,0x25,0x30,0x02,0x50,0x04,0x85,0x50, + 0x10,0x65,0x40,0x70,0xac,0x41,0x79,0x08,0x00,0x79,0x18,0x00,0x00,0xd7,0x00,0x00, + 0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37, + 0xb7,0x21,0xc6,0x42,0x3c,0x00,0x84,0x50,0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b, + 0xd3,0x2b,0x1b,0x62,0x2c,0xc2,0x23,0x2c,0x05,0xd9,0x20,0x08,0x0e,0x8e,0xad,0x0c, + 0x84,0x89,0xc9,0xaa,0x09,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x24,0x07,0x46, + 0xc6,0x25,0x86,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x25,0x07,0x46, + 0xc6,0x25,0x86,0xc6,0x25,0x27,0x65,0x88,0xf0,0x10,0x43,0x8c,0x45,0x58,0x8c,0x65, + 0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x79,0x8e,0x45,0x58,0x84,0x65,0xe0,0x16, + 0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56,0xe6,0xf6,0x26, + 0xd7,0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x78,0x12,0x72,0x61, + 0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61,0x73,0x74,0x5f, + 0x6d,0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x84,0x67,0x61,0x19, + 0x84,0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98,0xc9,0x85,0xb5, + 0x95,0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1,0x7d,0x91,0xa5, + 0xcd,0x85,0x89,0xb1,0x95,0x0d,0x11,0x9e,0x86,0x61,0x10,0x96,0x26,0xe7,0x32,0xf6, + 0xd6,0x06,0x97,0xc6,0x56,0xe6,0xe2,0x16,0x46,0x97,0x66,0x57,0xf6,0x45,0xf6,0x56, + 0x27,0xc6,0x56,0xf6,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x78,0x1e,0x92, + 0x41,0x58,0x9a,0x9c,0xcb,0xd8,0x5b,0x1b,0x5c,0x1a,0x5b,0x99,0x8b,0x5b,0x18,0x5d, + 0x9a,0x5d,0xd9,0x17,0xdb,0x9b,0xdb,0xd9,0x17,0xdb,0x9b,0xdb,0xd9,0x17,0x59,0xda, + 0x5c,0x98,0x18,0x5b,0xd9,0x10,0xe1,0x89,0x78,0x06,0x61,0x69,0x72,0x2e,0x63,0x6f, + 0x6d,0x70,0x69,0x6c,0x65,0x2e,0x6e,0x61,0x74,0x69,0x76,0x65,0x5f,0x77,0x69,0x64, + 0x65,0x5f,0x76,0x65,0x63,0x74,0x6f,0x72,0x73,0x5f,0x64,0x69,0x73,0x61,0x62,0x6c, + 0x65,0x43,0x84,0x67,0x62,0x14,0x96,0x26,0xe7,0x22,0x57,0xe6,0x46,0x56,0x26,0xf7, + 0x45,0x17,0x26,0x77,0x56,0x46,0xc7,0x28,0x2c,0x4d,0xce,0x25,0x4c,0xee,0xec,0x8b, + 0x2e,0x0f,0xae,0xec,0xcb,0x2d,0xac,0xad,0x8c,0x86,0x19,0xdb,0x5b,0x18,0x1d,0x0d, + 0x99,0xb0,0x34,0x39,0x97,0x30,0xb9,0xb3,0x2f,0xb7,0xb0,0xb6,0x32,0x2a,0x66,0x72, + 0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x43,0x98,0xa7,0x5a,0x86,0xc7,0x7a,0xae, + 0x07,0x7b,0xb2,0x21,0xc2,0xa3,0x51,0x0a,0x4b,0x93,0x73,0x31,0x93,0x0b,0x3b,0x6b, + 0x2b,0x73,0xa3,0xfb,0x4a,0x73,0x83,0xab,0xa3,0xe3,0x52,0x37,0x57,0x26,0x87,0xc2, + 0xf6,0x36,0xe6,0x06,0x93,0x42,0x25,0x2c,0x4d,0xce,0x65,0xac,0xcc,0x8d,0xae,0x4c, + 0x8e,0x4f,0x58,0x9a,0x9c,0x0b,0x5c,0x99,0xdc,0x1c,0x5c,0xd9,0x18,0x5d,0x9a,0x5d, + 0x19,0x85,0x3a,0xbb,0x21,0xd2,0x32,0x3c,0xdc,0xd3,0x3d,0xde,0xf3,0x3d,0xd6,0x73, + 0x3d,0xd8,0x03,0x06,0x5c,0xea,0xe6,0xca,0xe4,0x50,0xd8,0xde,0xc6,0xdc,0x62,0x52, + 0x58,0x8c,0xbd,0xb1,0xbd,0xc9,0x0d,0x91,0x16,0xe1,0xe1,0x1e,0x31,0x78,0xbc,0xe7, + 0x7b,0xac,0xe7,0x7a,0xb0,0x67,0x0c,0xb8,0x84,0xa5,0xc9,0xb9,0xd0,0x95,0xe1,0xd1, + 0xd5,0xc9,0x95,0x51,0x0a,0x4b,0x93,0x73,0x61,0x7b,0x1b,0x0b,0xa3,0x4b,0x7b,0x73, + 0xfb,0x4a,0x73,0x23,0x2b,0xc3,0xa3,0x12,0x96,0x26,0xe7,0x32,0x17,0xd6,0x06,0xc7, + 0x56,0x46,0x8c,0xae,0x0c,0x8f,0xae,0x4e,0xae,0x4c,0x86,0x8c,0xc7,0x8c,0xed,0x2d, + 0x8c,0x8e,0x05,0x64,0x2e,0xac,0x0d,0x8e,0xad,0xcc,0x87,0x03,0x5d,0x19,0xde,0x10, + 0x6a,0x21,0x9e,0x32,0x78,0xcc,0x60,0x19,0x16,0xe1,0x39,0x83,0xc7,0x7a,0xd0,0xe0, + 0xc1,0x9e,0x34,0xe0,0x12,0x96,0x26,0xe7,0x32,0x17,0xd6,0x06,0xc7,0x56,0x26,0xc7, + 0x63,0x2e,0xac,0x0d,0x8e,0xad,0x4c,0x8e,0x08,0x5d,0x19,0xde,0x54,0x1b,0x1c,0x9b, + 0xdc,0x10,0x69,0x39,0x9e,0x35,0x78,0xcc,0x60,0x19,0x16,0xe1,0xb1,0x1e,0x36,0x78, + 0xb0,0xa7,0x0d,0x86,0x20,0x4f,0x18,0x3c,0x64,0xf0,0xa8,0xc1,0xe3,0x06,0x43,0x0c, + 0x04,0x78,0xb6,0xe7,0x0d,0x46,0x44,0xec,0xc0,0x0e,0xf6,0xd0,0x0e,0x6e,0xd0,0x0e, + 0xef,0x40,0x0e,0xf5,0xc0,0x0e,0xe5,0xe0,0x06,0xe6,0xc0,0x0e,0xe1,0x70,0x0e,0xf3, + 0x30,0x45,0x08,0x86,0x11,0x0a,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b,0xa4,0x03,0x39, + 0x94,0x83,0x3b,0xd0,0xc3,0x94,0xa0,0x18,0xb1,0x84,0x43,0x3a,0xc8,0x83,0x1b,0xd8, + 0x43,0x39,0xc8,0xc3,0x3c,0xa4,0xc3,0x3b,0xb8,0xc3,0x94,0xc0,0x18,0x41,0x85,0x43, + 0x3a,0xc8,0x83,0x1b,0xb0,0x43,0x38,0xb8,0xc3,0x39,0xd4,0x43,0x38,0x9c,0x43,0x39, + 0xfc,0x82,0x3d,0x94,0x83,0x3c,0xcc,0x43,0x3a,0xbc,0x83,0x3b,0x4c,0x09,0x90,0x11, + 0x53,0x38,0xa4,0x83,0x3c,0xb8,0xc1,0x38,0xbc,0x43,0x3b,0xc0,0x43,0x3a,0xb0,0x43, + 0x39,0xfc,0xc2,0x3b,0xc0,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x3c,0x4c,0x31,0x14, + 0xc6,0x81,0x24,0x6a,0x04,0x13,0x0e,0xe9,0x20,0x0f,0x6e,0x60,0x0e,0xf2,0x10,0x0e, + 0xe7,0xd0,0x0e,0xe5,0xe0,0x0e,0xf4,0x30,0x25,0x80,0x03,0x00,0x00,0x79,0x18,0x00, + 0x00,0x6d,0x00,0x00,0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d, + 0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c, + 0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d, + 0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d, + 0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79, + 0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc, + 0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50, + 0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30, + 0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03, + 0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07, + 0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76, + 0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98, + 0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8, + 0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21, + 0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43, + 0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f, + 0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70, + 0x58,0x87,0x72,0x70,0x83,0x74,0x68,0x07,0x78,0x60,0x87,0x74,0x18,0x87,0x74,0xa0, + 0x87,0x19,0xce,0x53,0x0f,0xee,0x00,0x0f,0xf2,0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40, + 0x0f,0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33,0x20,0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41, + 0x1e,0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc,0xe0,0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e, + 0x66,0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c,0x83,0x3b,0xcc,0x50,0x24,0x76,0x60,0x07, + 0x7b,0x68,0x07,0x37,0x60,0x87,0x77,0x78,0x07,0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f, + 0xf0,0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca,0x61,0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d, + 0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06,0x54,0x85,0x83,0x38, + 0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88, + 0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87,0x79,0x98,0x87,0x77,0x18,0x87,0x74,0x08, + 0x07,0x7a,0x28,0x07,0x72,0x00,0x00,0x00,0x00,0x71,0x20,0x00,0x00,0x08,0x00,0x00, + 0x00,0x16,0xb0,0x01,0x48,0xe4,0x4b,0x00,0xf3,0x2c,0xc4,0x3f,0x11,0xd7,0x44,0x45, + 0xc4,0x6f,0x0f,0x7e,0x85,0x17,0xb7,0x6d,0x00,0x05,0x03,0x20,0x0d,0x0d,0x00,0x00, + 0x00,0x61,0x20,0x00,0x00,0x16,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00, + 0x00,0x0b,0x00,0x00,0x00,0x14,0xc7,0x22,0x80,0x40,0x20,0x88,0x8d,0x00,0x8c,0x25, + 0x00,0x01,0xa9,0x11,0x80,0x1a,0x20,0x31,0x03,0x40,0x61,0x0e,0xe2,0xba,0xae,0x6a, + 0x06,0x80,0xc0,0x0c,0xc0,0x08,0xc0,0x18,0x01,0x08,0x82,0x20,0xfe,0x01,0x00,0x00, + 0x00,0x83,0x0c,0x0f,0x91,0x8c,0x18,0x28,0x42,0x80,0x39,0x4d,0x80,0x2c,0xc9,0x30, + 0xc8,0x70,0x04,0x8d,0x05,0x91,0x7c,0x66,0x1b,0x94,0x00,0xc8,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sfons_vs_bytecode_metal_ios[3344] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x10,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0x58,0xff,0x12,0x36,0x80,0x88,0xb5, + 0xf7,0x1c,0xc3,0xd5,0x31,0x57,0x18,0x26,0x26,0xf0,0x8c,0xd4,0x15,0xa2,0x55,0x8b, + 0xd5,0x3c,0x6b,0x11,0xbd,0x17,0x49,0x4a,0x02,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0x45,0x4e,0x44,0x54,0x37,0x00,0x00, + 0x00,0x56,0x41,0x54,0x54,0x22,0x00,0x03,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x00,0x00,0x80,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x01,0x80, + 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x00,0x02,0x80,0x56,0x41,0x54,0x59,0x05,0x00,0x03, + 0x00,0x06,0x04,0x06,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54, + 0xde,0xc0,0x17,0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0xec,0x0b,0x00,0x00, + 0xff,0xff,0xff,0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0xf8,0x02,0x00,0x00, + 0x0b,0x82,0x20,0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91, + 0x41,0xc8,0x04,0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19, + 0x1e,0x04,0x8b,0x62,0x80,0x14,0x45,0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14, + 0x38,0x08,0x18,0x49,0x0a,0x32,0x44,0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80, + 0x0c,0x19,0x21,0x72,0x24,0x07,0xc8,0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0, + 0x01,0x00,0x00,0x00,0x51,0x18,0x00,0x00,0x82,0x00,0x00,0x00,0x1b,0xc8,0x25,0xf8, + 0xff,0xff,0xff,0xff,0x01,0x90,0x80,0x8a,0x18,0x87,0x77,0x90,0x07,0x79,0x28,0x87, + 0x71,0xa0,0x07,0x76,0xc8,0x87,0x36,0x90,0x87,0x77,0xa8,0x07,0x77,0x20,0x87,0x72, + 0x20,0x87,0x36,0x20,0x87,0x74,0xb0,0x87,0x74,0x20,0x87,0x72,0x68,0x83,0x79,0x88, + 0x07,0x79,0xa0,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07, + 0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c,0x00,0x82,0x1c,0xd2,0x61,0x1e,0xc2,0x41, + 0x1c,0xd8,0xa1,0x1c,0xda,0x80,0x1e,0xc2,0x21,0x1d,0xd8,0xa1,0x0d,0xc6,0x21,0x1c, + 0xd8,0x81,0x1d,0xe6,0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07,0x80,0x60,0x87, + 0x72,0x98,0x87,0x79,0x68,0x03,0x78,0x90,0x87,0x72,0x18,0x87,0x74,0x98,0x87,0x72, + 0x68,0x03,0x73,0x80,0x87,0x76,0x08,0x07,0x72,0x00,0xcc,0x21,0x1c,0xd8,0x61,0x1e, + 0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0xc0,0x1c,0xe4,0x21,0x1c,0xda,0xa1,0x1c,0xda, + 0x00,0x1e,0xde,0x21,0x1d,0xdc,0x81,0x1e,0xca,0x41,0x1e,0xda,0xa0,0x1c,0xd8,0x21, + 0x1d,0xda,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x06,0x77,0x78,0x87,0x36,0x30, + 0x07,0x79,0x08,0x87,0x76,0x28,0x87,0x36,0x80,0x87,0x77,0x48,0x07,0x77,0xa0,0x87, + 0x72,0x90,0x87,0x36,0x28,0x07,0x76,0x48,0x87,0x76,0x68,0x03,0x77,0x78,0x07,0x77, + 0x68,0x03,0x76,0x28,0x87,0x70,0x30,0x07,0x80,0x70,0x87,0x77,0x68,0x83,0x74,0x70, + 0x07,0x73,0x98,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07, + 0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0x40,0x1d,0xea,0xa1, + 0x1d,0xe0,0xa1,0x0d,0xe8,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x1e,0x00,0x73,0x08, + 0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x77,0x78,0x87,0x36,0x70,0x87,0x70,0x70,0x87, + 0x79,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41, + 0x1e,0xea,0xa1,0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xe6,0x21,0x1d,0xce,0xc1,0x1d, + 0xca,0x81,0x1c,0xda,0x40,0x1f,0xca,0x41,0x1e,0xde,0x61,0x1e,0xda,0xc0,0x1c,0xe0, + 0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80, + 0x70,0x87,0x77,0x68,0x03,0x7a,0x90,0x87,0x70,0x80,0x07,0x78,0x48,0x07,0x77,0x38, + 0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00, + 0x62,0x1e,0xe8,0x21,0x1c,0xc6,0x61,0x1d,0xda,0x00,0x1e,0xe4,0xe1,0x1d,0xe8,0xa1, + 0x1c,0xc6,0x81,0x1e,0xde,0x41,0x1e,0xda,0x40,0x1c,0xea,0xc1,0x1c,0xcc,0xa1,0x1c, + 0xe4,0xa1,0x0d,0xe6,0x21,0x1d,0xf4,0xa1,0x1c,0x00,0x3c,0x00,0x88,0x7a,0x70,0x87, + 0x79,0x08,0x07,0x73,0x28,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a, + 0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xea,0x61,0x1e,0xca,0xa1,0x0d, + 0xe6,0xe1,0x1d,0xcc,0x81,0x1e,0xda,0xc0,0x1c,0xd8,0xe1,0x1d,0xc2,0x81,0x1e,0x00, + 0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x36,0x20,0x02,0x01,0x24,0xc0,0x02,0x54, + 0x00,0x00,0x00,0x00,0x49,0x18,0x00,0x00,0x01,0x00,0x00,0x00,0x13,0x84,0x40,0x00, + 0x89,0x20,0x00,0x00,0x23,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85,0x04, + 0x93,0x22,0xa4,0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a,0x8c, + 0x0b,0x84,0xa4,0x4c,0x10,0x44,0x33,0x00,0xc3,0x08,0x04,0x70,0x90,0x34,0x45,0x94, + 0x30,0xf9,0x0c,0x80,0x34,0xf4,0xef,0x50,0x13,0x1a,0x42,0x08,0xc3,0x08,0x02,0x90, + 0x04,0x61,0x26,0x6a,0x1e,0xe8,0x41,0x1e,0xea,0x61,0x1c,0xe8,0xc1,0x0d,0xda,0xa1, + 0x1c,0xe8,0x21,0x1c,0xd8,0x41,0x0f,0xf4,0xa0,0x1d,0xc2,0x81,0x1e,0xe4,0x21,0x1d, + 0xf0,0x01,0x05,0xe4,0x20,0x69,0x8a,0x28,0x61,0xf2,0x2b,0xe9,0x7f,0x80,0x08,0x60, + 0x24,0x24,0x94,0x32,0x88,0x60,0x08,0xa5,0x10,0x61,0x84,0x43,0x68,0x20,0x60,0x8e, + 0x00,0x0c,0x52,0x60,0xcd,0x11,0x80,0xc2,0x20,0x42,0x20,0x0c,0x23,0x10,0xcb,0x08, + 0x00,0x00,0x00,0x00,0x13,0xa8,0x70,0x48,0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70, + 0x80,0x07,0x78,0x60,0x87,0x72,0x68,0x83,0x74,0x78,0x87,0x79,0xc8,0x03,0x37,0x80, + 0x03,0x37,0x80,0x83,0x0d,0xb7,0x51,0x0e,0x6d,0x00,0x0f,0x7a,0x60,0x07,0x74,0xa0, + 0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xe9,0x10,0x07,0x7a,0x80,0x07, + 0x7a,0x80,0x07,0x6d,0x90,0x0e,0x78,0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0,0x06,0xe9, + 0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xd0,0x06,0xe9,0x30, + 0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe9,0x60,0x07, + 0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xe6,0x30,0x07,0x72, + 0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe6,0x60,0x07,0x74,0xa0, + 0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x10,0x07,0x76,0xa0,0x07, + 0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xd0,0x06,0xf6,0x20,0x07,0x74,0xa0,0x07,0x73, + 0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xf6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20, + 0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xf6,0x40,0x07,0x78,0xa0,0x07,0x76,0x40,0x07, + 0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a, + 0x60,0x07,0x74,0xd0,0x06,0xf6,0x90,0x07,0x76,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0, + 0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07, + 0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x6d,0x60,0x0f,0x71,0x90,0x07,0x72, + 0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xd0,0x06,0xf6,0x20, + 0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07, + 0x6d,0x60,0x0f,0x75,0x10,0x07,0x72,0xa0,0x07,0x75,0x10,0x07,0x72,0xa0,0x07,0x75, + 0x10,0x07,0x72,0xd0,0x06,0xf6,0x10,0x07,0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00, + 0x07,0x72,0x40,0x07,0x7a,0x10,0x07,0x70,0x20,0x07,0x74,0xd0,0x06,0xee,0x80,0x07, + 0x7a,0x10,0x07,0x76,0xa0,0x07,0x73,0x20,0x07,0x43,0x98,0x04,0x00,0x80,0x00,0x00, + 0x00,0x00,0x00,0x80,0x2c,0x10,0x00,0x00,0x0b,0x00,0x00,0x00,0x32,0x1e,0x98,0x10, + 0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a,0x25,0x30,0x02,0x50, + 0x04,0x05,0x18,0x50,0x80,0x02,0x85,0x50,0x10,0x65,0x50,0x20,0xd4,0x46,0x00,0x88, + 0x8d,0x25,0x34,0x05,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x19,0x01,0x00,0x00, + 0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7, + 0x21,0xc6,0x32,0x28,0x00,0xb3,0x50,0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3, + 0x2b,0x1b,0x62,0x2c,0x81,0x22,0x2c,0x06,0xd9,0x20,0x08,0x0e,0x8e,0xad,0x0c,0x84, + 0x89,0xc9,0xaa,0x09,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x07,0x46, + 0xc6,0xc5,0xe6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x07, + 0x46,0xc6,0xc5,0xe6,0x26,0x65,0x88,0xa0,0x10,0x43,0x8c,0x25,0x58,0x8e,0x45,0x60, + 0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x51,0x8e,0x25,0x58,0x84,0x45,0xe0,0x16,0x96, + 0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56,0xe6,0xf6,0x26,0xd7, + 0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x50,0x12,0x72,0x61,0x69, + 0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61,0x73,0x74,0x5f,0x6d, + 0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x04,0x65,0x21,0x19,0x84, + 0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98,0xc9,0x85,0xb5,0x95, + 0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1,0x7d,0x95,0xb9,0x85, + 0x89,0xb1,0x95,0x0d,0x11,0x94,0x86,0x61,0x10,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06, + 0x97,0xc6,0x56,0xe6,0xe2,0x16,0x46,0x97,0x66,0x57,0xf6,0x45,0xf6,0x56,0x27,0xc6, + 0x56,0xf6,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x50,0x1e,0x92,0x41,0x58, + 0x9a,0x9c,0xcb,0xd8,0x5b,0x1b,0x5c,0x1a,0x5b,0x99,0x8b,0x5b,0x18,0x5d,0x9a,0x5d, + 0xd9,0x17,0xdb,0x9b,0xdb,0xd9,0x17,0xdb,0x9b,0xdb,0xd9,0x17,0x59,0xda,0x5c,0x98, + 0x18,0x5b,0xd9,0x10,0x41,0x89,0x78,0x06,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70, + 0x69,0x6c,0x65,0x2e,0x6e,0x61,0x74,0x69,0x76,0x65,0x5f,0x77,0x69,0x64,0x65,0x5f, + 0x76,0x65,0x63,0x74,0x6f,0x72,0x73,0x5f,0x64,0x69,0x73,0x61,0x62,0x6c,0x65,0x43, + 0x04,0x65,0x62,0x14,0x96,0x26,0xe7,0x62,0x57,0x26,0x47,0x57,0x86,0xf7,0xf5,0x56, + 0x47,0x07,0x57,0x47,0xc7,0xa5,0x6e,0xae,0x4c,0x0e,0x85,0xed,0x6d,0xcc,0x0d,0x26, + 0x85,0x51,0x58,0x9a,0x9c,0x4b,0x98,0xdc,0xd9,0x17,0x5d,0x1e,0x5c,0xd9,0x97,0x5b, + 0x58,0x5b,0x19,0x0d,0x33,0xb6,0xb7,0x30,0x3a,0x1a,0x32,0x61,0x69,0x72,0x2e,0x61, + 0x72,0x67,0x5f,0x6e,0x61,0x6d,0x65,0x14,0xea,0xec,0x86,0x30,0x4a,0xa5,0x58,0xca, + 0xa5,0x60,0x4a,0xa6,0x68,0x5c,0xea,0xe6,0xca,0xe4,0x50,0xd8,0xde,0xc6,0xdc,0x62, + 0x52,0x58,0x8c,0xbd,0xb1,0xbd,0xc9,0x0d,0x61,0x94,0x4a,0xe1,0x94,0x4b,0xc1,0x94, + 0x4c,0xe9,0xc8,0x84,0xa5,0xc9,0xb9,0xc0,0xbd,0xcd,0xa5,0xd1,0xa5,0xbd,0xb9,0x71, + 0x39,0x63,0xfb,0x82,0x7a,0x9b,0x4b,0xa3,0x4b,0x7b,0x73,0x1b,0xa2,0x28,0x9f,0x72, + 0x29,0x98,0x92,0x29,0x60,0x30,0xc4,0x50,0x36,0xc5,0x53,0xc2,0x80,0x50,0x58,0x9a, + 0x9c,0x8b,0x5d,0x99,0x1c,0x5d,0x19,0xde,0x57,0x9a,0x1b,0x5c,0x1d,0x1d,0xa5,0xb0, + 0x34,0x39,0x17,0xb6,0xb7,0xb1,0x30,0xba,0xb4,0x37,0xb7,0xaf,0x34,0x37,0xb2,0x32, + 0x3c,0x66,0x67,0x65,0x6e,0x65,0x72,0x61,0x74,0x65,0x64,0x28,0x38,0x70,0x6f,0x73, + 0x69,0x74,0x69,0x6f,0x6e,0x44,0x76,0x34,0x5f,0x66,0x29,0x44,0xe0,0xde,0xe6,0xd2, + 0xe8,0xd2,0xde,0xdc,0x86,0x50,0x8b,0xa0,0x8c,0x81,0x42,0x06,0x8b,0xb0,0x04,0x4a, + 0x19,0x28,0x97,0x82,0x29,0x99,0x62,0x06,0xd4,0xce,0xca,0xdc,0xca,0xe4,0xc2,0xe8, + 0xca,0xc8,0x50,0x72,0xe8,0xca,0xf0,0xc6,0xde,0xde,0xe4,0xc8,0x60,0x88,0xec,0x64, + 0xbe,0xcc,0x52,0x68,0x98,0xb1,0xbd,0x85,0xd1,0xc9,0x30,0xa1,0x2b,0xc3,0x1b,0x7b, + 0x7b,0x93,0x23,0x83,0x19,0x42,0x2d,0x81,0x32,0x06,0x0a,0x19,0x2c,0xc1,0x12,0x28, + 0x68,0xa0,0x5c,0x4a,0x1a,0x28,0x99,0xa2,0x06,0xbc,0xce,0xca,0xdc,0xca,0xe4,0xc2, + 0xe8,0xca,0xc8,0x50,0x6c,0xc6,0xde,0xd8,0xde,0xe4,0x60,0x88,0xec,0x68,0xbe,0xcc, + 0x52,0x68,0x8c,0xbd,0xb1,0xbd,0xc9,0xc1,0x0c,0xa1,0x96,0x41,0x19,0x03,0x85,0x0c, + 0x96,0x61,0x09,0x14,0x36,0x50,0x2e,0x05,0x53,0x32,0xa5,0x0d,0xa8,0x84,0xa5,0xc9, + 0xb9,0x88,0xd5,0x99,0x99,0x95,0xc9,0xf1,0x09,0x4b,0x93,0x73,0x11,0xab,0x33,0x33, + 0x2b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x23,0x12,0x96,0x26,0xe7,0x22,0x57,0x16,0x46, + 0x46,0x2a,0x2c,0x4d,0xce,0x65,0x8e,0x4e,0xae,0x6e,0x8c,0xee,0x8b,0x2e,0x0f,0xae, + 0xec,0x2b,0xcd,0xcd,0xec,0x8d,0x88,0x19,0xdb,0x5b,0x18,0x1d,0x0d,0x1e,0x0d,0x87, + 0x36,0x3b,0x38,0x0a,0x74,0x6d,0x43,0xa8,0x45,0x58,0x88,0x45,0x50,0xe6,0x40,0xa1, + 0x83,0x85,0x58,0x88,0x45,0x50,0xe6,0x40,0xa9,0x03,0x46,0x61,0x69,0x72,0x2e,0x61, + 0x72,0x67,0x5f,0x74,0x79,0x70,0x65,0x5f,0x73,0x69,0x7a,0x65,0xbc,0xc2,0xd2,0xe4, + 0x5c,0xc2,0xe4,0xce,0xbe,0xe8,0xf2,0xe0,0xca,0xbe,0xc2,0xd8,0xd2,0xce,0xdc,0xbe, + 0xe6,0xd2,0xf4,0xca,0x98,0xd8,0xcd,0x7d,0xc1,0x85,0xc9,0x85,0xb5,0xcd,0x71,0xf8, + 0x92,0x81,0x19,0x42,0x06,0x0b,0xa2,0xbc,0x81,0x02,0x07,0x4b,0xa1,0x90,0xc1,0x22, + 0x2c,0x81,0x12,0x07,0x8a,0x1c,0x28,0x76,0xa0,0xdc,0xc1,0x52,0x28,0x78,0xb0,0x24, + 0xca,0xa5,0xe4,0x81,0x92,0x29,0x7a,0x30,0x04,0x51,0xce,0x40,0x59,0x03,0xc5,0x0d, + 0x94,0x3d,0x18,0x62,0x24,0x80,0x22,0x06,0x0a,0x1f,0xf0,0x79,0x6b,0x73,0x4b,0x83, + 0x7b,0xa3,0x2b,0x73,0xa3,0x03,0x19,0x43,0x0b,0x93,0xe3,0x33,0x95,0xd6,0x06,0xc7, + 0x56,0x06,0x32,0xb4,0xb2,0x02,0x42,0x25,0x14,0x14,0x34,0x44,0x50,0xfe,0x60,0x88, + 0xa1,0xf8,0x81,0x02,0x0a,0x8d,0x32,0xc4,0x50,0x42,0x41,0x09,0x85,0x46,0x19,0x11, + 0xb1,0x03,0x3b,0xd8,0x43,0x3b,0xb8,0x41,0x3b,0xbc,0x03,0x39,0xd4,0x03,0x3b,0x94, + 0x83,0x1b,0x98,0x03,0x3b,0x84,0xc3,0x39,0xcc,0xc3,0x14,0x21,0x18,0x46,0x28,0xec, + 0xc0,0x0e,0xf6,0xd0,0x0e,0x6e,0x90,0x0e,0xe4,0x50,0x0e,0xee,0x40,0x0f,0x53,0x82, + 0x62,0xc4,0x12,0x0e,0xe9,0x20,0x0f,0x6e,0x60,0x0f,0xe5,0x20,0x0f,0xf3,0x90,0x0e, + 0xef,0xe0,0x0e,0x53,0x02,0x63,0x04,0x15,0x0e,0xe9,0x20,0x0f,0x6e,0xc0,0x0e,0xe1, + 0xe0,0x0e,0xe7,0x50,0x0f,0xe1,0x70,0x0e,0xe5,0xf0,0x0b,0xf6,0x50,0x0e,0xf2,0x30, + 0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x25,0x40,0x46,0x4c,0xe1,0x90,0x0e,0xf2,0xe0,0x06, + 0xe3,0xf0,0x0e,0xed,0x00,0x0f,0xe9,0xc0,0x0e,0xe5,0xf0,0x0b,0xef,0x00,0x0f,0xf4, + 0x90,0x0e,0xef,0xe0,0x0e,0xf3,0x30,0xc5,0x50,0x18,0x07,0x92,0xa8,0x11,0x4a,0x38, + 0xa4,0x83,0x3c,0xb8,0x81,0x3d,0x94,0x83,0x3c,0xd0,0x43,0x39,0xe0,0xc3,0x94,0xa0, + 0x0f,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x6d,0x00,0x00,0x00,0x33,0x08,0x80,0x1c, + 0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07, + 0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e, + 0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43, + 0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c, + 0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76, + 0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e, + 0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8, + 0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4, + 0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68, + 0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07, + 0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71, + 0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5, + 0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4, + 0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90, + 0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43, + 0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b, + 0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83,0x74,0x68,0x07,0x78, + 0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f,0xee,0x00,0x0f,0xf2, + 0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33,0x20, + 0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc,0xe0, + 0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c,0x83, + 0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60,0x87,0x77,0x78,0x07, + 0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca,0x61, + 0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21,0x1d, + 0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43,0x39, + 0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87,0x79, + 0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72,0x00,0x00,0x00,0x00, + 0x71,0x20,0x00,0x00,0x02,0x00,0x00,0x00,0x06,0x50,0x30,0x00,0xd2,0xd0,0x00,0x00, + 0x61,0x20,0x00,0x00,0x3d,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00, + 0x09,0x00,0x00,0x00,0xf4,0xc6,0x22,0x86,0x61,0x18,0xc6,0x22,0x04,0x41,0x10,0xc6, + 0x22,0x82,0x20,0x08,0xa8,0x95,0x40,0x19,0x14,0x01,0xbd,0x11,0x00,0x1a,0x33,0x00, + 0x24,0x66,0x00,0x28,0xcc,0x00,0x00,0x00,0xe3,0x15,0x0b,0x84,0x61,0x10,0x05,0x65, + 0x90,0x01,0x1a,0x0c,0x13,0x02,0xf9,0x8c,0x57,0x3c,0x14,0xc7,0x2d,0x14,0x94,0x41, + 0x06,0xea,0x70,0x4c,0x08,0xe4,0x63,0x41,0x01,0x9f,0xf1,0x0a,0x2a,0x0b,0x83,0x30, + 0x70,0x28,0x28,0x83,0x0c,0x19,0x43,0x99,0x10,0xc8,0xc7,0x8a,0x00,0x3e,0xe3,0x15, + 0x99,0x67,0x06,0x66,0x40,0x51,0x50,0x06,0x19,0xbc,0x48,0x33,0x21,0x90,0x8f,0x15, + 0x01,0x7c,0xc6,0x2b,0xbc,0x31,0x60,0x83,0x35,0x18,0x03,0x0a,0xca,0x20,0x83,0x18, + 0x60,0x99,0x09,0x81,0x7c,0xc6,0x2b,0xc4,0xe0,0x0c,0xe0,0xe0,0x0d,0x3c,0x0a,0xca, + 0x20,0x83,0x19,0x70,0x61,0x60,0x42,0x20,0x1f,0x0b,0x0a,0xf8,0x8c,0x57,0x9c,0x01, + 0x1b,0xd4,0x01,0x1d,0x88,0x01,0x05,0xc5,0x86,0x00,0x3e,0xb3,0x0d,0x61,0x10,0x00, + 0xb3,0x0d,0x01,0x1b,0x04,0xb3,0x0d,0xc1,0x23,0x64,0x10,0x10,0x03,0x00,0x00,0x00, + 0x09,0x00,0x00,0x00,0x5b,0x86,0x20,0x10,0x85,0x2d,0x43,0x11,0x88,0xc2,0x96,0x41, + 0x09,0x44,0x61,0xcb,0xf0,0x04,0xa2,0xb0,0x65,0xa0,0x02,0x51,0xd8,0x32,0x60,0x81, + 0x28,0x6c,0x19,0xba,0x40,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sfons_fs_bytecode_metal_ios[2925] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd5,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x90,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0x1b,0x2c,0xa9,0x59,0x2d,0x7d,0xde, + 0xcd,0x5c,0x34,0x76,0x1e,0x63,0x4d,0x0c,0xdc,0xfc,0x74,0x70,0x26,0x76,0xe8,0xde, + 0xed,0xf7,0xc3,0xf7,0xe9,0x62,0x28,0xfa,0xd6,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0x45,0x4e,0x44,0x54,0x04,0x00,0x00, + 0x00,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17, + 0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x7c,0x0a,0x00,0x00,0xff,0xff,0xff, + 0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0x9c,0x02,0x00,0x00,0x0b,0x82,0x20, + 0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04, + 0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b, + 0x62,0x80,0x14,0x45,0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18, + 0x49,0x0a,0x32,0x44,0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21, + 0x72,0x24,0x07,0xc8,0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00, + 0x00,0x51,0x18,0x00,0x00,0x89,0x00,0x00,0x00,0x1b,0xcc,0x25,0xf8,0xff,0xff,0xff, + 0xff,0x01,0x60,0x00,0x09,0xa8,0x88,0x71,0x78,0x07,0x79,0x90,0x87,0x72,0x18,0x07, + 0x7a,0x60,0x87,0x7c,0x68,0x03,0x79,0x78,0x87,0x7a,0x70,0x07,0x72,0x28,0x07,0x72, + 0x68,0x03,0x72,0x48,0x07,0x7b,0x48,0x07,0x72,0x28,0x87,0x36,0x98,0x87,0x78,0x90, + 0x07,0x7a,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xcc, + 0x21,0x1c,0xd8,0x61,0x1e,0xca,0x01,0x20,0xc8,0x21,0x1d,0xe6,0x21,0x1c,0xc4,0x81, + 0x1d,0xca,0xa1,0x0d,0xe8,0x21,0x1c,0xd2,0x81,0x1d,0xda,0x60,0x1c,0xc2,0x81,0x1d, + 0xd8,0x61,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x76,0x28,0x87, + 0x79,0x98,0x87,0x36,0x80,0x07,0x79,0x28,0x87,0x71,0x48,0x87,0x79,0x28,0x87,0x36, + 0x30,0x07,0x78,0x68,0x87,0x70,0x20,0x07,0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c, + 0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xcc,0x41,0x1e,0xc2,0xa1,0x1d,0xca,0xa1,0x0d,0xe0, + 0xe1,0x1d,0xd2,0xc1,0x1d,0xe8,0xa1,0x1c,0xe4,0xa1,0x0d,0xca,0x81,0x1d,0xd2,0xa1, + 0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x60,0x70,0x87,0x77,0x68,0x03,0x73,0x90, + 0x87,0x70,0x68,0x87,0x72,0x68,0x03,0x78,0x78,0x87,0x74,0x70,0x07,0x7a,0x28,0x07, + 0x79,0x68,0x83,0x72,0x60,0x87,0x74,0x68,0x87,0x36,0x70,0x87,0x77,0x70,0x87,0x36, + 0x60,0x87,0x72,0x08,0x07,0x73,0x00,0x08,0x77,0x78,0x87,0x36,0x48,0x07,0x77,0x30, + 0x87,0x79,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8, + 0x41,0x1e,0xea,0xa1,0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xd4,0xa1,0x1e,0xda,0x01, + 0x1e,0xda,0x80,0x1e,0xc2,0x41,0x1c,0xd8,0xa1,0x1c,0xe6,0x01,0x30,0x87,0x70,0x60, + 0x87,0x79,0x28,0x07,0x80,0x70,0x87,0x77,0x68,0x03,0x77,0x08,0x07,0x77,0x98,0x87, + 0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1, + 0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0x60,0x1e,0xd2,0xe1,0x1c,0xdc,0xa1,0x1c, + 0xc8,0xa1,0x0d,0xf4,0xa1,0x1c,0xe4,0xe1,0x1d,0xe6,0xa1,0x0d,0xcc,0x01,0x1e,0xda, + 0xa0,0x1d,0xc2,0x81,0x1e,0xd0,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x08,0x77, + 0x78,0x87,0x36,0xa0,0x07,0x79,0x08,0x07,0x78,0x80,0x87,0x74,0x70,0x87,0x73,0x68, + 0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xe6, + 0x81,0x1e,0xc2,0x61,0x1c,0xd6,0xa1,0x0d,0xe0,0x41,0x1e,0xde,0x81,0x1e,0xca,0x61, + 0x1c,0xe8,0xe1,0x1d,0xe4,0xa1,0x0d,0xc4,0xa1,0x1e,0xcc,0xc1,0x1c,0xca,0x41,0x1e, + 0xda,0x60,0x1e,0xd2,0x41,0x1f,0xca,0x01,0xc0,0x03,0x80,0xa8,0x07,0x77,0x98,0x87, + 0x70,0x30,0x87,0x72,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74, + 0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0xa2,0x1e,0xe6,0xa1,0x1c,0xda,0x60,0x1e, + 0xde,0xc1,0x1c,0xe8,0xa1,0x0d,0xcc,0x81,0x1d,0xde,0x21,0x1c,0xe8,0x01,0x30,0x87, + 0x70,0x60,0x87,0x79,0x28,0x07,0x60,0x83,0x21,0x0c,0xc0,0x02,0x54,0x1b,0x8c,0x81, + 0x00,0x16,0xa0,0xda,0x80,0x10,0xff,0xff,0xff,0xff,0x3f,0x00,0x0c,0x20,0x01,0xd5, + 0x06,0xa3,0x08,0x80,0x05,0xa8,0x36,0x18,0x86,0x00,0x2c,0x40,0x05,0x49,0x18,0x00, + 0x00,0x03,0x00,0x00,0x00,0x13,0x86,0x40,0x18,0x26,0x0c,0x44,0x61,0x00,0x00,0x00, + 0x00,0x89,0x20,0x00,0x00,0x21,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85, + 0x04,0x93,0x22,0xa4,0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a, + 0x8c,0x0b,0x84,0xa4,0x4c,0x10,0x4c,0x33,0x00,0xc3,0x08,0x04,0x70,0x90,0x34,0x45, + 0x94,0x30,0xf9,0x0c,0x80,0x34,0xf4,0xef,0x50,0x13,0x0a,0xc2,0x51,0xd2,0x14,0x51, + 0xc2,0xe4,0xff,0x13,0x71,0x4d,0x54,0x44,0xfc,0xf6,0xf0,0x4f,0x63,0x04,0xc0,0x20, + 0xc2,0x10,0x5c,0x24,0x4d,0x11,0x25,0x4c,0xfe,0x2f,0x01,0xcc,0xb3,0x10,0xd1,0x3f, + 0x8d,0x11,0x00,0x83,0x08,0x85,0x50,0x0a,0x11,0x02,0x31,0x74,0x86,0x11,0x04,0x60, + 0x8e,0x20,0x98,0x23,0x00,0x83,0x61,0x04,0x61,0x29,0x48,0x20,0x26,0x29,0xa6,0x00, + 0xb5,0x81,0x80,0x14,0x58,0xc3,0x08,0xc4,0x32,0x02,0x00,0x00,0x00,0x13,0xa8,0x70, + 0x48,0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68, + 0x83,0x74,0x78,0x87,0x79,0xc8,0x03,0x37,0x80,0x03,0x37,0x80,0x83,0x0d,0xb7,0x51, + 0x0e,0x6d,0x00,0x0f,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07, + 0x74,0xd0,0x06,0xe9,0x10,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90,0x0e,0x78, + 0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0,0x06,0xe9,0x10,0x07,0x76,0xa0,0x07,0x71,0x60, + 0x07,0x7a,0x10,0x07,0x76,0xd0,0x06,0xe9,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07, + 0x7a,0x30,0x07,0x72,0xd0,0x06,0xe9,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a, + 0x60,0x07,0x74,0xd0,0x06,0xe6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30, + 0x07,0x72,0xd0,0x06,0xe6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07, + 0x74,0xd0,0x06,0xf6,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x7a,0x10,0x07,0x76, + 0xd0,0x06,0xf6,0x20,0x07,0x74,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0, + 0x06,0xf6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06, + 0xf6,0x40,0x07,0x78,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6, + 0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x90, + 0x07,0x76,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06, + 0xf6,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72, + 0x80,0x07,0x6d,0x60,0x0f,0x71,0x90,0x07,0x72,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0, + 0x07,0x72,0x50,0x07,0x76,0xd0,0x06,0xf6,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07, + 0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f,0x75,0x10,0x07,0x72, + 0xa0,0x07,0x75,0x10,0x07,0x72,0xa0,0x07,0x75,0x10,0x07,0x72,0xd0,0x06,0xf6,0x10, + 0x07,0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07, + 0x70,0x20,0x07,0x74,0xd0,0x06,0xee,0x80,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x73, + 0x20,0x07,0x43,0x18,0x04,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x80,0x21,0x8c,0x03, + 0x04,0x80,0x00,0x00,0x00,0x00,0x00,0x40,0x16,0x08,0x00,0x00,0x00,0x08,0x00,0x00, + 0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43, + 0x5a,0x23,0x00,0x25,0x50,0x04,0x85,0x50,0x10,0x65,0x40,0x70,0x2c,0xa1,0x29,0x00, + 0x00,0x79,0x18,0x00,0x00,0xd7,0x00,0x00,0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2, + 0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x42,0x3c,0x00,0x84,0x50, + 0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62,0x2c,0xc3,0x23,0x2c, + 0x05,0xd9,0x20,0x08,0x0e,0x8e,0xad,0x0c,0x84,0x89,0xc9,0xaa,0x09,0xc4,0xae,0x4c, + 0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x07,0x46,0xc6,0xc5,0xe6,0x06,0x04,0xa5,0xad, + 0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x07,0x46,0xc6,0xc5,0xe6,0x26,0x65,0x88, + 0xf0,0x10,0x43,0x8c,0x65,0x58,0x8c,0x45,0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04, + 0x79,0x8e,0x65,0x58,0x84,0x45,0xe0,0x16,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97, + 0xc6,0x56,0xe6,0x42,0x56,0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45,0x96,0x36,0x17,0x26, + 0xc6,0x56,0x36,0x44,0x78,0x12,0x72,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69, + 0x6c,0x65,0x2e,0x66,0x61,0x73,0x74,0x5f,0x6d,0x61,0x74,0x68,0x5f,0x65,0x6e,0x61, + 0x62,0x6c,0x65,0x43,0x84,0x67,0x21,0x19,0x84,0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1, + 0xa5,0xb1,0x95,0xb9,0x98,0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d, + 0x99,0x95,0xd1,0x8d,0xa1,0x7d,0x95,0xb9,0x85,0x89,0xb1,0x95,0x0d,0x11,0x9e,0x86, + 0x61,0x10,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0xe2,0x16,0x46, + 0x97,0x66,0x57,0xf6,0x45,0xf6,0x56,0x27,0xc6,0x56,0xf6,0x45,0x96,0x36,0x17,0x26, + 0xc6,0x56,0x36,0x44,0x78,0x1e,0x92,0x41,0x58,0x9a,0x9c,0xcb,0xd8,0x5b,0x1b,0x5c, + 0x1a,0x5b,0x99,0x8b,0x5b,0x18,0x5d,0x9a,0x5d,0xd9,0x17,0xdb,0x9b,0xdb,0xd9,0x17, + 0xdb,0x9b,0xdb,0xd9,0x17,0x59,0xda,0x5c,0x98,0x18,0x5b,0xd9,0x10,0xe1,0x89,0x78, + 0x06,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x6e,0x61,0x74, + 0x69,0x76,0x65,0x5f,0x77,0x69,0x64,0x65,0x5f,0x76,0x65,0x63,0x74,0x6f,0x72,0x73, + 0x5f,0x64,0x69,0x73,0x61,0x62,0x6c,0x65,0x43,0x84,0x67,0x62,0x14,0x96,0x26,0xe7, + 0x22,0x57,0xe6,0x46,0x56,0x26,0xf7,0x45,0x17,0x26,0x77,0x56,0x46,0xc7,0x28,0x2c, + 0x4d,0xce,0x25,0x4c,0xee,0xec,0x8b,0x2e,0x0f,0xae,0xec,0xcb,0x2d,0xac,0xad,0x8c, + 0x86,0x19,0xdb,0x5b,0x18,0x1d,0x0d,0x99,0xb0,0x34,0x39,0x97,0x30,0xb9,0xb3,0x2f, + 0xb7,0xb0,0xb6,0x32,0x2a,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x43, + 0x98,0xa7,0x5a,0x84,0xc7,0x7a,0xae,0x07,0x7b,0xb2,0x21,0xc2,0xa3,0x51,0x0a,0x4b, + 0x93,0x73,0x31,0x93,0x0b,0x3b,0x6b,0x2b,0x73,0xa3,0xfb,0x4a,0x73,0x83,0xab,0xa3, + 0xe3,0x52,0x37,0x57,0x26,0x87,0xc2,0xf6,0x36,0xe6,0x06,0x93,0x42,0x25,0x2c,0x4d, + 0xce,0x65,0xac,0xcc,0x8d,0xae,0x4c,0x8e,0x4f,0x58,0x9a,0x9c,0x0b,0x5c,0x99,0xdc, + 0x1c,0x5c,0xd9,0x18,0x5d,0x9a,0x5d,0x19,0x85,0x3a,0xbb,0x21,0xd2,0x22,0x3c,0xdc, + 0xd3,0x3d,0xde,0xf3,0x3d,0xd6,0x73,0x3d,0xd8,0x03,0x06,0x5c,0xea,0xe6,0xca,0xe4, + 0x50,0xd8,0xde,0xc6,0xdc,0x62,0x52,0x58,0x8c,0xbd,0xb1,0xbd,0xc9,0x0d,0x91,0x96, + 0xe1,0xe1,0x1e,0x31,0x78,0xbc,0xe7,0x7b,0xac,0xe7,0x7a,0xb0,0x67,0x0c,0xb8,0x84, + 0xa5,0xc9,0xb9,0xd0,0x95,0xe1,0xd1,0xd5,0xc9,0x95,0x51,0x0a,0x4b,0x93,0x73,0x61, + 0x7b,0x1b,0x0b,0xa3,0x4b,0x7b,0x73,0xfb,0x4a,0x73,0x23,0x2b,0xc3,0xa3,0x12,0x96, + 0x26,0xe7,0x32,0x17,0xd6,0x06,0xc7,0x56,0x46,0x8c,0xae,0x0c,0x8f,0xae,0x4e,0xae, + 0x4c,0x86,0x8c,0xc7,0x8c,0xed,0x2d,0x8c,0x8e,0x05,0x64,0x2e,0xac,0x0d,0x8e,0xad, + 0xcc,0x87,0x03,0x5d,0x19,0xde,0x10,0x6a,0x21,0x9e,0x32,0x78,0xcc,0x60,0x11,0x96, + 0xe1,0x39,0x83,0xc7,0x7a,0xd0,0xe0,0xc1,0x9e,0x34,0xe0,0x12,0x96,0x26,0xe7,0x32, + 0x17,0xd6,0x06,0xc7,0x56,0x26,0xc7,0x63,0x2e,0xac,0x0d,0x8e,0xad,0x4c,0x8e,0x08, + 0x5d,0x19,0xde,0x54,0x1b,0x1c,0x9b,0xdc,0x10,0x69,0x39,0x9e,0x35,0x78,0xcc,0x60, + 0x11,0x96,0xe1,0xb1,0x1e,0x36,0x78,0xb0,0xa7,0x0d,0x86,0x20,0x4f,0x18,0x3c,0x64, + 0xf0,0xa8,0xc1,0xe3,0x06,0x43,0x0c,0x04,0x78,0xb6,0xe7,0x0d,0x46,0x44,0xec,0xc0, + 0x0e,0xf6,0xd0,0x0e,0x6e,0xd0,0x0e,0xef,0x40,0x0e,0xf5,0xc0,0x0e,0xe5,0xe0,0x06, + 0xe6,0xc0,0x0e,0xe1,0x70,0x0e,0xf3,0x30,0x45,0x08,0x86,0x11,0x0a,0x3b,0xb0,0x83, + 0x3d,0xb4,0x83,0x1b,0xa4,0x03,0x39,0x94,0x83,0x3b,0xd0,0xc3,0x94,0xa0,0x18,0xb1, + 0x84,0x43,0x3a,0xc8,0x83,0x1b,0xd8,0x43,0x39,0xc8,0xc3,0x3c,0xa4,0xc3,0x3b,0xb8, + 0xc3,0x94,0xc0,0x18,0x41,0x85,0x43,0x3a,0xc8,0x83,0x1b,0xb0,0x43,0x38,0xb8,0xc3, + 0x39,0xd4,0x43,0x38,0x9c,0x43,0x39,0xfc,0x82,0x3d,0x94,0x83,0x3c,0xcc,0x43,0x3a, + 0xbc,0x83,0x3b,0x4c,0x09,0x90,0x11,0x53,0x38,0xa4,0x83,0x3c,0xb8,0xc1,0x38,0xbc, + 0x43,0x3b,0xc0,0x43,0x3a,0xb0,0x43,0x39,0xfc,0xc2,0x3b,0xc0,0x03,0x3d,0xa4,0xc3, + 0x3b,0xb8,0xc3,0x3c,0x4c,0x31,0x14,0xc6,0x81,0x24,0x6a,0x04,0x13,0x0e,0xe9,0x20, + 0x0f,0x6e,0x60,0x0e,0xf2,0x10,0x0e,0xe7,0xd0,0x0e,0xe5,0xe0,0x0e,0xf4,0x30,0x25, + 0x80,0x03,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x6d,0x00,0x00,0x00,0x33,0x08,0x80, + 0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80, + 0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80, + 0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88, + 0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78, + 0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03, + 0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f, + 0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c, + 0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39, + 0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b, + 0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60, + 0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87, + 0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e, + 0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c, + 0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6, + 0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94, + 0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03, + 0x3b,0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83,0x74,0x68,0x07, + 0x78,0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f,0xee,0x00,0x0f, + 0xf2,0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33, + 0x20,0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc, + 0xe0,0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c, + 0x83,0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60,0x87,0x77,0x78, + 0x07,0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca, + 0x61,0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21, + 0x1d,0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43, + 0x39,0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87, + 0x79,0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72,0x00,0x00,0x00, + 0x00,0x71,0x20,0x00,0x00,0x08,0x00,0x00,0x00,0x16,0xb0,0x01,0x48,0xe4,0x4b,0x00, + 0xf3,0x2c,0xc4,0x3f,0x11,0xd7,0x44,0x45,0xc4,0x6f,0x0f,0x7e,0x85,0x17,0xb7,0x6d, + 0x00,0x05,0x03,0x20,0x0d,0x0d,0x00,0x00,0x00,0x61,0x20,0x00,0x00,0x16,0x00,0x00, + 0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x14,0xc7,0x22, + 0x80,0x40,0x20,0x88,0x8d,0x00,0x8c,0x25,0x00,0x01,0xa9,0x11,0x80,0x1a,0x20,0x31, + 0x03,0x40,0x61,0x0e,0xe2,0xba,0xae,0x6a,0x06,0x80,0xc0,0x0c,0xc0,0x08,0xc0,0x18, + 0x01,0x08,0x82,0x20,0xfe,0x01,0x00,0x00,0x00,0x83,0x0c,0x0f,0x91,0x8c,0x18,0x28, + 0x42,0x80,0x39,0x4d,0x80,0x2c,0xc9,0x30,0xc8,0x70,0x04,0x8d,0x05,0x91,0x7c,0x66, + 0x1b,0x94,0x00,0xc8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const char _sfons_vs_source_metal_sim[698] = { + 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, + 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, + 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x76, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x20,0x6d,0x76,0x70,0x3b,0x0a,0x20,0x20,0x20, + 0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x20,0x74,0x6d,0x3b,0x0a,0x7d,0x3b, + 0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f, + 0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20, + 0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29, + 0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e, + 0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34, + 0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x5b,0x5b,0x70, + 0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73, + 0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69,0x6e,0x0a,0x7b, + 0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x70,0x6f,0x73,0x69, + 0x74,0x69,0x6f,0x6e,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65, + 0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74, + 0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x20,0x5b,0x5b,0x61,0x74, + 0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20, + 0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x20, + 0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x32,0x29,0x5d,0x5d, + 0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31,0x36,0x20,0x22, + 0x22,0x0a,0x76,0x65,0x72,0x74,0x65,0x78,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f, + 0x75,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x28,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69, + 0x6e,0x20,0x69,0x6e,0x20,0x5b,0x5b,0x73,0x74,0x61,0x67,0x65,0x5f,0x69,0x6e,0x5d, + 0x5d,0x2c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,0x76,0x73,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x73,0x26,0x20,0x5f,0x32,0x30,0x20,0x5b,0x5b,0x62,0x75,0x66, + 0x66,0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20, + 0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20, + 0x7b,0x7d,0x3b,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31,0x36,0x20,0x22,0x22,0x0a, + 0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74, + 0x69,0x6f,0x6e,0x20,0x3d,0x20,0x5f,0x32,0x30,0x2e,0x6d,0x76,0x70,0x20,0x2a,0x20, + 0x69,0x6e,0x2e,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a,0x23,0x6c,0x69, + 0x6e,0x65,0x20,0x31,0x37,0x20,0x22,0x22,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74, + 0x2e,0x75,0x76,0x20,0x3d,0x20,0x5f,0x32,0x30,0x2e,0x74,0x6d,0x20,0x2a,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x2e,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72, + 0x64,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x23, + 0x6c,0x69,0x6e,0x65,0x20,0x31,0x38,0x20,0x22,0x22,0x0a,0x20,0x20,0x20,0x20,0x6f, + 0x75,0x74,0x2e,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x69,0x6e,0x2e,0x63,0x6f, + 0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e, + 0x20,0x6f,0x75,0x74,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +static const char _sfons_fs_source_metal_sim[498] = { + 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, + 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, + 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d, + 0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x5b,0x5b,0x63,0x6f,0x6c,0x6f,0x72,0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x7d, + 0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f, + 0x69,0x6e,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20, + 0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29, + 0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e, + 0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20, + 0x31,0x31,0x20,0x22,0x22,0x0a,0x66,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x20,0x6d, + 0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x28,0x6d, + 0x61,0x69,0x6e,0x30,0x5f,0x69,0x6e,0x20,0x69,0x6e,0x20,0x5b,0x5b,0x73,0x74,0x61, + 0x67,0x65,0x5f,0x69,0x6e,0x5d,0x5d,0x2c,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65, + 0x32,0x64,0x3c,0x66,0x6c,0x6f,0x61,0x74,0x3e,0x20,0x74,0x65,0x78,0x20,0x5b,0x5b, + 0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x30,0x29,0x5d,0x5d,0x2c,0x20,0x73,0x61, + 0x6d,0x70,0x6c,0x65,0x72,0x20,0x74,0x65,0x78,0x53,0x6d,0x70,0x6c,0x72,0x20,0x5b, + 0x5b,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,0x29,0x0a,0x7b, + 0x0a,0x20,0x20,0x20,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6f, + 0x75,0x74,0x20,0x3d,0x20,0x7b,0x7d,0x3b,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31, + 0x31,0x20,0x22,0x22,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x66,0x72,0x61, + 0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34, + 0x28,0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x2c,0x20, + 0x74,0x65,0x78,0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x74,0x65,0x78,0x53,0x6d, + 0x70,0x6c,0x72,0x2c,0x20,0x69,0x6e,0x2e,0x75,0x76,0x2e,0x78,0x79,0x29,0x2e,0x78, + 0x29,0x20,0x2a,0x20,0x69,0x6e,0x2e,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20,0x20, + 0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6f,0x75,0x74,0x3b,0x0a,0x7d,0x0a, + 0x0a,0x00, +}; +#elif defined(SOKOL_D3D11) +static const uint8_t _sfons_vs_bytecode_hlsl4[1008] = { + 0x44,0x58,0x42,0x43,0xf3,0xd9,0xf4,0x58,0x44,0xc2,0xb9,0x96,0x56,0x7c,0xb3,0x71, + 0x84,0xc5,0xde,0x61,0x01,0x00,0x00,0x00,0xf0,0x03,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0x14,0x01,0x00,0x00,0x78,0x01,0x00,0x00,0xe8,0x01,0x00,0x00, + 0x74,0x03,0x00,0x00,0x52,0x44,0x45,0x46,0xd8,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x48,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x04,0xfe,0xff, + 0x10,0x81,0x00,0x00,0xaf,0x00,0x00,0x00,0x3c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x73,0x00,0xab,0xab,0x3c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x60,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x90,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x98,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xa8,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x40,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5f,0x32,0x30,0x5f, + 0x6d,0x76,0x70,0x00,0x02,0x00,0x03,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x5f,0x32,0x30,0x5f,0x74,0x6d,0x00,0x4d,0x69,0x63,0x72,0x6f, + 0x73,0x6f,0x66,0x74,0x20,0x28,0x52,0x29,0x20,0x48,0x4c,0x53,0x4c,0x20,0x53,0x68, + 0x61,0x64,0x65,0x72,0x20,0x43,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x72,0x20,0x31,0x30, + 0x2e,0x31,0x00,0xab,0x49,0x53,0x47,0x4e,0x5c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x50,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x03,0x03,0x00,0x00,0x50,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x54,0x45,0x58,0x43, + 0x4f,0x4f,0x52,0x44,0x00,0xab,0xab,0xab,0x4f,0x53,0x47,0x4e,0x68,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, + 0x50,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x59,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, + 0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0x53,0x56,0x5f,0x50,0x6f,0x73,0x69, + 0x74,0x69,0x6f,0x6e,0x00,0xab,0xab,0xab,0x53,0x48,0x44,0x52,0x84,0x01,0x00,0x00, + 0x40,0x00,0x01,0x00,0x61,0x00,0x00,0x00,0x59,0x00,0x00,0x04,0x46,0x8e,0x20,0x00, + 0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x5f,0x00,0x00,0x03,0xf2,0x10,0x10,0x00, + 0x00,0x00,0x00,0x00,0x5f,0x00,0x00,0x03,0x32,0x10,0x10,0x00,0x01,0x00,0x00,0x00, + 0x5f,0x00,0x00,0x03,0xf2,0x10,0x10,0x00,0x02,0x00,0x00,0x00,0x65,0x00,0x00,0x03, + 0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x65,0x00,0x00,0x03,0xf2,0x20,0x10,0x00, + 0x01,0x00,0x00,0x00,0x67,0x00,0x00,0x04,0xf2,0x20,0x10,0x00,0x02,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x68,0x00,0x00,0x02,0x01,0x00,0x00,0x00,0x38,0x00,0x00,0x08, + 0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x56,0x15,0x10,0x00,0x01,0x00,0x00,0x00, + 0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x32,0x00,0x00,0x0a, + 0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x06,0x10,0x10,0x00,0x01,0x00,0x00,0x00, + 0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x46,0x0e,0x10,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00, + 0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x36,0x00,0x00,0x05,0xf2,0x20,0x10,0x00,0x01,0x00,0x00,0x00, + 0x46,0x1e,0x10,0x00,0x02,0x00,0x00,0x00,0x38,0x00,0x00,0x08,0xf2,0x00,0x10,0x00, + 0x00,0x00,0x00,0x00,0x56,0x15,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x32,0x00,0x00,0x0a,0xf2,0x00,0x10,0x00, + 0x00,0x00,0x00,0x00,0x06,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00, + 0x32,0x00,0x00,0x0a,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0xa6,0x1a,0x10,0x00, + 0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x32,0x00,0x00,0x0a,0xf2,0x20,0x10,0x00, + 0x02,0x00,0x00,0x00,0xf6,0x1f,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00, + 0x3e,0x00,0x00,0x01,0x53,0x54,0x41,0x54,0x74,0x00,0x00,0x00,0x09,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + +}; +static const uint8_t _sfons_fs_bytecode_hlsl4[640] = { + 0x44,0x58,0x42,0x43,0x5e,0xbc,0x77,0xd9,0xc9,0xdf,0x7d,0x8e,0x0c,0x24,0x18,0x66, + 0x36,0xd3,0xf4,0x78,0x01,0x00,0x00,0x00,0x80,0x02,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0xd4,0x00,0x00,0x00,0x20,0x01,0x00,0x00,0x54,0x01,0x00,0x00, + 0x04,0x02,0x00,0x00,0x52,0x44,0x45,0x46,0x98,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x04,0xff,0xff, + 0x10,0x81,0x00,0x00,0x6d,0x00,0x00,0x00,0x5c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x69,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x5f,0x74,0x65,0x78,0x5f,0x73,0x61,0x6d, + 0x70,0x6c,0x65,0x72,0x00,0x74,0x65,0x78,0x00,0x4d,0x69,0x63,0x72,0x6f,0x73,0x6f, + 0x66,0x74,0x20,0x28,0x52,0x29,0x20,0x48,0x4c,0x53,0x4c,0x20,0x53,0x68,0x61,0x64, + 0x65,0x72,0x20,0x43,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x72,0x20,0x31,0x30,0x2e,0x31, + 0x00,0xab,0xab,0xab,0x49,0x53,0x47,0x4e,0x44,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x03,0x00,0x00,0x38,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0f,0x0f,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0xab,0xab,0xab, + 0x4f,0x53,0x47,0x4e,0x2c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x53,0x56,0x5f,0x54,0x61,0x72,0x67,0x65, + 0x74,0x00,0xab,0xab,0x53,0x48,0x44,0x52,0xa8,0x00,0x00,0x00,0x40,0x00,0x00,0x00, + 0x2a,0x00,0x00,0x00,0x5a,0x00,0x00,0x03,0x00,0x60,0x10,0x00,0x00,0x00,0x00,0x00, + 0x58,0x18,0x00,0x04,0x00,0x70,0x10,0x00,0x00,0x00,0x00,0x00,0x55,0x55,0x00,0x00, + 0x62,0x10,0x00,0x03,0x32,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x62,0x10,0x00,0x03, + 0xf2,0x10,0x10,0x00,0x01,0x00,0x00,0x00,0x65,0x00,0x00,0x03,0xf2,0x20,0x10,0x00, + 0x00,0x00,0x00,0x00,0x68,0x00,0x00,0x02,0x01,0x00,0x00,0x00,0x45,0x00,0x00,0x09, + 0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x10,0x10,0x00,0x00,0x00,0x00,0x00, + 0x96,0x73,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x10,0x00,0x00,0x00,0x00,0x00, + 0x36,0x00,0x00,0x05,0x12,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x01,0x40,0x00,0x00, + 0x00,0x00,0x80,0x3f,0x38,0x00,0x00,0x07,0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00, + 0x06,0x0c,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x1e,0x10,0x00,0x01,0x00,0x00,0x00, + 0x3e,0x00,0x00,0x01,0x53,0x54,0x41,0x54,0x74,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + +}; +#elif defined(SOKOL_WGPU) +/* + WebGPU shader blobs: Vertex shader source: - cbuffer params: register(b0) { - float4x4 mvp; - float4x4 tm; - }; - struct vs_in { - float4 pos: POSITION; - float2 uv: TEXCOORD0; - float4 color: COLOR0; - }; - struct vs_out { - float4 uv: TEXCOORD0; - float4 color: COLOR0; - float4 pos: SV_Position; - }; - vs_out main(vs_in inp) { - vs_out outp; - outp.pos = mul(mvp, inp.pos); - outp.uv = mul(tm, float4(inp.uv, 0.0, 1.0)); - outp.color = inp.color; - return outp; - }; + #version 450 - Pixel shader source: + layout(set = 0, binding = 0, std140) uniform vs_params + { + mat4 mvp; + mat4 tm; + } _20; - Texture2D tex: register(t0); - sampler smp: register(s0); - float4 main(float4 uv: TEXCOORD0, float4 color: COLOR0): SV_Target0 { - return float4(1.0, 1.0, 1.0, tex.Sample(smp, uv.xy).r) * color; - } + layout(location = 0) in vec4 position; + layout(location = 0) out vec4 uv; + layout(location = 1) in vec2 texcoord0; + layout(location = 1) out vec4 color; + layout(location = 2) in vec4 color0; + + void main() + { + gl_Position = _20.mvp * position; + uv = _20.tm * vec4(texcoord0, 0.0, 1.0); + color = color0; + } + + Fragment shader source: + + #version 450 + + layout(location = 0, set = 2, binding = 0) uniform sampler2D tex; + + layout(location = 0) out vec4 frag_color; + layout(location = 0) in vec4 uv; + layout(location = 1) in vec4 color; + + void main() + { + frag_color = vec4(1.0, 1.0, 1.0, texture(tex, uv.xy).x) * color; + } */ -static const uint8_t _sfons_vs_bin[] = { - 68, 88, 66, 67, 89, 173, - 124, 225, 74, 102, 159, 55, - 47, 64, 241, 32, 31, 107, - 240, 204, 1, 0, 0, 0, - 244, 3, 0, 0, 5, 0, - 0, 0, 52, 0, 0, 0, - 8, 1, 0, 0, 120, 1, - 0, 0, 236, 1, 0, 0, - 120, 3, 0, 0, 82, 68, - 69, 70, 204, 0, 0, 0, - 1, 0, 0, 0, 68, 0, - 0, 0, 1, 0, 0, 0, - 28, 0, 0, 0, 0, 4, - 254, 255, 0, 17, 0, 0, - 163, 0, 0, 0, 60, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 0, - 112, 97, 114, 97, 109, 115, - 0, 171, 60, 0, 0, 0, - 2, 0, 0, 0, 92, 0, - 0, 0, 128, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 140, 0, 0, 0, - 0, 0, 0, 0, 64, 0, - 0, 0, 2, 0, 0, 0, - 144, 0, 0, 0, 0, 0, - 0, 0, 160, 0, 0, 0, - 64, 0, 0, 0, 64, 0, - 0, 0, 2, 0, 0, 0, - 144, 0, 0, 0, 0, 0, - 0, 0, 109, 118, 112, 0, - 3, 0, 3, 0, 4, 0, - 4, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 116, 109, - 0, 77, 105, 99, 114, 111, - 115, 111, 102, 116, 32, 40, - 82, 41, 32, 72, 76, 83, - 76, 32, 83, 104, 97, 100, - 101, 114, 32, 67, 111, 109, - 112, 105, 108, 101, 114, 32, - 49, 48, 46, 49, 0, 171, - 73, 83, 71, 78, 104, 0, - 0, 0, 3, 0, 0, 0, - 8, 0, 0, 0, 80, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3, 0, - 0, 0, 0, 0, 0, 0, - 15, 15, 0, 0, 89, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3, 0, - 0, 0, 1, 0, 0, 0, - 3, 3, 0, 0, 98, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3, 0, - 0, 0, 2, 0, 0, 0, - 15, 15, 0, 0, 80, 79, - 83, 73, 84, 73, 79, 78, - 0, 84, 69, 88, 67, 79, - 79, 82, 68, 0, 67, 79, - 76, 79, 82, 0, 79, 83, - 71, 78, 108, 0, 0, 0, - 3, 0, 0, 0, 8, 0, - 0, 0, 80, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 3, 0, 0, 0, - 0, 0, 0, 0, 15, 0, - 0, 0, 89, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 3, 0, 0, 0, - 1, 0, 0, 0, 15, 0, - 0, 0, 95, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 3, 0, 0, 0, - 2, 0, 0, 0, 15, 0, - 0, 0, 84, 69, 88, 67, - 79, 79, 82, 68, 0, 67, - 79, 76, 79, 82, 0, 83, - 86, 95, 80, 111, 115, 105, - 116, 105, 111, 110, 0, 171, - 83, 72, 68, 82, 132, 1, - 0, 0, 64, 0, 1, 0, - 97, 0, 0, 0, 89, 0, - 0, 4, 70, 142, 32, 0, - 0, 0, 0, 0, 8, 0, - 0, 0, 95, 0, 0, 3, - 242, 16, 16, 0, 0, 0, - 0, 0, 95, 0, 0, 3, - 50, 16, 16, 0, 1, 0, - 0, 0, 95, 0, 0, 3, - 242, 16, 16, 0, 2, 0, - 0, 0, 101, 0, 0, 3, - 242, 32, 16, 0, 0, 0, - 0, 0, 101, 0, 0, 3, - 242, 32, 16, 0, 1, 0, - 0, 0, 103, 0, 0, 4, - 242, 32, 16, 0, 2, 0, - 0, 0, 1, 0, 0, 0, - 104, 0, 0, 2, 1, 0, - 0, 0, 56, 0, 0, 8, - 242, 0, 16, 0, 0, 0, - 0, 0, 86, 21, 16, 0, - 1, 0, 0, 0, 70, 142, - 32, 0, 0, 0, 0, 0, - 5, 0, 0, 0, 50, 0, - 0, 10, 242, 0, 16, 0, - 0, 0, 0, 0, 70, 142, - 32, 0, 0, 0, 0, 0, - 4, 0, 0, 0, 6, 16, - 16, 0, 1, 0, 0, 0, - 70, 14, 16, 0, 0, 0, - 0, 0, 0, 0, 0, 8, - 242, 32, 16, 0, 0, 0, - 0, 0, 70, 14, 16, 0, - 0, 0, 0, 0, 70, 142, - 32, 0, 0, 0, 0, 0, - 7, 0, 0, 0, 54, 0, - 0, 5, 242, 32, 16, 0, - 1, 0, 0, 0, 70, 30, - 16, 0, 2, 0, 0, 0, - 56, 0, 0, 8, 242, 0, - 16, 0, 0, 0, 0, 0, - 86, 21, 16, 0, 0, 0, - 0, 0, 70, 142, 32, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 50, 0, 0, 10, - 242, 0, 16, 0, 0, 0, - 0, 0, 70, 142, 32, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 6, 16, 16, 0, - 0, 0, 0, 0, 70, 14, - 16, 0, 0, 0, 0, 0, - 50, 0, 0, 10, 242, 0, - 16, 0, 0, 0, 0, 0, - 70, 142, 32, 0, 0, 0, - 0, 0, 2, 0, 0, 0, - 166, 26, 16, 0, 0, 0, - 0, 0, 70, 14, 16, 0, - 0, 0, 0, 0, 50, 0, - 0, 10, 242, 32, 16, 0, - 2, 0, 0, 0, 70, 142, - 32, 0, 0, 0, 0, 0, - 3, 0, 0, 0, 246, 31, - 16, 0, 0, 0, 0, 0, - 70, 14, 16, 0, 0, 0, - 0, 0, 62, 0, 0, 1, - 83, 84, 65, 84, 116, 0, - 0, 0, 9, 0, 0, 0, - 1, 0, 0, 0, 0, 0, - 0, 0, 6, 0, 0, 0, - 7, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 +static const uint8_t _sfons_vs_bytecode_wgpu[1932] = { + 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x08,0x00,0x08,0x00,0x32,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, + 0x02,0x00,0x00,0x00,0x47,0x4c,0x53,0x4c,0x2e,0x73,0x74,0x64,0x2e,0x34,0x35,0x30, + 0x00,0x00,0x00,0x00,0x0e,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0f,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, + 0x00,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x24,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x30,0x00,0x00,0x00, + 0x31,0x00,0x00,0x00,0x07,0x00,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x37,0x00,0x02,0x00,0x00,0x00,0xc2,0x01,0x00,0x00,0x01,0x00,0x00,0x00, + 0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65, + 0x73,0x73,0x65,0x64,0x20,0x63,0x6c,0x69,0x65,0x6e,0x74,0x20,0x76,0x75,0x6c,0x6b, + 0x61,0x6e,0x31,0x30,0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c, + 0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x63,0x6c,0x69,0x65,0x6e, + 0x74,0x20,0x6f,0x70,0x65,0x6e,0x67,0x6c,0x31,0x30,0x30,0x0a,0x2f,0x2f,0x20,0x4f, + 0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64, + 0x20,0x74,0x61,0x72,0x67,0x65,0x74,0x2d,0x65,0x6e,0x76,0x20,0x76,0x75,0x6c,0x6b, + 0x61,0x6e,0x31,0x2e,0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c, + 0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x74,0x61,0x72,0x67,0x65, + 0x74,0x2d,0x65,0x6e,0x76,0x20,0x6f,0x70,0x65,0x6e,0x67,0x6c,0x0a,0x2f,0x2f,0x20, + 0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65, + 0x64,0x20,0x65,0x6e,0x74,0x72,0x79,0x2d,0x70,0x6f,0x69,0x6e,0x74,0x20,0x6d,0x61, + 0x69,0x6e,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31,0x0a,0x00,0x05,0x00,0x04,0x00, + 0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00, + 0x0c,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50,0x65,0x72,0x56,0x65,0x72,0x74,0x65,0x78, + 0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x06,0x00,0x07,0x00, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50,0x6f,0x69,0x6e,0x74, + 0x53,0x69,0x7a,0x65,0x00,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x0c,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x67,0x6c,0x5f,0x43,0x6c,0x69,0x70,0x44,0x69,0x73,0x74,0x61, + 0x6e,0x63,0x65,0x00,0x06,0x00,0x07,0x00,0x0c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x67,0x6c,0x5f,0x43,0x75,0x6c,0x6c,0x44,0x69,0x73,0x74,0x61,0x6e,0x63,0x65,0x00, + 0x05,0x00,0x03,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00, + 0x12,0x00,0x00,0x00,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x00,0x00,0x00, + 0x06,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0x76,0x70,0x00, + 0x06,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x74,0x6d,0x00,0x00, + 0x05,0x00,0x03,0x00,0x14,0x00,0x00,0x00,0x5f,0x32,0x30,0x00,0x05,0x00,0x05,0x00, + 0x19,0x00,0x00,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0x1e,0x00,0x00,0x00,0x75,0x76,0x00,0x00,0x05,0x00,0x05,0x00, + 0x24,0x00,0x00,0x00,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x2c,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x2d,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x00,0x00, + 0x05,0x00,0x05,0x00,0x30,0x00,0x00,0x00,0x67,0x6c,0x5f,0x56,0x65,0x72,0x74,0x65, + 0x78,0x49,0x44,0x00,0x05,0x00,0x06,0x00,0x31,0x00,0x00,0x00,0x67,0x6c,0x5f,0x49, + 0x6e,0x73,0x74,0x61,0x6e,0x63,0x65,0x49,0x44,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x0b,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x47,0x00,0x03,0x00, + 0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x48,0x00,0x04,0x00,0x12,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x12,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00, + 0x48,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x05,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x47,0x00,0x03,0x00,0x12,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x22,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x1e,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x24,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x2c,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x2d,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x30,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x31,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x03,0x00,0x00,0x00,0x21,0x00,0x03,0x00, + 0x04,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x07,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x15,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x1c,0x00,0x04,0x00,0x0b,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x0a,0x00,0x00,0x00,0x1e,0x00,0x06,0x00,0x0c,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x0d,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x0d,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x15,0x00,0x04,0x00, + 0x0f,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, + 0x0f,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x1e,0x00,0x04,0x00, + 0x12,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x13,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x13,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x15,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x18,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x18,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x1c,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x1c,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, + 0x0f,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x17,0x00,0x04,0x00, + 0x22,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x23,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x23,0x00,0x00,0x00,0x24,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x00,0x00,0x80,0x3f,0x3b,0x00,0x04,0x00, + 0x1c,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x18,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x2f,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x2f,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x2f,0x00,0x00,0x00,0x31,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x36,0x00,0x05,0x00, + 0x03,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0xf8,0x00,0x02,0x00,0x06,0x00,0x00,0x00,0x08,0x00,0x04,0x00,0x01,0x00,0x00,0x00, + 0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x15,0x00,0x00,0x00, + 0x16,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x91,0x00,0x05,0x00, + 0x08,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x1a,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x1c,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x0e,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x1d,0x00,0x00,0x00,0x1b,0x00,0x00,0x00, + 0x08,0x00,0x04,0x00,0x01,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x15,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x14,0x00,0x00,0x00, + 0x1f,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x22,0x00,0x00,0x00,0x25,0x00,0x00,0x00, + 0x24,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x28,0x00,0x00,0x00, + 0x25,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x07,0x00,0x00,0x00, + 0x29,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x50,0x00,0x07,0x00, + 0x08,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x29,0x00,0x00,0x00, + 0x26,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x91,0x00,0x05,0x00,0x08,0x00,0x00,0x00, + 0x2b,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x3e,0x00,0x03,0x00, + 0x1e,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x08,0x00,0x04,0x00,0x01,0x00,0x00,0x00, + 0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x2e,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x2c,0x00,0x00,0x00, + 0x2e,0x00,0x00,0x00,0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, }; -static uint8_t _sfons_fs_bin[] = { - 68, 88, 66, 67, 180, 53, - 115, 174, 239, 17, 254, 112, - 63, 104, 217, 123, 150, 145, - 179, 27, 1, 0, 0, 0, - 120, 2, 0, 0, 5, 0, - 0, 0, 52, 0, 0, 0, - 200, 0, 0, 0, 24, 1, - 0, 0, 76, 1, 0, 0, - 252, 1, 0, 0, 82, 68, - 69, 70, 140, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 2, 0, 0, 0, - 28, 0, 0, 0, 0, 4, - 255, 255, 0, 17, 0, 0, - 100, 0, 0, 0, 92, 0, - 0, 0, 3, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 0, - 96, 0, 0, 0, 2, 0, - 0, 0, 5, 0, 0, 0, - 4, 0, 0, 0, 255, 255, - 255, 255, 0, 0, 0, 0, - 1, 0, 0, 0, 13, 0, - 0, 0, 115, 109, 112, 0, - 116, 101, 120, 0, 77, 105, - 99, 114, 111, 115, 111, 102, - 116, 32, 40, 82, 41, 32, - 72, 76, 83, 76, 32, 83, - 104, 97, 100, 101, 114, 32, - 67, 111, 109, 112, 105, 108, - 101, 114, 32, 49, 48, 46, - 49, 0, 73, 83, 71, 78, - 72, 0, 0, 0, 2, 0, - 0, 0, 8, 0, 0, 0, - 56, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 3, 0, 0, 0, 0, 0, - 0, 0, 15, 3, 0, 0, - 65, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 3, 0, 0, 0, 1, 0, - 0, 0, 15, 15, 0, 0, - 84, 69, 88, 67, 79, 79, - 82, 68, 0, 67, 79, 76, - 79, 82, 0, 171, 79, 83, - 71, 78, 44, 0, 0, 0, - 1, 0, 0, 0, 8, 0, - 0, 0, 32, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 3, 0, 0, 0, - 0, 0, 0, 0, 15, 0, - 0, 0, 83, 86, 95, 84, - 97, 114, 103, 101, 116, 0, - 171, 171, 83, 72, 68, 82, - 168, 0, 0, 0, 64, 0, - 0, 0, 42, 0, 0, 0, - 90, 0, 0, 3, 0, 96, - 16, 0, 0, 0, 0, 0, - 88, 24, 0, 4, 0, 112, - 16, 0, 0, 0, 0, 0, - 85, 85, 0, 0, 98, 16, - 0, 3, 50, 16, 16, 0, - 0, 0, 0, 0, 98, 16, - 0, 3, 242, 16, 16, 0, - 1, 0, 0, 0, 101, 0, - 0, 3, 242, 32, 16, 0, - 0, 0, 0, 0, 104, 0, - 0, 2, 1, 0, 0, 0, - 69, 0, 0, 9, 242, 0, - 16, 0, 0, 0, 0, 0, - 70, 16, 16, 0, 0, 0, - 0, 0, 150, 115, 16, 0, - 0, 0, 0, 0, 0, 96, - 16, 0, 0, 0, 0, 0, - 54, 0, 0, 5, 18, 0, - 16, 0, 0, 0, 0, 0, - 1, 64, 0, 0, 0, 0, - 128, 63, 56, 0, 0, 7, - 242, 32, 16, 0, 0, 0, - 0, 0, 6, 12, 16, 0, - 0, 0, 0, 0, 70, 30, - 16, 0, 1, 0, 0, 0, - 62, 0, 0, 1, 83, 84, - 65, 84, 116, 0, 0, 0, - 4, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 3, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0 +static const uint8_t fs_bytecode_wgpu[980] = { + 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x08,0x00,0x08,0x00,0x1e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, + 0x02,0x00,0x00,0x00,0x47,0x4c,0x53,0x4c,0x2e,0x73,0x74,0x64,0x2e,0x34,0x35,0x30, + 0x00,0x00,0x00,0x00,0x0e,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0f,0x00,0x08,0x00,0x04,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, + 0x00,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x1b,0x00,0x00,0x00, + 0x10,0x00,0x03,0x00,0x05,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x07,0x00,0x03,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x37,0x00,0x02,0x00,0x00,0x00, + 0xc2,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64, + 0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x63,0x6c,0x69, + 0x65,0x6e,0x74,0x20,0x76,0x75,0x6c,0x6b,0x61,0x6e,0x31,0x30,0x30,0x0a,0x2f,0x2f, + 0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73, + 0x65,0x64,0x20,0x63,0x6c,0x69,0x65,0x6e,0x74,0x20,0x6f,0x70,0x65,0x6e,0x67,0x6c, + 0x31,0x30,0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50, + 0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x74,0x61,0x72,0x67,0x65,0x74,0x2d, + 0x65,0x6e,0x76,0x20,0x76,0x75,0x6c,0x6b,0x61,0x6e,0x31,0x2e,0x30,0x0a,0x2f,0x2f, + 0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73, + 0x65,0x64,0x20,0x74,0x61,0x72,0x67,0x65,0x74,0x2d,0x65,0x6e,0x76,0x20,0x6f,0x70, + 0x65,0x6e,0x67,0x6c,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65, + 0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x65,0x6e,0x74,0x72,0x79,0x2d, + 0x70,0x6f,0x69,0x6e,0x74,0x20,0x6d,0x61,0x69,0x6e,0x0a,0x23,0x6c,0x69,0x6e,0x65, + 0x20,0x31,0x0a,0x00,0x05,0x00,0x04,0x00,0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, + 0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x0a,0x00,0x00,0x00,0x66,0x72,0x61,0x67, + 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x05,0x00,0x03,0x00,0x0f,0x00,0x00,0x00, + 0x74,0x65,0x78,0x00,0x05,0x00,0x03,0x00,0x12,0x00,0x00,0x00,0x75,0x76,0x00,0x00, + 0x05,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x0a,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x0f,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x0f,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x0f,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x13,0x00,0x02,0x00,0x03,0x00,0x00,0x00,0x21,0x00,0x03,0x00,0x04,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x07,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x17,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x2b,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x80,0x3f, + 0x19,0x00,0x09,0x00,0x0c,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x1b,0x00,0x03,0x00,0x0d,0x00,0x00,0x00,0x0c,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x17,0x00,0x04,0x00,0x13,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x36,0x00,0x05,0x00,0x03,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x06,0x00,0x00,0x00,0x08,0x00,0x04,0x00, + 0x01,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x0d,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x4f,0x00,0x07,0x00, + 0x13,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x14,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x57,0x00,0x05,0x00,0x08,0x00,0x00,0x00, + 0x16,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x51,0x00,0x05,0x00, + 0x07,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x50,0x00,0x07,0x00,0x08,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x0b,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x85,0x00,0x05,0x00, + 0x08,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x1c,0x00,0x00,0x00, + 0x3e,0x00,0x03,0x00,0x0a,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0xfd,0x00,0x01,0x00, + 0x38,0x00,0x01,0x00, }; #elif defined(SOKOL_DUMMY_BACKEND) -static const char* _sfons_vs_src = ""; -static const char* _sfons_fs_src = ""; +static const char* _sfons_vs_source_dummy = ""; +static const char* _sfons_fs_source_dummy = ""; +#else +#error "Please define one of SOKOL_GLCORE33, SOKOL_GLES2, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND!" #endif typedef struct _sfons_t { @@ -646,25 +1591,61 @@ static int _sfons_render_create(void* user_ptr, int width, int height) { shd_desc.attrs[0].name = "position"; shd_desc.attrs[1].name = "texcoord0"; shd_desc.attrs[2].name = "color0"; - shd_desc.attrs[0].sem_name = "POSITION"; + shd_desc.attrs[0].sem_name = "TEXCOORD"; + shd_desc.attrs[0].sem_index = 0; shd_desc.attrs[1].sem_name = "TEXCOORD"; - shd_desc.attrs[2].sem_name = "COLOR"; + shd_desc.attrs[1].sem_index = 1; + shd_desc.attrs[2].sem_name = "TEXCOORD"; + shd_desc.attrs[2].sem_index = 2; sg_shader_uniform_block_desc* ub = &shd_desc.vs.uniform_blocks[0]; ub->size = 128; - ub->uniforms[0].name = "mvp"; - ub->uniforms[0].type = SG_UNIFORMTYPE_MAT4; - ub->uniforms[1].name = "tm"; - ub->uniforms[1].type = SG_UNIFORMTYPE_MAT4; + ub->uniforms[0].name = "vs_params"; + ub->uniforms[0].type = SG_UNIFORMTYPE_FLOAT4; + ub->uniforms[0].array_count = 8; shd_desc.fs.images[0].name = "tex"; shd_desc.fs.images[0].type = SG_IMAGETYPE_2D; - #if defined(SOKOL_D3D11) - shd_desc.vs.byte_code = _sfons_vs_bin; - shd_desc.vs.byte_code_size = sizeof(_sfons_vs_bin); - shd_desc.fs.byte_code = _sfons_fs_bin; - shd_desc.fs.byte_code_size = sizeof(_sfons_fs_bin); + shd_desc.fs.images[0].sampler_type = SG_SAMPLERTYPE_FLOAT; + shd_desc.label = "sokol-fontstash-shader"; + #if defined(SOKOL_GLCORE33) + shd_desc.vs.source = _sfons_vs_source_glsl330; + shd_desc.fs.source = _sfons_fs_source_glsl330; + #elif defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + shd_desc.vs.source = _sfons_vs_source_glsl100; + shd_desc.fs.source = _sfons_fs_source_glsl100; + #elif defined(SOKOL_METAL) + shd_desc.vs.entry = "main0"; + shd_desc.fs.entry = "main0"; + switch (sg_query_backend()) { + case SG_BACKEND_METAL_MACOS: + shd_desc.vs.byte_code = _sfons_vs_bytecode_metal_macos; + shd_desc.vs.byte_code_size = sizeof(_sfons_vs_bytecode_metal_macos); + shd_desc.fs.byte_code = _sfons_fs_bytecode_metal_macos; + shd_desc.fs.byte_code_size = sizeof(_sfons_fs_bytecode_metal_macos); + break; + case SG_BACKEND_METAL_IOS: + shd_desc.vs.byte_code = _sfons_vs_bytecode_metal_ios; + shd_desc.vs.byte_code_size = sizeof(_sfons_vs_bytecode_metal_ios); + shd_desc.fs.byte_code = _sfons_fs_bytecode_metal_ios; + shd_desc.fs.byte_code_size = sizeof(_sfons_fs_bytecode_metal_ios); + break; + default: + shd_desc.vs.source = _sfons_vs_source_metal_sim; + shd_desc.fs.source = _sfons_fs_source_metal_sim; + break; + } + #elif defined(SOKOL_D3D11) + shd_desc.vs.byte_code = _sfons_vs_bytecode_hlsl4; + shd_desc.vs.byte_code_size = sizeof(_sfons_vs_bytecode_hlsl4); + shd_desc.fs.byte_code = _sfons_fs_bytecode_hlsl4; + shd_desc.fs.byte_code_size = sizeof(_sfons_fs_bytecode_hlsl4); + #elif defined(SOKOL_WGPU) + shd_desc.vs.byte_code = _sfons_vs_bytecode_wgpu; + shd_desc.vs.byte_code_size = sizeof(_sfons_vs_bytecode_wgpu); + shd_desc.fs.byte_code = _sfons_fs_bytecode_wgpu; + shd_desc.fs.byte_code_size = sizeof(_sfons_fs_bytecode_wgpu); #else - shd_desc.vs.source = _sfons_vs_src; - shd_desc.fs.source = _sfons_fs_src; + shd_desc.vs.source = _sfons_vs_src_dummy; + shd_desc.fs.source = _sfons_fs_src_dummy; #endif shd_desc.label = "sfons-shader"; sfons->shd = sg_make_shader(&shd_desc); @@ -708,6 +1689,8 @@ static int _sfons_render_resize(void* user_ptr, int width, int height) { static void _sfons_render_update(void* user_ptr, int* rect, const unsigned char* data) { SOKOL_ASSERT(user_ptr && rect && data); + _SOKOL_UNUSED(rect); + _SOKOL_UNUSED(data); _sfons_t* sfons = (_sfons_t*) user_ptr; sfons->img_dirty = true; } diff --git a/thirdparty/sokol/util/sokol_gl.h b/thirdparty/sokol/util/sokol_gl.h index 0a46e64a1f..e7b7b5537e 100755 --- a/thirdparty/sokol/util/sokol_gl.h +++ b/thirdparty/sokol/util/sokol_gl.h @@ -18,6 +18,7 @@ SOKOL_GLES3 SOKOL_D3D11 SOKOL_METAL + SOKOL_WGPU ...optionally provide the following macros to override defaults: @@ -281,7 +282,7 @@ sgl_begin_triangle_strip() sgl_begin_quads() - ...after sgl_begin_*() specifiy vertices: + ...after sgl_begin_*() specify vertices: sgl_v*(...) sgl_v*_t*(...) @@ -576,6 +577,10 @@ SOKOL_API_DECL void sgl_draw(void); #ifdef __cplusplus } /* extern "C" */ + +/* reference-based equivalents for C++ */ +inline void sgl_setup(const sgl_desc_t& desc) { return sgl_setup(&desc); } +inline sgl_pipeline sgl_make_pipeline(const sg_pipeline_desc& desc) { return sgl_make_pipeline(&desc); } #endif #endif /* SOKOL_GL_INCLUDED */ @@ -623,439 +628,1295 @@ SOKOL_API_DECL void sgl_draw(void); #define _sgl_def(val, def) (((val) == 0) ? (def) : (val)) #define _SGL_INIT_COOKIE (0xABCDABCD) -#if defined(SOKOL_GLCORE33) -static const char* _sgl_vs_src = - "#version 330\n" - "uniform mat4 mvp;\n" - "uniform mat4 tm;\n" - "in vec4 position;\n" - "in vec2 texcoord0;\n" - "in vec4 color0;\n" - "out vec4 uv;\n" - "out vec4 color;\n" - "void main() {\n" - " gl_Position = mvp * position;\n" - " uv = tm * vec4(texcoord0, 0.0, 1.0);\n" - " color = color0;\n" - "}\n"; -static const char* _sgl_fs_src = - "#version 330\n" - "uniform sampler2D tex;\n" - "in vec4 uv;\n" - "in vec4 color;\n" - "out vec4 frag_color;\n" - "void main() {\n" - " frag_color = texture(tex, uv.xy) * color;\n" - "}\n"; -#elif defined(SOKOL_GLES2) || defined(SOKOL_GLES3) -static const char* _sgl_vs_src = - "uniform mat4 mvp;\n" - "uniform mat4 tm;\n" - "attribute vec4 position;\n" - "attribute vec2 texcoord0;\n" - "attribute vec4 color0;\n" - "varying vec4 uv;\n" - "varying vec4 color;\n" - "void main() {\n" - " gl_Position = mvp * position;\n" - " uv = tm * vec4(texcoord0, 0.0, 1.0);\n" - " color = color0;\n" - "}\n"; -static const char* _sgl_fs_src = - "precision mediump float;\n" - "uniform sampler2D tex;\n" - "varying vec4 uv;\n" - "varying vec4 color;\n" - "void main() {\n" - " gl_FragColor = texture2D(tex, uv.xy) * color;\n" - "}\n"; -#elif defined(SOKOL_METAL) -static const char* _sgl_vs_src = - "#include \n" - "using namespace metal;\n" - "struct params_t {\n" - " float4x4 mvp;\n" - " float4x4 tm;\n" - "};\n" - "struct vs_in {\n" - " float4 pos [[attribute(0)]];\n" - " float2 uv [[attribute(1)]];\n" - " float4 color [[attribute(2)]];\n" - "};\n" - "struct vs_out {\n" - " float4 pos [[position]];\n" - " float4 uv;\n" - " float4 color;\n" - "};\n" - "vertex vs_out _main(vs_in in [[stage_in]], constant params_t& params [[buffer(0)]]) {\n" - " vs_out out;\n" - " out.pos = params.mvp * in.pos;\n" - " out.uv = params.tm * float4(in.uv, 0.0, 1.0);\n" - " out.color = in.color;\n" - " return out;\n" - "}\n"; -static const char* _sgl_fs_src = - "#include \n" - "using namespace metal;\n" - "struct fs_in {\n" - " float4 uv;\n" - " float4 color;\n" - "};\n" - "fragment float4 _main(fs_in in [[stage_in]], texture2d tex [[texture(0)]], sampler smp [[sampler(0)]]) {\n" - " return tex.sample(smp, in.uv.xy) * in.color;\n" - "}\n"; -#elif defined(SOKOL_D3D11) /* - Shader blobs for D3D11, compiled with: + Embedded source code compiled with: - fxc.exe /T vs_5_0 /Fh vs.h /Gec /O3 vs.hlsl - fxc.exe /T ps_5_0 /Fh fs.h /Gec /O3 fs.hlsl + sokol-shdc -i sgl.glsl -o sgl.h -l glsl330:glsl100:hlsl4:metal_macos:metal_ios:metal_sim:wgpu -b - Vertex shader source: + (not that for Metal and D3D11 byte code, sokol-shdc must be run + on macOS and Windows) - cbuffer params: register(b0) { - float4x4 mvp; - float4x4 tm; - }; - struct vs_in { - float4 pos: POSITION; - float2 uv: TEXCOORD0; - float4 color: COLOR0; - }; - struct vs_out { - float4 uv: TEXCOORD0; - float4 color: COLOR0; - float4 pos: SV_Position; - }; - vs_out main(vs_in inp) { - vs_out outp; - outp.pos = mul(mvp, inp.pos); - outp.uv = mul(tm, float4(inp.uv, 0.0, 1.0)); - outp.color = inp.color; - return outp; - }; + @vs vs + uniform vs_params { + mat4 mvp; + mat4 tm; + }; + in vec4 position; + in vec2 texcoord0; + in vec4 color0; + out vec4 uv; + out vec4 color; + void main() { + gl_Position = mvp * position; + uv = tm * vec4(texcoord0, 0.0, 1.0); + color = color0; + } + @end - Pixel shader source: + @fs fs + uniform sampler2D tex; + in vec4 uv; + in vec4 color; + out vec4 frag_color; + void main() { + frag_color = texture(tex, uv.xy) * color; + } + @end - Texture2D tex: register(t0); - sampler smp: register(s0); - float4 main(float4 uv: TEXCOORD0, float4 color: COLOR0): SV_Target0 { - return tex.Sample(smp, uv.xy) * color; - } + @program sgl vs fs */ -static const uint8_t _sgl_vs_bin[] = { - 68, 88, 66, 67, 239, 161, - 1, 229, 179, 68, 206, 40, - 34, 15, 57, 169, 103, 117, - 134, 191, 1, 0, 0, 0, - 120, 4, 0, 0, 5, 0, - 0, 0, 52, 0, 0, 0, - 104, 1, 0, 0, 216, 1, - 0, 0, 76, 2, 0, 0, - 220, 3, 0, 0, 82, 68, - 69, 70, 44, 1, 0, 0, - 1, 0, 0, 0, 100, 0, - 0, 0, 1, 0, 0, 0, - 60, 0, 0, 0, 0, 5, - 254, 255, 0, 145, 0, 0, - 3, 1, 0, 0, 82, 68, - 49, 49, 60, 0, 0, 0, - 24, 0, 0, 0, 32, 0, - 0, 0, 40, 0, 0, 0, - 36, 0, 0, 0, 12, 0, - 0, 0, 0, 0, 0, 0, - 92, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 1, 0, - 0, 0, 112, 97, 114, 97, - 109, 115, 0, 171, 92, 0, - 0, 0, 2, 0, 0, 0, - 124, 0, 0, 0, 128, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 204, 0, - 0, 0, 0, 0, 0, 0, - 64, 0, 0, 0, 2, 0, - 0, 0, 220, 0, 0, 0, - 0, 0, 0, 0, 255, 255, - 255, 255, 0, 0, 0, 0, - 255, 255, 255, 255, 0, 0, - 0, 0, 0, 1, 0, 0, - 64, 0, 0, 0, 64, 0, - 0, 0, 2, 0, 0, 0, - 220, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 255, - 0, 0, 0, 0, 255, 255, - 255, 255, 0, 0, 0, 0, - 109, 118, 112, 0, 102, 108, - 111, 97, 116, 52, 120, 52, - 0, 171, 171, 171, 3, 0, - 3, 0, 4, 0, 4, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 208, 0, 0, 0, 116, 109, - 0, 77, 105, 99, 114, 111, - 115, 111, 102, 116, 32, 40, - 82, 41, 32, 72, 76, 83, - 76, 32, 83, 104, 97, 100, - 101, 114, 32, 67, 111, 109, - 112, 105, 108, 101, 114, 32, - 49, 48, 46, 49, 0, 171, - 73, 83, 71, 78, 104, 0, - 0, 0, 3, 0, 0, 0, - 8, 0, 0, 0, 80, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3, 0, - 0, 0, 0, 0, 0, 0, - 15, 15, 0, 0, 89, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3, 0, - 0, 0, 1, 0, 0, 0, - 3, 3, 0, 0, 98, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3, 0, - 0, 0, 2, 0, 0, 0, - 15, 15, 0, 0, 80, 79, - 83, 73, 84, 73, 79, 78, - 0, 84, 69, 88, 67, 79, - 79, 82, 68, 0, 67, 79, - 76, 79, 82, 0, 79, 83, - 71, 78, 108, 0, 0, 0, - 3, 0, 0, 0, 8, 0, - 0, 0, 80, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 3, 0, 0, 0, - 0, 0, 0, 0, 15, 0, - 0, 0, 89, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 3, 0, 0, 0, - 1, 0, 0, 0, 15, 0, - 0, 0, 95, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 3, 0, 0, 0, - 2, 0, 0, 0, 15, 0, - 0, 0, 84, 69, 88, 67, - 79, 79, 82, 68, 0, 67, - 79, 76, 79, 82, 0, 83, - 86, 95, 80, 111, 115, 105, - 116, 105, 111, 110, 0, 171, - 83, 72, 69, 88, 136, 1, - 0, 0, 80, 0, 1, 0, - 98, 0, 0, 0, 106, 8, - 0, 1, 89, 0, 0, 4, - 70, 142, 32, 0, 0, 0, - 0, 0, 8, 0, 0, 0, - 95, 0, 0, 3, 242, 16, - 16, 0, 0, 0, 0, 0, - 95, 0, 0, 3, 50, 16, - 16, 0, 1, 0, 0, 0, - 95, 0, 0, 3, 242, 16, - 16, 0, 2, 0, 0, 0, - 101, 0, 0, 3, 242, 32, - 16, 0, 0, 0, 0, 0, - 101, 0, 0, 3, 242, 32, - 16, 0, 1, 0, 0, 0, - 103, 0, 0, 4, 242, 32, - 16, 0, 2, 0, 0, 0, - 1, 0, 0, 0, 104, 0, - 0, 2, 1, 0, 0, 0, - 56, 0, 0, 8, 242, 0, - 16, 0, 0, 0, 0, 0, - 86, 21, 16, 0, 1, 0, - 0, 0, 70, 142, 32, 0, - 0, 0, 0, 0, 5, 0, - 0, 0, 50, 0, 0, 10, - 242, 0, 16, 0, 0, 0, - 0, 0, 70, 142, 32, 0, - 0, 0, 0, 0, 4, 0, - 0, 0, 6, 16, 16, 0, - 1, 0, 0, 0, 70, 14, - 16, 0, 0, 0, 0, 0, - 0, 0, 0, 8, 242, 32, - 16, 0, 0, 0, 0, 0, - 70, 14, 16, 0, 0, 0, - 0, 0, 70, 142, 32, 0, - 0, 0, 0, 0, 7, 0, - 0, 0, 54, 0, 0, 5, - 242, 32, 16, 0, 1, 0, - 0, 0, 70, 30, 16, 0, - 2, 0, 0, 0, 56, 0, - 0, 8, 242, 0, 16, 0, - 0, 0, 0, 0, 86, 21, - 16, 0, 0, 0, 0, 0, - 70, 142, 32, 0, 0, 0, - 0, 0, 1, 0, 0, 0, - 50, 0, 0, 10, 242, 0, - 16, 0, 0, 0, 0, 0, - 70, 142, 32, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 6, 16, 16, 0, 0, 0, - 0, 0, 70, 14, 16, 0, - 0, 0, 0, 0, 50, 0, - 0, 10, 242, 0, 16, 0, - 0, 0, 0, 0, 70, 142, - 32, 0, 0, 0, 0, 0, - 2, 0, 0, 0, 166, 26, - 16, 0, 0, 0, 0, 0, - 70, 14, 16, 0, 0, 0, - 0, 0, 50, 0, 0, 10, - 242, 32, 16, 0, 2, 0, - 0, 0, 70, 142, 32, 0, - 0, 0, 0, 0, 3, 0, - 0, 0, 246, 31, 16, 0, - 0, 0, 0, 0, 70, 14, - 16, 0, 0, 0, 0, 0, - 62, 0, 0, 1, 83, 84, - 65, 84, 148, 0, 0, 0, - 9, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 6, 0, 0, 0, 7, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 + +#if defined(SOKOL_GLCORE33) +static const char _sgl_vs_source_glsl330[415] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x33,0x30,0x0a,0x0a,0x75,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65,0x63,0x34,0x20,0x76,0x73,0x5f,0x70,0x61, + 0x72,0x61,0x6d,0x73,0x5b,0x38,0x5d,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28, + 0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x69,0x6e, + 0x20,0x76,0x65,0x63,0x34,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a, + 0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x75,0x76,0x3b,0x0a,0x6c,0x61,0x79, + 0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31, + 0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f, + 0x72,0x64,0x30,0x3b,0x0a,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f, + 0x6c,0x6f,0x72,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61, + 0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x32,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63, + 0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20, + 0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f, + 0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x6d,0x61,0x74,0x34,0x28, + 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2c,0x20,0x76,0x73, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x73,0x5b,0x32,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72, + 0x61,0x6d,0x73,0x5b,0x33,0x5d,0x29,0x20,0x2a,0x20,0x70,0x6f,0x73,0x69,0x74,0x69, + 0x6f,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x6d,0x61,0x74, + 0x34,0x28,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x34,0x5d,0x2c,0x20, + 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x35,0x5d,0x2c,0x20,0x76,0x73, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x36,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x73,0x5b,0x37,0x5d,0x29,0x20,0x2a,0x20,0x76,0x65,0x63,0x34, + 0x28,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c, + 0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, }; -static uint8_t _sgl_fs_bin[] = { - 68, 88, 66, 67, 145, 182, - 34, 101, 114, 183, 46, 3, - 176, 243, 147, 199, 109, 42, - 196, 114, 1, 0, 0, 0, - 176, 2, 0, 0, 5, 0, - 0, 0, 52, 0, 0, 0, - 232, 0, 0, 0, 56, 1, - 0, 0, 108, 1, 0, 0, - 20, 2, 0, 0, 82, 68, - 69, 70, 172, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 2, 0, 0, 0, - 60, 0, 0, 0, 0, 5, - 255, 255, 0, 145, 0, 0, - 132, 0, 0, 0, 82, 68, - 49, 49, 60, 0, 0, 0, - 24, 0, 0, 0, 32, 0, - 0, 0, 40, 0, 0, 0, - 36, 0, 0, 0, 12, 0, - 0, 0, 0, 0, 0, 0, - 124, 0, 0, 0, 3, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 1, 0, - 0, 0, 128, 0, 0, 0, - 2, 0, 0, 0, 5, 0, - 0, 0, 4, 0, 0, 0, - 255, 255, 255, 255, 0, 0, - 0, 0, 1, 0, 0, 0, - 13, 0, 0, 0, 115, 109, - 112, 0, 116, 101, 120, 0, - 77, 105, 99, 114, 111, 115, - 111, 102, 116, 32, 40, 82, - 41, 32, 72, 76, 83, 76, - 32, 83, 104, 97, 100, 101, - 114, 32, 67, 111, 109, 112, - 105, 108, 101, 114, 32, 49, - 48, 46, 49, 0, 73, 83, - 71, 78, 72, 0, 0, 0, - 2, 0, 0, 0, 8, 0, - 0, 0, 56, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 3, 0, 0, 0, - 0, 0, 0, 0, 15, 3, - 0, 0, 65, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 3, 0, 0, 0, - 1, 0, 0, 0, 15, 15, - 0, 0, 84, 69, 88, 67, - 79, 79, 82, 68, 0, 67, - 79, 76, 79, 82, 0, 171, - 79, 83, 71, 78, 44, 0, - 0, 0, 1, 0, 0, 0, - 8, 0, 0, 0, 32, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3, 0, - 0, 0, 0, 0, 0, 0, - 15, 0, 0, 0, 83, 86, - 95, 84, 97, 114, 103, 101, - 116, 0, 171, 171, 83, 72, - 69, 88, 160, 0, 0, 0, - 80, 0, 0, 0, 40, 0, - 0, 0, 106, 8, 0, 1, - 90, 0, 0, 3, 0, 96, - 16, 0, 0, 0, 0, 0, - 88, 24, 0, 4, 0, 112, - 16, 0, 0, 0, 0, 0, - 85, 85, 0, 0, 98, 16, - 0, 3, 50, 16, 16, 0, - 0, 0, 0, 0, 98, 16, - 0, 3, 242, 16, 16, 0, - 1, 0, 0, 0, 101, 0, - 0, 3, 242, 32, 16, 0, - 0, 0, 0, 0, 104, 0, - 0, 2, 1, 0, 0, 0, - 69, 0, 0, 139, 194, 0, - 0, 128, 67, 85, 21, 0, - 242, 0, 16, 0, 0, 0, - 0, 0, 70, 16, 16, 0, - 0, 0, 0, 0, 70, 126, - 16, 0, 0, 0, 0, 0, - 0, 96, 16, 0, 0, 0, - 0, 0, 56, 0, 0, 7, - 242, 32, 16, 0, 0, 0, - 0, 0, 70, 14, 16, 0, - 0, 0, 0, 0, 70, 30, - 16, 0, 1, 0, 0, 0, - 62, 0, 0, 1, 83, 84, - 65, 84, 148, 0, 0, 0, - 3, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 3, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 +static const char _sgl_fs_source_glsl330[172] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x33,0x30,0x0a,0x0a,0x75,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x32,0x44,0x20, + 0x74,0x65,0x78,0x3b,0x0a,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63, + 0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x76, + 0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a, + 0x69,0x6e,0x20,0x76,0x65,0x63,0x34,0x20,0x75,0x76,0x3b,0x0a,0x69,0x6e,0x20,0x76, + 0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64, + 0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72, + 0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75, + 0x72,0x65,0x28,0x74,0x65,0x78,0x2c,0x20,0x75,0x76,0x2e,0x78,0x79,0x29,0x20,0x2a, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_GLES2) || defined(SOKOL_GLES3) +static const char _sgl_vs_source_glsl100[381] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x31,0x30,0x30,0x0a,0x0a,0x75,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65,0x63,0x34,0x20,0x76,0x73,0x5f,0x70,0x61, + 0x72,0x61,0x6d,0x73,0x5b,0x38,0x5d,0x3b,0x0a,0x61,0x74,0x74,0x72,0x69,0x62,0x75, + 0x74,0x65,0x20,0x76,0x65,0x63,0x34,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, + 0x3b,0x0a,0x76,0x61,0x72,0x79,0x69,0x6e,0x67,0x20,0x76,0x65,0x63,0x34,0x20,0x75, + 0x76,0x3b,0x0a,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x20,0x76,0x65,0x63, + 0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a,0x76,0x61,0x72, + 0x79,0x69,0x6e,0x67,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b, + 0x0a,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x20,0x76,0x65,0x63,0x34,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61, + 0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f, + 0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x6d,0x61,0x74,0x34,0x28,0x76,0x73, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72, + 0x61,0x6d,0x73,0x5b,0x32,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x73,0x5b,0x33,0x5d,0x29,0x20,0x2a,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, + 0x3b,0x0a,0x20,0x20,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x6d,0x61,0x74,0x34,0x28, + 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x34,0x5d,0x2c,0x20,0x76,0x73, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x35,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x73,0x5b,0x36,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72, + 0x61,0x6d,0x73,0x5b,0x37,0x5d,0x29,0x20,0x2a,0x20,0x76,0x65,0x63,0x34,0x28,0x74, + 0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x31, + 0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +static const char _sgl_fs_source_glsl100[210] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x31,0x30,0x30,0x0a,0x70,0x72,0x65, + 0x63,0x69,0x73,0x69,0x6f,0x6e,0x20,0x6d,0x65,0x64,0x69,0x75,0x6d,0x70,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x3b,0x0a,0x70,0x72,0x65,0x63,0x69,0x73,0x69,0x6f,0x6e,0x20, + 0x68,0x69,0x67,0x68,0x70,0x20,0x69,0x6e,0x74,0x3b,0x0a,0x0a,0x75,0x6e,0x69,0x66, + 0x6f,0x72,0x6d,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65, + 0x72,0x32,0x44,0x20,0x74,0x65,0x78,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x79,0x69,0x6e, + 0x67,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x34,0x20,0x75,0x76,0x3b, + 0x0a,0x76,0x61,0x72,0x79,0x69,0x6e,0x67,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76, + 0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64, + 0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c, + 0x5f,0x46,0x72,0x61,0x67,0x44,0x61,0x74,0x61,0x5b,0x30,0x5d,0x20,0x3d,0x20,0x74, + 0x65,0x78,0x74,0x75,0x72,0x65,0x32,0x44,0x28,0x74,0x65,0x78,0x2c,0x20,0x75,0x76, + 0x2e,0x78,0x79,0x29,0x20,0x2a,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x7d,0x0a, + 0x0a,0x00, +}; +#elif defined(SOKOL_METAL) +static const uint8_t _sgl_vs_bytecode_metal_macos[3360] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x20,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x10,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0x54,0xec,0xec,0xa8,0x23,0x83,0x8d, + 0xc7,0xcf,0x6e,0x67,0x94,0x95,0x54,0x94,0xeb,0x98,0x15,0x2a,0x2c,0x54,0xa0,0x4b, + 0x24,0x6f,0x5e,0xed,0x6e,0x76,0x45,0xb7,0x5e,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x45,0x4e,0x44,0x54,0x37,0x00,0x00, + 0x00,0x56,0x41,0x54,0x54,0x22,0x00,0x03,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x00,0x00,0x80,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x01,0x80, + 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x00,0x02,0x80,0x56,0x41,0x54,0x59,0x05,0x00,0x03, + 0x00,0x06,0x04,0x06,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54, + 0xde,0xc0,0x17,0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0xf0,0x0b,0x00,0x00, + 0xff,0xff,0xff,0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0xf9,0x02,0x00,0x00, + 0x0b,0x82,0x20,0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91, + 0x41,0xc8,0x04,0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19, + 0x1e,0x04,0x8b,0x62,0x80,0x14,0x45,0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14, + 0x38,0x08,0x18,0x49,0x0a,0x32,0x44,0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80, + 0x0c,0x19,0x21,0x72,0x24,0x07,0xc8,0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0, + 0x01,0x00,0x00,0x00,0x51,0x18,0x00,0x00,0x81,0x00,0x00,0x00,0x1b,0xc8,0x25,0xf8, + 0xff,0xff,0xff,0xff,0x01,0x90,0x80,0x8a,0x18,0x87,0x77,0x90,0x07,0x79,0x28,0x87, + 0x71,0xa0,0x07,0x76,0xc8,0x87,0x36,0x90,0x87,0x77,0xa8,0x07,0x77,0x20,0x87,0x72, + 0x20,0x87,0x36,0x20,0x87,0x74,0xb0,0x87,0x74,0x20,0x87,0x72,0x68,0x83,0x79,0x88, + 0x07,0x79,0xa0,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07, + 0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c,0x00,0x82,0x1c,0xd2,0x61,0x1e,0xc2,0x41, + 0x1c,0xd8,0xa1,0x1c,0xda,0x80,0x1e,0xc2,0x21,0x1d,0xd8,0xa1,0x0d,0xc6,0x21,0x1c, + 0xd8,0x81,0x1d,0xe6,0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07,0x80,0x60,0x87, + 0x72,0x98,0x87,0x79,0x68,0x03,0x78,0x90,0x87,0x72,0x18,0x87,0x74,0x98,0x87,0x72, + 0x68,0x03,0x73,0x80,0x87,0x76,0x08,0x07,0x72,0x00,0xcc,0x21,0x1c,0xd8,0x61,0x1e, + 0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0xc0,0x1c,0xe4,0x21,0x1c,0xda,0xa1,0x1c,0xda, + 0x00,0x1e,0xde,0x21,0x1d,0xdc,0x81,0x1e,0xca,0x41,0x1e,0xda,0xa0,0x1c,0xd8,0x21, + 0x1d,0xda,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x06,0x77,0x78,0x87,0x36,0x30, + 0x07,0x79,0x08,0x87,0x76,0x28,0x87,0x36,0x80,0x87,0x77,0x48,0x07,0x77,0xa0,0x87, + 0x72,0x90,0x87,0x36,0x28,0x07,0x76,0x48,0x87,0x76,0x68,0x03,0x77,0x78,0x07,0x77, + 0x68,0x03,0x76,0x28,0x87,0x70,0x30,0x07,0x80,0x70,0x87,0x77,0x68,0x83,0x74,0x70, + 0x07,0x73,0x98,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07, + 0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0x40,0x1d,0xea,0xa1, + 0x1d,0xe0,0xa1,0x0d,0xe8,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x1e,0x00,0x73,0x08, + 0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x77,0x78,0x87,0x36,0x70,0x87,0x70,0x70,0x87, + 0x79,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41, + 0x1e,0xea,0xa1,0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xe6,0x21,0x1d,0xce,0xc1,0x1d, + 0xca,0x81,0x1c,0xda,0x40,0x1f,0xca,0x41,0x1e,0xde,0x61,0x1e,0xda,0xc0,0x1c,0xe0, + 0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80, + 0x70,0x87,0x77,0x68,0x03,0x7a,0x90,0x87,0x70,0x80,0x07,0x78,0x48,0x07,0x77,0x38, + 0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00, + 0x62,0x1e,0xe8,0x21,0x1c,0xc6,0x61,0x1d,0xda,0x00,0x1e,0xe4,0xe1,0x1d,0xe8,0xa1, + 0x1c,0xc6,0x81,0x1e,0xde,0x41,0x1e,0xda,0x40,0x1c,0xea,0xc1,0x1c,0xcc,0xa1,0x1c, + 0xe4,0xa1,0x0d,0xe6,0x21,0x1d,0xf4,0xa1,0x1c,0x00,0x3c,0x00,0x88,0x7a,0x70,0x87, + 0x79,0x08,0x07,0x73,0x28,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a, + 0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xea,0x61,0x1e,0xca,0xa1,0x0d, + 0xe6,0xe1,0x1d,0xcc,0x81,0x1e,0xda,0xc0,0x1c,0xd8,0xe1,0x1d,0xc2,0x81,0x1e,0x00, + 0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x36,0x18,0x02,0x01,0x2c,0x40,0x05,0x00, + 0x49,0x18,0x00,0x00,0x01,0x00,0x00,0x00,0x13,0x84,0x40,0x00,0x89,0x20,0x00,0x00, + 0x23,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85,0x04,0x93,0x22,0xa4,0x84, + 0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a,0x8c,0x0b,0x84,0xa4,0x4c, + 0x10,0x44,0x33,0x00,0xc3,0x08,0x04,0x70,0x90,0x34,0x45,0x94,0x30,0xf9,0x0c,0x80, + 0x34,0xf4,0xef,0x50,0x13,0x1a,0x42,0x08,0xc3,0x08,0x02,0x90,0x04,0x61,0x26,0x6a, + 0x1e,0xe8,0x41,0x1e,0xea,0x61,0x1c,0xe8,0xc1,0x0d,0xda,0xa1,0x1c,0xe8,0x21,0x1c, + 0xd8,0x41,0x0f,0xf4,0xa0,0x1d,0xc2,0x81,0x1e,0xe4,0x21,0x1d,0xf0,0x01,0x05,0xe4, + 0x20,0x69,0x8a,0x28,0x61,0xf2,0x2b,0xe9,0x7f,0x80,0x08,0x60,0x24,0x24,0x94,0x32, + 0x88,0x60,0x08,0xa5,0x10,0x61,0x84,0x43,0x68,0x20,0x60,0x8e,0x00,0x0c,0x72,0x60, + 0xcd,0x11,0x80,0xc2,0x20,0x42,0x20,0x0c,0x23,0x10,0xcb,0x08,0x00,0x00,0x00,0x00, + 0x13,0xb2,0x70,0x48,0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60, + 0x87,0x72,0x68,0x83,0x76,0x08,0x87,0x71,0x78,0x87,0x79,0xc0,0x87,0x38,0x80,0x03, + 0x37,0x88,0x83,0x38,0x70,0x03,0x38,0xd8,0x70,0x1b,0xe5,0xd0,0x06,0xf0,0xa0,0x07, + 0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x90,0x0e,0x71, + 0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0,0x06,0xe9,0x80,0x07,0x7a,0x80,0x07,0x7a,0x80, + 0x07,0x6d,0x90,0x0e,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07, + 0x6d,0x90,0x0e,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d, + 0x90,0x0e,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60, + 0x0e,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e, + 0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x71, + 0x60,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x60,0x0f,0x72,0x40, + 0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x73,0x20,0x07, + 0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x74,0x80,0x07,0x7a, + 0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x76,0x40,0x07,0x7a,0x60, + 0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x79,0x60,0x07,0x7a,0x10,0x07, + 0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x6d,0x60,0x0f,0x71,0x20,0x07,0x78, + 0xa0,0x07,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10, + 0x07,0x79,0x20,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07, + 0x6d,0x60,0x0f,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72, + 0x50,0x07,0x76,0xd0,0x06,0xf6,0x50,0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20, + 0x07,0x7a,0x50,0x07,0x71,0x20,0x07,0x6d,0x60,0x0f,0x71,0x00,0x07,0x72,0x40,0x07, + 0x7a,0x10,0x07,0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x6d, + 0xe0,0x0e,0x78,0xa0,0x07,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0x30,0x84,0x49,0x00, + 0x00,0x08,0x00,0x00,0x00,0x00,0x00,0xc8,0x02,0x01,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x32,0x1e,0x98,0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a, + 0x25,0x30,0x02,0x50,0x04,0x05,0x18,0x50,0x80,0x02,0x85,0x50,0x10,0x65,0x50,0x20, + 0xd4,0x46,0x00,0x88,0x8d,0x35,0x28,0x0f,0x01,0x00,0x00,0x00,0x79,0x18,0x00,0x00, + 0x19,0x01,0x00,0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9, + 0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x32,0x28,0x00,0xb3,0x50,0xb9,0x1b,0x43,0x0b,0x93, + 0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62,0x2c,0x81,0x22,0x2c,0x06,0xd9,0x20,0x08,0x0e, + 0x8e,0xad,0x0c,0x84,0x89,0xc9,0xaa,0x09,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d, + 0x24,0x07,0x46,0xc6,0x25,0x86,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac, + 0x25,0x07,0x46,0xc6,0x25,0x86,0xc6,0x25,0x27,0x65,0x88,0xa0,0x10,0x43,0x8c,0x25, + 0x58,0x8e,0x45,0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x51,0x8e,0x25,0x58,0x82, + 0x45,0xe0,0x16,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56, + 0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x50, + 0x12,0x72,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61, + 0x73,0x74,0x5f,0x6d,0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x04, + 0x65,0x61,0x19,0x84,0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98, + 0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1, + 0x7d,0x91,0xa5,0xcd,0x85,0x89,0xb1,0x95,0x0d,0x11,0x94,0x86,0x61,0x10,0x96,0x26, + 0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0xe2,0x16,0x46,0x97,0x66,0x57,0xf6, + 0x45,0xf6,0x56,0x27,0xc6,0x56,0xf6,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44, + 0x50,0x1e,0x92,0x41,0x58,0x9a,0x9c,0xcb,0xd8,0x5b,0x1b,0x5c,0x1a,0x5b,0x99,0x8b, + 0x5b,0x18,0x5d,0x9a,0x5d,0xd9,0x17,0xdb,0x9b,0xdb,0xd9,0x17,0xdb,0x9b,0xdb,0xd9, + 0x17,0x59,0xda,0x5c,0x98,0x18,0x5b,0xd9,0x10,0x41,0x89,0x78,0x06,0x61,0x69,0x72, + 0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x6e,0x61,0x74,0x69,0x76,0x65,0x5f, + 0x77,0x69,0x64,0x65,0x5f,0x76,0x65,0x63,0x74,0x6f,0x72,0x73,0x5f,0x64,0x69,0x73, + 0x61,0x62,0x6c,0x65,0x43,0x04,0x65,0x62,0x14,0x96,0x26,0xe7,0x62,0x57,0x26,0x47, + 0x57,0x86,0xf7,0xf5,0x56,0x47,0x07,0x57,0x47,0xc7,0xa5,0x6e,0xae,0x4c,0x0e,0x85, + 0xed,0x6d,0xcc,0x0d,0x26,0x85,0x51,0x58,0x9a,0x9c,0x4b,0x98,0xdc,0xd9,0x17,0x5d, + 0x1e,0x5c,0xd9,0x97,0x5b,0x58,0x5b,0x19,0x0d,0x33,0xb6,0xb7,0x30,0x3a,0x1a,0x32, + 0x61,0x69,0x72,0x2e,0x61,0x72,0x67,0x5f,0x6e,0x61,0x6d,0x65,0x14,0xea,0xec,0x86, + 0x30,0x4a,0xa5,0x58,0xca,0xa5,0x60,0x4a,0xa6,0x68,0x5c,0xea,0xe6,0xca,0xe4,0x50, + 0xd8,0xde,0xc6,0xdc,0x62,0x52,0x58,0x8c,0xbd,0xb1,0xbd,0xc9,0x0d,0x61,0x94,0x4a, + 0xe1,0x94,0x4b,0xc1,0x94,0x4c,0xe9,0xc8,0x84,0xa5,0xc9,0xb9,0xc0,0xbd,0xcd,0xa5, + 0xd1,0xa5,0xbd,0xb9,0x71,0x39,0x63,0xfb,0x82,0x7a,0x9b,0x4b,0xa3,0x4b,0x7b,0x73, + 0x1b,0xa2,0x28,0x9f,0x72,0x29,0x98,0x92,0x29,0x60,0x30,0xc4,0x50,0x36,0xc5,0x53, + 0xc2,0x80,0x50,0x58,0x9a,0x9c,0x8b,0x5d,0x99,0x1c,0x5d,0x19,0xde,0x57,0x9a,0x1b, + 0x5c,0x1d,0x1d,0xa5,0xb0,0x34,0x39,0x17,0xb6,0xb7,0xb1,0x30,0xba,0xb4,0x37,0xb7, + 0xaf,0x34,0x37,0xb2,0x32,0x3c,0x66,0x67,0x65,0x6e,0x65,0x72,0x61,0x74,0x65,0x64, + 0x28,0x38,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x44,0x76,0x34,0x5f,0x66,0x29, + 0x44,0xe0,0xde,0xe6,0xd2,0xe8,0xd2,0xde,0xdc,0x86,0x50,0x8b,0xa0,0x8c,0x81,0x42, + 0x06,0x8b,0xb0,0x04,0x4a,0x19,0x28,0x97,0x82,0x29,0x99,0x62,0x06,0xd4,0xce,0xca, + 0xdc,0xca,0xe4,0xc2,0xe8,0xca,0xc8,0x50,0x72,0xe8,0xca,0xf0,0xc6,0xde,0xde,0xe4, + 0xc8,0x60,0x88,0xec,0x64,0xbe,0xcc,0x52,0x68,0x98,0xb1,0xbd,0x85,0xd1,0xc9,0x30, + 0xa1,0x2b,0xc3,0x1b,0x7b,0x7b,0x93,0x23,0x83,0x19,0x42,0x2d,0x81,0x32,0x06,0x0a, + 0x19,0x2c,0xc1,0x12,0x28,0x68,0xa0,0x5c,0x4a,0x1a,0x28,0x99,0xa2,0x06,0xbc,0xce, + 0xca,0xdc,0xca,0xe4,0xc2,0xe8,0xca,0xc8,0x50,0x6c,0xc6,0xde,0xd8,0xde,0xe4,0x60, + 0x88,0xec,0x68,0xbe,0xcc,0x52,0x68,0x8c,0xbd,0xb1,0xbd,0xc9,0xc1,0x0c,0xa1,0x96, + 0x41,0x19,0x03,0x85,0x0c,0x96,0x61,0x09,0x14,0x36,0x50,0x2e,0x05,0x53,0x32,0xa5, + 0x0d,0xa8,0x84,0xa5,0xc9,0xb9,0x88,0xd5,0x99,0x99,0x95,0xc9,0xf1,0x09,0x4b,0x93, + 0x73,0x11,0xab,0x33,0x33,0x2b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x23,0x12,0x96,0x26, + 0xe7,0x22,0x57,0x16,0x46,0x46,0x2a,0x2c,0x4d,0xce,0x65,0x8e,0x4e,0xae,0x6e,0x8c, + 0xee,0x8b,0x2e,0x0f,0xae,0xec,0x2b,0xcd,0xcd,0xec,0x8d,0x88,0x19,0xdb,0x5b,0x18, + 0x1d,0x0d,0x1e,0x0d,0x87,0x36,0x3b,0x38,0x0a,0x74,0x6d,0x43,0xa8,0x45,0x58,0x88, + 0x45,0x50,0xe6,0x40,0xa1,0x83,0x85,0x58,0x88,0x45,0x50,0xe6,0x40,0xa9,0x03,0x46, + 0x61,0x69,0x72,0x2e,0x61,0x72,0x67,0x5f,0x74,0x79,0x70,0x65,0x5f,0x73,0x69,0x7a, + 0x65,0xbc,0xc2,0xd2,0xe4,0x5c,0xc2,0xe4,0xce,0xbe,0xe8,0xf2,0xe0,0xca,0xbe,0xc2, + 0xd8,0xd2,0xce,0xdc,0xbe,0xe6,0xd2,0xf4,0xca,0x98,0xd8,0xcd,0x7d,0xc1,0x85,0xc9, + 0x85,0xb5,0xcd,0x71,0xf8,0x92,0x81,0x19,0x42,0x06,0x0b,0xa2,0xbc,0x81,0x02,0x07, + 0x4b,0xa1,0x90,0xc1,0x22,0x2c,0x81,0x12,0x07,0x8a,0x1c,0x28,0x76,0xa0,0xdc,0xc1, + 0x52,0x28,0x78,0xb0,0x24,0xca,0xa5,0xe4,0x81,0x92,0x29,0x7a,0x30,0x04,0x51,0xce, + 0x40,0x59,0x03,0xc5,0x0d,0x94,0x3d,0x18,0x62,0x24,0x80,0x22,0x06,0x0a,0x1f,0xf0, + 0x79,0x6b,0x73,0x4b,0x83,0x7b,0xa3,0x2b,0x73,0xa3,0x03,0x19,0x43,0x0b,0x93,0xe3, + 0x33,0x95,0xd6,0x06,0xc7,0x56,0x06,0x32,0xb4,0xb2,0x02,0x42,0x25,0x14,0x14,0x34, + 0x44,0x50,0xfe,0x60,0x88,0xa1,0xf8,0x81,0x02,0x0a,0x8d,0x32,0xc4,0x50,0x42,0x41, + 0x09,0x85,0x46,0x19,0x11,0xb1,0x03,0x3b,0xd8,0x43,0x3b,0xb8,0x41,0x3b,0xbc,0x03, + 0x39,0xd4,0x03,0x3b,0x94,0x83,0x1b,0x98,0x03,0x3b,0x84,0xc3,0x39,0xcc,0xc3,0x14, + 0x21,0x18,0x46,0x28,0xec,0xc0,0x0e,0xf6,0xd0,0x0e,0x6e,0x90,0x0e,0xe4,0x50,0x0e, + 0xee,0x40,0x0f,0x53,0x82,0x62,0xc4,0x12,0x0e,0xe9,0x20,0x0f,0x6e,0x60,0x0f,0xe5, + 0x20,0x0f,0xf3,0x90,0x0e,0xef,0xe0,0x0e,0x53,0x02,0x63,0x04,0x15,0x0e,0xe9,0x20, + 0x0f,0x6e,0xc0,0x0e,0xe1,0xe0,0x0e,0xe7,0x50,0x0f,0xe1,0x70,0x0e,0xe5,0xf0,0x0b, + 0xf6,0x50,0x0e,0xf2,0x30,0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x25,0x40,0x46,0x4c,0xe1, + 0x90,0x0e,0xf2,0xe0,0x06,0xe3,0xf0,0x0e,0xed,0x00,0x0f,0xe9,0xc0,0x0e,0xe5,0xf0, + 0x0b,0xef,0x00,0x0f,0xf4,0x90,0x0e,0xef,0xe0,0x0e,0xf3,0x30,0xc5,0x50,0x18,0x07, + 0x92,0xa8,0x11,0x4a,0x38,0xa4,0x83,0x3c,0xb8,0x81,0x3d,0x94,0x83,0x3c,0xd0,0x43, + 0x39,0xe0,0xc3,0x94,0xa0,0x0f,0x00,0x00,0x79,0x18,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3, + 0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10, + 0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30, + 0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03, + 0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07, + 0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e, + 0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d, + 0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b, + 0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76, + 0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90, + 0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87, + 0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e, + 0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c, + 0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca, + 0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8, + 0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82, + 0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83, + 0x74,0x68,0x07,0x78,0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f, + 0xee,0x00,0x0f,0xf2,0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec, + 0x50,0x0e,0x33,0x20,0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc, + 0x81,0x1e,0xdc,0xe0,0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0, + 0x43,0x3a,0x9c,0x83,0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60, + 0x87,0x77,0x78,0x07,0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e, + 0x6a,0x1e,0xca,0x61,0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1, + 0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43, + 0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c, + 0xc5,0x0a,0x87,0x79,0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72, + 0x00,0x00,0x00,0x00,0x71,0x20,0x00,0x00,0x02,0x00,0x00,0x00,0x06,0x50,0x30,0x00, + 0xd2,0xd0,0x00,0x00,0x61,0x20,0x00,0x00,0x3d,0x00,0x00,0x00,0x13,0x04,0x41,0x2c, + 0x10,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0xf4,0xc6,0x22,0x86,0x61,0x18,0xc6,0x22, + 0x04,0x41,0x10,0xc6,0x22,0x82,0x20,0x08,0xa8,0x95,0x40,0x19,0x14,0x01,0xbd,0x11, + 0x00,0x1a,0x33,0x00,0x24,0x66,0x00,0x28,0xcc,0x00,0x00,0x00,0xe3,0x15,0x0b,0x84, + 0x61,0x10,0x05,0x65,0x90,0x01,0x1a,0x0c,0x13,0x02,0xf9,0x8c,0x57,0x3c,0x14,0xc7, + 0x2d,0x14,0x94,0x41,0x06,0xea,0x70,0x4c,0x08,0xe4,0x63,0x41,0x01,0x9f,0xf1,0x0a, + 0x2a,0x0b,0x83,0x30,0x70,0x28,0x28,0x83,0x0c,0x19,0x43,0x99,0x10,0xc8,0xc7,0x8a, + 0x00,0x3e,0xe3,0x15,0x99,0x67,0x06,0x66,0x40,0x51,0x50,0x06,0x19,0xbc,0x48,0x33, + 0x21,0x90,0x8f,0x15,0x01,0x7c,0xc6,0x2b,0xbc,0x31,0x60,0x83,0x35,0x18,0x03,0x0a, + 0xca,0x20,0x83,0x18,0x60,0x99,0x09,0x81,0x7c,0xc6,0x2b,0xc4,0xe0,0x0c,0xe0,0xe0, + 0x0d,0x3c,0x0a,0xca,0x20,0x83,0x19,0x70,0x61,0x60,0x42,0x20,0x1f,0x0b,0x0a,0xf8, + 0x8c,0x57,0x9c,0x01,0x1b,0xd4,0x01,0x1d,0x88,0x01,0x05,0xc5,0x86,0x00,0x3e,0xb3, + 0x0d,0x61,0x10,0x00,0xb3,0x0d,0x01,0x1b,0x04,0xb3,0x0d,0xc1,0x23,0x64,0x10,0x10, + 0x03,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x5b,0x86,0x20,0x10,0x85,0x2d,0x43,0x11, + 0x88,0xc2,0x96,0x41,0x09,0x44,0x61,0xcb,0xf0,0x04,0xa2,0xb0,0x65,0xa0,0x02,0x51, + 0xd8,0x32,0x60,0x81,0x28,0x6c,0x19,0xba,0x40,0x14,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sgl_fs_bytecode_metal_macos[2909] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x5d,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd5,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x80,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0xc3,0xf2,0x7e,0x47,0xcc,0xf2,0x87, + 0xc2,0x27,0x51,0x05,0xdb,0xac,0xc5,0x2d,0x03,0xfc,0xe6,0x91,0x1c,0x38,0xc9,0xd8, + 0x34,0x4c,0xa7,0xd1,0x68,0xc2,0x62,0x41,0xf6,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x45,0x4e,0x44,0x54,0x04,0x00,0x00, + 0x00,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17, + 0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x64,0x0a,0x00,0x00,0xff,0xff,0xff, + 0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0x96,0x02,0x00,0x00,0x0b,0x82,0x20, + 0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04, + 0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b, + 0x62,0x80,0x14,0x45,0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18, + 0x49,0x0a,0x32,0x44,0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21, + 0x72,0x24,0x07,0xc8,0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00, + 0x00,0x51,0x18,0x00,0x00,0x89,0x00,0x00,0x00,0x1b,0xcc,0x25,0xf8,0xff,0xff,0xff, + 0xff,0x01,0x60,0x00,0x09,0xa8,0x88,0x71,0x78,0x07,0x79,0x90,0x87,0x72,0x18,0x07, + 0x7a,0x60,0x87,0x7c,0x68,0x03,0x79,0x78,0x87,0x7a,0x70,0x07,0x72,0x28,0x07,0x72, + 0x68,0x03,0x72,0x48,0x07,0x7b,0x48,0x07,0x72,0x28,0x87,0x36,0x98,0x87,0x78,0x90, + 0x07,0x7a,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xcc, + 0x21,0x1c,0xd8,0x61,0x1e,0xca,0x01,0x20,0xc8,0x21,0x1d,0xe6,0x21,0x1c,0xc4,0x81, + 0x1d,0xca,0xa1,0x0d,0xe8,0x21,0x1c,0xd2,0x81,0x1d,0xda,0x60,0x1c,0xc2,0x81,0x1d, + 0xd8,0x61,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x76,0x28,0x87, + 0x79,0x98,0x87,0x36,0x80,0x07,0x79,0x28,0x87,0x71,0x48,0x87,0x79,0x28,0x87,0x36, + 0x30,0x07,0x78,0x68,0x87,0x70,0x20,0x07,0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c, + 0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xcc,0x41,0x1e,0xc2,0xa1,0x1d,0xca,0xa1,0x0d,0xe0, + 0xe1,0x1d,0xd2,0xc1,0x1d,0xe8,0xa1,0x1c,0xe4,0xa1,0x0d,0xca,0x81,0x1d,0xd2,0xa1, + 0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x60,0x70,0x87,0x77,0x68,0x03,0x73,0x90, + 0x87,0x70,0x68,0x87,0x72,0x68,0x03,0x78,0x78,0x87,0x74,0x70,0x07,0x7a,0x28,0x07, + 0x79,0x68,0x83,0x72,0x60,0x87,0x74,0x68,0x87,0x36,0x70,0x87,0x77,0x70,0x87,0x36, + 0x60,0x87,0x72,0x08,0x07,0x73,0x00,0x08,0x77,0x78,0x87,0x36,0x48,0x07,0x77,0x30, + 0x87,0x79,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8, + 0x41,0x1e,0xea,0xa1,0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xd4,0xa1,0x1e,0xda,0x01, + 0x1e,0xda,0x80,0x1e,0xc2,0x41,0x1c,0xd8,0xa1,0x1c,0xe6,0x01,0x30,0x87,0x70,0x60, + 0x87,0x79,0x28,0x07,0x80,0x70,0x87,0x77,0x68,0x03,0x77,0x08,0x07,0x77,0x98,0x87, + 0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1, + 0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0x60,0x1e,0xd2,0xe1,0x1c,0xdc,0xa1,0x1c, + 0xc8,0xa1,0x0d,0xf4,0xa1,0x1c,0xe4,0xe1,0x1d,0xe6,0xa1,0x0d,0xcc,0x01,0x1e,0xda, + 0xa0,0x1d,0xc2,0x81,0x1e,0xd0,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x08,0x77, + 0x78,0x87,0x36,0xa0,0x07,0x79,0x08,0x07,0x78,0x80,0x87,0x74,0x70,0x87,0x73,0x68, + 0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xe6, + 0x81,0x1e,0xc2,0x61,0x1c,0xd6,0xa1,0x0d,0xe0,0x41,0x1e,0xde,0x81,0x1e,0xca,0x61, + 0x1c,0xe8,0xe1,0x1d,0xe4,0xa1,0x0d,0xc4,0xa1,0x1e,0xcc,0xc1,0x1c,0xca,0x41,0x1e, + 0xda,0x60,0x1e,0xd2,0x41,0x1f,0xca,0x01,0xc0,0x03,0x80,0xa8,0x07,0x77,0x98,0x87, + 0x70,0x30,0x87,0x72,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74, + 0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0xa2,0x1e,0xe6,0xa1,0x1c,0xda,0x60,0x1e, + 0xde,0xc1,0x1c,0xe8,0xa1,0x0d,0xcc,0x81,0x1d,0xde,0x21,0x1c,0xe8,0x01,0x30,0x87, + 0x70,0x60,0x87,0x79,0x28,0x07,0x60,0x83,0x21,0x0c,0xc0,0x02,0x54,0x1b,0x8c,0x81, + 0x00,0x16,0xa0,0xda,0x80,0x10,0xff,0xff,0xff,0xff,0x3f,0x00,0x0c,0x20,0x01,0xd5, + 0x06,0xa3,0x08,0x80,0x05,0xa8,0x36,0x18,0x86,0x00,0x2c,0x40,0x05,0x49,0x18,0x00, + 0x00,0x03,0x00,0x00,0x00,0x13,0x86,0x40,0x18,0x26,0x0c,0x44,0x61,0x00,0x00,0x00, + 0x00,0x89,0x20,0x00,0x00,0x20,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85, + 0x04,0x93,0x22,0xa4,0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a, + 0x8c,0x0b,0x84,0xa4,0x4c,0x10,0x48,0x33,0x00,0xc3,0x08,0x04,0x70,0x90,0x34,0x45, + 0x94,0x30,0xf9,0x0c,0x80,0x34,0xf4,0xef,0x50,0x13,0x0a,0xc2,0x51,0xd2,0x14,0x51, + 0xc2,0xe4,0xff,0x13,0x71,0x4d,0x54,0x44,0xfc,0xf6,0xf0,0x4f,0x63,0x04,0xc0,0x20, + 0xc2,0x10,0x5c,0x24,0x4d,0x11,0x25,0x4c,0xfe,0x2f,0x01,0xcc,0xb3,0x10,0xd1,0x3f, + 0x8d,0x11,0x00,0x83,0x08,0x85,0x50,0x0a,0x11,0x02,0x31,0x74,0x86,0x11,0x04,0x60, + 0x8e,0x20,0x98,0x23,0x00,0x83,0x61,0x04,0x61,0x29,0x48,0x20,0x26,0x29,0xa6,0x00, + 0xb5,0x81,0x80,0x1c,0x58,0x23,0x00,0x00,0x00,0x13,0xb2,0x70,0x48,0x07,0x79,0xb0, + 0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68,0x83,0x76,0x08,0x87, + 0x71,0x78,0x87,0x79,0xc0,0x87,0x38,0x80,0x03,0x37,0x88,0x83,0x38,0x70,0x03,0x38, + 0xd8,0x70,0x1b,0xe5,0xd0,0x06,0xf0,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74, + 0xa0,0x07,0x76,0x40,0x07,0x6d,0x90,0x0e,0x71,0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0, + 0x06,0xe9,0x80,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90,0x0e,0x71,0x60,0x07, + 0x7a,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x90,0x0e,0x73,0x20,0x07,0x7a, + 0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x76,0x40,0x07,0x7a,0x60, + 0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0e,0x73,0x20,0x07,0x7a,0x30,0x07, + 0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e,0x76,0x40,0x07,0x7a,0x60,0x07,0x74, + 0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xa0, + 0x07,0x71,0x60,0x07,0x6d,0x60,0x0f,0x72,0x40,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07, + 0x73,0x20,0x07,0x6d,0x60,0x0f,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73, + 0x20,0x07,0x6d,0x60,0x0f,0x74,0x80,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40, + 0x07,0x6d,0x60,0x0f,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07, + 0x6d,0x60,0x0f,0x79,0x60,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72, + 0x80,0x07,0x6d,0x60,0x0f,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0, + 0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x79,0x20,0x07,0x7a,0x20,0x07, + 0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f,0x72,0x50,0x07,0x76, + 0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xd0,0x06,0xf6,0x50, + 0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20,0x07, + 0x6d,0x60,0x0f,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07,0x70,0x20,0x07,0x74, + 0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x6d,0xe0,0x0e,0x78,0xa0,0x07,0x71,0x60, + 0x07,0x7a,0x30,0x07,0x72,0x30,0x84,0x41,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00, + 0x18,0xc2,0x38,0x40,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x64,0x81,0x00,0x00,0x00, + 0x00,0x08,0x00,0x00,0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26, + 0x47,0xc6,0x04,0x43,0x5a,0x25,0x30,0x02,0x50,0x04,0x85,0x50,0x10,0x65,0x40,0x70, + 0xac,0x41,0x79,0x08,0x00,0x79,0x18,0x00,0x00,0xd7,0x00,0x00,0x00,0x1a,0x03,0x4c, + 0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x42, + 0x3c,0x00,0x84,0x50,0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62, + 0x2c,0xc2,0x23,0x2c,0x05,0xd9,0x20,0x08,0x0e,0x8e,0xad,0x0c,0x84,0x89,0xc9,0xaa, + 0x09,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x24,0x07,0x46,0xc6,0x25,0x86,0x06, + 0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x25,0x07,0x46,0xc6,0x25,0x86,0xc6, + 0x25,0x27,0x65,0x88,0xf0,0x10,0x43,0x8c,0x45,0x58,0x8c,0x65,0x60,0xd1,0x54,0x46, + 0x17,0xc6,0x36,0x04,0x79,0x8e,0x45,0x58,0x84,0x65,0xe0,0x16,0x96,0x26,0xe7,0x32, + 0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56,0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45, + 0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x78,0x12,0x72,0x61,0x69,0x72,0x2e,0x63, + 0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61,0x73,0x74,0x5f,0x6d,0x61,0x74,0x68, + 0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x84,0x67,0x61,0x19,0x84,0xa5,0xc9,0xb9, + 0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98,0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99, + 0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1,0x7d,0x91,0xa5,0xcd,0x85,0x89,0xb1, + 0x95,0x0d,0x11,0x9e,0x86,0x61,0x10,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6, + 0x56,0xe6,0xe2,0x16,0x46,0x97,0x66,0x57,0xf6,0x45,0xf6,0x56,0x27,0xc6,0x56,0xf6, + 0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x78,0x1e,0x92,0x41,0x58,0x9a,0x9c, + 0xcb,0xd8,0x5b,0x1b,0x5c,0x1a,0x5b,0x99,0x8b,0x5b,0x18,0x5d,0x9a,0x5d,0xd9,0x17, + 0xdb,0x9b,0xdb,0xd9,0x17,0xdb,0x9b,0xdb,0xd9,0x17,0x59,0xda,0x5c,0x98,0x18,0x5b, + 0xd9,0x10,0xe1,0x89,0x78,0x06,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c, + 0x65,0x2e,0x6e,0x61,0x74,0x69,0x76,0x65,0x5f,0x77,0x69,0x64,0x65,0x5f,0x76,0x65, + 0x63,0x74,0x6f,0x72,0x73,0x5f,0x64,0x69,0x73,0x61,0x62,0x6c,0x65,0x43,0x84,0x67, + 0x62,0x14,0x96,0x26,0xe7,0x22,0x57,0xe6,0x46,0x56,0x26,0xf7,0x45,0x17,0x26,0x77, + 0x56,0x46,0xc7,0x28,0x2c,0x4d,0xce,0x25,0x4c,0xee,0xec,0x8b,0x2e,0x0f,0xae,0xec, + 0xcb,0x2d,0xac,0xad,0x8c,0x86,0x19,0xdb,0x5b,0x18,0x1d,0x0d,0x99,0xb0,0x34,0x39, + 0x97,0x30,0xb9,0xb3,0x2f,0xb7,0xb0,0xb6,0x32,0x2a,0x66,0x72,0x61,0x67,0x5f,0x63, + 0x6f,0x6c,0x6f,0x72,0x43,0x98,0xa7,0x5a,0x86,0xc7,0x7a,0xae,0x07,0x7b,0xb2,0x21, + 0xc2,0xa3,0x51,0x0a,0x4b,0x93,0x73,0x31,0x93,0x0b,0x3b,0x6b,0x2b,0x73,0xa3,0xfb, + 0x4a,0x73,0x83,0xab,0xa3,0xe3,0x52,0x37,0x57,0x26,0x87,0xc2,0xf6,0x36,0xe6,0x06, + 0x93,0x42,0x25,0x2c,0x4d,0xce,0x65,0xac,0xcc,0x8d,0xae,0x4c,0x8e,0x4f,0x58,0x9a, + 0x9c,0x0b,0x5c,0x99,0xdc,0x1c,0x5c,0xd9,0x18,0x5d,0x9a,0x5d,0x19,0x85,0x3a,0xbb, + 0x21,0xd2,0x32,0x3c,0xdc,0xd3,0x3d,0xde,0xf3,0x3d,0xd6,0x73,0x3d,0xd8,0x03,0x06, + 0x5c,0xea,0xe6,0xca,0xe4,0x50,0xd8,0xde,0xc6,0xdc,0x62,0x52,0x58,0x8c,0xbd,0xb1, + 0xbd,0xc9,0x0d,0x91,0x16,0xe1,0xe1,0x1e,0x31,0x78,0xbc,0xe7,0x7b,0xac,0xe7,0x7a, + 0xb0,0x67,0x0c,0xb8,0x84,0xa5,0xc9,0xb9,0xd0,0x95,0xe1,0xd1,0xd5,0xc9,0x95,0x51, + 0x0a,0x4b,0x93,0x73,0x61,0x7b,0x1b,0x0b,0xa3,0x4b,0x7b,0x73,0xfb,0x4a,0x73,0x23, + 0x2b,0xc3,0xa3,0x12,0x96,0x26,0xe7,0x32,0x17,0xd6,0x06,0xc7,0x56,0x46,0x8c,0xae, + 0x0c,0x8f,0xae,0x4e,0xae,0x4c,0x86,0x8c,0xc7,0x8c,0xed,0x2d,0x8c,0x8e,0x05,0x64, + 0x2e,0xac,0x0d,0x8e,0xad,0xcc,0x87,0x03,0x5d,0x19,0xde,0x10,0x6a,0x21,0x9e,0x32, + 0x78,0xcc,0x60,0x19,0x16,0xe1,0x39,0x83,0xc7,0x7a,0xd0,0xe0,0xc1,0x9e,0x34,0xe0, + 0x12,0x96,0x26,0xe7,0x32,0x17,0xd6,0x06,0xc7,0x56,0x26,0xc7,0x63,0x2e,0xac,0x0d, + 0x8e,0xad,0x4c,0x8e,0x08,0x5d,0x19,0xde,0x54,0x1b,0x1c,0x9b,0xdc,0x10,0x69,0x39, + 0x9e,0x35,0x78,0xcc,0x60,0x19,0x16,0xe1,0xb1,0x1e,0x36,0x78,0xb0,0xa7,0x0d,0x86, + 0x20,0x4f,0x18,0x3c,0x64,0xf0,0xa8,0xc1,0xe3,0x06,0x43,0x0c,0x04,0x78,0xb6,0xe7, + 0x0d,0x46,0x44,0xec,0xc0,0x0e,0xf6,0xd0,0x0e,0x6e,0xd0,0x0e,0xef,0x40,0x0e,0xf5, + 0xc0,0x0e,0xe5,0xe0,0x06,0xe6,0xc0,0x0e,0xe1,0x70,0x0e,0xf3,0x30,0x45,0x08,0x86, + 0x11,0x0a,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b,0xa4,0x03,0x39,0x94,0x83,0x3b,0xd0, + 0xc3,0x94,0xa0,0x18,0xb1,0x84,0x43,0x3a,0xc8,0x83,0x1b,0xd8,0x43,0x39,0xc8,0xc3, + 0x3c,0xa4,0xc3,0x3b,0xb8,0xc3,0x94,0xc0,0x18,0x41,0x85,0x43,0x3a,0xc8,0x83,0x1b, + 0xb0,0x43,0x38,0xb8,0xc3,0x39,0xd4,0x43,0x38,0x9c,0x43,0x39,0xfc,0x82,0x3d,0x94, + 0x83,0x3c,0xcc,0x43,0x3a,0xbc,0x83,0x3b,0x4c,0x09,0x90,0x11,0x53,0x38,0xa4,0x83, + 0x3c,0xb8,0xc1,0x38,0xbc,0x43,0x3b,0xc0,0x43,0x3a,0xb0,0x43,0x39,0xfc,0xc2,0x3b, + 0xc0,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x3c,0x4c,0x31,0x14,0xc6,0x81,0x24,0x6a, + 0x04,0x13,0x0e,0xe9,0x20,0x0f,0x6e,0x60,0x0e,0xf2,0x10,0x0e,0xe7,0xd0,0x0e,0xe5, + 0xe0,0x0e,0xf4,0x30,0x25,0x80,0x03,0x00,0x00,0x79,0x18,0x00,0x00,0x6d,0x00,0x00, + 0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84, + 0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed, + 0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66, + 0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c, + 0x03,0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70, + 0x07,0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90, + 0x0e,0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4, + 0x1d,0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83, + 0x3b,0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14, + 0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70, + 0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80, + 0x87,0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0, + 0x0e,0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1, + 0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d, + 0xca,0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39, + 0xb8,0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc, + 0x82,0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70, + 0x83,0x74,0x68,0x07,0x78,0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53, + 0x0f,0xee,0x00,0x0f,0xf2,0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e, + 0xec,0x50,0x0e,0x33,0x20,0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c, + 0xdc,0x81,0x1e,0xdc,0xe0,0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38, + 0xb0,0x43,0x3a,0x9c,0x83,0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37, + 0x60,0x87,0x77,0x78,0x07,0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33, + 0x1e,0x6a,0x1e,0xca,0x61,0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4, + 0xa1,0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0, + 0x43,0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3, + 0x8c,0xc5,0x0a,0x87,0x79,0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07, + 0x72,0x00,0x00,0x00,0x00,0x71,0x20,0x00,0x00,0x08,0x00,0x00,0x00,0x16,0xb0,0x01, + 0x48,0xe4,0x4b,0x00,0xf3,0x2c,0xc4,0x3f,0x11,0xd7,0x44,0x45,0xc4,0x6f,0x0f,0x7e, + 0x85,0x17,0xb7,0x6d,0x00,0x05,0x03,0x20,0x0d,0x0d,0x00,0x00,0x00,0x61,0x20,0x00, + 0x00,0x0f,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x06,0x00,0x00, + 0x00,0xc4,0x46,0x00,0xc6,0x12,0x80,0x80,0xd4,0x08,0x40,0x0d,0x90,0x98,0x01,0xa0, + 0x30,0x03,0x40,0x60,0x04,0x00,0x00,0x00,0x00,0x83,0x0c,0x8b,0x60,0x8c,0x18,0x28, + 0x42,0x40,0x29,0x49,0x50,0x20,0x86,0x60,0x01,0x23,0x9f,0xd9,0x06,0x23,0x00,0x32, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sgl_vs_bytecode_metal_ios[3344] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x10,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0x58,0xff,0x12,0x36,0x80,0x88,0xb5, + 0xf7,0x1c,0xc3,0xd5,0x31,0x57,0x18,0x26,0x26,0xf0,0x8c,0xd4,0x15,0xa2,0x55,0x8b, + 0xd5,0x3c,0x6b,0x11,0xbd,0x17,0x49,0x4a,0x02,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0x45,0x4e,0x44,0x54,0x37,0x00,0x00, + 0x00,0x56,0x41,0x54,0x54,0x22,0x00,0x03,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x00,0x00,0x80,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x01,0x80, + 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x00,0x02,0x80,0x56,0x41,0x54,0x59,0x05,0x00,0x03, + 0x00,0x06,0x04,0x06,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54, + 0xde,0xc0,0x17,0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0xec,0x0b,0x00,0x00, + 0xff,0xff,0xff,0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0xf8,0x02,0x00,0x00, + 0x0b,0x82,0x20,0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91, + 0x41,0xc8,0x04,0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19, + 0x1e,0x04,0x8b,0x62,0x80,0x14,0x45,0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14, + 0x38,0x08,0x18,0x49,0x0a,0x32,0x44,0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80, + 0x0c,0x19,0x21,0x72,0x24,0x07,0xc8,0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0, + 0x01,0x00,0x00,0x00,0x51,0x18,0x00,0x00,0x82,0x00,0x00,0x00,0x1b,0xc8,0x25,0xf8, + 0xff,0xff,0xff,0xff,0x01,0x90,0x80,0x8a,0x18,0x87,0x77,0x90,0x07,0x79,0x28,0x87, + 0x71,0xa0,0x07,0x76,0xc8,0x87,0x36,0x90,0x87,0x77,0xa8,0x07,0x77,0x20,0x87,0x72, + 0x20,0x87,0x36,0x20,0x87,0x74,0xb0,0x87,0x74,0x20,0x87,0x72,0x68,0x83,0x79,0x88, + 0x07,0x79,0xa0,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07, + 0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c,0x00,0x82,0x1c,0xd2,0x61,0x1e,0xc2,0x41, + 0x1c,0xd8,0xa1,0x1c,0xda,0x80,0x1e,0xc2,0x21,0x1d,0xd8,0xa1,0x0d,0xc6,0x21,0x1c, + 0xd8,0x81,0x1d,0xe6,0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07,0x80,0x60,0x87, + 0x72,0x98,0x87,0x79,0x68,0x03,0x78,0x90,0x87,0x72,0x18,0x87,0x74,0x98,0x87,0x72, + 0x68,0x03,0x73,0x80,0x87,0x76,0x08,0x07,0x72,0x00,0xcc,0x21,0x1c,0xd8,0x61,0x1e, + 0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0xc0,0x1c,0xe4,0x21,0x1c,0xda,0xa1,0x1c,0xda, + 0x00,0x1e,0xde,0x21,0x1d,0xdc,0x81,0x1e,0xca,0x41,0x1e,0xda,0xa0,0x1c,0xd8,0x21, + 0x1d,0xda,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x06,0x77,0x78,0x87,0x36,0x30, + 0x07,0x79,0x08,0x87,0x76,0x28,0x87,0x36,0x80,0x87,0x77,0x48,0x07,0x77,0xa0,0x87, + 0x72,0x90,0x87,0x36,0x28,0x07,0x76,0x48,0x87,0x76,0x68,0x03,0x77,0x78,0x07,0x77, + 0x68,0x03,0x76,0x28,0x87,0x70,0x30,0x07,0x80,0x70,0x87,0x77,0x68,0x83,0x74,0x70, + 0x07,0x73,0x98,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07, + 0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0x40,0x1d,0xea,0xa1, + 0x1d,0xe0,0xa1,0x0d,0xe8,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x1e,0x00,0x73,0x08, + 0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x77,0x78,0x87,0x36,0x70,0x87,0x70,0x70,0x87, + 0x79,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41, + 0x1e,0xea,0xa1,0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xe6,0x21,0x1d,0xce,0xc1,0x1d, + 0xca,0x81,0x1c,0xda,0x40,0x1f,0xca,0x41,0x1e,0xde,0x61,0x1e,0xda,0xc0,0x1c,0xe0, + 0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80, + 0x70,0x87,0x77,0x68,0x03,0x7a,0x90,0x87,0x70,0x80,0x07,0x78,0x48,0x07,0x77,0x38, + 0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00, + 0x62,0x1e,0xe8,0x21,0x1c,0xc6,0x61,0x1d,0xda,0x00,0x1e,0xe4,0xe1,0x1d,0xe8,0xa1, + 0x1c,0xc6,0x81,0x1e,0xde,0x41,0x1e,0xda,0x40,0x1c,0xea,0xc1,0x1c,0xcc,0xa1,0x1c, + 0xe4,0xa1,0x0d,0xe6,0x21,0x1d,0xf4,0xa1,0x1c,0x00,0x3c,0x00,0x88,0x7a,0x70,0x87, + 0x79,0x08,0x07,0x73,0x28,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a, + 0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xea,0x61,0x1e,0xca,0xa1,0x0d, + 0xe6,0xe1,0x1d,0xcc,0x81,0x1e,0xda,0xc0,0x1c,0xd8,0xe1,0x1d,0xc2,0x81,0x1e,0x00, + 0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x36,0x20,0x02,0x01,0x24,0xc0,0x02,0x54, + 0x00,0x00,0x00,0x00,0x49,0x18,0x00,0x00,0x01,0x00,0x00,0x00,0x13,0x84,0x40,0x00, + 0x89,0x20,0x00,0x00,0x23,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85,0x04, + 0x93,0x22,0xa4,0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a,0x8c, + 0x0b,0x84,0xa4,0x4c,0x10,0x44,0x33,0x00,0xc3,0x08,0x04,0x70,0x90,0x34,0x45,0x94, + 0x30,0xf9,0x0c,0x80,0x34,0xf4,0xef,0x50,0x13,0x1a,0x42,0x08,0xc3,0x08,0x02,0x90, + 0x04,0x61,0x26,0x6a,0x1e,0xe8,0x41,0x1e,0xea,0x61,0x1c,0xe8,0xc1,0x0d,0xda,0xa1, + 0x1c,0xe8,0x21,0x1c,0xd8,0x41,0x0f,0xf4,0xa0,0x1d,0xc2,0x81,0x1e,0xe4,0x21,0x1d, + 0xf0,0x01,0x05,0xe4,0x20,0x69,0x8a,0x28,0x61,0xf2,0x2b,0xe9,0x7f,0x80,0x08,0x60, + 0x24,0x24,0x94,0x32,0x88,0x60,0x08,0xa5,0x10,0x61,0x84,0x43,0x68,0x20,0x60,0x8e, + 0x00,0x0c,0x52,0x60,0xcd,0x11,0x80,0xc2,0x20,0x42,0x20,0x0c,0x23,0x10,0xcb,0x08, + 0x00,0x00,0x00,0x00,0x13,0xa8,0x70,0x48,0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70, + 0x80,0x07,0x78,0x60,0x87,0x72,0x68,0x83,0x74,0x78,0x87,0x79,0xc8,0x03,0x37,0x80, + 0x03,0x37,0x80,0x83,0x0d,0xb7,0x51,0x0e,0x6d,0x00,0x0f,0x7a,0x60,0x07,0x74,0xa0, + 0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xe9,0x10,0x07,0x7a,0x80,0x07, + 0x7a,0x80,0x07,0x6d,0x90,0x0e,0x78,0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0,0x06,0xe9, + 0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xd0,0x06,0xe9,0x30, + 0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe9,0x60,0x07, + 0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xe6,0x30,0x07,0x72, + 0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe6,0x60,0x07,0x74,0xa0, + 0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x10,0x07,0x76,0xa0,0x07, + 0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xd0,0x06,0xf6,0x20,0x07,0x74,0xa0,0x07,0x73, + 0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xf6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20, + 0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xf6,0x40,0x07,0x78,0xa0,0x07,0x76,0x40,0x07, + 0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a, + 0x60,0x07,0x74,0xd0,0x06,0xf6,0x90,0x07,0x76,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0, + 0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07, + 0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x6d,0x60,0x0f,0x71,0x90,0x07,0x72, + 0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xd0,0x06,0xf6,0x20, + 0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07, + 0x6d,0x60,0x0f,0x75,0x10,0x07,0x72,0xa0,0x07,0x75,0x10,0x07,0x72,0xa0,0x07,0x75, + 0x10,0x07,0x72,0xd0,0x06,0xf6,0x10,0x07,0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00, + 0x07,0x72,0x40,0x07,0x7a,0x10,0x07,0x70,0x20,0x07,0x74,0xd0,0x06,0xee,0x80,0x07, + 0x7a,0x10,0x07,0x76,0xa0,0x07,0x73,0x20,0x07,0x43,0x98,0x04,0x00,0x80,0x00,0x00, + 0x00,0x00,0x00,0x80,0x2c,0x10,0x00,0x00,0x0b,0x00,0x00,0x00,0x32,0x1e,0x98,0x10, + 0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a,0x25,0x30,0x02,0x50, + 0x04,0x05,0x18,0x50,0x80,0x02,0x85,0x50,0x10,0x65,0x50,0x20,0xd4,0x46,0x00,0x88, + 0x8d,0x25,0x34,0x05,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x19,0x01,0x00,0x00, + 0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7, + 0x21,0xc6,0x32,0x28,0x00,0xb3,0x50,0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3, + 0x2b,0x1b,0x62,0x2c,0x81,0x22,0x2c,0x06,0xd9,0x20,0x08,0x0e,0x8e,0xad,0x0c,0x84, + 0x89,0xc9,0xaa,0x09,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x07,0x46, + 0xc6,0xc5,0xe6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x07, + 0x46,0xc6,0xc5,0xe6,0x26,0x65,0x88,0xa0,0x10,0x43,0x8c,0x25,0x58,0x8e,0x45,0x60, + 0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x51,0x8e,0x25,0x58,0x84,0x45,0xe0,0x16,0x96, + 0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56,0xe6,0xf6,0x26,0xd7, + 0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x50,0x12,0x72,0x61,0x69, + 0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61,0x73,0x74,0x5f,0x6d, + 0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x04,0x65,0x21,0x19,0x84, + 0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98,0xc9,0x85,0xb5,0x95, + 0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1,0x7d,0x95,0xb9,0x85, + 0x89,0xb1,0x95,0x0d,0x11,0x94,0x86,0x61,0x10,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06, + 0x97,0xc6,0x56,0xe6,0xe2,0x16,0x46,0x97,0x66,0x57,0xf6,0x45,0xf6,0x56,0x27,0xc6, + 0x56,0xf6,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x50,0x1e,0x92,0x41,0x58, + 0x9a,0x9c,0xcb,0xd8,0x5b,0x1b,0x5c,0x1a,0x5b,0x99,0x8b,0x5b,0x18,0x5d,0x9a,0x5d, + 0xd9,0x17,0xdb,0x9b,0xdb,0xd9,0x17,0xdb,0x9b,0xdb,0xd9,0x17,0x59,0xda,0x5c,0x98, + 0x18,0x5b,0xd9,0x10,0x41,0x89,0x78,0x06,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70, + 0x69,0x6c,0x65,0x2e,0x6e,0x61,0x74,0x69,0x76,0x65,0x5f,0x77,0x69,0x64,0x65,0x5f, + 0x76,0x65,0x63,0x74,0x6f,0x72,0x73,0x5f,0x64,0x69,0x73,0x61,0x62,0x6c,0x65,0x43, + 0x04,0x65,0x62,0x14,0x96,0x26,0xe7,0x62,0x57,0x26,0x47,0x57,0x86,0xf7,0xf5,0x56, + 0x47,0x07,0x57,0x47,0xc7,0xa5,0x6e,0xae,0x4c,0x0e,0x85,0xed,0x6d,0xcc,0x0d,0x26, + 0x85,0x51,0x58,0x9a,0x9c,0x4b,0x98,0xdc,0xd9,0x17,0x5d,0x1e,0x5c,0xd9,0x97,0x5b, + 0x58,0x5b,0x19,0x0d,0x33,0xb6,0xb7,0x30,0x3a,0x1a,0x32,0x61,0x69,0x72,0x2e,0x61, + 0x72,0x67,0x5f,0x6e,0x61,0x6d,0x65,0x14,0xea,0xec,0x86,0x30,0x4a,0xa5,0x58,0xca, + 0xa5,0x60,0x4a,0xa6,0x68,0x5c,0xea,0xe6,0xca,0xe4,0x50,0xd8,0xde,0xc6,0xdc,0x62, + 0x52,0x58,0x8c,0xbd,0xb1,0xbd,0xc9,0x0d,0x61,0x94,0x4a,0xe1,0x94,0x4b,0xc1,0x94, + 0x4c,0xe9,0xc8,0x84,0xa5,0xc9,0xb9,0xc0,0xbd,0xcd,0xa5,0xd1,0xa5,0xbd,0xb9,0x71, + 0x39,0x63,0xfb,0x82,0x7a,0x9b,0x4b,0xa3,0x4b,0x7b,0x73,0x1b,0xa2,0x28,0x9f,0x72, + 0x29,0x98,0x92,0x29,0x60,0x30,0xc4,0x50,0x36,0xc5,0x53,0xc2,0x80,0x50,0x58,0x9a, + 0x9c,0x8b,0x5d,0x99,0x1c,0x5d,0x19,0xde,0x57,0x9a,0x1b,0x5c,0x1d,0x1d,0xa5,0xb0, + 0x34,0x39,0x17,0xb6,0xb7,0xb1,0x30,0xba,0xb4,0x37,0xb7,0xaf,0x34,0x37,0xb2,0x32, + 0x3c,0x66,0x67,0x65,0x6e,0x65,0x72,0x61,0x74,0x65,0x64,0x28,0x38,0x70,0x6f,0x73, + 0x69,0x74,0x69,0x6f,0x6e,0x44,0x76,0x34,0x5f,0x66,0x29,0x44,0xe0,0xde,0xe6,0xd2, + 0xe8,0xd2,0xde,0xdc,0x86,0x50,0x8b,0xa0,0x8c,0x81,0x42,0x06,0x8b,0xb0,0x04,0x4a, + 0x19,0x28,0x97,0x82,0x29,0x99,0x62,0x06,0xd4,0xce,0xca,0xdc,0xca,0xe4,0xc2,0xe8, + 0xca,0xc8,0x50,0x72,0xe8,0xca,0xf0,0xc6,0xde,0xde,0xe4,0xc8,0x60,0x88,0xec,0x64, + 0xbe,0xcc,0x52,0x68,0x98,0xb1,0xbd,0x85,0xd1,0xc9,0x30,0xa1,0x2b,0xc3,0x1b,0x7b, + 0x7b,0x93,0x23,0x83,0x19,0x42,0x2d,0x81,0x32,0x06,0x0a,0x19,0x2c,0xc1,0x12,0x28, + 0x68,0xa0,0x5c,0x4a,0x1a,0x28,0x99,0xa2,0x06,0xbc,0xce,0xca,0xdc,0xca,0xe4,0xc2, + 0xe8,0xca,0xc8,0x50,0x6c,0xc6,0xde,0xd8,0xde,0xe4,0x60,0x88,0xec,0x68,0xbe,0xcc, + 0x52,0x68,0x8c,0xbd,0xb1,0xbd,0xc9,0xc1,0x0c,0xa1,0x96,0x41,0x19,0x03,0x85,0x0c, + 0x96,0x61,0x09,0x14,0x36,0x50,0x2e,0x05,0x53,0x32,0xa5,0x0d,0xa8,0x84,0xa5,0xc9, + 0xb9,0x88,0xd5,0x99,0x99,0x95,0xc9,0xf1,0x09,0x4b,0x93,0x73,0x11,0xab,0x33,0x33, + 0x2b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x23,0x12,0x96,0x26,0xe7,0x22,0x57,0x16,0x46, + 0x46,0x2a,0x2c,0x4d,0xce,0x65,0x8e,0x4e,0xae,0x6e,0x8c,0xee,0x8b,0x2e,0x0f,0xae, + 0xec,0x2b,0xcd,0xcd,0xec,0x8d,0x88,0x19,0xdb,0x5b,0x18,0x1d,0x0d,0x1e,0x0d,0x87, + 0x36,0x3b,0x38,0x0a,0x74,0x6d,0x43,0xa8,0x45,0x58,0x88,0x45,0x50,0xe6,0x40,0xa1, + 0x83,0x85,0x58,0x88,0x45,0x50,0xe6,0x40,0xa9,0x03,0x46,0x61,0x69,0x72,0x2e,0x61, + 0x72,0x67,0x5f,0x74,0x79,0x70,0x65,0x5f,0x73,0x69,0x7a,0x65,0xbc,0xc2,0xd2,0xe4, + 0x5c,0xc2,0xe4,0xce,0xbe,0xe8,0xf2,0xe0,0xca,0xbe,0xc2,0xd8,0xd2,0xce,0xdc,0xbe, + 0xe6,0xd2,0xf4,0xca,0x98,0xd8,0xcd,0x7d,0xc1,0x85,0xc9,0x85,0xb5,0xcd,0x71,0xf8, + 0x92,0x81,0x19,0x42,0x06,0x0b,0xa2,0xbc,0x81,0x02,0x07,0x4b,0xa1,0x90,0xc1,0x22, + 0x2c,0x81,0x12,0x07,0x8a,0x1c,0x28,0x76,0xa0,0xdc,0xc1,0x52,0x28,0x78,0xb0,0x24, + 0xca,0xa5,0xe4,0x81,0x92,0x29,0x7a,0x30,0x04,0x51,0xce,0x40,0x59,0x03,0xc5,0x0d, + 0x94,0x3d,0x18,0x62,0x24,0x80,0x22,0x06,0x0a,0x1f,0xf0,0x79,0x6b,0x73,0x4b,0x83, + 0x7b,0xa3,0x2b,0x73,0xa3,0x03,0x19,0x43,0x0b,0x93,0xe3,0x33,0x95,0xd6,0x06,0xc7, + 0x56,0x06,0x32,0xb4,0xb2,0x02,0x42,0x25,0x14,0x14,0x34,0x44,0x50,0xfe,0x60,0x88, + 0xa1,0xf8,0x81,0x02,0x0a,0x8d,0x32,0xc4,0x50,0x42,0x41,0x09,0x85,0x46,0x19,0x11, + 0xb1,0x03,0x3b,0xd8,0x43,0x3b,0xb8,0x41,0x3b,0xbc,0x03,0x39,0xd4,0x03,0x3b,0x94, + 0x83,0x1b,0x98,0x03,0x3b,0x84,0xc3,0x39,0xcc,0xc3,0x14,0x21,0x18,0x46,0x28,0xec, + 0xc0,0x0e,0xf6,0xd0,0x0e,0x6e,0x90,0x0e,0xe4,0x50,0x0e,0xee,0x40,0x0f,0x53,0x82, + 0x62,0xc4,0x12,0x0e,0xe9,0x20,0x0f,0x6e,0x60,0x0f,0xe5,0x20,0x0f,0xf3,0x90,0x0e, + 0xef,0xe0,0x0e,0x53,0x02,0x63,0x04,0x15,0x0e,0xe9,0x20,0x0f,0x6e,0xc0,0x0e,0xe1, + 0xe0,0x0e,0xe7,0x50,0x0f,0xe1,0x70,0x0e,0xe5,0xf0,0x0b,0xf6,0x50,0x0e,0xf2,0x30, + 0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x25,0x40,0x46,0x4c,0xe1,0x90,0x0e,0xf2,0xe0,0x06, + 0xe3,0xf0,0x0e,0xed,0x00,0x0f,0xe9,0xc0,0x0e,0xe5,0xf0,0x0b,0xef,0x00,0x0f,0xf4, + 0x90,0x0e,0xef,0xe0,0x0e,0xf3,0x30,0xc5,0x50,0x18,0x07,0x92,0xa8,0x11,0x4a,0x38, + 0xa4,0x83,0x3c,0xb8,0x81,0x3d,0x94,0x83,0x3c,0xd0,0x43,0x39,0xe0,0xc3,0x94,0xa0, + 0x0f,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x6d,0x00,0x00,0x00,0x33,0x08,0x80,0x1c, + 0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07, + 0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e, + 0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43, + 0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c, + 0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76, + 0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e, + 0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8, + 0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4, + 0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68, + 0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07, + 0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71, + 0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5, + 0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4, + 0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90, + 0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43, + 0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b, + 0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83,0x74,0x68,0x07,0x78, + 0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f,0xee,0x00,0x0f,0xf2, + 0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33,0x20, + 0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc,0xe0, + 0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c,0x83, + 0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60,0x87,0x77,0x78,0x07, + 0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca,0x61, + 0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21,0x1d, + 0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43,0x39, + 0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87,0x79, + 0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72,0x00,0x00,0x00,0x00, + 0x71,0x20,0x00,0x00,0x02,0x00,0x00,0x00,0x06,0x50,0x30,0x00,0xd2,0xd0,0x00,0x00, + 0x61,0x20,0x00,0x00,0x3d,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00, + 0x09,0x00,0x00,0x00,0xf4,0xc6,0x22,0x86,0x61,0x18,0xc6,0x22,0x04,0x41,0x10,0xc6, + 0x22,0x82,0x20,0x08,0xa8,0x95,0x40,0x19,0x14,0x01,0xbd,0x11,0x00,0x1a,0x33,0x00, + 0x24,0x66,0x00,0x28,0xcc,0x00,0x00,0x00,0xe3,0x15,0x0b,0x84,0x61,0x10,0x05,0x65, + 0x90,0x01,0x1a,0x0c,0x13,0x02,0xf9,0x8c,0x57,0x3c,0x14,0xc7,0x2d,0x14,0x94,0x41, + 0x06,0xea,0x70,0x4c,0x08,0xe4,0x63,0x41,0x01,0x9f,0xf1,0x0a,0x2a,0x0b,0x83,0x30, + 0x70,0x28,0x28,0x83,0x0c,0x19,0x43,0x99,0x10,0xc8,0xc7,0x8a,0x00,0x3e,0xe3,0x15, + 0x99,0x67,0x06,0x66,0x40,0x51,0x50,0x06,0x19,0xbc,0x48,0x33,0x21,0x90,0x8f,0x15, + 0x01,0x7c,0xc6,0x2b,0xbc,0x31,0x60,0x83,0x35,0x18,0x03,0x0a,0xca,0x20,0x83,0x18, + 0x60,0x99,0x09,0x81,0x7c,0xc6,0x2b,0xc4,0xe0,0x0c,0xe0,0xe0,0x0d,0x3c,0x0a,0xca, + 0x20,0x83,0x19,0x70,0x61,0x60,0x42,0x20,0x1f,0x0b,0x0a,0xf8,0x8c,0x57,0x9c,0x01, + 0x1b,0xd4,0x01,0x1d,0x88,0x01,0x05,0xc5,0x86,0x00,0x3e,0xb3,0x0d,0x61,0x10,0x00, + 0xb3,0x0d,0x01,0x1b,0x04,0xb3,0x0d,0xc1,0x23,0x64,0x10,0x10,0x03,0x00,0x00,0x00, + 0x09,0x00,0x00,0x00,0x5b,0x86,0x20,0x10,0x85,0x2d,0x43,0x11,0x88,0xc2,0x96,0x41, + 0x09,0x44,0x61,0xcb,0xf0,0x04,0xa2,0xb0,0x65,0xa0,0x02,0x51,0xd8,0x32,0x60,0x81, + 0x28,0x6c,0x19,0xba,0x40,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sgl_fs_bytecode_metal_ios[2893] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x4d,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd5,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x70,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0xa8,0xe6,0x5c,0x09,0x1c,0x98,0x6d, + 0xf9,0x59,0x27,0x6f,0xf0,0xd6,0x41,0x4d,0xf9,0x93,0x2f,0x51,0x99,0xc5,0xc8,0xc7, + 0xad,0x1e,0x89,0xca,0xfe,0x25,0x89,0x6a,0x2a,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0x45,0x4e,0x44,0x54,0x04,0x00,0x00, + 0x00,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17, + 0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x5c,0x0a,0x00,0x00,0xff,0xff,0xff, + 0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0x94,0x02,0x00,0x00,0x0b,0x82,0x20, + 0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04, + 0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b, + 0x62,0x80,0x14,0x45,0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18, + 0x49,0x0a,0x32,0x44,0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21, + 0x72,0x24,0x07,0xc8,0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00, + 0x00,0x51,0x18,0x00,0x00,0x89,0x00,0x00,0x00,0x1b,0xcc,0x25,0xf8,0xff,0xff,0xff, + 0xff,0x01,0x60,0x00,0x09,0xa8,0x88,0x71,0x78,0x07,0x79,0x90,0x87,0x72,0x18,0x07, + 0x7a,0x60,0x87,0x7c,0x68,0x03,0x79,0x78,0x87,0x7a,0x70,0x07,0x72,0x28,0x07,0x72, + 0x68,0x03,0x72,0x48,0x07,0x7b,0x48,0x07,0x72,0x28,0x87,0x36,0x98,0x87,0x78,0x90, + 0x07,0x7a,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xcc, + 0x21,0x1c,0xd8,0x61,0x1e,0xca,0x01,0x20,0xc8,0x21,0x1d,0xe6,0x21,0x1c,0xc4,0x81, + 0x1d,0xca,0xa1,0x0d,0xe8,0x21,0x1c,0xd2,0x81,0x1d,0xda,0x60,0x1c,0xc2,0x81,0x1d, + 0xd8,0x61,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x76,0x28,0x87, + 0x79,0x98,0x87,0x36,0x80,0x07,0x79,0x28,0x87,0x71,0x48,0x87,0x79,0x28,0x87,0x36, + 0x30,0x07,0x78,0x68,0x87,0x70,0x20,0x07,0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c, + 0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xcc,0x41,0x1e,0xc2,0xa1,0x1d,0xca,0xa1,0x0d,0xe0, + 0xe1,0x1d,0xd2,0xc1,0x1d,0xe8,0xa1,0x1c,0xe4,0xa1,0x0d,0xca,0x81,0x1d,0xd2,0xa1, + 0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x60,0x70,0x87,0x77,0x68,0x03,0x73,0x90, + 0x87,0x70,0x68,0x87,0x72,0x68,0x03,0x78,0x78,0x87,0x74,0x70,0x07,0x7a,0x28,0x07, + 0x79,0x68,0x83,0x72,0x60,0x87,0x74,0x68,0x87,0x36,0x70,0x87,0x77,0x70,0x87,0x36, + 0x60,0x87,0x72,0x08,0x07,0x73,0x00,0x08,0x77,0x78,0x87,0x36,0x48,0x07,0x77,0x30, + 0x87,0x79,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8, + 0x41,0x1e,0xea,0xa1,0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xd4,0xa1,0x1e,0xda,0x01, + 0x1e,0xda,0x80,0x1e,0xc2,0x41,0x1c,0xd8,0xa1,0x1c,0xe6,0x01,0x30,0x87,0x70,0x60, + 0x87,0x79,0x28,0x07,0x80,0x70,0x87,0x77,0x68,0x03,0x77,0x08,0x07,0x77,0x98,0x87, + 0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1, + 0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0x60,0x1e,0xd2,0xe1,0x1c,0xdc,0xa1,0x1c, + 0xc8,0xa1,0x0d,0xf4,0xa1,0x1c,0xe4,0xe1,0x1d,0xe6,0xa1,0x0d,0xcc,0x01,0x1e,0xda, + 0xa0,0x1d,0xc2,0x81,0x1e,0xd0,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x08,0x77, + 0x78,0x87,0x36,0xa0,0x07,0x79,0x08,0x07,0x78,0x80,0x87,0x74,0x70,0x87,0x73,0x68, + 0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xe6, + 0x81,0x1e,0xc2,0x61,0x1c,0xd6,0xa1,0x0d,0xe0,0x41,0x1e,0xde,0x81,0x1e,0xca,0x61, + 0x1c,0xe8,0xe1,0x1d,0xe4,0xa1,0x0d,0xc4,0xa1,0x1e,0xcc,0xc1,0x1c,0xca,0x41,0x1e, + 0xda,0x60,0x1e,0xd2,0x41,0x1f,0xca,0x01,0xc0,0x03,0x80,0xa8,0x07,0x77,0x98,0x87, + 0x70,0x30,0x87,0x72,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74, + 0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0xa2,0x1e,0xe6,0xa1,0x1c,0xda,0x60,0x1e, + 0xde,0xc1,0x1c,0xe8,0xa1,0x0d,0xcc,0x81,0x1d,0xde,0x21,0x1c,0xe8,0x01,0x30,0x87, + 0x70,0x60,0x87,0x79,0x28,0x07,0x60,0x83,0x21,0x0c,0xc0,0x02,0x54,0x1b,0x8c,0x81, + 0x00,0x16,0xa0,0xda,0x80,0x10,0xff,0xff,0xff,0xff,0x3f,0x00,0x0c,0x20,0x01,0xd5, + 0x06,0xa3,0x08,0x80,0x05,0xa8,0x36,0x18,0x86,0x00,0x2c,0x40,0x05,0x49,0x18,0x00, + 0x00,0x03,0x00,0x00,0x00,0x13,0x86,0x40,0x18,0x26,0x0c,0x44,0x61,0x00,0x00,0x00, + 0x00,0x89,0x20,0x00,0x00,0x20,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85, + 0x04,0x93,0x22,0xa4,0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a, + 0x8c,0x0b,0x84,0xa4,0x4c,0x10,0x48,0x33,0x00,0xc3,0x08,0x04,0x70,0x90,0x34,0x45, + 0x94,0x30,0xf9,0x0c,0x80,0x34,0xf4,0xef,0x50,0x13,0x0a,0xc2,0x51,0xd2,0x14,0x51, + 0xc2,0xe4,0xff,0x13,0x71,0x4d,0x54,0x44,0xfc,0xf6,0xf0,0x4f,0x63,0x04,0xc0,0x20, + 0xc2,0x10,0x5c,0x24,0x4d,0x11,0x25,0x4c,0xfe,0x2f,0x01,0xcc,0xb3,0x10,0xd1,0x3f, + 0x8d,0x11,0x00,0x83,0x08,0x85,0x50,0x0a,0x11,0x02,0x31,0x74,0x86,0x11,0x04,0x60, + 0x8e,0x20,0x98,0x23,0x00,0x83,0x61,0x04,0x61,0x29,0x48,0x20,0x26,0x29,0xa6,0x00, + 0xb5,0x81,0x80,0x14,0x58,0x23,0x00,0x00,0x00,0x13,0xa8,0x70,0x48,0x07,0x79,0xb0, + 0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68,0x83,0x74,0x78,0x87, + 0x79,0xc8,0x03,0x37,0x80,0x03,0x37,0x80,0x83,0x0d,0xb7,0x51,0x0e,0x6d,0x00,0x0f, + 0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xe9, + 0x10,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90,0x0e,0x78,0xa0,0x07,0x78,0xa0, + 0x07,0x78,0xd0,0x06,0xe9,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x7a,0x10,0x07, + 0x76,0xd0,0x06,0xe9,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72, + 0xd0,0x06,0xe9,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0, + 0x06,0xe6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06, + 0xe6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6, + 0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xd0,0x06,0xf6,0x20, + 0x07,0x74,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xf6,0x30,0x07, + 0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xf6,0x40,0x07,0x78, + 0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x60,0x07,0x74,0xa0, + 0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x90,0x07,0x76,0xa0,0x07, + 0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x72, + 0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x6d,0x60, + 0x0f,0x71,0x90,0x07,0x72,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07, + 0x76,0xd0,0x06,0xf6,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x7a, + 0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f,0x75,0x10,0x07,0x72,0xa0,0x07,0x75,0x10, + 0x07,0x72,0xa0,0x07,0x75,0x10,0x07,0x72,0xd0,0x06,0xf6,0x10,0x07,0x70,0x20,0x07, + 0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07,0x70,0x20,0x07,0x74, + 0xd0,0x06,0xee,0x80,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x73,0x20,0x07,0x43,0x18, + 0x04,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x80,0x21,0x8c,0x03,0x04,0x80,0x00,0x00, + 0x00,0x00,0x00,0x40,0x16,0x08,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x32,0x1e,0x98, + 0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a,0x23,0x00,0x25, + 0x50,0x04,0x85,0x50,0x10,0x65,0x40,0x70,0x2c,0xa1,0x29,0x00,0x00,0x79,0x18,0x00, + 0x00,0xd7,0x00,0x00,0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32, + 0xb9,0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x42,0x3c,0x00,0x84,0x50,0xb9,0x1b,0x43,0x0b, + 0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62,0x2c,0xc3,0x23,0x2c,0x05,0xd9,0x20,0x08, + 0x0e,0x8e,0xad,0x0c,0x84,0x89,0xc9,0xaa,0x09,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd, + 0x0d,0x64,0x26,0x07,0x46,0xc6,0xc5,0xe6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd, + 0xac,0xac,0x65,0x26,0x07,0x46,0xc6,0xc5,0xe6,0x26,0x65,0x88,0xf0,0x10,0x43,0x8c, + 0x65,0x58,0x8c,0x45,0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x79,0x8e,0x65,0x58, + 0x84,0x45,0xe0,0x16,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42, + 0x56,0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44, + 0x78,0x12,0x72,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66, + 0x61,0x73,0x74,0x5f,0x6d,0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43, + 0x84,0x67,0x21,0x19,0x84,0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9, + 0x98,0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d, + 0xa1,0x7d,0x95,0xb9,0x85,0x89,0xb1,0x95,0x0d,0x11,0x9e,0x86,0x61,0x10,0x96,0x26, + 0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0xe2,0x16,0x46,0x97,0x66,0x57,0xf6, + 0x45,0xf6,0x56,0x27,0xc6,0x56,0xf6,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44, + 0x78,0x1e,0x92,0x41,0x58,0x9a,0x9c,0xcb,0xd8,0x5b,0x1b,0x5c,0x1a,0x5b,0x99,0x8b, + 0x5b,0x18,0x5d,0x9a,0x5d,0xd9,0x17,0xdb,0x9b,0xdb,0xd9,0x17,0xdb,0x9b,0xdb,0xd9, + 0x17,0x59,0xda,0x5c,0x98,0x18,0x5b,0xd9,0x10,0xe1,0x89,0x78,0x06,0x61,0x69,0x72, + 0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x6e,0x61,0x74,0x69,0x76,0x65,0x5f, + 0x77,0x69,0x64,0x65,0x5f,0x76,0x65,0x63,0x74,0x6f,0x72,0x73,0x5f,0x64,0x69,0x73, + 0x61,0x62,0x6c,0x65,0x43,0x84,0x67,0x62,0x14,0x96,0x26,0xe7,0x22,0x57,0xe6,0x46, + 0x56,0x26,0xf7,0x45,0x17,0x26,0x77,0x56,0x46,0xc7,0x28,0x2c,0x4d,0xce,0x25,0x4c, + 0xee,0xec,0x8b,0x2e,0x0f,0xae,0xec,0xcb,0x2d,0xac,0xad,0x8c,0x86,0x19,0xdb,0x5b, + 0x18,0x1d,0x0d,0x99,0xb0,0x34,0x39,0x97,0x30,0xb9,0xb3,0x2f,0xb7,0xb0,0xb6,0x32, + 0x2a,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x43,0x98,0xa7,0x5a,0x84, + 0xc7,0x7a,0xae,0x07,0x7b,0xb2,0x21,0xc2,0xa3,0x51,0x0a,0x4b,0x93,0x73,0x31,0x93, + 0x0b,0x3b,0x6b,0x2b,0x73,0xa3,0xfb,0x4a,0x73,0x83,0xab,0xa3,0xe3,0x52,0x37,0x57, + 0x26,0x87,0xc2,0xf6,0x36,0xe6,0x06,0x93,0x42,0x25,0x2c,0x4d,0xce,0x65,0xac,0xcc, + 0x8d,0xae,0x4c,0x8e,0x4f,0x58,0x9a,0x9c,0x0b,0x5c,0x99,0xdc,0x1c,0x5c,0xd9,0x18, + 0x5d,0x9a,0x5d,0x19,0x85,0x3a,0xbb,0x21,0xd2,0x22,0x3c,0xdc,0xd3,0x3d,0xde,0xf3, + 0x3d,0xd6,0x73,0x3d,0xd8,0x03,0x06,0x5c,0xea,0xe6,0xca,0xe4,0x50,0xd8,0xde,0xc6, + 0xdc,0x62,0x52,0x58,0x8c,0xbd,0xb1,0xbd,0xc9,0x0d,0x91,0x96,0xe1,0xe1,0x1e,0x31, + 0x78,0xbc,0xe7,0x7b,0xac,0xe7,0x7a,0xb0,0x67,0x0c,0xb8,0x84,0xa5,0xc9,0xb9,0xd0, + 0x95,0xe1,0xd1,0xd5,0xc9,0x95,0x51,0x0a,0x4b,0x93,0x73,0x61,0x7b,0x1b,0x0b,0xa3, + 0x4b,0x7b,0x73,0xfb,0x4a,0x73,0x23,0x2b,0xc3,0xa3,0x12,0x96,0x26,0xe7,0x32,0x17, + 0xd6,0x06,0xc7,0x56,0x46,0x8c,0xae,0x0c,0x8f,0xae,0x4e,0xae,0x4c,0x86,0x8c,0xc7, + 0x8c,0xed,0x2d,0x8c,0x8e,0x05,0x64,0x2e,0xac,0x0d,0x8e,0xad,0xcc,0x87,0x03,0x5d, + 0x19,0xde,0x10,0x6a,0x21,0x9e,0x32,0x78,0xcc,0x60,0x11,0x96,0xe1,0x39,0x83,0xc7, + 0x7a,0xd0,0xe0,0xc1,0x9e,0x34,0xe0,0x12,0x96,0x26,0xe7,0x32,0x17,0xd6,0x06,0xc7, + 0x56,0x26,0xc7,0x63,0x2e,0xac,0x0d,0x8e,0xad,0x4c,0x8e,0x08,0x5d,0x19,0xde,0x54, + 0x1b,0x1c,0x9b,0xdc,0x10,0x69,0x39,0x9e,0x35,0x78,0xcc,0x60,0x11,0x96,0xe1,0xb1, + 0x1e,0x36,0x78,0xb0,0xa7,0x0d,0x86,0x20,0x4f,0x18,0x3c,0x64,0xf0,0xa8,0xc1,0xe3, + 0x06,0x43,0x0c,0x04,0x78,0xb6,0xe7,0x0d,0x46,0x44,0xec,0xc0,0x0e,0xf6,0xd0,0x0e, + 0x6e,0xd0,0x0e,0xef,0x40,0x0e,0xf5,0xc0,0x0e,0xe5,0xe0,0x06,0xe6,0xc0,0x0e,0xe1, + 0x70,0x0e,0xf3,0x30,0x45,0x08,0x86,0x11,0x0a,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b, + 0xa4,0x03,0x39,0x94,0x83,0x3b,0xd0,0xc3,0x94,0xa0,0x18,0xb1,0x84,0x43,0x3a,0xc8, + 0x83,0x1b,0xd8,0x43,0x39,0xc8,0xc3,0x3c,0xa4,0xc3,0x3b,0xb8,0xc3,0x94,0xc0,0x18, + 0x41,0x85,0x43,0x3a,0xc8,0x83,0x1b,0xb0,0x43,0x38,0xb8,0xc3,0x39,0xd4,0x43,0x38, + 0x9c,0x43,0x39,0xfc,0x82,0x3d,0x94,0x83,0x3c,0xcc,0x43,0x3a,0xbc,0x83,0x3b,0x4c, + 0x09,0x90,0x11,0x53,0x38,0xa4,0x83,0x3c,0xb8,0xc1,0x38,0xbc,0x43,0x3b,0xc0,0x43, + 0x3a,0xb0,0x43,0x39,0xfc,0xc2,0x3b,0xc0,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x3c, + 0x4c,0x31,0x14,0xc6,0x81,0x24,0x6a,0x04,0x13,0x0e,0xe9,0x20,0x0f,0x6e,0x60,0x0e, + 0xf2,0x10,0x0e,0xe7,0xd0,0x0e,0xe5,0xe0,0x0e,0xf4,0x30,0x25,0x80,0x03,0x00,0x00, + 0x00,0x79,0x18,0x00,0x00,0x6d,0x00,0x00,0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c, + 0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07, + 0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42, + 0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83, + 0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07, + 0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76,0x78,0x87,0x70, + 0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3, + 0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2, + 0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc, + 0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68, + 0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07, + 0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71,0x18,0x87,0x72, + 0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec, + 0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc, + 0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90,0x43,0x39,0xc8, + 0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43,0x38,0x88,0x03, + 0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x0c, + 0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83,0x74,0x68,0x07,0x78,0x60,0x87,0x74, + 0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f,0xee,0x00,0x0f,0xf2,0x50,0x0e,0xe4, + 0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33,0x20,0x28,0x1d,0xdc, + 0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc,0xe0,0x1c,0xe4,0xe1, + 0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c,0x83,0x3b,0xcc,0x50, + 0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60,0x87,0x77,0x78,0x07,0x78,0x98,0x51, + 0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca,0x61,0x1c,0xe8,0x21, + 0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06, + 0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c, + 0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87,0x79,0x98,0x87,0x77, + 0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72,0x00,0x00,0x00,0x00,0x71,0x20,0x00, + 0x00,0x08,0x00,0x00,0x00,0x16,0xb0,0x01,0x48,0xe4,0x4b,0x00,0xf3,0x2c,0xc4,0x3f, + 0x11,0xd7,0x44,0x45,0xc4,0x6f,0x0f,0x7e,0x85,0x17,0xb7,0x6d,0x00,0x05,0x03,0x20, + 0x0d,0x0d,0x00,0x00,0x00,0x61,0x20,0x00,0x00,0x0f,0x00,0x00,0x00,0x13,0x04,0x41, + 0x2c,0x10,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0xc4,0x46,0x00,0xc6,0x12,0x80,0x80, + 0xd4,0x08,0x40,0x0d,0x90,0x98,0x01,0xa0,0x30,0x03,0x40,0x60,0x04,0x00,0x00,0x00, + 0x00,0x83,0x0c,0x8b,0x60,0x8c,0x18,0x28,0x42,0x40,0x29,0x49,0x50,0x20,0x86,0x60, + 0x01,0x23,0x9f,0xd9,0x06,0x23,0x00,0x32,0x00,0x00,0x00,0x00,0x00, +}; +static const char _sgl_vs_source_metal_sim[698] = { + 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, + 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, + 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x76, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x20,0x6d,0x76,0x70,0x3b,0x0a,0x20,0x20,0x20, + 0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x20,0x74,0x6d,0x3b,0x0a,0x7d,0x3b, + 0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f, + 0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20, + 0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29, + 0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e, + 0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34, + 0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x5b,0x5b,0x70, + 0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73, + 0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69,0x6e,0x0a,0x7b, + 0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x70,0x6f,0x73,0x69, + 0x74,0x69,0x6f,0x6e,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65, + 0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74, + 0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x20,0x5b,0x5b,0x61,0x74, + 0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20, + 0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x20, + 0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x32,0x29,0x5d,0x5d, + 0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31,0x36,0x20,0x22, + 0x22,0x0a,0x76,0x65,0x72,0x74,0x65,0x78,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f, + 0x75,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x28,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69, + 0x6e,0x20,0x69,0x6e,0x20,0x5b,0x5b,0x73,0x74,0x61,0x67,0x65,0x5f,0x69,0x6e,0x5d, + 0x5d,0x2c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,0x76,0x73,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x73,0x26,0x20,0x5f,0x32,0x30,0x20,0x5b,0x5b,0x62,0x75,0x66, + 0x66,0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20, + 0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20, + 0x7b,0x7d,0x3b,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31,0x36,0x20,0x22,0x22,0x0a, + 0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74, + 0x69,0x6f,0x6e,0x20,0x3d,0x20,0x5f,0x32,0x30,0x2e,0x6d,0x76,0x70,0x20,0x2a,0x20, + 0x69,0x6e,0x2e,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a,0x23,0x6c,0x69, + 0x6e,0x65,0x20,0x31,0x37,0x20,0x22,0x22,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74, + 0x2e,0x75,0x76,0x20,0x3d,0x20,0x5f,0x32,0x30,0x2e,0x74,0x6d,0x20,0x2a,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x2e,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72, + 0x64,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x23, + 0x6c,0x69,0x6e,0x65,0x20,0x31,0x38,0x20,0x22,0x22,0x0a,0x20,0x20,0x20,0x20,0x6f, + 0x75,0x74,0x2e,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x69,0x6e,0x2e,0x63,0x6f, + 0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e, + 0x20,0x6f,0x75,0x74,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +static const char _sgl_fs_source_metal_sim[473] = { + 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, + 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, + 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d, + 0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x5b,0x5b,0x63,0x6f,0x6c,0x6f,0x72,0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x7d, + 0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f, + 0x69,0x6e,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20, + 0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29, + 0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e, + 0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20, + 0x31,0x31,0x20,0x22,0x22,0x0a,0x66,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x20,0x6d, + 0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x28,0x6d, + 0x61,0x69,0x6e,0x30,0x5f,0x69,0x6e,0x20,0x69,0x6e,0x20,0x5b,0x5b,0x73,0x74,0x61, + 0x67,0x65,0x5f,0x69,0x6e,0x5d,0x5d,0x2c,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65, + 0x32,0x64,0x3c,0x66,0x6c,0x6f,0x61,0x74,0x3e,0x20,0x74,0x65,0x78,0x20,0x5b,0x5b, + 0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x30,0x29,0x5d,0x5d,0x2c,0x20,0x73,0x61, + 0x6d,0x70,0x6c,0x65,0x72,0x20,0x74,0x65,0x78,0x53,0x6d,0x70,0x6c,0x72,0x20,0x5b, + 0x5b,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,0x29,0x0a,0x7b, + 0x0a,0x20,0x20,0x20,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6f, + 0x75,0x74,0x20,0x3d,0x20,0x7b,0x7d,0x3b,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31, + 0x31,0x20,0x22,0x22,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x66,0x72,0x61, + 0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x74,0x65,0x78,0x2e,0x73,0x61, + 0x6d,0x70,0x6c,0x65,0x28,0x74,0x65,0x78,0x53,0x6d,0x70,0x6c,0x72,0x2c,0x20,0x69, + 0x6e,0x2e,0x75,0x76,0x2e,0x78,0x79,0x29,0x20,0x2a,0x20,0x69,0x6e,0x2e,0x63,0x6f, + 0x6c,0x6f,0x72,0x3b,0x0a,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20, + 0x6f,0x75,0x74,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_D3D11) +static const uint8_t _sgl_vs_bytecode_hlsl4[1008] = { + 0x44,0x58,0x42,0x43,0xf3,0xd9,0xf4,0x58,0x44,0xc2,0xb9,0x96,0x56,0x7c,0xb3,0x71, + 0x84,0xc5,0xde,0x61,0x01,0x00,0x00,0x00,0xf0,0x03,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0x14,0x01,0x00,0x00,0x78,0x01,0x00,0x00,0xe8,0x01,0x00,0x00, + 0x74,0x03,0x00,0x00,0x52,0x44,0x45,0x46,0xd8,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x48,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x04,0xfe,0xff, + 0x10,0x81,0x00,0x00,0xaf,0x00,0x00,0x00,0x3c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x73,0x00,0xab,0xab,0x3c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x60,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x90,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x98,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xa8,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x40,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5f,0x32,0x30,0x5f, + 0x6d,0x76,0x70,0x00,0x02,0x00,0x03,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x5f,0x32,0x30,0x5f,0x74,0x6d,0x00,0x4d,0x69,0x63,0x72,0x6f, + 0x73,0x6f,0x66,0x74,0x20,0x28,0x52,0x29,0x20,0x48,0x4c,0x53,0x4c,0x20,0x53,0x68, + 0x61,0x64,0x65,0x72,0x20,0x43,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x72,0x20,0x31,0x30, + 0x2e,0x31,0x00,0xab,0x49,0x53,0x47,0x4e,0x5c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x50,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x03,0x03,0x00,0x00,0x50,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x54,0x45,0x58,0x43, + 0x4f,0x4f,0x52,0x44,0x00,0xab,0xab,0xab,0x4f,0x53,0x47,0x4e,0x68,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, + 0x50,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x59,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, + 0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0x53,0x56,0x5f,0x50,0x6f,0x73,0x69, + 0x74,0x69,0x6f,0x6e,0x00,0xab,0xab,0xab,0x53,0x48,0x44,0x52,0x84,0x01,0x00,0x00, + 0x40,0x00,0x01,0x00,0x61,0x00,0x00,0x00,0x59,0x00,0x00,0x04,0x46,0x8e,0x20,0x00, + 0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x5f,0x00,0x00,0x03,0xf2,0x10,0x10,0x00, + 0x00,0x00,0x00,0x00,0x5f,0x00,0x00,0x03,0x32,0x10,0x10,0x00,0x01,0x00,0x00,0x00, + 0x5f,0x00,0x00,0x03,0xf2,0x10,0x10,0x00,0x02,0x00,0x00,0x00,0x65,0x00,0x00,0x03, + 0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x65,0x00,0x00,0x03,0xf2,0x20,0x10,0x00, + 0x01,0x00,0x00,0x00,0x67,0x00,0x00,0x04,0xf2,0x20,0x10,0x00,0x02,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x68,0x00,0x00,0x02,0x01,0x00,0x00,0x00,0x38,0x00,0x00,0x08, + 0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x56,0x15,0x10,0x00,0x01,0x00,0x00,0x00, + 0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x32,0x00,0x00,0x0a, + 0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x06,0x10,0x10,0x00,0x01,0x00,0x00,0x00, + 0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x46,0x0e,0x10,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00, + 0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x36,0x00,0x00,0x05,0xf2,0x20,0x10,0x00,0x01,0x00,0x00,0x00, + 0x46,0x1e,0x10,0x00,0x02,0x00,0x00,0x00,0x38,0x00,0x00,0x08,0xf2,0x00,0x10,0x00, + 0x00,0x00,0x00,0x00,0x56,0x15,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x32,0x00,0x00,0x0a,0xf2,0x00,0x10,0x00, + 0x00,0x00,0x00,0x00,0x06,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00, + 0x32,0x00,0x00,0x0a,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0xa6,0x1a,0x10,0x00, + 0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x32,0x00,0x00,0x0a,0xf2,0x20,0x10,0x00, + 0x02,0x00,0x00,0x00,0xf6,0x1f,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00, + 0x3e,0x00,0x00,0x01,0x53,0x54,0x41,0x54,0x74,0x00,0x00,0x00,0x09,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + +}; +static const uint8_t _sgl_fs_bytecode_hlsl4[620] = { + 0x44,0x58,0x42,0x43,0x4f,0x7a,0xe1,0xcf,0x0c,0x89,0xc0,0x09,0x50,0x5a,0xca,0xe9, + 0x75,0x77,0xd1,0x26,0x01,0x00,0x00,0x00,0x6c,0x02,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0xd4,0x00,0x00,0x00,0x20,0x01,0x00,0x00,0x54,0x01,0x00,0x00, + 0xf0,0x01,0x00,0x00,0x52,0x44,0x45,0x46,0x98,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x04,0xff,0xff, + 0x10,0x81,0x00,0x00,0x6d,0x00,0x00,0x00,0x5c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x69,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x5f,0x74,0x65,0x78,0x5f,0x73,0x61,0x6d, + 0x70,0x6c,0x65,0x72,0x00,0x74,0x65,0x78,0x00,0x4d,0x69,0x63,0x72,0x6f,0x73,0x6f, + 0x66,0x74,0x20,0x28,0x52,0x29,0x20,0x48,0x4c,0x53,0x4c,0x20,0x53,0x68,0x61,0x64, + 0x65,0x72,0x20,0x43,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x72,0x20,0x31,0x30,0x2e,0x31, + 0x00,0xab,0xab,0xab,0x49,0x53,0x47,0x4e,0x44,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x03,0x00,0x00,0x38,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0f,0x0f,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0xab,0xab,0xab, + 0x4f,0x53,0x47,0x4e,0x2c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x53,0x56,0x5f,0x54,0x61,0x72,0x67,0x65, + 0x74,0x00,0xab,0xab,0x53,0x48,0x44,0x52,0x94,0x00,0x00,0x00,0x40,0x00,0x00,0x00, + 0x25,0x00,0x00,0x00,0x5a,0x00,0x00,0x03,0x00,0x60,0x10,0x00,0x00,0x00,0x00,0x00, + 0x58,0x18,0x00,0x04,0x00,0x70,0x10,0x00,0x00,0x00,0x00,0x00,0x55,0x55,0x00,0x00, + 0x62,0x10,0x00,0x03,0x32,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x62,0x10,0x00,0x03, + 0xf2,0x10,0x10,0x00,0x01,0x00,0x00,0x00,0x65,0x00,0x00,0x03,0xf2,0x20,0x10,0x00, + 0x00,0x00,0x00,0x00,0x68,0x00,0x00,0x02,0x01,0x00,0x00,0x00,0x45,0x00,0x00,0x09, + 0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x10,0x10,0x00,0x00,0x00,0x00,0x00, + 0x46,0x7e,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x10,0x00,0x00,0x00,0x00,0x00, + 0x38,0x00,0x00,0x07,0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x0e,0x10,0x00, + 0x00,0x00,0x00,0x00,0x46,0x1e,0x10,0x00,0x01,0x00,0x00,0x00,0x3e,0x00,0x00,0x01, + 0x53,0x54,0x41,0x54,0x74,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +#elif defined(SOKOL_WGPU) +static const uint8_t _sgl_vs_bytecode_wgpu[1932] = { + 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x08,0x00,0x08,0x00,0x32,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, + 0x02,0x00,0x00,0x00,0x47,0x4c,0x53,0x4c,0x2e,0x73,0x74,0x64,0x2e,0x34,0x35,0x30, + 0x00,0x00,0x00,0x00,0x0e,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0f,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, + 0x00,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x24,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x30,0x00,0x00,0x00, + 0x31,0x00,0x00,0x00,0x07,0x00,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x37,0x00,0x02,0x00,0x00,0x00,0xc2,0x01,0x00,0x00,0x01,0x00,0x00,0x00, + 0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65, + 0x73,0x73,0x65,0x64,0x20,0x63,0x6c,0x69,0x65,0x6e,0x74,0x20,0x76,0x75,0x6c,0x6b, + 0x61,0x6e,0x31,0x30,0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c, + 0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x63,0x6c,0x69,0x65,0x6e, + 0x74,0x20,0x6f,0x70,0x65,0x6e,0x67,0x6c,0x31,0x30,0x30,0x0a,0x2f,0x2f,0x20,0x4f, + 0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64, + 0x20,0x74,0x61,0x72,0x67,0x65,0x74,0x2d,0x65,0x6e,0x76,0x20,0x76,0x75,0x6c,0x6b, + 0x61,0x6e,0x31,0x2e,0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c, + 0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x74,0x61,0x72,0x67,0x65, + 0x74,0x2d,0x65,0x6e,0x76,0x20,0x6f,0x70,0x65,0x6e,0x67,0x6c,0x0a,0x2f,0x2f,0x20, + 0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65, + 0x64,0x20,0x65,0x6e,0x74,0x72,0x79,0x2d,0x70,0x6f,0x69,0x6e,0x74,0x20,0x6d,0x61, + 0x69,0x6e,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31,0x0a,0x00,0x05,0x00,0x04,0x00, + 0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00, + 0x0c,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50,0x65,0x72,0x56,0x65,0x72,0x74,0x65,0x78, + 0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x06,0x00,0x07,0x00, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50,0x6f,0x69,0x6e,0x74, + 0x53,0x69,0x7a,0x65,0x00,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x0c,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x67,0x6c,0x5f,0x43,0x6c,0x69,0x70,0x44,0x69,0x73,0x74,0x61, + 0x6e,0x63,0x65,0x00,0x06,0x00,0x07,0x00,0x0c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x67,0x6c,0x5f,0x43,0x75,0x6c,0x6c,0x44,0x69,0x73,0x74,0x61,0x6e,0x63,0x65,0x00, + 0x05,0x00,0x03,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00, + 0x12,0x00,0x00,0x00,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x00,0x00,0x00, + 0x06,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0x76,0x70,0x00, + 0x06,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x74,0x6d,0x00,0x00, + 0x05,0x00,0x03,0x00,0x14,0x00,0x00,0x00,0x5f,0x32,0x30,0x00,0x05,0x00,0x05,0x00, + 0x19,0x00,0x00,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0x1e,0x00,0x00,0x00,0x75,0x76,0x00,0x00,0x05,0x00,0x05,0x00, + 0x24,0x00,0x00,0x00,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x2c,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x2d,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x00,0x00, + 0x05,0x00,0x05,0x00,0x30,0x00,0x00,0x00,0x67,0x6c,0x5f,0x56,0x65,0x72,0x74,0x65, + 0x78,0x49,0x44,0x00,0x05,0x00,0x06,0x00,0x31,0x00,0x00,0x00,0x67,0x6c,0x5f,0x49, + 0x6e,0x73,0x74,0x61,0x6e,0x63,0x65,0x49,0x44,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x0b,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x47,0x00,0x03,0x00, + 0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x48,0x00,0x04,0x00,0x12,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x12,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00, + 0x48,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x05,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x47,0x00,0x03,0x00,0x12,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x22,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x1e,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x24,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x2c,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x2d,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x30,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x31,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x03,0x00,0x00,0x00,0x21,0x00,0x03,0x00, + 0x04,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x07,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x15,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x1c,0x00,0x04,0x00,0x0b,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x0a,0x00,0x00,0x00,0x1e,0x00,0x06,0x00,0x0c,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x0d,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x0d,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x15,0x00,0x04,0x00, + 0x0f,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, + 0x0f,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x1e,0x00,0x04,0x00, + 0x12,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x13,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x13,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x15,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x18,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x18,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x1c,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x1c,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, + 0x0f,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x17,0x00,0x04,0x00, + 0x22,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x23,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x23,0x00,0x00,0x00,0x24,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x00,0x00,0x80,0x3f,0x3b,0x00,0x04,0x00, + 0x1c,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x18,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x2f,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x2f,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x2f,0x00,0x00,0x00,0x31,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x36,0x00,0x05,0x00, + 0x03,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0xf8,0x00,0x02,0x00,0x06,0x00,0x00,0x00,0x08,0x00,0x04,0x00,0x01,0x00,0x00,0x00, + 0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x15,0x00,0x00,0x00, + 0x16,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x91,0x00,0x05,0x00, + 0x08,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x1a,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x1c,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x0e,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x1d,0x00,0x00,0x00,0x1b,0x00,0x00,0x00, + 0x08,0x00,0x04,0x00,0x01,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x15,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x14,0x00,0x00,0x00, + 0x1f,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x22,0x00,0x00,0x00,0x25,0x00,0x00,0x00, + 0x24,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x28,0x00,0x00,0x00, + 0x25,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x07,0x00,0x00,0x00, + 0x29,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x50,0x00,0x07,0x00, + 0x08,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x29,0x00,0x00,0x00, + 0x26,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x91,0x00,0x05,0x00,0x08,0x00,0x00,0x00, + 0x2b,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x3e,0x00,0x03,0x00, + 0x1e,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x08,0x00,0x04,0x00,0x01,0x00,0x00,0x00, + 0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x2e,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x2c,0x00,0x00,0x00, + 0x2e,0x00,0x00,0x00,0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, +}; +static const uint8_t _sgl_fs_bytecode_wgpu[916] = { + 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x08,0x00,0x08,0x00,0x19,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, + 0x02,0x00,0x00,0x00,0x47,0x4c,0x53,0x4c,0x2e,0x73,0x74,0x64,0x2e,0x34,0x35,0x30, + 0x00,0x00,0x00,0x00,0x0e,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0f,0x00,0x08,0x00,0x04,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, + 0x00,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x16,0x00,0x00,0x00, + 0x10,0x00,0x03,0x00,0x05,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x07,0x00,0x03,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x37,0x00,0x02,0x00,0x00,0x00, + 0xc2,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64, + 0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x63,0x6c,0x69, + 0x65,0x6e,0x74,0x20,0x76,0x75,0x6c,0x6b,0x61,0x6e,0x31,0x30,0x30,0x0a,0x2f,0x2f, + 0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73, + 0x65,0x64,0x20,0x63,0x6c,0x69,0x65,0x6e,0x74,0x20,0x6f,0x70,0x65,0x6e,0x67,0x6c, + 0x31,0x30,0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50, + 0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x74,0x61,0x72,0x67,0x65,0x74,0x2d, + 0x65,0x6e,0x76,0x20,0x76,0x75,0x6c,0x6b,0x61,0x6e,0x31,0x2e,0x30,0x0a,0x2f,0x2f, + 0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73, + 0x65,0x64,0x20,0x74,0x61,0x72,0x67,0x65,0x74,0x2d,0x65,0x6e,0x76,0x20,0x6f,0x70, + 0x65,0x6e,0x67,0x6c,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65, + 0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x65,0x6e,0x74,0x72,0x79,0x2d, + 0x70,0x6f,0x69,0x6e,0x74,0x20,0x6d,0x61,0x69,0x6e,0x0a,0x23,0x6c,0x69,0x6e,0x65, + 0x20,0x31,0x0a,0x00,0x05,0x00,0x04,0x00,0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, + 0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x0a,0x00,0x00,0x00,0x66,0x72,0x61,0x67, + 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x05,0x00,0x03,0x00,0x0e,0x00,0x00,0x00, + 0x74,0x65,0x78,0x00,0x05,0x00,0x03,0x00,0x11,0x00,0x00,0x00,0x75,0x76,0x00,0x00, + 0x05,0x00,0x04,0x00,0x16,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x0a,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x16,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x13,0x00,0x02,0x00,0x03,0x00,0x00,0x00,0x21,0x00,0x03,0x00,0x04,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x07,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x17,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x19,0x00,0x09,0x00,0x0b,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x1b,0x00,0x03,0x00,0x0c,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x0d,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x17,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x36,0x00,0x05,0x00,0x03,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x06,0x00,0x00,0x00,0x08,0x00,0x04,0x00, + 0x01,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x0c,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x4f,0x00,0x07,0x00, + 0x12,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x13,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x57,0x00,0x05,0x00,0x08,0x00,0x00,0x00, + 0x15,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x85,0x00,0x05,0x00, + 0x08,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x17,0x00,0x00,0x00, + 0x3e,0x00,0x03,0x00,0x0a,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0xfd,0x00,0x01,0x00, + 0x38,0x00,0x01,0x00, }; #elif defined(SOKOL_DUMMY_BACKEND) -static const char* _sgl_vs_src = ""; -static const char* _sgl_fs_src = ""; +static const char* _sgl_vs_source_dummy = ""; +static const char* _sgl_fs_source_dummy = ""; +#else +#error "Please define one of SOKOL_GLCORE33, SOKOL_GLES2, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND!" #endif typedef enum { @@ -1331,15 +2192,22 @@ static _sgl_pipeline_t* _sgl_lookup_pipeline(uint32_t pip_id) { return 0; } +/* make pipeline id from uint32_t id */ +static sgl_pipeline _sgl_make_pip_id(uint32_t pip_id) { + sgl_pipeline pip; + pip.id = pip_id; + return pip; +} + static sgl_pipeline _sgl_alloc_pipeline(void) { sgl_pipeline res; int slot_index = _sgl_pool_alloc_index(&_sgl.pip_pool.pool); if (_SGL_INVALID_SLOT_INDEX != slot_index) { - res.id =_sgl_slot_alloc(&_sgl.pip_pool.pool, &_sgl.pip_pool.pips[slot_index].slot, slot_index); + res = _sgl_make_pip_id(_sgl_slot_alloc(&_sgl.pip_pool.pool, &_sgl.pip_pool.pips[slot_index].slot, slot_index)); } else { /* pool is exhausted */ - res.id = SG_INVALID_ID; + res = _sgl_make_pip_id(SG_INVALID_ID); } return res; } @@ -1446,9 +2314,8 @@ static sg_pipeline _sgl_get_pipeline(sgl_pipeline pip_id, _sgl_primitive_type_t return pip->pip[prim_type]; } else { - sg_pipeline dummy_pip; - dummy_pip.id = SG_INVALID_ID; - return dummy_pip; + sg_pipeline dummy_id = { SG_INVALID_ID }; + return dummy_id; } } @@ -1817,28 +2684,64 @@ SOKOL_API_IMPL void sgl_setup(const sgl_desc_t* desc) { shd_desc.attrs[0].name = "position"; shd_desc.attrs[1].name = "texcoord0"; shd_desc.attrs[2].name = "color0"; - shd_desc.attrs[0].sem_name = "POSITION"; + shd_desc.attrs[0].sem_name = "TEXCOORD"; + shd_desc.attrs[0].sem_index = 0; shd_desc.attrs[1].sem_name = "TEXCOORD"; - shd_desc.attrs[2].sem_name = "COLOR"; + shd_desc.attrs[1].sem_index = 1; + shd_desc.attrs[2].sem_name = "TEXCOORD"; + shd_desc.attrs[2].sem_index = 2; sg_shader_uniform_block_desc* ub = &shd_desc.vs.uniform_blocks[0]; ub->size = sizeof(_sgl_uniform_t); - ub->uniforms[0].name = "mvp"; - ub->uniforms[0].type = SG_UNIFORMTYPE_MAT4; - ub->uniforms[1].name = "tm"; - ub->uniforms[1].type = SG_UNIFORMTYPE_MAT4; + ub->uniforms[0].name = "vs_params"; + ub->uniforms[0].type = SG_UNIFORMTYPE_FLOAT4; + ub->uniforms[0].array_count = 8; shd_desc.fs.images[0].name = "tex"; shd_desc.fs.images[0].type = SG_IMAGETYPE_2D; - #if defined(SOKOL_D3D11) - shd_desc.vs.byte_code = _sgl_vs_bin; - shd_desc.vs.byte_code_size = sizeof(_sgl_vs_bin); - shd_desc.fs.byte_code = _sgl_fs_bin; - shd_desc.fs.byte_code_size = sizeof(_sgl_fs_bin); - #else - shd_desc.vs.source = _sgl_vs_src; - shd_desc.fs.source = _sgl_fs_src; - #endif + shd_desc.fs.images[0].sampler_type = SG_SAMPLERTYPE_FLOAT; shd_desc.label = "sgl-shader"; + #if defined(SOKOL_GLCORE33) + shd_desc.vs.source = _sgl_vs_source_glsl330; + shd_desc.fs.source = _sgl_fs_source_glsl330; + #elif defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + shd_desc.vs.source = _sgl_vs_source_glsl100; + shd_desc.fs.source = _sgl_fs_source_glsl100; + #elif defined(SOKOL_METAL) + shd_desc.vs.entry = "main0"; + shd_desc.fs.entry = "main0"; + switch (sg_query_backend()) { + case SG_BACKEND_METAL_MACOS: + shd_desc.vs.byte_code = _sgl_vs_bytecode_metal_macos; + shd_desc.vs.byte_code_size = sizeof(_sgl_vs_bytecode_metal_macos); + shd_desc.fs.byte_code = _sgl_fs_bytecode_metal_macos; + shd_desc.fs.byte_code_size = sizeof(_sgl_fs_bytecode_metal_macos); + break; + case SG_BACKEND_METAL_IOS: + shd_desc.vs.byte_code = _sgl_vs_bytecode_metal_ios; + shd_desc.vs.byte_code_size = sizeof(_sgl_vs_bytecode_metal_ios); + shd_desc.fs.byte_code = _sgl_fs_bytecode_metal_ios; + shd_desc.fs.byte_code_size = sizeof(_sgl_fs_bytecode_metal_ios); + break; + default: + shd_desc.vs.source = _sgl_vs_source_metal_sim; + shd_desc.fs.source = _sgl_fs_source_metal_sim; + break; + } + #elif defined(SOKOL_D3D11) + shd_desc.vs.byte_code = _sgl_vs_bytecode_hlsl4; + shd_desc.vs.byte_code_size = sizeof(_sgl_vs_bytecode_hlsl4); + shd_desc.fs.byte_code = _sgl_fs_bytecode_hlsl4; + shd_desc.fs.byte_code_size = sizeof(_sgl_fs_bytecode_hlsl4); + #elif defined(SOKOL_WGPU) + shd_desc.vs.byte_code = _sgl_vs_bytecode_wgpu; + shd_desc.vs.byte_code_size = sizeof(_sgl_vs_bytecode_wgpu); + shd_desc.fs.byte_code = _sgl_fs_bytecode_wgpu; + shd_desc.fs.byte_code_size = sizeof(_sgl_fs_bytecode_wgpu); + #else + shd_desc.vs.source = _sgl_vs_src_dummy; + shd_desc.fs.source = _sgl_fs_src_dummy; + #endif _sgl.shd = sg_make_shader(&shd_desc); + SOKOL_ASSERT(SG_INVALID_ID != _sgl.shd.id); /* create default pipeline object */ sg_pipeline_desc def_pip_desc; @@ -1857,15 +2760,19 @@ SOKOL_API_IMPL void sgl_setup(const sgl_desc_t* desc) { } SOKOL_API_IMPL void sgl_shutdown(void) { - SOKOL_ASSERT(_sgl.init_cookie == 0xABCDABCD); + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); SOKOL_FREE(_sgl.vertices); _sgl.vertices = 0; SOKOL_FREE(_sgl.uniforms); _sgl.uniforms = 0; SOKOL_FREE(_sgl.commands); _sgl.commands = 0; + sg_push_debug_group("sokol-gl"); sg_destroy_buffer(_sgl.vbuf); sg_destroy_image(_sgl.def_img); sg_destroy_shader(_sgl.shd); - _sgl_destroy_pipeline(_sgl.def_pip); - // FIXME: need to destroy ALL valid pipeline objects in pool here + for (int i = 0; i < _sgl.pip_pool.pool.size; i++) { + _sgl_pipeline_t* pip = &_sgl.pip_pool.pips[i]; + _sgl_destroy_pipeline(_sgl_make_pip_id(pip->slot.id)); + } + sg_pop_debug_group(); _sgl_discard_pipeline_pool(); _sgl.init_cookie = 0; } @@ -2030,10 +2937,8 @@ SOKOL_API_IMPL void sgl_begin_quads(void) { SOKOL_API_IMPL void sgl_end(void) { SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); SOKOL_ASSERT(_sgl.in_begin); + SOKOL_ASSERT(_sgl.cur_vertex >= _sgl.base_vertex); _sgl.in_begin = false; - if (_sgl.base_vertex == _sgl.cur_vertex) { - return; - } bool matrix_dirty = _sgl.matrix_dirty; if (matrix_dirty) { _sgl.matrix_dirty = false; @@ -2360,7 +3265,9 @@ SOKOL_API_IMPL void sgl_draw(void) { cur_uniform_index = args->uniform_index; } /* FIXME: what if number of vertices doesn't match the primitive type? */ - sg_draw(args->base_vertex, args->num_vertices, 1); + if (args->num_vertices > 0) { + sg_draw(args->base_vertex, args->num_vertices, 1); + } } break; } diff --git a/vlib/gg/gg.v b/vlib/gg/gg.v index c8f6cd2fd7..f7e3faa086 100644 --- a/vlib/gg/gg.v +++ b/vlib/gg/gg.v @@ -74,6 +74,24 @@ pub: fn gg_init_sokol_window(user_data voidptr) { mut g := &Context(user_data) + mtl_desc := C.sg_mtl_context_desc { + device: sapp.metal_get_device() + renderpass_descriptor_cb: sapp.metal_get_renderpass_descriptor + drawable_cb: sapp.metal_get_drawable + } + d3d11_desc := C.sg_d3d11_context_desc { + device: sapp.d3d11_get_device() + device_context: sapp.d3d11_get_device_context() + render_target_view_cb: sapp.d3d11_get_render_target_view + depth_stencil_view_cb: sapp.d3d11_get_depth_stencil_view + } + desc := C.sg_desc{ + context: C.sg_context_desc{ + metal: mtl_desc + d3d11: d3d11_desc + } + } + /* desc := C.sg_desc{ mtl_device: sapp.metal_get_device() mtl_renderpass_descriptor_cb: sapp.metal_get_renderpass_descriptor @@ -83,6 +101,7 @@ fn gg_init_sokol_window(user_data voidptr) { d3d11_render_target_view_cb: sapp.d3d11_get_render_target_view d3d11_depth_stencil_view_cb: sapp.d3d11_get_depth_stencil_view } + */ gfx.setup(&desc) sgl_desc := C.sgl_desc_t{} sgl.setup(&sgl_desc) diff --git a/vlib/sokol/f/f.v b/vlib/sokol/f/f.v index 2054bf9168..7df6fab0e8 100644 --- a/vlib/sokol/f/f.v +++ b/vlib/sokol/f/f.v @@ -6,7 +6,6 @@ pub const ( used_import = 1 + fontstash.used_import ) -/* #flag windows -I @VROOT/thirdparty/freetype/include #flag windows -L @VROOT/thirdparty/freetype/win64 @@ -23,7 +22,11 @@ pub const ( #flag darwin -lfreetype #flag darwin -lpng -lbz2 -lz -*/ + + + + + #flag linux -I. diff --git a/vlib/sokol/gfx/gfx_structs.v b/vlib/sokol/gfx/gfx_structs.v index 232bb9d324..146280f160 100644 --- a/vlib/sokol/gfx/gfx_structs.v +++ b/vlib/sokol/gfx/gfx_structs.v @@ -8,22 +8,60 @@ pub struct C.sg_desc { pipeline_pool_size int pass_pool_size int context_pool_size int - /* GL specific */ + context C.sg_context_desc + /* + // GL specific gl_force_gles2 bool - /* Metal-specific */ + // Metal-specific mtl_device voidptr mtl_renderpass_descriptor_cb fn() voidptr mtl_drawable_cb fn() voidptr mtl_global_uniform_buffer_size int mtl_sampler_cache_size int - /* D3D11-specific */ + // D3D11-specific d3d11_device voidptr d3d11_device_context voidptr d3d11_render_target_view_cb fn() voidptr d3d11_depth_stencil_view_cb fn() voidptr + */ _end_canary u32 } + +pub struct C.sg_context_desc { + /* + sg_pixel_format color_format; + sg_pixel_format depth_format; + int sample_count; + sg_wgpu_context_desc wgpu; + */ + sample_count int +gl C.sg_gl_context_desc +metal C.sg_mtl_context_desc +d3d11 C.sg_d3d11_context_desc + + color_format PixelFormat + depth_format PixelFormat +} + +pub struct C.sg_gl_context_desc { + gl_force_gles2 bool +} + +pub struct C.sg_mtl_context_desc { + device voidptr + renderpass_descriptor_cb fn() voidptr + drawable_cb fn() voidptr +} + +pub struct C.sg_d3d11_context_desc { + device voidptr + device_context voidptr + render_target_view_cb fn() voidptr + depth_stencil_view_cb fn() voidptr +} + + pub struct C.sg_pipeline_desc { pub mut: _start_canary u32