From 0bf85d049e200a31c8b83ab09bbe579a75aa3085 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Thu, 5 Oct 2023 20:22:41 +0300 Subject: [PATCH] sokol: upgrade to latest version, fix all related issues --- .github/workflows/other_ci.yml | 2 +- cmd/tools/vshader.v | 58 +- examples/sokol/01_cubes/cube.v | 8 +- examples/sokol/02_cubes_glsl/cube_glsl.v | 14 +- .../sokol/03_march_tracing_glsl/rt_glsl.v | 14 +- examples/sokol/04_multi_shader_glsl/rt_glsl.v | 16 +- examples/sokol/05_instancing_glsl/rt_glsl.v | 14 +- .../sokol/06_obj_viewer/modules/obj/rend.v | 10 +- examples/sokol/06_obj_viewer/show_obj.v | 4 +- examples/sokol/fonts.v | 4 +- examples/sokol/freetype_raven.v | 4 +- examples/viewer/view.v | 8 +- thirdparty/sokol/sokol_app.h | 2363 ++--- thirdparty/sokol/sokol_audio.h | 1218 ++- thirdparty/sokol/sokol_gfx.h | 8380 +++++++++-------- thirdparty/sokol/util/sokol_fontstash.h | 1797 ++-- thirdparty/sokol/util/sokol_gl.h | 2077 ++-- vlib/gg/gg.c.v | 18 +- vlib/gg/image.c.v | 12 +- vlib/sokol/c/declaration.c.v | 2 +- vlib/sokol/gfx/enums.v | 3 +- vlib/sokol/gfx/gfx.c.v | 10 +- vlib/sokol/gfx/gfx_allocator_and_logger.c.v | 6 +- vlib/sokol/gfx/gfx_structs.c.v | 105 +- vlib/sokol/gfx/gfx_utils.c.v | 4 +- vlib/sokol/memory/memory.v | 3 + vlib/sokol/sapp/sapp.c.v | 16 +- vlib/sokol/sapp/sapp_allocator_and_logger.c.v | 6 +- vlib/sokol/sapp/sapp_funcs.c.v | 3 - vlib/sokol/sapp/sapp_structs.c.v | 5 +- vlib/sokol/sfons/sfons.c.v | 4 +- vlib/sokol/sfons/sfons_funcs.c.v | 4 +- vlib/sokol/sgl/sgl.c.v | 12 +- vlib/sokol/sgl/sgl_allocator_and_logger.c.v | 6 +- vlib/sokol/sgl/sgl_funcs.c.v | 2 +- vlib/x/ttf/render_sokol_cpu.v | 8 +- 36 files changed, 8214 insertions(+), 8006 deletions(-) diff --git a/.github/workflows/other_ci.yml b/.github/workflows/other_ci.yml index 1a30918edc9ff6..df75196d6414a1 100644 --- a/.github/workflows/other_ci.yml +++ b/.github/workflows/other_ci.yml @@ -101,7 +101,7 @@ jobs: - name: Shader examples can be build run: | - wget https://github.com/floooh/sokol-tools-bin/raw/33d2e4cc26088c6c28eaef5467990f8940d15aab/bin/linux/sokol-shdc + wget https://github.com/floooh/sokol-tools-bin/raw/6040b18d39649d56dbae2ae1aed59fb755b26369/bin/linux/sokol-shdc chmod +x ./sokol-shdc for f in examples/sokol/02_cubes_glsl/cube_glsl \ examples/sokol/03_march_tracing_glsl/rt_glsl \ diff --git a/cmd/tools/vshader.v b/cmd/tools/vshader.v index be53e3fba55d0b..9e056f29d47b6c 100644 --- a/cmd/tools/vshader.v +++ b/cmd/tools/vshader.v @@ -13,13 +13,14 @@ // The shader language used is, as described on the overview page linked above, an 'annotated GLSL' // and 'modern GLSL' (v450) shader language format. import os +import time import io.util import flag import net.http const ( - shdc_full_hash = '33d2e4cc26088c6c28eaef5467990f8940d15aab' - tool_version = '0.0.1' + shdc_full_hash = '6040b18d39649d56dbae2ae1aed59fb755b26369' + tool_version = '0.0.2' tool_description = "Compile shaders in sokol's annotated GLSL format to C headers for use with sokol based apps" tool_name = os.file_name(os.executable()) cache_dir = os.join_path(os.cache_dir(), 'v', tool_name) @@ -29,15 +30,15 @@ const ( const ( supported_hosts = ['linux', 'macos', 'windows'] supported_slangs = [ - 'glsl330', // desktop GL - 'glsl100', // GLES2 / WebGL - 'glsl300es', // GLES3 / WebGL2 - 'hlsl4', // D3D11 - 'hlsl5', // D3D11 - 'metal_macos', // Metal on macOS - 'metal_ios', // Metal on iOS device - 'metal_sim', // Metal on iOS simulator - 'wgpu', // WebGPU + 'glsl330', // desktop OpenGL backend (SOKOL_GLCORE33) + 'glsl100', // OpenGLES2 and WebGL (SOKOL_GLES3) + 'glsl300es', // OpenGLES3 and WebGL2 (SOKOL_GLES3) + 'hlsl4', // Direct3D11 with HLSL4 (SOKOL_D3D11) + 'hlsl5', // Direct3D11 with HLSL5 (SOKOL_D3D11) + 'metal_macos', // Metal on macOS (SOKOL_METAL) + 'metal_ios', // Metal on iOS devices (SOKOL_METAL) + 'metal_sim', // Metal on iOS simulator (SOKOL_METAL) + 'wgsl', // WebGPU (SOKOL_WGPU) ] default_slangs = [ 'glsl330', @@ -48,7 +49,7 @@ const ( 'metal_macos', 'metal_ios', 'metal_sim', - 'wgpu', + 'wgsl', ] shdc_version = shdc_full_hash[0..8] @@ -56,6 +57,7 @@ const ( 'windows': 'https://github.com/floooh/sokol-tools-bin/raw/${shdc_full_hash}/bin/win32/sokol-shdc.exe' 'macos': 'https://github.com/floooh/sokol-tools-bin/raw/${shdc_full_hash}/bin/osx/sokol-shdc' 'linux': 'https://github.com/floooh/sokol-tools-bin/raw/${shdc_full_hash}/bin/linux/sokol-shdc' + 'osx_a64': 'https://github.com/floooh/sokol-tools-bin/raw/${shdc_full_hash}/bin/osx_arm64/sokol-shdc' } shdc_version_file = os.join_path(cache_dir, 'sokol-shdc.version') shdc = shdc_exe() @@ -242,11 +244,20 @@ fn ensure_external_tools(opt Options) ! { is_shdc_available := os.is_file(shdc) is_shdc_executable := os.is_executable(shdc) - if is_shdc_available && is_shdc_executable { - if opt.verbose { - version := os.read_file(shdc_version_file) or { 'unknown' } - eprintln('${tool_name} using sokol-shdc version ${version} at "${shdc}"') + if opt.verbose { + eprintln('reading version from ${shdc_version_file} ...') + version := os.read_file(shdc_version_file) or { 'unknown' } + eprintln('${tool_name} using sokol-shdc version ${version} at "${shdc}"') + eprintln('executable: ${is_shdc_executable}') + eprintln(' available: ${is_shdc_available}') + if is_shdc_available { + eprintln(' file path: ${shdc}') + eprintln(' file size: ${os.file_size(shdc)}') + eprintln(' file time: ${time.unix_microsecond(os.file_last_mod_unix(shdc), + 0)}') } + } + if is_shdc_available && is_shdc_executable { return } @@ -263,15 +274,24 @@ fn shdc_exe() string { // download_shdc downloads the `sokol-shdc` tool to an OS specific cache directory. fn download_shdc(opt Options) ! { // We want to use the same, runtime, OS type as this tool is invoked on. - download_url := shdc_urls[runtime_os] or { '' } + mut download_url := shdc_urls[runtime_os] or { '' } + $if arm64 && macos { + download_url = shdc_urls['osx_a64'] + } if download_url == '' { return error('${tool_name} failed to download an external dependency "sokol-shdc" for ${runtime_os}.\nThe supported host platforms for shader compilation is ${supported_hosts}') } + if opt.verbose { + eprintln('> reading version from ${shdc_version_file} ...') + } update_to_shdc_version := os.read_file(shdc_version_file) or { shdc_version } + if opt.verbose { + eprintln('> update_to_shdc_version: ${update_to_shdc_version} | shdc_version: ${shdc_version}') + } file := shdc_exe() if opt.verbose { if shdc_version != update_to_shdc_version && os.exists(file) { - eprintln('${tool_name} updating sokol-shdc to version ${update_to_shdc_version} ...') + eprintln('${tool_name} updating sokol-shdc to version ${shdc_version} ...') } else { eprintln('${tool_name} installing sokol-shdc version ${update_to_shdc_version} ...') } @@ -298,5 +318,5 @@ fn download_shdc(opt Options) ! { os.mv(file, shdc)! } // Update internal version file - os.write_file(shdc_version_file, update_to_shdc_version)! + os.write_file(shdc_version_file, shdc_version)! } diff --git a/examples/sokol/01_cubes/cube.v b/examples/sokol/01_cubes/cube.v index 48ba66d19acec4..d0cecf5660be1d 100644 --- a/examples/sokol/01_cubes/cube.v +++ b/examples/sokol/01_cubes/cube.v @@ -45,11 +45,11 @@ fn create_texture(w int, h int, buf &u8) gfx.Image { width: w height: h num_mipmaps: 0 - min_filter: .linear - mag_filter: .linear + // min_filter: .linear + // mag_filter: .linear // usage: .dynamic - wrap_u: .clamp_to_edge - wrap_v: .clamp_to_edge + // wrap_u: .clamp_to_edge + // wrap_v: .clamp_to_edge label: &u8(0) d3d11_texture: 0 } diff --git a/examples/sokol/02_cubes_glsl/cube_glsl.v b/examples/sokol/02_cubes_glsl/cube_glsl.v index 33a8427b54392c..f1769628abd4fa 100644 --- a/examples/sokol/02_cubes_glsl/cube_glsl.v +++ b/examples/sokol/02_cubes_glsl/cube_glsl.v @@ -61,11 +61,11 @@ fn create_texture(w int, h int, buf &byte) gfx.Image { width: w height: h num_mipmaps: 0 - min_filter: .linear - mag_filter: .linear + // min_filter: .linear + // mag_filter: .linear // usage: .dynamic - wrap_u: .clamp_to_edge - wrap_v: .clamp_to_edge + // wrap_u: .clamp_to_edge + // wrap_v: .clamp_to_edge label: &u8(0) d3d11_texture: 0 } @@ -361,7 +361,7 @@ fn init_cube_glsl(mut app App) { app.cube_bind.vertex_buffers[0] = vbuf app.cube_bind.index_buffer = ibuf - app.cube_bind.fs_images[C.SLOT_tex] = app.texture + app.cube_bind.fs.images[C.SLOT_tex] = app.texture app.cube_pip_glsl = gfx.make_pipeline(&pipdesc) println('GLSL init DONE!') } @@ -374,8 +374,8 @@ fn draw_cube_glsl(app App) { // clear ws := gg.window_size_real_pixels() mut color_action := gfx.ColorAttachmentAction{ - action: unsafe { gfx.Action(C.SG_ACTION_DONTCARE) } // C.SG_ACTION_CLEAR) - value: gfx.Color{ + load_action: unsafe { gfx.Action(C.SG_LOADACTION_DONTCARE) } // C.SG_ACTION_CLEAR) + clear_value: gfx.Color{ r: 1.0 g: 1.0 b: 1.0 diff --git a/examples/sokol/03_march_tracing_glsl/rt_glsl.v b/examples/sokol/03_march_tracing_glsl/rt_glsl.v index fe75fa5dcb644a..84e383af764721 100644 --- a/examples/sokol/03_march_tracing_glsl/rt_glsl.v +++ b/examples/sokol/03_march_tracing_glsl/rt_glsl.v @@ -60,11 +60,11 @@ fn create_texture(w int, h int, buf &byte) gfx.Image { width: w height: h num_mipmaps: 0 - min_filter: .linear - mag_filter: .linear + // min_filter: .linear + // mag_filter: .linear // usage: .dynamic - wrap_u: .clamp_to_edge - wrap_v: .clamp_to_edge + // wrap_u: .clamp_to_edge + // wrap_v: .clamp_to_edge label: &u8(0) d3d11_texture: 0 } @@ -223,7 +223,7 @@ fn init_cube_glsl(mut app App) { app.cube_bind.vertex_buffers[0] = vbuf app.cube_bind.index_buffer = ibuf - app.cube_bind.fs_images[C.SLOT_tex] = app.texture + app.cube_bind.fs.images[C.SLOT_tex] = app.texture app.cube_pip_glsl = gfx.make_pipeline(&pipdesc) println('GLSL init DONE!') } @@ -312,8 +312,8 @@ fn frame(mut app App) { // clear mut color_action := gfx.ColorAttachmentAction{ - action: .clear - value: gfx.Color{ + load_action: .clear + clear_value: gfx.Color{ r: 0.0 g: 0.0 b: 0.0 diff --git a/examples/sokol/04_multi_shader_glsl/rt_glsl.v b/examples/sokol/04_multi_shader_glsl/rt_glsl.v index c6697118e947fb..e2e966bdfe551a 100644 --- a/examples/sokol/04_multi_shader_glsl/rt_glsl.v +++ b/examples/sokol/04_multi_shader_glsl/rt_glsl.v @@ -63,11 +63,11 @@ fn create_texture(w int, h int, buf byteptr) gfx.Image { width: w height: h num_mipmaps: 0 - min_filter: .linear - mag_filter: .linear + // min_filter: .linear + // mag_filter: .linear // usage: .dynamic - wrap_u: .clamp_to_edge - wrap_v: .clamp_to_edge + // wrap_u: .clamp_to_edge + // wrap_v: .clamp_to_edge label: &u8(0) d3d11_texture: 0 } @@ -227,7 +227,7 @@ fn init_cube_glsl_m(mut app App) { unsafe { vmemset(&bind, 0, int(sizeof(bind))) } bind.vertex_buffers[0] = vbuf bind.index_buffer = ibuf - bind.fs_images[C.SLOT_tex] = app.texture + bind.fs.images[C.SLOT_tex] = app.texture app.bind['march'] = bind app.pipe['march'] = gfx.make_pipeline(&pipdesc) @@ -342,7 +342,7 @@ fn init_cube_glsl_p(mut app App) { unsafe { vmemset(&bind, 0, int(sizeof(bind))) } bind.vertex_buffers[0] = vbuf bind.index_buffer = ibuf - bind.fs_images[C.SLOT_tex] = app.texture + bind.fs.images[C.SLOT_tex] = app.texture app.bind['puppy'] = bind app.pipe['puppy'] = gfx.make_pipeline(&pipdesc) @@ -501,8 +501,8 @@ fn frame(mut app App) { // clear mut color_action := gfx.ColorAttachmentAction{ - action: .clear - value: gfx.Color{ + load_action: .clear + clear_value: gfx.Color{ r: 0.0 g: 0.0 b: 0.0 diff --git a/examples/sokol/05_instancing_glsl/rt_glsl.v b/examples/sokol/05_instancing_glsl/rt_glsl.v index 9fb9179a40daf1..912bcd80d70c3c 100644 --- a/examples/sokol/05_instancing_glsl/rt_glsl.v +++ b/examples/sokol/05_instancing_glsl/rt_glsl.v @@ -71,11 +71,11 @@ fn create_texture(w int, h int, buf byteptr) gfx.Image { width: w height: h num_mipmaps: 0 - min_filter: .linear - mag_filter: .linear +// min_filter: .linear +// mag_filter: .linear //usage: .dynamic - wrap_u: .clamp_to_edge - wrap_v: .clamp_to_edge +// wrap_u: .clamp_to_edge +// wrap_v: .clamp_to_edge label: &u8(0) d3d11_texture: 0 } @@ -257,7 +257,7 @@ fn init_cube_glsl_i(mut app App) { bind.vertex_buffers[0] = vbuf // vertex buffer bind.vertex_buffers[1] = inst_buf // instance buffer bind.index_buffer = ibuf - bind.fs_images[C.SLOT_tex] = app.texture + bind.fs.images[C.SLOT_tex] = app.texture app.bind['inst'] = bind app.pipe['inst'] = gfx.make_pipeline(&pipdesc) @@ -384,8 +384,8 @@ fn frame(mut app App) { // clear mut color_action := gfx.ColorAttachmentAction{ - action: .clear - value: gfx.Color{ + load_action: .clear + clear_value: gfx.Color{ r: 0.0 g: 0.0 b: 0.0 diff --git a/examples/sokol/06_obj_viewer/modules/obj/rend.v b/examples/sokol/06_obj_viewer/modules/obj/rend.v index 0daad4f139ea8b..d60c19251f72d2 100644 --- a/examples/sokol/06_obj_viewer/modules/obj/rend.v +++ b/examples/sokol/06_obj_viewer/modules/obj/rend.v @@ -24,11 +24,11 @@ pub fn create_texture(w int, h int, buf &u8) gfx.Image { width: w height: h num_mipmaps: 0 - min_filter: .linear - mag_filter: .linear + // min_filter: .linear + // mag_filter: .linear // usage: .dynamic - wrap_u: .clamp_to_edge - wrap_v: .clamp_to_edge + // wrap_u: .clamp_to_edge + // wrap_v: .clamp_to_edge label: &u8(0) d3d11_texture: 0 } @@ -133,7 +133,7 @@ pub fn (mut obj_part ObjPart) create_pipeline(in_part []int, shader gfx.Shader, res.bind.vertex_buffers[0] = vbuf res.bind.index_buffer = ibuf - res.bind.fs_images[C.SLOT_tex] = texture + res.bind.fs.images[C.SLOT_tex] = texture res.pipeline = gfx.make_pipeline(&pipdesc) // println('Buffers part [$in_part] init done!') diff --git a/examples/sokol/06_obj_viewer/show_obj.v b/examples/sokol/06_obj_viewer/show_obj.v index d561540a72ecec..68364736a732b3 100644 --- a/examples/sokol/06_obj_viewer/show_obj.v +++ b/examples/sokol/06_obj_viewer/show_obj.v @@ -150,8 +150,8 @@ fn frame(mut app App) { // clear mut color_action := gfx.ColorAttachmentAction{ - action: .clear - value: gfx.Color{ + load_action: .clear + clear_value: gfx.Color{ r: 0.0 g: 0.0 b: 0.0 diff --git a/examples/sokol/fonts.v b/examples/sokol/fonts.v index 05e562ed2e815c..600932eb0dc936 100644 --- a/examples/sokol/fonts.v +++ b/examples/sokol/fonts.v @@ -15,8 +15,8 @@ mut: fn main() { mut color_action := gfx.ColorAttachmentAction{ - action: .clear - value: gfx.Color{ + load_action: .clear + clear_value: gfx.Color{ r: 0.3 g: 0.3 b: 0.32 diff --git a/examples/sokol/freetype_raven.v b/examples/sokol/freetype_raven.v index d523d89b8e78c6..9669b152ead552 100644 --- a/examples/sokol/freetype_raven.v +++ b/examples/sokol/freetype_raven.v @@ -63,8 +63,8 @@ mut: fn main() { mut color_action := gfx.ColorAttachmentAction{ - action: .clear - value: gfx.Color{ + load_action: .clear + clear_value: gfx.Color{ r: 1.0 g: 1.0 b: 1.0 diff --git a/examples/viewer/view.v b/examples/viewer/view.v index a30d27193997bb..cb8b4139017d3e 100644 --- a/examples/viewer/view.v +++ b/examples/viewer/view.v @@ -121,11 +121,11 @@ fn create_texture(w int, h int, buf &u8) gfx.Image { width: w height: h num_mipmaps: 0 - min_filter: .linear - mag_filter: .linear + // min_filter: .linear + // mag_filter: .linear // usage: .dynamic - wrap_u: .clamp_to_edge - wrap_v: .clamp_to_edge + // wrap_u: .clamp_to_edge + // wrap_v: .clamp_to_edge label: &u8(0) d3d11_texture: 0 } diff --git a/thirdparty/sokol/sokol_app.h b/thirdparty/sokol/sokol_app.h index 674d5b0485d449..b6cd9fdbac3e26 100644 --- a/thirdparty/sokol/sokol_app.h +++ b/thirdparty/sokol/sokol_app.h @@ -26,7 +26,6 @@ project): #define SOKOL_GLCORE33 - #define SOKOL_GLES2 #define SOKOL_GLES3 #define SOKOL_D3D11 #define SOKOL_METAL @@ -36,7 +35,6 @@ SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) 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_APP_API_DECL - public function declaration prefix (default: extern) @@ -84,10 +82,6 @@ On Linux, you also need to use the -pthread compiler and linker option, otherwise weird things will happen, see here for details: https://github.com/floooh/sokol/issues/376 - Building for UWP requires a recent Visual Studio toolchain and Windows SDK - (at least VS2019 and Windows SDK 10.0.19041.0). When the UWP backend is - selected, the sokol_app.h implementation must be compiled as C++17. - On macOS and iOS, the implementation must be compiled as Objective-C. FEATURE OVERVIEW @@ -100,52 +94,51 @@ - makes the rendered frame visible - provides keyboard-, mouse- and low-level touch-events - platforms: MacOS, iOS, HTML5, Win32, Linux/RaspberryPi, Android - - 3D-APIs: Metal, D3D11, GL3.2, GLES2, GLES3, WebGL, WebGL2 + - 3D-APIs: Metal, D3D11, GL3.2, GLES3, WebGL, WebGL2 FEATURE/PLATFORM MATRIX ======================= - | Windows | macOS | Linux | iOS | Android | UWP | HTML5 - --------------------+---------+-------+-------+-------+---------+------+------- - gl 3.x | YES | YES | YES | --- | --- | --- | --- - gles2/webgl | --- | --- | YES(2)| YES | YES | --- | YES - gles3/webgl2 | --- | --- | YES(2)| YES | YES | --- | YES - metal | --- | YES | --- | YES | --- | --- | --- - d3d11 | YES | --- | --- | --- | --- | YES | --- - KEY_DOWN | YES | YES | YES | SOME | TODO | YES | YES - KEY_UP | YES | YES | YES | SOME | TODO | YES | YES - CHAR | YES | YES | YES | YES | TODO | YES | YES - MOUSE_DOWN | YES | YES | YES | --- | --- | YES | YES - MOUSE_UP | YES | YES | YES | --- | --- | YES | YES - MOUSE_SCROLL | YES | YES | YES | --- | --- | YES | YES - MOUSE_MOVE | YES | YES | YES | --- | --- | YES | YES - MOUSE_ENTER | YES | YES | YES | --- | --- | YES | YES - MOUSE_LEAVE | YES | YES | YES | --- | --- | YES | YES - TOUCHES_BEGAN | --- | --- | --- | YES | YES | TODO | YES - TOUCHES_MOVED | --- | --- | --- | YES | YES | TODO | YES - TOUCHES_ENDED | --- | --- | --- | YES | YES | TODO | YES - TOUCHES_CANCELLED | --- | --- | --- | YES | YES | TODO | YES - RESIZED | YES | YES | YES | YES | YES | YES | YES - ICONIFIED | YES | YES | YES | --- | --- | YES | --- - RESTORED | YES | YES | YES | --- | --- | YES | --- - FOCUSED | YES | YES | YES | --- | --- | --- | YES - UNFOCUSED | YES | YES | YES | --- | --- | --- | YES - SUSPENDED | --- | --- | --- | YES | YES | YES | TODO - RESUMED | --- | --- | --- | YES | YES | YES | TODO - QUIT_REQUESTED | YES | YES | YES | --- | --- | --- | YES - IME | TODO | TODO? | TODO | ??? | TODO | --- | ??? - key repeat flag | YES | YES | YES | --- | --- | YES | YES - windowed | YES | YES | YES | --- | --- | YES | YES - fullscreen | YES | YES | YES | YES | YES | YES | --- - mouse hide | YES | YES | YES | --- | --- | YES | YES - mouse lock | YES | YES | YES | --- | --- | TODO | YES - set cursor type | YES | YES | YES | --- | --- | YES | YES - screen keyboard | --- | --- | --- | YES | TODO | TODO | YES - swap interval | YES | YES | YES | YES | TODO | --- | YES - high-dpi | YES | YES | TODO | YES | YES | YES | YES - clipboard | YES | YES | TODO | --- | --- | TODO | YES - MSAA | YES | YES | YES | YES | YES | TODO | YES - drag'n'drop | YES | YES | YES | --- | --- | TODO | YES - window icon | YES | YES(1)| YES | --- | --- | TODO | YES + | Windows | macOS | Linux | iOS | Android | HTML5 + --------------------+---------+-------+-------+-------+---------+-------- + gl 3.x | YES | YES | YES | --- | --- | --- + gles3/webgl2 | --- | --- | YES(2)| YES | YES | YES + metal | --- | YES | --- | YES | --- | --- + d3d11 | YES | --- | --- | --- | --- | --- + KEY_DOWN | YES | YES | YES | SOME | TODO | YES + KEY_UP | YES | YES | YES | SOME | TODO | YES + CHAR | YES | YES | YES | YES | TODO | YES + MOUSE_DOWN | YES | YES | YES | --- | --- | YES + MOUSE_UP | YES | YES | YES | --- | --- | YES + MOUSE_SCROLL | YES | YES | YES | --- | --- | YES + MOUSE_MOVE | YES | YES | YES | --- | --- | YES + MOUSE_ENTER | YES | YES | YES | --- | --- | YES + MOUSE_LEAVE | YES | YES | YES | --- | --- | YES + TOUCHES_BEGAN | --- | --- | --- | YES | YES | YES + TOUCHES_MOVED | --- | --- | --- | YES | YES | YES + TOUCHES_ENDED | --- | --- | --- | YES | YES | YES + TOUCHES_CANCELLED | --- | --- | --- | YES | YES | YES + RESIZED | YES | YES | YES | YES | YES | YES + ICONIFIED | YES | YES | YES | --- | --- | --- + RESTORED | YES | YES | YES | --- | --- | --- + FOCUSED | YES | YES | YES | --- | --- | YES + UNFOCUSED | YES | YES | YES | --- | --- | YES + SUSPENDED | --- | --- | --- | YES | YES | TODO + RESUMED | --- | --- | --- | YES | YES | TODO + QUIT_REQUESTED | YES | YES | YES | --- | --- | YES + IME | TODO | TODO? | TODO | ??? | TODO | ??? + key repeat flag | YES | YES | YES | --- | --- | YES + windowed | YES | YES | YES | --- | --- | YES + fullscreen | YES | YES | YES | YES | YES | --- + mouse hide | YES | YES | YES | --- | --- | YES + mouse lock | YES | YES | YES | --- | --- | YES + set cursor type | YES | YES | YES | --- | --- | YES + screen keyboard | --- | --- | --- | YES | TODO | YES + swap interval | YES | YES | YES | YES | TODO | YES + high-dpi | YES | YES | TODO | YES | YES | YES + clipboard | YES | YES | TODO | --- | --- | YES + MSAA | YES | YES | YES | YES | YES | YES + drag'n'drop | YES | YES | YES | --- | --- | YES + window icon | YES | YES(1)| YES | --- | --- | YES (1) macOS has no regular window icons, instead the dock icon is changed (2) supported with EGL only (not GLX) @@ -171,6 +164,18 @@ }; } + To get any logging output in case of errors you need to provide a log + callback. The easiest way is via sokol_log.h: + + #include "sokol_log.h" + + sapp_desc sokol_main(int argc, char* argv[]) { + return (sapp_desc) { + ... + .logger.func = slog_func, + }; + } + There are many more setup parameters, but these are the most important. For a complete list search for the sapp_desc structure declaration below. @@ -206,11 +211,6 @@ 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. - Providing a callback here gives you a chance to show an error message - to the user. The default behaviour is SAPP_LOG(msg) As you can see, those 'standard callbacks' don't have a user_data argument, so any data that needs to be preserved between callbacks @@ -224,10 +224,6 @@ .frame_userdata_cb (void (*)(void* user_data)) .cleanup_userdata_cb (void (*)(void* user_data)) .event_userdata_cb (void(*)(const sapp_event* event, void* user_data)) - .fail_userdata_cb (void(*)(const char* msg, void* user_data)) - These are the user-data versions of the callback functions. You - can mix those with the standard callbacks that don't have the - user_data argument. The function sapp_userdata() can be used to query the user_data pointer provided in the sapp_desc struct. @@ -269,18 +265,13 @@ 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 + 28 == SG_PIXELFORMAT_BGRA8 + 42 == SG_PIXELFORMAT_DEPTH + 43 == 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 - is useful when a GLES3/WebGL2 context was requested but is not - available so that sokol_app.h had to fallback to GLES2/WebGL. - const void* sapp_metal_get_device(void) const void* sapp_metal_get_renderpass_descriptor(void) const void* sapp_metal_get_drawable(void) @@ -789,7 +780,7 @@ 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 + closes a browser 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 @@ -967,9 +958,11 @@ OPTIONAL: DON'T HIJACK main() (#define SOKOL_NO_ENTRY) ====================================================== + NOTE: SOKOL_NO_ENTRY and sapp_run() is currently not supported on Android. + In its default configuration, sokol_app.h "hijacks" the platform's standard main() function. This was done because different platforms - have different main functions which are not compatible with + have different entry point conventions which are not compatible with C's main() (for instance WinMain on Windows has completely different arguments). However, this "main hijacking" posed a problem for usage scenarios like integrating sokol_app.h with other languages than @@ -981,12 +974,30 @@ - instead provide the standard main() function of the platform - from the main function, call the function ```sapp_run()``` which takes a pointer to an ```sapp_desc``` structure. - - ```sapp_run()``` takes over control and calls the provided init-, frame-, - shutdown- and event-callbacks just like in the default model, it - will only return when the application quits (or not at all on some - platforms, like emscripten) + - from here on```sapp_run()``` takes over control and calls the provided + init-, frame-, event- and cleanup-callbacks just like in the default model. + + sapp_run() behaves differently across platforms: - NOTE: SOKOL_NO_ENTRY is currently not supported on Android. + - on some platforms, sapp_run() will return when the application quits + - on other platforms, sapp_run() will never return, even when the + application quits (the operating system is free to simply terminate + the application at any time) + - on Emscripten specifically, sapp_run() will return immediately while + the frame callback keeps being called + + This different behaviour of sapp_run() essentially means that there shouldn't + be any code *after* sapp_run(), because that may either never be called, or in + case of Emscripten will be called at an unexpected time (at application start). + + An application also should not depend on the cleanup-callback being called + when cross-platform compatibility is required. + + Since sapp_run() returns immediately on Emscripten you shouldn't activate + the 'EXIT_RUNTIME' linker option (this is disabled by default when compiling + for the browser target), since the C/C++ exit runtime would be called immediately at + application start, causing any global objects to be destroyed and global + variables to be zeroed. WINDOWS CONSOLE OUTPUT ====================== @@ -1035,8 +1046,8 @@ return (sapp_desc){ // ... .allocator = { - .alloc = my_alloc, - .free = my_free, + .alloc_fn = my_alloc, + .free_fn = my_free, .user_data = ..., } }; @@ -1048,26 +1059,51 @@ itself though, not any allocations in OS libraries. - LOG FUNCTION OVERRIDE - ===================== - You can override the log function at initialization time like this: + ERROR REPORTING AND LOGGING + =========================== + To get any logging information at all you need to provide a logging callback in the setup call + the easiest way is to use sokol_log.h: - void my_log(const char* message, void* user_data) { - printf("sapp says: \s\n", message); + #include "sokol_log.h" + + sapp_desc sokol_main(int argc, char* argv[]) { + return (sapp_desc) { + ... + .logger.func = slog_func, + }; } + To override logging with your own callback, first write a logging function like this: + + void my_log(const char* tag, // e.g. 'sapp' + uint32_t log_level, // 0=panic, 1=error, 2=warn, 3=info + uint32_t log_item_id, // SAPP_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_app.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data) + { + ... + } + + ...and then setup sokol-app like this: + sapp_desc sokol_main(int argc, char* argv[]) { - return (sapp_desc){ - // ... + return (sapp_desc) { + ... .logger = { - .log_cb = my_log, - .user_data = ..., + .func = my_log, + .user_data = my_user_data, } }; } - If no overrides are provided, puts will be used on most platforms. - On Android, __android_log_write will be used instead. + The provided logging function must be reentrant (e.g. be callable from + different threads). + + If you don't want to provide your own custom logger it is highly recommended to use + the standard logger in sokol_log.h instead, otherwise you won't see any warnings or + errors. TEMP NOTE DUMP @@ -1464,23 +1500,134 @@ typedef struct sapp_icon_desc { Used in sapp_desc to provide custom memory-alloc and -free functions to sokol_app.h. If memory management should be overridden, both the - alloc and free function must be provided (e.g. it's not valid to + alloc_fb and free_fn function must be provided (e.g. it's not valid to override one function but not the other). */ typedef struct sapp_allocator { - void* (*alloc)(size_t size, void* user_data); - void (*free)(void* ptr, void* user_data); + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); void* user_data; } sapp_allocator; +/* + sapp_log_item + + Log items are defined via X-Macros and expanded to an enum + 'sapp_log_item', and in debug mode to corresponding + human readable error messages. +*/ +#define _SAPP_LOG_ITEMS \ + _SAPP_LOGITEM_XMACRO(OK, "Ok") \ + _SAPP_LOGITEM_XMACRO(MALLOC_FAILED, "memory allocation failed") \ + _SAPP_LOGITEM_XMACRO(MACOS_INVALID_NSOPENGL_PROFILE, "macos: invalid NSOpenGLProfile (valid choices are 1.0, 3.2 and 4.1)") \ + _SAPP_LOGITEM_XMACRO(WIN32_LOAD_OPENGL32_DLL_FAILED, "failed loading opengl32.dll") \ + _SAPP_LOGITEM_XMACRO(WIN32_CREATE_HELPER_WINDOW_FAILED, "failed to create helper window") \ + _SAPP_LOGITEM_XMACRO(WIN32_HELPER_WINDOW_GETDC_FAILED, "failed to get helper window DC") \ + _SAPP_LOGITEM_XMACRO(WIN32_DUMMY_CONTEXT_SET_PIXELFORMAT_FAILED, "failed to set pixel format for dummy GL context") \ + _SAPP_LOGITEM_XMACRO(WIN32_CREATE_DUMMY_CONTEXT_FAILED, "failed to create dummy GL context") \ + _SAPP_LOGITEM_XMACRO(WIN32_DUMMY_CONTEXT_MAKE_CURRENT_FAILED, "failed to make dummy GL context current") \ + _SAPP_LOGITEM_XMACRO(WIN32_GET_PIXELFORMAT_ATTRIB_FAILED, "failed to get WGL pixel format attribute") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_FIND_PIXELFORMAT_FAILED, "failed to find matching WGL pixel format") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_DESCRIBE_PIXELFORMAT_FAILED, "failed to get pixel format descriptor") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_SET_PIXELFORMAT_FAILED, "failed to set selected pixel format") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_ARB_CREATE_CONTEXT_REQUIRED, "ARB_create_context required") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_ARB_CREATE_CONTEXT_PROFILE_REQUIRED, "ARB_create_context_profile required") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_OPENGL_3_2_NOT_SUPPORTED, "OpenGL 3.2 not supported by GL driver (ERROR_INVALID_VERSION_ARB)") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_OPENGL_PROFILE_NOT_SUPPORTED, "requested OpenGL profile not support by GL driver (ERROR_INVALID_PROFILE_ARB)") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_INCOMPATIBLE_DEVICE_CONTEXT, "CreateContextAttribsARB failed with ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB") \ + _SAPP_LOGITEM_XMACRO(WIN32_WGL_CREATE_CONTEXT_ATTRIBS_FAILED_OTHER, "CreateContextAttribsARB failed for other reason") \ + _SAPP_LOGITEM_XMACRO(WIN32_D3D11_CREATE_DEVICE_AND_SWAPCHAIN_WITH_DEBUG_FAILED, "D3D11CreateDeviceAndSwapChain() with D3D11_CREATE_DEVICE_DEBUG failed, retrying without debug flag.") \ + _SAPP_LOGITEM_XMACRO(WIN32_D3D11_GET_IDXGIFACTORY_FAILED, "could not obtain IDXGIFactory object") \ + _SAPP_LOGITEM_XMACRO(WIN32_D3D11_GET_IDXGIADAPTER_FAILED, "could not obtain IDXGIAdapter object") \ + _SAPP_LOGITEM_XMACRO(WIN32_D3D11_QUERY_INTERFACE_IDXGIDEVICE1_FAILED, "could not obtain IDXGIDevice1 interface") \ + _SAPP_LOGITEM_XMACRO(WIN32_REGISTER_RAW_INPUT_DEVICES_FAILED_MOUSE_LOCK, "RegisterRawInputDevices() failed (on mouse lock)") \ + _SAPP_LOGITEM_XMACRO(WIN32_REGISTER_RAW_INPUT_DEVICES_FAILED_MOUSE_UNLOCK, "RegisterRawInputDevices() failed (on mouse unlock)") \ + _SAPP_LOGITEM_XMACRO(WIN32_GET_RAW_INPUT_DATA_FAILED, "GetRawInputData() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_LOAD_LIBGL_FAILED, "failed to load libGL") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_LOAD_ENTRY_POINTS_FAILED, "failed to load GLX entry points") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_EXTENSION_NOT_FOUND, "GLX extension not found") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_QUERY_VERSION_FAILED, "failed to query GLX version") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_VERSION_TOO_LOW, "GLX version too low (need at least 1.3)") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_NO_GLXFBCONFIGS, "glXGetFBConfigs() returned no configs") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_NO_SUITABLE_GLXFBCONFIG, "failed to find a suitable GLXFBConfig") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_GET_VISUAL_FROM_FBCONFIG_FAILED, "glXGetVisualFromFBConfig failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_REQUIRED_EXTENSIONS_MISSING, "GLX extensions ARB_create_context and ARB_create_context_profile missing") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_CREATE_CONTEXT_FAILED, "Failed to create GL context via glXCreateContextAttribsARB") \ + _SAPP_LOGITEM_XMACRO(LINUX_GLX_CREATE_WINDOW_FAILED, "glXCreateWindow() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_X11_CREATE_WINDOW_FAILED, "XCreateWindow() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_BIND_OPENGL_API_FAILED, "eglBindAPI(EGL_OPENGL_API) failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_BIND_OPENGL_ES_API_FAILED, "eglBindAPI(EGL_OPENGL_ES_API) failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_GET_DISPLAY_FAILED, "eglGetDisplay() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_INITIALIZE_FAILED, "eglInitialize() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_NO_CONFIGS, "eglChooseConfig() returned no configs") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_NO_NATIVE_VISUAL, "eglGetConfigAttrib() for EGL_NATIVE_VISUAL_ID failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_GET_VISUAL_INFO_FAILED, "XGetVisualInfo() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_CREATE_WINDOW_SURFACE_FAILED, "eglCreateWindowSurface() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_CREATE_CONTEXT_FAILED, "eglCreateContext() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_EGL_MAKE_CURRENT_FAILED, "eglMakeCurrent() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_X11_OPEN_DISPLAY_FAILED, "XOpenDisplay() failed") \ + _SAPP_LOGITEM_XMACRO(LINUX_X11_QUERY_SYSTEM_DPI_FAILED, "failed to query system dpi value, assuming default 96.0") \ + _SAPP_LOGITEM_XMACRO(LINUX_X11_DROPPED_FILE_URI_WRONG_SCHEME, "dropped file URL doesn't start with 'file://'") \ + _SAPP_LOGITEM_XMACRO(ANDROID_UNSUPPORTED_INPUT_EVENT_INPUT_CB, "unsupported input event encountered in _sapp_android_input_cb()") \ + _SAPP_LOGITEM_XMACRO(ANDROID_UNSUPPORTED_INPUT_EVENT_MAIN_CB, "unsupported input event encountered in _sapp_android_main_cb()") \ + _SAPP_LOGITEM_XMACRO(ANDROID_READ_MSG_FAILED, "failed to read message in _sapp_android_main_cb()") \ + _SAPP_LOGITEM_XMACRO(ANDROID_WRITE_MSG_FAILED, "failed to write message in _sapp_android_msg") \ + _SAPP_LOGITEM_XMACRO(ANDROID_MSG_CREATE, "MSG_CREATE") \ + _SAPP_LOGITEM_XMACRO(ANDROID_MSG_RESUME, "MSG_RESUME") \ + _SAPP_LOGITEM_XMACRO(ANDROID_MSG_PAUSE, "MSG_PAUSE") \ + _SAPP_LOGITEM_XMACRO(ANDROID_MSG_FOCUS, "MSG_FOCUS") \ + _SAPP_LOGITEM_XMACRO(ANDROID_MSG_NO_FOCUS, "MSG_NO_FOCUS") \ + _SAPP_LOGITEM_XMACRO(ANDROID_MSG_SET_NATIVE_WINDOW, "MSG_SET_NATIVE_WINDOW") \ + _SAPP_LOGITEM_XMACRO(ANDROID_MSG_SET_INPUT_QUEUE, "MSG_SET_INPUT_QUEUE") \ + _SAPP_LOGITEM_XMACRO(ANDROID_MSG_DESTROY, "MSG_DESTROY") \ + _SAPP_LOGITEM_XMACRO(ANDROID_UNKNOWN_MSG, "unknown msg type received") \ + _SAPP_LOGITEM_XMACRO(ANDROID_LOOP_THREAD_STARTED, "loop thread started") \ + _SAPP_LOGITEM_XMACRO(ANDROID_LOOP_THREAD_DONE, "loop thread done") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONSTART, "NativeActivity onStart()") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONRESUME, "NativeActivity onResume") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONSAVEINSTANCESTATE, "NativeActivity onSaveInstanceState") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONWINDOWFOCUSCHANGED, "NativeActivity onWindowFocusChanged") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONPAUSE, "NativeActivity onPause") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONSTOP, "NativeActivity onStop()") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONNATIVEWINDOWCREATED, "NativeActivity onNativeWindowCreated") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONNATIVEWINDOWDESTROYED, "NativeActivity onNativeWindowDestroyed") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONINPUTQUEUECREATED, "NativeActivity onInputQueueCreated") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONINPUTQUEUEDESTROYED, "NativeActivity onInputQueueDestroyed") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONCONFIGURATIONCHANGED, "NativeActivity onConfigurationChanged") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONLOWMEMORY, "NativeActivity onLowMemory") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONDESTROY, "NativeActivity onDestroy") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_DONE, "NativeActivity done") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONCREATE, "NativeActivity onCreate") \ + _SAPP_LOGITEM_XMACRO(ANDROID_CREATE_THREAD_PIPE_FAILED, "failed to create thread pipe") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_CREATE_SUCCESS, "NativeActivity sucessfully created") \ + _SAPP_LOGITEM_XMACRO(IMAGE_DATA_SIZE_MISMATCH, "image data size mismatch (must be width*height*4 bytes)") \ + _SAPP_LOGITEM_XMACRO(DROPPED_FILE_PATH_TOO_LONG, "dropped file path too long (sapp_desc.max_dropped_filed_path_length)") \ + _SAPP_LOGITEM_XMACRO(CLIPBOARD_STRING_TOO_BIG, "clipboard string didn't fit into clipboard buffer") \ + +#define _SAPP_LOGITEM_XMACRO(item,msg) SAPP_LOGITEM_##item, +typedef enum sapp_log_item { + _SAPP_LOG_ITEMS +} sapp_log_item; +#undef _SAPP_LOGITEM_XMACRO + /* sapp_logger - Used in sapp_desc to provide custom log callbacks to sokol_app.h. - Default behavior is SOKOL_LOG(message). + Used in sapp_desc to provide a logging function. Please be aware that + without logging function, sokol-app will be completely silent, e.g. it will + not report errors or warnings. For maximum error verbosity, compile in + debug mode (e.g. NDEBUG *not* defined) and install a logger (for instance + the standard logging function from sokol_log.h). */ typedef struct sapp_logger { - void (*log_cb)(const char* message, void* user_data); + void (*func)( + const char* tag, // always "sapp" + uint32_t log_level, // 0=panic, 1=error, 2=warning, 3=info + uint32_t log_item_id, // SAPP_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_app.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data); void* user_data; } sapp_logger; @@ -1489,14 +1636,12 @@ typedef struct sapp_desc { void (*frame_cb)(void); void (*cleanup_cb)(void); void (*event_cb)(const sapp_event*); - void (*fail_cb)(const char*); void* user_data; // these are the user-provided callbacks with user data void (*init_userdata_cb)(void*); void (*frame_userdata_cb)(void*); void (*cleanup_userdata_cb)(void*); void (*event_userdata_cb)(const sapp_event*, void*); - void (*fail_userdata_cb)(const char*, void*); int width; // the preferred width of the window / canvas int height; // the preferred height of the window / canvas @@ -1513,10 +1658,9 @@ typedef struct sapp_desc { int max_dropped_file_path_length; // max length in bytes of a dropped UTF-8 file path (default: 2048) sapp_icon_desc icon; // the initial window icon to set sapp_allocator allocator; // optional memory allocation overrides (default: malloc/free) - sapp_logger logger; // optional log callback overrides (default: SAPP_LOG(message)) + sapp_logger logger; // logging callback override (default: NO LOGGING!) /* backend-specific options */ - bool gl_force_gles2; // if true, setup GLES2/WebGL even if GLES3/WebGL2 is available int gl_major_version; // override GL major and minor version (the default GL version is 3.2) int gl_minor_version; bool win32_console_utf8; // if true, set the output console codepage to UTF-8 @@ -1658,9 +1802,6 @@ SOKOL_APP_API_DECL const void* sapp_egl_get_display(void); /* EGL: get EGLContext object */ SOKOL_APP_API_DECL const void* sapp_egl_get_context(void); -/* GL: return true when GLES2 fallback is active (to detect fallback from GLES3) */ -SOKOL_APP_API_DECL bool sapp_gles2(void); - /* HTML5: enable or disable the hardwired "Leave Site?" dialog box */ SOKOL_APP_API_DECL void sapp_html5_ask_leave_site(bool ask); /* HTML5: get byte size of a dropped file */ @@ -1722,7 +1863,13 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #endif // SOKOL_APP_INCLUDED -/*-- IMPLEMENTATION ----------------------------------------------------------*/ +// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████ +// +// >>implementation #ifdef SOKOL_APP_IMPL #define SOKOL_APP_IMPL_INCLUDED (1) @@ -1733,7 +1880,7 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #include // malloc, free #include // memset #include // size_t -#include /* roundf() */ +#include // roundf /* check if the config defines are alright */ #if defined(__APPLE__) @@ -1761,31 +1908,20 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #elif defined(__EMSCRIPTEN__) /* emscripten (asm.js or wasm) */ #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") + #if !defined(SOKOL_GLES3) && !defined(SOKOL_WGPU) + #error("sokol_app.h: unknown 3D API selected for emscripten, must be SOKOL_GLES3 or SOKOL_WGPU") #endif #elif defined(_WIN32) /* Windows (D3D11 or GL) */ - #include - #if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) - #define _SAPP_UWP (1) - #if !defined(SOKOL_D3D11) - #error("sokol_app.h: unknown 3D API selected for UWP, must be SOKOL_D3D11") - #endif - #if !defined(__cplusplus) - #error("sokol_app.h: UWP bindings require C++/17") - #endif - #else - #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 + #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") + #if !defined(SOKOL_GLES3) + #error("sokol_app.h: unknown 3D API selected for Android, must be SOKOL_GLES3") #endif #if defined(SOKOL_NO_ENTRY) #error("sokol_app.h: SOKOL_NO_ENTRY is not supported on Android") @@ -1797,8 +1933,8 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #if !defined(SOKOL_FORCE_EGL) #define _SAPP_GLX (1) #endif - #elif !defined(SOKOL_GLES3) && !defined(SOKOL_GLES2) - #error("sokol_app.h: unknown 3D API selected for Linux, must be SOKOL_GLCORE33, SOKOL_GLES3 or SOKOL_GLES2") + #elif !defined(SOKOL_GLES3) + #error("sokol_app.h: unknown 3D API selected for Linux, must be SOKOL_GLCORE33, SOKOL_GLES3") #endif #else #error "sokol_app.h: Unknown platform" @@ -1820,24 +1956,6 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) #endif -#if !defined(SOKOL_DEBUG) - #define SAPP_LOG(s) -#else - #define SAPP_LOG(s) _sapp_log(s) - #ifndef SOKOL_LOG - #if defined(__ANDROID__) - #include - #define SOKOL_LOG(s) __android_log_write(ANDROID_LOG_INFO, "SOKOL_APP", s) - #else - #include - #define SOKOL_LOG(s) puts(s) - #endif - #endif -#endif - -#ifndef SOKOL_ABORT - #define SOKOL_ABORT() abort() -#endif #ifndef _SOKOL_PRIVATE #if defined(__GNUC__) || defined(__clang__) #define _SOKOL_PRIVATE __attribute__((unused)) static @@ -1849,7 +1967,6 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #define _SOKOL_UNUSED(x) (void)(x) #endif -/*== PLATFORM SPECIFIC INCLUDES AND DEFINES ==================================*/ #if defined(_SAPP_APPLE) #if defined(SOKOL_METAL) #import @@ -1929,36 +2046,6 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #ifndef WM_DPICHANGED #define WM_DPICHANGED (0x02E0) #endif -#elif defined(_SAPP_UWP) - #ifndef NOMINMAX - #define NOMINMAX - #endif - #ifdef _MSC_VER - #pragma warning(push) - #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ - #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 - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - - #include - #include - #include - - #pragma comment (lib, "WindowsApp") - #pragma comment (lib, "dxguid") #elif defined(_SAPP_ANDROID) #include #include @@ -1987,7 +2074,13 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #include #endif -/*== frame timing helpers ===================================================*/ +// ███████ ██████ █████ ███ ███ ███████ ████████ ██ ███ ███ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ████ ████ ██ ██ ██ ████ ████ ██ ████ ██ ██ +// █████ ██████ ███████ ██ ████ ██ █████ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ████ ██████ +// +// >>frame timing #define _SAPP_RING_NUM_SLOTS (256) typedef struct { int head; @@ -2056,7 +2149,7 @@ typedef struct { } mach; #elif defined(_SAPP_EMSCRIPTEN) // empty - #elif defined(_SAPP_WIN32) || defined(_SAPP_UWP) + #elif defined(_SAPP_WIN32) struct { LARGE_INTEGER freq; LARGE_INTEGER start; @@ -2086,7 +2179,7 @@ _SOKOL_PRIVATE void _sapp_timestamp_init(_sapp_timestamp_t* ts) { ts->mach.start = mach_absolute_time(); #elif defined(_SAPP_EMSCRIPTEN) (void)ts; - #elif defined(_SAPP_WIN32) || defined(_SAPP_UWP) + #elif defined(_SAPP_WIN32) QueryPerformanceFrequency(&ts->win.freq); QueryPerformanceCounter(&ts->win.start); #else @@ -2105,7 +2198,7 @@ _SOKOL_PRIVATE double _sapp_timestamp_now(_sapp_timestamp_t* ts) { (void)ts; SOKOL_ASSERT(false); return 0.0; - #elif defined(_SAPP_WIN32) || defined(_SAPP_UWP) + #elif defined(_SAPP_WIN32) LARGE_INTEGER qpc; QueryPerformanceCounter(&qpc); const uint64_t now = (uint64_t)_sapp_int64_muldiv(qpc.QuadPart - ts->win.start.QuadPart, 1000000000, ts->win.freq.QuadPart); @@ -2198,9 +2291,16 @@ _SOKOL_PRIVATE double _sapp_timing_get_avg(_sapp_timing_t* t) { return t->avg; } -/*== MACOS DECLARATIONS ======================================================*/ +// ███████ ████████ ██████ ██ ██ ██████ ████████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██████ ██ ██ ██ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ██ ██████ ██████ ██ ███████ +// +// >> structs #if defined(_SAPP_MACOS) + // __v_ start @interface SokolWindow : NSWindow { } @@ -2241,6 +2341,7 @@ typedef struct { _sapp_macos_window* window; // __v_ added // __v_ end NSTrackingArea* tracking_area; + id keyup_monitor; _sapp_macos_app_delegate* app_dlg; _sapp_macos_window_delegate* win_dlg; _sapp_macos_view* view; @@ -2252,7 +2353,6 @@ typedef struct { #endif // _SAPP_MACOS -/*== IOS DECLARATIONS ========================================================*/ #if defined(_SAPP_IOS) @interface _sapp_app_delegate : NSObject @@ -2287,7 +2387,6 @@ typedef struct { #endif // _SAPP_IOS -/*== EMSCRIPTEN DECLARATIONS =================================================*/ #if defined(_SAPP_EMSCRIPTEN) #if defined(SOKOL_WGPU) @@ -2316,8 +2415,7 @@ typedef struct { } _sapp_emsc_t; #endif // _SAPP_EMSCRIPTEN -/*== WIN32 DECLARATIONS ======================================================*/ -#if defined(SOKOL_D3D11) && (defined(_SAPP_WIN32) || defined(_SAPP_UWP)) +#if defined(SOKOL_D3D11) && defined(_SAPP_WIN32) typedef struct { ID3D11Device* device; ID3D11DeviceContext* device_context; @@ -2335,7 +2433,6 @@ typedef struct { } _sapp_d3d11_t; #endif -/*== WIN32 DECLARATIONS ======================================================*/ #if defined(_SAPP_WIN32) #ifndef DPI_ENUMS_DECLARED @@ -2443,25 +2540,6 @@ typedef struct { #endif // _SAPP_WIN32 -/*== UWP DECLARATIONS ======================================================*/ -#if defined(_SAPP_UWP) - -typedef struct { - float content_scale; - float window_scale; - float mouse_scale; -} _sapp_uwp_dpi_t; - -typedef struct { - bool mouse_tracked; - uint8_t mouse_buttons; - _sapp_uwp_dpi_t dpi; -} _sapp_uwp_t; - -#endif // _SAPP_UWP - -/*== ANDROID DECLARATIONS ====================================================*/ - #if defined(_SAPP_ANDROID) typedef enum { _SOKOL_ANDROID_MSG_CREATE, @@ -2507,7 +2585,6 @@ typedef struct { #endif // _SAPP_ANDROID -/*== LINUX DECLARATIONS ======================================================*/ #if defined(_SAPP_LINUX) #define _SAPP_X11_XDND_VERSION (5) @@ -2658,8 +2735,6 @@ typedef struct { #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)) @@ -2669,9 +2744,9 @@ typedef struct { #define _SAPP_FALLBACK_DEFAULT_WINDOW_HEIGHT (480) /* NOTE: the pixel format values *must* be compatible with sg_pixel_format */ #define _SAPP_PIXELFORMAT_RGBA8 (23) -#define _SAPP_PIXELFORMAT_BGRA8 (27) -#define _SAPP_PIXELFORMAT_DEPTH (41) -#define _SAPP_PIXELFORMAT_DEPTH_STENCIL (42) +#define _SAPP_PIXELFORMAT_BGRA8 (28) +#define _SAPP_PIXELFORMAT_DEPTH (42) +#define _SAPP_PIXELFORMAT_DEPTH_STENCIL (43) #if defined(_SAPP_MACOS) || defined(_SAPP_IOS) // this is ARC compatible @@ -2712,7 +2787,6 @@ typedef struct { sapp_desc desc; bool valid; bool fullscreen; - bool gles2_fallback; bool first_frame; bool init_called; bool cleanup_called; @@ -2749,11 +2823,6 @@ typedef struct { #elif defined(SOKOL_GLCORE33) _sapp_wgl_t wgl; #endif - #elif defined(_SAPP_UWP) - _sapp_uwp_t uwp; - #if defined(SOKOL_D3D11) - _sapp_d3d11_t d3d11; - #endif #elif defined(_SAPP_ANDROID) _sapp_android_t android; #elif defined(_SAPP_LINUX) @@ -2774,7 +2843,52 @@ typedef struct { } _sapp_t; static _sapp_t _sapp; -/*=== PRIVATE HELPER FUNCTIONS ===============================================*/ +// ██ ██████ ██████ ██████ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ████ ██ ██ +// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██████ ██ ██ ████ ██████ +// +// >>logging +#if defined(SOKOL_DEBUG) +#define _SAPP_LOGITEM_XMACRO(item,msg) #item ": " msg, +static const char* _sapp_log_messages[] = { + _SAPP_LOG_ITEMS +}; +#undef _SAPP_LOGITEM_XMACRO +#endif // SOKOL_DEBUG + +#define _SAPP_PANIC(code) _sapp_log(SAPP_LOGITEM_ ##code, 0, 0, __LINE__) +#define _SAPP_ERROR(code) _sapp_log(SAPP_LOGITEM_ ##code, 1, 0, __LINE__) +#define _SAPP_WARN(code) _sapp_log(SAPP_LOGITEM_ ##code, 2, 0, __LINE__) +#define _SAPP_INFO(code) _sapp_log(SAPP_LOGITEM_ ##code, 3, 0, __LINE__) + +static void _sapp_log(sapp_log_item log_item, uint32_t log_level, const char* msg, uint32_t line_nr) { + if (_sapp.desc.logger.func) { + const char* filename = 0; + #if defined(SOKOL_DEBUG) + filename = __FILE__; + if (0 == msg) { + msg = _sapp_log_messages[log_item]; + } + #endif + _sapp.desc.logger.func("sapp", log_level, log_item, msg, line_nr, filename, _sapp.desc.logger.user_data); + } + else { + // for log level PANIC it would be 'undefined behaviour' to continue + if (log_level == 0) { + abort(); + } + } +} + +// ███ ███ ███████ ███ ███ ██████ ██████ ██ ██ +// ████ ████ ██ ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ █████ ██ ████ ██ ██ ██ ██████ ████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ██ ██████ ██ ██ ██ +// +// >>memory _SOKOL_PRIVATE void _sapp_clear(void* ptr, size_t size) { SOKOL_ASSERT(ptr && (size > 0)); memset(ptr, 0, size); @@ -2783,13 +2897,14 @@ _SOKOL_PRIVATE void _sapp_clear(void* ptr, size_t size) { _SOKOL_PRIVATE void* _sapp_malloc(size_t size) { SOKOL_ASSERT(size > 0); void* ptr; - if (_sapp.desc.allocator.alloc) { - ptr = _sapp.desc.allocator.alloc(size, _sapp.desc.allocator.user_data); - } - else { + if (_sapp.desc.allocator.alloc_fn) { + ptr = _sapp.desc.allocator.alloc_fn(size, _sapp.desc.allocator.user_data); + } else { ptr = malloc(size); } - SOKOL_ASSERT(ptr); + if (0 == ptr) { + _SAPP_PANIC(MALLOC_FAILED); + } return ptr; } @@ -2800,39 +2915,21 @@ _SOKOL_PRIVATE void* _sapp_malloc_clear(size_t size) { } _SOKOL_PRIVATE void _sapp_free(void* ptr) { - if (_sapp.desc.allocator.free) { - _sapp.desc.allocator.free(ptr, _sapp.desc.allocator.user_data); + if (_sapp.desc.allocator.free_fn) { + _sapp.desc.allocator.free_fn(ptr, _sapp.desc.allocator.user_data); } else { free(ptr); } } -#if defined(SOKOL_DEBUG) -_SOKOL_PRIVATE void _sapp_log(const char* msg) { - SOKOL_ASSERT(msg); - if (_sapp.desc.logger.log_cb) { - _sapp.desc.logger.log_cb(msg, _sapp.desc.logger.user_data); - } - else { - SOKOL_LOG(msg); - } -} -#endif - -_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 { - SAPP_LOG(msg); - } - SOKOL_ABORT(); -} - +// ██ ██ ███████ ██ ██████ ███████ ██████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ █████ ██ ██████ █████ ██████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ███████ ██ ███████ ██ ██ ███████ +// +// >>helpers _SOKOL_PRIVATE void _sapp_call_init(void) { if (_sapp.desc.init_cb) { _sapp.desc.init_cb(); @@ -2848,7 +2945,6 @@ _SOKOL_PRIVATE void _sapp_call_frame(void) { if (_sapp.__v_native_render) { return; } - // __v_ end if (_sapp.init_called && !_sapp.cleanup_called) { if (_sapp.desc.frame_cb) { _sapp.desc.frame_cb(); @@ -2943,7 +3039,7 @@ _SOKOL_PRIVATE bool _sapp_strcpy(const char* src, char* dst, int max_len) { } _SOKOL_PRIVATE sapp_desc _sapp_desc_defaults(const sapp_desc* desc) { - SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free)); + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); sapp_desc res = *desc; res.sample_count = _sapp_def(res.sample_count, 1); res.swap_interval = _sapp_def(res.swap_interval, 1); @@ -3076,7 +3172,7 @@ _SOKOL_PRIVATE bool _sapp_image_validate(const sapp_image_desc* desc) { SOKOL_ASSERT(desc->pixels.size > 0); const size_t wh_size = (size_t)(desc->width * desc->height) * sizeof(uint32_t); if (wh_size != desc->pixels.size) { - SAPP_LOG("Image data size mismatch (must be width*height*4 bytes)\n"); + _SAPP_ERROR(IMAGE_DATA_SIZE_MISMATCH); return false; } return true; @@ -3232,7 +3328,13 @@ _SOKOL_PRIVATE void _sapp_setup_default_icon(void) { SOKOL_ASSERT(dst == dst_end); } -/*== MacOS/iOS ===============================================================*/ +// █████ ██████ ██████ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██ █████ +// ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ +// +// >>apple #if defined(_SAPP_APPLE) #if __has_feature(objc_arc) @@ -3241,7 +3343,13 @@ _SOKOL_PRIVATE void _sapp_setup_default_icon(void) { #define _SAPP_OBJC_RELEASE(obj) { [obj release]; obj = nil; } #endif -/*== MacOS ===================================================================*/ +// ███ ███ █████ ██████ ██████ ███████ +// ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ ███████ ██ ██ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██████ ██████ ███████ +// +// >>macos #if defined(_SAPP_MACOS) _SOKOL_PRIVATE void _sapp_macos_init_keytable(void) { @@ -3360,6 +3468,11 @@ _SOKOL_PRIVATE void _sapp_macos_init_keytable(void) { _SOKOL_PRIVATE void _sapp_macos_discard_state(void) { // NOTE: it's safe to call [release] on a nil object + if (_sapp.macos.keyup_monitor != nil) { + [NSEvent removeMonitor:_sapp.macos.keyup_monitor]; + // NOTE: removeMonitor also releases the object + _sapp.macos.keyup_monitor = nil; + } _SAPP_OBJC_RELEASE(_sapp.macos.tracking_area); _SAPP_OBJC_RELEASE(_sapp.macos.app_dlg); _SAPP_OBJC_RELEASE(_sapp.macos.win_dlg); @@ -3396,11 +3509,22 @@ _SOKOL_PRIVATE void _sapp_macos_run(const sapp_desc* desc) { _sapp_init_state(desc); _sapp_macos_init_keytable(); [NSApplication sharedApplication]; + // set the application dock icon as early as possible, otherwise // the dummy icon will be visible for a short time sapp_set_icon(&_sapp.desc.icon); _sapp.macos.app_dlg = [[_sapp_macos_app_delegate alloc] init]; NSApp.delegate = _sapp.macos.app_dlg; + + // workaround for "no key-up sent while Cmd is pressed" taken from GLFW: + NSEvent* (^keyup_monitor)(NSEvent*) = ^NSEvent* (NSEvent* event) { + if ([event modifierFlags] & NSEventModifierFlagCommand) { + [[NSApp keyWindow] sendEvent:event]; + } + return event; + }; + _sapp.macos.keyup_monitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskKeyUp handler:keyup_monitor]; + [NSApp run]; // NOTE: [NSApp run] never returns, instead cleanup code // must be put into applicationWillTerminate @@ -3416,7 +3540,7 @@ int main(int argc, char* argv[]) { #endif /* SOKOL_NO_ENTRY */ _SOKOL_PRIVATE uint32_t _sapp_macos_mods(NSEvent* ev) { - const NSEventModifierFlags f = ev.modifierFlags; + const NSEventModifierFlags f = (ev == nil) ? NSEvent.modifierFlags : ev.modifierFlags; const NSUInteger b = NSEvent.pressedMouseButtons; uint32_t m = 0; if (f & NSEventModifierFlagShift) { @@ -3562,13 +3686,16 @@ _SOKOL_PRIVATE void _sapp_macos_update_window_title(void) { [_sapp.macos.window setTitle: [NSString stringWithUTF8String:_sapp.window_title]]; } -_SOKOL_PRIVATE void _sapp_macos_update_mouse(NSEvent* event) { +_SOKOL_PRIVATE void _sapp_macos_mouse_update_from_nspoint(NSPoint mouse_pos, bool clear_dxdy) { if (!_sapp.mouse.locked) { - const NSPoint mouse_pos = event.locationInWindow; 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) { + if (clear_dxdy) { + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; + } + else if (_sapp.mouse.pos_valid) { + // don't update dx/dy in the very first update _sapp.mouse.dx = new_x - _sapp.mouse.x; _sapp.mouse.dy = new_y - _sapp.mouse.y; } @@ -3578,6 +3705,10 @@ _SOKOL_PRIVATE void _sapp_macos_update_mouse(NSEvent* event) { } } +_SOKOL_PRIVATE void _sapp_macos_mouse_update_from_nsevent(NSEvent* event, bool clear_dxdy) { + _sapp_macos_mouse_update_from_nspoint(event.locationInWindow, clear_dxdy); +} + _SOKOL_PRIVATE void _sapp_macos_show_mouse(bool visible) { /* NOTE: this function is only called when the mouse visibility actually changes */ if (visible) { @@ -3767,7 +3898,7 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { case 32: attrs[i++] = NSOpenGLProfileVersion3_2Core; break; case 41: attrs[i++] = NSOpenGLProfileVersion4_1Core; break; default: - _sapp_fail("Invalid NSOpenGLProfile (valid choices are 1.0, 3.2, and 4.1)\n"); + _SAPP_PANIC(MACOS_INVALID_NSOPENGL_PROFILE); } attrs[i++] = NSOpenGLPFAColorSize; attrs[i++] = 24; attrs[i++] = NSOpenGLPFAAlphaSize; attrs[i++] = 8; @@ -3860,16 +3991,14 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { } ////////////////////////////////// // __v_ end - - + [_sapp.macos.window makeKeyAndOrderFront:nil]; _sapp_macos_update_dimensions(); // __v_ start - // [NSEvent setMouseCoalescingEnabled:NO]; + //[NSEvent setMouseCoalescingEnabled:NO]; // __v_ end - } - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)sender { @@ -3987,14 +4116,16 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { for (int i = 0; i < _sapp.drop.num_files; i++) { NSURL *fileUrl = [NSURL fileURLWithPath:[pboard.pasteboardItems[(NSUInteger)i] stringForType:NSPasteboardTypeFileURL]]; if (!_sapp_strcpy(fileUrl.standardizedURL.path.UTF8String, _sapp_dropped_file_path_ptr(i), _sapp.drop.max_path_length)) { - SAPP_LOG("sokol_app.h: dropped file path too long (sapp_desc.max_dropped_file_path_length)\n"); + _SAPP_ERROR(DROPPED_FILE_PATH_TOO_LONG); drop_failed = true; break; } } if (!drop_failed) { if (_sapp_events_enabled()) { + _sapp_macos_mouse_update_from_nspoint(sender.draggingLocation, true); _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); + _sapp.event.modifiers = _sapp_macos_mods(nil); _sapp_call_event(&_sapp.event); } } @@ -4011,19 +4142,6 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { @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]; @@ -4041,7 +4159,7 @@ _SOKOL_PRIVATE void _sapp_macos_poll_input_events() { /* NOTE: late event polling temporarily out-commented to check if this - causes infrequent and almost impossible to reproduce probelms with the + causes infrequent and almost impossible to reproduce problems with the window close events, see: https://github.com/floooh/sokol/pull/483#issuecomment-805148815 @@ -4117,8 +4235,17 @@ _SOKOL_PRIVATE void _sapp_macos_poll_input_events() { [self addTrackingArea:_sapp.macos.tracking_area]; [super updateTrackingAreas]; } + +// helper function to make GL context active +static void _sapp_gl_make_current(void) { + #if defined(SOKOL_GLCORE33) + [[_sapp.macos.view openGLContext] makeCurrentContext]; + #endif +} + - (void)mouseEntered:(NSEvent*)event { - _sapp_macos_update_mouse(event); + _sapp_gl_make_current(); + _sapp_macos_mouse_update_from_nsevent(event, true); /* don't send mouse enter/leave while dragging (so that it behaves the same as on Windows while SetCapture is active */ @@ -4127,47 +4254,55 @@ _SOKOL_PRIVATE void _sapp_macos_poll_input_events() { } } - (void)mouseExited:(NSEvent*)event { - _sapp_macos_update_mouse(event); + _sapp_gl_make_current(); + _sapp_macos_mouse_update_from_nsevent(event, true); if (0 == _sapp.macos.mouse_buttons) { _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mods(event)); } } - (void)mouseDown:(NSEvent*)event { - _sapp_macos_update_mouse(event); + _sapp_gl_make_current(); + _sapp_macos_mouse_update_from_nsevent(event, false); _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_LEFT, _sapp_macos_mods(event)); _sapp.macos.mouse_buttons |= (1< 0) { @@ -4253,6 +4386,7 @@ _SOKOL_PRIVATE void _sapp_macos_poll_input_events() { } } - (void)keyUp:(NSEvent*)event { + _sapp_gl_make_current(); _sapp_macos_key_event(SAPP_EVENTTYPE_KEY_UP, _sapp_translate_key(event.keyCode), event.isARepeat, @@ -4289,9 +4423,15 @@ _SOKOL_PRIVATE void _sapp_macos_poll_input_events() { } @end -#endif /* MacOS */ +#endif // macOS -/*== iOS =====================================================================*/ +// ██ ██████ ███████ +// ██ ██ ██ ██ +// ██ ██ ██ ███████ +// ██ ██ ██ ██ +// ██ ██████ ███████ +// +// >>ios #if defined(_SAPP_IOS) _SOKOL_PRIVATE void _sapp_ios_discard_state(void) { @@ -4457,17 +4597,7 @@ _SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) { _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.eagl_ctx = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; _sapp.ios.view = [[_sapp_ios_view alloc] initWithFrame:screen_rect]; _sapp.ios.view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888; _sapp.ios.view.drawableDepthFormat = GLKViewDrawableDepthFormat24; @@ -4624,7 +4754,13 @@ _SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) { #endif /* _SAPP_APPLE */ -/*== EMSCRIPTEN ==============================================================*/ +// ███████ ███ ███ ███████ ██████ ██████ ██ ██████ ████████ ███████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// █████ ██ ████ ██ ███████ ██ ██████ ██ ██████ ██ █████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ███████ ██████ ██ ██ ██ ██ ██ ███████ ██ ████ +// +// >>emscripten #if defined(_SAPP_EMSCRIPTEN) #if defined(EM_JS_DEPS) @@ -4686,12 +4822,12 @@ EMSCRIPTEN_KEEPALIVE void _sapp_emsc_drop(int i, const char* name) { return; } if (!_sapp_strcpy(name, _sapp_dropped_file_path_ptr(i), _sapp.drop.max_path_length)) { - SAPP_LOG("sokol_app.h: dropped file path too long!\n"); + _SAPP_ERROR(DROPPED_FILE_PATH_TOO_LONG); _sapp.drop.num_files = 0; } } -EMSCRIPTEN_KEEPALIVE void _sapp_emsc_end_drop(int x, int y) { +EMSCRIPTEN_KEEPALIVE void _sapp_emsc_end_drop(int x, int y, int mods) { if (!_sapp.drop.enabled) { return; } @@ -4707,6 +4843,11 @@ EMSCRIPTEN_KEEPALIVE void _sapp_emsc_end_drop(int x, int y) { _sapp.mouse.dx = 0.0f; _sapp.mouse.dy = 0.0f; _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); + // see sapp_js_add_dragndrop_listeners for mods constants + if (mods & 1) { _sapp.event.modifiers |= SAPP_MODIFIER_SHIFT; } + if (mods & 2) { _sapp.event.modifiers |= SAPP_MODIFIER_CTRL; } + if (mods & 4) { _sapp.event.modifiers |= SAPP_MODIFIER_ALT; } + if (mods & 8) { _sapp.event.modifiers |= SAPP_MODIFIER_SUPER; } _sapp_call_event(&_sapp.event); } } @@ -4829,8 +4970,13 @@ EM_JS(void, sapp_js_add_dragndrop_listeners, (const char* canvas_name_cstr), { __sapp_emsc_drop(i, cstr); }); } + let mods = 0; + if (event.shiftKey) { mods |= 1; } + if (event.ctrlKey) { mods |= 2; } + if (event.altKey) { mods |= 4; } + if (event.metaKey) { mods |= 8; } // FIXME? see computation of targetX/targetY in emscripten via getClientBoundingRect - __sapp_emsc_end_drop(event.clientX, event.clientY); + __sapp_emsc_end_drop(event.clientX, event.clientY, mods); }; canvas.addEventListener('dragenter', Module.sokol_dragenter, false); canvas.addEventListener('dragleave', Module.sokol_dragleave, false); @@ -5150,8 +5296,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_mouse_cb(int emsc_type, const EmscriptenMouseE if (_sapp.mouse.locked) { _sapp.mouse.dx = (float) emsc_event->movementX; _sapp.mouse.dy = (float) emsc_event->movementY; - } - else { + } else { float new_x = emsc_event->targetX * _sapp.dpi_scale; float new_y = emsc_event->targetY * _sapp.dpi_scale; if (_sapp.mouse.pos_valid) { @@ -5165,6 +5310,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_mouse_cb(int emsc_type, const EmscriptenMouseE if (_sapp_events_enabled() && (emsc_event->button >= 0) && (emsc_event->button < SAPP_MAX_MOUSEBUTTONS)) { sapp_event_type type; bool is_button_event = false; + bool clear_dxdy = false; switch (emsc_type) { case EMSCRIPTEN_EVENT_MOUSEDOWN: type = SAPP_EVENTTYPE_MOUSE_DOWN; @@ -5179,14 +5325,20 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_mouse_cb(int emsc_type, const EmscriptenMouseE break; case EMSCRIPTEN_EVENT_MOUSEENTER: type = SAPP_EVENTTYPE_MOUSE_ENTER; + clear_dxdy = true; break; case EMSCRIPTEN_EVENT_MOUSELEAVE: type = SAPP_EVENTTYPE_MOUSE_LEAVE; + clear_dxdy = true; break; default: type = SAPP_EVENTTYPE_INVALID; break; } + if (clear_dxdy) { + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; + } if (type != SAPP_EVENTTYPE_INVALID) { _sapp_init_event(type); _sapp.event.modifiers = _sapp_emsc_mouse_event_mods(emsc_event); @@ -5197,13 +5349,12 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_mouse_cb(int emsc_type, const EmscriptenMouseE case 2: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_RIGHT; break; default: _sapp.event.mouse_button = (sapp_mousebutton)emsc_event->button; break; } - } - else { + } 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) */ + // 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(); } @@ -5382,6 +5533,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboard _sapp.event.key_repeat = emsc_event->repeat; _sapp.event.modifiers = _sapp_emsc_key_event_mods(emsc_event); if (type == SAPP_EVENTTYPE_CHAR) { + // FIXME: this doesn't appear to work on Android Chrome _sapp.event.char_code = emsc_event->charCode; /* workaround to make Cmd+V work on Safari */ if ((emsc_event->metaKey) && (emsc_event->charCode == 118)) { @@ -5389,7 +5541,18 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboard } } else { - _sapp.event.key_code = _sapp_emsc_translate_key(emsc_event->code); + if (0 != emsc_event->code[0]) { + // This code path is for desktop browsers which send untranslated 'physical' key code strings + // (which is what we actually want for key events) + _sapp.event.key_code = _sapp_emsc_translate_key(emsc_event->code); + } else { + // This code path is for mobile browsers which only send localized key code + // strings. Note that the translation will only work for a small subset + // of localization-agnostic keys (like Enter, arrow keys, etc...), but + // regular alpha-numeric keys will all result in an SAPP_KEYCODE_INVALID) + _sapp.event.key_code = _sapp_emsc_translate_key(emsc_event->key); + } + /* Special hack for macOS: if the Super key is pressed, macOS doesn't send keyUp events. As a workaround, to prevent keys from "sticking", we'll send a keyup event following a keydown @@ -5402,7 +5565,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboard { send_keyup_followup = true; } - /* only forward a certain key ranges to the browser */ + // only forward keys to the browser (can further be suppressed by sapp_consume_event()) switch (_sapp.event.key_code) { case SAPP_KEYCODE_WORLD_1: case SAPP_KEYCODE_WORLD_2: @@ -5468,7 +5631,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboard } } if (_sapp_call_event(&_sapp.event)) { - /* consume event via sapp_consume_event() */ + // event was consumed via sapp_consume_event() retval = true; } if (send_keyup_followup) { @@ -5551,7 +5714,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_blur_cb(int emsc_type, const EmscriptenFocusEv return true; } -#if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) +#if 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); @@ -5578,21 +5741,9 @@ _SOKOL_PRIVATE void _sapp_emsc_webgl_init(void) { 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 + attrs.majorVersion = 2; EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(_sapp.html5_canvas_selector, &attrs); - if (!ctx) { - attrs.majorVersion = 1; - ctx = emscripten_webgl_create_context(_sapp.html5_canvas_selector, &attrs); - _sapp.gles2_fallback = true; - } + // FIXME: error message? emscripten_webgl_make_context_current(ctx); /* some WebGL extension are not enabled automatically by emscripten */ @@ -5735,7 +5886,7 @@ _SOKOL_PRIVATE void _sapp_emsc_register_eventhandlers(void) { if (_sapp.drop.enabled) { sapp_js_add_dragndrop_listeners(&_sapp.html5_canvas_selector[1]); } - #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + #if defined(SOKOL_GLES3) emscripten_set_webglcontextlost_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_webgl_context_cb); emscripten_set_webglcontextrestored_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_webgl_context_cb); #endif @@ -5766,7 +5917,7 @@ _SOKOL_PRIVATE void _sapp_emsc_unregister_eventhandlers() { if (_sapp.drop.enabled) { sapp_js_remove_dragndrop_listeners(&_sapp.html5_canvas_selector[1]); } - #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + #if defined(SOKOL_GLES3) emscripten_set_webglcontextlost_callback(_sapp.html5_canvas_selector, 0, true, 0); emscripten_set_webglcontextrestored_callback(_sapp.html5_canvas_selector, 0, true, 0); #endif @@ -5839,7 +5990,7 @@ _SOKOL_PRIVATE void _sapp_emsc_run(const sapp_desc* desc) { _sapp.framebuffer_width = (int)roundf(w * _sapp.dpi_scale); _sapp.framebuffer_height = (int)roundf(h * _sapp.dpi_scale); emscripten_set_canvas_element_size(_sapp.html5_canvas_selector, _sapp.framebuffer_width, _sapp.framebuffer_height); - #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + #if defined(SOKOL_GLES3) _sapp_emsc_webgl_init(); #elif defined(SOKOL_WGPU) sapp_js_wgpu_init(); @@ -5865,7 +6016,13 @@ int main(int argc, char* argv[]) { #endif /* SOKOL_NO_ENTRY */ #endif /* _SAPP_EMSCRIPTEN */ -/*== MISC GL SUPPORT FUNCTIONS ================================================*/ +// ██████ ██ ██ ██ ███████ ██ ██████ ███████ ██████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ███ ██ ███████ █████ ██ ██████ █████ ██████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ███████ ██ ██ ███████ ███████ ██ ███████ ██ ██ ███████ +// +// >>gl helpers #if defined(SOKOL_GLCORE33) typedef struct { int red_bits; @@ -5974,9 +6131,15 @@ _SOKOL_PRIVATE const _sapp_gl_fbconfig* _sapp_gl_choose_fbconfig(const _sapp_gl_ } #endif -/*== WINDOWS DESKTOP and UWP====================================================*/ -#if defined(_SAPP_WIN32) || defined(_SAPP_UWP) -_SOKOL_PRIVATE bool _sapp_win32_uwp_utf8_to_wide(const char* src, wchar_t* dst, int dst_num_bytes) { +// ██ ██ ██ ███ ██ ██████ ██████ ██ ██ ███████ +// ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ █ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ █ ██ ███████ +// ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ███ ██ ██ +// ███ ███ ██ ██ ████ ██████ ██████ ███ ███ ███████ +// +// >>windows +#if defined(_SAPP_WIN32) +_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)); _sapp_clear(dst, (size_t)dst_num_bytes); const int dst_chars = dst_num_bytes / (int)sizeof(wchar_t); @@ -5991,14 +6154,14 @@ _SOKOL_PRIVATE bool _sapp_win32_uwp_utf8_to_wide(const char* src, wchar_t* dst, } } -_SOKOL_PRIVATE void _sapp_win32_uwp_app_event(sapp_event_type type) { +_SOKOL_PRIVATE void _sapp_win32_app_event(sapp_event_type type) { if (_sapp_events_enabled()) { _sapp_init_event(type); _sapp_call_event(&_sapp.event); } } -_SOKOL_PRIVATE void _sapp_win32_uwp_init_keytable(void) { +_SOKOL_PRIVATE void _sapp_win32_init_keytable(void) { /* same as GLFW */ _sapp.keycodes[0x00B] = SAPP_KEYCODE_0; _sapp.keycodes[0x002] = SAPP_KEYCODE_1; @@ -6119,9 +6282,8 @@ _SOKOL_PRIVATE void _sapp_win32_uwp_init_keytable(void) { _sapp.keycodes[0x037] = SAPP_KEYCODE_KP_MULTIPLY; _sapp.keycodes[0x04A] = SAPP_KEYCODE_KP_SUBTRACT; } -#endif // _SAPP_WIN32 || _SAPP_UWP +#endif // _SAPP_WIN32 -/*== WINDOWS DESKTOP===========================================================*/ #if defined(_SAPP_WIN32) #if defined(SOKOL_D3D11) @@ -6288,7 +6450,7 @@ _SOKOL_PRIVATE void _sapp_d3d11_create_device_and_swapchain(void) { _SOKOL_UNUSED(hr); #if defined(SOKOL_DEBUG) if (!SUCCEEDED(hr)) { - // if initialization with D3D11_CREATE_DEVICE_DEBUG failes, this could be because the + // if initialization with D3D11_CREATE_DEVICE_DEBUG fails, this could be because the // 'D3D11 debug layer' stopped working, indicated by the error message: // === // D3D11CreateDevice: Flags (0x2) were specified which require the D3D11 SDK Layers for Windows 10, but they are not present on the system. @@ -6297,7 +6459,7 @@ _SOKOL_PRIVATE void _sapp_d3d11_create_device_and_swapchain(void) { // === // // ...just retry with the DEBUG flag switched off - SAPP_LOG("sokol_app.h: D3D11CreateDeviceAndSwapChain() with D3D11_CREATE_DEVICE_DEBUG failed, retrying without debug flag.\n"); + _SAPP_ERROR(WIN32_D3D11_CREATE_DEVICE_AND_SWAPCHAIN_WITH_DEBUG_FAILED); create_flags &= ~D3D11_CREATE_DEVICE_DEBUG; hr = D3D11CreateDeviceAndSwapChain( NULL, /* pAdapter (use default) */ @@ -6316,7 +6478,7 @@ _SOKOL_PRIVATE void _sapp_d3d11_create_device_and_swapchain(void) { #endif SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.swap_chain && _sapp.d3d11.device && _sapp.d3d11.device_context); - // mimimize frame latency, disable Alt-Enter + // minimize frame latency, disable Alt-Enter hr = _sapp_d3d11_QueryInterface(_sapp.d3d11.device, _sapp_win32_refiid(_sapp_IID_IDXGIDevice1), (void**)&_sapp.d3d11.dxgi_device); if (SUCCEEDED(hr) && _sapp.d3d11.dxgi_device) { _sapp_dxgi_SetMaximumFrameLatency(_sapp.d3d11.dxgi_device, 1); @@ -6330,16 +6492,16 @@ _SOKOL_PRIVATE void _sapp_d3d11_create_device_and_swapchain(void) { _SAPP_SAFE_RELEASE(dxgi_factory); } else { - SAPP_LOG("sokol_app.h: could not obtain IDXGIFactory object.\n"); + _SAPP_ERROR(WIN32_D3D11_GET_IDXGIFACTORY_FAILED); } _SAPP_SAFE_RELEASE(dxgi_adapter); } else { - SAPP_LOG("sokol_app.h: could not obtain IDXGIAdapter object.\n"); + _SAPP_ERROR(WIN32_D3D11_GET_IDXGIADAPTER_FAILED); } } else { - SAPP_LOG("sokol_app.h: could not obtain IDXGIDevice1 interface\n"); + _SAPP_PANIC(WIN32_D3D11_QUERY_INTERFACE_IDXGIDEVICE1_FAILED); } } @@ -6437,7 +6599,7 @@ _SOKOL_PRIVATE void _sapp_d3d11_present(bool do_not_wait) { _SOKOL_PRIVATE void _sapp_wgl_init(void) { _sapp.wgl.opengl32 = LoadLibraryA("opengl32.dll"); if (!_sapp.wgl.opengl32) { - _sapp_fail("Failed to load opengl32.dll\n"); + _SAPP_PANIC(WIN32_LOAD_OPENGL32_DLL_FAILED); } SOKOL_ASSERT(_sapp.wgl.opengl32); _sapp.wgl.CreateContext = (PFN_wglCreateContext)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglCreateContext"); @@ -6460,7 +6622,7 @@ _SOKOL_PRIVATE void _sapp_wgl_init(void) { GetModuleHandleW(NULL), NULL); if (!_sapp.wgl.msg_hwnd) { - _sapp_fail("Win32: failed to create helper window!\n"); + _SAPP_PANIC(WIN32_CREATE_HELPER_WINDOW_FAILED); } SOKOL_ASSERT(_sapp.wgl.msg_hwnd); ShowWindow(_sapp.wgl.msg_hwnd, SW_HIDE); @@ -6471,7 +6633,7 @@ _SOKOL_PRIVATE void _sapp_wgl_init(void) { } _sapp.wgl.msg_dc = GetDC(_sapp.wgl.msg_hwnd); if (!_sapp.wgl.msg_dc) { - _sapp_fail("Win32: failed to obtain helper window DC!\n"); + _SAPP_PANIC(WIN32_HELPER_WINDOW_GETDC_FAILED); } } @@ -6531,14 +6693,14 @@ _SOKOL_PRIVATE void _sapp_wgl_load_extensions(void) { pfd.iPixelType = PFD_TYPE_RGBA; pfd.cColorBits = 24; 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"); + _SAPP_PANIC(WIN32_DUMMY_CONTEXT_SET_PIXELFORMAT_FAILED); } HGLRC rc = _sapp.wgl.CreateContext(_sapp.wgl.msg_dc); if (!rc) { - _sapp_fail("WGL: Failed to create dummy context\n"); + _SAPP_PANIC(WIN32_CREATE_DUMMY_CONTEXT_FAILED); } if (!_sapp.wgl.MakeCurrent(_sapp.wgl.msg_dc, rc)) { - _sapp_fail("WGL: Failed to make context current\n"); + _SAPP_PANIC(WIN32_DUMMY_CONTEXT_MAKE_CURRENT_FAILED); } _sapp.wgl.GetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC)(void*) _sapp.wgl.GetProcAddress("wglGetExtensionsStringEXT"); _sapp.wgl.GetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)(void*) _sapp.wgl.GetProcAddress("wglGetExtensionsStringARB"); @@ -6558,16 +6720,59 @@ _SOKOL_PRIVATE int _sapp_wgl_attrib(int pixel_format, int attrib) { SOKOL_ASSERT(_sapp.wgl.arb_pixel_format); int value = 0; if (!_sapp.wgl.GetPixelFormatAttribivARB(_sapp.win32.dc, pixel_format, 0, 1, &attrib, &value)) { - _sapp_fail("WGL: Failed to retrieve pixel format attribute\n"); + _SAPP_PANIC(WIN32_GET_PIXELFORMAT_ATTRIB_FAILED); } return value; } +_SOKOL_PRIVATE void _sapp_wgl_attribiv(int pixel_format, int num_attribs, const int* attribs, int* results) { + SOKOL_ASSERT(_sapp.wgl.arb_pixel_format); + if (!_sapp.wgl.GetPixelFormatAttribivARB(_sapp.win32.dc, pixel_format, 0, num_attribs, attribs, results)) { + _SAPP_PANIC(WIN32_GET_PIXELFORMAT_ATTRIB_FAILED); + } +} + _SOKOL_PRIVATE int _sapp_wgl_find_pixel_format(void) { SOKOL_ASSERT(_sapp.win32.dc); SOKOL_ASSERT(_sapp.wgl.arb_pixel_format); const _sapp_gl_fbconfig* closest; + #define _sapp_wgl_num_query_tags (12) + const int query_tags[_sapp_wgl_num_query_tags] = { + WGL_SUPPORT_OPENGL_ARB, + WGL_DRAW_TO_WINDOW_ARB, + WGL_PIXEL_TYPE_ARB, + WGL_ACCELERATION_ARB, + WGL_DOUBLE_BUFFER_ARB, + WGL_RED_BITS_ARB, + WGL_GREEN_BITS_ARB, + WGL_BLUE_BITS_ARB, + WGL_ALPHA_BITS_ARB, + WGL_DEPTH_BITS_ARB, + WGL_STENCIL_BITS_ARB, + WGL_SAMPLES_ARB, + }; + const int result_support_opengl_index = 0; + const int result_draw_to_window_index = 1; + const int result_pixel_type_index = 2; + const int result_acceleration_index = 3; + const int result_double_buffer_index = 4; + const int result_red_bits_index = 5; + const int result_green_bits_index = 6; + const int result_blue_bits_index = 7; + const int result_alpha_bits_index = 8; + const int result_depth_bits_index = 9; + const int result_stencil_bits_index = 10; + const int result_samples_index = 11; + + int query_results[_sapp_wgl_num_query_tags] = {0}; + // Drop the last item if multisample extension is not supported. + // If in future querying with multiple extensions, will have to shuffle index values to have active extensions on the end. + int query_count = _sapp_wgl_num_query_tags; + if (!_sapp.wgl.arb_multisample) { + query_count = _sapp_wgl_num_query_tags - 1; + } + int native_count = _sapp_wgl_attrib(1, WGL_NUMBER_PIXEL_FORMATS_ARB); _sapp_gl_fbconfig* usable_configs = (_sapp_gl_fbconfig*) _sapp_malloc_clear((size_t)native_count * sizeof(_sapp_gl_fbconfig)); SOKOL_ASSERT(usable_configs); @@ -6576,27 +6781,27 @@ _SOKOL_PRIVATE int _sapp_wgl_find_pixel_format(void) { const int n = i + 1; _sapp_gl_fbconfig* u = usable_configs + usable_count; _sapp_gl_init_fbconfig(u); - if (!_sapp_wgl_attrib(n, WGL_SUPPORT_OPENGL_ARB) || !_sapp_wgl_attrib(n, WGL_DRAW_TO_WINDOW_ARB)) { - continue; - } - if (_sapp_wgl_attrib(n, WGL_PIXEL_TYPE_ARB) != WGL_TYPE_RGBA_ARB) { - continue; - } - if (_sapp_wgl_attrib(n, WGL_ACCELERATION_ARB) == WGL_NO_ACCELERATION_ARB) { + _sapp_wgl_attribiv(n, query_count, query_tags, query_results); + + if (query_results[result_support_opengl_index] == 0 + || query_results[result_draw_to_window_index] == 0 + || query_results[result_pixel_type_index] != WGL_TYPE_RGBA_ARB + || query_results[result_acceleration_index] == WGL_NO_ACCELERATION_ARB) + { continue; } - u->red_bits = _sapp_wgl_attrib(n, WGL_RED_BITS_ARB); - u->green_bits = _sapp_wgl_attrib(n, WGL_GREEN_BITS_ARB); - u->blue_bits = _sapp_wgl_attrib(n, WGL_BLUE_BITS_ARB); - u->alpha_bits = _sapp_wgl_attrib(n, WGL_ALPHA_BITS_ARB); - u->depth_bits = _sapp_wgl_attrib(n, WGL_DEPTH_BITS_ARB); - u->stencil_bits = _sapp_wgl_attrib(n, WGL_STENCIL_BITS_ARB); - if (_sapp_wgl_attrib(n, WGL_DOUBLE_BUFFER_ARB)) { + u->red_bits = query_results[result_red_bits_index]; + u->green_bits = query_results[result_green_bits_index]; + u->blue_bits = query_results[result_blue_bits_index]; + u->alpha_bits = query_results[result_alpha_bits_index]; + u->depth_bits = query_results[result_depth_bits_index]; + u->stencil_bits = query_results[result_stencil_bits_index]; + if (query_results[result_double_buffer_index]) { u->doublebuffer = true; } - if (_sapp.wgl.arb_multisample) { - u->samples = _sapp_wgl_attrib(n, WGL_SAMPLES_ARB); - } + + u->samples = query_results[result_samples_index]; // NOTE: If arb_multisample is not supported - just takes the default 0 + u->handle = (uintptr_t)n; usable_count++; } @@ -6623,20 +6828,20 @@ _SOKOL_PRIVATE int _sapp_wgl_find_pixel_format(void) { _SOKOL_PRIVATE void _sapp_wgl_create_context(void) { int pixel_format = _sapp_wgl_find_pixel_format(); if (0 == pixel_format) { - _sapp_fail("WGL: Didn't find matching pixel format.\n"); + _SAPP_PANIC(WIN32_WGL_FIND_PIXELFORMAT_FAILED); } PIXELFORMATDESCRIPTOR pfd; if (!DescribePixelFormat(_sapp.win32.dc, pixel_format, sizeof(pfd), &pfd)) { - _sapp_fail("WGL: Failed to retrieve PFD for selected pixel format!\n"); + _SAPP_PANIC(WIN32_WGL_DESCRIBE_PIXELFORMAT_FAILED); } if (!SetPixelFormat(_sapp.win32.dc, pixel_format, &pfd)) { - _sapp_fail("WGL: Failed to set selected pixel format!\n"); + _SAPP_PANIC(WIN32_WGL_SET_PIXELFORMAT_FAILED); } if (!_sapp.wgl.arb_create_context) { - _sapp_fail("WGL: ARB_create_context required!\n"); + _SAPP_PANIC(WIN32_WGL_ARB_CREATE_CONTEXT_REQUIRED); } if (!_sapp.wgl.arb_create_context_profile) { - _sapp_fail("WGL: ARB_create_context_profile required!\n"); + _SAPP_PANIC(WIN32_WGL_ARB_CREATE_CONTEXT_PROFILE_REQUIRED); } const int attrs[] = { WGL_CONTEXT_MAJOR_VERSION_ARB, _sapp.desc.gl_major_version, @@ -6649,16 +6854,16 @@ _SOKOL_PRIVATE void _sapp_wgl_create_context(void) { 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"); + _SAPP_PANIC(WIN32_WGL_OPENGL_3_2_NOT_SUPPORTED); } else if (err == (0xc0070000 | ERROR_INVALID_PROFILE_ARB)) { - _sapp_fail("WGL: Driver does not support the requested OpenGL profile"); + _SAPP_PANIC(WIN32_WGL_OPENGL_PROFILE_NOT_SUPPORTED); } else if (err == (0xc0070000 | ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB)) { - _sapp_fail("WGL: The share context is not compatible with the requested context"); + _SAPP_PANIC(WIN32_WGL_INCOMPATIBLE_DEVICE_CONTEXT); } else { - _sapp_fail("WGL: Failed to create OpenGL context"); + _SAPP_PANIC(WIN32_WGL_CREATE_CONTEXT_ATTRIBS_FAILED_OTHER); } } _sapp.wgl.MakeCurrent(_sapp.win32.dc, _sapp.wgl.gl_ctx); @@ -6867,7 +7072,7 @@ _SOKOL_PRIVATE void _sapp_win32_lock_mouse(bool lock) { /* 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) + (so that we don't miss any mouse up events) */ RECT client_rect = { _sapp.win32.mouse_locked_x, @@ -6888,7 +7093,7 @@ _SOKOL_PRIVATE void _sapp_win32_lock_mouse(bool lock) { _sapp.win32.hwnd // hwndTarget }; if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) { - SAPP_LOG("RegisterRawInputDevices() failed (on mouse lock).\n"); + _SAPP_ERROR(WIN32_REGISTER_RAW_INPUT_DEVICES_FAILED_MOUSE_LOCK); } /* 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 @@ -6899,7 +7104,7 @@ _SOKOL_PRIVATE void _sapp_win32_lock_mouse(bool lock) { /* disable raw input for mouse */ const RAWINPUTDEVICE rid = { 0x01, 0x02, RIDEV_REMOVE, NULL }; if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) { - SAPP_LOG("RegisterRawInputDevices() failed (on mouse unlock).\n"); + _SAPP_ERROR(WIN32_REGISTER_RAW_INPUT_DEVICES_FAILED_MOUSE_UNLOCK); } /* let the mouse roam freely again */ @@ -6950,6 +7155,21 @@ _SOKOL_PRIVATE uint32_t _sapp_win32_mods(void) { return mods; } +_SOKOL_PRIVATE void _sapp_win32_mouse_update(LPARAM lParam) { + 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; + if (_sapp.mouse.pos_valid) { + // don't update dx/dy in the very first event + _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_win32_mouse_event(sapp_event_type type, sapp_mousebutton btn) { if (_sapp_events_enabled()) { _sapp_init_event(type); @@ -7039,7 +7259,7 @@ _SOKOL_PRIVATE void _sapp_win32_files_dropped(HDROP hdrop) { WCHAR* buffer = (WCHAR*) _sapp_malloc_clear(num_chars * sizeof(WCHAR)); DragQueryFileW(hdrop, i, buffer, num_chars); if (!_sapp_win32_wide_to_utf8(buffer, _sapp_dropped_file_path_ptr((int)i), _sapp.drop.max_path_length)) { - SAPP_LOG("sokol_app.h: dropped file path too long (sapp_desc.max_dropped_file_path_length)\n"); + _SAPP_ERROR(DROPPED_FILE_PATH_TOO_LONG); drop_failed = true; } _sapp_free(buffer); @@ -7048,6 +7268,7 @@ _SOKOL_PRIVATE void _sapp_win32_files_dropped(HDROP hdrop) { if (!drop_failed) { if (_sapp_events_enabled()) { _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); + _sapp.event.modifiers = _sapp_win32_mods(); _sapp_call_event(&_sapp.event); } } @@ -7095,7 +7316,7 @@ _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM a change to intervene via sapp_cancel_quit() */ _sapp.quit_requested = true; - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED); + _sapp_win32_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED); /* if user code hasn't intervened, quit the app */ if (_sapp.quit_requested) { _sapp.quit_ordered = true; @@ -7127,23 +7348,23 @@ _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM if (iconified != _sapp.win32.iconified) { _sapp.win32.iconified = iconified; if (iconified) { - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_ICONIFIED); + _sapp_win32_app_event(SAPP_EVENTTYPE_ICONIFIED); } else { - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESTORED); + _sapp_win32_app_event(SAPP_EVENTTYPE_RESTORED); } } } break; case WM_SETFOCUS: - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_FOCUSED); + _sapp_win32_app_event(SAPP_EVENTTYPE_FOCUSED); break; case WM_KILLFOCUS: /* if focus is lost for any reason, and we're in mouse locked mode, disable mouse lock */ if (_sapp.mouse.locked) { _sapp_win32_lock_mouse(false); } - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_UNFOCUSED); + _sapp_win32_app_event(SAPP_EVENTTYPE_UNFOCUSED); break; case WM_SETCURSOR: if (LOWORD(lParam) == HTCLIENT) { @@ -7160,41 +7381,38 @@ _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM break; } case WM_LBUTTONDOWN: + _sapp_win32_mouse_update(lParam); _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_LEFT); _sapp_win32_capture_mouse(1<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. + NOTE: This code is untested and will most likely behave wrong in Remote Desktop sessions. + (such remote desktop sessions are setting the MOUSE_MOVE_ABSOLUTE flag). + See: https://github.com/floooh/sokol/issues/806 and + https://github.com/microsoft/DirectXTK/commit/ef56b63f3739381e451f7a5a5bd2c9779d2a7555) */ + LONG new_x = raw_mouse_data->data.mouse.lLastX; + LONG new_y = raw_mouse_data->data.mouse.lLastY; 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; } + _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) */ @@ -7246,6 +7467,8 @@ _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM case WM_MOUSELEAVE: if (!_sapp.mouse.locked) { + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; _sapp.win32.mouse_tracked = false; _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID); } @@ -7290,7 +7513,7 @@ _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM #if defined(SOKOL_D3D11) _sapp_d3d11_resize_default_render_target(); #endif - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); + _sapp_win32_app_event(SAPP_EVENTTYPE_RESIZED); } */ break; @@ -7513,26 +7736,32 @@ _SOKOL_PRIVATE bool _sapp_win32_set_clipboard_string(const char* str) { SOKOL_ASSERT(_sapp.win32.hwnd); SOKOL_ASSERT(_sapp.clipboard.enabled && (_sapp.clipboard.buf_size > 0)); + if (!OpenClipboard(_sapp.win32.hwnd)) { + return false; + } + + HANDLE object = 0; wchar_t* wchar_buf = 0; + const SIZE_T wchar_buf_size = (SIZE_T)_sapp.clipboard.buf_size * sizeof(wchar_t); - HANDLE object = GlobalAlloc(GMEM_MOVEABLE, wchar_buf_size); - if (!object) { + object = GlobalAlloc(GMEM_MOVEABLE, wchar_buf_size); + if (NULL == object) { goto error; } wchar_buf = (wchar_t*) GlobalLock(object); - if (!wchar_buf) { + if (NULL == wchar_buf) { goto error; } - if (!_sapp_win32_uwp_utf8_to_wide(str, wchar_buf, (int)wchar_buf_size)) { + if (!_sapp_win32_utf8_to_wide(str, wchar_buf, (int)wchar_buf_size)) { goto error; } - GlobalUnlock(wchar_buf); + GlobalUnlock(object); wchar_buf = 0; - if (!OpenClipboard(_sapp.win32.hwnd)) { + EmptyClipboard(); + // NOTE: when successful, SetClipboardData() takes ownership of memory object! + if (NULL == SetClipboardData(CF_UNICODETEXT, object)) { goto error; } - EmptyClipboard(); - SetClipboardData(CF_UNICODETEXT, object); CloseClipboard(); return true; @@ -7543,6 +7772,7 @@ _SOKOL_PRIVATE bool _sapp_win32_set_clipboard_string(const char* str) { if (object) { GlobalFree(object); } + CloseClipboard(); return false; } @@ -7566,7 +7796,7 @@ _SOKOL_PRIVATE const char* _sapp_win32_get_clipboard_string(void) { return _sapp.clipboard.buffer; } if (!_sapp_win32_wide_to_utf8(wchar_buf, _sapp.clipboard.buffer, _sapp.clipboard.buf_size)) { - SAPP_LOG("sokol_app.h: clipboard string didn't fit into clipboard buffer\n"); + _SAPP_ERROR(CLIPBOARD_STRING_TOO_BIG); } GlobalUnlock(object); CloseClipboard(); @@ -7574,7 +7804,7 @@ _SOKOL_PRIVATE const char* _sapp_win32_get_clipboard_string(void) { } _SOKOL_PRIVATE void _sapp_win32_update_window_title(void) { - _sapp_win32_uwp_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide)); + _sapp_win32_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide)); SetWindowTextW(_sapp.win32.hwnd, _sapp.window_title_wide); } @@ -7676,8 +7906,8 @@ _SOKOL_PRIVATE void _sapp_win32_run(const sapp_desc* desc) { _sapp_init_state(desc); _sapp_win32_init_console(); _sapp.win32.is_win10_or_greater = _sapp_win32_is_win10_or_greater(); - _sapp_win32_uwp_init_keytable(); - _sapp_win32_uwp_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide)); + _sapp_win32_init_keytable(); + _sapp_win32_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide)); _sapp_win32_init_dpi(); _sapp_win32_init_cursors(); _sapp_win32_create_window(); @@ -7722,7 +7952,7 @@ _SOKOL_PRIVATE void _sapp_win32_run(const sapp_desc* desc) { #if defined(SOKOL_D3D11) _sapp_d3d11_resize_default_render_target(); #endif - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); + _sapp_win32_app_event(SAPP_EVENTTYPE_RESIZED); } /* check if the window monitor has changed, need to reset timing because the new monitor might have a different refresh rate @@ -7756,7 +7986,7 @@ _SOKOL_PRIVATE char** _sapp_win32_command_line_to_utf8_argv(LPWSTR w_command_lin LPWSTR* w_argv = CommandLineToArgvW(w_command_line, &argc); if (w_argv == NULL) { - _sapp_fail("Win32: failed to parse command line"); + // FIXME: chicken egg problem, can't report errors before sokol_main() is called! } else { size_t size = wcslen(w_command_line) * 4; argv = (char**) _sapp_malloc_clear(((size_t)argc + 1) * sizeof(char*) + size); @@ -7766,7 +7996,7 @@ _SOKOL_PRIVATE char** _sapp_win32_command_line_to_utf8_argv(LPWSTR w_command_lin for (int i = 0; i < argc; ++i) { n = WideCharToMultiByte(CP_UTF8, 0, w_argv[i], -1, args, (int)size, NULL, NULL); if (n == 0) { - _sapp_fail("Win32: failed to convert all arguments to utf8"); + // FIXME: chicken egg problem, can't report errors before sokol_main() is called! break; } argv[i] = args; @@ -7808,1035 +8038,13 @@ int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _ #endif /* _SAPP_WIN32 */ -/*== UWP ================================================================*/ -#if defined(_SAPP_UWP) - -// Helper functions -_SOKOL_PRIVATE void _sapp_uwp_configure_dpi(float monitor_dpi) { - _sapp.uwp.dpi.window_scale = monitor_dpi / 96.0f; - if (_sapp.desc.high_dpi) { - _sapp.uwp.dpi.content_scale = _sapp.uwp.dpi.window_scale; - _sapp.uwp.dpi.mouse_scale = 1.0f * _sapp.uwp.dpi.window_scale; - } - else { - _sapp.uwp.dpi.content_scale = 1.0f; - _sapp.uwp.dpi.mouse_scale = 1.0f; - } - _sapp.dpi_scale = _sapp.uwp.dpi.content_scale; -} - -_SOKOL_PRIVATE void _sapp_uwp_update_cursor(sapp_mouse_cursor cursor, bool shown) { - using namespace winrt::Windows::UI::Core; - - CoreCursor uwp_cursor(nullptr); - if (shown) { - switch (cursor) { - case SAPP_MOUSECURSOR_ARROW: uwp_cursor = CoreCursor(CoreCursorType::Arrow, 0); break; - case SAPP_MOUSECURSOR_IBEAM: uwp_cursor = CoreCursor(CoreCursorType::IBeam, 0); break; - case SAPP_MOUSECURSOR_CROSSHAIR: uwp_cursor = CoreCursor(CoreCursorType::Cross, 0); break; - case SAPP_MOUSECURSOR_POINTING_HAND: uwp_cursor = CoreCursor(CoreCursorType::Hand, 0); break; - case SAPP_MOUSECURSOR_RESIZE_EW: uwp_cursor = CoreCursor(CoreCursorType::SizeWestEast, 0); break; - case SAPP_MOUSECURSOR_RESIZE_NS: uwp_cursor = CoreCursor(CoreCursorType::SizeNorthSouth, 0); break; - case SAPP_MOUSECURSOR_RESIZE_NWSE: uwp_cursor = CoreCursor(CoreCursorType::SizeNorthwestSoutheast, 0); break; - case SAPP_MOUSECURSOR_RESIZE_NESW: uwp_cursor = CoreCursor(CoreCursorType::SizeNortheastSouthwest, 0); break; - case SAPP_MOUSECURSOR_RESIZE_ALL: uwp_cursor = CoreCursor(CoreCursorType::SizeAll, 0); break; - case SAPP_MOUSECURSOR_NOT_ALLOWED: uwp_cursor = CoreCursor(CoreCursorType::UniversalNo, 0); break; - default: uwp_cursor = CoreCursor(CoreCursorType::Arrow, 0); break; - } - } - CoreWindow::GetForCurrentThread().PointerCursor(uwp_cursor); -} - -_SOKOL_PRIVATE uint32_t _sapp_uwp_mods(winrt::Windows::UI::Core::CoreWindow const& sender_window) { - using namespace winrt::Windows::System; - using namespace winrt::Windows::UI::Core; - - uint32_t mods = 0; - if ((sender_window.GetKeyState(VirtualKey::Shift) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) { - mods |= SAPP_MODIFIER_SHIFT; - } - if ((sender_window.GetKeyState(VirtualKey::Control) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) { - mods |= SAPP_MODIFIER_CTRL; - } - if ((sender_window.GetKeyState(VirtualKey::Menu) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) { - mods |= SAPP_MODIFIER_ALT; - } - if (((sender_window.GetKeyState(VirtualKey::LeftWindows) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) || - ((sender_window.GetKeyState(VirtualKey::RightWindows) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down)) - { - mods |= SAPP_MODIFIER_SUPER; - } - if (0 != (_sapp.uwp.mouse_buttons & (1<= 32)) { - _sapp_init_event(SAPP_EVENTTYPE_CHAR); - _sapp.event.modifiers = _sapp_uwp_mods(sender_window); - _sapp.event.char_code = c; - _sapp.event.key_repeat = repeat; - _sapp_call_event(&_sapp.event); - } -} - -_SOKOL_PRIVATE void _sapp_uwp_toggle_fullscreen(void) { - auto appView = winrt::Windows::UI::ViewManagement::ApplicationView::GetForCurrentView(); - _sapp.fullscreen = appView.IsFullScreenMode(); - if (!_sapp.fullscreen) { - appView.TryEnterFullScreenMode(); - } - else { - appView.ExitFullScreenMode(); - } - _sapp.fullscreen = appView.IsFullScreenMode(); -} - -namespace {/* Empty namespace to ensure internal linkage (same as _SOKOL_PRIVATE) */ - -// Controls all the DirectX device resources. -class DeviceResources { -public: - // Provides an interface for an application that owns DeviceResources to be notified of the device being lost or created. - interface IDeviceNotify { - virtual void OnDeviceLost() = 0; - virtual void OnDeviceRestored() = 0; - }; - - DeviceResources(); - ~DeviceResources(); - void SetWindow(winrt::Windows::UI::Core::CoreWindow const& window); - void SetLogicalSize(winrt::Windows::Foundation::Size logicalSize); - void SetCurrentOrientation(winrt::Windows::Graphics::Display::DisplayOrientations currentOrientation); - void SetDpi(float dpi); - void ValidateDevice(); - void HandleDeviceLost(); - void RegisterDeviceNotify(IDeviceNotify* deviceNotify); - void Trim(); - void Present(); - -private: - - // Swapchain Rotation Matrices (Z-rotation) - static inline const DirectX::XMFLOAT4X4 DeviceResources::m_rotation0 = { - 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f - }; - static inline const DirectX::XMFLOAT4X4 DeviceResources::m_rotation90 = { - 0.0f, 1.0f, 0.0f, 0.0f, - -1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f - }; - static inline const DirectX::XMFLOAT4X4 DeviceResources::m_rotation180 = { - -1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, -1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f - }; - static inline const DirectX::XMFLOAT4X4 DeviceResources::m_rotation270 = { - 0.0f, -1.0f, 0.0f, 0.0f, - 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f - }; - - void CreateDeviceResources(); - void CreateWindowSizeDependentResources(); - void UpdateRenderTargetSize(); - DXGI_MODE_ROTATION ComputeDisplayRotation(); - bool SdkLayersAvailable(); - - // Direct3D objects. - winrt::com_ptr m_d3dDevice; - winrt::com_ptr m_d3dContext; - winrt::com_ptr m_swapChain; - - // Direct3D rendering objects. Required for 3D. - winrt::com_ptr m_d3dRenderTarget; - winrt::com_ptr m_d3dRenderTargetView; - winrt::com_ptr m_d3dMSAARenderTarget; - winrt::com_ptr m_d3dMSAARenderTargetView; - winrt::com_ptr m_d3dDepthStencil; - winrt::com_ptr m_d3dDepthStencilView; - D3D11_VIEWPORT m_screenViewport = { }; - - // Cached reference to the Window. - winrt::agile_ref< winrt::Windows::UI::Core::CoreWindow> m_window; - - // Cached device properties. - D3D_FEATURE_LEVEL m_d3dFeatureLevel = D3D_FEATURE_LEVEL_9_1; - winrt::Windows::Foundation::Size m_d3dRenderTargetSize = { }; - winrt::Windows::Foundation::Size m_outputSize = { }; - winrt::Windows::Foundation::Size m_logicalSize = { }; - winrt::Windows::Graphics::Display::DisplayOrientations m_nativeOrientation = winrt::Windows::Graphics::Display::DisplayOrientations::None; - winrt::Windows::Graphics::Display::DisplayOrientations m_currentOrientation = winrt::Windows::Graphics::Display::DisplayOrientations::None; - float m_dpi = -1.0f; - - // Transforms used for display orientation. - DirectX::XMFLOAT4X4 m_orientationTransform3D; - - // The IDeviceNotify can be held directly as it owns the DeviceResources. - IDeviceNotify* m_deviceNotify = nullptr; -}; - -// Main entry point for our app. Connects the app with the Windows shell and handles application lifecycle events. -struct App : winrt::implements { -public: - // IFrameworkViewSource Methods - winrt::Windows::ApplicationModel::Core::IFrameworkView CreateView() { return *this; } - - // IFrameworkView Methods. - virtual void Initialize(winrt::Windows::ApplicationModel::Core::CoreApplicationView const& applicationView); - virtual void SetWindow(winrt::Windows::UI::Core::CoreWindow const& window); - virtual void Load(winrt::hstring const& entryPoint); - virtual void Run(); - virtual void Uninitialize(); - -protected: - // Application lifecycle event handlers - void OnActivated(winrt::Windows::ApplicationModel::Core::CoreApplicationView const& applicationView, winrt::Windows::ApplicationModel::Activation::IActivatedEventArgs const& args); - void OnSuspending(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::ApplicationModel::SuspendingEventArgs const& args); - void OnResuming(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::Foundation::IInspectable const& args); - - // Window event handlers - void OnWindowSizeChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::WindowSizeChangedEventArgs const& args); - void OnVisibilityChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::VisibilityChangedEventArgs const& args); - - // Navigation event handlers - void OnBackRequested(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Core::BackRequestedEventArgs const& args); - - // Input event handlers - void OnKeyDown(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::KeyEventArgs const& args); - void OnKeyUp(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::KeyEventArgs const& args); - void OnCharacterReceived(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::CharacterReceivedEventArgs const& args); - - // Pointer event handlers - void OnPointerEntered(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); - void OnPointerExited(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); - void OnPointerPressed(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); - void OnPointerReleased(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); - void OnPointerMoved(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); - void OnPointerWheelChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); - - // DisplayInformation event handlers. - void OnDpiChanged(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args); - void OnOrientationChanged(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args); - void OnDisplayContentsInvalidated(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args); - -private: - std::unique_ptr m_deviceResources; - bool m_windowVisible = true; -}; - -DeviceResources::DeviceResources() { - CreateDeviceResources(); -} - -DeviceResources::~DeviceResources() { - // Cleanup Sokol Context - _sapp.d3d11.device = nullptr; - _sapp.d3d11.device_context = nullptr; -} - -void DeviceResources::CreateDeviceResources() { - // This flag adds support for surfaces with a different color channel ordering - // than the API default. It is required for compatibility with Direct2D. - UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; - - #if defined(_DEBUG) - if (SdkLayersAvailable()) { - // If the project is in a debug build, enable debugging via SDK Layers with this flag. - creationFlags |= D3D11_CREATE_DEVICE_DEBUG; - } - #endif - - // This array defines the set of DirectX hardware feature levels this app will support. - // Note the ordering should be preserved. - // Don't forget to declare your application's minimum required feature level in its - // description. All applications are assumed to support 9.1 unless otherwise stated. - D3D_FEATURE_LEVEL featureLevels[] = { - D3D_FEATURE_LEVEL_12_1, - D3D_FEATURE_LEVEL_12_0, - D3D_FEATURE_LEVEL_11_1, - D3D_FEATURE_LEVEL_11_0, - D3D_FEATURE_LEVEL_10_1, - D3D_FEATURE_LEVEL_10_0, - D3D_FEATURE_LEVEL_9_3, - D3D_FEATURE_LEVEL_9_2, - D3D_FEATURE_LEVEL_9_1 - }; - - // Create the Direct3D 11 API device object and a corresponding context. - winrt::com_ptr device; - winrt::com_ptr context; - - HRESULT hr = D3D11CreateDevice( - nullptr, // Specify nullptr to use the default adapter. - D3D_DRIVER_TYPE_HARDWARE, // Create a device using the hardware graphics driver. - 0, // Should be 0 unless the driver is D3D_DRIVER_TYPE_SOFTWARE. - creationFlags, // Set debug and Direct2D compatibility flags. - featureLevels, // List of feature levels this app can support. - ARRAYSIZE(featureLevels), // Size of the list above. - D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Microsoft Store apps. - device.put(), // Returns the Direct3D device created. - &m_d3dFeatureLevel, // Returns feature level of device created. - context.put() // Returns the device immediate context. - ); - - if (FAILED(hr)) { - // If the initialization fails, fall back to the WARP device. - // For more information on WARP, see: - // https://go.microsoft.com/fwlink/?LinkId=286690 - winrt::check_hresult( - D3D11CreateDevice( - nullptr, - D3D_DRIVER_TYPE_WARP, // Create a WARP device instead of a hardware device. - 0, - creationFlags, - featureLevels, - ARRAYSIZE(featureLevels), - D3D11_SDK_VERSION, - device.put(), - &m_d3dFeatureLevel, - context.put() - ) - ); - } - - // Store pointers to the Direct3D 11.3 API device and immediate context. - m_d3dDevice = device.as(); - m_d3dContext = context.as(); - - // Setup Sokol Context - _sapp.d3d11.device = m_d3dDevice.get(); - _sapp.d3d11.device_context = m_d3dContext.get(); -} - -void DeviceResources::CreateWindowSizeDependentResources() { - // Cleanup Sokol Context (these are non-owning raw pointers) - _sapp.d3d11.rt = nullptr; - _sapp.d3d11.rtv = nullptr; - _sapp.d3d11.msaa_rt = nullptr; - _sapp.d3d11.msaa_rtv = nullptr; - _sapp.d3d11.ds = nullptr; - _sapp.d3d11.dsv = nullptr; - - // Clear the previous window size specific context. - ID3D11RenderTargetView* nullViews[] = { nullptr }; - m_d3dContext->OMSetRenderTargets(ARRAYSIZE(nullViews), nullViews, nullptr); - // these are smart pointers, setting to nullptr will delete the objects - m_d3dRenderTarget = nullptr; - m_d3dRenderTargetView = nullptr; - m_d3dMSAARenderTarget = nullptr; - m_d3dMSAARenderTargetView = nullptr; - m_d3dDepthStencilView = nullptr; - m_d3dDepthStencil = nullptr; - m_d3dContext->Flush1(D3D11_CONTEXT_TYPE_ALL, nullptr); - - UpdateRenderTargetSize(); - - // The width and height of the swap chain must be based on the window's - // natively-oriented width and height. If the window is not in the native - // orientation, the dimensions must be reversed. - DXGI_MODE_ROTATION displayRotation = ComputeDisplayRotation(); - - bool swapDimensions = displayRotation == DXGI_MODE_ROTATION_ROTATE90 || displayRotation == DXGI_MODE_ROTATION_ROTATE270; - m_d3dRenderTargetSize.Width = swapDimensions ? m_outputSize.Height : m_outputSize.Width; - m_d3dRenderTargetSize.Height = swapDimensions ? m_outputSize.Width : m_outputSize.Height; - - if (m_swapChain != nullptr) { - // If the swap chain already exists, resize it. - HRESULT hr = m_swapChain->ResizeBuffers( - 2, // Double-buffered swap chain. - lround(m_d3dRenderTargetSize.Width), - lround(m_d3dRenderTargetSize.Height), - DXGI_FORMAT_B8G8R8A8_UNORM, - 0 - ); - - if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) { - // If the device was removed for any reason, a new device and swap chain will need to be created. - HandleDeviceLost(); - - // Everything is set up now. Do not continue execution of this method. HandleDeviceLost will reenter this method - // and correctly set up the new device. - return; - } - else { - winrt::check_hresult(hr); - } - } - else { - // Otherwise, create a new one using the same adapter as the existing Direct3D device. - DXGI_SCALING scaling = (_sapp.uwp.dpi.content_scale == _sapp.uwp.dpi.window_scale) ? DXGI_SCALING_NONE : DXGI_SCALING_STRETCH; - DXGI_SWAP_CHAIN_DESC1 swapChainDesc = { 0 }; - - swapChainDesc.Width = lround(m_d3dRenderTargetSize.Width); // Match the size of the window. - swapChainDesc.Height = lround(m_d3dRenderTargetSize.Height); - swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // This is the most common swap chain format. - swapChainDesc.Stereo = false; - swapChainDesc.SampleDesc.Count = 1; // Don't use multi-sampling. - swapChainDesc.SampleDesc.Quality = 0; - swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - swapChainDesc.BufferCount = 2; // Use double-buffering to minimize latency. - swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All Microsoft Store apps must use this SwapEffect. - swapChainDesc.Flags = 0; - swapChainDesc.Scaling = scaling; - swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; - - // This sequence obtains the DXGI factory that was used to create the Direct3D device above. - winrt::com_ptr dxgiDevice = m_d3dDevice.as(); - winrt::com_ptr dxgiAdapter; - winrt::check_hresult(dxgiDevice->GetAdapter(dxgiAdapter.put())); - winrt::com_ptr dxgiFactory; - winrt::check_hresult(dxgiAdapter->GetParent(__uuidof(IDXGIFactory4), dxgiFactory.put_void())); - winrt::com_ptr swapChain; - winrt::check_hresult(dxgiFactory->CreateSwapChainForCoreWindow(m_d3dDevice.get(), m_window.get().as<::IUnknown>().get(), &swapChainDesc, nullptr, swapChain.put())); - m_swapChain = swapChain.as(); - - // Ensure that DXGI does not queue more than one frame at a time. This both reduces latency and - // ensures that the application will only render after each VSync, minimizing power consumption. - winrt::check_hresult(dxgiDevice->SetMaximumFrameLatency(1)); - - // Setup Sokol Context - winrt::check_hresult(swapChain->GetDesc(&_sapp.d3d11.swap_chain_desc)); - _sapp.d3d11.swap_chain = m_swapChain.as().detach(); - } - - // Set the proper orientation for the swap chain, and generate 2D and - // 3D matrix transformations for rendering to the rotated swap chain. - // Note the rotation angle for the 2D and 3D transforms are different. - // This is due to the difference in coordinate spaces. Additionally, - // the 3D matrix is specified explicitly to avoid rounding errors. - switch (displayRotation) { - case DXGI_MODE_ROTATION_IDENTITY: - m_orientationTransform3D = m_rotation0; - break; - - case DXGI_MODE_ROTATION_ROTATE90: - m_orientationTransform3D = m_rotation270; - break; - - case DXGI_MODE_ROTATION_ROTATE180: - m_orientationTransform3D = m_rotation180; - break; - - case DXGI_MODE_ROTATION_ROTATE270: - m_orientationTransform3D = m_rotation90; - break; - } - winrt::check_hresult(m_swapChain->SetRotation(displayRotation)); - - // Create a render target view of the swap chain back buffer. - winrt::check_hresult(m_swapChain->GetBuffer(0, IID_PPV_ARGS(&m_d3dRenderTarget))); - winrt::check_hresult(m_d3dDevice->CreateRenderTargetView1(m_d3dRenderTarget.get(), nullptr, m_d3dRenderTargetView.put())); - - // Create MSAA texture and view if needed - if (_sapp.sample_count > 1) { - CD3D11_TEXTURE2D_DESC1 msaaTexDesc( - DXGI_FORMAT_B8G8R8A8_UNORM, - lround(m_d3dRenderTargetSize.Width), - lround(m_d3dRenderTargetSize.Height), - 1, // arraySize - 1, // mipLevels - D3D11_BIND_RENDER_TARGET, - D3D11_USAGE_DEFAULT, - 0, // cpuAccessFlags - _sapp.sample_count, - _sapp.sample_count > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0 - ); - winrt::check_hresult(m_d3dDevice->CreateTexture2D1(&msaaTexDesc, nullptr, m_d3dMSAARenderTarget.put())); - winrt::check_hresult(m_d3dDevice->CreateRenderTargetView1(m_d3dMSAARenderTarget.get(), nullptr, m_d3dMSAARenderTargetView.put())); - } - - // Create a depth stencil view for use with 3D rendering if needed. - CD3D11_TEXTURE2D_DESC1 depthStencilDesc( - DXGI_FORMAT_D24_UNORM_S8_UINT, - lround(m_d3dRenderTargetSize.Width), - lround(m_d3dRenderTargetSize.Height), - 1, // This depth stencil view has only one texture. - 1, // Use a single mipmap level. - D3D11_BIND_DEPTH_STENCIL, - D3D11_USAGE_DEFAULT, - 0, // cpuAccessFlag - _sapp.sample_count, - _sapp.sample_count > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0 - ); - winrt::check_hresult(m_d3dDevice->CreateTexture2D1(&depthStencilDesc, nullptr, m_d3dDepthStencil.put())); - - CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(D3D11_DSV_DIMENSION_TEXTURE2D); - winrt::check_hresult(m_d3dDevice->CreateDepthStencilView(m_d3dDepthStencil.get(), nullptr, m_d3dDepthStencilView.put())); - - // Set sokol window and framebuffer sizes - _sapp.window_width = (int) m_logicalSize.Width; - _sapp.window_height = (int) m_logicalSize.Height; - _sapp.framebuffer_width = lround(m_d3dRenderTargetSize.Width); - _sapp.framebuffer_height = lround(m_d3dRenderTargetSize.Height); - - // Setup Sokol Context - _sapp.d3d11.rt = m_d3dRenderTarget.as().get(); - _sapp.d3d11.rtv = m_d3dRenderTargetView.as().get(); - _sapp.d3d11.ds = m_d3dDepthStencil.as().get(); - _sapp.d3d11.dsv = m_d3dDepthStencilView.get(); - if (_sapp.sample_count > 1) { - _sapp.d3d11.msaa_rt = m_d3dMSAARenderTarget.as().get(); - _sapp.d3d11.msaa_rtv = m_d3dMSAARenderTargetView.as().get(); - } - - // Sokol app is now valid - _sapp.valid = true; -} - -// Determine the dimensions of the render target and whether it will be scaled down. -void DeviceResources::UpdateRenderTargetSize() { - // Calculate the necessary render target size in pixels. - m_outputSize.Width = m_logicalSize.Width * _sapp.uwp.dpi.content_scale; - m_outputSize.Height = m_logicalSize.Height * _sapp.uwp.dpi.content_scale; - - // Prevent zero size DirectX content from being created. - m_outputSize.Width = std::max(m_outputSize.Width, 1.0f); - m_outputSize.Height = std::max(m_outputSize.Height, 1.0f); -} - -// This method is called when the CoreWindow is created (or re-created). -void DeviceResources::SetWindow(winrt::Windows::UI::Core::CoreWindow const& window) { - auto currentDisplayInformation = winrt::Windows::Graphics::Display::DisplayInformation::GetForCurrentView(); - m_window = window; - m_logicalSize = winrt::Windows::Foundation::Size(window.Bounds().Width, window.Bounds().Height); - m_nativeOrientation = currentDisplayInformation.NativeOrientation(); - m_currentOrientation = currentDisplayInformation.CurrentOrientation(); - m_dpi = currentDisplayInformation.LogicalDpi(); - _sapp_uwp_configure_dpi(m_dpi); - CreateWindowSizeDependentResources(); -} - -// This method is called in the event handler for the SizeChanged event. -void DeviceResources::SetLogicalSize(winrt::Windows::Foundation::Size logicalSize) { - if (m_logicalSize != logicalSize) { - m_logicalSize = logicalSize; - CreateWindowSizeDependentResources(); - } -} - -// This method is called in the event handler for the DpiChanged event. -void DeviceResources::SetDpi(float dpi) { - if (dpi != m_dpi) { - m_dpi = dpi; - _sapp_uwp_configure_dpi(m_dpi); - // When the display DPI changes, the logical size of the window (measured in Dips) also changes and needs to be updated. - auto window = m_window.get(); - m_logicalSize = winrt::Windows::Foundation::Size(window.Bounds().Width, window.Bounds().Height); - CreateWindowSizeDependentResources(); - } -} - -// This method is called in the event handler for the OrientationChanged event. -void DeviceResources::SetCurrentOrientation(winrt::Windows::Graphics::Display::DisplayOrientations currentOrientation) { - if (m_currentOrientation != currentOrientation) { - m_currentOrientation = currentOrientation; - CreateWindowSizeDependentResources(); - } -} - -// This method is called in the event handler for the DisplayContentsInvalidated event. -void DeviceResources::ValidateDevice() { - // The D3D Device is no longer valid if the default adapter changed since the device - // was created or if the device has been removed. - - // First, get the information for the default adapter from when the device was created. - winrt::com_ptr dxgiDevice = m_d3dDevice.as< IDXGIDevice3>(); - winrt::com_ptr deviceAdapter; - winrt::check_hresult(dxgiDevice->GetAdapter(deviceAdapter.put())); - winrt::com_ptr deviceFactory; - winrt::check_hresult(deviceAdapter->GetParent(IID_PPV_ARGS(&deviceFactory))); - winrt::com_ptr previousDefaultAdapter; - winrt::check_hresult(deviceFactory->EnumAdapters1(0, previousDefaultAdapter.put())); - DXGI_ADAPTER_DESC1 previousDesc; - winrt::check_hresult(previousDefaultAdapter->GetDesc1(&previousDesc)); - - // Next, get the information for the current default adapter. - winrt::com_ptr currentFactory; - winrt::check_hresult(CreateDXGIFactory1(IID_PPV_ARGS(¤tFactory))); - winrt::com_ptr currentDefaultAdapter; - winrt::check_hresult(currentFactory->EnumAdapters1(0, currentDefaultAdapter.put())); - DXGI_ADAPTER_DESC1 currentDesc; - winrt::check_hresult(currentDefaultAdapter->GetDesc1(¤tDesc)); - - // If the adapter LUIDs don't match, or if the device reports that it has been removed, - // a new D3D device must be created. - if (previousDesc.AdapterLuid.LowPart != currentDesc.AdapterLuid.LowPart || - previousDesc.AdapterLuid.HighPart != currentDesc.AdapterLuid.HighPart || - FAILED(m_d3dDevice->GetDeviceRemovedReason())) - { - // Release references to resources related to the old device. - dxgiDevice = nullptr; - deviceAdapter = nullptr; - deviceFactory = nullptr; - previousDefaultAdapter = nullptr; - - // Create a new device and swap chain. - HandleDeviceLost(); - } -} - -// Recreate all device resources and set them back to the current state. -void DeviceResources::HandleDeviceLost() { - m_swapChain = nullptr; - if (m_deviceNotify != nullptr) { - m_deviceNotify->OnDeviceLost(); - } - CreateDeviceResources(); - CreateWindowSizeDependentResources(); - if (m_deviceNotify != nullptr) { - m_deviceNotify->OnDeviceRestored(); - } -} - -// Register our DeviceNotify to be informed on device lost and creation. -void DeviceResources::RegisterDeviceNotify(IDeviceNotify* deviceNotify) { - m_deviceNotify = deviceNotify; -} - -// Call this method when the app suspends. It provides a hint to the driver that the app -// is entering an idle state and that temporary buffers can be reclaimed for use by other apps. -void DeviceResources::Trim() { - m_d3dDevice.as()->Trim(); -} - -// Present the contents of the swap chain to the screen. -void DeviceResources::Present() { - - // MSAA resolve if needed - if (_sapp.sample_count > 1) { - m_d3dContext->ResolveSubresource(m_d3dRenderTarget.get(), 0, m_d3dMSAARenderTarget.get(), 0, DXGI_FORMAT_B8G8R8A8_UNORM); - m_d3dContext->DiscardView1(m_d3dMSAARenderTargetView.get(), nullptr, 0); - } - - // The first argument instructs DXGI to block until VSync, putting the application - // to sleep until the next VSync. This ensures we don't waste any cycles rendering - // frames that will never be displayed to the screen. - DXGI_PRESENT_PARAMETERS parameters = { 0 }; - HRESULT hr = m_swapChain->Present1(1, 0, ¶meters); - - // Discard the contents of the render target. - // This is a valid operation only when the existing contents will be entirely - // overwritten. If dirty or scroll rects are used, this call should be removed. - m_d3dContext->DiscardView1(m_d3dRenderTargetView.get(), nullptr, 0); - - // Discard the contents of the depth stencil. - m_d3dContext->DiscardView1(m_d3dDepthStencilView.get(), nullptr, 0); - - // If the device was removed either by a disconnection or a driver upgrade, we - // must recreate all device resources. - if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) { - HandleDeviceLost(); - } - else { - winrt::check_hresult(hr); - } -} - -// This method determines the rotation between the display device's native orientation and the -// current display orientation. -DXGI_MODE_ROTATION DeviceResources::ComputeDisplayRotation() { - DXGI_MODE_ROTATION rotation = DXGI_MODE_ROTATION_UNSPECIFIED; - - // Note: NativeOrientation can only be Landscape or Portrait even though - // the DisplayOrientations enum has other values. - switch (m_nativeOrientation) { - case winrt::Windows::Graphics::Display::DisplayOrientations::Landscape: - switch (m_currentOrientation) { - case winrt::Windows::Graphics::Display::DisplayOrientations::Landscape: - rotation = DXGI_MODE_ROTATION_IDENTITY; - break; - - case winrt::Windows::Graphics::Display::DisplayOrientations::Portrait: - rotation = DXGI_MODE_ROTATION_ROTATE270; - break; - - case winrt::Windows::Graphics::Display::DisplayOrientations::LandscapeFlipped: - rotation = DXGI_MODE_ROTATION_ROTATE180; - break; - - case winrt::Windows::Graphics::Display::DisplayOrientations::PortraitFlipped: - rotation = DXGI_MODE_ROTATION_ROTATE90; - break; - } - break; - - case winrt::Windows::Graphics::Display::DisplayOrientations::Portrait: - switch (m_currentOrientation) { - case winrt::Windows::Graphics::Display::DisplayOrientations::Landscape: - rotation = DXGI_MODE_ROTATION_ROTATE90; - break; - - case winrt::Windows::Graphics::Display::DisplayOrientations::Portrait: - rotation = DXGI_MODE_ROTATION_IDENTITY; - break; - - case winrt::Windows::Graphics::Display::DisplayOrientations::LandscapeFlipped: - rotation = DXGI_MODE_ROTATION_ROTATE270; - break; - - case winrt::Windows::Graphics::Display::DisplayOrientations::PortraitFlipped: - rotation = DXGI_MODE_ROTATION_ROTATE180; - break; - } - break; - } - return rotation; -} - -// Check for SDK Layer support. -bool DeviceResources::SdkLayersAvailable() { - #if defined(_DEBUG) - HRESULT hr = D3D11CreateDevice( - nullptr, - D3D_DRIVER_TYPE_NULL, // There is no need to create a real hardware device. - 0, - D3D11_CREATE_DEVICE_DEBUG, // Check for the SDK layers. - nullptr, // Any feature level will do. - 0, - D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Microsoft Store apps. - nullptr, // No need to keep the D3D device reference. - nullptr, // No need to know the feature level. - nullptr // No need to keep the D3D device context reference. - ); - return SUCCEEDED(hr); - #else - return false; - #endif -} - -// The first method called when the IFrameworkView is being created. -void App::Initialize(winrt::Windows::ApplicationModel::Core::CoreApplicationView const& applicationView) { - // Register event handlers for app lifecycle. This example includes Activated, so that we - // can make the CoreWindow active and start rendering on the window. - applicationView.Activated({ this, &App::OnActivated }); - - winrt::Windows::ApplicationModel::Core::CoreApplication::Suspending({ this, &App::OnSuspending }); - winrt::Windows::ApplicationModel::Core::CoreApplication::Resuming({ this, &App::OnResuming }); - - // At this point we have access to the device. - // We can create the device-dependent resources. - m_deviceResources = std::make_unique(); -} - -// Called when the CoreWindow object is created (or re-created). -void App::SetWindow(winrt::Windows::UI::Core::CoreWindow const& window) { - window.SizeChanged({ this, &App::OnWindowSizeChanged }); - window.VisibilityChanged({ this, &App::OnVisibilityChanged }); - - window.KeyDown({ this, &App::OnKeyDown }); - window.KeyUp({ this, &App::OnKeyUp }); - window.CharacterReceived({ this, &App::OnCharacterReceived }); - - window.PointerEntered({ this, &App::OnPointerEntered }); - window.PointerExited({ this, &App::OnPointerExited }); - window.PointerPressed({ this, &App::OnPointerPressed }); - window.PointerReleased({ this, &App::OnPointerReleased }); - window.PointerMoved({ this, &App::OnPointerMoved }); - window.PointerWheelChanged({ this, &App::OnPointerWheelChanged }); - - auto currentDisplayInformation = winrt::Windows::Graphics::Display::DisplayInformation::GetForCurrentView(); - - currentDisplayInformation.DpiChanged({ this, &App::OnDpiChanged }); - currentDisplayInformation.OrientationChanged({ this, &App::OnOrientationChanged }); - winrt::Windows::Graphics::Display::DisplayInformation::DisplayContentsInvalidated({ this, &App::OnDisplayContentsInvalidated }); - - winrt::Windows::UI::Core::SystemNavigationManager::GetForCurrentView().BackRequested({ this, &App::OnBackRequested }); - - m_deviceResources->SetWindow(window); -} - -// Initializes scene resources, or loads a previously saved app state. -void App::Load(winrt::hstring const& entryPoint) { - _SOKOL_UNUSED(entryPoint); -} - -// This method is called after the window becomes active. -void App::Run() { - // NOTE: UWP will simply terminate an application, it's not possible to detect when an application is being closed - while (true) { - if (m_windowVisible) { - _sapp_timing_measure(&_sapp.timing); - winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread().Dispatcher().ProcessEvents(winrt::Windows::UI::Core::CoreProcessEventsOption::ProcessAllIfPresent); - _sapp_frame(); - m_deviceResources->Present(); - } - else { - winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread().Dispatcher().ProcessEvents(winrt::Windows::UI::Core::CoreProcessEventsOption::ProcessOneAndAllPending); - } - } -} - -// Required for IFrameworkView. -// Terminate events do not cause Uninitialize to be called. It will be called if your IFrameworkView -// class is torn down while the app is in the foreground. -void App::Uninitialize() { - // empty -} - -// Application lifecycle event handlers. -void App::OnActivated(winrt::Windows::ApplicationModel::Core::CoreApplicationView const& applicationView, winrt::Windows::ApplicationModel::Activation::IActivatedEventArgs const& args) { - _SOKOL_UNUSED(args); - _SOKOL_UNUSED(applicationView); - auto appView = winrt::Windows::UI::ViewManagement::ApplicationView::GetForCurrentView(); - const float window_width = (float)_sapp_def(_sapp.desc.width, _SAPP_FALLBACK_DEFAULT_WINDOW_WIDTH); - const float window_height = (float)_sapp_def(_sapp.desc.height, _SAPP_FALLBACK_DEFAULT_WINDOW_HEIGHT); - auto targetSize = winrt::Windows::Foundation::Size(window_width, window_height); - winrt::Windows::UI::ViewManagement::ApplicationView::PreferredLaunchViewSize(targetSize); - winrt::Windows::UI::ViewManagement::ApplicationView::PreferredLaunchWindowingMode(winrt::Windows::UI::ViewManagement::ApplicationViewWindowingMode::PreferredLaunchViewSize); - appView.SetPreferredMinSize(targetSize); - appView.TryResizeView(targetSize); - - // Disabling this since it can only append the title to the app name (Title - Appname). - // There's no way of just setting a string to be the window title. - //appView.Title(_sapp.window_title_wide); - - // Run() won't start until the CoreWindow is activated. - winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread().Activate(); - if (_sapp.desc.fullscreen) { - appView.TryEnterFullScreenMode(); - } - _sapp.fullscreen = appView.IsFullScreenMode(); -} - -void App::OnSuspending(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::ApplicationModel::SuspendingEventArgs const& args) { - _SOKOL_UNUSED(sender); - _SOKOL_UNUSED(args); - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_SUSPENDED); -} - -void App::OnResuming(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::Foundation::IInspectable const& args) { - _SOKOL_UNUSED(args); - _SOKOL_UNUSED(sender); - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESUMED); -} - -void App::OnWindowSizeChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::WindowSizeChangedEventArgs const& args) { - _SOKOL_UNUSED(args); - m_deviceResources->SetLogicalSize(winrt::Windows::Foundation::Size(sender.Bounds().Width, sender.Bounds().Height)); - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); -} - -void App::OnVisibilityChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::VisibilityChangedEventArgs const& args) { - _SOKOL_UNUSED(sender); - m_windowVisible = args.Visible(); - _sapp_win32_uwp_app_event(m_windowVisible ? SAPP_EVENTTYPE_RESTORED : SAPP_EVENTTYPE_ICONIFIED); -} - -void App::OnBackRequested(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Core::BackRequestedEventArgs const& args) { - _SOKOL_UNUSED(sender); - args.Handled(true); -} - -void App::OnKeyDown(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::KeyEventArgs const& args) { - auto status = args.KeyStatus(); - _sapp_uwp_key_event(SAPP_EVENTTYPE_KEY_DOWN, sender, args); -} - -void App::OnKeyUp(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::KeyEventArgs const& args) { - auto status = args.KeyStatus(); - _sapp_uwp_key_event(SAPP_EVENTTYPE_KEY_UP, sender, args); -} - -void App::OnCharacterReceived(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::CharacterReceivedEventArgs const& args) { - _sapp_uwp_char_event(args.KeyCode(), args.KeyStatus().WasKeyDown, sender); -} - -void App::OnPointerEntered(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { - _SOKOL_UNUSED(args); - _sapp.uwp.mouse_tracked = true; - _sapp_uwp_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, sender); -} - -void App::OnPointerExited(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { - _SOKOL_UNUSED(args); - _sapp.uwp.mouse_tracked = false; - _sapp_uwp_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, sender); -} - -void App::OnPointerPressed(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { - _sapp_uwp_extract_mouse_button_events(sender, args); -} - -// NOTE: for some reason this event handler is never called?? -void App::OnPointerReleased(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { - _sapp_uwp_extract_mouse_button_events(sender, args); -} - -void App::OnPointerMoved(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { - auto position = args.CurrentPoint().Position(); - const float new_x = (float)(int)(position.X * _sapp.uwp.dpi.mouse_scale + 0.5f); - const float new_y = (float)(int)(position.Y * _sapp.uwp.dpi.mouse_scale + 0.5f); - // 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.uwp.mouse_tracked) { - _sapp.uwp.mouse_tracked = true; - _sapp_uwp_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, sender); - } - _sapp_uwp_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, sender); - - // HACK for detecting multiple mouse button presses - _sapp_uwp_extract_mouse_button_events(sender, args); -} - -void App::OnPointerWheelChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { - auto properties = args.CurrentPoint().Properties(); - _sapp_uwp_scroll_event((float)properties.MouseWheelDelta(), properties.IsHorizontalMouseWheel(), sender); -} - -void App::OnDpiChanged(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args) { - // NOTE: UNTESTED - _SOKOL_UNUSED(args); - m_deviceResources->SetDpi(sender.LogicalDpi()); - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); -} - -void App::OnOrientationChanged(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args) { - // NOTE: UNTESTED - _SOKOL_UNUSED(args); - m_deviceResources->SetCurrentOrientation(sender.CurrentOrientation()); - _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); -} - -void App::OnDisplayContentsInvalidated(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args) { - // NOTE: UNTESTED - _SOKOL_UNUSED(args); - _SOKOL_UNUSED(sender); - m_deviceResources->ValidateDevice(); -} - -} /* End empty namespace */ - -_SOKOL_PRIVATE void _sapp_uwp_run(const sapp_desc* desc) { - _sapp_init_state(desc); - _sapp_win32_uwp_init_keytable(); - _sapp_win32_uwp_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide)); - winrt::Windows::ApplicationModel::Core::CoreApplication::Run(winrt::make()); -} - -#if !defined(SOKOL_NO_ENTRY) -#if defined(UNICODE) -int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { -#else -int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) { -#endif - _SOKOL_UNUSED(hInstance); - _SOKOL_UNUSED(hPrevInstance); - _SOKOL_UNUSED(lpCmdLine); - _SOKOL_UNUSED(nCmdShow); - sapp_desc desc = sokol_main(0, nullptr); - _sapp_uwp_run(&desc); - return 0; -} -#endif /* SOKOL_NO_ENTRY */ -#endif /* _SAPP_UWP */ - -/*== Android ================================================================*/ +// █████ ███ ██ ██████ ██████ ██████ ██ ██████ +// ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ██ ██ ██ ██████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ████ ██████ ██ ██ ██████ ██ ██████ +// +// >>android #if defined(_SAPP_ANDROID) /* android loop thread */ @@ -8851,16 +8059,10 @@ _SOKOL_PRIVATE bool _sapp_android_init_egl(void) { if (eglInitialize(display, NULL, NULL) == EGL_FALSE) { return false; } - _sapp.gles2_fallback = _sapp.desc.gl_force_gles2; - EGLint alpha_size = _sapp.desc.alpha ? 8 : 0; const EGLint cfg_attributes[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - #if defined(SOKOL_GLES3) - EGL_RENDERABLE_TYPE, _sapp.desc.gl_force_gles2?EGL_OPENGL_ES2_BIT:EGL_OPENGL_ES3_BIT, - #else - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - #endif + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, @@ -8897,11 +8099,7 @@ _SOKOL_PRIVATE bool _sapp_android_init_egl(void) { } EGLint ctx_attributes[] = { - #if defined(SOKOL_GLES3) - EGL_CONTEXT_CLIENT_VERSION, _sapp.desc.gl_force_gles2 ? 2 : 3, - #else - EGL_CONTEXT_CLIENT_VERSION, 2, - #endif + EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE, }; EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, ctx_attributes); @@ -8919,16 +8117,13 @@ _SOKOL_PRIVATE void _sapp_android_cleanup_egl(void) { 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) { - SAPP_LOG("Destroying egl surface"); eglDestroySurface(_sapp.android.display, _sapp.android.surface); _sapp.android.surface = EGL_NO_SURFACE; } if (_sapp.android.context != EGL_NO_CONTEXT) { - SAPP_LOG("Destroying egl context"); eglDestroyContext(_sapp.android.display, _sapp.android.context); _sapp.android.context = EGL_NO_CONTEXT; } - SAPP_LOG("Terminating egl display"); eglTerminate(_sapp.android.display); _sapp.android.display = EGL_NO_DISPLAY; } @@ -8969,7 +8164,6 @@ _SOKOL_PRIVATE void _sapp_android_cleanup_egl_surface(void) { _SOKOL_PRIVATE void _sapp_android_app_event(sapp_event_type type) { if (_sapp_events_enabled()) { _sapp_init_event(type); - SAPP_LOG("event_cb()"); _sapp_call_event(&_sapp.event); } } @@ -9015,18 +8209,15 @@ _SOKOL_PRIVATE void _sapp_android_update_dimensions(ANativeWindow* window, bool _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float)_sapp.window_width; if (win_changed || fb_changed || force_update) { if (!_sapp.first_frame) { - SAPP_LOG("SAPP_EVENTTYPE_RESIZED"); _sapp_android_app_event(SAPP_EVENTTYPE_RESIZED); } } } _SOKOL_PRIVATE void _sapp_android_cleanup(void) { - SAPP_LOG("Cleaning up"); if (_sapp.android.surface != EGL_NO_SURFACE) { /* egl context is bound, cleanup gracefully */ if (_sapp.init_called && !_sapp.cleanup_called) { - SAPP_LOG("cleanup_cb()"); _sapp_call_cleanup(); } } @@ -9063,22 +8254,17 @@ _SOKOL_PRIVATE bool _sapp_android_touch_event(const AInputEvent* e) { sapp_event_type type = SAPP_EVENTTYPE_INVALID; switch (action) { case AMOTION_EVENT_ACTION_DOWN: - SAPP_LOG("Touch: down"); case AMOTION_EVENT_ACTION_POINTER_DOWN: - SAPP_LOG("Touch: ptr down"); type = SAPP_EVENTTYPE_TOUCHES_BEGAN; break; case AMOTION_EVENT_ACTION_MOVE: type = SAPP_EVENTTYPE_TOUCHES_MOVED; break; case AMOTION_EVENT_ACTION_UP: - SAPP_LOG("Touch: up"); case AMOTION_EVENT_ACTION_POINTER_UP: - SAPP_LOG("Touch: ptr up"); type = SAPP_EVENTTYPE_TOUCHES_ENDED; break; case AMOTION_EVENT_ACTION_CANCEL: - SAPP_LOG("Touch: cancel"); type = SAPP_EVENTTYPE_TOUCHES_CANCELLED; break; default: @@ -9129,7 +8315,7 @@ _SOKOL_PRIVATE int _sapp_android_input_cb(int fd, int events, void* data) { _SOKOL_UNUSED(fd); _SOKOL_UNUSED(data); if ((events & ALOOPER_EVENT_INPUT) == 0) { - SAPP_LOG("_sapp_android_input_cb() encountered unsupported event"); + _SAPP_ERROR(ANDROID_UNSUPPORTED_INPUT_EVENT_INPUT_CB); return 1; } SOKOL_ASSERT(_sapp.android.current.input); @@ -9150,13 +8336,13 @@ _SOKOL_PRIVATE int _sapp_android_input_cb(int fd, int events, void* data) { _SOKOL_PRIVATE int _sapp_android_main_cb(int fd, int events, void* data) { _SOKOL_UNUSED(data); if ((events & ALOOPER_EVENT_INPUT) == 0) { - SAPP_LOG("_sapp_android_main_cb() encountered unsupported event"); + _SAPP_ERROR(ANDROID_UNSUPPORTED_INPUT_EVENT_MAIN_CB); return 1; } _sapp_android_msg_t msg; if (read(fd, &msg, sizeof(msg)) != sizeof(msg)) { - SAPP_LOG("Could not write to read_from_main_fd"); + _SAPP_ERROR(ANDROID_READ_MSG_FAILED); return 1; } @@ -9164,7 +8350,7 @@ _SOKOL_PRIVATE int _sapp_android_main_cb(int fd, int events, void* data) { switch (msg) { case _SOKOL_ANDROID_MSG_CREATE: { - SAPP_LOG("MSG_CREATE"); + _SAPP_INFO(ANDROID_MSG_CREATE); SOKOL_ASSERT(!_sapp.valid); bool result = _sapp_android_init_egl(); SOKOL_ASSERT(result); _SOKOL_UNUSED(result); @@ -9173,36 +8359,33 @@ _SOKOL_PRIVATE int _sapp_android_main_cb(int fd, int events, void* data) { } break; case _SOKOL_ANDROID_MSG_RESUME: - SAPP_LOG("MSG_RESUME"); + _SAPP_INFO(ANDROID_MSG_RESUME); _sapp.android.has_resumed = true; _sapp_android_app_event(SAPP_EVENTTYPE_RESUMED); break; case _SOKOL_ANDROID_MSG_PAUSE: - SAPP_LOG("MSG_PAUSE"); + _SAPP_INFO(ANDROID_MSG_PAUSE); _sapp.android.has_resumed = false; _sapp_android_app_event(SAPP_EVENTTYPE_SUSPENDED); break; case _SOKOL_ANDROID_MSG_FOCUS: - SAPP_LOG("MSG_FOCUS"); + _SAPP_INFO(ANDROID_MSG_FOCUS); _sapp.android.has_focus = true; break; case _SOKOL_ANDROID_MSG_NO_FOCUS: - SAPP_LOG("MSG_NO_FOCUS"); + _SAPP_INFO(ANDROID_MSG_NO_FOCUS); _sapp.android.has_focus = false; break; case _SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW: - SAPP_LOG("MSG_SET_NATIVE_WINDOW"); + _SAPP_INFO(ANDROID_MSG_SET_NATIVE_WINDOW); if (_sapp.android.current.window != _sapp.android.pending.window) { if (_sapp.android.current.window != NULL) { _sapp_android_cleanup_egl_surface(); } if (_sapp.android.pending.window != NULL) { - SAPP_LOG("Creating egl surface ..."); if (_sapp_android_init_egl_surface(_sapp.android.pending.window)) { - SAPP_LOG("... ok!"); _sapp_android_update_dimensions(_sapp.android.pending.window, true); } else { - SAPP_LOG("... failed!"); _sapp_android_shutdown(); } } @@ -9210,7 +8393,7 @@ _SOKOL_PRIVATE int _sapp_android_main_cb(int fd, int events, void* data) { _sapp.android.current.window = _sapp.android.pending.window; break; case _SOKOL_ANDROID_MSG_SET_INPUT_QUEUE: - SAPP_LOG("MSG_SET_INPUT_QUEUE"); + _SAPP_INFO(ANDROID_MSG_SET_INPUT_QUEUE); if (_sapp.android.current.input != _sapp.android.pending.input) { if (_sapp.android.current.input != NULL) { AInputQueue_detachLooper(_sapp.android.current.input); @@ -9227,13 +8410,13 @@ _SOKOL_PRIVATE int _sapp_android_main_cb(int fd, int events, void* data) { _sapp.android.current.input = _sapp.android.pending.input; break; case _SOKOL_ANDROID_MSG_DESTROY: - SAPP_LOG("MSG_DESTROY"); + _SAPP_INFO(ANDROID_MSG_DESTROY); _sapp_android_cleanup(); _sapp.valid = false; _sapp.android.is_thread_stopping = true; break; default: - SAPP_LOG("Unknown msg type received"); + _SAPP_WARN(ANDROID_UNKNOWN_MSG); break; } pthread_cond_broadcast(&_sapp.android.pt.cond); /* signal "received" */ @@ -9251,17 +8434,15 @@ _SOKOL_PRIVATE void _sapp_android_show_keyboard(bool shown) { SOKOL_ASSERT(_sapp.valid); /* This seems to be broken in the NDK, but there is (a very cumbersome) workaround... */ if (shown) { - SAPP_LOG("Showing keyboard"); ANativeActivity_showSoftInput(_sapp.android.activity, ANATIVEACTIVITY_SHOW_SOFT_INPUT_FORCED); } else { - SAPP_LOG("Hiding keyboard"); ANativeActivity_hideSoftInput(_sapp.android.activity, ANATIVEACTIVITY_HIDE_SOFT_INPUT_NOT_ALWAYS); } } _SOKOL_PRIVATE void* _sapp_android_loop(void* arg) { _SOKOL_UNUSED(arg); - SAPP_LOG("Loop thread started"); + _SAPP_INFO(ANDROID_LOOP_THREAD_STARTED); _sapp.android.looper = ALooper_prepare(0 /* or ALOOPER_PREPARE_ALLOW_NON_CALLBACKS*/); ALooper_addFd(_sapp.android.looper, @@ -9306,38 +8487,39 @@ _SOKOL_PRIVATE void* _sapp_android_loop(void* arg) { _sapp.android.is_thread_stopped = true; pthread_cond_broadcast(&_sapp.android.pt.cond); pthread_mutex_unlock(&_sapp.android.pt.mutex); - SAPP_LOG("Loop thread done"); + + _SAPP_INFO(ANDROID_LOOP_THREAD_DONE); return NULL; } /* android main/ui thread */ _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)) { - SAPP_LOG("Could not write to write_from_main_fd"); + _SAPP_ERROR(ANDROID_WRITE_MSG_FAILED); } } _SOKOL_PRIVATE void _sapp_android_on_start(ANativeActivity* activity) { _SOKOL_UNUSED(activity); - SAPP_LOG("NativeActivity onStart()"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONSTART); } _SOKOL_PRIVATE void _sapp_android_on_resume(ANativeActivity* activity) { _SOKOL_UNUSED(activity); - SAPP_LOG("NativeActivity onResume()"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONRESUME); _sapp_android_msg(_SOKOL_ANDROID_MSG_RESUME); } _SOKOL_PRIVATE void* _sapp_android_on_save_instance_state(ANativeActivity* activity, size_t* out_size) { _SOKOL_UNUSED(activity); - SAPP_LOG("NativeActivity onSaveInstanceState()"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONSAVEINSTANCESTATE); *out_size = 0; return NULL; } _SOKOL_PRIVATE void _sapp_android_on_window_focus_changed(ANativeActivity* activity, int has_focus) { _SOKOL_UNUSED(activity); - SAPP_LOG("NativeActivity onWindowFocusChanged()"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONWINDOWFOCUSCHANGED); if (has_focus) { _sapp_android_msg(_SOKOL_ANDROID_MSG_FOCUS); } else { @@ -9347,13 +8529,13 @@ _SOKOL_PRIVATE void _sapp_android_on_window_focus_changed(ANativeActivity* activ _SOKOL_PRIVATE void _sapp_android_on_pause(ANativeActivity* activity) { _SOKOL_UNUSED(activity); - SAPP_LOG("NativeActivity onPause()"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONPAUSE); _sapp_android_msg(_SOKOL_ANDROID_MSG_PAUSE); } _SOKOL_PRIVATE void _sapp_android_on_stop(ANativeActivity* activity) { _SOKOL_UNUSED(activity); - SAPP_LOG("NativeActivity onStop()"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONSTOP); } _SOKOL_PRIVATE void _sapp_android_msg_set_native_window(ANativeWindow* window) { @@ -9368,14 +8550,14 @@ _SOKOL_PRIVATE void _sapp_android_msg_set_native_window(ANativeWindow* window) { _SOKOL_PRIVATE void _sapp_android_on_native_window_created(ANativeActivity* activity, ANativeWindow* window) { _SOKOL_UNUSED(activity); - SAPP_LOG("NativeActivity onNativeWindowCreated()"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONNATIVEWINDOWCREATED); _sapp_android_msg_set_native_window(window); } _SOKOL_PRIVATE void _sapp_android_on_native_window_destroyed(ANativeActivity* activity, ANativeWindow* window) { _SOKOL_UNUSED(activity); _SOKOL_UNUSED(window); - SAPP_LOG("NativeActivity onNativeWindowDestroyed()"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONNATIVEWINDOWDESTROYED); _sapp_android_msg_set_native_window(NULL); } @@ -9391,26 +8573,26 @@ _SOKOL_PRIVATE void _sapp_android_msg_set_input_queue(AInputQueue* input) { _SOKOL_PRIVATE void _sapp_android_on_input_queue_created(ANativeActivity* activity, AInputQueue* queue) { _SOKOL_UNUSED(activity); - SAPP_LOG("NativeActivity onInputQueueCreated()"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONINPUTQUEUECREATED); _sapp_android_msg_set_input_queue(queue); } _SOKOL_PRIVATE void _sapp_android_on_input_queue_destroyed(ANativeActivity* activity, AInputQueue* queue) { _SOKOL_UNUSED(activity); _SOKOL_UNUSED(queue); - SAPP_LOG("NativeActivity onInputQueueDestroyed()"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONINPUTQUEUEDESTROYED); _sapp_android_msg_set_input_queue(NULL); } _SOKOL_PRIVATE void _sapp_android_on_config_changed(ANativeActivity* activity) { _SOKOL_UNUSED(activity); - SAPP_LOG("NativeActivity onConfigurationChanged()"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONCONFIGURATIONCHANGED); /* see android:configChanges in manifest */ } _SOKOL_PRIVATE void _sapp_android_on_low_memory(ANativeActivity* activity) { _SOKOL_UNUSED(activity); - SAPP_LOG("NativeActivity onLowMemory()"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONLOWMEMORY); } _SOKOL_PRIVATE void _sapp_android_on_destroy(ANativeActivity* activity) { @@ -9423,7 +8605,7 @@ _SOKOL_PRIVATE void _sapp_android_on_destroy(ANativeActivity* activity) { * _sapp_android_on_stop(), the crash disappears. Is this a bug in NativeActivity? */ _SOKOL_UNUSED(activity); - SAPP_LOG("NativeActivity onDestroy()"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONDESTROY); /* send destroy msg */ pthread_mutex_lock(&_sapp.android.pt.mutex); @@ -9440,7 +8622,7 @@ _SOKOL_PRIVATE void _sapp_android_on_destroy(ANativeActivity* activity) { close(_sapp.android.pt.read_from_main_fd); close(_sapp.android.pt.write_from_main_fd); - SAPP_LOG("NativeActivity done"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_DONE); /* this is a bit naughty, but causes a clean restart of the app (static globals are reset) */ exit(0); @@ -9450,7 +8632,7 @@ JNIEXPORT void ANativeActivity_onCreate(ANativeActivity* activity, void* saved_state, size_t saved_state_size) { _SOKOL_UNUSED(saved_state); _SOKOL_UNUSED(saved_state_size); - SAPP_LOG("NativeActivity onCreate()"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_ONCREATE); // the NativeActity pointer needs to be available inside sokol_main() // (see https://github.com/floooh/sokol/issues/708), however _sapp_init_state() @@ -9464,7 +8646,7 @@ void ANativeActivity_onCreate(ANativeActivity* activity, void* saved_state, size int pipe_fd[2]; if (pipe(pipe_fd) != 0) { - SAPP_LOG("Could not create thread pipe"); + _SAPP_ERROR(ANDROID_CREATE_THREAD_PIPE_FAILED); return; } _sapp.android.pt.read_from_main_fd = pipe_fd[0]; @@ -9512,14 +8694,20 @@ void ANativeActivity_onCreate(ANativeActivity* activity, void* saved_state, size activity->callbacks->onConfigurationChanged = _sapp_android_on_config_changed; activity->callbacks->onLowMemory = _sapp_android_on_low_memory; - SAPP_LOG("NativeActivity successfully created"); + _SAPP_INFO(ANDROID_NATIVE_ACTIVITY_CREATE_SUCCESS); /* NOT A BUG: do NOT call sapp_discard_state() */ } #endif /* _SAPP_ANDROID */ -/*== LINUX ==================================================================*/ +// ██ ██ ███ ██ ██ ██ ██ ██ +// ██ ██ ████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ████ ██████ ██ ██ +// +// >>linux #if defined(_SAPP_LINUX) /* see GLFW's xkb_unicode.c */ @@ -10438,7 +9626,7 @@ _SOKOL_PRIVATE void _sapp_x11_query_system_dpi(void) { // fallback if querying DPI had failed: assume the standard DPI 96.0f if (!dpi_ok) { _sapp.x11.dpi = 96.0f; - SAPP_LOG("sokol_app.h: failed to query system dpi value, assuming default 96.0"); + _SAPP_WARN(LINUX_X11_QUERY_SYSTEM_DPI_FAILED); } } @@ -10494,7 +9682,7 @@ _SOKOL_PRIVATE void _sapp_glx_init() { } } if (!_sapp.glx.libgl) { - _sapp_fail("GLX: failed to load libGL"); + _SAPP_PANIC(LINUX_GLX_LOAD_LIBGL_FAILED); } _sapp.glx.GetFBConfigs = (PFNGLXGETFBCONFIGSPROC) dlsym(_sapp.glx.libgl, "glXGetFBConfigs"); _sapp.glx.GetFBConfigAttrib = (PFNGLXGETFBCONFIGATTRIBPROC) dlsym(_sapp.glx.libgl, "glXGetFBConfigAttrib"); @@ -10525,17 +9713,17 @@ _SOKOL_PRIVATE void _sapp_glx_init() { !_sapp.glx.GetProcAddressARB || !_sapp.glx.GetVisualFromFBConfig) { - _sapp_fail("GLX: failed to load required entry points"); + _SAPP_PANIC(LINUX_GLX_LOAD_ENTRY_POINTS_FAILED); } if (!_sapp.glx.QueryExtension(_sapp.x11.display, &_sapp.glx.error_base, &_sapp.glx.event_base)) { - _sapp_fail("GLX: GLX extension not found"); + _SAPP_PANIC(LINUX_GLX_EXTENSION_NOT_FOUND); } if (!_sapp.glx.QueryVersion(_sapp.x11.display, &_sapp.glx.major, &_sapp.glx.minor)) { - _sapp_fail("GLX: Failed to query GLX version"); + _SAPP_PANIC(LINUX_GLX_QUERY_VERSION_FAILED); } if (_sapp.glx.major == 1 && _sapp.glx.minor < 3) { - _sapp_fail("GLX: GLX version 1.3 is required"); + _SAPP_PANIC(LINUX_GLX_VERSION_TOO_LOW); } const char* exts = _sapp.glx.QueryExtensionsString(_sapp.x11.display, _sapp.x11.screen); if (_sapp_glx_extsupported("GLX_EXT_swap_control", exts)) { @@ -10578,7 +9766,7 @@ _SOKOL_PRIVATE GLXFBConfig _sapp_glx_choosefbconfig() { native_configs = _sapp.glx.GetFBConfigs(_sapp.x11.display, _sapp.x11.screen, &native_count); if (!native_configs || !native_count) { - _sapp_fail("GLX: No GLXFBConfigs returned"); + _SAPP_PANIC(LINUX_GLX_NO_GLXFBCONFIGS); } usable_configs = (_sapp_gl_fbconfig*) _sapp_malloc_clear((size_t)native_count * sizeof(_sapp_gl_fbconfig)); @@ -10636,11 +9824,11 @@ _SOKOL_PRIVATE GLXFBConfig _sapp_glx_choosefbconfig() { _SOKOL_PRIVATE void _sapp_glx_choose_visual(Visual** visual, int* depth) { GLXFBConfig native = _sapp_glx_choosefbconfig(); if (0 == native) { - _sapp_fail("GLX: Failed to find a suitable GLXFBConfig"); + _SAPP_PANIC(LINUX_GLX_NO_SUITABLE_GLXFBCONFIG); } XVisualInfo* result = _sapp.glx.GetVisualFromFBConfig(_sapp.x11.display, native); if (!result) { - _sapp_fail("GLX: Failed to retrieve Visual for GLXFBConfig"); + _SAPP_PANIC(LINUX_GLX_GET_VISUAL_FROM_FBCONFIG_FAILED); } *visual = result->visual; *depth = result->depth; @@ -10650,10 +9838,10 @@ _SOKOL_PRIVATE void _sapp_glx_choose_visual(Visual** visual, int* depth) { _SOKOL_PRIVATE void _sapp_glx_create_context(void) { GLXFBConfig native = _sapp_glx_choosefbconfig(); if (0 == native){ - _sapp_fail("GLX: Failed to find a suitable GLXFBConfig (2)"); + _SAPP_PANIC(LINUX_GLX_NO_SUITABLE_GLXFBCONFIG); } 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_PANIC(LINUX_GLX_REQUIRED_EXTENSIONS_MISSING); } _sapp_x11_grab_error_handler(); const int attribs[] = { @@ -10665,12 +9853,12 @@ _SOKOL_PRIVATE void _sapp_glx_create_context(void) { }; _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_PANIC(LINUX_GLX_CREATE_CONTEXT_FAILED); } _sapp_x11_release_error_handler(); _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"); + _SAPP_PANIC(LINUX_GLX_CREATE_WINDOW_FAILED); } } @@ -10975,7 +10163,7 @@ _SOKOL_PRIVATE void _sapp_x11_create_window(Visual* visual, int depth) { &wa); _sapp_x11_release_error_handler(); if (!_sapp.x11.window) { - _sapp_fail("X11: Failed to create window"); + _SAPP_PANIC(LINUX_X11_CREATE_WINDOW_FAILED); } Atom protocols[] = { _sapp.x11.WM_DELETE_WINDOW @@ -11138,6 +10326,23 @@ _SOKOL_PRIVATE sapp_mousebutton _sapp_x11_translate_button(const XEvent* event) } } +_SOKOL_PRIVATE void _sapp_x11_mouse_update(int x, int y, bool clear_dxdy) { + if (!_sapp.mouse.locked) { + const float new_x = (float) x; + const float new_y = (float) y; + if (clear_dxdy) { + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; + } else 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_x11_mouse_event(sapp_event_type type, sapp_mousebutton btn, uint32_t mods) { if (_sapp_events_enabled()) { _sapp_init_event(type); @@ -11387,7 +10592,7 @@ _SOKOL_PRIVATE bool _sapp_x11_parse_dropped_files_list(const char* src) { ((src_count == 6) && (src_chr != '/')) || ((src_count == 7) && (src_chr != '/'))) { - SAPP_LOG("sokol_app.h: dropped file URI doesn't start with file://"); + _SAPP_ERROR(LINUX_X11_DROPPED_FILE_URI_WRONG_SCHEME); err = true; break; } @@ -11420,7 +10625,7 @@ _SOKOL_PRIVATE bool _sapp_x11_parse_dropped_files_list(const char* src) { *dst_ptr++ = dst_chr; } else { - SAPP_LOG("sokol_app.h: dropped file path too long (sapp_desc.max_dropped_file_path_length)"); + _SAPP_ERROR(DROPPED_FILE_PATH_TOO_LONG); err = true; break; } @@ -11467,7 +10672,7 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { } break; case FocusIn: - // NOTE: ingnoring NotifyGrab and NotifyUngrab is same behaviour as GLFW + // NOTE: ignoring NotifyGrab and NotifyUngrab is same behaviour as GLFW if ((event->xfocus.mode != NotifyGrab) && (event->xfocus.mode != NotifyUngrab)) { _sapp_x11_app_event(SAPP_EVENTTYPE_FOCUSED); } @@ -11477,7 +10682,7 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { if (_sapp.mouse.locked) { _sapp_x11_lock_mouse(false); } - // NOTE: ingnoring NotifyGrab and NotifyUngrab is same behaviour as GLFW + // NOTE: ignoring NotifyGrab and NotifyUngrab is same behaviour as GLFW if ((event->xfocus.mode != NotifyGrab) && (event->xfocus.mode != NotifyUngrab)) { _sapp_x11_app_event(SAPP_EVENTTYPE_UNFOCUSED); } @@ -11517,6 +10722,7 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { break; case ButtonPress: { + _sapp_x11_mouse_update(event->xbutton.x, event->xbutton.y, false); const sapp_mousebutton btn = _sapp_x11_translate_button(event); uint32_t mods = _sapp_x11_mods(event->xbutton.state); // X11 doesn't set modifier bit on button down, so emulate that @@ -11538,6 +10744,7 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { break; case ButtonRelease: { + _sapp_x11_mouse_update(event->xbutton.x, event->xbutton.y, false); const sapp_mousebutton btn = _sapp_x11_translate_button(event); if (btn != SAPP_MOUSEBUTTON_INVALID) { uint32_t mods = _sapp_x11_mods(event->xbutton.state); @@ -11551,25 +10758,19 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { case EnterNotify: /* don't send enter/leave events while mouse button held down */ if (0 == _sapp.x11.mouse_buttons) { + _sapp_x11_mouse_update(event->xcrossing.x, event->xcrossing.y, true); _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xcrossing.state)); } break; case LeaveNotify: if (0 == _sapp.x11.mouse_buttons) { + _sapp_x11_mouse_update(event->xcrossing.x, event->xcrossing.y, true); _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xcrossing.state)); } break; case MotionNotify: 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_update(event->xmotion.x, event->xmotion.y, false); _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mods(event->xmotion.state)); } break; @@ -11655,7 +10856,7 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { XEvent reply; _sapp_clear(&reply, sizeof(reply)); reply.type = ClientMessage; - reply.xclient.window = _sapp.x11.window; + reply.xclient.window = _sapp.x11.xdnd.source; reply.xclient.message_type = _sapp.x11.xdnd.XdndFinished; reply.xclient.format = 32; reply.xclient.data.l[0] = (long)_sapp.x11.window; @@ -11700,7 +10901,12 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { (unsigned char**) &data); if (_sapp.drop.enabled && result) { if (_sapp_x11_parse_dropped_files_list(data)) { + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; if (_sapp_events_enabled()) { + // FIXME: Figure out how to get modifier key state here. + // The XSelection event has no 'state' item, and + // XQueryKeymap() always returns a zeroed array. _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); _sapp_call_event(&_sapp.event); } @@ -11710,7 +10916,7 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { XEvent reply; _sapp_clear(&reply, sizeof(reply)); reply.type = ClientMessage; - reply.xclient.window = _sapp.x11.window; + reply.xclient.window = _sapp.x11.xdnd.source; reply.xclient.message_type = _sapp.x11.xdnd.XdndFinished; reply.xclient.format = 32; reply.xclient.data.l[0] = (long)_sapp.x11.window; @@ -11731,22 +10937,22 @@ _SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { _SOKOL_PRIVATE void _sapp_egl_init(void) { #if defined(SOKOL_GLCORE33) if (!eglBindAPI(EGL_OPENGL_API)) { - _sapp_fail("EGL: failed to bind API"); + _SAPP_PANIC(LINUX_EGL_BIND_OPENGL_API_FAILED); } #else if (!eglBindAPI(EGL_OPENGL_ES_API)) { - _sapp_fail("EGL: failed to bind API"); + _SAPP_PANIC(LINUX_EGL_BIND_OPENGL_ES_API_FAILED); } #endif _sapp.egl.display = eglGetDisplay((EGLNativeDisplayType)_sapp.x11.display); if (EGL_NO_DISPLAY == _sapp.egl.display) { - _sapp_fail("EGL: failed to get display"); + _SAPP_PANIC(LINUX_EGL_GET_DISPLAY_FAILED); } EGLint major, minor; if (!eglInitialize(_sapp.egl.display, &major, &minor)) { - _sapp_fail("EGL: failed to initialize"); + _SAPP_PANIC(LINUX_EGL_INITIALIZE_FAILED); } EGLint sample_count = _sapp.desc.sample_count > 1 ? _sapp.desc.sample_count : 0; @@ -11756,9 +10962,7 @@ _SOKOL_PRIVATE void _sapp_egl_init(void) { #if defined(SOKOL_GLCORE33) EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, #elif defined(SOKOL_GLES3) - EGL_RENDERABLE_TYPE, _sapp.desc.gl_force_gles2 ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_ES3_BIT, - #else - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, #endif EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, @@ -11774,7 +10978,7 @@ _SOKOL_PRIVATE void _sapp_egl_init(void) { EGLConfig egl_configs[32]; EGLint config_count; if (!eglChooseConfig(_sapp.egl.display, config_attrs, egl_configs, 32, &config_count) || config_count == 0) { - _sapp_fail("EGL: no available configs"); + _SAPP_PANIC(LINUX_EGL_NO_CONFIGS); } EGLConfig config = egl_configs[0]; @@ -11796,7 +11000,7 @@ _SOKOL_PRIVATE void _sapp_egl_init(void) { EGLint visual_id; if (!eglGetConfigAttrib(_sapp.egl.display, config, EGL_NATIVE_VISUAL_ID, &visual_id)) { - _sapp_fail("EGL: failed to get native visual"); + _SAPP_PANIC(LINUX_EGL_NO_NATIVE_VISUAL); } XVisualInfo visual_info_template; @@ -11806,7 +11010,7 @@ _SOKOL_PRIVATE void _sapp_egl_init(void) { int num_visuals; XVisualInfo* visual_info = XGetVisualInfo(_sapp.x11.display, VisualIDMask, &visual_info_template, &num_visuals); if (!visual_info) { - _sapp_fail("EGL: failed to get x11 visual"); + _SAPP_PANIC(LINUX_EGL_GET_VISUAL_INFO_FAILED); } _sapp_x11_create_window(visual_info->visual, visual_info->depth); @@ -11814,7 +11018,7 @@ _SOKOL_PRIVATE void _sapp_egl_init(void) { _sapp.egl.surface = eglCreateWindowSurface(_sapp.egl.display, config, (EGLNativeWindowType)_sapp.x11.window, NULL); if (EGL_NO_SURFACE == _sapp.egl.surface) { - _sapp_fail("EGL: failed to create EGL surface"); + _SAPP_PANIC(LINUX_EGL_CREATE_WINDOW_SURFACE_FAILED); } EGLint ctx_attrs[] = { @@ -11823,27 +11027,21 @@ _SOKOL_PRIVATE void _sapp_egl_init(void) { EGL_CONTEXT_MINOR_VERSION, _sapp.desc.gl_minor_version, EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, #elif defined(SOKOL_GLES3) - EGL_CONTEXT_CLIENT_VERSION, _sapp.desc.gl_force_gles2 ? 2 : 3, - #else - EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_CONTEXT_CLIENT_VERSION, 3, #endif EGL_NONE, }; _sapp.egl.context = eglCreateContext(_sapp.egl.display, config, EGL_NO_CONTEXT, ctx_attrs); if (EGL_NO_CONTEXT == _sapp.egl.context) { - _sapp_fail("EGL: failed to create GL context"); + _SAPP_PANIC(LINUX_EGL_CREATE_CONTEXT_FAILED); } if (!eglMakeCurrent(_sapp.egl.display, _sapp.egl.surface, _sapp.egl.surface, _sapp.egl.context)) { - _sapp_fail("EGL: failed to set current context"); + _SAPP_PANIC(LINUX_EGL_MAKE_CURRENT_FAILED); } eglSwapInterval(_sapp.egl.display, _sapp.swap_interval); - -#if defined(SOKOL_GLES3) - _sapp.gles2_fallback = _sapp.desc.gl_force_gles2; -#endif } _SOKOL_PRIVATE void _sapp_egl_destroy(void) { @@ -11883,7 +11081,7 @@ _SOKOL_PRIVATE void _sapp_linux_run(const sapp_desc* desc) { XrmInitialize(); _sapp.x11.display = XOpenDisplay(NULL); if (!_sapp.x11.display) { - _sapp_fail("XOpenDisplay() failed!\n"); + _SAPP_PANIC(LINUX_X11_OPEN_DISPLAY_FAILED); } _sapp.x11.screen = DefaultScreen(_sapp.x11.display); _sapp.x11.root = DefaultRootWindow(_sapp.x11.display); @@ -11957,7 +11155,13 @@ int main(int argc, char* argv[]) { #endif /* SOKOL_NO_ENTRY */ #endif /* _SAPP_LINUX */ -/*== PUBLIC API FUNCTIONS ====================================================*/ +// ██████ ██ ██ ██████ ██ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ ██ ██████ +// +// >>public #if defined(SOKOL_NO_ENTRY) SOKOL_API_IMPL void sapp_run(const sapp_desc* desc) { SOKOL_ASSERT(desc); @@ -11969,13 +11173,10 @@ SOKOL_API_IMPL void sapp_run(const sapp_desc* desc) { _sapp_emsc_run(desc); #elif defined(_SAPP_WIN32) _sapp_win32_run(desc); - #elif defined(_SAPP_UWP) - _sapp_uwp_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!"); + #error "sapp_run() not supported on this platform" #endif } @@ -12064,7 +11265,7 @@ SOKOL_API_IMPL float sapp_dpi_scale(void) { return _sapp.dpi_scale; } -SOKOL_APP_IMPL const void* sapp_egl_get_display(void) { +SOKOL_API_IMPL const void* sapp_egl_get_display(void) { SOKOL_ASSERT(_sapp.valid); #if defined(_SAPP_ANDROID) return _sapp.android.display; @@ -12075,7 +11276,7 @@ SOKOL_APP_IMPL const void* sapp_egl_get_display(void) { #endif } -SOKOL_APP_IMPL const void* sapp_egl_get_context(void) { +SOKOL_API_IMPL const void* sapp_egl_get_context(void) { SOKOL_ASSERT(_sapp.valid); #if defined(_SAPP_ANDROID) return _sapp.android.context; @@ -12086,10 +11287,6 @@ SOKOL_APP_IMPL const void* sapp_egl_get_context(void) { #endif } -SOKOL_API_IMPL bool sapp_gles2(void) { - return _sapp.gles2_fallback; -} - SOKOL_API_IMPL void sapp_show_keyboard(bool show) { #if defined(_SAPP_IOS) _sapp_ios_show_keyboard(show); @@ -12115,8 +11312,6 @@ SOKOL_API_IMPL void sapp_toggle_fullscreen(void) { _sapp_macos_toggle_fullscreen(); #elif defined(_SAPP_WIN32) _sapp_win32_toggle_fullscreen(); - #elif defined(_SAPP_UWP) - _sapp_uwp_toggle_fullscreen(); #elif defined(_SAPP_LINUX) _sapp_x11_toggle_fullscreen(); #endif @@ -12131,8 +11326,6 @@ SOKOL_API_IMPL void sapp_show_mouse(bool show) { _sapp_win32_update_cursor(_sapp.mouse.current_cursor, show, false); #elif defined(_SAPP_LINUX) _sapp_x11_update_cursor(_sapp.mouse.current_cursor, show); - #elif defined(_SAPP_UWP) - _sapp_uwp_update_cursor(_sapp.mouse.current_cursor, show); #elif defined(_SAPP_EMSCRIPTEN) _sapp_emsc_update_cursor(_sapp.mouse.current_cursor, show); #endif @@ -12171,8 +11364,6 @@ SOKOL_API_IMPL void sapp_set_mouse_cursor(sapp_mouse_cursor cursor) { _sapp_win32_update_cursor(cursor, _sapp.mouse.shown, false); #elif defined(_SAPP_LINUX) _sapp_x11_update_cursor(cursor, _sapp.mouse.shown); - #elif defined(_SAPP_UWP) - _sapp_uwp_update_cursor(cursor, _sapp.mouse.shown); #elif defined(_SAPP_EMSCRIPTEN) _sapp_emsc_update_cursor(cursor, _sapp.mouse.shown); #endif diff --git a/thirdparty/sokol/sokol_audio.h b/thirdparty/sokol/sokol_audio.h index 5e487f37a55945..6b60937cb8a3ab 100644 --- a/thirdparty/sokol/sokol_audio.h +++ b/thirdparty/sokol/sokol_audio.h @@ -24,6 +24,8 @@ SAUDIO_RING_MAX_SLOTS - max number of slots in the push-audio ring buffer (default 1024) SAUDIO_OSX_USE_SYSTEM_HEADERS - define this to force inclusion of system headers on macOS instead of using embedded CoreAudio declarations + SAUDIO_ANDROID_AAUDIO - on Android, select the AAudio backend (default) + SAUDIO_ANDROID_SLES - on Android, select the OpenSLES backend If sokol_audio.h is compiled as a DLL, define the following before including the declaration or implementation: @@ -38,7 +40,7 @@ - on macOS: AudioToolbox - on iOS: AudioToolbox, AVFoundation - on Linux: asound - - on Android: link with OpenSLES + - on Android: link with OpenSLES or aaudio - on Windows with MSVC or Clang toolchain: no action needed, libs are defined in-source via pragma-comment-lib - on Windows with MINGW/MSYS2 gcc: compile with '-mwin32' and link with -lole32 @@ -52,7 +54,7 @@ - macOS: CoreAudio - iOS: CoreAudio+AVAudioSession - emscripten: WebAudio with ScriptProcessorNode - - Android: OpenSLES + - Android: AAudio (default) or OpenSLES, select at build time Sokol Audio will not do any buffer mixing or volume control, if you have multiple independent input streams of sample data you need to perform the @@ -130,18 +132,30 @@ a good balance between low-latency and glitch-free playback on all audio backends. + You should always provide a logging callback to be aware of any + warnings and errors. The easiest way is to use sokol_log.h for this: + + #include "sokol_log.h" + // ... + saudio_setup(&(saudio_desc){ + .logger = { + .func = slog_func, + } + }); + If you want to use the callback-model, you need to provide a stream callback function either in saudio_desc.stream_cb or saudio_desc.stream_userdata_cb, otherwise keep both function pointers zero-initialized. Use push model and default playback parameters: - saudio_setup(&(saudio_desc){0}); + saudio_setup(&(saudio_desc){ .logger.func = slog_func }); Use stream callback model and default playback parameters: saudio_setup(&(saudio_desc){ .stream_cb = my_stream_callback + .logger.func = slog_func, }); The standard stream callback doesn't have a user data argument, if you want @@ -150,6 +164,7 @@ saudio_setup(&(saudio_desc){ .stream_userdata_cb = my_stream_callback, .user_data = &my_data + .logger.func = slog_func, }); The following playback parameters can be provided through the @@ -382,8 +397,8 @@ saudio_setup(&(saudio_desc){ // ... .allocator = { - .alloc = my_alloc, - .free = my_free, + .alloc_fn = my_alloc, + .free_fn = my_free, .user_data = ..., } }); @@ -398,27 +413,43 @@ was called, so you don't need to worry about thread-safety. - LOG FUNCTION OVERRIDE - ===================== - You can override the log function at initialization time like this: + ERROR REPORTING AND LOGGING + =========================== + To get any logging information at all you need to provide a logging callback in the setup call + the easiest way is to use sokol_log.h: + + #include "sokol_log.h" + + saudio_setup(&(saudio_desc){ .logger.func = slog_func }); - void my_log(const char* message, void* user_data) { - printf("saudio says: \s\n", message); + To override logging with your own callback, first write a logging function like this: + + void my_log(const char* tag, // e.g. 'saudio' + uint32_t log_level, // 0=panic, 1=error, 2=warn, 3=info + uint32_t log_item_id, // SAUDIO_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_audio.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data) + { + ... } - ... - saudio_setup(&(saudio_desc){ - // ... - .logger = { - .log_cb = my_log, - .user_data = ..., - } - }); - ... + ...and then setup sokol-audio like this: + + saudio_setup(&(saudio_desc){ + .logger = { + .func = my_log, + .user_data = my_user_data, + } + }); - If no overrides are provided, puts will be used on most platforms. - On Android, __android_log_write will be used instead. + The provided logging function must be reentrant (e.g. be callable from + different threads). + If you don't want to provide your own custom logger it is highly recommended to use + the standard logger in sokol_log.h instead, otherwise you won't see any warnings or + errors. LICENSE ======= @@ -469,30 +500,90 @@ extern "C" { #endif /* - saudio_allocator + saudio_log_item - Used in saudio_desc to provide custom memory-alloc and -free functions - to sokol_audio.h. If memory management should be overridden, both the - alloc and free function must be provided (e.g. it's not valid to - override one function but not the other). + Log items are defined via X-Macros, and expanded to an + enum 'saudio_log_item', and in debug mode only, + corresponding strings. + + Used as parameter in the logging callback. */ -typedef struct saudio_allocator { - void* (*alloc)(size_t size, void* user_data); - void (*free)(void* ptr, void* user_data); - void* user_data; -} saudio_allocator; +#define _SAUDIO_LOG_ITEMS \ + _SAUDIO_LOGITEM_XMACRO(OK, "Ok") \ + _SAUDIO_LOGITEM_XMACRO(MALLOC_FAILED, "memory allocation failed") \ + _SAUDIO_LOGITEM_XMACRO(ALSA_SND_PCM_OPEN_FAILED, "snd_pcm_open() failed") \ + _SAUDIO_LOGITEM_XMACRO(ALSA_FLOAT_SAMPLES_NOT_SUPPORTED, "floating point sample format not supported") \ + _SAUDIO_LOGITEM_XMACRO(ALSA_REQUESTED_BUFFER_SIZE_NOT_SUPPORTED, "requested buffer size not supported") \ + _SAUDIO_LOGITEM_XMACRO(ALSA_REQUESTED_CHANNEL_COUNT_NOT_SUPPORTED, "requested channel count not supported") \ + _SAUDIO_LOGITEM_XMACRO(ALSA_SND_PCM_HW_PARAMS_SET_RATE_NEAR_FAILED, "snd_pcm_hw_params_set_rate_near() failed") \ + _SAUDIO_LOGITEM_XMACRO(ALSA_SND_PCM_HW_PARAMS_FAILED, "snd_pcm_hw_params() failed") \ + _SAUDIO_LOGITEM_XMACRO(ALSA_PTHREAD_CREATE_FAILED, "pthread_create() failed") \ + _SAUDIO_LOGITEM_XMACRO(WASAPI_CREATE_EVENT_FAILED, "CreateEvent() failed") \ + _SAUDIO_LOGITEM_XMACRO(WASAPI_CREATE_DEVICE_ENUMERATOR_FAILED, "CoCreateInstance() for IMMDeviceEnumerator failed") \ + _SAUDIO_LOGITEM_XMACRO(WASAPI_GET_DEFAULT_AUDIO_ENDPOINT_FAILED, "IMMDeviceEnumerator.GetDefaultAudioEndpoint() failed") \ + _SAUDIO_LOGITEM_XMACRO(WASAPI_DEVICE_ACTIVATE_FAILED, "IMMDevice.Activate() failed") \ + _SAUDIO_LOGITEM_XMACRO(WASAPI_AUDIO_CLIENT_INITIALIZE_FAILED, "IAudioClient.Initialize() failed") \ + _SAUDIO_LOGITEM_XMACRO(WASAPI_AUDIO_CLIENT_GET_BUFFER_SIZE_FAILED, "IAudioClient.GetBufferSize() failed") \ + _SAUDIO_LOGITEM_XMACRO(WASAPI_AUDIO_CLIENT_GET_SERVICE_FAILED, "IAudioClient.GetService() failed") \ + _SAUDIO_LOGITEM_XMACRO(WASAPI_AUDIO_CLIENT_SET_EVENT_HANDLE_FAILED, "IAudioClient.SetEventHandle() failed") \ + _SAUDIO_LOGITEM_XMACRO(WASAPI_CREATE_THREAD_FAILED, "CreateThread() failed") \ + _SAUDIO_LOGITEM_XMACRO(AAUDIO_STREAMBUILDER_OPEN_STREAM_FAILED, "AAudioStreamBuilder_openStream() failed") \ + _SAUDIO_LOGITEM_XMACRO(AAUDIO_PTHREAD_CREATE_FAILED, "pthread_create() failed after AAUDIO_ERROR_DISCONNECTED") \ + _SAUDIO_LOGITEM_XMACRO(AAUDIO_RESTARTING_STREAM_AFTER_ERROR, "restarting AAudio stream after error") \ + _SAUDIO_LOGITEM_XMACRO(USING_AAUDIO_BACKEND, "using AAudio backend") \ + _SAUDIO_LOGITEM_XMACRO(AAUDIO_CREATE_STREAMBUILDER_FAILED, "AAudio_createStreamBuilder() failed") \ + _SAUDIO_LOGITEM_XMACRO(USING_SLES_BACKEND, "using OpenSLES backend") \ + _SAUDIO_LOGITEM_XMACRO(SLES_CREATE_ENGINE_FAILED, "slCreateEngine() failed") \ + _SAUDIO_LOGITEM_XMACRO(SLES_ENGINE_GET_ENGINE_INTERFACE_FAILED, "GetInterface() for SL_IID_ENGINE failed") \ + _SAUDIO_LOGITEM_XMACRO(SLES_CREATE_OUTPUT_MIX_FAILED, "CreateOutputMix() failed") \ + _SAUDIO_LOGITEM_XMACRO(SLES_MIXER_GET_VOLUME_INTERFACE_FAILED, "GetInterface() for SL_IID_VOLUME failed") \ + _SAUDIO_LOGITEM_XMACRO(SLES_ENGINE_CREATE_AUDIO_PLAYER_FAILED, "CreateAudioPlayer() failed") \ + _SAUDIO_LOGITEM_XMACRO(SLES_PLAYER_GET_PLAY_INTERFACE_FAILED, "GetInterface() for SL_IID_PLAY failed") \ + _SAUDIO_LOGITEM_XMACRO(SLES_PLAYER_GET_VOLUME_INTERFACE_FAILED, "GetInterface() for SL_IID_VOLUME failed") \ + _SAUDIO_LOGITEM_XMACRO(SLES_PLAYER_GET_BUFFERQUEUE_INTERFACE_FAILED, "GetInterface() for SL_IID_ANDROIDSIMPLEBUFFERQUEUE failed") \ + _SAUDIO_LOGITEM_XMACRO(COREAUDIO_NEW_OUTPUT_FAILED, "AudioQueueNewOutput() failed") \ + _SAUDIO_LOGITEM_XMACRO(COREAUDIO_ALLOCATE_BUFFER_FAILED, "AudioQueueAllocateBuffer() failed") \ + _SAUDIO_LOGITEM_XMACRO(COREAUDIO_START_FAILED, "AudioQueueStart() failed") \ + _SAUDIO_LOGITEM_XMACRO(BACKEND_BUFFER_SIZE_ISNT_MULTIPLE_OF_PACKET_SIZE, "backend buffer size isn't multiple of packet size") \ + +#define _SAUDIO_LOGITEM_XMACRO(item,msg) SAUDIO_LOGITEM_##item, +typedef enum saudio_log_item { + _SAUDIO_LOG_ITEMS +} saudio_log_item; +#undef _SAUDIO_LOGITEM_XMACRO /* saudio_logger - Used in saudio_desc to provide custom log callbacks to sokol_audio.h. - Default behavior is SOKOL_LOG(message). + Used in saudio_desc to provide a custom logging and error reporting + callback to sokol-audio. */ typedef struct saudio_logger { - void (*log_cb)(const char* message, void* user_data); + void (*func)( + const char* tag, // always "saudio" + uint32_t log_level, // 0=panic, 1=error, 2=warning, 3=info + uint32_t log_item_id, // SAUDIO_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_audio.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data); void* user_data; } saudio_logger; +/* + saudio_allocator + + Used in saudio_desc to provide custom memory-alloc and -free functions + to sokol_audio.h. If memory management should be overridden, both the + alloc_fn and free_fn function must be provided (e.g. it's not valid to + override one function but not the other). +*/ +typedef struct saudio_allocator { + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); + void* user_data; +} saudio_allocator; + typedef struct saudio_desc { int sample_rate; // requested sample rate int num_channels; // number of channels, default: 1 (mono) @@ -503,7 +594,7 @@ typedef struct saudio_desc { void (*stream_userdata_cb)(float* buffer, int num_frames, int num_channels, void* user_data); //... and with user data void* user_data; // optional user data argument for stream_userdata_cb saudio_allocator allocator; // optional allocation override functions - saudio_logger logger; // optional log override functions + saudio_logger logger; // optional logging function (default: NO LOGGING!) } saudio_desc; /* setup sokol-audio */ @@ -538,7 +629,13 @@ inline void saudio_setup(const saudio_desc& desc) { return saudio_setup(&desc); #endif #endif // SOKOL_AUDIO_INCLUDED -/*=== IMPLEMENTATION =========================================================*/ +// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████ +// +// >>implementation #ifdef SOKOL_AUDIO_IMPL #define SOKOL_AUDIO_IMPL_INCLUDED (1) @@ -563,21 +660,6 @@ inline void saudio_setup(const saudio_desc& desc) { return saudio_setup(&desc); #define SOKOL_ASSERT(c) assert(c) #endif -#if !defined(SOKOL_DEBUG) - #define SAUDIO_LOG(s) -#else - #define SAUDIO_LOG(s) _saudio_log(s) - #ifndef SOKOL_LOG - #if defined(__ANDROID__) - #include - #define SOKOL_LOG(s) __android_log_write(ANDROID_LOG_INFO, "SOKOL_AUDIO", s) - #else - #include - #define SOKOL_LOG(s) puts(s) - #endif - #endif -#endif - #ifndef _SOKOL_PRIVATE #if defined(__GNUC__) || defined(__clang__) #define _SOKOL_PRIVATE __attribute__((unused)) static @@ -602,17 +684,18 @@ inline void saudio_setup(const saudio_desc& desc) { return saudio_setup(&desc); #define _SAUDIO_MACOS (1) #endif #elif defined(__EMSCRIPTEN__) - #define _SAUDIO_EMSCRIPTEN + #define _SAUDIO_EMSCRIPTEN (1) #elif defined(_WIN32) #define _SAUDIO_WINDOWS (1) #include #if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) - #define _SAUDIO_UWP (1) - #else - #define _SAUDIO_WIN32 (1) + #error "sokol_audio.h no longer supports UWP" #endif #elif defined(__ANDROID__) #define _SAUDIO_ANDROID (1) + #if !defined(SAUDIO_ANDROID_SLES) && !defined(SAUDIO_ANDROID_AAUDIO) + #define SAUDIO_ANDROID_AAUDIO (1) + #endif #elif defined(__linux__) || defined(__unix__) #define _SAUDIO_LINUX (1) #else @@ -632,12 +715,8 @@ inline void saudio_setup(const saudio_desc& desc) { return saudio_setup(&desc); #endif #include #include - #if defined(_SAUDIO_UWP) - #pragma comment (lib, "WindowsApp") - #else - #pragma comment (lib, "kernel32") - #pragma comment (lib, "ole32") - #endif + #pragma comment (lib, "kernel32") + #pragma comment (lib, "ole32") #ifndef CINTERFACE #define CINTERFACE #endif @@ -695,8 +774,13 @@ inline void saudio_setup(const saudio_desc& desc) { return saudio_setup(&desc); #elif defined(_SAUDIO_ANDROID) #define _SAUDIO_PTHREADS (1) #include - #include "SLES/OpenSLES_Android.h" + #if defined(SAUDIO_ANDROID_SLES) + #include "SLES/OpenSLES_Android.h" + #elif defined(SAUDIO_ANDROID_AAUDIO) + #include "aaudio/AAudio.h" + #endif #elif defined(_SAUDIO_LINUX) + #include #define _SAUDIO_PTHREADS (1) #include #define ALSA_PCM_NEW_HW_PARAMS_API @@ -718,7 +802,13 @@ inline void saudio_setup(const saudio_desc& desc) { return saudio_setup(&desc); #define SAUDIO_RING_MAX_SLOTS (1024) #endif -/*=== MUTEX WRAPPER DECLARATIONS =============================================*/ +// ███████ ████████ ██████ ██ ██ ██████ ████████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██████ ██ ██ ██ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ██ ██████ ██████ ██ ███████ +// +// >>structs #if defined(_SAUDIO_PTHREADS) typedef struct { @@ -739,14 +829,12 @@ typedef struct { #endif -/*=== DUMMY BACKEND DECLARATIONS =============================================*/ #if defined(SOKOL_DUMMY_BACKEND) typedef struct { - int dummy_backend; -} _saudio_backend_t; + int dummy; +} _saudio_dummy_backend_t; -/*=== COREAUDIO BACKEND DECLARATIONS =========================================*/ #elif defined(_SAUDIO_APPLE) #if defined(SAUDIO_OSX_USE_SYSTEM_HEADERS) @@ -851,9 +939,8 @@ typedef struct { #if defined(_SAUDIO_IOS) id ca_interruption_handler; #endif -} _saudio_backend_t; +} _saudio_apple_backend_t; -/*=== ALSA BACKEND DECLARATIONS ==============================================*/ #elif defined(_SAUDIO_LINUX) typedef struct { @@ -863,18 +950,17 @@ typedef struct { int buffer_frames; pthread_t thread; bool thread_stop; -} _saudio_backend_t; +} _saudio_alsa_backend_t; -/*=== OpenSLES BACKEND DECLARATIONS ==============================================*/ -#elif defined(_SAUDIO_ANDROID) +#elif defined(SAUDIO_ANDROID_SLES) -#define SAUDIO_NUM_BUFFERS 2 +#define SAUDIO_SLES_NUM_BUFFERS (2) typedef struct { pthread_mutex_t mutex; pthread_cond_t cond; int count; -} _saudio_semaphore_t; +} _saudio_sles_semaphore_t; typedef struct { SLObjectItf engine_obj; @@ -888,16 +974,24 @@ typedef struct { SLVolumeItf player_vol; SLAndroidSimpleBufferQueueItf player_buffer_queue; - int16_t* output_buffers[SAUDIO_NUM_BUFFERS]; + int16_t* output_buffers[SAUDIO_SLES_NUM_BUFFERS]; float* src_buffer; int active_buffer; - _saudio_semaphore_t buffer_sem; + _saudio_sles_semaphore_t buffer_sem; pthread_t thread; volatile int thread_stop; SLDataLocator_AndroidSimpleBufferQueue in_locator; -} _saudio_backend_t; +} _saudio_sles_backend_t; + +#elif defined(SAUDIO_ANDROID_AAUDIO) + +typedef struct { + AAudioStreamBuilder* builder; + AAudioStream* stream; + pthread_t thread; + pthread_mutex_t mutex; +} _saudio_aaudio_backend_t; -/*=== WASAPI BACKEND DECLARATIONS ============================================*/ #elif defined(_SAUDIO_WINDOWS) typedef struct { @@ -912,32 +1006,38 @@ typedef struct { } _saudio_wasapi_thread_data_t; typedef struct { - #if defined(_SAUDIO_UWP) - LPOLESTR interface_activation_audio_interface_uid_string; - IActivateAudioInterfaceAsyncOperation* interface_activation_operation; - BOOL interface_activation_success; - HANDLE interface_activation_mutex; - #else - IMMDeviceEnumerator* device_enumerator; - IMMDevice* device; - #endif + IMMDeviceEnumerator* device_enumerator; + IMMDevice* device; IAudioClient* audio_client; IAudioRenderClient* render_client; _saudio_wasapi_thread_data_t thread; -} _saudio_backend_t; +} _saudio_wasapi_backend_t; -/*=== WEBAUDIO BACKEND DECLARATIONS ==========================================*/ #elif defined(_SAUDIO_EMSCRIPTEN) typedef struct { uint8_t* buffer; -} _saudio_backend_t; +} _saudio_web_backend_t; #else #error "unknown platform" #endif -/*=== GENERAL DECLARATIONS ===================================================*/ +#if defined(SOKOL_DUMMY_BACKEND) +typedef _saudio_dummy_backend_t _saudio_backend_t; +#elif defined(_SAUDIO_APPLE) +typedef _saudio_apple_backend_t _saudio_backend_t; +#elif defined(_SAUDIO_EMSCRIPTEN) +typedef _saudio_web_backend_t _saudio_backend_t; +#elif defined(_SAUDIO_WINDOWS) +typedef _saudio_wasapi_backend_t _saudio_backend_t; +#elif defined(SAUDIO_ANDROID_SLES) +typedef _saudio_sles_backend_t _saudio_backend_t; +#elif defined(SAUDIO_ANDROID_AAUDIO) +typedef _saudio_aaudio_backend_t _saudio_backend_t; +#elif defined(_SAUDIO_LINUX) +typedef _saudio_alsa_backend_t _saudio_backend_t; +#endif /* a ringbuffer structure */ typedef struct { @@ -977,7 +1077,7 @@ typedef struct { _saudio_backend_t backend; } _saudio_state_t; -static _saudio_state_t _saudio; +_SOKOL_PRIVATE _saudio_state_t _saudio; _SOKOL_PRIVATE bool _saudio_has_callback(void) { return (_saudio.stream_cb || _saudio.stream_userdata_cb); @@ -992,7 +1092,52 @@ _SOKOL_PRIVATE void _saudio_stream_callback(float* buffer, int num_frames, int n } } -/*=== MEMORY HELPERS ========================================================*/ +// ██ ██████ ██████ ██████ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ████ ██ ██ +// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██████ ██ ██ ████ ██████ +// +// >>logging +#if defined(SOKOL_DEBUG) +#define _SAUDIO_LOGITEM_XMACRO(item,msg) #item ": " msg, +static const char* _saudio_log_messages[] = { + _SAUDIO_LOG_ITEMS +}; +#undef _SAUDIO_LOGITEM_XMACRO +#endif // SOKOL_DEBUG + +#define _SAUDIO_PANIC(code) _saudio_log(SAUDIO_LOGITEM_ ##code, 0, __LINE__) +#define _SAUDIO_ERROR(code) _saudio_log(SAUDIO_LOGITEM_ ##code, 1, __LINE__) +#define _SAUDIO_WARN(code) _saudio_log(SAUDIO_LOGITEM_ ##code, 2, __LINE__) +#define _SAUDIO_INFO(code) _saudio_log(SAUDIO_LOGITEM_ ##code, 3, __LINE__) + +static void _saudio_log(saudio_log_item log_item, uint32_t log_level, uint32_t line_nr) { + if (_saudio.desc.logger.func) { + #if defined(SOKOL_DEBUG) + const char* filename = __FILE__; + const char* message = _saudio_log_messages[log_item]; + #else + const char* filename = 0; + const char* message = 0; + #endif + _saudio.desc.logger.func("saudio", log_level, log_item, message, line_nr, filename, _saudio.desc.logger.user_data); + } + else { + // for log level PANIC it would be 'undefined behaviour' to continue + if (log_level == 0) { + abort(); + } + } +} + +// ███ ███ ███████ ███ ███ ██████ ██████ ██ ██ +// ████ ████ ██ ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ █████ ██ ████ ██ ██ ██ ██████ ████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ██ ██████ ██ ██ ██ +// +// >>memory _SOKOL_PRIVATE void _saudio_clear(void* ptr, size_t size) { SOKOL_ASSERT(ptr && (size > 0)); memset(ptr, 0, size); @@ -1001,13 +1146,14 @@ _SOKOL_PRIVATE void _saudio_clear(void* ptr, size_t size) { _SOKOL_PRIVATE void* _saudio_malloc(size_t size) { SOKOL_ASSERT(size > 0); void* ptr; - if (_saudio.desc.allocator.alloc) { - ptr = _saudio.desc.allocator.alloc(size, _saudio.desc.allocator.user_data); - } - else { + if (_saudio.desc.allocator.alloc_fn) { + ptr = _saudio.desc.allocator.alloc_fn(size, _saudio.desc.allocator.user_data); + } else { ptr = malloc(size); } - SOKOL_ASSERT(ptr); + if (0 == ptr) { + _SAUDIO_PANIC(MALLOC_FAILED); + } return ptr; } @@ -1018,26 +1164,20 @@ _SOKOL_PRIVATE void* _saudio_malloc_clear(size_t size) { } _SOKOL_PRIVATE void _saudio_free(void* ptr) { - if (_saudio.desc.allocator.free) { - _saudio.desc.allocator.free(ptr, _saudio.desc.allocator.user_data); - } - else { - free(ptr); - } -} - -#if defined(SOKOL_DEBUG) -_SOKOL_PRIVATE void _saudio_log(const char* msg) { - SOKOL_ASSERT(msg); - if (_saudio.desc.logger.log_cb) { - _saudio.desc.logger.log_cb(msg, _saudio.desc.logger.user_data); + if (_saudio.desc.allocator.free_fn) { + _saudio.desc.allocator.free_fn(ptr, _saudio.desc.allocator.user_data); } else { - SOKOL_LOG(msg); + free(ptr); } } -#endif -/*=== MUTEX IMPLEMENTATION ===================================================*/ +// ███ ███ ██ ██ ████████ ███████ ██ ██ +// ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ ██ ██ ██ █████ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██████ ██ ███████ ██ ██ +// +// >>mutex #if defined(_SAUDIO_NOTHREADS) _SOKOL_PRIVATE void _saudio_mutex_init(_saudio_mutex_t* m) { (void)m; } @@ -1083,10 +1223,16 @@ _SOKOL_PRIVATE void _saudio_mutex_unlock(_saudio_mutex_t* m) { LeaveCriticalSection(&m->critsec); } #else -#error "unknown platform!" +#error "sokol_audio.h: unknown platform!" #endif -/*=== RING-BUFFER QUEUE IMPLEMENTATION =======================================*/ +// ██████ ██ ███ ██ ██████ ██████ ██ ██ ███████ ███████ ███████ ██████ +// ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██ ██ ██ ███ ██████ ██ ██ █████ █████ █████ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ████ ██████ ██████ ██████ ██ ██ ███████ ██ ██ +// +// >>ringbuffer _SOKOL_PRIVATE int _saudio_ring_idx(_saudio_ring_t* ring, int i) { return (i % ring->num); } @@ -1132,12 +1278,22 @@ _SOKOL_PRIVATE int _saudio_ring_dequeue(_saudio_ring_t* ring) { return val; } -/*--- a packet fifo for queueing audio data from main thread ----------------*/ +// ███████ ██ ███████ ██████ +// ██ ██ ██ ██ ██ +// █████ ██ █████ ██ ██ +// ██ ██ ██ ██ ██ +// ██ ██ ██ ██████ +// +// >>fifo _SOKOL_PRIVATE void _saudio_fifo_init_mutex(_saudio_fifo_t* fifo) { /* this must be called before initializing both the backend and the fifo itself! */ _saudio_mutex_init(&fifo->mutex); } +_SOKOL_PRIVATE void _saudio_fifo_destroy_mutex(_saudio_fifo_t* fifo) { + _saudio_mutex_destroy(&fifo->mutex); +} + _SOKOL_PRIVATE void _saudio_fifo_init(_saudio_fifo_t* fifo, int packet_size, int num_packets) { /* NOTE: there's a chicken-egg situation during the init phase where the streaming thread must be started before the fifo is actually initialized, @@ -1168,7 +1324,6 @@ _SOKOL_PRIVATE void _saudio_fifo_shutdown(_saudio_fifo_t* fifo) { _saudio_free(fifo->base_ptr); fifo->base_ptr = 0; fifo->valid = false; - _saudio_mutex_destroy(&fifo->mutex); } _SOKOL_PRIVATE int _saudio_fifo_writable_bytes(_saudio_fifo_t* fifo) { @@ -1259,154 +1414,27 @@ _SOKOL_PRIVATE int _saudio_fifo_read(_saudio_fifo_t* fifo, uint8_t* ptr, int num return num_bytes_copied; } -/*=== DUMMY BACKEND IMPLEMENTATION ===========================================*/ +// ██████ ██ ██ ███ ███ ███ ███ ██ ██ +// ██ ██ ██ ██ ████ ████ ████ ████ ██ ██ +// ██ ██ ██ ██ ██ ████ ██ ██ ████ ██ ████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██████ ██ ██ ██ ██ ██ +// +// >>dummy #if defined(SOKOL_DUMMY_BACKEND) -_SOKOL_PRIVATE bool _saudio_backend_init(void) { +_SOKOL_PRIVATE bool _saudio_dummy_backend_init(void) { _saudio.bytes_per_frame = _saudio.num_channels * (int)sizeof(float); return true; }; -_SOKOL_PRIVATE void _saudio_backend_shutdown(void) { }; - -/*=== COREAUDIO BACKEND IMPLEMENTATION =======================================*/ -#elif defined(_SAUDIO_APPLE) - -#if defined(_SAUDIO_IOS) -#if __has_feature(objc_arc) -#define _SAUDIO_OBJC_RELEASE(obj) { obj = nil; } -#else -#define _SAUDIO_OBJC_RELEASE(obj) { [obj release]; obj = nil; } -#endif - -@interface _saudio_interruption_handler : NSObject { } -@end - -@implementation _saudio_interruption_handler --(id)init { - self = [super init]; - AVAudioSession* session = [AVAudioSession sharedInstance]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_interruption:) name:AVAudioSessionInterruptionNotification object:session]; - return self; -} - --(void)dealloc { - [self remove_handler]; - #if !__has_feature(objc_arc) - [super dealloc]; - #endif -} - --(void)remove_handler { - [[NSNotificationCenter defaultCenter] removeObserver:self name:@"AVAudioSessionInterruptionNotification" object:nil]; -} - --(void)handle_interruption:(NSNotification*)notification { - AVAudioSession* session = [AVAudioSession sharedInstance]; - SOKOL_ASSERT(session); - NSDictionary* dict = notification.userInfo; - SOKOL_ASSERT(dict); - NSInteger type = [[dict valueForKey:AVAudioSessionInterruptionTypeKey] integerValue]; - switch (type) { - case AVAudioSessionInterruptionTypeBegan: - AudioQueuePause(_saudio.backend.ca_audio_queue); - [session setActive:false error:nil]; - break; - case AVAudioSessionInterruptionTypeEnded: - [session setActive:true error:nil]; - AudioQueueStart(_saudio.backend.ca_audio_queue, NULL); - break; - default: - break; - } -} -@end -#endif // _SAUDIO_IOS - -/* NOTE: the buffer data callback is called on a separate thread! */ -_SOKOL_PRIVATE void _saudio_coreaudio_callback(void* user_data, _saudio_AudioQueueRef queue, _saudio_AudioQueueBufferRef buffer) { - _SOKOL_UNUSED(user_data); - if (_saudio_has_callback()) { - const int num_frames = (int)buffer->mAudioDataByteSize / _saudio.bytes_per_frame; - const int num_channels = _saudio.num_channels; - _saudio_stream_callback((float*)buffer->mAudioData, num_frames, num_channels); - } - else { - uint8_t* ptr = (uint8_t*)buffer->mAudioData; - int num_bytes = (int) buffer->mAudioDataByteSize; - if (0 == _saudio_fifo_read(&_saudio.fifo, ptr, num_bytes)) { - /* not enough read data available, fill the entire buffer with silence */ - _saudio_clear(ptr, (size_t)num_bytes); - } - } - AudioQueueEnqueueBuffer(queue, buffer, 0, NULL); -} - -_SOKOL_PRIVATE bool _saudio_backend_init(void) { - SOKOL_ASSERT(0 == _saudio.backend.ca_audio_queue); - - #if defined(_SAUDIO_IOS) - /* activate audio session */ - AVAudioSession* session = [AVAudioSession sharedInstance]; - SOKOL_ASSERT(session != nil); - [session setCategory: AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error:nil]; - [session setActive:true error:nil]; - - /* create interruption handler */ - _saudio.backend.ca_interruption_handler = [[_saudio_interruption_handler alloc] init]; - #endif // _SAUDIO_IOS - - /* create an audio queue with fp32 samples */ - _saudio_AudioStreamBasicDescription fmt; - _saudio_clear(&fmt, sizeof(fmt)); - fmt.mSampleRate = (double) _saudio.sample_rate; - fmt.mFormatID = _saudio_kAudioFormatLinearPCM; - fmt.mFormatFlags = _saudio_kLinearPCMFormatFlagIsFloat | _saudio_kAudioFormatFlagIsPacked; - fmt.mFramesPerPacket = 1; - fmt.mChannelsPerFrame = (uint32_t) _saudio.num_channels; - fmt.mBytesPerFrame = (uint32_t)sizeof(float) * (uint32_t)_saudio.num_channels; - fmt.mBytesPerPacket = fmt.mBytesPerFrame; - fmt.mBitsPerChannel = 32; - _saudio_OSStatus res = AudioQueueNewOutput(&fmt, _saudio_coreaudio_callback, 0, NULL, NULL, 0, &_saudio.backend.ca_audio_queue); - SOKOL_ASSERT((res == 0) && _saudio.backend.ca_audio_queue); (void)res; - - /* create 2 audio buffers */ - for (int i = 0; i < 2; i++) { - _saudio_AudioQueueBufferRef buf = NULL; - const uint32_t buf_byte_size = (uint32_t)_saudio.buffer_frames * fmt.mBytesPerFrame; - res = AudioQueueAllocateBuffer(_saudio.backend.ca_audio_queue, buf_byte_size, &buf); - SOKOL_ASSERT((res == 0) && buf); (void)res; - buf->mAudioDataByteSize = buf_byte_size; - _saudio_clear(buf->mAudioData, buf->mAudioDataByteSize); - AudioQueueEnqueueBuffer(_saudio.backend.ca_audio_queue, buf, 0, NULL); - } - - /* init or modify actual playback parameters */ - _saudio.bytes_per_frame = (int)fmt.mBytesPerFrame; - - /* ...and start playback */ - res = AudioQueueStart(_saudio.backend.ca_audio_queue, NULL); - SOKOL_ASSERT(0 == res); (void)res; - - return true; -} - -_SOKOL_PRIVATE void _saudio_backend_shutdown(void) { - AudioQueueStop(_saudio.backend.ca_audio_queue, true); - AudioQueueDispose(_saudio.backend.ca_audio_queue, false); - _saudio.backend.ca_audio_queue = NULL; - #if defined(_SAUDIO_IOS) - /* remove interruption handler */ - if (_saudio.backend.ca_interruption_handler != nil) { - [_saudio.backend.ca_interruption_handler remove_handler]; - _SAUDIO_OBJC_RELEASE(_saudio.backend.ca_interruption_handler); - } - /* deactivate audio session */ - AVAudioSession* session = [AVAudioSession sharedInstance]; - SOKOL_ASSERT(session); - [session setActive:false error:nil];; - #endif // _SAUDIO_IOS -} - -/*=== ALSA BACKEND IMPLEMENTATION ============================================*/ +_SOKOL_PRIVATE void _saudio_dummy_backend_shutdown(void) { }; + +// █████ ██ ███████ █████ +// ██ ██ ██ ██ ██ ██ +// ███████ ██ ███████ ███████ +// ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ███████ ██ ██ +// +// >>alsa #elif defined(_SAUDIO_LINUX) /* the streaming callback runs in a separate thread */ @@ -1435,11 +1463,11 @@ _SOKOL_PRIVATE void* _saudio_alsa_cb(void* param) { return 0; } -_SOKOL_PRIVATE bool _saudio_backend_init(void) { +_SOKOL_PRIVATE bool _saudio_alsa_backend_init(void) { int dir; uint32_t rate; int rc = snd_pcm_open(&_saudio.backend.device, "default", SND_PCM_STREAM_PLAYBACK, 0); if (rc < 0) { - SAUDIO_LOG("sokol_audio.h: snd_pcm_open() failed"); + _SAUDIO_ERROR(ALSA_SND_PCM_OPEN_FAILED); return false; } @@ -1452,26 +1480,26 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) { snd_pcm_hw_params_any(_saudio.backend.device, params); snd_pcm_hw_params_set_access(_saudio.backend.device, params, SND_PCM_ACCESS_RW_INTERLEAVED); if (0 > snd_pcm_hw_params_set_format(_saudio.backend.device, params, SND_PCM_FORMAT_FLOAT_LE)) { - SAUDIO_LOG("sokol_audio.h: float samples not supported"); + _SAUDIO_ERROR(ALSA_FLOAT_SAMPLES_NOT_SUPPORTED); goto error; } if (0 > snd_pcm_hw_params_set_buffer_size(_saudio.backend.device, params, (snd_pcm_uframes_t)_saudio.buffer_frames)) { - SAUDIO_LOG("sokol_audio.h: requested buffer size not supported"); + _SAUDIO_ERROR(ALSA_REQUESTED_BUFFER_SIZE_NOT_SUPPORTED); goto error; } if (0 > snd_pcm_hw_params_set_channels(_saudio.backend.device, params, (uint32_t)_saudio.num_channels)) { - SAUDIO_LOG("sokol_audio.h: requested channel count not supported"); + _SAUDIO_ERROR(ALSA_REQUESTED_CHANNEL_COUNT_NOT_SUPPORTED); goto error; } /* let ALSA pick a nearby sampling rate */ rate = (uint32_t) _saudio.sample_rate; dir = 0; if (0 > snd_pcm_hw_params_set_rate_near(_saudio.backend.device, params, &rate, &dir)) { - SAUDIO_LOG("sokol_audio.h: snd_pcm_hw_params_set_rate_near() failed"); + _SAUDIO_ERROR(ALSA_SND_PCM_HW_PARAMS_SET_RATE_NEAR_FAILED); goto error; } if (0 > snd_pcm_hw_params(_saudio.backend.device, params)) { - SAUDIO_LOG("sokol_audio.h: snd_pcm_hw_params() failed"); + _SAUDIO_ERROR(ALSA_SND_PCM_HW_PARAMS_FAILED); goto error; } @@ -1486,7 +1514,7 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) { /* create the buffer-streaming start thread */ if (0 != pthread_create(&_saudio.backend.thread, 0, _saudio_alsa_cb, 0)) { - SAUDIO_LOG("sokol_audio.h: pthread_create() failed"); + _SAUDIO_ERROR(ALSA_PTHREAD_CREATE_FAILED); goto error; } @@ -1499,7 +1527,7 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) { return false; }; -_SOKOL_PRIVATE void _saudio_backend_shutdown(void) { +_SOKOL_PRIVATE void _saudio_alsa_backend_shutdown(void) { SOKOL_ASSERT(_saudio.backend.device); _saudio.backend.thread_stop = true; pthread_join(_saudio.backend.thread, 0); @@ -1508,51 +1536,15 @@ _SOKOL_PRIVATE void _saudio_backend_shutdown(void) { _saudio_free(_saudio.backend.buffer); }; -/*=== WASAPI BACKEND IMPLEMENTATION ==========================================*/ +// ██ ██ █████ ███████ █████ ██████ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ █ ██ ███████ ███████ ███████ ██████ ██ +// ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ +// ███ ███ ██ ██ ███████ ██ ██ ██ ██ +// +// >>wasapi #elif defined(_SAUDIO_WINDOWS) -#if defined(_SAUDIO_UWP) -/* Minimal implementation of an IActivateAudioInterfaceCompletionHandler COM object in plain C. - Meant to be a static singleton (always one reference when add/remove reference) - and implements IUnknown and IActivateAudioInterfaceCompletionHandler when queryinterface'd - - Do not know why but IActivateAudioInterfaceCompletionHandler's GUID is not the one system queries for, - so I'm advertising the one actually requested. -*/ -_SOKOL_PRIVATE HRESULT STDMETHODCALLTYPE _saudio_interface_completion_handler_queryinterface(IActivateAudioInterfaceCompletionHandler* instance, REFIID riid, void** ppvObject) { - if (!ppvObject) { - return E_POINTER; - } - - if (IsEqualIID(riid, _SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IActivateAudioInterface_Completion_Handler)) || IsEqualIID(riid, _SOKOL_AUDIO_WIN32COM_ID(IID_IUnknown))) - { - *ppvObject = (void*)instance; - return S_OK; - } - - *ppvObject = NULL; - return E_NOINTERFACE; -} - -_SOKOL_PRIVATE ULONG STDMETHODCALLTYPE _saudio_interface_completion_handler_addref_release(IActivateAudioInterfaceCompletionHandler* instance) { - _SOKOL_UNUSED(instance); - return 1; -} - -_SOKOL_PRIVATE HRESULT STDMETHODCALLTYPE _saudio_backend_activate_audio_interface_cb(IActivateAudioInterfaceCompletionHandler* instance, IActivateAudioInterfaceAsyncOperation* activateOperation) { - _SOKOL_UNUSED(instance); - WaitForSingleObject(_saudio.backend.interface_activation_mutex, INFINITE); - _saudio.backend.interface_activation_success = TRUE; - HRESULT activation_result; - if (FAILED(activateOperation->lpVtbl->GetActivateResult(activateOperation, &activation_result, (IUnknown**)(&_saudio.backend.audio_client))) || FAILED(activation_result)) { - _saudio.backend.interface_activation_success = FALSE; - } - - ReleaseMutex(_saudio.backend.interface_activation_mutex); - return S_OK; -} -#endif // _SAUDIO_UWP - /* fill intermediate buffer with new data and reset buffer_pos */ _SOKOL_PRIVATE void _saudio_wasapi_fill_buffer(void) { if (_saudio_has_callback()) { @@ -1640,108 +1632,56 @@ _SOKOL_PRIVATE void _saudio_wasapi_release(void) { IAudioClient_Release(_saudio.backend.audio_client); _saudio.backend.audio_client = 0; } - #if defined(_SAUDIO_UWP) - if (_saudio.backend.interface_activation_audio_interface_uid_string) { - CoTaskMemFree(_saudio.backend.interface_activation_audio_interface_uid_string); - _saudio.backend.interface_activation_audio_interface_uid_string = 0; - } - if (_saudio.backend.interface_activation_operation) { - IActivateAudioInterfaceAsyncOperation_Release(_saudio.backend.interface_activation_operation); - _saudio.backend.interface_activation_operation = 0; - } - #else - if (_saudio.backend.device) { - IMMDevice_Release(_saudio.backend.device); - _saudio.backend.device = 0; - } - if (_saudio.backend.device_enumerator) { - IMMDeviceEnumerator_Release(_saudio.backend.device_enumerator); - _saudio.backend.device_enumerator = 0; - } - #endif + if (_saudio.backend.device) { + IMMDevice_Release(_saudio.backend.device); + _saudio.backend.device = 0; + } + if (_saudio.backend.device_enumerator) { + IMMDeviceEnumerator_Release(_saudio.backend.device_enumerator); + _saudio.backend.device_enumerator = 0; + } if (0 != _saudio.backend.thread.buffer_end_event) { CloseHandle(_saudio.backend.thread.buffer_end_event); _saudio.backend.thread.buffer_end_event = 0; } } -_SOKOL_PRIVATE bool _saudio_backend_init(void) { +_SOKOL_PRIVATE bool _saudio_wasapi_backend_init(void) { REFERENCE_TIME dur; - /* UWP Threads are CoInitialized by default with a different threading model, and this call fails - See https://github.com/Microsoft/cppwinrt/issues/6#issuecomment-253930637 */ - #if defined(_SAUDIO_WIN32) - /* CoInitializeEx could have been called elsewhere already, in which - case the function returns with S_FALSE (thus it does not make much - sense to check the result) - */ - HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED); - _SOKOL_UNUSED(hr); - #endif + /* CoInitializeEx could have been called elsewhere already, in which + case the function returns with S_FALSE (thus it does not make much + sense to check the result) + */ + HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED); + _SOKOL_UNUSED(hr); _saudio.backend.thread.buffer_end_event = CreateEvent(0, FALSE, FALSE, 0); if (0 == _saudio.backend.thread.buffer_end_event) { - SAUDIO_LOG("sokol_audio wasapi: failed to create buffer_end_event"); + _SAUDIO_ERROR(WASAPI_CREATE_EVENT_FAILED); + goto error; + } + if (FAILED(CoCreateInstance(_SOKOL_AUDIO_WIN32COM_ID(_saudio_CLSID_IMMDeviceEnumerator), + 0, CLSCTX_ALL, + _SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IMMDeviceEnumerator), + (void**)&_saudio.backend.device_enumerator))) + { + _SAUDIO_ERROR(WASAPI_CREATE_DEVICE_ENUMERATOR_FAILED); + goto error; + } + if (FAILED(IMMDeviceEnumerator_GetDefaultAudioEndpoint(_saudio.backend.device_enumerator, + eRender, eConsole, + &_saudio.backend.device))) + { + _SAUDIO_ERROR(WASAPI_GET_DEFAULT_AUDIO_ENDPOINT_FAILED); + goto error; + } + if (FAILED(IMMDevice_Activate(_saudio.backend.device, + _SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IAudioClient), + CLSCTX_ALL, 0, + (void**)&_saudio.backend.audio_client))) + { + _SAUDIO_ERROR(WASAPI_DEVICE_ACTIVATE_FAILED); goto error; } - #if defined(_SAUDIO_UWP) - _saudio.backend.interface_activation_mutex = CreateMutexA(NULL, FALSE, "interface_activation_mutex"); - if (_saudio.backend.interface_activation_mutex == NULL) { - SAUDIO_LOG("sokol_audio wasapi: failed to create interface activation mutex"); - goto error; - } - if (FAILED(StringFromIID(_SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_Devinterface_Audio_Render), &_saudio.backend.interface_activation_audio_interface_uid_string))) { - SAUDIO_LOG("sokol_audio wasapi: failed to get default audio device ID string"); - goto error; - } - - /* static instance of the fake COM object */ - static IActivateAudioInterfaceCompletionHandlerVtbl completion_handler_interface_vtable = { - _saudio_interface_completion_handler_queryinterface, - _saudio_interface_completion_handler_addref_release, - _saudio_interface_completion_handler_addref_release, - _saudio_backend_activate_audio_interface_cb - }; - static IActivateAudioInterfaceCompletionHandler completion_handler_interface = { &completion_handler_interface_vtable }; - - if (FAILED(ActivateAudioInterfaceAsync(_saudio.backend.interface_activation_audio_interface_uid_string, _SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IAudioClient), NULL, &completion_handler_interface, &_saudio.backend.interface_activation_operation))) { - SAUDIO_LOG("sokol_audio wasapi: failed to get default audio device ID string"); - goto error; - } - while (!(_saudio.backend.audio_client)) { - if (WaitForSingleObject(_saudio.backend.interface_activation_mutex, 10) != WAIT_TIMEOUT) { - ReleaseMutex(_saudio.backend.interface_activation_mutex); - } - } - - if (!(_saudio.backend.interface_activation_success)) { - SAUDIO_LOG("sokol_audio wasapi: interface activation failed. Unable to get audio client"); - goto error; - } - - #else - if (FAILED(CoCreateInstance(_SOKOL_AUDIO_WIN32COM_ID(_saudio_CLSID_IMMDeviceEnumerator), - 0, CLSCTX_ALL, - _SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IMMDeviceEnumerator), - (void**)&_saudio.backend.device_enumerator))) - { - SAUDIO_LOG("sokol_audio wasapi: failed to create device enumerator"); - goto error; - } - if (FAILED(IMMDeviceEnumerator_GetDefaultAudioEndpoint(_saudio.backend.device_enumerator, - eRender, eConsole, - &_saudio.backend.device))) - { - SAUDIO_LOG("sokol_audio wasapi: GetDefaultAudioEndPoint failed"); - goto error; - } - if (FAILED(IMMDevice_Activate(_saudio.backend.device, - _SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IAudioClient), - CLSCTX_ALL, 0, - (void**)&_saudio.backend.audio_client))) - { - SAUDIO_LOG("sokol_audio wasapi: device activate failed"); - goto error; - } - #endif WAVEFORMATEXTENSIBLE fmtex; _saudio_clear(&fmtex, sizeof(fmtex)); @@ -1767,22 +1707,22 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) { AUDCLNT_STREAMFLAGS_EVENTCALLBACK|AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM|AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY, dur, 0, (WAVEFORMATEX*)&fmtex, 0))) { - SAUDIO_LOG("sokol_audio wasapi: audio client initialize failed"); + _SAUDIO_ERROR(WASAPI_AUDIO_CLIENT_INITIALIZE_FAILED); goto error; } if (FAILED(IAudioClient_GetBufferSize(_saudio.backend.audio_client, &_saudio.backend.thread.dst_buffer_frames))) { - SAUDIO_LOG("sokol_audio wasapi: audio client get buffer size failed"); + _SAUDIO_ERROR(WASAPI_AUDIO_CLIENT_GET_BUFFER_SIZE_FAILED); goto error; } if (FAILED(IAudioClient_GetService(_saudio.backend.audio_client, _SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IAudioRenderClient), (void**)&_saudio.backend.render_client))) { - SAUDIO_LOG("sokol_audio wasapi: audio client GetService failed"); + _SAUDIO_ERROR(WASAPI_AUDIO_CLIENT_GET_SERVICE_FAILED); goto error; } if (FAILED(IAudioClient_SetEventHandle(_saudio.backend.audio_client, _saudio.backend.thread.buffer_end_event))) { - SAUDIO_LOG("sokol_audio wasapi: audio client SetEventHandle failed"); + _SAUDIO_ERROR(WASAPI_AUDIO_CLIENT_SET_EVENT_HANDLE_FAILED); goto error; } _saudio.bytes_per_frame = _saudio.num_channels * (int)sizeof(float); @@ -1795,7 +1735,7 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) { /* create streaming thread */ _saudio.backend.thread.thread_handle = CreateThread(NULL, 0, _saudio_wasapi_thread_fn, 0, 0, 0); if (0 == _saudio.backend.thread.thread_handle) { - SAUDIO_LOG("sokol_audio wasapi: CreateThread failed"); + _SAUDIO_ERROR(WASAPI_CREATE_THREAD_FAILED); goto error; } return true; @@ -1804,7 +1744,7 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) { return false; } -_SOKOL_PRIVATE void _saudio_backend_shutdown(void) { +_SOKOL_PRIVATE void _saudio_wasapi_backend_shutdown(void) { if (_saudio.backend.thread.thread_handle) { _saudio.backend.thread.stop = true; SetEvent(_saudio.backend.thread.buffer_end_event); @@ -1816,13 +1756,16 @@ _SOKOL_PRIVATE void _saudio_backend_shutdown(void) { IAudioClient_Stop(_saudio.backend.audio_client); } _saudio_wasapi_release(); - - #if defined(_SAUDIO_WIN32) - CoUninitialize(); - #endif + CoUninitialize(); } -/*=== EMSCRIPTEN BACKEND IMPLEMENTATION ======================================*/ +// ██ ██ ███████ ██████ █████ ██ ██ ██████ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ █ ██ █████ ██████ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███ ███ ███████ ██████ ██ ██ ██████ ██████ ██ ██████ +// +// >>webaudio #elif defined(_SAUDIO_EMSCRIPTEN) #ifdef __cplusplus @@ -1895,7 +1838,7 @@ EM_JS(int, saudio_js_init, (int sample_rate, int num_channels, int buffer_size), } }; document.addEventListener('click', resume_webaudio, {once:true}); - document.addEventListener('touchstart', resume_webaudio, {once:true}); + document.addEventListener('touchend', resume_webaudio, {once:true}); document.addEventListener('keydown', resume_webaudio, {once:true}); return 1; } @@ -1950,7 +1893,7 @@ EM_JS(int, saudio_js_suspended, (void), { } }); -_SOKOL_PRIVATE bool _saudio_backend_init(void) { +_SOKOL_PRIVATE bool _saudio_webaudio_backend_init(void) { if (saudio_js_init(_saudio.sample_rate, _saudio.num_channels, _saudio.buffer_frames)) { _saudio.bytes_per_frame = (int)sizeof(float) * _saudio.num_channels; _saudio.sample_rate = saudio_js_sample_rate(); @@ -1964,7 +1907,7 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) { } } -_SOKOL_PRIVATE void _saudio_backend_shutdown(void) { +_SOKOL_PRIVATE void _saudio_webaudio_backend_shutdown(void) { saudio_js_shutdown(); if (_saudio.backend.buffer) { _saudio_free(_saudio.backend.buffer); @@ -1972,56 +1915,154 @@ _SOKOL_PRIVATE void _saudio_backend_shutdown(void) { } } -/*=== ANDROID BACKEND IMPLEMENTATION ======================================*/ -#elif defined(_SAUDIO_ANDROID) +// █████ █████ ██ ██ ██████ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██████ ██████ ██ ██████ +// +// >>aaudio +#elif defined(SAUDIO_ANDROID_AAUDIO) -#ifdef __cplusplus -extern "C" { -#endif +_SOKOL_PRIVATE aaudio_data_callback_result_t _saudio_aaudio_data_callback(AAudioStream* stream, void* user_data, void* audio_data, int32_t num_frames) { + _SOKOL_UNUSED(user_data); + _SOKOL_UNUSED(stream); + if (_saudio_has_callback()) { + _saudio_stream_callback((float*)audio_data, (int)num_frames, _saudio.num_channels); + } + else { + uint8_t* ptr = (uint8_t*)audio_data; + int num_bytes = _saudio.bytes_per_frame * num_frames; + if (0 == _saudio_fifo_read(&_saudio.fifo, ptr, num_bytes)) { + // not enough read data available, fill the entire buffer with silence + memset(ptr, 0, (size_t)num_bytes); + } + } + return AAUDIO_CALLBACK_RESULT_CONTINUE; +} + +_SOKOL_PRIVATE bool _saudio_aaudio_start_stream(void) { + if (AAudioStreamBuilder_openStream(_saudio.backend.builder, &_saudio.backend.stream) != AAUDIO_OK) { + _SAUDIO_ERROR(AAUDIO_STREAMBUILDER_OPEN_STREAM_FAILED); + return false; + } + AAudioStream_requestStart(_saudio.backend.stream); + return true; +} + +_SOKOL_PRIVATE void _saudio_aaudio_stop_stream(void) { + if (_saudio.backend.stream) { + AAudioStream_requestStop(_saudio.backend.stream); + AAudioStream_close(_saudio.backend.stream); + _saudio.backend.stream = 0; + } +} + +_SOKOL_PRIVATE void* _saudio_aaudio_restart_stream_thread_fn(void* param) { + _SOKOL_UNUSED(param); + _SAUDIO_WARN(AAUDIO_RESTARTING_STREAM_AFTER_ERROR); + pthread_mutex_lock(&_saudio.backend.mutex); + _saudio_aaudio_stop_stream(); + _saudio_aaudio_start_stream(); + pthread_mutex_unlock(&_saudio.backend.mutex); + return 0; +} + +_SOKOL_PRIVATE void _saudio_aaudio_error_callback(AAudioStream* stream, void* user_data, aaudio_result_t error) { + _SOKOL_UNUSED(stream); + _SOKOL_UNUSED(user_data); + if (error == AAUDIO_ERROR_DISCONNECTED) { + if (0 != pthread_create(&_saudio.backend.thread, 0, _saudio_aaudio_restart_stream_thread_fn, 0)) { + _SAUDIO_ERROR(AAUDIO_PTHREAD_CREATE_FAILED); + } + } +} + +_SOKOL_PRIVATE void _saudio_aaudio_backend_shutdown(void) { + pthread_mutex_lock(&_saudio.backend.mutex); + _saudio_aaudio_stop_stream(); + pthread_mutex_unlock(&_saudio.backend.mutex); + if (_saudio.backend.builder) { + AAudioStreamBuilder_delete(_saudio.backend.builder); + _saudio.backend.builder = 0; + } + pthread_mutex_destroy(&_saudio.backend.mutex); +} -_SOKOL_PRIVATE void _saudio_semaphore_init(_saudio_semaphore_t* sem) { +_SOKOL_PRIVATE bool _saudio_aaudio_backend_init(void) { + _SAUDIO_INFO(USING_AAUDIO_BACKEND); + + _saudio.bytes_per_frame = _saudio.num_channels * (int)sizeof(float); + + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutex_init(&_saudio.backend.mutex, &attr); + + if (AAudio_createStreamBuilder(&_saudio.backend.builder) != AAUDIO_OK) { + _SAUDIO_ERROR(AAUDIO_CREATE_STREAMBUILDER_FAILED); + _saudio_aaudio_backend_shutdown(); + return false; + } + + AAudioStreamBuilder_setFormat(_saudio.backend.builder, AAUDIO_FORMAT_PCM_FLOAT); + AAudioStreamBuilder_setSampleRate(_saudio.backend.builder, _saudio.sample_rate); + AAudioStreamBuilder_setChannelCount(_saudio.backend.builder, _saudio.num_channels); + AAudioStreamBuilder_setBufferCapacityInFrames(_saudio.backend.builder, _saudio.buffer_frames * 2); + AAudioStreamBuilder_setFramesPerDataCallback(_saudio.backend.builder, _saudio.buffer_frames); + AAudioStreamBuilder_setDataCallback(_saudio.backend.builder, _saudio_aaudio_data_callback, 0); + AAudioStreamBuilder_setErrorCallback(_saudio.backend.builder, _saudio_aaudio_error_callback, 0); + + if (!_saudio_aaudio_start_stream()) { + _saudio_aaudio_backend_shutdown(); + return false; + } + + return true; +} + +// ██████ ██████ ███████ ███ ██ ███████ ██ ███████ ███████ +// ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ +// ██ ██ ██████ █████ ██ ██ ██ ███████ ██ █████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ███████ ██ ████ ███████ ███████ ███████ ███████ +// +// >>opensles +// >>sles +#elif defined(SAUDIO_ANDROID_SLES) + +_SOKOL_PRIVATE void _saudio_sles_semaphore_init(_saudio_sles_semaphore_t* sem) { sem->count = 0; int r = pthread_mutex_init(&sem->mutex, NULL); SOKOL_ASSERT(r == 0); - r = pthread_cond_init(&sem->cond, NULL); SOKOL_ASSERT(r == 0); - (void)(r); } -_SOKOL_PRIVATE void _saudio_semaphore_destroy(_saudio_semaphore_t* sem) -{ +_SOKOL_PRIVATE void _saudio_sles_semaphore_destroy(_saudio_sles_semaphore_t* sem) { pthread_cond_destroy(&sem->cond); pthread_mutex_destroy(&sem->mutex); } -_SOKOL_PRIVATE void _saudio_semaphore_post(_saudio_semaphore_t* sem, int count) -{ +_SOKOL_PRIVATE void _saudio_sles_semaphore_post(_saudio_sles_semaphore_t* sem, int count) { int r = pthread_mutex_lock(&sem->mutex); SOKOL_ASSERT(r == 0); - for (int ii = 0; ii < count; ii++) { r = pthread_cond_signal(&sem->cond); SOKOL_ASSERT(r == 0); } - sem->count += count; r = pthread_mutex_unlock(&sem->mutex); SOKOL_ASSERT(r == 0); - (void)(r); } -_SOKOL_PRIVATE bool _saudio_semaphore_wait(_saudio_semaphore_t* sem) -{ +_SOKOL_PRIVATE bool _saudio_sles_semaphore_wait(_saudio_sles_semaphore_t* sem) { int r = pthread_mutex_lock(&sem->mutex); SOKOL_ASSERT(r == 0); - while (r == 0 && sem->count <= 0) { r = pthread_cond_wait(&sem->cond, &sem->mutex); } - bool ok = (r == 0); if (ok) { --sem->count; @@ -2032,7 +2073,7 @@ _SOKOL_PRIVATE bool _saudio_semaphore_wait(_saudio_semaphore_t* sem) } /* fill intermediate buffer with new data and reset buffer_pos */ -_SOKOL_PRIVATE void _saudio_opensles_fill_buffer(void) { +_SOKOL_PRIVATE void _saudio_sles_fill_buffer(void) { int src_buffer_frames = _saudio.buffer_frames; if (_saudio_has_callback()) { _saudio_stream_callback(_saudio.backend.src_buffer, src_buffer_frames, _saudio.num_channels); @@ -2046,21 +2087,20 @@ _SOKOL_PRIVATE void _saudio_opensles_fill_buffer(void) { } } -_SOKOL_PRIVATE void SLAPIENTRY _saudio_opensles_play_cb(SLPlayItf player, void *context, SLuint32 event) { - (void)(context); - (void)(player); - +_SOKOL_PRIVATE void SLAPIENTRY _saudio_sles_play_cb(SLPlayItf player, void *context, SLuint32 event) { + _SOKOL_UNUSED(context); + _SOKOL_UNUSED(player); if (event & SL_PLAYEVENT_HEADATEND) { - _saudio_semaphore_post(&_saudio.backend.buffer_sem, 1); + _saudio_sles_semaphore_post(&_saudio.backend.buffer_sem, 1); } } -_SOKOL_PRIVATE void* _saudio_opensles_thread_fn(void* param) { +_SOKOL_PRIVATE void* _saudio_sles_thread_fn(void* param) { _SOKOL_UNUSED(param); while (!_saudio.backend.thread_stop) { /* get next output buffer, advance, next buffer. */ int16_t* out_buffer = _saudio.backend.output_buffers[_saudio.backend.active_buffer]; - _saudio.backend.active_buffer = (_saudio.backend.active_buffer + 1) % SAUDIO_NUM_BUFFERS; + _saudio.backend.active_buffer = (_saudio.backend.active_buffer + 1) % SAUDIO_SLES_NUM_BUFFERS; int16_t* next_buffer = _saudio.backend.output_buffers[_saudio.backend.active_buffer]; /* queue this buffer */ @@ -2068,19 +2108,19 @@ _SOKOL_PRIVATE void* _saudio_opensles_thread_fn(void* param) { (*_saudio.backend.player_buffer_queue)->Enqueue(_saudio.backend.player_buffer_queue, out_buffer, (SLuint32)buffer_size_bytes); /* fill the next buffer */ - _saudio_opensles_fill_buffer(); + _saudio_sles_fill_buffer(); const int num_samples = _saudio.num_channels * _saudio.buffer_frames; for (int i = 0; i < num_samples; ++i) { next_buffer[i] = (int16_t) (_saudio.backend.src_buffer[i] * 0x7FFF); } - _saudio_semaphore_wait(&_saudio.backend.buffer_sem); + _saudio_sles_semaphore_wait(&_saudio.backend.buffer_sem); } return 0; } -_SOKOL_PRIVATE void _saudio_backend_shutdown(void) { +_SOKOL_PRIVATE void _saudio_sles_backend_shutdown(void) { _saudio.backend.thread_stop = 1; pthread_join(_saudio.backend.thread, 0); @@ -2096,16 +2136,18 @@ _SOKOL_PRIVATE void _saudio_backend_shutdown(void) { (*_saudio.backend.engine_obj)->Destroy(_saudio.backend.engine_obj); } - for (int i = 0; i < SAUDIO_NUM_BUFFERS; i++) { + for (int i = 0; i < SAUDIO_SLES_NUM_BUFFERS; i++) { _saudio_free(_saudio.backend.output_buffers[i]); } _saudio_free(_saudio.backend.src_buffer); } -_SOKOL_PRIVATE bool _saudio_backend_init(void) { +_SOKOL_PRIVATE bool _saudio_sles_backend_init(void) { + _SAUDIO_INFO(USING_SLES_BACKEND); + _saudio.bytes_per_frame = (int)sizeof(float) * _saudio.num_channels; - for (int i = 0; i < SAUDIO_NUM_BUFFERS; ++i) { + for (int i = 0; i < SAUDIO_SLES_NUM_BUFFERS; ++i) { const int buffer_size_bytes = (int)sizeof(int16_t) * _saudio.num_channels * _saudio.buffer_frames; _saudio.backend.output_buffers[i] = (int16_t*) _saudio_malloc_clear((size_t)buffer_size_bytes); } @@ -2118,15 +2160,15 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) { /* Create engine */ const SLEngineOption opts[] = { { SL_ENGINEOPTION_THREADSAFE, SL_BOOLEAN_TRUE } }; if (slCreateEngine(&_saudio.backend.engine_obj, 1, opts, 0, NULL, NULL ) != SL_RESULT_SUCCESS) { - SAUDIO_LOG("sokol_audio opensles: slCreateEngine failed"); - _saudio_backend_shutdown(); + _SAUDIO_ERROR(SLES_CREATE_ENGINE_FAILED); + _saudio_sles_backend_shutdown(); return false; } (*_saudio.backend.engine_obj)->Realize(_saudio.backend.engine_obj, SL_BOOLEAN_FALSE); if ((*_saudio.backend.engine_obj)->GetInterface(_saudio.backend.engine_obj, SL_IID_ENGINE, &_saudio.backend.engine) != SL_RESULT_SUCCESS) { - SAUDIO_LOG("sokol_audio opensles: GetInterface->Engine failed"); - _saudio_backend_shutdown(); + _SAUDIO_ERROR(SLES_ENGINE_GET_ENGINE_INTERFACE_FAILED); + _saudio_sles_backend_shutdown(); return false; } @@ -2135,22 +2177,21 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) { const SLInterfaceID ids[] = { SL_IID_VOLUME }; const SLboolean req[] = { SL_BOOLEAN_FALSE }; - if( (*_saudio.backend.engine)->CreateOutputMix(_saudio.backend.engine, &_saudio.backend.output_mix_obj, 1, ids, req) != SL_RESULT_SUCCESS) - { - SAUDIO_LOG("sokol_audio opensles: CreateOutputMix failed"); - _saudio_backend_shutdown(); + if ((*_saudio.backend.engine)->CreateOutputMix(_saudio.backend.engine, &_saudio.backend.output_mix_obj, 1, ids, req) != SL_RESULT_SUCCESS) { + _SAUDIO_ERROR(SLES_CREATE_OUTPUT_MIX_FAILED); + _saudio_sles_backend_shutdown(); return false; } (*_saudio.backend.output_mix_obj)->Realize(_saudio.backend.output_mix_obj, SL_BOOLEAN_FALSE); - if((*_saudio.backend.output_mix_obj)->GetInterface(_saudio.backend.output_mix_obj, SL_IID_VOLUME, &_saudio.backend.output_mix_vol) != SL_RESULT_SUCCESS) { - SAUDIO_LOG("sokol_audio opensles: GetInterface->OutputMixVol failed"); + if ((*_saudio.backend.output_mix_obj)->GetInterface(_saudio.backend.output_mix_obj, SL_IID_VOLUME, &_saudio.backend.output_mix_vol) != SL_RESULT_SUCCESS) { + _SAUDIO_WARN(SLES_MIXER_GET_VOLUME_INTERFACE_FAILED); } } /* android buffer queue */ _saudio.backend.in_locator.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; - _saudio.backend.in_locator.numBuffers = SAUDIO_NUM_BUFFERS; + _saudio.backend.in_locator.numBuffers = SAUDIO_SLES_NUM_BUFFERS; /* data format */ SLDataFormat_PCM format; @@ -2183,49 +2224,266 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) { const SLInterfaceID ids[] = { SL_IID_VOLUME, SL_IID_ANDROIDSIMPLEBUFFERQUEUE }; const SLboolean req[] = { SL_BOOLEAN_FALSE, SL_BOOLEAN_TRUE }; - (*_saudio.backend.engine)->CreateAudioPlayer(_saudio.backend.engine, &_saudio.backend.player_obj, &src, &_saudio.backend.dst_data_sink, sizeof(ids) / sizeof(ids[0]), ids, req); - + if ((*_saudio.backend.engine)->CreateAudioPlayer(_saudio.backend.engine, &_saudio.backend.player_obj, &src, &_saudio.backend.dst_data_sink, sizeof(ids) / sizeof(ids[0]), ids, req) != SL_RESULT_SUCCESS) + { + _SAUDIO_ERROR(SLES_ENGINE_CREATE_AUDIO_PLAYER_FAILED); + _saudio_sles_backend_shutdown(); + return false; + } (*_saudio.backend.player_obj)->Realize(_saudio.backend.player_obj, SL_BOOLEAN_FALSE); - (*_saudio.backend.player_obj)->GetInterface(_saudio.backend.player_obj, SL_IID_PLAY, &_saudio.backend.player); - (*_saudio.backend.player_obj)->GetInterface(_saudio.backend.player_obj, SL_IID_VOLUME, &_saudio.backend.player_vol); - - (*_saudio.backend.player_obj)->GetInterface(_saudio.backend.player_obj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &_saudio.backend.player_buffer_queue); + if ((*_saudio.backend.player_obj)->GetInterface(_saudio.backend.player_obj, SL_IID_PLAY, &_saudio.backend.player) != SL_RESULT_SUCCESS) { + _SAUDIO_ERROR(SLES_PLAYER_GET_PLAY_INTERFACE_FAILED); + _saudio_sles_backend_shutdown(); + return false; + } + if ((*_saudio.backend.player_obj)->GetInterface(_saudio.backend.player_obj, SL_IID_VOLUME, &_saudio.backend.player_vol) != SL_RESULT_SUCCESS) { + _SAUDIO_ERROR(SLES_PLAYER_GET_VOLUME_INTERFACE_FAILED); + } + if ((*_saudio.backend.player_obj)->GetInterface(_saudio.backend.player_obj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &_saudio.backend.player_buffer_queue) != SL_RESULT_SUCCESS) { + _SAUDIO_ERROR(SLES_PLAYER_GET_BUFFERQUEUE_INTERFACE_FAILED); + _saudio_sles_backend_shutdown(); + return false; + } } /* begin */ { const int buffer_size_bytes = (int)sizeof(int16_t) * _saudio.num_channels * _saudio.buffer_frames; (*_saudio.backend.player_buffer_queue)->Enqueue(_saudio.backend.player_buffer_queue, _saudio.backend.output_buffers[0], (SLuint32)buffer_size_bytes); - _saudio.backend.active_buffer = (_saudio.backend.active_buffer + 1) % SAUDIO_NUM_BUFFERS; + _saudio.backend.active_buffer = (_saudio.backend.active_buffer + 1) % SAUDIO_SLES_NUM_BUFFERS; - (*_saudio.backend.player)->RegisterCallback(_saudio.backend.player, _saudio_opensles_play_cb, NULL); + (*_saudio.backend.player)->RegisterCallback(_saudio.backend.player, _saudio_sles_play_cb, NULL); (*_saudio.backend.player)->SetCallbackEventsMask(_saudio.backend.player, SL_PLAYEVENT_HEADATEND); (*_saudio.backend.player)->SetPlayState(_saudio.backend.player, SL_PLAYSTATE_PLAYING); } /* create the buffer-streaming start thread */ - if (0 != pthread_create(&_saudio.backend.thread, 0, _saudio_opensles_thread_fn, 0)) { - _saudio_backend_shutdown(); + if (0 != pthread_create(&_saudio.backend.thread, 0, _saudio_sles_thread_fn, 0)) { + _saudio_sles_backend_shutdown(); return false; } return true; } -#ifdef __cplusplus -} /* extern "C" */ +// ██████ ██████ ██████ ███████ █████ ██ ██ ██████ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██████ █████ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██████ ██ ██ ███████ ██ ██ ██████ ██████ ██ ██████ +// +// >>coreaudio +#elif defined(_SAUDIO_APPLE) + +#if defined(_SAUDIO_IOS) +#if __has_feature(objc_arc) +#define _SAUDIO_OBJC_RELEASE(obj) { obj = nil; } +#else +#define _SAUDIO_OBJC_RELEASE(obj) { [obj release]; obj = nil; } #endif +@interface _saudio_interruption_handler : NSObject { } +@end + +@implementation _saudio_interruption_handler +-(id)init { + self = [super init]; + AVAudioSession* session = [AVAudioSession sharedInstance]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_interruption:) name:AVAudioSessionInterruptionNotification object:session]; + return self; +} + +-(void)dealloc { + [self remove_handler]; + #if !__has_feature(objc_arc) + [super dealloc]; + #endif +} + +-(void)remove_handler { + [[NSNotificationCenter defaultCenter] removeObserver:self name:@"AVAudioSessionInterruptionNotification" object:nil]; +} + +-(void)handle_interruption:(NSNotification*)notification { + AVAudioSession* session = [AVAudioSession sharedInstance]; + SOKOL_ASSERT(session); + NSDictionary* dict = notification.userInfo; + SOKOL_ASSERT(dict); + NSInteger type = [[dict valueForKey:AVAudioSessionInterruptionTypeKey] integerValue]; + switch (type) { + case AVAudioSessionInterruptionTypeBegan: + if (_saudio.backend.ca_audio_queue) { + AudioQueuePause(_saudio.backend.ca_audio_queue); + } + [session setActive:false error:nil]; + break; + case AVAudioSessionInterruptionTypeEnded: + [session setActive:true error:nil]; + if (_saudio.backend.ca_audio_queue) { + AudioQueueStart(_saudio.backend.ca_audio_queue, NULL); + } + break; + default: + break; + } +} +@end +#endif // _SAUDIO_IOS + +/* NOTE: the buffer data callback is called on a separate thread! */ +_SOKOL_PRIVATE void _saudio_coreaudio_callback(void* user_data, _saudio_AudioQueueRef queue, _saudio_AudioQueueBufferRef buffer) { + _SOKOL_UNUSED(user_data); + if (_saudio_has_callback()) { + const int num_frames = (int)buffer->mAudioDataByteSize / _saudio.bytes_per_frame; + const int num_channels = _saudio.num_channels; + _saudio_stream_callback((float*)buffer->mAudioData, num_frames, num_channels); + } + else { + uint8_t* ptr = (uint8_t*)buffer->mAudioData; + int num_bytes = (int) buffer->mAudioDataByteSize; + if (0 == _saudio_fifo_read(&_saudio.fifo, ptr, num_bytes)) { + /* not enough read data available, fill the entire buffer with silence */ + _saudio_clear(ptr, (size_t)num_bytes); + } + } + AudioQueueEnqueueBuffer(queue, buffer, 0, NULL); +} + +_SOKOL_PRIVATE void _saudio_coreaudio_backend_shutdown(void) { + if (_saudio.backend.ca_audio_queue) { + AudioQueueStop(_saudio.backend.ca_audio_queue, true); + AudioQueueDispose(_saudio.backend.ca_audio_queue, false); + _saudio.backend.ca_audio_queue = 0; + } + #if defined(_SAUDIO_IOS) + /* remove interruption handler */ + if (_saudio.backend.ca_interruption_handler != nil) { + [_saudio.backend.ca_interruption_handler remove_handler]; + _SAUDIO_OBJC_RELEASE(_saudio.backend.ca_interruption_handler); + } + /* deactivate audio session */ + AVAudioSession* session = [AVAudioSession sharedInstance]; + SOKOL_ASSERT(session); + [session setActive:false error:nil];; + #endif // _SAUDIO_IOS +} + +_SOKOL_PRIVATE bool _saudio_coreaudio_backend_init(void) { + SOKOL_ASSERT(0 == _saudio.backend.ca_audio_queue); + + #if defined(_SAUDIO_IOS) + /* activate audio session */ + AVAudioSession* session = [AVAudioSession sharedInstance]; + SOKOL_ASSERT(session != nil); + [session setCategory: AVAudioSessionCategoryPlayback error:nil]; + [session setActive:true error:nil]; + + /* create interruption handler */ + _saudio.backend.ca_interruption_handler = [[_saudio_interruption_handler alloc] init]; + #endif + + /* create an audio queue with fp32 samples */ + _saudio_AudioStreamBasicDescription fmt; + _saudio_clear(&fmt, sizeof(fmt)); + fmt.mSampleRate = (double) _saudio.sample_rate; + fmt.mFormatID = _saudio_kAudioFormatLinearPCM; + fmt.mFormatFlags = _saudio_kLinearPCMFormatFlagIsFloat | _saudio_kAudioFormatFlagIsPacked; + fmt.mFramesPerPacket = 1; + fmt.mChannelsPerFrame = (uint32_t) _saudio.num_channels; + fmt.mBytesPerFrame = (uint32_t)sizeof(float) * (uint32_t)_saudio.num_channels; + fmt.mBytesPerPacket = fmt.mBytesPerFrame; + fmt.mBitsPerChannel = 32; + _saudio_OSStatus res = AudioQueueNewOutput(&fmt, _saudio_coreaudio_callback, 0, NULL, NULL, 0, &_saudio.backend.ca_audio_queue); + if (0 != res) { + _SAUDIO_ERROR(COREAUDIO_NEW_OUTPUT_FAILED); + return false; + } + SOKOL_ASSERT(_saudio.backend.ca_audio_queue); + + /* create 2 audio buffers */ + for (int i = 0; i < 2; i++) { + _saudio_AudioQueueBufferRef buf = NULL; + const uint32_t buf_byte_size = (uint32_t)_saudio.buffer_frames * fmt.mBytesPerFrame; + res = AudioQueueAllocateBuffer(_saudio.backend.ca_audio_queue, buf_byte_size, &buf); + if (0 != res) { + _SAUDIO_ERROR(COREAUDIO_ALLOCATE_BUFFER_FAILED); + _saudio_coreaudio_backend_shutdown(); + return false; + } + buf->mAudioDataByteSize = buf_byte_size; + _saudio_clear(buf->mAudioData, buf->mAudioDataByteSize); + AudioQueueEnqueueBuffer(_saudio.backend.ca_audio_queue, buf, 0, NULL); + } + + /* init or modify actual playback parameters */ + _saudio.bytes_per_frame = (int)fmt.mBytesPerFrame; + + /* ...and start playback */ + res = AudioQueueStart(_saudio.backend.ca_audio_queue, NULL); + if (0 != res) { + _SAUDIO_ERROR(COREAUDIO_START_FAILED); + _saudio_coreaudio_backend_shutdown(); + return false; + } + return true; +} + #else #error "unsupported platform" #endif -/*=== PUBLIC API FUNCTIONS ===================================================*/ +bool _saudio_backend_init(void) { + #if defined(SOKOL_DUMMY_BACKEND) + return _saudio_dummy_backend_init(); + #elif defined(_SAUDIO_LINUX) + return _saudio_alsa_backend_init(); + #elif defined(_SAUDIO_WINDOWS) + return _saudio_wasapi_backend_init(); + #elif defined(_SAUDIO_EMSCRIPTEN) + return _saudio_webaudio_backend_init(); + #elif defined(SAUDIO_ANDROID_AAUDIO) + return _saudio_aaudio_backend_init(); + #elif defined(SAUDIO_ANDROID_SLES) + return _saudio_sles_backend_init(); + #elif defined(_SAUDIO_APPLE) + return _saudio_coreaudio_backend_init(); + #else + #error "unknown platform" + #endif +} + +void _saudio_backend_shutdown(void) { + #if defined(SOKOL_DUMMY_BACKEND) + _saudio_dummy_backend_shutdown(); + #elif defined(_SAUDIO_LINUX) + _saudio_alsa_backend_shutdown(); + #elif defined(_SAUDIO_WINDOWS) + _saudio_wasapi_backend_shutdown(); + #elif defined(_SAUDIO_EMSCRIPTEN) + _saudio_webaudio_backend_shutdown(); + #elif defined(SAUDIO_ANDROID_AAUDIO) + _saudio_aaudio_backend_shutdown(); + #elif defined(SAUDIO_ANDROID_SLES) + _saudio_sles_backend_shutdown(); + #elif defined(_SAUDIO_APPLE) + return _saudio_coreaudio_backend_shutdown(); + #else + #error "unknown platform" + #endif +} + +// ██████ ██ ██ ██████ ██ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ ██ ██████ +// +// >>public SOKOL_API_IMPL void saudio_setup(const saudio_desc* desc) { SOKOL_ASSERT(!_saudio.valid); SOKOL_ASSERT(desc); - SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free)); + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); _saudio_clear(&_saudio, sizeof(_saudio)); _saudio.desc = *desc; _saudio.stream_cb = desc->stream_cb; @@ -2243,7 +2501,7 @@ SOKOL_API_IMPL void saudio_setup(const saudio_desc* desc) { the requested packet size */ if (0 != (_saudio.buffer_frames % _saudio.packet_frames)) { - SAUDIO_LOG("sokol_audio.h: actual backend buffer size isn't multiple of requested packet size"); + _SAUDIO_ERROR(BACKEND_BUFFER_SIZE_ISNT_MULTIPLE_OF_PACKET_SIZE); _saudio_backend_shutdown(); return; } @@ -2251,12 +2509,16 @@ SOKOL_API_IMPL void saudio_setup(const saudio_desc* desc) { _saudio_fifo_init(&_saudio.fifo, _saudio.packet_frames * _saudio.bytes_per_frame, _saudio.num_packets); _saudio.valid = true; } + else { + _saudio_fifo_destroy_mutex(&_saudio.fifo); + } } SOKOL_API_IMPL void saudio_shutdown(void) { if (_saudio.valid) { _saudio_backend_shutdown(); _saudio_fifo_shutdown(&_saudio.fifo); + _saudio_fifo_destroy_mutex(&_saudio.fifo); _saudio.valid = false; } } diff --git a/thirdparty/sokol/sokol_gfx.h b/thirdparty/sokol/sokol_gfx.h index 53df14bbeb9744..62509bddb546ef 100644 --- a/thirdparty/sokol/sokol_gfx.h +++ b/thirdparty/sokol/sokol_gfx.h @@ -18,7 +18,6 @@ In the same place define one of the following to select the rendering backend: #define SOKOL_GLCORE33 - #define SOKOL_GLES2 #define SOKOL_GLES3 #define SOKOL_D3D11 #define SOKOL_METAL @@ -89,11 +88,36 @@ sg_setup(const sg_desc*) + Depending on the selected 3D backend, sokol-gfx requires some + information, like a device pointer framebuffer pixel formats + and so on. If you are using sokol_app.h for the window system + glue, you can use a helper function provided in the sokol_glue.h + header: + + #include "sokol_gfx.h" + #include "sokol_app.h" + #include "sokol_glue.h" + //... + sg_setup(&(sg_desc){ + .context = sapp_sgcontext(), + }); + + To get any logging output for errors and from the validation layer, you + need to provide a logging callback. Easiest way is through sokol_log.h: + + #include "sokol_log.h" + //... + sg_setup(&(sg_desc){ + //... + .logger.func = slog_func, + }); + --- create resource objects (at least buffers, shaders and pipelines, - and optionally images and passes): + and optionally images, samplers and passes): sg_buffer sg_make_buffer(const sg_buffer_desc*) sg_image sg_make_image(const sg_image_desc*) + sg_sampler sg_make_sampler(const sg_sampler_desc*) sg_shader sg_make_shader(const sg_shader_desc*) sg_pipeline sg_make_pipeline(const sg_pipeline_desc*) sg_pass sg_make_pass(const sg_pass_desc*) @@ -117,8 +141,8 @@ sg_apply_pipeline(sg_pipeline pip) --- fill an sg_bindings struct with the resource bindings for the next - draw call (1..N vertex buffers, 0 or 1 index buffer, 0..N image objects - to use as textures each on the vertex-shader- and fragment-shader-stage + draw call (1..N vertex buffers, 0 or 1 index buffer, 0..N image objects and + 0..N sampler objects on the vertex-shader- and fragment-shader-stage and then call sg_apply_bindings(const sg_bindings* bindings) @@ -162,6 +186,7 @@ sg_destroy_buffer(sg_buffer buf) sg_destroy_image(sg_image img) + sg_destroy_sampler(sg_sampler smp) sg_destroy_shader(sg_shader shd) sg_destroy_pipeline(sg_pipeline pip) sg_destroy_pass(sg_pass pass) @@ -170,7 +195,7 @@ sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left) - ...or if you want to specifiy the viewport rectangle with float values: + ...or if you want to specify the viewport rectangle with float values: sg_apply_viewportf(float x, float y, float width, float height, bool origin_top_left) @@ -277,27 +302,32 @@ by calling sg_query_desc(). This will return an sg_desc struct with the default values patched in instead of any zero-initialized values - --- you can inspect various internal resource attributes via: + --- you can get a desc struct matching the creation attributes of a + specific resource object via: - sg_buffer_info sg_query_buffer_info(sg_buffer buf) - sg_image_info sg_query_image_info(sg_image img) - sg_shader_info sg_query_shader_info(sg_shader shd) - sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip) - sg_pass_info sg_query_pass_info(sg_pass pass) - - ...please note that the returned info-structs are tied quite closely - to sokol_gfx.h internals, and may change more often than other - public API functions and structs. + sg_buffer_desc sg_query_buffer_desc(sg_buffer buf) + sg_image_desc sg_query_image_desc(sg_image img) + sg_sampler_desc sg_query_sampler_desc(sg_sampler smp) + sg_shader_desc sq_query_shader_desc(sg_shader shd) + sg_pipeline_desc sg_query_pipeline_desc(sg_pipeline pip) + sg_pass_desc sg_query_pass_desc(sg_pass pass) - --- you can ask at runtime what backend sokol_gfx.h has been compiled - for, or whether the GLES3 backend had to fall back to GLES2 with: + ...but NOTE that the returned desc structs may be incomplete, only + creation attributes that are kept around internally after resource + creation will be filled in, and in some cases (like shaders) that's + very little. Any missing attributes will be set to zero. The returned + desc structs might still be useful as partial blueprint for creating + similar resources if filled up with the missing attributes. - sg_backend sg_query_backend(void) + Calling the query-desc functions on an invalid resource will return + completely zeroed structs (it makes sense to check the resource state + with sg_query_*_state() first) --- you can query the default resource creation parameters through the functions sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc* desc) sg_image_desc sg_query_image_defaults(const sg_image_desc* desc) + sg_sampler_desc sg_query_sampler_defaults(const sg_sampler_desc* desc) sg_shader_desc sg_query_shader_defaults(const sg_shader_desc* desc) sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc* desc) sg_pass_desc sg_query_pass_defaults(const sg_pass_desc* desc) @@ -307,6 +337,23 @@ will be replaced with their concrete values in the returned desc struct. + --- you can inspect various internal resource runtime values via: + + sg_buffer_info sg_query_buffer_info(sg_buffer buf) + sg_image_info sg_query_image_info(sg_image img) + sg_sampler_info sg_query_sampler_info(sg_sampler smp) + sg_shader_info sg_query_shader_info(sg_shader shd) + sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip) + sg_pass_info sg_query_pass_info(sg_pass pass) + + ...please note that the returned info-structs are tied quite closely + to sokol_gfx.h internals, and may change more often than other + public API functions and structs. + + --- you can ask at runtime what backend sokol_gfx.h has been compiled for: + + sg_backend sg_query_backend(void) + ON INITIALIZATION: ================== @@ -324,8 +371,6 @@ 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. @@ -341,7 +386,309 @@ 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. + See the documentation block of the sg_desc struct below for more information. + + + ON RENDER PASSES + ================ + Relevant samples: + - https://floooh.github.io/sokol-html5/offscreen-sapp.html + - https://floooh.github.io/sokol-html5/offscreen-msaa-sapp.html + - https://floooh.github.io/sokol-html5/mrt-sapp.html + - https://floooh.github.io/sokol-html5/mrt-pixelformats-sapp.html + + A render pass wraps rendering commands into a common set of render target images + (called 'pass attachments'). Render target images can be used in subsequent + passes as textures (it is invalid to use the same image both as render target + and as texture in the same pass). + + The following sokol-gfx functions must be called inside a render pass: + + sg_apply_viewport(f) + sg_apply_scissor_rect(f) + sg_apply_pipeline + sg_apply_bindings + sg_apply_uniforms + sg_draw + + A frame must have at least one render pass, and this must be the 'default + pass' which renders into the 'default' (swapchain) framebuffer. The default + pass must always be the last pass in the frame before the sg_commit() + call. + + The default and offscreen passes form a dependency tree with the default + pass at the root, offscreen passes as nodes, and render target images as + dependencies between passes. + + For offscreen render passes, the render target images used in a render pass + are baked into an immutable sg_pass object (for the default pass, the + pass-state is managed internally instead). + + For a simple offscreen scenario with one color-, one depth-stencil-render + target and without multisampling, creating a pass object looks like this: + + First create two render target images, one with a color pixel format, + and one with the depth- or depth-stencil pixel format. Both images + must have the same dimensions: + + const sg_image color_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 256, + .height = 256, + .pixel_format = SG_PIXELFORMAT_RGBA8, + .sample_count = 1, + }); + const sg_image depth_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 256, + .height = 256, + .pixel_format = SG_PIXELFORMAT_DEPTH, + .sample_count = 1, + }); + + NOTE: when creating render target images, have in mind that some default values + are aligned with the default framebuffer attributes, this is sometimes not + what you want: + + - the default values for .pixel_format and .sample_count are the same + as the default framebuffer + - the default value for .num_mipmaps is always 1 + + Next create a pass object: + + const sg_pass pass = sg_make_pass(&(sg_pass_desc){ + .color_attachments[0].image = color_img, + .depth_stencil_attachment.image = depth_img, + }); + + When using the sg_pass object in a render pass you also need to define + what actions should happen at the start and end of the render pass + in an sg_pass_action struct (for instance whether the render target should + be cleared). + + A typical sg_pass_action object which clears the color attachment to black + might look like this: + + const sg_pass_action = { + .colors[0] = { + .load_action = SG_LOADACTION_CLEAR, + .clear_value = { 0.0f, 0.0f, 0.0f, 1.0f } + } + }; + + This omits the defaults for the color attachment store action, and + the depth-stencil-attachments actions. The same pass action with the + defaults explicitly filled in would look like this: + + const sg_pass_action = { + .colors[0] = { + .load_action = SG_LOADACTION_CLEAR, + .store_action = SG_STOREACTION_STORE, + .clear_value = { 0.0f, 0.0f, 0.0f, 1.0f } + }, + .depth = = { + .load_action = SG_LOADACTION_CLEAR, + .store_action = SG_STOREACTION_DONTCARE, + .clear_value = 1.0f, + }, + .stencil = { + .load_action = SG_LOADACTION_CLEAR, + .store_action = SG_STOREACTION_DONTCARE, + .clear_value = 0 + } + }; + + With the sg_pass object and sg_pass_action struct in place everything + is ready now for the actual render pass: + + sg_begin_pass(pass, &pass_action); + ... + sg_end_pass(); + + Offscreen rendering can also go into a mipmap, or a slice/face of + a cube-, array- or 3d-image (which some restrictions, for instance + it's not possible to create a 3D image with a depth/stencil pixel format, + these exceptions are generally caught by the sokol-gfx validation layer). + + The mipmap/slice selection happens at pass creation time, for instance + to render into mipmap 2 of slice 3 of an array texture: + + const sg_pass pass = sg_make_pass(&(sg_pass_desc){ + .color_attachments[0] = { + .image = color_img, + .mip_level = 2, + .slice = 3, + }, + .depth_stencil_attachment.image = depth_img, + }); + + If MSAA offscreen rendering is desired, the multi-sample rendering result + must be 'resolved' into a separate 'resolve image', before that image can + be used as texture. + + NOTE: currently multisample-images cannot be bound as textures. + + Creating a simple pass object for multisampled rendering requires + 3 attachment images: the color attachment image which has a sample + count > 1, a resolve attachment image of the same size and pixel format + but a sample count == 1, and a depth/stencil attachment image with + the same size and sample count as the color attachment image: + + const sg_image color_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 256, + .height = 256, + .pixel_format = SG_PIXELFORMAT_RGBA8, + .sample_count = 4, + }); + const sg_image resolve_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 256, + .height = 256, + .pixel_format = SG_PIXELFORMAT_RGBA8, + .sample_count = 1, + }); + const sg_image depth_img = sg_make_image(&(sg_image_desc){ + .render_target = true, + .width = 256, + .height = 256, + .pixel_format = SG_PIXELFORMAT_DEPTH, + .sample_count = 4, + }); + + ...create the pass object: + + const sg_pass pass = sg_make_pass(&(sg_pass_desc){ + .color_attachments[0].image = color_img, + .resolve_attachments[0].image = resolve_img, + .depth_stencil_attachment.image = depth_img, + }); + + If a pass object defines a resolve image in a specific resolve attachment slot, + an 'msaa resolve operation' will happen in sg_end_pass(). + + In this scenario, the content of the MSAA color attachment doesn't need to be + preserved (since it's only needed inside sg_end_pass for the msaa-resolve), so + the .store_action should be set to "don't care": + + const sg_pass_action = { + .colors[0] = { + .load_action = SG_LOADACTION_CLEAR, + .store_action = SG_STOREACTION_DONTCARE, + .clear_value = { 0.0f, 0.0f, 0.0f, 1.0f } + } + }; + + The actual render pass looks as usual: + + sg_begin_pass(pass, &pass_action); + ... + sg_end_pass(); + + ...after sg_end_pass() the only difference to the non-msaa scenario is that the + rendering result which is going to be used as texture in a followup pass is + in 'resolve_img', not in 'color_img' (in fact, trying to bind color_img as a + texture would result in a validation error). + + + ON SHADER CREATION + ================== + sokol-gfx doesn't come with an integrated shader cross-compiler, instead + backend-specific shader sources or binary blobs need to be provided when + creating a shader object, along with information about the shader interface + needed in the sokol-gfx validation layer and to properly bind shader resources + on the CPU-side to be consumable by the GPU-side. + + The easiest way to provide all this shader creation data is to use the + sokol-shdc shader compiler tool to compile shaders from a common + GLSL syntax into backend-specific sources or binary blobs, along with + shader interface information and uniform blocks mapped to C structs. + + To create a shader using a C header which has been code-generated by sokol-shdc: + + // include the C header code-generated by sokol-shdc: + #include "myshader.glsl.h" + ... + + // create shader using a code-generated helper function from the C header: + sg_shader shd = sg_make_shader(myshader_shader_desc(sg_query_backend())); + + The samples in the 'sapp' subdirectory of the sokol-samples project + also use the sokol-shdc approach: + + https://github.com/floooh/sokol-samples/tree/master/sapp + + If you're planning to use sokol-shdc, you can stop reading here, instead + continue with the sokol-shdc documentation: + + https://github.com/floooh/sokol-tools/blob/master/docs/sokol-shdc.md + + To create shaders with backend-specific shader code or binary blobs, + the sg_make_shader() function requires the following information: + + - Shader code or shader binary blobs for the vertex- and fragment- shader-stage: + - for the desktop GL backend, source code must be provided in '#version 330' syntax + - for the GLES3 backend, source code must be provided in '#version 300 es' syntax + - for the D3D11 backend, shaders can be provided as source or binary blobs, the + source code should be in HLSL4.0 (for best compatibility) or alternatively + in HLSL5.0 syntax (other versions may work but are not tested), NOTE: when + shader source code is provided for the D3D11 backend, sokol-gfx will dynamically + load 'd3dcompiler_47.dll' + - for the Metal backends, shaders can be provided as source or binary blobs, the + MSL version should be in 'metal-1.1' (other versions may work but are not tested) + - optionally the following shader-code related attributes can be provided: + - an entry function name (only on D3D11 or Metal, but not OpenGL) + - on D3D11 only, a compilation target (default is "vs_4_0" and "ps_4_0") + + - Depending on backend, information about the input vertex attributes used by the + vertex shader: + - Metal: no information needed since vertex attributes are always bound + by their attribute location defined in the shader via '[[attribute(N)]]' + - GLSL: vertex attribute names can be optionally provided, in that case their + location will be looked up by name, otherwise, the vertex attribute location + can be defined with 'layout(location = N)', PLEASE NOTE that the name-lookup method + may be removed at some point + - D3D11: a 'semantic name' and 'semantic index' must be provided for each vertex + attribute, e.g. if the vertex attribute is defined as 'TEXCOORD1' in the shader, + the semantic name would be 'TEXCOORD', and the semantic index would be '1' + + - Information about each uniform block used in the shader: + - The size of the uniform block in number of bytes. + - A memory layout hint (currently 'native' or 'std140') where 'native' defines a + backend-specific memory layout which shouldn't be used for cross-platform code. + Only std140 guarantees a backend-agnostic memory layout. + - For GLSL only: a description of the internal uniform block layout, which maps + member types and their offsets on the CPU side to uniform variable names + in the GLSL shader + - please also NOTE the documentation sections about UNIFORM DATA LAYOUT + and CROSS-BACKEND COMMON UNIFORM DATA LAYOUT below! + + - A description of each texture/image used in the shader: + - the expected image type (e.g. 2D, 3D, etc...) + - the 'image sample type' (e.g. float, depth, signed- or unsigned-int) + - a flag whether the texture is expected to be multisampled + (currently it's not supported to fetch data from multisampled + textures in shaders, but this is planned for a later time) + + - A description of each sampler used in the shader: + - just wether the sampler is a regular 'sampling sampler', + or a 'comparison sampler' (which is usually used for + shadow mapping) + + - An array of 'image-sampler-pairs' used by the shader to sample textures, + for D3D11 and Metal this is only used for validation purposes to check + whether the texture and sampler are compatible with each other. For GLSL + an additional 'combined-image-sampler name' must be provided because + 'OpenGL style GLSL' cannot handle separate texture and sampler objects, + but still groups them into a tradtional GLSL 'sampler object'. + + For example code of how to create backend-specific shader objects, + please refer to the following samples: + + - for D3D11: https://github.com/floooh/sokol-samples/tree/master/d3d11 + - for Metal: https://github.com/floooh/sokol-samples/tree/master/metal + - for OpenGL: https://github.com/floooh/sokol-samples/tree/master/glfw + - for GLES3: https://github.com/floooh/sokol-samples/tree/master/html5 UNIFORM DATA LAYOUT: @@ -421,7 +768,7 @@ layout on the CPU side can be used for all sokol-gfx backends. To achieve this, a common subset of the std140 layout must be used: - - The uniform block layout hint in sg_shader_desc must be explicitely set to + - The uniform block layout hint in sg_shader_desc must be explicitly set to SG_UNIFORMLAYOUT_STD140. - Only the following GLSL uniform types can be used (with their associated sokol-gfx enums): - float => SG_UNIFORMTYPE_FLOAT @@ -449,98 +796,10 @@ std140 layout rules, this means that the cbuffer declarations in HLSL code must be tweaked so that the layout is compatible with std140. - The by far easiest way to tacke the common uniform block layout problem is + The by far easiest way to tackle the common uniform block layout problem is to use the sokol-shdc shader cross-compiler tool! - BACKEND-SPECIFIC TOPICS: - ======================== - --- The GL backends need to know about the internal structure of uniform - blocks, and the texture sampler-name and -type. The uniform layout details - are described in the UNIFORM DATA LAYOUT section above. - - // uniform block structure and texture image definition in sg_shader_desc: - sg_shader_desc desc = { - // uniform block description (size and internal structure) - .vs.uniform_blocks[0] = { - ... - }, - // one texture on the fragment-shader-stage, GLES2/WebGL needs name and image type - .fs.images[0] = { .name="tex", .type=SG_IMAGETYPE_ARRAY } - ... - }; - - --- the Metal and D3D11 backends only need to know the size of uniform blocks, - not their internal member structure, and they only need to know - the type of a texture sampler, not its name: - - sg_shader_desc desc = { - .vs.uniform_blocks[0].size = sizeof(params_t), - .fs.images[0].type = SG_IMAGETYPE_ARRAY, - ... - }; - - --- when creating a shader object, GLES2/WebGL need to know the vertex - attribute names as used in the vertex shader: - - sg_shader_desc desc = { - .attrs = { - [0] = { .name="position" }, - [1] = { .name="color1" } - } - }; - - The vertex attribute names provided when creating a shader will be - used later in sg_create_pipeline() for matching the vertex layout - to vertex shader inputs. - - --- on D3D11 you need to provide a semantic name and semantic index in the - shader description struct instead (see the D3D11 documentation on - D3D11_INPUT_ELEMENT_DESC for details): - - sg_shader_desc desc = { - .attrs = { - [0] = { .sem_name="POSITION", .sem_index=0 } - [1] = { .sem_name="COLOR", .sem_index=1 } - } - }; - - 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): - - sg_pipeline_desc desc = { - .layout = { - .attrs = { - [0] = { .format=SG_VERTEXFORMAT_FLOAT3 }, - [1] = { .format=SG_VERTEXFORMAT_FLOAT4 } - } - } - }; - - WORKING WITH CONTEXTS ===================== sokol-gfx allows to switch between different rendering contexts and @@ -643,11 +902,7 @@ - SG_VERTEXFORMAT_SHORT2 - SG_VERTEXFORMAT_SHORT4 - - WebGL/GLES2 cannot use integer vertex shader inputs (int or ivecn) - - - SG_VERTEXFORMAT_UINT10_N2 is not supported on WebGL/GLES2 - - So for a vertex input layout which works on all platforms, only use the following + For a vertex input layout which works on all platforms, only use the following vertex formats, and if needed "expand" the normalized vertex shader inputs in the vertex shader by multiplying with 127.0, 255.0, 32767.0 or 65535.0: @@ -662,6 +917,9 @@ - SG_VERTEXFORMAT_USHORT2N - SG_VERTEXFORMAT_SHORT4N, - SG_VERTEXFORMAT_USHORT4N + - SG_VERTEXFORMAT_UINT10_N2 + - SG_VERTEXFORMAT_HALF2 + - SG_VERTEXFORMAT_HALF4 MEMORY ALLOCATION OVERRIDE @@ -681,8 +939,8 @@ sg_setup(&(sg_desc){ // ... .allocator = { - .alloc = my_alloc, - .free = my_free, + .alloc_fn = my_alloc, + .free_fn = my_free, .user_data = ..., } }); @@ -694,26 +952,43 @@ itself though, not any allocations in OS libraries. - LOG FUNCTION OVERRIDE - ===================== - You can override the log function at initialization time like this: + ERROR REPORTING AND LOGGING + =========================== + To get any logging information at all you need to provide a logging callback in the setup call + the easiest way is to use sokol_log.h: + + #include "sokol_log.h" - void my_log(const char* message, void* user_data) { - printf("sg says: \s\n", message); + sg_setup(&(sg_desc){ .logger.func = slog_func }); + + To override logging with your own callback, first write a logging function like this: + + void my_log(const char* tag, // e.g. 'sg' + uint32_t log_level, // 0=panic, 1=error, 2=warn, 3=info + uint32_t log_item_id, // SG_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_gfx.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data) + { + ... } - ... - sg_setup(&(sg_desc){ - // ... - .logger = { - .log_cb = my_log, - .user_data = ..., - } - }); - ... + ...and then setup sokol-gfx like this: + + sg_setup(&(sg_desc){ + .logger = { + .func = my_log, + .user_data = my_user_data, + } + }); + + The provided logging function must be reentrant (e.g. be callable from + different threads). - If no overrides are provided, puts will be used on most platforms. - On Android, __android_log_write will be used instead. + If you don't want to provide your own custom logger it is highly recommended to use + the standard logger in sokol_log.h instead, otherwise you won't see any warnings or + errors. COMMIT LISTENERS @@ -767,6 +1042,7 @@ sg_buffer sg_make_buffer(const sg_buffer_desc* desc) sg_image sg_make_image(const sg_image_desc* desc) + sg_sampler sg_make_sampler(const sg_sampler_desc* desc) sg_shader sg_make_shader(const sg_shader_desc* desc) sg_pipeline sg_make_pipeline(const sg_pipeline_desc* desc) sg_pass sg_make_pass(const sg_pass_desc* desc) @@ -824,6 +1100,7 @@ sg_buffer sg_alloc_buffer(void) sg_image sg_alloc_image(void) + sg_sampler sg_alloc_sampler(void) sg_shader sg_alloc_shader(void) sg_pipeline sg_alloc_pipeline(void) sg_pass sg_alloc_pass(void) @@ -848,6 +1125,7 @@ void sg_init_buffer(sg_buffer buf, const sg_buffer_desc* desc) void sg_init_image(sg_image img, const sg_image_desc* desc) + void sg_init_sampler(sg_sampler smp, const sg_sampler_desc* desc) void sg_init_shader(sg_shader shd, const sg_shader_desc* desc) void sg_init_pipeline(sg_pipeline pip, const sg_pipeline_desc* desc) void sg_init_pass(sg_pass pass, const sg_pass_desc* desc) @@ -863,6 +1141,7 @@ void sg_uninit_buffer(sg_buffer buf) void sg_uninit_image(sg_image img) + void sg_uninit_sampler(sg_sampler smp) void sg_uninit_shader(sg_shader shd) void sg_uninit_pipeline(sg_pipeline pip) void sg_uninit_pass(sg_pass pass) @@ -874,6 +1153,7 @@ void sg_dealloc_buffer(sg_buffer buf) void sg_dealloc_image(sg_image img) + void sg_dealloc_sampler(sg_sampler smp) void sg_dealloc_shader(sg_shader shd) void sg_dealloc_pipeline(sg_pipeline pip) void sg_dealloc_pass(sg_pass pass) @@ -886,6 +1166,7 @@ void sg_destroy_buffer(sg_buffer buf) void sg_destroy_image(sg_image img) + void sg_destroy_sampler(sg_sampler smp) void sg_destroy_shader(sg_shader shd) void sg_destroy_pipeline(sg_pipeline pip) void sg_destroy_pass(sg_pass pass) @@ -899,23 +1180,24 @@ sg_fail_buffer(sg_buffer buf) sg_fail_image(sg_image img) + sg_fail_sampler(sg_sampler smp) sg_fail_shader(sg_shader shd) sg_fail_pipeline(sg_pipeline pip) sg_fail_pass(sg_pass pass) This is recommended if anything went wrong outside of sokol-gfx during asynchronous - resource creation (for instance the file loading operation failed). In this case, + resource creation (for instance a file loading operation failed). In this case, the 'fail function' should be called instead of the 'init function'. Calling a 'fail function' on a resource that's not in ALLOC state is a no-op, but will generate a warning log message. NOTE: that two-step resource creation usually only makes sense for buffers - and images, but not for shaders, pipelines or passes. Most notably, trying + and images, but not for samplers, shaders, pipelines or passes. Most notably, trying to create a pipeline object with a shader that's not in VALID state will trigger a validation layer error, or if the validation layer is disabled, result in a pipeline object in FAILED state. Same when trying to create - a pass object with image invalid image objects. + a pass object with invalid image objects. LICENSE ======= @@ -968,7 +1250,8 @@ extern "C" { Resource id typedefs: sg_buffer: vertex- and index-buffers - sg_image: textures and render targets + sg_image: images used as textures and render targets + sg_sampler sampler object describing how a texture is sampled in a shader sg_shader: vertex- and fragment-shaders, uniform blocks sg_pipeline: associated shader and vertex-layouts, and render states sg_pass: a bundle of render targets and actions on them @@ -988,6 +1271,7 @@ extern "C" { */ typedef struct sg_buffer { uint32_t id; } sg_buffer; typedef struct sg_image { uint32_t id; } sg_image; +typedef struct sg_sampler { uint32_t id; } sg_sampler; typedef struct sg_shader { uint32_t id; } sg_shader; typedef struct sg_pipeline { uint32_t id; } sg_pipeline; typedef struct sg_pass { uint32_t id; } sg_pass; @@ -1007,8 +1291,8 @@ typedef struct sg_range { // disabling this for every includer isn't great, but the warnings are also quite pointless #if defined(_MSC_VER) -#pragma warning(disable:4221) /* /W4 only: nonstandard extension used: 'x': cannot be initialized using address of automatic variable 'y' */ -#pragma warning(disable:4204) /* VS2015: nonstandard extension used: non-constant aggregate initializer */ +#pragma warning(disable:4221) // /W4 only: nonstandard extension used: 'x': cannot be initialized using address of automatic variable 'y' +#pragma warning(disable:4204) // VS2015: nonstandard extension used: non-constant aggregate initializer #endif #if defined(__cplusplus) #define SG_RANGE(x) sg_range{ &x, sizeof(x) } @@ -1024,11 +1308,13 @@ enum { SG_NUM_SHADER_STAGES = 2, SG_NUM_INFLIGHT_FRAMES = 2, SG_MAX_COLOR_ATTACHMENTS = 4, - SG_MAX_SHADERSTAGE_BUFFERS = 8, + SG_MAX_VERTEX_BUFFERS = 8, SG_MAX_SHADERSTAGE_IMAGES = 12, + SG_MAX_SHADERSTAGE_SAMPLERS = 8, + SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS = 12, SG_MAX_SHADERSTAGE_UBS = 4, SG_MAX_UB_MEMBERS = 16, - SG_MAX_VERTEX_ATTRIBUTES = 16, /* NOTE: actual max vertex attrs can be less on GLES2, see sg_limits! */ + SG_MAX_VERTEX_ATTRIBUTES = 16, SG_MAX_MIPMAPS = 16, SG_MAX_TEXTUREARRAY_LAYERS = 128 }; @@ -1045,14 +1331,9 @@ typedef struct sg_color { float r, g, b, a; } sg_color; The active 3D-API backend, use the function sg_query_backend() to get the currently active backend. - - NOTE that SG_BACKEND_GLES2 will be returned if sokol-gfx was - compiled with SOKOL_GLES3, but the runtime platform doesn't support - GLES3/WebGL2 and sokol-gfx had to fallback to GLES2/WebGL. */ typedef enum sg_backend { SG_BACKEND_GLCORE33, - SG_BACKEND_GLES2, SG_BACKEND_GLES3, SG_BACKEND_D3D11, SG_BACKEND_METAL_IOS, @@ -1066,10 +1347,7 @@ typedef enum sg_backend { sg_pixel_format sokol_gfx.h basically uses the same pixel formats as WebGPU, since these - are supported on most newer GPUs. GLES2 and WebGL only supports a much - smaller subset of actually available pixel formats. Call - sg_query_pixelformat() to check at runtime if a pixel format supports the - desired features. + are supported on most newer GPUs. A pixelformat name consist of three parts: @@ -1097,11 +1375,6 @@ typedef enum sg_backend { pixelformat for render targets - depth: the pixelformat can be used for depth-stencil attachments - When targeting GLES2/WebGL, the only safe formats to use - as texture are SG_PIXELFORMAT_R8 and SG_PIXELFORMAT_RGBA8. For rendering - in GLES2/WebGL, only SG_PIXELFORMAT_RGBA8 is safe. All other formats - must be checked via sg_query_pixelformats(). - The default pixel format for texture images is SG_PIXELFORMAT_RGBA8. The default pixel format for render target images is platform-dependent: @@ -1114,7 +1387,7 @@ typedef enum sg_backend { use whatever renderable pixel format is convenient for you. */ typedef enum sg_pixel_format { - _SG_PIXELFORMAT_DEFAULT, /* value 0 reserved for default-init */ + _SG_PIXELFORMAT_DEFAULT, // value 0 reserved for default-init SG_PIXELFORMAT_NONE, SG_PIXELFORMAT_R8, @@ -1141,6 +1414,7 @@ typedef enum sg_pixel_format { SG_PIXELFORMAT_RG16SI, SG_PIXELFORMAT_RG16F, SG_PIXELFORMAT_RGBA8, + SG_PIXELFORMAT_SRGB8A8, SG_PIXELFORMAT_RGBA8SN, SG_PIXELFORMAT_RGBA8UI, SG_PIXELFORMAT_RGBA8SI, @@ -1195,8 +1469,8 @@ typedef enum sg_pixel_format { by sg_query_pixelformat(). */ typedef struct sg_pixelformat_info { - bool sample; // pixel format can be sampled in shaders - bool filter; // pixel format can be sampled with filtering + bool sample; // pixel format can be sampled in shaders at least with nearest filtering + bool filter; // pixel format can be sampled with linear filtering bool render; // pixel format can be used as render target bool blend; // alpha-blending is supported bool msaa; // pixel format can be used as MSAA render target @@ -1211,12 +1485,7 @@ typedef struct sg_pixelformat_info { returned by sg_query_features() */ typedef struct sg_features { - 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 bool mrt_independent_blend_state; // multiple-render-target rendering can use per-render-target blend state bool mrt_independent_write_mask; // multiple-render-target rendering can use per-render-target color write masks @@ -1234,8 +1503,9 @@ typedef struct sg_limits { int max_image_size_3d; // max width/height/depth of SG_IMAGETYPE_3D images int max_image_size_array; // max width/height of SG_IMAGETYPE_ARRAY images int max_image_array_layers; // max number of layers in SG_IMAGETYPE_ARRAY images - int max_vertex_attrs; // <= SG_MAX_VERTEX_ATTRIBUTES or less (on some GLES2 impls) + int max_vertex_attrs; // max number of vertex attributes, clamped to SG_MAX_VERTEX_ATTRIBUTES int gl_max_vertex_uniform_vectors; // <= GL_MAX_VERTEX_UNIFORM_VECTORS (only on GL backends) + int gl_max_combined_texture_image_units; // <= GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS (only on GL backends) } sg_limits; /* @@ -1300,7 +1570,7 @@ typedef enum sg_resource_state { The default usage is SG_USAGE_IMMUTABLE. */ typedef enum sg_usage { - _SG_USAGE_DEFAULT, /* value 0 reserved for default-init */ + _SG_USAGE_DEFAULT, // value 0 reserved for default-init SG_USAGE_IMMUTABLE, SG_USAGE_DYNAMIC, SG_USAGE_STREAM, @@ -1317,7 +1587,7 @@ typedef enum sg_usage { The default value is SG_BUFFERTYPE_VERTEXBUFFER. */ typedef enum sg_buffer_type { - _SG_BUFFERTYPE_DEFAULT, /* value 0 reserved for default-init */ + _SG_BUFFERTYPE_DEFAULT, // value 0 reserved for default-init SG_BUFFERTYPE_VERTEXBUFFER, SG_BUFFERTYPE_INDEXBUFFER, _SG_BUFFERTYPE_NUM, @@ -1335,7 +1605,7 @@ typedef enum sg_buffer_type { The default index type is SG_INDEXTYPE_NONE. */ typedef enum sg_index_type { - _SG_INDEXTYPE_DEFAULT, /* value 0 reserved for default-init */ + _SG_INDEXTYPE_DEFAULT, // value 0 reserved for default-init SG_INDEXTYPE_NONE, SG_INDEXTYPE_UINT16, SG_INDEXTYPE_UINT32, @@ -1347,16 +1617,15 @@ typedef enum sg_index_type { sg_image_type 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. + 3D-texture or 2D-array-texture). Used in the sg_image_desc.type member when + creating an image, and in sg_shader_image_desc to describe a sampled texture + in the shader (both must match and will be checked in the validation layer + when calling sg_apply_bindings). The default image type when creating an image is SG_IMAGETYPE_2D. */ typedef enum sg_image_type { - _SG_IMAGETYPE_DEFAULT, /* value 0 reserved for default-init */ + _SG_IMAGETYPE_DEFAULT, // value 0 reserved for default-init SG_IMAGETYPE_2D, SG_IMAGETYPE_CUBE, SG_IMAGETYPE_3D, @@ -1366,20 +1635,36 @@ typedef enum sg_image_type { } sg_image_type; /* - sg_sampler_type + sg_image_sample_type + + The basic data type of a texture sample as expected by a shader. + Must be provided in sg_shader_image_desc and used by the validation + layer in sg_apply_bindings() to check if the provided image object + is compatible with what the shader expects, and also required by the + WebGPU backend. +*/ +typedef enum sg_image_sample_type { + _SG_IMAGESAMPLETYPE_DEFAULT, // value 0 reserved for default-init + SG_IMAGESAMPLETYPE_FLOAT, + SG_IMAGESAMPLETYPE_DEPTH, + SG_IMAGESAMPLETYPE_SINT, + SG_IMAGESAMPLETYPE_UINT, + _SG_IMAGESAMPLETYPE_NUM, + _SG_IMAGESAMPLETYPE_FORCE_U32 = 0x7FFFFFFF +} sg_image_sample_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. +/* + sg_sampler_type - The default sampler type is SG_SAMPLERTYPE_FLOAT. + The basic type of a texture sampler (sampling vs comparison) as + defined in a shader. Must be provided in sg_shader_sampler_desc. */ typedef enum sg_sampler_type { - _SG_SAMPLERTYPE_DEFAULT, /* value 0 reserved for default-init */ - SG_SAMPLERTYPE_FLOAT, - SG_SAMPLERTYPE_SINT, - SG_SAMPLERTYPE_UINT, + _SG_SAMPLERTYPE_DEFAULT, + SG_SAMPLERTYPE_SAMPLE, + SG_SAMPLERTYPE_COMPARE, + _SG_SAMPLERTYPE_NUM, + _SG_SAMPLERTYPE_FORCE_U32, } sg_sampler_type; /* @@ -1426,7 +1711,7 @@ typedef enum sg_shader_stage { The default primitive type is SG_PRIMITIVETYPE_TRIANGLES. */ typedef enum sg_primitive_type { - _SG_PRIMITIVETYPE_DEFAULT, /* value 0 reserved for default-init */ + _SG_PRIMITIVETYPE_DEFAULT, // value 0 reserved for default-init SG_PRIMITIVETYPE_POINTS, SG_PRIMITIVETYPE_LINES, SG_PRIMITIVETYPE_LINE_STRIP, @@ -1440,19 +1725,25 @@ typedef enum sg_primitive_type { sg_filter The filtering mode when sampling a texture image. This is - used in the sg_image_desc.min_filter and sg_image_desc.mag_filter - members when creating an image object. + used in the sg_sampler_desc.min_filter, sg_sampler_desc.mag_filter + and sg_sampler_desc.mipmap_filter members when creating a sampler object. + + For min_filter and mag_filter the default is SG_FILTER_NEAREST. - The default filter mode is SG_FILTER_NEAREST. + For mipmap_filter the default is SG_FILTER_NONE. + + The following restrictions apply: + + - an image object with (num_mipmaps == 1) must use SG_FILTER_NONE + - min_filter and mag_filter cannot be SG_FILTER_NONE + + Those restrictions are checked in the validation layer. */ typedef enum sg_filter { - _SG_FILTER_DEFAULT, /* value 0 reserved for default-init */ + _SG_FILTER_DEFAULT, // value 0 reserved for default-init + SG_FILTER_NONE, SG_FILTER_NEAREST, SG_FILTER_LINEAR, - SG_FILTER_NEAREST_MIPMAP_NEAREST, - SG_FILTER_NEAREST_MIPMAP_LINEAR, - SG_FILTER_LINEAR_MIPMAP_NEAREST, - SG_FILTER_LINEAR_MIPMAP_LINEAR, _SG_FILTER_NUM, _SG_FILTER_FORCE_U32 = 0x7FFFFFFF } sg_filter; @@ -1473,20 +1764,9 @@ typedef enum sg_filter { Platforms which don't support SG_WRAP_CLAMP_TO_BORDER will silently fall back to SG_WRAP_CLAMP_TO_EDGE without a validation error. - - Platforms which support clamp-to-border are: - - - all desktop GL platforms - - Metal on macOS - - D3D11 - - Platforms which do not support clamp-to-border: - - - GLES2/3 and WebGL/WebGL2 - - Metal on iOS */ typedef enum sg_wrap { - _SG_WRAP_DEFAULT, /* value 0 reserved for default-init */ + _SG_WRAP_DEFAULT, // value 0 reserved for default-init SG_WRAP_REPEAT, SG_WRAP_CLAMP_TO_EDGE, SG_WRAP_CLAMP_TO_BORDER, @@ -1504,7 +1784,7 @@ typedef enum sg_wrap { The default border color is SG_BORDERCOLOR_OPAQUE_BLACK */ typedef enum sg_border_color { - _SG_BORDERCOLOR_DEFAULT, /* value 0 reserved for default-init */ + _SG_BORDERCOLOR_DEFAULT, // value 0 reserved for default-init SG_BORDERCOLOR_TRANSPARENT_BLACK, SG_BORDERCOLOR_OPAQUE_BLACK, SG_BORDERCOLOR_OPAQUE_WHITE, @@ -1535,6 +1815,8 @@ typedef enum sg_vertex_format { SG_VERTEXFORMAT_SHORT4N, SG_VERTEXFORMAT_USHORT4N, SG_VERTEXFORMAT_UINT10_N2, + SG_VERTEXFORMAT_HALF2, + SG_VERTEXFORMAT_HALF4, _SG_VERTEXFORMAT_NUM, _SG_VERTEXFORMAT_FORCE_U32 = 0x7FFFFFFF } sg_vertex_format; @@ -1551,7 +1833,7 @@ typedef enum sg_vertex_format { when creating pipeline objects. */ typedef enum sg_vertex_step { - _SG_VERTEXSTEP_DEFAULT, /* value 0 reserved for default-init */ + _SG_VERTEXSTEP_DEFAULT, // value 0 reserved for default-init SG_VERTEXSTEP_PER_VERTEX, SG_VERTEXSTEP_PER_INSTANCE, _SG_VERTEXSTEP_NUM, @@ -1615,9 +1897,9 @@ typedef enum sg_uniform_type { at the start of the header. */ typedef enum sg_uniform_layout { - _SG_UNIFORMLAYOUT_DEFAULT, /* value 0 reserved for default-init */ - SG_UNIFORMLAYOUT_NATIVE, /* default: layout depends on currently active backend */ - SG_UNIFORMLAYOUT_STD140, /* std140: memory layout according to std140 */ + _SG_UNIFORMLAYOUT_DEFAULT, // value 0 reserved for default-init + SG_UNIFORMLAYOUT_NATIVE, // default: layout depends on currently active backend + SG_UNIFORMLAYOUT_STD140, // std140: memory layout according to std140 _SG_UNIFORMLAYOUT_NUM, _SG_UNIFORMLAYOUT_FORCE_U32 = 0x7FFFFFFF } sg_uniform_layout; @@ -1632,7 +1914,7 @@ typedef enum sg_uniform_layout { The default cull mode is SG_CULLMODE_NONE */ typedef enum sg_cull_mode { - _SG_CULLMODE_DEFAULT, /* value 0 reserved for default-init */ + _SG_CULLMODE_DEFAULT, // value 0 reserved for default-init SG_CULLMODE_NONE, SG_CULLMODE_FRONT, SG_CULLMODE_BACK, @@ -1650,7 +1932,7 @@ typedef enum sg_cull_mode { The default winding is SG_FACEWINDING_CW (clockwise) */ typedef enum sg_face_winding { - _SG_FACEWINDING_DEFAULT, /* value 0 reserved for default-init */ + _SG_FACEWINDING_DEFAULT, // value 0 reserved for default-init SG_FACEWINDING_CCW, SG_FACEWINDING_CW, _SG_FACEWINDING_NUM, @@ -1660,8 +1942,9 @@ typedef enum sg_face_winding { /* sg_compare_func - The compare-function for depth- and stencil-ref tests. - This is used when creating pipeline objects in the members: + The compare-function for configuring depth- and stencil-ref tests + in pipeline objects, and for texture samplers which perform a comparison + instead of regular sampling operation. sg_pipeline_desc .depth @@ -1670,11 +1953,16 @@ typedef enum sg_face_winding { .front.compare .back.compar + sg_sampler_desc + .compare + The default compare func for depth- and stencil-tests is SG_COMPAREFUNC_ALWAYS. + + The default compare func for sampler is SG_COMPAREFUNC_NEVER. */ typedef enum sg_compare_func { - _SG_COMPAREFUNC_DEFAULT, /* value 0 reserved for default-init */ + _SG_COMPAREFUNC_DEFAULT, // value 0 reserved for default-init SG_COMPAREFUNC_NEVER, SG_COMPAREFUNC_LESS, SG_COMPAREFUNC_EQUAL, @@ -1708,7 +1996,7 @@ typedef enum sg_compare_func { The default value is SG_STENCILOP_KEEP. */ typedef enum sg_stencil_op { - _SG_STENCILOP_DEFAULT, /* value 0 reserved for default-init */ + _SG_STENCILOP_DEFAULT, // value 0 reserved for default-init SG_STENCILOP_KEEP, SG_STENCILOP_ZERO, SG_STENCILOP_REPLACE, @@ -1739,7 +2027,7 @@ typedef enum sg_stencil_op { factors, and SG_BLENDFACTOR_ZERO for destination factors. */ typedef enum sg_blend_factor { - _SG_BLENDFACTOR_DEFAULT, /* value 0 reserved for default-init */ + _SG_BLENDFACTOR_DEFAULT, // value 0 reserved for default-init SG_BLENDFACTOR_ZERO, SG_BLENDFACTOR_ONE, SG_BLENDFACTOR_SRC_COLOR, @@ -1775,7 +2063,7 @@ typedef enum sg_blend_factor { The default value is SG_BLENDOP_ADD. */ typedef enum sg_blend_op { - _SG_BLENDOP_DEFAULT, /* value 0 reserved for default-init */ + _SG_BLENDOP_DEFAULT, // value 0 reserved for default-init SG_BLENDOP_ADD, SG_BLENDOP_SUBTRACT, SG_BLENDOP_REVERSE_SUBTRACT, @@ -1797,8 +2085,8 @@ typedef enum sg_blend_op { should be disabled. */ typedef enum sg_color_mask { - _SG_COLORMASK_DEFAULT = 0, /* value 0 reserved for default-init */ - SG_COLORMASK_NONE = 0x10, /* special value for 'all channels disabled */ + _SG_COLORMASK_DEFAULT = 0, // value 0 reserved for default-init + SG_COLORMASK_NONE = 0x10, // special value for 'all channels disabled SG_COLORMASK_R = 0x1, SG_COLORMASK_G = 0x2, SG_COLORMASK_RG = 0x3, @@ -1818,64 +2106,75 @@ typedef enum sg_color_mask { } sg_color_mask; /* - sg_action + sg_load_action - Defines what action should be performed at the start of a render pass: + Defines the load action that should be performed at the start of a render pass: - SG_ACTION_CLEAR: clear the render target image - SG_ACTION_LOAD: load the previous content of the render target image - SG_ACTION_DONTCARE: leave the render target image content undefined + SG_LOADACTION_CLEAR: clear the render target + SG_LOADACTION_LOAD: load the previous content of the render target + SG_LOADACTION_DONTCARE: leave the render target in an undefined state This is used in the sg_pass_action structure. - The default action for all pass attachments is SG_ACTION_CLEAR, with the - clear color rgba = {0.5f, 0.5f, 0.5f, 1.0f], depth=1.0 and stencil=0. + The default load action for all pass attachments is SG_LOADACTION_CLEAR, + with the values rgba = { 0.5f, 0.5f, 0.5f, 1.0f }, depth=1.0f and stencil=0. If you want to override the default behaviour, it is important to not only set the clear color, but the 'action' field as well (as long as this - is in its _SG_ACTION_DEFAULT, the value fields will be ignored). + is _SG_LOADACTION_DEFAULT, the value fields will be ignored). */ -typedef enum sg_action { - _SG_ACTION_DEFAULT, - SG_ACTION_CLEAR, - SG_ACTION_LOAD, - SG_ACTION_DONTCARE, - _SG_ACTION_NUM, - _SG_ACTION_FORCE_U32 = 0x7FFFFFFF -} sg_action; +typedef enum sg_load_action { + _SG_LOADACTION_DEFAULT, + SG_LOADACTION_CLEAR, + SG_LOADACTION_LOAD, + SG_LOADACTION_DONTCARE, + _SG_LOADACTION_FORCE_U32 = 0x7FFFFFFF +} sg_load_action; /* - sg_pass_action + sg_store_action + + Defines the store action that be performed at the end of a render pass: + + SG_STOREACTION_STORE: store the rendered content to the color attachment image + SG_STOREACTION_DONTCARE: allows the GPU to discard the rendered content +*/ +typedef enum sg_store_action { + _SG_STOREACTION_DEFAULT, + SG_STOREACTION_STORE, + SG_STOREACTION_DONTCARE, + _SG_STOREACTION_FORCE_U32 = 0x7FFFFFFF +} sg_store_action; - The sg_pass_action struct defines the actions to be performed - at the start of a rendering pass in the functions sg_begin_pass() - and sg_begin_default_pass(). - A separate action and clear values can be defined for each - color attachment, and for the depth-stencil attachment. +/* + sg_pass_action - The default clear values are defined by the macros: + The sg_pass_action struct defines the actions to be performed + at the start of and end of a render pass. - - SG_DEFAULT_CLEAR_RED: 0.5f - - SG_DEFAULT_CLEAR_GREEN: 0.5f - - SG_DEFAULT_CLEAR_BLUE: 0.5f - - SG_DEFAULT_CLEAR_ALPHA: 1.0f - - SG_DEFAULT_CLEAR_DEPTH: 1.0f - - SG_DEFAULT_CLEAR_STENCIL: 0 + - at the start of the pass whether the render targets should be cleared + loaded with their previous content, or start in an undefined state + - for clear operations: the clear value (color, depth, or stencil values) + - at the end of the pass, whether the rendering result should be + stored back into the render target, or discarded */ typedef struct sg_color_attachment_action { - sg_action action; - sg_color value; + sg_load_action load_action; // default: SG_LOADACTION_CLEAR + sg_store_action store_action; // default: SG_STOREACTION_STORE + sg_color clear_value; // default: { 0.5f, 0.5f, 0.5f, 1.0f } } sg_color_attachment_action; typedef struct sg_depth_attachment_action { - sg_action action; - float value; + sg_load_action load_action; // default: SG_LOADACTION_CLEAR + sg_store_action store_action; // default: SG_STOREACTION_DONTCARE + float clear_value; // default: 1.0 } sg_depth_attachment_action; typedef struct sg_stencil_attachment_action { - sg_action action; - uint8_t value; + sg_load_action load_action; // default: SG_LOADACTION_CLEAR + sg_store_action store_action; // default: SG_STOREACTION_DONTCARE + uint8_t clear_value; // default: 0 } sg_stencil_attachment_action; typedef struct sg_pass_action { @@ -1900,23 +2199,30 @@ typedef struct sg_pass_action { - 0..1 index buffers - 0..1 index buffer offsets - 0..N vertex shader stage images + - 0..N vertex shader stage samplers - 0..N fragment shader stage images + - 0..N fragment shader stage samplers The max number of vertex buffer and shader stage images - are defined by the SG_MAX_SHADERSTAGE_BUFFERS and + are defined by the SG_MAX_VERTEX_BUFFERS and SG_MAX_SHADERSTAGE_IMAGES configuration constants. 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_stage_bindings { + sg_image images[SG_MAX_SHADERSTAGE_IMAGES]; + sg_sampler samplers[SG_MAX_SHADERSTAGE_SAMPLERS]; +} sg_stage_bindings; + typedef struct sg_bindings { uint32_t _start_canary; - sg_buffer vertex_buffers[SG_MAX_SHADERSTAGE_BUFFERS]; - int vertex_buffer_offsets[SG_MAX_SHADERSTAGE_BUFFERS]; + sg_buffer vertex_buffers[SG_MAX_VERTEX_BUFFERS]; + int vertex_buffer_offsets[SG_MAX_VERTEX_BUFFERS]; sg_buffer index_buffer; int index_buffer_offset; - sg_image vs_images[SG_MAX_SHADERSTAGE_IMAGES]; - sg_image fs_images[SG_MAX_SHADERSTAGE_IMAGES]; + sg_stage_bindings vs; + sg_stage_bindings fs; uint32_t _end_canary; } sg_bindings; @@ -1980,13 +2286,10 @@ typedef struct sg_buffer_desc { sg_usage usage; sg_range data; const char* label; - /* GL specific */ + // optionally inject backend-specific resources uint32_t gl_buffers[SG_NUM_INFLIGHT_FRAMES]; - /* Metal specific */ 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; @@ -2005,8 +2308,7 @@ typedef struct sg_image_data { /* sg_image_desc - Creation parameters for sg_image objects, used in the sg_make_image() - call. + Creation parameters for sg_image objects, used in the sg_make_image() call. The default configuration is: @@ -2019,17 +2321,8 @@ typedef struct sg_image_data { .usage: SG_USAGE_IMMUTABLE .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 targets - .min_filter: SG_FILTER_NEAREST - .mag_filter: SG_FILTER_NEAREST - .wrap_u: SG_WRAP_REPEAT - .wrap_v: SG_WRAP_REPEAT - .wrap_w: SG_WRAP_REPEAT (only SG_IMAGETYPE_3D) - .border_color SG_BORDERCOLOR_OPAQUE_BLACK - .max_anisotropy 1 (must be 1..16) - .min_lod 0.0f - .max_lod FLT_MAX .data an sg_image_data struct to define the initial content - .label 0 (optional string label for trace hooks) + .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? @@ -2042,11 +2335,6 @@ typedef struct sg_image_data { 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 if array- and - 3D-textures are supported. - Images with usage SG_USAGE_IMMUTABLE must be fully initialized by providing a valid .data member which points to initialization data. @@ -2083,30 +2371,58 @@ typedef struct sg_image_desc { 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; - float min_lod; - float max_lod; sg_image_data data; const char* label; - /* GL specific */ + // optionally inject backend-specific resources uint32_t gl_textures[SG_NUM_INFLIGHT_FRAMES]; uint32_t gl_texture_target; - /* Metal specific */ const void* mtl_textures[SG_NUM_INFLIGHT_FRAMES]; - /* D3D11 specific */ const void* d3d11_texture; const void* d3d11_shader_resource_view; - /* WebGPU specific */ const void* wgpu_texture; uint32_t _end_canary; } sg_image_desc; +/* + sg_sampler_desc + + Creation parameters for sg_sampler objects, used in the sg_make_sampler() call + + .min_filter: SG_FILTER_NEAREST + .mag_filter: SG_FILTER_NEAREST + .mipmap_filter SG_FILTER_NONE + .wrap_u: SG_WRAP_REPEAT + .wrap_v: SG_WRAP_REPEAT + .wrap_w: SG_WRAP_REPEAT (only SG_IMAGETYPE_3D) + .min_lod 0.0f + .max_lod FLT_MAX + .border_color SG_BORDERCOLOR_OPAQUE_BLACK + .compare SG_COMPAREFUNC_NEVER + .max_anisotropy 1 (must be 1..16) + +*/ +typedef struct sg_sampler_desc { + uint32_t _start_canary; + sg_filter min_filter; + sg_filter mag_filter; + sg_filter mipmap_filter; + sg_wrap wrap_u; + sg_wrap wrap_v; + sg_wrap wrap_w; + float min_lod; + float max_lod; + sg_border_color border_color; + sg_compare_func compare; + uint32_t max_anisotropy; + const char* label; + // optionally inject backend-specific resources + uint32_t gl_sampler; + const void* mtl_sampler; + const void* d3d11_sampler; + const void* wgpu_sampler; + uint32_t _end_canary; +} sg_sampler_desc; + /* sg_shader_desc @@ -2114,7 +2430,7 @@ typedef struct sg_image_desc { programs, used as input to the sg_make_shader() function: - reflection information for vertex attributes (vertex shader inputs): - - vertex attribute name (required for GLES2, optional for GLES3 and GL) + - vertex attribute name (only optionally used by GLES3 and GL) - a semantic name and index (required for D3D11) - for each shader-stage (vertex and fragment): - the shader source or bytecode @@ -2128,10 +2444,16 @@ typedef struct sg_image_desc { - member name - member type (SG_UNIFORMTYPE_xxx) - if the member is an array, the number of array items - - reflection info for the texture images used by the shader stage: + - reflection info for textures used in 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) + - the image-sample type (SG_IMAGESAMPLETYPE_xxx, default is SG_IMAGESAMPLETYPE_FLOAT) + - whether the shader expects a multisampled texture + - reflection info for samplers used in the shader stage: + - the sampler type (SG_SAMPLERTYPE_xxx) + - reflection info for each image-sampler-pair used by the shader: + - the texture slot of the involved texture + - the sampler slot of the involved sampler + - for GLSL only: the name of the combined image-sampler object For all GL backends, shader source-code must be provided. For D3D11 and Metal, either shader source-code or byte-code can be provided. @@ -2143,7 +2465,7 @@ typedef struct sg_image_desc { 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 strictly required for GLES2) + const char* name; // GLSL vertex attribute name (optional) const char* sem_name; // HLSL semantic name int sem_index; // HLSL semantic index } sg_shader_attr_desc; @@ -2161,11 +2483,24 @@ typedef struct sg_shader_uniform_block_desc { } sg_shader_uniform_block_desc; typedef struct sg_shader_image_desc { - const char* name; + bool used; + bool multisampled; sg_image_type image_type; - sg_sampler_type sampler_type; + sg_image_sample_type sample_type; } sg_shader_image_desc; +typedef struct sg_shader_sampler_desc { + bool used; + sg_sampler_type sampler_type; +} sg_shader_sampler_desc; + +typedef struct sg_shader_image_sampler_pair_desc { + bool used; + int image_slot; + int sampler_slot; + const char* glsl_name; +} sg_shader_image_sampler_pair_desc; + typedef struct sg_shader_stage_desc { const char* source; sg_range bytecode; @@ -2173,6 +2508,8 @@ typedef struct sg_shader_stage_desc { 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_sampler_desc samplers[SG_MAX_SHADERSTAGE_SAMPLERS]; + sg_shader_image_sampler_pair_desc image_sampler_pairs[SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS]; } sg_shader_stage_desc; typedef struct sg_shader_desc { @@ -2253,28 +2590,28 @@ typedef struct sg_shader_desc { .alpha_to_coverage_enabled: false .label 0 (optional string label for trace hooks) */ -typedef struct sg_buffer_layout_desc { +typedef struct sg_vertex_buffer_layout_state { int stride; sg_vertex_step step_func; int step_rate; #if defined(SOKOL_ZIG_BINDINGS) uint32_t __pad[2]; #endif -} sg_buffer_layout_desc; +} sg_vertex_buffer_layout_state; -typedef struct sg_vertex_attr_desc { +typedef struct sg_vertex_attr_state { int buffer_index; int offset; sg_vertex_format format; #if defined(SOKOL_ZIG_BINDINGS) uint32_t __pad[2]; #endif -} sg_vertex_attr_desc; +} sg_vertex_attr_state; -typedef struct sg_layout_desc { - sg_buffer_layout_desc buffers[SG_MAX_SHADERSTAGE_BUFFERS]; - sg_vertex_attr_desc attrs[SG_MAX_VERTEX_ATTRIBUTES]; -} sg_layout_desc; +typedef struct sg_vertex_layout_state { + sg_vertex_buffer_layout_state buffers[SG_MAX_VERTEX_BUFFERS]; + sg_vertex_attr_state attrs[SG_MAX_VERTEX_ATTRIBUTES]; +} sg_vertex_layout_state; typedef struct sg_stencil_face_state { sg_compare_func compare; @@ -2311,20 +2648,20 @@ typedef struct sg_blend_state { sg_blend_op op_alpha; } sg_blend_state; -typedef struct sg_color_state { +typedef struct sg_color_target_state { sg_pixel_format pixel_format; sg_color_mask write_mask; sg_blend_state blend; -} sg_color_state; +} sg_color_target_state; typedef struct sg_pipeline_desc { uint32_t _start_canary; sg_shader shader; - sg_layout_desc layout; + sg_vertex_layout_state layout; sg_depth_state depth; sg_stencil_state stencil; int color_count; - sg_color_state colors[SG_MAX_COLOR_ATTACHMENTS]; + sg_color_target_state colors[SG_MAX_COLOR_ATTACHMENTS]; sg_primitive_type primitive_type; sg_index_type index_type; sg_cull_mode cull_mode; @@ -2339,34 +2676,41 @@ typedef struct sg_pipeline_desc { /* sg_pass_desc - Creation parameters for an sg_pass object, used as argument - to the sg_make_pass() function. + Creation parameters for an sg_pass object, used as argument to the + sg_make_pass() function. + + A pass object contains 1..4 color attachments, 0..4 msaa-resolve + attachemnts, and none or one depth-stencil attachment. - 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 to: one mipmap index, and - if the image is a cubemap, array-texture or 3D-texture, the - face-index, array-layer or depth-slice. + Each attachment consists of an image, and two additional indices describing + which subimage the pass will render into: one mipmap index, and if the image + is a cubemap, array-texture or 3D-texture, the face-index, array-layer or + depth-slice. - Pass images must fulfill the following requirements: + All attachments must have the same width and height. - All images must have: - - been created as render target (sg_image_desc.render_target = true) - - the same size - - the same sample count + All color attachments and the depth-stencil attachment must have the + same sample count. - In addition, all color-attachment images must have the same pixel format. + If a resolve attachment is set, an MSAA-resolve operation from the + associated color attachment into the resolve attachment image will take + place in the sg_end_pass() function. In this case, the color attachment + must have a (sample_count>1), and the resolve attachment a + (sample_count==1). The resolve attachment also must have the same pixel + format as the color attachment. + + NOTE that MSAA depth-stencil attachments cannot be msaa-resolved! */ typedef struct sg_pass_attachment_desc { sg_image image; int mip_level; - int slice; /* cube texture: face; array texture: layer; 3D texture: slice */ + int slice; // cube texture: face; array texture: layer; 3D texture: slice } sg_pass_attachment_desc; typedef struct sg_pass_desc { uint32_t _start_canary; sg_pass_attachment_desc color_attachments[SG_MAX_COLOR_ATTACHMENTS]; + sg_pass_attachment_desc resolve_attachments[SG_MAX_COLOR_ATTACHMENTS]; sg_pass_attachment_desc depth_stencil_attachment; const char* label; uint32_t _end_canary; @@ -2389,11 +2733,13 @@ typedef struct sg_trace_hooks { void (*reset_state_cache)(void* user_data); void (*make_buffer)(const sg_buffer_desc* desc, sg_buffer result, void* user_data); void (*make_image)(const sg_image_desc* desc, sg_image result, void* user_data); + void (*make_sampler)(const sg_sampler_desc* desc, sg_sampler result, void* user_data); void (*make_shader)(const sg_shader_desc* desc, sg_shader result, void* user_data); void (*make_pipeline)(const sg_pipeline_desc* desc, sg_pipeline result, void* user_data); void (*make_pass)(const sg_pass_desc* desc, sg_pass result, void* user_data); void (*destroy_buffer)(sg_buffer buf, void* user_data); void (*destroy_image)(sg_image img, void* user_data); + void (*destroy_sampler)(sg_sampler smp, void* user_data); void (*destroy_shader)(sg_shader shd, void* user_data); void (*destroy_pipeline)(sg_pipeline pip, void* user_data); void (*destroy_pass)(sg_pass pass, void* user_data); @@ -2412,45 +2758,42 @@ typedef struct sg_trace_hooks { void (*commit)(void* user_data); void (*alloc_buffer)(sg_buffer result, void* user_data); void (*alloc_image)(sg_image result, void* user_data); + void (*alloc_sampler)(sg_sampler result, void* user_data); void (*alloc_shader)(sg_shader result, void* user_data); void (*alloc_pipeline)(sg_pipeline result, void* user_data); void (*alloc_pass)(sg_pass result, void* user_data); void (*dealloc_buffer)(sg_buffer buf_id, void* user_data); void (*dealloc_image)(sg_image img_id, void* user_data); + void (*dealloc_sampler)(sg_sampler smp_id, void* user_data); void (*dealloc_shader)(sg_shader shd_id, void* user_data); void (*dealloc_pipeline)(sg_pipeline pip_id, void* user_data); void (*dealloc_pass)(sg_pass pass_id, void* user_data); void (*init_buffer)(sg_buffer buf_id, const sg_buffer_desc* desc, void* user_data); void (*init_image)(sg_image img_id, const sg_image_desc* desc, void* user_data); + void (*init_sampler)(sg_sampler smp_id, const sg_sampler_desc* desc, void* user_data); void (*init_shader)(sg_shader shd_id, const sg_shader_desc* desc, void* user_data); void (*init_pipeline)(sg_pipeline pip_id, const sg_pipeline_desc* desc, void* user_data); void (*init_pass)(sg_pass pass_id, const sg_pass_desc* desc, void* user_data); void (*uninit_buffer)(sg_buffer buf_id, void* user_data); void (*uninit_image)(sg_image img_id, void* user_data); + void (*uninit_sampler)(sg_sampler smp_id, void* user_data); void (*uninit_shader)(sg_shader shd_id, void* user_data); void (*uninit_pipeline)(sg_pipeline pip_id, void* user_data); void (*uninit_pass)(sg_pass pass_id, void* user_data); void (*fail_buffer)(sg_buffer buf_id, void* user_data); void (*fail_image)(sg_image img_id, void* user_data); + void (*fail_sampler)(sg_sampler smp_id, void* user_data); void (*fail_shader)(sg_shader shd_id, void* user_data); void (*fail_pipeline)(sg_pipeline pip_id, void* user_data); void (*fail_pass)(sg_pass pass_id, void* user_data); void (*push_debug_group)(const char* name, void* user_data); void (*pop_debug_group)(void* user_data); - void (*err_buffer_pool_exhausted)(void* user_data); - void (*err_image_pool_exhausted)(void* user_data); - void (*err_shader_pool_exhausted)(void* user_data); - void (*err_pipeline_pool_exhausted)(void* user_data); - void (*err_pass_pool_exhausted)(void* user_data); - void (*err_context_mismatch)(void* user_data); - void (*err_pass_invalid)(void* user_data); - void (*err_draw_invalid)(void* user_data); - void (*err_bindings_invalid)(void* user_data); } sg_trace_hooks; /* sg_buffer_info sg_image_info + sg_sampler_info sg_shader_info sg_pipeline_info sg_pass_info @@ -2465,47 +2808,302 @@ typedef struct sg_trace_hooks { sg_query_buffer_info() sg_query_image_info() + sg_query_sampler_info() sg_query_shader_info() sg_query_pipeline_info() sg_query_pass_info() */ typedef struct sg_slot_info { - sg_resource_state state; /* the current state of this resource slot */ - uint32_t res_id; /* type-neutral resource if (e.g. sg_buffer.id) */ - uint32_t ctx_id; /* the context this resource belongs to */ + sg_resource_state state; // the current state of this resource slot + uint32_t res_id; // type-neutral resource if (e.g. sg_buffer.id) + uint32_t ctx_id; // the context this resource belongs to } sg_slot_info; typedef struct sg_buffer_info { - sg_slot_info slot; /* resource pool slot info */ - uint32_t update_frame_index; /* frame index of last sg_update_buffer() */ - uint32_t append_frame_index; /* frame index of last sg_append_buffer() */ - int append_pos; /* current position in buffer for sg_append_buffer() */ - bool append_overflow; /* is buffer in overflow state (due to sg_append_buffer) */ - int num_slots; /* number of renaming-slots for dynamically updated buffers */ - int active_slot; /* currently active write-slot for dynamically updated buffers */ + sg_slot_info slot; // resource pool slot info + uint32_t update_frame_index; // frame index of last sg_update_buffer() + uint32_t append_frame_index; // frame index of last sg_append_buffer() + int append_pos; // current position in buffer for sg_append_buffer() + bool append_overflow; // is buffer in overflow state (due to sg_append_buffer) + int num_slots; // number of renaming-slots for dynamically updated buffers + int active_slot; // currently active write-slot for dynamically updated buffers } sg_buffer_info; typedef struct sg_image_info { - sg_slot_info slot; /* resource pool slot 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_slot_info slot; // resource pool slot 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 } sg_image_info; +typedef struct sg_sampler_info { + sg_slot_info slot; // resource pool slot info +} sg_sampler_info; + typedef struct sg_shader_info { - sg_slot_info slot; /* resoure pool slot info */ + sg_slot_info slot; // resource pool slot info } sg_shader_info; typedef struct sg_pipeline_info { - sg_slot_info slot; /* resource pool slot info */ + sg_slot_info slot; // resource pool slot info } sg_pipeline_info; typedef struct sg_pass_info { - sg_slot_info slot; /* resource pool slot info */ + sg_slot_info slot; // resource pool slot info } sg_pass_info; +/* + sg_log_item + + An enum with a unique item for each log message, warning, error + and validation layer message. +*/ +#define _SG_LOG_ITEMS \ + _SG_LOGITEM_XMACRO(OK, "Ok") \ + _SG_LOGITEM_XMACRO(MALLOC_FAILED, "memory allocation failed") \ + _SG_LOGITEM_XMACRO(GL_TEXTURE_FORMAT_NOT_SUPPORTED, "pixel format not supported for texture (gl)") \ + _SG_LOGITEM_XMACRO(GL_3D_TEXTURES_NOT_SUPPORTED, "3d textures not supported (gl)") \ + _SG_LOGITEM_XMACRO(GL_ARRAY_TEXTURES_NOT_SUPPORTED, "array textures not supported (gl)") \ + _SG_LOGITEM_XMACRO(GL_SHADER_COMPILATION_FAILED, "shader compilation failed (gl)") \ + _SG_LOGITEM_XMACRO(GL_SHADER_LINKING_FAILED, "shader linking failed (gl)") \ + _SG_LOGITEM_XMACRO(GL_VERTEX_ATTRIBUTE_NOT_FOUND_IN_SHADER, "vertex attribute not found in shader (gl)") \ + _SG_LOGITEM_XMACRO(GL_TEXTURE_NAME_NOT_FOUND_IN_SHADER, "texture name not found in shader (gl)") \ + _SG_LOGITEM_XMACRO(GL_FRAMEBUFFER_INCOMPLETE, "framebuffer completeness check failed (gl)") \ + _SG_LOGITEM_XMACRO(GL_MSAA_FRAMEBUFFER_INCOMPLETE, "completeness check failed for msaa resolve framebuffer (gl)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_BUFFER_FAILED, "CreateBuffer() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_DEPTH_TEXTURE_UNSUPPORTED_PIXEL_FORMAT, "pixel format not supported for depth-stencil texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_DEPTH_TEXTURE_FAILED, "CreateTexture2D() failed for depth-stencil texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_2D_TEXTURE_UNSUPPORTED_PIXEL_FORMAT, "pixel format not supported for 2d-, cube- or array-texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_2D_TEXTURE_FAILED, "CreateTexture2D() failed for 2d-, cube- or array-texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_2D_SRV_FAILED, "CreateShaderResourceView() failed for 2d-, cube- or array-texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_3D_TEXTURE_UNSUPPORTED_PIXEL_FORMAT, "pixel format not supported for 3D texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_3D_TEXTURE_FAILED, "CreateTexture3D() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_3D_SRV_FAILED, "CreateShaderResourceView() failed for 3d texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_MSAA_TEXTURE_FAILED, "CreateTexture2D() failed for MSAA render target texture (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_SAMPLER_STATE_FAILED, "CreateSamplerState() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_LOAD_D3DCOMPILER_47_DLL_FAILED, "loading d3dcompiler_47.dll failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_SHADER_COMPILATION_FAILED, "shader compilation failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_SHADER_COMPILATION_OUTPUT, "") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_CONSTANT_BUFFER_FAILED, "CreateBuffer() failed for uniform constant buffer (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_INPUT_LAYOUT_FAILED, "CreateInputLayout() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_RASTERIZER_STATE_FAILED, "CreateRasterizerState() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_DEPTH_STENCIL_STATE_FAILED, "CreateDepthStencilState() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_BLEND_STATE_FAILED, "CreateBlendState() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_RTV_FAILED, "CreateRenderTargetView() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_CREATE_DSV_FAILED, "CreateDepthStencilView() failed (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_MAP_FOR_UPDATE_BUFFER_FAILED, "Map() failed when updating buffer (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_MAP_FOR_APPEND_BUFFER_FAILED, "Map() failed when appending to buffer (d3d11)") \ + _SG_LOGITEM_XMACRO(D3D11_MAP_FOR_UPDATE_IMAGE_FAILED, "Map() failed when updating image (d3d11)") \ + _SG_LOGITEM_XMACRO(METAL_CREATE_BUFFER_FAILED, "failed to create buffer object (metal)") \ + _SG_LOGITEM_XMACRO(METAL_TEXTURE_FORMAT_NOT_SUPPORTED, "pixel format not supported for texture (metal)") \ + _SG_LOGITEM_XMACRO(METAL_CREATE_TEXTURE_FAILED, "failed to create texture object (metal)") \ + _SG_LOGITEM_XMACRO(METAL_CREATE_SAMPLER_FAILED, "failed to create sampler object (metal)") \ + _SG_LOGITEM_XMACRO(METAL_SHADER_COMPILATION_FAILED, "shader compilation failed (metal)") \ + _SG_LOGITEM_XMACRO(METAL_SHADER_CREATION_FAILED, "shader creation failed (metal)") \ + _SG_LOGITEM_XMACRO(METAL_SHADER_COMPILATION_OUTPUT, "") \ + _SG_LOGITEM_XMACRO(METAL_VERTEX_SHADER_ENTRY_NOT_FOUND, "vertex shader entry function not found (metal)") \ + _SG_LOGITEM_XMACRO(METAL_FRAGMENT_SHADER_ENTRY_NOT_FOUND, "fragment shader entry not found (metal)") \ + _SG_LOGITEM_XMACRO(METAL_CREATE_RPS_FAILED, "failed to create render pipeline state (metal)") \ + _SG_LOGITEM_XMACRO(METAL_CREATE_RPS_OUTPUT, "") \ + _SG_LOGITEM_XMACRO(METAL_CREATE_DSS_FAILED, "failed to create depth stencil state (metal)") \ + _SG_LOGITEM_XMACRO(WGPU_MAP_UNIFORM_BUFFER_FAILED, "mapping uniform buffer failed (wgpu)") \ + _SG_LOGITEM_XMACRO(WGPU_STAGING_BUFFER_FULL_COPY_TO_BUFFER, "per frame staging buffer full when copying to buffer (wgpu)") \ + _SG_LOGITEM_XMACRO(WGPU_STAGING_BUFFER_FULL_COPY_TO_TEXTURE, "per frame staging buffer full when copying to texture (wgpu)") \ + _SG_LOGITEM_XMACRO(WGPU_RESET_STATE_CACHE_FIXME, "_sg_wgpu_reset_state_cache: fixme") \ + _SG_LOGITEM_XMACRO(WGPU_ACTIVATE_CONTEXT_FIXME, "_sg_wgpu_activate_context: fixme") \ + _SG_LOGITEM_XMACRO(UNINIT_BUFFER_ACTIVE_CONTEXT_MISMATCH, "active context mismatch in buffer uninit (must be same as for creation)") \ + _SG_LOGITEM_XMACRO(UNINIT_IMAGE_ACTIVE_CONTEXT_MISMATCH, "active context mismatch in image uninit (must be same as for creation)") \ + _SG_LOGITEM_XMACRO(UNINIT_SAMPLER_ACTIVE_CONTEXT_MISMATCH, "active context mismatch in sampler uninit (must be same as for creation)") \ + _SG_LOGITEM_XMACRO(UNINIT_SHADER_ACTIVE_CONTEXT_MISMATCH, "active context mismatch in shader uninit (must be same as for creation)") \ + _SG_LOGITEM_XMACRO(UNINIT_PIPELINE_ACTIVE_CONTEXT_MISMATCH, "active context mismatch in pipeline uninit (must be same as for creation)") \ + _SG_LOGITEM_XMACRO(UNINIT_PASS_ACTIVE_CONTEXT_MISMATCH, "active context mismatch in pass uninit (must be same as for creation)") \ + _SG_LOGITEM_XMACRO(IDENTICAL_COMMIT_LISTENER, "attempting to add identical commit listener") \ + _SG_LOGITEM_XMACRO(COMMIT_LISTENER_ARRAY_FULL, "commit listener array full") \ + _SG_LOGITEM_XMACRO(TRACE_HOOKS_NOT_ENABLED, "sg_install_trace_hooks() called, but SG_TRACE_HOOKS is not defined") \ + _SG_LOGITEM_XMACRO(DEALLOC_BUFFER_INVALID_STATE, "sg_dealloc_buffer(): buffer must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(DEALLOC_IMAGE_INVALID_STATE, "sg_dealloc_image(): image must be in alloc state") \ + _SG_LOGITEM_XMACRO(DEALLOC_SAMPLER_INVALID_STATE, "sg_dealloc_sampler(): sampler must be in alloc state") \ + _SG_LOGITEM_XMACRO(DEALLOC_SHADER_INVALID_STATE, "sg_dealloc_shader(): shader must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(DEALLOC_PIPELINE_INVALID_STATE, "sg_dealloc_pipeline(): pipeline must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(DEALLOC_PASS_INVALID_STATE, "sg_dealloc_pass(): pass must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(INIT_BUFFER_INVALID_STATE, "sg_init_buffer(): buffer must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(INIT_IMAGE_INVALID_STATE, "sg_init_image(): image must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(INIT_SAMPLER_INVALID_STATE, "sg_init_sampler(): sampler must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(INIT_SHADER_INVALID_STATE, "sg_init_shader(): shader must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(INIT_PIPELINE_INVALID_STATE, "sg_init_pipeline(): pipeline must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(INIT_PASS_INVALID_STATE, "sg_init_pass(): pass must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(UNINIT_BUFFER_INVALID_STATE, "sg_uninit_buffer(): buffer must be in VALID or FAILED state") \ + _SG_LOGITEM_XMACRO(UNINIT_IMAGE_INVALID_STATE, "sg_uninit_image(): image must be in VALID or FAILED state") \ + _SG_LOGITEM_XMACRO(UNINIT_SAMPLER_INVALID_STATE, "sg_uninit_sampler(): sampler must be in VALID or FAILED state") \ + _SG_LOGITEM_XMACRO(UNINIT_SHADER_INVALID_STATE, "sg_uninit_shader(): shader must be in VALID or FAILED state") \ + _SG_LOGITEM_XMACRO(UNINIT_PIPELINE_INVALID_STATE, "sg_uninit_pipeline(): pipeline must be in VALID or FAILED state") \ + _SG_LOGITEM_XMACRO(UNINIT_PASS_INVALID_STATE, "sg_uninit_pass(): pass must be in VALID or FAILED state") \ + _SG_LOGITEM_XMACRO(FAIL_BUFFER_INVALID_STATE, "sg_fail_buffer(): buffer must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(FAIL_IMAGE_INVALID_STATE, "sg_fail_image(): image must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(FAIL_SAMPLER_INVALID_STATE, "sg_fail_sampler(): sampler must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(FAIL_SHADER_INVALID_STATE, "sg_fail_shader(): shader must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(FAIL_PIPELINE_INVALID_STATE, "sg_fail_pipeline(): pipeline must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(FAIL_PASS_INVALID_STATE, "sg_fail_pass(): pass must be in ALLOC state") \ + _SG_LOGITEM_XMACRO(BUFFER_POOL_EXHAUSTED, "buffer pool exhausted") \ + _SG_LOGITEM_XMACRO(IMAGE_POOL_EXHAUSTED, "image pool exhausted") \ + _SG_LOGITEM_XMACRO(SAMPLER_POOL_EXHAUSTED, "sampler pool exhausted") \ + _SG_LOGITEM_XMACRO(SHADER_POOL_EXHAUSTED, "shader pool exhausted") \ + _SG_LOGITEM_XMACRO(PIPELINE_POOL_EXHAUSTED, "pipeline pool exhausted") \ + _SG_LOGITEM_XMACRO(PASS_POOL_EXHAUSTED, "pass pool exhausted") \ + _SG_LOGITEM_XMACRO(DRAW_WITHOUT_BINDINGS, "attempting to draw without resource bindings") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_CANARY, "sg_buffer_desc not initialized") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_SIZE, "sg_buffer_desc.size and .data.size cannot both be 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_DATA, "immutable buffers must be initialized with data (sg_buffer_desc.data.ptr and sg_buffer_desc.data.size)") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_DATA_SIZE, "immutable buffer data size differs from buffer size") \ + _SG_LOGITEM_XMACRO(VALIDATE_BUFFERDESC_NO_DATA, "dynamic/stream usage buffers cannot be initialized with data") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDATA_NODATA, "sg_image_data: no data (.ptr and/or .size is zero)") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDATA_DATA_SIZE, "sg_image_data: data size doesn't match expected surface size") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_CANARY, "sg_image_desc not initialized") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_WIDTH, "sg_image_desc.width must be > 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_HEIGHT, "sg_image_desc.height must be > 0") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_RT_PIXELFORMAT, "invalid pixel format for render-target image") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT, "invalid pixel format for non-render-target image") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT, "non-render-target images cannot be multisampled") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT, "MSAA not supported for this pixel format") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_MSAA_NUM_MIPMAPS, "MSAA images must have num_mipmaps == 1") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_MSAA_3D_IMAGE, "3D images cannot have a sample_count > 1") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_DEPTH_3D_IMAGE, "3D images cannot have a depth/stencil image format") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_RT_IMMUTABLE, "render target images must be SG_USAGE_IMMUTABLE") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_RT_NO_DATA, "render target images cannot be initialized with data") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_INJECTED_NO_DATA, "images with injected textures cannot be initialized with data") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA, "dynamic/stream images cannot be initialized with data") \ + _SG_LOGITEM_XMACRO(VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE, "compressed images must be immutable") \ + _SG_LOGITEM_XMACRO(VALIDATE_SAMPLERDESC_CANARY, "sg_sampler_desc not initialized") \ + _SG_LOGITEM_XMACRO(VALIDATE_SAMPLERDESC_MINFILTER_NONE, "sg_sampler_desc.min_filter cannot be SG_FILTER_NONE") \ + _SG_LOGITEM_XMACRO(VALIDATE_SAMPLERDESC_MAGFILTER_NONE, "sg_sampler_desc.mag_filter cannot be SG_FILTER_NONE") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_CANARY, "sg_shader_desc not initialized") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SOURCE, "shader source code required") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_BYTECODE, "shader byte code required") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE, "shader source or byte code required") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_BYTECODE_SIZE, "shader byte code length (in bytes) required") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_CONT_UBS, "shader uniform blocks must occupy continuous slots") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS, "uniform block members must occupy continuous slots") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_UB_MEMBERS, "GL backend requires uniform block member declarations") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_MEMBER_NAME, "uniform block member name missing") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_SIZE_MISMATCH, "size of uniform block members doesn't match uniform block size") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_ARRAY_COUNT, "uniform array count must be >= 1") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_UB_STD140_ARRAY_TYPE, "uniform arrays only allowed for FLOAT4, INT4, MAT4 in std140 layout") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_CONT_IMAGES, "shader stage images must occupy continuous slots (sg_shader_desc.vs|fs.images[])") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_CONT_SAMPLERS, "shader stage samplers must occupy continuous slots (sg_shader_desc.vs|fs.samplers[])") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_IMAGE_SLOT_OUT_OF_RANGE, "shader stage: image-sampler-pair image slot index is out of range (sg_shader_desc.vs|fs.image_sampler_pairs[].image_slot)") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_SAMPLER_SLOT_OUT_OF_RANGE, "shader stage: image-sampler-pair image slot index is out of range (sg_shader_desc.vs|fs.image_sampler_pairs[].sampler_slot)") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_NAME_REQUIRED_FOR_GL, "shader stage: image-sampler-pairs must be named in GL (sg_shader_desc.vs|fs.image_sampler_pairs[].name)") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_HAS_NAME_BUT_NOT_USED, "shader stage: image-sampler-pair has name but .used field not true") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_HAS_IMAGE_BUT_NOT_USED, "shader stage: image-sampler-pair has .image_slot != 0 but .used field not true") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_HAS_SAMPLER_BUT_NOT_USED, "shader stage: image-sampler-pair .sampler_slot != 0 but .used field not true") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_IMAGE_NOT_REFERENCED_BY_IMAGE_SAMPLER_PAIRS, "shader stage: one or more images are note referenced by (sg_shader_desc.vs|fs.image_sampler_pairs[].image_slot)") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_SAMPLER_NOT_REFERENCED_BY_IMAGE_SAMPLER_PAIRS, "shader stage: one or more samplers are not referenced by image-sampler-pairs (sg_shader_desc.vs|fs.image_sampler_pairs[].sampler_slot)") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_NO_CONT_IMAGE_SAMPLER_PAIRS, "shader stage image-sampler-pairs must occupy continuous slots (sg_shader_desc.vs|fs.image_samplers[])") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_ATTR_SEMANTICS, "D3D11 backend requires vertex attribute semantics") \ + _SG_LOGITEM_XMACRO(VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG, "vertex attribute name/semantic string too long (max len 16)") \ + _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_CANARY, "sg_pipeline_desc not initialized") \ + _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_SHADER, "sg_pipeline_desc.shader missing or invalid") \ + _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_NO_ATTRS, "sg_pipeline_desc.layout.attrs is empty or not continuous") \ + _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4, "sg_pipeline_desc.layout.buffers[].stride must be multiple of 4") \ + _SG_LOGITEM_XMACRO(VALIDATE_PIPELINEDESC_ATTR_SEMANTICS, "D3D11 missing vertex attribute semantics in shader") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_CANARY, "sg_pass_desc not initialized") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_NO_ATTACHMENTS, "sg_pass_desc no color or depth-stencil attachments") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_NO_CONT_COLOR_ATTS, "color attachments must occupy continuous slots") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_IMAGE, "pass attachment image is not valid") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_MIPLEVEL, "pass attachment mip level is bigger than image has mipmaps") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_FACE, "pass attachment image is cubemap, but face index is too big") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_LAYER, "pass attachment image is array texture, but layer index is too big") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_SLICE, "pass attachment image is 3d texture, but slice value is too big") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_IMAGE_NO_RT, "pass attachment image must be have render_target=true") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT, "pass color-attachment images must be renderable color pixel format") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_DEPTH_INV_PIXELFORMAT, "pass depth-attachment image must be depth or depth-stencil pixel format") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_IMAGE_SIZES, "all pass attachments must have the same size") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS, "all pass attachments must have the same sample count") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_RESOLVE_COLOR_IMAGE_MSAA, "pass resolve attachments must have a color attachment image with sample count > 1") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_RESOLVE_IMAGE, "pass resolve attachment image not valid") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_RESOLVE_SAMPLE_COUNT, "pass resolve attachment image sample count must be 1") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_RESOLVE_MIPLEVEL, "pass resolve attachment mip level is bigger than image has mipmaps") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_RESOLVE_FACE, "pass resolve attachment is cubemap, but face index is too big") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_RESOLVE_LAYER, "pass resolve attachment is array texture, but layer index is too big") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_RESOLVE_SLICE, "pass resolve attachment is 3d texture, but slice value is too big") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_RESOLVE_IMAGE_NO_RT, "pass resolve attachment image must have render_target=true") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_RESOLVE_IMAGE_SIZES, "pass resolve attachment size must match color attachment image size") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_RESOLVE_IMAGE_FORMAT, "pass resolve attachment pixel format must match color attachment pixel format") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_DEPTH_IMAGE, "pass depth attachment image is not valid") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_DEPTH_MIPLEVEL, "pass depth attachment mip level is bigger than image has mipmaps") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_DEPTH_FACE, "pass depth attachment image is cubemap, but face index is too big") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_DEPTH_LAYER, "pass depth attachment image is array texture, but layer index is too big") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_DEPTH_SLICE, "pass depth attachment image is 3d texture, but slice value is too big") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_DEPTH_IMAGE_NO_RT, "pass depth attachment image must be have render_target=true") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_DEPTH_IMAGE_SIZES, "pass depth attachment image size must match color attachment image size") \ + _SG_LOGITEM_XMACRO(VALIDATE_PASSDESC_DEPTH_IMAGE_SAMPLE_COUNT, "pass depth attachment sample count must match color attachment sample count") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_PASS, "sg_begin_pass: pass must be valid") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_COLOR_ATTACHMENT_IMAGE, "sg_begin_pass: one or more color attachment images are not valid") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_RESOLVE_ATTACHMENT_IMAGE, "sg_begin_pass: one or more resolve attachment images are not valid") \ + _SG_LOGITEM_XMACRO(VALIDATE_BEGINPASS_DEPTHSTENCIL_ATTACHMENT_IMAGE, "sg_begin_pass: one or more depth-stencil attachment images are not valid") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_PIPELINE_VALID_ID, "sg_apply_pipeline: invalid pipeline id provided") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_PIPELINE_EXISTS, "sg_apply_pipeline: pipeline object no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_PIPELINE_VALID, "sg_apply_pipeline: pipeline object not in valid state") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_SHADER_EXISTS, "sg_apply_pipeline: shader object no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_SHADER_VALID, "sg_apply_pipeline: shader object not in valid state") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_ATT_COUNT, "sg_apply_pipeline: number of pipeline color attachments doesn't match number of pass color attachments") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_COLOR_FORMAT, "sg_apply_pipeline: pipeline color attachment pixel format doesn't match pass color attachment pixel format") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_DEPTH_FORMAT, "sg_apply_pipeline: pipeline depth pixel_format doesn't match pass depth attachment pixel format") \ + _SG_LOGITEM_XMACRO(VALIDATE_APIP_SAMPLE_COUNT, "sg_apply_pipeline: pipeline MSAA sample count doesn't match render pass attachment sample count") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_PIPELINE, "sg_apply_bindings: must be called after sg_apply_pipeline") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_PIPELINE_EXISTS, "sg_apply_bindings: currently applied pipeline object no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_PIPELINE_VALID, "sg_apply_bindings: currently applied pipeline object not in valid state") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VBS, "sg_apply_bindings: number of vertex buffers doesn't match number of pipeline vertex layouts") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VB_EXISTS, "sg_apply_bindings: vertex buffer no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VB_TYPE, "sg_apply_bindings: buffer in vertex buffer slot is not a SG_BUFFERTYPE_VERTEXBUFFER") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VB_OVERFLOW, "sg_apply_bindings: buffer in vertex buffer slot is overflown") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_NO_IB, "sg_apply_bindings: pipeline object defines indexed rendering, but no index buffer provided") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_IB, "sg_apply_bindings: pipeline object defines non-indexed rendering, but index buffer provided") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_IB_EXISTS, "sg_apply_bindings: index buffer no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_IB_TYPE, "sg_apply_bindings: buffer in index buffer slot is not a SG_BUFFERTYPE_INDEXBUFFER") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_IB_OVERFLOW, "sg_apply_bindings: buffer in index buffer slot is overflown") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_EXPECTED_IMAGE_BINDING, "sg_apply_bindings: missing image binding on vertex stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_IMG_EXISTS, "sg_apply_bindings: image bound to vertex stage no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_IMAGE_TYPE_MISMATCH, "sg_apply_bindings: type of image bound to vertex stage doesn't match shader desc") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_IMAGE_MSAA, "sg_apply_bindings: cannot bind image with sample_count>1 to vertex stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_UNEXPECTED_IMAGE_BINDING, "sg_apply_bindings: unexpected image binding on vertex stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_EXPECTED_SAMPLER_BINDING, "sg_apply_bindings: missing sampler binding on vertex stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_UNEXPECTED_SAMPLER_COMPARE_NEVER, "sg_apply_bindings: shader expects SG_SAMPLERTYPE_COMPARE on vertex stage but sampler has SG_COMPAREFUNC_NEVER") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_EXPECTED_SAMPLER_COMPARE_NEVER, "sg_apply_bindings: shader expects SG_SAMPLERTYPE_SAMPLE on vertex stage but sampler doesn't have SG_COMPAREFUNC_NEVER") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_UNEXPECTED_SAMPLER_BINDING, "sg_apply_bindings: unexpected sampler binding on vertex stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_SMP_EXISTS, "sg_apply_bindings: sampler bound to vertex stage no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_VS_IMG_SMP_MIPMAPS, "sg_apply_bindings: image bound to vertex stage has mipmap_count == 1, but associated sampler mipmap filer is not SG_MIPMAPFILTER_NONE") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_IMAGE_BINDING, "sg_apply_bindings: missing image binding on fragment stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_IMG_EXISTS, "sg_apply_bindings: image bound to fragment stage no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_IMAGE_TYPE_MISMATCH, "sg_apply_bindings: type of image bound to fragment stage doesn't match shader desc") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_IMAGE_MSAA, "sg_apply_bindings: cannot bind image with sample_count>1 to fragment stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_UNEXPECTED_IMAGE_BINDING, "sg_apply_bindings: unexpected image binding on fragment stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_SAMPLER_BINDING, "sg_apply_bindings: missing sampler binding on fragment stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_UNEXPECTED_SAMPLER_COMPARE_NEVER, "sg_apply_bindings: shader expects SG_SAMPLERTYPE_COMPARE on fragment stage but sampler has SG_COMPAREFUNC_NEVER") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_EXPECTED_SAMPLER_COMPARE_NEVER, "sg_apply_bindings: shader expects SG_SAMPLERTYPE_SAMPLE on fragment stage but sampler doesn't have SG_COMPAREFUNC_NEVER") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_UNEXPECTED_SAMPLER_BINDING, "sg_apply_bindings: unexpected sampler binding on fragment stage") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_SMP_EXISTS, "sg_apply_bindings: sampler bound to fragment stage no longer alive") \ + _SG_LOGITEM_XMACRO(VALIDATE_ABND_FS_IMG_SMP_MIPMAPS, "sg_apply_bindings: image bound to fragment stage has mipmap_count == 1, but associated sampler mipmap filer is not SG_MIPMAPFILTER_NONE") \ + _SG_LOGITEM_XMACRO(VALIDATE_AUB_NO_PIPELINE, "sg_apply_uniforms: must be called after sg_apply_pipeline()") \ + _SG_LOGITEM_XMACRO(VALIDATE_AUB_NO_UB_AT_SLOT, "sg_apply_uniforms: no uniform block declaration at this shader stage UB slot") \ + _SG_LOGITEM_XMACRO(VALIDATE_AUB_SIZE, "sg_apply_uniforms: data size doesn't match declared uniform block size") \ + _SG_LOGITEM_XMACRO(VALIDATE_UPDATEBUF_USAGE, "sg_update_buffer: cannot update immutable buffer") \ + _SG_LOGITEM_XMACRO(VALIDATE_UPDATEBUF_SIZE, "sg_update_buffer: update size is bigger than buffer size") \ + _SG_LOGITEM_XMACRO(VALIDATE_UPDATEBUF_ONCE, "sg_update_buffer: only one update allowed per buffer and frame") \ + _SG_LOGITEM_XMACRO(VALIDATE_UPDATEBUF_APPEND, "sg_update_buffer: cannot call sg_update_buffer and sg_append_buffer in same frame") \ + _SG_LOGITEM_XMACRO(VALIDATE_APPENDBUF_USAGE, "sg_append_buffer: cannot append to immutable buffer") \ + _SG_LOGITEM_XMACRO(VALIDATE_APPENDBUF_SIZE, "sg_append_buffer: overall appended size is bigger than buffer size") \ + _SG_LOGITEM_XMACRO(VALIDATE_APPENDBUF_UPDATE, "sg_append_buffer: cannot call sg_append_buffer and sg_update_buffer in same frame") \ + _SG_LOGITEM_XMACRO(VALIDATE_UPDIMG_USAGE, "sg_update_image: cannot update immutable image") \ + _SG_LOGITEM_XMACRO(VALIDATE_UPDIMG_ONCE, "sg_update_image: only one update allowed per image and frame") \ + _SG_LOGITEM_XMACRO(VALIDATION_FAILED, "validation layer checks failed") \ + +#define _SG_LOGITEM_XMACRO(item,msg) SG_LOGITEM_##item, +typedef enum sg_log_item { + _SG_LOG_ITEMS +} sg_log_item; +#undef _SG_LOGITEM_XMACRO + /* sg_desc @@ -2523,18 +3121,18 @@ typedef struct sg_pass_info { .buffer_pool_size 128 .image_pool_size 128 + .sampler_pool_size 64 .shader_pool_size 32 .pipeline_pool_size 64 .pass_pool_size 16 .context_pool_size 16 .uniform_buffer_size 4 MB (4*1024*1024) .staging_buffer_size 8 MB (8*1024*1024) - .sampler_cache_size 64 .max_commit_listeners 1024 .disable_validation false - .allocator.alloc 0 (in this case, malloc() will be called) - .allocator.free 0 (in this case, free() will be called) + .allocator.alloc_fn 0 (in this case, malloc() will be called) + .allocator.free_fn 0 (in this case, free() will be called) .allocator.user_data 0 .context.color_format: default value depends on selected backend: @@ -2544,12 +3142,6 @@ typedef struct sg_pass_info { .context.depth_format SG_PIXELFORMAT_DEPTH_STENCIL .context.sample_count 1 - GL specific: - .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 a bridged (const void*) to sokol_gfx, which will use a unretained @@ -2592,7 +3184,7 @@ typedef struct sg_pass_info { ID3D11DepthStencilView object of the default framebuffer, this function will be called in sg_begin_pass() when rendering to the default framebuffer - .context.metal.user_data + .context.d3d11.user_data optional user data pointer passed to the userdata versions of callback functions @@ -2613,7 +3205,7 @@ typedef struct sg_pass_info { .context.wgpu.depth_stencil_view_userdata_cb callback to get current default-pass depth-stencil-surface WGPUTextureView the pixel format of the default WGPUTextureView must be WGPUTextureFormat_Depth24Plus8 - .context.metal.user_data + .context.wgpu.user_data optional user data pointer passed to the userdata versions of callback functions @@ -2623,10 +3215,6 @@ typedef struct sg_pass_info { 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_metal_context_desc { const void* device; const void* (*renderpass_descriptor_cb)(void); @@ -2647,24 +3235,30 @@ typedef struct sg_d3d11_context_desc { } sg_d3d11_context_desc; typedef struct sg_wgpu_context_desc { - const void* device; /* WGPUDevice */ - const void* (*render_view_cb)(void); /* returns WGPUTextureView */ + const void* device; // WGPUDevice + const void* (*render_view_cb)(void); // returns WGPUTextureView const void* (*render_view_userdata_cb)(void*); - const void* (*resolve_view_cb)(void); /* returns WGPUTextureView */ + const void* (*resolve_view_cb)(void); // returns WGPUTextureView const void* (*resolve_view_userdata_cb)(void*); - const void* (*depth_stencil_view_cb)(void); /* returns WGPUTextureView, must be WGPUTextureFormat_Depth24Plus8 */ + const void* (*depth_stencil_view_cb)(void); // returns WGPUTextureView, must be WGPUTextureFormat_Depth24Plus8 const void* (*depth_stencil_view_userdata_cb)(void*); void* user_data; } sg_wgpu_context_desc; +typedef struct sg_gl_context_desc { + uint32_t (*default_framebuffer_cb)(void); + uint32_t (*default_framebuffer_userdata_cb)(void*); + void* user_data; +} sg_gl_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_gl_context_desc gl; } sg_context_desc; /* @@ -2686,23 +3280,34 @@ typedef struct sg_commit_listener { Used in sg_desc to provide custom memory-alloc and -free functions to sokol_gfx.h. If memory management should be overridden, both the - alloc and free function must be provided (e.g. it's not valid to + alloc_fn and free_fn function must be provided (e.g. it's not valid to override one function but not the other). */ typedef struct sg_allocator { - void* (*alloc)(size_t size, void* user_data); - void (*free)(void* ptr, void* user_data); + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); void* user_data; } sg_allocator; /* sg_logger - Used in sg_desc to provide custom log callbacks to sokol_gfx.h. - Default behavior is SOKOL_LOG(message). + Used in sg_desc to provide a logging function. Please be aware + that without logging function, sokol-gfx will be completely + silent, e.g. it will not report errors, warnings and + validation layer messages. For maximum error verbosity, + compile in debug mode (e.g. NDEBUG *not* defined) and install + a logger (for instance the standard logging function from sokol_log.h). */ typedef struct sg_logger { - void (*log_cb)(const char* message, void* user_data); + void (*func)( + const char* tag, // always "sg" + uint32_t log_level, // 0=panic, 1=error, 2=warning, 3=info + uint32_t log_item_id, // SG_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_gfx.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data); void* user_data; } sg_logger; @@ -2710,22 +3315,23 @@ typedef struct sg_desc { uint32_t _start_canary; int buffer_pool_size; int image_pool_size; + int sampler_pool_size; int shader_pool_size; int pipeline_pool_size; int pass_pool_size; int context_pool_size; int uniform_buffer_size; int staging_buffer_size; - int sampler_cache_size; int max_commit_listeners; bool disable_validation; // disable validation layer even in debug mode, useful for tests + bool mtl_force_managed_storage_mode; // for debugging: use Metal managed storage mode for resources even with UMA sg_allocator allocator; sg_logger logger; // optional log function override sg_context_desc context; uint32_t _end_canary; } sg_desc; -/* setup and misc functions */ +// setup and misc functions SOKOL_GFX_API_DECL void sg_setup(const sg_desc* desc); SOKOL_GFX_API_DECL void sg_shutdown(void); SOKOL_GFX_API_DECL bool sg_isvalid(void); @@ -2736,14 +3342,16 @@ SOKOL_GFX_API_DECL void sg_pop_debug_group(void); SOKOL_GFX_API_DECL bool sg_add_commit_listener(sg_commit_listener listener); SOKOL_GFX_API_DECL bool sg_remove_commit_listener(sg_commit_listener listener); -/* resource creation, destruction and updating */ +// resource creation, destruction and updating SOKOL_GFX_API_DECL sg_buffer sg_make_buffer(const sg_buffer_desc* desc); SOKOL_GFX_API_DECL sg_image sg_make_image(const sg_image_desc* desc); +SOKOL_GFX_API_DECL sg_sampler sg_make_sampler(const sg_sampler_desc* desc); SOKOL_GFX_API_DECL sg_shader sg_make_shader(const sg_shader_desc* desc); SOKOL_GFX_API_DECL sg_pipeline sg_make_pipeline(const sg_pipeline_desc* desc); SOKOL_GFX_API_DECL sg_pass sg_make_pass(const sg_pass_desc* desc); SOKOL_GFX_API_DECL void sg_destroy_buffer(sg_buffer buf); SOKOL_GFX_API_DECL void sg_destroy_image(sg_image img); +SOKOL_GFX_API_DECL void sg_destroy_sampler(sg_sampler smp); SOKOL_GFX_API_DECL void sg_destroy_shader(sg_shader shd); SOKOL_GFX_API_DECL void sg_destroy_pipeline(sg_pipeline pip); SOKOL_GFX_API_DECL void sg_destroy_pass(sg_pass pass); @@ -2753,7 +3361,7 @@ SOKOL_GFX_API_DECL int sg_append_buffer(sg_buffer buf, const sg_range* data); SOKOL_GFX_API_DECL bool sg_query_buffer_overflow(sg_buffer buf); SOKOL_GFX_API_DECL bool sg_query_buffer_will_overflow(sg_buffer buf, size_t size); -/* rendering functions */ +// rendering functions SOKOL_GFX_API_DECL void sg_begin_default_pass(const sg_pass_action* pass_action, int width, int height); SOKOL_GFX_API_DECL void sg_begin_default_passf(const sg_pass_action* pass_action, float width, float height); SOKOL_GFX_API_DECL void sg_begin_pass(sg_pass pass, const sg_pass_action* pass_action); @@ -2768,59 +3376,74 @@ SOKOL_GFX_API_DECL void sg_draw(int base_element, int num_elements, int num_inst SOKOL_GFX_API_DECL void sg_end_pass(void); SOKOL_GFX_API_DECL void sg_commit(void); -/* getting information */ +// getting information SOKOL_GFX_API_DECL sg_desc sg_query_desc(void); SOKOL_GFX_API_DECL sg_backend sg_query_backend(void); SOKOL_GFX_API_DECL sg_features sg_query_features(void); SOKOL_GFX_API_DECL sg_limits sg_query_limits(void); SOKOL_GFX_API_DECL sg_pixelformat_info sg_query_pixelformat(sg_pixel_format fmt); -/* get current state of a resource (INITIAL, ALLOC, VALID, FAILED, INVALID) */ +// get current state of a resource (INITIAL, ALLOC, VALID, FAILED, INVALID) SOKOL_GFX_API_DECL sg_resource_state sg_query_buffer_state(sg_buffer buf); SOKOL_GFX_API_DECL sg_resource_state sg_query_image_state(sg_image img); +SOKOL_GFX_API_DECL sg_resource_state sg_query_sampler_state(sg_sampler smp); SOKOL_GFX_API_DECL sg_resource_state sg_query_shader_state(sg_shader shd); SOKOL_GFX_API_DECL sg_resource_state sg_query_pipeline_state(sg_pipeline pip); SOKOL_GFX_API_DECL sg_resource_state sg_query_pass_state(sg_pass pass); -/* get runtime information about a resource */ +// get runtime information about a resource SOKOL_GFX_API_DECL sg_buffer_info sg_query_buffer_info(sg_buffer buf); SOKOL_GFX_API_DECL sg_image_info sg_query_image_info(sg_image img); +SOKOL_GFX_API_DECL sg_sampler_info sg_query_sampler_info(sg_sampler smp); SOKOL_GFX_API_DECL sg_shader_info sg_query_shader_info(sg_shader shd); SOKOL_GFX_API_DECL sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip); SOKOL_GFX_API_DECL sg_pass_info sg_query_pass_info(sg_pass pass); -/* get resource creation desc struct with their default values replaced */ +// get desc structs matching a specific resource (NOTE that not all creation attributes may be provided) +SOKOL_GFX_API_DECL sg_buffer_desc sg_query_buffer_desc(sg_buffer buf); +SOKOL_GFX_API_DECL sg_image_desc sg_query_image_desc(sg_image img); +SOKOL_GFX_API_DECL sg_sampler_desc sg_query_sampler_desc(sg_sampler smp); +SOKOL_GFX_API_DECL sg_shader_desc sg_query_shader_desc(sg_shader shd); +SOKOL_GFX_API_DECL sg_pipeline_desc sg_query_pipeline_desc(sg_pipeline pip); +SOKOL_GFX_API_DECL sg_pass_desc sg_query_pass_desc(sg_pass pass); +// get resource creation desc struct with their default values replaced SOKOL_GFX_API_DECL sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc* desc); SOKOL_GFX_API_DECL sg_image_desc sg_query_image_defaults(const sg_image_desc* desc); +SOKOL_GFX_API_DECL sg_sampler_desc sg_query_sampler_defaults(const sg_sampler_desc* desc); SOKOL_GFX_API_DECL sg_shader_desc sg_query_shader_defaults(const sg_shader_desc* desc); SOKOL_GFX_API_DECL sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc* desc); SOKOL_GFX_API_DECL sg_pass_desc sg_query_pass_defaults(const sg_pass_desc* desc); -/* separate resource allocation and initialization (for async setup) */ +// separate resource allocation and initialization (for async setup) SOKOL_GFX_API_DECL sg_buffer sg_alloc_buffer(void); SOKOL_GFX_API_DECL sg_image sg_alloc_image(void); +SOKOL_GFX_API_DECL sg_sampler sg_alloc_sampler(void); SOKOL_GFX_API_DECL sg_shader sg_alloc_shader(void); SOKOL_GFX_API_DECL sg_pipeline sg_alloc_pipeline(void); SOKOL_GFX_API_DECL sg_pass sg_alloc_pass(void); SOKOL_GFX_API_DECL void sg_dealloc_buffer(sg_buffer buf); SOKOL_GFX_API_DECL void sg_dealloc_image(sg_image img); +SOKOL_GFX_API_DECL void sg_dealloc_sampler(sg_sampler smp); SOKOL_GFX_API_DECL void sg_dealloc_shader(sg_shader shd); SOKOL_GFX_API_DECL void sg_dealloc_pipeline(sg_pipeline pip); SOKOL_GFX_API_DECL void sg_dealloc_pass(sg_pass pass); SOKOL_GFX_API_DECL void sg_init_buffer(sg_buffer buf, const sg_buffer_desc* desc); SOKOL_GFX_API_DECL void sg_init_image(sg_image img, const sg_image_desc* desc); +SOKOL_GFX_API_DECL void sg_init_sampler(sg_sampler smg, const sg_sampler_desc* desc); SOKOL_GFX_API_DECL void sg_init_shader(sg_shader shd, const sg_shader_desc* desc); SOKOL_GFX_API_DECL void sg_init_pipeline(sg_pipeline pip, const sg_pipeline_desc* desc); SOKOL_GFX_API_DECL void sg_init_pass(sg_pass pass, const sg_pass_desc* desc); SOKOL_GFX_API_DECL void sg_uninit_buffer(sg_buffer buf); SOKOL_GFX_API_DECL void sg_uninit_image(sg_image img); +SOKOL_GFX_API_DECL void sg_uninit_sampler(sg_sampler smp); SOKOL_GFX_API_DECL void sg_uninit_shader(sg_shader shd); SOKOL_GFX_API_DECL void sg_uninit_pipeline(sg_pipeline pip); SOKOL_GFX_API_DECL void sg_uninit_pass(sg_pass pass); SOKOL_GFX_API_DECL void sg_fail_buffer(sg_buffer buf); SOKOL_GFX_API_DECL void sg_fail_image(sg_image img); +SOKOL_GFX_API_DECL void sg_fail_sampler(sg_sampler smp); SOKOL_GFX_API_DECL void sg_fail_shader(sg_shader shd); SOKOL_GFX_API_DECL void sg_fail_pipeline(sg_pipeline pip); SOKOL_GFX_API_DECL void sg_fail_pass(sg_pass pass); -/* rendering contexts (optional) */ +// rendering contexts (optional) SOKOL_GFX_API_DECL sg_context sg_setup_context(void); SOKOL_GFX_API_DECL void sg_activate_context(sg_context ctx_id); SOKOL_GFX_API_DECL void sg_discard_context(sg_context ctx_id); @@ -2831,23 +3454,24 @@ SOKOL_GFX_API_DECL void sg_discard_context(sg_context ctx_id); This group of functions will be expanded as needed. */ -/* D3D11: return ID3D11Device */ +// D3D11: return ID3D11Device SOKOL_GFX_API_DECL const void* sg_d3d11_device(void); -/* Metal: return __bridge-casted MTLDevice */ +// Metal: return __bridge-casted MTLDevice SOKOL_GFX_API_DECL const void* sg_mtl_device(void); -/* Metal: return __bridge-casted MTLRenderCommandEncoder in current pass (or zero if outside pass) */ +// Metal: return __bridge-casted MTLRenderCommandEncoder in current pass (or zero if outside pass) SOKOL_GFX_API_DECL const void* sg_mtl_render_command_encoder(void); #ifdef __cplusplus -} /* extern "C" */ +} // extern "C" -/* reference-based equivalents for 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_sampler sg_make_sampler(const sg_sampler_desc& desc) { return sg_make_sampler(&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); } @@ -2861,27 +3485,35 @@ inline void sg_apply_uniforms(sg_shader_stage stage, int ub_index, const sg_rang 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_sampler_desc sg_query_sampler_defaults(const sg_sampler_desc& desc) { return sg_query_sampler_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); } +inline void sg_init_buffer(sg_buffer buf, const sg_buffer_desc& desc) { return sg_init_buffer(buf, &desc); } +inline void sg_init_image(sg_image img, const sg_image_desc& desc) { return sg_init_image(img, &desc); } +inline void sg_init_sampler(sg_sampler smp, const sg_sampler_desc& desc) { return sg_init_sampler(smp, &desc); } +inline void sg_init_shader(sg_shader shd, const sg_shader_desc& desc) { return sg_init_shader(shd, &desc); } +inline void sg_init_pipeline(sg_pipeline pip, const sg_pipeline_desc& desc) { return sg_init_pipeline(pip, &desc); } +inline void sg_init_pass(sg_pass pass, const sg_pass_desc& desc) { return sg_init_pass(pass, &desc); } inline void sg_update_buffer(sg_buffer buf_id, const sg_range& data) { return sg_update_buffer(buf_id, &data); } inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_append_buffer(buf_id, &data); } #endif #endif // SOKOL_GFX_INCLUDED -/*--- IMPLEMENTATION ---------------------------------------------------------*/ +// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████ +// +// >>implementation #ifdef SOKOL_GFX_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_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" +#if !(defined(SOKOL_GLCORE33)||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_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND" #endif #if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE) #error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use sg_desc.allocator to override memory allocation functions" @@ -2903,34 +3535,10 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #include #define SOKOL_ASSERT(c) assert(c) #endif -#ifndef SOKOL_VALIDATE_BEGIN - #define SOKOL_VALIDATE_BEGIN() _sg_validate_begin() -#endif -#ifndef SOKOL_VALIDATE - #define SOKOL_VALIDATE(cond, err) _sg_validate((cond), err) -#endif -#ifndef SOKOL_VALIDATE_END - #define SOKOL_VALIDATE_END() _sg_validate_end() -#endif #ifndef SOKOL_UNREACHABLE #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) #endif -#if !defined(SOKOL_DEBUG) - #define SG_LOG(s) -#else - #define SG_LOG(s) _sg_log(s) - #ifndef SOKOL_LOG - #if defined(__ANDROID__) - #include - #define SOKOL_LOG(s) __android_log_write(ANDROID_LOG_INFO, "SOKOL_GFX", s) - #else - #include - #define SOKOL_LOG(s) puts(s) - #endif - #endif -#endif - #ifndef _SOKOL_PRIVATE #if defined(__GNUC__) || defined(__clang__) #define _SOKOL_PRIVATE __attribute__((unused)) static @@ -2951,7 +3559,7 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #define _SG_TRACE_NOARGS(fn) #endif -/* default clear values */ +// default clear values #ifndef SG_DEFAULT_CLEAR_RED #define SG_DEFAULT_CLEAR_RED (0.5f) #endif @@ -2973,11 +3581,11 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #ifdef _MSC_VER #pragma warning(push) -#pragma warning(disable:4115) /* named type definition in parentheses */ -#pragma warning(disable:4505) /* unreferenced local function has been removed */ -#pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union (needed by d3d11.h) */ -#pragma warning(disable:4054) /* 'type cast': from function pointer */ -#pragma warning(disable:4055) /* 'type cast': from data pointer */ +#pragma warning(disable:4115) // named type definition in parentheses +#pragma warning(disable:4505) // unreferenced local function has been removed +#pragma warning(disable:4201) // nonstandard extension used: nameless struct/union (needed by d3d11.h) +#pragma warning(disable:4054) // 'type cast': from function pointer +#pragma warning(disable:4055) // 'type cast': from data pointer #endif #if defined(SOKOL_D3D11) @@ -2993,14 +3601,10 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #include #include #ifdef _MSC_VER - #if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) - #pragma comment (lib, "WindowsApp") - #else - #pragma comment (lib, "kernel32") - #pragma comment (lib, "user32") - #pragma comment (lib, "dxgi") - #pragma comment (lib, "d3d11") - #endif + #pragma comment (lib, "kernel32") + #pragma comment (lib, "user32") + #pragma comment (lib, "dxgi") + #pragma comment (lib, "d3d11") #endif #elif defined(SOKOL_METAL) // see https://clang.llvm.org/docs/LanguageExtensions.html#automatic-reference-counting @@ -3010,6 +3614,7 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #endif #endif #include + #include #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE #define _SG_TARGET_MACOS (1) #else @@ -3025,7 +3630,7 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #else #include #endif -#elif defined(SOKOL_GLCORE33) || defined(SOKOL_GLES2) || defined(SOKOL_GLES3) +#elif defined(SOKOL_GLCORE33) || defined(SOKOL_GLES3) #define _SOKOL_ANY_GL (1) // include platform specific GL headers (or on Win32: use an embedded GL loader) @@ -3056,12 +3661,6 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #elif defined(__EMSCRIPTEN__) || defined(__ANDROID__) #if defined(SOKOL_GLES3) #include - #elif defined(SOKOL_GLES2) - #ifndef GL_EXT_PROTOTYPES - #define GL_GLEXT_PROTOTYPES - #endif - #include - #include #endif #elif defined(__linux__) || defined(__unix__) #define GL_GLEXT_PROTOTYPES @@ -3102,13 +3701,12 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ typedef void GLvoid; typedef int64_t GLint64; typedef float GLfloat; - typedef struct __GLsync * GLsync; typedef int GLint; #define GL_INT_2_10_10_10_REV 0x8D9F #define GL_R32F 0x822E #define GL_PROGRAM_POINT_SIZE 0x8642 - #define GL_STENCIL_ATTACHMENT 0x8D20 #define GL_DEPTH_ATTACHMENT 0x8D00 + #define GL_DEPTH_STENCIL_ATTACHMENT 0x821A #define GL_COLOR_ATTACHMENT2 0x8CE2 #define GL_COLOR_ATTACHMENT0 0x8CE0 #define GL_R16F 0x822D @@ -3153,6 +3751,7 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #define GL_NEAREST_MIPMAP_LINEAR 0x2702 #define GL_RGB10_A2 0x8059 #define GL_RGBA8 0x8058 + #define GL_SRGB8_ALPHA8 0x8C43 #define GL_COLOR_ATTACHMENT1 0x8CE1 #define GL_RGBA4 0x8056 #define GL_RGB8 0x8051 @@ -3216,7 +3815,7 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #define GL_LEQUAL 0x0203 #define GL_STENCIL_TEST 0x0B90 #define GL_DITHER 0x0BD0 - #define GL_DEPTH_COMPONENT16 0x81A5 + #define GL_DEPTH_COMPONENT32F 0x8CAC #define GL_EQUAL 0x0202 #define GL_FRAMEBUFFER 0x8D40 #define GL_RGB5 0x8050 @@ -3304,6 +3903,11 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #define GL_TEXTURE_BORDER_COLOR 0x1004 #define GL_CURRENT_PROGRAM 0x8B8D #define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB + #define GL_UNPACK_ALIGNMENT 0x0CF5 + #define GL_FRAMEBUFFER_SRGB 0x8DB9 + #define GL_TEXTURE_COMPARE_MODE 0x884C + #define GL_TEXTURE_COMPARE_FUNC 0x884D + #define GL_COMPARE_REF_TO_TEXTURE 0x884E #endif #ifndef GL_UNSIGNED_INT_2_10_10_10_REV @@ -3390,40 +3994,24 @@ inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_ #ifndef GL_LUMINANCE #define GL_LUMINANCE 0x1909 #endif - - #ifdef SOKOL_GLES2 - #ifdef GL_ANGLE_instanced_arrays - #define _SOKOL_GL_INSTANCING_ENABLED - #define glDrawArraysInstanced(mode, first, count, instancecount) glDrawArraysInstancedANGLE(mode, first, count, instancecount) - #define glDrawElementsInstanced(mode, count, type, indices, instancecount) glDrawElementsInstancedANGLE(mode, count, type, indices, instancecount) - #define glVertexAttribDivisor(index, divisor) glVertexAttribDivisorANGLE(index, divisor) - #elif defined(GL_EXT_draw_instanced) && defined(GL_EXT_instanced_arrays) - #define _SOKOL_GL_INSTANCING_ENABLED - #define glDrawArraysInstanced(mode, first, count, instancecount) glDrawArraysInstancedEXT(mode, first, count, instancecount) - #define glDrawElementsInstanced(mode, count, type, indices, instancecount) glDrawElementsInstancedEXT(mode, count, type, indices, instancecount) - #define glVertexAttribDivisor(index, divisor) glVertexAttribDivisorEXT(index, divisor) - #else - #define _SOKOL_GLES2_INSTANCING_ERROR "Select GL_ANGLE_instanced_arrays or (GL_EXT_draw_instanced & GL_EXT_instanced_arrays) to enable instancing in GLES2" - #define glDrawArraysInstanced(mode, first, count, instancecount) SOKOL_ASSERT(0 && _SOKOL_GLES2_INSTANCING_ERROR) - #define glDrawElementsInstanced(mode, count, type, indices, instancecount) SOKOL_ASSERT(0 && _SOKOL_GLES2_INSTANCING_ERROR) - #define glVertexAttribDivisor(index, divisor) SOKOL_ASSERT(0 && _SOKOL_GLES2_INSTANCING_ERROR) - #endif - #else - #define _SOKOL_GL_INSTANCING_ENABLED - #endif #define _SG_GL_CHECK_ERROR() { SOKOL_ASSERT(glGetError() == GL_NO_ERROR); } #endif -/*=== COMMON BACKEND STUFF ===================================================*/ - -/* resource pool slots */ +// ███████ ████████ ██████ ██ ██ ██████ ████████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██████ ██ ██ ██ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ██ ██████ ██████ ██ ███████ +// +// >>structs +// resource pool slots typedef struct { uint32_t id; uint32_t ctx_id; sg_resource_state state; } _sg_slot_t; -/* constants */ +// constants enum { _SG_STRING_SIZE = 16, _SG_SLOT_SHIFT = 16, @@ -3431,22 +4019,22 @@ enum { _SG_MAX_POOL_SIZE = (1<<_SG_SLOT_SHIFT), _SG_DEFAULT_BUFFER_POOL_SIZE = 128, _SG_DEFAULT_IMAGE_POOL_SIZE = 128, + _SG_DEFAULT_SAMPLER_POOL_SIZE = 64, _SG_DEFAULT_SHADER_POOL_SIZE = 32, _SG_DEFAULT_PIPELINE_POOL_SIZE = 64, _SG_DEFAULT_PASS_POOL_SIZE = 16, _SG_DEFAULT_CONTEXT_POOL_SIZE = 16, - _SG_DEFAULT_SAMPLER_CACHE_CAPACITY = 64, _SG_DEFAULT_UB_SIZE = 4 * 1024 * 1024, _SG_DEFAULT_STAGING_SIZE = 8 * 1024 * 1024, _SG_DEFAULT_MAX_COMMIT_LISTENERS = 1024, }; -/* fixed-size string */ +// fixed-size string typedef struct { char buf[_SG_STRING_SIZE]; } _sg_str_t; -/* helper macros */ +// helper macros #define _sg_def(val, def) (((val) == 0) ? (def) : (val)) #define _sg_def_flt(val, def) (((val) == 0.0f) ? (def) : (val)) #define _sg_min(a,b) (((a)<(b))?(a):(b)) @@ -3462,27 +4050,30 @@ typedef struct { 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; + sg_buffer_type type; + sg_usage usage; } _sg_buffer_common_t; _SOKOL_PRIVATE void _sg_buffer_common_init(_sg_buffer_common_t* cmn, const sg_buffer_desc* desc) { cmn->size = (int)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->num_slots = (desc->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; cmn->active_slot = 0; + cmn->type = desc->type; + cmn->usage = desc->usage; } typedef struct { + uint32_t upd_frame_index; + int num_slots; + int active_slot; sg_image_type type; bool render_target; int width; @@ -3492,19 +4083,12 @@ typedef struct { 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; - int num_slots; - int active_slot; } _sg_image_common_t; _SOKOL_PRIVATE void _sg_image_common_init(_sg_image_common_t* cmn, const sg_image_desc* desc) { + cmn->upd_frame_index = 0; + cmn->num_slots = (desc->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; + cmn->active_slot = 0; cmn->type = desc->type; cmn->render_target = desc->render_target; cmn->width = desc->width; @@ -3514,32 +4098,65 @@ _SOKOL_PRIVATE void _sg_image_common_init(_sg_image_common_t* cmn, const sg_imag cmn->usage = desc->usage; cmn->pixel_format = desc->pixel_format; cmn->sample_count = desc->sample_count; +} + +typedef struct { + sg_filter min_filter; + sg_filter mag_filter; + sg_filter mipmap_filter; + sg_wrap wrap_u; + sg_wrap wrap_v; + sg_wrap wrap_w; + float min_lod; + float max_lod; + sg_border_color border_color; + sg_compare_func compare; + uint32_t max_anisotropy; +} _sg_sampler_common_t; + +_SOKOL_PRIVATE void _sg_sampler_common_init(_sg_sampler_common_t* cmn, const sg_sampler_desc* desc) { cmn->min_filter = desc->min_filter; cmn->mag_filter = desc->mag_filter; + cmn->mipmap_filter = desc->mipmap_filter; cmn->wrap_u = desc->wrap_u; cmn->wrap_v = desc->wrap_v; cmn->wrap_w = desc->wrap_w; + cmn->min_lod = desc->min_lod; + cmn->max_lod = desc->max_lod; cmn->border_color = desc->border_color; + cmn->compare = desc->compare; 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 { size_t size; -} _sg_uniform_block_t; +} _sg_shader_uniform_block_t; typedef struct { sg_image_type image_type; - sg_sampler_type sampler_type; + sg_image_sample_type sample_type; + bool multisampled; } _sg_shader_image_t; +typedef struct { + sg_sampler_type sampler_type; +} _sg_shader_sampler_t; + +// combined image sampler mappings, only needed on GL +typedef struct { + int image_slot; + int sampler_slot; +} _sg_shader_image_sampler_t; + typedef struct { int num_uniform_blocks; int num_images; - _sg_uniform_block_t uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; + int num_samplers; + int num_image_samplers; + _sg_shader_uniform_block_t uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; _sg_shader_image_t images[SG_MAX_SHADERSTAGE_IMAGES]; + _sg_shader_sampler_t samplers[SG_MAX_SHADERSTAGE_SAMPLERS]; + _sg_shader_image_sampler_t image_samplers[SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS]; } _sg_shader_stage_t; typedef struct { @@ -3562,49 +4179,77 @@ _SOKOL_PRIVATE void _sg_shader_common_init(_sg_shader_common_t* cmn, const sg_sh 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->image_type == _SG_IMAGETYPE_DEFAULT) { + if (!img_desc->used) { break; } + stage->images[img_index].multisampled = img_desc->multisampled; stage->images[img_index].image_type = img_desc->image_type; - stage->images[img_index].sampler_type = img_desc->sampler_type; + stage->images[img_index].sample_type = img_desc->sample_type; stage->num_images++; } + SOKOL_ASSERT(stage->num_samplers == 0); + for (int smp_index = 0; smp_index < SG_MAX_SHADERSTAGE_SAMPLERS; smp_index++) { + const sg_shader_sampler_desc* smp_desc = &stage_desc->samplers[smp_index]; + if (!smp_desc->used) { + break; + } + stage->samplers[smp_index].sampler_type = smp_desc->sampler_type; + stage->num_samplers++; + } + SOKOL_ASSERT(stage->num_image_samplers == 0); + for (int img_smp_index = 0; img_smp_index < SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS; img_smp_index++) { + const sg_shader_image_sampler_pair_desc* img_smp_desc = &stage_desc->image_sampler_pairs[img_smp_index]; + if (!img_smp_desc->used) { + break; + } + SOKOL_ASSERT((img_smp_desc->image_slot >= 0) && (img_smp_desc->image_slot < stage->num_images)); + stage->image_samplers[img_smp_index].image_slot = img_smp_desc->image_slot; + SOKOL_ASSERT((img_smp_desc->sampler_slot >= 0) && (img_smp_desc->sampler_slot < stage->num_samplers)); + stage->image_samplers[img_smp_index].sampler_slot = img_smp_desc->sampler_slot; + stage->num_image_samplers++; + } } } typedef struct { + bool vertex_buffer_layout_active[SG_MAX_VERTEX_BUFFERS]; + bool use_instanced_draw; sg_shader shader_id; + sg_vertex_layout_state layout; + sg_depth_state depth; + sg_stencil_state stencil; + int color_count; + sg_color_target_state colors[SG_MAX_COLOR_ATTACHMENTS]; + sg_primitive_type primitive_type; sg_index_type index_type; - bool use_instanced_draw; - bool vertex_layout_valid[SG_MAX_SHADERSTAGE_BUFFERS]; - int color_attachment_count; - sg_pixel_format color_formats[SG_MAX_COLOR_ATTACHMENTS]; - sg_pixel_format depth_format; + sg_cull_mode cull_mode; + sg_face_winding face_winding; int sample_count; - float depth_bias; - float depth_bias_slope_scale; - float depth_bias_clamp; sg_color blend_color; + bool alpha_to_coverage_enabled; } _sg_pipeline_common_t; _SOKOL_PRIVATE void _sg_pipeline_common_init(_sg_pipeline_common_t* cmn, const sg_pipeline_desc* desc) { - SOKOL_ASSERT((desc->color_count >= 1) && (desc->color_count <= SG_MAX_COLOR_ATTACHMENTS)); + SOKOL_ASSERT((desc->color_count >= 0) && (desc->color_count <= SG_MAX_COLOR_ATTACHMENTS)); + for (int i = 0; i < SG_MAX_VERTEX_BUFFERS; i++) { + cmn->vertex_buffer_layout_active[i] = false; + } + cmn->use_instanced_draw = false; cmn->shader_id = desc->shader; + cmn->layout = desc->layout; + cmn->depth = desc->depth; + cmn->stencil = desc->stencil; + cmn->color_count = desc->color_count; + for (int i = 0; i < desc->color_count; i++) { + cmn->colors[i] = desc->colors[i]; + } + cmn->primitive_type = desc->primitive_type; cmn->index_type = desc->index_type; - cmn->use_instanced_draw = false; - for (int i = 0; i < SG_MAX_SHADERSTAGE_BUFFERS; i++) { - cmn->vertex_layout_valid[i] = false; - } - cmn->color_attachment_count = desc->color_count; - for (int i = 0; i < cmn->color_attachment_count; i++) { - cmn->color_formats[i] = desc->colors[i].pixel_format; - } - cmn->depth_format = desc->depth.pixel_format; + cmn->cull_mode = desc->cull_mode; + cmn->face_winding = desc->face_winding; cmn->sample_count = desc->sample_count; - cmn->depth_bias = desc->depth.bias; - cmn->depth_bias_slope_scale = desc->depth.bias_slope_scale; - cmn->depth_bias_clamp = desc->depth.bias_clamp; cmn->blend_color = desc->blend_color; + cmn->alpha_to_coverage_enabled = desc->alpha_to_coverage_enabled; } typedef struct { @@ -3614,132 +4259,36 @@ typedef struct { } _sg_pass_attachment_common_t; typedef struct { + int width; + int height; int num_color_atts; _sg_pass_attachment_common_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; + _sg_pass_attachment_common_t resolve_atts[SG_MAX_COLOR_ATTACHMENTS]; _sg_pass_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_pass_attachment_desc* att_desc; - _sg_pass_attachment_common_t* att; +_SOKOL_PRIVATE void _sg_pass_attachment_common_init(_sg_pass_attachment_common_t* cmn, const sg_pass_attachment_desc* desc) { + cmn->image_id = desc->image; + cmn->mip_level = desc->mip_level; + cmn->slice = desc->slice; +} + +_SOKOL_PRIVATE void _sg_pass_common_init(_sg_pass_common_t* cmn, const sg_pass_desc* desc, int width, int height) { + SOKOL_ASSERT((width > 0) && (height > 0)); + cmn->width = width; + cmn->height = height; for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - att_desc = &desc->color_attachments[i]; - if (att_desc->image.id != SG_INVALID_ID) { + if (desc->color_attachments[i].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; + _sg_pass_attachment_common_init(&cmn->color_atts[i], &desc->color_attachments[i]); + _sg_pass_attachment_common_init(&cmn->resolve_atts[i], &desc->resolve_attachments[i]); } } - 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)); - _sg_clear(cache, sizeof(_sg_sampler_cache_t)); - cache->capacity = capacity; - const size_t size = (size_t)cache->capacity * sizeof(_sg_sampler_cache_item_t); - cache->items = (_sg_sampler_cache_item_t*) _sg_malloc_clear(size); -} - -_SOKOL_PRIVATE void _sg_smpcache_discard(_sg_sampler_cache_t* cache) { - SOKOL_ASSERT(cache && cache->items); - _sg_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; - } + if (desc->depth_stencil_attachment.image.id != SG_INVALID_ID) { + _sg_pass_attachment_common_init(&cmn->ds_att, &desc->depth_stencil_attachment); } - /* 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 < cache->num_items); - return cache->items[item_index].sampler_handle; -} - -/*=== DUMMY BACKEND DECLARATIONS =============================================*/ #if defined(SOKOL_DUMMY_BACKEND) typedef struct { _sg_slot_t slot; @@ -3753,6 +4302,12 @@ typedef struct { } _sg_dummy_image_t; typedef _sg_dummy_image_t _sg_image_t; +typedef struct { + _sg_slot_t slot; + _sg_sampler_common_t cmn; +} _sg_dummy_sampler_t; +typedef _sg_dummy_sampler_t _sg_sampler_t; + typedef struct { _sg_slot_t slot; _sg_shader_common_t cmn; @@ -3775,6 +4330,7 @@ typedef struct { _sg_pass_common_t cmn; struct { _sg_dummy_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; + _sg_dummy_attachment_t resolve_atts[SG_MAX_COLOR_ATTACHMENTS]; _sg_dummy_attachment_t ds_att; } dmy; } _sg_dummy_pass_t; @@ -3786,14 +4342,13 @@ typedef struct { } _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 */ + bool injected; // 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; @@ -3803,14 +4358,23 @@ typedef struct { _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 */ + bool injected; // 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 { + _sg_slot_t slot; + _sg_sampler_common_t cmn; + struct { + GLuint smp; + bool injected; // true if external sampler was injects in sg_sampler_desc.gl_sampler + } gl; +} _sg_gl_sampler_t; +typedef _sg_gl_sampler_t _sg_sampler_t; + typedef struct { GLint gl_loc; sg_uniform_type type; @@ -3825,7 +4389,7 @@ typedef struct { typedef struct { int gl_tex_slot; -} _sg_gl_shader_image_t; +} _sg_gl_shader_image_sampler_t; typedef struct { _sg_str_t name; @@ -3833,7 +4397,7 @@ typedef struct { 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_image_sampler_t image_samplers[SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS]; } _sg_gl_shader_stage_t; typedef struct { @@ -3848,8 +4412,8 @@ typedef struct { typedef _sg_gl_shader_t _sg_shader_t; typedef struct { - int8_t vb_index; /* -1 if attr is not enabled */ - int8_t divisor; /* -1 if not initialized */ + int8_t vb_index; // -1 if attr is not enabled + int8_t divisor; // -1 if not initialized uint8_t stride; uint8_t size; uint8_t normalized; @@ -3878,7 +4442,6 @@ typedef _sg_gl_pipeline_t _sg_pipeline_t; typedef struct { _sg_image_t* image; - GLuint gl_msaa_resolve_buffer; } _sg_gl_attachment_t; typedef struct { @@ -3887,7 +4450,9 @@ typedef struct { struct { GLuint fb; _sg_gl_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; + _sg_gl_attachment_t resolve_atts[SG_MAX_COLOR_ATTACHMENTS]; _sg_gl_attachment_t ds_att; + GLuint msaa_resolve_framebuffer[SG_MAX_COLOR_ATTACHMENTS]; } gl; } _sg_gl_pass_t; typedef _sg_gl_pass_t _sg_pass_t; @@ -3895,9 +4460,7 @@ typedef _sg_pass_attachment_common_t _sg_pass_attachment_t; typedef struct { _sg_slot_t slot; - #if !defined(SOKOL_GLES2) GLuint vao; - #endif GLuint default_framebuffer; } _sg_gl_context_t; typedef _sg_gl_context_t _sg_context_t; @@ -3910,7 +4473,10 @@ typedef struct { typedef struct { GLenum target; GLuint texture; -} _sg_gl_texture_bind_slot; + GLuint sampler; +} _sg_gl_cache_texture_sampler_bind_slot; + +#define _SG_GL_TEXTURE_SAMPLER_CACHE_SIZE (SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS * SG_NUM_SHADER_STAGES) typedef struct { sg_depth_state depth; @@ -3929,8 +4495,8 @@ typedef struct { 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; + _sg_gl_cache_texture_sampler_bind_slot texture_samplers[_SG_GL_TEXTURE_SAMPLER_CACHE_SIZE]; + _sg_gl_cache_texture_sampler_bind_slot stored_texture_sampler; int cur_ib_offset; GLenum cur_primitive_type; GLenum cur_index_type; @@ -3941,7 +4507,6 @@ typedef struct { typedef struct { bool valid; - bool gles2; bool in_pass; int cur_pass_width; int cur_pass_height; @@ -3951,13 +4516,14 @@ typedef struct { _sg_gl_state_cache_t cache; bool ext_anisotropic; GLint max_anisotropy; - GLint max_combined_texture_image_units; + sg_store_action color_store_actions[SG_MAX_COLOR_ATTACHMENTS]; + sg_store_action depth_store_action; + sg_store_action stencil_store_action; #if _SOKOL_USE_WIN32_GL_LOADER HINSTANCE opengl32_dll; #endif } _sg_gl_backend_t; -/*== D3D11 BACKEND DECLARATIONS ==============================================*/ #elif defined(SOKOL_D3D11) typedef struct { @@ -3976,14 +4542,21 @@ typedef struct { DXGI_FORMAT format; ID3D11Texture2D* tex2d; ID3D11Texture3D* tex3d; - ID3D11Texture2D* texds; - ID3D11Texture2D* texmsaa; + ID3D11Resource* res; // either tex2d or tex3d ID3D11ShaderResourceView* srv; - ID3D11SamplerState* smp; } d3d11; } _sg_d3d11_image_t; typedef _sg_d3d11_image_t _sg_image_t; +typedef struct { + _sg_slot_t slot; + _sg_sampler_common_t cmn; + struct { + ID3D11SamplerState* smp; + } d3d11; +} _sg_d3d11_sampler_t; +typedef _sg_d3d11_sampler_t _sg_sampler_t; + typedef struct { _sg_str_t sem_name; int sem_index; @@ -4013,7 +4586,7 @@ typedef struct { _sg_shader_t* shader; struct { UINT stencil_ref; - UINT vb_strides[SG_MAX_SHADERSTAGE_BUFFERS]; + UINT vb_strides[SG_MAX_VERTEX_BUFFERS]; D3D_PRIMITIVE_TOPOLOGY topology; DXGI_FORMAT index_format; ID3D11InputLayout* il; @@ -4026,20 +4599,19 @@ typedef _sg_d3d11_pipeline_t _sg_pipeline_t; typedef struct { _sg_image_t* image; - ID3D11RenderTargetView* rtv; -} _sg_d3d11_color_attachment_t; - -typedef struct { - _sg_image_t* image; - ID3D11DepthStencilView* dsv; -} _sg_d3d11_ds_attachment_t; + union { + ID3D11RenderTargetView* rtv; + ID3D11DepthStencilView* dsv; + } view; +} _sg_d3d11_attachment_t; typedef struct { _sg_slot_t slot; _sg_pass_common_t cmn; struct { - _sg_d3d11_color_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; - _sg_d3d11_ds_attachment_t ds_att; + _sg_d3d11_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; + _sg_d3d11_attachment_t resolve_atts[SG_MAX_COLOR_ATTACHMENTS]; + _sg_d3d11_attachment_t ds_att; } d3d11; } _sg_d3d11_pass_t; typedef _sg_d3d11_pass_t _sg_pass_t; @@ -4071,15 +4643,14 @@ typedef struct { sg_pipeline cur_pipeline_id; ID3D11RenderTargetView* cur_rtvs[SG_MAX_COLOR_ATTACHMENTS]; ID3D11DepthStencilView* cur_dsv; - /* on-demand loaded d3dcompiler_47.dll handles */ + // on-demand loaded d3dcompiler_47.dll handles HINSTANCE d3dcompiler_dll; bool d3dcompiler_dll_load_failed; pD3DCompile D3DCompile_func; - /* global subresourcedata array for texture updates */ + // global subresourcedata array for texture updates D3D11_SUBRESOURCE_DATA subres_data[SG_MAX_MIPMAPS * SG_MAX_TEXTUREARRAY_LAYERS]; } _sg_d3d11_backend_t; -/*=== METAL BACKEND DECLARATIONS =============================================*/ #elif defined(SOKOL_METAL) #if defined(_SG_TARGET_MACOS) || defined(_SG_TARGET_IOS_SIMULATOR) @@ -4090,7 +4661,7 @@ typedef struct { #define _SG_MTL_INVALID_SLOT_INDEX (0) typedef struct { - uint32_t frame_index; /* frame index at which it is safe to release this resource */ + uint32_t frame_index; // frame index at which it is safe to release this resource int slot_index; } _sg_mtl_release_item_t; @@ -4108,7 +4679,7 @@ typedef struct { _sg_slot_t slot; _sg_buffer_common_t cmn; struct { - int buf[SG_NUM_INFLIGHT_FRAMES]; /* index into _sg_mtl_pool */ + int buf[SG_NUM_INFLIGHT_FRAMES]; // index into _sg_mtl_pool } mtl; } _sg_mtl_buffer_t; typedef _sg_mtl_buffer_t _sg_buffer_t; @@ -4118,13 +4689,19 @@ typedef struct { _sg_image_common_t cmn; struct { int tex[SG_NUM_INFLIGHT_FRAMES]; - int depth_tex; - int msaa_tex; - int sampler_state; } mtl; } _sg_mtl_image_t; typedef _sg_mtl_image_t _sg_image_t; +typedef struct { + _sg_slot_t slot; + _sg_sampler_common_t cmn; + struct { + int sampler_state; + } mtl; +} _sg_mtl_sampler_t; +typedef _sg_mtl_sampler_t _sg_sampler_t; + typedef struct { int mtl_lib; int mtl_func; @@ -4165,6 +4742,7 @@ typedef struct { _sg_pass_common_t cmn; struct { _sg_mtl_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; + _sg_mtl_attachment_t resolve_atts[SG_MAX_COLOR_ATTACHMENTS]; _sg_mtl_attachment_t ds_att; } mtl; } _sg_mtl_pass_t; @@ -4176,24 +4754,29 @@ typedef struct { } _sg_mtl_context_t; typedef _sg_mtl_context_t _sg_context_t; -/* resouce binding state cache */ +// resouce binding state cache typedef struct { const _sg_pipeline_t* cur_pipeline; sg_pipeline cur_pipeline_id; const _sg_buffer_t* cur_indexbuffer; int cur_indexbuffer_offset; sg_buffer cur_indexbuffer_id; - const _sg_buffer_t* cur_vertexbuffers[SG_MAX_SHADERSTAGE_BUFFERS]; - int cur_vertexbuffer_offsets[SG_MAX_SHADERSTAGE_BUFFERS]; - sg_buffer cur_vertexbuffer_ids[SG_MAX_SHADERSTAGE_BUFFERS]; + const _sg_buffer_t* cur_vertexbuffers[SG_MAX_VERTEX_BUFFERS]; + int cur_vertexbuffer_offsets[SG_MAX_VERTEX_BUFFERS]; + sg_buffer cur_vertexbuffer_ids[SG_MAX_VERTEX_BUFFERS]; const _sg_image_t* cur_vs_images[SG_MAX_SHADERSTAGE_IMAGES]; sg_image cur_vs_image_ids[SG_MAX_SHADERSTAGE_IMAGES]; const _sg_image_t* cur_fs_images[SG_MAX_SHADERSTAGE_IMAGES]; sg_image cur_fs_image_ids[SG_MAX_SHADERSTAGE_IMAGES]; + const _sg_sampler_t* cur_vs_samplers[SG_MAX_SHADERSTAGE_SAMPLERS]; + sg_sampler cur_vs_sampler_ids[SG_MAX_SHADERSTAGE_SAMPLERS]; + const _sg_sampler_t* cur_fs_samplers[SG_MAX_SHADERSTAGE_SAMPLERS]; + sg_sampler cur_fs_sampler_ids[SG_MAX_SHADERSTAGE_SAMPLERS]; } _sg_mtl_state_cache_t; typedef struct { bool valid; + bool use_shared_storage_mode; const void*(*renderpass_descriptor_cb)(void); const void*(*renderpass_descriptor_userdata_cb)(void*); const void*(*drawable_cb)(void); @@ -4209,18 +4792,15 @@ typedef struct { int cur_width; int cur_height; _sg_mtl_state_cache_t state_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 present_cmd_buffer; id cmd_encoder; id uniform_buffers[SG_NUM_INFLIGHT_FRAMES]; } _sg_mtl_backend_t; -/*=== WGPU BACKEND DECLARATIONS ==============================================*/ #elif defined(SOKOL_WGPU) #define _SG_WGPU_STAGING_ALIGN (256) @@ -4298,33 +4878,33 @@ typedef struct { } _sg_wgpu_context_t; typedef _sg_wgpu_context_t _sg_context_t; -/* a pool of per-frame uniform buffers */ +// 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 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 */ + 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 */ + 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 */ +// ...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 */ + 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 */ +// the WGPU backend state typedef struct { bool valid; bool in_pass; @@ -4346,15 +4926,14 @@ typedef struct { 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 =============================================*/ +// POOL STRUCTS -/* this *MUST* remain 0 */ +// this *MUST* remain 0 #define _SG_INVALID_SLOT_INDEX (0) typedef struct { @@ -4367,149 +4946,20 @@ typedef struct { typedef struct { _sg_pool_t buffer_pool; _sg_pool_t image_pool; + _sg_pool_t sampler_pool; _sg_pool_t shader_pool; _sg_pool_t pipeline_pool; _sg_pool_t pass_pool; _sg_pool_t context_pool; _sg_buffer_t* buffers; _sg_image_t* images; + _sg_sampler_t* samplers; _sg_shader_t* shaders; _sg_pipeline_t* pipelines; _sg_pass_t* passes; _sg_context_t* contexts; } _sg_pools_t; -/*=== VALIDATION LAYER DECLARATIONS ==========================================*/ -typedef enum { - /* special case 'validation was successful' */ - _SG_VALIDATE_SUCCESS, - - /* buffer creation */ - _SG_VALIDATE_BUFFERDESC_CANARY, - _SG_VALIDATE_BUFFERDESC_SIZE, - _SG_VALIDATE_BUFFERDESC_DATA, - _SG_VALIDATE_BUFFERDESC_DATA_SIZE, - _SG_VALIDATE_BUFFERDESC_NO_DATA, - - /* image data (for image creation and updating) */ - _SG_VALIDATE_IMAGEDATA_NODATA, - _SG_VALIDATE_IMAGEDATA_DATA_SIZE, - - /* image creation */ - _SG_VALIDATE_IMAGEDESC_CANARY, - _SG_VALIDATE_IMAGEDESC_WIDTH, - _SG_VALIDATE_IMAGEDESC_HEIGHT, - _SG_VALIDATE_IMAGEDESC_RT_PIXELFORMAT, - _SG_VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT, - _SG_VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT, - _SG_VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT, - _SG_VALIDATE_IMAGEDESC_RT_IMMUTABLE, - _SG_VALIDATE_IMAGEDESC_RT_NO_DATA, - _SG_VALIDATE_IMAGEDESC_INJECTED_NO_DATA, - _SG_VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA, - _SG_VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE, - - /* shader creation */ - _SG_VALIDATE_SHADERDESC_CANARY, - _SG_VALIDATE_SHADERDESC_SOURCE, - _SG_VALIDATE_SHADERDESC_BYTECODE, - _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE, - _SG_VALIDATE_SHADERDESC_NO_BYTECODE_SIZE, - _SG_VALIDATE_SHADERDESC_NO_CONT_UBS, - _SG_VALIDATE_SHADERDESC_NO_CONT_IMGS, - _SG_VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS, - _SG_VALIDATE_SHADERDESC_NO_UB_MEMBERS, - _SG_VALIDATE_SHADERDESC_UB_MEMBER_NAME, - _SG_VALIDATE_SHADERDESC_UB_SIZE_MISMATCH, - _SG_VALIDATE_SHADERDESC_UB_ARRAY_COUNT, - _SG_VALIDATE_SHADERDESC_UB_STD140_ARRAY_TYPE, - _SG_VALIDATE_SHADERDESC_IMG_NAME, - _SG_VALIDATE_SHADERDESC_ATTR_NAMES, - _SG_VALIDATE_SHADERDESC_ATTR_SEMANTICS, - _SG_VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG, - - /* pipeline creation */ - _SG_VALIDATE_PIPELINEDESC_CANARY, - _SG_VALIDATE_PIPELINEDESC_SHADER, - _SG_VALIDATE_PIPELINEDESC_NO_ATTRS, - _SG_VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4, - _SG_VALIDATE_PIPELINEDESC_ATTR_NAME, - _SG_VALIDATE_PIPELINEDESC_ATTR_SEMANTICS, - - /* pass creation */ - _SG_VALIDATE_PASSDESC_CANARY, - _SG_VALIDATE_PASSDESC_NO_COLOR_ATTS, - _SG_VALIDATE_PASSDESC_NO_CONT_COLOR_ATTS, - _SG_VALIDATE_PASSDESC_IMAGE, - _SG_VALIDATE_PASSDESC_MIPLEVEL, - _SG_VALIDATE_PASSDESC_FACE, - _SG_VALIDATE_PASSDESC_LAYER, - _SG_VALIDATE_PASSDESC_SLICE, - _SG_VALIDATE_PASSDESC_IMAGE_NO_RT, - _SG_VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT, - _SG_VALIDATE_PASSDESC_DEPTH_INV_PIXELFORMAT, - _SG_VALIDATE_PASSDESC_IMAGE_SIZES, - _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS, - - /* sg_begin_pass validation */ - _SG_VALIDATE_BEGINPASS_PASS, - _SG_VALIDATE_BEGINPASS_IMAGE, - - /* sg_apply_pipeline validation */ - _SG_VALIDATE_APIP_PIPELINE_VALID_ID, - _SG_VALIDATE_APIP_PIPELINE_EXISTS, - _SG_VALIDATE_APIP_PIPELINE_VALID, - _SG_VALIDATE_APIP_SHADER_EXISTS, - _SG_VALIDATE_APIP_SHADER_VALID, - _SG_VALIDATE_APIP_ATT_COUNT, - _SG_VALIDATE_APIP_COLOR_FORMAT, - _SG_VALIDATE_APIP_DEPTH_FORMAT, - _SG_VALIDATE_APIP_SAMPLE_COUNT, - - /* sg_apply_bindings validation */ - _SG_VALIDATE_ABND_PIPELINE, - _SG_VALIDATE_ABND_PIPELINE_EXISTS, - _SG_VALIDATE_ABND_PIPELINE_VALID, - _SG_VALIDATE_ABND_VBS, - _SG_VALIDATE_ABND_VB_EXISTS, - _SG_VALIDATE_ABND_VB_TYPE, - _SG_VALIDATE_ABND_VB_OVERFLOW, - _SG_VALIDATE_ABND_NO_IB, - _SG_VALIDATE_ABND_IB, - _SG_VALIDATE_ABND_IB_EXISTS, - _SG_VALIDATE_ABND_IB_TYPE, - _SG_VALIDATE_ABND_IB_OVERFLOW, - _SG_VALIDATE_ABND_VS_IMGS, - _SG_VALIDATE_ABND_VS_IMG_EXISTS, - _SG_VALIDATE_ABND_VS_IMG_TYPES, - _SG_VALIDATE_ABND_FS_IMGS, - _SG_VALIDATE_ABND_FS_IMG_EXISTS, - _SG_VALIDATE_ABND_FS_IMG_TYPES, - - /* sg_apply_uniforms validation */ - _SG_VALIDATE_AUB_NO_PIPELINE, - _SG_VALIDATE_AUB_NO_UB_AT_SLOT, - _SG_VALIDATE_AUB_SIZE, - - /* sg_update_buffer validation */ - _SG_VALIDATE_UPDATEBUF_USAGE, - _SG_VALIDATE_UPDATEBUF_SIZE, - _SG_VALIDATE_UPDATEBUF_ONCE, - _SG_VALIDATE_UPDATEBUF_APPEND, - - /* sg_append_buffer validation */ - _SG_VALIDATE_APPENDBUF_USAGE, - _SG_VALIDATE_APPENDBUF_SIZE, - _SG_VALIDATE_APPENDBUF_UPDATE, - - /* sg_update_image validation */ - _SG_VALIDATE_UPDIMG_USAGE, - _SG_VALIDATE_UPDIMG_NOTENOUGHDATA, - _SG_VALIDATE_UPDIMG_ONCE -} _sg_validate_error_t; - -/*=== GENERIC BACKEND STATE ==================================================*/ - typedef struct { int num; // number of allocated commit listener items int upper; // the current upper index (no valid items past this point) @@ -4518,7 +4968,7 @@ typedef struct { typedef struct { bool valid; - sg_desc desc; /* original desc with default values patched in */ + sg_desc desc; // original desc with default values patched in uint32_t frame_index; sg_context active_context; sg_pass cur_pass; @@ -4527,7 +4977,7 @@ typedef struct { bool bindings_valid; bool next_draw_valid; #if defined(SOKOL_DEBUG) - _sg_validate_error_t validate_error; + sg_log_item validate_error; #endif _sg_pools_t pools; sg_backend backend; @@ -4550,7 +5000,53 @@ typedef struct { } _sg_state_t; static _sg_state_t _sg; -/*-- helper functions --------------------------------------------------------*/ +// ██ ██████ ██████ ██████ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ████ ██ ██ +// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██████ ██ ██ ████ ██████ +// +// >>logging +#if defined(SOKOL_DEBUG) +#define _SG_LOGITEM_XMACRO(item,msg) #item ": " msg, +static const char* _sg_log_messages[] = { + _SG_LOG_ITEMS +}; +#undef _SG_LOGITEM_XMACRO +#endif // SOKOL_DEBUG + +#define _SG_PANIC(code) _sg_log(SG_LOGITEM_ ##code, 0, 0, __LINE__) +#define _SG_ERROR(code) _sg_log(SG_LOGITEM_ ##code, 1, 0, __LINE__) +#define _SG_WARN(code) _sg_log(SG_LOGITEM_ ##code, 2, 0, __LINE__) +#define _SG_INFO(code) _sg_log(SG_LOGITEM_ ##code, 3, 0, __LINE__) +#define _SG_LOGMSG(code,msg) _sg_log(SG_LOGITEM_ ##code, 3, msg, __LINE__) +#define _SG_VALIDATE(cond,code) if (!(cond)){ _sg.validate_error = SG_LOGITEM_ ##code; _sg_log(SG_LOGITEM_ ##code, 1, 0, __LINE__); } + +static void _sg_log(sg_log_item log_item, uint32_t log_level, const char* msg, uint32_t line_nr) { + if (_sg.desc.logger.func) { + const char* filename = 0; + #if defined(SOKOL_DEBUG) + filename = __FILE__; + if (0 == msg) { + msg = _sg_log_messages[log_item]; + } + #endif + _sg.desc.logger.func("sg", log_level, log_item, msg, line_nr, filename, _sg.desc.logger.user_data); + } else { + // for log level PANIC it would be 'undefined behaviour' to continue + if (log_level == 0) { + abort(); + } + } +} + +// ███ ███ ███████ ███ ███ ██████ ██████ ██ ██ +// ████ ████ ██ ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ █████ ██ ████ ██ ██ ██ ██████ ████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ██ ██████ ██ ██ ██ +// +// >>memory // a helper macro to clear a struct with potentially ARC'ed ObjC references #if defined(SOKOL_METAL) @@ -4571,13 +5067,14 @@ _SOKOL_PRIVATE void _sg_clear(void* ptr, size_t size) { _SOKOL_PRIVATE void* _sg_malloc(size_t size) { SOKOL_ASSERT(size > 0); void* ptr; - if (_sg.desc.allocator.alloc) { - ptr = _sg.desc.allocator.alloc(size, _sg.desc.allocator.user_data); - } - else { + if (_sg.desc.allocator.alloc_fn) { + ptr = _sg.desc.allocator.alloc_fn(size, _sg.desc.allocator.user_data); + } else { ptr = malloc(size); } - SOKOL_ASSERT(ptr); + if (0 == ptr) { + _SG_PANIC(MALLOC_FAILED); + } return ptr; } @@ -4588,24 +5085,12 @@ _SOKOL_PRIVATE void* _sg_malloc_clear(size_t size) { } _SOKOL_PRIVATE void _sg_free(void* ptr) { - if (_sg.desc.allocator.free) { - _sg.desc.allocator.free(ptr, _sg.desc.allocator.user_data); - } - else { - free(ptr); - } -} - -#if defined(SOKOL_DEBUG) -_SOKOL_PRIVATE void _sg_log(const char* msg) { - SOKOL_ASSERT(msg); - if (_sg.desc.logger.log_cb) { - _sg.desc.logger.log_cb(msg, _sg.desc.logger.user_data); + if (_sg.desc.allocator.free_fn) { + _sg.desc.allocator.free_fn(ptr, _sg.desc.allocator.user_data); } else { - SOKOL_LOG(msg); + free(ptr); } } -#endif _SOKOL_PRIVATE bool _sg_strempty(const _sg_str_t* str) { return 0 == str->buf[0]; @@ -4624,18 +5109,23 @@ _SOKOL_PRIVATE void _sg_strcpy(_sg_str_t* dst, const char* src) { strncpy(dst->buf, src, _SG_STRING_SIZE); #endif dst->buf[_SG_STRING_SIZE-1] = 0; - } - else { + } else { _sg_clear(dst->buf, _SG_STRING_SIZE); } } +// ██ ██ ███████ ██ ██████ ███████ ██████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ █████ ██ ██████ █████ ██████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ███████ ██ ███████ ██ ██ ███████ +// +// >>helpers _SOKOL_PRIVATE uint32_t _sg_align_u32(uint32_t val, uint32_t align) { SOKOL_ASSERT((align > 0) && ((align & (align - 1)) == 0)); return (val + (align - 1)) & ~(align - 1); } -/* return byte size of a vertex format */ _SOKOL_PRIVATE int _sg_vertexformat_bytesize(sg_vertex_format fmt) { switch (fmt) { case SG_VERTEXFORMAT_FLOAT: return 4; @@ -4653,6 +5143,8 @@ _SOKOL_PRIVATE int _sg_vertexformat_bytesize(sg_vertex_format fmt) { case SG_VERTEXFORMAT_SHORT4N: return 8; case SG_VERTEXFORMAT_USHORT4N: return 8; case SG_VERTEXFORMAT_UINT10_N2: return 4; + case SG_VERTEXFORMAT_HALF2: return 4; + case SG_VERTEXFORMAT_HALF4: return 8; case SG_VERTEXFORMAT_INVALID: return 0; default: SOKOL_UNREACHABLE; @@ -4663,8 +5155,7 @@ _SOKOL_PRIVATE int _sg_vertexformat_bytesize(sg_vertex_format fmt) { _SOKOL_PRIVATE uint32_t _sg_uniform_alignment(sg_uniform_type type, int array_count, sg_uniform_layout ub_layout) { if (ub_layout == SG_UNIFORMLAYOUT_NATIVE) { return 1; - } - else { + } else { SOKOL_ASSERT(array_count > 0); if (array_count == 1) { switch (type) { @@ -4685,8 +5176,7 @@ _SOKOL_PRIVATE uint32_t _sg_uniform_alignment(sg_uniform_type type, int array_co SOKOL_UNREACHABLE; return 1; } - } - else { + } else { return 16; } } @@ -4714,8 +5204,7 @@ _SOKOL_PRIVATE uint32_t _sg_uniform_size(sg_uniform_type type, int array_count, SOKOL_UNREACHABLE; return 0; } - } - else { + } else { if (ub_layout == SG_UNIFORMLAYOUT_NATIVE) { switch (type) { case SG_UNIFORMTYPE_FLOAT: @@ -4736,8 +5225,7 @@ _SOKOL_PRIVATE uint32_t _sg_uniform_size(sg_uniform_type type, int array_count, SOKOL_UNREACHABLE; return 0; } - } - else { + } else { switch (type) { case SG_UNIFORMTYPE_FLOAT: case SG_UNIFORMTYPE_FLOAT2: @@ -4758,7 +5246,6 @@ _SOKOL_PRIVATE uint32_t _sg_uniform_size(sg_uniform_type type, int array_count, } } -/* return true if pixel format is a compressed format */ _SOKOL_PRIVATE bool _sg_is_compressed_pixel_format(sg_pixel_format fmt) { switch (fmt) { case SG_PIXELFORMAT_BC1_RGBA: @@ -4786,26 +5273,26 @@ _SOKOL_PRIVATE bool _sg_is_compressed_pixel_format(sg_pixel_format fmt) { } } -/* return true if pixel format is a valid render target format */ _SOKOL_PRIVATE bool _sg_is_valid_rendertarget_color_format(sg_pixel_format fmt) { const int fmt_index = (int) fmt; SOKOL_ASSERT((fmt_index >= 0) && (fmt_index < _SG_PIXELFORMAT_NUM)); return _sg.formats[fmt_index].render && !_sg.formats[fmt_index].depth; } -/* return true if pixel format is a valid depth format */ _SOKOL_PRIVATE bool _sg_is_valid_rendertarget_depth_format(sg_pixel_format fmt) { const int fmt_index = (int) fmt; SOKOL_ASSERT((fmt_index >= 0) && (fmt_index < _SG_PIXELFORMAT_NUM)); return _sg.formats[fmt_index].render && _sg.formats[fmt_index].depth; } -/* return true if pixel format is a depth-stencil format */ +_SOKOL_PRIVATE bool _sg_is_depth_or_depth_stencil_format(sg_pixel_format fmt) { + return (SG_PIXELFORMAT_DEPTH == fmt) || (SG_PIXELFORMAT_DEPTH_STENCIL == fmt); +} + _SOKOL_PRIVATE bool _sg_is_depth_stencil_format(sg_pixel_format fmt) { return (SG_PIXELFORMAT_DEPTH_STENCIL == fmt); } -/* return the bytes-per-pixel for a pixel format */ _SOKOL_PRIVATE int _sg_pixelformat_bytesize(sg_pixel_format fmt) { switch (fmt) { case SG_PIXELFORMAT_R8: @@ -4813,7 +5300,6 @@ _SOKOL_PRIVATE int _sg_pixelformat_bytesize(sg_pixel_format fmt) { case SG_PIXELFORMAT_R8UI: case SG_PIXELFORMAT_R8SI: return 1; - case SG_PIXELFORMAT_R16: case SG_PIXELFORMAT_R16SN: case SG_PIXELFORMAT_R16UI: @@ -4824,7 +5310,6 @@ _SOKOL_PRIVATE int _sg_pixelformat_bytesize(sg_pixel_format fmt) { case SG_PIXELFORMAT_RG8UI: case SG_PIXELFORMAT_RG8SI: return 2; - case SG_PIXELFORMAT_R32UI: case SG_PIXELFORMAT_R32SI: case SG_PIXELFORMAT_R32F: @@ -4834,6 +5319,7 @@ _SOKOL_PRIVATE int _sg_pixelformat_bytesize(sg_pixel_format fmt) { case SG_PIXELFORMAT_RG16SI: case SG_PIXELFORMAT_RG16F: case SG_PIXELFORMAT_RGBA8: + case SG_PIXELFORMAT_SRGB8A8: case SG_PIXELFORMAT_RGBA8SN: case SG_PIXELFORMAT_RGBA8UI: case SG_PIXELFORMAT_RGBA8SI: @@ -4842,7 +5328,6 @@ _SOKOL_PRIVATE int _sg_pixelformat_bytesize(sg_pixel_format fmt) { case SG_PIXELFORMAT_RG11B10F: case SG_PIXELFORMAT_RGB9E5: return 4; - case SG_PIXELFORMAT_RG32UI: case SG_PIXELFORMAT_RG32SI: case SG_PIXELFORMAT_RG32F: @@ -4852,12 +5337,10 @@ _SOKOL_PRIVATE int _sg_pixelformat_bytesize(sg_pixel_format fmt) { case SG_PIXELFORMAT_RGBA16SI: case SG_PIXELFORMAT_RGBA16F: return 8; - case SG_PIXELFORMAT_RGBA32UI: case SG_PIXELFORMAT_RGBA32SI: case SG_PIXELFORMAT_RGBA32F: return 16; - default: SOKOL_UNREACHABLE; return 0; @@ -4925,7 +5408,7 @@ _SOKOL_PRIVATE int _sg_row_pitch(sg_pixel_format fmt, int width, int row_align) return pitch; } -/* compute the number of rows in a surface depending on pixel format */ +// 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) { @@ -4968,6 +5451,11 @@ _SOKOL_PRIVATE int _sg_num_rows(sg_pixel_format fmt, int height) { return num_rows; } +// return size of a mipmap level +_SOKOL_PRIVATE int _sg_miplevel_dim(int base_dim, int mip_level) { + return _sg_max(base_dim >> mip_level, 1); +} + /* return pitch of a 2D subimage / texture slice see ComputePitch in https://github.com/microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexUtil.cpp */ @@ -4976,7 +5464,7 @@ _SOKOL_PRIVATE int _sg_surface_pitch(sg_pixel_format fmt, int width, int height, return num_rows * _sg_row_pitch(fmt, width, row_align); } -/* capability table pixel format helper functions */ +// capability table pixel format helper functions _SOKOL_PRIVATE void _sg_pixelformat_all(sg_pixelformat_info* pfi) { pfi->sample = true; pfi->filter = true; @@ -5038,30 +5526,44 @@ _SOKOL_PRIVATE void _sg_pixelformat_sfbr(sg_pixelformat_info* pfi) { pfi->render = true; } -/* resolve pass action defaults into a new pass action struct */ _SOKOL_PRIVATE void _sg_resolve_default_pass_action(const sg_pass_action* from, sg_pass_action* to) { SOKOL_ASSERT(from && to); *to = *from; for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - if (to->colors[i].action == _SG_ACTION_DEFAULT) { - to->colors[i].action = SG_ACTION_CLEAR; - to->colors[i].value.r = SG_DEFAULT_CLEAR_RED; - to->colors[i].value.g = SG_DEFAULT_CLEAR_GREEN; - to->colors[i].value.b = SG_DEFAULT_CLEAR_BLUE; - to->colors[i].value.a = SG_DEFAULT_CLEAR_ALPHA; + if (to->colors[i].load_action == _SG_LOADACTION_DEFAULT) { + to->colors[i].load_action = SG_LOADACTION_CLEAR; + to->colors[i].clear_value.r = SG_DEFAULT_CLEAR_RED; + to->colors[i].clear_value.g = SG_DEFAULT_CLEAR_GREEN; + to->colors[i].clear_value.b = SG_DEFAULT_CLEAR_BLUE; + to->colors[i].clear_value.a = SG_DEFAULT_CLEAR_ALPHA; } + if (to->colors[i].store_action == _SG_STOREACTION_DEFAULT) { + to->colors[i].store_action = SG_STOREACTION_STORE; + } + } + if (to->depth.load_action == _SG_LOADACTION_DEFAULT) { + to->depth.load_action = SG_LOADACTION_CLEAR; + to->depth.clear_value = SG_DEFAULT_CLEAR_DEPTH; } - if (to->depth.action == _SG_ACTION_DEFAULT) { - to->depth.action = SG_ACTION_CLEAR; - to->depth.value = SG_DEFAULT_CLEAR_DEPTH; + if (to->depth.store_action == _SG_STOREACTION_DEFAULT) { + to->depth.store_action = SG_STOREACTION_DONTCARE; } - if (to->stencil.action == _SG_ACTION_DEFAULT) { - to->stencil.action = SG_ACTION_CLEAR; - to->stencil.value = SG_DEFAULT_CLEAR_STENCIL; + if (to->stencil.load_action == _SG_LOADACTION_DEFAULT) { + to->stencil.load_action = SG_LOADACTION_CLEAR; + to->stencil.clear_value = SG_DEFAULT_CLEAR_STENCIL; + } + if (to->stencil.store_action == _SG_STOREACTION_DEFAULT) { + to->stencil.store_action = SG_STOREACTION_DONTCARE; } } -/*== DUMMY BACKEND IMPL ======================================================*/ +// ██████ ██ ██ ███ ███ ███ ███ ██ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ██ ██ ██ ██ ████ ████ ████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ ██ ██ ██ ██ ████ ██ ██ ████ ██ ████ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██████ ██ ██ ██ ██ ██ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>dummy backend #if defined(SOKOL_DUMMY_BACKEND) _SOKOL_PRIVATE void _sg_dummy_setup_backend(const sg_desc* desc) { @@ -5080,11 +5582,11 @@ _SOKOL_PRIVATE void _sg_dummy_setup_backend(const sg_desc* desc) { } _SOKOL_PRIVATE void _sg_dummy_discard_backend(void) { - /* empty */ + // empty } _SOKOL_PRIVATE void _sg_dummy_reset_state_cache(void) { - /* empty*/ + // empty } _SOKOL_PRIVATE sg_resource_state _sg_dummy_create_context(_sg_context_t* ctx) { @@ -5105,7 +5607,8 @@ _SOKOL_PRIVATE void _sg_dummy_activate_context(_sg_context_t* ctx) { _SOKOL_PRIVATE sg_resource_state _sg_dummy_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { SOKOL_ASSERT(buf && desc); - _sg_buffer_common_init(&buf->cmn, desc); + _SOKOL_UNUSED(buf); + _SOKOL_UNUSED(desc); return SG_RESOURCESTATE_VALID; } @@ -5116,7 +5619,8 @@ _SOKOL_PRIVATE void _sg_dummy_discard_buffer(_sg_buffer_t* buf) { _SOKOL_PRIVATE sg_resource_state _sg_dummy_create_image(_sg_image_t* img, const sg_image_desc* desc) { SOKOL_ASSERT(img && desc); - _sg_image_common_init(&img->cmn, desc); + _SOKOL_UNUSED(img); + _SOKOL_UNUSED(desc); return SG_RESOURCESTATE_VALID; } @@ -5125,9 +5629,22 @@ _SOKOL_PRIVATE void _sg_dummy_discard_image(_sg_image_t* img) { _SOKOL_UNUSED(img); } +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { + SOKOL_ASSERT(smp && desc); + _SOKOL_UNUSED(smp); + _SOKOL_UNUSED(desc); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_dummy_discard_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp); + _SOKOL_UNUSED(smp); +} + _SOKOL_PRIVATE sg_resource_state _sg_dummy_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { SOKOL_ASSERT(shd && desc); - _sg_shader_common_init(&shd->cmn, desc); + _SOKOL_UNUSED(shd); + _SOKOL_UNUSED(desc); return SG_RESOURCESTATE_VALID; } @@ -5139,14 +5656,13 @@ _SOKOL_PRIVATE void _sg_dummy_discard_shader(_sg_shader_t* shd) { _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; - _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) { + const sg_vertex_attr_state* a_state = &desc->layout.attrs[attr_index]; + if (a_state->format == SG_VERTEXFORMAT_INVALID) { break; } - SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); - pip->cmn.vertex_layout_valid[a_desc->buffer_index] = true; + SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEX_BUFFERS); + pip->cmn.vertex_buffer_layout_active[a_state->buffer_index] = true; } return SG_RESOURCESTATE_VALID; } @@ -5156,29 +5672,34 @@ _SOKOL_PRIVATE void _sg_dummy_discard_pipeline(_sg_pipeline_t* pip) { _SOKOL_UNUSED(pip); } -_SOKOL_PRIVATE sg_resource_state _sg_dummy_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** color_images, _sg_image_t** resolve_images, _sg_image_t* ds_img, const sg_pass_desc* desc) { SOKOL_ASSERT(pass && desc); - SOKOL_ASSERT(att_images && att_images[0]); + SOKOL_ASSERT(color_images && resolve_images); - _sg_pass_common_init(&pass->cmn, desc); - - const sg_pass_attachment_desc* att_desc; for (int i = 0; i < pass->cmn.num_color_atts; i++) { - att_desc = &desc->color_attachments[i]; - SOKOL_ASSERT(att_desc->image.id != SG_INVALID_ID); + const sg_pass_attachment_desc* color_desc = &desc->color_attachments[i]; + _SOKOL_UNUSED(color_desc); + SOKOL_ASSERT(color_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(color_images[i] && (color_images[i]->slot.id == color_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(color_images[i]->cmn.pixel_format)); + pass->dmy.color_atts[i].image = color_images[i]; + + const sg_pass_attachment_desc* resolve_desc = &desc->resolve_attachments[i]; + if (resolve_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(0 == pass->dmy.resolve_atts[i].image); + SOKOL_ASSERT(resolve_images[i] && (resolve_images[i]->slot.id == resolve_desc->image.id)); + SOKOL_ASSERT(color_images[i] && (color_images[i]->cmn.pixel_format == resolve_images[i]->cmn.pixel_format)); + pass->dmy.resolve_atts[i].image = resolve_images[i]; + } } SOKOL_ASSERT(0 == pass->dmy.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)); - pass->dmy.ds_att.image = att_images[ds_img_index]; + const sg_pass_attachment_desc* ds_desc = &desc->depth_stencil_attachment; + if (ds_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(ds_img && (ds_img->slot.id == ds_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(ds_img->cmn.pixel_format)); + pass->dmy.ds_att.image = ds_img; } return SG_RESOURCESTATE_VALID; } @@ -5190,12 +5711,15 @@ _SOKOL_PRIVATE void _sg_dummy_discard_pass(_sg_pass_t* pass) { _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_resolve_image(const _sg_pass_t* pass, int index) { + SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + return pass->dmy.resolve_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; } @@ -5209,11 +5733,11 @@ _SOKOL_PRIVATE void _sg_dummy_begin_pass(_sg_pass_t* pass, const sg_pass_action* } _SOKOL_PRIVATE void _sg_dummy_end_pass(void) { - /* empty */ + // empty } _SOKOL_PRIVATE void _sg_dummy_commit(void) { - /* empty */ + // empty } _SOKOL_PRIVATE void _sg_dummy_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { @@ -5242,17 +5766,23 @@ _SOKOL_PRIVATE void _sg_dummy_apply_bindings( _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) + _sg_image_t** fs_imgs, int num_fs_imgs, + _sg_sampler_t** vs_smps, int num_vs_smps, + _sg_sampler_t** fs_smps, int num_fs_smps) { SOKOL_ASSERT(pip); SOKOL_ASSERT(vbs && vb_offsets); SOKOL_ASSERT(vs_imgs); SOKOL_ASSERT(fs_imgs); + SOKOL_ASSERT(vs_smps); + SOKOL_ASSERT(fs_smps); _SOKOL_UNUSED(pip); _SOKOL_UNUSED(vbs); _SOKOL_UNUSED(vb_offsets); _SOKOL_UNUSED(num_vbs); _SOKOL_UNUSED(ib); _SOKOL_UNUSED(ib_offset); _SOKOL_UNUSED(vs_imgs); _SOKOL_UNUSED(num_vs_imgs); _SOKOL_UNUSED(fs_imgs); _SOKOL_UNUSED(num_fs_imgs); + _SOKOL_UNUSED(vs_smps); _SOKOL_UNUSED(num_vs_smps); + _SOKOL_UNUSED(fs_smps); _SOKOL_UNUSED(num_fs_smps); } _SOKOL_PRIVATE void _sg_dummy_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { @@ -5283,7 +5813,7 @@ _SOKOL_PRIVATE int _sg_dummy_append_buffer(_sg_buffer_t* buf, const sg_range* da buf->cmn.active_slot = 0; } } - /* NOTE: this is a requirement from WebGPU, but we want identical behaviour across all backend */ + // NOTE: this is a requirement from WebGPU, but we want identical behaviour across all backend return _sg_roundup((int)data->size, 4); } @@ -5295,10 +5825,16 @@ _SOKOL_PRIVATE void _sg_dummy_update_image(_sg_image_t* img, const sg_image_data } } -/*== GL BACKEND ==============================================================*/ +// ██████ ██████ ███████ ███ ██ ██████ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ ██ ██████ █████ ██ ██ ██ ██ ███ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ███████ ██ ████ ██████ ███████ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>opengl backend #elif defined(_SOKOL_ANY_GL) -/*=== OPTIONAL GL LOADER FOR WIN32 ===========================================*/ +// optional GL loader for win32 #if defined(_SOKOL_USE_WIN32_GL_LOADER) // X Macro list of GL function names and signatures @@ -5359,7 +5895,6 @@ _SOKOL_PRIVATE void _sg_dummy_update_image(_sg_image_t* img, const sg_image_data _SG_XMACRO(glTexImage3D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void * pixels)) \ _SG_XMACRO(glCreateShader, GLuint, (GLenum type)) \ _SG_XMACRO(glTexSubImage2D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels)) \ - _SG_XMACRO(glClearDepth, void, (GLdouble depth)) \ _SG_XMACRO(glFramebufferTexture2D, void, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)) \ _SG_XMACRO(glCreateProgram, GLuint, (void)) \ _SG_XMACRO(glViewport, void, (GLint x, GLint y, GLsizei width, GLsizei height)) \ @@ -5375,7 +5910,6 @@ _SOKOL_PRIVATE void _sg_dummy_update_image(_sg_image_t* img, const sg_image_data _SG_XMACRO(glDeleteVertexArrays, void, (GLsizei n, const GLuint * arrays)) \ _SG_XMACRO(glDepthMask, void, (GLboolean flag)) \ _SG_XMACRO(glDrawArraysInstanced, void, (GLenum mode, GLint first, GLsizei count, GLsizei instancecount)) \ - _SG_XMACRO(glClearStencil, void, (GLint s)) \ _SG_XMACRO(glScissor, void, (GLint x, GLint y, GLsizei width, GLsizei height)) \ _SG_XMACRO(glGenRenderbuffers, void, (GLsizei n, GLuint * renderbuffers)) \ _SG_XMACRO(glBufferData, void, (GLenum target, GLsizeiptr size, const void * data, GLenum usage)) \ @@ -5387,10 +5921,9 @@ _SOKOL_PRIVATE void _sg_dummy_update_image(_sg_image_t* img, const sg_image_data _SG_XMACRO(glStencilMask, void, (GLuint mask)) \ _SG_XMACRO(glAttachShader, void, (GLuint program, GLuint shader)) \ _SG_XMACRO(glGetError, GLenum, (void)) \ - _SG_XMACRO(glClearColor, void, (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)) \ _SG_XMACRO(glBlendColor, void, (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)) \ _SG_XMACRO(glTexParameterf, void, (GLenum target, GLenum pname, GLfloat param)) \ - _SG_XMACRO(glTexParameterfv, void, (GLenum target, GLenum pname, GLfloat* params)) \ + _SG_XMACRO(glTexParameterfv, void, (GLenum target, GLenum pname, const GLfloat* params)) \ _SG_XMACRO(glGetShaderInfoLog, void, (GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * infoLog)) \ _SG_XMACRO(glDepthFunc, void, (GLenum func)) \ _SG_XMACRO(glStencilOp , void, (GLenum fail, GLenum zfail, GLenum zpass)) \ @@ -5398,12 +5931,17 @@ _SOKOL_PRIVATE void _sg_dummy_update_image(_sg_image_t* img, const sg_image_data _SG_XMACRO(glEnableVertexAttribArray, void, (GLuint index)) \ _SG_XMACRO(glBlendFunc, void, (GLenum sfactor, GLenum dfactor)) \ _SG_XMACRO(glReadBuffer, void, (GLenum src)) \ - _SG_XMACRO(glReadPixels, void, (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void * data)) \ - _SG_XMACRO(glClear, void, (GLbitfield mask)) \ _SG_XMACRO(glTexImage2D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * pixels)) \ _SG_XMACRO(glGenVertexArrays, void, (GLsizei n, GLuint * arrays)) \ _SG_XMACRO(glFrontFace, void, (GLenum mode)) \ - _SG_XMACRO(glCullFace, void, (GLenum mode)) + _SG_XMACRO(glCullFace, void, (GLenum mode)) \ + _SG_XMACRO(glPixelStorei, void, (GLenum pname, GLint param)) \ + _SG_XMACRO(glBindSampler, void, (GLuint unit, GLuint sampler)) \ + _SG_XMACRO(glGenSamplers, void, (GLsizei n, GLuint* samplers)) \ + _SG_XMACRO(glSamplerParameteri, void, (GLuint sampler, GLenum pname, GLint param)) \ + _SG_XMACRO(glSamplerParameterf, void, (GLuint sampler, GLenum pname, GLfloat param)) \ + _SG_XMACRO(glSamplerParameterfv, void, (GLuint sampler, GLenum pname, const GLfloat* params)) \ + _SG_XMACRO(glDeleteSamplers, void, (GLsizei n, const GLuint* samplers)) // generate GL function pointer typedefs #define _SG_XMACRO(name, ret, args) typedef ret (GL_APIENTRY* PFN_ ## name) args; @@ -5445,7 +5983,7 @@ _SOKOL_PRIVATE void _sg_gl_unload_opengl(void) { } #endif // _SOKOL_USE_WIN32_GL_LOADER -/*-- type translation --------------------------------------------------------*/ +//-- type translation ---------------------------------------------------------- _SOKOL_PRIVATE GLenum _sg_gl_buffer_target(sg_buffer_type t) { switch (t) { case SG_BUFFERTYPE_VERTEXBUFFER: return GL_ARRAY_BUFFER; @@ -5458,10 +5996,8 @@ _SOKOL_PRIVATE GLenum _sg_gl_texture_target(sg_image_type t) { switch (t) { case SG_IMAGETYPE_2D: return GL_TEXTURE_2D; case SG_IMAGETYPE_CUBE: return GL_TEXTURE_CUBE_MAP; - #if !defined(SOKOL_GLES2) case SG_IMAGETYPE_3D: return GL_TEXTURE_3D; case SG_IMAGETYPE_ARRAY: return GL_TEXTURE_2D_ARRAY; - #endif default: SOKOL_UNREACHABLE; return 0; } } @@ -5500,6 +6036,8 @@ _SOKOL_PRIVATE GLint _sg_gl_vertexformat_size(sg_vertex_format fmt) { case SG_VERTEXFORMAT_SHORT4N: return 4; case SG_VERTEXFORMAT_USHORT4N: return 4; case SG_VERTEXFORMAT_UINT10_N2: return 4; + case SG_VERTEXFORMAT_HALF2: return 2; + case SG_VERTEXFORMAT_HALF4: return 4; default: SOKOL_UNREACHABLE; return 0; } } @@ -5527,6 +6065,9 @@ _SOKOL_PRIVATE GLenum _sg_gl_vertexformat_type(sg_vertex_format fmt) { return GL_UNSIGNED_SHORT; case SG_VERTEXFORMAT_UINT10_N2: return GL_UNSIGNED_INT_2_10_10_10_REV; + case SG_VERTEXFORMAT_HALF2: + case SG_VERTEXFORMAT_HALF4: + return GL_HALF_FLOAT; default: SOKOL_UNREACHABLE; return 0; } @@ -5625,15 +6166,31 @@ _SOKOL_PRIVATE GLenum _sg_gl_blend_op(sg_blend_op op) { } } -_SOKOL_PRIVATE GLenum _sg_gl_filter(sg_filter f) { - switch (f) { - case SG_FILTER_NEAREST: return GL_NEAREST; - case SG_FILTER_LINEAR: return GL_LINEAR; - case SG_FILTER_NEAREST_MIPMAP_NEAREST: return GL_NEAREST_MIPMAP_NEAREST; - case SG_FILTER_NEAREST_MIPMAP_LINEAR: return GL_NEAREST_MIPMAP_LINEAR; - case SG_FILTER_LINEAR_MIPMAP_NEAREST: return GL_LINEAR_MIPMAP_NEAREST; - case SG_FILTER_LINEAR_MIPMAP_LINEAR: return GL_LINEAR_MIPMAP_LINEAR; - default: SOKOL_UNREACHABLE; return 0; +_SOKOL_PRIVATE GLenum _sg_gl_min_filter(sg_filter min_f, sg_filter mipmap_f) { + if (min_f == SG_FILTER_NEAREST) { + switch (mipmap_f) { + case SG_FILTER_NONE: return GL_NEAREST; + case SG_FILTER_NEAREST: return GL_NEAREST_MIPMAP_NEAREST; + case SG_FILTER_LINEAR: return GL_NEAREST_MIPMAP_LINEAR; + default: SOKOL_UNREACHABLE; return (GLenum)0; + } + } else if (min_f == SG_FILTER_LINEAR) { + switch (mipmap_f) { + case SG_FILTER_NONE: return GL_LINEAR; + case SG_FILTER_NEAREST: return GL_LINEAR_MIPMAP_NEAREST; + case SG_FILTER_LINEAR: return GL_LINEAR_MIPMAP_LINEAR; + default: SOKOL_UNREACHABLE; return (GLenum)0; + } + } else { + SOKOL_UNREACHABLE; return (GLenum)0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_mag_filter(sg_filter mag_f) { + if (mag_f == SG_FILTER_NEAREST) { + return GL_NEAREST; + } else { + return GL_LINEAR; } } @@ -5658,6 +6215,7 @@ _SOKOL_PRIVATE GLenum _sg_gl_teximage_type(sg_pixel_format fmt) { case SG_PIXELFORMAT_RG8: case SG_PIXELFORMAT_RG8UI: case SG_PIXELFORMAT_RGBA8: + case SG_PIXELFORMAT_SRGB8A8: case SG_PIXELFORMAT_RGBA8UI: case SG_PIXELFORMAT_BGRA8: return GL_UNSIGNED_BYTE; @@ -5698,16 +6256,14 @@ _SOKOL_PRIVATE GLenum _sg_gl_teximage_type(sg_pixel_format fmt) { case SG_PIXELFORMAT_RG32F: case SG_PIXELFORMAT_RGBA32F: return GL_FLOAT; - #if !defined(SOKOL_GLES2) case SG_PIXELFORMAT_RGB10A2: return GL_UNSIGNED_INT_2_10_10_10_REV; case SG_PIXELFORMAT_RG11B10F: return GL_UNSIGNED_INT_10F_11F_11F_REV; case SG_PIXELFORMAT_RGB9E5: return GL_UNSIGNED_INT_5_9_9_9_REV; - #endif case SG_PIXELFORMAT_DEPTH: - return GL_UNSIGNED_SHORT; + return GL_FLOAT; case SG_PIXELFORMAT_DEPTH_STENCIL: return GL_UNSIGNED_INT_24_8; default: @@ -5723,40 +6279,30 @@ _SOKOL_PRIVATE GLenum _sg_gl_teximage_format(sg_pixel_format fmt) { case SG_PIXELFORMAT_R16SN: case SG_PIXELFORMAT_R16F: case SG_PIXELFORMAT_R32F: - #if defined(SOKOL_GLES2) - return GL_LUMINANCE; - #else - if (_sg.gl.gles2) { - return GL_LUMINANCE; - } - else { - return GL_RED; - } - #endif - #if !defined(SOKOL_GLES2) - case SG_PIXELFORMAT_R8UI: - case SG_PIXELFORMAT_R8SI: - case SG_PIXELFORMAT_R16UI: - case SG_PIXELFORMAT_R16SI: - case SG_PIXELFORMAT_R32UI: - case SG_PIXELFORMAT_R32SI: - return GL_RED_INTEGER; - case SG_PIXELFORMAT_RG8: - case SG_PIXELFORMAT_RG8SN: - case SG_PIXELFORMAT_RG16: - case SG_PIXELFORMAT_RG16SN: - case SG_PIXELFORMAT_RG16F: - case SG_PIXELFORMAT_RG32F: - return GL_RG; - case SG_PIXELFORMAT_RG8UI: - case SG_PIXELFORMAT_RG8SI: - case SG_PIXELFORMAT_RG16UI: - case SG_PIXELFORMAT_RG16SI: - case SG_PIXELFORMAT_RG32UI: - case SG_PIXELFORMAT_RG32SI: - return GL_RG_INTEGER; - #endif + return GL_RED; + case SG_PIXELFORMAT_R8UI: + case SG_PIXELFORMAT_R8SI: + case SG_PIXELFORMAT_R16UI: + case SG_PIXELFORMAT_R16SI: + case SG_PIXELFORMAT_R32UI: + case SG_PIXELFORMAT_R32SI: + return GL_RED_INTEGER; + case SG_PIXELFORMAT_RG8: + case SG_PIXELFORMAT_RG8SN: + case SG_PIXELFORMAT_RG16: + case SG_PIXELFORMAT_RG16SN: + case SG_PIXELFORMAT_RG16F: + case SG_PIXELFORMAT_RG32F: + return GL_RG; + case SG_PIXELFORMAT_RG8UI: + case SG_PIXELFORMAT_RG8SI: + case SG_PIXELFORMAT_RG16UI: + case SG_PIXELFORMAT_RG16SI: + case SG_PIXELFORMAT_RG32UI: + case SG_PIXELFORMAT_RG32SI: + return GL_RG_INTEGER; case SG_PIXELFORMAT_RGBA8: + case SG_PIXELFORMAT_SRGB8A8: case SG_PIXELFORMAT_RGBA8SN: case SG_PIXELFORMAT_RGBA16: case SG_PIXELFORMAT_RGBA16SN: @@ -5764,15 +6310,13 @@ _SOKOL_PRIVATE GLenum _sg_gl_teximage_format(sg_pixel_format fmt) { case SG_PIXELFORMAT_RGBA32F: case SG_PIXELFORMAT_RGB10A2: return GL_RGBA; - #if !defined(SOKOL_GLES2) - case SG_PIXELFORMAT_RGBA8UI: - case SG_PIXELFORMAT_RGBA8SI: - case SG_PIXELFORMAT_RGBA16UI: - case SG_PIXELFORMAT_RGBA16SI: - case SG_PIXELFORMAT_RGBA32UI: - case SG_PIXELFORMAT_RGBA32SI: - return GL_RGBA_INTEGER; - #endif + case SG_PIXELFORMAT_RGBA8UI: + case SG_PIXELFORMAT_RGBA8SI: + case SG_PIXELFORMAT_RGBA16UI: + case SG_PIXELFORMAT_RGBA16SI: + case SG_PIXELFORMAT_RGBA32UI: + case SG_PIXELFORMAT_RGBA32SI: + return GL_RGBA_INTEGER; case SG_PIXELFORMAT_RG11B10F: case SG_PIXELFORMAT_RGB9E5: return GL_RGB; @@ -5824,84 +6368,76 @@ _SOKOL_PRIVATE GLenum _sg_gl_teximage_format(sg_pixel_format fmt) { } _SOKOL_PRIVATE GLenum _sg_gl_teximage_internal_format(sg_pixel_format fmt) { - #if defined(SOKOL_GLES2) - return _sg_gl_teximage_format(fmt); - #else - if (_sg.gl.gles2) { - return _sg_gl_teximage_format(fmt); - } - else { - switch (fmt) { - case SG_PIXELFORMAT_R8: return GL_R8; - case SG_PIXELFORMAT_R8SN: return GL_R8_SNORM; - case SG_PIXELFORMAT_R8UI: return GL_R8UI; - case SG_PIXELFORMAT_R8SI: return GL_R8I; - #if !defined(SOKOL_GLES3) - case SG_PIXELFORMAT_R16: return GL_R16; - case SG_PIXELFORMAT_R16SN: return GL_R16_SNORM; - #endif - case SG_PIXELFORMAT_R16UI: return GL_R16UI; - case SG_PIXELFORMAT_R16SI: return GL_R16I; - case SG_PIXELFORMAT_R16F: return GL_R16F; - case SG_PIXELFORMAT_RG8: return GL_RG8; - case SG_PIXELFORMAT_RG8SN: return GL_RG8_SNORM; - case SG_PIXELFORMAT_RG8UI: return GL_RG8UI; - case SG_PIXELFORMAT_RG8SI: return GL_RG8I; - case SG_PIXELFORMAT_R32UI: return GL_R32UI; - case SG_PIXELFORMAT_R32SI: return GL_R32I; - case SG_PIXELFORMAT_R32F: return GL_R32F; - #if !defined(SOKOL_GLES3) - case SG_PIXELFORMAT_RG16: return GL_RG16; - case SG_PIXELFORMAT_RG16SN: return GL_RG16_SNORM; - #endif - case SG_PIXELFORMAT_RG16UI: return GL_RG16UI; - case SG_PIXELFORMAT_RG16SI: return GL_RG16I; - case SG_PIXELFORMAT_RG16F: return GL_RG16F; - case SG_PIXELFORMAT_RGBA8: return GL_RGBA8; - case SG_PIXELFORMAT_RGBA8SN: return GL_RGBA8_SNORM; - case SG_PIXELFORMAT_RGBA8UI: return GL_RGBA8UI; - case SG_PIXELFORMAT_RGBA8SI: return GL_RGBA8I; - case SG_PIXELFORMAT_RGB10A2: return GL_RGB10_A2; - case SG_PIXELFORMAT_RG11B10F: return GL_R11F_G11F_B10F; - case SG_PIXELFORMAT_RGB9E5: return GL_RGB9_E5; - case SG_PIXELFORMAT_RG32UI: return GL_RG32UI; - case SG_PIXELFORMAT_RG32SI: return GL_RG32I; - case SG_PIXELFORMAT_RG32F: return GL_RG32F; - #if !defined(SOKOL_GLES3) - case SG_PIXELFORMAT_RGBA16: return GL_RGBA16; - case SG_PIXELFORMAT_RGBA16SN: return GL_RGBA16_SNORM; - #endif - case SG_PIXELFORMAT_RGBA16UI: return GL_RGBA16UI; - case SG_PIXELFORMAT_RGBA16SI: return GL_RGBA16I; - case SG_PIXELFORMAT_RGBA16F: return GL_RGBA16F; - case SG_PIXELFORMAT_RGBA32UI: return GL_RGBA32UI; - case SG_PIXELFORMAT_RGBA32SI: return GL_RGBA32I; - case SG_PIXELFORMAT_RGBA32F: return GL_RGBA32F; - case SG_PIXELFORMAT_DEPTH: return GL_DEPTH_COMPONENT16; - case SG_PIXELFORMAT_DEPTH_STENCIL: return GL_DEPTH24_STENCIL8; - case SG_PIXELFORMAT_BC1_RGBA: return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; - case SG_PIXELFORMAT_BC2_RGBA: return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; - case SG_PIXELFORMAT_BC3_RGBA: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; - case SG_PIXELFORMAT_BC4_R: return GL_COMPRESSED_RED_RGTC1; - case SG_PIXELFORMAT_BC4_RSN: return GL_COMPRESSED_SIGNED_RED_RGTC1; - case SG_PIXELFORMAT_BC5_RG: return GL_COMPRESSED_RED_GREEN_RGTC2; - case SG_PIXELFORMAT_BC5_RGSN: return GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2; - case SG_PIXELFORMAT_BC6H_RGBF: return GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB; - case SG_PIXELFORMAT_BC6H_RGBUF: return GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB; - case SG_PIXELFORMAT_BC7_RGBA: return GL_COMPRESSED_RGBA_BPTC_UNORM_ARB; - case SG_PIXELFORMAT_PVRTC_RGB_2BPP: return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; - case SG_PIXELFORMAT_PVRTC_RGB_4BPP: return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; - case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; - case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; - case SG_PIXELFORMAT_ETC2_RGB8: return GL_COMPRESSED_RGB8_ETC2; - case SG_PIXELFORMAT_ETC2_RGB8A1: return GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; - case SG_PIXELFORMAT_ETC2_RGBA8: return GL_COMPRESSED_RGBA8_ETC2_EAC; - case SG_PIXELFORMAT_ETC2_RG11: return GL_COMPRESSED_RG11_EAC; - case SG_PIXELFORMAT_ETC2_RG11SN: return GL_COMPRESSED_SIGNED_RG11_EAC; - default: SOKOL_UNREACHABLE; return 0; - } + switch (fmt) { + case SG_PIXELFORMAT_R8: return GL_R8; + case SG_PIXELFORMAT_R8SN: return GL_R8_SNORM; + case SG_PIXELFORMAT_R8UI: return GL_R8UI; + case SG_PIXELFORMAT_R8SI: return GL_R8I; + #if !defined(SOKOL_GLES3) + case SG_PIXELFORMAT_R16: return GL_R16; + case SG_PIXELFORMAT_R16SN: return GL_R16_SNORM; + #endif + case SG_PIXELFORMAT_R16UI: return GL_R16UI; + case SG_PIXELFORMAT_R16SI: return GL_R16I; + case SG_PIXELFORMAT_R16F: return GL_R16F; + case SG_PIXELFORMAT_RG8: return GL_RG8; + case SG_PIXELFORMAT_RG8SN: return GL_RG8_SNORM; + case SG_PIXELFORMAT_RG8UI: return GL_RG8UI; + case SG_PIXELFORMAT_RG8SI: return GL_RG8I; + case SG_PIXELFORMAT_R32UI: return GL_R32UI; + case SG_PIXELFORMAT_R32SI: return GL_R32I; + case SG_PIXELFORMAT_R32F: return GL_R32F; + #if !defined(SOKOL_GLES3) + case SG_PIXELFORMAT_RG16: return GL_RG16; + case SG_PIXELFORMAT_RG16SN: return GL_RG16_SNORM; + #endif + case SG_PIXELFORMAT_RG16UI: return GL_RG16UI; + case SG_PIXELFORMAT_RG16SI: return GL_RG16I; + case SG_PIXELFORMAT_RG16F: return GL_RG16F; + case SG_PIXELFORMAT_RGBA8: return GL_RGBA8; + case SG_PIXELFORMAT_SRGB8A8: return GL_SRGB8_ALPHA8; + case SG_PIXELFORMAT_RGBA8SN: return GL_RGBA8_SNORM; + case SG_PIXELFORMAT_RGBA8UI: return GL_RGBA8UI; + case SG_PIXELFORMAT_RGBA8SI: return GL_RGBA8I; + case SG_PIXELFORMAT_RGB10A2: return GL_RGB10_A2; + case SG_PIXELFORMAT_RG11B10F: return GL_R11F_G11F_B10F; + case SG_PIXELFORMAT_RGB9E5: return GL_RGB9_E5; + case SG_PIXELFORMAT_RG32UI: return GL_RG32UI; + case SG_PIXELFORMAT_RG32SI: return GL_RG32I; + case SG_PIXELFORMAT_RG32F: return GL_RG32F; + #if !defined(SOKOL_GLES3) + case SG_PIXELFORMAT_RGBA16: return GL_RGBA16; + case SG_PIXELFORMAT_RGBA16SN: return GL_RGBA16_SNORM; + #endif + case SG_PIXELFORMAT_RGBA16UI: return GL_RGBA16UI; + case SG_PIXELFORMAT_RGBA16SI: return GL_RGBA16I; + case SG_PIXELFORMAT_RGBA16F: return GL_RGBA16F; + case SG_PIXELFORMAT_RGBA32UI: return GL_RGBA32UI; + case SG_PIXELFORMAT_RGBA32SI: return GL_RGBA32I; + case SG_PIXELFORMAT_RGBA32F: return GL_RGBA32F; + case SG_PIXELFORMAT_DEPTH: return GL_DEPTH_COMPONENT32F; + case SG_PIXELFORMAT_DEPTH_STENCIL: return GL_DEPTH24_STENCIL8; + case SG_PIXELFORMAT_BC1_RGBA: return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + case SG_PIXELFORMAT_BC2_RGBA: return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + case SG_PIXELFORMAT_BC3_RGBA: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + case SG_PIXELFORMAT_BC4_R: return GL_COMPRESSED_RED_RGTC1; + case SG_PIXELFORMAT_BC4_RSN: return GL_COMPRESSED_SIGNED_RED_RGTC1; + case SG_PIXELFORMAT_BC5_RG: return GL_COMPRESSED_RED_GREEN_RGTC2; + case SG_PIXELFORMAT_BC5_RGSN: return GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2; + case SG_PIXELFORMAT_BC6H_RGBF: return GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB; + case SG_PIXELFORMAT_BC6H_RGBUF: return GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB; + case SG_PIXELFORMAT_BC7_RGBA: return GL_COMPRESSED_RGBA_BPTC_UNORM_ARB; + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; + case SG_PIXELFORMAT_ETC2_RGB8: return GL_COMPRESSED_RGB8_ETC2; + case SG_PIXELFORMAT_ETC2_RGB8A1: return GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; + case SG_PIXELFORMAT_ETC2_RGBA8: return GL_COMPRESSED_RGBA8_ETC2_EAC; + case SG_PIXELFORMAT_ETC2_RG11: return GL_COMPRESSED_RG11_EAC; + case SG_PIXELFORMAT_ETC2_RG11SN: return GL_COMPRESSED_SIGNED_RG11_EAC; + default: SOKOL_UNREACHABLE; return 0; } - #endif } _SOKOL_PRIVATE GLenum _sg_gl_cubeface_target(int face_index) { @@ -5916,203 +6452,96 @@ _SOKOL_PRIVATE GLenum _sg_gl_cubeface_target(int face_index) { } } -_SOKOL_PRIVATE GLenum _sg_gl_depth_attachment_format(sg_pixel_format fmt) { - switch (fmt) { - case SG_PIXELFORMAT_DEPTH: return GL_DEPTH_COMPONENT16; - case SG_PIXELFORMAT_DEPTH_STENCIL: return GL_DEPTH24_STENCIL8; - default: SOKOL_UNREACHABLE; return 0; - } -} - -/* see: https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glTexImage2D.xhtml */ +// see: https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glTexImage2D.xhtml _SOKOL_PRIVATE void _sg_gl_init_pixelformats(bool has_bgra) { - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R8]); - } - else { - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R8]); - } - #else - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R8]); + _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]); + #if !defined(SOKOL_GLES3) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16SN]); #endif - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R8SN]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8SI]); - #if !defined(SOKOL_GLES3) - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16SN]); - #endif - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16SI]); - _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]); - #if !defined(SOKOL_GLES3) - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16SN]); - #endif - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16SI]); - } + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16SI]); + _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]); + #if !defined(SOKOL_GLES3) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16SN]); #endif + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16SI]); _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]); - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]); - } - #endif + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_SRGB8A8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]); if (has_bgra) { _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_BGRA8]); } - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB10A2]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG11B10F]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGB9E5]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32SI]); - #if !defined(SOKOL_GLES3) - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16SN]); - #endif - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); - _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); - } + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB10A2]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG11B10F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGB9E5]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32SI]); + #if !defined(SOKOL_GLES3) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16SN]); #endif - // FIXME: WEBGL_depth_texture extension? + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH]); _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL]); } -/* FIXME: OES_half_float_blend */ -_SOKOL_PRIVATE void _sg_gl_init_pixelformats_half_float(bool has_colorbuffer_half_float, bool has_texture_half_float_linear) { - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - if (has_texture_half_float_linear) { - if (has_colorbuffer_half_float) { - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16F]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16F]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); - } - else { - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R16F]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG16F]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); - } - } - else { - if (has_colorbuffer_half_float) { - _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_R16F]); - _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RG16F]); - _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); - } - else { - _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_R16F]); - _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RG16F]); - _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); - } - } - } - else { - #endif - /* GLES2 can only render to RGBA, and there's no RG format */ - if (has_texture_half_float_linear) { - if (has_colorbuffer_half_float) { - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); - } - else { - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); - } - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R16F]); - } - else { - if (has_colorbuffer_half_float) { - _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); - } - else { - _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); - } - _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_R16F]); - } - #if !defined(SOKOL_GLES2) +// FIXME: OES_half_float_blend +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_half_float(bool has_colorbuffer_half_float) { + if (has_colorbuffer_half_float) { + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + } else { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R16F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG16F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); } - #endif } _SOKOL_PRIVATE void _sg_gl_init_pixelformats_float(bool has_colorbuffer_float, bool has_texture_float_linear, bool has_float_blend) { - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - if (has_texture_float_linear) { - if (has_colorbuffer_float) { - if (has_float_blend) { - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R32F]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG32F]); - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); - } - else { - _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_R32F]); - _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_RG32F]); - _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); - } - } - else { - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R32F]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG32F]); - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); - } - } - else { - if (has_colorbuffer_float) { - _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_R32F]); - _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RG32F]); - _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); - } - else { - _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_R32F]); - _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RG32F]); - _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); - } - } - } - else { - #endif - /* GLES2 can only render to RGBA, and there's no RG format */ - if (has_texture_float_linear) { - if (has_colorbuffer_float) { - if (has_float_blend) { - _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); - } - else { - _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); - } - } - else { - _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + if (has_texture_float_linear) { + if (has_colorbuffer_float) { + if (has_float_blend) { + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } else { + _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); } + } else { _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); } - else { - if (has_colorbuffer_float) { - _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); - } - else { - _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); - } + } else { + if (has_colorbuffer_float) { + _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } else { _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); } - #if !defined(SOKOL_GLES2) } - #endif } _SOKOL_PRIVATE void _sg_gl_init_pixelformats_s3tc(void) { @@ -6168,27 +6597,22 @@ _SOKOL_PRIVATE void _sg_gl_init_limits(void) { glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &gl_int); _SG_GL_CHECK_ERROR(); _sg.limits.gl_max_vertex_uniform_vectors = gl_int; - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &gl_int); - _SG_GL_CHECK_ERROR(); - _sg.limits.max_image_size_3d = gl_int; - glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &gl_int); - _SG_GL_CHECK_ERROR(); - _sg.limits.max_image_array_layers = gl_int; - } - #endif + glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.max_image_size_3d = gl_int; + glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.max_image_array_layers = gl_int; if (_sg.gl.ext_anisotropic) { glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gl_int); _SG_GL_CHECK_ERROR(); _sg.gl.max_anisotropy = gl_int; - } - else { + } else { _sg.gl.max_anisotropy = 1; } glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &gl_int); _SG_GL_CHECK_ERROR(); - _sg.gl.max_combined_texture_image_units = gl_int; + _sg.limits.gl_max_combined_texture_image_units = gl_int; } #if defined(SOKOL_GLCORE33) @@ -6196,19 +6620,14 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_glcore33(void) { _sg.backend = SG_BACKEND_GLCORE33; _sg.features.origin_top_left = false; - _sg.features.instancing = 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 = true; _sg.features.mrt_independent_blend_state = false; _sg.features.mrt_independent_write_mask = true; - /* scan extensions */ - bool has_s3tc = false; /* BC1..BC3 */ - bool has_rgtc = false; /* BC4 and BC5 */ - bool has_bptc = false; /* BC6H and BC7 */ + // scan extensions + bool has_s3tc = false; // BC1..BC3 + bool has_rgtc = false; // BC4 and BC5 + bool has_bptc = false; // BC6H and BC7 bool has_pvrtc = false; bool has_etc2 = false; GLint num_ext = 0; @@ -6218,38 +6637,32 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_glcore33(void) { if (ext) { if (strstr(ext, "_texture_compression_s3tc")) { has_s3tc = true; - } - else if (strstr(ext, "_texture_compression_rgtc")) { + } else if (strstr(ext, "_texture_compression_rgtc")) { has_rgtc = true; - } - else if (strstr(ext, "_texture_compression_bptc")) { + } else if (strstr(ext, "_texture_compression_bptc")) { has_bptc = true; - } - else if (strstr(ext, "_texture_compression_pvrtc")) { + } else if (strstr(ext, "_texture_compression_pvrtc")) { has_pvrtc = true; - } - else if (strstr(ext, "_ES3_compatibility")) { + } else if (strstr(ext, "_ES3_compatibility")) { has_etc2 = true; - } - else if (strstr(ext, "_texture_filter_anisotropic")) { + } else if (strstr(ext, "_texture_filter_anisotropic")) { _sg.gl.ext_anisotropic = true; } } } - /* limits */ + // limits _sg_gl_init_limits(); - /* pixel formats */ - const bool has_bgra = false; /* not a bug */ + // pixel formats + const bool has_bgra = false; // not a bug const bool has_colorbuffer_float = true; const bool has_colorbuffer_half_float = true; - const bool has_texture_float_linear = true; /* FIXME??? */ - const bool has_texture_half_float_linear = true; + const bool has_texture_float_linear = true; // FIXME??? const bool has_float_blend = true; _sg_gl_init_pixelformats(has_bgra); _sg_gl_init_pixelformats_float(has_colorbuffer_float, has_texture_float_linear, has_float_blend); - _sg_gl_init_pixelformats_half_float(has_colorbuffer_half_float, has_texture_half_float_linear); + _sg_gl_init_pixelformats_half_float(has_colorbuffer_half_float); if (has_s3tc) { _sg_gl_init_pixelformats_s3tc(); } @@ -6273,18 +6686,13 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_gles3(void) { _sg.backend = SG_BACKEND_GLES3; _sg.features.origin_top_left = false; - _sg.features.instancing = 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; _sg.features.mrt_independent_blend_state = false; _sg.features.mrt_independent_write_mask = false; - bool has_s3tc = false; /* BC1..BC3 */ - bool has_rgtc = false; /* BC4 and BC5 */ - bool has_bptc = false; /* BC6H and BC7 */ + bool has_s3tc = false; // BC1..BC3 + bool has_rgtc = false; // BC4 and BC5 + bool has_bptc = false; // BC6H and BC7 bool has_pvrtc = false; #if defined(__EMSCRIPTEN__) bool has_etc2 = false; @@ -6302,38 +6710,27 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_gles3(void) { if (ext) { if (strstr(ext, "_texture_compression_s3tc")) { has_s3tc = true; - } - else if (strstr(ext, "_compressed_texture_s3tc")) { + } else if (strstr(ext, "_compressed_texture_s3tc")) { has_s3tc = true; - } - else if (strstr(ext, "_texture_compression_rgtc")) { + } else if (strstr(ext, "_texture_compression_rgtc")) { has_rgtc = true; - } - else if (strstr(ext, "_texture_compression_bptc")) { + } else if (strstr(ext, "_texture_compression_bptc")) { has_bptc = true; - } - else if (strstr(ext, "_texture_compression_pvrtc")) { + } else if (strstr(ext, "_texture_compression_pvrtc")) { has_pvrtc = true; - } - else if (strstr(ext, "_compressed_texture_pvrtc")) { + } else if (strstr(ext, "_compressed_texture_pvrtc")) { has_pvrtc = true; - } - else if (strstr(ext, "_compressed_texture_etc")) { + } else if (strstr(ext, "_compressed_texture_etc")) { has_etc2 = true; - } - else if (strstr(ext, "_color_buffer_float")) { + } else if (strstr(ext, "_color_buffer_float")) { has_colorbuffer_float = true; - } - else if (strstr(ext, "_color_buffer_half_float")) { + } else if (strstr(ext, "_color_buffer_half_float")) { has_colorbuffer_half_float = true; - } - else if (strstr(ext, "_texture_float_linear")) { + } else if (strstr(ext, "_texture_float_linear")) { has_texture_float_linear = true; - } - else if (strstr(ext, "_float_blend")) { + } else if (strstr(ext, "_float_blend")) { has_float_blend = true; - } - else if (strstr(ext, "_texture_filter_anisotropic")) { + } else if (strstr(ext, "_texture_filter_anisotropic")) { _sg.gl.ext_anisotropic = true; } } @@ -6343,97 +6740,19 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_gles3(void) { see: https://developer.mozilla.org/en-US/docs/Web/API/EXT_color_buffer_float */ #if defined(__EMSCRIPTEN__) - has_colorbuffer_half_float = has_colorbuffer_float; - #endif - - /* limits */ - _sg_gl_init_limits(); - - /* pixel formats */ - const bool has_texture_half_float_linear = true; - const bool has_bgra = false; /* not a bug */ - _sg_gl_init_pixelformats(has_bgra); - _sg_gl_init_pixelformats_float(has_colorbuffer_float, has_texture_float_linear, has_float_blend); - _sg_gl_init_pixelformats_half_float(has_colorbuffer_half_float, has_texture_half_float_linear); - if (has_s3tc) { - _sg_gl_init_pixelformats_s3tc(); - } - if (has_rgtc) { - _sg_gl_init_pixelformats_rgtc(); - } - if (has_bptc) { - _sg_gl_init_pixelformats_bptc(); + if (!has_colorbuffer_half_float && has_colorbuffer_float) { + has_colorbuffer_half_float = has_colorbuffer_float; } - if (has_pvrtc) { - _sg_gl_init_pixelformats_pvrtc(); - } - if (has_etc2) { - _sg_gl_init_pixelformats_etc2(); - } -} -#endif - -#if defined(SOKOL_GLES3) || defined(SOKOL_GLES2) -_SOKOL_PRIVATE void _sg_gl_init_caps_gles2(void) { - _sg.backend = SG_BACKEND_GLES2; - - bool has_s3tc = false; /* BC1..BC3 */ - bool has_rgtc = false; /* BC4 and BC5 */ - bool has_bptc = false; /* BC6H and BC7 */ - bool has_pvrtc = false; - bool has_etc2 = false; - bool has_texture_float = false; - bool has_texture_float_linear = false; - bool has_colorbuffer_float = false; - bool has_float_blend = false; - bool has_instancing = false; - const char* ext = (const char*) glGetString(GL_EXTENSIONS); - if (ext) { - 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") || 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"); - has_colorbuffer_float = strstr(ext, "_color_buffer_float"); - has_float_blend = strstr(ext, "_float_blend"); - /* don't bother with half_float support on WebGL1 - has_texture_half_float = strstr(ext, "_texture_half_float"); - has_texture_half_float_linear = strstr(ext, "_texture_half_float_linear"); - has_colorbuffer_half_float = strstr(ext, "_color_buffer_half_float"); - */ - has_instancing = strstr(ext, "_instanced_arrays"); - _sg.gl.ext_anisotropic = strstr(ext, "ext_anisotropic"); - } - - _sg.features.origin_top_left = false; - #if defined(_SOKOL_GL_INSTANCING_ENABLED) - _sg.features.instancing = has_instancing; #endif - _sg.features.multiple_render_targets = false; - _sg.features.msaa_render_targets = false; - _sg.features.imagetype_3d = false; - _sg.features.imagetype_array = false; - _sg.features.image_clamp_to_border = false; - _sg.features.mrt_independent_blend_state = false; - _sg.features.mrt_independent_write_mask = false; - /* limits */ + // limits _sg_gl_init_limits(); - /* pixel formats */ - const bool has_bgra = false; /* not a bug */ - const bool has_texture_half_float = false; - const bool has_texture_half_float_linear = false; - const bool has_colorbuffer_half_float = false; + // pixel formats + const bool has_bgra = false; // not a bug _sg_gl_init_pixelformats(has_bgra); - if (has_texture_float) { - _sg_gl_init_pixelformats_float(has_colorbuffer_float, has_texture_float_linear, has_float_blend); - } - if (has_texture_half_float) { - _sg_gl_init_pixelformats_half_float(has_colorbuffer_half_float, has_texture_half_float_linear); - } + _sg_gl_init_pixelformats_float(has_colorbuffer_float, has_texture_float_linear, has_float_blend); + _sg_gl_init_pixelformats_half_float(has_colorbuffer_half_float); if (has_s3tc) { _sg_gl_init_pixelformats_s3tc(); } @@ -6449,14 +6768,10 @@ _SOKOL_PRIVATE void _sg_gl_init_caps_gles2(void) { if (has_etc2) { _sg_gl_init_pixelformats_etc2(); } - /* GLES2 doesn't allow multi-sampled render targets at all */ - for (int i = 0; i < _SG_PIXELFORMAT_NUM; i++) { - _sg.formats[i].msaa = false; - } } #endif -/*-- state cache implementation ----------------------------------------------*/ +//-- state cache implementation ------------------------------------------------ _SOKOL_PRIVATE void _sg_gl_cache_clear_buffer_bindings(bool force) { if (force || (_sg.gl.cache.vertex_buffer != 0)) { glBindBuffer(GL_ARRAY_BUFFER, 0); @@ -6475,8 +6790,7 @@ _SOKOL_PRIVATE void _sg_gl_cache_bind_buffer(GLenum target, GLuint buffer) { _sg.gl.cache.vertex_buffer = buffer; glBindBuffer(target, buffer); } - } - else { + } else { if (_sg.gl.cache.index_buffer != buffer) { _sg.gl.cache.index_buffer = buffer; glBindBuffer(target, buffer); @@ -6487,8 +6801,7 @@ _SOKOL_PRIVATE void _sg_gl_cache_bind_buffer(GLenum target, GLuint buffer) { _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; - } - else { + } else { _sg.gl.cache.stored_index_buffer = _sg.gl.cache.index_buffer; } } @@ -6496,21 +6809,20 @@ _SOKOL_PRIVATE void _sg_gl_cache_store_buffer_binding(GLenum target) { _SOKOL_PRIVATE void _sg_gl_cache_restore_buffer_binding(GLenum target) { if (target == GL_ARRAY_BUFFER) { if (_sg.gl.cache.stored_vertex_buffer != 0) { - /* we only care restoring valid ids */ + // we only care about restoring valid ids _sg_gl_cache_bind_buffer(target, _sg.gl.cache.stored_vertex_buffer); _sg.gl.cache.stored_vertex_buffer = 0; } - } - else { + } else { if (_sg.gl.cache.stored_index_buffer != 0) { - /* we only care restoring valid ids */ + // we only care about restoring valid ids _sg_gl_cache_bind_buffer(target, _sg.gl.cache.stored_index_buffer); _sg.gl.cache.stored_index_buffer = 0; } } } -/* called when _sg_gl_deinit_buffer() */ +// called when _sg_gl_discard_buffer() _SOKOL_PRIVATE void _sg_gl_cache_invalidate_buffer(GLuint buf) { if (buf == _sg.gl.cache.vertex_buffer) { _sg.gl.cache.vertex_buffer = 0; @@ -6534,92 +6846,109 @@ _SOKOL_PRIVATE void _sg_gl_cache_invalidate_buffer(GLuint buf) { } _SOKOL_PRIVATE void _sg_gl_cache_active_texture(GLenum texture) { + _SG_GL_CHECK_ERROR(); if (_sg.gl.cache.cur_active_texture != texture) { _sg.gl.cache.cur_active_texture = texture; glActiveTexture(texture); } + _SG_GL_CHECK_ERROR(); } -_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)) { - GLenum gl_texture_slot = (GLenum) (GL_TEXTURE0 + i); - glActiveTexture(gl_texture_slot); +_SOKOL_PRIVATE void _sg_gl_cache_clear_texture_sampler_bindings(bool force) { + _SG_GL_CHECK_ERROR(); + for (int i = 0; (i < _SG_GL_TEXTURE_SAMPLER_CACHE_SIZE) && (i < _sg.limits.gl_max_combined_texture_image_units); i++) { + if (force || (_sg.gl.cache.texture_samplers[i].texture != 0)) { + GLenum gl_texture_unit = (GLenum) (GL_TEXTURE0 + i); + glActiveTexture(gl_texture_unit); glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_CUBE_MAP, 0); - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - glBindTexture(GL_TEXTURE_3D, 0); - glBindTexture(GL_TEXTURE_2D_ARRAY, 0); - } - #endif - _sg.gl.cache.textures[i].target = 0; - _sg.gl.cache.textures[i].texture = 0; - _sg.gl.cache.cur_active_texture = gl_texture_slot; + glBindTexture(GL_TEXTURE_3D, 0); + glBindTexture(GL_TEXTURE_2D_ARRAY, 0); + glBindSampler((GLuint)i, 0); + _sg.gl.cache.texture_samplers[i].target = 0; + _sg.gl.cache.texture_samplers[i].texture = 0; + _sg.gl.cache.texture_samplers[i].sampler = 0; + _sg.gl.cache.cur_active_texture = gl_texture_unit; } } + _SG_GL_CHECK_ERROR(); } -_SOKOL_PRIVATE void _sg_gl_cache_bind_texture(int slot_index, GLenum target, GLuint texture) { +_SOKOL_PRIVATE void _sg_gl_cache_bind_texture_sampler(int slot_index, GLenum target, GLuint texture, GLuint sampler) { /* 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 */ - SOKOL_ASSERT(slot_index < SG_MAX_SHADERSTAGE_IMAGES); - if (slot_index >= _sg.gl.max_combined_texture_image_units) { + SOKOL_ASSERT((slot_index >= 0) && (slot_index < _SG_GL_TEXTURE_SAMPLER_CACHE_SIZE)); + if (slot_index >= _sg.limits.gl_max_combined_texture_image_units) { return; } - _sg_gl_texture_bind_slot* slot = &_sg.gl.cache.textures[slot_index]; - if ((slot->target != target) || (slot->texture != texture)) { + _SG_GL_CHECK_ERROR(); + _sg_gl_cache_texture_sampler_bind_slot* slot = &_sg.gl.cache.texture_samplers[slot_index]; + if ((slot->target != target) || (slot->texture != texture) || (slot->sampler != sampler)) { _sg_gl_cache_active_texture((GLenum)(GL_TEXTURE0 + slot_index)); - /* if the target has changed, clear the previous binding on that target */ + // if the target has changed, clear the previous binding on that target if ((target != slot->target) && (slot->target != 0)) { glBindTexture(slot->target, 0); + _SG_GL_CHECK_ERROR(); } - /* apply new binding (texture can be 0 to unbind) */ + // apply new binding (can be 0 to unbind) if (target != 0) { glBindTexture(target, texture); + _SG_GL_CHECK_ERROR(); } + // apply new sampler (can be 0 to unbind) + glBindSampler((GLuint)slot_index, sampler); + _SG_GL_CHECK_ERROR(); + slot->target = target; slot->texture = texture; + slot->sampler = sampler; } } -_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_cache_store_texture_sampler_binding(int slot_index) { + SOKOL_ASSERT((slot_index >= 0) && (slot_index < _SG_GL_TEXTURE_SAMPLER_CACHE_SIZE)); + _sg.gl.cache.stored_texture_sampler = _sg.gl.cache.texture_samplers[slot_index]; } -_SOKOL_PRIVATE void _sg_gl_cache_restore_texture_binding(int slot_index) { - SOKOL_ASSERT(slot_index < SG_MAX_SHADERSTAGE_IMAGES); - _sg_gl_texture_bind_slot* slot = &_sg.gl.cache.stored_texture; +_SOKOL_PRIVATE void _sg_gl_cache_restore_texture_sampler_binding(int slot_index) { + SOKOL_ASSERT((slot_index >= 0) && (slot_index < _SG_GL_TEXTURE_SAMPLER_CACHE_SIZE)); + _sg_gl_cache_texture_sampler_bind_slot* slot = &_sg.gl.cache.stored_texture_sampler; if (slot->texture != 0) { - /* we only care restoring valid ids */ + // we only care about restoring valid ids SOKOL_ASSERT(slot->target != 0); - _sg_gl_cache_bind_texture(slot_index, slot->target, slot->texture); + _sg_gl_cache_bind_texture_sampler(slot_index, slot->target, slot->texture, slot->sampler); slot->target = 0; slot->texture = 0; + slot->sampler = 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) { +// called from _sg_gl_discard_texture() and _sg_gl_discard_sampler() +_SOKOL_PRIVATE void _sg_gl_cache_invalidate_texture_sampler(GLuint tex, GLuint smp) { + _SG_GL_CHECK_ERROR(); + for (int i = 0; i < _SG_GL_TEXTURE_SAMPLER_CACHE_SIZE; i++) { + _sg_gl_cache_texture_sampler_bind_slot* slot = &_sg.gl.cache.texture_samplers[i]; + if ((0 != slot->target) && ((tex == slot->texture) || (smp == slot->sampler))) { _sg_gl_cache_active_texture((GLenum)(GL_TEXTURE0 + i)); glBindTexture(slot->target, 0); + _SG_GL_CHECK_ERROR(); + glBindSampler((GLuint)i, 0); + _SG_GL_CHECK_ERROR(); slot->target = 0; slot->texture = 0; + slot->sampler = 0; } } - if (tex == _sg.gl.cache.stored_texture.texture) { - _sg.gl.cache.stored_texture.target = 0; - _sg.gl.cache.stored_texture.texture = 0; + if ((tex == _sg.gl.cache.stored_texture_sampler.texture) || (smp == _sg.gl.cache.stored_texture_sampler.sampler)) { + _sg.gl.cache.stored_texture_sampler.target = 0; + _sg.gl.cache.stored_texture_sampler.texture = 0; + _sg.gl.cache.stored_texture_sampler.sampler = 0; } } -/* called from _sg_gl_discard_shader() */ +// called from _sg_gl_discard_shader() _SOKOL_PRIVATE void _sg_gl_cache_invalidate_program(GLuint prog) { if (prog == _sg.gl.cache.prog) { _sg.gl.cache.prog = 0; @@ -6627,7 +6956,7 @@ _SOKOL_PRIVATE void _sg_gl_cache_invalidate_program(GLuint prog) { } } -/* called from _sg_gl_discard_pipeline() */ +// called from _sg_gl_discard_pipeline() _SOKOL_PRIVATE void _sg_gl_cache_invalidate_pipeline(_sg_pipeline_t* pip) { if (pip == _sg.gl.cache.cur_pipeline) { _sg.gl.cache.cur_pipeline = 0; @@ -6638,16 +6967,12 @@ _SOKOL_PRIVATE void _sg_gl_cache_invalidate_pipeline(_sg_pipeline_t* pip) { _SOKOL_PRIVATE void _sg_gl_reset_state_cache(void) { 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 + glBindVertexArray(_sg.gl.cur_context->vao); + _SG_GL_CHECK_ERROR(); _sg_clear(&_sg.gl.cache, sizeof(_sg.gl.cache)); _sg_gl_cache_clear_buffer_bindings(true); _SG_GL_CHECK_ERROR(); - _sg_gl_cache_clear_texture_bindings(true); + _sg_gl_cache_clear_texture_sampler_bindings(true); _SG_GL_CHECK_ERROR(); for (int i = 0; i < _sg.limits.max_vertex_attrs; i++) { _sg_gl_attr_t* attr = &_sg.gl.cache.attrs[i].gl_attr; @@ -6658,11 +6983,11 @@ _SOKOL_PRIVATE void _sg_gl_reset_state_cache(void) { } _sg.gl.cache.cur_primitive_type = GL_TRIANGLES; - /* shader program */ + // shader program glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&_sg.gl.cache.prog); _SG_GL_CHECK_ERROR(); - /* depth and stencil state */ + // depth and stencil state _sg.gl.cache.depth.compare = SG_COMPAREFUNC_ALWAYS; _sg.gl.cache.stencil.front.compare = SG_COMPAREFUNC_ALWAYS; _sg.gl.cache.stencil.front.fail_op = SG_STENCILOP_KEEP; @@ -6680,7 +7005,7 @@ _SOKOL_PRIVATE void _sg_gl_reset_state_cache(void) { glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); glStencilMask(0); - /* blend state */ + // blend state _sg.gl.cache.blend.src_factor_rgb = SG_BLENDFACTOR_ONE; _sg.gl.cache.blend.dst_factor_rgb = SG_BLENDFACTOR_ZERO; _sg.gl.cache.blend.op_rgb = SG_BLENDOP_ADD; @@ -6692,7 +7017,7 @@ _SOKOL_PRIVATE void _sg_gl_reset_state_cache(void) { glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD); glBlendColor(0.0f, 0.0f, 0.0f, 0.0f); - /* standalone state */ + // standalone state for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { _sg.gl.cache.color_write_mask[i] = SG_COLORMASK_RGBA; } @@ -6717,34 +7042,24 @@ _SOKOL_PRIVATE void _sg_gl_reset_state_cache(void) { } _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->context.gl.force_gles2; - #else _SOKOL_UNUSED(desc); - _sg.gl.gles2 = false; - #endif + SOKOL_ASSERT(desc->context.gl.default_framebuffer_cb == 0 || desc->context.gl.default_framebuffer_userdata_cb == 0); + + // assumes that _sg.gl is already zero-initialized + _sg.gl.valid = true; #if defined(_SOKOL_USE_WIN32_GL_LOADER) _sg_gl_load_opengl(); #endif - /* clear initial GL error state */ + // clear initial GL error state #if defined(SOKOL_DEBUG) while (glGetError() != GL_NO_ERROR); #endif #if defined(SOKOL_GLCORE33) _sg_gl_init_caps_glcore33(); #elif defined(SOKOL_GLES3) - if (_sg.gl.gles2) { - _sg_gl_init_caps_gles2(); - } - else { - _sg_gl_init_caps_gles3(); - } - #else - _sg_gl_init_caps_gles2(); + _sg_gl_init_caps_gles3(); #endif } @@ -6758,57 +7073,47 @@ _SOKOL_PRIVATE void _sg_gl_discard_backend(void) { _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 */ + // NOTE: ctx can be 0 to unset the current context _sg.gl.cur_context = ctx; _sg_gl_reset_state_cache(); } -/*-- GL backend resource creation and destruction ----------------------------*/ +//-- GL backend resource creation and destruction ------------------------------ _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(); glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&ctx->default_framebuffer); _SG_GL_CHECK_ERROR(); - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - SOKOL_ASSERT(0 == ctx->vao); - glGenVertexArrays(1, &ctx->vao); - glBindVertexArray(ctx->vao); - _SG_GL_CHECK_ERROR(); - } - #endif + SOKOL_ASSERT(0 == ctx->vao); + glGenVertexArrays(1, &ctx->vao); + glBindVertexArray(ctx->vao); + _SG_GL_CHECK_ERROR(); + // incoming texture data is generally expected to be packed tightly + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); return SG_RESOURCESTATE_VALID; } _SOKOL_PRIVATE void _sg_gl_discard_context(_sg_context_t* ctx) { SOKOL_ASSERT(ctx); - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - if (ctx->vao) { - glDeleteVertexArrays(1, &ctx->vao); - } - _SG_GL_CHECK_ERROR(); + if (ctx->vao) { + glDeleteVertexArrays(1, &ctx->vao); } - #else - _SOKOL_UNUSED(ctx); - #endif + _SG_GL_CHECK_ERROR(); } _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(); - _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); + buf->gl.injected = (0 != desc->gl_buffers[0]); + const GLenum gl_target = _sg_gl_buffer_target(buf->cmn.type); + const 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->gl.ext_buffers) { + if (buf->gl.injected) { SOKOL_ASSERT(desc->gl_buffers[slot]); gl_buf = desc->gl_buffers[slot]; - } - else { + } else { glGenBuffers(1, &gl_buf); SOKOL_ASSERT(gl_buf); _sg_gl_cache_store_buffer_binding(gl_target); @@ -6832,7 +7137,7 @@ _SOKOL_PRIVATE void _sg_gl_discard_buffer(_sg_buffer_t* buf) { 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) { + if (!buf->gl.injected) { glDeleteBuffers(1, &buf->gl.buf[slot]); } } @@ -6849,183 +7154,80 @@ _SOKOL_PRIVATE bool _sg_gl_supported_texture_format(sg_pixel_format fmt) { _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(); - _sg_image_common_init(&img->cmn, desc); - img->gl.ext_textures = (0 != desc->gl_textures[0]); + img->gl.injected = (0 != desc->gl_textures[0]); - /* check if texture format is support */ + // check if texture format is support if (!_sg_gl_supported_texture_format(img->cmn.pixel_format)) { - SG_LOG("texture format not supported by GL context\n"); + _SG_ERROR(GL_TEXTURE_FORMAT_NOT_SUPPORTED); return SG_RESOURCESTATE_FAILED; } - /* check for optional texture types */ - if ((img->cmn.type == SG_IMAGETYPE_3D) && !_sg.features.imagetype_3d) { - SG_LOG("3D textures not supported by GL context\n"); - return SG_RESOURCESTATE_FAILED; - } - if ((img->cmn.type == SG_IMAGETYPE_ARRAY) && !_sg.features.imagetype_array) { - SG_LOG("array textures not supported by GL context\n"); - return SG_RESOURCESTATE_FAILED; - } - - #if !defined(SOKOL_GLES2) - bool msaa = false; - if (!_sg.gl.gles2) { - msaa = (img->cmn.sample_count > 1) && (_sg.features.msaa_render_targets); - } - #endif + const GLenum gl_internal_format = _sg_gl_teximage_internal_format(img->cmn.pixel_format); - if (_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format)) { - /* special case depth-stencil-buffer? */ - 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->cmn.sample_count, gl_depth_format, img->cmn.width, img->cmn.height); - } - else - #endif - { - glRenderbufferStorage(GL_RENDERBUFFER, gl_depth_format, img->cmn.width, img->cmn.height); - } - } - else { - /* regular color texture */ + // if this is a MSAA render target, a render buffer object will be created instead of a regulat texture + // (since GLES3 has no multisampled texture objects) + if (img->cmn.render_target && (img->cmn.sample_count > 1)) { + 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); + } else if (img->gl.injected) { 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->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); + // inject externally GL textures + for (int slot = 0; slot < img->cmn.num_slots; slot++) { + SOKOL_ASSERT(desc->gl_textures[slot]); + img->gl.tex[slot] = desc->gl_textures[slot]; } - #endif - - if (img->gl.ext_textures) { - /* inject externally GL textures */ - for (int slot = 0; slot < img->cmn.num_slots; slot++) { - SOKOL_ASSERT(desc->gl_textures[slot]); - img->gl.tex[slot] = desc->gl_textures[slot]; - } - if (desc->gl_texture_target) { - img->gl.target = (GLenum)desc->gl_texture_target; - } + if (desc->gl_texture_target) { + img->gl.target = (GLenum)desc->gl_texture_target; } - else { - /* create our own GL texture(s) */ - 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]); - SOKOL_ASSERT(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, (GLint)gl_min_filter); - glTexParameteri(img->gl.target, GL_TEXTURE_MAG_FILTER, (GLint)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); - } - 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, (GLint)_sg_gl_wrap(img->cmn.wrap_u)); - glTexParameteri(img->gl.target, GL_TEXTURE_WRAP_T, (GLint)_sg_gl_wrap(img->cmn.wrap_v)); - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2 && (img->cmn.type == SG_IMAGETYPE_3D)) { - glTexParameteri(img->gl.target, GL_TEXTURE_WRAP_R, (GLint)_sg_gl_wrap(img->cmn.wrap_w)); - } - #endif - #if defined(SOKOL_GLCORE33) - float border[4]; - 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; - case SG_BORDERCOLOR_OPAQUE_WHITE: - border[0] = 1.0f; border[1] = 1.0f; border[2] = 1.0f; border[3] = 1.0f; - break; - default: - border[0] = 0.0f; border[1] = 0.0f; border[2] = 0.0f; border[3] = 1.0f; - break; + } else { + // create our own GL texture(s) + img->gl.target = _sg_gl_texture_target(img->cmn.type); + 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]); + SOKOL_ASSERT(img->gl.tex[slot]); + _sg_gl_cache_store_texture_sampler_binding(0); + _sg_gl_cache_bind_texture_sampler(0, img->gl.target, img->gl.tex[slot], 0); + 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->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); } - glTexParameterfv(img->gl.target, GL_TEXTURE_BORDER_COLOR, border); - #endif - } - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - /* 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); - } - #endif - 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->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->data.subimage[face_index][mip_index].ptr; + const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); + if ((SG_IMAGETYPE_2D == img->cmn.type) || (SG_IMAGETYPE_CUBE == img->cmn.type)) { + if (is_compressed) { + const GLsizei data_size = (GLsizei) desc->data.subimage[face_index][mip_index].size; + 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->cmn.pixel_format); + glTexImage2D(gl_img_target, mip_index, (GLint)gl_internal_format, + mip_width, mip_height, 0, gl_format, gl_type, data_ptr); } - const GLvoid* data_ptr = desc->data.subimage[face_index][mip_index].ptr; - int mip_width = img->cmn.width >> mip_index; - if (mip_width == 0) { - mip_width = 1; + } else if ((SG_IMAGETYPE_3D == img->cmn.type) || (SG_IMAGETYPE_ARRAY == img->cmn.type)) { + int mip_depth = img->cmn.num_slices; + if (SG_IMAGETYPE_3D == img->cmn.type) { + mip_depth = _sg_miplevel_dim(mip_depth, mip_index); } - int mip_height = img->cmn.height >> mip_index; - if (mip_height == 0) { - mip_height = 1; - } - if ((SG_IMAGETYPE_2D == img->cmn.type) || (SG_IMAGETYPE_CUBE == img->cmn.type)) { - if (is_compressed) { - const GLsizei data_size = (GLsizei) desc->data.subimage[face_index][mip_index].size; - 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->cmn.pixel_format); - glTexImage2D(gl_img_target, mip_index, (GLint)gl_internal_format, - mip_width, mip_height, 0, gl_format, gl_type, data_ptr); - } + if (is_compressed) { + const GLsizei data_size = (GLsizei) desc->data.subimage[face_index][mip_index].size; + glCompressedTexImage3D(gl_img_target, mip_index, gl_internal_format, + mip_width, mip_height, mip_depth, 0, data_size, data_ptr); + } else { + const GLenum gl_type = _sg_gl_teximage_type(img->cmn.pixel_format); + glTexImage3D(gl_img_target, mip_index, (GLint)gl_internal_format, + mip_width, mip_height, mip_depth, 0, gl_format, gl_type, data_ptr); } - #if !defined(SOKOL_GLES2) - else if (!_sg.gl.gles2 && ((SG_IMAGETYPE_3D == img->cmn.type) || (SG_IMAGETYPE_ARRAY == img->cmn.type))) { - int mip_depth = img->cmn.num_slices; - if (SG_IMAGETYPE_3D == img->cmn.type) { - mip_depth >>= mip_index; - } - if (mip_depth == 0) { - mip_depth = 1; - } - if (is_compressed) { - const GLsizei data_size = (GLsizei) desc->data.subimage[face_index][mip_index].size; - glCompressedTexImage3D(gl_img_target, mip_index, gl_internal_format, - mip_width, mip_height, mip_depth, 0, data_size, data_ptr); - } - else { - const GLenum gl_type = _sg_gl_teximage_type(img->cmn.pixel_format); - glTexImage3D(gl_img_target, mip_index, (GLint)gl_internal_format, - mip_width, mip_height, mip_depth, 0, gl_format, gl_type, data_ptr); - } - } - #endif } } - _sg_gl_cache_restore_texture_binding(0); } + _sg_gl_cache_restore_texture_sampler_binding(0); } } _SG_GL_CHECK_ERROR(); @@ -7037,21 +7239,83 @@ _SOKOL_PRIVATE void _sg_gl_discard_image(_sg_image_t* img) { _SG_GL_CHECK_ERROR(); 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) { + _sg_gl_cache_invalidate_texture_sampler(img->gl.tex[slot], 0); + if (!img->gl.injected) { glDeleteTextures(1, &img->gl.tex[slot]); } } } - 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); } _SG_GL_CHECK_ERROR(); } +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { + SOKOL_ASSERT(smp && desc); + _SG_GL_CHECK_ERROR(); + smp->gl.injected = (0 != desc->gl_sampler); + if (smp->gl.injected) { + smp->gl.smp = (GLuint) desc->gl_sampler; + } else { + glGenSamplers(1, &smp->gl.smp); + SOKOL_ASSERT(smp->gl.smp); + + const GLenum gl_min_filter = _sg_gl_min_filter(smp->cmn.min_filter, smp->cmn.mipmap_filter); + const GLenum gl_mag_filter = _sg_gl_mag_filter(smp->cmn.mag_filter); + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_MIN_FILTER, (GLint)gl_min_filter); + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_MAG_FILTER, (GLint)gl_mag_filter); + // 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); + glSamplerParameterf(smp->gl.smp, GL_TEXTURE_MIN_LOD, min_lod); + glSamplerParameterf(smp->gl.smp, GL_TEXTURE_MAX_LOD, max_lod); + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_WRAP_S, (GLint)_sg_gl_wrap(smp->cmn.wrap_u)); + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_WRAP_T, (GLint)_sg_gl_wrap(smp->cmn.wrap_v)); + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_WRAP_R, (GLint)_sg_gl_wrap(smp->cmn.wrap_w)); + #if defined(SOKOL_GLCORE33) + float border[4]; + switch (smp->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; + case SG_BORDERCOLOR_OPAQUE_WHITE: + border[0] = 1.0f; border[1] = 1.0f; border[2] = 1.0f; border[3] = 1.0f; + break; + default: + border[0] = 0.0f; border[1] = 0.0f; border[2] = 0.0f; border[3] = 1.0f; + break; + } + glSamplerParameterfv(smp->gl.smp, GL_TEXTURE_BORDER_COLOR, border); + #endif + if (smp->cmn.compare != SG_COMPAREFUNC_NEVER) { + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_COMPARE_FUNC, (GLint)_sg_gl_compare_func(smp->cmn.compare)); + } else { + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_COMPARE_MODE, GL_NONE); + } + if (_sg.gl.ext_anisotropic && (smp->cmn.max_anisotropy > 1)) { + GLint max_aniso = (GLint) smp->cmn.max_anisotropy; + if (max_aniso > _sg.gl.max_anisotropy) { + max_aniso = _sg.gl.max_anisotropy; + } + glSamplerParameteri(smp->gl.smp, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_aniso); + } + } + _SG_GL_CHECK_ERROR(); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_gl_discard_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp); + _SG_GL_CHECK_ERROR(); + _sg_gl_cache_invalidate_texture_sampler(0, smp->gl.smp); + if (!smp->gl.injected) { + glDeleteSamplers(1, &smp->gl.smp); + } + _SG_GL_CHECK_ERROR(); +} + _SOKOL_PRIVATE GLuint _sg_gl_compile_shader(sg_shader_stage stage, const char* src) { SOKOL_ASSERT(src); _SG_GL_CHECK_ERROR(); @@ -7061,13 +7325,14 @@ _SOKOL_PRIVATE GLuint _sg_gl_compile_shader(sg_shader_stage stage, const char* s GLint compile_status = 0; glGetShaderiv(gl_shd, GL_COMPILE_STATUS, &compile_status); if (!compile_status) { - /* compilation failed, log error and delete shader */ + // compilation failed, log error and delete shader GLint log_len = 0; glGetShaderiv(gl_shd, GL_INFO_LOG_LENGTH, &log_len); if (log_len > 0) { GLchar* log_buf = (GLchar*) _sg_malloc((size_t)log_len); glGetShaderInfoLog(gl_shd, log_len, &log_len, log_buf); - SG_LOG(log_buf); + _SG_ERROR(GL_SHADER_COMPILATION_FAILED); + _SG_LOGMSG(GL_SHADER_COMPILATION_FAILED, log_buf); _sg_free(log_buf); } glDeleteShader(gl_shd); @@ -7082,9 +7347,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_shader(_sg_shader_t* shd, const s 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 */ + // copy the optional vertex attribute names over for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { _sg_strcpy(&shd->gl.attrs[i].name, desc->attrs[i].name); } @@ -7110,7 +7373,8 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_shader(_sg_shader_t* shd, const s if (log_len > 0) { GLchar* log_buf = (GLchar*) _sg_malloc((size_t)log_len); glGetProgramInfoLog(gl_prog, log_len, &log_len, log_buf); - SG_LOG(log_buf); + _SG_ERROR(GL_SHADER_LINKING_FAILED); + _SG_LOGMSG(GL_SHADER_LINKING_FAILED, log_buf); _sg_free(log_buf); } glDeleteProgram(gl_prog); @@ -7118,12 +7382,13 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_shader(_sg_shader_t* shd, const s } shd->gl.prog = gl_prog; - /* resolve uniforms */ + // 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; + const _sg_shader_stage_t* stage = &shd->cmn.stage[stage_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++) { + for (int ub_index = 0; ub_index < stage->num_uniform_blocks; ub_index++) { const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; SOKOL_ASSERT(ub_desc->size > 0); _sg_gl_uniform_block_t* ub = &gl_stage->uniform_blocks[ub_index]; @@ -7144,8 +7409,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_shader(_sg_shader_t* shd, const s cur_uniform_offset += u_size; if (u_desc->name) { u->gl_loc = glGetUniformLocation(gl_prog, u_desc->name); - } - else { + } else { u->gl_loc = u_index; } ub->num_uniforms++; @@ -7158,7 +7422,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_shader(_sg_shader_t* shd, const s } } - /* resolve image locations */ + // resolve combined image samplers _SG_GL_CHECK_ERROR(); GLuint cur_prog = 0; glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&cur_prog); @@ -7166,25 +7430,24 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_shader(_sg_shader_t* shd, const s 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; + const _sg_shader_stage_t* stage = &shd->cmn.stage[stage_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]; - SOKOL_ASSERT(img_desc->image_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) { - gl_loc = glGetUniformLocation(gl_prog, img_desc->name); - } + for (int img_smp_index = 0; img_smp_index < stage->num_image_samplers; img_smp_index++) { + const sg_shader_image_sampler_pair_desc* img_smp_desc = &stage_desc->image_sampler_pairs[img_smp_index]; + _sg_gl_shader_image_sampler_t* gl_img_smp = &gl_stage->image_samplers[img_smp_index]; + SOKOL_ASSERT(img_smp_desc->glsl_name); + GLint gl_loc = glGetUniformLocation(gl_prog, img_smp_desc->glsl_name); if (gl_loc != -1) { - gl_img->gl_tex_slot = gl_tex_slot++; - glUniform1i(gl_loc, gl_img->gl_tex_slot); - } - else { - gl_img->gl_tex_slot = -1; + gl_img_smp->gl_tex_slot = gl_tex_slot++; + glUniform1i(gl_loc, gl_img_smp->gl_tex_slot); + } else { + gl_img_smp->gl_tex_slot = -1; + _SG_ERROR(GL_TEXTURE_NAME_NOT_FOUND_IN_SHADER); + _SG_LOGMSG(GL_TEXTURE_NAME_NOT_FOUND_IN_SHADER, img_smp_desc->glsl_name); } } } - /* it's legal to call glUseProgram with 0 */ + // it's legal to call glUseProgram with 0 glUseProgram(cur_prog); _SG_GL_CHECK_ERROR(); return SG_RESOURCESTATE_VALID; @@ -7202,11 +7465,10 @@ _SOKOL_PRIVATE void _sg_gl_discard_shader(_sg_shader_t* shd) { _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->cmn.shader_id.id == SG_INVALID_ID); + SOKOL_ASSERT((pip->shader == 0) && (pip->cmn.shader_id.id != SG_INVALID_ID)); SOKOL_ASSERT(desc->shader.id == shd->slot.id); SOKOL_ASSERT(shd->gl.prog); pip->shader = shd; - _sg_pipeline_common_init(&pip->cmn, desc); pip->gl.primitive_type = desc->primitive_type; pip->gl.depth = desc->depth; pip->gl.stencil = desc->stencil; @@ -7220,19 +7482,19 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_pipeline(_sg_pipeline_t* pip, _sg pip->gl.sample_count = desc->sample_count; pip->gl.alpha_to_coverage_enabled = desc->alpha_to_coverage_enabled; - /* resolve vertex attributes */ + // resolve vertex attributes for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { pip->gl.attrs[attr_index].vb_index = -1; } for (int attr_index = 0; attr_index < _sg.limits.max_vertex_attrs; attr_index++) { - const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; - if (a_desc->format == SG_VERTEXFORMAT_INVALID) { + const sg_vertex_attr_state* a_state = &desc->layout.attrs[attr_index]; + if (a_state->format == SG_VERTEXFORMAT_INVALID) { break; } - SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); - const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[a_desc->buffer_index]; - const sg_vertex_step step_func = l_desc->step_func; - const int step_rate = l_desc->step_rate; + SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEX_BUFFERS); + const sg_vertex_buffer_layout_state* l_state = &desc->layout.buffers[a_state->buffer_index]; + const sg_vertex_step step_func = l_state->step_func; + const int step_rate = l_state->step_rate; GLint attr_loc = attr_index; if (!_sg_strempty(&shd->gl.attrs[attr_index].name)) { attr_loc = glGetAttribLocation(pip->shader->gl.prog, _sg_strptr(&shd->gl.attrs[attr_index].name)); @@ -7241,25 +7503,23 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_pipeline(_sg_pipeline_t* pip, _sg if (attr_loc != -1) { _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; + gl_attr->vb_index = (int8_t) a_state->buffer_index; if (step_func == SG_VERTEXSTEP_PER_VERTEX) { gl_attr->divisor = 0; - } - else { + } else { gl_attr->divisor = (int8_t) step_rate; pip->cmn.use_instanced_draw = true; } - SOKOL_ASSERT(l_desc->stride > 0); - gl_attr->stride = (uint8_t) l_desc->stride; - gl_attr->offset = a_desc->offset; - 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->cmn.vertex_layout_valid[a_desc->buffer_index] = true; - } - else { - SG_LOG("Vertex attribute not found in shader: "); - SG_LOG(_sg_strptr(&shd->gl.attrs[attr_index].name)); + SOKOL_ASSERT(l_state->stride > 0); + gl_attr->stride = (uint8_t) l_state->stride; + gl_attr->offset = a_state->offset; + gl_attr->size = (uint8_t) _sg_gl_vertexformat_size(a_state->format); + gl_attr->type = _sg_gl_vertexformat_type(a_state->format); + gl_attr->normalized = _sg_gl_vertexformat_normalized(a_state->format); + pip->cmn.vertex_buffer_layout_active[a_state->buffer_index] = true; + } else { + _SG_ERROR(GL_VERTEX_ATTRIBUTE_NOT_FOUND_IN_SHADER); + _SG_LOGMSG(GL_VERTEX_ATTRIBUTE_NOT_FOUND_IN_SHADER, _sg_strptr(&shd->gl.attrs[attr_index].name)); } } return SG_RESOURCESTATE_VALID; @@ -7270,162 +7530,137 @@ _SOKOL_PRIVATE void _sg_gl_discard_pipeline(_sg_pipeline_t* pip) { _sg_gl_cache_invalidate_pipeline(pip); } -/* - _sg_create_pass +_SOKOL_PRIVATE void _sg_gl_fb_attach_texture(const _sg_gl_attachment_t* gl_att, const _sg_pass_attachment_common_t* cmn_att, GLenum gl_att_type) { + const _sg_image_t* img = gl_att->image; + SOKOL_ASSERT(img); + const GLuint gl_tex = img->gl.tex[0]; + SOKOL_ASSERT(gl_tex); + const GLuint gl_target = img->gl.target; + SOKOL_ASSERT(gl_target); + const int mip_level = cmn_att->mip_level; + const int slice = cmn_att->slice; + switch (img->cmn.type) { + case SG_IMAGETYPE_2D: + glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att_type, gl_target, gl_tex, mip_level); + break; + case SG_IMAGETYPE_CUBE: + glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att_type, _sg_gl_cubeface_target(slice), gl_tex, mip_level); + break; + default: + glFramebufferTextureLayer(GL_FRAMEBUFFER, gl_att_type, gl_tex, mip_level, slice); + break; + } +} - att_imgs must point to a _sg_image* att_imgs[SG_MAX_COLOR_ATTACHMENTS+1] array, - first entries are the color attachment images (or nullptr), last entry - is the depth-stencil image (or nullptr). -*/ -_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(); +_SOKOL_PRIVATE GLenum _sg_gl_depth_stencil_attachment_type(const _sg_gl_attachment_t* ds_att) { + const _sg_image_t* img = ds_att->image; + SOKOL_ASSERT(img); + if (_sg_is_depth_stencil_format(img->cmn.pixel_format)) { + return GL_DEPTH_STENCIL_ATTACHMENT; + } else { + return GL_DEPTH_ATTACHMENT; + } +} - _sg_pass_common_init(&pass->cmn, desc); +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_pass(_sg_pass_t* pass, _sg_image_t** color_images, _sg_image_t** resolve_images, _sg_image_t* ds_image, const sg_pass_desc* desc) { + SOKOL_ASSERT(pass && desc); + SOKOL_ASSERT(color_images && resolve_images); + _SG_GL_CHECK_ERROR(); - /* copy image pointers */ - const sg_pass_attachment_desc* att_desc; + // copy image pointers for (int i = 0; i < pass->cmn.num_color_atts; i++) { - att_desc = &desc->color_attachments[i]; - SOKOL_ASSERT(att_desc->image.id != SG_INVALID_ID); + const sg_pass_attachment_desc* color_desc = &desc->color_attachments[i]; + _SOKOL_UNUSED(color_desc); + SOKOL_ASSERT(color_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(color_images[i] && (color_images[i]->slot.id == color_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(color_images[i]->cmn.pixel_format)); + pass->gl.color_atts[i].image = color_images[i]; + + const sg_pass_attachment_desc* resolve_desc = &desc->resolve_attachments[i]; + if (resolve_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(0 == pass->gl.resolve_atts[i].image); + SOKOL_ASSERT(resolve_images[i] && (resolve_images[i]->slot.id == resolve_desc->image.id)); + SOKOL_ASSERT(color_images[i] && (color_images[i]->cmn.pixel_format == resolve_images[i]->cmn.pixel_format)); + pass->gl.resolve_atts[i].image = resolve_images[i]; + } } SOKOL_ASSERT(0 == pass->gl.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)); - pass->gl.ds_att.image = att_images[ds_img_index]; + const sg_pass_attachment_desc* ds_desc = &desc->depth_stencil_attachment; + if (ds_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(ds_image && (ds_image->slot.id == ds_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(ds_image->cmn.pixel_format)); + pass->gl.ds_att.image = ds_image; } - /* store current framebuffer binding (restored at end of function) */ + // store current framebuffer binding (restored at end of function) GLuint gl_orig_fb; glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&gl_orig_fb); - /* create a framebuffer object */ + // create a framebuffer object 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); - if (is_msaa) { - for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - 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; - SOKOL_ASSERT(gl_render_buffer); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, (GLenum)(GL_COLOR_ATTACHMENT0+i), GL_RENDERBUFFER, gl_render_buffer); - } - } - } - else { - for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - 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]; - SOKOL_ASSERT(gl_tex); - const GLenum gl_att = (GLenum)(GL_COLOR_ATTACHMENT0 + i); - switch (att_img->cmn.type) { - case SG_IMAGETYPE_2D: - glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att, GL_TEXTURE_2D, gl_tex, mip_level); - break; - case SG_IMAGETYPE_CUBE: - glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att, _sg_gl_cubeface_target(slice), gl_tex, mip_level); - break; - default: - /* 3D- or array-texture */ - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2) { - glFramebufferTextureLayer(GL_FRAMEBUFFER, gl_att, gl_tex, mip_level, slice); - } - #endif - break; - } - } + // attach color attachments to framebuffer + for (int i = 0; i < pass->cmn.num_color_atts; i++) { + const _sg_image_t* color_img = pass->gl.color_atts[i].image; + SOKOL_ASSERT(color_img); + const GLuint gl_msaa_render_buffer = color_img->gl.msaa_render_buffer; + if (gl_msaa_render_buffer) { + glFramebufferRenderbuffer(GL_FRAMEBUFFER, (GLenum)(GL_COLOR_ATTACHMENT0+i), GL_RENDERBUFFER, gl_msaa_render_buffer); + } else { + const GLenum gl_att_type = (GLenum)(GL_COLOR_ATTACHMENT0 + i); + _sg_gl_fb_attach_texture(&pass->gl.color_atts[i], &pass->cmn.color_atts[i], gl_att_type); } } - - /* attach depth-stencil buffer to framebuffer */ + // attach depth-stencil attachement 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->gl.ds_att.image->cmn.pixel_format)) { - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, gl_render_buffer); + const GLenum gl_att = _sg_gl_depth_stencil_attachment_type(&pass->gl.ds_att); + const _sg_image_t* ds_img = pass->gl.ds_att.image; + const GLuint gl_msaa_render_buffer = ds_img->gl.msaa_render_buffer; + if (gl_msaa_render_buffer) { + glFramebufferRenderbuffer(GL_FRAMEBUFFER, gl_att, GL_RENDERBUFFER, gl_msaa_render_buffer); + } else { + const GLenum gl_att_type = _sg_gl_depth_stencil_attachment_type(&pass->gl.ds_att); + _sg_gl_fb_attach_texture(&pass->gl.ds_att, &pass->cmn.ds_att, gl_att_type); } } - /* check if framebuffer is complete */ + // check if framebuffer is complete if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - SG_LOG("Framebuffer completeness check failed!\n"); + _SG_ERROR(GL_FRAMEBUFFER_INCOMPLETE); 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 + // setup color attachments for the framebuffer + static const GLenum gl_draw_bufs[SG_MAX_COLOR_ATTACHMENTS] = { + GL_COLOR_ATTACHMENT0, + GL_COLOR_ATTACHMENT1, + GL_COLOR_ATTACHMENT2, + GL_COLOR_ATTACHMENT3 + }; + glDrawBuffers(pass->cmn.num_color_atts, gl_draw_bufs); - /* create MSAA resolve framebuffers if necessary */ - if (is_msaa) { - for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - _sg_gl_attachment_t* gl_att = &pass->gl.color_atts[i]; - _sg_pass_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 (gl_att->image->cmn.type) { - case SG_IMAGETYPE_2D: - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, gl_tex, cmn_att->mip_level); - break; - case SG_IMAGETYPE_CUBE: - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - _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, cmn_att->mip_level, cmn_att->slice); - } - #endif - break; - } - /* check if framebuffer is complete */ - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - SG_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 + // create MSAA resolve framebuffers if necessary + for (int i = 0; i < pass->cmn.num_color_atts; i++) { + _sg_gl_attachment_t* gl_resolve_att = &pass->gl.resolve_atts[i]; + if (gl_resolve_att->image) { + _sg_pass_attachment_t* cmn_resolve_att = &pass->cmn.resolve_atts[i]; + SOKOL_ASSERT(0 == pass->gl.msaa_resolve_framebuffer[i]); + glGenFramebuffers(1, &pass->gl.msaa_resolve_framebuffer[i]); + glBindFramebuffer(GL_FRAMEBUFFER, pass->gl.msaa_resolve_framebuffer[i]); + _sg_gl_fb_attach_texture(gl_resolve_att, cmn_resolve_att, GL_COLOR_ATTACHMENT0); + // check if framebuffer is complete + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + _SG_ERROR(GL_MSAA_FRAMEBUFFER_INCOMPLETE); + return SG_RESOURCESTATE_FAILED; } + // setup color attachments for the framebuffer + glDrawBuffers(1, &gl_draw_bufs[0]); } } - /* restore original framebuffer binding */ + // restore original framebuffer binding glBindFramebuffer(GL_FRAMEBUFFER, gl_orig_fb); _SG_GL_CHECK_ERROR(); return SG_RESOURCESTATE_VALID; @@ -7439,75 +7674,92 @@ _SOKOL_PRIVATE void _sg_gl_discard_pass(_sg_pass_t* pass) { glDeleteFramebuffers(1, &pass->gl.fb); } for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - if (pass->gl.color_atts[i].gl_msaa_resolve_buffer) { - glDeleteFramebuffers(1, &pass->gl.color_atts[i].gl_msaa_resolve_buffer); + if (pass->gl.msaa_resolve_framebuffer[i]) { + glDeleteFramebuffers(1, &pass->gl.msaa_resolve_framebuffer[i]); } } - if (pass->gl.ds_att.gl_msaa_resolve_buffer) { - glDeleteFramebuffers(1, &pass->gl.ds_att.gl_msaa_resolve_buffer); - } _SG_GL_CHECK_ERROR(); } _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_resolve_image(const _sg_pass_t* pass, int index) { + SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + return pass->gl.resolve_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? */ + // 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); SOKOL_ASSERT(!_sg.gl.in_pass); _SG_GL_CHECK_ERROR(); _sg.gl.in_pass = true; - _sg.gl.cur_pass = pass; /* can be 0 */ + _sg.gl.cur_pass = pass; // can be 0 if (pass) { _sg.gl.cur_pass_id.id = pass->slot.id; - } - else { + } else { _sg.gl.cur_pass_id.id = SG_INVALID_ID; } _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 */ + // bind the render pass framebuffer + // + // FIXME: Disabling SRGB conversion for the default framebuffer is + // a crude hack to make behaviour for sRGB render target textures + // identical with the Metal and D3D11 swapchains created by sokol-app. + // + // This will need a cleaner solution (e.g. allowing to configure + // sokol_app.h with an sRGB or RGB framebuffer. if (pass) { - /* offscreen pass */ + // offscreen pass SOKOL_ASSERT(pass->gl.fb); + #if defined(SOKOL_GLCORE33) + glEnable(GL_FRAMEBUFFER_SRGB); + #endif glBindFramebuffer(GL_FRAMEBUFFER, pass->gl.fb); - } - else { - /* default pass */ + } else { + // default pass SOKOL_ASSERT(_sg.gl.cur_context); + #if defined(SOKOL_GLCORE33) + glDisable(GL_FRAMEBUFFER_SRGB); + #endif + if (_sg.desc.context.gl.default_framebuffer_userdata_cb) { + _sg.gl.cur_context->default_framebuffer = _sg.desc.context.gl.default_framebuffer_userdata_cb(_sg.desc.context.gl.user_data); + } else if (_sg.desc.context.gl.default_framebuffer_cb) { + _sg.gl.cur_context->default_framebuffer = _sg.desc.context.gl.default_framebuffer_cb(); + } + glBindFramebuffer(GL_FRAMEBUFFER, _sg.gl.cur_context->default_framebuffer); } glViewport(0, 0, w, h); glScissor(0, 0, w, h); - /* clear color and depth-stencil attachments if needed */ - bool clear_color = false; + // number of color attachments + const int num_color_atts = pass ? pass->cmn.num_color_atts : 1; + + // clear color and depth-stencil attachments if needed + bool clear_any_color = false; for (int i = 0; i < num_color_atts; i++) { - if (SG_ACTION_CLEAR == action->colors[i].action) { - clear_color = true; + if (SG_LOADACTION_CLEAR == action->colors[i].load_action) { + clear_any_color = true; break; } } - const bool clear_depth = (action->depth.action == SG_ACTION_CLEAR); - const bool clear_stencil = (action->stencil.action == SG_ACTION_CLEAR); + const bool clear_depth = (action->depth.load_action == SG_LOADACTION_CLEAR); + const bool clear_stencil = (action->stencil.load_action == SG_LOADACTION_CLEAR); bool need_pip_cache_flush = false; - if (clear_color) { + if (clear_any_color) { bool need_color_mask_flush = false; // NOTE: not a bug to iterate over all possible color attachments for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { @@ -7541,64 +7793,33 @@ _SOKOL_PRIVATE void _sg_gl_begin_pass(_sg_pass_t* pass, const sg_pass_action* ac } } if (need_pip_cache_flush) { - /* we messed with the state cache directly, need to clear cached - pipeline to force re-evaluation in next sg_apply_pipeline() */ + // we messed with the state cache directly, need to clear cached + // pipeline to force re-evaluation in next sg_apply_pipeline() _sg.gl.cache.cur_pipeline = 0; _sg.gl.cache.cur_pipeline_id.id = SG_INVALID_ID; } - bool use_mrt_clear = (0 != pass); - #if defined(SOKOL_GLES2) - use_mrt_clear = false; - #else - if (_sg.gl.gles2) { - use_mrt_clear = false; - } - #endif - if (!use_mrt_clear) { - GLbitfield clear_mask = 0; - if (clear_color) { - clear_mask |= GL_COLOR_BUFFER_BIT; - const sg_color c = action->colors[0].value; - glClearColor(c.r, c.g, c.b, c.a); - } - if (clear_depth) { - clear_mask |= GL_DEPTH_BUFFER_BIT; - #ifdef SOKOL_GLCORE33 - glClearDepth(action->depth.value); - #else - glClearDepthf(action->depth.value); - #endif - } - if (clear_stencil) { - clear_mask |= GL_STENCIL_BUFFER_BIT; - glClearStencil(action->stencil.value); - } - if (0 != clear_mask) { - glClear(clear_mask); + for (int i = 0; i < num_color_atts; i++) { + if (action->colors[i].load_action == SG_LOADACTION_CLEAR) { + glClearBufferfv(GL_COLOR, i, &action->colors[i].clear_value.r); } } - #if !defined SOKOL_GLES2 - else { - SOKOL_ASSERT(pass); - for (int i = 0; i < num_color_atts; i++) { - if (action->colors[i].action == SG_ACTION_CLEAR) { - glClearBufferfv(GL_COLOR, i, &action->colors[i].value.r); - } - } - if (pass->gl.ds_att.image) { - if (clear_depth && clear_stencil) { - glClearBufferfi(GL_DEPTH_STENCIL, 0, action->depth.value, action->stencil.value); - } - else if (clear_depth) { - glClearBufferfv(GL_DEPTH, 0, &action->depth.value); - } - else if (clear_stencil) { - GLint val = (GLint) action->stencil.value; - glClearBufferiv(GL_STENCIL, 0, &val); - } + if ((pass == 0) || (pass->gl.ds_att.image)) { + if (clear_depth && clear_stencil) { + glClearBufferfi(GL_DEPTH_STENCIL, 0, action->depth.clear_value, action->stencil.clear_value); + } else if (clear_depth) { + glClearBufferfv(GL_DEPTH, 0, &action->depth.clear_value); + } else if (clear_stencil) { + GLint val = (GLint) action->stencil.clear_value; + glClearBufferiv(GL_STENCIL, 0, &val); } } - #endif + // keep store actions for end-pass + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + _sg.gl.color_store_actions[i] = action->colors[i].store_action; + } + _sg.gl.depth_store_action = action->depth.store_action; + _sg.gl.stencil_store_action = action->stencil.store_action; + _SG_GL_CHECK_ERROR(); } @@ -7606,35 +7827,55 @@ _SOKOL_PRIVATE void _sg_gl_end_pass(void) { SOKOL_ASSERT(_sg.gl.in_pass); _SG_GL_CHECK_ERROR(); - /* if this was an offscreen pass, and MSAA rendering was used, need - to resolve into the pass images */ - #if !defined(SOKOL_GLES2) - if (!_sg.gl.gles2 && _sg.gl.cur_pass) { - /* check if the pass object is still valid */ + if (_sg.gl.cur_pass) { 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->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->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_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((GLenum)(GL_COLOR_ATTACHMENT0 + att_index)); - glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST); - } - else { - break; + bool fb_read_bound = false; + bool fb_draw_bound = false; + const int num_atts = pass->cmn.num_color_atts; + for (int i = 0; i < num_atts; i++) { + // perform MSAA resolve if needed + if (pass->gl.msaa_resolve_framebuffer[i] != 0) { + if (!fb_read_bound) { + SOKOL_ASSERT(pass->gl.fb); + glBindFramebuffer(GL_READ_FRAMEBUFFER, pass->gl.fb); + fb_read_bound = true; } + const int w = pass->gl.color_atts[i].image->cmn.width; + const int h = pass->gl.color_atts[i].image->cmn.height; + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, pass->gl.msaa_resolve_framebuffer[i]); + glReadBuffer((GLenum)(GL_COLOR_ATTACHMENT0 + i)); + glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST); + fb_draw_bound = true; } } + + // invalidate framebuffers + _SOKOL_UNUSED(fb_draw_bound); + #if defined(SOKOL_GLES3) + // need to restore framebuffer binding before invalidate if the MSAA resolve had changed the binding + if (fb_draw_bound) { + glBindFramebuffer(GL_FRAMEBUFFER, pass->gl.fb); + } + GLenum invalidate_atts[SG_MAX_COLOR_ATTACHMENTS + 2] = { 0 }; + int att_index = 0; + for (int i = 0; i < num_atts; i++) { + if (_sg.gl.color_store_actions[i] == SG_STOREACTION_DONTCARE) { + invalidate_atts[att_index++] = (GLenum)(GL_COLOR_ATTACHMENT0 + i); + } + } + if (_sg.gl.depth_store_action == SG_STOREACTION_DONTCARE) { + invalidate_atts[att_index++] = GL_DEPTH_ATTACHMENT; + } + if (_sg.gl.stencil_store_action == SG_STOREACTION_DONTCARE) { + invalidate_atts[att_index++] = GL_STENCIL_ATTACHMENT; + } + if (att_index > 0) { + glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER, att_index, invalidate_atts); + } + #endif } - #endif + _sg.gl.cur_pass = 0; _sg.gl.cur_pass_id.id = SG_INVALID_ID; _sg.gl.cur_pass_width = 0; @@ -7668,7 +7909,7 @@ _SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { _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 state */ + // update depth state { const sg_depth_state* state_ds = &pip->gl.depth; sg_depth_state* cache_ds = &_sg.gl.cache.depth; @@ -7701,15 +7942,14 @@ _SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { _sg.gl.cache.polygon_offset_enabled = po_enabled; if (po_enabled) { glEnable(GL_POLYGON_OFFSET_FILL); - } - else { + } else { glDisable(GL_POLYGON_OFFSET_FILL); } } } } - /* update stencil state */ + // update stencil state { const sg_stencil_state* state_ss = &pip->gl.stencil; sg_stencil_state* cache_ss = &_sg.gl.cache.stencil; @@ -7717,8 +7957,7 @@ _SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { cache_ss->enabled = state_ss->enabled; if (state_ss->enabled) { glEnable(GL_STENCIL_TEST); - } - else { + } else { glDisable(GL_STENCIL_TEST); } } @@ -7757,18 +7996,16 @@ _SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { cache_ss->ref = state_ss->ref; } - /* update blend state - FIXME: separate blend state per color attachment not support, needs GL4 - */ - { + if (pip->cmn.color_count > 0) { + // update blend state + // FIXME: separate blend state per color attachment not support, needs GL4 const sg_blend_state* state_bs = &pip->gl.blend; sg_blend_state* cache_bs = &_sg.gl.cache.blend; if (state_bs->enabled != cache_bs->enabled) { cache_bs->enabled = state_bs->enabled; if (state_bs->enabled) { glEnable(GL_BLEND); - } - else { + } else { glDisable(GL_BLEND); } } @@ -7791,45 +8028,45 @@ _SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { cache_bs->op_alpha = state_bs->op_alpha; glBlendEquationSeparate(_sg_gl_blend_op(state_bs->op_rgb), _sg_gl_blend_op(state_bs->op_alpha)); } - } - /* standalone state */ - for (GLuint i = 0; i < (GLuint)pip->cmn.color_attachment_count; i++) { - if (pip->gl.color_write_mask[i] != _sg.gl.cache.color_write_mask[i]) { - const sg_color_mask cm = pip->gl.color_write_mask[i]; - _sg.gl.cache.color_write_mask[i] = cm; - #ifdef SOKOL_GLCORE33 - glColorMaski(i, - (cm & SG_COLORMASK_R) != 0, - (cm & SG_COLORMASK_G) != 0, - (cm & SG_COLORMASK_B) != 0, - (cm & SG_COLORMASK_A) != 0); - #else - if (0 == i) { - glColorMask((cm & SG_COLORMASK_R) != 0, + // standalone color target state + for (GLuint i = 0; i < (GLuint)pip->cmn.color_count; i++) { + if (pip->gl.color_write_mask[i] != _sg.gl.cache.color_write_mask[i]) { + const sg_color_mask cm = pip->gl.color_write_mask[i]; + _sg.gl.cache.color_write_mask[i] = cm; + #ifdef SOKOL_GLCORE33 + glColorMaski(i, + (cm & SG_COLORMASK_R) != 0, (cm & SG_COLORMASK_G) != 0, (cm & SG_COLORMASK_B) != 0, (cm & SG_COLORMASK_A) != 0); - } - #endif + #else + if (0 == i) { + glColorMask((cm & SG_COLORMASK_R) != 0, + (cm & SG_COLORMASK_G) != 0, + (cm & SG_COLORMASK_B) != 0, + (cm & SG_COLORMASK_A) != 0); + } + #endif + } } - } - if (!_sg_fequal(pip->cmn.blend_color.r, _sg.gl.cache.blend_color.r, 0.0001f) || - !_sg_fequal(pip->cmn.blend_color.g, _sg.gl.cache.blend_color.g, 0.0001f) || - !_sg_fequal(pip->cmn.blend_color.b, _sg.gl.cache.blend_color.b, 0.0001f) || - !_sg_fequal(pip->cmn.blend_color.a, _sg.gl.cache.blend_color.a, 0.0001f)) - { - sg_color c = pip->cmn.blend_color; - _sg.gl.cache.blend_color = c; - glBlendColor(c.r, c.g, c.b, c.a); - } + if (!_sg_fequal(pip->cmn.blend_color.r, _sg.gl.cache.blend_color.r, 0.0001f) || + !_sg_fequal(pip->cmn.blend_color.g, _sg.gl.cache.blend_color.g, 0.0001f) || + !_sg_fequal(pip->cmn.blend_color.b, _sg.gl.cache.blend_color.b, 0.0001f) || + !_sg_fequal(pip->cmn.blend_color.a, _sg.gl.cache.blend_color.a, 0.0001f)) + { + sg_color c = pip->cmn.blend_color; + _sg.gl.cache.blend_color = c; + glBlendColor(c.r, c.g, c.b, c.a); + } + } // pip->cmn.color_count > 0 + if (pip->gl.cull_mode != _sg.gl.cache.cull_mode) { _sg.gl.cache.cull_mode = pip->gl.cull_mode; if (SG_CULLMODE_NONE == pip->gl.cull_mode) { glDisable(GL_CULL_FACE); - } - else { + } else { glEnable(GL_CULL_FACE); GLenum gl_mode = (SG_CULLMODE_FRONT == pip->gl.cull_mode) ? GL_FRONT : GL_BACK; glCullFace(gl_mode); @@ -7844,8 +8081,7 @@ _SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { _sg.gl.cache.alpha_to_coverage_enabled = pip->gl.alpha_to_coverage_enabled; if (pip->gl.alpha_to_coverage_enabled) { glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE); - } - else { + } else { glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); } } @@ -7854,14 +8090,13 @@ _SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { _sg.gl.cache.sample_count = pip->gl.sample_count; if (pip->gl.sample_count > 1) { glEnable(GL_MULTISAMPLE); - } - else { + } else { glDisable(GL_MULTISAMPLE); } } #endif - /* bind shader program */ + // bind shader program if (pip->shader->gl.prog != _sg.gl.cache.prog) { _sg.gl.cache.prog = pip->shader->gl.prog; glUseProgram(pip->shader->gl.prog); @@ -7875,40 +8110,49 @@ _SOKOL_PRIVATE void _sg_gl_apply_bindings( _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) + _sg_image_t** fs_imgs, int num_fs_imgs, + _sg_sampler_t** vs_smps, int num_vs_smps, + _sg_sampler_t** fs_smps, int num_fs_smps) { SOKOL_ASSERT(pip); - _SOKOL_UNUSED(num_fs_imgs); - _SOKOL_UNUSED(num_vs_imgs); _SOKOL_UNUSED(num_vbs); _SG_GL_CHECK_ERROR(); - /* bind textures */ + // bind combined image-samplers _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->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_gl_shader_image_t* gl_shd_img = &gl_stage->images[img_index]; - if (gl_shd_img->gl_tex_slot != -1) { + _sg_image_t** imgs = (stage_index == SG_SHADERSTAGE_VS) ? vs_imgs : fs_imgs; + _sg_sampler_t** smps = (stage_index == SG_SHADERSTAGE_VS) ? vs_smps : fs_smps; + const int num_imgs = (stage_index == SG_SHADERSTAGE_VS) ? num_vs_imgs : num_fs_imgs; + const int num_smps = (stage_index == SG_SHADERSTAGE_VS) ? num_vs_smps : num_fs_smps; + SOKOL_ASSERT(num_imgs == stage->num_images); _SOKOL_UNUSED(num_imgs); + SOKOL_ASSERT(num_smps == stage->num_samplers); _SOKOL_UNUSED(num_smps); + for (int img_smp_index = 0; img_smp_index < stage->num_image_samplers; img_smp_index++) { + const int gl_tex_slot = gl_stage->image_samplers[img_smp_index].gl_tex_slot; + if (gl_tex_slot != -1) { + const int img_index = stage->image_samplers[img_smp_index].image_slot; + const int smp_index = stage->image_samplers[img_smp_index].sampler_slot; + SOKOL_ASSERT(img_index < num_imgs); + SOKOL_ASSERT(smp_index < num_smps); _sg_image_t* img = imgs[img_index]; + _sg_sampler_t* smp = smps[smp_index]; + const GLenum gl_tgt = img->gl.target; 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); + const GLuint gl_smp = smp->gl.smp; + _sg_gl_cache_bind_texture_sampler(gl_tex_slot, gl_tgt, gl_tex, gl_smp); } } } _SG_GL_CHECK_ERROR(); - /* index buffer (can be 0) */ + // index buffer (can be 0) 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 */ + // vertex attributes for (GLuint attr_index = 0; attr_index < (GLuint)_sg.limits.max_vertex_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]; @@ -7916,7 +8160,7 @@ _SOKOL_PRIVATE void _sg_gl_apply_bindings( int vb_offset = 0; GLuint gl_vb = 0; if (attr->vb_index >= 0) { - /* attribute is enabled */ + // attribute is enabled SOKOL_ASSERT(attr->vb_index < num_vbs); _sg_buffer_t* vb = vbs[attr->vb_index]; SOKOL_ASSERT(vb); @@ -7934,20 +8178,15 @@ _SOKOL_PRIVATE void _sg_gl_apply_bindings( glVertexAttribPointer(attr_index, attr->size, attr->type, attr->normalized, attr->stride, (const GLvoid*)(GLintptr)vb_offset); - #if defined(_SOKOL_GL_INSTANCING_ENABLED) - if (_sg.features.instancing) { - glVertexAttribDivisor(attr_index, (GLuint)attr->divisor); - } - #endif + glVertexAttribDivisor(attr_index, (GLuint)attr->divisor); cache_attr_dirty = true; } if (cache_attr->gl_attr.vb_index == -1) { glEnableVertexAttribArray(attr_index); cache_attr_dirty = true; } - } - else { - /* attribute is disabled */ + } else { + // attribute is disabled if (cache_attr->gl_attr.vb_index != -1) { glDisableVertexAttribArray(attr_index); cache_attr_dirty = true; @@ -8020,27 +8259,20 @@ _SOKOL_PRIVATE void _sg_gl_draw(int base_element, int num_elements, int num_inst const GLenum i_type = _sg.gl.cache.cur_index_type; const GLenum p_type = _sg.gl.cache.cur_primitive_type; if (0 != i_type) { - /* indexed rendering */ + // indexed rendering const int i_size = (i_type == GL_UNSIGNED_SHORT) ? 2 : 4; const int ib_offset = _sg.gl.cache.cur_ib_offset; const GLvoid* indices = (const GLvoid*)(GLintptr)(base_element*i_size+ib_offset); if (_sg.gl.cache.cur_pipeline->cmn.use_instanced_draw) { - if (_sg.features.instancing) { - glDrawElementsInstanced(p_type, num_elements, i_type, indices, num_instances); - } - } - else { + glDrawElementsInstanced(p_type, num_elements, i_type, indices, num_instances); + } else { glDrawElements(p_type, num_elements, i_type, indices); } - } - else { - /* non-indexed rendering */ + } else { + // non-indexed rendering if (_sg.gl.cache.cur_pipeline->cmn.use_instanced_draw) { - if (_sg.features.instancing) { - glDrawArraysInstanced(p_type, base_element, num_elements, num_instances); - } - } - else { + glDrawArraysInstanced(p_type, base_element, num_elements, num_instances); + } else { glDrawArrays(p_type, base_element, num_elements); } } @@ -8048,14 +8280,14 @@ _SOKOL_PRIVATE void _sg_gl_draw(int base_element, int num_elements, int num_inst _SOKOL_PRIVATE void _sg_gl_commit(void) { SOKOL_ASSERT(!_sg.gl.in_pass); - /* "soft" clear bindings (only those that are actually bound) */ + // "soft" clear bindings (only those that are actually bound) _sg_gl_cache_clear_buffer_bindings(false); - _sg_gl_cache_clear_texture_bindings(false); + _sg_gl_cache_clear_texture_sampler_bindings(false); } _SOKOL_PRIVATE void _sg_gl_update_buffer(_sg_buffer_t* buf, const sg_range* data) { SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); - /* only one update per buffer per frame allowed */ + // only one update per buffer per frame allowed if (++buf->cmn.active_slot >= buf->cmn.num_slots) { buf->cmn.active_slot = 0; } @@ -8088,20 +8320,20 @@ _SOKOL_PRIVATE int _sg_gl_append_buffer(_sg_buffer_t* buf, const sg_range* data, glBufferSubData(gl_tgt, buf->cmn.append_pos, (GLsizeiptr)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 */ + // NOTE: this is a requirement from WebGPU, but we want identical behaviour across all backend return _sg_roundup((int)data->size, 4); } _SOKOL_PRIVATE void _sg_gl_update_image(_sg_image_t* img, const sg_image_data* data) { SOKOL_ASSERT(img && data); - /* only one update per image per frame allowed */ + // only one update per image per frame allowed if (++img->cmn.active_slot >= img->cmn.num_slots) { img->cmn.active_slot = 0; } 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]); + _sg_gl_cache_store_texture_sampler_binding(0); + _sg_gl_cache_bind_texture_sampler(0, img->gl.target, img->gl.tex[img->cmn.active_slot], 0); 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; @@ -8113,26 +8345,18 @@ _SOKOL_PRIVATE void _sg_gl_update_image(_sg_image_t* img, const sg_image_data* d gl_img_target = _sg_gl_cubeface_target(face_index); } const GLvoid* data_ptr = data->subimage[face_index][mip_index].ptr; - int mip_width = img->cmn.width >> mip_index; - if (mip_width == 0) { - mip_width = 1; - } - int mip_height = img->cmn.height >> mip_index; - if (mip_height == 0) { - mip_height = 1; - } + int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); 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, gl_img_format, gl_img_type, data_ptr); - } - #if !defined(SOKOL_GLES2) - else if (!_sg.gl.gles2 && ((SG_IMAGETYPE_3D == img->cmn.type) || (SG_IMAGETYPE_ARRAY == img->cmn.type))) { - int mip_depth = img->cmn.num_slices >> mip_index; - if (mip_depth == 0) { - mip_depth = 1; + } else if ((SG_IMAGETYPE_3D == img->cmn.type) || (SG_IMAGETYPE_ARRAY == img->cmn.type)) { + int mip_depth = img->cmn.num_slices; + if (SG_IMAGETYPE_3D == img->cmn.type) { + mip_depth = _sg_miplevel_dim(img->cmn.num_slices, mip_index); } glTexSubImage3D(gl_img_target, mip_index, 0, 0, 0, @@ -8141,13 +8365,18 @@ _SOKOL_PRIVATE void _sg_gl_update_image(_sg_image_t* img, const sg_image_data* d data_ptr); } - #endif } } - _sg_gl_cache_restore_texture_binding(0); + _sg_gl_cache_restore_texture_sampler_binding(0); } -/*== D3D11 BACKEND IMPLEMENTATION ============================================*/ +// ██████ ██████ ██████ ██ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ██ ██ ██ ██ ██ ███ ███ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ ██ █████ ██ ██ ██ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██████ ██████ ██ ██ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>d3d11 backend #elif defined(SOKOL_D3D11) #if defined(__cplusplus) @@ -8162,7 +8391,7 @@ _SOKOL_PRIVATE void _sg_gl_update_image(_sg_image_t* img, const sg_image_data* d #define _sg_d3d11_Release(self) (self)->lpVtbl->Release(self) #endif -/*-- D3D11 C/C++ wrappers ----------------------------------------------------*/ +//-- D3D11 C/C++ wrappers ------------------------------------------------------ static inline HRESULT _sg_d3d11_CheckFormatSupport(ID3D11Device* self, DXGI_FORMAT Format, UINT* pFormatSupport) { #if defined(__cplusplus) return self->CheckFormatSupport(Format, pFormatSupport); @@ -8531,7 +8760,7 @@ static inline void _sg_d3d11_ClearState(ID3D11DeviceContext* self) { #endif } -/*-- enum translation functions ----------------------------------------------*/ +//-- enum translation functions ------------------------------------------------ _SOKOL_PRIVATE D3D11_USAGE _sg_d3d11_usage(sg_usage usg) { switch (usg) { case SG_USAGE_IMMUTABLE: @@ -8558,7 +8787,7 @@ _SOKOL_PRIVATE UINT _sg_d3d11_cpu_access_flags(sg_usage usg) { } } -_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_pixel_format(sg_pixel_format fmt) { +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_texture_pixel_format(sg_pixel_format fmt) { switch (fmt) { case SG_PIXELFORMAT_R8: return DXGI_FORMAT_R8_UNORM; case SG_PIXELFORMAT_R8SN: return DXGI_FORMAT_R8_SNORM; @@ -8582,6 +8811,7 @@ _SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_pixel_format(sg_pixel_format fmt) { case SG_PIXELFORMAT_RG16SI: return DXGI_FORMAT_R16G16_SINT; case SG_PIXELFORMAT_RG16F: return DXGI_FORMAT_R16G16_FLOAT; case SG_PIXELFORMAT_RGBA8: return DXGI_FORMAT_R8G8B8A8_UNORM; + case SG_PIXELFORMAT_SRGB8A8: return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; case SG_PIXELFORMAT_RGBA8SN: return DXGI_FORMAT_R8G8B8A8_SNORM; case SG_PIXELFORMAT_RGBA8UI: return DXGI_FORMAT_R8G8B8A8_UINT; case SG_PIXELFORMAT_RGBA8SI: return DXGI_FORMAT_R8G8B8A8_SINT; @@ -8600,7 +8830,7 @@ _SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_pixel_format(sg_pixel_format fmt) { case SG_PIXELFORMAT_RGBA32UI: return DXGI_FORMAT_R32G32B32A32_UINT; case SG_PIXELFORMAT_RGBA32SI: return DXGI_FORMAT_R32G32B32A32_SINT; case SG_PIXELFORMAT_RGBA32F: return DXGI_FORMAT_R32G32B32A32_FLOAT; - case SG_PIXELFORMAT_DEPTH: return DXGI_FORMAT_D32_FLOAT; + case SG_PIXELFORMAT_DEPTH: return DXGI_FORMAT_R32_TYPELESS; case SG_PIXELFORMAT_DEPTH_STENCIL: return DXGI_FORMAT_D24_UNORM_S8_UINT; case SG_PIXELFORMAT_BC1_RGBA: return DXGI_FORMAT_BC1_UNORM; case SG_PIXELFORMAT_BC2_RGBA: return DXGI_FORMAT_BC2_UNORM; @@ -8616,6 +8846,30 @@ _SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_pixel_format(sg_pixel_format fmt) { }; } +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_srv_pixel_format(sg_pixel_format fmt) { + if (fmt == SG_PIXELFORMAT_DEPTH) { + return DXGI_FORMAT_R32_FLOAT; + } else { + return _sg_d3d11_texture_pixel_format(fmt); + } +} + +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_dsv_pixel_format(sg_pixel_format fmt) { + if (fmt == SG_PIXELFORMAT_DEPTH) { + return DXGI_FORMAT_D32_FLOAT; + } else { + return _sg_d3d11_texture_pixel_format(fmt); + } +} + +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_rtv_pixel_format(sg_pixel_format fmt) { + if (fmt == SG_PIXELFORMAT_DEPTH) { + return DXGI_FORMAT_R32_FLOAT; + } else { + return _sg_d3d11_texture_pixel_format(fmt); + } +} + _SOKOL_PRIVATE D3D11_PRIMITIVE_TOPOLOGY _sg_d3d11_primitive_topology(sg_primitive_type prim_type) { switch (prim_type) { case SG_PRIMITIVETYPE_POINTS: return D3D11_PRIMITIVE_TOPOLOGY_POINTLIST; @@ -8636,45 +8890,43 @@ _SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_index_format(sg_index_type index_type) { } } -_SOKOL_PRIVATE D3D11_FILTER _sg_d3d11_filter(sg_filter min_f, sg_filter mag_f, uint32_t max_anisotropy) { +_SOKOL_PRIVATE D3D11_FILTER _sg_d3d11_filter(sg_filter min_f, sg_filter mag_f, sg_filter mipmap_f, bool comparison, uint32_t max_anisotropy) { + uint32_t d3d11_filter = 0; if (max_anisotropy > 1) { - return D3D11_FILTER_ANISOTROPIC; - } - else if (mag_f == SG_FILTER_NEAREST) { - switch (min_f) { - case SG_FILTER_NEAREST: - case SG_FILTER_NEAREST_MIPMAP_NEAREST: - return D3D11_FILTER_MIN_MAG_MIP_POINT; - case SG_FILTER_LINEAR: - case SG_FILTER_LINEAR_MIPMAP_NEAREST: - return D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT; - case SG_FILTER_NEAREST_MIPMAP_LINEAR: - return D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR; - case SG_FILTER_LINEAR_MIPMAP_LINEAR: - return D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR; - default: - SOKOL_UNREACHABLE; break; - } - } - else if (mag_f == SG_FILTER_LINEAR) { - switch (min_f) { - case SG_FILTER_NEAREST: - case SG_FILTER_NEAREST_MIPMAP_NEAREST: - return D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT; - case SG_FILTER_LINEAR: - case SG_FILTER_LINEAR_MIPMAP_NEAREST: - return D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT; - case SG_FILTER_NEAREST_MIPMAP_LINEAR: - return D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR; - case SG_FILTER_LINEAR_MIPMAP_LINEAR: - return D3D11_FILTER_MIN_MAG_MIP_LINEAR; - default: - SOKOL_UNREACHABLE; break; - } - } - /* invalid value for mag filter */ - SOKOL_UNREACHABLE; - return D3D11_FILTER_MIN_MAG_MIP_POINT; + // D3D11_FILTER_ANISOTROPIC = 0x55, + d3d11_filter |= 0x55; + } else { + // D3D11_FILTER_MIN_MAG_MIP_POINT = 0, + // D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR = 0x1, + // D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT = 0x4, + // D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR = 0x5, + // D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT = 0x10, + // D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR = 0x11, + // D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT = 0x14, + // D3D11_FILTER_MIN_MAG_MIP_LINEAR = 0x15, + if (mipmap_f == SG_FILTER_LINEAR) { + d3d11_filter |= 0x01; + } + if (mag_f == SG_FILTER_LINEAR) { + d3d11_filter |= 0x04; + } + if (min_f == SG_FILTER_LINEAR) { + d3d11_filter |= 0x10; + } + } + // D3D11_FILTER_COMPARISON_MIN_MAG_MIP_POINT = 0x80, + // D3D11_FILTER_COMPARISON_MIN_MAG_POINT_MIP_LINEAR = 0x81, + // D3D11_FILTER_COMPARISON_MIN_POINT_MAG_LINEAR_MIP_POINT = 0x84, + // D3D11_FILTER_COMPARISON_MIN_POINT_MAG_MIP_LINEAR = 0x85, + // D3D11_FILTER_COMPARISON_MIN_LINEAR_MAG_MIP_POINT = 0x90, + // D3D11_FILTER_COMPARISON_MIN_LINEAR_MAG_POINT_MIP_LINEAR = 0x91, + // D3D11_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT = 0x94, + // D3D11_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR = 0x95, + // D3D11_FILTER_COMPARISON_ANISOTROPIC = 0xd5, + if (comparison) { + d3d11_filter |= 0x80; + } + return (D3D11_FILTER)d3d11_filter; } _SOKOL_PRIVATE D3D11_TEXTURE_ADDRESS_MODE _sg_d3d11_address_mode(sg_wrap m) { @@ -8704,6 +8956,8 @@ _SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_vertex_format(sg_vertex_format fmt) { case SG_VERTEXFORMAT_SHORT4N: return DXGI_FORMAT_R16G16B16A16_SNORM; case SG_VERTEXFORMAT_USHORT4N: return DXGI_FORMAT_R16G16B16A16_UNORM; case SG_VERTEXFORMAT_UINT10_N2: return DXGI_FORMAT_R10G10B10A2_UNORM; + case SG_VERTEXFORMAT_HALF2: return DXGI_FORMAT_R16G16_FLOAT; + case SG_VERTEXFORMAT_HALF4: return DXGI_FORMAT_R16G16B16A16_FLOAT; default: SOKOL_UNREACHABLE; return (DXGI_FORMAT) 0; } } @@ -8800,16 +9054,23 @@ _SOKOL_PRIVATE UINT8 _sg_d3d11_color_write_mask(sg_color_mask m) { return res; } -/* see: https://docs.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-limits#resource-limits-for-feature-level-11-hardware */ +_SOKOL_PRIVATE UINT _sg_d3d11_dxgi_fmt_caps(DXGI_FORMAT dxgi_fmt) { + UINT dxgi_fmt_caps = 0; + if (dxgi_fmt != DXGI_FORMAT_UNKNOWN) { + HRESULT hr = _sg_d3d11_CheckFormatSupport(_sg.d3d11.dev, dxgi_fmt, &dxgi_fmt_caps); + SOKOL_ASSERT(SUCCEEDED(hr) || (E_FAIL == hr)); + if (!SUCCEEDED(hr)) { + dxgi_fmt_caps = 0; + } + } + return dxgi_fmt_caps; +} + +// see: https://docs.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-limits#resource-limits-for-feature-level-11-hardware _SOKOL_PRIVATE void _sg_d3d11_init_caps(void) { _sg.backend = SG_BACKEND_D3D11; - _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 = true; _sg.features.mrt_independent_blend_state = true; _sg.features.mrt_independent_write_mask = true; @@ -8821,32 +9082,23 @@ _SOKOL_PRIVATE void _sg_d3d11_init_caps(void) { _sg.limits.max_image_array_layers = 2 * 1024; _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 */ + // see: https://docs.microsoft.com/en-us/windows/win32/api/d3d11/ne-d3d11-d3d11_format_support for (int fmt = (SG_PIXELFORMAT_NONE+1); fmt < _SG_PIXELFORMAT_NUM; fmt++) { - 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 = _sg_d3d11_CheckFormatSupport(_sg.d3d11.dev, dxgi_fmt, &dxgi_fmt_caps); - SOKOL_ASSERT(SUCCEEDED(hr) || (E_FAIL == hr)); - if (!SUCCEEDED(hr)) { - dxgi_fmt_caps = 0; - } - } + const UINT srv_dxgi_fmt_caps = _sg_d3d11_dxgi_fmt_caps(_sg_d3d11_srv_pixel_format((sg_pixel_format)fmt)); + const UINT rtv_dxgi_fmt_caps = _sg_d3d11_dxgi_fmt_caps(_sg_d3d11_rtv_pixel_format((sg_pixel_format)fmt)); + const UINT dsv_dxgi_fmt_caps = _sg_d3d11_dxgi_fmt_caps(_sg_d3d11_dsv_pixel_format((sg_pixel_format)fmt)); 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); - info->render = 0 != (dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_RENDER_TARGET); - info->blend = 0 != (dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_BLENDABLE); - info->msaa = 0 != (dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_MULTISAMPLE_RENDERTARGET); - info->depth = 0 != (dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_DEPTH_STENCIL); - if (info->depth) { - info->render = true; - } + info->sample = 0 != (srv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_TEXTURE2D); + info->filter = 0 != (srv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_SHADER_SAMPLE); + info->render = 0 != (rtv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_RENDER_TARGET); + info->blend = 0 != (rtv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_BLENDABLE); + info->msaa = 0 != (rtv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_MULTISAMPLE_RENDERTARGET); + info->depth = 0 != (dsv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_DEPTH_STENCIL); } } _SOKOL_PRIVATE void _sg_d3d11_setup_backend(const sg_desc* desc) { - /* assume _sg.d3d11 already is zero-initialized */ + // assume _sg.d3d11 already is zero-initialized SOKOL_ASSERT(desc); SOKOL_ASSERT(desc->context.d3d11.device); SOKOL_ASSERT(desc->context.d3d11.device_context); @@ -8869,12 +9121,12 @@ _SOKOL_PRIVATE void _sg_d3d11_discard_backend(void) { } _SOKOL_PRIVATE void _sg_d3d11_clear_state(void) { - /* clear all the device context state, so that resource refs don't keep stuck in the d3d device context */ + // clear all the device context state, so that resource refs don't keep stuck in the d3d device context _sg_d3d11_ClearState(_sg.d3d11.ctx); } _SOKOL_PRIVATE void _sg_d3d11_reset_state_cache(void) { - /* just clear the d3d11 device context state */ + // just clear the d3d11 device context state _sg_d3d11_clear_state(); } @@ -8892,19 +9144,17 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_context(_sg_context_t* ctx) { _SOKOL_PRIVATE void _sg_d3d11_discard_context(_sg_context_t* ctx) { SOKOL_ASSERT(ctx); _SOKOL_UNUSED(ctx); - /* empty */ + // empty } _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); - _sg_buffer_common_init(&buf->cmn, desc); const bool injected = (0 != desc->d3d11_buffer); if (injected) { buf->d3d11.buf = (ID3D11Buffer*) desc->d3d11_buffer; _sg_d3d11_AddRef(buf->d3d11.buf); - } - else { + } else { D3D11_BUFFER_DESC d3d11_desc; _sg_clear(&d3d11_desc, sizeof(d3d11_desc)); d3d11_desc.ByteWidth = (UINT)buf->cmn.size; @@ -8921,7 +9171,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_buffer(_sg_buffer_t* buf, cons } HRESULT hr = _sg_d3d11_CreateBuffer(_sg.d3d11.dev, &d3d11_desc, init_data_ptr, &buf->d3d11.buf); if (!(SUCCEEDED(hr) && buf->d3d11.buf)) { - SG_LOG("failed to create D3D11 buffer\n"); + _SG_ERROR(D3D11_CREATE_BUFFER_FAILED); return SG_RESOURCESTATE_FAILED; } } @@ -8944,8 +9194,8 @@ _SOKOL_PRIVATE void _sg_d3d11_fill_subres_data(const _sg_image_t* img, const sg_ 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->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 mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); const sg_range* subimg_data = &(data->subimage[face_index][mip_index]); const size_t slice_size = subimg_data->size / (size_t)num_slices; const size_t slice_offset = slice_size * (size_t)slice_index; @@ -8953,10 +9203,9 @@ _SOKOL_PRIVATE void _sg_d3d11_fill_subres_data(const _sg_image_t* img, const sg_ subres_data->pSysMem = ptr + slice_offset; subres_data->SysMemPitch = (UINT)_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; */ + // FIXME? const int mip_depth = _sg_miplevel_dim(img->depth, mip_index); subres_data->SysMemSlicePitch = (UINT)_sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1); - } - else { + } else { subres_data->SysMemSlicePitch = 0; } } @@ -8966,238 +9215,213 @@ _SOKOL_PRIVATE void _sg_d3d11_fill_subres_data(const _sg_image_t* img, const sg_ _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((0 == img->d3d11.tex2d) && (0 == img->d3d11.tex3d) && (0 == img->d3d11.res) && (0 == img->d3d11.srv)); HRESULT hr; - _sg_image_common_init(&img->cmn, desc); const bool injected = (0 != desc->d3d11_texture) || (0 != desc->d3d11_shader_resource_view); const bool msaa = (img->cmn.sample_count > 1); - img->d3d11.format = _sg_d3d11_pixel_format(img->cmn.pixel_format); - - /* special case depth-stencil buffer? */ - if (_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format)) { - /* create only a depth-texture */ - SOKOL_ASSERT(!injected); - if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) { - SG_LOG("trying to create a D3D11 depth-texture with unsupported pixel format\n"); - return SG_RESOURCESTATE_FAILED; - } - D3D11_TEXTURE2D_DESC d3d11_desc; - _sg_clear(&d3d11_desc, sizeof(d3d11_desc)); - d3d11_desc.Width = (UINT)img->cmn.width; - d3d11_desc.Height = (UINT)img->cmn.height; - d3d11_desc.MipLevels = 1; - d3d11_desc.ArraySize = 1; - d3d11_desc.Format = img->d3d11.format; - d3d11_desc.Usage = D3D11_USAGE_DEFAULT; - d3d11_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; - d3d11_desc.SampleDesc.Count = (UINT)img->cmn.sample_count; - d3d11_desc.SampleDesc.Quality = (UINT) (msaa ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0); - hr = _sg_d3d11_CreateTexture2D(_sg.d3d11.dev, &d3d11_desc, NULL, &img->d3d11.texds); - if (!(SUCCEEDED(hr) && img->d3d11.texds)) { - SG_LOG("failed to create D3D11 texture 2D\n"); - return SG_RESOURCESTATE_FAILED; - } + img->d3d11.format = _sg_d3d11_texture_pixel_format(img->cmn.pixel_format); + if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) { + _SG_ERROR(D3D11_CREATE_2D_TEXTURE_UNSUPPORTED_PIXEL_FORMAT); + return SG_RESOURCESTATE_FAILED; } - else { - /* create (or inject) color texture and shader-resource-view */ - /* prepare initial content pointers */ - D3D11_SUBRESOURCE_DATA* init_data = 0; - if (!injected && (img->cmn.usage == SG_USAGE_IMMUTABLE) && !img->cmn.render_target) { - _sg_d3d11_fill_subres_data(img, &desc->data); - init_data = _sg.d3d11.subres_data; + // prepare initial content pointers + D3D11_SUBRESOURCE_DATA* init_data = 0; + if (!injected && (img->cmn.usage == SG_USAGE_IMMUTABLE) && !img->cmn.render_target) { + _sg_d3d11_fill_subres_data(img, &desc->data); + init_data = _sg.d3d11.subres_data; + } + if (img->cmn.type != SG_IMAGETYPE_3D) { + // 2D-, cube- or array-texture + // first check for injected texture and/or resource view + if (injected) { + img->d3d11.tex2d = (ID3D11Texture2D*) desc->d3d11_texture; + img->d3d11.srv = (ID3D11ShaderResourceView*) desc->d3d11_shader_resource_view; + if (img->d3d11.tex2d) { + _sg_d3d11_AddRef(img->d3d11.tex2d); + } else { + // if only a shader-resource-view was provided, but no texture, lookup + // the texture from the shader-resource-view, this also bumps the refcount + SOKOL_ASSERT(img->d3d11.srv); + _sg_d3d11_GetResource((ID3D11View*)img->d3d11.srv, (ID3D11Resource**)&img->d3d11.tex2d); + SOKOL_ASSERT(img->d3d11.tex2d); + } + if (img->d3d11.srv) { + _sg_d3d11_AddRef(img->d3d11.srv); + } } - 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' */ - /* first check for injected texture and/or resource view */ - if (injected) { - img->d3d11.tex2d = (ID3D11Texture2D*) desc->d3d11_texture; - img->d3d11.srv = (ID3D11ShaderResourceView*) desc->d3d11_shader_resource_view; - if (img->d3d11.tex2d) { - _sg_d3d11_AddRef(img->d3d11.tex2d); - } - else { - /* if only a shader-resource-view was provided, but no texture, lookup - the texture from the shader-resource-view, this also bumps the refcount - */ - SOKOL_ASSERT(img->d3d11.srv); - _sg_d3d11_GetResource((ID3D11View*)img->d3d11.srv, (ID3D11Resource**)&img->d3d11.tex2d); - SOKOL_ASSERT(img->d3d11.tex2d); - } - if (img->d3d11.srv) { - _sg_d3d11_AddRef(img->d3d11.srv); - } + if (0 == img->d3d11.tex2d) { + // if not injected, create texture + D3D11_TEXTURE2D_DESC d3d11_tex_desc; + _sg_clear(&d3d11_tex_desc, sizeof(d3d11_tex_desc)); + d3d11_tex_desc.Width = (UINT)img->cmn.width; + d3d11_tex_desc.Height = (UINT)img->cmn.height; + d3d11_tex_desc.MipLevels = (UINT)img->cmn.num_mipmaps; + switch (img->cmn.type) { + case SG_IMAGETYPE_ARRAY: d3d11_tex_desc.ArraySize = (UINT)img->cmn.num_slices; break; + case SG_IMAGETYPE_CUBE: d3d11_tex_desc.ArraySize = 6; break; + default: d3d11_tex_desc.ArraySize = 1; break; } - - /* if not injected, create texture */ - if (0 == img->d3d11.tex2d) { - D3D11_TEXTURE2D_DESC d3d11_tex_desc; - _sg_clear(&d3d11_tex_desc, sizeof(d3d11_tex_desc)); - d3d11_tex_desc.Width = (UINT)img->cmn.width; - d3d11_tex_desc.Height = (UINT)img->cmn.height; - d3d11_tex_desc.MipLevels = (UINT)img->cmn.num_mipmaps; - switch (img->cmn.type) { - case SG_IMAGETYPE_ARRAY: d3d11_tex_desc.ArraySize = (UINT)img->cmn.num_slices; 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; - d3d11_tex_desc.Format = img->d3d11.format; - if (img->cmn.render_target) { - d3d11_tex_desc.Usage = D3D11_USAGE_DEFAULT; - if (!msaa) { - d3d11_tex_desc.BindFlags |= D3D11_BIND_RENDER_TARGET; - } - d3d11_tex_desc.CPUAccessFlags = 0; - } - else { - 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) { - /* trying to create a texture format that's not supported by D3D */ - SG_LOG("trying to create a D3D11 texture with unsupported pixel format\n"); - return SG_RESOURCESTATE_FAILED; + d3d11_tex_desc.Format = img->d3d11.format; + if (img->cmn.render_target) { + d3d11_tex_desc.Usage = D3D11_USAGE_DEFAULT; + if (_sg_is_depth_or_depth_stencil_format(img->cmn.pixel_format)) { + d3d11_tex_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; + } else { + d3d11_tex_desc.BindFlags = D3D11_BIND_RENDER_TARGET; } - d3d11_tex_desc.SampleDesc.Count = 1; - d3d11_tex_desc.SampleDesc.Quality = 0; - d3d11_tex_desc.MiscFlags = (img->cmn.type == SG_IMAGETYPE_CUBE) ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0; - - hr = _sg_d3d11_CreateTexture2D(_sg.d3d11.dev, &d3d11_tex_desc, init_data, &img->d3d11.tex2d); - if (!(SUCCEEDED(hr) && img->d3d11.tex2d)) { - SG_LOG("failed to create D3D11 texture 2D\n"); - return SG_RESOURCESTATE_FAILED; + if (!msaa) { + d3d11_tex_desc.BindFlags |= D3D11_BIND_SHADER_RESOURCE; } + d3d11_tex_desc.CPUAccessFlags = 0; + } else { + d3d11_tex_desc.Usage = _sg_d3d11_usage(img->cmn.usage); + d3d11_tex_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + d3d11_tex_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(img->cmn.usage); } + d3d11_tex_desc.SampleDesc.Count = (UINT)img->cmn.sample_count; + d3d11_tex_desc.SampleDesc.Quality = (UINT) (msaa ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0); + d3d11_tex_desc.MiscFlags = (img->cmn.type == SG_IMAGETYPE_CUBE) ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0; - /* ...and similar, if not injected, create shader-resource-view */ - if (0 == img->d3d11.srv) { - D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; - _sg_clear(&d3d11_srv_desc, sizeof(d3d11_srv_desc)); - d3d11_srv_desc.Format = img->d3d11.format; - switch (img->cmn.type) { - case SG_IMAGETYPE_2D: - d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; - d3d11_srv_desc.Texture2D.MipLevels = (UINT)img->cmn.num_mipmaps; - break; - case SG_IMAGETYPE_CUBE: - d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; - d3d11_srv_desc.TextureCube.MipLevels = (UINT)img->cmn.num_mipmaps; - break; - case SG_IMAGETYPE_ARRAY: - d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; - d3d11_srv_desc.Texture2DArray.MipLevels = (UINT)img->cmn.num_mipmaps; - d3d11_srv_desc.Texture2DArray.ArraySize = (UINT)img->cmn.num_slices; - break; - default: - SOKOL_UNREACHABLE; break; - } - hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)img->d3d11.tex2d, &d3d11_srv_desc, &img->d3d11.srv); - if (!(SUCCEEDED(hr) && img->d3d11.srv)) { - SG_LOG("failed to create D3D11 resource view\n"); - return SG_RESOURCESTATE_FAILED; - } + hr = _sg_d3d11_CreateTexture2D(_sg.d3d11.dev, &d3d11_tex_desc, init_data, &img->d3d11.tex2d); + if (!(SUCCEEDED(hr) && img->d3d11.tex2d)) { + _SG_ERROR(D3D11_CREATE_2D_TEXTURE_FAILED); + return SG_RESOURCESTATE_FAILED; } } - else { - /* 3D texture - same procedure, first check if injected, than create non-injected */ - if (injected) { - img->d3d11.tex3d = (ID3D11Texture3D*) desc->d3d11_texture; - img->d3d11.srv = (ID3D11ShaderResourceView*) desc->d3d11_shader_resource_view; - if (img->d3d11.tex3d) { - _sg_d3d11_AddRef(img->d3d11.tex3d); - } - else { - SOKOL_ASSERT(img->d3d11.srv); - _sg_d3d11_GetResource((ID3D11View*)img->d3d11.srv, (ID3D11Resource**)&img->d3d11.tex3d); - SOKOL_ASSERT(img->d3d11.tex3d); - } - if (img->d3d11.srv) { - _sg_d3d11_AddRef(img->d3d11.srv); - } + SOKOL_ASSERT(img->d3d11.tex2d); + img->d3d11.res = (ID3D11Resource*)img->d3d11.tex2d; + _sg_d3d11_AddRef(img->d3d11.res); + + // ...and similar, if not injected, create shader-resource-view + // FIXME: currently we don't support setting MSAA texture as shader resource + if ((0 == img->d3d11.srv) && !msaa) { + D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; + _sg_clear(&d3d11_srv_desc, sizeof(d3d11_srv_desc)); + d3d11_srv_desc.Format = _sg_d3d11_srv_pixel_format(img->cmn.pixel_format); + switch (img->cmn.type) { + case SG_IMAGETYPE_2D: + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + d3d11_srv_desc.Texture2D.MipLevels = (UINT)img->cmn.num_mipmaps; + break; + case SG_IMAGETYPE_CUBE: + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; + d3d11_srv_desc.TextureCube.MipLevels = (UINT)img->cmn.num_mipmaps; + break; + case SG_IMAGETYPE_ARRAY: + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; + d3d11_srv_desc.Texture2DArray.MipLevels = (UINT)img->cmn.num_mipmaps; + d3d11_srv_desc.Texture2DArray.ArraySize = (UINT)img->cmn.num_slices; + break; + default: + SOKOL_UNREACHABLE; break; } - - if (0 == img->d3d11.tex3d) { - D3D11_TEXTURE3D_DESC d3d11_tex_desc; - _sg_clear(&d3d11_tex_desc, sizeof(d3d11_tex_desc)); - d3d11_tex_desc.Width = (UINT)img->cmn.width; - d3d11_tex_desc.Height = (UINT)img->cmn.height; - d3d11_tex_desc.Depth = (UINT)img->cmn.num_slices; - d3d11_tex_desc.MipLevels = (UINT)img->cmn.num_mipmaps; - d3d11_tex_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; - d3d11_tex_desc.Format = img->d3d11.format; - if (img->cmn.render_target) { - d3d11_tex_desc.Usage = D3D11_USAGE_DEFAULT; - if (!msaa) { - d3d11_tex_desc.BindFlags |= D3D11_BIND_RENDER_TARGET; - } - d3d11_tex_desc.CPUAccessFlags = 0; - } - else { - 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) { - /* trying to create a texture format that's not supported by D3D */ - SG_LOG("trying to create a D3D11 texture with unsupported pixel format\n"); - return SG_RESOURCESTATE_FAILED; - } - hr = _sg_d3d11_CreateTexture3D(_sg.d3d11.dev, &d3d11_tex_desc, init_data, &img->d3d11.tex3d); - if (!(SUCCEEDED(hr) && img->d3d11.tex3d)) { - SG_LOG("failed to create D3D11 texture 3D\n"); - return SG_RESOURCESTATE_FAILED; - } + hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, img->d3d11.res, &d3d11_srv_desc, &img->d3d11.srv); + if (!(SUCCEEDED(hr) && img->d3d11.srv)) { + _SG_ERROR(D3D11_CREATE_2D_SRV_FAILED); + return SG_RESOURCESTATE_FAILED; } - - if (0 == img->d3d11.srv) { - D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; - _sg_clear(&d3d11_srv_desc, sizeof(d3d11_srv_desc)); - d3d11_srv_desc.Format = img->d3d11.format; - d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; - d3d11_srv_desc.Texture3D.MipLevels = (UINT)img->cmn.num_mipmaps; - hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)img->d3d11.tex3d, &d3d11_srv_desc, &img->d3d11.srv); - if (!(SUCCEEDED(hr) && img->d3d11.srv)) { - SG_LOG("failed to create D3D11 resource view\n"); - return SG_RESOURCESTATE_FAILED; - } + } + } else { + // 3D texture - same procedure, first check if injected, than create non-injected + if (injected) { + img->d3d11.tex3d = (ID3D11Texture3D*) desc->d3d11_texture; + img->d3d11.srv = (ID3D11ShaderResourceView*) desc->d3d11_shader_resource_view; + if (img->d3d11.tex3d) { + _sg_d3d11_AddRef(img->d3d11.tex3d); + } else { + SOKOL_ASSERT(img->d3d11.srv); + _sg_d3d11_GetResource((ID3D11View*)img->d3d11.srv, (ID3D11Resource**)&img->d3d11.tex3d); + SOKOL_ASSERT(img->d3d11.tex3d); + } + if (img->d3d11.srv) { + _sg_d3d11_AddRef(img->d3d11.srv); } } - /* also need to create a separate MSAA render target texture? */ - if (msaa) { - D3D11_TEXTURE2D_DESC d3d11_tex_desc; + if (0 == img->d3d11.tex3d) { + D3D11_TEXTURE3D_DESC d3d11_tex_desc; _sg_clear(&d3d11_tex_desc, sizeof(d3d11_tex_desc)); d3d11_tex_desc.Width = (UINT)img->cmn.width; d3d11_tex_desc.Height = (UINT)img->cmn.height; - d3d11_tex_desc.MipLevels = 1; - d3d11_tex_desc.ArraySize = 1; + d3d11_tex_desc.Depth = (UINT)img->cmn.num_slices; + d3d11_tex_desc.MipLevels = (UINT)img->cmn.num_mipmaps; 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 = (UINT)img->cmn.sample_count; - d3d11_tex_desc.SampleDesc.Quality = (UINT)D3D11_STANDARD_MULTISAMPLE_PATTERN; - hr = _sg_d3d11_CreateTexture2D(_sg.d3d11.dev, &d3d11_tex_desc, NULL, &img->d3d11.texmsaa); - if (!(SUCCEEDED(hr) && img->d3d11.texmsaa)) { - SG_LOG("failed to create D3D11 texture 2D\n"); + if (img->cmn.render_target) { + d3d11_tex_desc.Usage = D3D11_USAGE_DEFAULT; + d3d11_tex_desc.BindFlags = D3D11_BIND_RENDER_TARGET; + d3d11_tex_desc.CPUAccessFlags = 0; + } else { + d3d11_tex_desc.Usage = _sg_d3d11_usage(img->cmn.usage); + d3d11_tex_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + d3d11_tex_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(img->cmn.usage); + } + if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) { + _SG_ERROR(D3D11_CREATE_3D_TEXTURE_UNSUPPORTED_PIXEL_FORMAT); + return SG_RESOURCESTATE_FAILED; + } + hr = _sg_d3d11_CreateTexture3D(_sg.d3d11.dev, &d3d11_tex_desc, init_data, &img->d3d11.tex3d); + if (!(SUCCEEDED(hr) && img->d3d11.tex3d)) { + _SG_ERROR(D3D11_CREATE_3D_TEXTURE_FAILED); + return SG_RESOURCESTATE_FAILED; + } + } + SOKOL_ASSERT(img->d3d11.tex3d); + img->d3d11.res = (ID3D11Resource*)img->d3d11.tex3d; + _sg_d3d11_AddRef(img->d3d11.res); + + if ((0 == img->d3d11.srv) && !msaa) { + D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; + _sg_clear(&d3d11_srv_desc, sizeof(d3d11_srv_desc)); + d3d11_srv_desc.Format = _sg_d3d11_srv_pixel_format(img->cmn.pixel_format); + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; + d3d11_srv_desc.Texture3D.MipLevels = (UINT)img->cmn.num_mipmaps; + hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, img->d3d11.res, &d3d11_srv_desc, &img->d3d11.srv); + if (!(SUCCEEDED(hr) && img->d3d11.srv)) { + _SG_ERROR(D3D11_CREATE_3D_SRV_FAILED); return SG_RESOURCESTATE_FAILED; } } + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_d3d11_discard_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + if (img->d3d11.tex2d) { + _sg_d3d11_Release(img->d3d11.tex2d); + } + if (img->d3d11.tex3d) { + _sg_d3d11_Release(img->d3d11.tex3d); + } + if (img->d3d11.res) { + _sg_d3d11_Release(img->d3d11.res); + } + if (img->d3d11.srv) { + _sg_d3d11_Release(img->d3d11.srv); + } +} - /* sampler state object, note D3D11 implements an internal shared-pool for sampler objects */ +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { + SOKOL_ASSERT(smp && desc); + SOKOL_ASSERT(0 == smp->d3d11.smp); + const bool injected = (0 != desc->d3d11_sampler); + if (injected) { + smp->d3d11.smp = (ID3D11SamplerState*)desc->d3d11_sampler; + _sg_d3d11_AddRef(smp->d3d11.smp); + } else { D3D11_SAMPLER_DESC d3d11_smp_desc; _sg_clear(&d3d11_smp_desc, sizeof(d3d11_smp_desc)); - 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) { + d3d11_smp_desc.Filter = _sg_d3d11_filter(desc->min_filter, desc->mag_filter, desc->mipmap_filter, desc->compare != SG_COMPAREFUNC_NEVER, desc->max_anisotropy); + d3d11_smp_desc.AddressU = _sg_d3d11_address_mode(desc->wrap_u); + d3d11_smp_desc.AddressV = _sg_d3d11_address_mode(desc->wrap_v); + d3d11_smp_desc.AddressW = _sg_d3d11_address_mode(desc->wrap_w); + d3d11_smp_desc.MipLODBias = 0.0f; // FIXME? + switch (desc->border_color) { case SG_BORDERCOLOR_TRANSPARENT_BLACK: - /* all 0.0f */ + // all 0.0f break; case SG_BORDERCOLOR_OPAQUE_WHITE: for (int i = 0; i < 4; i++) { @@ -9205,73 +9429,46 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const } break; default: - /* opaque black */ + // opaque black d3d11_smp_desc.BorderColor[3] = 1.0f; break; } - d3d11_smp_desc.MaxAnisotropy = img->cmn.max_anisotropy; - d3d11_smp_desc.ComparisonFunc = D3D11_COMPARISON_NEVER; + d3d11_smp_desc.MaxAnisotropy = desc->max_anisotropy; + d3d11_smp_desc.ComparisonFunc = _sg_d3d11_compare_func(desc->compare); d3d11_smp_desc.MinLOD = desc->min_lod; d3d11_smp_desc.MaxLOD = desc->max_lod; - hr = _sg_d3d11_CreateSamplerState(_sg.d3d11.dev, &d3d11_smp_desc, &img->d3d11.smp); - if (!(SUCCEEDED(hr) && img->d3d11.smp)) { - SG_LOG("failed to create D3D11 sampler state\n"); + HRESULT hr = _sg_d3d11_CreateSamplerState(_sg.d3d11.dev, &d3d11_smp_desc, &smp->d3d11.smp); + if (!(SUCCEEDED(hr) && smp->d3d11.smp)) { + _SG_ERROR(D3D11_CREATE_SAMPLER_STATE_FAILED); return SG_RESOURCESTATE_FAILED; } } return SG_RESOURCESTATE_VALID; } -_SOKOL_PRIVATE void _sg_d3d11_discard_image(_sg_image_t* img) { - SOKOL_ASSERT(img); - if (img->d3d11.tex2d) { - _sg_d3d11_Release(img->d3d11.tex2d); - } - if (img->d3d11.tex3d) { - _sg_d3d11_Release(img->d3d11.tex3d); - } - if (img->d3d11.texds) { - _sg_d3d11_Release(img->d3d11.texds); - } - if (img->d3d11.texmsaa) { - _sg_d3d11_Release(img->d3d11.texmsaa); - } - if (img->d3d11.srv) { - _sg_d3d11_Release(img->d3d11.srv); - } - if (img->d3d11.smp) { - _sg_d3d11_Release(img->d3d11.smp); +_SOKOL_PRIVATE void _sg_d3d11_discard_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp); + if (smp->d3d11.smp) { + _sg_d3d11_Release(smp->d3d11.smp); } } _SOKOL_PRIVATE bool _sg_d3d11_load_d3dcompiler_dll(void) { - /* on UWP, don't do anything (not tested) */ - #if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) - return true; - #else - /* load DLL on demand */ - if ((0 == _sg.d3d11.d3dcompiler_dll) && !_sg.d3d11.d3dcompiler_dll_load_failed) { - _sg.d3d11.d3dcompiler_dll = LoadLibraryA("d3dcompiler_47.dll"); - if (0 == _sg.d3d11.d3dcompiler_dll) { - /* don't attempt to load missing DLL in the future */ - SG_LOG("failed to load d3dcompiler_47.dll!\n"); - _sg.d3d11.d3dcompiler_dll_load_failed = true; - return false; - } - /* look up function pointers */ - _sg.d3d11.D3DCompile_func = (pD3DCompile)(void*) GetProcAddress(_sg.d3d11.d3dcompiler_dll, "D3DCompile"); - SOKOL_ASSERT(_sg.d3d11.D3DCompile_func); + if ((0 == _sg.d3d11.d3dcompiler_dll) && !_sg.d3d11.d3dcompiler_dll_load_failed) { + _sg.d3d11.d3dcompiler_dll = LoadLibraryA("d3dcompiler_47.dll"); + if (0 == _sg.d3d11.d3dcompiler_dll) { + // don't attempt to load missing DLL in the future + _SG_ERROR(D3D11_LOAD_D3DCOMPILER_47_DLL_FAILED); + _sg.d3d11.d3dcompiler_dll_load_failed = true; + return false; } - return 0 != _sg.d3d11.d3dcompiler_dll; - #endif + // look up function pointers + _sg.d3d11.D3DCompile_func = (pD3DCompile)(void*) GetProcAddress(_sg.d3d11.d3dcompiler_dll, "D3DCompile"); + SOKOL_ASSERT(_sg.d3d11.D3DCompile_func); + } + return 0 != _sg.d3d11.d3dcompiler_dll; } -#if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) -#define _sg_d3d11_D3DCompile D3DCompile -#else -#define _sg_d3d11_D3DCompile _sg.d3d11.D3DCompile_func -#endif - _SOKOL_PRIVATE ID3DBlob* _sg_d3d11_compile_shader(const sg_shader_stage_desc* stage_desc) { if (!_sg_d3d11_load_d3dcompiler_dll()) { return NULL; @@ -9279,24 +9476,28 @@ _SOKOL_PRIVATE ID3DBlob* _sg_d3d11_compile_shader(const sg_shader_stage_desc* st SOKOL_ASSERT(stage_desc->d3d11_target); ID3DBlob* output = NULL; 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 */ - 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_or_warnings); /* ppErrorMsgs */ + HRESULT hr = _sg.d3d11.D3DCompile_func( + stage_desc->source, // pSrcData + strlen(stage_desc->source), // SrcDataSize + NULL, // pSourceName + NULL, // pDefines + NULL, // pInclude + stage_desc->entry ? stage_desc->entry : "main", // pEntryPoint + stage_desc->d3d11_target, // pTarget + D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR | D3DCOMPILE_OPTIMIZATION_LEVEL3, // Flags1 + 0, // Flags2 + &output, // ppCode + &errors_or_warnings); // ppErrorMsgs + if (FAILED(hr)) { + _SG_ERROR(D3D11_SHADER_COMPILATION_FAILED); + } if (errors_or_warnings) { - SG_LOG((LPCSTR)_sg_d3d11_GetBufferPointer(errors_or_warnings)); + _SG_WARN(D3D11_SHADER_COMPILATION_OUTPUT); + _SG_LOGMSG(D3D11_SHADER_COMPILATION_OUTPUT, (LPCSTR)_sg_d3d11_GetBufferPointer(errors_or_warnings)); _sg_d3d11_Release(errors_or_warnings); errors_or_warnings = NULL; } if (FAILED(hr)) { - /* just in case, usually output is NULL here */ + // just in case, usually output is NULL here if (output) { _sg_d3d11_Release(output); output = NULL; @@ -9310,22 +9511,20 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_shader(_sg_shader_t* shd, cons SOKOL_ASSERT(!shd->d3d11.vs && !shd->d3d11.fs && !shd->d3d11.vs_blob); HRESULT hr; - _sg_shader_common_init(&shd->cmn, desc); - - /* copy vertex attribute semantic names and indices */ + // copy vertex attribute semantic names and indices for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { _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 */ + // shader stage uniform blocks and image slots for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_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++) { - const _sg_uniform_block_t* ub = &cmn_stage->uniform_blocks[ub_index]; + const _sg_shader_uniform_block_t* ub = &cmn_stage->uniform_blocks[ub_index]; - /* create a D3D constant buffer for each uniform block */ + // create a D3D constant buffer for each uniform block SOKOL_ASSERT(0 == d3d11_stage->cbufs[ub_index]); D3D11_BUFFER_DESC cb_desc; _sg_clear(&cb_desc, sizeof(cb_desc)); @@ -9334,7 +9533,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_shader(_sg_shader_t* shd, cons cb_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; hr = _sg_d3d11_CreateBuffer(_sg.d3d11.dev, &cb_desc, NULL, &d3d11_stage->cbufs[ub_index]); if (!(SUCCEEDED(hr) && d3d11_stage->cbufs[ub_index])) { - SG_LOG("failed to create D3D11 buffer\n"); + _SG_ERROR(D3D11_CREATE_CONSTANT_BUFFER_FAILED); return SG_RESOURCESTATE_FAILED; } } @@ -9344,14 +9543,13 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_shader(_sg_shader_t* shd, cons SIZE_T vs_length = 0, fs_length = 0; ID3DBlob* vs_blob = 0, *fs_blob = 0; if (desc->vs.bytecode.ptr && desc->fs.bytecode.ptr) { - /* create from shader byte code */ + // create from shader byte code vs_ptr = desc->vs.bytecode.ptr; fs_ptr = desc->fs.bytecode.ptr; vs_length = desc->vs.bytecode.size; fs_length = desc->fs.bytecode.size; - } - else { - /* compile from shader source code */ + } else { + // 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) { @@ -9363,13 +9561,13 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_shader(_sg_shader_t* shd, cons } 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 */ + // create the D3D vertex- and pixel-shader objects hr = _sg_d3d11_CreateVertexShader(_sg.d3d11.dev, vs_ptr, vs_length, NULL, &shd->d3d11.vs); bool vs_succeeded = SUCCEEDED(hr) && shd->d3d11.vs; hr = _sg_d3d11_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 */ + // need to store the vertex shader byte code, this is needed later in sg_create_pipeline if (vs_succeeded && fs_succeeded) { shd->d3d11.vs_blob_length = vs_length; shd->d3d11.vs_blob = _sg_malloc((size_t)vs_length); @@ -9417,79 +9615,77 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip, SOKOL_ASSERT(!pip->d3d11.il && !pip->d3d11.rs && !pip->d3d11.dss && !pip->d3d11.bs); pip->shader = shd; - _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->stencil.ref; - /* create input layout object */ + // create input layout object HRESULT hr; D3D11_INPUT_ELEMENT_DESC d3d11_comps[SG_MAX_VERTEX_ATTRIBUTES]; _sg_clear(d3d11_comps, sizeof(d3d11_comps)); int attr_index = 0; for (; 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) { + const sg_vertex_attr_state* a_state = &desc->layout.attrs[attr_index]; + if (a_state->format == SG_VERTEXFORMAT_INVALID) { break; } - SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); - const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[a_desc->buffer_index]; - const sg_vertex_step step_func = l_desc->step_func; - const int step_rate = l_desc->step_rate; + SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEX_BUFFERS); + const sg_vertex_buffer_layout_state* l_state = &desc->layout.buffers[a_state->buffer_index]; + const sg_vertex_step step_func = l_state->step_func; + const int step_rate = l_state->step_rate; D3D11_INPUT_ELEMENT_DESC* d3d11_comp = &d3d11_comps[attr_index]; d3d11_comp->SemanticName = _sg_strptr(&shd->d3d11.attrs[attr_index].sem_name); d3d11_comp->SemanticIndex = (UINT)shd->d3d11.attrs[attr_index].sem_index; - d3d11_comp->Format = _sg_d3d11_vertex_format(a_desc->format); - d3d11_comp->InputSlot = (UINT)a_desc->buffer_index; - d3d11_comp->AlignedByteOffset = (UINT)a_desc->offset; + d3d11_comp->Format = _sg_d3d11_vertex_format(a_state->format); + d3d11_comp->InputSlot = (UINT)a_state->buffer_index; + d3d11_comp->AlignedByteOffset = (UINT)a_state->offset; d3d11_comp->InputSlotClass = _sg_d3d11_input_classification(step_func); if (SG_VERTEXSTEP_PER_INSTANCE == step_func) { d3d11_comp->InstanceDataStepRate = (UINT)step_rate; pip->cmn.use_instanced_draw = true; } - pip->cmn.vertex_layout_valid[a_desc->buffer_index] = true; + pip->cmn.vertex_buffer_layout_active[a_state->buffer_index] = true; } - for (int layout_index = 0; layout_index < SG_MAX_SHADERSTAGE_BUFFERS; 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] = (UINT)l_desc->stride; - } - else { + for (int layout_index = 0; layout_index < SG_MAX_VERTEX_BUFFERS; layout_index++) { + if (pip->cmn.vertex_buffer_layout_active[layout_index]) { + const sg_vertex_buffer_layout_state* l_state = &desc->layout.buffers[layout_index]; + SOKOL_ASSERT(l_state->stride > 0); + pip->d3d11.vb_strides[layout_index] = (UINT)l_state->stride; + } else { pip->d3d11.vb_strides[layout_index] = 0; } } hr = _sg_d3d11_CreateInputLayout(_sg.d3d11.dev, - d3d11_comps, /* pInputElementDesc */ - (UINT)attr_index, /* NumElements */ - shd->d3d11.vs_blob, /* pShaderByteCodeWithInputSignature */ - shd->d3d11.vs_blob_length, /* BytecodeLength */ + d3d11_comps, // pInputElementDesc + (UINT)attr_index, // NumElements + shd->d3d11.vs_blob, // pShaderByteCodeWithInputSignature + shd->d3d11.vs_blob_length, // BytecodeLength &pip->d3d11.il); if (!(SUCCEEDED(hr) && pip->d3d11.il)) { - SG_LOG("failed to create D3D11 input layout\n"); + _SG_ERROR(D3D11_CREATE_INPUT_LAYOUT_FAILED); return SG_RESOURCESTATE_FAILED; } - /* create rasterizer state */ + // create rasterizer state D3D11_RASTERIZER_DESC rs_desc; _sg_clear(&rs_desc, sizeof(rs_desc)); rs_desc.FillMode = D3D11_FILL_SOLID; rs_desc.CullMode = _sg_d3d11_cull_mode(desc->cull_mode); rs_desc.FrontCounterClockwise = desc->face_winding == SG_FACEWINDING_CCW; - 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.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->sample_count > 1; rs_desc.AntialiasedLineEnable = FALSE; hr = _sg_d3d11_CreateRasterizerState(_sg.d3d11.dev, &rs_desc, &pip->d3d11.rs); if (!(SUCCEEDED(hr) && pip->d3d11.rs)) { - SG_LOG("failed to create D3D11 rasterizer state\n"); + _SG_ERROR(D3D11_CREATE_RASTERIZER_STATE_FAILED); return SG_RESOURCESTATE_FAILED; } - /* create depth-stencil state */ + // create depth-stencil state D3D11_DEPTH_STENCIL_DESC dss_desc; _sg_clear(&dss_desc, sizeof(dss_desc)); dss_desc.DepthEnable = TRUE; @@ -9510,11 +9706,11 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip, dss_desc.BackFace.StencilFunc = _sg_d3d11_compare_func(sb->compare); hr = _sg_d3d11_CreateDepthStencilState(_sg.d3d11.dev, &dss_desc, &pip->d3d11.dss); if (!(SUCCEEDED(hr) && pip->d3d11.dss)) { - SG_LOG("failed to create D3D11 depth stencil state\n"); + _SG_ERROR(D3D11_CREATE_DEPTH_STENCIL_STATE_FAILED); return SG_RESOURCESTATE_FAILED; } - /* create blend state */ + // create blend state D3D11_BLEND_DESC bs_desc; _sg_clear(&bs_desc, sizeof(bs_desc)); bs_desc.AlphaToCoverageEnable = desc->alpha_to_coverage_enabled; @@ -9544,10 +9740,9 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip, } hr = _sg_d3d11_CreateBlendState(_sg.d3d11.dev, &bs_desc, &pip->d3d11.bs); if (!(SUCCEEDED(hr) && pip->d3d11.bs)) { - SG_LOG("failed to create D3D11 blend state\n"); + _SG_ERROR(D3D11_CREATE_BLEND_STATE_FAILED); return SG_RESOURCESTATE_FAILED; } - return SG_RESOURCESTATE_VALID; } @@ -9571,94 +9766,110 @@ _SOKOL_PRIVATE void _sg_d3d11_discard_pipeline(_sg_pipeline_t* pip) { } } -_SOKOL_PRIVATE sg_resource_state _sg_d3d11_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** color_images, _sg_image_t** resolve_images, _sg_image_t* ds_img, const sg_pass_desc* desc) { SOKOL_ASSERT(pass && desc); - SOKOL_ASSERT(att_images && att_images[0]); + SOKOL_ASSERT(color_images && resolve_images); SOKOL_ASSERT(_sg.d3d11.dev); - _sg_pass_common_init(&pass->cmn, desc); - + // copy image pointers for (int i = 0; i < pass->cmn.num_color_atts; i++) { - const sg_pass_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)); + const sg_pass_attachment_desc* color_desc = &desc->color_attachments[i]; + _SOKOL_UNUSED(color_desc); + SOKOL_ASSERT(color_desc->image.id != SG_INVALID_ID); SOKOL_ASSERT(0 == pass->d3d11.color_atts[i].image); - pass->d3d11.color_atts[i].image = att_img; + SOKOL_ASSERT(color_images[i] && (color_images[i]->slot.id == color_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(color_images[i]->cmn.pixel_format)); + pass->d3d11.color_atts[i].image = color_images[i]; - /* create D3D11 render-target-view */ - const _sg_pass_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; + const sg_pass_attachment_desc* resolve_desc = &desc->resolve_attachments[i]; + if (resolve_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(0 == pass->d3d11.resolve_atts[i].image); + SOKOL_ASSERT(resolve_images[i] && (resolve_images[i]->slot.id == resolve_desc->image.id)); + SOKOL_ASSERT(color_images[i] && (color_images[i]->cmn.pixel_format == resolve_images[i]->cmn.pixel_format)); + pass->d3d11.resolve_atts[i].image = resolve_images[i]; + } + } + SOKOL_ASSERT(0 == pass->d3d11.ds_att.image); + const sg_pass_attachment_desc* ds_desc = &desc->depth_stencil_attachment; + if (ds_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(ds_img && (ds_img->slot.id == ds_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(ds_img->cmn.pixel_format)); + pass->d3d11.ds_att.image = ds_img; + } + + // create render-target views + for (int i = 0; i < pass->cmn.num_color_atts; i++) { + const _sg_pass_attachment_t* cmn_color_att = &pass->cmn.color_atts[i]; + const _sg_image_t* color_img = color_images[i]; + SOKOL_ASSERT(0 == pass->d3d11.color_atts[i].view.rtv); + const bool msaa = color_img->cmn.sample_count > 1; D3D11_RENDER_TARGET_VIEW_DESC d3d11_rtv_desc; _sg_clear(&d3d11_rtv_desc, 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.Format = _sg_d3d11_rtv_pixel_format(color_img->cmn.pixel_format); + if (color_img->cmn.type == SG_IMAGETYPE_2D) { + if (msaa) { d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS; - } - else { - d3d11_res = (ID3D11Resource*) att_img->d3d11.tex2d; + } else { d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; - d3d11_rtv_desc.Texture2D.MipSlice = (UINT)cmn_att->mip_level; + d3d11_rtv_desc.Texture2D.MipSlice = (UINT)cmn_color_att->mip_level; } - } - 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 = (UINT)cmn_att->mip_level; - d3d11_rtv_desc.Texture2DArray.FirstArraySlice = (UINT)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; + } else if ((color_img->cmn.type == SG_IMAGETYPE_CUBE) || (color_img->cmn.type == SG_IMAGETYPE_ARRAY)) { + if (msaa) { + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY; + d3d11_rtv_desc.Texture2DMSArray.FirstArraySlice = (UINT)cmn_color_att->slice; + d3d11_rtv_desc.Texture2DMSArray.ArraySize = 1; + } else { + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY; + d3d11_rtv_desc.Texture2DArray.MipSlice = (UINT)cmn_color_att->mip_level; + d3d11_rtv_desc.Texture2DArray.FirstArraySlice = (UINT)cmn_color_att->slice; + d3d11_rtv_desc.Texture2DArray.ArraySize = 1; + } + } else { + SOKOL_ASSERT(color_img->cmn.type == SG_IMAGETYPE_3D); + SOKOL_ASSERT(!msaa); d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D; - d3d11_rtv_desc.Texture3D.MipSlice = (UINT)cmn_att->mip_level; - d3d11_rtv_desc.Texture3D.FirstWSlice = (UINT)cmn_att->slice; + d3d11_rtv_desc.Texture3D.MipSlice = (UINT)cmn_color_att->mip_level; + d3d11_rtv_desc.Texture3D.FirstWSlice = (UINT)cmn_color_att->slice; d3d11_rtv_desc.Texture3D.WSize = 1; } - SOKOL_ASSERT(d3d11_res); - HRESULT hr = _sg_d3d11_CreateRenderTargetView(_sg.d3d11.dev, d3d11_res, &d3d11_rtv_desc, &pass->d3d11.color_atts[i].rtv); - if (!(SUCCEEDED(hr) && pass->d3d11.color_atts[i].rtv)) { - SG_LOG("failed to create D3D11 render target view\n"); + SOKOL_ASSERT(color_img->d3d11.res); + HRESULT hr = _sg_d3d11_CreateRenderTargetView(_sg.d3d11.dev, color_img->d3d11.res, &d3d11_rtv_desc, &pass->d3d11.color_atts[i].view.rtv); + if (!(SUCCEEDED(hr) && pass->d3d11.color_atts[i].view.rtv)) { + _SG_ERROR(D3D11_CREATE_RTV_FAILED); return SG_RESOURCESTATE_FAILED; } } - - /* optional depth-stencil image */ - 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_pass_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 */ + SOKOL_ASSERT(0 == pass->d3d11.ds_att.view.dsv); + if (ds_desc->image.id != SG_INVALID_ID) { + const _sg_pass_attachment_t* cmn_ds_att = &pass->cmn.ds_att; + const bool msaa = ds_img->cmn.sample_count > 1; D3D11_DEPTH_STENCIL_VIEW_DESC d3d11_dsv_desc; _sg_clear(&d3d11_dsv_desc, sizeof(d3d11_dsv_desc)); - 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_img->d3d11.texds; - SOKOL_ASSERT(d3d11_res); - HRESULT hr = _sg_d3d11_CreateDepthStencilView(_sg.d3d11.dev, d3d11_res, &d3d11_dsv_desc, &pass->d3d11.ds_att.dsv); - if (!(SUCCEEDED(hr) && pass->d3d11.ds_att.dsv)) { - SG_LOG("failed to create D3D11 depth stencil view\n"); + d3d11_dsv_desc.Format = _sg_d3d11_dsv_pixel_format(ds_img->cmn.pixel_format); + SOKOL_ASSERT(ds_img && ds_img->cmn.type != SG_IMAGETYPE_3D); + if (ds_img->cmn.type == SG_IMAGETYPE_2D) { + if (msaa) { + d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS; + } else { + d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; + d3d11_dsv_desc.Texture2D.MipSlice = (UINT)cmn_ds_att->mip_level; + } + } else if ((ds_img->cmn.type == SG_IMAGETYPE_CUBE) || (ds_img->cmn.type == SG_IMAGETYPE_ARRAY)) { + if (msaa) { + d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY; + d3d11_dsv_desc.Texture2DMSArray.FirstArraySlice = (UINT)cmn_ds_att->slice; + d3d11_dsv_desc.Texture2DMSArray.ArraySize = 1; + } else { + d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DARRAY; + d3d11_dsv_desc.Texture2DArray.MipSlice = (UINT)cmn_ds_att->mip_level; + d3d11_dsv_desc.Texture2DArray.FirstArraySlice = (UINT)cmn_ds_att->slice; + d3d11_dsv_desc.Texture2DArray.ArraySize = 1; + } + } + SOKOL_ASSERT(ds_img->d3d11.res); + HRESULT hr = _sg_d3d11_CreateDepthStencilView(_sg.d3d11.dev, ds_img->d3d11.res, &d3d11_dsv_desc, &pass->d3d11.ds_att.view.dsv); + if (!(SUCCEEDED(hr) && pass->d3d11.ds_att.view.dsv)) { + _SG_ERROR(D3D11_CREATE_DSV_FAILED); return SG_RESOURCESTATE_FAILED; } } @@ -9669,23 +9880,29 @@ _SOKOL_PRIVATE void _sg_d3d11_discard_pass(_sg_pass_t* pass) { SOKOL_ASSERT(pass); SOKOL_ASSERT(pass != _sg.d3d11.cur_pass); for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - if (pass->d3d11.color_atts[i].rtv) { - _sg_d3d11_Release(pass->d3d11.color_atts[i].rtv); + if (pass->d3d11.color_atts[i].view.rtv) { + _sg_d3d11_Release(pass->d3d11.color_atts[i].view.rtv); + } + if (pass->d3d11.resolve_atts[i].view.rtv) { + _sg_d3d11_Release(pass->d3d11.resolve_atts[i].view.rtv); } } - if (pass->d3d11.ds_att.dsv) { - _sg_d3d11_Release(pass->d3d11.ds_att.dsv); + if (pass->d3d11.ds_att.view.dsv) { + _sg_d3d11_Release(pass->d3d11.ds_att.view.dsv); } } _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_resolve_image(const _sg_pass_t* pass, int index) { + SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + return pass->d3d11.resolve_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; } @@ -9703,22 +9920,20 @@ _SOKOL_PRIVATE void _sg_d3d11_begin_pass(_sg_pass_t* pass, const sg_pass_action* _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.color_atts[i].rtv; + _sg.d3d11.cur_rtvs[i] = pass->d3d11.color_atts[i].view.rtv; if (_sg.d3d11.cur_rtvs[i]) { _sg.d3d11.num_rtvs++; } } - _sg.d3d11.cur_dsv = pass->d3d11.ds_att.dsv; - } - else { - /* render to default frame buffer */ + _sg.d3d11.cur_dsv = pass->d3d11.ds_att.view.dsv; + } else { + // render to default frame buffer _sg.d3d11.cur_pass = 0; _sg.d3d11.cur_pass_id.id = SG_INVALID_ID; _sg.d3d11.num_rtvs = 1; if (_sg.d3d11.rtv_cb) { _sg.d3d11.cur_rtvs[0] = (ID3D11RenderTargetView*) _sg.d3d11.rtv_cb(); - } - else { + } else { _sg.d3d11.cur_rtvs[0] = (ID3D11RenderTargetView*) _sg.d3d11.rtv_userdata_cb(_sg.d3d11.user_data); } for (int i = 1; i < SG_MAX_COLOR_ATTACHMENTS; i++) { @@ -9726,16 +9941,15 @@ _SOKOL_PRIVATE void _sg_d3d11_begin_pass(_sg_pass_t* pass, const sg_pass_action* } if (_sg.d3d11.dsv_cb) { _sg.d3d11.cur_dsv = (ID3D11DepthStencilView*) _sg.d3d11.dsv_cb(); - } - else { + } else { _sg.d3d11.cur_dsv = (ID3D11DepthStencilView*) _sg.d3d11.dsv_userdata_cb(_sg.d3d11.user_data); } SOKOL_ASSERT(_sg.d3d11.cur_rtvs[0] && _sg.d3d11.cur_dsv); } - /* apply the render-target- and depth-stencil-views */ + // apply the render-target- and depth-stencil-views _sg_d3d11_OMSetRenderTargets(_sg.d3d11.ctx, SG_MAX_COLOR_ATTACHMENTS, _sg.d3d11.cur_rtvs, _sg.d3d11.cur_dsv); - /* set viewport and scissor rect to cover whole screen */ + // set viewport and scissor rect to cover whole screen D3D11_VIEWPORT vp; _sg_clear(&vp, sizeof(vp)); vp.Width = (FLOAT) w; @@ -9749,25 +9963,25 @@ _SOKOL_PRIVATE void _sg_d3d11_begin_pass(_sg_pass_t* pass, const sg_pass_action* rect.bottom = h; _sg_d3d11_RSSetScissorRects(_sg.d3d11.ctx, 1, &rect); - /* perform clear action */ + // perform clear action for (int i = 0; i < _sg.d3d11.num_rtvs; i++) { - if (action->colors[i].action == SG_ACTION_CLEAR) { - _sg_d3d11_ClearRenderTargetView(_sg.d3d11.ctx, _sg.d3d11.cur_rtvs[i], &action->colors[i].value.r); + if (action->colors[i].load_action == SG_LOADACTION_CLEAR) { + _sg_d3d11_ClearRenderTargetView(_sg.d3d11.ctx, _sg.d3d11.cur_rtvs[i], &action->colors[i].clear_value.r); } } UINT ds_flags = 0; - if (action->depth.action == SG_ACTION_CLEAR) { + if (action->depth.load_action == SG_LOADACTION_CLEAR) { ds_flags |= D3D11_CLEAR_DEPTH; } - if (action->stencil.action == SG_ACTION_CLEAR) { + if (action->stencil.load_action == SG_LOADACTION_CLEAR) { ds_flags |= D3D11_CLEAR_STENCIL; } if ((0 != ds_flags) && _sg.d3d11.cur_dsv) { - _sg_d3d11_ClearDepthStencilView(_sg.d3d11.ctx, _sg.d3d11.cur_dsv, ds_flags, action->depth.value, action->stencil.value); + _sg_d3d11_ClearDepthStencilView(_sg.d3d11.ctx, _sg.d3d11.cur_dsv, ds_flags, action->depth.clear_value, action->stencil.clear_value); } } -/* D3D11CalcSubresource only exists for C++ */ +// D3D11CalcSubresource only exists for C++ _SOKOL_PRIVATE UINT _sg_d3d11_calcsubresource(UINT mip_slice, UINT array_slice, UINT mip_levels) { return mip_slice + array_slice * mip_levels; } @@ -9776,27 +9990,37 @@ _SOKOL_PRIVATE void _sg_d3d11_end_pass(void) { SOKOL_ASSERT(_sg.d3d11.in_pass && _sg.d3d11.ctx); _sg.d3d11.in_pass = false; - /* need to resolve MSAA render target into texture? */ + // need to resolve MSAA render attachments into texture? 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_pass_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_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((UINT)cmn_att->mip_level, (UINT)cmn_att->slice, (UINT)att_img->cmn.num_mipmaps); + const _sg_image_t* resolve_img = _sg.d3d11.cur_pass->d3d11.resolve_atts[i].image; + if (resolve_img) { + const _sg_image_t* color_img = _sg.d3d11.cur_pass->d3d11.color_atts[i].image; + const _sg_pass_attachment_t* cmn_color_att = &_sg.d3d11.cur_pass->cmn.color_atts[i]; + const _sg_pass_attachment_t* cmn_resolve_att = &_sg.d3d11.cur_pass->cmn.resolve_atts[i]; + SOKOL_ASSERT(resolve_img->slot.id == cmn_resolve_att->image_id.id); + SOKOL_ASSERT(color_img && (color_img->slot.id == cmn_color_att->image_id.id)); + SOKOL_ASSERT(color_img->cmn.sample_count > 1); + SOKOL_ASSERT(resolve_img->cmn.sample_count == 1); + const UINT src_subres = _sg_d3d11_calcsubresource( + (UINT)cmn_color_att->mip_level, + (UINT)cmn_color_att->slice, + (UINT)color_img->cmn.num_mipmaps); + const UINT dst_subres = _sg_d3d11_calcsubresource( + (UINT)cmn_resolve_att->mip_level, + (UINT)cmn_resolve_att->slice, + (UINT)resolve_img->cmn.num_mipmaps); _sg_d3d11_ResolveSubresource(_sg.d3d11.ctx, - (ID3D11Resource*) att_img->d3d11.tex2d, /* pDstResource */ - dst_subres, /* DstSubresource */ - (ID3D11Resource*) att_img->d3d11.texmsaa, /* pSrcResource */ - 0, /* SrcSubresource */ - att_img->d3d11.format); + resolve_img->d3d11.res, + dst_subres, + color_img->d3d11.res, + src_subres, + color_img->d3d11.format); } } } + _sg.d3d11.cur_pass = 0; _sg.d3d11.cur_pass_id.id = SG_INVALID_ID; _sg.d3d11.cur_pipeline = 0; @@ -9860,57 +10084,49 @@ _SOKOL_PRIVATE void _sg_d3d11_apply_bindings( _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) + _sg_image_t** fs_imgs, int num_fs_imgs, + _sg_sampler_t** vs_smps, int num_vs_smps, + _sg_sampler_t** fs_smps, int num_fs_smps) { SOKOL_ASSERT(pip); SOKOL_ASSERT(_sg.d3d11.ctx); SOKOL_ASSERT(_sg.d3d11.in_pass); - /* gather all the D3D11 resources into arrays */ + // gather all the D3D11 resources into arrays 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]; - ID3D11SamplerState* d3d11_vs_smps[SG_MAX_SHADERSTAGE_IMAGES]; - ID3D11ShaderResourceView* d3d11_fs_srvs[SG_MAX_SHADERSTAGE_IMAGES]; - ID3D11SamplerState* d3d11_fs_smps[SG_MAX_SHADERSTAGE_IMAGES]; - int i; - for (i = 0; i < num_vbs; i++) { + ID3D11Buffer* d3d11_vbs[SG_MAX_VERTEX_BUFFERS] = {0}; + UINT d3d11_vb_offsets[SG_MAX_VERTEX_BUFFERS] = {0}; + ID3D11ShaderResourceView* d3d11_vs_srvs[SG_MAX_SHADERSTAGE_IMAGES] = {0}; + ID3D11ShaderResourceView* d3d11_fs_srvs[SG_MAX_SHADERSTAGE_IMAGES] = {0}; + ID3D11SamplerState* d3d11_vs_smps[SG_MAX_SHADERSTAGE_SAMPLERS] = {0}; + ID3D11SamplerState* d3d11_fs_smps[SG_MAX_SHADERSTAGE_SAMPLERS] = {0}; + for (int i = 0; i < num_vbs; i++) { SOKOL_ASSERT(vbs[i]->d3d11.buf); d3d11_vbs[i] = vbs[i]->d3d11.buf; d3d11_vb_offsets[i] = (UINT)vb_offsets[i]; } - for (; i < SG_MAX_SHADERSTAGE_BUFFERS; i++) { - d3d11_vbs[i] = 0; - d3d11_vb_offsets[i] = 0; - } - for (i = 0; i < num_vs_imgs; i++) { + for (int 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; - } - 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++) { + for (int 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; } - for (; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { - d3d11_fs_srvs[i] = 0; - d3d11_fs_smps[i] = 0; + for (int i = 0; i < num_vs_smps; i++) { + SOKOL_ASSERT(vs_smps[i]->d3d11.smp); + d3d11_vs_smps[i] = vs_smps[i]->d3d11.smp; } - - _sg_d3d11_IASetVertexBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_BUFFERS, d3d11_vbs, pip->d3d11.vb_strides, d3d11_vb_offsets); + for (int i = 0; i < num_fs_smps; i++) { + SOKOL_ASSERT(fs_smps[i]->d3d11.smp); + d3d11_fs_smps[i] = fs_smps[i]->d3d11.smp; + } + _sg_d3d11_IASetVertexBuffers(_sg.d3d11.ctx, 0, SG_MAX_VERTEX_BUFFERS, d3d11_vbs, pip->d3d11.vb_strides, d3d11_vb_offsets); _sg_d3d11_IASetIndexBuffer(_sg.d3d11.ctx, d3d11_ib, pip->d3d11.index_format, (UINT)ib_offset); _sg_d3d11_VSSetShaderResources(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_vs_srvs); - _sg_d3d11_VSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_vs_smps); _sg_d3d11_PSSetShaderResources(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_fs_srvs); - _sg_d3d11_PSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_fs_smps); + _sg_d3d11_VSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_SAMPLERS, d3d11_vs_smps); + _sg_d3d11_PSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_SAMPLERS, d3d11_fs_smps); } _SOKOL_PRIVATE void _sg_d3d11_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { @@ -9929,16 +10145,13 @@ _SOKOL_PRIVATE void _sg_d3d11_draw(int base_element, int num_elements, int num_i if (_sg.d3d11.use_indexed_draw) { if (_sg.d3d11.use_instanced_draw) { _sg_d3d11_DrawIndexedInstanced(_sg.d3d11.ctx, (UINT)num_elements, (UINT)num_instances, (UINT)base_element, 0, 0); - } - else { + } else { _sg_d3d11_DrawIndexed(_sg.d3d11.ctx, (UINT)num_elements, (UINT)base_element, 0); } - } - else { + } else { if (_sg.d3d11.use_instanced_draw) { _sg_d3d11_DrawInstanced(_sg.d3d11.ctx, (UINT)num_elements, (UINT)num_instances, (UINT)base_element, 0); - } - else { + } else { _sg_d3d11_Draw(_sg.d3d11.ctx, (UINT)num_elements, (UINT)base_element); } } @@ -9958,7 +10171,7 @@ _SOKOL_PRIVATE void _sg_d3d11_update_buffer(_sg_buffer_t* buf, const sg_range* d memcpy(d3d11_msr.pData, data->ptr, data->size); _sg_d3d11_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0); } else { - SG_LOG("failed to map buffer while updating!\n"); + _SG_ERROR(D3D11_MAP_FOR_UPDATE_BUFFER_FAILED); } } @@ -9974,24 +10187,16 @@ _SOKOL_PRIVATE int _sg_d3d11_append_buffer(_sg_buffer_t* buf, const sg_range* da memcpy(dst_ptr, data->ptr, data->size); _sg_d3d11_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0); } else { - SG_LOG("failed to map buffer while appending!\n"); + _SG_ERROR(D3D11_MAP_FOR_APPEND_BUFFER_FAILED); } - /* NOTE: this alignment is a requirement from WebGPU, but we want identical behaviour across all backend */ + // NOTE: this alignment is a requirement from WebGPU, but we want identical behaviour across all backend return _sg_roundup((int)data->size, 4); } _SOKOL_PRIVATE void _sg_d3d11_update_image(_sg_image_t* img, const sg_image_data* data) { SOKOL_ASSERT(img && data); SOKOL_ASSERT(_sg.d3d11.ctx); - SOKOL_ASSERT(img->d3d11.tex2d || img->d3d11.tex3d); - ID3D11Resource* d3d11_res = 0; - if (img->d3d11.tex3d) { - d3d11_res = (ID3D11Resource*) img->d3d11.tex3d; - } - else { - d3d11_res = (ID3D11Resource*) img->d3d11.tex2d; - } - SOKOL_ASSERT(d3d11_res); + SOKOL_ASSERT(img->d3d11.res); const int num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6:1; const int num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.num_slices:1; UINT subres_index = 0; @@ -10001,20 +10206,19 @@ _SOKOL_PRIVATE void _sg_d3d11_update_image(_sg_image_t* img, const sg_image_data for (int slice_index = 0; slice_index < num_slices; slice_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->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 mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); const int src_pitch = _sg_row_pitch(img->cmn.pixel_format, mip_width, 1); const sg_range* subimg_data = &(data->subimage[face_index][mip_index]); const size_t slice_size = subimg_data->size / (size_t)num_slices; const size_t slice_offset = slice_size * (size_t)slice_index; const uint8_t* slice_ptr = ((const uint8_t*)subimg_data->ptr) + slice_offset; - hr = _sg_d3d11_Map(_sg.d3d11.ctx, d3d11_res, subres_index, D3D11_MAP_WRITE_DISCARD, 0, &d3d11_msr); + hr = _sg_d3d11_Map(_sg.d3d11.ctx, img->d3d11.res, subres_index, D3D11_MAP_WRITE_DISCARD, 0, &d3d11_msr); if (SUCCEEDED(hr)) { - /* FIXME: need to handle difference in depth-pitch for 3D textures as well! */ + // FIXME: need to handle difference in depth-pitch for 3D textures as well! if (src_pitch == (int)d3d11_msr.RowPitch) { memcpy(d3d11_msr.pData, slice_ptr, slice_size); - } - else { + } else { SOKOL_ASSERT(src_pitch < (int)d3d11_msr.RowPitch); const uint8_t* src_ptr = slice_ptr; uint8_t* dst_ptr = (uint8_t*) d3d11_msr.pData; @@ -10024,16 +10228,22 @@ _SOKOL_PRIVATE void _sg_d3d11_update_image(_sg_image_t* img, const sg_image_data dst_ptr += d3d11_msr.RowPitch; } } - _sg_d3d11_Unmap(_sg.d3d11.ctx, d3d11_res, subres_index); + _sg_d3d11_Unmap(_sg.d3d11.ctx, img->d3d11.res, subres_index); } else { - SG_LOG("failed to map texture!\n"); + _SG_ERROR(D3D11_MAP_FOR_UPDATE_IMAGE_FAILED); } } } } } -/*== METAL BACKEND IMPLEMENTATION ============================================*/ +// ███ ███ ███████ ████████ █████ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ ████ ██ █████ ██ ███████ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ██ ██ ███████ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>metal backend #elif defined(SOKOL_METAL) #if __has_feature(objc_arc) @@ -10044,31 +10254,56 @@ _SOKOL_PRIVATE void _sg_d3d11_update_image(_sg_image_t* img, const sg_image_data #define _SG_OBJC_RELEASE(obj) { [obj release]; obj = nil; } #endif -/*-- enum translation functions ----------------------------------------------*/ -_SOKOL_PRIVATE MTLLoadAction _sg_mtl_load_action(sg_action a) { +//-- enum translation functions ------------------------------------------------ +_SOKOL_PRIVATE MTLLoadAction _sg_mtl_load_action(sg_load_action a) { switch (a) { - case SG_ACTION_CLEAR: return MTLLoadActionClear; - case SG_ACTION_LOAD: return MTLLoadActionLoad; - case SG_ACTION_DONTCARE: return MTLLoadActionDontCare; - default: SOKOL_UNREACHABLE; return (MTLLoadAction)0; + case SG_LOADACTION_CLEAR: return MTLLoadActionClear; + case SG_LOADACTION_LOAD: return MTLLoadActionLoad; + case SG_LOADACTION_DONTCARE: return MTLLoadActionDontCare; + default: SOKOL_UNREACHABLE; return (MTLLoadAction)0; } } +_SOKOL_PRIVATE MTLStoreAction _sg_mtl_store_action(sg_store_action a, bool resolve) { + switch (a) { + case SG_STOREACTION_STORE: + if (resolve) { + return MTLStoreActionStoreAndMultisampleResolve; + } else { + return MTLStoreActionStore; + } + break; + case SG_STOREACTION_DONTCARE: + if (resolve) { + return MTLStoreActionMultisampleResolve; + } else { + return MTLStoreActionDontCare; + } + break; + default: SOKOL_UNREACHABLE; return (MTLStoreAction)0; + } +} + +_SOKOL_PRIVATE MTLResourceOptions _sg_mtl_resource_options_storage_mode_managed_or_shared(void) { + #if defined(_SG_TARGET_MACOS) + if (_sg.mtl.use_shared_storage_mode) { + return MTLResourceStorageModeShared; + } else { + return MTLResourceStorageModeManaged; + } + #else + // MTLResourceStorageModeManaged is not even defined on iOS SDK + return MTLResourceStorageModeShared; + #endif +} + _SOKOL_PRIVATE MTLResourceOptions _sg_mtl_buffer_resource_options(sg_usage usg) { switch (usg) { case SG_USAGE_IMMUTABLE: - #if defined(_SG_TARGET_MACOS) - return MTLResourceStorageModeManaged; - #else - return MTLResourceStorageModeShared; - #endif + return _sg_mtl_resource_options_storage_mode_managed_or_shared(); case SG_USAGE_DYNAMIC: case SG_USAGE_STREAM: - #if defined(_SG_TARGET_MACOS) - return MTLResourceCPUCacheModeWriteCombined|MTLResourceStorageModeManaged; - #else - return MTLResourceCPUCacheModeWriteCombined|MTLResourceStorageModeShared; - #endif + return MTLResourceCPUCacheModeWriteCombined | _sg_mtl_resource_options_storage_mode_managed_or_shared(); default: SOKOL_UNREACHABLE; return 0; @@ -10100,6 +10335,8 @@ _SOKOL_PRIVATE MTLVertexFormat _sg_mtl_vertex_format(sg_vertex_format fmt) { case SG_VERTEXFORMAT_SHORT4N: return MTLVertexFormatShort4Normalized; case SG_VERTEXFORMAT_USHORT4N: return MTLVertexFormatUShort4Normalized; case SG_VERTEXFORMAT_UINT10_N2: return MTLVertexFormatUInt1010102Normalized; + case SG_VERTEXFORMAT_HALF2: return MTLVertexFormatHalf2; + case SG_VERTEXFORMAT_HALF4: return MTLVertexFormatHalf4; default: SOKOL_UNREACHABLE; return (MTLVertexFormat)0; } } @@ -10139,6 +10376,7 @@ _SOKOL_PRIVATE MTLPixelFormat _sg_mtl_pixel_format(sg_pixel_format fmt) { case SG_PIXELFORMAT_RG16SI: return MTLPixelFormatRG16Sint; case SG_PIXELFORMAT_RG16F: return MTLPixelFormatRG16Float; case SG_PIXELFORMAT_RGBA8: return MTLPixelFormatRGBA8Unorm; + case SG_PIXELFORMAT_SRGB8A8: return MTLPixelFormatRGBA8Unorm_sRGB; case SG_PIXELFORMAT_RGBA8SN: return MTLPixelFormatRGBA8Snorm; case SG_PIXELFORMAT_RGBA8UI: return MTLPixelFormatRGBA8Uint; case SG_PIXELFORMAT_RGBA8SI: return MTLPixelFormatRGBA8Sint; @@ -10317,22 +10555,29 @@ _SOKOL_PRIVATE bool _sg_mtl_is_pvrtc(sg_pixel_format fmt) { } _SOKOL_PRIVATE MTLSamplerAddressMode _sg_mtl_address_mode(sg_wrap w) { + if (_sg.features.image_clamp_to_border) { + if (@available(macOS 12.0, iOS 14.0, *)) { + // border color feature available + switch (w) { + case SG_WRAP_REPEAT: return MTLSamplerAddressModeRepeat; + case SG_WRAP_CLAMP_TO_EDGE: return MTLSamplerAddressModeClampToEdge; + case SG_WRAP_CLAMP_TO_BORDER: return MTLSamplerAddressModeClampToBorderColor; + case SG_WRAP_MIRRORED_REPEAT: return MTLSamplerAddressModeMirrorRepeat; + default: SOKOL_UNREACHABLE; return (MTLSamplerAddressMode)0; + } + } + } + // fallthrough: clamp to border no supported switch (w) { case SG_WRAP_REPEAT: return MTLSamplerAddressModeRepeat; case SG_WRAP_CLAMP_TO_EDGE: return MTLSamplerAddressModeClampToEdge; - #if defined(_SG_TARGET_MACOS) - case SG_WRAP_CLAMP_TO_BORDER: return MTLSamplerAddressModeClampToBorderColor; - #else - /* clamp-to-border not supported on iOS, fall back to clamp-to-edge */ case SG_WRAP_CLAMP_TO_BORDER: return MTLSamplerAddressModeClampToEdge; - #endif case SG_WRAP_MIRRORED_REPEAT: return MTLSamplerAddressModeMirrorRepeat; default: SOKOL_UNREACHABLE; return (MTLSamplerAddressMode)0; } } -#if defined(_SG_TARGET_MACOS) -_SOKOL_PRIVATE MTLSamplerBorderColor _sg_mtl_border_color(sg_border_color c) { +_SOKOL_PRIVATE API_AVAILABLE(ios(14.0), macos(12.0)) MTLSamplerBorderColor _sg_mtl_border_color(sg_border_color c) { switch (c) { case SG_BORDERCOLOR_TRANSPARENT_BLACK: return MTLSamplerBorderColorTransparentBlack; case SG_BORDERCOLOR_OPAQUE_BLACK: return MTLSamplerBorderColorOpaqueBlack; @@ -10340,49 +10585,42 @@ _SOKOL_PRIVATE MTLSamplerBorderColor _sg_mtl_border_color(sg_border_color c) { default: SOKOL_UNREACHABLE; return (MTLSamplerBorderColor)0; } } -#endif _SOKOL_PRIVATE MTLSamplerMinMagFilter _sg_mtl_minmag_filter(sg_filter f) { switch (f) { case SG_FILTER_NEAREST: - case SG_FILTER_NEAREST_MIPMAP_NEAREST: - case SG_FILTER_NEAREST_MIPMAP_LINEAR: return MTLSamplerMinMagFilterNearest; case SG_FILTER_LINEAR: - case SG_FILTER_LINEAR_MIPMAP_NEAREST: - case SG_FILTER_LINEAR_MIPMAP_LINEAR: return MTLSamplerMinMagFilterLinear; default: SOKOL_UNREACHABLE; return (MTLSamplerMinMagFilter)0; } } -_SOKOL_PRIVATE MTLSamplerMipFilter _sg_mtl_mip_filter(sg_filter f) { +_SOKOL_PRIVATE MTLSamplerMipFilter _sg_mtl_mipmap_filter(sg_filter f) { switch (f) { - case SG_FILTER_NEAREST: - case SG_FILTER_LINEAR: + case SG_FILTER_NONE: return MTLSamplerMipFilterNotMipmapped; - case SG_FILTER_NEAREST_MIPMAP_NEAREST: - case SG_FILTER_LINEAR_MIPMAP_NEAREST: + case SG_FILTER_NEAREST: return MTLSamplerMipFilterNearest; - case SG_FILTER_NEAREST_MIPMAP_LINEAR: - case SG_FILTER_LINEAR_MIPMAP_LINEAR: + case SG_FILTER_LINEAR: return MTLSamplerMipFilterLinear; default: SOKOL_UNREACHABLE; return (MTLSamplerMipFilter)0; } } -/*-- a pool for all Metal resource objects, with deferred release queue -------*/ - +//-- a pool for all Metal resource objects, with deferred release queue --------- _SOKOL_PRIVATE void _sg_mtl_init_pool(const sg_desc* desc) { _sg.mtl.idpool.num_slots = 2 * ( 2 * desc->buffer_pool_size + - 5 * desc->image_pool_size + + 4 * desc->image_pool_size + + 1 * desc->sampler_pool_size + 4 * desc->shader_pool_size + 2 * desc->pipeline_pool_size + - desc->pass_pool_size + desc->pass_pool_size + + 128 ); _sg.mtl.idpool.pool = [NSMutableArray arrayWithCapacity:(NSUInteger)_sg.mtl.idpool.num_slots]; _SG_OBJC_RETAIN(_sg.mtl.idpool.pool); @@ -10391,17 +10629,14 @@ _SOKOL_PRIVATE void _sg_mtl_init_pool(const sg_desc* desc) { [_sg.mtl.idpool.pool addObject:null]; } SOKOL_ASSERT([_sg.mtl.idpool.pool count] == (NSUInteger)_sg.mtl.idpool.num_slots); - /* a queue of currently free slot indices */ + // a queue of currently free slot indices _sg.mtl.idpool.free_queue_top = 0; _sg.mtl.idpool.free_queue = (int*)_sg_malloc_clear((size_t)_sg.mtl.idpool.num_slots * sizeof(int)); - /* pool slot 0 is reserved! */ + // pool slot 0 is reserved! for (int i = _sg.mtl.idpool.num_slots-1; i >= 1; i--) { _sg.mtl.idpool.free_queue[_sg.mtl.idpool.free_queue_top++] = i; } - /* a circular queue which holds release items (frame index - when a resource is to be released, and the resource's - pool index - */ + // a circular queue which holds release items (frame index when a resource is to be released, and the resource's pool index _sg.mtl.idpool.release_queue_front = 0; _sg.mtl.idpool.release_queue_back = 0; _sg.mtl.idpool.release_queue = (_sg_mtl_release_item_t*)_sg_malloc_clear((size_t)_sg.mtl.idpool.num_slots * sizeof(_sg_mtl_release_item_t)); @@ -10417,7 +10652,7 @@ _SOKOL_PRIVATE void _sg_mtl_destroy_pool(void) { _SG_OBJC_RELEASE(_sg.mtl.idpool.pool); } -/* get a new free resource pool slot */ +// get a new free resource pool slot _SOKOL_PRIVATE int _sg_mtl_alloc_pool_slot(void) { SOKOL_ASSERT(_sg.mtl.idpool.free_queue_top > 0); const int slot_index = _sg.mtl.idpool.free_queue[--_sg.mtl.idpool.free_queue_top]; @@ -10425,14 +10660,14 @@ _SOKOL_PRIVATE int _sg_mtl_alloc_pool_slot(void) { return slot_index; } -/* put a free resource pool slot back into the free-queue */ +// put a free resource pool slot back into the free-queue _SOKOL_PRIVATE void _sg_mtl_free_pool_slot(int slot_index) { SOKOL_ASSERT(_sg.mtl.idpool.free_queue_top < _sg.mtl.idpool.num_slots); SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); _sg.mtl.idpool.free_queue[_sg.mtl.idpool.free_queue_top++] = slot_index; } -/* add an MTLResource to the pool, return pool index or 0 if input was 'nil' */ +// add an MTLResource to the pool, return pool index or 0 if input was 'nil' _SOKOL_PRIVATE int _sg_mtl_add_resource(id res) { if (nil == res) { return _SG_MTL_INVALID_SLOT_INDEX; @@ -10457,10 +10692,10 @@ _SOKOL_PRIVATE void _sg_mtl_release_resource(uint32_t frame_index, int slot_inde SOKOL_ASSERT([NSNull null] != _sg.mtl.idpool.pool[(NSUInteger)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 */ + // wrap-around _sg.mtl.idpool.release_queue_front = 0; } - /* release queue full? */ + // release queue full? SOKOL_ASSERT(_sg.mtl.idpool.release_queue_front != _sg.mtl.idpool.release_queue_back); SOKOL_ASSERT(0 == _sg.mtl.idpool.release_queue[release_index].frame_index); const uint32_t safe_to_release_frame_index = frame_index + SG_NUM_INFLIGHT_FRAMES + 1; @@ -10468,29 +10703,28 @@ _SOKOL_PRIVATE void _sg_mtl_release_resource(uint32_t frame_index, int slot_inde _sg.mtl.idpool.release_queue[release_index].slot_index = slot_index; } -/* run garbage-collection pass on all resources in the release-queue */ +// run garbage-collection pass on all resources in the release-queue _SOKOL_PRIVATE void _sg_mtl_garbage_collect(uint32_t frame_index) { while (_sg.mtl.idpool.release_queue_back != _sg.mtl.idpool.release_queue_front) { if (frame_index < _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].frame_index) { - /* don't need to check further, release-items past this are too young */ + // don't need to check further, release-items past this are too young break; } - /* safe to release this resource */ + // safe to release this resource const int 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)); - /* note: the NSMutableArray takes ownership of its items, assigning an NSNull object will - release the object, no matter if using ARC or not - */ + // note: the NSMutableArray takes ownership of its items, assigning an NSNull object will + // release the object, no matter if using ARC or not SOKOL_ASSERT(_sg.mtl.idpool.pool[(NSUInteger)slot_index] != [NSNull null]); _sg.mtl.idpool.pool[(NSUInteger)slot_index] = [NSNull null]; - /* put the now free pool index back on the free queue */ + // 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 */ + // reset the release queue slot and advance the back index _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].frame_index = 0; _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].slot_index = _SG_MTL_INVALID_SLOT_INDEX; _sg.mtl.idpool.release_queue_back++; if (_sg.mtl.idpool.release_queue_back >= _sg.mtl.idpool.num_slots) { - /* wrap-around */ + // wrap-around _sg.mtl.idpool.release_queue_back = 0; } } @@ -10500,64 +10734,11 @@ _SOKOL_PRIVATE id _sg_mtl_id(int slot_index) { return _sg.mtl.idpool.pool[(NSUInteger)slot_index]; } -_SOKOL_PRIVATE void _sg_mtl_init_sampler_cache(const sg_desc* desc) { - 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 */ -_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, (int)_sg_smpcache_sampler(&_sg.mtl.sampler_cache, i)); - } - _sg_smpcache_discard(&_sg.mtl.sampler_cache); -} - -/* - create and add an MTLSamplerStateObject and return its resource pool index, - reuse identical sampler state if one exists -*/ -_SOKOL_PRIVATE int _sg_mtl_create_sampler(id mtl_device, const sg_image_desc* img_desc) { - SOKOL_ASSERT(img_desc); - int index = _sg_smpcache_find_item(&_sg.mtl.sampler_cache, img_desc); - if (index >= 0) { - /* reuse existing sampler */ - return (int)_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); - int sampler_handle = _sg_mtl_add_resource(mtl_sampler); - _SG_OBJC_RELEASE(mtl_sampler); - _sg_smpcache_add_item(&_sg.mtl.sampler_cache, img_desc, (uintptr_t)sampler_handle); - return sampler_handle; - } -} - _SOKOL_PRIVATE void _sg_mtl_clear_state_cache(void) { _sg_clear(&_sg.mtl.state_cache, sizeof(_sg.mtl.state_cache)); } -/* https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf */ +// https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf _SOKOL_PRIVATE void _sg_mtl_init_caps(void) { #if defined(_SG_TARGET_MACOS) _sg.backend = SG_BACKEND_METAL_MACOS; @@ -10568,20 +10749,26 @@ _SOKOL_PRIVATE void _sg_mtl_init_caps(void) { _sg.backend = SG_BACKEND_METAL_IOS; #endif #endif - _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; - #if defined(_SG_TARGET_MACOS) - _sg.features.image_clamp_to_border = true; - #else - _sg.features.image_clamp_to_border = false; - #endif _sg.features.mrt_independent_blend_state = true; _sg.features.mrt_independent_write_mask = true; + _sg.features.image_clamp_to_border = false; + #if (MAC_OS_X_VERSION_MAX_ALLOWED >= 120000) || (__IPHONE_OS_VERSION_MAX_ALLOWED >= 140000) + if (@available(macOS 12.0, iOS 14.0, *)) { + _sg.features.image_clamp_to_border = [_sg.mtl.device supportsFamily:MTLGPUFamilyApple7] + || [_sg.mtl.device supportsFamily:MTLGPUFamilyApple8] + || [_sg.mtl.device supportsFamily:MTLGPUFamilyMac2]; + #if (MAC_OS_X_VERSION_MAX_ALLOWED >= 130000) || (__IPHONE_OS_VERSION_MAX_ALLOWED >= 160000) + if (!_sg.features.image_clamp_to_border) { + if (@available(macOS 13.0, iOS 16.0, *)) { + _sg.features.image_clamp_to_border = [_sg.mtl.device supportsFamily:MTLGPUFamilyMetal3]; + } + } + #endif + } + #endif + #if defined(_SG_TARGET_MACOS) _sg.limits.max_image_size_2d = 16 * 1024; _sg.limits.max_image_size_cube = 16 * 1024; @@ -10589,7 +10776,7 @@ _SOKOL_PRIVATE void _sg_mtl_init_caps(void) { _sg.limits.max_image_size_array = 16 * 1024; _sg.limits.max_image_array_layers = 2 * 1024; #else - /* newer iOS devices support 16k textures */ + // FIXME: newer iOS devices support 16k textures _sg.limits.max_image_size_2d = 8 * 1024; _sg.limits.max_image_size_cube = 8 * 1024; _sg.limits.max_image_size_3d = 2 * 1024; @@ -10634,6 +10821,7 @@ _SOKOL_PRIVATE void _sg_mtl_init_caps(void) { _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_all(&_sg.formats[SG_PIXELFORMAT_SRGB8A8]); _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]); _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]); _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]); @@ -10699,16 +10887,15 @@ _SOKOL_PRIVATE void _sg_mtl_init_caps(void) { #endif } -/*-- main Metal backend state and functions ----------------------------------*/ +//-- main Metal backend state and functions ------------------------------------ _SOKOL_PRIVATE void _sg_mtl_setup_backend(const sg_desc* desc) { - /* assume already zero-initialized */ + // assume already zero-initialized SOKOL_ASSERT(desc); SOKOL_ASSERT(desc->context.metal.device); SOKOL_ASSERT(desc->context.metal.renderpass_descriptor_cb || desc->context.metal.renderpass_descriptor_userdata_cb); SOKOL_ASSERT(desc->context.metal.drawable_cb || desc->context.metal.drawable_userdata_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->context.metal.renderpass_descriptor_cb; @@ -10721,26 +10908,48 @@ _SOKOL_PRIVATE void _sg_mtl_setup_backend(const sg_desc* desc) { _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]; + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { _sg.mtl.uniform_buffers[i] = [_sg.mtl.device newBufferWithLength:(NSUInteger)_sg.mtl.ub_size options:MTLResourceCPUCacheModeWriteCombined|MTLResourceStorageModeShared ]; + #if defined(SOKOL_DEBUG) + _sg.mtl.uniform_buffers[i].label = [NSString stringWithFormat:@"sg-uniform-buffer.%d", i]; + #endif + } + + if (desc->mtl_force_managed_storage_mode) { + _sg.mtl.use_shared_storage_mode = false; + } else if (@available(macOS 10.15, iOS 13.0, *)) { + // on Intel Macs, always use managed resources even though the + // device says it supports unified memory (because of texture restrictions) + const bool is_apple_gpu = [_sg.mtl.device supportsFamily:MTLGPUFamilyApple1]; + if (!is_apple_gpu) { + _sg.mtl.use_shared_storage_mode = false; + } else { + _sg.mtl.use_shared_storage_mode = true; + } + } else { + #if defined(_SG_TARGET_MACOS) + _sg.mtl.use_shared_storage_mode = false; + #else + _sg.mtl.use_shared_storage_mode = true; + #endif } _sg_mtl_init_caps(); } _SOKOL_PRIVATE void _sg_mtl_discard_backend(void) { SOKOL_ASSERT(_sg.mtl.valid); - /* wait for the last frame to finish */ + // 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); } - /* semaphore must be "relinquished" before destruction */ + // 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; @@ -10751,9 +10960,8 @@ _SOKOL_PRIVATE void _sg_mtl_discard_backend(void) { for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { _SG_OBJC_RELEASE(_sg.mtl.uniform_buffers[i]); } - /* NOTE: MTLCommandBuffer and MTLRenderCommandEncoder are auto-released */ + // NOTE: MTLCommandBuffer and MTLRenderCommandEncoder are auto-released _sg.mtl.cmd_buffer = nil; - _sg.mtl.present_cmd_buffer = nil; _sg.mtl.cmd_encoder = nil; } @@ -10773,10 +10981,7 @@ _SOKOL_PRIVATE void _sg_mtl_bind_uniform_buffers(void) { _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() - */ + // 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(); } @@ -10791,7 +10996,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_context(_sg_context_t* ctx) { _SOKOL_PRIVATE void _sg_mtl_discard_context(_sg_context_t* ctx) { SOKOL_ASSERT(ctx); _SOKOL_UNUSED(ctx); - /* empty */ + // empty } _SOKOL_PRIVATE void _sg_mtl_activate_context(_sg_context_t* ctx) { @@ -10801,7 +11006,6 @@ _SOKOL_PRIVATE void _sg_mtl_activate_context(_sg_context_t* ctx) { _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { SOKOL_ASSERT(buf && desc); - _sg_buffer_common_init(&buf->cmn, desc); const bool injected = (0 != desc->mtl_buffers[0]); MTLResourceOptions mtl_options = _sg_mtl_buffer_resource_options(buf->cmn.usage); for (int slot = 0; slot < buf->cmn.num_slots; slot++) { @@ -10809,16 +11013,23 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_buffer(_sg_buffer_t* buf, const if (injected) { SOKOL_ASSERT(desc->mtl_buffers[slot]); mtl_buf = (__bridge id) desc->mtl_buffers[slot]; - } - else { + } else { if (buf->cmn.usage == SG_USAGE_IMMUTABLE) { SOKOL_ASSERT(desc->data.ptr); mtl_buf = [_sg.mtl.device newBufferWithBytes:desc->data.ptr length:(NSUInteger)buf->cmn.size options:mtl_options]; - } - else { + } else { mtl_buf = [_sg.mtl.device newBufferWithLength:(NSUInteger)buf->cmn.size options:mtl_options]; } + if (nil == mtl_buf) { + _SG_ERROR(METAL_CREATE_BUFFER_FAILED); + return SG_RESOURCESTATE_FAILED; + } } + #if defined(SOKOL_DEBUG) + if (desc->label) { + mtl_buf.label = [NSString stringWithFormat:@"%s.%d", desc->label, slot]; + } + #endif buf->mtl.buf[slot] = _sg_mtl_add_resource(mtl_buf); _SG_OBJC_RELEASE(mtl_buf); } @@ -10828,7 +11039,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_buffer(_sg_buffer_t* buf, const _SOKOL_PRIVATE void _sg_mtl_discard_buffer(_sg_buffer_t* buf) { SOKOL_ASSERT(buf); for (int slot = 0; slot < buf->cmn.num_slots; slot++) { - /* it's valid to call release resource with '0' */ + // it's valid to call release resource with '0' _sg_mtl_release_resource(_sg.mtl.frame_index, buf->mtl.buf[slot]); } } @@ -10841,9 +11052,9 @@ _SOKOL_PRIVATE void _sg_mtl_copy_image_data(const _sg_image_t* img, __unsafe_unr SOKOL_ASSERT(data->subimage[face_index][mip_index].ptr); SOKOL_ASSERT(data->subimage[face_index][mip_index].size > 0); const uint8_t* data_ptr = (const uint8_t*)data->subimage[face_index][mip_index].ptr; - 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 and bytesPerImage must be 0 */ + const int mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + const int mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); + // special case PVRTC formats: bytePerRow and bytesPerImage must be 0 int bytes_per_row = 0; int bytes_per_slice = 0; if (!_sg_mtl_is_pvrtc(img->cmn.pixel_format)) { @@ -10857,13 +11068,11 @@ _SOKOL_PRIVATE void _sg_mtl_copy_image_data(const _sg_image_t* img, __unsafe_unr MTLRegion region; int bytes_per_image; if (img->cmn.type == SG_IMAGETYPE_3D) { - const int mip_depth = _sg_max(img->cmn.num_slices >> mip_index, 1); + const int mip_depth = _sg_miplevel_dim(img->cmn.num_slices, mip_index); region = MTLRegionMake3D(0, 0, 0, (NSUInteger)mip_width, (NSUInteger)mip_height, (NSUInteger)mip_depth); bytes_per_image = bytes_per_slice; - /* FIXME: apparently the minimal bytes_per_image size for 3D texture - is 4 KByte... somehow need to handle this */ - } - else { + // FIXME: apparently the minimal bytes_per_image size for 3D texture is 4 KByte... somehow need to handle this + } else { region = MTLRegionMake2D(0, 0, (NSUInteger)mip_width, (NSUInteger)mip_height); bytes_per_image = 0; } @@ -10879,168 +11088,103 @@ _SOKOL_PRIVATE void _sg_mtl_copy_image_data(const _sg_image_t* img, __unsafe_unr bytesPerRow:(NSUInteger)bytes_per_row bytesPerImage:(NSUInteger)bytes_per_image]; } - } - } -} - -/* - FIXME: METAL RESOURCE STORAGE MODE FOR macOS AND iOS - - For immutable textures on macOS, the recommended procedure is to create - a MTLStorageModeManaged texture with the immutable content first, - and then use the GPU to blit the content into a MTLStorageModePrivate - texture before the first use. - - On iOS use the same one-time-blit procedure, but from a - MTLStorageModeShared to a MTLStorageModePrivate texture. - - It probably makes sense to handle this in a separate 'resource manager' - with a recycable pool of blit-source-textures? -*/ + } + } +} -/* initialize MTLTextureDescritor with common attributes */ +// 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->cmn.type); mtl_desc.pixelFormat = _sg_mtl_pixel_format(img->cmn.pixel_format); if (MTLPixelFormatInvalid == mtl_desc.pixelFormat) { - SG_LOG("Unsupported texture pixel format!\n"); + _SG_ERROR(METAL_TEXTURE_FORMAT_NOT_SUPPORTED); return false; } mtl_desc.width = (NSUInteger)img->cmn.width; mtl_desc.height = (NSUInteger)img->cmn.height; if (SG_IMAGETYPE_3D == img->cmn.type) { mtl_desc.depth = (NSUInteger)img->cmn.num_slices; - } - else { + } else { mtl_desc.depth = 1; } mtl_desc.mipmapLevelCount = (NSUInteger)img->cmn.num_mipmaps; if (SG_IMAGETYPE_ARRAY == img->cmn.type) { mtl_desc.arrayLength = (NSUInteger)img->cmn.num_slices; - } - else { + } else { mtl_desc.arrayLength = 1; } mtl_desc.usage = MTLTextureUsageShaderRead; - if (img->cmn.render_target) { - mtl_desc.usage |= MTLTextureUsageRenderTarget; - } MTLResourceOptions res_options = 0; if (img->cmn.usage != SG_USAGE_IMMUTABLE) { res_options |= MTLResourceCPUCacheModeWriteCombined; } - #if defined(_SG_TARGET_MACOS) - /* macOS: use managed textures */ - res_options |= MTLResourceStorageModeManaged; - #else - /* iOS: use CPU/GPU shared memory */ - res_options |= MTLResourceStorageModeShared; - #endif + res_options |= _sg_mtl_resource_options_storage_mode_managed_or_shared(); mtl_desc.resourceOptions = res_options; return true; } -/* initialize MTLTextureDescritor with rendertarget attributes */ +// initialize MTLTextureDescritor with rendertarget attributes _SOKOL_PRIVATE void _sg_mtl_init_texdesc_rt(MTLTextureDescriptor* mtl_desc, _sg_image_t* img) { SOKOL_ASSERT(img->cmn.render_target); _SOKOL_UNUSED(img); - /* render targets are only visible to the GPU */ - mtl_desc.resourceOptions = MTLResourceStorageModePrivate; - /* non-MSAA render targets are shader-readable */ mtl_desc.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget; + mtl_desc.resourceOptions = MTLResourceStorageModePrivate; } -/* initialize MTLTextureDescritor with MSAA attributes */ +// initialize MTLTextureDescritor with MSAA attributes _SOKOL_PRIVATE void _sg_mtl_init_texdesc_rt_msaa(MTLTextureDescriptor* mtl_desc, _sg_image_t* img) { SOKOL_ASSERT(img->cmn.sample_count > 1); - /* render targets are only visible to the GPU */ - mtl_desc.resourceOptions = MTLResourceStorageModePrivate; - /* MSAA render targets are not shader-readable (instead they are resolved) */ mtl_desc.usage = MTLTextureUsageRenderTarget; + mtl_desc.resourceOptions = MTLResourceStorageModePrivate; mtl_desc.textureType = MTLTextureType2DMultisample; - mtl_desc.depth = 1; - mtl_desc.arrayLength = 1; - mtl_desc.mipmapLevelCount = 1; mtl_desc.sampleCount = (NSUInteger)img->cmn.sample_count; } _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_image(_sg_image_t* img, const sg_image_desc* desc) { SOKOL_ASSERT(img && desc); - _sg_image_common_init(&img->cmn, desc); const bool injected = (0 != desc->mtl_textures[0]); - const bool msaa = (img->cmn.sample_count > 1); - /* first initialize all Metal resource pool slots to 'empty' */ + // 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.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 */ + // initialize a Metal texture descriptor 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->cmn.pixel_format)) { - /* depth-stencil buffer texture must always be a render target */ - 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) { + if (img->cmn.render_target) { + if (img->cmn.sample_count > 1) { _sg_mtl_init_texdesc_rt_msaa(mtl_desc, img); - } - else { - _sg_mtl_init_texdesc_rt(mtl_desc, img); - } - id tex = [_sg.mtl.device newTextureWithDescriptor:mtl_desc]; - SOKOL_ASSERT(nil != tex); - img->mtl.depth_tex = _sg_mtl_add_resource(tex); - _SG_OBJC_RELEASE(tex); - } - else { - /* create the color texture - In case this is a render target without MSAA, add the relevant - render-target descriptor attributes. - In case this is a render target *with* MSAA, the color texture - will serve as MSAA-resolve target (not as render target), and rendering - will go into a separate render target texture of type - MTLTextureType2DMultisample. - */ - if (img->cmn.render_target && !msaa) { + } else { _sg_mtl_init_texdesc_rt(mtl_desc, img); } - 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]; + } + for (int slot = 0; slot < img->cmn.num_slots; slot++) { + id mtl_tex; + if (injected) { + SOKOL_ASSERT(desc->mtl_textures[slot]); + mtl_tex = (__bridge id) desc->mtl_textures[slot]; + } else { + mtl_tex = [_sg.mtl.device newTextureWithDescriptor:mtl_desc]; + if (nil == mtl_tex) { + _SG_OBJC_RELEASE(mtl_desc); + _SG_ERROR(METAL_CREATE_TEXTURE_FAILED); + return SG_RESOURCESTATE_FAILED; } - else { - tex = [_sg.mtl.device newTextureWithDescriptor:mtl_desc]; - if ((img->cmn.usage == SG_USAGE_IMMUTABLE) && !img->cmn.render_target) { - _sg_mtl_copy_image_data(img, tex, &desc->data); - } + if ((img->cmn.usage == SG_USAGE_IMMUTABLE) && !img->cmn.render_target) { + _sg_mtl_copy_image_data(img, mtl_tex, &desc->data); } - img->mtl.tex[slot] = _sg_mtl_add_resource(tex); - _SG_OBJC_RELEASE(tex); - } - - /* if MSAA color render target, create an additional MSAA render-surface texture */ - 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); - _SG_OBJC_RELEASE(tex); } - - /* create (possibly shared) sampler state */ - img->mtl.sampler_state = _sg_mtl_create_sampler(_sg.mtl.device, desc); + #if defined(SOKOL_DEBUG) + if (desc->label) { + mtl_tex.label = [NSString stringWithFormat:@"%s.%d", desc->label, slot]; + } + #endif + img->mtl.tex[slot] = _sg_mtl_add_resource(mtl_tex); + _SG_OBJC_RELEASE(mtl_tex); } _SG_OBJC_RELEASE(mtl_desc); return SG_RESOURCESTATE_VALID; @@ -11048,13 +11192,59 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_image(_sg_image_t* img, const sg _SOKOL_PRIVATE void _sg_mtl_discard_image(_sg_image_t* img) { SOKOL_ASSERT(img); - /* it's valid to call release resource with a 'null resource' */ + // it's valid to call release resource with a 'null resource' 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); - /* NOTE: sampler state objects are shared and not released until shutdown */ +} + +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { + SOKOL_ASSERT(smp && desc); + id mtl_smp; + const bool injected = (0 != desc->mtl_sampler); + if (injected) { + SOKOL_ASSERT(desc->mtl_sampler); + mtl_smp = (__bridge id) desc->mtl_sampler; + } else { + MTLSamplerDescriptor* mtl_desc = [[MTLSamplerDescriptor alloc] init]; + mtl_desc.sAddressMode = _sg_mtl_address_mode(desc->wrap_u); + mtl_desc.tAddressMode = _sg_mtl_address_mode(desc->wrap_v); + mtl_desc.rAddressMode = _sg_mtl_address_mode(desc->wrap_w); + if (_sg.features.image_clamp_to_border) { + if (@available(macOS 12.0, iOS 14.0, *)) { + mtl_desc.borderColor = _sg_mtl_border_color(desc->border_color); + } + } + mtl_desc.minFilter = _sg_mtl_minmag_filter(desc->min_filter); + mtl_desc.magFilter = _sg_mtl_minmag_filter(desc->mag_filter); + mtl_desc.mipFilter = _sg_mtl_mipmap_filter(desc->mipmap_filter); + mtl_desc.lodMinClamp = desc->min_lod; + mtl_desc.lodMaxClamp = desc->max_lod; + // FIXME: lodAverage? + mtl_desc.maxAnisotropy = desc->max_anisotropy; + mtl_desc.normalizedCoordinates = YES; + mtl_desc.compareFunction = _sg_mtl_compare_func(desc->compare); + #if defined(SOKOL_DEBUG) + if (desc->label) { + mtl_desc.label = [NSString stringWithUTF8String:desc->label]; + } + #endif + mtl_smp = [_sg.mtl.device newSamplerStateWithDescriptor:mtl_desc]; + _SG_OBJC_RELEASE(mtl_desc); + if (nil == mtl_smp) { + _SG_ERROR(METAL_CREATE_SAMPLER_FAILED); + return SG_RESOURCESTATE_FAILED; + } + } + smp->mtl.sampler_state = _sg_mtl_add_resource(mtl_smp); + _SG_OBJC_RELEASE(mtl_smp); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_mtl_discard_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp); + // it's valid to call release resource with a 'null resource' + _sg_mtl_release_resource(_sg.mtl.frame_index, smp->mtl.sampler_state); } _SOKOL_PRIVATE id _sg_mtl_compile_library(const char* src) { @@ -11065,7 +11255,8 @@ _SOKOL_PRIVATE id _sg_mtl_compile_library(const char* src) { error:&err ]; if (err) { - SG_LOG([err.localizedDescription UTF8String]); + _SG_ERROR(METAL_SHADER_COMPILATION_FAILED); + _SG_LOGMSG(METAL_SHADER_COMPILATION_OUTPUT, [err.localizedDescription UTF8String]); } return lib; } @@ -11075,7 +11266,8 @@ _SOKOL_PRIVATE id _sg_mtl_library_from_bytecode(const void* ptr, siz 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]; if (err) { - SG_LOG([err.localizedDescription UTF8String]); + _SG_ERROR(METAL_SHADER_CREATION_FAILED); + _SG_LOGMSG(METAL_SHADER_COMPILATION_OUTPUT, [err.localizedDescription UTF8String]); } _SG_OBJC_RELEASE(lib_data); return lib; @@ -11084,9 +11276,7 @@ _SOKOL_PRIVATE id _sg_mtl_library_from_bytecode(const void* ptr, siz _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { SOKOL_ASSERT(shd && desc); - _sg_shader_common_init(&shd->cmn, desc); - - /* create metal libray objects and lookup entry functions */ + // create metal library objects and lookup entry functions id vs_lib = nil; id fs_lib = nil; id vs_func = nil; @@ -11094,7 +11284,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_shader(_sg_shader_t* shd, const const char* vs_entry = desc->vs.entry; const char* fs_entry = desc->fs.entry; if (desc->vs.bytecode.ptr && desc->fs.bytecode.ptr) { - /* separate byte code provided */ + // separate byte code provided vs_lib = _sg_mtl_library_from_bytecode(desc->vs.bytecode.ptr, desc->vs.bytecode.size); fs_lib = _sg_mtl_library_from_bytecode(desc->fs.bytecode.ptr, desc->fs.bytecode.size); if ((nil == vs_lib) || (nil == fs_lib)) { @@ -11102,9 +11292,8 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_shader(_sg_shader_t* shd, const } vs_func = [vs_lib newFunctionWithName:[NSString stringWithUTF8String:vs_entry]]; fs_func = [fs_lib newFunctionWithName:[NSString stringWithUTF8String:fs_entry]]; - } - else if (desc->vs.source && desc->fs.source) { - /* separate sources provided */ + } else if (desc->vs.source && desc->fs.source) { + // separate sources provided vs_lib = _sg_mtl_compile_library(desc->vs.source); fs_lib = _sg_mtl_compile_library(desc->fs.source); if ((nil == vs_lib) || (nil == fs_lib)) { @@ -11112,19 +11301,24 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_shader(_sg_shader_t* shd, const } vs_func = [vs_lib newFunctionWithName:[NSString stringWithUTF8String:vs_entry]]; fs_func = [fs_lib newFunctionWithName:[NSString stringWithUTF8String:fs_entry]]; - } - else { + } else { goto failed; } if (nil == vs_func) { - SG_LOG("vertex shader entry function not found\n"); + _SG_ERROR(METAL_VERTEX_SHADER_ENTRY_NOT_FOUND); goto failed; } if (nil == fs_func) { - SG_LOG("fragment shader entry function not found\n"); + _SG_ERROR(METAL_FRAGMENT_SHADER_ENTRY_NOT_FOUND); goto failed; } - /* it is legal to call _sg_mtl_add_resource with a nil value, this will return a special 0xFFFFFFFF index */ + #if defined(SOKOL_DEBUG) + if (desc->label) { + vs_lib.label = [NSString stringWithFormat:@"%s.vs", desc->label]; + fs_lib.label = [NSString stringWithFormat:@"%s.fs", desc->label]; + } + #endif + // it is legal to call _sg_mtl_add_resource with a nil value, this will return a special 0xFFFFFFFF index shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_lib = _sg_mtl_add_resource(vs_lib); _SG_OBJC_RELEASE(vs_lib); shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_lib = _sg_mtl_add_resource(fs_lib); @@ -11152,7 +11346,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_shader(_sg_shader_t* shd, const _SOKOL_PRIVATE void _sg_mtl_discard_shader(_sg_shader_t* shd) { SOKOL_ASSERT(shd); - /* it is valid to call _sg_mtl_release_resource with a 'null resource' */ + // it is valid to call _sg_mtl_release_resource with a 'null resource' _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); @@ -11164,7 +11358,6 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pipeline(_sg_pipeline_t* pip, _s SOKOL_ASSERT(desc->shader.id == shd->slot.id); pip->shader = shd; - _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); @@ -11176,35 +11369,35 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pipeline(_sg_pipeline_t* pip, _s pip->mtl.winding = _sg_mtl_winding(desc->face_winding); pip->mtl.stencil_ref = desc->stencil.ref; - /* create vertex-descriptor */ + // create vertex-descriptor MTLVertexDescriptor* vtx_desc = [MTLVertexDescriptor vertexDescriptor]; for (NSUInteger 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) { + const sg_vertex_attr_state* a_state = &desc->layout.attrs[attr_index]; + if (a_state->format == SG_VERTEXFORMAT_INVALID) { break; } - SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); - vtx_desc.attributes[attr_index].format = _sg_mtl_vertex_format(a_desc->format); - vtx_desc.attributes[attr_index].offset = (NSUInteger)a_desc->offset; - vtx_desc.attributes[attr_index].bufferIndex = (NSUInteger)(a_desc->buffer_index + SG_MAX_SHADERSTAGE_UBS); - pip->cmn.vertex_layout_valid[a_desc->buffer_index] = true; + SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEX_BUFFERS); + vtx_desc.attributes[attr_index].format = _sg_mtl_vertex_format(a_state->format); + vtx_desc.attributes[attr_index].offset = (NSUInteger)a_state->offset; + vtx_desc.attributes[attr_index].bufferIndex = (NSUInteger)(a_state->buffer_index + SG_MAX_SHADERSTAGE_UBS); + pip->cmn.vertex_buffer_layout_active[a_state->buffer_index] = true; } - for (NSUInteger layout_index = 0; layout_index < SG_MAX_SHADERSTAGE_BUFFERS; layout_index++) { - if (pip->cmn.vertex_layout_valid[layout_index]) { - const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[layout_index]; + for (NSUInteger layout_index = 0; layout_index < SG_MAX_VERTEX_BUFFERS; layout_index++) { + if (pip->cmn.vertex_buffer_layout_active[layout_index]) { + const sg_vertex_buffer_layout_state* l_state = &desc->layout.buffers[layout_index]; const NSUInteger mtl_vb_slot = layout_index + SG_MAX_SHADERSTAGE_UBS; - SOKOL_ASSERT(l_desc->stride > 0); - vtx_desc.layouts[mtl_vb_slot].stride = (NSUInteger)l_desc->stride; - vtx_desc.layouts[mtl_vb_slot].stepFunction = _sg_mtl_step_function(l_desc->step_func); - vtx_desc.layouts[mtl_vb_slot].stepRate = (NSUInteger)l_desc->step_rate; - if (SG_VERTEXSTEP_PER_INSTANCE == l_desc->step_func) { + SOKOL_ASSERT(l_state->stride > 0); + vtx_desc.layouts[mtl_vb_slot].stride = (NSUInteger)l_state->stride; + vtx_desc.layouts[mtl_vb_slot].stepFunction = _sg_mtl_step_function(l_state->step_func); + vtx_desc.layouts[mtl_vb_slot].stepRate = (NSUInteger)l_state->step_rate; + if (SG_VERTEXSTEP_PER_INSTANCE == l_state->step_func) { // NOTE: not actually used in _sg_mtl_draw() pip->cmn.use_instanced_draw = true; } } } - /* render-pipeline descriptor */ + // render-pipeline descriptor MTLRenderPipelineDescriptor* rp_desc = [[MTLRenderPipelineDescriptor alloc] init]; rp_desc.vertexDescriptor = vtx_desc; SOKOL_ASSERT(shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func != _SG_MTL_INVALID_SLOT_INDEX); @@ -11219,17 +11412,17 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pipeline(_sg_pipeline_t* pip, _s if (desc->depth.pixel_format == SG_PIXELFORMAT_DEPTH_STENCIL) { rp_desc.stencilAttachmentPixelFormat = _sg_mtl_pixel_format(desc->depth.pixel_format); } - /* FIXME: this only works on macOS 10.13! - for (int i = 0; i < (SG_MAX_SHADERSTAGE_UBS+SG_MAX_SHADERSTAGE_BUFFERS); i++) { - rp_desc.vertexBuffers[i].mutability = MTLMutabilityImmutable; - } - for (int i = 0; i < SG_MAX_SHADERSTAGE_UBS; i++) { - rp_desc.fragmentBuffers[i].mutability = MTLMutabilityImmutable; + if (@available(macOS 10.13, iOS 11.0, *)) { + for (NSUInteger i = 0; i < (SG_MAX_SHADERSTAGE_UBS+SG_MAX_VERTEX_BUFFERS); i++) { + rp_desc.vertexBuffers[i].mutability = MTLMutabilityImmutable; + } + for (NSUInteger i = 0; i < SG_MAX_SHADERSTAGE_UBS; i++) { + rp_desc.fragmentBuffers[i].mutability = MTLMutabilityImmutable; + } } - */ for (NSUInteger i = 0; i < (NSUInteger)desc->color_count; i++) { SOKOL_ASSERT(i < SG_MAX_COLOR_ATTACHMENTS); - const sg_color_state* cs = &desc->colors[i]; + const sg_color_target_state* cs = &desc->colors[i]; rp_desc.colorAttachments[i].pixelFormat = _sg_mtl_pixel_format(cs->pixel_format); rp_desc.colorAttachments[i].writeMask = _sg_mtl_color_write_mask(cs->write_mask); rp_desc.colorAttachments[i].blendingEnabled = cs->blend.enabled; @@ -11240,16 +11433,24 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pipeline(_sg_pipeline_t* pip, _s rp_desc.colorAttachments[i].sourceAlphaBlendFactor = _sg_mtl_blend_factor(cs->blend.src_factor_alpha); rp_desc.colorAttachments[i].sourceRGBBlendFactor = _sg_mtl_blend_factor(cs->blend.src_factor_rgb); } + #if defined(SOKOL_DEBUG) + if (desc->label) { + rp_desc.label = [NSString stringWithFormat:@"%s", desc->label]; + } + #endif NSError* err = NULL; id mtl_rps = [_sg.mtl.device newRenderPipelineStateWithDescriptor:rp_desc error:&err]; _SG_OBJC_RELEASE(rp_desc); if (nil == mtl_rps) { SOKOL_ASSERT(err); - SG_LOG([err.localizedDescription UTF8String]); + _SG_ERROR(METAL_CREATE_RPS_FAILED); + _SG_LOGMSG(METAL_CREATE_RPS_OUTPUT, [err.localizedDescription UTF8String]); return SG_RESOURCESTATE_FAILED; } + pip->mtl.rps = _sg_mtl_add_resource(mtl_rps); + _SG_OBJC_RELEASE(mtl_rps); - /* depth-stencil-state */ + // depth-stencil-state MTLDepthStencilDescriptor* ds_desc = [[MTLDepthStencilDescriptor alloc] init]; ds_desc.depthCompareFunction = _sg_mtl_compare_func(desc->depth.compare); ds_desc.depthWriteEnabled = desc->depth.write_enabled; @@ -11271,10 +11472,17 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pipeline(_sg_pipeline_t* pip, _s ds_desc.frontFaceStencil.readMask = desc->stencil.read_mask; ds_desc.frontFaceStencil.writeMask = desc->stencil.write_mask; } + #if defined(SOKOL_DEBUG) + if (desc->label) { + ds_desc.label = [NSString stringWithFormat:@"%s.dss", desc->label]; + } + #endif id mtl_dss = [_sg.mtl.device newDepthStencilStateWithDescriptor:ds_desc]; _SG_OBJC_RELEASE(ds_desc); - pip->mtl.rps = _sg_mtl_add_resource(mtl_rps); - _SG_OBJC_RELEASE(mtl_rps); + if (nil == mtl_dss) { + _SG_ERROR(METAL_CREATE_DSS_FAILED); + return SG_RESOURCESTATE_FAILED; + } pip->mtl.dss = _sg_mtl_add_resource(mtl_dss); _SG_OBJC_RELEASE(mtl_dss); return SG_RESOURCESTATE_VALID; @@ -11282,36 +11490,39 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pipeline(_sg_pipeline_t* pip, _s _SOKOL_PRIVATE void _sg_mtl_discard_pipeline(_sg_pipeline_t* pip) { SOKOL_ASSERT(pip); - /* it's valid to call release resource with a 'null resource' */ + // 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); } -_SOKOL_PRIVATE sg_resource_state _sg_mtl_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** color_images, _sg_image_t** resolve_images, _sg_image_t* ds_img, const sg_pass_desc* desc) { SOKOL_ASSERT(pass && desc); - SOKOL_ASSERT(att_images && att_images[0]); - - _sg_pass_common_init(&pass->cmn, desc); + SOKOL_ASSERT(color_images && resolve_images); - /* copy image pointers */ - const sg_pass_attachment_desc* att_desc; + // copy image pointers 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->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]->cmn.pixel_format)); - pass->mtl.color_atts[i].image = att_images[i]; + const sg_pass_attachment_desc* color_desc = &desc->color_attachments[i]; + _SOKOL_UNUSED(color_desc); + SOKOL_ASSERT(color_desc->image.id != SG_INVALID_ID); + SOKOL_ASSERT(0 == pass->mtl.color_atts[i].image); + SOKOL_ASSERT(color_images[i] && (color_images[i]->slot.id == color_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(color_images[i]->cmn.pixel_format)); + pass->mtl.color_atts[i].image = color_images[i]; + + const sg_pass_attachment_desc* resolve_desc = &desc->resolve_attachments[i]; + if (resolve_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(0 == pass->mtl.resolve_atts[i].image); + SOKOL_ASSERT(resolve_images[i] && (resolve_images[i]->slot.id == resolve_desc->image.id)); + SOKOL_ASSERT(color_images[i] && (color_images[i]->cmn.pixel_format == resolve_images[i]->cmn.pixel_format)); + pass->mtl.resolve_atts[i].image = resolve_images[i]; } } SOKOL_ASSERT(0 == pass->mtl.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)); - pass->mtl.ds_att.image = att_images[ds_img_index]; + const sg_pass_attachment_desc* ds_desc = &desc->depth_stencil_attachment; + if (ds_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(ds_img && (ds_img->slot.id == ds_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(ds_img->cmn.pixel_format)); + pass->mtl.ds_att.image = ds_img; } return SG_RESOURCESTATE_VALID; } @@ -11322,13 +11533,19 @@ _SOKOL_PRIVATE void _sg_mtl_discard_pass(_sg_pass_t* pass) { } _SOKOL_PRIVATE _sg_image_t* _sg_mtl_pass_color_image(const _sg_pass_t* pass, int index) { + // NOTE: may return null 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_resolve_image(const _sg_pass_t* pass, int index) { + // NOTE: may return null + SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + return pass->mtl.resolve_atts[index].image; +} + _SOKOL_PRIVATE _sg_image_t* _sg_mtl_pass_ds_image(const _sg_pass_t* pass) { - /* NOTE: may return null */ + // NOTE: may return null SOKOL_ASSERT(pass); return pass->mtl.ds_att.image; } @@ -11356,92 +11573,88 @@ _SOKOL_PRIVATE void _sg_mtl_begin_pass(_sg_pass_t* pass, const sg_pass_action* a Also see: https://github.com/floooh/sokol/issues/762 */ if (nil == _sg.mtl.cmd_buffer) { - SOKOL_ASSERT(nil == _sg.mtl.present_cmd_buffer); - /* block until the oldest frame in flight has finished */ + // 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]; - _sg.mtl.present_cmd_buffer = [_sg.mtl.cmd_queue commandBuffer]; [_sg.mtl.cmd_buffer enqueue]; - [_sg.mtl.present_cmd_buffer enqueue]; - [_sg.mtl.present_cmd_buffer addCompletedHandler:^(id cmd_buf) { + [_sg.mtl.cmd_buffer addCompletedHandler:^(id cmd_buf) { // NOTE: this code is called on a different thread! _SOKOL_UNUSED(cmd_buf); dispatch_semaphore_signal(_sg.mtl.sem); }]; } - /* if this is first pass in frame, get uniform buffer base pointer */ + // 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]; } - /* initialize a render pass descriptor */ + // initialize a render pass descriptor MTLRenderPassDescriptor* pass_desc = nil; if (pass) { - /* offscreen render pass */ + // offscreen render pass pass_desc = [MTLRenderPassDescriptor renderPassDescriptor]; - } - else { - /* default render pass, call user-provided callback to provide render pass descriptor */ + } else { + // default render pass, call user-provided callback to provide render pass descriptor if (_sg.mtl.renderpass_descriptor_cb) { pass_desc = (__bridge MTLRenderPassDescriptor*) _sg.mtl.renderpass_descriptor_cb(); - } - else { + } else { pass_desc = (__bridge MTLRenderPassDescriptor*) _sg.mtl.renderpass_descriptor_userdata_cb(_sg.mtl.user_data); } - + // pin the swapchain resources into memory so that they outlive their command buffer + // (this is necessary because the command buffer doesn't retain references) + int default_pass_desc_ref = _sg_mtl_add_resource(pass_desc); + _sg_mtl_release_resource(_sg.mtl.frame_index, default_pass_desc_ref); } if (pass_desc) { _sg.mtl.pass_valid = true; - } - else { - /* default pass descriptor will not be valid if window is minimized, - don't do any rendering in this case */ + } else { + // default pass descriptor will not be valid if window is minimized, don't do any rendering in this case _sg.mtl.pass_valid = false; return; } if (pass) { - /* setup pass descriptor for offscreen rendering */ + // setup pass descriptor for offscreen rendering SOKOL_ASSERT(pass->slot.state == SG_RESOURCESTATE_VALID); for (NSUInteger i = 0; i < (NSUInteger)pass->cmn.num_color_atts; i++) { - const _sg_pass_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; - sg_color c = action->colors[i].value; + const _sg_pass_attachment_t* cmn_color_att = &pass->cmn.color_atts[i]; + const _sg_mtl_attachment_t* mtl_color_att = &pass->mtl.color_atts[i]; + const _sg_image_t* color_att_img = mtl_color_att->image; + const _sg_pass_attachment_t* cmn_resolve_att = &pass->cmn.resolve_atts[i]; + const _sg_mtl_attachment_t* mtl_resolve_att = &pass->mtl.resolve_atts[i]; + const _sg_image_t* resolve_att_img = mtl_resolve_att->image; + SOKOL_ASSERT(color_att_img->slot.state == SG_RESOURCESTATE_VALID); + SOKOL_ASSERT(color_att_img->slot.id == cmn_color_att->image_id.id); + SOKOL_ASSERT(color_att_img->mtl.tex[color_att_img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + pass_desc.colorAttachments[i].loadAction = _sg_mtl_load_action(action->colors[i].load_action); + pass_desc.colorAttachments[i].storeAction = _sg_mtl_store_action(action->colors[i].store_action, resolve_att_img != 0); + sg_color c = action->colors[i].clear_value; pass_desc.colorAttachments[i].clearColor = MTLClearColorMake(c.r, c.g, c.b, c.a); - if (is_msaa) { - 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 = (NSUInteger)cmn_att->mip_level; - switch (att_img->cmn.type) { - case SG_IMAGETYPE_CUBE: - case SG_IMAGETYPE_ARRAY: - pass_desc.colorAttachments[i].resolveSlice = (NSUInteger)cmn_att->slice; - break; - case SG_IMAGETYPE_3D: - pass_desc.colorAttachments[i].resolveDepthPlane = (NSUInteger)cmn_att->slice; - break; - default: break; - } + pass_desc.colorAttachments[i].texture = _sg_mtl_id(color_att_img->mtl.tex[color_att_img->cmn.active_slot]); + pass_desc.colorAttachments[i].level = (NSUInteger)cmn_color_att->mip_level; + switch (color_att_img->cmn.type) { + case SG_IMAGETYPE_CUBE: + case SG_IMAGETYPE_ARRAY: + pass_desc.colorAttachments[i].slice = (NSUInteger)cmn_color_att->slice; + break; + case SG_IMAGETYPE_3D: + pass_desc.colorAttachments[i].depthPlane = (NSUInteger)cmn_color_att->slice; + break; + default: break; } - else { - 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 = (NSUInteger)cmn_att->mip_level; - switch (att_img->cmn.type) { + if (resolve_att_img) { + SOKOL_ASSERT(resolve_att_img->slot.state == SG_RESOURCESTATE_VALID); + SOKOL_ASSERT(resolve_att_img->slot.id == cmn_resolve_att->image_id.id); + SOKOL_ASSERT(resolve_att_img->mtl.tex[resolve_att_img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + pass_desc.colorAttachments[i].resolveTexture = _sg_mtl_id(resolve_att_img->mtl.tex[resolve_att_img->cmn.active_slot]); + pass_desc.colorAttachments[i].resolveLevel = (NSUInteger)cmn_resolve_att->mip_level; + switch (resolve_att_img->cmn.type) { case SG_IMAGETYPE_CUBE: case SG_IMAGETYPE_ARRAY: - pass_desc.colorAttachments[i].slice = (NSUInteger)cmn_att->slice; + pass_desc.colorAttachments[i].resolveSlice = (NSUInteger)cmn_resolve_att->slice; break; case SG_IMAGETYPE_3D: - pass_desc.colorAttachments[i].depthPlane = (NSUInteger)cmn_att->slice; + pass_desc.colorAttachments[i].resolveDepthPlane = (NSUInteger)cmn_resolve_att->slice; break; default: break; } @@ -11451,36 +11664,58 @@ _SOKOL_PRIVATE void _sg_mtl_begin_pass(_sg_pass_t* pass, const sg_pass_action* a 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.value; + SOKOL_ASSERT(ds_att_img->mtl.tex[ds_att_img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + pass_desc.depthAttachment.texture = _sg_mtl_id(ds_att_img->mtl.tex[ds_att_img->cmn.active_slot]); + pass_desc.depthAttachment.loadAction = _sg_mtl_load_action(action->depth.load_action); + pass_desc.depthAttachment.storeAction = _sg_mtl_store_action(action->depth.store_action, false); + pass_desc.depthAttachment.clearDepth = action->depth.clear_value; + const _sg_pass_attachment_t* cmn_ds_att = &pass->cmn.ds_att; + switch (ds_att_img->cmn.type) { + case SG_IMAGETYPE_CUBE: + case SG_IMAGETYPE_ARRAY: + pass_desc.depthAttachment.slice = (NSUInteger)cmn_ds_att->slice; + break; + case SG_IMAGETYPE_3D: + pass_desc.depthAttachment.resolveDepthPlane = (NSUInteger)cmn_ds_att->slice; + break; + default: break; + } 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.value; + pass_desc.stencilAttachment.texture = _sg_mtl_id(ds_att_img->mtl.tex[ds_att_img->cmn.active_slot]); + pass_desc.stencilAttachment.loadAction = _sg_mtl_load_action(action->stencil.load_action); + pass_desc.stencilAttachment.storeAction = _sg_mtl_store_action(action->depth.store_action, false); + pass_desc.stencilAttachment.clearStencil = action->stencil.clear_value; + switch (ds_att_img->cmn.type) { + case SG_IMAGETYPE_CUBE: + case SG_IMAGETYPE_ARRAY: + pass_desc.stencilAttachment.slice = (NSUInteger)cmn_ds_att->slice; + break; + case SG_IMAGETYPE_3D: + pass_desc.stencilAttachment.resolveDepthPlane = (NSUInteger)cmn_ds_att->slice; + break; + default: break; + } } } - } - else { - /* setup pass descriptor for default rendering */ - pass_desc.colorAttachments[0].loadAction = _sg_mtl_load_action(action->colors[0].action); - sg_color c = action->colors[0].value; + } else { + // setup pass descriptor for default rendering + pass_desc.colorAttachments[0].loadAction = _sg_mtl_load_action(action->colors[0].load_action); + sg_color c = action->colors[0].clear_value; pass_desc.colorAttachments[0].clearColor = MTLClearColorMake(c.r, c.g, c.b, c.a); - pass_desc.depthAttachment.loadAction = _sg_mtl_load_action(action->depth.action); - pass_desc.depthAttachment.clearDepth = action->depth.value; - pass_desc.stencilAttachment.loadAction = _sg_mtl_load_action(action->stencil.action); - pass_desc.stencilAttachment.clearStencil = action->stencil.value; + pass_desc.depthAttachment.loadAction = _sg_mtl_load_action(action->depth.load_action); + pass_desc.depthAttachment.clearDepth = action->depth.clear_value; + pass_desc.stencilAttachment.loadAction = _sg_mtl_load_action(action->stencil.load_action); + pass_desc.stencilAttachment.clearStencil = action->stencil.clear_value; } - /* create a render command encoder, this might return nil if window is minimized */ + // 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 (nil == _sg.mtl.cmd_encoder) { _sg.mtl.pass_valid = false; return; } - /* bind the global uniform buffer, this only happens once per pass */ + // bind the global uniform buffer, this only happens once per pass _sg_mtl_bind_uniform_buffers(); } @@ -11490,7 +11725,7 @@ _SOKOL_PRIVATE void _sg_mtl_end_pass(void) { _sg.mtl.pass_valid = false; if (nil != _sg.mtl.cmd_encoder) { [_sg.mtl.cmd_encoder endEncoding]; - /* NOTE: MTLRenderCommandEncoder is autoreleased */ + // NOTE: MTLRenderCommandEncoder is autoreleased _sg.mtl.cmd_encoder = nil; } } @@ -11501,35 +11736,31 @@ _SOKOL_PRIVATE void _sg_mtl_commit(void) { SOKOL_ASSERT(_sg.mtl.drawable_cb || _sg.mtl.drawable_userdata_cb); SOKOL_ASSERT(nil == _sg.mtl.cmd_encoder); SOKOL_ASSERT(nil != _sg.mtl.cmd_buffer); - SOKOL_ASSERT(nil != _sg.mtl.present_cmd_buffer); - /* present, commit and signal semaphore when done */ + // present, commit and signal semaphore when done id cur_drawable = nil; if (_sg.mtl.drawable_cb) { cur_drawable = (__bridge id) _sg.mtl.drawable_cb(); - } - else { + } else { cur_drawable = (__bridge id) _sg.mtl.drawable_userdata_cb(_sg.mtl.user_data); } if (nil != cur_drawable) { - [_sg.mtl.present_cmd_buffer presentDrawable:cur_drawable]; + [_sg.mtl.cmd_buffer presentDrawable:cur_drawable]; } [_sg.mtl.cmd_buffer commit]; - [_sg.mtl.present_cmd_buffer commit]; - /* garbage-collect resources pending for release */ + // garbage-collect resources pending for release _sg_mtl_garbage_collect(_sg.mtl.frame_index); - /* rotate uniform buffer slot */ + // rotate uniform buffer slot if (++_sg.mtl.cur_frame_rotate_index >= SG_NUM_INFLIGHT_FRAMES) { _sg.mtl.cur_frame_rotate_index = 0; } _sg.mtl.frame_index++; _sg.mtl.cur_ub_offset = 0; _sg.mtl.cur_ub_base_ptr = 0; - /* NOTE: MTLCommandBuffer is autoreleased */ + // NOTE: MTLCommandBuffer is autoreleased _sg.mtl.cmd_buffer = nil; - _sg.mtl.present_cmd_buffer = nil; } _SOKOL_PRIVATE void _sg_mtl_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { @@ -11554,7 +11785,7 @@ _SOKOL_PRIVATE void _sg_mtl_apply_scissor_rect(int x, int y, int w, int h, bool return; } SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); - /* clip against framebuffer rect */ + // 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); if ((x + w) > _sg.mtl.cur_width) { @@ -11583,7 +11814,7 @@ _SOKOL_PRIVATE void _sg_mtl_apply_pipeline(_sg_pipeline_t* pip) { } 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)) { + if (_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; sg_color c = pip->cmn.blend_color; @@ -11591,7 +11822,7 @@ _SOKOL_PRIVATE void _sg_mtl_apply_pipeline(_sg_pipeline_t* pip) { [_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]; + [_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); @@ -11604,7 +11835,9 @@ _SOKOL_PRIVATE void _sg_mtl_apply_bindings( _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) + _sg_image_t** fs_imgs, int num_fs_imgs, + _sg_sampler_t** vs_smps, int num_vs_smps, + _sg_sampler_t** fs_smps, int num_fs_smps) { _SOKOL_UNUSED(pip); SOKOL_ASSERT(_sg.mtl.in_pass); @@ -11613,25 +11846,22 @@ _SOKOL_PRIVATE void _sg_mtl_apply_bindings( } SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); - /* store index buffer binding, this will be needed later in sg_draw() */ + // 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->cmn.index_type != SG_INDEXTYPE_NONE); _sg.mtl.state_cache.cur_indexbuffer_id.id = ib->slot.id; - } - else { + } else { SOKOL_ASSERT(pip->cmn.index_type == SG_INDEXTYPE_NONE); _sg.mtl.state_cache.cur_indexbuffer_id.id = SG_INVALID_ID; } - /* apply vertex buffers */ - NSUInteger slot; - for (slot = 0; slot < (NSUInteger)num_vbs; slot++) { + // apply vertex buffers + for (NSUInteger slot = 0; slot < (NSUInteger)num_vbs; slot++) { const _sg_buffer_t* vb = vbs[slot]; - if ((_sg.mtl.state_cache.cur_vertexbuffers[slot] != vb) || - (_sg.mtl.state_cache.cur_vertexbuffer_offsets[slot] != vb_offsets[slot]) || - (_sg.mtl.state_cache.cur_vertexbuffer_ids[slot].id != vb->slot.id)) + if ((_sg.mtl.state_cache.cur_vertexbuffer_ids[slot].id != vb->slot.id) || + (_sg.mtl.state_cache.cur_vertexbuffer_offsets[slot] != vb_offsets[slot])) { _sg.mtl.state_cache.cur_vertexbuffers[slot] = vb; _sg.mtl.state_cache.cur_vertexbuffer_offsets[slot] = vb_offsets[slot]; @@ -11644,29 +11874,47 @@ _SOKOL_PRIVATE void _sg_mtl_apply_bindings( } } - /* apply vertex shader images */ - for (slot = 0; slot < (NSUInteger)num_vs_imgs; slot++) { + // apply vertex shader images + for (NSUInteger slot = 0; slot < (NSUInteger)num_vs_imgs; slot++) { const _sg_image_t* img = vs_imgs[slot]; - if ((_sg.mtl.state_cache.cur_vs_images[slot] != img) || (_sg.mtl.state_cache.cur_vs_image_ids[slot].id != img->slot.id)) { + if (_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->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]; } } - /* apply fragment shader images */ - for (slot = 0; slot < (NSUInteger)num_fs_imgs; slot++) { + // apply fragment shader images + for (NSUInteger slot = 0; slot < (NSUInteger)num_fs_imgs; slot++) { const _sg_image_t* img = fs_imgs[slot]; - if ((_sg.mtl.state_cache.cur_fs_images[slot] != img) || (_sg.mtl.state_cache.cur_fs_image_ids[slot].id != img->slot.id)) { + if (_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->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]; + } + } + + // apply vertex shader samplers + for (NSUInteger slot = 0; slot < (NSUInteger)num_vs_smps; slot++) { + const _sg_sampler_t* smp = vs_smps[slot]; + if (_sg.mtl.state_cache.cur_vs_sampler_ids[slot].id != smp->slot.id) { + _sg.mtl.state_cache.cur_vs_samplers[slot] = smp; + _sg.mtl.state_cache.cur_vs_sampler_ids[slot].id = smp->slot.id; + SOKOL_ASSERT(smp->mtl.sampler_state != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setVertexSamplerState:_sg_mtl_id(smp->mtl.sampler_state) atIndex:slot]; + } + } + + // apply fragment shader samplers + for (NSUInteger slot = 0; slot < (NSUInteger)num_fs_smps; slot++) { + const _sg_sampler_t* smp = fs_smps[slot]; + if (_sg.mtl.state_cache.cur_fs_sampler_ids[slot].id != smp->slot.id) { + _sg.mtl.state_cache.cur_fs_samplers[slot] = smp; + _sg.mtl.state_cache.cur_fs_sampler_ids[slot].id = smp->slot.id; + SOKOL_ASSERT(smp->mtl.sampler_state != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setFragmentSamplerState:_sg_mtl_id(smp->mtl.sampler_state) atIndex:slot]; } } } @@ -11683,15 +11931,14 @@ _SOKOL_PRIVATE void _sg_mtl_apply_uniforms(sg_shader_stage stage_index, int ub_i 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->cmn.shader_id.id); SOKOL_ASSERT(ub_index < _sg.mtl.state_cache.cur_pipeline->shader->cmn.stage[stage_index].num_uniform_blocks); - SOKOL_ASSERT(data->size <= _sg.mtl.state_cache.cur_pipeline->shader->cmn.stage[stage_index].uniform_blocks[ub_index].size); + SOKOL_ASSERT(data->size == _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 */ + // 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->ptr, data->size); if (stage_index == SG_SHADERSTAGE_VS) { [_sg.mtl.cmd_encoder setVertexBufferOffset:(NSUInteger)_sg.mtl.cur_ub_offset atIndex:(NSUInteger)ub_index]; - } - else { + } else { [_sg.mtl.cmd_encoder setFragmentBufferOffset:(NSUInteger)_sg.mtl.cur_ub_offset atIndex:(NSUInteger)ub_index]; } _sg.mtl.cur_ub_offset = _sg_roundup(_sg.mtl.cur_ub_offset + (int)data->size, _SG_MTL_UB_ALIGN); @@ -11705,7 +11952,7 @@ _SOKOL_PRIVATE void _sg_mtl_draw(int base_element, int num_elements, int num_ins 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->cmn.index_type) { - /* indexed rendering */ + // 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->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); @@ -11716,9 +11963,8 @@ _SOKOL_PRIVATE void _sg_mtl_draw(int base_element, int num_elements, int num_ins indexBuffer:_sg_mtl_id(ib->mtl.buf[ib->cmn.active_slot]) indexBufferOffset:index_buffer_offset instanceCount:(NSUInteger)num_instances]; - } - else { - /* non-indexed rendering */ + } else { + // non-indexed rendering [_sg.mtl.cmd_encoder drawPrimitives:_sg.mtl.state_cache.cur_pipeline->mtl.prim_type vertexStart:(NSUInteger)base_element vertexCount:(NSUInteger)num_elements @@ -11735,7 +11981,9 @@ _SOKOL_PRIVATE void _sg_mtl_update_buffer(_sg_buffer_t* buf, const sg_range* dat void* dst_ptr = [mtl_buf contents]; memcpy(dst_ptr, data->ptr, data->size); #if defined(_SG_TARGET_MACOS) - [mtl_buf didModifyRange:NSMakeRange(0, data->size)]; + if (_sg_mtl_resource_options_storage_mode_managed_or_shared() == MTLStorageModeManaged) { + [mtl_buf didModifyRange:NSMakeRange(0, data->size)]; + } #endif } @@ -11751,9 +11999,11 @@ _SOKOL_PRIVATE int _sg_mtl_append_buffer(_sg_buffer_t* buf, const sg_range* data dst_ptr += buf->cmn.append_pos; memcpy(dst_ptr, data->ptr, data->size); #if defined(_SG_TARGET_MACOS) - [mtl_buf didModifyRange:NSMakeRange((NSUInteger)buf->cmn.append_pos, (NSUInteger)data->size)]; + if (_sg_mtl_resource_options_storage_mode_managed_or_shared() == MTLStorageModeManaged) { + [mtl_buf didModifyRange:NSMakeRange((NSUInteger)buf->cmn.append_pos, (NSUInteger)data->size)]; + } #endif - /* NOTE: this is a requirement from WebGPU, but we want identical behaviour across all backends */ + // NOTE: this is a requirement from WebGPU, but we want identical behaviour across all backends return _sg_roundup((int)data->size, 4); } @@ -11766,15 +12016,33 @@ _SOKOL_PRIVATE void _sg_mtl_update_image(_sg_image_t* img, const sg_image_data* _sg_mtl_copy_image_data(img, mtl_tex, data); } -/*== WEBGPU BACKEND IMPLEMENTATION ===========================================*/ +_SOKOL_PRIVATE void _sg_mtl_push_debug_group(const char* name) { + SOKOL_ASSERT(name); + if (_sg.mtl.cmd_encoder) { + [_sg.mtl.cmd_encoder pushDebugGroup:[NSString stringWithUTF8String:name]]; + } +} + +_SOKOL_PRIVATE void _sg_mtl_pop_debug_group(void) { + if (_sg.mtl.cmd_encoder) { + [_sg.mtl.cmd_encoder popDebugGroup]; + } +} + +// ██ ██ ███████ ██████ ██████ ██████ ██ ██ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ █ ██ █████ ██████ ██ ███ ██████ ██ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███ ███ ███████ ██████ ██████ ██ ██████ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>webgpu backend #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 { + } else { res |= WGPUBufferUsage_Index; } if (SG_USAGE_IMMUTABLE != u) { @@ -11785,10 +12053,10 @@ _SOKOL_PRIVATE WGPUBufferUsageFlags _sg_wgpu_buffer_usage(sg_buffer_type t, sg_u _SOKOL_PRIVATE WGPULoadOp _sg_wgpu_load_op(sg_action a) { switch (a) { - case SG_ACTION_CLEAR: - case SG_ACTION_DONTCARE: + case SG_LOADACTION_CLEAR: + case SG_LOADACTION_DONTCARE: return WGPULoadOp_Clear; - case SG_ACTION_LOAD: + case SG_LOADACTION_LOAD: return WGPULoadOp_Load; default: SOKOL_UNREACHABLE; @@ -11806,11 +12074,12 @@ _SOKOL_PRIVATE WGPUTextureViewDimension _sg_wgpu_tex_viewdim(sg_image_type t) { } } -_SOKOL_PRIVATE WGPUTextureComponentType _sg_wgpu_tex_comptype(sg_sampler_type t) { +_SOKOL_PRIVATE WGPUTextureComponentType _sg_wgpu_tex_comptype(sg_image_sample_type t) { + // FIXME switch (t) { - case SG_SAMPLERTYPE_FLOAT: return WGPUTextureComponentType_Float; - case SG_SAMPLERTYPE_SINT: return WGPUTextureComponentType_Sint; - case SG_SAMPLERTYPE_UINT: return WGPUTextureComponentType_Uint; + case SG_IMAGESAMPLETYPE_FLOAT: return WGPUTextureComponentType_Float; + case SG_IMAGESAMPLETYPE_SINT: return WGPUTextureComponentType_Sint; + case SG_IMAGESAMPLETYPE_UINT: return WGPUTextureComponentType_Uint; default: SOKOL_UNREACHABLE; return WGPUTextureComponentType_Force32; } } @@ -11818,8 +12087,7 @@ _SOKOL_PRIVATE WGPUTextureComponentType _sg_wgpu_tex_comptype(sg_sampler_type t) _SOKOL_PRIVATE WGPUTextureDimension _sg_wgpu_tex_dim(sg_image_type t) { if (SG_IMAGETYPE_3D == t) { return WGPUTextureDimension_3D; - } - else { + } else { return WGPUTextureDimension_2D; } } @@ -11872,7 +12140,7 @@ _SOKOL_PRIVATE WGPUFilterMode _sg_wgpu_sampler_mipfilter(sg_filter f) { } _SOKOL_PRIVATE WGPUIndexFormat _sg_wgpu_indexformat(sg_index_type t) { - /* NOTE: there's no WGPUIndexFormat_None */ + // NOTE: there's no WGPUIndexFormat_None return (t == SG_INDEXTYPE_UINT16) ? WGPUIndexFormat_Uint16 : WGPUIndexFormat_Uint32; } @@ -11896,7 +12164,9 @@ _SOKOL_PRIVATE WGPUVertexFormat _sg_wgpu_vertexformat(sg_vertex_format f) { 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_HALF2: return WGPUVertexFormat_Half2; + case SG_VERTEXFORMAT_HALF3: return WGPUVertexFormat_Half4; + // FIXME! UINT10_N2 case SG_VERTEXFORMAT_UINT10_N2: default: SOKOL_UNREACHABLE; @@ -11964,7 +12234,7 @@ _SOKOL_PRIVATE WGPUTextureFormat _sg_wgpu_textureformat(sg_pixel_format p) { 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: return WGPUTextureFormat_Depth32Float; case SG_PIXELFORMAT_DEPTH_STENCIL: return WGPUTextureFormat_Depth24PlusStencil8; case SG_PIXELFORMAT_BC1_RGBA: return WGPUTextureFormat_BC1RGBAUnorm; case SG_PIXELFORMAT_BC2_RGBA: return WGPUTextureFormat_BC2RGBAUnorm; @@ -11977,13 +12247,14 @@ _SOKOL_PRIVATE WGPUTextureFormat _sg_wgpu_textureformat(sg_pixel_format p) { case SG_PIXELFORMAT_BC6H_RGBUF: return WGPUTextureFormat_BC6HRGBUfloat; case SG_PIXELFORMAT_BC7_RGBA: return WGPUTextureFormat_BC7RGBAUnorm; - /* NOT SUPPORTED */ + // 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_SRGB8A8: case SG_PIXELFORMAT_RGB9E5: case SG_PIXELFORMAT_PVRTC_RGB_2BPP: case SG_PIXELFORMAT_PVRTC_RGB_4BPP: @@ -12064,7 +12335,7 @@ _SOKOL_PRIVATE WGPUBlendFactor _sg_wgpu_blendfactor(sg_blend_factor f) { 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? */ + // 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: @@ -12091,17 +12362,12 @@ _SOKOL_PRIVATE WGPUColorWriteMaskFlags _sg_wgpu_colorwritemask(uint8_t m) { _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; _sg.features.mrt_independent_blend_state = true; _sg.features.mrt_independent_write_mask = true; - /* FIXME: max images size??? */ + // 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; @@ -12132,7 +12398,7 @@ _SOKOL_PRIVATE void _sg_wgpu_init_caps(void) { _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]); _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_BGRA8]); _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB10A2]); - /* FIXME: missing SG_PIXELFORMAT_RG11B10F */ + // FIXME: missing SG_PIXELFORMAT_RG11B10F _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32UI]); _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32SI]); _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_RG32F]); @@ -12256,9 +12522,9 @@ _SOKOL_PRIVATE void _sg_wgpu_ubpool_mapped_callback(WGPUBufferMapAsyncStatus sta if (!_sg.wgpu.valid) { return; } - /* FIXME: better handling for this */ + // FIXME: better handling for this if (WGPUBufferMapAsyncStatus_Success != status) { - SG_LOG("Mapping uniform buffer failed!\n"); + _SG_ERROR(WGPU_MAP_UNIFORM_BUFFER_FAILED); SOKOL_ASSERT(false); } SOKOL_ASSERT(data && (data_len == _sg.wgpu.ub.num_bytes)); @@ -12270,17 +12536,17 @@ _SOKOL_PRIVATE void _sg_wgpu_ubpool_mapped_callback(WGPUBufferMapAsyncStatus sta _SOKOL_PRIVATE void _sg_wgpu_ubpool_next_frame(bool first_frame) { - /* immediately request a new mapping for the last frame's current staging buffer */ + // 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 */ + // rewind per-frame offsets _sg.wgpu.ub.offset = 0; _sg_clear(&_sg.wgpu.ub.bind_offsets, sizeof(_sg.wgpu.ub.bind_offsets)); - /* check if a mapped staging buffer is available, otherwise create one */ + // 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; @@ -12288,7 +12554,7 @@ _SOKOL_PRIVATE void _sg_wgpu_ubpool_next_frame(bool first_frame) { } } - /* no mapped uniform buffer available, create one */ + // 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; @@ -12306,7 +12572,7 @@ _SOKOL_PRIVATE void _sg_wgpu_ubpool_next_frame(bool first_frame) { } _SOKOL_PRIVATE void _sg_wgpu_ubpool_flush(void) { - /* unmap staging buffer and copy to uniform buffer */ + // 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; @@ -12318,15 +12584,15 @@ _SOKOL_PRIVATE void _sg_wgpu_ubpool_flush(void) { } } -/* helper function to compute number of bytes needed in staging buffer to copy image data */ +// helper function to compute number of bytes needed in staging buffer to copy image data _SOKOL_PRIVATE uint32_t _sg_wgpu_image_data_buffer_size(const _sg_image_t* img) { 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.num_slices : 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 mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + const uint32_t mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); + // 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; } @@ -12362,9 +12628,9 @@ _SOKOL_PRIVATE uint32_t _sg_wgpu_copy_image_data(WGPUBuffer stg_buf, uint8_t* st 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.num_slices >> mip_index, 1) : 1; + const uint32_t mip_width = _sg_miplevel_dim(img->cmn.width, mip_index); + const uint32_t mip_height = _sg_miplevel_dim(img->cmn.height, mip_index); + const uint32_t mip_depth = (img->cmn.type == SG_IMAGETYPE_3D) ? _sg_miplevel_dim(img->cmn.num_slices, mip_index) : 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); @@ -12376,14 +12642,13 @@ _SOKOL_PRIVATE uint32_t _sg_wgpu_copy_image_data(WGPUBuffer stg_buf, uint8_t* st SOKOL_ASSERT(dst_bytes_per_slice == (dst_bytes_per_row * num_rows)); _SOKOL_UNUSED(src_bytes_per_slice); - /* copy data into mapped staging buffer */ + // copy data into mapped staging buffer if (src_bytes_per_row == dst_bytes_per_row) { - /* can do a single memcpy */ + // can do a single memcpy uint32_t num_bytes = data->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 */ + } 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++) { @@ -12396,7 +12661,7 @@ _SOKOL_PRIVATE uint32_t _sg_wgpu_copy_image_data(WGPUBuffer stg_buf, uint8_t* st } } - /* record the staging copy operation into command encoder */ + // 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; @@ -12438,7 +12703,7 @@ _SOKOL_PRIVATE uint32_t _sg_wgpu_copy_image_data(WGPUBuffer stg_buf, uint8_t* st _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 */ + // there's actually nothing more to do here } _SOKOL_PRIVATE void _sg_wgpu_staging_discard(void) { @@ -12455,7 +12720,7 @@ _SOKOL_PRIVATE void _sg_wgpu_staging_mapped_callback(WGPUBufferMapAsyncStatus st if (!_sg.wgpu.valid) { return; } - /* FIXME: better handling for this */ + // FIXME: better handling for this if (WGPUBufferMapAsyncStatus_Success != status) { SOKOL_ASSERT("Mapping staging buffer failed!\n"); SOKOL_ASSERT(false); @@ -12469,16 +12734,16 @@ _SOKOL_PRIVATE void _sg_wgpu_staging_mapped_callback(WGPUBufferMapAsyncStatus st _SOKOL_PRIVATE void _sg_wgpu_staging_next_frame(bool first_frame) { - /* immediately request a new mapping for the last frame's current staging buffer */ + // 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 */ + // rewind staging-buffer offset _sg.wgpu.staging.offset = 0; - /* check if mapped staging buffer is available, otherwise create one */ + // 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; @@ -12486,7 +12751,7 @@ _SOKOL_PRIVATE void _sg_wgpu_staging_next_frame(bool first_frame) { } } - /* no mapped buffer available, create one */ + // 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; @@ -12517,7 +12782,7 @@ _SOKOL_PRIVATE uint32_t _sg_wgpu_staging_copy_to_buffer(WGPUBuffer dst_buf, uint 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) { - SG_LOG("WGPU: Per frame staging buffer full (in _sg_wgpu_staging_copy_to_buffer())!\n"); + _SG_ERROR(WGPU_STAGING_BUFFER_FULL_COPY_TO_BUFFER); return false; } const int cur = _sg.wgpu.staging.cur; @@ -12532,11 +12797,11 @@ _SOKOL_PRIVATE uint32_t _sg_wgpu_staging_copy_to_buffer(WGPUBuffer dst_buf, uint } _SOKOL_PRIVATE bool _sg_wgpu_staging_copy_to_texture(_sg_image_t* img, const sg_image_data* data) { - /* similar to _sg_wgpu_staging_copy_to_buffer(), but with image data instead */ + // 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_data_buffer_size(img); if ((_sg.wgpu.staging.offset + num_bytes) >= _sg.wgpu.staging.num_bytes) { - SG_LOG("WGPU: Per frame staging buffer full (in _sg_wgpu_staging_copy_to_texture)!\n"); + _SG_ERROR(WGPU_STAGING_BUFFER_FULL_COPY_TO_TEXTURE); return false; } const int cur = _sg.wgpu.staging.cur; @@ -12552,56 +12817,33 @@ _SOKOL_PRIVATE bool _sg_wgpu_staging_copy_to_texture(_sg_image_t* img, const sg_ } _SOKOL_PRIVATE void _sg_wgpu_staging_unmap(void) { - /* called at end of frame before queue-submit */ + // 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; - _sg_clear(&smp_desc, 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 ---*/ + // create a new WGPU sampler + // FIXME: anisotropic filtering not supported? + WGPUSamplerDescriptor smp_desc; + _sg_clear(&smp_desc, 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); + 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); @@ -12623,17 +12865,16 @@ _SOKOL_PRIVATE void _sg_wgpu_setup_backend(const sg_desc* desc) { _sg.wgpu.queue = wgpuDeviceCreateQueue(_sg.wgpu.dev); SOKOL_ASSERT(_sg.wgpu.queue); - /* setup WebGPU features and limits */ + // 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); + // setup the uniform and staging buffer pools _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 */ + // create an empty bind group for shader stages without bound images WGPUBindGroupLayoutDescriptor bgl_desc; _sg_clear(&bgl_desc, sizeof(bgl_desc)); WGPUBindGroupLayout empty_bgl = wgpuDeviceCreateBindGroupLayout(_sg.wgpu.dev, &bgl_desc); @@ -12645,7 +12886,7 @@ _SOKOL_PRIVATE void _sg_wgpu_setup_backend(const sg_desc* desc) { SOKOL_ASSERT(_sg.wgpu.empty_bind_group); wgpuBindGroupLayoutRelease(empty_bgl); - /* create initial per-frame command encoders */ + // create initial per-frame command encoders WGPUCommandEncoderDescriptor cmd_enc_desc; _sg_clear(&cmd_enc_desc, sizeof(cmd_enc_desc)); _sg.wgpu.render_cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); @@ -12661,7 +12902,6 @@ _SOKOL_PRIVATE void _sg_wgpu_discard_backend(void) { _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; @@ -12674,7 +12914,7 @@ _SOKOL_PRIVATE void _sg_wgpu_discard_backend(void) { } _SOKOL_PRIVATE void _sg_wgpu_reset_state_cache(void) { - SG_LOG("_sg_wgpu_reset_state_cache: FIXME\n"); + _SG_WARN(WGPU_RESET_STATE_CACHE_FIXME); } _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_context(_sg_context_t* ctx) { @@ -12690,18 +12930,16 @@ _SOKOL_PRIVATE void _sg_wgpu_discard_context(_sg_context_t* ctx) { _SOKOL_PRIVATE void _sg_wgpu_activate_context(_sg_context_t* ctx) { (void)ctx; - SG_LOG("_sg_wgpu_activate_context: FIXME\n"); + _SG_WARN(WGPU_ACTIVATE_CONTEXT_FIXME); } _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 { + } else { WGPUBufferDescriptor wgpu_buf_desc; _sg_clear(&wgpu_buf_desc, sizeof(wgpu_buf_desc)); wgpu_buf_desc.usage = _sg_wgpu_buffer_usage(buf->cmn.type, buf->cmn.usage); @@ -12713,8 +12951,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_buffer(_sg_buffer_t* buf, const SOKOL_ASSERT(res.data && (res.dataLength == buf->cmn.size)); memcpy(res.data, desc->data.ptr, buf->cmn.size); wgpuBufferUnmap(res.buffer); - } - else { + } else { buf->wgpu.buf = wgpuDeviceCreateBuffer(_sg.wgpu.dev, &wgpu_buf_desc); } } @@ -12737,12 +12974,10 @@ _SOKOL_PRIVATE void _sg_wgpu_init_texdesc_common(WGPUTextureDescriptor* wgpu_tex if (desc->type == SG_IMAGETYPE_3D) { wgpu_tex_desc->size.depth = desc->num_slices; wgpu_tex_desc->arrayLayerCount = 1; - } - else if (desc->type == SG_IMAGETYPE_CUBE) { + } else if (desc->type == SG_IMAGETYPE_CUBE) { wgpu_tex_desc->size.depth = 1; wgpu_tex_desc->arrayLayerCount = 6; - } - else { + } else { wgpu_tex_desc->size.depth = 1; wgpu_tex_desc->arrayLayerCount = desc->num_slices; } @@ -12756,8 +12991,6 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_image(_sg_image_t* img, const s 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; @@ -12775,13 +13008,11 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_image(_sg_image_t* img, const s wgpu_tex_desc.sampleCount = desc->sample_count; img->wgpu.tex = wgpuDeviceCreateTexture(_sg.wgpu.dev, &wgpu_tex_desc); SOKOL_ASSERT(img->wgpu.tex); - } - else { + } else { if (injected) { img->wgpu.tex = (WGPUTexture) desc->wgpu_texture; wgpuTextureReference(img->wgpu.tex); - } - else { + } else { /* NOTE: in the MSAA-rendertarget case, both the MSAA texture *and* the resolve texture need OutputAttachment usage */ @@ -12791,7 +13022,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_image(_sg_image_t* img, const s 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 */ + // copy content into texture via a throw-away staging buffer if (desc->usage == SG_USAGE_IMMUTABLE && !desc->render_target) { WGPUBufferDescriptor wgpu_buf_desc; _sg_clear(&wgpu_buf_desc, sizeof(wgpu_buf_desc)); @@ -12807,7 +13038,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_image(_sg_image_t* img, const s } } - /* create texture view object */ + // create texture view object WGPUTextureViewDescriptor wgpu_view_desc; _sg_clear(&wgpu_view_desc, sizeof(wgpu_view_desc)); wgpu_view_desc.dimension = _sg_wgpu_tex_viewdim(desc->type); @@ -12828,7 +13059,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_image(_sg_image_t* img, const s SOKOL_ASSERT(img->wgpu.msaa_tex); } - /* create sampler via shared-sampler-cache */ + // create sampler via shared-sampler-cache img->wgpu.sampler = _sg_wgpu_create_sampler(desc); SOKOL_ASSERT(img->wgpu.sampler); } @@ -12849,7 +13080,7 @@ _SOKOL_PRIVATE void _sg_wgpu_discard_image(_sg_image_t* img) { wgpuTextureRelease(img->wgpu.msaa_tex); img->wgpu.msaa_tex = 0; } - /* NOTE: do *not* destroy the sampler from the shared-sampler-cache */ + // NOTE: do *not* destroy the sampler from the shared-sampler-cache img->wgpu.sampler = 0; } @@ -12883,7 +13114,6 @@ _SOKOL_PRIVATE void _sg_wgpu_discard_image(_sg_image_t* img) { _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.bytecode.ptr && desc->fs.bytecode.ptr); - _sg_shader_common_init(&shd->cmn, desc); bool success = true; for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { @@ -12903,7 +13133,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_shader(_sg_shader_t* shd, const success = false; } - /* create image/sampler bind group for the shader stage */ + // 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) { @@ -12912,7 +13142,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_shader(_sg_shader_t* shd, const WGPUBindGroupLayoutBinding bglb_desc[_SG_WGPU_MAX_SHADERSTAGE_IMAGES * 2]; _sg_clear(bglb_desc, sizeof(bglb_desc)); for (int img_index = 0; img_index < num_imgs; img_index++) { - /* texture- and sampler-bindings */ + // texture- and sampler-bindings WGPUBindGroupLayoutBinding* tex_desc = &bglb_desc[img_index*2 + 0]; WGPUBindGroupLayoutBinding* smp_desc = &bglb_desc[img_index*2 + 1]; @@ -12957,7 +13187,6 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pipeline(_sg_pipeline_t* pip, _ 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->stencil.ref; WGPUBindGroupLayout pip_bgl[3] = { @@ -12971,12 +13200,12 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pipeline(_sg_pipeline_t* pip, _ pl_desc.bindGroupLayouts = &pip_bgl[0]; WGPUPipelineLayout pip_layout = wgpuDeviceCreatePipelineLayout(_sg.wgpu.dev, &pl_desc); - WGPUVertexBufferLayoutDescriptor vb_desc[SG_MAX_SHADERSTAGE_BUFFERS]; + WGPUVertexBufferLayoutDescriptor vb_desc[SG_MAX_VERTEX_BUFFERS]; _sg_clear(&vb_desc, sizeof(vb_desc)); - WGPUVertexAttributeDescriptor va_desc[SG_MAX_SHADERSTAGE_BUFFERS][SG_MAX_VERTEX_ATTRIBUTES]; + WGPUVertexAttributeDescriptor va_desc[SG_MAX_VERTEX_BUFFERS][SG_MAX_VERTEX_ATTRIBUTES]; _sg_clear(&va_desc, sizeof(va_desc)); int vb_idx = 0; - for (; vb_idx < SG_MAX_SHADERSTAGE_BUFFERS; vb_idx++) { + for (; vb_idx < SG_MAX_VERTEX_BUFFERS; vb_idx++) { const sg_buffer_layout_desc* src_vb_desc = &desc->layout.buffers[vb_idx]; if (0 == src_vb_desc->stride) { break; @@ -12992,7 +13221,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pipeline(_sg_pipeline_t* pip, _ if (SG_VERTEXFORMAT_INVALID == src_va_desc->format) { break; } - pip->cmn.vertex_layout_valid[src_va_desc->buffer_index] = true; + pip->cmn.vertex_buffer_layout_active[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; @@ -13067,7 +13296,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pipeline(_sg_pipeline_t* pip, _ } pip_desc.colorStateCount = desc->color_count; pip_desc.colorStates = cs_desc; - pip_desc.sampleMask = 0xFFFFFFFF; /* FIXME: ??? */ + pip_desc.sampleMask = 0xFFFFFFFF; // FIXME: ??? pip->wgpu.pip = wgpuDeviceCreateRenderPipeline(_sg.wgpu.dev, &pip_desc); SOKOL_ASSERT(0 != pip->wgpu.pip); wgpuPipelineLayoutRelease(pip_layout); @@ -13090,9 +13319,8 @@ _SOKOL_PRIVATE void _sg_wgpu_discard_pipeline(_sg_pipeline_t* pip) { _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 */ + // copy image pointers and create render-texture views const sg_pass_attachment_desc* att_desc; for (uint32_t i = 0; i < pass->cmn.num_color_atts; i++) { att_desc = &desc->color_attachments[i]; @@ -13103,7 +13331,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pass(_sg_pass_t* pass, _sg_imag 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 */ + // create a render-texture-view to render into the right sub-surface const bool is_msaa = img->cmn.sample_count > 1; WGPUTextureViewDescriptor view_desc; _sg_clear(&view_desc, sizeof(view_desc)); @@ -13115,7 +13343,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pass(_sg_pass_t* pass, _sg_imag 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 */ + // ... and if needed a separate resolve texture view if (is_msaa) { view_desc.baseMipLevel = att_desc->mip_level; view_desc.baseArrayLayer = att_desc->slice; @@ -13133,7 +13361,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pass(_sg_pass_t* pass, _sg_imag 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 */ + // create a render-texture view SOKOL_ASSERT(0 == att_desc->mip_level); SOKOL_ASSERT(0 == att_desc->slice); WGPUTextureViewDescriptor view_desc; @@ -13166,12 +13394,12 @@ _SOKOL_PRIVATE void _sg_wgpu_discard_pass(_sg_pass_t* pass) { _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 */ + // 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 */ + // NOTE: may return null SOKOL_ASSERT(pass); return pass->wgpu.ds_att.image; } @@ -13223,9 +13451,8 @@ _SOKOL_PRIVATE void _sg_wgpu_begin_pass(_sg_pass_t* pass, const sg_pass_action* 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 */ + } else { + // default render pass WGPUTextureView wgpu_render_view = _sg.wgpu.render_view_cb ? _sg.wgpu.render_view_cb() : _sg.wgpu.render_view_userdata_cb(_sg.wgpu.user_data); WGPUTextureView wgpu_resolve_view = _sg.wgpu.resolve_view_cb ? _sg.wgpu.resolve_view_cb() : _sg.wgpu.resolve_view_userdata_cb(_sg.wgpu.user_data); WGPUTextureView wgpu_depth_stencil_view = _sg.wgpu.depth_stencil_view_cb ? _sg.wgpu.depth_stencil_view_cb() : _sg.wgpu.depth_stencil_view_userdata_cb(_sg.wgpu.user_data); @@ -13240,7 +13467,7 @@ _SOKOL_PRIVATE void _sg_wgpu_begin_pass(_sg_pass_t* pass, const sg_pass_action* color_att_desc.clearColor.b = action->colors[0].value.b; color_att_desc.clearColor.a = action->colors[0].value.a; color_att_desc.attachment = wgpu_render_view; - color_att_desc.resolveTarget = wgpu_resolve_view; /* null if no MSAA rendering */ + 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; @@ -13256,9 +13483,9 @@ _SOKOL_PRIVATE void _sg_wgpu_begin_pass(_sg_pass_t* pass, const sg_pass_action* } SOKOL_ASSERT(_sg.wgpu.pass_enc); - /* initial uniform buffer binding (required even if no uniforms are set in the frame) */ + // 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 */ + 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]); @@ -13279,7 +13506,7 @@ _SOKOL_PRIVATE void _sg_wgpu_commit(void) { SOKOL_ASSERT(_sg.wgpu.render_cmd_enc); SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); - /* finish and submit this frame's work */ + // finish and submit this frame's work _sg_wgpu_ubpool_flush(); _sg_wgpu_staging_unmap(); @@ -13302,13 +13529,13 @@ _SOKOL_PRIVATE void _sg_wgpu_commit(void) { wgpuCommandBufferRelease(cmd_bufs[0]); wgpuCommandBufferRelease(cmd_bufs[1]); - /* create a new render- and staging-command-encoders for next frame */ + // create a new render- and staging-command-encoders for next frame WGPUCommandEncoderDescriptor cmd_enc_desc; _sg_clear(&cmd_enc_desc, 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 */ + // grab new staging buffers for uniform- and vertex/image-updates _sg_wgpu_ubpool_next_frame(false); _sg_wgpu_staging_next_frame(false); } @@ -13329,7 +13556,7 @@ _SOKOL_PRIVATE void _sg_wgpu_apply_scissor_rect(int x, int y, int w, int h, bool SOKOL_ASSERT(_sg.wgpu.in_pass); SOKOL_ASSERT(_sg.wgpu.pass_enc); - /* clip against framebuffer rect */ + // 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) { @@ -13395,17 +13622,17 @@ _SOKOL_PRIVATE void _sg_wgpu_apply_bindings( SOKOL_ASSERT(_sg.wgpu.pass_enc); SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); - /* index buffer */ + // index buffer if (ib) { wgpuRenderPassEncoderSetIndexBuffer(_sg.wgpu.pass_enc, ib->wgpu.buf, ib_offset); } - /* vertex buffers */ + // 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 */ + // 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; @@ -13415,8 +13642,7 @@ _SOKOL_PRIVATE void _sg_wgpu_apply_bindings( 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 { + } else { wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, 1, _sg.wgpu.empty_bind_group, 0, 0); } if (num_fs_imgs > 0) { @@ -13428,8 +13654,7 @@ _SOKOL_PRIVATE void _sg_wgpu_apply_bindings( 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 { + } else { wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, 2, _sg.wgpu.empty_bind_group, 0, 0); } } @@ -13451,7 +13676,7 @@ _SOKOL_PRIVATE void _sg_wgpu_apply_uniforms(sg_shader_stage stage_index, int ub_ memcpy(dst_ptr, data->ptr, data->size); _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 */ + 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]); @@ -13463,8 +13688,7 @@ _SOKOL_PRIVATE void _sg_wgpu_draw(int base_element, int num_elements, int num_in 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 { + } else { wgpuRenderPassEncoderDraw(_sg.wgpu.pass_enc, num_elements, num_instances, base_element, 0); } } @@ -13491,7 +13715,13 @@ _SOKOL_PRIVATE void _sg_wgpu_update_image(_sg_image_t* img, const sg_image_data* } #endif -/*== BACKEND API WRAPPERS ====================================================*/ +// ██████ ███████ ███ ██ ███████ ██████ ██ ██████ ██████ █████ ██████ ██ ██ ███████ ███ ██ ██████ +// ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██ ███ █████ ██ ██ ██ █████ ██████ ██ ██ ██████ ███████ ██ █████ █████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ███████ ██ ████ ███████ ██ ██ ██ ██████ ██████ ██ ██ ██████ ██ ██ ███████ ██ ████ ██████ +// +// >>generic backend static inline void _sg_setup_backend(const sg_desc* desc) { #if defined(_SOKOL_ANY_GL) _sg_gl_setup_backend(desc); @@ -13652,6 +13882,38 @@ static inline void _sg_discard_image(_sg_image_t* img) { #endif } +static inline sg_resource_state _sg_create_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_create_sampler(smp, desc); + #elif defined(SOKOL_METAL) + return _sg_mtl_create_sampler(smp, desc); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_create_sampler(smp, desc); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_create_sampler(smp, desc); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_sampler(smp, desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_discard_sampler(_sg_sampler_t* smp) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_discard_sampler(smp); + #elif defined(SOKOL_METAL) + _sg_mtl_discard_sampler(smp); + #elif defined(SOKOL_D3D11) + _sg_d3d11_discard_sampler(smp); + #elif defined(SOKOL_WGPU) + _sg_wgpu_discard_sampler(smp); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_discard_sampler(smp); + #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); @@ -13716,17 +13978,17 @@ static inline void _sg_discard_pipeline(_sg_pipeline_t* pip) { #endif } -static inline sg_resource_state _sg_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { +static inline sg_resource_state _sg_create_pass(_sg_pass_t* pass, _sg_image_t** color_images, _sg_image_t** resolve_images, _sg_image_t* ds_image, const sg_pass_desc* desc) { #if defined(_SOKOL_ANY_GL) - return _sg_gl_create_pass(pass, att_images, desc); + return _sg_gl_create_pass(pass, color_images, resolve_images, ds_image, desc); #elif defined(SOKOL_METAL) - return _sg_mtl_create_pass(pass, att_images, desc); + return _sg_mtl_create_pass(pass, color_images, resolve_images, ds_image, desc); #elif defined(SOKOL_D3D11) - return _sg_d3d11_create_pass(pass, att_images, desc); + return _sg_d3d11_create_pass(pass, color_images, resolve_images, ds_image, desc); #elif defined(SOKOL_WGPU) - return _sg_wgpu_create_pass(pass, att_images, desc); + return _sg_wgpu_create_pass(pass, color_images, resolve_images, ds_image, desc); #elif defined(SOKOL_DUMMY_BACKEND) - return _sg_dummy_create_pass(pass, att_images, desc); + return _sg_dummy_create_pass(pass, color_images, resolve_images, ds_image, desc); #else #error("INVALID BACKEND"); #endif @@ -13764,6 +14026,22 @@ static inline _sg_image_t* _sg_pass_color_image(const _sg_pass_t* pass, int inde #endif } +static inline _sg_image_t* _sg_pass_resolve_image(const _sg_pass_t* pass, int index) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_pass_resolve_image(pass, index); + #elif defined(SOKOL_METAL) + return _sg_mtl_pass_resolve_image(pass, index); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_pass_resolve_image(pass, index); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_pass_resolve_image(pass, index); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_pass_resolve_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); @@ -13865,18 +14143,20 @@ static inline void _sg_apply_bindings( _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) + _sg_image_t** fs_imgs, int num_fs_imgs, + _sg_sampler_t** vs_smps, int num_vs_smps, + _sg_sampler_t** fs_smps, int num_fs_smps) { #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); + _sg_gl_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs, vs_smps, num_vs_smps, fs_smps, num_fs_smps); #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); + _sg_mtl_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs, vs_smps, num_vs_smps, fs_smps, num_fs_smps); #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); + _sg_d3d11_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs, vs_smps, num_vs_smps, fs_smps, num_fs_smps); #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); + _sg_wgpu_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs, vs_smps, num_vs_smps, fs_smps, num_fs_smps); #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); + _sg_dummy_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs, vs_smps, num_vs_smps, fs_smps, num_fs_smps); #else #error("INVALID BACKEND"); #endif @@ -13978,19 +14258,38 @@ static inline void _sg_update_image(_sg_image_t* img, const sg_image_data* data) #endif } -/*== RESOURCE POOLS ==========================================================*/ +static inline void _sg_push_debug_group(const char* name) { + #if defined(SOKOL_METAL) + _sg_mtl_push_debug_group(name); + #else + _SOKOL_UNUSED(name); + #endif +} + +static inline void _sg_pop_debug_group(void) { + #if defined(SOKOL_METAL) + _sg_mtl_pop_debug_group(); + #endif +} +// ██████ ██████ ██████ ██ +// ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ +// +// >>pool _SOKOL_PRIVATE void _sg_init_pool(_sg_pool_t* pool, int num) { SOKOL_ASSERT(pool && (num >= 1)); - /* slot 0 is reserved for the 'invalid id', so bump the pool size by 1 */ + // slot 0 is reserved for the 'invalid id', so bump the pool size by 1 pool->size = num + 1; pool->queue_top = 0; - /* generation counters indexable by pool slot index, slot 0 is reserved */ + // generation counters indexable by pool slot index, slot 0 is reserved size_t gen_ctrs_size = sizeof(uint32_t) * (size_t)pool->size; pool->gen_ctrs = (uint32_t*)_sg_malloc_clear(gen_ctrs_size); - /* it's not a bug to only reserve 'num' here */ + // it's not a bug to only reserve 'num' here pool->free_queue = (int*) _sg_malloc_clear(sizeof(int) * (size_t)num); - /* never allocate the zero-th pool item since the invalid id is 0 */ + // never allocate the zero-th pool item since the invalid id is 0 for (int i = pool->size-1; i >= 1; i--) { pool->free_queue[pool->queue_top++] = i; } @@ -14015,9 +14314,8 @@ _SOKOL_PRIVATE int _sg_pool_alloc_index(_sg_pool_t* pool) { int slot_index = pool->free_queue[--pool->queue_top]; SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size)); return slot_index; - } - else { - /* pool exhausted */ + } else { + // pool exhausted return _SG_INVALID_SLOT_INDEX; } } @@ -14028,7 +14326,7 @@ _SOKOL_PRIVATE void _sg_pool_free_index(_sg_pool_t* pool, int slot_index) { SOKOL_ASSERT(pool->free_queue); SOKOL_ASSERT(pool->queue_top < pool->size); #ifdef SOKOL_DEBUG - /* debug check against double-free */ + // debug check against double-free for (int i = 0; i < pool->queue_top; i++) { SOKOL_ASSERT(pool->free_queue[i] != slot_index); } @@ -14058,6 +14356,14 @@ _SOKOL_PRIVATE void _sg_reset_image_to_alloc_state(_sg_image_t* img) { img->slot.state = SG_RESOURCESTATE_ALLOC; } +_SOKOL_PRIVATE void _sg_reset_sampler_to_alloc_state(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp); + _sg_slot_t slot = smp->slot; + _sg_clear(smp, sizeof(_sg_sampler_t)); + smp->slot = slot; + smp->slot.state = SG_RESOURCESTATE_ALLOC; +} + _SOKOL_PRIVATE void _sg_reset_shader_to_alloc_state(_sg_shader_t* shd) { SOKOL_ASSERT(shd); _sg_slot_t slot = shd->slot; @@ -14093,7 +14399,7 @@ _SOKOL_PRIVATE void _sg_reset_context_to_alloc_state(_sg_context_t* ctx) { _SOKOL_PRIVATE void _sg_setup_pools(_sg_pools_t* p, const sg_desc* desc) { SOKOL_ASSERT(p); SOKOL_ASSERT(desc); - /* note: the pools here will have an additional item, since slot 0 is reserved */ + // note: the pools here will have an additional item, since slot 0 is reserved SOKOL_ASSERT((desc->buffer_pool_size > 0) && (desc->buffer_pool_size < _SG_MAX_POOL_SIZE)); _sg_init_pool(&p->buffer_pool, desc->buffer_pool_size); size_t buffer_pool_byte_size = sizeof(_sg_buffer_t) * (size_t)p->buffer_pool.size; @@ -14104,6 +14410,11 @@ _SOKOL_PRIVATE void _sg_setup_pools(_sg_pools_t* p, const sg_desc* desc) { size_t image_pool_byte_size = sizeof(_sg_image_t) * (size_t)p->image_pool.size; p->images = (_sg_image_t*) _sg_malloc_clear(image_pool_byte_size); + SOKOL_ASSERT((desc->sampler_pool_size > 0) && (desc->sampler_pool_size < _SG_MAX_POOL_SIZE)); + _sg_init_pool(&p->sampler_pool, desc->sampler_pool_size); + size_t sampler_pool_byte_size = sizeof(_sg_sampler_t) * (size_t)p->sampler_pool.size; + p->samplers = (_sg_sampler_t*) _sg_malloc_clear(sampler_pool_byte_size); + SOKOL_ASSERT((desc->shader_pool_size > 0) && (desc->shader_pool_size < _SG_MAX_POOL_SIZE)); _sg_init_pool(&p->shader_pool, desc->shader_pool_size); size_t shader_pool_byte_size = sizeof(_sg_shader_t) * (size_t)p->shader_pool.size; @@ -14131,12 +14442,14 @@ _SOKOL_PRIVATE void _sg_discard_pools(_sg_pools_t* p) { _sg_free(p->passes); p->passes = 0; _sg_free(p->pipelines); p->pipelines = 0; _sg_free(p->shaders); p->shaders = 0; + _sg_free(p->samplers); p->samplers = 0; _sg_free(p->images); p->images = 0; _sg_free(p->buffers); p->buffers = 0; _sg_discard_pool(&p->context_pool); _sg_discard_pool(&p->pass_pool); _sg_discard_pool(&p->pipeline_pool); _sg_discard_pool(&p->shader_pool); + _sg_discard_pool(&p->sampler_pool); _sg_discard_pool(&p->image_pool); _sg_discard_pool(&p->buffer_pool); } @@ -14162,14 +14475,14 @@ _SOKOL_PRIVATE uint32_t _sg_slot_alloc(_sg_pool_t* pool, _sg_slot_t* slot, int s return slot->id; } -/* extract slot index from id */ +// extract slot index from id _SOKOL_PRIVATE int _sg_slot_index(uint32_t id) { int slot_index = (int) (id & _SG_SLOT_MASK); SOKOL_ASSERT(_SG_INVALID_SLOT_INDEX != slot_index); return slot_index; } -/* returns pointer to resource by id without matching id check */ +// returns pointer to resource by id without matching id check _SOKOL_PRIVATE _sg_buffer_t* _sg_buffer_at(const _sg_pools_t* p, uint32_t buf_id) { SOKOL_ASSERT(p && (SG_INVALID_ID != buf_id)); int slot_index = _sg_slot_index(buf_id); @@ -14184,6 +14497,13 @@ _SOKOL_PRIVATE _sg_image_t* _sg_image_at(const _sg_pools_t* p, uint32_t img_id) return &p->images[slot_index]; } +_SOKOL_PRIVATE _sg_sampler_t* _sg_sampler_at(const _sg_pools_t* p, uint32_t smp_id) { + SOKOL_ASSERT(p && (SG_INVALID_ID != smp_id)); + int slot_index = _sg_slot_index(smp_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->sampler_pool.size)); + return &p->samplers[slot_index]; +} + _SOKOL_PRIVATE _sg_shader_t* _sg_shader_at(const _sg_pools_t* p, uint32_t shd_id) { SOKOL_ASSERT(p && (SG_INVALID_ID != shd_id)); int slot_index = _sg_slot_index(shd_id); @@ -14212,7 +14532,7 @@ _SOKOL_PRIVATE _sg_context_t* _sg_context_at(const _sg_pools_t* p, uint32_t cont return &p->contexts[slot_index]; } -/* returns pointer to resource with matching id check, may return 0 */ +// returns pointer to resource with matching id check, may return 0 _SOKOL_PRIVATE _sg_buffer_t* _sg_lookup_buffer(const _sg_pools_t* p, uint32_t buf_id) { if (SG_INVALID_ID != buf_id) { _sg_buffer_t* buf = _sg_buffer_at(p, buf_id); @@ -14233,6 +14553,16 @@ _SOKOL_PRIVATE _sg_image_t* _sg_lookup_image(const _sg_pools_t* p, uint32_t img_ return 0; } +_SOKOL_PRIVATE _sg_sampler_t* _sg_lookup_sampler(const _sg_pools_t* p, uint32_t smp_id) { + if (SG_INVALID_ID != smp_id) { + _sg_sampler_t* smp = _sg_sampler_at(p, smp_id); + if (smp->slot.id == smp_id) { + return smp; + } + } + return 0; +} + _SOKOL_PRIVATE _sg_shader_t* _sg_lookup_shader(const _sg_pools_t* p, uint32_t shd_id) { SOKOL_ASSERT(p); if (SG_INVALID_ID != shd_id) { @@ -14301,6 +14631,14 @@ _SOKOL_PRIVATE void _sg_discard_all_resources(_sg_pools_t* p, uint32_t ctx_id) { } } } + for (int i = 1; i < p->sampler_pool.size; i++) { + if (p->samplers[i].slot.ctx_id == ctx_id) { + sg_resource_state state = p->samplers[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_discard_sampler(&p->samplers[i]); + } + } + } for (int i = 1; i < p->shader_pool.size; i++) { if (p->shaders[i].slot.ctx_id == ctx_id) { sg_resource_state state = p->shaders[i].slot.state; @@ -14327,161 +14665,27 @@ _SOKOL_PRIVATE void _sg_discard_all_resources(_sg_pools_t* p, uint32_t ctx_id) { } } -/*== VALIDATION LAYER ========================================================*/ -#if defined(SOKOL_DEBUG) -/* return a human readable string for an _sg_validate_error */ -_SOKOL_PRIVATE const char* _sg_validate_string(_sg_validate_error_t err) { - switch (err) { - /* buffer creation validation errors */ - case _SG_VALIDATE_BUFFERDESC_CANARY: return "sg_buffer_desc not initialized"; - case _SG_VALIDATE_BUFFERDESC_SIZE: return "sg_buffer_desc.size cannot be 0"; - case _SG_VALIDATE_BUFFERDESC_DATA: return "immutable buffers must be initialized with data (sg_buffer_desc.data.ptr and sg_buffer_desc.data.size)"; - case _SG_VALIDATE_BUFFERDESC_DATA_SIZE: return "immutable buffer data size differs from buffer size"; - case _SG_VALIDATE_BUFFERDESC_NO_DATA: return "dynamic/stream usage buffers cannot be initialized with data"; - - /* image data (in image creation and updating) */ - case _SG_VALIDATE_IMAGEDATA_NODATA: return "sg_image_data: no data (.ptr and/or .size is zero)"; - case _SG_VALIDATE_IMAGEDATA_DATA_SIZE: return "sg_image_data: data size doesn't match expected surface size"; - - /* image creation validation errros */ - case _SG_VALIDATE_IMAGEDESC_CANARY: return "sg_image_desc not initialized"; - case _SG_VALIDATE_IMAGEDESC_WIDTH: return "sg_image_desc.width must be > 0"; - case _SG_VALIDATE_IMAGEDESC_HEIGHT: return "sg_image_desc.height must be > 0"; - case _SG_VALIDATE_IMAGEDESC_RT_PIXELFORMAT: return "invalid pixel format for render-target image"; - case _SG_VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT: return "invalid pixel format for non-render-target image"; - case _SG_VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT: return "non-render-target images cannot be multisampled"; - case _SG_VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT: return "MSAA not supported for this pixel format"; - case _SG_VALIDATE_IMAGEDESC_RT_IMMUTABLE: return "render target images must be SG_USAGE_IMMUTABLE"; - case _SG_VALIDATE_IMAGEDESC_RT_NO_DATA: return "render target images cannot be initialized with data"; - case _SG_VALIDATE_IMAGEDESC_INJECTED_NO_DATA: return "images with injected textures cannot be initialized with data"; - case _SG_VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA: return "dynamic/stream images cannot be initialized with data"; - case _SG_VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE: return "compressed images must be immutable"; - - /* shader creation */ - case _SG_VALIDATE_SHADERDESC_CANARY: return "sg_shader_desc not initialized"; - case _SG_VALIDATE_SHADERDESC_SOURCE: return "shader source code required"; - case _SG_VALIDATE_SHADERDESC_BYTECODE: return "shader byte code required"; - case _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE: return "shader source or byte code required"; - case _SG_VALIDATE_SHADERDESC_NO_BYTECODE_SIZE: return "shader byte code length (in bytes) required"; - case _SG_VALIDATE_SHADERDESC_NO_CONT_UBS: return "shader uniform blocks must occupy continuous slots"; - case _SG_VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS: return "uniform block members must occupy continuous slots"; - case _SG_VALIDATE_SHADERDESC_NO_UB_MEMBERS: return "GL backend requires uniform block member declarations"; - case _SG_VALIDATE_SHADERDESC_UB_MEMBER_NAME: return "uniform block member name missing"; - case _SG_VALIDATE_SHADERDESC_UB_SIZE_MISMATCH: return "size of uniform block members doesn't match uniform block size"; - case _SG_VALIDATE_SHADERDESC_UB_ARRAY_COUNT: return "uniform array count must be >= 1"; - case _SG_VALIDATE_SHADERDESC_UB_STD140_ARRAY_TYPE: return "uniform arrays only allowed for FLOAT4, INT4, MAT4 in std140 layout"; - - case _SG_VALIDATE_SHADERDESC_NO_CONT_IMGS: return "shader images must occupy continuous slots"; - case _SG_VALIDATE_SHADERDESC_IMG_NAME: return "GL backend requires uniform block member names"; - case _SG_VALIDATE_SHADERDESC_ATTR_NAMES: return "GLES2 backend requires vertex attribute names"; - case _SG_VALIDATE_SHADERDESC_ATTR_SEMANTICS: return "D3D11 backend requires vertex attribute semantics"; - case _SG_VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG: return "vertex attribute name/semantic string too long (max len 16)"; - - /* pipeline creation */ - case _SG_VALIDATE_PIPELINEDESC_CANARY: return "sg_pipeline_desc not initialized"; - case _SG_VALIDATE_PIPELINEDESC_SHADER: return "sg_pipeline_desc.shader missing or invalid"; - case _SG_VALIDATE_PIPELINEDESC_NO_ATTRS: return "sg_pipeline_desc.layout.attrs is empty or not continuous"; - case _SG_VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4: return "sg_pipeline_desc.layout.buffers[].stride must be multiple of 4"; - case _SG_VALIDATE_PIPELINEDESC_ATTR_NAME: return "GLES2/WebGL missing vertex attribute name in shader"; - case _SG_VALIDATE_PIPELINEDESC_ATTR_SEMANTICS: return "D3D11 missing vertex attribute semantics in shader"; - - /* pass creation */ - case _SG_VALIDATE_PASSDESC_CANARY: return "sg_pass_desc not initialized"; - case _SG_VALIDATE_PASSDESC_NO_COLOR_ATTS: return "sg_pass_desc.color_attachments[0] must be valid"; - case _SG_VALIDATE_PASSDESC_NO_CONT_COLOR_ATTS: return "color attachments must occupy continuous slots"; - case _SG_VALIDATE_PASSDESC_IMAGE: return "pass attachment image is not valid"; - case _SG_VALIDATE_PASSDESC_MIPLEVEL: return "pass attachment mip level is bigger than image has mipmaps"; - case _SG_VALIDATE_PASSDESC_FACE: return "pass attachment image is cubemap, but face index is too big"; - case _SG_VALIDATE_PASSDESC_LAYER: return "pass attachment image is array texture, but layer index is too big"; - case _SG_VALIDATE_PASSDESC_SLICE: return "pass attachment image is 3d texture, but slice value is too big"; - case _SG_VALIDATE_PASSDESC_IMAGE_NO_RT: return "pass attachment image must be render targets"; - case _SG_VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT: return "pass color-attachment images must have a renderable pixel format"; - case _SG_VALIDATE_PASSDESC_DEPTH_INV_PIXELFORMAT: return "pass depth-attachment image must have depth pixel format"; - case _SG_VALIDATE_PASSDESC_IMAGE_SIZES: return "all pass attachments must have the same size"; - case _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS: return "all pass attachments must have the same sample count"; - - /* sg_begin_pass */ - case _SG_VALIDATE_BEGINPASS_PASS: return "sg_begin_pass: pass must be valid"; - case _SG_VALIDATE_BEGINPASS_IMAGE: return "sg_begin_pass: one or more attachment images are not valid"; - - /* sg_apply_pipeline */ - case _SG_VALIDATE_APIP_PIPELINE_VALID_ID: return "sg_apply_pipeline: invalid pipeline id provided"; - case _SG_VALIDATE_APIP_PIPELINE_EXISTS: return "sg_apply_pipeline: pipeline object no longer alive"; - case _SG_VALIDATE_APIP_PIPELINE_VALID: return "sg_apply_pipeline: pipeline object not in valid state"; - case _SG_VALIDATE_APIP_SHADER_EXISTS: return "sg_apply_pipeline: shader object no longer alive"; - case _SG_VALIDATE_APIP_SHADER_VALID: return "sg_apply_pipeline: shader object not in valid state"; - case _SG_VALIDATE_APIP_ATT_COUNT: return "sg_apply_pipeline: number of pipeline color attachments doesn't match number of pass color attachments"; - case _SG_VALIDATE_APIP_COLOR_FORMAT: return "sg_apply_pipeline: pipeline color attachment pixel format doesn't match pass color attachment pixel format"; - case _SG_VALIDATE_APIP_DEPTH_FORMAT: return "sg_apply_pipeline: pipeline depth pixel_format doesn't match pass depth attachment pixel format"; - case _SG_VALIDATE_APIP_SAMPLE_COUNT: return "sg_apply_pipeline: pipeline MSAA sample count doesn't match render pass attachment sample count"; - - /* sg_apply_bindings */ - case _SG_VALIDATE_ABND_PIPELINE: return "sg_apply_bindings: must be called after sg_apply_pipeline"; - case _SG_VALIDATE_ABND_PIPELINE_EXISTS: return "sg_apply_bindings: currently applied pipeline object no longer alive"; - case _SG_VALIDATE_ABND_PIPELINE_VALID: return "sg_apply_bindings: currently applied pipeline object not in valid state"; - case _SG_VALIDATE_ABND_VBS: return "sg_apply_bindings: number of vertex buffers doesn't match number of pipeline vertex layouts"; - case _SG_VALIDATE_ABND_VB_EXISTS: return "sg_apply_bindings: vertex buffer no longer alive"; - case _SG_VALIDATE_ABND_VB_TYPE: return "sg_apply_bindings: buffer in vertex buffer slot is not a SG_BUFFERTYPE_VERTEXBUFFER"; - case _SG_VALIDATE_ABND_VB_OVERFLOW: return "sg_apply_bindings: buffer in vertex buffer slot is overflown"; - case _SG_VALIDATE_ABND_NO_IB: return "sg_apply_bindings: pipeline object defines indexed rendering, but no index buffer provided"; - case _SG_VALIDATE_ABND_IB: return "sg_apply_bindings: pipeline object defines non-indexed rendering, but index buffer provided"; - case _SG_VALIDATE_ABND_IB_EXISTS: return "sg_apply_bindings: index buffer no longer alive"; - case _SG_VALIDATE_ABND_IB_TYPE: return "sg_apply_bindings: buffer in index buffer slot is not a SG_BUFFERTYPE_INDEXBUFFER"; - case _SG_VALIDATE_ABND_IB_OVERFLOW: return "sg_apply_bindings: buffer in index buffer slot is overflown"; - case _SG_VALIDATE_ABND_VS_IMGS: return "sg_apply_bindings: vertex shader image count doesn't match sg_shader_desc"; - case _SG_VALIDATE_ABND_VS_IMG_EXISTS: return "sg_apply_bindings: vertex shader image no longer alive"; - case _SG_VALIDATE_ABND_VS_IMG_TYPES: return "sg_apply_bindings: one or more vertex shader image types don't match sg_shader_desc"; - case _SG_VALIDATE_ABND_FS_IMGS: return "sg_apply_bindings: fragment shader image count doesn't match sg_shader_desc"; - case _SG_VALIDATE_ABND_FS_IMG_EXISTS: return "sg_apply_bindings: fragment shader image no longer alive"; - case _SG_VALIDATE_ABND_FS_IMG_TYPES: return "sg_apply_bindings: one or more fragment shader image types don't match sg_shader_desc"; - - /* sg_apply_uniforms */ - case _SG_VALIDATE_AUB_NO_PIPELINE: return "sg_apply_uniforms: must be called after sg_apply_pipeline()"; - case _SG_VALIDATE_AUB_NO_UB_AT_SLOT: return "sg_apply_uniforms: no uniform block declaration at this shader stage UB slot"; - case _SG_VALIDATE_AUB_SIZE: return "sg_apply_uniforms: data size exceeds declared uniform block size"; - - /* sg_update_buffer */ - case _SG_VALIDATE_UPDATEBUF_USAGE: return "sg_update_buffer: cannot update immutable buffer"; - case _SG_VALIDATE_UPDATEBUF_SIZE: return "sg_update_buffer: update size is bigger than buffer size"; - case _SG_VALIDATE_UPDATEBUF_ONCE: return "sg_update_buffer: only one update allowed per buffer and frame"; - case _SG_VALIDATE_UPDATEBUF_APPEND: return "sg_update_buffer: cannot call sg_update_buffer and sg_append_buffer in same frame"; - - /* sg_append_buffer */ - case _SG_VALIDATE_APPENDBUF_USAGE: return "sg_append_buffer: cannot append to immutable buffer"; - case _SG_VALIDATE_APPENDBUF_SIZE: return "sg_append_buffer: overall appended size is bigger than buffer size"; - case _SG_VALIDATE_APPENDBUF_UPDATE: return "sg_append_buffer: cannot call sg_append_buffer and sg_update_buffer in same frame"; - - /* sg_update_image */ - case _SG_VALIDATE_UPDIMG_USAGE: return "sg_update_image: cannot update immutable image"; - case _SG_VALIDATE_UPDIMG_ONCE: return "sg_update_image: only one update allowed per image and frame"; - - default: return "unknown validation error"; - } -} -#endif /* defined(SOKOL_DEBUG) */ - -/*-- validation checks -------------------------------------------------------*/ +// ██ ██ █████ ██ ██ ██████ █████ ████████ ██ ██████ ███ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ███████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ████ ██ ██ ███████ ██ ██████ ██ ██ ██ ██ ██████ ██ ████ +// +// >>validation #if defined(SOKOL_DEBUG) _SOKOL_PRIVATE void _sg_validate_begin(void) { - _sg.validate_error = _SG_VALIDATE_SUCCESS; -} - -_SOKOL_PRIVATE void _sg_validate(bool cond, _sg_validate_error_t err) { - if (!cond) { - _sg.validate_error = err; - SG_LOG(_sg_validate_string(err)); - } + _sg.validate_error = SG_LOGITEM_OK; } _SOKOL_PRIVATE bool _sg_validate_end(void) { - if (_sg.validate_error != _SG_VALIDATE_SUCCESS) { + if (_sg.validate_error != SG_LOGITEM_OK) { #if !defined(SOKOL_VALIDATE_NON_FATAL) - SG_LOG("^^^^ SOKOL-GFX VALIDATION FAILED, TERMINATING ^^^^"); - SOKOL_ASSERT(false); + _SG_PANIC(VALIDATION_FAILED); + return false; + #else + return false; #endif - return false; - } - else { + } else { return true; } } @@ -14496,22 +14700,21 @@ _SOKOL_PRIVATE bool _sg_validate_buffer_desc(const sg_buffer_desc* desc) { return true; } SOKOL_ASSERT(desc); - SOKOL_VALIDATE_BEGIN(); - 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); + _sg_validate_begin(); + _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_BUFFERDESC_CANARY); + _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_BUFFERDESC_CANARY); + _SG_VALIDATE(desc->size > 0, VALIDATE_BUFFERDESC_SIZE); 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->data.ptr) && (desc->data.size > 0), _SG_VALIDATE_BUFFERDESC_DATA); - SOKOL_VALIDATE(desc->size == desc->data.size, _SG_VALIDATE_BUFFERDESC_DATA_SIZE); + _SG_VALIDATE((0 != desc->data.ptr) && (desc->data.size > 0), VALIDATE_BUFFERDESC_DATA); + _SG_VALIDATE(desc->size == desc->data.size, VALIDATE_BUFFERDESC_DATA_SIZE); + } else { + _SG_VALIDATE(0 == desc->data.ptr, VALIDATE_BUFFERDESC_NO_DATA); } - else { - SOKOL_VALIDATE(0 == desc->data.ptr, _SG_VALIDATE_BUFFERDESC_NO_DATA); - } - return SOKOL_VALIDATE_END(); + return _sg_validate_end(); #endif } @@ -14529,12 +14732,12 @@ _SOKOL_PRIVATE void _sg_validate_image_data(const sg_image_data* data, sg_pixel_ for (int mip_index = 0; mip_index < num_mips; mip_index++) { const bool has_data = data->subimage[face_index][mip_index].ptr != 0; const bool has_size = data->subimage[face_index][mip_index].size > 0; - SOKOL_VALIDATE(has_data && has_size, _SG_VALIDATE_IMAGEDATA_NODATA); - const int mip_width = _sg_max(width >> mip_index, 1); - const int mip_height = _sg_max(height >> mip_index, 1); + _SG_VALIDATE(has_data && has_size, VALIDATE_IMAGEDATA_NODATA); + const int mip_width = _sg_miplevel_dim(width, mip_index); + const int mip_height = _sg_miplevel_dim(height, mip_index); const int bytes_per_slice = _sg_surface_pitch(fmt, mip_width, mip_height, 1); const int expected_size = bytes_per_slice * num_slices; - SOKOL_VALIDATE(expected_size == (int)data->subimage[face_index][mip_index].size, _SG_VALIDATE_IMAGEDATA_DATA_SIZE); + _SG_VALIDATE(expected_size == (int)data->subimage[face_index][mip_index].size, VALIDATE_IMAGEDATA_DATA_SIZE); } } #endif @@ -14549,41 +14752,38 @@ _SOKOL_PRIVATE bool _sg_validate_image_desc(const sg_image_desc* desc) { return true; } SOKOL_ASSERT(desc); - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_IMAGEDESC_CANARY); - SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_IMAGEDESC_CANARY); - SOKOL_VALIDATE(desc->width > 0, _SG_VALIDATE_IMAGEDESC_WIDTH); - SOKOL_VALIDATE(desc->height > 0, _SG_VALIDATE_IMAGEDESC_HEIGHT); + _sg_validate_begin(); + _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_IMAGEDESC_CANARY); + _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_IMAGEDESC_CANARY); + _SG_VALIDATE(desc->width > 0, VALIDATE_IMAGEDESC_WIDTH); + _SG_VALIDATE(desc->height > 0, VALIDATE_IMAGEDESC_HEIGHT); const sg_pixel_format fmt = desc->pixel_format; const sg_usage usage = desc->usage; const bool injected = (0 != desc->gl_textures[0]) || (0 != desc->mtl_textures[0]) || (0 != desc->d3d11_texture) || (0 != desc->wgpu_texture); + if (_sg_is_depth_or_depth_stencil_format(fmt)) { + _SG_VALIDATE(desc->type != SG_IMAGETYPE_3D, VALIDATE_IMAGEDESC_DEPTH_3D_IMAGE); + } 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); - /* on GLES2, sample count for render targets is completely ignored */ - #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) - if (!_sg.gl.gles2) { - #endif - if (desc->sample_count > 1) { - SOKOL_VALIDATE(_sg.features.msaa_render_targets && _sg.formats[fmt].msaa, _SG_VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT); - } - #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + _SG_VALIDATE(_sg.formats[fmt].render, VALIDATE_IMAGEDESC_RT_PIXELFORMAT); + _SG_VALIDATE(usage == SG_USAGE_IMMUTABLE, VALIDATE_IMAGEDESC_RT_IMMUTABLE); + _SG_VALIDATE(desc->data.subimage[0][0].ptr==0, VALIDATE_IMAGEDESC_RT_NO_DATA); + if (desc->sample_count > 1) { + _SG_VALIDATE(_sg.formats[fmt].msaa, VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT); + _SG_VALIDATE(desc->num_mipmaps == 1, VALIDATE_IMAGEDESC_MSAA_NUM_MIPMAPS); + _SG_VALIDATE(desc->type != SG_IMAGETYPE_3D, VALIDATE_IMAGEDESC_MSAA_3D_IMAGE); } - #endif - SOKOL_VALIDATE(usage == SG_USAGE_IMMUTABLE, _SG_VALIDATE_IMAGEDESC_RT_IMMUTABLE); - SOKOL_VALIDATE(desc->data.subimage[0][0].ptr==0, _SG_VALIDATE_IMAGEDESC_RT_NO_DATA); - } - else { - SOKOL_VALIDATE(desc->sample_count <= 1, _SG_VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT); + } else { + _SG_VALIDATE(desc->sample_count == 1, VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT); const bool valid_nonrt_fmt = !_sg_is_valid_rendertarget_depth_format(fmt); - SOKOL_VALIDATE(valid_nonrt_fmt, _SG_VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT); + _SG_VALIDATE(valid_nonrt_fmt, VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT); const bool is_compressed = _sg_is_compressed_pixel_format(desc->pixel_format); const bool is_immutable = (usage == SG_USAGE_IMMUTABLE); if (is_compressed) { - SOKOL_VALIDATE(is_immutable, _SG_VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE); + _SG_VALIDATE(is_immutable, VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE); } if (!injected && is_immutable) { // image desc must have valid data @@ -14594,24 +14794,41 @@ _SOKOL_PRIVATE bool _sg_validate_image_desc(const sg_image_desc* desc) { (desc->type == SG_IMAGETYPE_CUBE) ? 6 : 1, desc->num_mipmaps, desc->num_slices); - } - else { + } else { // image desc must not have data for (int face_index = 0; face_index < SG_CUBEFACE_NUM; face_index++) { for (int mip_index = 0; mip_index < SG_MAX_MIPMAPS; mip_index++) { const bool no_data = 0 == desc->data.subimage[face_index][mip_index].ptr; const bool no_size = 0 == desc->data.subimage[face_index][mip_index].size; if (injected) { - SOKOL_VALIDATE(no_data && no_size, _SG_VALIDATE_IMAGEDESC_INJECTED_NO_DATA); + _SG_VALIDATE(no_data && no_size, VALIDATE_IMAGEDESC_INJECTED_NO_DATA); } if (!is_immutable) { - SOKOL_VALIDATE(no_data && no_size, _SG_VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA); + _SG_VALIDATE(no_data && no_size, VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA); } } } } } - return SOKOL_VALIDATE_END(); + return _sg_validate_end(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_sampler_desc(const sg_sampler_desc* desc) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; + #else + if (_sg.desc.disable_validation) { + return true; + } + SOKOL_ASSERT(desc); + _sg_validate_begin(); + _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_SAMPLERDESC_CANARY); + _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_SAMPLERDESC_CANARY); + _SG_VALIDATE(desc->min_filter != SG_FILTER_NONE, VALIDATE_SAMPLERDESC_MINFILTER_NONE); + _SG_VALIDATE(desc->mag_filter != SG_FILTER_NONE, VALIDATE_SAMPLERDESC_MAGFILTER_NONE); + return _sg_validate_end(); #endif } @@ -14624,43 +14841,41 @@ _SOKOL_PRIVATE bool _sg_validate_shader_desc(const sg_shader_desc* desc) { return true; } SOKOL_ASSERT(desc); - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_SHADERDESC_CANARY); - SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_SHADERDESC_CANARY); - #if defined(SOKOL_GLES2) - SOKOL_VALIDATE(0 != desc->attrs[0].name, _SG_VALIDATE_SHADERDESC_ATTR_NAMES); - #elif defined(SOKOL_D3D11) - SOKOL_VALIDATE(0 != desc->attrs[0].sem_name, _SG_VALIDATE_SHADERDESC_ATTR_SEMANTICS); + _sg_validate_begin(); + _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_SHADERDESC_CANARY); + _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_SHADERDESC_CANARY); + #if defined(SOKOL_D3D11) + _SG_VALIDATE(0 != desc->attrs[0].sem_name, VALIDATE_SHADERDESC_ATTR_SEMANTICS); #endif - #if defined(SOKOL_GLCORE33) || defined(SOKOL_GLES2) || defined(SOKOL_GLES3) - /* on GL, must provide shader source code */ - SOKOL_VALIDATE(0 != desc->vs.source, _SG_VALIDATE_SHADERDESC_SOURCE); - SOKOL_VALIDATE(0 != desc->fs.source, _SG_VALIDATE_SHADERDESC_SOURCE); + #if defined(SOKOL_GLCORE33) || defined(SOKOL_GLES3) + // on GL, must provide shader source code + _SG_VALIDATE(0 != desc->vs.source, VALIDATE_SHADERDESC_SOURCE); + _SG_VALIDATE(0 != desc->fs.source, VALIDATE_SHADERDESC_SOURCE); #elif defined(SOKOL_METAL) || defined(SOKOL_D3D11) - /* on Metal or D3D11, must provide shader source code or byte code */ - SOKOL_VALIDATE((0 != desc->vs.source)||(0 != desc->vs.bytecode.ptr), _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE); - SOKOL_VALIDATE((0 != desc->fs.source)||(0 != desc->fs.bytecode.ptr), _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE); + // on Metal or D3D11, must provide shader source code or byte code + _SG_VALIDATE((0 != desc->vs.source)||(0 != desc->vs.bytecode.ptr), VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE); + _SG_VALIDATE((0 != desc->fs.source)||(0 != desc->fs.bytecode.ptr), VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE); #elif defined(SOKOL_WGPU) - /* on WGPU byte code must be provided */ - SOKOL_VALIDATE((0 != desc->vs.bytecode.ptr), _SG_VALIDATE_SHADERDESC_BYTECODE); - SOKOL_VALIDATE((0 != desc->fs.bytecode.ptr), _SG_VALIDATE_SHADERDESC_BYTECODE); + // on WGPU byte code must be provided + _SG_VALIDATE((0 != desc->vs.bytecode.ptr), VALIDATE_SHADERDESC_BYTECODE); + _SG_VALIDATE((0 != desc->fs.bytecode.ptr), VALIDATE_SHADERDESC_BYTECODE); #else - /* Dummy Backend, don't require source or bytecode */ + // Dummy Backend, don't require source or bytecode #endif for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { if (desc->attrs[i].name) { - SOKOL_VALIDATE(strlen(desc->attrs[i].name) < _SG_STRING_SIZE, _SG_VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG); + _SG_VALIDATE(strlen(desc->attrs[i].name) < _SG_STRING_SIZE, VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG); } if (desc->attrs[i].sem_name) { - SOKOL_VALIDATE(strlen(desc->attrs[i].sem_name) < _SG_STRING_SIZE, _SG_VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG); + _SG_VALIDATE(strlen(desc->attrs[i].sem_name) < _SG_STRING_SIZE, VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG); } } - /* if shader byte code, the size must also be provided */ + // if shader byte code, the size must also be provided if (0 != desc->vs.bytecode.ptr) { - SOKOL_VALIDATE(desc->vs.bytecode.size > 0, _SG_VALIDATE_SHADERDESC_NO_BYTECODE_SIZE); + _SG_VALIDATE(desc->vs.bytecode.size > 0, VALIDATE_SHADERDESC_NO_BYTECODE_SIZE); } if (0 != desc->fs.bytecode.ptr) { - SOKOL_VALIDATE(desc->fs.bytecode.size > 0, _SG_VALIDATE_SHADERDESC_NO_BYTECODE_SIZE); + _SG_VALIDATE(desc->fs.bytecode.size > 0, VALIDATE_SHADERDESC_NO_BYTECODE_SIZE); } for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { const sg_shader_stage_desc* stage_desc = (stage_index == 0)? &desc->vs : &desc->fs; @@ -14668,7 +14883,7 @@ _SOKOL_PRIVATE bool _sg_validate_shader_desc(const sg_shader_desc* desc) { 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 (ub_desc->size > 0) { - SOKOL_VALIDATE(uniform_blocks_continuous, _SG_VALIDATE_SHADERDESC_NO_CONT_UBS); + _SG_VALIDATE(uniform_blocks_continuous, VALIDATE_SHADERDESC_NO_CONT_UBS); #if defined(_SOKOL_ANY_GL) bool uniforms_continuous = true; uint32_t uniform_offset = 0; @@ -14676,12 +14891,12 @@ _SOKOL_PRIVATE bool _sg_validate_shader_desc(const sg_shader_desc* desc) { for (int u_index = 0; u_index < SG_MAX_UB_MEMBERS; u_index++) { const sg_shader_uniform_desc* u_desc = &ub_desc->uniforms[u_index]; if (u_desc->type != SG_UNIFORMTYPE_INVALID) { - SOKOL_VALIDATE(uniforms_continuous, _SG_VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS); - #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) - SOKOL_VALIDATE(0 != u_desc->name, _SG_VALIDATE_SHADERDESC_UB_MEMBER_NAME); + _SG_VALIDATE(uniforms_continuous, VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS); + #if defined(SOKOL_GLES3) + _SG_VALIDATE(0 != u_desc->name, VALIDATE_SHADERDESC_UB_MEMBER_NAME); #endif const int array_count = u_desc->array_count; - SOKOL_VALIDATE(array_count > 0, _SG_VALIDATE_SHADERDESC_UB_ARRAY_COUNT); + _SG_VALIDATE(array_count > 0, VALIDATE_SHADERDESC_UB_ARRAY_COUNT); const uint32_t u_align = _sg_uniform_alignment(u_desc->type, array_count, ub_desc->layout); const uint32_t u_size = _sg_uniform_size(u_desc->type, array_count, ub_desc->layout); uniform_offset = _sg_align_u32(uniform_offset, u_align); @@ -14690,40 +14905,80 @@ _SOKOL_PRIVATE bool _sg_validate_shader_desc(const sg_shader_desc* desc) { // with std140, arrays are only allowed for FLOAT4, INT4, MAT4 if (ub_desc->layout == SG_UNIFORMLAYOUT_STD140) { if (array_count > 1) { - SOKOL_VALIDATE((u_desc->type == SG_UNIFORMTYPE_FLOAT4) || (u_desc->type == SG_UNIFORMTYPE_INT4) || (u_desc->type == SG_UNIFORMTYPE_MAT4), _SG_VALIDATE_SHADERDESC_UB_STD140_ARRAY_TYPE); + _SG_VALIDATE((u_desc->type == SG_UNIFORMTYPE_FLOAT4) || (u_desc->type == SG_UNIFORMTYPE_INT4) || (u_desc->type == SG_UNIFORMTYPE_MAT4), VALIDATE_SHADERDESC_UB_STD140_ARRAY_TYPE); } } - } - else { + } else { uniforms_continuous = false; } } if (ub_desc->layout == SG_UNIFORMLAYOUT_STD140) { uniform_offset = _sg_align_u32(uniform_offset, 16); } - SOKOL_VALIDATE((size_t)uniform_offset == ub_desc->size, _SG_VALIDATE_SHADERDESC_UB_SIZE_MISMATCH); - SOKOL_VALIDATE(num_uniforms > 0, _SG_VALIDATE_SHADERDESC_NO_UB_MEMBERS); + _SG_VALIDATE((size_t)uniform_offset == ub_desc->size, VALIDATE_SHADERDESC_UB_SIZE_MISMATCH); + _SG_VALIDATE(num_uniforms > 0, VALIDATE_SHADERDESC_NO_UB_MEMBERS); #endif - } - else { + } else { uniform_blocks_continuous = false; } } bool images_continuous = true; + int 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->image_type != _SG_IMAGETYPE_DEFAULT) { - SOKOL_VALIDATE(images_continuous, _SG_VALIDATE_SHADERDESC_NO_CONT_IMGS); - #if defined(SOKOL_GLES2) - SOKOL_VALIDATE(0 != img_desc->name, _SG_VALIDATE_SHADERDESC_IMG_NAME); - #endif - } - else { + if (img_desc->used) { + _SG_VALIDATE(images_continuous, VALIDATE_SHADERDESC_NO_CONT_IMAGES); + num_images++; + } else { images_continuous = false; } } + bool samplers_continuous = true; + int num_samplers = 0; + for (int smp_index = 0; smp_index < SG_MAX_SHADERSTAGE_SAMPLERS; smp_index++) { + const sg_shader_sampler_desc* smp_desc = &stage_desc->samplers[smp_index]; + if (smp_desc->used) { + _SG_VALIDATE(samplers_continuous, VALIDATE_SHADERDESC_NO_CONT_SAMPLERS); + num_samplers++; + } else { + samplers_continuous = false; + } + } + bool image_samplers_continuous = true; + int num_image_samplers = 0; + for (int img_smp_index = 0; img_smp_index < SG_MAX_SHADERSTAGE_IMAGESAMPLERPAIRS; img_smp_index++) { + const sg_shader_image_sampler_pair_desc* img_smp_desc = &stage_desc->image_sampler_pairs[img_smp_index]; + if (img_smp_desc->used) { + _SG_VALIDATE(image_samplers_continuous, VALIDATE_SHADERDESC_NO_CONT_IMAGE_SAMPLER_PAIRS); + num_image_samplers++; + const bool img_slot_in_range = (img_smp_desc->image_slot >= 0) && (img_smp_desc->image_slot < SG_MAX_SHADERSTAGE_IMAGES); + const bool smp_slot_in_range = (img_smp_desc->sampler_slot >= 0) && (img_smp_desc->sampler_slot < SG_MAX_SHADERSTAGE_SAMPLERS); + _SG_VALIDATE(img_slot_in_range && (img_smp_desc->image_slot < num_images), VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_IMAGE_SLOT_OUT_OF_RANGE); + _SG_VALIDATE(smp_slot_in_range && (img_smp_desc->sampler_slot < num_samplers), VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_IMAGE_SLOT_OUT_OF_RANGE); + #if defined(_SOKOL_ANY_GL) + _SG_VALIDATE(img_smp_desc->glsl_name != 0, VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_NAME_REQUIRED_FOR_GL); + #endif + } else { + _SG_VALIDATE(img_smp_desc->glsl_name == 0, VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_HAS_NAME_BUT_NOT_USED); + _SG_VALIDATE(img_smp_desc->image_slot == 0, VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_HAS_IMAGE_BUT_NOT_USED); + _SG_VALIDATE(img_smp_desc->sampler_slot == 0, VALIDATE_SHADERDESC_IMAGE_SAMPLER_PAIR_HAS_SAMPLER_BUT_NOT_USED); + image_samplers_continuous = false; + } + } + // each image and sampler must be referenced by an image sampler + const uint32_t expected_img_slot_mask = (uint32_t)((1 << num_images) - 1); + const uint32_t expected_smp_slot_mask = (uint32_t)((1 << num_samplers) - 1); + uint32_t actual_img_slot_mask = 0; + uint32_t actual_smp_slot_mask = 0; + for (int img_smp_index = 0; img_smp_index < num_image_samplers; img_smp_index++) { + const sg_shader_image_sampler_pair_desc* img_smp_desc = &stage_desc->image_sampler_pairs[img_smp_index]; + actual_img_slot_mask |= (1 << ((uint32_t)img_smp_desc->image_slot & 31)); + actual_smp_slot_mask |= (1 << ((uint32_t)img_smp_desc->sampler_slot & 31)); + } + _SG_VALIDATE(expected_img_slot_mask == actual_img_slot_mask, VALIDATE_SHADERDESC_IMAGE_NOT_REFERENCED_BY_IMAGE_SAMPLER_PAIRS); + _SG_VALIDATE(expected_smp_slot_mask == actual_smp_slot_mask, VALIDATE_SHADERDESC_SAMPLER_NOT_REFERENCED_BY_IMAGE_SAMPLER_PAIRS); } - return SOKOL_VALIDATE_END(); + return _sg_validate_end(); #endif } @@ -14736,41 +14991,38 @@ _SOKOL_PRIVATE bool _sg_validate_pipeline_desc(const sg_pipeline_desc* desc) { return true; } SOKOL_ASSERT(desc); - SOKOL_VALIDATE_BEGIN(); - 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); - 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) { + _sg_validate_begin(); + _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_PIPELINEDESC_CANARY); + _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_PIPELINEDESC_CANARY); + _SG_VALIDATE(desc->shader.id != SG_INVALID_ID, VALIDATE_PIPELINEDESC_SHADER); + for (int buf_index = 0; buf_index < SG_MAX_VERTEX_BUFFERS; buf_index++) { + const sg_vertex_buffer_layout_state* l_state = &desc->layout.buffers[buf_index]; + if (l_state->stride == 0) { continue; } - SOKOL_VALIDATE((l_desc->stride & 3) == 0, _SG_VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4); + _SG_VALIDATE((l_state->stride & 3) == 0, VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4); } - SOKOL_VALIDATE(desc->layout.attrs[0].format != SG_VERTEXFORMAT_INVALID, _SG_VALIDATE_PIPELINEDESC_NO_ATTRS); + _SG_VALIDATE(desc->layout.attrs[0].format != SG_VERTEXFORMAT_INVALID, VALIDATE_PIPELINEDESC_NO_ATTRS); const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, desc->shader.id); - SOKOL_VALIDATE(0 != shd, _SG_VALIDATE_PIPELINEDESC_SHADER); + _SG_VALIDATE(0 != shd, VALIDATE_PIPELINEDESC_SHADER); if (shd) { - SOKOL_VALIDATE(shd->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_PIPELINEDESC_SHADER); + _SG_VALIDATE(shd->slot.state == SG_RESOURCESTATE_VALID, 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) { + const sg_vertex_attr_state* a_state = &desc->layout.attrs[attr_index]; + if (a_state->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); + _SG_VALIDATE(attrs_cont, VALIDATE_PIPELINEDESC_NO_ATTRS); + SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEX_BUFFERS); + #if defined(SOKOL_D3D11) + // on D3D11, semantic names (and semantic indices) must be provided + _SG_VALIDATE(!_sg_strempty(&shd->d3d11.attrs[attr_index].sem_name), VALIDATE_PIPELINEDESC_ATTR_SEMANTICS); #endif } } - return SOKOL_VALIDATE_END(); + return _sg_validate_end(); #endif } @@ -14783,67 +15035,96 @@ _SOKOL_PRIVATE bool _sg_validate_pass_desc(const sg_pass_desc* desc) { return true; } SOKOL_ASSERT(desc); - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_PASSDESC_CANARY); - SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_PASSDESC_CANARY); + _sg_validate_begin(); + _SG_VALIDATE(desc->_start_canary == 0, VALIDATE_PASSDESC_CANARY); + _SG_VALIDATE(desc->_end_canary == 0, VALIDATE_PASSDESC_CANARY); bool atts_cont = true; - int width = -1, height = -1, sample_count = -1; + int color_width = -1, color_height = -1, color_sample_count = -1; + bool has_color_atts = false; for (int att_index = 0; att_index < SG_MAX_COLOR_ATTACHMENTS; att_index++) { const sg_pass_attachment_desc* att = &desc->color_attachments[att_index]; if (att->image.id == SG_INVALID_ID) { - SOKOL_VALIDATE(att_index > 0, _SG_VALIDATE_PASSDESC_NO_COLOR_ATTS); atts_cont = false; continue; } - SOKOL_VALIDATE(atts_cont, _SG_VALIDATE_PASSDESC_NO_CONT_COLOR_ATTS); + _SG_VALIDATE(atts_cont, VALIDATE_PASSDESC_NO_CONT_COLOR_ATTS); + has_color_atts = true; const _sg_image_t* img = _sg_lookup_image(&_sg.pools, att->image.id); - SOKOL_ASSERT(img); - SOKOL_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_PASSDESC_IMAGE); - SOKOL_VALIDATE(att->mip_level < img->cmn.num_mipmaps, _SG_VALIDATE_PASSDESC_MIPLEVEL); - if (img->cmn.type == SG_IMAGETYPE_CUBE) { - SOKOL_VALIDATE(att->slice < 6, _SG_VALIDATE_PASSDESC_FACE); - } - else if (img->cmn.type == SG_IMAGETYPE_ARRAY) { - SOKOL_VALIDATE(att->slice < img->cmn.num_slices, _SG_VALIDATE_PASSDESC_LAYER); - } - else if (img->cmn.type == SG_IMAGETYPE_3D) { - SOKOL_VALIDATE(att->slice < img->cmn.num_slices, _SG_VALIDATE_PASSDESC_SLICE); - } - SOKOL_VALIDATE(img->cmn.render_target, _SG_VALIDATE_PASSDESC_IMAGE_NO_RT); - if (att_index == 0) { - width = img->cmn.width >> att->mip_level; - height = img->cmn.height >> att->mip_level; - sample_count = img->cmn.sample_count; - } - else { - 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); + _SG_VALIDATE(img, VALIDATE_PASSDESC_IMAGE); + if (0 != img) { + _SG_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_PASSDESC_IMAGE); + _SG_VALIDATE(img->cmn.render_target, VALIDATE_PASSDESC_IMAGE_NO_RT); + _SG_VALIDATE(att->mip_level < img->cmn.num_mipmaps, VALIDATE_PASSDESC_MIPLEVEL); + if (img->cmn.type == SG_IMAGETYPE_CUBE) { + _SG_VALIDATE(att->slice < 6, VALIDATE_PASSDESC_FACE); + } else if (img->cmn.type == SG_IMAGETYPE_ARRAY) { + _SG_VALIDATE(att->slice < img->cmn.num_slices, VALIDATE_PASSDESC_LAYER); + } else if (img->cmn.type == SG_IMAGETYPE_3D) { + _SG_VALIDATE(att->slice < img->cmn.num_slices, VALIDATE_PASSDESC_SLICE); + } + if (att_index == 0) { + color_width = _sg_miplevel_dim(img->cmn.width, att->mip_level); + color_height = _sg_miplevel_dim(img->cmn.height, att->mip_level); + color_sample_count = img->cmn.sample_count; + } else { + _SG_VALIDATE(color_width == _sg_miplevel_dim(img->cmn.width, att->mip_level), VALIDATE_PASSDESC_IMAGE_SIZES); + _SG_VALIDATE(color_height == _sg_miplevel_dim(img->cmn.height, att->mip_level), VALIDATE_PASSDESC_IMAGE_SIZES); + _SG_VALIDATE(color_sample_count == img->cmn.sample_count, VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS); + } + _SG_VALIDATE(_sg_is_valid_rendertarget_color_format(img->cmn.pixel_format), VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT); + + // check resolve attachment + const sg_pass_attachment_desc* res_att = &desc->resolve_attachments[att_index]; + if (res_att->image.id != SG_INVALID_ID) { + // associated color attachment must be MSAA + _SG_VALIDATE(img->cmn.sample_count > 1, VALIDATE_PASSDESC_RESOLVE_COLOR_IMAGE_MSAA); + const _sg_image_t* res_img = _sg_lookup_image(&_sg.pools, res_att->image.id); + _SG_VALIDATE(res_img, VALIDATE_PASSDESC_RESOLVE_IMAGE); + if (res_img != 0) { + _SG_VALIDATE(res_img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_PASSDESC_RESOLVE_IMAGE); + _SG_VALIDATE(res_img->cmn.render_target, VALIDATE_PASSDESC_RESOLVE_IMAGE_NO_RT); + _SG_VALIDATE(res_img->cmn.sample_count == 1, VALIDATE_PASSDESC_RESOLVE_SAMPLE_COUNT); + _SG_VALIDATE(res_att->mip_level < res_img->cmn.num_mipmaps, VALIDATE_PASSDESC_RESOLVE_MIPLEVEL); + if (res_img->cmn.type == SG_IMAGETYPE_CUBE) { + _SG_VALIDATE(res_att->slice < 6, VALIDATE_PASSDESC_RESOLVE_FACE); + } else if (res_img->cmn.type == SG_IMAGETYPE_ARRAY) { + _SG_VALIDATE(res_att->slice < res_img->cmn.num_slices, VALIDATE_PASSDESC_RESOLVE_LAYER); + } else if (res_img->cmn.type == SG_IMAGETYPE_3D) { + _SG_VALIDATE(res_att->slice < res_img->cmn.num_slices, VALIDATE_PASSDESC_RESOLVE_SLICE); + } + _SG_VALIDATE(img->cmn.pixel_format == res_img->cmn.pixel_format, VALIDATE_PASSDESC_RESOLVE_IMAGE_FORMAT); + _SG_VALIDATE(color_width == _sg_miplevel_dim(res_img->cmn.width, res_att->mip_level), VALIDATE_PASSDESC_RESOLVE_IMAGE_SIZES); + _SG_VALIDATE(color_height == _sg_miplevel_dim(res_img->cmn.height, res_att->mip_level), VALIDATE_PASSDESC_RESOLVE_IMAGE_SIZES); + } + } } - SOKOL_VALIDATE(_sg_is_valid_rendertarget_color_format(img->cmn.pixel_format), _SG_VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT); } + bool has_depth_stencil_att = false; if (desc->depth_stencil_attachment.image.id != SG_INVALID_ID) { const sg_pass_attachment_desc* att = &desc->depth_stencil_attachment; const _sg_image_t* img = _sg_lookup_image(&_sg.pools, att->image.id); - SOKOL_ASSERT(img); - SOKOL_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_PASSDESC_IMAGE); - SOKOL_VALIDATE(att->mip_level < img->cmn.num_mipmaps, _SG_VALIDATE_PASSDESC_MIPLEVEL); - if (img->cmn.type == SG_IMAGETYPE_CUBE) { - SOKOL_VALIDATE(att->slice < 6, _SG_VALIDATE_PASSDESC_FACE); - } - else if (img->cmn.type == SG_IMAGETYPE_ARRAY) { - SOKOL_VALIDATE(att->slice < img->cmn.num_slices, _SG_VALIDATE_PASSDESC_LAYER); - } - else if (img->cmn.type == SG_IMAGETYPE_3D) { - SOKOL_VALIDATE(att->slice < img->cmn.num_slices, _SG_VALIDATE_PASSDESC_SLICE); + _SG_VALIDATE(img, VALIDATE_PASSDESC_DEPTH_IMAGE); + has_depth_stencil_att = true; + if (img) { + _SG_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_PASSDESC_DEPTH_IMAGE); + _SG_VALIDATE(att->mip_level < img->cmn.num_mipmaps, VALIDATE_PASSDESC_DEPTH_MIPLEVEL); + if (img->cmn.type == SG_IMAGETYPE_CUBE) { + _SG_VALIDATE(att->slice < 6, VALIDATE_PASSDESC_DEPTH_FACE); + } else if (img->cmn.type == SG_IMAGETYPE_ARRAY) { + _SG_VALIDATE(att->slice < img->cmn.num_slices, VALIDATE_PASSDESC_DEPTH_LAYER); + } else if (img->cmn.type == SG_IMAGETYPE_3D) { + // NOTE: this can't actually happen because of VALIDATE_IMAGEDESC_DEPTH_3D_IMAGE + _SG_VALIDATE(att->slice < img->cmn.num_slices, VALIDATE_PASSDESC_DEPTH_SLICE); + } + _SG_VALIDATE(img->cmn.render_target, VALIDATE_PASSDESC_DEPTH_IMAGE_NO_RT); + _SG_VALIDATE((color_width == -1) || (color_width == _sg_miplevel_dim(img->cmn.width, att->mip_level)), VALIDATE_PASSDESC_DEPTH_IMAGE_SIZES); + _SG_VALIDATE((color_height == -1) || (color_height == _sg_miplevel_dim(img->cmn.height, att->mip_level)), VALIDATE_PASSDESC_DEPTH_IMAGE_SIZES); + _SG_VALIDATE((color_sample_count == -1) || (color_sample_count == img->cmn.sample_count), VALIDATE_PASSDESC_DEPTH_IMAGE_SAMPLE_COUNT); + _SG_VALIDATE(_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format), 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(); + _SG_VALIDATE(has_color_atts || has_depth_stencil_att, VALIDATE_PASSDESC_NO_ATTACHMENTS); + return _sg_validate_end(); #endif } @@ -14855,24 +15136,30 @@ _SOKOL_PRIVATE bool _sg_validate_begin_pass(_sg_pass_t* pass) { if (_sg.desc.disable_validation) { return true; } - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(pass->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_BEGINPASS_PASS); + _sg_validate_begin(); + _SG_VALIDATE(pass->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_PASS); for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { - const _sg_pass_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); + const _sg_pass_attachment_t* color_att = &pass->cmn.color_atts[i]; + const _sg_image_t* color_img = _sg_pass_color_image(pass, i); + if (color_img) { + _SG_VALIDATE(color_img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_COLOR_ATTACHMENT_IMAGE); + _SG_VALIDATE(color_img->slot.id == color_att->image_id.id, VALIDATE_BEGINPASS_COLOR_ATTACHMENT_IMAGE); + } + const _sg_pass_attachment_t* resolve_att = &pass->cmn.resolve_atts[i]; + const _sg_image_t* resolve_img = _sg_pass_resolve_image(pass, i); + if (resolve_img) { + _SG_VALIDATE(resolve_img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_RESOLVE_ATTACHMENT_IMAGE); + _SG_VALIDATE(resolve_img->slot.id == resolve_att->image_id.id, VALIDATE_BEGINPASS_RESOLVE_ATTACHMENT_IMAGE); } } const _sg_image_t* ds_img = _sg_pass_ds_image(pass); if (ds_img) { const _sg_pass_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); + _SG_VALIDATE(ds_img->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_BEGINPASS_DEPTHSTENCIL_ATTACHMENT_IMAGE); + _SG_VALIDATE(ds_img->slot.id == att->image_id.id, VALIDATE_BEGINPASS_DEPTHSTENCIL_ATTACHMENT_IMAGE); } - return SOKOL_VALIDATE_END(); + return _sg_validate_end(); #endif } @@ -14884,45 +15171,43 @@ _SOKOL_PRIVATE bool _sg_validate_apply_pipeline(sg_pipeline pip_id) { if (_sg.desc.disable_validation) { return true; } - SOKOL_VALIDATE_BEGIN(); - /* the pipeline object must be alive and valid */ - SOKOL_VALIDATE(pip_id.id != SG_INVALID_ID, _SG_VALIDATE_APIP_PIPELINE_VALID_ID); + _sg_validate_begin(); + // the pipeline object must be alive and valid + _SG_VALIDATE(pip_id.id != SG_INVALID_ID, VALIDATE_APIP_PIPELINE_VALID_ID); const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); - SOKOL_VALIDATE(pip != 0, _SG_VALIDATE_APIP_PIPELINE_EXISTS); + _SG_VALIDATE(pip != 0, VALIDATE_APIP_PIPELINE_EXISTS); if (!pip) { - return SOKOL_VALIDATE_END(); + return _sg_validate_end(); } - SOKOL_VALIDATE(pip->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_APIP_PIPELINE_VALID); - /* the pipeline's shader must be alive and valid */ + _SG_VALIDATE(pip->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_APIP_PIPELINE_VALID); + // the pipeline's shader must be alive and valid SOKOL_ASSERT(pip->shader); - 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 */ + _SG_VALIDATE(pip->shader->slot.id == pip->cmn.shader_id.id, VALIDATE_APIP_SHADER_EXISTS); + _SG_VALIDATE(pip->shader->slot.state == SG_RESOURCESTATE_VALID, 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->cmn.color_attachment_count == pass->cmn.num_color_atts, _SG_VALIDATE_APIP_ATT_COUNT); - for (int i = 0; i < pip->cmn.color_attachment_count; i++) { + // an offscreen pass + _SG_VALIDATE(pip->cmn.color_count == pass->cmn.num_color_atts, VALIDATE_APIP_ATT_COUNT); + for (int i = 0; i < pip->cmn.color_count; i++) { const _sg_image_t* att_img = _sg_pass_color_image(pass, i); - SOKOL_VALIDATE(pip->cmn.color_formats[i] == 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); + _SG_VALIDATE(pip->cmn.colors[i].pixel_format == att_img->cmn.pixel_format, VALIDATE_APIP_COLOR_FORMAT); + _SG_VALIDATE(pip->cmn.sample_count == att_img->cmn.sample_count, 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); + _SG_VALIDATE(pip->cmn.depth.pixel_format == att_dsimg->cmn.pixel_format, VALIDATE_APIP_DEPTH_FORMAT); + } else { + _SG_VALIDATE(pip->cmn.depth.pixel_format == SG_PIXELFORMAT_NONE, VALIDATE_APIP_DEPTH_FORMAT); } - else { - SOKOL_VALIDATE(pip->cmn.depth_format == SG_PIXELFORMAT_NONE, _SG_VALIDATE_APIP_DEPTH_FORMAT); - } - } - else { - /* default pass */ - SOKOL_VALIDATE(pip->cmn.color_attachment_count == 1, _SG_VALIDATE_APIP_ATT_COUNT); - SOKOL_VALIDATE(pip->cmn.color_formats[0] == _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(); + } else { + // default pass + _SG_VALIDATE(pip->cmn.color_count == 1, VALIDATE_APIP_ATT_COUNT); + _SG_VALIDATE(pip->cmn.colors[0].pixel_format == _sg.desc.context.color_format, VALIDATE_APIP_COLOR_FORMAT); + _SG_VALIDATE(pip->cmn.depth.pixel_format == _sg.desc.context.depth_format, VALIDATE_APIP_DEPTH_FORMAT); + _SG_VALIDATE(pip->cmn.sample_count == _sg.desc.context.sample_count, VALIDATE_APIP_SAMPLE_COUNT); + } + return _sg_validate_end(); #endif } @@ -14934,87 +15219,159 @@ _SOKOL_PRIVATE bool _sg_validate_apply_bindings(const sg_bindings* bindings) { if (_sg.desc.disable_validation) { return true; } - SOKOL_VALIDATE_BEGIN(); + _sg_validate_begin(); - /* a pipeline object must have been applied */ - SOKOL_VALIDATE(_sg.cur_pipeline.id != SG_INVALID_ID, _SG_VALIDATE_ABND_PIPELINE); + // a pipeline object must have been applied + _SG_VALIDATE(_sg.cur_pipeline.id != SG_INVALID_ID, VALIDATE_ABND_PIPELINE); const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, _sg.cur_pipeline.id); - SOKOL_VALIDATE(pip != 0, _SG_VALIDATE_ABND_PIPELINE_EXISTS); + _SG_VALIDATE(pip != 0, VALIDATE_ABND_PIPELINE_EXISTS); if (!pip) { - return SOKOL_VALIDATE_END(); + return _sg_validate_end(); } - SOKOL_VALIDATE(pip->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_ABND_PIPELINE_VALID); + _SG_VALIDATE(pip->slot.state == SG_RESOURCESTATE_VALID, VALIDATE_ABND_PIPELINE_VALID); 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++) { + // has expected vertex buffers, and vertex buffers still exist + for (int i = 0; i < SG_MAX_VERTEX_BUFFERS; i++) { if (bindings->vertex_buffers[i].id != SG_INVALID_ID) { - SOKOL_VALIDATE(pip->cmn.vertex_layout_valid[i], _SG_VALIDATE_ABND_VBS); - /* buffers in vertex-buffer-slots must be of type SG_BUFFERTYPE_VERTEXBUFFER */ + _SG_VALIDATE(pip->cmn.vertex_buffer_layout_active[i], 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); + _SG_VALIDATE(buf != 0, VALIDATE_ABND_VB_EXISTS); if (buf && buf->slot.state == SG_RESOURCESTATE_VALID) { - SOKOL_VALIDATE(SG_BUFFERTYPE_VERTEXBUFFER == buf->cmn.type, _SG_VALIDATE_ABND_VB_TYPE); - SOKOL_VALIDATE(!buf->cmn.append_overflow, _SG_VALIDATE_ABND_VB_OVERFLOW); + _SG_VALIDATE(SG_BUFFERTYPE_VERTEXBUFFER == buf->cmn.type, VALIDATE_ABND_VB_TYPE); + _SG_VALIDATE(!buf->cmn.append_overflow, VALIDATE_ABND_VB_OVERFLOW); } - } - else { - /* vertex buffer provided in a slot which has no vertex layout in pipeline */ - SOKOL_VALIDATE(!pip->cmn.vertex_layout_valid[i], _SG_VALIDATE_ABND_VBS); + } else { + // vertex buffer provided in a slot which has no vertex layout in pipeline + _SG_VALIDATE(!pip->cmn.vertex_buffer_layout_active[i], VALIDATE_ABND_VBS); } } - /* index buffer expected or not, and index buffer still exists */ + // index buffer expected or not, and index buffer still exists 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); - } - else { - /* pipeline defines indexed rendering, but no index buffer provided */ - SOKOL_VALIDATE(bindings->index_buffer.id != SG_INVALID_ID, _SG_VALIDATE_ABND_NO_IB); + // pipeline defines non-indexed rendering, but index buffer provided + _SG_VALIDATE(bindings->index_buffer.id == SG_INVALID_ID, VALIDATE_ABND_IB); + } else { + // pipeline defines indexed rendering, but no index buffer provided + _SG_VALIDATE(bindings->index_buffer.id != SG_INVALID_ID, VALIDATE_ABND_NO_IB); } if (bindings->index_buffer.id != SG_INVALID_ID) { - /* buffer in index-buffer-slot must be of type SG_BUFFERTYPE_INDEXBUFFER */ + // buffer in index-buffer-slot must be of type SG_BUFFERTYPE_INDEXBUFFER const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, bindings->index_buffer.id); - SOKOL_VALIDATE(buf != 0, _SG_VALIDATE_ABND_IB_EXISTS); + _SG_VALIDATE(buf != 0, VALIDATE_ABND_IB_EXISTS); if (buf && buf->slot.state == SG_RESOURCESTATE_VALID) { - SOKOL_VALIDATE(SG_BUFFERTYPE_INDEXBUFFER == buf->cmn.type, _SG_VALIDATE_ABND_IB_TYPE); - SOKOL_VALIDATE(!buf->cmn.append_overflow, _SG_VALIDATE_ABND_IB_OVERFLOW); + _SG_VALIDATE(SG_BUFFERTYPE_INDEXBUFFER == buf->cmn.type, VALIDATE_ABND_IB_TYPE); + _SG_VALIDATE(!buf->cmn.append_overflow, VALIDATE_ABND_IB_OVERFLOW); } } - /* has expected vertex shader images */ + // has expected vertex shader images for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { - _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->cmn.type == stage->images[i].image_type, _SG_VALIDATE_ABND_VS_IMG_TYPES); + const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_VS]; + if (stage->images[i].image_type != _SG_IMAGETYPE_DEFAULT) { + _SG_VALIDATE(bindings->vs.images[i].id != SG_INVALID_ID, VALIDATE_ABND_VS_EXPECTED_IMAGE_BINDING); + if (bindings->vs.images[i].id != SG_INVALID_ID) { + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, bindings->vs.images[i].id); + _SG_VALIDATE(img != 0, VALIDATE_ABND_VS_IMG_EXISTS); + if (img && img->slot.state == SG_RESOURCESTATE_VALID) { + _SG_VALIDATE(img->cmn.type == stage->images[i].image_type, VALIDATE_ABND_VS_IMAGE_TYPE_MISMATCH); + _SG_VALIDATE(img->cmn.sample_count == 1, VALIDATE_ABND_VS_IMAGE_MSAA); + } } + } else { + _SG_VALIDATE(bindings->vs.images[i].id == SG_INVALID_ID, VALIDATE_ABND_VS_UNEXPECTED_IMAGE_BINDING); } - else { - SOKOL_VALIDATE(i >= stage->num_images, _SG_VALIDATE_ABND_VS_IMGS); + } + + // has expected vertex shader image samplers + for (int i = 0; i < SG_MAX_SHADERSTAGE_SAMPLERS; i++) { + const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_VS]; + if (stage->samplers[i].sampler_type != _SG_SAMPLERTYPE_DEFAULT) { + _SG_VALIDATE(bindings->vs.samplers[i].id != SG_INVALID_ID, VALIDATE_ABND_VS_EXPECTED_SAMPLER_BINDING); + if (bindings->vs.samplers[i].id != SG_INVALID_ID) { + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, bindings->vs.samplers[i].id); + _SG_VALIDATE(smp != 0, VALIDATE_ABND_VS_SMP_EXISTS); + if (smp) { + if (stage->samplers[i].sampler_type == SG_SAMPLERTYPE_COMPARE) { + _SG_VALIDATE(smp->cmn.compare != SG_COMPAREFUNC_NEVER, VALIDATE_ABND_VS_UNEXPECTED_SAMPLER_COMPARE_NEVER); + } else { + _SG_VALIDATE(smp->cmn.compare == SG_COMPAREFUNC_NEVER, VALIDATE_ABND_VS_EXPECTED_SAMPLER_COMPARE_NEVER); + } + } + } + } else { + _SG_VALIDATE(bindings->vs.samplers[i].id == SG_INVALID_ID, VALIDATE_ABND_VS_UNEXPECTED_SAMPLER_BINDING); } } - /* has expected fragment shader images */ + // has expected fragment shader images for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { - _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->cmn.type == stage->images[i].image_type, _SG_VALIDATE_ABND_FS_IMG_TYPES); + const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_FS]; + if (stage->images[i].image_type != _SG_IMAGETYPE_DEFAULT) { + _SG_VALIDATE(bindings->fs.images[i].id != SG_INVALID_ID, VALIDATE_ABND_FS_EXPECTED_IMAGE_BINDING); + if (bindings->fs.images[i].id != SG_INVALID_ID) { + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, bindings->fs.images[i].id); + _SG_VALIDATE(img != 0, VALIDATE_ABND_FS_IMG_EXISTS); + if (img && img->slot.state == SG_RESOURCESTATE_VALID) { + _SG_VALIDATE(img->cmn.type == stage->images[i].image_type, VALIDATE_ABND_FS_IMAGE_TYPE_MISMATCH); + _SG_VALIDATE(img->cmn.sample_count == 1, VALIDATE_ABND_FS_IMAGE_MSAA); + } } + } else { + _SG_VALIDATE(bindings->fs.images[i].id == SG_INVALID_ID, VALIDATE_ABND_FS_UNEXPECTED_IMAGE_BINDING); } - else { - SOKOL_VALIDATE(i >= stage->num_images, _SG_VALIDATE_ABND_FS_IMGS); + } + + // has expected fragment shader samplers + for (int i = 0; i < SG_MAX_SHADERSTAGE_SAMPLERS; i++) { + const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_FS]; + if (stage->samplers[i].sampler_type != _SG_SAMPLERTYPE_DEFAULT) { + _SG_VALIDATE(bindings->fs.samplers[i].id != SG_INVALID_ID, VALIDATE_ABND_FS_EXPECTED_SAMPLER_BINDING); + if (bindings->fs.samplers[i].id != SG_INVALID_ID) { + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, bindings->fs.samplers[i].id); + _SG_VALIDATE(smp != 0, VALIDATE_ABND_FS_SMP_EXISTS); + if (smp) { + if (stage->samplers[i].sampler_type == SG_SAMPLERTYPE_COMPARE) { + _SG_VALIDATE(smp->cmn.compare != SG_COMPAREFUNC_NEVER, VALIDATE_ABND_FS_UNEXPECTED_SAMPLER_COMPARE_NEVER); + } else { + _SG_VALIDATE(smp->cmn.compare == SG_COMPAREFUNC_NEVER, VALIDATE_ABND_FS_EXPECTED_SAMPLER_COMPARE_NEVER); + } + } + } + } else { + _SG_VALIDATE(bindings->fs.samplers[i].id == SG_INVALID_ID, VALIDATE_ABND_FS_UNEXPECTED_SAMPLER_BINDING); + } + } + + // if image-sampler-pair info was provided in shader desc, check that that the mipmap filter matches image num mipmaps + for (int img_smp_index = 0; img_smp_index < pip->shader->cmn.stage[SG_SHADERSTAGE_VS].num_image_samplers; img_smp_index++) { + const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_VS]; + const int img_index = stage->image_samplers[img_smp_index].image_slot; + const int smp_index = stage->image_samplers[img_smp_index].sampler_slot; + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, bindings->vs.images[img_index].id); + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, bindings->vs.samplers[smp_index].id); + if (img && smp) { + if (img->cmn.num_mipmaps == 1) { + _SG_VALIDATE(smp->cmn.mipmap_filter == SG_FILTER_NONE, VALIDATE_ABND_VS_IMG_SMP_MIPMAPS); + } + } + } + for (int img_smp_index = 0; img_smp_index < pip->shader->cmn.stage[SG_SHADERSTAGE_FS].num_image_samplers; img_smp_index++) { + const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_FS]; + const int img_index = stage->image_samplers[img_smp_index].image_slot; + const int smp_index = stage->image_samplers[img_smp_index].sampler_slot; + SOKOL_ASSERT(img_index < stage->num_images); + SOKOL_ASSERT(smp_index < stage->num_samplers); + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, bindings->fs.images[img_index].id); + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, bindings->fs.samplers[smp_index].id); + if (img && smp) { + if (img->cmn.num_mipmaps == 1) { + _SG_VALIDATE(smp->cmn.mipmap_filter == SG_FILTER_NONE, VALIDATE_ABND_FS_IMG_SMP_MIPMAPS); + } } } - return SOKOL_VALIDATE_END(); + return _sg_validate_end(); #endif } @@ -15030,20 +15387,20 @@ _SOKOL_PRIVATE bool _sg_validate_apply_uniforms(sg_shader_stage stage_index, int } SOKOL_ASSERT((stage_index == SG_SHADERSTAGE_VS) || (stage_index == SG_SHADERSTAGE_FS)); SOKOL_ASSERT((ub_index >= 0) && (ub_index < SG_MAX_SHADERSTAGE_UBS)); - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(_sg.cur_pipeline.id != SG_INVALID_ID, _SG_VALIDATE_AUB_NO_PIPELINE); + _sg_validate_begin(); + _SG_VALIDATE(_sg.cur_pipeline.id != SG_INVALID_ID, 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->cmn.shader_id.id)); - /* check that there is a uniform block at 'stage' and 'ub_index' */ + // check that there is a uniform block at 'stage' and 'ub_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); + _SG_VALIDATE(ub_index < stage->num_uniform_blocks, VALIDATE_AUB_NO_UB_AT_SLOT); - /* check that the provided data size doesn't exceed the uniform block size */ - SOKOL_VALIDATE(data->size <= stage->uniform_blocks[ub_index].size, _SG_VALIDATE_AUB_SIZE); + // check that the provided data size matches the uniform block size + _SG_VALIDATE(data->size == stage->uniform_blocks[ub_index].size, VALIDATE_AUB_SIZE); - return SOKOL_VALIDATE_END(); + return _sg_validate_end(); #endif } @@ -15057,12 +15414,12 @@ _SOKOL_PRIVATE bool _sg_validate_update_buffer(const _sg_buffer_t* buf, const sg return true; } SOKOL_ASSERT(buf && data && data->ptr); - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(buf->cmn.usage != SG_USAGE_IMMUTABLE, _SG_VALIDATE_UPDATEBUF_USAGE); - SOKOL_VALIDATE(buf->cmn.size >= (int)data->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(); + _sg_validate_begin(); + _SG_VALIDATE(buf->cmn.usage != SG_USAGE_IMMUTABLE, VALIDATE_UPDATEBUF_USAGE); + _SG_VALIDATE(buf->cmn.size >= (int)data->size, VALIDATE_UPDATEBUF_SIZE); + _SG_VALIDATE(buf->cmn.update_frame_index != _sg.frame_index, VALIDATE_UPDATEBUF_ONCE); + _SG_VALIDATE(buf->cmn.append_frame_index != _sg.frame_index, VALIDATE_UPDATEBUF_APPEND); + return _sg_validate_end(); #endif } @@ -15076,11 +15433,11 @@ _SOKOL_PRIVATE bool _sg_validate_append_buffer(const _sg_buffer_t* buf, const sg return true; } SOKOL_ASSERT(buf && data && data->ptr); - SOKOL_VALIDATE_BEGIN(); - SOKOL_VALIDATE(buf->cmn.usage != SG_USAGE_IMMUTABLE, _SG_VALIDATE_APPENDBUF_USAGE); - SOKOL_VALIDATE(buf->cmn.size >= (buf->cmn.append_pos + (int)data->size), _SG_VALIDATE_APPENDBUF_SIZE); - SOKOL_VALIDATE(buf->cmn.update_frame_index != _sg.frame_index, _SG_VALIDATE_APPENDBUF_UPDATE); - return SOKOL_VALIDATE_END(); + _sg_validate_begin(); + _SG_VALIDATE(buf->cmn.usage != SG_USAGE_IMMUTABLE, VALIDATE_APPENDBUF_USAGE); + _SG_VALIDATE(buf->cmn.size >= (buf->cmn.append_pos + (int)data->size), VALIDATE_APPENDBUF_SIZE); + _SG_VALIDATE(buf->cmn.update_frame_index != _sg.frame_index, VALIDATE_APPENDBUF_UPDATE); + return _sg_validate_end(); #endif } @@ -15094,9 +15451,9 @@ _SOKOL_PRIVATE bool _sg_validate_update_image(const _sg_image_t* img, const sg_i return true; } SOKOL_ASSERT(img && data); - SOKOL_VALIDATE_BEGIN(); - 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); + _sg_validate_begin(); + _SG_VALIDATE(img->cmn.usage != SG_USAGE_IMMUTABLE, VALIDATE_UPDIMG_USAGE); + _SG_VALIDATE(img->cmn.upd_frame_index != _sg.frame_index, VALIDATE_UPDIMG_ONCE); _sg_validate_image_data(data, img->cmn.pixel_format, img->cmn.width, @@ -15104,19 +15461,24 @@ _SOKOL_PRIVATE bool _sg_validate_update_image(const _sg_image_t* img, const sg_i (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6 : 1, img->cmn.num_mipmaps, img->cmn.num_slices); - return SOKOL_VALIDATE_END(); + return _sg_validate_end(); #endif } -/*== fill in desc default values =============================================*/ +// ██████ ███████ ███████ ██████ ██ ██ ██████ ██████ ███████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ █████ ███████ ██ ██ ██ ██ ██████ ██ █████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ███████ ██████ ██████ ██ ██ ██████ ███████ ███████ +// +// >>resources _SOKOL_PRIVATE sg_buffer_desc _sg_buffer_desc_defaults(const sg_buffer_desc* desc) { sg_buffer_desc def = *desc; def.type = _sg_def(def.type, SG_BUFFERTYPE_VERTEXBUFFER); def.usage = _sg_def(def.usage, SG_USAGE_IMMUTABLE); if (def.size == 0) { def.size = def.data.size; - } - else if (def.data.size == 0) { + } else if (def.data.size == 0) { def.data.size = def.size; } return def; @@ -15131,19 +15493,25 @@ _SOKOL_PRIVATE sg_image_desc _sg_image_desc_defaults(const sg_image_desc* desc) if (desc->render_target) { 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 { + } else { def.pixel_format = _sg_def(def.pixel_format, SG_PIXELFORMAT_RGBA8); def.sample_count = _sg_def(def.sample_count, 1); } + return def; +} + +_SOKOL_PRIVATE sg_sampler_desc _sg_sampler_desc_defaults(const sg_sampler_desc* desc) { + sg_sampler_desc def = *desc; def.min_filter = _sg_def(def.min_filter, SG_FILTER_NEAREST); def.mag_filter = _sg_def(def.mag_filter, SG_FILTER_NEAREST); + def.mipmap_filter = _sg_def(def.mipmap_filter, SG_FILTER_NONE); def.wrap_u = _sg_def(def.wrap_u, SG_WRAP_REPEAT); def.wrap_v = _sg_def(def.wrap_v, SG_WRAP_REPEAT); def.wrap_w = _sg_def(def.wrap_w, SG_WRAP_REPEAT); + def.max_lod = _sg_def_flt(def.max_lod, FLT_MAX); def.border_color = _sg_def(def.border_color, SG_BORDERCOLOR_OPAQUE_BLACK); + def.compare = _sg_def(def.compare, SG_COMPAREFUNC_NEVER); def.max_anisotropy = _sg_def(def.max_anisotropy, 1); - def.max_lod = _sg_def_flt(def.max_lod, FLT_MAX); return def; } @@ -15182,10 +15550,18 @@ _SOKOL_PRIVATE sg_shader_desc _sg_shader_desc_defaults(const sg_shader_desc* des } 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->image_type == _SG_IMAGETYPE_DEFAULT) { + if (!img_desc->used) { break; } - img_desc->sampler_type = _sg_def(img_desc->sampler_type, SG_SAMPLERTYPE_FLOAT); + img_desc->image_type = _sg_def(img_desc->image_type, SG_IMAGETYPE_2D); + img_desc->sample_type = _sg_def(img_desc->sample_type, SG_IMAGESAMPLETYPE_FLOAT); + } + for (int smp_index = 0; smp_index < SG_MAX_SHADERSTAGE_SAMPLERS; smp_index++) { + sg_shader_sampler_desc* smp_desc = &stage_desc->samplers[smp_index]; + if (!smp_desc->used) { + break; + } + smp_desc->sampler_type = _sg_def(smp_desc->sampler_type, SG_SAMPLERTYPE_SAMPLE); } } return def; @@ -15211,12 +15587,17 @@ _SOKOL_PRIVATE sg_pipeline_desc _sg_pipeline_desc_defaults(const sg_pipeline_des def.depth.compare = _sg_def(def.depth.compare, SG_COMPAREFUNC_ALWAYS); def.depth.pixel_format = _sg_def(def.depth.pixel_format, _sg.desc.context.depth_format); - def.color_count = _sg_def(def.color_count, 1); + if (def.colors[0].pixel_format == SG_PIXELFORMAT_NONE) { + // special case depth-only rendering, enforce a color count of 0 + def.color_count = 0; + } else { + def.color_count = _sg_def(def.color_count, 1); + } if (def.color_count > SG_MAX_COLOR_ATTACHMENTS) { def.color_count = SG_MAX_COLOR_ATTACHMENTS; } for (int i = 0; i < def.color_count; i++) { - sg_color_state* cs = &def.colors[i]; + sg_color_target_state* cs = &def.colors[i]; cs->pixel_format = _sg_def(cs->pixel_format, _sg.desc.context.color_format); cs->write_mask = _sg_def(cs->write_mask, SG_COLORMASK_RGBA); sg_blend_state* bs = &def.colors[i].blend; @@ -15229,42 +15610,42 @@ _SOKOL_PRIVATE sg_pipeline_desc _sg_pipeline_desc_defaults(const sg_pipeline_des } for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { - sg_vertex_attr_desc* a_desc = &def.layout.attrs[attr_index]; - if (a_desc->format == SG_VERTEXFORMAT_INVALID) { + sg_vertex_attr_state* a_state = &def.layout.attrs[attr_index]; + if (a_state->format == SG_VERTEXFORMAT_INVALID) { break; } - SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); - sg_buffer_layout_desc* b_desc = &def.layout.buffers[a_desc->buffer_index]; - b_desc->step_func = _sg_def(b_desc->step_func, SG_VERTEXSTEP_PER_VERTEX); - b_desc->step_rate = _sg_def(b_desc->step_rate, 1); + SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEX_BUFFERS); + sg_vertex_buffer_layout_state* l_state = &def.layout.buffers[a_state->buffer_index]; + l_state->step_func = _sg_def(l_state->step_func, SG_VERTEXSTEP_PER_VERTEX); + l_state->step_rate = _sg_def(l_state->step_rate, 1); } - /* resolve vertex layout strides and offsets */ - int auto_offset[SG_MAX_SHADERSTAGE_BUFFERS]; + // resolve vertex layout strides and offsets + int auto_offset[SG_MAX_VERTEX_BUFFERS]; _sg_clear(auto_offset, sizeof(auto_offset)); bool use_auto_offset = true; for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { - /* to use computed offsets, *all* attr offsets must be 0 */ + // to use computed offsets, *all* attr offsets must be 0 if (def.layout.attrs[attr_index].offset != 0) { use_auto_offset = false; } } for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { - sg_vertex_attr_desc* a_desc = &def.layout.attrs[attr_index]; - if (a_desc->format == SG_VERTEXFORMAT_INVALID) { + sg_vertex_attr_state* a_state = &def.layout.attrs[attr_index]; + if (a_state->format == SG_VERTEXFORMAT_INVALID) { break; } - SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); + SOKOL_ASSERT(a_state->buffer_index < SG_MAX_VERTEX_BUFFERS); if (use_auto_offset) { - a_desc->offset = auto_offset[a_desc->buffer_index]; + a_state->offset = auto_offset[a_state->buffer_index]; } - auto_offset[a_desc->buffer_index] += _sg_vertexformat_bytesize(a_desc->format); + auto_offset[a_state->buffer_index] += _sg_vertexformat_bytesize(a_state->format); } - /* compute vertex strides if needed */ - for (int buf_index = 0; buf_index < SG_MAX_SHADERSTAGE_BUFFERS; buf_index++) { - sg_buffer_layout_desc* l_desc = &def.layout.buffers[buf_index]; - if (l_desc->stride == 0) { - l_desc->stride = auto_offset[buf_index]; + // compute vertex strides if needed + for (int buf_index = 0; buf_index < SG_MAX_VERTEX_BUFFERS; buf_index++) { + sg_vertex_buffer_layout_state* l_state = &def.layout.buffers[buf_index]; + if (l_state->stride == 0) { + l_state->stride = auto_offset[buf_index]; } } @@ -15272,21 +15653,19 @@ _SOKOL_PRIVATE sg_pipeline_desc _sg_pipeline_desc_defaults(const sg_pipeline_des } _SOKOL_PRIVATE sg_pass_desc _sg_pass_desc_defaults(const sg_pass_desc* desc) { - /* FIXME: no values to replace in sg_pass_desc? */ + // FIXME: no values to replace in sg_pass_desc? sg_pass_desc def = *desc; return def; } -/*== allocate/initialize resource private functions ==========================*/ _SOKOL_PRIVATE sg_buffer _sg_alloc_buffer(void) { sg_buffer res; int slot_index = _sg_pool_alloc_index(&_sg.pools.buffer_pool); if (_SG_INVALID_SLOT_INDEX != slot_index) { res.id = _sg_slot_alloc(&_sg.pools.buffer_pool, &_sg.pools.buffers[slot_index].slot, slot_index); - } - else { - /* pool is exhausted */ + } else { res.id = SG_INVALID_ID; + _SG_ERROR(BUFFER_POOL_EXHAUSTED); } return res; } @@ -15296,10 +15675,21 @@ _SOKOL_PRIVATE sg_image _sg_alloc_image(void) { int slot_index = _sg_pool_alloc_index(&_sg.pools.image_pool); if (_SG_INVALID_SLOT_INDEX != slot_index) { res.id = _sg_slot_alloc(&_sg.pools.image_pool, &_sg.pools.images[slot_index].slot, slot_index); + } else { + res.id = SG_INVALID_ID; + _SG_ERROR(IMAGE_POOL_EXHAUSTED); } - else { - /* pool is exhausted */ + return res; +} + +_SOKOL_PRIVATE sg_sampler _sg_alloc_sampler(void) { + sg_sampler res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.sampler_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&_sg.pools.sampler_pool, &_sg.pools.samplers[slot_index].slot, slot_index); + } else { res.id = SG_INVALID_ID; + _SG_ERROR(SAMPLER_POOL_EXHAUSTED); } return res; } @@ -15309,10 +15699,9 @@ _SOKOL_PRIVATE sg_shader _sg_alloc_shader(void) { int slot_index = _sg_pool_alloc_index(&_sg.pools.shader_pool); if (_SG_INVALID_SLOT_INDEX != slot_index) { res.id = _sg_slot_alloc(&_sg.pools.shader_pool, &_sg.pools.shaders[slot_index].slot, slot_index); - } - else { - /* pool is exhausted */ + } else { res.id = SG_INVALID_ID; + _SG_ERROR(SHADER_POOL_EXHAUSTED); } return res; } @@ -15322,10 +15711,9 @@ _SOKOL_PRIVATE sg_pipeline _sg_alloc_pipeline(void) { int slot_index = _sg_pool_alloc_index(&_sg.pools.pipeline_pool); if (_SG_INVALID_SLOT_INDEX != slot_index) { res.id =_sg_slot_alloc(&_sg.pools.pipeline_pool, &_sg.pools.pipelines[slot_index].slot, slot_index); - } - else { - /* pool is exhausted */ + } else { res.id = SG_INVALID_ID; + _SG_ERROR(PIPELINE_POOL_EXHAUSTED); } return res; } @@ -15335,10 +15723,9 @@ _SOKOL_PRIVATE sg_pass _sg_alloc_pass(void) { int slot_index = _sg_pool_alloc_index(&_sg.pools.pass_pool); if (_SG_INVALID_SLOT_INDEX != slot_index) { res.id = _sg_slot_alloc(&_sg.pools.pass_pool, &_sg.pools.passes[slot_index].slot, slot_index); - } - else { - /* pool is exhausted */ + } else { res.id = SG_INVALID_ID; + _SG_ERROR(PASS_POOL_EXHAUSTED); } return res; } @@ -15355,6 +15742,12 @@ _SOKOL_PRIVATE void _sg_dealloc_image(_sg_image_t* img) { _sg_reset_slot(&img->slot); } +_SOKOL_PRIVATE void _sg_dealloc_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp && (smp->slot.state == SG_RESOURCESTATE_ALLOC) && (smp->slot.id != SG_INVALID_ID)); + _sg_pool_free_index(&_sg.pools.sampler_pool, _sg_slot_index(smp->slot.id)); + _sg_reset_slot(&smp->slot); +} + _SOKOL_PRIVATE void _sg_dealloc_shader(_sg_shader_t* shd) { SOKOL_ASSERT(shd && (shd->slot.state == SG_RESOURCESTATE_ALLOC) && (shd->slot.id != SG_INVALID_ID)); _sg_pool_free_index(&_sg.pools.shader_pool, _sg_slot_index(shd->slot.id)); @@ -15378,9 +15771,9 @@ _SOKOL_PRIVATE void _sg_init_buffer(_sg_buffer_t* buf, const sg_buffer_desc* des SOKOL_ASSERT(desc); buf->slot.ctx_id = _sg.active_context.id; if (_sg_validate_buffer_desc(desc)) { + _sg_buffer_common_init(&buf->cmn, desc); buf->slot.state = _sg_create_buffer(buf, desc); - } - else { + } else { buf->slot.state = SG_RESOURCESTATE_FAILED; } SOKOL_ASSERT((buf->slot.state == SG_RESOURCESTATE_VALID)||(buf->slot.state == SG_RESOURCESTATE_FAILED)); @@ -15391,22 +15784,35 @@ _SOKOL_PRIVATE void _sg_init_image(_sg_image_t* img, const sg_image_desc* desc) SOKOL_ASSERT(desc); img->slot.ctx_id = _sg.active_context.id; if (_sg_validate_image_desc(desc)) { + _sg_image_common_init(&img->cmn, desc); img->slot.state = _sg_create_image(img, desc); - } - else { + } else { img->slot.state = SG_RESOURCESTATE_FAILED; } SOKOL_ASSERT((img->slot.state == SG_RESOURCESTATE_VALID)||(img->slot.state == SG_RESOURCESTATE_FAILED)); } +_SOKOL_PRIVATE void _sg_init_sampler(_sg_sampler_t* smp, const sg_sampler_desc* desc) { + SOKOL_ASSERT(smp && (smp->slot.state == SG_RESOURCESTATE_ALLOC)); + SOKOL_ASSERT(desc); + smp->slot.ctx_id = _sg.active_context.id; + if (_sg_validate_sampler_desc(desc)) { + _sg_sampler_common_init(&smp->cmn, desc); + smp->slot.state = _sg_create_sampler(smp, desc); + } else { + smp->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((smp->slot.state == SG_RESOURCESTATE_VALID)||(smp->slot.state == SG_RESOURCESTATE_FAILED)); +} + _SOKOL_PRIVATE void _sg_init_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { SOKOL_ASSERT(shd && (shd->slot.state == SG_RESOURCESTATE_ALLOC)); SOKOL_ASSERT(desc); shd->slot.ctx_id = _sg.active_context.id; if (_sg_validate_shader_desc(desc)) { + _sg_shader_common_init(&shd->cmn, desc); shd->slot.state = _sg_create_shader(shd, desc); - } - else { + } else { shd->slot.state = SG_RESOURCESTATE_FAILED; } SOKOL_ASSERT((shd->slot.state == SG_RESOURCESTATE_VALID)||(shd->slot.state == SG_RESOURCESTATE_FAILED)); @@ -15419,13 +15825,12 @@ _SOKOL_PRIVATE void _sg_init_pipeline(_sg_pipeline_t* pip, const sg_pipeline_des if (_sg_validate_pipeline_desc(desc)) { _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, desc->shader.id); if (shd && (shd->slot.state == SG_RESOURCESTATE_VALID)) { + _sg_pipeline_common_init(&pip->cmn, desc); pip->slot.state = _sg_create_pipeline(pip, shd, desc); - } - else { + } else { pip->slot.state = SG_RESOURCESTATE_FAILED; } - } - else { + } else { pip->slot.state = SG_RESOURCESTATE_FAILED; } SOKOL_ASSERT((pip->slot.state == SG_RESOURCESTATE_VALID)||(pip->slot.state == SG_RESOURCESTATE_FAILED)); @@ -15436,34 +15841,45 @@ _SOKOL_PRIVATE void _sg_init_pass(_sg_pass_t* pass, const sg_pass_desc* desc) { SOKOL_ASSERT(desc); pass->slot.ctx_id = _sg.active_context.id; if (_sg_validate_pass_desc(desc)) { - /* lookup pass attachment image pointers */ - _sg_image_t* att_imgs[SG_MAX_COLOR_ATTACHMENTS + 1]; + // lookup pass attachment image pointers + _sg_image_t* color_images[SG_MAX_COLOR_ATTACHMENTS] = { 0 }; + _sg_image_t* resolve_images[SG_MAX_COLOR_ATTACHMENTS] = { 0 }; + _sg_image_t* ds_image = 0; + // NOTE: validation already checked that all surfaces are same width/height + int width = 0; + int height = 0; 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); - if (!(att_imgs[i] && att_imgs[i]->slot.state == SG_RESOURCESTATE_VALID)) { + color_images[i] = _sg_lookup_image(&_sg.pools, desc->color_attachments[i].image.id); + if (!(color_images[i] && color_images[i]->slot.state == SG_RESOURCESTATE_VALID)) { pass->slot.state = SG_RESOURCESTATE_FAILED; return; } + const int mip_level = desc->color_attachments[i].mip_level; + width = _sg_miplevel_dim(color_images[i]->cmn.width, mip_level); + height = _sg_miplevel_dim(color_images[i]->cmn.height, mip_level); } - else { - att_imgs[i] = 0; + if (desc->resolve_attachments[i].image.id) { + resolve_images[i] = _sg_lookup_image(&_sg.pools, desc->resolve_attachments[i].image.id); + if (!(resolve_images[i] && resolve_images[i]->slot.state == SG_RESOURCESTATE_VALID)) { + pass->slot.state = SG_RESOURCESTATE_FAILED; + return; + } } } - 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); - if (!(att_imgs[ds_att_index] && att_imgs[ds_att_index]->slot.state == SG_RESOURCESTATE_VALID)) { + ds_image = _sg_lookup_image(&_sg.pools, desc->depth_stencil_attachment.image.id); + if (!(ds_image && ds_image->slot.state == SG_RESOURCESTATE_VALID)) { pass->slot.state = SG_RESOURCESTATE_FAILED; return; } + const int mip_level = desc->depth_stencil_attachment.mip_level; + width = _sg_miplevel_dim(ds_image->cmn.width, mip_level); + height = _sg_miplevel_dim(ds_image->cmn.height, mip_level); } - else { - att_imgs[ds_att_index] = 0; - } - pass->slot.state = _sg_create_pass(pass, att_imgs, desc); - } - else { + _sg_pass_common_init(&pass->cmn, desc, width, height); + pass->slot.state = _sg_create_pass(pass, color_images, resolve_images, ds_image, desc); + } else { pass->slot.state = SG_RESOURCESTATE_FAILED; } SOKOL_ASSERT((pass->slot.state == SG_RESOURCESTATE_VALID)||(pass->slot.state == SG_RESOURCESTATE_FAILED)); @@ -15474,10 +15890,8 @@ _SOKOL_PRIVATE void _sg_uninit_buffer(_sg_buffer_t* buf) { if (buf->slot.ctx_id == _sg.active_context.id) { _sg_discard_buffer(buf); _sg_reset_buffer_to_alloc_state(buf); - } - else { - SG_LOG("_sg_uninit_buffer: active context mismatch (must be same as for creation)"); - _SG_TRACE_NOARGS(err_context_mismatch); + } else { + _SG_WARN(UNINIT_BUFFER_ACTIVE_CONTEXT_MISMATCH); } } @@ -15486,10 +15900,18 @@ _SOKOL_PRIVATE void _sg_uninit_image(_sg_image_t* img) { if (img->slot.ctx_id == _sg.active_context.id) { _sg_discard_image(img); _sg_reset_image_to_alloc_state(img); + } else { + _SG_WARN(UNINIT_IMAGE_ACTIVE_CONTEXT_MISMATCH); } - else { - SG_LOG("_sg_uninit_image: active context mismatch (must be same as for creation)"); - _SG_TRACE_NOARGS(err_context_mismatch); +} + +_SOKOL_PRIVATE void _sg_uninit_sampler(_sg_sampler_t* smp) { + SOKOL_ASSERT(smp && ((smp->slot.state == SG_RESOURCESTATE_VALID) || (smp->slot.state == SG_RESOURCESTATE_FAILED))); + if (smp->slot.ctx_id == _sg.active_context.id) { + _sg_discard_sampler(smp); + _sg_reset_sampler_to_alloc_state(smp); + } else { + _SG_WARN(UNINIT_SAMPLER_ACTIVE_CONTEXT_MISMATCH); } } @@ -15498,10 +15920,8 @@ _SOKOL_PRIVATE void _sg_uninit_shader(_sg_shader_t* shd) { if (shd->slot.ctx_id == _sg.active_context.id) { _sg_discard_shader(shd); _sg_reset_shader_to_alloc_state(shd); - } - else { - SG_LOG("_sg_uninit_shader: active context mismatch (must be same as for creation)"); - _SG_TRACE_NOARGS(err_context_mismatch); + } else { + _SG_WARN(UNINIT_SHADER_ACTIVE_CONTEXT_MISMATCH); } } @@ -15510,10 +15930,8 @@ _SOKOL_PRIVATE void _sg_uninit_pipeline(_sg_pipeline_t* pip) { if (pip->slot.ctx_id == _sg.active_context.id) { _sg_discard_pipeline(pip); _sg_reset_pipeline_to_alloc_state(pip); - } - else { - SG_LOG("_sg_uninit_pipeline: active context mismatch (must be same as for creation)"); - _SG_TRACE_NOARGS(err_context_mismatch); + } else { + _SG_WARN(UNINIT_PIPELINE_ACTIVE_CONTEXT_MISMATCH); } } @@ -15522,10 +15940,8 @@ _SOKOL_PRIVATE void _sg_uninit_pass(_sg_pass_t* pass) { if (pass->slot.ctx_id == _sg.active_context.id) { _sg_discard_pass(pass); _sg_reset_pass_to_alloc_state(pass); - } - else { - SG_LOG("_sg_uninit_pass: active context mismatch (must be same as for creation)"); - _SG_TRACE_NOARGS(err_context_mismatch); + } else { + _SG_WARN(UNINIT_PASS_ACTIVE_CONTEXT_MISMATCH); } } @@ -15562,7 +15978,7 @@ _SOKOL_PRIVATE bool _sg_add_commit_listener(const sg_commit_listener* new_listen for (int i = 0; i < _sg.commit_listeners.upper; i++) { const sg_commit_listener* slot = &_sg.commit_listeners.items[i]; if ((slot->func == new_listener->func) && (slot->user_data == new_listener->user_data)) { - SG_LOG("attempting to add identical commit listener\n"); + _SG_ERROR(IDENTICAL_COMMIT_LISTENER); return false; } } @@ -15581,7 +15997,7 @@ _SOKOL_PRIVATE bool _sg_add_commit_listener(const sg_commit_listener* new_listen } } if (!slot) { - SG_LOG("commit listener array full\n"); + _SG_ERROR(COMMIT_LISTENER_ARRAY_FULL); return false; } *slot = *new_listener; @@ -15622,23 +16038,28 @@ _SOKOL_PRIVATE sg_desc _sg_desc_defaults(const sg_desc* desc) { res.context.sample_count = _sg_def(res.context.sample_count, 1); res.buffer_pool_size = _sg_def(res.buffer_pool_size, _SG_DEFAULT_BUFFER_POOL_SIZE); res.image_pool_size = _sg_def(res.image_pool_size, _SG_DEFAULT_IMAGE_POOL_SIZE); + res.sampler_pool_size = _sg_def(res.sampler_pool_size, _SG_DEFAULT_SAMPLER_POOL_SIZE); res.shader_pool_size = _sg_def(res.shader_pool_size, _SG_DEFAULT_SHADER_POOL_SIZE); res.pipeline_pool_size = _sg_def(res.pipeline_pool_size, _SG_DEFAULT_PIPELINE_POOL_SIZE); res.pass_pool_size = _sg_def(res.pass_pool_size, _SG_DEFAULT_PASS_POOL_SIZE); res.context_pool_size = _sg_def(res.context_pool_size, _SG_DEFAULT_CONTEXT_POOL_SIZE); res.uniform_buffer_size = _sg_def(res.uniform_buffer_size, _SG_DEFAULT_UB_SIZE); res.staging_buffer_size = _sg_def(res.staging_buffer_size, _SG_DEFAULT_STAGING_SIZE); - res.sampler_cache_size = _sg_def(res.sampler_cache_size, _SG_DEFAULT_SAMPLER_CACHE_CAPACITY); res.max_commit_listeners = _sg_def(res.max_commit_listeners, _SG_DEFAULT_MAX_COMMIT_LISTENERS); return res; } -/*== PUBLIC API FUNCTIONS ====================================================*/ - +// ██████ ██ ██ ██████ ██ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ ██ ██████ +// +// >>public SOKOL_API_IMPL void sg_setup(const sg_desc* desc) { SOKOL_ASSERT(desc); SOKOL_ASSERT((desc->_start_canary == 0) && (desc->_end_canary == 0)); - SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free)); + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); _SG_CLEAR_ARC_STRUCT(_sg_state_t, _sg); _sg.desc = _sg_desc_defaults(desc); _sg_setup_pools(&_sg.pools, &_sg.desc); @@ -15708,9 +16129,8 @@ SOKOL_API_IMPL sg_context sg_setup_context(void) { ctx->slot.state = _sg_create_context(ctx); SOKOL_ASSERT(ctx->slot.state == SG_RESOURCESTATE_VALID); _sg_activate_context(ctx); - } - else { - /* pool is exhausted */ + } else { + // pool is exhausted res.id = SG_INVALID_ID; } _sg.active_context = res; @@ -15735,7 +16155,7 @@ SOKOL_API_IMPL void sg_activate_context(sg_context ctx_id) { SOKOL_ASSERT(_sg.valid); _sg.active_context = ctx_id; _sg_context_t* ctx = _sg_lookup_context(&_sg.pools, ctx_id.id); - /* NOTE: ctx can be 0 here if the context is no longer valid */ + // NOTE: ctx can be 0 here if the context is no longer valid _sg_activate_context(ctx); } @@ -15748,7 +16168,7 @@ SOKOL_API_IMPL sg_trace_hooks sg_install_trace_hooks(const sg_trace_hooks* trace _sg.hooks = *trace_hooks; #else static sg_trace_hooks old_hooks; - SG_LOG("sg_install_trace_hooks() called, but SG_TRACE_HOOKS is not defined!"); + _SG_WARN(TRACE_HOOKS_NOT_ENABLED); #endif return old_hooks; } @@ -15767,6 +16187,13 @@ SOKOL_API_IMPL sg_image sg_alloc_image(void) { return res; } +SOKOL_API_IMPL sg_sampler sg_alloc_sampler(void) { + SOKOL_ASSERT(_sg.valid); + sg_sampler res = _sg_alloc_sampler(); + _SG_TRACE_ARGS(alloc_sampler, res); + return res; +} + SOKOL_API_IMPL sg_shader sg_alloc_shader(void) { SOKOL_ASSERT(_sg.valid); sg_shader res = _sg_alloc_shader(); @@ -15794,9 +16221,8 @@ SOKOL_API_IMPL void sg_dealloc_buffer(sg_buffer buf_id) { if (buf) { if (buf->slot.state == SG_RESOURCESTATE_ALLOC) { _sg_dealloc_buffer(buf); - } - else { - SG_LOG("sg_dealloc_buffer: buffer must be in ALLOC state\n"); + } else { + _SG_ERROR(DEALLOC_BUFFER_INVALID_STATE); } } _SG_TRACE_ARGS(dealloc_buffer, buf_id); @@ -15808,23 +16234,34 @@ SOKOL_API_IMPL void sg_dealloc_image(sg_image img_id) { if (img) { if (img->slot.state == SG_RESOURCESTATE_ALLOC) { _sg_dealloc_image(img); - } - else { - SG_LOG("sg_dealloc_image: image must be in ALLOC state\n"); + } else { + _SG_ERROR(DEALLOC_IMAGE_INVALID_STATE); } } _SG_TRACE_ARGS(dealloc_image, img_id); } +SOKOL_API_IMPL void sg_dealloc_sampler(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + if (smp->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_sampler(smp); + } else { + _SG_ERROR(DEALLOC_SAMPLER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(dealloc_sampler, smp_id); +} + SOKOL_API_IMPL void sg_dealloc_shader(sg_shader shd_id) { SOKOL_ASSERT(_sg.valid); _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); if (shd) { if (shd->slot.state == SG_RESOURCESTATE_ALLOC) { _sg_dealloc_shader(shd); - } - else { - SG_LOG("sg_dealloc_shader: shader must be in ALLOC state\n"); + } else { + _SG_ERROR(DEALLOC_SHADER_INVALID_STATE); } } _SG_TRACE_ARGS(dealloc_shader, shd_id); @@ -15836,9 +16273,8 @@ SOKOL_API_IMPL void sg_dealloc_pipeline(sg_pipeline pip_id) { if (pip) { if (pip->slot.state == SG_RESOURCESTATE_ALLOC) { _sg_dealloc_pipeline(pip); - } - else { - SG_LOG("sg_dealloc_pipeline: pipeline must be in ALLOC state\n"); + } else { + _SG_ERROR(DEALLOC_PIPELINE_INVALID_STATE); } } _SG_TRACE_ARGS(dealloc_pipeline, pip_id); @@ -15850,9 +16286,8 @@ SOKOL_API_IMPL void sg_dealloc_pass(sg_pass pass_id) { if (pass) { if (pass->slot.state == SG_RESOURCESTATE_ALLOC) { _sg_dealloc_pass(pass); - } - else { - SG_LOG("sg_dealloc_pass: pass must be in ALLOC state\n"); + } else { + _SG_ERROR(DEALLOC_PASS_INVALID_STATE); } } _SG_TRACE_ARGS(dealloc_pass, pass_id); @@ -15866,9 +16301,8 @@ SOKOL_API_IMPL void sg_init_buffer(sg_buffer buf_id, const sg_buffer_desc* desc) if (buf->slot.state == SG_RESOURCESTATE_ALLOC) { _sg_init_buffer(buf, &desc_def); SOKOL_ASSERT((buf->slot.state == SG_RESOURCESTATE_VALID) || (buf->slot.state == SG_RESOURCESTATE_FAILED)); - } - else { - SG_LOG("sg_init_buffer: buffer must be in alloc state\n"); + } else { + _SG_ERROR(INIT_BUFFER_INVALID_STATE); } } _SG_TRACE_ARGS(init_buffer, buf_id, &desc_def); @@ -15882,14 +16316,28 @@ SOKOL_API_IMPL void sg_init_image(sg_image img_id, const sg_image_desc* desc) { if (img->slot.state == SG_RESOURCESTATE_ALLOC) { _sg_init_image(img, &desc_def); SOKOL_ASSERT((img->slot.state == SG_RESOURCESTATE_VALID) || (img->slot.state == SG_RESOURCESTATE_FAILED)); - } - else { - SG_LOG("sg_init_image: image must be in alloc state\n"); + } else { + _SG_ERROR(INIT_IMAGE_INVALID_STATE); } } _SG_TRACE_ARGS(init_image, img_id, &desc_def); } +SOKOL_API_IMPL void sg_init_sampler(sg_sampler smp_id, const sg_sampler_desc* desc) { + SOKOL_ASSERT(_sg.valid); + sg_sampler_desc desc_def = _sg_sampler_desc_defaults(desc); + _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + if (smp->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_init_sampler(smp, &desc_def); + SOKOL_ASSERT((smp->slot.state == SG_RESOURCESTATE_VALID) || (smp->slot.state == SG_RESOURCESTATE_FAILED)); + } else { + _SG_ERROR(INIT_SAMPLER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(init_sampler, smp_id, &desc_def); +} + SOKOL_API_IMPL void sg_init_shader(sg_shader shd_id, const sg_shader_desc* desc) { SOKOL_ASSERT(_sg.valid); sg_shader_desc desc_def = _sg_shader_desc_defaults(desc); @@ -15898,9 +16346,8 @@ SOKOL_API_IMPL void sg_init_shader(sg_shader shd_id, const sg_shader_desc* desc) if (shd->slot.state == SG_RESOURCESTATE_ALLOC) { _sg_init_shader(shd, &desc_def); SOKOL_ASSERT((shd->slot.state == SG_RESOURCESTATE_VALID) || (shd->slot.state == SG_RESOURCESTATE_FAILED)); - } - else { - SG_LOG("sg_init_shader: shader must be in alloc state\n"); + } else { + _SG_ERROR(INIT_SHADER_INVALID_STATE); } } _SG_TRACE_ARGS(init_shader, shd_id, &desc_def); @@ -15914,9 +16361,8 @@ SOKOL_API_IMPL void sg_init_pipeline(sg_pipeline pip_id, const sg_pipeline_desc* if (pip->slot.state == SG_RESOURCESTATE_ALLOC) { _sg_init_pipeline(pip, &desc_def); SOKOL_ASSERT((pip->slot.state == SG_RESOURCESTATE_VALID) || (pip->slot.state == SG_RESOURCESTATE_FAILED)); - } - else { - SG_LOG("sg_init_pipeline: pipeline must be in alloc state\n"); + } else { + _SG_ERROR(INIT_PIPELINE_INVALID_STATE); } } _SG_TRACE_ARGS(init_pipeline, pip_id, &desc_def); @@ -15930,9 +16376,8 @@ SOKOL_API_IMPL void sg_init_pass(sg_pass pass_id, const sg_pass_desc* desc) { if (pass->slot.state == SG_RESOURCESTATE_ALLOC) { _sg_init_pass(pass, &desc_def); SOKOL_ASSERT((pass->slot.state == SG_RESOURCESTATE_VALID) || (pass->slot.state == SG_RESOURCESTATE_FAILED)); - } - else { - SG_LOG("sg_init_pass: pass must be in alloc state\n"); + } else { + _SG_ERROR(INIT_PASS_INVALID_STATE); } } _SG_TRACE_ARGS(init_pass, pass_id, &desc_def); @@ -15945,9 +16390,8 @@ SOKOL_API_IMPL void sg_uninit_buffer(sg_buffer buf_id) { if ((buf->slot.state == SG_RESOURCESTATE_VALID) || (buf->slot.state == SG_RESOURCESTATE_FAILED)) { _sg_uninit_buffer(buf); SOKOL_ASSERT(buf->slot.state == SG_RESOURCESTATE_ALLOC); - } - else { - SG_LOG("sg_uninit_buffer: buffer must be in VALID or FAILED state\n"); + } else { + _SG_ERROR(UNINIT_BUFFER_INVALID_STATE); } } _SG_TRACE_ARGS(uninit_buffer, buf_id); @@ -15960,14 +16404,27 @@ SOKOL_API_IMPL void sg_uninit_image(sg_image img_id) { if ((img->slot.state == SG_RESOURCESTATE_VALID) || (img->slot.state == SG_RESOURCESTATE_FAILED)) { _sg_uninit_image(img); SOKOL_ASSERT(img->slot.state == SG_RESOURCESTATE_ALLOC); - } - else { - SG_LOG("sg_uninit_image: image must be in VALID or FAILED state\n"); + } else { + _SG_ERROR(UNINIT_IMAGE_INVALID_STATE); } } _SG_TRACE_ARGS(uninit_image, img_id); } +SOKOL_API_IMPL void sg_uninit_sampler(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + if ((smp->slot.state == SG_RESOURCESTATE_VALID) || (smp->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_sampler(smp); + SOKOL_ASSERT(smp->slot.state == SG_RESOURCESTATE_ALLOC); + } else { + _SG_ERROR(UNINIT_SAMPLER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(uninit_sampler, smp_id); +} + SOKOL_API_IMPL void sg_uninit_shader(sg_shader shd_id) { SOKOL_ASSERT(_sg.valid); _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); @@ -15975,9 +16432,8 @@ SOKOL_API_IMPL void sg_uninit_shader(sg_shader shd_id) { if ((shd->slot.state == SG_RESOURCESTATE_VALID) || (shd->slot.state == SG_RESOURCESTATE_FAILED)) { _sg_uninit_shader(shd); SOKOL_ASSERT(shd->slot.state == SG_RESOURCESTATE_ALLOC); - } - else { - SG_LOG("sg_uninit_shader: shader must be in VALID or FAILED state\n"); + } else { + _SG_ERROR(UNINIT_SHADER_INVALID_STATE); } } _SG_TRACE_ARGS(uninit_shader, shd_id); @@ -15990,9 +16446,8 @@ SOKOL_API_IMPL void sg_uninit_pipeline(sg_pipeline pip_id) { if ((pip->slot.state == SG_RESOURCESTATE_VALID) || (pip->slot.state == SG_RESOURCESTATE_FAILED)) { _sg_uninit_pipeline(pip); SOKOL_ASSERT(pip->slot.state == SG_RESOURCESTATE_ALLOC); - } - else { - SG_LOG("sg_uninit_pipeline: pipeline must be in VALID or FAILED state\n"); + } else { + _SG_ERROR(UNINIT_PIPELINE_INVALID_STATE); } } _SG_TRACE_ARGS(uninit_pipeline, pip_id); @@ -16005,15 +16460,13 @@ SOKOL_API_IMPL void sg_uninit_pass(sg_pass pass_id) { if ((pass->slot.state == SG_RESOURCESTATE_VALID) || (pass->slot.state == SG_RESOURCESTATE_FAILED)) { _sg_uninit_pass(pass); SOKOL_ASSERT(pass->slot.state == SG_RESOURCESTATE_ALLOC); - } - else { - SG_LOG("sg_uninit_pass: pass must be in VALID or FAILED state\n"); + } else { + _SG_ERROR(UNINIT_PASS_INVALID_STATE); } } _SG_TRACE_ARGS(uninit_pass, pass_id); } -/*-- set allocated resource to failed state ----------------------------------*/ SOKOL_API_IMPL void sg_fail_buffer(sg_buffer buf_id) { SOKOL_ASSERT(_sg.valid); _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); @@ -16021,9 +16474,8 @@ SOKOL_API_IMPL void sg_fail_buffer(sg_buffer buf_id) { if (buf->slot.state == SG_RESOURCESTATE_ALLOC) { buf->slot.ctx_id = _sg.active_context.id; buf->slot.state = SG_RESOURCESTATE_FAILED; - } - else { - SG_LOG("sg_fail_buffer: buffer must be in ALLOC state\n"); + } else { + _SG_ERROR(FAIL_BUFFER_INVALID_STATE); } } _SG_TRACE_ARGS(fail_buffer, buf_id); @@ -16036,14 +16488,27 @@ SOKOL_API_IMPL void sg_fail_image(sg_image img_id) { if (img->slot.state == SG_RESOURCESTATE_ALLOC) { img->slot.ctx_id = _sg.active_context.id; img->slot.state = SG_RESOURCESTATE_FAILED; - } - else { - SG_LOG("sg_fail_image: image must be in ALLOC state\n"); + } else { + _SG_ERROR(FAIL_IMAGE_INVALID_STATE); } } _SG_TRACE_ARGS(fail_image, img_id); } +SOKOL_API_IMPL void sg_fail_sampler(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + if (smp->slot.state == SG_RESOURCESTATE_ALLOC) { + smp->slot.ctx_id = _sg.active_context.id; + smp->slot.state = SG_RESOURCESTATE_FAILED; + } else { + _SG_ERROR(FAIL_SAMPLER_INVALID_STATE); + } + } + _SG_TRACE_ARGS(fail_sampler, smp_id); +} + SOKOL_API_IMPL void sg_fail_shader(sg_shader shd_id) { SOKOL_ASSERT(_sg.valid); _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); @@ -16051,9 +16516,8 @@ SOKOL_API_IMPL void sg_fail_shader(sg_shader shd_id) { if (shd->slot.state == SG_RESOURCESTATE_ALLOC) { shd->slot.ctx_id = _sg.active_context.id; shd->slot.state = SG_RESOURCESTATE_FAILED; - } - else { - SG_LOG("sg_fail_shader: shader must be in ALLOC state\n"); + } else { + _SG_ERROR(FAIL_SHADER_INVALID_STATE); } } _SG_TRACE_ARGS(fail_shader, shd_id); @@ -16066,9 +16530,8 @@ SOKOL_API_IMPL void sg_fail_pipeline(sg_pipeline pip_id) { if (pip->slot.state == SG_RESOURCESTATE_ALLOC) { pip->slot.ctx_id = _sg.active_context.id; pip->slot.state = SG_RESOURCESTATE_FAILED; - } - else { - SG_LOG("sg_fail_pipeline: pipeline must be in ALLOC state\n"); + } else { + _SG_ERROR(FAIL_PIPELINE_INVALID_STATE); } } _SG_TRACE_ARGS(fail_pipeline, pip_id); @@ -16081,15 +16544,13 @@ SOKOL_API_IMPL void sg_fail_pass(sg_pass pass_id) { if (pass->slot.state == SG_RESOURCESTATE_ALLOC) { pass->slot.ctx_id = _sg.active_context.id; pass->slot.state = SG_RESOURCESTATE_FAILED; - } - else { - SG_LOG("sg_fail_pass: pass must be in ALLOC state\n"); + } else { + _SG_ERROR(FAIL_PASS_INVALID_STATE); } } _SG_TRACE_ARGS(fail_pass, pass_id); } -/*-- get resource state */ SOKOL_API_IMPL sg_resource_state sg_query_buffer_state(sg_buffer buf_id) { SOKOL_ASSERT(_sg.valid); _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); @@ -16104,6 +16565,13 @@ SOKOL_API_IMPL sg_resource_state sg_query_image_state(sg_image img_id) { return res; } +SOKOL_API_IMPL sg_resource_state sg_query_sampler_state(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + sg_resource_state res = smp ? smp->slot.state : SG_RESOURCESTATE_INVALID; + return res; +} + SOKOL_API_IMPL sg_resource_state sg_query_shader_state(sg_shader shd_id) { SOKOL_ASSERT(_sg.valid); _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); @@ -16125,7 +16593,6 @@ SOKOL_API_IMPL sg_resource_state sg_query_pass_state(sg_pass pass_id) { return res; } -/*-- allocate and initialize resource ----------------------------------------*/ SOKOL_API_IMPL sg_buffer sg_make_buffer(const sg_buffer_desc* desc) { SOKOL_ASSERT(_sg.valid); SOKOL_ASSERT(desc); @@ -16137,10 +16604,6 @@ SOKOL_API_IMPL sg_buffer sg_make_buffer(const sg_buffer_desc* desc) { _sg_init_buffer(buf, &desc_def); SOKOL_ASSERT((buf->slot.state == SG_RESOURCESTATE_VALID) || (buf->slot.state == SG_RESOURCESTATE_FAILED)); } - else { - SG_LOG("buffer pool exhausted!"); - _SG_TRACE_NOARGS(err_buffer_pool_exhausted); - } _SG_TRACE_ARGS(make_buffer, &desc_def, buf_id); return buf_id; } @@ -16156,14 +16619,25 @@ SOKOL_API_IMPL sg_image sg_make_image(const sg_image_desc* desc) { _sg_init_image(img, &desc_def); SOKOL_ASSERT((img->slot.state == SG_RESOURCESTATE_VALID) || (img->slot.state == SG_RESOURCESTATE_FAILED)); } - else { - SG_LOG("image pool exhausted!"); - _SG_TRACE_NOARGS(err_image_pool_exhausted); - } _SG_TRACE_ARGS(make_image, &desc_def, img_id); return img_id; } +SOKOL_API_IMPL sg_sampler sg_make_sampler(const sg_sampler_desc* desc) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(desc); + sg_sampler_desc desc_def = _sg_sampler_desc_defaults(desc); + sg_sampler smp_id = _sg_alloc_sampler(); + if (smp_id.id != SG_INVALID_ID) { + _sg_sampler_t* smp = _sg_sampler_at(&_sg.pools, smp_id.id); + SOKOL_ASSERT(smp && (smp->slot.state == SG_RESOURCESTATE_ALLOC)); + _sg_init_sampler(smp, &desc_def); + SOKOL_ASSERT((smp->slot.state == SG_RESOURCESTATE_VALID) || (smp->slot.state == SG_RESOURCESTATE_FAILED)); + } + _SG_TRACE_ARGS(make_sampler, &desc_def, smp_id); + return smp_id; +} + SOKOL_API_IMPL sg_shader sg_make_shader(const sg_shader_desc* desc) { SOKOL_ASSERT(_sg.valid); SOKOL_ASSERT(desc); @@ -16175,10 +16649,6 @@ SOKOL_API_IMPL sg_shader sg_make_shader(const sg_shader_desc* desc) { _sg_init_shader(shd, &desc_def); SOKOL_ASSERT((shd->slot.state == SG_RESOURCESTATE_VALID) || (shd->slot.state == SG_RESOURCESTATE_FAILED)); } - else { - SG_LOG("shader pool exhausted!"); - _SG_TRACE_NOARGS(err_shader_pool_exhausted); - } _SG_TRACE_ARGS(make_shader, &desc_def, shd_id); return shd_id; } @@ -16194,10 +16664,6 @@ SOKOL_API_IMPL sg_pipeline sg_make_pipeline(const sg_pipeline_desc* desc) { _sg_init_pipeline(pip, &desc_def); SOKOL_ASSERT((pip->slot.state == SG_RESOURCESTATE_VALID) || (pip->slot.state == SG_RESOURCESTATE_FAILED)); } - else { - SG_LOG("pipeline pool exhausted!"); - _SG_TRACE_NOARGS(err_pipeline_pool_exhausted); - } _SG_TRACE_ARGS(make_pipeline, &desc_def, pip_id); return pip_id; } @@ -16213,15 +16679,10 @@ SOKOL_API_IMPL sg_pass sg_make_pass(const sg_pass_desc* desc) { _sg_init_pass(pass, &desc_def); SOKOL_ASSERT((pass->slot.state == SG_RESOURCESTATE_VALID) || (pass->slot.state == SG_RESOURCESTATE_FAILED)); } - else { - SG_LOG("pass pool exhausted!"); - _SG_TRACE_NOARGS(err_pass_pool_exhausted); - } _SG_TRACE_ARGS(make_pass, &desc_def, pass_id); return pass_id; } -/*-- destroy resource --------------------------------------------------------*/ SOKOL_API_IMPL void sg_destroy_buffer(sg_buffer buf_id) { SOKOL_ASSERT(_sg.valid); _SG_TRACE_ARGS(destroy_buffer, buf_id); @@ -16254,6 +16715,22 @@ SOKOL_API_IMPL void sg_destroy_image(sg_image img_id) { } } +SOKOL_API_IMPL void sg_destroy_sampler(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + _SG_TRACE_ARGS(destroy_sampler, smp_id); + _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + if ((smp->slot.state == SG_RESOURCESTATE_VALID) || (smp->slot.state == SG_RESOURCESTATE_FAILED)) { + _sg_uninit_sampler(smp); + SOKOL_ASSERT(smp->slot.state == SG_RESOURCESTATE_ALLOC); + } + if (smp->slot.state == SG_RESOURCESTATE_ALLOC) { + _sg_dealloc_sampler(smp); + SOKOL_ASSERT(smp->slot.state == SG_RESOURCESTATE_INITIAL); + } + } +} + SOKOL_API_IMPL void sg_destroy_shader(sg_shader shd_id) { SOKOL_ASSERT(_sg.valid); _SG_TRACE_ARGS(destroy_shader, shd_id); @@ -16311,7 +16788,7 @@ SOKOL_API_IMPL void sg_begin_default_pass(const sg_pass_action* pass_action, int _sg.cur_pass.id = SG_INVALID_ID; _sg.pass_valid = true; _sg_begin_pass(0, &pa, width, height); - _SG_TRACE_ARGS(begin_default_pass, pass_action, width, height); + _SG_TRACE_ARGS(begin_default_pass, &pa, width, height); } SOKOL_API_IMPL void sg_begin_default_passf(const sg_pass_action* pass_action, float width, float height) { @@ -16328,23 +16805,16 @@ 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 _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); - } - else { + _sg_begin_pass(pass, &pa, pass->cmn.width, pass->cmn.height); + _SG_TRACE_ARGS(begin_pass, pass_id, &pa); + } else { _sg.pass_valid = false; - _SG_TRACE_NOARGS(err_pass_invalid); } } SOKOL_API_IMPL void sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left) { SOKOL_ASSERT(_sg.valid); if (!_sg.pass_valid) { - _SG_TRACE_NOARGS(err_pass_invalid); return; } _sg_apply_viewport(x, y, width, height, origin_top_left); @@ -16358,7 +16828,6 @@ SOKOL_API_IMPL void sg_apply_viewportf(float x, float y, float width, float heig SOKOL_API_IMPL void sg_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left) { SOKOL_ASSERT(_sg.valid); if (!_sg.pass_valid) { - _SG_TRACE_NOARGS(err_pass_invalid); return; } _sg_apply_scissor_rect(x, y, width, height, origin_top_left); @@ -16374,11 +16843,9 @@ SOKOL_API_IMPL void sg_apply_pipeline(sg_pipeline pip_id) { _sg.bindings_valid = false; if (!_sg_validate_apply_pipeline(pip_id)) { _sg.next_draw_valid = false; - _SG_TRACE_NOARGS(err_draw_invalid); return; } if (!_sg.pass_valid) { - _SG_TRACE_NOARGS(err_pass_invalid); return; } _sg.cur_pipeline = pip_id; @@ -16396,7 +16863,6 @@ SOKOL_API_IMPL void sg_apply_bindings(const sg_bindings* bindings) { SOKOL_ASSERT((bindings->_start_canary == 0) && (bindings->_end_canary==0)); if (!_sg_validate_apply_bindings(bindings)) { _sg.next_draw_valid = false; - _SG_TRACE_NOARGS(err_draw_invalid); return; } _sg.bindings_valid = true; @@ -16404,16 +16870,15 @@ SOKOL_API_IMPL void sg_apply_bindings(const sg_bindings* bindings) { _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, _sg.cur_pipeline.id); SOKOL_ASSERT(pip); - _sg_buffer_t* vbs[SG_MAX_SHADERSTAGE_BUFFERS] = { 0 }; + _sg_buffer_t* vbs[SG_MAX_VERTEX_BUFFERS] = { 0 }; int num_vbs = 0; - for (int i = 0; i < SG_MAX_SHADERSTAGE_BUFFERS; i++, num_vbs++) { + for (int i = 0; i < SG_MAX_VERTEX_BUFFERS; i++, num_vbs++) { if (bindings->vertex_buffers[i].id) { 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]->cmn.append_overflow; - } - else { + } else { break; } } @@ -16429,12 +16894,11 @@ SOKOL_API_IMPL void sg_apply_bindings(const sg_bindings* bindings) { _sg_image_t* vs_imgs[SG_MAX_SHADERSTAGE_IMAGES] = { 0 }; int num_vs_imgs = 0; for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++, num_vs_imgs++) { - if (bindings->vs_images[i].id) { - vs_imgs[i] = _sg_lookup_image(&_sg.pools, bindings->vs_images[i].id); + if (bindings->vs.images[i].id) { + vs_imgs[i] = _sg_lookup_image(&_sg.pools, bindings->vs.images[i].id); SOKOL_ASSERT(vs_imgs[i]); _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == vs_imgs[i]->slot.state); - } - else { + } else { break; } } @@ -16442,24 +16906,45 @@ SOKOL_API_IMPL void sg_apply_bindings(const sg_bindings* bindings) { _sg_image_t* fs_imgs[SG_MAX_SHADERSTAGE_IMAGES] = { 0 }; int num_fs_imgs = 0; for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++, num_fs_imgs++) { - if (bindings->fs_images[i].id) { - fs_imgs[i] = _sg_lookup_image(&_sg.pools, bindings->fs_images[i].id); + if (bindings->fs.images[i].id) { + fs_imgs[i] = _sg_lookup_image(&_sg.pools, bindings->fs.images[i].id); SOKOL_ASSERT(fs_imgs[i]); _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == fs_imgs[i]->slot.state); + } else { + break; + } + } + + _sg_sampler_t* vs_smps[SG_MAX_SHADERSTAGE_SAMPLERS] = { 0 }; + int num_vs_smps = 0; + for (int i = 0; i < SG_MAX_SHADERSTAGE_SAMPLERS; i++, num_vs_smps++) { + if (bindings->vs.samplers[i].id) { + vs_smps[i] = _sg_lookup_sampler(&_sg.pools, bindings->vs.samplers[i].id); + SOKOL_ASSERT(vs_smps[i]); + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == vs_smps[i]->slot.state); + } else { + break; } - else { + } + + _sg_sampler_t* fs_smps[SG_MAX_SHADERSTAGE_SAMPLERS] = { 0 }; + int num_fs_smps = 0; + for (int i = 0; i < SG_MAX_SHADERSTAGE_SAMPLERS; i++, num_fs_smps++) { + if (bindings->fs.samplers[i].id) { + fs_smps[i] = _sg_lookup_sampler(&_sg.pools, bindings->fs.samplers[i].id); + SOKOL_ASSERT(fs_smps[i]); + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == fs_smps[i]->slot.state); + } else { break; } } + if (_sg.next_draw_valid) { const int* vb_offsets = bindings->vertex_buffer_offsets; int ib_offset = bindings->index_buffer_offset; - _sg_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs); + _sg_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs, vs_smps, num_vs_smps, fs_smps, num_fs_smps); _SG_TRACE_ARGS(apply_bindings, bindings); } - else { - _SG_TRACE_NOARGS(err_draw_invalid); - } } SOKOL_API_IMPL void sg_apply_uniforms(sg_shader_stage stage, int ub_index, const sg_range* data) { @@ -16469,15 +16954,12 @@ SOKOL_API_IMPL void sg_apply_uniforms(sg_shader_stage stage, int ub_index, const SOKOL_ASSERT(data && data->ptr && (data->size > 0)); if (!_sg_validate_apply_uniforms(stage, ub_index, data)) { _sg.next_draw_valid = false; - _SG_TRACE_NOARGS(err_draw_invalid); return; } if (!_sg.pass_valid) { - _SG_TRACE_NOARGS(err_pass_invalid); return; } if (!_sg.next_draw_valid) { - _SG_TRACE_NOARGS(err_draw_invalid); return; } _sg_apply_uniforms(stage, ub_index, data); @@ -16491,26 +16973,22 @@ SOKOL_API_IMPL void sg_draw(int base_element, int num_elements, int num_instance SOKOL_ASSERT(num_instances >= 0); #if defined(SOKOL_DEBUG) if (!_sg.bindings_valid) { - SG_LOG("attempting to draw without resource bindings"); + _SG_WARN(DRAW_WITHOUT_BINDINGS); } #endif if (!_sg.pass_valid) { - _SG_TRACE_NOARGS(err_pass_invalid); return; } if (!_sg.next_draw_valid) { - _SG_TRACE_NOARGS(err_draw_invalid); return; } if (!_sg.bindings_valid) { - _SG_TRACE_NOARGS(err_bindings_invalid); return; } /* attempting to draw with zero elements or instances is not technically an error, but might be handled as an error in the backend API (e.g. on Metal) */ if ((0 == num_elements) || (0 == num_instances)) { - _SG_TRACE_NOARGS(err_draw_invalid); return; } _sg_draw(base_element, num_elements, num_instances); @@ -16520,7 +16998,6 @@ SOKOL_API_IMPL void sg_draw(int base_element, int num_elements, int num_instance SOKOL_API_IMPL void sg_end_pass(void) { SOKOL_ASSERT(_sg.valid); if (!_sg.pass_valid) { - _SG_TRACE_NOARGS(err_pass_invalid); return; } _sg_end_pass(); @@ -16551,9 +17028,9 @@ SOKOL_API_IMPL void sg_update_buffer(sg_buffer buf_id, const sg_range* data) { if ((data->size > 0) && buf && (buf->slot.state == SG_RESOURCESTATE_VALID)) { if (_sg_validate_update_buffer(buf, data)) { SOKOL_ASSERT(data->size <= (size_t)buf->cmn.size); - /* only one update allowed per buffer and frame */ + // only one update allowed per buffer and frame SOKOL_ASSERT(buf->cmn.update_frame_index != _sg.frame_index); - /* update and append on same buffer in same frame not allowed */ + // update and append on same buffer in same frame not allowed SOKOL_ASSERT(buf->cmn.append_frame_index != _sg.frame_index); _sg_update_buffer(buf, data); buf->cmn.update_frame_index = _sg.frame_index; @@ -16568,7 +17045,7 @@ SOKOL_API_IMPL int sg_append_buffer(sg_buffer buf_id, const sg_range* data) { _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); int result; if (buf) { - /* rewind append cursor in a new frame */ + // rewind append cursor in a new frame if (buf->cmn.append_frame_index != _sg.frame_index) { buf->cmn.append_pos = 0; buf->cmn.append_overflow = false; @@ -16580,7 +17057,7 @@ SOKOL_API_IMPL int sg_append_buffer(sg_buffer buf_id, const sg_range* data) { if (buf->slot.state == SG_RESOURCESTATE_VALID) { if (_sg_validate_append_buffer(buf, data)) { if (!buf->cmn.append_overflow && (data->size > 0)) { - /* update and append on same buffer in same frame not allowed */ + // update and append on same buffer in same frame not allowed SOKOL_ASSERT(buf->cmn.update_frame_index != _sg.frame_index); int copied_num_bytes = _sg_append_buffer(buf, data, buf->cmn.append_frame_index != _sg.frame_index); buf->cmn.append_pos += copied_num_bytes; @@ -16589,9 +17066,8 @@ SOKOL_API_IMPL int sg_append_buffer(sg_buffer buf_id, const sg_range* data) { } } result = start_pos; - } - else { - /* FIXME: should we return -1 here? */ + } else { + // FIXME: should we return -1 here? result = 0; } _SG_TRACE_ARGS(append_buffer, buf_id, data, result); @@ -16611,7 +17087,7 @@ SOKOL_API_IMPL bool sg_query_buffer_will_overflow(sg_buffer buf_id, size_t size) bool result = false; if (buf) { int append_pos = buf->cmn.append_pos; - /* rewind append cursor in a new frame */ + // rewind append cursor in a new frame if (buf->cmn.append_frame_index != _sg.frame_index) { append_pos = 0; } @@ -16638,12 +17114,13 @@ SOKOL_API_IMPL void sg_update_image(sg_image img_id, const sg_image_data* data) SOKOL_API_IMPL void sg_push_debug_group(const char* name) { SOKOL_ASSERT(_sg.valid); SOKOL_ASSERT(name); - _SOKOL_UNUSED(name); + _sg_push_debug_group(name); _SG_TRACE_ARGS(push_debug_group, name); } SOKOL_API_IMPL void sg_pop_debug_group(void) { SOKOL_ASSERT(_sg.valid); + _sg_pop_debug_group(); _SG_TRACE_NOARGS(pop_debug_group); } @@ -16698,8 +17175,19 @@ SOKOL_API_IMPL sg_image_info sg_query_image_info(sg_image img_id) { 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; +} + +SOKOL_API_IMPL sg_sampler_info sg_query_sampler_info(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + sg_sampler_info info; + _sg_clear(&info, sizeof(info)); + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + info.slot.state = smp->slot.state; + info.slot.res_id = smp->slot.id; + info.slot.ctx_id = smp->slot.ctx_id; } return info; } @@ -16743,6 +17231,143 @@ SOKOL_API_IMPL sg_pass_info sg_query_pass_info(sg_pass pass_id) { return info; } +SOKOL_API_IMPL sg_buffer_desc sg_query_buffer_desc(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + sg_buffer_desc desc; + _sg_clear(&desc, sizeof(desc)); + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + desc.size = (size_t)buf->cmn.size; + desc.type = buf->cmn.type; + desc.usage = buf->cmn.usage; + } + return desc; +} + +SOKOL_API_IMPL sg_image_desc sg_query_image_desc(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + sg_image_desc desc; + _sg_clear(&desc, sizeof(desc)); + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + desc.type = img->cmn.type; + desc.render_target = img->cmn.render_target; + desc.width = img->cmn.width; + desc.height = img->cmn.height; + desc.num_slices = img->cmn.num_slices; + desc.num_mipmaps = img->cmn.num_mipmaps; + desc.usage = img->cmn.usage; + desc.pixel_format = img->cmn.pixel_format; + desc.sample_count = img->cmn.sample_count; + } + return desc; +} + +SOKOL_API_IMPL sg_sampler_desc sg_query_sampler_desc(sg_sampler smp_id) { + SOKOL_ASSERT(_sg.valid); + sg_sampler_desc desc; + _sg_clear(&desc, sizeof(desc)); + const _sg_sampler_t* smp = _sg_lookup_sampler(&_sg.pools, smp_id.id); + if (smp) { + desc.min_filter = smp->cmn.min_filter; + desc.mag_filter = smp->cmn.mag_filter; + desc.mipmap_filter = smp->cmn.mipmap_filter; + desc.wrap_u = smp->cmn.wrap_u; + desc.wrap_v = smp->cmn.wrap_v; + desc.wrap_w = smp->cmn.wrap_w; + desc.min_lod = smp->cmn.min_lod; + desc.max_lod = smp->cmn.max_lod; + desc.border_color = smp->cmn.border_color; + desc.compare = smp->cmn.compare; + desc.max_anisotropy = smp->cmn.max_anisotropy; + } + return desc; +} + +SOKOL_API_IMPL sg_shader_desc sg_query_shader_desc(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + sg_shader_desc desc; + _sg_clear(&desc, sizeof(desc)); + const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + for (int stage_idx = 0; stage_idx < SG_NUM_SHADER_STAGES; stage_idx++) { + sg_shader_stage_desc* stage_desc = (stage_idx == 0) ? &desc.vs : &desc.fs; + const _sg_shader_stage_t* stage = &shd->cmn.stage[stage_idx]; + for (int ub_idx = 0; ub_idx < stage->num_uniform_blocks; ub_idx++) { + sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_idx]; + const _sg_shader_uniform_block_t* ub = &stage->uniform_blocks[ub_idx]; + ub_desc->size = ub->size; + } + for (int img_idx = 0; img_idx < stage->num_images; img_idx++) { + sg_shader_image_desc* img_desc = &stage_desc->images[img_idx]; + const _sg_shader_image_t* img = &stage->images[img_idx]; + img_desc->used = true; + img_desc->image_type = img->image_type; + img_desc->sample_type = img->sample_type; + img_desc->multisampled = img->multisampled; + } + for (int smp_idx = 0; smp_idx < stage->num_samplers; smp_idx++) { + sg_shader_sampler_desc* smp_desc = &stage_desc->samplers[smp_idx]; + const _sg_shader_sampler_t* smp = &stage->samplers[smp_idx]; + smp_desc->used = true; + smp_desc->sampler_type = smp->sampler_type; + } + for (int img_smp_idx = 0; img_smp_idx < stage->num_image_samplers; img_smp_idx++) { + sg_shader_image_sampler_pair_desc* img_smp_desc = &stage_desc->image_sampler_pairs[img_smp_idx]; + const _sg_shader_image_sampler_t* img_smp = &stage->image_samplers[img_smp_idx]; + img_smp_desc->used = true; + img_smp_desc->image_slot = img_smp->image_slot; + img_smp_desc->sampler_slot = img_smp->sampler_slot; + img_smp_desc->glsl_name = 0; + } + } + } + return desc; +} + +SOKOL_API_IMPL sg_pipeline_desc sg_query_pipeline_desc(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + sg_pipeline_desc desc; + _sg_clear(&desc, sizeof(desc)); + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + desc.shader = pip->cmn.shader_id; + desc.layout = pip->cmn.layout; + desc.depth = pip->cmn.depth; + desc.stencil = pip->cmn.stencil; + desc.color_count = pip->cmn.color_count; + for (int i = 0; i < pip->cmn.color_count; i++) { + desc.colors[i] = pip->cmn.colors[i]; + } + desc.primitive_type = pip->cmn.primitive_type; + desc.index_type = pip->cmn.index_type; + desc.cull_mode = pip->cmn.cull_mode; + desc.face_winding = pip->cmn.face_winding; + desc.sample_count = pip->cmn.sample_count; + desc.blend_color = pip->cmn.blend_color; + desc.alpha_to_coverage_enabled = pip->cmn.alpha_to_coverage_enabled; + } + return desc; +} + +SOKOL_API_IMPL sg_pass_desc sg_query_pass_desc(sg_pass pass_id) { + SOKOL_ASSERT(_sg.valid); + sg_pass_desc desc; + _sg_clear(&desc, sizeof(desc)); + const _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); + if (pass) { + for (int i = 0; i < pass->cmn.num_color_atts; i++) { + desc.color_attachments[i].image = pass->cmn.color_atts[i].image_id; + desc.color_attachments[i].mip_level = pass->cmn.color_atts[i].mip_level; + desc.color_attachments[i].slice = pass->cmn.color_atts[i].slice; + } + desc.depth_stencil_attachment.image = pass->cmn.ds_att.image_id; + desc.depth_stencil_attachment.mip_level = pass->cmn.ds_att.mip_level; + desc.depth_stencil_attachment.slice = pass->cmn.ds_att.slice; + } + return desc; +} + SOKOL_API_IMPL sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc* desc) { SOKOL_ASSERT(_sg.valid && desc); return _sg_buffer_desc_defaults(desc); @@ -16753,6 +17378,11 @@ SOKOL_API_IMPL sg_image_desc sg_query_image_defaults(const sg_image_desc* desc) return _sg_image_desc_defaults(desc); } +SOKOL_API_IMPL sg_sampler_desc sg_query_sampler_defaults(const sg_sampler_desc* desc) { + SOKOL_ASSERT(_sg.valid && desc); + return _sg_sampler_desc_defaults(desc); +} + SOKOL_API_IMPL sg_shader_desc sg_query_shader_defaults(const sg_shader_desc* desc) { SOKOL_ASSERT(_sg.valid && desc); return _sg_shader_desc_defaults(desc); @@ -16780,8 +17410,7 @@ SOKOL_API_IMPL const void* sg_mtl_device(void) { #if defined(SOKOL_METAL) if (nil != _sg.mtl.device) { return (__bridge const void*) _sg.mtl.device; - } - else { + } else { return 0; } #else @@ -16793,8 +17422,7 @@ 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 { + } else { return 0; } #else @@ -16806,4 +17434,4 @@ SOKOL_API_IMPL const void* sg_mtl_render_command_encoder(void) { #pragma warning(pop) #endif -#endif /* SOKOL_GFX_IMPL */ +#endif // SOKOL_GFX_IMPL diff --git a/thirdparty/sokol/util/sokol_fontstash.h b/thirdparty/sokol/util/sokol_fontstash.h index 049d5e1154bd3d..fe2944ba8d069f 100644 --- a/thirdparty/sokol/util/sokol_fontstash.h +++ b/thirdparty/sokol/util/sokol_fontstash.h @@ -20,7 +20,6 @@ used by sokol_gfx.h and sokol_app.h): SOKOL_GLCORE33 - SOKOL_GLES2 SOKOL_GLES3 SOKOL_D3D11 SOKOL_METAL @@ -148,8 +147,8 @@ FONScontext* fons_context = sfons_create(&(sfons_desc_t){ ... .allocator = { - .alloc = my_alloc, - .free = my_free, + .alloc_fn = my_alloc, + .free_fn = my_free, .user_data = ..., } }); @@ -214,15 +213,15 @@ extern "C" { Used in sfons_desc_t to provide custom memory-alloc and -free functions to sokol_fontstash.h. If memory management should be overridden, both the - alloc and free function must be provided (e.g. it's not valid to + alloc_fn and free_fn function must be provided (e.g. it's not valid to override one function but not the other). NOTE that this does not affect memory allocation calls inside fontstash.h */ typedef struct sfons_allocator_t { - void* (*alloc)(size_t size, void* user_data); - void (*free)(void* ptr, void* user_data); + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); void* user_data; } sfons_allocator_t; @@ -283,7 +282,7 @@ SOKOL_FONTSTASH_API_DECL uint32_t sfons_rgba(uint8_t r, uint8_t g, uint8_t b, ui /* Embedded source code compiled with: - sokol-shdc -i sfons.glsl -o sfons.h -l glsl330:glsl100:hlsl4:metal_macos:metal_ios:metal_sim:wgpu -b + sokol-shdc -i sfons.glsl -o sfons.h -l glsl330:glsl300es:hlsl4:metal_macos:metal_ios:metal_sim:wgsl -b (not that for Metal and D3D11 byte code, sokol-shdc must be run on macOS and Windows) @@ -308,12 +307,13 @@ SOKOL_FONTSTASH_API_DECL uint32_t sfons_rgba(uint8_t r, uint8_t g, uint8_t b, ui @end @fs fs - uniform sampler2D tex; + uniform texture2D tex; + uniform sampler smp; 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; + frag_color = vec4(1.0, 1.0, 1.0, texture(sampler2D(tex, smp), uv.xy).r) * color; } @end @@ -351,431 +351,426 @@ static const char _sfons_vs_source_glsl330[478] = { 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] = { +static const char _sfons_fs_source_glsl330[203] = { 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[430] = { - 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,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x20,0x66,0x6c,0x6f,0x61, - 0x74,0x20,0x70,0x73,0x69,0x7a,0x65,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, + 0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,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,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,0x67,0x6c,0x5f, - 0x50,0x6f,0x69,0x6e,0x74,0x53,0x69,0x7a,0x65,0x20,0x3d,0x20,0x70,0x73,0x69,0x7a, - 0x65,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, + 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,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, + 0x30,0x2c,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x74,0x65,0x78,0x5f,0x73, + 0x6d,0x70,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_GLES3) +static const char _sfons_vs_source_glsl300es[481] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,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,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69, + 0x6f,0x6e,0x20,0x3d,0x20,0x33,0x29,0x20,0x69,0x6e,0x20,0x66,0x6c,0x6f,0x61,0x74, + 0x20,0x70,0x73,0x69,0x7a,0x65,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, + 0x67,0x6c,0x5f,0x50,0x6f,0x69,0x6e,0x74,0x53,0x69,0x7a,0x65,0x20,0x3d,0x20,0x70, + 0x73,0x69,0x7a,0x65,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_glsl300es[276] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,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,0x5f,0x73,0x6d,0x70,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,0x68,0x69,0x67,0x68,0x70,0x20, + 0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b, + 0x0a,0x69,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x34,0x20,0x75, + 0x76,0x3b,0x0a,0x69,0x6e,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,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,0x5f,0x73,0x6d,0x70,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[3417] = { - 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x59,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, - 0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x01,0x00,0x00,0x00,0x00,0x00,0x00, - 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x19,0x01,0x00,0x00,0x00,0x00,0x00,0x00, - 0x40,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, +static const uint8_t _sfons_vs_bytecode_metal_macos[3317] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xf5,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x15,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0xe0,0x0b,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,0x0a,0x16,0x9b,0x1c,0x17,0xd6,0x38, - 0xd5,0x49,0x10,0x98,0x04,0x66,0x15,0xf9,0x7e,0x49,0x87,0x6c,0x57,0x88,0x31,0x1d, - 0xab,0xb6,0x09,0x92,0x56,0xe2,0x6f,0x4d,0x48,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x01,0x00,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0x76,0x25,0x5f,0x37,0x22,0xd0,0x3f, + 0x64,0xef,0xff,0xc3,0x45,0x1a,0x3d,0xb7,0x5e,0x83,0x13,0x96,0xd3,0x09,0xec,0x53, + 0x25,0xd5,0x7e,0x0c,0xed,0xb9,0x58,0x34,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,0x01,0x00,0x45,0x4e,0x44,0x54,0x45,0x4e,0x44,0x54,0x40,0x00,0x00, - 0x00,0x56,0x41,0x54,0x54,0x2a,0x00,0x04,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,0x70,0x73,0x69,0x7a,0x65,0x00,0x03, - 0x80,0x56,0x41,0x54,0x59,0x06,0x00,0x04,0x00,0x06,0x04,0x06,0x03,0x45,0x4e,0x44, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x40,0x00,0x00,0x00,0x56,0x41,0x54, + 0x54,0x2a,0x00,0x04,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,0x70,0x73,0x69,0x7a,0x65,0x00,0x03,0x80,0x56,0x41,0x54, + 0x59,0x06,0x00,0x04,0x00,0x06,0x04,0x06,0x03,0x45,0x4e,0x44,0x54,0x04,0x00,0x00, + 0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00, + 0x00,0xc4,0x0b,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00, + 0x00,0xee,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,0x42, + 0x01,0x2c,0x40,0x05,0x00,0x49,0x18,0x00,0x00,0x01,0x00,0x00,0x00,0x13,0x84,0x40, + 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,0x44,0x33,0x00,0xc3,0x08,0x04,0x60,0x89,0x10,0x02, + 0x18,0x46,0x10,0x80,0x24,0x08,0x33,0x51,0xf3,0x40,0x0f,0xf2,0x50,0x0f,0xe3,0x40, + 0x0f,0x6e,0xd0,0x0e,0xe5,0x40,0x0f,0xe1,0xc0,0x0e,0x7a,0xa0,0x07,0xed,0x10,0x0e, + 0xf4,0x20,0x0f,0xe9,0x80,0x0f,0x28,0x20,0x07,0x49,0x53,0x44,0x09,0x93,0x5f,0x49, + 0xff,0x03,0x44,0x00,0x23,0x21,0xa1,0x94,0x41,0x04,0x43,0x28,0x86,0x08,0x23,0x80, + 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,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,0x08,0x65,0x50,0x80,0x02,0x05,0x51,0x20,0xd4,0x46,0x00,0x88,0x8d,0x25,0x34, + 0x01,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x02,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,0x05,0xe7,0x20,0x08,0x0e,0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e, + 0x8c,0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x06,0x06,0x26,0xc6, + 0xc5,0xc6,0xe6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x06, + 0x06,0x26,0xc6,0xc5,0xc6,0xe6,0xc6,0x45,0x26,0x65,0x88,0xa0,0x10,0x43,0x8c,0x25, + 0x58,0x90,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,0x51,0x58,0x9a,0x9c, + 0x8b,0x5d,0x99,0x1c,0x5d,0x19,0xde,0xd7,0x5b,0x1d,0x1d,0x5c,0x1d,0x1d,0x97,0xba, + 0xb9,0x32,0x39,0x14,0xb6,0xb7,0x31,0x37,0x98,0x14,0x46,0x61,0x69,0x72,0x2e,0x61, + 0x72,0x67,0x5f,0x74,0x79,0x70,0x65,0x5f,0x6e,0x61,0x6d,0x65,0x34,0xcc,0xd8,0xde, + 0xc2,0xe8,0x68,0xc8,0x84,0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xb9,0x85,0xb5,0x95, + 0x51,0xa8,0xb3,0x1b,0xc2,0x28,0x8f,0x02,0x29,0x91,0x22,0x29,0x93,0x42,0x71,0xa9, + 0x9b,0x2b,0x93,0x43,0x61,0x7b,0x1b,0x73,0x8b,0x49,0x61,0x31,0xf6,0xc6,0xf6,0x26, + 0x37,0x84,0x51,0x1e,0xc5,0x52,0x22,0x45,0x52,0x26,0xe5,0x22,0x13,0x96,0x26,0xe7, + 0x02,0xf7,0x36,0x97,0x46,0x97,0xf6,0xe6,0xc6,0xe5,0x8c,0xed,0x0b,0xea,0x6d,0x2e, + 0x8d,0x2e,0xed,0xcd,0x6d,0x88,0xa2,0x64,0x4a,0xa4,0x48,0xca,0xa4,0x68,0x74,0xc2, + 0xd2,0xe4,0x5c,0xe0,0xde,0xd2,0xdc,0xe8,0xbe,0xe6,0xd2,0xf4,0xca,0x58,0x98,0xb1, + 0xbd,0x85,0xd1,0x91,0x39,0x63,0xfb,0x82,0x7a,0x4b,0x73,0xa3,0x9b,0x4a,0xd3,0x2b, + 0x1b,0xa2,0x28,0x9c,0x12,0x29,0x9d,0x32,0x29,0xde,0x10,0x44,0xa9,0x14,0x4c,0xd9, + 0x94,0x8f,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,0x7a,0x67,0x65,0x6e,0x65,0x72,0x61,0x74,0x65,0x64, + 0x28,0x5f,0x5f,0x61,0x69,0x72,0x5f,0x70,0x6c,0x61,0x63,0x65,0x68,0x6f,0x6c,0x64, + 0x65,0x72,0x5f,0x5f,0x29,0x44,0xe0,0xde,0xe6,0xd2,0xe8,0xd2,0xde,0xdc,0x86,0x50, + 0x8b,0xa0,0x84,0x81,0x22,0x06,0x8b,0xb0,0x04,0xca,0x18,0x28,0x91,0x22,0x29,0x93, + 0x42,0x06,0x34,0xcc,0xd8,0xde,0xc2,0xe8,0x64,0x98,0xd0,0x95,0xe1,0x8d,0xbd,0xbd, + 0xc9,0x91,0xc1,0x0c,0xa1,0x96,0x40,0x09,0x03,0x45,0x0c,0x96,0x60,0x09,0x94,0x31, + 0x50,0x22,0xc5,0x0c,0x94,0x49,0x39,0x03,0x1a,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x43, + 0xa8,0x65,0x50,0xc2,0x40,0x11,0x83,0x65,0x58,0x02,0x65,0x0c,0x94,0x48,0x91,0x94, + 0x49,0x49,0x03,0x16,0x70,0x73,0x69,0x7a,0x65,0x43,0xa8,0xc5,0x50,0xc2,0x40,0x11, + 0x83,0xc5,0x58,0x02,0x65,0x0c,0x94,0x48,0xe9,0x94,0x49,0x59,0x03,0x2a,0x61,0x69, + 0x72,0x2e,0x62,0x75,0x66,0x66,0x65,0x72,0x7c,0xc2,0xd2,0xe4,0x5c,0xc4,0xea,0xcc, + 0xcc,0xca,0xe4,0xbe,0xe6,0xd2,0xf4,0xca,0x88,0x84,0xa5,0xc9,0xb9,0xc8,0x95,0x85, + 0x91,0x91,0x0a,0x4b,0x93,0x73,0x99,0xa3,0x93,0xab,0x1b,0xa3,0xfb,0xa2,0xcb,0x83, + 0x2b,0xfb,0x4a,0x73,0x33,0x7b,0x23,0x62,0xc6,0xf6,0x16,0x46,0x47,0x83,0x47,0xc3, + 0xa1,0xcd,0x0e,0x8e,0x02,0x5d,0xdb,0x10,0x6a,0x11,0x16,0x62,0x11,0x94,0x38,0x50, + 0xe4,0x60,0x21,0x16,0x62,0x11,0x94,0x38,0x50,0xe6,0x80,0x51,0x58,0x9a,0x9c,0x4b, + 0x98,0xdc,0xd9,0x17,0x5d,0x1e,0x5c,0xd9,0xd7,0x5c,0x9a,0x5e,0x19,0xaf,0xb0,0x34, + 0x39,0x97,0x30,0xb9,0xb3,0x2f,0xba,0x3c,0xb8,0xb2,0xaf,0x30,0xb6,0xb4,0x33,0xb7, + 0xaf,0xb9,0x34,0xbd,0x32,0x26,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x1c, + 0xbe,0x62,0x72,0x86,0x90,0xc1,0x52,0x28,0x6d,0xa0,0xb8,0xc1,0x72,0x28,0x62,0xb0, + 0x08,0x4b,0xa0,0xbc,0x81,0x02,0x07,0x0a,0x1d,0x28,0x75,0xb0,0x1c,0x8a,0x1d,0x2c, + 0x89,0x12,0x29,0x77,0xa0,0x4c,0x0a,0x1e,0x0c,0x51,0x94,0x32,0x50,0xd0,0x40,0x51, + 0x03,0x85,0x0d,0x94,0x3c,0x18,0x62,0x24,0x80,0x02,0x06,0x8a,0x1e,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, + 0xfa,0x60,0x88,0xa1,0xf0,0x81,0xe2,0x07,0x8d,0x32,0xc4,0x50,0xfe,0x40,0xf9,0x83, + 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,0x65,0x50,0x18,0x67,0x84,0x12, + 0x0e,0xe9,0x20,0x0f,0x6e,0x60,0x0f,0xe5,0x20,0x0f,0xf4,0x50,0x0e,0xf8,0x30,0x25, + 0xd8,0x03,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x7b,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,0x98,0x81,0x5c, + 0xe3,0x10,0x0e,0xec,0xc0,0x0e,0xe5,0x50,0x0e,0xf3,0x30,0x23,0xc1,0xd2,0x41,0x1e, + 0xe4,0xe1,0x17,0xd8,0xe1,0x1d,0xde,0x01,0x1e,0x66,0x50,0x59,0x38,0xa4,0x83,0x3c, + 0xb8,0x81,0x39,0xd4,0x83,0x3b,0x8c,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x2f,0x9c, + 0x83,0x3c,0xbc,0x43,0x3d,0xc0,0xc3,0x3c,0x00,0x71,0x20,0x00,0x00,0x02,0x00,0x00, + 0x00,0x06,0x50,0x30,0x00,0xd2,0xd0,0x00,0x00,0x61,0x20,0x00,0x00,0x3e,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,0x4b,0x94,0x65,0x11,0x05,0x65,0x90,0x21,0x1a,0x0c,0x13,0x02,0xf9, + 0x8c,0x57,0x3c,0x55,0xd7,0x2d,0x14,0x94,0x41,0x86,0xea,0x70,0x4c,0x08,0xe4,0x63, + 0x41,0x01,0x9f,0xf1,0x0a,0x4a,0x13,0x03,0x31,0x70,0x28,0x28,0x83,0x0c,0x1a,0x43, + 0x99,0x10,0xc8,0xc7,0x8a,0x00,0x3e,0xe3,0x15,0xd9,0x77,0x06,0x67,0x40,0x51,0x50, + 0x06,0x19,0xbe,0x48,0x33,0x21,0x90,0x8f,0x15,0x01,0x7c,0xc6,0x2b,0x3c,0x32,0x68, + 0x03,0x36,0x20,0x03,0x0a,0xca,0x20,0xc3,0x18,0x60,0x99,0x09,0x81,0x7c,0xc6,0x2b, + 0xc4,0x00,0x0d,0xe2,0x00,0x0e,0x3c,0x0a,0xca,0x20,0xc3,0x19,0x70,0x61,0x60,0x42, + 0x20,0x1f,0x0b,0x0a,0xf8,0x8c,0x57,0x9c,0x41,0x1b,0xd8,0x41,0x1d,0x88,0x01,0x05, + 0xc5,0x86,0x00,0x3e,0xb3,0x0d,0x61,0x10,0x00,0xb3,0x0d,0x41,0x1b,0x04,0xb3,0x0d, + 0xc1,0x23,0xcc,0x36,0x04,0x6e,0x30,0x64,0x10,0x10,0x03,0x00,0x00,0x09,0x00,0x00, + 0x00,0x5b,0x86,0x20,0x00,0x85,0x2d,0x43,0x11,0x80,0xc2,0x96,0x41,0x09,0x40,0x61, + 0xcb,0xf0,0x04,0xa0,0xb0,0x65,0xa0,0x02,0x50,0xd8,0x32,0x60,0x01,0x28,0x6c,0x19, + 0xba,0x00,0x14,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[2857] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x29,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd1,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x50,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,0x38,0x3c,0x07,0x7d,0xe0,0x21,0x6b, + 0x04,0x03,0x36,0x12,0x1d,0xbc,0x24,0x12,0xc4,0x27,0xab,0xef,0x84,0x17,0xbe,0x12, + 0x8d,0xc7,0xbc,0x06,0xbc,0x98,0x53,0xdb,0x0b,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,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,0x24,0x0c,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, - 0xde,0x21,0x0c,0x00,0x00,0x06,0x03,0x00,0x00,0x0b,0x82,0x20,0x00,0x02,0x00,0x00, + 0x00,0x14,0x00,0x00,0x00,0x30,0x0a,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0x89,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,0xf6,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,0x08,0x7a,0x08,0x07,0x79,0x38,0x87,0x72,0xa0,0x87,0x36, - 0x30,0x87,0x72,0x08,0x07,0x7a,0xa8,0x07,0x79,0x28,0x87,0x79,0x00,0xda,0xc0,0x1c, - 0xe0,0x21,0x0e,0xec,0x00,0x20,0xea,0xc1,0x1d,0xe6,0x21,0x1c,0xcc,0xa1,0x1c,0xda, - 0xc0,0x1c,0xe0,0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90,0x87,0x7a, - 0x28,0x07,0x80,0xa8,0x87,0x79,0x28,0x87,0x36,0x98,0x87,0x77,0x30,0x07,0x7a,0x68, - 0x03,0x73,0x60,0x87,0x77,0x08,0x07,0x7a,0x00,0xcc,0x21,0x1c,0xd8,0x61,0x1e,0xca, - 0x01,0xd8,0x60,0x08,0x05,0xb0,0x00,0x15,0x00,0x49,0x18,0x00,0x00,0x01,0x00,0x00, - 0x00,0x13,0x84,0x40,0x00,0x89,0x20,0x00,0x00,0x24,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,0x40,0x33,0x00,0xc3,0x08,0x04, - 0x70,0x97,0x34,0x45,0x94,0x30,0xf9,0x0c,0x80,0x34,0xf4,0xef,0x50,0x93,0xff,0x00, - 0x82,0x42,0x0c,0x98,0x08,0x21,0x80,0x61,0x04,0x01,0x48,0x82,0x30,0x13,0x35,0x0f, - 0xf4,0x20,0x0f,0xf5,0x30,0x0e,0xf4,0xe0,0x06,0xed,0x50,0x0e,0xf4,0x10,0x0e,0xec, - 0xa0,0x07,0x7a,0xd0,0x0e,0xe1,0x40,0x0f,0xf2,0x90,0x0e,0xf8,0x80,0x02,0x72,0x90, - 0x34,0x45,0x94,0x30,0xf9,0x95,0xf4,0x3f,0x40,0x04,0x30,0x12,0x12,0x4a,0x19,0x44, - 0x30,0x84,0x62,0x88,0x30,0x02,0x38,0x84,0x06,0x02,0xe6,0x08,0xc0,0x60,0x8e,0x00, - 0x14,0x06,0x11,0x02,0x61,0x18,0x81,0x58,0x46,0x00,0x00,0x00,0x00,0x13,0xb2,0x70, + 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,0x1e,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,0x60,0x83,0x70,0x94,0x34,0x45,0x94,0x30, + 0xf9,0xff,0x44,0x5c,0x13,0x15,0x11,0xbf,0x3d,0xfc,0xd3,0x18,0x01,0x30,0x88,0x30, + 0x04,0x17,0x49,0x53,0x44,0x09,0x93,0xff,0x4b,0x00,0xf3,0x2c,0x44,0xf4,0x4f,0x63, + 0x04,0xc0,0x20,0x42,0x21,0x94,0x42,0x84,0x40,0x0c,0x9d,0x61,0x04,0x01,0x98,0x23, + 0x08,0xe6,0x08,0xc0,0x60,0x18,0x41,0x58,0x0a,0x12,0x88,0x49,0x8a,0x29,0x40,0x6d, + 0x20,0x20,0x05,0xd6,0x30,0x02,0xb1,0x8c,0x00,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,0xf0,0x1e,0xe5,0xd0,0x06,0xf0,0xa0,0x07,0x76,0x40,0x07, + 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,0x0e,0x78,0x00,0x07,0x7a, - 0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,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,0x60, - 0x0e,0x78,0x00,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,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,0x09,0x00,0x00, - 0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43, - 0x5a,0x25,0x30,0x02,0x50,0x80,0x01,0x85,0x50,0x04,0x65,0x50,0x80,0x02,0x05,0x51, - 0x20,0xc4,0x46,0x00,0x00,0x79,0x18,0x00,0x00,0x15,0x01,0x00,0x00,0x1a,0x03,0x4c, - 0x10,0x95,0xbb,0x31,0xb4,0x30,0xb9,0xaf,0xb9,0x34,0xbd,0xb2,0x21,0xc6,0x12,0x28, - 0xc0,0x42,0x70,0x0d,0x82,0xe0,0xe0,0xd8,0xca,0x40,0x98,0x98,0xac,0x9a,0x40,0xec, - 0xca,0xe4,0xe6,0xd2,0xde,0xdc,0x40,0x72,0x60,0x64,0x5c,0x62,0x40,0x50,0xda,0xca, - 0xe8,0xc2,0xd8,0xcc,0xca,0x5a,0x72,0x60,0x64,0x5c,0x62,0x5c,0x68,0x72,0x52,0x86, - 0x08,0x8a,0x30,0xc4,0x58,0x82,0x05,0x59,0x04,0x16,0x4d,0x65,0x74,0x61,0x6c,0x43, - 0x10,0xa5,0x58,0x82,0x25,0x58,0x04,0x6e,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70, - 0x69,0x6c,0x65,0x2e,0x64,0x65,0x6e,0x6f,0x72,0x6d,0x73,0x5f,0x64,0x69,0x73,0x61, - 0x62,0x6c,0x65,0x43,0x04,0xe5,0x20,0x17,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97, - 0xc6,0x56,0xe6,0x62,0x16,0x36,0x47,0xf7,0xd5,0x16,0x46,0x87,0xf6,0x55,0xe6,0x16, - 0x26,0xc6,0x56,0x36,0x44,0x50,0x12,0x96,0x41,0x58,0x9a,0x9c,0xcb,0xd8,0x5b,0x1b, - 0x5c,0x1a,0x5b,0x99,0x8b,0x99,0x5c,0x58,0x5b,0x99,0x58,0x9d,0x99,0x59,0x99,0xdc, - 0x97,0x59,0x19,0xdd,0x18,0xda,0x17,0x59,0xda,0x5c,0x98,0x18,0x5b,0xd9,0x10,0x41, - 0x59,0x18,0x06,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x6e, - 0x61,0x74,0x69,0x76,0x65,0x5f,0x64,0x6f,0x75,0x62,0x6c,0x65,0x5f,0x64,0x69,0x73, - 0x61,0x62,0x6c,0x65,0x43,0x04,0xa5,0x61,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,0xca,0xa3,0x40,0x4a,0xa4,0x48,0xca,0xa4,0x50,0x5c,0xea,0xe6,0xca,0xe4,0x50, - 0xd8,0xde,0xc6,0xdc,0x62,0x52,0x58,0x8c,0xbd,0xb1,0xbd,0xc9,0x0d,0x61,0x94,0x47, - 0xb1,0x94,0x48,0x91,0x94,0x49,0xb9,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,0x99,0x12,0x29,0x92,0x32,0x29,0x1a,0x9d,0xb0,0x34,0x39,0x17,0xb8, - 0xb7,0x34,0x37,0xba,0xaf,0xb9,0x34,0xbd,0x32,0x16,0x66,0x6c,0x6f,0x61,0x74,0x64, - 0xce,0xd8,0xbe,0xa0,0xde,0xd2,0xdc,0xe8,0xa6,0xd2,0xf4,0xca,0x86,0x28,0x0a,0xa7, - 0x44,0x4a,0xa7,0x4c,0x8a,0x37,0x04,0x51,0x2a,0x05,0x53,0x36,0xe5,0x23,0x14,0x96, - 0x26,0xe7,0x62,0x57,0x26,0x47,0x57,0x86,0xf7,0x95,0xe6,0x06,0x57,0x47,0x47,0x29, - 0x2c,0x4d,0xce,0x85,0xed,0x6d,0x2c,0x8c,0x2e,0xed,0xcd,0xed,0x2b,0xcd,0x8d,0xac, - 0x0c,0x8f,0xd9,0x59,0x99,0x5b,0x99,0x5c,0x18,0x5d,0x19,0x19,0x0a,0x0e,0xdc,0xdb, - 0x5c,0x1a,0x5d,0xda,0x9b,0x1b,0x91,0x1d,0xcd,0x97,0x59,0x0a,0x11,0xb8,0xb7,0xb9, - 0x34,0xba,0xb4,0x37,0xb7,0x21,0xd4,0x22,0x28,0x61,0xa0,0x88,0xc1,0x22,0x2c,0x81, - 0x32,0x06,0x4a,0xa4,0x48,0xca,0xa4,0x90,0x01,0xb5,0xb3,0x32,0xb7,0x32,0xb9,0x30, - 0xba,0x32,0x32,0x94,0x1c,0xba,0x32,0xbc,0xb1,0xb7,0x37,0x39,0x32,0x18,0x22,0x3b, - 0x99,0x2f,0xb3,0x14,0x1a,0x66,0x6c,0x6f,0x61,0x74,0x32,0x4c,0xe8,0xca,0xf0,0xc6, - 0xde,0xde,0xe4,0xc8,0x60,0x86,0x50,0x4b,0xa0,0x84,0x81,0x22,0x06,0x4b,0xb0,0x04, - 0x8a,0x19,0x28,0x91,0x72,0x06,0xca,0xa4,0xa0,0x01,0xaf,0xb3,0x32,0xb7,0x32,0xb9, - 0x30,0xba,0x32,0x32,0x14,0x9b,0xb1,0x37,0xb6,0x37,0x39,0x18,0x22,0x3b,0x9a,0x2f, - 0xb3,0x14,0x1a,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x43,0xa8,0xa5,0x50,0xc2,0x40,0x11, - 0x83,0xa5,0x58,0x02,0x45,0x0d,0x94,0x48,0x91,0x94,0x49,0x59,0x03,0x4a,0x67,0x65, - 0x6e,0x65,0x72,0x61,0x74,0x65,0x64,0x28,0x35,0x70,0x73,0x69,0x7a,0x65,0x66,0x29, - 0x2c,0xe0,0xe6,0xd2,0xf4,0xca,0x86,0x50,0x8b,0xa1,0x84,0x81,0x22,0x06,0x8b,0xb1, - 0x04,0x4a,0x1b,0x28,0x91,0xd2,0x29,0x93,0xe2,0x06,0x54,0xc2,0xd2,0xe4,0x5c,0xc4, - 0xea,0xcc,0xcc,0xca,0xe4,0xf8,0x84,0xa5,0xc9,0xb9,0x88,0xd5,0x99,0x99,0x95,0xc9, - 0x7d,0xcd,0xa5,0xe9,0x95,0x11,0x09,0x4b,0x93,0x73,0x91,0x2b,0x0b,0x23,0x23,0x15, - 0x96,0x26,0xe7,0x32,0x47,0x27,0x57,0x37,0x46,0xf7,0x45,0x97,0x07,0x57,0xf6,0x95, - 0xe6,0x66,0xf6,0x46,0xc4,0x8c,0xed,0x2d,0x8c,0x8e,0x06,0x8f,0x86,0x43,0x9b,0x1d, - 0x1c,0x05,0xba,0xb6,0x21,0xd4,0x22,0x2c,0xc3,0x22,0x28,0x74,0xa0,0xd4,0xc1,0x32, - 0x2c,0xc3,0x22,0x28,0x74,0xa0,0xd8,0x01,0xa3,0xb0,0x34,0x39,0x97,0x30,0xb9,0xb3, - 0x2f,0xba,0x3c,0xb8,0xb2,0xaf,0xb9,0x34,0xbd,0x32,0x5e,0x61,0x69,0x72,0x2e,0x61, - 0x72,0x67,0x5f,0x74,0x79,0x70,0x65,0x5f,0x61,0x6c,0x69,0x67,0x6e,0x5f,0x73,0x69, - 0x7a,0x65,0x4c,0xec,0xe6,0xbe,0xe0,0xc2,0xe4,0xc2,0xda,0xe6,0x38,0x7c,0xc9,0xc4, - 0x0c,0x21,0x83,0x85,0x50,0xe0,0x40,0x89,0x83,0xe5,0x50,0xc4,0x60,0x11,0x96,0x40, - 0x91,0x03,0x65,0x0e,0x94,0x3b,0x50,0xf0,0x60,0x39,0x94,0x3c,0x58,0x12,0x25,0x52, - 0xf4,0x40,0x99,0x94,0x3d,0x18,0xa2,0x28,0x65,0xa0,0xa4,0x81,0xc2,0x06,0xca,0x1b, - 0x28,0x7c,0x30,0xc4,0x48,0x00,0x05,0x0c,0x94,0x3e,0xe0,0xf3,0xd6,0xe6,0x96,0x06, - 0xf7,0x46,0x57,0xe6,0x46,0x07,0x32,0x86,0x16,0x26,0xc7,0x67,0x2a,0xad,0x0d,0x8e, - 0xad,0x0c,0x64,0x68,0x65,0x05,0x84,0x4a,0x28,0x28,0x68,0x88,0xa0,0x80,0xc2,0x10, - 0x43,0xf9,0x03,0x25,0x14,0x18,0x65,0x88,0xa1,0x88,0x82,0x22,0x0a,0x8c,0x32,0x22, - 0x62,0x07,0x76,0xb0,0x87,0x76,0x70,0x83,0x76,0x78,0x07,0x72,0xa8,0x07,0x76,0x28, - 0x07,0x37,0x30,0x07,0x76,0x08,0x87,0x73,0x98,0x87,0x29,0x41,0x30,0x42,0x61,0x07, - 0x76,0xb0,0x87,0x76,0x70,0x83,0x74,0x20,0x87,0x72,0x70,0x07,0x7a,0x98,0x12,0x0c, - 0x23,0x96,0x70,0x48,0x07,0x79,0x70,0x03,0x7b,0x28,0x07,0x79,0x98,0x87,0x74,0x78, - 0x07,0x77,0x98,0x12,0x10,0x23,0xa8,0x70,0x48,0x07,0x79,0x70,0x03,0x76,0x08,0x07, - 0x77,0x38,0x87,0x7a,0x08,0x87,0x73,0x28,0x87,0x5f,0xb0,0x87,0x72,0x90,0x87,0x79, - 0x48,0x87,0x77,0x70,0x87,0x29,0x81,0x31,0x62,0x0a,0x87,0x74,0x90,0x07,0x37,0x18, - 0x87,0x77,0x68,0x07,0x78,0x48,0x07,0x76,0x28,0x87,0x5f,0x78,0x07,0x78,0xa0,0x87, - 0x74,0x78,0x07,0x77,0x98,0x87,0x29,0x04,0xa2,0x30,0xce,0x08,0x25,0x1c,0xd2,0x41, - 0x1e,0xdc,0xc0,0x1e,0xca,0x41,0x1e,0xe8,0xa1,0x1c,0xf0,0x61,0x4a,0xe0,0x07,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,0x3e,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x09,0x00,0x00, - 0x00,0xe4,0xc6,0x22,0x86,0x61,0x18,0xc6,0x22,0x04,0x41,0x10,0xc6,0x22,0x82,0x20, - 0x08,0x88,0x95,0x40,0x19,0x14,0x01,0xb9,0x11,0x00,0x1a,0x33,0x00,0x24,0x66,0x00, - 0x28,0xcc,0x00,0x00,0x00,0xe3,0x15,0x0b,0x84,0x61,0x10,0x05,0x65,0x90,0x21,0x1a, - 0x0c,0x13,0x02,0xf9,0x8c,0x57,0x3c,0x14,0xc7,0x2d,0x14,0x94,0x41,0x86,0xea,0x70, - 0x4c,0x08,0xe4,0x63,0x41,0x01,0x9f,0xf1,0x0a,0x2a,0x0b,0x83,0x30,0x70,0x28,0x28, - 0x83,0x0c,0x1a,0x43,0x99,0x10,0xc8,0xc7,0x8a,0x00,0x3e,0xe3,0x15,0x99,0x67,0x06, - 0x66,0x40,0x51,0x50,0x06,0x19,0xbe,0x48,0x33,0x21,0x90,0x8f,0x15,0x01,0x7c,0xc6, - 0x2b,0xbc,0x31,0x60,0x83,0x35,0x18,0x03,0x0a,0xca,0x20,0xc3,0x18,0x60,0x99,0x09, - 0x81,0x7c,0xc6,0x2b,0xc4,0xe0,0x0c,0xe0,0xe0,0x0d,0x3c,0x0a,0xca,0x20,0xc3,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,0x41, - 0x1b,0x04,0xb3,0x0d,0xc1,0x23,0xcc,0x36,0x04,0x6e,0x30,0x64,0x10,0x10,0x03,0x00, - 0x00,0x09,0x00,0x00,0x00,0x5b,0x86,0x20,0x18,0x85,0x2d,0x43,0x11,0x8c,0xc2,0x96, - 0x41,0x09,0x46,0x61,0xcb,0xf0,0x04,0xa3,0xb0,0x65,0xa0,0x82,0x51,0xd8,0x32,0x60, - 0xc1,0x28,0x6c,0x19,0xba,0x60,0x14,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[2893] = { - 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x03,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,0xb6,0x8b,0xa9,0x7e,0x5a,0xee,0x89, - 0x6e,0x6c,0x22,0x2c,0x4b,0xad,0x03,0xda,0xe6,0xd7,0x2e,0x88,0x05,0x1f,0x44,0x92, - 0x88,0x08,0xa6,0x2c,0x57,0xb7,0x10,0x8a,0x6e,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,0x54,0x0a,0x00,0x00,0xff,0xff,0xff, - 0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0x92,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,0x92,0x00,0x00,0x00,0x1b,0xfa,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,0xa0,0x87,0x70,0x90,0x87, - 0x73,0x28,0x07,0x7a,0x68,0x03,0x73,0x28,0x87,0x70,0xa0,0x87,0x7a,0x90,0x87,0x72, - 0x98,0x07,0xa0,0x0d,0xcc,0x01,0x1e,0xe2,0xc0,0x0e,0x00,0xa2,0x1e,0xdc,0x61,0x1e, - 0xc2,0xc1,0x1c,0xca,0xa1,0x0d,0xcc,0x01,0x1e,0xda,0xa0,0x1d,0xc2,0x81,0x1e,0xd0, - 0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x88,0x7a,0x98,0x87,0x72,0x68,0x83,0x79, - 0x78,0x07,0x73,0xa0,0x87,0x36,0x30,0x07,0x76,0x78,0x87,0x70,0xa0,0x07,0xc0,0x1c, - 0xc2,0x81,0x1d,0xe6,0xa1,0x1c,0x80,0x0d,0x86,0x30,0x00,0x0b,0x50,0x6d,0x30,0x06, - 0x02,0x58,0x80,0x6a,0x03,0x42,0xfc,0xff,0xff,0xff,0xff,0x00,0x30,0x80,0x04,0x54, - 0x1b,0x8c,0x22,0x00,0x16,0xa0,0xda,0x60,0x18,0x02,0xb0,0x00,0x15,0x00,0x00,0x00, - 0x00,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,0x61,0x04,0x62,0x19,0x01,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,0xf0,0x1e,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,0x0e,0x78,0x00,0x07,0x7a, - 0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,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,0x60, - 0x0e,0x78,0x00,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,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,0x07,0x00,0x00,0x00,0x32,0x1e,0x98, - 0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a,0x25,0x30,0x02, - 0x50,0x08,0x05,0x51,0x04,0x65,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0xbb,0x00,0x00, - 0x00,0x1a,0x03,0x4c,0x10,0x95,0xbb,0x31,0xb4,0x30,0xb9,0xaf,0xb9,0x34,0xbd,0xb2, - 0x21,0xc6,0x22,0x3c,0xc0,0x42,0x70,0x0d,0x82,0xe0,0xe0,0xd8,0xca,0x40,0x98,0x98, - 0xac,0x9a,0x40,0xec,0xca,0xe4,0xe6,0xd2,0xde,0xdc,0x40,0x72,0x60,0x64,0x5c,0x62, - 0x40,0x50,0xda,0xca,0xe8,0xc2,0xd8,0xcc,0xca,0x5a,0x72,0x60,0x64,0x5c,0x62,0x5c, - 0x68,0x72,0x52,0x86,0x08,0x8f,0x30,0xc4,0x58,0x84,0xa5,0x58,0x06,0x16,0x4d,0x65, - 0x74,0x61,0x6c,0x43,0x90,0xa7,0x58,0x84,0x45,0x58,0x06,0x6e,0x61,0x69,0x72,0x2e, - 0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x64,0x65,0x6e,0x6f,0x72,0x6d,0x73,0x5f, - 0x64,0x69,0x73,0x61,0x62,0x6c,0x65,0x43,0x84,0xe7,0x20,0x17,0x96,0x26,0xe7,0x32, - 0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x62,0x16,0x36,0x47,0xf7,0xd5,0x16,0x46,0x87, - 0xf6,0x55,0xe6,0x16,0x26,0xc6,0x56,0x36,0x44,0x78,0x12,0x96,0x41,0x58,0x9a,0x9c, - 0xcb,0xd8,0x5b,0x1b,0x5c,0x1a,0x5b,0x99,0x8b,0x99,0x5c,0x58,0x5b,0x99,0x58,0x9d, - 0x99,0x59,0x99,0xdc,0x97,0x59,0x19,0xdd,0x18,0xda,0x17,0x59,0xda,0x5c,0x98,0x18, - 0x5b,0xd9,0x10,0xe1,0x59,0x18,0x06,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69, - 0x6c,0x65,0x2e,0x6e,0x61,0x74,0x69,0x76,0x65,0x5f,0x64,0x6f,0x75,0x62,0x6c,0x65, - 0x5f,0x64,0x69,0x73,0x61,0x62,0x6c,0x65,0x43,0x84,0xa7,0x61,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,0xe7,0x59,0x86,0x07,0x7a,0xa2,0x47,0x7a,0xa6,0x21,0xc2,0x43,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,0xd6, - 0x73,0x3d,0xd8,0x93,0x3d,0xd0,0x13,0x3d,0xd2,0xa3,0x71,0xa9,0x9b,0x2b,0x93,0x43, - 0x61,0x7b,0x1b,0x73,0x8b,0x49,0x61,0x31,0xf6,0xc6,0xf6,0x26,0x37,0x44,0x5a,0x84, - 0xc7,0x7a,0xb8,0x07,0x7b,0xb2,0x07,0x7a,0xa2,0x47,0x7a,0x3a,0x2e,0x61,0x69,0x72, - 0x2e,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x94,0xc2,0xd2,0xe4,0x5c,0xd8,0xde,0xc6, - 0xc2,0xe8,0xd2,0xde,0xdc,0xbe,0xd2,0xdc,0xc8,0xca,0xf0,0xa8,0x84,0xa5,0xc9,0xb9, - 0xcc,0x85,0xb5,0xc1,0xb1,0x95,0x11,0xa3,0x2b,0xc3,0xa3,0xab,0x93,0x2b,0x93,0x21, - 0xe3,0x31,0x63,0x7b,0x0b,0xa3,0x63,0x01,0x99,0x0b,0x6b,0x83,0x63,0x2b,0xf3,0xe1, - 0x40,0x57,0x86,0x37,0x84,0x5a,0x8c,0xe7,0x7b,0xc0,0x60,0x19,0x16,0xe1,0x09,0x83, - 0x07,0x7a,0xc4,0xe0,0x91,0x9e,0x31,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,0x32,0x78,0xc0,0x60,0x19,0x16,0xe1, - 0x81,0x1e,0x33,0x78,0xa4,0xe7,0x0c,0x86,0x20,0xcf,0xf6,0x78,0x0f,0x19,0x3c,0x68, - 0x30,0xc4,0x40,0x80,0xa7,0x7a,0xd2,0x60,0x44,0xc4,0x0e,0xec,0x60,0x0f,0xed,0xe0, - 0x06,0xed,0xf0,0x0e,0xe4,0x50,0x0f,0xec,0x50,0x0e,0x6e,0x60,0x0e,0xec,0x10,0x0e, - 0xe7,0x30,0x0f,0x53,0x82,0x60,0x84,0xc2,0x0e,0xec,0x60,0x0f,0xed,0xe0,0x06,0xe9, - 0x40,0x0e,0xe5,0xe0,0x0e,0xf4,0x30,0x25,0x18,0x46,0x2c,0xe1,0x90,0x0e,0xf2,0xe0, - 0x06,0xf6,0x50,0x0e,0xf2,0x30,0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x25,0x20,0x46,0x50, + 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,0x2c,0xa1,0x09,0x00,0x00,0x79,0x18,0x00,0x00,0xb7,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,0xe7,0x20,0x08,0x0e,0x8e,0xad,0x0c, + 0xa4,0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26, + 0x06,0x06,0x26,0xc6,0xc5,0xc6,0xe6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac, + 0xac,0x65,0x26,0x06,0x06,0x26,0xc6,0xc5,0xc6,0xe6,0xc6,0x45,0x26,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, + 0x51,0x58,0x9a,0x9c,0x8b,0x5c,0x99,0x1b,0x59,0x99,0xdc,0x17,0x5d,0x98,0xdc,0x59, + 0x19,0x1d,0xa3,0xb0,0x34,0x39,0x97,0x30,0xb9,0xb3,0x2f,0xba,0x3c,0xb8,0xb2,0x2f, + 0xb7,0xb0,0xb6,0x32,0x1a,0x66,0x6c,0x6f,0x61,0x74,0x34,0x64,0xc2,0xd2,0xe4,0x5c, + 0xc2,0xe4,0xce,0xbe,0xdc,0xc2,0xda,0xca,0xa8,0x98,0xc9,0x85,0x9d,0x7d,0x8d,0xbd, + 0xb1,0xbd,0xc9,0x0d,0x61,0x9e,0x67,0x19,0x1e,0xe8,0x89,0x1e,0xe9,0x99,0x86,0x08, + 0x0f,0x45,0x29,0x2c,0x4d,0xce,0xc5,0x4c,0x2e,0xec,0xac,0xad,0xcc,0x8d,0xee,0x2b, + 0xcd,0x0d,0xae,0x8e,0x8e,0x4b,0xdd,0x5c,0x99,0x1c,0x0a,0xdb,0xdb,0x98,0x1b,0x4c, + 0x0a,0x95,0xb0,0x34,0x39,0x97,0xb1,0x32,0x37,0xba,0x32,0x39,0x3e,0x61,0x69,0x72, + 0x2e,0x70,0x65,0x72,0x73,0x70,0x65,0x63,0x74,0x69,0x76,0x65,0x14,0xea,0xec,0x86, + 0x48,0xcb,0xf0,0x58,0xcf,0xf5,0x60,0x4f,0xf6,0x40,0x4f,0xf4,0x48,0x8f,0xc6,0xa5, + 0x6e,0xae,0x4c,0x0e,0x85,0xed,0x6d,0xcc,0x2d,0x26,0x85,0xc5,0xd8,0x1b,0xdb,0x9b, + 0xdc,0x10,0x69,0x11,0x1e,0xeb,0xe1,0x1e,0xec,0xc9,0x1e,0xe8,0x89,0x1e,0xe9,0xe9, + 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,0xef,0x01,0x83,0x65, + 0x58,0x84,0x27,0x0c,0x1e,0xe8,0x11,0x83,0x47,0x7a,0xc6,0x80,0x4b,0x58,0x9a,0x9c, + 0xcb,0x5c,0x58,0x1b,0x1c,0x5b,0x99,0x1c,0x8f,0xb9,0xb0,0x36,0x38,0xb6,0x32,0x39, + 0x0e,0x73,0x6d,0x70,0x43,0xa4,0xe5,0x78,0xca,0xe0,0x01,0x83,0x65,0x58,0x84,0x07, + 0x7a,0xcc,0xe0,0x91,0x9e,0x33,0x18,0x82,0x3c,0xdb,0xe3,0x3d,0x64,0xf0,0xa0,0xc1, + 0x10,0x03,0x01,0x9e,0xea,0x49,0x83,0x11,0x11,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b, + 0xb4,0xc3,0x3b,0x90,0x43,0x3d,0xb0,0x43,0x39,0xb8,0x81,0x39,0xb0,0x43,0x38,0x9c, + 0xc3,0x3c,0x4c,0x11,0x82,0x61,0x84,0xc2,0x0e,0xec,0x60,0x0f,0xed,0xe0,0x06,0xe9, + 0x40,0x0e,0xe5,0xe0,0x0e,0xf4,0x30,0x25,0x28,0x46,0x2c,0xe1,0x90,0x0e,0xf2,0xe0, + 0x06,0xf6,0x50,0x0e,0xf2,0x30,0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x25,0x30,0x46,0x50, 0xe1,0x90,0x0e,0xf2,0xe0,0x06,0xec,0x10,0x0e,0xee,0x70,0x0e,0xf5,0x10,0x0e,0xe7, 0x50,0x0e,0xbf,0x60,0x0f,0xe5,0x20,0x0f,0xf3,0x90,0x0e,0xef,0xe0,0x0e,0x53,0x02, - 0x63,0xc4,0x14,0x0e,0xe9,0x20,0x0f,0x6e,0x30,0x0e,0xef,0xd0,0x0e,0xf0,0x90,0x0e, + 0x64,0xc4,0x14,0x0e,0xe9,0x20,0x0f,0x6e,0x30,0x0e,0xef,0xd0,0x0e,0xf0,0x90,0x0e, 0xec,0x50,0x0e,0xbf,0xf0,0x0e,0xf0,0x40,0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x0f,0x53, - 0x08,0x44,0x61,0x9c,0x11,0x4c,0x38,0xa4,0x83,0x3c,0xb8,0x81,0x39,0xc8,0x43,0x38, - 0x9c,0x43,0x3b,0x94,0x83,0x3b,0xd0,0xc3,0x94,0x40,0x0d,0x00,0x00,0x79,0x18,0x00, - 0x00,0x6d,0x00,0x00,0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d, + 0x06,0x85,0x71,0x46,0x30,0xe1,0x90,0x0e,0xf2,0xe0,0x06,0xe6,0x20,0x0f,0xe1,0x70, + 0x0e,0xed,0x50,0x0e,0xee,0x40,0x0f,0x53,0x02,0x35,0x00,0x00,0x00,0x79,0x18,0x00, + 0x00,0x7b,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, @@ -802,304 +797,299 @@ static const uint8_t _sfons_fs_bytecode_metal_macos[2893] = { 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,0x04,0xc7,0x22,0x80,0x40,0x20,0x88,0x8d,0x00,0x8c,0x25, - 0x00,0x01,0xa9,0x11,0x80,0x1a,0x20,0x31,0x03,0x40,0x61,0x0e,0xc2,0xb2,0x2c,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,0x70,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, + 0x07,0x7a,0x28,0x07,0x72,0x98,0x81,0x5c,0xe3,0x10,0x0e,0xec,0xc0,0x0e,0xe5,0x50, + 0x0e,0xf3,0x30,0x23,0xc1,0xd2,0x41,0x1e,0xe4,0xe1,0x17,0xd8,0xe1,0x1d,0xde,0x01, + 0x1e,0x66,0x50,0x59,0x38,0xa4,0x83,0x3c,0xb8,0x81,0x39,0xd4,0x83,0x3b,0x8c,0x03, + 0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x2f,0x9c,0x83,0x3c,0xbc,0x43,0x3d,0xc0,0xc3,0x3c, + 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,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sfons_vs_bytecode_metal_ios[3317] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xf5,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x15,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0xe0,0x0b,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,0x17,0x11,0x57,0x16,0x94,0x42,0x52, + 0xfb,0x1e,0xd0,0x32,0xfd,0x87,0x16,0xb0,0xa4,0xd0,0xc2,0x43,0xbe,0x93,0x8c,0xe0, + 0x2d,0x7a,0x5c,0x3e,0x06,0x4c,0x57,0xeb,0x4b,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,0x40,0x00,0x00,0x00,0x56,0x41,0x54, + 0x54,0x2a,0x00,0x04,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,0x70,0x73,0x69,0x7a,0x65,0x00,0x03,0x80,0x56,0x41,0x54, + 0x59,0x06,0x00,0x04,0x00,0x06,0x04,0x06,0x03,0x45,0x4e,0x44,0x54,0x04,0x00,0x00, + 0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00, + 0x00,0xc0,0x0b,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00, + 0x00,0xed,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,0x42, + 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,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,0x44,0x33,0x00,0xc3,0x08,0x04, + 0x60,0x89,0x10,0x02,0x18,0x46,0x10,0x80,0x24,0x08,0x33,0x51,0xf3,0x40,0x0f,0xf2, + 0x50,0x0f,0xe3,0x40,0x0f,0x6e,0xd0,0x0e,0xe5,0x40,0x0f,0xe1,0xc0,0x0e,0x7a,0xa0, + 0x07,0xed,0x10,0x0e,0xf4,0x20,0x0f,0xe9,0x80,0x0f,0x28,0x20,0x07,0x49,0x53,0x44, + 0x09,0x93,0x5f,0x49,0xff,0x03,0x44,0x00,0x23,0x21,0xa1,0x94,0x41,0x04,0x43,0x28, + 0x86,0x08,0x23,0x80,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,0x08,0x65,0x50, + 0x80,0x02,0x05,0x51,0x20,0xd4,0x46,0x00,0x88,0x8d,0x25,0x40,0x02,0x00,0x00,0x00, + 0x00,0x79,0x18,0x00,0x00,0x02,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, + 0x05,0xe7,0x20,0x08,0x0e,0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae, + 0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x06,0x06,0x26,0xc6,0xc5,0xc6,0xe6,0x06, + 0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x06,0x06,0x26,0xc6,0xc5, + 0xc6,0xe6,0xc6,0x45,0x26,0x65,0x88,0xa0,0x10,0x43,0x8c,0x25,0x58,0x90,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,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,0x51,0x58,0x9a,0x9c,0x8b,0x5d,0x99,0x1c,0x5d, + 0x19,0xde,0xd7,0x5b,0x1d,0x1d,0x5c,0x1d,0x1d,0x97,0xba,0xb9,0x32,0x39,0x14,0xb6, + 0xb7,0x31,0x37,0x98,0x14,0x46,0x61,0x69,0x72,0x2e,0x61,0x72,0x67,0x5f,0x74,0x79, + 0x70,0x65,0x5f,0x6e,0x61,0x6d,0x65,0x34,0xcc,0xd8,0xde,0xc2,0xe8,0x68,0xc8,0x84, + 0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xb9,0x85,0xb5,0x95,0x51,0xa8,0xb3,0x1b,0xc2, + 0x28,0x8f,0x02,0x29,0x91,0x22,0x29,0x93,0x42,0x71,0xa9,0x9b,0x2b,0x93,0x43,0x61, + 0x7b,0x1b,0x73,0x8b,0x49,0x61,0x31,0xf6,0xc6,0xf6,0x26,0x37,0x84,0x51,0x1e,0xc5, + 0x52,0x22,0x45,0x52,0x26,0xe5,0x22,0x13,0x96,0x26,0xe7,0x02,0xf7,0x36,0x97,0x46, + 0x97,0xf6,0xe6,0xc6,0xe5,0x8c,0xed,0x0b,0xea,0x6d,0x2e,0x8d,0x2e,0xed,0xcd,0x6d, + 0x88,0xa2,0x64,0x4a,0xa4,0x48,0xca,0xa4,0x68,0x74,0xc2,0xd2,0xe4,0x5c,0xe0,0xde, + 0xd2,0xdc,0xe8,0xbe,0xe6,0xd2,0xf4,0xca,0x58,0x98,0xb1,0xbd,0x85,0xd1,0x91,0x39, + 0x63,0xfb,0x82,0x7a,0x4b,0x73,0xa3,0x9b,0x4a,0xd3,0x2b,0x1b,0xa2,0x28,0x9c,0x12, + 0x29,0x9d,0x32,0x29,0xde,0x10,0x44,0xa9,0x14,0x4c,0xd9,0x94,0x8f,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,0x7a,0x67,0x65,0x6e,0x65,0x72,0x61,0x74,0x65,0x64,0x28,0x5f,0x5f,0x61,0x69, + 0x72,0x5f,0x70,0x6c,0x61,0x63,0x65,0x68,0x6f,0x6c,0x64,0x65,0x72,0x5f,0x5f,0x29, + 0x44,0xe0,0xde,0xe6,0xd2,0xe8,0xd2,0xde,0xdc,0x86,0x50,0x8b,0xa0,0x84,0x81,0x22, + 0x06,0x8b,0xb0,0x04,0xca,0x18,0x28,0x91,0x22,0x29,0x93,0x42,0x06,0x34,0xcc,0xd8, + 0xde,0xc2,0xe8,0x64,0x98,0xd0,0x95,0xe1,0x8d,0xbd,0xbd,0xc9,0x91,0xc1,0x0c,0xa1, + 0x96,0x40,0x09,0x03,0x45,0x0c,0x96,0x60,0x09,0x94,0x31,0x50,0x22,0xc5,0x0c,0x94, + 0x49,0x39,0x03,0x1a,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x43,0xa8,0x65,0x50,0xc2,0x40, + 0x11,0x83,0x65,0x58,0x02,0x65,0x0c,0x94,0x48,0x91,0x94,0x49,0x49,0x03,0x16,0x70, + 0x73,0x69,0x7a,0x65,0x43,0xa8,0xc5,0x50,0xc2,0x40,0x11,0x83,0xc5,0x58,0x02,0x65, + 0x0c,0x94,0x48,0xe9,0x94,0x49,0x59,0x03,0x2a,0x61,0x69,0x72,0x2e,0x62,0x75,0x66, + 0x66,0x65,0x72,0x7c,0xc2,0xd2,0xe4,0x5c,0xc4,0xea,0xcc,0xcc,0xca,0xe4,0xbe,0xe6, + 0xd2,0xf4,0xca,0x88,0x84,0xa5,0xc9,0xb9,0xc8,0x95,0x85,0x91,0x91,0x0a,0x4b,0x93, + 0x73,0x99,0xa3,0x93,0xab,0x1b,0xa3,0xfb,0xa2,0xcb,0x83,0x2b,0xfb,0x4a,0x73,0x33, + 0x7b,0x23,0x62,0xc6,0xf6,0x16,0x46,0x47,0x83,0x47,0xc3,0xa1,0xcd,0x0e,0x8e,0x02, + 0x5d,0xdb,0x10,0x6a,0x11,0x16,0x62,0x11,0x94,0x38,0x50,0xe4,0x60,0x21,0x16,0x62, + 0x11,0x94,0x38,0x50,0xe6,0x80,0x51,0x58,0x9a,0x9c,0x4b,0x98,0xdc,0xd9,0x17,0x5d, + 0x1e,0x5c,0xd9,0xd7,0x5c,0x9a,0x5e,0x19,0xaf,0xb0,0x34,0x39,0x97,0x30,0xb9,0xb3, + 0x2f,0xba,0x3c,0xb8,0xb2,0xaf,0x30,0xb6,0xb4,0x33,0xb7,0xaf,0xb9,0x34,0xbd,0x32, + 0x26,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x1c,0xbe,0x62,0x72,0x86,0x90, + 0xc1,0x52,0x28,0x6d,0xa0,0xb8,0xc1,0x72,0x28,0x62,0xb0,0x08,0x4b,0xa0,0xbc,0x81, + 0x02,0x07,0x0a,0x1d,0x28,0x75,0xb0,0x1c,0x8a,0x1d,0x2c,0x89,0x12,0x29,0x77,0xa0, + 0x4c,0x0a,0x1e,0x0c,0x51,0x94,0x32,0x50,0xd0,0x40,0x51,0x03,0x85,0x0d,0x94,0x3c, + 0x18,0x62,0x24,0x80,0x02,0x06,0x8a,0x1e,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,0xfa,0x60,0x88,0xa1,0xf0, + 0x81,0xe2,0x07,0x8d,0x32,0xc4,0x50,0xfe,0x40,0xf9,0x83,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,0x65,0x50,0x18,0x67,0x84,0x12,0x0e,0xe9,0x20,0x0f,0x6e, + 0x60,0x0f,0xe5,0x20,0x0f,0xf4,0x50,0x0e,0xf8,0x30,0x25,0xd8,0x03,0x00,0x00,0x00, + 0x00,0x79,0x18,0x00,0x00,0x7b,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,0x98,0x81,0x5c,0xe3,0x10,0x0e,0xec, + 0xc0,0x0e,0xe5,0x50,0x0e,0xf3,0x30,0x23,0xc1,0xd2,0x41,0x1e,0xe4,0xe1,0x17,0xd8, + 0xe1,0x1d,0xde,0x01,0x1e,0x66,0x50,0x59,0x38,0xa4,0x83,0x3c,0xb8,0x81,0x39,0xd4, + 0x83,0x3b,0x8c,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x2f,0x9c,0x83,0x3c,0xbc,0x43, + 0x3d,0xc0,0xc3,0x3c,0x00,0x71,0x20,0x00,0x00,0x02,0x00,0x00,0x00,0x06,0x50,0x30, + 0x00,0xd2,0xd0,0x00,0x00,0x61,0x20,0x00,0x00,0x3e,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,0x4b, + 0x94,0x65,0x11,0x05,0x65,0x90,0x21,0x1a,0x0c,0x13,0x02,0xf9,0x8c,0x57,0x3c,0x55, + 0xd7,0x2d,0x14,0x94,0x41,0x86,0xea,0x70,0x4c,0x08,0xe4,0x63,0x41,0x01,0x9f,0xf1, + 0x0a,0x4a,0x13,0x03,0x31,0x70,0x28,0x28,0x83,0x0c,0x1a,0x43,0x99,0x10,0xc8,0xc7, + 0x8a,0x00,0x3e,0xe3,0x15,0xd9,0x77,0x06,0x67,0x40,0x51,0x50,0x06,0x19,0xbe,0x48, + 0x33,0x21,0x90,0x8f,0x15,0x01,0x7c,0xc6,0x2b,0x3c,0x32,0x68,0x03,0x36,0x20,0x03, + 0x0a,0xca,0x20,0xc3,0x18,0x60,0x99,0x09,0x81,0x7c,0xc6,0x2b,0xc4,0x00,0x0d,0xe2, + 0x00,0x0e,0x3c,0x0a,0xca,0x20,0xc3,0x19,0x70,0x61,0x60,0x42,0x20,0x1f,0x0b,0x0a, + 0xf8,0x8c,0x57,0x9c,0x41,0x1b,0xd8,0x41,0x1d,0x88,0x01,0x05,0xc5,0x86,0x00,0x3e, + 0xb3,0x0d,0x61,0x10,0x00,0xb3,0x0d,0x41,0x1b,0x04,0xb3,0x0d,0xc1,0x23,0xcc,0x36, + 0x04,0x6e,0x30,0x64,0x10,0x10,0x03,0x00,0x00,0x09,0x00,0x00,0x00,0x5b,0x86,0x20, + 0x00,0x85,0x2d,0x43,0x11,0x80,0xc2,0x96,0x41,0x09,0x40,0x61,0xcb,0xf0,0x04,0xa0, + 0xb0,0x65,0xa0,0x02,0x50,0xd8,0x32,0x60,0x01,0x28,0x6c,0x19,0xba,0x00,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_vs_bytecode_metal_ios[3417] = { - 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x59,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, - 0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x01,0x00,0x00,0x00,0x00,0x00,0x00, - 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x19,0x01,0x00,0x00,0x00,0x00,0x00,0x00, - 0x40,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, +static const uint8_t _sfons_fs_bytecode_metal_ios[2841] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x19,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd1,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x40,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,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0x53,0xd2,0x06,0xf6,0xae,0x63,0x48, - 0x79,0x19,0x71,0xbe,0x11,0x2a,0xc0,0x72,0x90,0x9c,0xd4,0x1c,0xf2,0xfa,0xd7,0x02, - 0x09,0x76,0xb9,0xee,0x24,0x42,0xd7,0xbd,0x99,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x01,0x00,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0x7c,0x59,0x61,0x85,0x8c,0x08,0x62, + 0xf6,0xd0,0x45,0x0a,0x3a,0xcb,0xa8,0xa5,0x3c,0x2d,0x6c,0x6b,0x9c,0x3d,0xf0,0xf5, + 0xc4,0xdc,0xb5,0x90,0xdc,0xee,0x5a,0x9f,0x63,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,0x40,0x00,0x00, - 0x00,0x56,0x41,0x54,0x54,0x2a,0x00,0x04,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,0x70,0x73,0x69,0x7a,0x65,0x00,0x03, - 0x80,0x56,0x41,0x54,0x59,0x06,0x00,0x04,0x00,0x06,0x04,0x06,0x03,0x45,0x4e,0x44, + 0x00,0x01,0x00,0x01,0x00,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,0x20,0x0c,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, - 0xde,0x21,0x0c,0x00,0x00,0x05,0x03,0x00,0x00,0x0b,0x82,0x20,0x00,0x02,0x00,0x00, + 0x00,0x14,0x00,0x00,0x00,0x28,0x0a,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0x87,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,0x8a,0x00,0x00,0x00,0x1b,0xf6,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,0x08,0x7a,0x08,0x07,0x79,0x38,0x87,0x72,0xa0,0x87,0x36, - 0x30,0x87,0x72,0x08,0x07,0x7a,0xa8,0x07,0x79,0x28,0x87,0x79,0x00,0xda,0xc0,0x1c, - 0xe0,0x21,0x0e,0xec,0x00,0x20,0xea,0xc1,0x1d,0xe6,0x21,0x1c,0xcc,0xa1,0x1c,0xda, - 0xc0,0x1c,0xe0,0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90,0x87,0x7a, - 0x28,0x07,0x80,0xa8,0x87,0x79,0x28,0x87,0x36,0x98,0x87,0x77,0x30,0x07,0x7a,0x68, - 0x03,0x73,0x60,0x87,0x77,0x08,0x07,0x7a,0x00,0xcc,0x21,0x1c,0xd8,0x61,0x1e,0xca, - 0x01,0xd8,0x80,0x08,0x05,0x90,0x00,0x0b,0x50,0x01,0x00,0x00,0x00,0x49,0x18,0x00, - 0x00,0x01,0x00,0x00,0x00,0x13,0x84,0x40,0x00,0x89,0x20,0x00,0x00,0x24,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,0x40,0x33, - 0x00,0xc3,0x08,0x04,0x70,0x97,0x34,0x45,0x94,0x30,0xf9,0x0c,0x80,0x34,0xf4,0xef, - 0x50,0x93,0xff,0x00,0x82,0x42,0x0c,0x98,0x08,0x21,0x80,0x61,0x04,0x01,0x48,0x82, - 0x30,0x13,0x35,0x0f,0xf4,0x20,0x0f,0xf5,0x30,0x0e,0xf4,0xe0,0x06,0xed,0x50,0x0e, - 0xf4,0x10,0x0e,0xec,0xa0,0x07,0x7a,0xd0,0x0e,0xe1,0x40,0x0f,0xf2,0x90,0x0e,0xf8, - 0x80,0x02,0x72,0x90,0x34,0x45,0x94,0x30,0xf9,0x95,0xf4,0x3f,0x40,0x04,0x30,0x12, - 0x12,0x4a,0x19,0x44,0x30,0x84,0x62,0x88,0x30,0x02,0x38,0x84,0x06,0x02,0xe6,0x08, - 0xc0,0x60,0x8e,0x00,0x14,0x06,0x11,0x02,0x61,0x18,0x81,0x58,0x46,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,0xef,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,0xe6,0x80,0x07,0x70,0xa0,0x07,0x71,0x20,0x07, - 0x78,0xa0,0x07,0x71,0x20,0x07,0x78,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,0xe6,0x80,0x07,0x70, - 0xa0,0x07,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,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,0x09,0x00,0x00,0x00,0x32,0x1e,0x98, - 0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a,0x25,0x30,0x02, - 0x50,0x80,0x01,0x85,0x50,0x04,0x65,0x50,0x80,0x02,0x05,0x51,0x20,0xc4,0x46,0x00, - 0x00,0x79,0x18,0x00,0x00,0x15,0x01,0x00,0x00,0x1a,0x03,0x4c,0x10,0x95,0xbb,0x31, - 0xb4,0x30,0xb9,0xaf,0xb9,0x34,0xbd,0xb2,0x21,0xc6,0x12,0x28,0xc0,0x42,0x70,0x0d, - 0x82,0xe0,0xe0,0xd8,0xca,0x40,0x98,0x98,0xac,0x9a,0x40,0xec,0xca,0xe4,0xe6,0xd2, - 0xde,0xdc,0x40,0x72,0x60,0x64,0x5c,0x62,0x40,0x50,0xda,0xca,0xe8,0xc2,0xd8,0xcc, - 0xca,0x5a,0x72,0x60,0x64,0x5c,0x62,0x5c,0x6a,0x60,0x52,0x86,0x08,0x8a,0x30,0xc4, - 0x58,0x82,0x05,0x59,0x04,0x16,0x4d,0x65,0x74,0x61,0x6c,0x43,0x10,0xa5,0x58,0x82, - 0x45,0x58,0x04,0x6e,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e, - 0x64,0x65,0x6e,0x6f,0x72,0x6d,0x73,0x5f,0x64,0x69,0x73,0x61,0x62,0x6c,0x65,0x43, - 0x04,0xe5,0x20,0x17,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x62, - 0x16,0x36,0x47,0xf7,0xd5,0x16,0x46,0x87,0xf6,0x55,0xe6,0x16,0x26,0xc6,0x56,0x36, - 0x44,0x50,0x12,0x92,0x41,0x58,0x9a,0x9c,0xcb,0xd8,0x5b,0x1b,0x5c,0x1a,0x5b,0x99, - 0x8b,0x99,0x5c,0x58,0x5b,0x99,0x58,0x9d,0x99,0x59,0x99,0xdc,0x97,0x59,0x19,0xdd, - 0x18,0xda,0x57,0x99,0x5b,0x98,0x18,0x5b,0xd9,0x10,0x41,0x59,0x18,0x06,0x61,0x69, - 0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x6e,0x61,0x74,0x69,0x76,0x65, - 0x5f,0x64,0x6f,0x75,0x62,0x6c,0x65,0x5f,0x64,0x69,0x73,0x61,0x62,0x6c,0x65,0x43, - 0x04,0xa5,0x61,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,0xca,0xa3,0x40,0x4a, - 0xa4,0x48,0xca,0xa4,0x50,0x5c,0xea,0xe6,0xca,0xe4,0x50,0xd8,0xde,0xc6,0xdc,0x62, - 0x52,0x58,0x8c,0xbd,0xb1,0xbd,0xc9,0x0d,0x61,0x94,0x47,0xb1,0x94,0x48,0x91,0x94, - 0x49,0xb9,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,0x99,0x12, - 0x29,0x92,0x32,0x29,0x1a,0x9d,0xb0,0x34,0x39,0x17,0xb8,0xb7,0x34,0x37,0xba,0xaf, - 0xb9,0x34,0xbd,0x32,0x16,0x66,0x6c,0x6f,0x61,0x74,0x64,0xce,0xd8,0xbe,0xa0,0xde, - 0xd2,0xdc,0xe8,0xa6,0xd2,0xf4,0xca,0x86,0x28,0x0a,0xa7,0x44,0x4a,0xa7,0x4c,0x8a, - 0x37,0x04,0x51,0x2a,0x05,0x53,0x36,0xe5,0x23,0x14,0x96,0x26,0xe7,0x62,0x57,0x26, - 0x47,0x57,0x86,0xf7,0x95,0xe6,0x06,0x57,0x47,0x47,0x29,0x2c,0x4d,0xce,0x85,0xed, - 0x6d,0x2c,0x8c,0x2e,0xed,0xcd,0xed,0x2b,0xcd,0x8d,0xac,0x0c,0x8f,0xd9,0x59,0x99, - 0x5b,0x99,0x5c,0x18,0x5d,0x19,0x19,0x0a,0x0e,0xdc,0xdb,0x5c,0x1a,0x5d,0xda,0x9b, - 0x1b,0x91,0x1d,0xcd,0x97,0x59,0x0a,0x11,0xb8,0xb7,0xb9,0x34,0xba,0xb4,0x37,0xb7, - 0x21,0xd4,0x22,0x28,0x61,0xa0,0x88,0xc1,0x22,0x2c,0x81,0x32,0x06,0x4a,0xa4,0x48, - 0xca,0xa4,0x90,0x01,0xb5,0xb3,0x32,0xb7,0x32,0xb9,0x30,0xba,0x32,0x32,0x94,0x1c, - 0xba,0x32,0xbc,0xb1,0xb7,0x37,0x39,0x32,0x18,0x22,0x3b,0x99,0x2f,0xb3,0x14,0x1a, - 0x66,0x6c,0x6f,0x61,0x74,0x32,0x4c,0xe8,0xca,0xf0,0xc6,0xde,0xde,0xe4,0xc8,0x60, - 0x86,0x50,0x4b,0xa0,0x84,0x81,0x22,0x06,0x4b,0xb0,0x04,0x8a,0x19,0x28,0x91,0x72, - 0x06,0xca,0xa4,0xa0,0x01,0xaf,0xb3,0x32,0xb7,0x32,0xb9,0x30,0xba,0x32,0x32,0x14, - 0x9b,0xb1,0x37,0xb6,0x37,0x39,0x18,0x22,0x3b,0x9a,0x2f,0xb3,0x14,0x1a,0x63,0x6f, - 0x6c,0x6f,0x72,0x30,0x43,0xa8,0xa5,0x50,0xc2,0x40,0x11,0x83,0xa5,0x58,0x02,0x45, - 0x0d,0x94,0x48,0x91,0x94,0x49,0x59,0x03,0x4a,0x67,0x65,0x6e,0x65,0x72,0x61,0x74, - 0x65,0x64,0x28,0x35,0x70,0x73,0x69,0x7a,0x65,0x66,0x29,0x2c,0xe0,0xe6,0xd2,0xf4, - 0xca,0x86,0x50,0x8b,0xa1,0x84,0x81,0x22,0x06,0x8b,0xb1,0x04,0x4a,0x1b,0x28,0x91, - 0xd2,0x29,0x93,0xe2,0x06,0x54,0xc2,0xd2,0xe4,0x5c,0xc4,0xea,0xcc,0xcc,0xca,0xe4, - 0xf8,0x84,0xa5,0xc9,0xb9,0x88,0xd5,0x99,0x99,0x95,0xc9,0x7d,0xcd,0xa5,0xe9,0x95, - 0x11,0x09,0x4b,0x93,0x73,0x91,0x2b,0x0b,0x23,0x23,0x15,0x96,0x26,0xe7,0x32,0x47, - 0x27,0x57,0x37,0x46,0xf7,0x45,0x97,0x07,0x57,0xf6,0x95,0xe6,0x66,0xf6,0x46,0xc4, - 0x8c,0xed,0x2d,0x8c,0x8e,0x06,0x8f,0x86,0x43,0x9b,0x1d,0x1c,0x05,0xba,0xb6,0x21, - 0xd4,0x22,0x2c,0xc3,0x22,0x28,0x74,0xa0,0xd4,0xc1,0x32,0x2c,0xc3,0x22,0x28,0x74, - 0xa0,0xd8,0x01,0xa3,0xb0,0x34,0x39,0x97,0x30,0xb9,0xb3,0x2f,0xba,0x3c,0xb8,0xb2, - 0xaf,0xb9,0x34,0xbd,0x32,0x5e,0x61,0x69,0x72,0x2e,0x61,0x72,0x67,0x5f,0x74,0x79, - 0x70,0x65,0x5f,0x61,0x6c,0x69,0x67,0x6e,0x5f,0x73,0x69,0x7a,0x65,0x4c,0xec,0xe6, - 0xbe,0xe0,0xc2,0xe4,0xc2,0xda,0xe6,0x38,0x7c,0xc9,0xc4,0x0c,0x21,0x83,0x85,0x50, - 0xe0,0x40,0x89,0x83,0xe5,0x50,0xc4,0x60,0x11,0x96,0x40,0x91,0x03,0x65,0x0e,0x94, - 0x3b,0x50,0xf0,0x60,0x39,0x94,0x3c,0x58,0x12,0x25,0x52,0xf4,0x40,0x99,0x94,0x3d, - 0x18,0xa2,0x28,0x65,0xa0,0xa4,0x81,0xc2,0x06,0xca,0x1b,0x28,0x7c,0x30,0xc4,0x48, - 0x00,0x05,0x0c,0x94,0x3e,0xe0,0xf3,0xd6,0xe6,0x96,0x06,0xf7,0x46,0x57,0xe6,0x46, - 0x07,0x32,0x86,0x16,0x26,0xc7,0x67,0x2a,0xad,0x0d,0x8e,0xad,0x0c,0x64,0x68,0x65, - 0x05,0x84,0x4a,0x28,0x28,0x68,0x88,0xa0,0x80,0xc2,0x10,0x43,0xf9,0x03,0x25,0x14, - 0x18,0x65,0x88,0xa1,0x88,0x82,0x22,0x0a,0x8c,0x32,0x22,0x62,0x07,0x76,0xb0,0x87, - 0x76,0x70,0x83,0x76,0x78,0x07,0x72,0xa8,0x07,0x76,0x28,0x07,0x37,0x30,0x07,0x76, - 0x08,0x87,0x73,0x98,0x87,0x29,0x41,0x30,0x42,0x61,0x07,0x76,0xb0,0x87,0x76,0x70, - 0x83,0x74,0x20,0x87,0x72,0x70,0x07,0x7a,0x98,0x12,0x0c,0x23,0x96,0x70,0x48,0x07, - 0x79,0x70,0x03,0x7b,0x28,0x07,0x79,0x98,0x87,0x74,0x78,0x07,0x77,0x98,0x12,0x10, - 0x23,0xa8,0x70,0x48,0x07,0x79,0x70,0x03,0x76,0x08,0x07,0x77,0x38,0x87,0x7a,0x08, - 0x87,0x73,0x28,0x87,0x5f,0xb0,0x87,0x72,0x90,0x87,0x79,0x48,0x87,0x77,0x70,0x87, - 0x29,0x81,0x31,0x62,0x0a,0x87,0x74,0x90,0x07,0x37,0x18,0x87,0x77,0x68,0x07,0x78, - 0x48,0x07,0x76,0x28,0x87,0x5f,0x78,0x07,0x78,0xa0,0x87,0x74,0x78,0x07,0x77,0x98, - 0x87,0x29,0x04,0xa2,0x30,0xce,0x08,0x25,0x1c,0xd2,0x41,0x1e,0xdc,0xc0,0x1e,0xca, - 0x41,0x1e,0xe8,0xa1,0x1c,0xf0,0x61,0x4a,0xe0,0x07,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,0x3e,0x00,0x00, - 0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0xe4,0xc6,0x22, - 0x86,0x61,0x18,0xc6,0x22,0x04,0x41,0x10,0xc6,0x22,0x82,0x20,0x08,0x88,0x95,0x40, - 0x19,0x14,0x01,0xb9,0x11,0x00,0x1a,0x33,0x00,0x24,0x66,0x00,0x28,0xcc,0x00,0x00, - 0x00,0xe3,0x15,0x0b,0x84,0x61,0x10,0x05,0x65,0x90,0x21,0x1a,0x0c,0x13,0x02,0xf9, - 0x8c,0x57,0x3c,0x14,0xc7,0x2d,0x14,0x94,0x41,0x86,0xea,0x70,0x4c,0x08,0xe4,0x63, - 0x41,0x01,0x9f,0xf1,0x0a,0x2a,0x0b,0x83,0x30,0x70,0x28,0x28,0x83,0x0c,0x1a,0x43, - 0x99,0x10,0xc8,0xc7,0x8a,0x00,0x3e,0xe3,0x15,0x99,0x67,0x06,0x66,0x40,0x51,0x50, - 0x06,0x19,0xbe,0x48,0x33,0x21,0x90,0x8f,0x15,0x01,0x7c,0xc6,0x2b,0xbc,0x31,0x60, - 0x83,0x35,0x18,0x03,0x0a,0xca,0x20,0xc3,0x18,0x60,0x99,0x09,0x81,0x7c,0xc6,0x2b, - 0xc4,0xe0,0x0c,0xe0,0xe0,0x0d,0x3c,0x0a,0xca,0x20,0xc3,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,0x41,0x1b,0x04,0xb3,0x0d, - 0xc1,0x23,0xcc,0x36,0x04,0x6e,0x30,0x64,0x10,0x10,0x03,0x00,0x00,0x09,0x00,0x00, - 0x00,0x5b,0x86,0x20,0x18,0x85,0x2d,0x43,0x11,0x8c,0xc2,0x96,0x41,0x09,0x46,0x61, - 0xcb,0xf0,0x04,0xa3,0xb0,0x65,0xa0,0x82,0x51,0xd8,0x32,0x60,0xc1,0x28,0x6c,0x19, - 0xba,0x60,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_ios[2877] = { - 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x3d,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, - 0x60,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,0x54,0xcb,0x23,0xc1,0x79,0xec,0x33, - 0x7a,0x1d,0x12,0x8c,0x7c,0xe5,0x11,0xa5,0xdb,0xc5,0x1c,0x52,0xd5,0x69,0x04,0xe6, - 0x72,0x3d,0xdb,0xf1,0xf1,0xf4,0x44,0xa4,0xb5,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,0x4c,0x0a,0x00,0x00,0xff,0xff,0xff, - 0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0x90,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,0x92,0x00,0x00,0x00,0x1b,0xfa,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,0xa0,0x87,0x70,0x90,0x87, - 0x73,0x28,0x07,0x7a,0x68,0x03,0x73,0x28,0x87,0x70,0xa0,0x87,0x7a,0x90,0x87,0x72, - 0x98,0x07,0xa0,0x0d,0xcc,0x01,0x1e,0xe2,0xc0,0x0e,0x00,0xa2,0x1e,0xdc,0x61,0x1e, - 0xc2,0xc1,0x1c,0xca,0xa1,0x0d,0xcc,0x01,0x1e,0xda,0xa0,0x1d,0xc2,0x81,0x1e,0xd0, - 0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x88,0x7a,0x98,0x87,0x72,0x68,0x83,0x79, - 0x78,0x07,0x73,0xa0,0x87,0x36,0x30,0x07,0x76,0x78,0x87,0x70,0xa0,0x07,0xc0,0x1c, - 0xc2,0x81,0x1d,0xe6,0xa1,0x1c,0x80,0x0d,0x86,0x30,0x00,0x0b,0x50,0x6d,0x30,0x06, - 0x02,0x58,0x80,0x6a,0x03,0x42,0xfc,0xff,0xff,0xff,0xff,0x00,0x30,0x80,0x04,0x54, - 0x1b,0x8c,0x22,0x00,0x16,0xa0,0xda,0x60,0x18,0x02,0xb0,0x00,0x15,0x00,0x00,0x00, - 0x00,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,0x61,0x04,0x62,0x19,0x01,0x00,0x13,0xa8,0x70, + 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,0x1e,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,0x60,0x83,0x70,0x94,0x34,0x45,0x94,0x30, + 0xf9,0xff,0x44,0x5c,0x13,0x15,0x11,0xbf,0x3d,0xfc,0xd3,0x18,0x01,0x30,0x88,0x30, + 0x04,0x17,0x49,0x53,0x44,0x09,0x93,0xff,0x4b,0x00,0xf3,0x2c,0x44,0xf4,0x4f,0x63, + 0x04,0xc0,0x20,0x42,0x21,0x94,0x42,0x84,0x40,0x0c,0x9d,0x61,0x04,0x01,0x98,0x23, + 0x08,0xe6,0x08,0xc0,0x60,0x18,0x41,0x58,0x0a,0x12,0x88,0x49,0x8a,0x29,0x40,0x6d, + 0x20,0x20,0x05,0xd6,0x30,0x02,0xb1,0x8c,0x00,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,0xef,0x51, + 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, @@ -1107,73 +1097,70 @@ static const uint8_t _sfons_fs_bytecode_metal_ios[2877] = { 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,0xe6,0x80,0x07,0x70,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0,0x07,0x71, - 0x20,0x07,0x78,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,0xe6,0x80,0x07,0x70,0xa0,0x07,0x71,0x20, - 0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,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,0x07,0x00,0x00,0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26, - 0x47,0xc6,0x04,0x43,0x5a,0x23,0x00,0x25,0x50,0x08,0x05,0x51,0x04,0x65,0x00,0x00, - 0x00,0x79,0x18,0x00,0x00,0xbb,0x00,0x00,0x00,0x1a,0x03,0x4c,0x10,0x95,0xbb,0x31, - 0xb4,0x30,0xb9,0xaf,0xb9,0x34,0xbd,0xb2,0x21,0xc6,0x32,0x3c,0xc0,0x42,0x70,0x0d, - 0x82,0xe0,0xe0,0xd8,0xca,0x40,0x98,0x98,0xac,0x9a,0x40,0xec,0xca,0xe4,0xe6,0xd2, - 0xde,0xdc,0x40,0x72,0x60,0x64,0x5c,0x62,0x40,0x50,0xda,0xca,0xe8,0xc2,0xd8,0xcc, - 0xca,0x5a,0x72,0x60,0x64,0x5c,0x62,0x5c,0x6a,0x60,0x52,0x86,0x08,0x8f,0x30,0xc4, - 0x58,0x86,0xa5,0x58,0x04,0x16,0x4d,0x65,0x74,0x61,0x6c,0x43,0x90,0xa7,0x58,0x86, - 0x45,0x58,0x04,0x6e,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e, - 0x64,0x65,0x6e,0x6f,0x72,0x6d,0x73,0x5f,0x64,0x69,0x73,0x61,0x62,0x6c,0x65,0x43, - 0x84,0xe7,0x20,0x17,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x62, - 0x16,0x36,0x47,0xf7,0xd5,0x16,0x46,0x87,0xf6,0x55,0xe6,0x16,0x26,0xc6,0x56,0x36, - 0x44,0x78,0x12,0x92,0x41,0x58,0x9a,0x9c,0xcb,0xd8,0x5b,0x1b,0x5c,0x1a,0x5b,0x99, - 0x8b,0x99,0x5c,0x58,0x5b,0x99,0x58,0x9d,0x99,0x59,0x99,0xdc,0x97,0x59,0x19,0xdd, - 0x18,0xda,0x57,0x99,0x5b,0x98,0x18,0x5b,0xd9,0x10,0xe1,0x59,0x18,0x06,0x61,0x69, - 0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x6e,0x61,0x74,0x69,0x76,0x65, - 0x5f,0x64,0x6f,0x75,0x62,0x6c,0x65,0x5f,0x64,0x69,0x73,0x61,0x62,0x6c,0x65,0x43, - 0x84,0xa7,0x61,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,0xe7,0x59,0x84,0x07,0x7a,0xa2,0x47,0x7a, - 0xa6,0x21,0xc2,0x43,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,0xd6,0x73,0x3d,0xd8,0x93,0x3d,0xd0,0x13,0x3d,0xd2, - 0xa3,0x71,0xa9,0x9b,0x2b,0x93,0x43,0x61,0x7b,0x1b,0x73,0x8b,0x49,0x61,0x31,0xf6, - 0xc6,0xf6,0x26,0x37,0x44,0x5a,0x86,0xc7,0x7a,0xb8,0x07,0x7b,0xb2,0x07,0x7a,0xa2, - 0x47,0x7a,0x3a,0x2e,0x61,0x69,0x72,0x2e,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x94, - 0xc2,0xd2,0xe4,0x5c,0xd8,0xde,0xc6,0xc2,0xe8,0xd2,0xde,0xdc,0xbe,0xd2,0xdc,0xc8, - 0xca,0xf0,0xa8,0x84,0xa5,0xc9,0xb9,0xcc,0x85,0xb5,0xc1,0xb1,0x95,0x11,0xa3,0x2b, - 0xc3,0xa3,0xab,0x93,0x2b,0x93,0x21,0xe3,0x31,0x63,0x7b,0x0b,0xa3,0x63,0x01,0x99, - 0x0b,0x6b,0x83,0x63,0x2b,0xf3,0xe1,0x40,0x57,0x86,0x37,0x84,0x5a,0x8c,0xe7,0x7b, - 0xc0,0x60,0x11,0x96,0xe1,0x09,0x83,0x07,0x7a,0xc4,0xe0,0x91,0x9e,0x31,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, - 0x32,0x78,0xc0,0x60,0x11,0x96,0xe1,0x81,0x1e,0x33,0x78,0xa4,0xe7,0x0c,0x86,0x20, - 0xcf,0xf6,0x78,0x0f,0x19,0x3c,0x68,0x30,0xc4,0x40,0x80,0xa7,0x7a,0xd2,0x60,0x44, - 0xc4,0x0e,0xec,0x60,0x0f,0xed,0xe0,0x06,0xed,0xf0,0x0e,0xe4,0x50,0x0f,0xec,0x50, - 0x0e,0x6e,0x60,0x0e,0xec,0x10,0x0e,0xe7,0x30,0x0f,0x53,0x82,0x60,0x84,0xc2,0x0e, - 0xec,0x60,0x0f,0xed,0xe0,0x06,0xe9,0x40,0x0e,0xe5,0xe0,0x0e,0xf4,0x30,0x25,0x18, + 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,0x25,0x30,0x02,0x50,0x04,0x85,0x50,0x10,0x65,0x40,0x70,0x2c,0x01,0x12,0x00, + 0x00,0x79,0x18,0x00,0x00,0xb7,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,0xe7,0x20,0x08,0x0e,0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae, + 0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x06,0x06,0x26,0xc6,0xc5,0xc6,0xe6,0x06, + 0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x06,0x06,0x26,0xc6,0xc5, + 0xc6,0xe6,0xc6,0x45,0x26,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,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,0x51,0x58,0x9a,0x9c,0x8b,0x5c,0x99,0x1b,0x59, + 0x99,0xdc,0x17,0x5d,0x98,0xdc,0x59,0x19,0x1d,0xa3,0xb0,0x34,0x39,0x97,0x30,0xb9, + 0xb3,0x2f,0xba,0x3c,0xb8,0xb2,0x2f,0xb7,0xb0,0xb6,0x32,0x1a,0x66,0x6c,0x6f,0x61, + 0x74,0x34,0x64,0xc2,0xd2,0xe4,0x5c,0xc2,0xe4,0xce,0xbe,0xdc,0xc2,0xda,0xca,0xa8, + 0x98,0xc9,0x85,0x9d,0x7d,0x8d,0xbd,0xb1,0xbd,0xc9,0x0d,0x61,0x9e,0x67,0x19,0x1e, + 0xe8,0x89,0x1e,0xe9,0x99,0x86,0x08,0x0f,0x45,0x29,0x2c,0x4d,0xce,0xc5,0x4c,0x2e, + 0xec,0xac,0xad,0xcc,0x8d,0xee,0x2b,0xcd,0x0d,0xae,0x8e,0x8e,0x4b,0xdd,0x5c,0x99, + 0x1c,0x0a,0xdb,0xdb,0x98,0x1b,0x4c,0x0a,0x95,0xb0,0x34,0x39,0x97,0xb1,0x32,0x37, + 0xba,0x32,0x39,0x3e,0x61,0x69,0x72,0x2e,0x70,0x65,0x72,0x73,0x70,0x65,0x63,0x74, + 0x69,0x76,0x65,0x14,0xea,0xec,0x86,0x48,0xcb,0xf0,0x58,0xcf,0xf5,0x60,0x4f,0xf6, + 0x40,0x4f,0xf4,0x48,0x8f,0xc6,0xa5,0x6e,0xae,0x4c,0x0e,0x85,0xed,0x6d,0xcc,0x2d, + 0x26,0x85,0xc5,0xd8,0x1b,0xdb,0x9b,0xdc,0x10,0x69,0x11,0x1e,0xeb,0xe1,0x1e,0xec, + 0xc9,0x1e,0xe8,0x89,0x1e,0xe9,0xe9,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,0xef,0x01,0x83,0x65,0x58,0x84,0x27,0x0c,0x1e,0xe8,0x11,0x83,0x47, + 0x7a,0xc6,0x80,0x4b,0x58,0x9a,0x9c,0xcb,0x5c,0x58,0x1b,0x1c,0x5b,0x99,0x1c,0x8f, + 0xb9,0xb0,0x36,0x38,0xb6,0x32,0x39,0x0e,0x73,0x6d,0x70,0x43,0xa4,0xe5,0x78,0xca, + 0xe0,0x01,0x83,0x65,0x58,0x84,0x07,0x7a,0xcc,0xe0,0x91,0x9e,0x33,0x18,0x82,0x3c, + 0xdb,0xe3,0x3d,0x64,0xf0,0xa0,0xc1,0x10,0x03,0x01,0x9e,0xea,0x49,0x83,0x11,0x11, + 0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b,0xb4,0xc3,0x3b,0x90,0x43,0x3d,0xb0,0x43,0x39, + 0xb8,0x81,0x39,0xb0,0x43,0x38,0x9c,0xc3,0x3c,0x4c,0x11,0x82,0x61,0x84,0xc2,0x0e, + 0xec,0x60,0x0f,0xed,0xe0,0x06,0xe9,0x40,0x0e,0xe5,0xe0,0x0e,0xf4,0x30,0x25,0x28, 0x46,0x2c,0xe1,0x90,0x0e,0xf2,0xe0,0x06,0xf6,0x50,0x0e,0xf2,0x30,0x0f,0xe9,0xf0, - 0x0e,0xee,0x30,0x25,0x20,0x46,0x50,0xe1,0x90,0x0e,0xf2,0xe0,0x06,0xec,0x10,0x0e, + 0x0e,0xee,0x30,0x25,0x30,0x46,0x50,0xe1,0x90,0x0e,0xf2,0xe0,0x06,0xec,0x10,0x0e, 0xee,0x70,0x0e,0xf5,0x10,0x0e,0xe7,0x50,0x0e,0xbf,0x60,0x0f,0xe5,0x20,0x0f,0xf3, - 0x90,0x0e,0xef,0xe0,0x0e,0x53,0x02,0x63,0xc4,0x14,0x0e,0xe9,0x20,0x0f,0x6e,0x30, + 0x90,0x0e,0xef,0xe0,0x0e,0x53,0x02,0x64,0xc4,0x14,0x0e,0xe9,0x20,0x0f,0x6e,0x30, 0x0e,0xef,0xd0,0x0e,0xf0,0x90,0x0e,0xec,0x50,0x0e,0xbf,0xf0,0x0e,0xf0,0x40,0x0f, - 0xe9,0xf0,0x0e,0xee,0x30,0x0f,0x53,0x08,0x44,0x61,0x9c,0x11,0x4c,0x38,0xa4,0x83, - 0x3c,0xb8,0x81,0x39,0xc8,0x43,0x38,0x9c,0x43,0x3b,0x94,0x83,0x3b,0xd0,0xc3,0x94, - 0x40,0x0d,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x6d,0x00,0x00,0x00,0x33,0x08,0x80, + 0xe9,0xf0,0x0e,0xee,0x30,0x0f,0x53,0x06,0x85,0x71,0x46,0x30,0xe1,0x90,0x0e,0xf2, + 0xe0,0x06,0xe6,0x20,0x0f,0xe1,0x70,0x0e,0xed,0x50,0x0e,0xee,0x40,0x0f,0x53,0x02, + 0x35,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x7b,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, @@ -1200,18 +1187,22 @@ static const uint8_t _sfons_fs_bytecode_metal_ios[2877] = { 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,0x04,0xc7,0x22, - 0x80,0x40,0x20,0x88,0x8d,0x00,0x8c,0x25,0x00,0x01,0xa9,0x11,0x80,0x1a,0x20,0x31, - 0x03,0x40,0x61,0x0e,0xc2,0xb2,0x2c,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,0x70,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, + 0x79,0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72,0x98,0x81,0x5c, + 0xe3,0x10,0x0e,0xec,0xc0,0x0e,0xe5,0x50,0x0e,0xf3,0x30,0x23,0xc1,0xd2,0x41,0x1e, + 0xe4,0xe1,0x17,0xd8,0xe1,0x1d,0xde,0x01,0x1e,0x66,0x50,0x59,0x38,0xa4,0x83,0x3c, + 0xb8,0x81,0x39,0xd4,0x83,0x3b,0x8c,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x2f,0x9c, + 0x83,0x3c,0xbc,0x43,0x3d,0xc0,0xc3,0x3c,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, }; -static const char _sfons_vs_source_metal_sim[866] = { +static const char _sfons_vs_source_metal_sim[756] = { 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, @@ -1240,35 +1231,28 @@ static const char _sfons_vs_source_metal_sim[866] = { 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,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, 0x61,0x74,0x20,0x70,0x73,0x69,0x7a,0x65,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69, - 0x62,0x75,0x74,0x65,0x28,0x33,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x23, - 0x6c,0x69,0x6e,0x65,0x20,0x31,0x37,0x20,0x22,0x73,0x66,0x6f,0x6e,0x73,0x2e,0x67, - 0x6c,0x73,0x6c,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,0x31,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,0x37,0x20, - 0x22,0x73,0x66,0x6f,0x6e,0x73,0x2e,0x67,0x6c,0x73,0x6c,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,0x31,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,0x38,0x20,0x22,0x73,0x66,0x6f,0x6e,0x73,0x2e,0x67,0x6c,0x73,0x6c,0x22,0x0a, - 0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x67,0x6c,0x5f,0x50,0x6f,0x69,0x6e,0x74, - 0x53,0x69,0x7a,0x65,0x20,0x3d,0x20,0x69,0x6e,0x2e,0x70,0x73,0x69,0x7a,0x65,0x3b, - 0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31,0x39,0x20,0x22,0x73,0x66,0x6f,0x6e,0x73, - 0x2e,0x67,0x6c,0x73,0x6c,0x22,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x75, - 0x76,0x20,0x3d,0x20,0x5f,0x32,0x31,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,0x32,0x30,0x20,0x22,0x73,0x66,0x6f,0x6e,0x73,0x2e,0x67,0x6c,0x73, - 0x6c,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, + 0x62,0x75,0x74,0x65,0x28,0x33,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,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,0x31,0x39,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,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69, + 0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x5f,0x31,0x39,0x2e,0x6d,0x76,0x70,0x20,0x2a, + 0x20,0x69,0x6e,0x2e,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a,0x20,0x20, + 0x20,0x20,0x6f,0x75,0x74,0x2e,0x67,0x6c,0x5f,0x50,0x6f,0x69,0x6e,0x74,0x53,0x69, + 0x7a,0x65,0x20,0x3d,0x20,0x69,0x6e,0x2e,0x70,0x73,0x69,0x7a,0x65,0x3b,0x0a,0x20, + 0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x75,0x76,0x20,0x3d,0x20,0x5f,0x31,0x39,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,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[518] = { +static const char _sfons_fs_source_metal_sim[464] = { 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, @@ -1282,31 +1266,28 @@ static const char _sfons_fs_source_metal_sim[518] = { 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,0x73,0x66,0x6f,0x6e,0x73,0x2e,0x67,0x6c,0x73,0x6c,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,0x73,0x66,0x6f, - 0x6e,0x73,0x2e,0x67,0x6c,0x73,0x6c,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, + 0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,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,0x73,0x6d,0x70,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,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,0x73,0x6d, + 0x70,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[1032] = { - 0x44,0x58,0x42,0x43,0x09,0x96,0xbb,0xbb,0xfc,0x44,0x44,0xa8,0xa4,0x1c,0x9e,0x45, - 0x50,0x97,0xf1,0xde,0x01,0x00,0x00,0x00,0x08,0x04,0x00,0x00,0x05,0x00,0x00,0x00, + 0x44,0x58,0x42,0x43,0x74,0x7f,0x01,0xd9,0xf4,0xd5,0xed,0x1d,0x74,0xc1,0x30,0x27, + 0xd8,0xe9,0x9d,0x50,0x01,0x00,0x00,0x00,0x08,0x04,0x00,0x00,0x05,0x00,0x00,0x00, 0x34,0x00,0x00,0x00,0x14,0x01,0x00,0x00,0x90,0x01,0x00,0x00,0x00,0x02,0x00,0x00, 0x8c,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, @@ -1317,9 +1298,9 @@ static const uint8_t _sfons_vs_bytecode_hlsl4[1032] = { 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,0x31,0x5f, + 0x02,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5f,0x31,0x39,0x5f, 0x6d,0x76,0x70,0x00,0x02,0x00,0x03,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x5f,0x32,0x31,0x5f,0x74,0x6d,0x00,0x4d,0x69,0x63,0x72,0x6f, + 0x00,0x00,0x00,0x00,0x5f,0x31,0x39,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,0x74,0x00,0x00,0x00,0x04,0x00,0x00,0x00, @@ -1371,92 +1352,49 @@ static const uint8_t _sfons_vs_bytecode_hlsl4[1032] = { 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, +static const uint8_t _sfons_fs_bytecode_hlsl4[628] = { + 0x44,0x58,0x42,0x43,0xb6,0x66,0xf0,0xfc,0x09,0x54,0x2a,0x35,0x84,0x1d,0x27,0xd2, + 0xff,0xb3,0x2e,0xdb,0x01,0x00,0x00,0x00,0x74,0x02,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0xc8,0x00,0x00,0x00,0x14,0x01,0x00,0x00,0x48,0x01,0x00,0x00, + 0xf8,0x01,0x00,0x00,0x52,0x44,0x45,0x46,0x8c,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, + 0x10,0x81,0x00,0x00,0x64,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, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x60,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, + 0x01,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x73,0x6d,0x70,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,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,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,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: - - #version 450 - - layout(set = 0, binding = 0, std140) uniform vs_params - { - mat4 mvp; - mat4 tm; - } _21; - - layout(location = 0) in vec4 position; - layout(location = 3) in float psize; - 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 = _21.mvp * position; - gl_PointSize = psize; - uv = _21.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_bytecode_wgpu[1968] = { 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x08,0x00,0x08,0x00,0x35,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, @@ -1651,7 +1589,7 @@ static const uint8_t fs_bytecode_wgpu[1000] = { 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!" +#error "Please define one of SOKOL_GLCORE33, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND!" #endif typedef struct _sfons_t { @@ -1659,6 +1597,7 @@ typedef struct _sfons_t { sg_shader shd; sgl_pipeline pip; sg_image img; + sg_sampler smp; int cur_width, cur_height; bool img_dirty; } _sfons_t; @@ -1671,10 +1610,9 @@ static void _sfons_clear(void* ptr, size_t size) { static void* _sfons_malloc(const sfons_allocator_t* allocator, size_t size) { SOKOL_ASSERT(allocator && (size > 0)); void* ptr; - if (allocator->alloc) { - ptr = allocator->alloc(size, allocator->user_data); - } - else { + if (allocator->alloc_fn) { + ptr = allocator->alloc_fn(size, allocator->user_data); + } else { ptr = malloc(size); } SOKOL_ASSERT(ptr); @@ -1689,10 +1627,9 @@ static void* _sfons_malloc_clear(const sfons_allocator_t* allocator, size_t size static void _sfons_free(const sfons_allocator_t* allocator, void* ptr) { SOKOL_ASSERT(allocator); - if (allocator->free) { - allocator->free(ptr, allocator->user_data); - } - else { + if (allocator->free_fn) { + allocator->free_fn(ptr, allocator->user_data); + } else { free(ptr); } } @@ -1701,7 +1638,7 @@ static int _sfons_render_create(void* user_ptr, int width, int height) { SOKOL_ASSERT(user_ptr && (width > 8) && (height > 8)); _sfons_t* sfons = (_sfons_t*) user_ptr; - /* sokol-gl compatible shader which treats RED channel as alpha */ + // sokol-gl compatible shader which treats RED channel as alpha if (sfons->shd.id == SG_INVALID_ID) { sg_shader_desc shd_desc; _sfons_clear(&shd_desc, sizeof(shd_desc)); @@ -1722,16 +1659,22 @@ static int _sfons_render_create(void* user_ptr, int width, int height) { 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].used = true; shd_desc.fs.images[0].image_type = SG_IMAGETYPE_2D; - shd_desc.fs.images[0].sampler_type = SG_SAMPLERTYPE_FLOAT; + shd_desc.fs.images[0].sample_type = SG_IMAGESAMPLETYPE_FLOAT; + shd_desc.fs.samplers[0].used = true; + shd_desc.fs.samplers[0].sampler_type = SG_SAMPLERTYPE_SAMPLE; + shd_desc.fs.image_sampler_pairs[0].used = true; + shd_desc.fs.image_sampler_pairs[0].glsl_name = "tex_smp"; + shd_desc.fs.image_sampler_pairs[0].image_slot = 0; + shd_desc.fs.image_sampler_pairs[0].sampler_slot = 0; 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_GLES3) + shd_desc.vs.source = _sfons_vs_source_glsl300es; + shd_desc.fs.source = _sfons_fs_source_glsl300es; #elif defined(SOKOL_METAL) shd_desc.vs.entry = "main0"; shd_desc.fs.entry = "main0"; @@ -1765,7 +1708,7 @@ static int _sfons_render_create(void* user_ptr, int width, int height) { sfons->shd = sg_make_shader(&shd_desc); } - /* sokol-gl pipeline object */ + // sokol-gl pipeline object if (sfons->pip.id == SG_INVALID_ID) { sg_pipeline_desc pip_desc; _sfons_clear(&pip_desc, sizeof(pip_desc)); @@ -1776,7 +1719,17 @@ static int _sfons_render_create(void* user_ptr, int width, int height) { sfons->pip = sgl_make_pipeline(&pip_desc); } - /* create or re-create font atlas texture */ + // a sampler object + if (sfons->smp.id == SG_INVALID_ID) { + sg_sampler_desc smp_desc; + _sfons_clear(&smp_desc, sizeof(smp_desc)); + smp_desc.min_filter = SG_FILTER_LINEAR; + smp_desc.mag_filter = SG_FILTER_LINEAR; + smp_desc.mipmap_filter = SG_FILTER_NONE; + sfons->smp = sg_make_sampler(&smp_desc); + } + + // create or re-create font atlas texture if (sfons->img.id != SG_INVALID_ID) { sg_destroy_image(sfons->img); sfons->img.id = SG_INVALID_ID; @@ -1789,8 +1742,6 @@ static int _sfons_render_create(void* user_ptr, int width, int height) { _sfons_clear(&img_desc, sizeof(img_desc)); img_desc.width = sfons->cur_width; img_desc.height = sfons->cur_height; - img_desc.min_filter = SG_FILTER_LINEAR; - img_desc.mag_filter = SG_FILTER_LINEAR; img_desc.usage = SG_USAGE_DYNAMIC; img_desc.pixel_format = SG_PIXELFORMAT_R8; sfons->img = sg_make_image(&img_desc); @@ -1813,7 +1764,7 @@ static void _sfons_render_draw(void* user_ptr, const float* verts, const float* SOKOL_ASSERT(user_ptr && verts && tcoords && colors && (nverts > 0)); _sfons_t* sfons = (_sfons_t*) user_ptr; sgl_enable_texture(); - sgl_texture(sfons->img); + sgl_texture(sfons->img, sfons->smp); sgl_push_pipeline(); sgl_load_pipeline(sfons->pip); sgl_begin_triangles(); @@ -1832,6 +1783,10 @@ static void _sfons_render_delete(void* user_ptr) { sg_destroy_image(sfons->img); sfons->img.id = SG_INVALID_ID; } + if (sfons->smp.id != SG_INVALID_ID) { + sg_destroy_sampler(sfons->smp); + sfons->smp.id = SG_INVALID_ID; + } if (sfons->pip.id != SG_INVALID_ID) { sgl_destroy_pipeline(sfons->pip); sfons->pip.id = SG_INVALID_ID; @@ -1854,7 +1809,7 @@ static sfons_desc_t _sfons_desc_defaults(const sfons_desc_t* desc) { SOKOL_API_IMPL FONScontext* sfons_create(const sfons_desc_t* desc) { SOKOL_ASSERT(desc); - SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free)); + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); _sfons_t* sfons = (_sfons_t*) _sfons_malloc_clear(&desc->allocator, sizeof(_sfons_t)); sfons->desc = _sfons_desc_defaults(desc); FONSparams params; @@ -1896,4 +1851,4 @@ SOKOL_API_IMPL uint32_t sfons_rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { return ((uint32_t)r) | ((uint32_t)g<<8) | ((uint32_t)b<<16) | ((uint32_t)a<<24); } -#endif /* SOKOL_FONTSTASH_IMPL */ +#endif // SOKOL_FONTSTASH_IMPL diff --git a/thirdparty/sokol/util/sokol_gl.h b/thirdparty/sokol/util/sokol_gl.h index eeb0ad7038d922..1bc4e1c094d122 100644 --- a/thirdparty/sokol/util/sokol_gl.h +++ b/thirdparty/sokol/util/sokol_gl.h @@ -18,7 +18,6 @@ used by sokol_gfx.h and sokol_app.h): SOKOL_GLCORE33 - SOKOL_GLES2 SOKOL_GLES3 SOKOL_D3D11 SOKOL_METAL @@ -49,7 +48,7 @@ FEATURE OVERVIEW: ================= sokol_gl.h implements a subset of the OpenGLES 1.x feature set useful for - when you just want to quickly render a bunch of colored triangles or + when you just want to quickly render a bunch of triangles or lines without having to mess with buffers and shaders. The current feature set is mostly useful for debug visualizations @@ -101,14 +100,26 @@ sokol-gfx resource objects. If you're intending to render to the default pass, and also don't - want to tweak memory usage, you can just keep sgl_desc_t zero-initialized: + want to tweak memory usage, and don't want any logging output you can + just keep sgl_desc_t zero-initialized: sgl_setup(&(sgl_desc_t*){ 0 }); In this case, sokol-gl will create internal sg_pipeline objects that - are compatible with the sokol-app default framebuffer. If you want - to render into a framebuffer with different pixel-format and MSAA - attributes you need to provide the matching attributes in the + are compatible with the sokol-app default framebuffer. + + I would recommend to at least install a logging callback so that + you'll see any warnings and errors. The easiest way is through + sokol_log.h: + + #include "sokol_log.h" + + sgl_setup(&(sgl_desc_t){ + .logger.func = slog_func. + }); + + If you want to render into a framebuffer with different pixel-format + and MSAA attributes you need to provide the matching attributes in the sgl_setup() call: sgl_setup(&(sgl_desc_t*){ @@ -222,7 +233,11 @@ sgl_enable_texture() sgl_disable_texture() - sgl_texture(sg_image img) + sgl_texture(sg_image img, sg_sampler smp) + + NOTE: the img and smp handles can be invalid (SG_INVALID_ID), in this + case, sokol-gl will fall back to the internal default (white) texture + and sampler. --- set the current viewport and scissor rect with: @@ -591,8 +606,8 @@ sgl_setup(&(sgl_desc_t){ // ... .allocator = { - .alloc = my_alloc, - .free = my_free, + .alloc_fn = my_alloc, + .free_fn = my_free, .user_data = ...; } }); @@ -601,26 +616,46 @@ If no overrides are provided, malloc and free will be used. - LOG FUNCTION OVERRIDE - ===================== - You can override the log function at initialization time like this: + ERROR REPORTING AND LOGGING + =========================== + To get any logging information at all you need to provide a logging callback in the setup call, + the easiest way is to use sokol_log.h: - void my_log(const char* message, void* user_data) { - printf("sgl says: \s\n", message); + #include "sokol_log.h" + + sgl_setup(&(sgl_desc_t){ + // ... + .logger.func = slog_func + }); + + To override logging with your own callback, first write a logging function like this: + + void my_log(const char* tag, // e.g. 'sgl' + uint32_t log_level, // 0=panic, 1=error, 2=warn, 3=info + uint32_t log_item_id, // SGL_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_gl.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data) + { + ... } - ... - sgl_setup(&(sgl_desc_t){ - // ... - .logger = { - .log_cb = my_log, - .user_data = ..., - } - }); - ... + ...and then setup sokol-gl like this: + + sgl_setup(&(sgl_desc_t){ + .logger = { + .func = my_log, + .user_data = my_user_data, + } + }); - If no overrides are provided, puts will be used on most platforms. - On Android, __android_log_write will be used instead. + The provided logging function must be reentrant (e.g. be callable from + different threads). + + If you don't want to provide your own custom logger it is highly recommended to use + the standard logger in sokol_log.h instead, otherwise you won't see any warnings or + errors. LICENSE @@ -674,6 +709,47 @@ extern "C" { #endif +/* + sgl_log_item_t + + Log items are defined via X-Macros, and expanded to an + enum 'sgl_log_item' - and in debug mode only - corresponding strings. + + Used as parameter in the logging callback. +*/ +#define _SGL_LOG_ITEMS \ + _SGL_LOGITEM_XMACRO(OK, "Ok") \ + _SGL_LOGITEM_XMACRO(MALLOC_FAILED, "memory allocation failed") \ + _SGL_LOGITEM_XMACRO(MAKE_PIPELINE_FAILED, "sg_make_pipeline() failed") \ + _SGL_LOGITEM_XMACRO(PIPELINE_POOL_EXHAUSTED, "pipeline pool exhausted (use sgl_desc_t.pipeline_pool_size to adjust)") \ + _SGL_LOGITEM_XMACRO(ADD_COMMIT_LISTENER_FAILED, "sg_add_commit_listener() failed") \ + _SGL_LOGITEM_XMACRO(CONTEXT_POOL_EXHAUSTED, "context pool exhausted (use sgl_desc_t.context_pool_size to adjust)") \ + _SGL_LOGITEM_XMACRO(CANNOT_DESTROY_DEFAULT_CONTEXT, "cannot destroy default context") \ + +#define _SGL_LOGITEM_XMACRO(item,msg) SGL_LOGITEM_##item, +typedef enum sgl_log_item_t { + _SGL_LOG_ITEMS +} sgl_log_item_t; +#undef _SGL_LOGITEM_XMACRO + +/* + sgl_logger_t + + Used in sgl_desc_t to provide a custom logging and error reporting + callback to sokol-gl. +*/ +typedef struct sgl_logger_t { + void (*func)( + const char* tag, // always "sgl" + uint32_t log_level, // 0=panic, 1=error, 2=warning, 3=info + uint32_t log_item_id, // SGL_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_gl.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data); + void* user_data; +} sgl_logger_t; + /* sokol_gl pipeline handle (created with sgl_make_pipeline()) */ typedef struct sgl_pipeline { uint32_t id; } sgl_pipeline; @@ -720,22 +796,11 @@ typedef struct sgl_context_desc_t { override one function but not the other). */ typedef struct sgl_allocator_t { - void* (*alloc)(size_t size, void* user_data); - void (*free)(void* ptr, void* user_data); + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); void* user_data; } sgl_allocator_t; -/* - sgl_logger_t - - Used in sgl_desc_t to provide custom log callbacks to sokol_gl.h. - Default behavior is SOKOL_LOG(message). -*/ -typedef struct sgl_logger_t { - void (*log_cb)(const char* message, void* user_data); - void* user_data; -} sgl_logger_t; - typedef struct sgl_desc_t { int max_vertices; // default: 64k int max_commands; // default: 16k @@ -746,7 +811,7 @@ typedef struct sgl_desc_t { int sample_count; sg_face_winding face_winding; // default: SG_FACEWINDING_CCW sgl_allocator_t allocator; // optional memory allocation overrides (default: malloc/free) - sgl_logger_t logger; // optional memory allocation overrides (default: SOKOL_LOG(message)) + sgl_logger_t logger; // optional log function override (default: NO LOGGING) } sgl_desc_t; /* the default context handle */ @@ -786,7 +851,7 @@ SOKOL_GL_API_DECL void sgl_scissor_rect(int x, int y, int w, int h, bool origin_ SOKOL_GL_API_DECL void sgl_scissor_rectf(float x, float y, float w, float h, bool origin_top_left); SOKOL_GL_API_DECL void sgl_enable_texture(void); SOKOL_GL_API_DECL void sgl_disable_texture(void); -SOKOL_GL_API_DECL void sgl_texture(sg_image img); +SOKOL_GL_API_DECL void sgl_texture(sg_image img, sg_sampler smp); SOKOL_GL_API_DECL void sgl_layer(int layer_id); /* pipeline stack functions */ @@ -867,7 +932,13 @@ inline sgl_pipeline sgl_context_make_pipeline(sgl_context ctx, const sg_pipeline #endif #endif /* SOKOL_GL_INCLUDED */ -/*-- IMPLEMENTATION ----------------------------------------------------------*/ +// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████ +// +// >>implementation #ifdef SOKOL_GL_IMPL #define SOKOL_GL_IMPL_INCLUDED (1) @@ -896,28 +967,13 @@ inline sgl_pipeline sgl_context_make_pipeline(sgl_context ctx, const sg_pipeline #define SOKOL_ASSERT(c) assert(c) #endif -#if !defined(SOKOL_DEBUG) - #define SGL_LOG(s) -#else - #define SGL_LOG(s) _sgl_log(s) - #ifndef SOKOL_LOG - #if defined(__ANDROID__) - #include - #define SOKOL_LOG(s) __android_log_write(ANDROID_LOG_INFO, "SOKOL_GL", s) - #else - #include - #define SOKOL_LOG(s) puts(s) - #endif - #endif -#endif - #define _sgl_def(val, def) (((val) == 0) ? (def) : (val)) #define _SGL_INIT_COOKIE (0xABCDABCD) /* Embedded source code compiled with: - sokol-shdc -i sgl.glsl -o sgl.h -l glsl330:glsl100:hlsl4:metal_macos:metal_ios:metal_sim:wgpu -b + sokol-shdc -i sgl.glsl -o sgl.h -l glsl330:glsl300es: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) @@ -942,12 +998,13 @@ inline sgl_pipeline sgl_context_make_pipeline(sgl_context ctx, const sg_pipeline @end @fs fs - uniform sampler2D tex; + uniform texture2D tex; + uniform sampler smp; in vec4 uv; in vec4 color; out vec4 frag_color; void main() { - frag_color = texture(tex, uv.xy) * color; + frag_color = texture(sampler2D(tex, smp), uv.xy) * color; } @end @@ -987,226 +1044,233 @@ static const char _sgl_vs_source_glsl330[478] = { 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_glsl330[172] = { +static const char _sgl_fs_source_glsl330[180] = { 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[430] = { - 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,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x20,0x66,0x6c,0x6f,0x61, - 0x74,0x20,0x70,0x73,0x69,0x7a,0x65,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, + 0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,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,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,0x67,0x6c,0x5f, - 0x50,0x6f,0x69,0x6e,0x74,0x53,0x69,0x7a,0x65,0x20,0x3d,0x20,0x70,0x73,0x69,0x7a, - 0x65,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, + 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,0x5f,0x73,0x6d,0x70,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_GLES3) +static const char _sgl_vs_source_glsl300es[481] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,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,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69, + 0x6f,0x6e,0x20,0x3d,0x20,0x33,0x29,0x20,0x69,0x6e,0x20,0x66,0x6c,0x6f,0x61,0x74, + 0x20,0x70,0x73,0x69,0x7a,0x65,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, + 0x67,0x6c,0x5f,0x50,0x6f,0x69,0x6e,0x74,0x53,0x69,0x7a,0x65,0x20,0x3d,0x20,0x70, + 0x73,0x69,0x7a,0x65,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, +static const char _sgl_fs_source_glsl300es[253] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,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,0x5f,0x73,0x6d,0x70,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,0x68,0x69,0x67,0x68,0x70,0x20, + 0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b, + 0x0a,0x69,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x34,0x20,0x75, + 0x76,0x3b,0x0a,0x69,0x6e,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,0x66,0x72,0x61,0x67,0x5f, + 0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28, + 0x74,0x65,0x78,0x5f,0x73,0x6d,0x70,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[3321] = { - 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x06,0x00,0x00,0x81,0x0a,0x00,0x0b,0x00, - 0xf9,0x0c,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, - 0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x01,0x00,0x00,0x00,0x00,0x00,0x00, - 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x19,0x01,0x00,0x00,0x00,0x00,0x00,0x00, +static const uint8_t _sgl_vs_bytecode_metal_macos[3317] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xf5,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x15,0x01,0x00,0x00,0x00,0x00,0x00,0x00, 0xe0,0x0b,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,0x80,0x27,0x7a,0x67,0xc7,0xfc,0xfd, - 0xe3,0x47,0x44,0x1b,0xf3,0x40,0x18,0x72,0x5a,0xd3,0x6e,0xad,0xec,0x58,0x70,0xc5, - 0x20,0xfe,0x98,0x5e,0x82,0x7f,0xed,0xf9,0x89,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x01,0x00,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0x76,0x25,0x5f,0x37,0x22,0xd0,0x3f, + 0x64,0xef,0xff,0xc3,0x45,0x1a,0x3d,0xb7,0x5e,0x83,0x13,0x96,0xd3,0x09,0xec,0x53, + 0x25,0xd5,0x7e,0x0c,0xed,0xb9,0x58,0x34,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,0x01,0x00,0x45,0x4e,0x44,0x54,0x45,0x4e,0x44,0x54,0x40,0x00,0x00, - 0x00,0x56,0x41,0x54,0x54,0x2a,0x00,0x04,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,0x70,0x73,0x69,0x7a,0x65,0x00,0x03, - 0x80,0x56,0x41,0x54,0x59,0x06,0x00,0x04,0x00,0x06,0x04,0x06,0x03,0x45,0x4e,0x44, - 0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b,0x00,0x00,0x00, - 0x00,0x14,0x00,0x00,0x00,0xc0,0x0b,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, - 0xde,0x21,0x0c,0x00,0x00,0xed,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,0x42,0x01,0x2c,0x40,0x05,0x00,0x49,0x18,0x00,0x00,0x01,0x00,0x00, - 0x00,0x13,0x84,0x40,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,0x44,0x33,0x00,0xc3,0x08,0x04, - 0x60,0x89,0x10,0x02,0x18,0x46,0x10,0x80,0x24,0x08,0x33,0x51,0xf3,0x40,0x0f,0xf2, - 0x50,0x0f,0xe3,0x40,0x0f,0x6e,0xd0,0x0e,0xe5,0x40,0x0f,0xe1,0xc0,0x0e,0x7a,0xa0, - 0x07,0xed,0x10,0x0e,0xf4,0x20,0x0f,0xe9,0x80,0x0f,0x28,0x20,0x07,0x49,0x53,0x44, - 0x09,0x93,0x5f,0x49,0xff,0x03,0x44,0x00,0x23,0x21,0xa1,0x94,0x41,0x04,0x43,0x28, - 0x86,0x08,0x23,0x80,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,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,0x08,0x65,0x50,0x80,0x02,0x05,0x51,0x20,0xd4,0x46,0x00, - 0x88,0x8d,0x25,0x30,0x00,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x01,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,0x05,0xe3,0x20,0x08,0x0e,0x8e,0xad,0x0c, - 0xa4,0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26, - 0x06,0x06,0x26,0xc6,0x65,0x66,0x46,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac, - 0xac,0x65,0x26,0x06,0x06,0x26,0xc6,0x65,0x66,0x46,0x26,0x65,0x88,0xa0,0x10,0x43, - 0x8c,0x25,0x58,0x90,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,0x51,0x58, - 0x9a,0x9c,0x8b,0x5d,0x99,0x1c,0x5d,0x19,0xde,0xd7,0x5b,0x1d,0x1d,0x5c,0x1d,0x1d, - 0x97,0xba,0xb9,0x32,0x39,0x14,0xb6,0xb7,0x31,0x37,0x98,0x14,0x46,0x61,0x69,0x72, - 0x2e,0x61,0x72,0x67,0x5f,0x74,0x79,0x70,0x65,0x5f,0x6e,0x61,0x6d,0x65,0x34,0xcc, - 0xd8,0xde,0xc2,0xe8,0x68,0xc8,0x84,0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xb9,0x85, - 0xb5,0x95,0x51,0xa8,0xb3,0x1b,0xc2,0x28,0x8f,0x02,0x29,0x91,0x22,0x29,0x93,0x42, - 0x71,0xa9,0x9b,0x2b,0x93,0x43,0x61,0x7b,0x1b,0x73,0x8b,0x49,0x61,0x31,0xf6,0xc6, - 0xf6,0x26,0x37,0x84,0x51,0x1e,0xc5,0x52,0x22,0x45,0x52,0x26,0xe5,0x22,0x13,0x96, - 0x26,0xe7,0x02,0xf7,0x36,0x97,0x46,0x97,0xf6,0xe6,0xc6,0xe5,0x8c,0xed,0x0b,0xea, - 0x6d,0x2e,0x8d,0x2e,0xed,0xcd,0x6d,0x88,0xa2,0x64,0x4a,0xa4,0x48,0xca,0xa4,0x68, - 0x74,0xc2,0xd2,0xe4,0x5c,0xe0,0xde,0xd2,0xdc,0xe8,0xbe,0xe6,0xd2,0xf4,0xca,0x58, - 0x98,0xb1,0xbd,0x85,0xd1,0x91,0x39,0x63,0xfb,0x82,0x7a,0x4b,0x73,0xa3,0x9b,0x4a, - 0xd3,0x2b,0x1b,0xa2,0x28,0x9c,0x12,0x29,0x9d,0x32,0x29,0xde,0x10,0x44,0xa9,0x14, - 0x4c,0xd9,0x94,0x8f,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,0x7a,0x67,0x65,0x6e,0x65,0x72,0x61,0x74, - 0x65,0x64,0x28,0x5f,0x5f,0x61,0x69,0x72,0x5f,0x70,0x6c,0x61,0x63,0x65,0x68,0x6f, - 0x6c,0x64,0x65,0x72,0x5f,0x5f,0x29,0x44,0xe0,0xde,0xe6,0xd2,0xe8,0xd2,0xde,0xdc, - 0x86,0x50,0x8b,0xa0,0x84,0x81,0x22,0x06,0x8b,0xb0,0x04,0xca,0x18,0x28,0x91,0x22, - 0x29,0x93,0x42,0x06,0x34,0xcc,0xd8,0xde,0xc2,0xe8,0x64,0x98,0xd0,0x95,0xe1,0x8d, - 0xbd,0xbd,0xc9,0x91,0xc1,0x0c,0xa1,0x96,0x40,0x09,0x03,0x45,0x0c,0x96,0x60,0x09, - 0x94,0x31,0x50,0x22,0xc5,0x0c,0x94,0x49,0x39,0x03,0x1a,0x63,0x6f,0x6c,0x6f,0x72, - 0x30,0x43,0xa8,0x65,0x50,0xc2,0x40,0x11,0x83,0x65,0x58,0x02,0x65,0x0c,0x94,0x48, - 0x91,0x94,0x49,0x49,0x03,0x16,0x70,0x73,0x69,0x7a,0x65,0x43,0xa8,0xc5,0x50,0xc2, - 0x40,0x11,0x83,0xc5,0x58,0x02,0x65,0x0c,0x94,0x48,0xe9,0x94,0x49,0x59,0x03,0x2a, - 0x61,0x69,0x72,0x2e,0x62,0x75,0x66,0x66,0x65,0x72,0x7c,0xc2,0xd2,0xe4,0x5c,0xc4, - 0xea,0xcc,0xcc,0xca,0xe4,0xbe,0xe6,0xd2,0xf4,0xca,0x88,0x84,0xa5,0xc9,0xb9,0xc8, - 0x95,0x85,0x91,0x91,0x0a,0x4b,0x93,0x73,0x99,0xa3,0x93,0xab,0x1b,0xa3,0xfb,0xa2, - 0xcb,0x83,0x2b,0xfb,0x4a,0x73,0x33,0x7b,0x23,0x62,0xc6,0xf6,0x16,0x46,0x47,0x83, - 0x47,0xc3,0xa1,0xcd,0x0e,0x8e,0x02,0x5d,0xdb,0x10,0x6a,0x11,0x16,0x62,0x11,0x94, - 0x38,0x50,0xe4,0x60,0x21,0x16,0x62,0x11,0x94,0x38,0x50,0xe6,0x80,0x51,0x58,0x9a, - 0x9c,0x4b,0x98,0xdc,0xd9,0x17,0x5d,0x1e,0x5c,0xd9,0xd7,0x5c,0x9a,0x5e,0x19,0xaf, - 0xb0,0x34,0x39,0x97,0x30,0xb9,0xb3,0x2f,0xba,0x3c,0xb8,0xb2,0xaf,0x30,0xb6,0xb4, - 0x33,0xb7,0xaf,0xb9,0x34,0xbd,0x32,0x26,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, - 0x73,0x1c,0xbe,0x64,0x62,0x86,0x90,0xc1,0x52,0x28,0x6d,0xa0,0xb8,0xc1,0x72,0x28, - 0x62,0xb0,0x08,0x4b,0xa0,0xbc,0x81,0x02,0x07,0x0a,0x1d,0x28,0x75,0xb0,0x1c,0x8a, - 0x1d,0x2c,0x89,0x12,0x29,0x77,0xa0,0x4c,0x0a,0x1e,0x0c,0x51,0x94,0x32,0x50,0xd0, - 0x40,0x51,0x03,0x85,0x0d,0x94,0x3c,0x18,0x62,0x24,0x80,0x02,0x06,0x8a,0x1e,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,0xfa,0x60,0x88,0xa1,0xf0,0x81,0xe2,0x07,0x8d,0x32,0xc4,0x50,0xfe,0x40, - 0xf9,0x83,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,0x65,0x50,0x18,0x67, - 0x84,0x12,0x0e,0xe9,0x20,0x0f,0x6e,0x60,0x0f,0xe5,0x20,0x0f,0xf4,0x50,0x0e,0xf8, - 0x30,0x25,0xd8,0x03,0x00,0x79,0x18,0x00,0x00,0x7b,0x00,0x00,0x00,0x33,0x08,0x80, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x40,0x00,0x00,0x00,0x56,0x41,0x54, + 0x54,0x2a,0x00,0x04,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,0x70,0x73,0x69,0x7a,0x65,0x00,0x03,0x80,0x56,0x41,0x54, + 0x59,0x06,0x00,0x04,0x00,0x06,0x04,0x06,0x03,0x45,0x4e,0x44,0x54,0x04,0x00,0x00, + 0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00, + 0x00,0xc4,0x0b,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00, + 0x00,0xee,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,0x42, + 0x01,0x2c,0x40,0x05,0x00,0x49,0x18,0x00,0x00,0x01,0x00,0x00,0x00,0x13,0x84,0x40, + 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,0x44,0x33,0x00,0xc3,0x08,0x04,0x60,0x89,0x10,0x02, + 0x18,0x46,0x10,0x80,0x24,0x08,0x33,0x51,0xf3,0x40,0x0f,0xf2,0x50,0x0f,0xe3,0x40, + 0x0f,0x6e,0xd0,0x0e,0xe5,0x40,0x0f,0xe1,0xc0,0x0e,0x7a,0xa0,0x07,0xed,0x10,0x0e, + 0xf4,0x20,0x0f,0xe9,0x80,0x0f,0x28,0x20,0x07,0x49,0x53,0x44,0x09,0x93,0x5f,0x49, + 0xff,0x03,0x44,0x00,0x23,0x21,0xa1,0x94,0x41,0x04,0x43,0x28,0x86,0x08,0x23,0x80, + 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,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,0x08,0x65,0x50,0x80,0x02,0x05,0x51,0x20,0xd4,0x46,0x00,0x88,0x8d,0x25,0x34, + 0x01,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x02,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,0x05,0xe7,0x20,0x08,0x0e,0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e, + 0x8c,0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x06,0x06,0x26,0xc6, + 0xc5,0xc6,0xe6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x06, + 0x06,0x26,0xc6,0xc5,0xc6,0xe6,0xc6,0x45,0x26,0x65,0x88,0xa0,0x10,0x43,0x8c,0x25, + 0x58,0x90,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,0x51,0x58,0x9a,0x9c, + 0x8b,0x5d,0x99,0x1c,0x5d,0x19,0xde,0xd7,0x5b,0x1d,0x1d,0x5c,0x1d,0x1d,0x97,0xba, + 0xb9,0x32,0x39,0x14,0xb6,0xb7,0x31,0x37,0x98,0x14,0x46,0x61,0x69,0x72,0x2e,0x61, + 0x72,0x67,0x5f,0x74,0x79,0x70,0x65,0x5f,0x6e,0x61,0x6d,0x65,0x34,0xcc,0xd8,0xde, + 0xc2,0xe8,0x68,0xc8,0x84,0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xb9,0x85,0xb5,0x95, + 0x51,0xa8,0xb3,0x1b,0xc2,0x28,0x8f,0x02,0x29,0x91,0x22,0x29,0x93,0x42,0x71,0xa9, + 0x9b,0x2b,0x93,0x43,0x61,0x7b,0x1b,0x73,0x8b,0x49,0x61,0x31,0xf6,0xc6,0xf6,0x26, + 0x37,0x84,0x51,0x1e,0xc5,0x52,0x22,0x45,0x52,0x26,0xe5,0x22,0x13,0x96,0x26,0xe7, + 0x02,0xf7,0x36,0x97,0x46,0x97,0xf6,0xe6,0xc6,0xe5,0x8c,0xed,0x0b,0xea,0x6d,0x2e, + 0x8d,0x2e,0xed,0xcd,0x6d,0x88,0xa2,0x64,0x4a,0xa4,0x48,0xca,0xa4,0x68,0x74,0xc2, + 0xd2,0xe4,0x5c,0xe0,0xde,0xd2,0xdc,0xe8,0xbe,0xe6,0xd2,0xf4,0xca,0x58,0x98,0xb1, + 0xbd,0x85,0xd1,0x91,0x39,0x63,0xfb,0x82,0x7a,0x4b,0x73,0xa3,0x9b,0x4a,0xd3,0x2b, + 0x1b,0xa2,0x28,0x9c,0x12,0x29,0x9d,0x32,0x29,0xde,0x10,0x44,0xa9,0x14,0x4c,0xd9, + 0x94,0x8f,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,0x7a,0x67,0x65,0x6e,0x65,0x72,0x61,0x74,0x65,0x64, + 0x28,0x5f,0x5f,0x61,0x69,0x72,0x5f,0x70,0x6c,0x61,0x63,0x65,0x68,0x6f,0x6c,0x64, + 0x65,0x72,0x5f,0x5f,0x29,0x44,0xe0,0xde,0xe6,0xd2,0xe8,0xd2,0xde,0xdc,0x86,0x50, + 0x8b,0xa0,0x84,0x81,0x22,0x06,0x8b,0xb0,0x04,0xca,0x18,0x28,0x91,0x22,0x29,0x93, + 0x42,0x06,0x34,0xcc,0xd8,0xde,0xc2,0xe8,0x64,0x98,0xd0,0x95,0xe1,0x8d,0xbd,0xbd, + 0xc9,0x91,0xc1,0x0c,0xa1,0x96,0x40,0x09,0x03,0x45,0x0c,0x96,0x60,0x09,0x94,0x31, + 0x50,0x22,0xc5,0x0c,0x94,0x49,0x39,0x03,0x1a,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x43, + 0xa8,0x65,0x50,0xc2,0x40,0x11,0x83,0x65,0x58,0x02,0x65,0x0c,0x94,0x48,0x91,0x94, + 0x49,0x49,0x03,0x16,0x70,0x73,0x69,0x7a,0x65,0x43,0xa8,0xc5,0x50,0xc2,0x40,0x11, + 0x83,0xc5,0x58,0x02,0x65,0x0c,0x94,0x48,0xe9,0x94,0x49,0x59,0x03,0x2a,0x61,0x69, + 0x72,0x2e,0x62,0x75,0x66,0x66,0x65,0x72,0x7c,0xc2,0xd2,0xe4,0x5c,0xc4,0xea,0xcc, + 0xcc,0xca,0xe4,0xbe,0xe6,0xd2,0xf4,0xca,0x88,0x84,0xa5,0xc9,0xb9,0xc8,0x95,0x85, + 0x91,0x91,0x0a,0x4b,0x93,0x73,0x99,0xa3,0x93,0xab,0x1b,0xa3,0xfb,0xa2,0xcb,0x83, + 0x2b,0xfb,0x4a,0x73,0x33,0x7b,0x23,0x62,0xc6,0xf6,0x16,0x46,0x47,0x83,0x47,0xc3, + 0xa1,0xcd,0x0e,0x8e,0x02,0x5d,0xdb,0x10,0x6a,0x11,0x16,0x62,0x11,0x94,0x38,0x50, + 0xe4,0x60,0x21,0x16,0x62,0x11,0x94,0x38,0x50,0xe6,0x80,0x51,0x58,0x9a,0x9c,0x4b, + 0x98,0xdc,0xd9,0x17,0x5d,0x1e,0x5c,0xd9,0xd7,0x5c,0x9a,0x5e,0x19,0xaf,0xb0,0x34, + 0x39,0x97,0x30,0xb9,0xb3,0x2f,0xba,0x3c,0xb8,0xb2,0xaf,0x30,0xb6,0xb4,0x33,0xb7, + 0xaf,0xb9,0x34,0xbd,0x32,0x26,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x1c, + 0xbe,0x62,0x72,0x86,0x90,0xc1,0x52,0x28,0x6d,0xa0,0xb8,0xc1,0x72,0x28,0x62,0xb0, + 0x08,0x4b,0xa0,0xbc,0x81,0x02,0x07,0x0a,0x1d,0x28,0x75,0xb0,0x1c,0x8a,0x1d,0x2c, + 0x89,0x12,0x29,0x77,0xa0,0x4c,0x0a,0x1e,0x0c,0x51,0x94,0x32,0x50,0xd0,0x40,0x51, + 0x03,0x85,0x0d,0x94,0x3c,0x18,0x62,0x24,0x80,0x02,0x06,0x8a,0x1e,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, + 0xfa,0x60,0x88,0xa1,0xf0,0x81,0xe2,0x07,0x8d,0x32,0xc4,0x50,0xfe,0x40,0xf9,0x83, + 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,0x65,0x50,0x18,0x67,0x84,0x12, + 0x0e,0xe9,0x20,0x0f,0x6e,0x60,0x0f,0xe5,0x20,0x0f,0xf4,0x50,0x0e,0xf8,0x30,0x25, + 0xd8,0x03,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x7b,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, @@ -1255,147 +1319,346 @@ static const uint8_t _sgl_vs_bytecode_metal_macos[3321] = { 0x00,0x5b,0x86,0x20,0x00,0x85,0x2d,0x43,0x11,0x80,0xc2,0x96,0x41,0x09,0x40,0x61, 0xcb,0xf0,0x04,0xa0,0xb0,0x65,0xa0,0x02,0x50,0xd8,0x32,0x60,0x01,0x28,0x6c,0x19, 0xba,0x00,0x14,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_metal_macos[2829] = { - 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x06,0x00,0x00,0x81,0x0a,0x00,0x0b,0x00, - 0x0d,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, +static const uint8_t _sgl_fs_bytecode_metal_macos[2825] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x09,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd1,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x30,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,0xeb,0xd5,0xe5,0x1e,0x0a,0x10,0x1a, - 0x32,0xd3,0xbd,0xd9,0xe3,0x31,0x39,0x1e,0x13,0x0d,0xe0,0xb8,0x26,0xfa,0x4f,0x40, - 0xeb,0xf7,0x82,0x75,0x70,0x8a,0xc9,0x08,0x0c,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x01,0x00,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0xb9,0x65,0x80,0x88,0xa2,0xc8,0x18, + 0x8d,0x2a,0x38,0xc1,0x87,0xa4,0x7f,0x35,0x83,0x69,0xdc,0x8c,0xcb,0x7b,0x11,0x67, + 0x89,0xc7,0xf0,0x8a,0x99,0x36,0x06,0xc5,0x90,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,0x14,0x0a,0x00,0x00,0xff,0xff,0xff, - 0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0x82,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,0x1d,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,0x60,0x83,0x70,0x94, - 0x34,0x45,0x94,0x30,0xf9,0xff,0x44,0x5c,0x13,0x15,0x11,0xbf,0x3d,0xfc,0xd3,0x18, - 0x01,0x30,0x88,0x30,0x04,0x17,0x49,0x53,0x44,0x09,0x93,0xff,0x4b,0x00,0xf3,0x2c, - 0x44,0xf4,0x4f,0x63,0x04,0xc0,0x20,0x42,0x21,0x94,0x42,0x84,0x40,0x0c,0x9d,0x61, - 0x04,0x01,0x98,0x23,0x08,0xe6,0x08,0xc0,0x60,0x18,0x41,0x58,0x0a,0x12,0x88,0x49, - 0x8a,0x29,0x40,0x6d,0x20,0x20,0x05,0xd6,0x08,0x00,0x00,0x00,0x00,0x13,0xb2,0x70, + 0x00,0x01,0x00,0x01,0x00,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,0x10,0x0a,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0x81,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,0x1d,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,0x60,0x83,0x70,0x94,0x34,0x45,0x94,0x30, + 0xf9,0xff,0x44,0x5c,0x13,0x15,0x11,0xbf,0x3d,0xfc,0xd3,0x18,0x01,0x30,0x88,0x30, + 0x04,0x17,0x49,0x53,0x44,0x09,0x93,0xff,0x4b,0x00,0xf3,0x2c,0x44,0xf4,0x4f,0x63, + 0x04,0xc0,0x20,0x42,0x21,0x94,0x42,0x84,0x40,0x0c,0x9d,0x61,0x04,0x01,0x98,0x23, + 0x08,0xe6,0x08,0xc0,0x60,0x18,0x41,0x58,0x0a,0x12,0x88,0x49,0x8a,0x29,0x40,0x6d, + 0x20,0x20,0x05,0xd6,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,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, + 0x2c,0xa1,0x09,0x00,0x00,0x79,0x18,0x00,0x00,0xb7,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,0xe7,0x20,0x08,0x0e,0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e, + 0x8c,0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x06,0x06,0x26,0xc6, + 0xc5,0xc6,0xe6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x06, + 0x06,0x26,0xc6,0xc5,0xc6,0xe6,0xc6,0x45,0x26,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,0x51,0x58,0x9a,0x9c, + 0x8b,0x5c,0x99,0x1b,0x59,0x99,0xdc,0x17,0x5d,0x98,0xdc,0x59,0x19,0x1d,0xa3,0xb0, + 0x34,0x39,0x97,0x30,0xb9,0xb3,0x2f,0xba,0x3c,0xb8,0xb2,0x2f,0xb7,0xb0,0xb6,0x32, + 0x1a,0x66,0x6c,0x6f,0x61,0x74,0x34,0x64,0xc2,0xd2,0xe4,0x5c,0xc2,0xe4,0xce,0xbe, + 0xdc,0xc2,0xda,0xca,0xa8,0x98,0xc9,0x85,0x9d,0x7d,0x8d,0xbd,0xb1,0xbd,0xc9,0x0d, + 0x61,0x9e,0x67,0x19,0x1e,0xe8,0x89,0x1e,0xe9,0x99,0x86,0x08,0x0f,0x45,0x29,0x2c, + 0x4d,0xce,0xc5,0x4c,0x2e,0xec,0xac,0xad,0xcc,0x8d,0xee,0x2b,0xcd,0x0d,0xae,0x8e, + 0x8e,0x4b,0xdd,0x5c,0x99,0x1c,0x0a,0xdb,0xdb,0x98,0x1b,0x4c,0x0a,0x95,0xb0,0x34, + 0x39,0x97,0xb1,0x32,0x37,0xba,0x32,0x39,0x3e,0x61,0x69,0x72,0x2e,0x70,0x65,0x72, + 0x73,0x70,0x65,0x63,0x74,0x69,0x76,0x65,0x14,0xea,0xec,0x86,0x48,0xcb,0xf0,0x58, + 0xcf,0xf5,0x60,0x4f,0xf6,0x40,0x4f,0xf4,0x48,0x8f,0xc6,0xa5,0x6e,0xae,0x4c,0x0e, + 0x85,0xed,0x6d,0xcc,0x2d,0x26,0x85,0xc5,0xd8,0x1b,0xdb,0x9b,0xdc,0x10,0x69,0x11, + 0x1e,0xeb,0xe1,0x1e,0xec,0xc9,0x1e,0xe8,0x89,0x1e,0xe9,0xe9,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,0xef,0x01,0x83,0x65,0x58,0x84,0x27,0x0c, + 0x1e,0xe8,0x11,0x83,0x47,0x7a,0xc6,0x80,0x4b,0x58,0x9a,0x9c,0xcb,0x5c,0x58,0x1b, + 0x1c,0x5b,0x99,0x1c,0x8f,0xb9,0xb0,0x36,0x38,0xb6,0x32,0x39,0x0e,0x73,0x6d,0x70, + 0x43,0xa4,0xe5,0x78,0xca,0xe0,0x01,0x83,0x65,0x58,0x84,0x07,0x7a,0xcc,0xe0,0x91, + 0x9e,0x33,0x18,0x82,0x3c,0xdb,0xe3,0x3d,0x64,0xf0,0xa0,0xc1,0x10,0x03,0x01,0x9e, + 0xea,0x49,0x83,0x11,0x11,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b,0xb4,0xc3,0x3b,0x90, + 0x43,0x3d,0xb0,0x43,0x39,0xb8,0x81,0x39,0xb0,0x43,0x38,0x9c,0xc3,0x3c,0x4c,0x11, + 0x82,0x61,0x84,0xc2,0x0e,0xec,0x60,0x0f,0xed,0xe0,0x06,0xe9,0x40,0x0e,0xe5,0xe0, + 0x0e,0xf4,0x30,0x25,0x28,0x46,0x2c,0xe1,0x90,0x0e,0xf2,0xe0,0x06,0xf6,0x50,0x0e, + 0xf2,0x30,0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x25,0x30,0x46,0x50,0xe1,0x90,0x0e,0xf2, + 0xe0,0x06,0xec,0x10,0x0e,0xee,0x70,0x0e,0xf5,0x10,0x0e,0xe7,0x50,0x0e,0xbf,0x60, + 0x0f,0xe5,0x20,0x0f,0xf3,0x90,0x0e,0xef,0xe0,0x0e,0x53,0x02,0x64,0xc4,0x14,0x0e, + 0xe9,0x20,0x0f,0x6e,0x30,0x0e,0xef,0xd0,0x0e,0xf0,0x90,0x0e,0xec,0x50,0x0e,0xbf, + 0xf0,0x0e,0xf0,0x40,0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x0f,0x53,0x06,0x85,0x71,0x46, + 0x30,0xe1,0x90,0x0e,0xf2,0xe0,0x06,0xe6,0x20,0x0f,0xe1,0x70,0x0e,0xed,0x50,0x0e, + 0xee,0x40,0x0f,0x53,0x02,0x35,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x7b,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,0x98,0x81,0x5c,0xe3,0x10,0x0e,0xec,0xc0,0x0e,0xe5,0x50,0x0e,0xf3,0x30,0x23, + 0xc1,0xd2,0x41,0x1e,0xe4,0xe1,0x17,0xd8,0xe1,0x1d,0xde,0x01,0x1e,0x66,0x50,0x59, + 0x38,0xa4,0x83,0x3c,0xb8,0x81,0x39,0xd4,0x83,0x3b,0x8c,0x03,0x3d,0xa4,0xc3,0x3b, + 0xb8,0xc3,0x2f,0x9c,0x83,0x3c,0xbc,0x43,0x3d,0xc0,0xc3,0x3c,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,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sgl_vs_bytecode_metal_ios[3317] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xf5,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x15,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0xe0,0x0b,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,0x17,0x11,0x57,0x16,0x94,0x42,0x52, + 0xfb,0x1e,0xd0,0x32,0xfd,0x87,0x16,0xb0,0xa4,0xd0,0xc2,0x43,0xbe,0x93,0x8c,0xe0, + 0x2d,0x7a,0x5c,0x3e,0x06,0x4c,0x57,0xeb,0x4b,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,0x40,0x00,0x00,0x00,0x56,0x41,0x54, + 0x54,0x2a,0x00,0x04,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,0x70,0x73,0x69,0x7a,0x65,0x00,0x03,0x80,0x56,0x41,0x54, + 0x59,0x06,0x00,0x04,0x00,0x06,0x04,0x06,0x03,0x45,0x4e,0x44,0x54,0x04,0x00,0x00, + 0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00, + 0x00,0xc0,0x0b,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00, + 0x00,0xed,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,0x42, + 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,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,0x44,0x33,0x00,0xc3,0x08,0x04, + 0x60,0x89,0x10,0x02,0x18,0x46,0x10,0x80,0x24,0x08,0x33,0x51,0xf3,0x40,0x0f,0xf2, + 0x50,0x0f,0xe3,0x40,0x0f,0x6e,0xd0,0x0e,0xe5,0x40,0x0f,0xe1,0xc0,0x0e,0x7a,0xa0, + 0x07,0xed,0x10,0x0e,0xf4,0x20,0x0f,0xe9,0x80,0x0f,0x28,0x20,0x07,0x49,0x53,0x44, + 0x09,0x93,0x5f,0x49,0xff,0x03,0x44,0x00,0x23,0x21,0xa1,0x94,0x41,0x04,0x43,0x28, + 0x86,0x08,0x23,0x80,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,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,0x2c,0x81,0x01,0x00,0x00,0x79,0x18,0x00,0x00,0xb8,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,0xe3,0x20,0x08,0x0e,0x8e,0xad,0x0c, - 0xa4,0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26, - 0x06,0x06,0x26,0xc6,0x65,0x66,0x46,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac, - 0xac,0x65,0x26,0x06,0x06,0x26,0xc6,0x65,0x66,0x46,0x26,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,0x51,0x58, - 0x9a,0x9c,0x8b,0x5c,0x99,0x1b,0x59,0x99,0xdc,0x17,0x5d,0x98,0xdc,0x59,0x19,0x1d, - 0xa3,0xb0,0x34,0x39,0x97,0x30,0xb9,0xb3,0x2f,0xba,0x3c,0xb8,0xb2,0x2f,0xb7,0xb0, - 0xb6,0x32,0x1a,0x66,0x6c,0x6f,0x61,0x74,0x34,0x64,0xc2,0xd2,0xe4,0x5c,0xc2,0xe4, - 0xce,0xbe,0xdc,0xc2,0xda,0xca,0xa8,0x98,0xc9,0x85,0x9d,0x7d,0x8d,0xbd,0xb1,0xbd, - 0xc9,0x0d,0x61,0x9e,0x67,0x19,0x1e,0xe8,0x89,0x1e,0xe9,0x99,0x86,0x08,0x0f,0x45, - 0x29,0x2c,0x4d,0xce,0xc5,0x4c,0x2e,0xec,0xac,0xad,0xcc,0x8d,0xee,0x2b,0xcd,0x0d, - 0xae,0x8e,0x8e,0x4b,0xdd,0x5c,0x99,0x1c,0x0a,0xdb,0xdb,0x98,0x1b,0x4c,0x0a,0x95, - 0xb0,0x34,0x39,0x97,0xb1,0x32,0x37,0xba,0x32,0x39,0x3e,0x61,0x69,0x72,0x2e,0x70, - 0x65,0x72,0x73,0x70,0x65,0x63,0x74,0x69,0x76,0x65,0x14,0xea,0xec,0x86,0x48,0xcb, - 0xf0,0x58,0xcf,0xf5,0x60,0x4f,0xf6,0x40,0x4f,0xf4,0x48,0x8f,0xc6,0xa5,0x6e,0xae, - 0x4c,0x0e,0x85,0xed,0x6d,0xcc,0x2d,0x26,0x85,0xc5,0xd8,0x1b,0xdb,0x9b,0xdc,0x10, - 0x69,0x11,0x1e,0xeb,0xe1,0x1e,0xec,0xc9,0x1e,0xe8,0x89,0x1e,0xe9,0xe9,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,0xef,0x01,0x83,0x65,0x58,0x84, - 0x27,0x0c,0x1e,0xe8,0x11,0x83,0x47,0x7a,0xc6,0x80,0x4b,0x58,0x9a,0x9c,0xcb,0x5c, - 0x58,0x1b,0x1c,0x5b,0x99,0x1c,0x8f,0xb9,0xb0,0x36,0x38,0xb6,0x32,0x39,0x22,0x74, - 0x65,0x78,0x53,0x6d,0x70,0x6c,0x72,0x43,0xa4,0xe5,0x78,0xca,0xe0,0x01,0x83,0x65, - 0x58,0x84,0x07,0x7a,0xcc,0xe0,0x91,0x9e,0x33,0x18,0x82,0x3c,0xdb,0xe3,0x3d,0x64, - 0xf0,0xa0,0xc1,0x10,0x03,0x01,0x9e,0xea,0x49,0x83,0x11,0x11,0x3b,0xb0,0x83,0x3d, - 0xb4,0x83,0x1b,0xb4,0xc3,0x3b,0x90,0x43,0x3d,0xb0,0x43,0x39,0xb8,0x81,0x39,0xb0, - 0x43,0x38,0x9c,0xc3,0x3c,0x4c,0x11,0x82,0x61,0x84,0xc2,0x0e,0xec,0x60,0x0f,0xed, - 0xe0,0x06,0xe9,0x40,0x0e,0xe5,0xe0,0x0e,0xf4,0x30,0x25,0x28,0x46,0x2c,0xe1,0x90, - 0x0e,0xf2,0xe0,0x06,0xf6,0x50,0x0e,0xf2,0x30,0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x25, - 0x30,0x46,0x50,0xe1,0x90,0x0e,0xf2,0xe0,0x06,0xec,0x10,0x0e,0xee,0x70,0x0e,0xf5, - 0x10,0x0e,0xe7,0x50,0x0e,0xbf,0x60,0x0f,0xe5,0x20,0x0f,0xf3,0x90,0x0e,0xef,0xe0, - 0x0e,0x53,0x02,0x64,0xc4,0x14,0x0e,0xe9,0x20,0x0f,0x6e,0x30,0x0e,0xef,0xd0,0x0e, - 0xf0,0x90,0x0e,0xec,0x50,0x0e,0xbf,0xf0,0x0e,0xf0,0x40,0x0f,0xe9,0xf0,0x0e,0xee, - 0x30,0x0f,0x53,0x06,0x85,0x71,0x46,0x30,0xe1,0x90,0x0e,0xf2,0xe0,0x06,0xe6,0x20, - 0x0f,0xe1,0x70,0x0e,0xed,0x50,0x0e,0xee,0x40,0x0f,0x53,0x02,0x35,0x00,0x00,0x00, + 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,0x08,0x65,0x50, + 0x80,0x02,0x05,0x51,0x20,0xd4,0x46,0x00,0x88,0x8d,0x25,0x40,0x02,0x00,0x00,0x00, + 0x00,0x79,0x18,0x00,0x00,0x02,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, + 0x05,0xe7,0x20,0x08,0x0e,0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae, + 0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x06,0x06,0x26,0xc6,0xc5,0xc6,0xe6,0x06, + 0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x06,0x06,0x26,0xc6,0xc5, + 0xc6,0xe6,0xc6,0x45,0x26,0x65,0x88,0xa0,0x10,0x43,0x8c,0x25,0x58,0x90,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,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,0x51,0x58,0x9a,0x9c,0x8b,0x5d,0x99,0x1c,0x5d, + 0x19,0xde,0xd7,0x5b,0x1d,0x1d,0x5c,0x1d,0x1d,0x97,0xba,0xb9,0x32,0x39,0x14,0xb6, + 0xb7,0x31,0x37,0x98,0x14,0x46,0x61,0x69,0x72,0x2e,0x61,0x72,0x67,0x5f,0x74,0x79, + 0x70,0x65,0x5f,0x6e,0x61,0x6d,0x65,0x34,0xcc,0xd8,0xde,0xc2,0xe8,0x68,0xc8,0x84, + 0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xb9,0x85,0xb5,0x95,0x51,0xa8,0xb3,0x1b,0xc2, + 0x28,0x8f,0x02,0x29,0x91,0x22,0x29,0x93,0x42,0x71,0xa9,0x9b,0x2b,0x93,0x43,0x61, + 0x7b,0x1b,0x73,0x8b,0x49,0x61,0x31,0xf6,0xc6,0xf6,0x26,0x37,0x84,0x51,0x1e,0xc5, + 0x52,0x22,0x45,0x52,0x26,0xe5,0x22,0x13,0x96,0x26,0xe7,0x02,0xf7,0x36,0x97,0x46, + 0x97,0xf6,0xe6,0xc6,0xe5,0x8c,0xed,0x0b,0xea,0x6d,0x2e,0x8d,0x2e,0xed,0xcd,0x6d, + 0x88,0xa2,0x64,0x4a,0xa4,0x48,0xca,0xa4,0x68,0x74,0xc2,0xd2,0xe4,0x5c,0xe0,0xde, + 0xd2,0xdc,0xe8,0xbe,0xe6,0xd2,0xf4,0xca,0x58,0x98,0xb1,0xbd,0x85,0xd1,0x91,0x39, + 0x63,0xfb,0x82,0x7a,0x4b,0x73,0xa3,0x9b,0x4a,0xd3,0x2b,0x1b,0xa2,0x28,0x9c,0x12, + 0x29,0x9d,0x32,0x29,0xde,0x10,0x44,0xa9,0x14,0x4c,0xd9,0x94,0x8f,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,0x7a,0x67,0x65,0x6e,0x65,0x72,0x61,0x74,0x65,0x64,0x28,0x5f,0x5f,0x61,0x69, + 0x72,0x5f,0x70,0x6c,0x61,0x63,0x65,0x68,0x6f,0x6c,0x64,0x65,0x72,0x5f,0x5f,0x29, + 0x44,0xe0,0xde,0xe6,0xd2,0xe8,0xd2,0xde,0xdc,0x86,0x50,0x8b,0xa0,0x84,0x81,0x22, + 0x06,0x8b,0xb0,0x04,0xca,0x18,0x28,0x91,0x22,0x29,0x93,0x42,0x06,0x34,0xcc,0xd8, + 0xde,0xc2,0xe8,0x64,0x98,0xd0,0x95,0xe1,0x8d,0xbd,0xbd,0xc9,0x91,0xc1,0x0c,0xa1, + 0x96,0x40,0x09,0x03,0x45,0x0c,0x96,0x60,0x09,0x94,0x31,0x50,0x22,0xc5,0x0c,0x94, + 0x49,0x39,0x03,0x1a,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x43,0xa8,0x65,0x50,0xc2,0x40, + 0x11,0x83,0x65,0x58,0x02,0x65,0x0c,0x94,0x48,0x91,0x94,0x49,0x49,0x03,0x16,0x70, + 0x73,0x69,0x7a,0x65,0x43,0xa8,0xc5,0x50,0xc2,0x40,0x11,0x83,0xc5,0x58,0x02,0x65, + 0x0c,0x94,0x48,0xe9,0x94,0x49,0x59,0x03,0x2a,0x61,0x69,0x72,0x2e,0x62,0x75,0x66, + 0x66,0x65,0x72,0x7c,0xc2,0xd2,0xe4,0x5c,0xc4,0xea,0xcc,0xcc,0xca,0xe4,0xbe,0xe6, + 0xd2,0xf4,0xca,0x88,0x84,0xa5,0xc9,0xb9,0xc8,0x95,0x85,0x91,0x91,0x0a,0x4b,0x93, + 0x73,0x99,0xa3,0x93,0xab,0x1b,0xa3,0xfb,0xa2,0xcb,0x83,0x2b,0xfb,0x4a,0x73,0x33, + 0x7b,0x23,0x62,0xc6,0xf6,0x16,0x46,0x47,0x83,0x47,0xc3,0xa1,0xcd,0x0e,0x8e,0x02, + 0x5d,0xdb,0x10,0x6a,0x11,0x16,0x62,0x11,0x94,0x38,0x50,0xe4,0x60,0x21,0x16,0x62, + 0x11,0x94,0x38,0x50,0xe6,0x80,0x51,0x58,0x9a,0x9c,0x4b,0x98,0xdc,0xd9,0x17,0x5d, + 0x1e,0x5c,0xd9,0xd7,0x5c,0x9a,0x5e,0x19,0xaf,0xb0,0x34,0x39,0x97,0x30,0xb9,0xb3, + 0x2f,0xba,0x3c,0xb8,0xb2,0xaf,0x30,0xb6,0xb4,0x33,0xb7,0xaf,0xb9,0x34,0xbd,0x32, + 0x26,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x1c,0xbe,0x62,0x72,0x86,0x90, + 0xc1,0x52,0x28,0x6d,0xa0,0xb8,0xc1,0x72,0x28,0x62,0xb0,0x08,0x4b,0xa0,0xbc,0x81, + 0x02,0x07,0x0a,0x1d,0x28,0x75,0xb0,0x1c,0x8a,0x1d,0x2c,0x89,0x12,0x29,0x77,0xa0, + 0x4c,0x0a,0x1e,0x0c,0x51,0x94,0x32,0x50,0xd0,0x40,0x51,0x03,0x85,0x0d,0x94,0x3c, + 0x18,0x62,0x24,0x80,0x02,0x06,0x8a,0x1e,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,0xfa,0x60,0x88,0xa1,0xf0, + 0x81,0xe2,0x07,0x8d,0x32,0xc4,0x50,0xfe,0x40,0xf9,0x83,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,0x65,0x50,0x18,0x67,0x84,0x12,0x0e,0xe9,0x20,0x0f,0x6e, + 0x60,0x0f,0xe5,0x20,0x0f,0xf4,0x50,0x0e,0xf8,0x30,0x25,0xd8,0x03,0x00,0x00,0x00, 0x00,0x79,0x18,0x00,0x00,0x7b,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, @@ -1427,174 +1690,164 @@ static const uint8_t _sgl_fs_bytecode_metal_macos[2829] = { 0xc0,0x0e,0xe5,0x50,0x0e,0xf3,0x30,0x23,0xc1,0xd2,0x41,0x1e,0xe4,0xe1,0x17,0xd8, 0xe1,0x1d,0xde,0x01,0x1e,0x66,0x50,0x59,0x38,0xa4,0x83,0x3c,0xb8,0x81,0x39,0xd4, 0x83,0x3b,0x8c,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x2f,0x9c,0x83,0x3c,0xbc,0x43, - 0x3d,0xc0,0xc3,0x3c,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, + 0x3d,0xc0,0xc3,0x3c,0x00,0x71,0x20,0x00,0x00,0x02,0x00,0x00,0x00,0x06,0x50,0x30, + 0x00,0xd2,0xd0,0x00,0x00,0x61,0x20,0x00,0x00,0x3e,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,0x4b, + 0x94,0x65,0x11,0x05,0x65,0x90,0x21,0x1a,0x0c,0x13,0x02,0xf9,0x8c,0x57,0x3c,0x55, + 0xd7,0x2d,0x14,0x94,0x41,0x86,0xea,0x70,0x4c,0x08,0xe4,0x63,0x41,0x01,0x9f,0xf1, + 0x0a,0x4a,0x13,0x03,0x31,0x70,0x28,0x28,0x83,0x0c,0x1a,0x43,0x99,0x10,0xc8,0xc7, + 0x8a,0x00,0x3e,0xe3,0x15,0xd9,0x77,0x06,0x67,0x40,0x51,0x50,0x06,0x19,0xbe,0x48, + 0x33,0x21,0x90,0x8f,0x15,0x01,0x7c,0xc6,0x2b,0x3c,0x32,0x68,0x03,0x36,0x20,0x03, + 0x0a,0xca,0x20,0xc3,0x18,0x60,0x99,0x09,0x81,0x7c,0xc6,0x2b,0xc4,0x00,0x0d,0xe2, + 0x00,0x0e,0x3c,0x0a,0xca,0x20,0xc3,0x19,0x70,0x61,0x60,0x42,0x20,0x1f,0x0b,0x0a, + 0xf8,0x8c,0x57,0x9c,0x41,0x1b,0xd8,0x41,0x1d,0x88,0x01,0x05,0xc5,0x86,0x00,0x3e, + 0xb3,0x0d,0x61,0x10,0x00,0xb3,0x0d,0x41,0x1b,0x04,0xb3,0x0d,0xc1,0x23,0xcc,0x36, + 0x04,0x6e,0x30,0x64,0x10,0x10,0x03,0x00,0x00,0x09,0x00,0x00,0x00,0x5b,0x86,0x20, + 0x00,0x85,0x2d,0x43,0x11,0x80,0xc2,0x96,0x41,0x09,0x40,0x61,0xcb,0xf0,0x04,0xa0, + 0xb0,0x65,0xa0,0x02,0x50,0xd8,0x32,0x60,0x01,0x28,0x6c,0x19,0xba,0x00,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_vs_bytecode_metal_ios[3305] = { - 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x06,0x00,0x00,0x82,0x09,0x00,0x00,0x00, - 0xe9,0x0c,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, - 0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x01,0x00,0x00,0x00,0x00,0x00,0x00, - 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x19,0x01,0x00,0x00,0x00,0x00,0x00,0x00, - 0xd0,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, +static const uint8_t _sgl_fs_bytecode_metal_ios[2809] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xf9,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd1,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x20,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,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0x77,0x3d,0x05,0xbd,0x9f,0x26,0xc1, - 0xae,0x87,0x1d,0xc4,0x7e,0x6d,0x86,0xc6,0x23,0xd4,0x40,0x35,0xfc,0x64,0x3f,0x25, - 0xe4,0xe3,0x19,0x09,0x7d,0xba,0xe3,0x12,0x70,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x01,0x00,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0xc1,0x98,0x28,0x2b,0x2b,0x1f,0x36, + 0x7c,0x5c,0xb0,0x69,0x3f,0xc3,0xd1,0x80,0x4b,0xcf,0xa1,0x10,0xfc,0x19,0x31,0x58, + 0xad,0x45,0xd3,0x5a,0x81,0x72,0x0d,0x8f,0x85,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,0x40,0x00,0x00, - 0x00,0x56,0x41,0x54,0x54,0x2a,0x00,0x04,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,0x70,0x73,0x69,0x7a,0x65,0x00,0x03, - 0x80,0x56,0x41,0x54,0x59,0x06,0x00,0x04,0x00,0x06,0x04,0x06,0x03,0x45,0x4e,0x44, + 0x00,0x01,0x00,0x01,0x00,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,0xbc,0x0b,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, - 0xde,0x21,0x0c,0x00,0x00,0xec,0x02,0x00,0x00,0x0b,0x82,0x20,0x00,0x02,0x00,0x00, + 0x00,0x14,0x00,0x00,0x00,0x08,0x0a,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0x7f,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,0x42,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,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,0x44,0x33, - 0x00,0xc3,0x08,0x04,0x60,0x89,0x10,0x02,0x18,0x46,0x10,0x80,0x24,0x08,0x33,0x51, - 0xf3,0x40,0x0f,0xf2,0x50,0x0f,0xe3,0x40,0x0f,0x6e,0xd0,0x0e,0xe5,0x40,0x0f,0xe1, - 0xc0,0x0e,0x7a,0xa0,0x07,0xed,0x10,0x0e,0xf4,0x20,0x0f,0xe9,0x80,0x0f,0x28,0x20, - 0x07,0x49,0x53,0x44,0x09,0x93,0x5f,0x49,0xff,0x03,0x44,0x00,0x23,0x21,0xa1,0x94, - 0x41,0x04,0x43,0x28,0x86,0x08,0x23,0x80,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,0x08,0x65,0x50,0x80,0x02,0x05,0x51,0x20,0xd4,0x46,0x00,0x88,0x8d,0x25,0x3c, - 0x00,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x01,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,0x05,0xe3,0x20,0x08,0x0e,0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e, - 0x8c,0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x06,0x06,0x26,0xc6, - 0x65,0x46,0xa6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x06, - 0x06,0x26,0xc6,0x65,0x46,0xa6,0x26,0x65,0x88,0xa0,0x10,0x43,0x8c,0x25,0x58,0x90, - 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,0x51,0x58,0x9a,0x9c,0x8b,0x5d,0x99, - 0x1c,0x5d,0x19,0xde,0xd7,0x5b,0x1d,0x1d,0x5c,0x1d,0x1d,0x97,0xba,0xb9,0x32,0x39, - 0x14,0xb6,0xb7,0x31,0x37,0x98,0x14,0x46,0x61,0x69,0x72,0x2e,0x61,0x72,0x67,0x5f, - 0x74,0x79,0x70,0x65,0x5f,0x6e,0x61,0x6d,0x65,0x34,0xcc,0xd8,0xde,0xc2,0xe8,0x68, - 0xc8,0x84,0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xb9,0x85,0xb5,0x95,0x51,0xa8,0xb3, - 0x1b,0xc2,0x28,0x8f,0x02,0x29,0x91,0x22,0x29,0x93,0x42,0x71,0xa9,0x9b,0x2b,0x93, - 0x43,0x61,0x7b,0x1b,0x73,0x8b,0x49,0x61,0x31,0xf6,0xc6,0xf6,0x26,0x37,0x84,0x51, - 0x1e,0xc5,0x52,0x22,0x45,0x52,0x26,0xe5,0x22,0x13,0x96,0x26,0xe7,0x02,0xf7,0x36, - 0x97,0x46,0x97,0xf6,0xe6,0xc6,0xe5,0x8c,0xed,0x0b,0xea,0x6d,0x2e,0x8d,0x2e,0xed, - 0xcd,0x6d,0x88,0xa2,0x64,0x4a,0xa4,0x48,0xca,0xa4,0x68,0x74,0xc2,0xd2,0xe4,0x5c, - 0xe0,0xde,0xd2,0xdc,0xe8,0xbe,0xe6,0xd2,0xf4,0xca,0x58,0x98,0xb1,0xbd,0x85,0xd1, - 0x91,0x39,0x63,0xfb,0x82,0x7a,0x4b,0x73,0xa3,0x9b,0x4a,0xd3,0x2b,0x1b,0xa2,0x28, - 0x9c,0x12,0x29,0x9d,0x32,0x29,0xde,0x10,0x44,0xa9,0x14,0x4c,0xd9,0x94,0x8f,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,0x7a,0x67,0x65,0x6e,0x65,0x72,0x61,0x74,0x65,0x64,0x28,0x5f,0x5f, - 0x61,0x69,0x72,0x5f,0x70,0x6c,0x61,0x63,0x65,0x68,0x6f,0x6c,0x64,0x65,0x72,0x5f, - 0x5f,0x29,0x44,0xe0,0xde,0xe6,0xd2,0xe8,0xd2,0xde,0xdc,0x86,0x50,0x8b,0xa0,0x84, - 0x81,0x22,0x06,0x8b,0xb0,0x04,0xca,0x18,0x28,0x91,0x22,0x29,0x93,0x42,0x06,0x34, - 0xcc,0xd8,0xde,0xc2,0xe8,0x64,0x98,0xd0,0x95,0xe1,0x8d,0xbd,0xbd,0xc9,0x91,0xc1, - 0x0c,0xa1,0x96,0x40,0x09,0x03,0x45,0x0c,0x96,0x60,0x09,0x94,0x31,0x50,0x22,0xc5, - 0x0c,0x94,0x49,0x39,0x03,0x1a,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x43,0xa8,0x65,0x50, - 0xc2,0x40,0x11,0x83,0x65,0x58,0x02,0x65,0x0c,0x94,0x48,0x91,0x94,0x49,0x49,0x03, - 0x16,0x70,0x73,0x69,0x7a,0x65,0x43,0xa8,0xc5,0x50,0xc2,0x40,0x11,0x83,0xc5,0x58, - 0x02,0x65,0x0c,0x94,0x48,0xe9,0x94,0x49,0x59,0x03,0x2a,0x61,0x69,0x72,0x2e,0x62, - 0x75,0x66,0x66,0x65,0x72,0x7c,0xc2,0xd2,0xe4,0x5c,0xc4,0xea,0xcc,0xcc,0xca,0xe4, - 0xbe,0xe6,0xd2,0xf4,0xca,0x88,0x84,0xa5,0xc9,0xb9,0xc8,0x95,0x85,0x91,0x91,0x0a, - 0x4b,0x93,0x73,0x99,0xa3,0x93,0xab,0x1b,0xa3,0xfb,0xa2,0xcb,0x83,0x2b,0xfb,0x4a, - 0x73,0x33,0x7b,0x23,0x62,0xc6,0xf6,0x16,0x46,0x47,0x83,0x47,0xc3,0xa1,0xcd,0x0e, - 0x8e,0x02,0x5d,0xdb,0x10,0x6a,0x11,0x16,0x62,0x11,0x94,0x38,0x50,0xe4,0x60,0x21, - 0x16,0x62,0x11,0x94,0x38,0x50,0xe6,0x80,0x51,0x58,0x9a,0x9c,0x4b,0x98,0xdc,0xd9, - 0x17,0x5d,0x1e,0x5c,0xd9,0xd7,0x5c,0x9a,0x5e,0x19,0xaf,0xb0,0x34,0x39,0x97,0x30, - 0xb9,0xb3,0x2f,0xba,0x3c,0xb8,0xb2,0xaf,0x30,0xb6,0xb4,0x33,0xb7,0xaf,0xb9,0x34, - 0xbd,0x32,0x26,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x1c,0xbe,0x64,0x62, - 0x86,0x90,0xc1,0x52,0x28,0x6d,0xa0,0xb8,0xc1,0x72,0x28,0x62,0xb0,0x08,0x4b,0xa0, - 0xbc,0x81,0x02,0x07,0x0a,0x1d,0x28,0x75,0xb0,0x1c,0x8a,0x1d,0x2c,0x89,0x12,0x29, - 0x77,0xa0,0x4c,0x0a,0x1e,0x0c,0x51,0x94,0x32,0x50,0xd0,0x40,0x51,0x03,0x85,0x0d, - 0x94,0x3c,0x18,0x62,0x24,0x80,0x02,0x06,0x8a,0x1e,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,0xfa,0x60,0x88, - 0xa1,0xf0,0x81,0xe2,0x07,0x8d,0x32,0xc4,0x50,0xfe,0x40,0xf9,0x83,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,0x65,0x50,0x18,0x67,0x84,0x12,0x0e,0xe9,0x20, - 0x0f,0x6e,0x60,0x0f,0xe5,0x20,0x0f,0xf4,0x50,0x0e,0xf8,0x30,0x25,0xd8,0x03,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,0x1d,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,0x60,0x83,0x70,0x94,0x34,0x45,0x94,0x30, + 0xf9,0xff,0x44,0x5c,0x13,0x15,0x11,0xbf,0x3d,0xfc,0xd3,0x18,0x01,0x30,0x88,0x30, + 0x04,0x17,0x49,0x53,0x44,0x09,0x93,0xff,0x4b,0x00,0xf3,0x2c,0x44,0xf4,0x4f,0x63, + 0x04,0xc0,0x20,0x42,0x21,0x94,0x42,0x84,0x40,0x0c,0x9d,0x61,0x04,0x01,0x98,0x23, + 0x08,0xe6,0x08,0xc0,0x60,0x18,0x41,0x58,0x0a,0x12,0x88,0x49,0x8a,0x29,0x40,0x6d, + 0x20,0x20,0x05,0xd6,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,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,0x25,0x30,0x02, + 0x50,0x04,0x85,0x50,0x10,0x65,0x40,0x70,0x2c,0x01,0x12,0x00,0x00,0x79,0x18,0x00, + 0x00,0xb7,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,0xe7,0x20,0x08, + 0x0e,0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae,0x4c,0x6e,0x2e,0xed, + 0xcd,0x0d,0x64,0x26,0x06,0x06,0x26,0xc6,0xc5,0xc6,0xe6,0x06,0x04,0xa5,0xad,0x8c, + 0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x06,0x06,0x26,0xc6,0xc5,0xc6,0xe6,0xc6,0x45, + 0x26,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,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,0x51,0x58,0x9a,0x9c,0x8b,0x5c,0x99,0x1b,0x59,0x99,0xdc,0x17,0x5d, + 0x98,0xdc,0x59,0x19,0x1d,0xa3,0xb0,0x34,0x39,0x97,0x30,0xb9,0xb3,0x2f,0xba,0x3c, + 0xb8,0xb2,0x2f,0xb7,0xb0,0xb6,0x32,0x1a,0x66,0x6c,0x6f,0x61,0x74,0x34,0x64,0xc2, + 0xd2,0xe4,0x5c,0xc2,0xe4,0xce,0xbe,0xdc,0xc2,0xda,0xca,0xa8,0x98,0xc9,0x85,0x9d, + 0x7d,0x8d,0xbd,0xb1,0xbd,0xc9,0x0d,0x61,0x9e,0x67,0x19,0x1e,0xe8,0x89,0x1e,0xe9, + 0x99,0x86,0x08,0x0f,0x45,0x29,0x2c,0x4d,0xce,0xc5,0x4c,0x2e,0xec,0xac,0xad,0xcc, + 0x8d,0xee,0x2b,0xcd,0x0d,0xae,0x8e,0x8e,0x4b,0xdd,0x5c,0x99,0x1c,0x0a,0xdb,0xdb, + 0x98,0x1b,0x4c,0x0a,0x95,0xb0,0x34,0x39,0x97,0xb1,0x32,0x37,0xba,0x32,0x39,0x3e, + 0x61,0x69,0x72,0x2e,0x70,0x65,0x72,0x73,0x70,0x65,0x63,0x74,0x69,0x76,0x65,0x14, + 0xea,0xec,0x86,0x48,0xcb,0xf0,0x58,0xcf,0xf5,0x60,0x4f,0xf6,0x40,0x4f,0xf4,0x48, + 0x8f,0xc6,0xa5,0x6e,0xae,0x4c,0x0e,0x85,0xed,0x6d,0xcc,0x2d,0x26,0x85,0xc5,0xd8, + 0x1b,0xdb,0x9b,0xdc,0x10,0x69,0x11,0x1e,0xeb,0xe1,0x1e,0xec,0xc9,0x1e,0xe8,0x89, + 0x1e,0xe9,0xe9,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,0xef, + 0x01,0x83,0x65,0x58,0x84,0x27,0x0c,0x1e,0xe8,0x11,0x83,0x47,0x7a,0xc6,0x80,0x4b, + 0x58,0x9a,0x9c,0xcb,0x5c,0x58,0x1b,0x1c,0x5b,0x99,0x1c,0x8f,0xb9,0xb0,0x36,0x38, + 0xb6,0x32,0x39,0x0e,0x73,0x6d,0x70,0x43,0xa4,0xe5,0x78,0xca,0xe0,0x01,0x83,0x65, + 0x58,0x84,0x07,0x7a,0xcc,0xe0,0x91,0x9e,0x33,0x18,0x82,0x3c,0xdb,0xe3,0x3d,0x64, + 0xf0,0xa0,0xc1,0x10,0x03,0x01,0x9e,0xea,0x49,0x83,0x11,0x11,0x3b,0xb0,0x83,0x3d, + 0xb4,0x83,0x1b,0xb4,0xc3,0x3b,0x90,0x43,0x3d,0xb0,0x43,0x39,0xb8,0x81,0x39,0xb0, + 0x43,0x38,0x9c,0xc3,0x3c,0x4c,0x11,0x82,0x61,0x84,0xc2,0x0e,0xec,0x60,0x0f,0xed, + 0xe0,0x06,0xe9,0x40,0x0e,0xe5,0xe0,0x0e,0xf4,0x30,0x25,0x28,0x46,0x2c,0xe1,0x90, + 0x0e,0xf2,0xe0,0x06,0xf6,0x50,0x0e,0xf2,0x30,0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x25, + 0x30,0x46,0x50,0xe1,0x90,0x0e,0xf2,0xe0,0x06,0xec,0x10,0x0e,0xee,0x70,0x0e,0xf5, + 0x10,0x0e,0xe7,0x50,0x0e,0xbf,0x60,0x0f,0xe5,0x20,0x0f,0xf3,0x90,0x0e,0xef,0xe0, + 0x0e,0x53,0x02,0x64,0xc4,0x14,0x0e,0xe9,0x20,0x0f,0x6e,0x30,0x0e,0xef,0xd0,0x0e, + 0xf0,0x90,0x0e,0xec,0x50,0x0e,0xbf,0xf0,0x0e,0xf0,0x40,0x0f,0xe9,0xf0,0x0e,0xee, + 0x30,0x0f,0x53,0x06,0x85,0x71,0x46,0x30,0xe1,0x90,0x0e,0xf2,0xe0,0x06,0xe6,0x20, + 0x0f,0xe1,0x70,0x0e,0xed,0x50,0x0e,0xee,0x40,0x0f,0x53,0x02,0x35,0x00,0x00,0x00, 0x00,0x79,0x18,0x00,0x00,0x7b,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, @@ -1626,204 +1879,16 @@ static const uint8_t _sgl_vs_bytecode_metal_ios[3305] = { 0xc0,0x0e,0xe5,0x50,0x0e,0xf3,0x30,0x23,0xc1,0xd2,0x41,0x1e,0xe4,0xe1,0x17,0xd8, 0xe1,0x1d,0xde,0x01,0x1e,0x66,0x50,0x59,0x38,0xa4,0x83,0x3c,0xb8,0x81,0x39,0xd4, 0x83,0x3b,0x8c,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x2f,0x9c,0x83,0x3c,0xbc,0x43, - 0x3d,0xc0,0xc3,0x3c,0x00,0x71,0x20,0x00,0x00,0x02,0x00,0x00,0x00,0x06,0x50,0x30, - 0x00,0xd2,0xd0,0x00,0x00,0x61,0x20,0x00,0x00,0x3e,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,0x4b, - 0x94,0x65,0x11,0x05,0x65,0x90,0x21,0x1a,0x0c,0x13,0x02,0xf9,0x8c,0x57,0x3c,0x55, - 0xd7,0x2d,0x14,0x94,0x41,0x86,0xea,0x70,0x4c,0x08,0xe4,0x63,0x41,0x01,0x9f,0xf1, - 0x0a,0x4a,0x13,0x03,0x31,0x70,0x28,0x28,0x83,0x0c,0x1a,0x43,0x99,0x10,0xc8,0xc7, - 0x8a,0x00,0x3e,0xe3,0x15,0xd9,0x77,0x06,0x67,0x40,0x51,0x50,0x06,0x19,0xbe,0x48, - 0x33,0x21,0x90,0x8f,0x15,0x01,0x7c,0xc6,0x2b,0x3c,0x32,0x68,0x03,0x36,0x20,0x03, - 0x0a,0xca,0x20,0xc3,0x18,0x60,0x99,0x09,0x81,0x7c,0xc6,0x2b,0xc4,0x00,0x0d,0xe2, - 0x00,0x0e,0x3c,0x0a,0xca,0x20,0xc3,0x19,0x70,0x61,0x60,0x42,0x20,0x1f,0x0b,0x0a, - 0xf8,0x8c,0x57,0x9c,0x41,0x1b,0xd8,0x41,0x1d,0x88,0x01,0x05,0xc5,0x86,0x00,0x3e, - 0xb3,0x0d,0x61,0x10,0x00,0xb3,0x0d,0x41,0x1b,0x04,0xb3,0x0d,0xc1,0x23,0xcc,0x36, - 0x04,0x6e,0x30,0x64,0x10,0x10,0x03,0x00,0x00,0x09,0x00,0x00,0x00,0x5b,0x86,0x20, - 0x00,0x85,0x2d,0x43,0x11,0x80,0xc2,0x96,0x41,0x09,0x40,0x61,0xcb,0xf0,0x04,0xa0, - 0xb0,0x65,0xa0,0x02,0x50,0xd8,0x32,0x60,0x01,0x28,0x6c,0x19,0xba,0x00,0x14,0x00, + 0x3d,0xc0,0xc3,0x3c,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, }; -static const uint8_t _sgl_fs_bytecode_metal_ios[2813] = { - 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x06,0x00,0x00,0x82,0x09,0x00,0x00,0x00, - 0xfd,0x0a,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, - 0x20,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,0xa2,0xfb,0x9e,0x58,0x9a,0x22,0x99, - 0x8e,0xdc,0x41,0x5a,0x15,0xf4,0x1f,0xbc,0xfa,0xdd,0xa2,0x32,0xf3,0xff,0x3e,0xe2, - 0x2c,0x7f,0x20,0x9e,0x14,0xb3,0xef,0x8e,0x56,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,0x08,0x0a,0x00,0x00,0xff,0xff,0xff, - 0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0x7f,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,0x1d,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,0x60,0x83,0x70,0x94, - 0x34,0x45,0x94,0x30,0xf9,0xff,0x44,0x5c,0x13,0x15,0x11,0xbf,0x3d,0xfc,0xd3,0x18, - 0x01,0x30,0x88,0x30,0x04,0x17,0x49,0x53,0x44,0x09,0x93,0xff,0x4b,0x00,0xf3,0x2c, - 0x44,0xf4,0x4f,0x63,0x04,0xc0,0x20,0x42,0x21,0x94,0x42,0x84,0x40,0x0c,0x9d,0x61, - 0x04,0x01,0x98,0x23,0x08,0xe6,0x08,0xc0,0x60,0x18,0x41,0x58,0x0a,0x12,0x88,0x49, - 0x8a,0x29,0x40,0x6d,0x20,0x20,0x05,0xd6,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,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,0xe1,0x01,0x00, - 0x00,0x79,0x18,0x00,0x00,0xb7,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,0xe3,0x20,0x08,0x0e,0x8e,0xad,0x0c,0xa4,0xad,0x8c,0x2e,0x8c,0x0d,0xc4,0xae, - 0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x06,0x06,0x26,0xc6,0x65,0x46,0xa6,0x06, - 0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x06,0x06,0x26,0xc6,0x65, - 0x46,0xa6,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,0x51,0x58,0x9a,0x9c,0x8b,0x5c,0x99,0x1b,0x59,0x99,0xdc, - 0x17,0x5d,0x98,0xdc,0x59,0x19,0x1d,0xa3,0xb0,0x34,0x39,0x97,0x30,0xb9,0xb3,0x2f, - 0xba,0x3c,0xb8,0xb2,0x2f,0xb7,0xb0,0xb6,0x32,0x1a,0x66,0x6c,0x6f,0x61,0x74,0x34, - 0x64,0xc2,0xd2,0xe4,0x5c,0xc2,0xe4,0xce,0xbe,0xdc,0xc2,0xda,0xca,0xa8,0x98,0xc9, - 0x85,0x9d,0x7d,0x8d,0xbd,0xb1,0xbd,0xc9,0x0d,0x61,0x9e,0x67,0x11,0x1e,0xe8,0x89, - 0x1e,0xe9,0x99,0x86,0x08,0x0f,0x45,0x29,0x2c,0x4d,0xce,0xc5,0x4c,0x2e,0xec,0xac, - 0xad,0xcc,0x8d,0xee,0x2b,0xcd,0x0d,0xae,0x8e,0x8e,0x4b,0xdd,0x5c,0x99,0x1c,0x0a, - 0xdb,0xdb,0x98,0x1b,0x4c,0x0a,0x95,0xb0,0x34,0x39,0x97,0xb1,0x32,0x37,0xba,0x32, - 0x39,0x3e,0x61,0x69,0x72,0x2e,0x70,0x65,0x72,0x73,0x70,0x65,0x63,0x74,0x69,0x76, - 0x65,0x14,0xea,0xec,0x86,0x48,0x8b,0xf0,0x58,0xcf,0xf5,0x60,0x4f,0xf6,0x40,0x4f, - 0xf4,0x48,0x8f,0xc6,0xa5,0x6e,0xae,0x4c,0x0e,0x85,0xed,0x6d,0xcc,0x2d,0x26,0x85, - 0xc5,0xd8,0x1b,0xdb,0x9b,0xdc,0x10,0x69,0x19,0x1e,0xeb,0xe1,0x1e,0xec,0xc9,0x1e, - 0xe8,0x89,0x1e,0xe9,0xe9,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,0xef,0x01,0x83,0x45,0x58,0x86,0x27,0x0c,0x1e,0xe8,0x11,0x83,0x47,0x7a,0xc6, - 0x80,0x4b,0x58,0x9a,0x9c,0xcb,0x5c,0x58,0x1b,0x1c,0x5b,0x99,0x1c,0x8f,0xb9,0xb0, - 0x36,0x38,0xb6,0x32,0x39,0x22,0x74,0x65,0x78,0x53,0x6d,0x70,0x6c,0x72,0x43,0xa4, - 0xe5,0x78,0xca,0xe0,0x01,0x83,0x45,0x58,0x86,0x07,0x7a,0xcc,0xe0,0x91,0x9e,0x33, - 0x18,0x82,0x3c,0xdb,0xe3,0x3d,0x64,0xf0,0xa0,0xc1,0x10,0x03,0x01,0x9e,0xea,0x49, - 0x83,0x11,0x11,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b,0xb4,0xc3,0x3b,0x90,0x43,0x3d, - 0xb0,0x43,0x39,0xb8,0x81,0x39,0xb0,0x43,0x38,0x9c,0xc3,0x3c,0x4c,0x11,0x82,0x61, - 0x84,0xc2,0x0e,0xec,0x60,0x0f,0xed,0xe0,0x06,0xe9,0x40,0x0e,0xe5,0xe0,0x0e,0xf4, - 0x30,0x25,0x28,0x46,0x2c,0xe1,0x90,0x0e,0xf2,0xe0,0x06,0xf6,0x50,0x0e,0xf2,0x30, - 0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x25,0x30,0x46,0x50,0xe1,0x90,0x0e,0xf2,0xe0,0x06, - 0xec,0x10,0x0e,0xee,0x70,0x0e,0xf5,0x10,0x0e,0xe7,0x50,0x0e,0xbf,0x60,0x0f,0xe5, - 0x20,0x0f,0xf3,0x90,0x0e,0xef,0xe0,0x0e,0x53,0x02,0x64,0xc4,0x14,0x0e,0xe9,0x20, - 0x0f,0x6e,0x30,0x0e,0xef,0xd0,0x0e,0xf0,0x90,0x0e,0xec,0x50,0x0e,0xbf,0xf0,0x0e, - 0xf0,0x40,0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x0f,0x53,0x06,0x85,0x71,0x46,0x30,0xe1, - 0x90,0x0e,0xf2,0xe0,0x06,0xe6,0x20,0x0f,0xe1,0x70,0x0e,0xed,0x50,0x0e,0xee,0x40, - 0x0f,0x53,0x02,0x35,0x00,0x79,0x18,0x00,0x00,0x7b,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,0x98,0x81,0x5c, - 0xe3,0x10,0x0e,0xec,0xc0,0x0e,0xe5,0x50,0x0e,0xf3,0x30,0x23,0xc1,0xd2,0x41,0x1e, - 0xe4,0xe1,0x17,0xd8,0xe1,0x1d,0xde,0x01,0x1e,0x66,0x50,0x59,0x38,0xa4,0x83,0x3c, - 0xb8,0x81,0x39,0xd4,0x83,0x3b,0x8c,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x2f,0x9c, - 0x83,0x3c,0xbc,0x43,0x3d,0xc0,0xc3,0x3c,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, -}; -static const char _sgl_vs_source_metal_sim[856] = { +static const char _sgl_vs_source_metal_sim[756] = { 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, @@ -1852,34 +1917,28 @@ static const char _sgl_vs_source_metal_sim[856] = { 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,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, 0x61,0x74,0x20,0x70,0x73,0x69,0x7a,0x65,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69, - 0x62,0x75,0x74,0x65,0x28,0x33,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x23, - 0x6c,0x69,0x6e,0x65,0x20,0x31,0x37,0x20,0x22,0x73,0x67,0x6c,0x2e,0x67,0x6c,0x73, - 0x6c,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,0x31,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,0x37,0x20,0x22,0x73, - 0x67,0x6c,0x2e,0x67,0x6c,0x73,0x6c,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,0x31,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,0x38,0x20,0x22, - 0x73,0x67,0x6c,0x2e,0x67,0x6c,0x73,0x6c,0x22,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75, - 0x74,0x2e,0x67,0x6c,0x5f,0x50,0x6f,0x69,0x6e,0x74,0x53,0x69,0x7a,0x65,0x20,0x3d, - 0x20,0x69,0x6e,0x2e,0x70,0x73,0x69,0x7a,0x65,0x3b,0x0a,0x23,0x6c,0x69,0x6e,0x65, - 0x20,0x31,0x39,0x20,0x22,0x73,0x67,0x6c,0x2e,0x67,0x6c,0x73,0x6c,0x22,0x0a,0x20, - 0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x75,0x76,0x20,0x3d,0x20,0x5f,0x32,0x31,0x2e, + 0x62,0x75,0x74,0x65,0x28,0x33,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,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,0x31,0x39,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,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69, + 0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x5f,0x31,0x39,0x2e,0x6d,0x76,0x70,0x20,0x2a, + 0x20,0x69,0x6e,0x2e,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a,0x20,0x20, + 0x20,0x20,0x6f,0x75,0x74,0x2e,0x67,0x6c,0x5f,0x50,0x6f,0x69,0x6e,0x74,0x53,0x69, + 0x7a,0x65,0x20,0x3d,0x20,0x69,0x6e,0x2e,0x70,0x73,0x69,0x7a,0x65,0x3b,0x0a,0x20, + 0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x75,0x76,0x20,0x3d,0x20,0x5f,0x31,0x39,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,0x32,0x30,0x20,0x22,0x73, - 0x67,0x6c,0x2e,0x67,0x6c,0x73,0x6c,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, + 0x2e,0x30,0x29,0x3b,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[489] = { +static const char _sgl_fs_source_metal_sim[439] = { 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, @@ -1893,29 +1952,26 @@ static const char _sgl_fs_source_metal_sim[489] = { 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,0x73,0x67,0x6c,0x2e,0x67,0x6c,0x73,0x6c,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,0x73,0x67,0x6c,0x2e,0x67, - 0x6c,0x73,0x6c,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, + 0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,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,0x73,0x6d,0x70,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,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,0x73,0x6d,0x70,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[1032] = { - 0x44,0x58,0x42,0x43,0x09,0x96,0xbb,0xbb,0xfc,0x44,0x44,0xa8,0xa4,0x1c,0x9e,0x45, - 0x50,0x97,0xf1,0xde,0x01,0x00,0x00,0x00,0x08,0x04,0x00,0x00,0x05,0x00,0x00,0x00, + 0x44,0x58,0x42,0x43,0x74,0x7f,0x01,0xd9,0xf4,0xd5,0xed,0x1d,0x74,0xc1,0x30,0x27, + 0xd8,0xe9,0x9d,0x50,0x01,0x00,0x00,0x00,0x08,0x04,0x00,0x00,0x05,0x00,0x00,0x00, 0x34,0x00,0x00,0x00,0x14,0x01,0x00,0x00,0x90,0x01,0x00,0x00,0x00,0x02,0x00,0x00, 0x8c,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, @@ -1926,9 +1982,9 @@ static const uint8_t _sgl_vs_bytecode_hlsl4[1032] = { 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,0x31,0x5f, + 0x02,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5f,0x31,0x39,0x5f, 0x6d,0x76,0x70,0x00,0x02,0x00,0x03,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x5f,0x32,0x31,0x5f,0x74,0x6d,0x00,0x4d,0x69,0x63,0x72,0x6f, + 0x00,0x00,0x00,0x00,0x5f,0x31,0x39,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,0x74,0x00,0x00,0x00,0x04,0x00,0x00,0x00, @@ -1980,46 +2036,46 @@ static const uint8_t _sgl_vs_bytecode_hlsl4[1032] = { 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, +static const uint8_t _sgl_fs_bytecode_hlsl4[608] = { + 0x44,0x58,0x42,0x43,0xc8,0x9b,0x66,0x64,0x80,0x2f,0xbe,0x14,0xd9,0x88,0xa0,0x97, + 0x64,0x14,0x66,0xff,0x01,0x00,0x00,0x00,0x60,0x02,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0xc8,0x00,0x00,0x00,0x14,0x01,0x00,0x00,0x48,0x01,0x00,0x00, + 0xe4,0x01,0x00,0x00,0x52,0x44,0x45,0x46,0x8c,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, + 0x10,0x81,0x00,0x00,0x64,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, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x60,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,0x0d,0x00,0x00,0x00,0x73,0x6d,0x70,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,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, - 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,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, - 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[1968] = { @@ -2212,9 +2268,16 @@ static const uint8_t _sgl_fs_bytecode_wgpu[936] = { 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!" +#error "Please define one of SOKOL_GLCORE33, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND!" #endif +// ████████ ██ ██ ██████ ███████ ███████ +// ██ ██ ██ ██ ██ ██ ██ +// ██ ████ ██████ █████ ███████ +// ██ ██ ██ ██ ██ +// ██ ██ ██ ███████ ███████ +// +// >>types typedef enum { SGL_PRIMITIVETYPE_POINTS = 0, SGL_PRIMITIVETYPE_LINES, @@ -2279,6 +2342,7 @@ typedef enum { typedef struct { sg_pipeline pip; sg_image img; + sg_sampler smp; int base_vertex; int num_vertices; int uniform_index; @@ -2348,6 +2412,7 @@ typedef struct { float point_size; _sgl_primitive_type_t cur_prim_type; sg_image cur_img; + sg_sampler cur_smp; bool texturing_enabled; bool matrix_dirty; /* reset in sgl_end(), set in any of the matrix stack functions */ @@ -2375,6 +2440,7 @@ typedef struct { uint32_t init_cookie; sgl_desc_t desc; sg_image def_img; // a default white texture + sg_sampler def_smp; // a default sampler sg_shader shd; // same shader for all contexts sgl_context def_ctx_id; sgl_context cur_ctx_id; @@ -2384,7 +2450,51 @@ typedef struct { } _sgl_t; static _sgl_t _sgl; -/*== PRIVATE FUNCTIONS =======================================================*/ +// ██ ██████ ██████ ██████ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ████ ██ ██ +// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██████ ██ ██ ████ ██████ +// +// >>logging +#if defined(SOKOL_DEBUG) +#define _SGL_LOGITEM_XMACRO(item,msg) #item ": " msg, +static const char* _sgl_log_messages[] = { + _SGL_LOG_ITEMS +}; +#undef _SGL_LOGITEM_XMACRO +#endif // SOKOL_DEBUG + +#define _SGL_PANIC(code) _sgl_log(SGL_LOGITEM_ ##code, 0, __LINE__) +#define _SGL_ERROR(code) _sgl_log(SGL_LOGITEM_ ##code, 1, __LINE__) +#define _SGL_WARN(code) _sgl_log(SGL_LOGITEM_ ##code, 2, __LINE__) +#define _SGL_INFO(code) _sgl_log(SGL_LOGITEM_ ##code, 3, __LINE__) + +static void _sgl_log(sgl_log_item_t log_item, uint32_t log_level, uint32_t line_nr) { + if (_sgl.desc.logger.func) { + #if defined(SOKOL_DEBUG) + const char* filename = __FILE__; + const char* message = _sgl_log_messages[log_item]; + #else + const char* filename = 0; + const char* message = 0; + #endif + _sgl.desc.logger.func("sgl", log_level, log_item, message, line_nr, filename, _sgl.desc.logger.user_data); + } else { + // for log level PANIC it would be 'undefined behaviour' to continue + if (log_level == 0) { + abort(); + } + } +} + +// ███ ███ ███████ ███ ███ ██████ ██████ ██ ██ +// ████ ████ ██ ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ █████ ██ ████ ██ ██ ██ ██████ ████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ██ ██████ ██ ██ ██ +// +// >>memory static void _sgl_clear(void* ptr, size_t size) { SOKOL_ASSERT(ptr && (size > 0)); memset(ptr, 0, size); @@ -2393,13 +2503,14 @@ static void _sgl_clear(void* ptr, size_t size) { static void* _sgl_malloc(size_t size) { SOKOL_ASSERT(size > 0); void* ptr; - if (_sgl.desc.allocator.alloc) { - ptr = _sgl.desc.allocator.alloc(size, _sgl.desc.allocator.user_data); - } - else { + if (_sgl.desc.allocator.alloc_fn) { + ptr = _sgl.desc.allocator.alloc_fn(size, _sgl.desc.allocator.user_data); + } else { ptr = malloc(size); } - SOKOL_ASSERT(ptr); + if (0 == ptr) { + _SGL_PANIC(MALLOC_FAILED); + } return ptr; } @@ -2410,25 +2521,20 @@ static void* _sgl_malloc_clear(size_t size) { } static void _sgl_free(void* ptr) { - if (_sgl.desc.allocator.free) { - _sgl.desc.allocator.free(ptr, _sgl.desc.allocator.user_data); - } - else { - free(ptr); - } -} - -#if defined(SOKOL_DEBUG) -static void _sgl_log(const char* msg) { - SOKOL_ASSERT(msg); - if (_sgl.desc.logger.log_cb) { - _sgl.desc.logger.log_cb(msg, _sgl.desc.logger.user_data); + if (_sgl.desc.allocator.free_fn) { + _sgl.desc.allocator.free_fn(ptr, _sgl.desc.allocator.user_data); } else { - SOKOL_LOG(msg); + free(ptr); } } -#endif +// ██████ ██████ ██████ ██ +// ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ +// +// >>pool static void _sgl_init_pool(_sgl_pool_t* pool, int num) { SOKOL_ASSERT(pool && (num >= 1)); /* slot 0 is reserved for the 'invalid id', so bump the pool size by 1 */ @@ -2464,9 +2570,8 @@ static int _sgl_pool_alloc_index(_sgl_pool_t* pool) { int slot_index = pool->free_queue[--pool->queue_top]; SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size)); return slot_index; - } - else { - /* pool exhausted */ + } else { + // pool exhausted return _SGL_INVALID_SLOT_INDEX; } } @@ -2486,47 +2591,6 @@ static void _sgl_pool_free_index(_sgl_pool_t* pool, int slot_index) { SOKOL_ASSERT(pool->queue_top <= (pool->size-1)); } -static void _sgl_reset_context(_sgl_context_t* ctx) { - SOKOL_ASSERT(ctx); - SOKOL_ASSERT(0 == ctx->vertices.ptr); - SOKOL_ASSERT(0 == ctx->uniforms.ptr); - SOKOL_ASSERT(0 == ctx->commands.ptr); - _sgl_clear(ctx, sizeof(_sgl_context_t)); -} - -static void _sgl_setup_context_pool(int pool_size) { - /* note: the pools here will have an additional item, since slot 0 is reserved */ - SOKOL_ASSERT((pool_size > 0) && (pool_size < _SGL_MAX_POOL_SIZE)); - _sgl_init_pool(&_sgl.context_pool.pool, pool_size); - size_t pool_byte_size = sizeof(_sgl_context_t) * (size_t)_sgl.context_pool.pool.size; - _sgl.context_pool.contexts = (_sgl_context_t*) _sgl_malloc_clear(pool_byte_size); -} - -static void _sgl_discard_context_pool(void) { - SOKOL_ASSERT(0 != _sgl.context_pool.contexts); - _sgl_free(_sgl.context_pool.contexts); _sgl.context_pool.contexts = 0; - _sgl_discard_pool(&_sgl.context_pool.pool); -} - -static void _sgl_reset_pipeline(_sgl_pipeline_t* pip) { - SOKOL_ASSERT(pip); - _sgl_clear(pip, sizeof(_sgl_pipeline_t)); -} - -static void _sgl_setup_pipeline_pool(int pool_size) { - /* note: the pools here will have an additional item, since slot 0 is reserved */ - SOKOL_ASSERT((pool_size > 0) && (pool_size < _SGL_MAX_POOL_SIZE)); - _sgl_init_pool(&_sgl.pip_pool.pool, pool_size); - size_t pool_byte_size = sizeof(_sgl_pipeline_t) * (size_t)_sgl.pip_pool.pool.size; - _sgl.pip_pool.pips = (_sgl_pipeline_t*) _sgl_malloc_clear(pool_byte_size); -} - -static void _sgl_discard_pipeline_pool(void) { - SOKOL_ASSERT(0 != _sgl.pip_pool.pips); - _sgl_free(_sgl.pip_pool.pips); _sgl.pip_pool.pips = 0; - _sgl_discard_pool(&_sgl.pip_pool.pool); -} - /* allocate the slot at slot_index: - bump the slot's generation counter - create a resource id from the generation counter and slot index @@ -2555,6 +2619,32 @@ static int _sgl_slot_index(uint32_t id) { return slot_index; } +// ██████ ██ ██████ ███████ ██ ██ ███ ██ ███████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ +// ██████ ██ ██████ █████ ██ ██ ██ ██ ██ █████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ███████ ███████ ██ ██ ████ ███████ ███████ +// +// >>pipelines +static void _sgl_reset_pipeline(_sgl_pipeline_t* pip) { + SOKOL_ASSERT(pip); + _sgl_clear(pip, sizeof(_sgl_pipeline_t)); +} + +static void _sgl_setup_pipeline_pool(int pool_size) { + /* note: the pools here will have an additional item, since slot 0 is reserved */ + SOKOL_ASSERT((pool_size > 0) && (pool_size < _SGL_MAX_POOL_SIZE)); + _sgl_init_pool(&_sgl.pip_pool.pool, pool_size); + size_t pool_byte_size = sizeof(_sgl_pipeline_t) * (size_t)_sgl.pip_pool.pool.size; + _sgl.pip_pool.pips = (_sgl_pipeline_t*) _sgl_malloc_clear(pool_byte_size); +} + +static void _sgl_discard_pipeline_pool(void) { + SOKOL_ASSERT(0 != _sgl.pip_pool.pips); + _sgl_free(_sgl.pip_pool.pips); _sgl.pip_pool.pips = 0; + _sgl_discard_pool(&_sgl.pip_pool.pool); +} + /* get pipeline pointer without id-check */ static _sgl_pipeline_t* _sgl_pipeline_at(uint32_t pip_id) { SOKOL_ASSERT(SG_INVALID_ID != pip_id); @@ -2585,8 +2675,7 @@ static sgl_pipeline _sgl_alloc_pipeline(void) { int slot_index = _sgl_pool_alloc_index(&_sgl.pip_pool.pool); if (_SGL_INVALID_SLOT_INDEX != slot_index) { res = _sgl_make_pip_id(_sgl_slot_alloc(&_sgl.pip_pool.pool, &_sgl.pip_pool.pips[slot_index].slot, slot_index)); - } - else { + } else { /* pool is exhausted */ res = _sgl_make_pip_id(SG_INVALID_ID); } @@ -2600,22 +2689,22 @@ static void _sgl_init_pipeline(sgl_pipeline pip_id, const sg_pipeline_desc* in_d sg_pipeline_desc desc = *in_desc; desc.layout.buffers[0].stride = sizeof(_sgl_vertex_t); { - sg_vertex_attr_desc* pos = &desc.layout.attrs[0]; + sg_vertex_attr_state* pos = &desc.layout.attrs[0]; pos->offset = offsetof(_sgl_vertex_t, pos); pos->format = SG_VERTEXFORMAT_FLOAT3; } { - sg_vertex_attr_desc* uv = &desc.layout.attrs[1]; + sg_vertex_attr_state* uv = &desc.layout.attrs[1]; uv->offset = offsetof(_sgl_vertex_t, uv); uv->format = SG_VERTEXFORMAT_FLOAT2; } { - sg_vertex_attr_desc* rgba = &desc.layout.attrs[2]; + sg_vertex_attr_state* rgba = &desc.layout.attrs[2]; rgba->offset = offsetof(_sgl_vertex_t, rgba); rgba->format = SG_VERTEXFORMAT_UBYTE4N; } { - sg_vertex_attr_desc* psize = &desc.layout.attrs[3]; + sg_vertex_attr_state* psize = &desc.layout.attrs[3]; psize->offset = offsetof(_sgl_vertex_t, psize); psize->format = SG_VERTEXFORMAT_FLOAT; } @@ -2661,11 +2750,10 @@ static void _sgl_init_pipeline(sgl_pipeline pip_id, const sg_pipeline_desc* in_d if (SGL_PRIMITIVETYPE_QUADS == i) { /* quads are emulated via triangles, use the same pipeline object */ pip->pip[i] = pip->pip[SGL_PRIMITIVETYPE_TRIANGLES]; - } - else { + } else { pip->pip[i] = sg_make_pipeline(&desc); if (pip->pip[i].id == SG_INVALID_ID) { - SGL_LOG("sokol_gl.h: failed to create pipeline object"); + _SGL_ERROR(MAKE_PIPELINE_FAILED); pip->slot.state = SG_RESOURCESTATE_FAILED; } } @@ -2677,9 +2765,8 @@ static sgl_pipeline _sgl_make_pipeline(const sg_pipeline_desc* desc, const sgl_c sgl_pipeline pip_id = _sgl_alloc_pipeline(); if (pip_id.id != SG_INVALID_ID) { _sgl_init_pipeline(pip_id, desc, ctx_desc); - } - else { - SGL_LOG("sokol_gl.h: pipeline pool exhausted!"); + } else { + _SGL_ERROR(PIPELINE_POOL_EXHAUSTED); } return pip_id; } @@ -2703,13 +2790,41 @@ static sg_pipeline _sgl_get_pipeline(sgl_pipeline pip_id, _sgl_primitive_type_t _sgl_pipeline_t* pip = _sgl_lookup_pipeline(pip_id.id); if (pip) { return pip->pip[prim_type]; - } - else { + } else { sg_pipeline dummy_id = { SG_INVALID_ID }; return dummy_id; } } +// ██████ ██████ ███ ██ ████████ ███████ ██ ██ ████████ ███████ +// ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ █████ ███ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██████ ██ ████ ██ ███████ ██ ██ ██ ███████ +// +// >>contexts +static void _sgl_reset_context(_sgl_context_t* ctx) { + SOKOL_ASSERT(ctx); + SOKOL_ASSERT(0 == ctx->vertices.ptr); + SOKOL_ASSERT(0 == ctx->uniforms.ptr); + SOKOL_ASSERT(0 == ctx->commands.ptr); + _sgl_clear(ctx, sizeof(_sgl_context_t)); +} + +static void _sgl_setup_context_pool(int pool_size) { + /* note: the pools here will have an additional item, since slot 0 is reserved */ + SOKOL_ASSERT((pool_size > 0) && (pool_size < _SGL_MAX_POOL_SIZE)); + _sgl_init_pool(&_sgl.context_pool.pool, pool_size); + size_t pool_byte_size = sizeof(_sgl_context_t) * (size_t)_sgl.context_pool.pool.size; + _sgl.context_pool.contexts = (_sgl_context_t*) _sgl_malloc_clear(pool_byte_size); +} + +static void _sgl_discard_context_pool(void) { + SOKOL_ASSERT(0 != _sgl.context_pool.contexts); + _sgl_free(_sgl.context_pool.contexts); _sgl.context_pool.contexts = 0; + _sgl_discard_pool(&_sgl.context_pool.pool); +} + // get context pointer without id-check static _sgl_context_t* _sgl_context_at(uint32_t ctx_id) { SOKOL_ASSERT(SG_INVALID_ID != ctx_id); @@ -2740,8 +2855,7 @@ static sgl_context _sgl_alloc_context(void) { int slot_index = _sgl_pool_alloc_index(&_sgl.context_pool.pool); if (_SGL_INVALID_SLOT_INDEX != slot_index) { res = _sgl_make_ctx_id(_sgl_slot_alloc(&_sgl.context_pool.pool, &_sgl.context_pool.contexts[slot_index].slot, slot_index)); - } - else { + } else { // pool is exhausted res = _sgl_make_ctx_id(SG_INVALID_ID); } @@ -2766,6 +2880,7 @@ static void _sgl_init_context(sgl_context ctx_id, const sgl_context_desc_t* in_d // NOTE: frame_id must be non-zero, so that updates trigger in first frame ctx->frame_id = 1; ctx->cur_img = _sgl.def_img; + ctx->cur_smp = _sgl.def_smp; // allocate buffers and pools ctx->vertices.cap = ctx->desc.max_vertices; @@ -2792,9 +2907,7 @@ static void _sgl_init_context(sgl_context ctx_id, const sgl_context_desc_t* in_d def_pip_desc.depth.write_enabled = true; ctx->def_pip = _sgl_make_pipeline(&def_pip_desc, &ctx->desc); if (!sg_add_commit_listener(_sgl_make_commit_listener(ctx))) { - // FIXME: this should actually result in an invalid context, - // fix this when proper error logging/reporting is added - SGL_LOG("sokol_gl.h: failed to add sokol-gfx commit listener!"); + _SGL_ERROR(ADD_COMMIT_LISTENER_FAILED); } sg_pop_debug_group(); @@ -2813,9 +2926,8 @@ static sgl_context _sgl_make_context(const sgl_context_desc_t* desc) { sgl_context ctx_id = _sgl_alloc_context(); if (ctx_id.id != SG_INVALID_ID) { _sgl_init_context(ctx_id, desc); - } - else { - SGL_LOG("sokol_gl.h: context pool exhausted!"); + } else { + _SGL_ERROR(CONTEXT_POOL_EXHAUSTED); } return ctx_id; } @@ -2845,6 +2957,13 @@ static void _sgl_destroy_context(sgl_context ctx_id) { } } +// ███ ███ ██ ███████ ██████ +// ████ ████ ██ ██ ██ +// ██ ████ ██ ██ ███████ ██ +// ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ███████ ██████ +// +// >>misc static void _sgl_begin(_sgl_context_t* ctx, _sgl_primitive_type_t mode) { ctx->in_begin = true; ctx->base_vertex = ctx->vertices.next; @@ -2879,8 +2998,7 @@ static sg_commit_listener _sgl_make_commit_listener(_sgl_context_t* ctx) { static _sgl_vertex_t* _sgl_next_vertex(_sgl_context_t* ctx) { if (ctx->vertices.next < ctx->vertices.cap) { return &ctx->vertices.ptr[ctx->vertices.next++]; - } - else { + } else { ctx->error = SGL_ERROR_VERTICES_FULL; return 0; } @@ -2889,8 +3007,7 @@ static _sgl_vertex_t* _sgl_next_vertex(_sgl_context_t* ctx) { static _sgl_uniform_t* _sgl_next_uniform(_sgl_context_t* ctx) { if (ctx->uniforms.next < ctx->uniforms.cap) { return &ctx->uniforms.ptr[ctx->uniforms.next++]; - } - else { + } else { ctx->error = SGL_ERROR_UNIFORMS_FULL; return 0; } @@ -2899,8 +3016,7 @@ static _sgl_uniform_t* _sgl_next_uniform(_sgl_context_t* ctx) { static _sgl_command_t* _sgl_cur_command(_sgl_context_t* ctx) { if (ctx->commands.next > 0) { return &ctx->commands.ptr[ctx->commands.next - 1]; - } - else { + } else { return 0; } } @@ -2908,8 +3024,7 @@ static _sgl_command_t* _sgl_cur_command(_sgl_context_t* ctx) { static _sgl_command_t* _sgl_next_command(_sgl_context_t* ctx) { if (ctx->commands.next < ctx->commands.cap) { return &ctx->commands.ptr[ctx->commands.next++]; - } - else { + } else { ctx->error = SGL_ERROR_COMMANDS_FULL; return 0; } @@ -3168,7 +3283,7 @@ static _sgl_matrix_t* _sgl_matrix(_sgl_context_t* ctx) { // return sg_context_desc_t with patched defaults static sgl_desc_t _sgl_desc_defaults(const sgl_desc_t* desc) { - SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free)); + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); sgl_desc_t res = *desc; res.max_vertices = _sgl_def(desc->max_vertices, _SGL_DEFAULT_MAX_VERTICES); res.max_commands = _sgl_def(desc->max_commands, _SGL_DEFAULT_MAX_COMMANDS); @@ -3193,13 +3308,18 @@ static void _sgl_setup_common(void) { img_desc.height = 8; img_desc.num_mipmaps = 1; img_desc.pixel_format = SG_PIXELFORMAT_RGBA8; - img_desc.min_filter = SG_FILTER_NEAREST; - img_desc.mag_filter = SG_FILTER_NEAREST; img_desc.data.subimage[0][0] = SG_RANGE(pixels); img_desc.label = "sgl-default-texture"; _sgl.def_img = sg_make_image(&img_desc); SOKOL_ASSERT(SG_INVALID_ID != _sgl.def_img.id); + sg_sampler_desc smp_desc; + _sgl_clear(&smp_desc, sizeof(smp_desc)); + smp_desc.min_filter = SG_FILTER_NEAREST; + smp_desc.mag_filter = SG_FILTER_NEAREST; + _sgl.def_smp = sg_make_sampler(&smp_desc); + SOKOL_ASSERT(SG_INVALID_ID != _sgl.def_smp.id); + // one shader for all contexts sg_shader_desc shd_desc; _sgl_clear(&shd_desc, sizeof(shd_desc)); @@ -3220,16 +3340,22 @@ static void _sgl_setup_common(void) { 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].used = true; shd_desc.fs.images[0].image_type = SG_IMAGETYPE_2D; - shd_desc.fs.images[0].sampler_type = SG_SAMPLERTYPE_FLOAT; + shd_desc.fs.images[0].sample_type = SG_IMAGESAMPLETYPE_FLOAT; + shd_desc.fs.samplers[0].used = true; + shd_desc.fs.samplers[0].sampler_type = SG_SAMPLERTYPE_SAMPLE; + shd_desc.fs.image_sampler_pairs[0].used = true; + shd_desc.fs.image_sampler_pairs[0].image_slot = 0; + shd_desc.fs.image_sampler_pairs[0].sampler_slot = 0; + shd_desc.fs.image_sampler_pairs[0].glsl_name = "tex_smp"; 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_GLES3) + shd_desc.vs.source = _sgl_vs_source_glsl300es; + shd_desc.fs.source = _sgl_fs_source_glsl300es; #elif defined(SOKOL_METAL) shd_desc.vs.entry = "main0"; shd_desc.fs.entry = "main0"; @@ -3266,6 +3392,7 @@ static void _sgl_setup_common(void) { static void _sgl_discard_common(void) { sg_push_debug_group("sokol-gl"); sg_destroy_image(_sgl.def_img); + sg_destroy_sampler(_sgl.def_smp); sg_destroy_shader(_sgl.shd); sg_pop_debug_group(); } @@ -3281,6 +3408,7 @@ static void _sgl_draw(_sgl_context_t* ctx, int layer_id) { uint32_t cur_pip_id = SG_INVALID_ID; uint32_t cur_img_id = SG_INVALID_ID; + uint32_t cur_smp_id = SG_INVALID_ID; int cur_uniform_index = -1; if (ctx->update_frame_id != ctx->frame_id) { @@ -3315,12 +3443,15 @@ static void _sgl_draw(_sgl_context_t* ctx, int layer_id) { cur_pip_id = args->pip.id; /* when pipeline changes, also need to re-apply uniforms and bindings */ cur_img_id = SG_INVALID_ID; + cur_smp_id = SG_INVALID_ID; cur_uniform_index = -1; } - if (cur_img_id != args->img.id) { - ctx->bind.fs_images[0] = args->img; + if ((cur_img_id != args->img.id) || (cur_smp_id != args->smp.id)) { + ctx->bind.fs.images[0] = args->img; + ctx->bind.fs.samplers[0] = args->smp; sg_apply_bindings(&ctx->bind); cur_img_id = args->img.id; + cur_smp_id = args->smp.id; } if (cur_uniform_index != args->uniform_index) { const sg_range ub_range = { &ctx->uniforms.ptr[args->uniform_index], sizeof(_sgl_uniform_t) }; @@ -3350,7 +3481,13 @@ static sgl_context_desc_t _sgl_as_context_desc(const sgl_desc_t* desc) { return ctx_desc; } -/*== PUBLIC FUNCTIONS ========================================================*/ +// ██████ ██ ██ ██████ ██ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ ██ ██████ +// +// >>public SOKOL_API_IMPL void sgl_setup(const sgl_desc_t* desc) { SOKOL_ASSERT(desc); _sgl_clear(&_sgl, sizeof(_sgl)); @@ -3386,8 +3523,7 @@ SOKOL_API_IMPL sgl_error_t sgl_error(void) { _sgl_context_t* ctx = _sgl.cur_ctx; if (ctx) { return ctx->error; - } - else { + } else { return SGL_ERROR_NO_CONTEXT; } } @@ -3396,8 +3532,7 @@ SOKOL_API_IMPL sgl_error_t sgl_context_error(sgl_context ctx_id) { const _sgl_context_t* ctx = _sgl_lookup_context(ctx_id.id); if (ctx) { return ctx->error; - } - else { + } else { return SGL_ERROR_NO_CONTEXT; } } @@ -3418,7 +3553,7 @@ SOKOL_API_IMPL sgl_context sgl_make_context(const sgl_context_desc_t* desc) { SOKOL_API_IMPL void sgl_destroy_context(sgl_context ctx_id) { SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); if (_sgl_is_default_context(ctx_id)) { - SGL_LOG("sokol_gl.h: cannot destroy default context"); + _SGL_WARN(CANNOT_DESTROY_DEFAULT_CONTEXT); return; } _sgl_destroy_context(ctx_id); @@ -3431,8 +3566,7 @@ SOKOL_API_IMPL void sgl_set_context(sgl_context ctx_id) { SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); if (_sgl_is_default_context(ctx_id)) { _sgl.cur_ctx_id = _sgl.def_ctx_id; - } - else { + } else { _sgl.cur_ctx_id = ctx_id; } // this will return null if the handle isn't valid @@ -3453,8 +3587,7 @@ SOKOL_API_IMPL sgl_pipeline sgl_make_pipeline(const sg_pipeline_desc* desc) { _sgl_context_t* ctx = _sgl.cur_ctx; if (ctx) { return _sgl_make_pipeline(desc, &ctx->desc); - } - else { + } else { return _sgl_make_pip_id(SG_INVALID_ID); } } @@ -3464,8 +3597,7 @@ SOKOL_API_IMPL sgl_pipeline sgl_context_make_pipeline(sgl_context ctx_id, const const _sgl_context_t* ctx = _sgl_lookup_context(ctx_id.id); if (ctx) { return _sgl_make_pipeline(desc, &ctx->desc); - } - else { + } else { return _sgl_make_pip_id(SG_INVALID_ID); } } @@ -3504,8 +3636,7 @@ SOKOL_API_IMPL void sgl_push_pipeline(void) { if (ctx->pip_tos < (_SGL_MAX_STACK_DEPTH - 1)) { ctx->pip_tos++; ctx->pip_stack[ctx->pip_tos] = ctx->pip_stack[ctx->pip_tos-1]; - } - else { + } else { ctx->error = SGL_ERROR_STACK_OVERFLOW; } } @@ -3518,8 +3649,7 @@ SOKOL_API_IMPL void sgl_pop_pipeline(void) { } if (ctx->pip_tos > 0) { ctx->pip_tos--; - } - else { + } else { ctx->error = SGL_ERROR_STACK_UNDERFLOW; } } @@ -3536,6 +3666,7 @@ SOKOL_API_IMPL void sgl_defaults(void) { ctx->point_size = 1.0f; ctx->texturing_enabled = false; ctx->cur_img = _sgl.def_img; + ctx->cur_smp = _sgl.def_smp; sgl_load_default_pipeline(); _sgl_identity(_sgl_matrix_texture(ctx)); _sgl_identity(_sgl_matrix_modelview(ctx)); @@ -3620,7 +3751,7 @@ SOKOL_API_IMPL void sgl_disable_texture(void) { ctx->texturing_enabled = false; } -SOKOL_API_IMPL void sgl_texture(sg_image img) { +SOKOL_API_IMPL void sgl_texture(sg_image img, sg_sampler smp) { SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); _sgl_context_t* ctx = _sgl.cur_ctx; if (!ctx) { @@ -3629,10 +3760,14 @@ SOKOL_API_IMPL void sgl_texture(sg_image img) { SOKOL_ASSERT(!ctx->in_begin); if (SG_INVALID_ID != img.id) { ctx->cur_img = img; - } - else { + } else { ctx->cur_img = _sgl.def_img; } + if (SG_INVALID_ID != smp.id) { + ctx->cur_smp = smp; + } else { + ctx->cur_smp = _sgl.def_smp; + } } SOKOL_API_IMPL void sgl_begin_points(void) { @@ -3713,9 +3848,10 @@ SOKOL_API_IMPL void sgl_end(void) { uni->tm = *_sgl_matrix_texture(ctx); } } - /* check if command can be merged with current command */ + // check if command can be merged with current command sg_pipeline pip = _sgl_get_pipeline(ctx->pip_stack[ctx->pip_tos], ctx->cur_prim_type); sg_image img = ctx->texturing_enabled ? ctx->cur_img : _sgl.def_img; + sg_sampler smp = ctx->texturing_enabled ? ctx->cur_smp : _sgl.def_smp; _sgl_command_t* cur_cmd = _sgl_cur_command(ctx); bool merge_cmd = false; if (cur_cmd) { @@ -3725,23 +3861,24 @@ SOKOL_API_IMPL void sgl_end(void) { (ctx->cur_prim_type != SGL_PRIMITIVETYPE_TRIANGLE_STRIP) && !matrix_dirty && (cur_cmd->args.draw.img.id == img.id) && + (cur_cmd->args.draw.smp.id == smp.id) && (cur_cmd->args.draw.pip.id == pip.id)) { merge_cmd = true; } } if (merge_cmd) { - /* draw command can be merged with the previous command */ + // draw command can be merged with the previous command cur_cmd->args.draw.num_vertices += ctx->vertices.next - ctx->base_vertex; - } - else { - /* append a new draw command */ + } else { + // append a new draw command _sgl_command_t* cmd = _sgl_next_command(ctx); if (cmd) { SOKOL_ASSERT(ctx->uniforms.next > 0); cmd->cmd = SGL_COMMAND_DRAW; cmd->layer_id = ctx->layer_id; cmd->args.draw.img = img; + cmd->args.draw.smp = smp; cmd->args.draw.pip = _sgl_get_pipeline(ctx->pip_stack[ctx->pip_tos], ctx->cur_prim_type); cmd->args.draw.base_vertex = ctx->base_vertex; cmd->args.draw.num_vertices = ctx->vertices.next - ctx->base_vertex; @@ -4128,8 +4265,7 @@ SOKOL_GL_API_DECL void sgl_push_matrix(void) { ctx->matrix_tos[ctx->cur_matrix_mode]++; _sgl_matrix_t* dst = _sgl_matrix(ctx); *dst = *src; - } - else { + } else { ctx->error = SGL_ERROR_STACK_OVERFLOW; } } @@ -4144,8 +4280,7 @@ SOKOL_GL_API_DECL void sgl_pop_matrix(void) { ctx->matrix_dirty = true; if (ctx->matrix_tos[ctx->cur_matrix_mode] > 0) { ctx->matrix_tos[ctx->cur_matrix_mode]--; - } - else { + } else { ctx->error = SGL_ERROR_STACK_UNDERFLOW; } } diff --git a/vlib/gg/gg.c.v b/vlib/gg/gg.c.v index 3656f358e29047..140156d13bf980 100644 --- a/vlib/gg/gg.c.v +++ b/vlib/gg/gg.c.v @@ -452,7 +452,7 @@ pub fn new_context(cfg Config) &Context { init_userdata_cb: gg_init_sokol_window frame_userdata_cb: gg_frame_fn event_userdata_cb: gg_event_fn - fail_userdata_cb: gg_fail_fn + // fail_userdata_cb: gg_fail_fn cleanup_userdata_cb: gg_cleanup_fn window_title: &char(cfg.window_title.str) html5_canvas_name: &char(cfg.html5_canvas_name.str) @@ -533,20 +533,20 @@ pub struct EndOptions { const dontcare_pass = gfx.PassAction{ colors: [ gfx.ColorAttachmentAction{ - action: .dontcare - value: gfx.Color{1.0, 1.0, 1.0, 1.0} + load_action: .dontcare + clear_value: gfx.Color{1.0, 1.0, 1.0, 1.0} }, gfx.ColorAttachmentAction{ - action: .dontcare - value: gfx.Color{1.0, 1.0, 1.0, 1.0} + load_action: .dontcare + clear_value: gfx.Color{1.0, 1.0, 1.0, 1.0} }, gfx.ColorAttachmentAction{ - action: .dontcare - value: gfx.Color{1.0, 1.0, 1.0, 1.0} + load_action: .dontcare + clear_value: gfx.Color{1.0, 1.0, 1.0, 1.0} }, gfx.ColorAttachmentAction{ - action: .dontcare - value: gfx.Color{1.0, 1.0, 1.0, 1.0} + load_action: .dontcare + clear_value: gfx.Color{1.0, 1.0, 1.0, 1.0} }, ]! } diff --git a/vlib/gg/image.c.v b/vlib/gg/image.c.v index 11db39aa434f42..0e8b29521b5b1b 100644 --- a/vlib/gg/image.c.v +++ b/vlib/gg/image.c.v @@ -89,8 +89,8 @@ pub fn (mut img Image) init_sokol_image() &Image { width: img.width height: img.height num_mipmaps: 0 - wrap_u: .clamp_to_edge - wrap_v: .clamp_to_edge + // wrap_u: .clamp_to_edge // XTODO SAMPLER + // wrap_v: .clamp_to_edge label: img.path.str d3d11_texture: 0 } @@ -142,10 +142,10 @@ pub fn (mut ctx Context) new_streaming_image(w int, h int, channels int, sicfg S num_slices: 1 num_mipmaps: 1 usage: .stream - wrap_u: sicfg.wrap_u - wrap_v: sicfg.wrap_v - min_filter: sicfg.min_filter - mag_filter: sicfg.mag_filter + // wrap_u: sicfg.wrap_u // SAMPLER + // wrap_v: sicfg.wrap_v + // min_filter: sicfg.min_filter + // mag_filter: sicfg.mag_filter label: img.path.str } // Sokol requires that streamed images have NO .ptr/.size initially: diff --git a/vlib/sokol/c/declaration.c.v b/vlib/sokol/c/declaration.c.v index d629e0b38fa26a..3561b6f96c3a6f 100644 --- a/vlib/sokol/c/declaration.c.v +++ b/vlib/sokol/c/declaration.c.v @@ -36,7 +36,7 @@ $if ios { } $if emscripten ? { - #flag -DSOKOL_GLES2 + #flag -DSOKOL_GLES3 #flag -DSOKOL_NO_ENTRY #flag -s ERROR_ON_UNDEFINED_SYMBOLS=0 #flag -s ASSERTIONS=1 diff --git a/vlib/sokol/gfx/enums.v b/vlib/sokol/gfx/enums.v index 237f9916982b16..baaeacb9d8881b 100644 --- a/vlib/sokol/gfx/enums.v +++ b/vlib/sokol/gfx/enums.v @@ -2,12 +2,12 @@ module gfx pub enum Backend { glcore33 - gles2 gles3 d3d11 metal_ios metal_macos metal_simulator + wgpu dummy } @@ -37,6 +37,7 @@ pub enum PixelFormat { rg16si rg16f rgba8 + srgb8a8 rgba8sn rgba8ui rgba8si diff --git a/vlib/sokol/gfx/gfx.c.v b/vlib/sokol/gfx/gfx.c.v index 7d9379a4ec523d..3e23a6b3a91683 100644 --- a/vlib/sokol/gfx/gfx.c.v +++ b/vlib/sokol/gfx/gfx.c.v @@ -9,16 +9,16 @@ pub const used_import = c.used_import // setup initialises the SOKOL's gfx library, based on the information passed in `desc` pub fn setup(desc &Desc) { - if desc.allocator.alloc == unsafe { nil } && desc.allocator.free == unsafe { nil } { + if desc.allocator.alloc_fn == unsafe { nil } && desc.allocator.free_fn == unsafe { nil } { unsafe { - desc.allocator.alloc = memory.salloc - desc.allocator.free = memory.sfree + desc.allocator.alloc_fn = memory.salloc + desc.allocator.free_fn = memory.sfree desc.allocator.user_data = voidptr(0x1006fec5) } } - if desc.logger.log_cb == unsafe { nil } { + if desc.logger.func == unsafe { nil } { unsafe { - desc.logger.log_cb = memory.slog + desc.logger.func = memory.slog } } C.sg_setup(desc) diff --git a/vlib/sokol/gfx/gfx_allocator_and_logger.c.v b/vlib/sokol/gfx/gfx_allocator_and_logger.c.v index 76e7f9aa00d0ec..70330e419b3847 100644 --- a/vlib/sokol/gfx/gfx_allocator_and_logger.c.v +++ b/vlib/sokol/gfx/gfx_allocator_and_logger.c.v @@ -5,14 +5,14 @@ import sokol.memory [typedef] pub struct C.sg_allocator { pub mut: - alloc memory.FnAllocatorAlloc = unsafe { nil } - free memory.FnAllocatorFree = unsafe { nil } + alloc_fn memory.FnAllocatorAlloc = unsafe { nil } + free_fn memory.FnAllocatorFree = unsafe { nil } user_data voidptr } [typedef] pub struct C.sg_logger { pub mut: - log_cb memory.FnLogCb = unsafe { nil } + func memory.FnLogCb = unsafe { nil } user_data voidptr } diff --git a/vlib/sokol/gfx/gfx_structs.c.v b/vlib/sokol/gfx/gfx_structs.c.v index a40d64e4f052b2..d9d9b744d0fce4 100644 --- a/vlib/sokol/gfx/gfx_structs.c.v +++ b/vlib/sokol/gfx/gfx_structs.c.v @@ -12,7 +12,7 @@ pub mut: context_pool_size int uniform_buffer_size int staging_buffer_size int - sampler_cache_size int + sampler_pool_size int max_commit_listeners int disable_validation bool // disable validation layer even in debug mode, useful for tests // @@ -61,14 +61,14 @@ struct C.sg_d3d11_context_desc { pub type D3D11ContextDesc = C.sg_d3d11_context_desc -struct C.sg_color_state { +struct C.sg_color_target_state { pub mut: pixel_format PixelFormat write_mask ColorMask blend BlendState } -pub type ColorState = C.sg_color_state +pub type ColorState = C.sg_color_target_state pub struct C.sg_pipeline_desc { pub mut: @@ -115,19 +115,26 @@ pub mut: vertex_buffer_offsets [8]int index_buffer Buffer index_buffer_offset int - vs_images [8]Image - fs_images [8]Image - _end_canary u32 + vs C.sg_stage_bindings + fs C.sg_stage_bindings + // vs_images [8]Image // old + // fs_images [8]Image // old + _end_canary u32 +} + +pub struct C.sg_stage_bindings { +pub mut: + images [12]Image } pub type Bindings = C.sg_bindings pub fn (mut b Bindings) set_vert_image(index int, img Image) { - b.vs_images[index] = img + b.vs.images[index] = img } pub fn (mut b Bindings) set_frag_image(index int, img Image) { - b.fs_images[index] = img + b.fs.images[index] = img } pub fn (b &Bindings) update_vert_buffer(index int, data voidptr, element_size int, element_count int) { @@ -186,13 +193,13 @@ pub fn (mut desc C.sg_shader_desc) set_frag_src(src string) &ShaderDesc { } pub fn (mut desc C.sg_shader_desc) set_vert_image(index int, name string) &ShaderDesc { - desc.vs.images[index].name = &char(name.str) + // desc.vs.images[index].name = &char(name.str) desc.vs.images[index].image_type = ._2d return desc } pub fn (mut desc C.sg_shader_desc) set_frag_image(index int, name string) &ShaderDesc { - desc.fs.images[index].name = &char(name.str) + // desc.fs.images[index].name = &char(name.str) desc.fs.images[index].image_type = ._2d return desc } @@ -244,7 +251,7 @@ pub mut: pub type ShaderStageDesc = C.sg_shader_stage_desc pub fn (mut desc ShaderStageDesc) set_image(index int, name string) ShaderStageDesc { - desc.images[index].name = &char(name.str) + // desc.images[index].name = &char(name.str) desc.images[index].image_type = ._2d return *desc } @@ -269,7 +276,9 @@ pub type ShaderUniformDesc = C.sg_shader_uniform_desc struct C.sg_shader_image_desc { pub mut: - name &char + used bool + multisampled bool + // name &char image_type ImageType } @@ -397,27 +406,27 @@ pub fn (mut b Buffer) free() { pub struct C.sg_image_desc { pub mut: - _start_canary u32 - @type ImageType - render_target bool - width int - height int - num_slices int - num_mipmaps int - usage Usage - pixel_format PixelFormat - sample_count int - min_filter Filter - mag_filter Filter - wrap_u Wrap - wrap_v Wrap - wrap_w Wrap - border_color BorderColor - max_anisotropy u32 - min_lod f32 - max_lod f32 - data ImageData - label &char + _start_canary u32 + @type ImageType + render_target bool + width int + height int + num_slices int + num_mipmaps int + usage Usage + pixel_format PixelFormat + sample_count int + // min_filter Filter + // mag_filter Filter + // wrap_u Wrap + // wrap_v Wrap + // wrap_w Wrap + // border_color BorderColor + // max_anisotropy u32 + // min_lod f32 + // max_lod f32 + data ImageData + label &char // GL specific gl_textures [2]u32 gl_texture_target u32 @@ -433,6 +442,18 @@ pub mut: pub type ImageDesc = C.sg_image_desc +pub struct C.sg_sampler_desc { + min_filter Filter + mag_filter Filter + wrap_u Wrap + wrap_v Wrap + wrap_w Wrap + min_lod f32 + max_lod f32 + border_color BorderColor + max_anisotropy u32 +} + pub struct C.sg_image_info { pub mut: slot SlotInfo // resource pool slot info @@ -454,6 +475,8 @@ pub fn (mut i Image) free() { C.sg_destroy_image(*i) } +pub struct C.sg_sampler {} + pub const sg_cubeface_num = 6 pub const sg_max_mipmaps = 16 @@ -492,31 +515,31 @@ pub: pub type Limits = C.sg_limits -pub struct C.sg_layout_desc { +pub struct C.sg_vertex_layout_state { pub mut: buffers [8]BufferLayoutDesc attrs [16]VertexAttrDesc } -pub type LayoutDesc = C.sg_layout_desc +pub type LayoutDesc = C.sg_vertex_layout_state -pub struct C.sg_buffer_layout_desc { +pub struct C.sg_vertex_buffer_layout_state { pub mut: stride int step_func VertexStep step_rate int } -pub type BufferLayoutDesc = C.sg_buffer_layout_desc +pub type BufferLayoutDesc = C.sg_vertex_buffer_layout_state -pub struct C.sg_vertex_attr_desc { +pub struct C.sg_vertex_attr_state { pub mut: buffer_index int offset int format VertexFormat } -pub type VertexAttrDesc = C.sg_vertex_attr_desc +pub type VertexAttrDesc = C.sg_vertex_attr_state struct C.sg_stencil_state { enabled bool @@ -564,8 +587,8 @@ pub type BlendState = C.sg_blend_state struct C.sg_color_attachment_action { pub mut: - action Action - value Color + load_action Action + clear_value Color } pub type ColorAttachmentAction = C.sg_color_attachment_action diff --git a/vlib/sokol/gfx/gfx_utils.c.v b/vlib/sokol/gfx/gfx_utils.c.v index 93287cfc1fef8c..dc6b9eda47933d 100644 --- a/vlib/sokol/gfx/gfx_utils.c.v +++ b/vlib/sokol/gfx/gfx_utils.c.v @@ -2,8 +2,8 @@ module gfx pub fn create_clear_pass(r f32, g f32, b f32, a f32) PassAction { mut color_action := ColorAttachmentAction{ - action: unsafe { Action(C.SG_ACTION_CLEAR) } - value: Color{ + load_action: Action.clear // unsafe { Action(C.SG_ACTION_CLEAR) } + clear_value: Color{ r: r g: g b: b diff --git a/vlib/sokol/memory/memory.v b/vlib/sokol/memory/memory.v index be88fadafa44b9..3a6a6a94ca2b00 100644 --- a/vlib/sokol/memory/memory.v +++ b/vlib/sokol/memory/memory.v @@ -36,6 +36,8 @@ pub fn slog(const_message &char, user_data voidptr) { C.fprintf(C.stderr, c'sokol.memory.slog | user_data: %p, message: %s\n', user_data, const_message) } + C.fprintf(C.stderr, c'%s\n', const_message) + /* $if msvc { C.fprintf(C.stderr, c'%s\n', const_message) } $else { @@ -43,4 +45,5 @@ pub fn slog(const_message &char, user_data voidptr) { C.SOKOL_LOG(const_message) } } + */ } diff --git a/vlib/sokol/sapp/sapp.c.v b/vlib/sokol/sapp/sapp.c.v index 4c01cde5743348..90c9828fdce959 100644 --- a/vlib/sokol/sapp/sapp.c.v +++ b/vlib/sokol/sapp/sapp.c.v @@ -182,28 +182,22 @@ pub fn get_clipboard_string() &char { // special run-function for SOKOL_NO_ENTRY (in standard mode this is an empty stub) pub fn run(desc &Desc) { - if desc.allocator.alloc == unsafe { nil } && desc.allocator.free == unsafe { nil } { + if desc.allocator.alloc_fn == unsafe { nil } && desc.allocator.free_fn == unsafe { nil } { unsafe { - desc.allocator.alloc = memory.salloc - desc.allocator.free = memory.sfree + desc.allocator.alloc_fn = memory.salloc + desc.allocator.free_fn = memory.sfree desc.allocator.user_data = voidptr(0x10005a44) } } - if desc.logger.log_cb == unsafe { nil } { + if desc.logger.func == unsafe { nil } { unsafe { - desc.logger.log_cb = memory.slog + desc.logger.func = memory.slog } } g_desc = *desc C.sapp_run(desc) } -// GL: return true when GLES2 fallback is active (to detect fallback from GLES3) -[inline] -pub fn gles2() bool { - return C.sapp_gles2() -} - // HTML5: enable or disable the hardwired "Leave Site?" dialog box [inline] pub fn html5_ask_leave_site(ask bool) { diff --git a/vlib/sokol/sapp/sapp_allocator_and_logger.c.v b/vlib/sokol/sapp/sapp_allocator_and_logger.c.v index e2ef1f73ed67d5..fdc557c5b77869 100644 --- a/vlib/sokol/sapp/sapp_allocator_and_logger.c.v +++ b/vlib/sokol/sapp/sapp_allocator_and_logger.c.v @@ -5,14 +5,14 @@ import sokol.memory [typedef] pub struct C.sapp_allocator { pub mut: - alloc memory.FnAllocatorAlloc = unsafe { nil } - free memory.FnAllocatorFree = unsafe { nil } + alloc_fn memory.FnAllocatorAlloc = unsafe { nil } + free_fn memory.FnAllocatorFree = unsafe { nil } user_data voidptr } [typedef] pub struct C.sapp_logger { pub mut: - log_cb memory.FnLogCb = unsafe { nil } + func memory.FnLogCb = unsafe { nil } user_data voidptr } diff --git a/vlib/sokol/sapp/sapp_funcs.c.v b/vlib/sokol/sapp/sapp_funcs.c.v index f2007a56b972a7..a2f2b40385906c 100644 --- a/vlib/sokol/sapp/sapp_funcs.c.v +++ b/vlib/sokol/sapp/sapp_funcs.c.v @@ -98,9 +98,6 @@ fn C.sapp_get_dropped_file_path(int) &u8 // special run-function for SOKOL_NO_ENTRY (in standard mode this is an empty stub) fn C.sapp_run(desc &Desc) int -// GL: return true when GLES2 fallback is active (to detect fallback from GLES3) -fn C.sapp_gles2() bool - // HTML5: enable or disable the hardwired "Leave Site?" dialog box fn C.sapp_html5_ask_leave_site(ask bool) diff --git a/vlib/sokol/sapp/sapp_structs.c.v b/vlib/sokol/sapp/sapp_structs.c.v index 08da6996b222b3..55d695d4cf54d5 100644 --- a/vlib/sokol/sapp/sapp_structs.c.v +++ b/vlib/sokol/sapp/sapp_structs.c.v @@ -42,14 +42,14 @@ pub: frame_cb fn () = unsafe { nil } cleanup_cb fn () = unsafe { nil } event_cb fn (&Event) = unsafe { nil } // &sapp_event - fail_cb fn (&u8) = unsafe { nil } + // fail_cb fn (&u8) = unsafe { nil } user_data voidptr // these are the user-provided callbacks with user data init_userdata_cb fn (voidptr) = unsafe { nil } frame_userdata_cb fn (voidptr) = unsafe { nil } cleanup_userdata_cb fn (voidptr) = unsafe { nil } event_userdata_cb fn (&Event, voidptr) = unsafe { nil } - fail_userdata_cb fn (&char, voidptr) = unsafe { nil } + // fail_userdata_cb fn (&char, voidptr) = unsafe { nil } width int // the preferred width of the window / canvas height int // the preferred height of the window / canvas @@ -66,7 +66,6 @@ pub: max_dropped_file_path_length int // max length in bytes of a dropped UTF-8 file path (default: 2048) icon IconDesc // the initial window icon to set // backend-specific options - gl_force_gles2 bool // if true, setup GLES2/WebGL even if GLES3/WebGL2 is available win32_console_utf8 bool // if true, set the output console codepage to UTF-8 win32_console_create bool // if true, attach stdout/stderr to a new console window win32_console_attach bool // if true, attach stdout/stderr to parent process diff --git a/vlib/sokol/sfons/sfons.c.v b/vlib/sokol/sfons/sfons.c.v index 50941bcb10b4b2..a038c2a67a96fd 100644 --- a/vlib/sokol/sfons/sfons.c.v +++ b/vlib/sokol/sfons/sfons.c.v @@ -13,8 +13,8 @@ pub fn create(width int, height int, flags int) &fontstash.Context { assert is_power_of_two(width) assert is_power_of_two(height) allocator := C.sfons_allocator_t{ - alloc: memory.salloc - free: memory.sfree + alloc_fn: memory.salloc + free_fn: memory.sfree user_data: voidptr(0x100005f0) } desc := C.sfons_desc_t{ diff --git a/vlib/sokol/sfons/sfons_funcs.c.v b/vlib/sokol/sfons/sfons_funcs.c.v index 00510483828c34..692b96784de26e 100644 --- a/vlib/sokol/sfons/sfons_funcs.c.v +++ b/vlib/sokol/sfons/sfons_funcs.c.v @@ -6,8 +6,8 @@ import sokol.memory [typedef] pub struct C.sfons_allocator_t { pub: - alloc memory.FnAllocatorAlloc = unsafe { nil } - free memory.FnAllocatorFree = unsafe { nil } + alloc_fn memory.FnAllocatorAlloc = unsafe { nil } + free_fn memory.FnAllocatorFree = unsafe { nil } user_data voidptr } diff --git a/vlib/sokol/sgl/sgl.c.v b/vlib/sokol/sgl/sgl.c.v index e96f8f3248bf78..9ec853084d4f36 100644 --- a/vlib/sokol/sgl/sgl.c.v +++ b/vlib/sokol/sgl/sgl.c.v @@ -9,16 +9,16 @@ pub const context = Context{0x00010001} // C.SGL_DEFAULT_CONTEXT = { 0x00010001 // setup/shutdown/misc pub fn setup(desc &Desc) { - if desc.allocator.alloc == unsafe { nil } && desc.allocator.free == unsafe { nil } { + if desc.allocator.alloc_fn == unsafe { nil } && desc.allocator.free_fn == unsafe { nil } { unsafe { - desc.allocator.alloc = memory.salloc - desc.allocator.free = memory.sfree + desc.allocator.alloc_fn = memory.salloc + desc.allocator.free_fn = memory.sfree desc.allocator.user_data = voidptr(0x10000561) } } - if desc.logger.log_cb == unsafe { nil } { + if desc.logger.func == unsafe { nil } { unsafe { - desc.logger.log_cb = memory.slog + desc.logger.func = memory.slog } } C.sgl_setup(desc) @@ -123,7 +123,7 @@ pub fn disable_texture() { [inline] pub fn texture(img gfx.Image) { - C.sgl_texture(img) + C.sgl_texture(img, C.sg_sampler{}) // TODO need to pass SG_INVALID_ID ? } // pipeline stack functions diff --git a/vlib/sokol/sgl/sgl_allocator_and_logger.c.v b/vlib/sokol/sgl/sgl_allocator_and_logger.c.v index e745b61a890ad3..0bc3fbc4a42fcf 100644 --- a/vlib/sokol/sgl/sgl_allocator_and_logger.c.v +++ b/vlib/sokol/sgl/sgl_allocator_and_logger.c.v @@ -5,14 +5,14 @@ import sokol.memory [typedef] pub struct C.sgl_allocator_t { pub mut: - alloc memory.FnAllocatorAlloc = unsafe { nil } - free memory.FnAllocatorFree = unsafe { nil } + alloc_fn memory.FnAllocatorAlloc = unsafe { nil } + free_fn memory.FnAllocatorFree = unsafe { nil } user_data voidptr } [typedef] pub struct C.sgl_logger_t { pub mut: - log_cb memory.FnLogCb = unsafe { nil } + func memory.FnLogCb = unsafe { nil } user_data voidptr } diff --git a/vlib/sokol/sgl/sgl_funcs.c.v b/vlib/sokol/sgl/sgl_funcs.c.v index a1b444adc30d9a..06589eaf4047f0 100644 --- a/vlib/sokol/sgl/sgl_funcs.c.v +++ b/vlib/sokol/sgl/sgl_funcs.c.v @@ -28,7 +28,7 @@ fn C.sgl_scissor_rect(x int, y int, w int, h int, origin_top_left bool) fn C.sgl_scissor_rectf(x f32, y f32, w f32, h f32, origin_top_left bool) fn C.sgl_enable_texture() fn C.sgl_disable_texture() -fn C.sgl_texture(img C.sg_image) +fn C.sgl_texture(img C.sg_image, sampler C.sg_sampler) // pipeline stack functions fn C.sgl_load_default_pipeline() diff --git a/vlib/x/ttf/render_sokol_cpu.v b/vlib/x/ttf/render_sokol_cpu.v index 673f78f9efa9e7..f51c0976d435ea 100644 --- a/vlib/x/ttf/render_sokol_cpu.v +++ b/vlib/x/ttf/render_sokol_cpu.v @@ -123,11 +123,11 @@ pub fn (mut tf_skl TTF_render_Sokol) create_texture() { width: w height: h num_mipmaps: 0 - min_filter: .linear - mag_filter: .linear + // min_filter: .linear + // mag_filter: .linear // usage: .dynamic - wrap_u: .clamp_to_edge - wrap_v: .clamp_to_edge + // wrap_u: .clamp_to_edge + // wrap_v: .clamp_to_edge label: &char(0) d3d11_texture: 0 }