diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 62d7934..79a3d86 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,6 +25,7 @@ jobs: sudo apt-get install libglu1-mesa-dev mesa-common-dev xorg-dev libasound-dev - name: (Dub) Build Native + if: runner.os != 'Windows' run: | dub build :clear dub build :sgl_context @@ -35,6 +36,8 @@ jobs: dub build :triangle dub build :mrt dub build :user_data + dub build :imgui + dub build :droptest - name: (Zig) Running Test if: runner.os != 'Windows' diff --git a/.gitignore b/.gitignore index be576a5..3f75dfd 100644 --- a/.gitignore +++ b/.gitignore @@ -26,7 +26,7 @@ build/ *.lst # Zig build -zig-*/ +*zig-*/ # VScode .vscode/ \ No newline at end of file diff --git a/README.md b/README.md index 613cf19..0ae4fce 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Auto-generated [D](https://dlang.org) bindings for the [sokol headers](https://g **Required** - [zig](https://ziglang.org/download) v0.12.0 or master -- [ldc](https://ldc-developers.github.io) v1.38.0 or latest-CI (nightly) +- [ldc](https://ldc-developers.github.io) v1.39.0 or latest-CI (nightly) Supported platforms are: Windows, macOS, Linux (with X11) diff --git a/build.zig b/build.zig index b236b1b..f522560 100644 --- a/build.zig +++ b/build.zig @@ -23,6 +23,7 @@ pub const LibSokolOptions = struct { use_x11: bool = true, use_wayland: bool = false, emsdk: ?*Build.Dependency = null, + with_sokol_imgui: bool = false, }; // helper function to resolve .auto backend based on target platform @@ -53,21 +54,21 @@ pub fn buildLibSokol(b: *Build, options: LibSokolOptions) !*CompileStep { .name = "sokol", .target = options.target, .optimize = options.optimize, + .link_libc = true, }) else b.addStaticLibrary(.{ .name = "sokol", .target = options.target, .optimize = options.optimize, + .link_libc = true, }); lib.root_module.sanitize_c = false; - lib.linkLibC(); - lib.root_module.root_source_file = b.path("src/handmade/math.zig"); switch (options.optimize) { .Debug, .ReleaseSafe => lib.bundle_compiler_rt = true, else => lib.root_module.strip = true, } - if (options.target.result.isWasm()) { + lib.root_module.root_source_file = b.path("src/handmade/math.zig"); if (options.optimize != .Debug) lib.want_lto = true; // make sure we're building for the wasm32-emscripten target, not wasm32-freestanding @@ -80,7 +81,7 @@ pub fn buildLibSokol(b: *Build, options: LibSokolOptions) !*CompileStep { lib.step.dependOn(&emsdk_setup.step); } // add the Emscripten system include seach path - lib.addSystemIncludePath(emSdkLazyPath(b, options.emsdk.?, &.{ "upstream", "emscripten", "cache", "sysroot", "include" })); + lib.addIncludePath(emSdkLazyPath(b, options.emsdk.?, &.{ "upstream", "emscripten", "cache", "sysroot", "include" })); } // resolve .auto backend into specific backend by platform @@ -170,6 +171,7 @@ pub fn buildLibSokol(b: *Build, options: LibSokolOptions) !*CompileStep { csrc_root ++ "sokol_gl.c", csrc_root ++ "sokol_debugtext.c", csrc_root ++ "sokol_shape.c", + csrc_root ++ "sokol_fetch.c", csrc_root ++ "sokol_glue.c", }; for (csources) |csrc| { @@ -178,6 +180,18 @@ pub fn buildLibSokol(b: *Build, options: LibSokolOptions) !*CompileStep { .flags = cflags, }); } + + if (options.with_sokol_imgui) { + lib.addCSourceFile(.{ + .file = b.path(csrc_root ++ "sokol_imgui.c"), + .flags = cflags, + }); + const cimgui = try buildImgui(b, lib); + for (cimgui.root_module.include_dirs.items) |dir| { + try lib.root_module.include_dirs.append(b.allocator, dir); + } + lib.linkLibrary(cimgui); + } return lib; } @@ -187,9 +201,11 @@ pub fn build(b: *Build) !void { const opt_use_x11 = b.option(bool, "x11", "Force X11 (default: true, Linux only)") orelse true; const opt_use_wayland = b.option(bool, "wayland", "Force Wayland (default: false, Linux only, not supported in main-line headers)") orelse false; const opt_use_egl = b.option(bool, "egl", "Force EGL (default: false, Linux only)") orelse false; + const opt_with_sokol_imgui = b.option(bool, "imgui", "Add support for sokol_imgui.h bindings") orelse false; const sokol_backend: SokolBackend = if (opt_use_gl) .gl else if (opt_use_wgpu) .wgpu else .auto; - const dub_artifact = b.option(bool, "artifact", "Build artifacts (default: false)") orelse false; + // LDC-options options + const dub_artifact = b.option(bool, "artifact", "Build artifacts (default: false)") orelse false; const enable_betterC = b.option(bool, "betterC", "Omit generating some runtime information and helper functions (default: false)") orelse false; const enable_zigcc = b.option(bool, "zigCC", "Use zig cc as compiler and linker (default: false)") orelse false; // ldc2 w/ druntime + phobos2 works on MSVC @@ -204,6 +220,7 @@ pub fn build(b: *Build) !void { .use_wayland = opt_use_wayland, .use_x11 = opt_use_x11, .use_egl = opt_use_egl, + .with_sokol_imgui = opt_with_sokol_imgui, .emsdk = emsdk, }); if (dub_artifact) { @@ -221,17 +238,23 @@ pub fn build(b: *Build) !void { "sgl_points", "debugtext", "user_data", // Need GC for user data [associative array] + "imgui", + "droptest", }; inline for (examples) |example| { + if (std.mem.eql(u8, example, "imgui") or std.mem.eql(u8, example, "droptest")) + if (!opt_with_sokol_imgui) + break; const ldc = try ldcBuildStep(b, .{ .name = example, .artifact = lib_sokol, - .sources = &[_][]const u8{b.fmt("{s}/src/examples/{s}.d", .{ rootPath(), example })}, + .sources = &[_][]const u8{ + b.fmt("{s}/src/examples/{s}.d", .{ rootPath(), example }), + }, .betterC = if (std.mem.eql(u8, example, "user-data")) false else enable_betterC, - .dflags = &[_][]const u8{ - "-w", // warnings as error - // more info: ldc2 -preview=help (list all specs) + .dflags = &.{ + "-w", "-preview=all", }, // fixme: https://github.com/kassane/sokol-d/issues/1 - betterC works on darwin @@ -497,11 +520,8 @@ pub fn ldcBuildStep(b: *Build, options: DCompileStep) !*RunStep { try cmds.append(b.fmt("-mtriple={s}", .{mtriple})); - // cpu model (e.g. "baseline") - if (options.target.query.isNative()) { - const cpu_model = builtin.cpu.model.llvm_name orelse "generic"; - try cmds.append(b.fmt("-mcpu={s}", .{cpu_model})); - } + const cpu_model = options.target.result.cpu.model.llvm_name orelse "generic"; + try cmds.append(b.fmt("-mcpu={s}", .{cpu_model})); const outputDir = switch (options.kind) { .lib => "lib", @@ -575,7 +595,25 @@ pub fn ldcBuildStep(b: *Build, options: DCompileStep) !*RunStep { if (options.artifact) |lib_sokol| { if (lib_sokol.rootModuleTarget().os.tag == .windows and lib_sokol.isDynamicLibrary()) { ldc_exec.addArg(b.pathJoin(&.{ b.install_path, "lib", b.fmt("{s}.lib", .{lib_sokol.name}) })); - } else ldc_exec.addArtifactArg(lib_sokol); + } else { + ldc_exec.addArtifactArg(lib_sokol); + var it = lib_sokol.root_module.iterateDependencies(lib_sokol, false); + while (it.next()) |item| { + for (item.module.link_objects.items) |link_object| { + switch (link_object) { + .other_step => |compile_step| { + switch (compile_step.kind) { + .lib => { + ldc_exec.addArtifactArg(compile_step); + }, + else => {}, + } + }, + else => {}, + } + } + } + } } run.dependOn(&example_run.step); } @@ -689,58 +727,45 @@ pub const EmLinkOptions = struct { extra_args: []const []const u8 = &.{}, }; -pub fn emLinkStep(b: *Build, options: EmLinkOptions) !*Build.Step.Run { +pub fn emLinkStep(b: *Build, options: EmLinkOptions) !*Build.Step.InstallDir { const emcc_path = b.findProgram(&.{"emcc"}, &.{}) catch emSdkLazyPath(b, options.emsdk, &.{ "upstream", "emscripten", "emcc" }).getPath(b); - - // create a separate output directory zig-out/web - try std.fs.cwd().makePath(b.fmt("{s}/web", .{b.install_path})); - - var emcc_cmd = std.ArrayList([]const u8).init(b.allocator); - defer emcc_cmd.deinit(); - - try emcc_cmd.append(emcc_path); + const emcc = b.addSystemCommand(&.{emcc_path}); + emcc.setName("emcc"); // hide emcc path if (options.optimize == .Debug) { - try emcc_cmd.append("-Og"); - try emcc_cmd.append("-sSAFE_HEAP=1"); - try emcc_cmd.append("-sSTACK_OVERFLOW_CHECK=1"); + emcc.addArgs(&.{ "-Og", "-sSAFE_HEAP=1", "-sSTACK_OVERFLOW_CHECK=1" }); } else { - try emcc_cmd.append("-sASSERTIONS=0"); + emcc.addArg("-sASSERTIONS=0"); if (options.optimize == .ReleaseSmall) { - try emcc_cmd.append("-Oz"); + emcc.addArg("-Oz"); } else { - try emcc_cmd.append("-O3"); + emcc.addArg("-O3"); } if (options.release_use_lto) { - try emcc_cmd.append("-flto"); + emcc.addArg("-flto"); } if (options.release_use_closure) { - try emcc_cmd.append("--closure"); - try emcc_cmd.append("1"); + emcc.addArgs(&.{ "--closure", "1" }); } } if (options.use_webgpu) { - try emcc_cmd.append("-sUSE_WEBGPU=1"); + emcc.addArg("-sUSE_WEBGPU=1"); } if (options.use_webgl2) { - try emcc_cmd.append("-sUSE_WEBGL2=1"); + emcc.addArg("-sUSE_WEBGL2=1"); } if (!options.use_filesystem) { - try emcc_cmd.append("-sNO_FILESYSTEM=1"); + emcc.addArg("-sNO_FILESYSTEM=1"); } if (options.use_emmalloc) { - try emcc_cmd.append("-sMALLOC='emmalloc'"); + emcc.addArg("-sMALLOC='emmalloc'"); } if (options.shell_file_path) |shell_file_path| { - try emcc_cmd.append(b.fmt("--shell-file={s}", .{shell_file_path})); + emcc.addArg(b.fmt("--shell-file={s}", .{shell_file_path})); } - try emcc_cmd.append(b.fmt("-o{s}/web/{s}.html", .{ b.install_path, options.lib_main.name })); for (options.extra_args) |arg| { - try emcc_cmd.append(arg); + emcc.addArg(arg); } - const emcc = b.addSystemCommand(emcc_cmd.items); - emcc.setName("emcc"); // hide emcc path - // add the main lib, and then scan for library dependencies and add those too emcc.addArtifactArg(options.lib_main); var it = options.lib_main.root_module.iterateDependencies(options.lib_main, false); @@ -759,10 +784,20 @@ pub fn emLinkStep(b: *Build, options: EmLinkOptions) !*Build.Step.Run { } } } + emcc.addArg("-o"); + const out_file = emcc.addOutputFileArg(b.fmt("{s}.html", .{options.lib_main.name})); + + // the emcc linker creates 3 output files (.html, .wasm and .js) + const install = b.addInstallDirectory(.{ + .source_dir = out_file.dirname(), + .install_dir = .prefix, + .install_subdir = "web", + }); + install.step.dependOn(&emcc.step); // get the emcc step to run on 'zig build' - b.getInstallStep().dependOn(&emcc.step); - return emcc; + b.getInstallStep().dependOn(&install.step); + return install; } // build a run step which uses the emsdk emrun command to run a build target in the browser @@ -777,31 +812,36 @@ pub fn emRunStep(b: *Build, options: EmRunOptions) *Build.Step.Run { return emrun; } -// helper function to extract emsdk path from the emsdk package dependency +// helper function to build a LazyPath from the emsdk root and provided path components fn emSdkLazyPath(b: *Build, emsdk: *Build.Dependency, subPaths: []const []const u8) Build.LazyPath { return emsdk.path(b.pathJoin(subPaths)); } +fn createEmsdkStep(b: *Build, emsdk: *Build.Dependency) *Build.Step.Run { + if (builtin.os.tag == .windows) { + return b.addSystemCommand(&.{emSdkLazyPath(b, emsdk, &.{"emsdk.bat"}).getPath(b)}); + } else { + const step = b.addSystemCommand(&.{"bash"}); + step.addArg(emSdkLazyPath(b, emsdk, &.{"emsdk"}).getPath(b)); + return step; + } +} + // One-time setup of the Emscripten SDK (runs 'emsdk install + activate'). If the // SDK had to be setup, a run step will be returned which should be added // as dependency to the sokol library (since this needs the emsdk in place), // if the emsdk was already setup, null will be returned. // NOTE: ideally this would go into a separate emsdk-zig package +// NOTE 2: the file exists check is a bit hacky, it would be cleaner +// to build an on-the-fly helper tool which takes care of the SDK +// setup and just does nothing if it already happened fn emSdkSetupStep(b: *Build, emsdk: *Build.Dependency) !?*Build.Step.Run { const dot_emsc_path = emSdkLazyPath(b, emsdk, &.{".emscripten"}).getPath(b); const dot_emsc_exists = !std.meta.isError(std.fs.accessAbsolute(dot_emsc_path, .{})); if (!dot_emsc_exists) { - var cmd = std.ArrayList([]const u8).init(b.allocator); - defer cmd.deinit(); - if (builtin.os.tag == .windows) { - try cmd.append(emSdkLazyPath(b, emsdk, &.{"emsdk.bat"}).getPath(b)); - } else { - try cmd.append("bash"); // or try chmod - try cmd.append(emSdkLazyPath(b, emsdk, &.{"emsdk"}).getPath(b)); - } - const emsdk_install = b.addSystemCommand(cmd.items); + const emsdk_install = createEmsdkStep(b, emsdk); emsdk_install.addArgs(&.{ "install", "latest" }); - const emsdk_activate = b.addSystemCommand(cmd.items); + const emsdk_activate = createEmsdkStep(b, emsdk); emsdk_activate.addArgs(&.{ "activate", "latest" }); emsdk_activate.step.dependOn(&emsdk_install.step); return emsdk_activate; @@ -809,3 +849,74 @@ fn emSdkSetupStep(b: *Build, emsdk: *Build.Dependency) !?*Build.Step.Run { return null; } } + +fn buildImgui(b: *Build, options: *CompileStep) !*CompileStep { + const imgui_cpp = b.dependency("imgui", .{}); + const imgui_cpp_dir = imgui_cpp.path(""); + const cimgui = b.dependency("cimgui", .{}); + const cimgui_dir = cimgui.path(""); + + const libimgui = b.addStaticLibrary(.{ + .name = "cimgui", + .target = options.root_module.resolved_target.?, + .optimize = options.root_module.optimize.?, + }); + if (libimgui.linkage == .static) + libimgui.pie = true + else if (libimgui.linkage == .static) + libimgui.root_module.pic = true; + libimgui.addIncludePath(cimgui_dir); + libimgui.addIncludePath(imgui_cpp_dir); + libimgui.defineCMacro("IMGUI_DISABLE_OBSOLETE_FUNCTIONS", "1"); + for (options.root_module.include_dirs.items) |include| { + try libimgui.root_module.include_dirs.append(b.allocator, include); + } + if (libimgui.rootModuleTarget().isWasm()) + libimgui.defineCMacro("IMGUI_DISABLE_FILE_FUNCTIONS", null); + libimgui.addCSourceFiles(.{ + .root = cimgui_dir, + .files = &.{ + "cimgui.cpp", + }, + .flags = &.{ + "-Wall", + "-Wextra", + "-fno-rtti", + "-fno-exceptions", + "-Wno-unused-parameter", + "-Wno-missing-field-initializers", + "-fno-threadsafe-statics", + }, + }); + libimgui.addCSourceFiles(.{ + .root = imgui_cpp_dir, + .files = &.{ + "imgui.cpp", + "imgui_draw.cpp", + "imgui_demo.cpp", + "imgui_widgets.cpp", + "imgui_tables.cpp", + }, + .flags = &.{ + "-Wall", + "-Wextra", + "-fno-rtti", + "-fno-exceptions", + "-Wno-unused-parameter", + "-Wno-missing-field-initializers", + "-fno-threadsafe-statics", + }, + }); + libimgui.root_module.sanitize_c = false; + if (libimgui.rootModuleTarget().os.tag == .windows) + libimgui.linkSystemLibrary("imm32"); + + // https://github.com/ziglang/zig/issues/5312 + if (libimgui.rootModuleTarget().abi != .msvc) { + // llvm-libcxx + llvm-libunwind + os-libc + libimgui.linkLibCpp(); + } else { + libimgui.linkLibC(); + } + return libimgui; +} diff --git a/build.zig.zon b/build.zig.zon index 3ed5d65..21aa1b0 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -11,6 +11,14 @@ "README.md", }, .dependencies = .{ + .cimgui = .{ + .url = "git+https://github.com/kassane/cimgui#5c8026c21976e017b890f806eb74b5f210dc638c", + .hash = "1220f9e9b72baa5fa8fdfffad4b2be4b668cfecd4b18478687f1a8baf3b885724108", + }, + .imgui = .{ + .url = "git+https://github.com/ocornut/imgui.git#v1.90.7", + .hash = "122072b125179c7cbdbbee8fa81d22a1050a950ad61cfeefee8dc0dca5423b5d05b9", + }, .emsdk = .{ .url = "git+https://github.com/emscripten-core/emsdk?ref=3.1.60#ce74ca2b1c968f897150bdc55daa9e3c12a3fefc", .hash = "1220122266f201c6c3a774f7a5edd60e4b1b9655e345bca525360bed2ff9fcb479a8", diff --git a/dub.sdl b/dub.sdl index f07686b..e5e08da 100644 --- a/dub.sdl +++ b/dub.sdl @@ -40,7 +40,7 @@ subPackage { libs "user32" "gdi32" "kernel32" "dxgi" "d3d11" "ole32" platform="windows" lflags "-Lzig-out/lib" platform="posix" lflags "/LIBPATH:zig-out/lib" platform="windows" - excludedSourceFiles "src/examples/sgl_context.d" "src/examples/triangle.d" "src/examples/sgl_points.d" "src/examples/saudio.d" "src/examples/debugtext.d" "src/examples/mrt.d" "src/examples/user_data.d" "src/examples/cube.d" "src/examples/blend.d" "src/shaders/*.d" + excludedSourceFiles "src/examples/sgl_context.d" "src/examples/triangle.d" "src/examples/sgl_points.d" "src/examples/saudio.d" "src/examples/debugtext.d" "src/examples/mrt.d" "src/examples/user_data.d" "src/examples/cube.d" "src/examples/blend.d" "src/examples/imgui.d" "src/examples/droptest.d" "src/shaders/*.d" preBuildCommands "zig build -Doptimize=ReleaseFast -Dartifact" } subPackage { @@ -55,7 +55,7 @@ subPackage { libs "user32" "gdi32" "kernel32" "dxgi" "d3d11" "ole32" platform="windows" lflags "-Lzig-out/lib" platform="posix" lflags "/LIBPATH:zig-out/lib" platform="windows" - excludedSourceFiles "src/examples/clear.d" "src/examples/triangle.d" "src/examples/sgl_points.d" "src/examples/saudio.d" "src/examples/debugtext.d" "src/examples/mrt.d" "src/examples/user_data.d" "src/examples/cube.d" "src/examples/blend.d" "src/shaders/*.d" + excludedSourceFiles "src/examples/clear.d" "src/examples/triangle.d" "src/examples/sgl_points.d" "src/examples/saudio.d" "src/examples/debugtext.d" "src/examples/mrt.d" "src/examples/user_data.d" "src/examples/cube.d" "src/examples/blend.d" "src/examples/imgui.d" "src/examples/droptest.d" "src/shaders/*.d" preBuildCommands "zig build -Doptimize=ReleaseFast -Dartifact" } subPackage { @@ -70,7 +70,7 @@ subPackage { libs "user32" "gdi32" "kernel32" "dxgi" "d3d11" "ole32" platform="windows" lflags "-Lzig-out/lib" platform="posix" lflags "/LIBPATH:zig-out/lib" platform="windows" - excludedSourceFiles "src/examples/clear.d" "src/examples/triangle.d" "src/examples/sgl_context.d" "src/examples/saudio.d" "src/examples/debugtext.d" "src/examples/mrt.d" "src/examples/user_data.d" "src/examples/cube.d" "src/examples/blend.d" "src/shaders/*.d" + excludedSourceFiles "src/examples/clear.d" "src/examples/triangle.d" "src/examples/sgl_context.d" "src/examples/saudio.d" "src/examples/debugtext.d" "src/examples/mrt.d" "src/examples/user_data.d" "src/examples/cube.d" "src/examples/blend.d" "src/examples/imgui.d" "src/examples/droptest.d" "src/shaders/*.d" preBuildCommands "zig build -Doptimize=ReleaseFast -Dartifact" } subPackage { @@ -85,7 +85,7 @@ subPackage { libs "user32" "gdi32" "kernel32" "dxgi" "d3d11" "ole32" platform="windows" lflags "-Lzig-out/lib" platform="posix" lflags "/LIBPATH:zig-out/lib" platform="windows" - excludedSourceFiles "src/examples/sgl_context.d" "src/examples/triangle.d" "src/examples/sgl_points.d" "src/examples/saudio.d" "src/examples/clear.d" "src/examples/mrt.d" "src/examples/user_data.d" "src/examples/cube.d" "src/examples/blend.d" "src/shaders/*.d" + excludedSourceFiles "src/examples/sgl_context.d" "src/examples/triangle.d" "src/examples/sgl_points.d" "src/examples/saudio.d" "src/examples/clear.d" "src/examples/mrt.d" "src/examples/user_data.d" "src/examples/cube.d" "src/examples/blend.d" "src/examples/imgui.d" "src/examples/droptest.d" "src/shaders/*.d" preBuildCommands "zig build -Doptimize=ReleaseFast -Dartifact" } subPackage { @@ -100,7 +100,7 @@ subPackage { libs "user32" "gdi32" "kernel32" "dxgi" "d3d11" "ole32" platform="windows" lflags "-Lzig-out/lib" platform="posix" lflags "/LIBPATH:zig-out/lib" platform="windows" - excludedSourceFiles "src/examples/sgl_context.d" "src/examples/clear.d" "src/examples/sgl_points.d" "src/examples/saudio.d" "src/examples/debugtext.d" "src/examples/mrt.d" "src/examples/user_data.d" "src/examples/cube.d" "src/examples/blend.d" "src/shaders/*.d" + excludedSourceFiles "src/examples/sgl_context.d" "src/examples/clear.d" "src/examples/sgl_points.d" "src/examples/saudio.d" "src/examples/debugtext.d" "src/examples/mrt.d" "src/examples/user_data.d" "src/examples/cube.d" "src/examples/blend.d" "src/examples/imgui.d" "src/examples/droptest.d" "src/shaders/*.d" preBuildCommands "zig build -Doptimize=ReleaseFast -Dartifact" } subPackage { @@ -115,7 +115,7 @@ subPackage { libs "user32" "gdi32" "kernel32" "dxgi" "d3d11" "ole32" platform="windows" lflags "-Lzig-out/lib" platform="posix" lflags "/LIBPATH:zig-out/lib" platform="windows" - excludedSourceFiles "src/examples/sgl_context.d" "src/examples/clear.d" "src/examples/saudio.d" "src/examples/debugtext.d" "src/examples/mrt.d" "src/examples/user_data.d" "src/examples/cube.d" "src/examples/triangle.d" "src/examples/sgl_points.d" "src/shaders/*.d" + excludedSourceFiles "src/examples/sgl_context.d" "src/examples/clear.d" "src/examples/saudio.d" "src/examples/debugtext.d" "src/examples/mrt.d" "src/examples/user_data.d" "src/examples/cube.d" "src/examples/triangle.d" "src/examples/sgl_points.d" "src/examples/imgui.d" "src/examples/droptest.d" "src/shaders/*.d" preBuildCommands "zig build -Doptimize=ReleaseFast -Dartifact" } subPackage { @@ -130,7 +130,7 @@ subPackage { libs "user32" "gdi32" "kernel32" "dxgi" "d3d11" "ole32" platform="windows" lflags "-Lzig-out/lib" platform="posix" lflags "/LIBPATH:zig-out/lib" platform="windows" - excludedSourceFiles "src/examples/sgl_context.d" "src/examples/clear.d" "src/examples/triangle.d" "src/examples/sgl_points.d" "src/examples/debugtext.d" "src/examples/mrt.d" "src/examples/user_data.d" "src/examples/cube.d" "src/examples/blend.d" "src/shaders/*.d" + excludedSourceFiles "src/examples/sgl_context.d" "src/examples/clear.d" "src/examples/triangle.d" "src/examples/sgl_points.d" "src/examples/debugtext.d" "src/examples/mrt.d" "src/examples/user_data.d" "src/examples/cube.d" "src/examples/blend.d" "src/examples/imgui.d" "src/examples/droptest.d" "src/shaders/*.d" preBuildCommands "zig build -Doptimize=ReleaseFast -Dartifact" } subPackage { @@ -145,7 +145,7 @@ subPackage { libs "user32" "gdi32" "kernel32" "dxgi" "d3d11" "ole32" platform="windows" lflags "-Lzig-out/lib" platform="posix" lflags "/LIBPATH:zig-out/lib" platform="windows" - excludedSourceFiles "src/examples/sgl_context.d" "src/examples/clear.d" "src/examples/saudio.d" "src/examples/debugtext.d" "src/examples/triangle.d" "src/examples/sgl_points.d" "src/examples/user_data.d" "src/examples/cube.d" "src/examples/blend.d" "src/shaders/*.d" + excludedSourceFiles "src/examples/sgl_context.d" "src/examples/clear.d" "src/examples/saudio.d" "src/examples/debugtext.d" "src/examples/triangle.d" "src/examples/sgl_points.d" "src/examples/user_data.d" "src/examples/cube.d" "src/examples/blend.d" "src/examples/imgui.d" "src/examples/droptest.d" "src/shaders/*.d" preBuildCommands "zig build -Doptimize=ReleaseFast -Dartifact" } subPackage { @@ -160,7 +160,7 @@ subPackage { libs "user32" "gdi32" "kernel32" "dxgi" "d3d11" "ole32" platform="windows" lflags "-Lzig-out/lib" platform="posix" lflags "/LIBPATH:zig-out/lib" platform="windows" - excludedSourceFiles "src/examples/sgl_context.d" "src/examples/clear.d" "src/examples/saudio.d" "src/examples/debugtext.d" "src/examples/triangle.d" "src/examples/sgl_points.d" "src/examples/user_data.d" "src/examples/mrt.d" "src/examples/blend.d" "src/shaders/*.d" + excludedSourceFiles "src/examples/sgl_context.d" "src/examples/clear.d" "src/examples/saudio.d" "src/examples/debugtext.d" "src/examples/triangle.d" "src/examples/sgl_points.d" "src/examples/user_data.d" "src/examples/mrt.d" "src/examples/blend.d" "src/examples/imgui.d" "src/examples/droptest.d" "src/shaders/*.d" preBuildCommands "zig build -Doptimize=ReleaseFast -Dartifact" } subPackage { @@ -175,6 +175,28 @@ subPackage { libs "user32" "gdi32" "kernel32" "dxgi" "d3d11" "ole32" platform="windows" lflags "-Lzig-out/lib" platform="posix" lflags "/LIBPATH:zig-out/lib" platform="windows" - excludedSourceFiles "src/examples/sgl_context.d" "src/examples/clear.d" "src/examples/saudio.d" "src/examples/debugtext.d" "src/examples/triangle.d" "src/examples/sgl_points.d" "src/examples/cube.d" "src/examples/mrt.d" "src/examples/blend.d" "src/shaders/*.d" + excludedSourceFiles "src/examples/sgl_context.d" "src/examples/clear.d" "src/examples/saudio.d" "src/examples/debugtext.d" "src/examples/triangle.d" "src/examples/sgl_points.d" "src/examples/cube.d" "src/examples/mrt.d" "src/examples/blend.d" "src/examples/imgui.d" "src/examples/droptest.d" "src/shaders/*.d" preBuildCommands "zig build -Doptimize=ReleaseFast -Dartifact" } +subPackage { + name "imgui" + targetType "executable" + targetPath "build" + sourceFiles "src/examples/imgui.d" + libs "sokol" + lflags "-Lzig-out/lib" platform="posix" + lflags "/LIBPATH:zig-out/lib" platform="windows" + excludedSourceFiles "src/examples/sgl_context.d" "src/examples/clear.d" "src/examples/saudio.d" "src/examples/debugtext.d" "src/examples/triangle.d" "src/examples/sgl_points.d" "src/examples/cube.d" "src/examples/mrt.d" "src/examples/blend.d" "src/examples/user_data.d" "src/examples/droptest.d" "src/shaders/*.d" + preBuildCommands "zig build -Dshared -Doptimize=ReleaseFast -Dimgui -Dartifact" +} +subPackage { + name "droptest" + targetType "executable" + targetPath "build" + sourceFiles "src/examples/droptest.d" + libs "sokol" + lflags "-Lzig-out/lib" platform="posix" + lflags "/LIBPATH:zig-out/lib" platform="windows" + excludedSourceFiles "src/examples/sgl_context.d" "src/examples/clear.d" "src/examples/saudio.d" "src/examples/debugtext.d" "src/examples/triangle.d" "src/examples/sgl_points.d" "src/examples/cube.d" "src/examples/mrt.d" "src/examples/blend.d" "src/examples/user_data.d" "src/examples/imgui.d" "src/shaders/*.d" + preBuildCommands "zig build -Dshared -Doptimize=ReleaseFast -Dimgui -Dartifact" +} diff --git a/src/examples/droptest.d b/src/examples/droptest.d new file mode 100644 index 0000000..24bd68c --- /dev/null +++ b/src/examples/droptest.d @@ -0,0 +1,370 @@ +//------------------------------------------------------------------------------ +// droptest.d +// Test drag'n'drop file loading. +//------------------------------------------------------------------------------ + +module examples.droptest; + +import sg = sokol.gfx; +import sgapp = sokol.glue; +import sapp = sokol.app; +import sfetch = sokol.fetch; +import imgui = sokol.imgui; +import log = sokol.log; + +enum MAX_FILE_SIZE = 1024 * 1024; + +enum LoadState +{ + Unknown = 0, + Success, + Failed, + FileTooBig, +} + +struct State +{ + LoadState load_state = LoadState.Unknown; + uint size = 0; + ubyte[MAX_FILE_SIZE] buffer = 0; +} + +static State state; + +extern (C) void init() @safe @nogc nothrow +{ + sg.Desc gfx = { + environment: sgapp.environment, + logger: {func: &log.slog_func} + }; + sg.setup(gfx); + imgui.Desc imgui_desc = {0}; + imgui.setup(imgui_desc); + + // ifndef emscripten + static if((){version(Emscripten) return false; else return true;}()) + { + sfetch.Desc fetch_desc = { + num_channels: 1, + num_lanes: 1, + logger: {func: &log.slog_func}, + }; + sfetch.setup(fetch_desc); + } +} + +extern (C) void frame() @trusted @nogc nothrow +{ + // ifndef emscripten + static if((){version(Emscripten) return false;else return true;}()) + { + sfetch.dowork; + } + imgui.FrameDesc imgui_desc = { + width: sapp.width(), + height: sapp.height(), + delta_time: sapp.frameDuration(), + dpi_scale: sapp.dpiScale(), + }; + imgui.newFrame(imgui_desc); + + // /*=== UI CODE STARTS HERE ===*/ + const ImVec2 window_pos = {10, 10}; + const ImVec2 window_pos_pivot = {0, 0}; + const ImVec2 window_size = {600, 500}; + igSetNextWindowPos(window_pos, ImGuiCond_.ImGuiCond_Once, window_pos_pivot); + igSetNextWindowSize(window_size, ImGuiCond_.ImGuiCond_Once); + igBegin("Drop a file!".ptr, null, ImGuiWindowFlags_.ImGuiWindowFlags_None); + if(state.load_state != LoadState.Unknown) + { + igText("%s:", sapp.getDroppedFilePath(0)); + } + switch (state.load_state) { + case LoadState.Failed: + igText("LOAD FAILED!"); + break; + case LoadState.FileTooBig: + igText("FILE TOO BIG!"); + break; + case LoadState.Success: + igSeparator; + renderFileContent; + break; + default: + break; + } + igEnd(); + /*=== UI CODE ENDS HERE ===*/ + + sg.Pass pass = {swapchain: sgapp.swapchain}; + sg.beginPass(pass); + imgui.render; + sg.endPass; + sg.commit; +} + +extern (C) void event(const(sapp.Event)* ev) @trusted @nogc nothrow +{ + imgui.simgui_handle_event(ev); + if (ev.type == sapp.EventType.Files_dropped) + { + version (Emscripten) + { + // on emscripten need to use the sokol-app helper function to load the file data + sapp.Html5FetchRequest req = { + dropped_file_index: 0, + callback: &emsc_load_callback, + buffer: {ptr: &state.buffer[0], size: state.buffer.sizeof} + }; + sapp.html5FetchDroppedFile(req); + } + else + { + // native platform: use sokol-fetch to load file content + sfetch.Request req = { + path: sapp.getDroppedFilePath(0), + callback: &native_load_callback, + buffer: {ptr: &state.buffer[0], size: state.buffer.sizeof} + }; + sfetch.send(req); + } + } +} + +extern (C) void cleanup() @safe @nogc nothrow +{ + // ifndef emscripten + static if((){version(Emscripten) return false;else return true;}()) + { + sfetch.shutdown; + } + imgui.shutdown; + sg.shutdown; +} + +extern (C) void main() @safe @nogc nothrow +{ + sapp.Desc runner = { + window_title: "droptest.d", + init_cb: &init, + frame_cb: &frame, + cleanup_cb: &cleanup, + event_cb: &event, + width: 800, + height: 600, + enable_dragndrop: true, + max_dropped_files: 1, + icon: {sokol_default: true}, + logger: {func: &log.func} + }; + sapp.run(runner); +} + +void renderFileContent() @nogc nothrow +{ + immutable int bytes_per_line = 16; // keep this 2^N + immutable int num_lines = (state.size + (bytes_per_line - 1)) / bytes_per_line; + ImVec2 sz = {0, 0}; + igBeginChild_Str("##scrolling", sz, false, ImGuiWindowFlags_.ImGuiWindowFlags_NoMove | ImGuiWindowFlags_ + .ImGuiWindowFlags_NoNav); + ImGuiListClipper* clipper = ImGuiListClipper_ImGuiListClipper(); + ImGuiListClipper_Begin(clipper, num_lines, igGetTextLineHeight()); + ImGuiListClipper_Step(clipper); + for (int line_i = clipper.DisplayStart; line_i < clipper.DisplayEnd; line_i++) + { + int start_offset = line_i * bytes_per_line; + int end_offset = start_offset + bytes_per_line; + if (end_offset >= state.size) + { + end_offset = state.size; + } + igText("%04X: ", start_offset); + for (int i = start_offset; i < end_offset; i++) + { + igSameLine(0.0f, 0.0f); + igText("%02X ", state.buffer[i]); + } + igSameLine((6 * 7.0f) + (bytes_per_line * 3 * 7.0f) + (2 * 7.0f), 0.0f); + for (int i = start_offset; i < end_offset; i++) + { + if (i != start_offset) + { + igSameLine(0.0f, 0.0f); + } + ubyte c = state.buffer[i]; + if ((c < 32) || (c > 127)) + { + c = '.'; + } + igText("%c", c); + } + } + igText("EOF\n"); + ImGuiListClipper_End(clipper); + ImGuiListClipper_destroy(clipper); + igEndChild(); +} + +version (Emscripten) +{ + // the async-loading callback for sapp_html5_fetch_dropped_file + extern (C) void emsc_load_callback(const (sapp.Html5FetchResponse*) response) @nogc nothrow + { + if (response.succeeded) + { + state.load_state = LoadState.Success; + state.size = cast(uint) response.data.size; + } + else if (response.error_code == sapp.Html5FetchError.Fetch_error_buffer_too_small) + { + state.load_state = LoadState.FileTooBig; + } + else + { + state.load_state = LoadState.Failed; + } + } +} +else +{ + // the async-loading callback for native platforms + extern (C) void native_load_callback(const (sfetch.Response*) response) @nogc nothrow + { + if (response.fetched) + { + state.load_state = LoadState.Success; + state.size = cast(uint) response.data.size; + } + else if (response.error_code == sfetch.Error.Buffer_too_small) + { + state.load_state = LoadState.FileTooBig; + } + else + { + state.load_state = LoadState.Failed; + } + } +} + +// -- CIMGUI -- // +@nogc nothrow: +extern (C) bool igBeginChild_Str(scope const(char)* str_id, const ImVec2 size, bool border, ImGuiWindowFlags flags); +extern (C) bool igBegin(const(char)* name, scope bool* p_open, ImGuiWindowFlags flags); +extern (C) void igEnd(); +extern (C) void igSetNextWindowPos(const ImVec2 pos, ImGuiCond cond, const ImVec2 pivot); +extern (C) void igSetNextWindowSize(const ImVec2 size, ImGuiCond cond); +extern (C) void igText(scope const(char)* fmt, ...); +extern (C) void igSameLine(float offset_from_start_x, float spacing); +extern (C) scope ImGuiListClipper* ImGuiListClipper_ImGuiListClipper(); +extern (C) void ImGuiListClipper_destroy(scope ImGuiListClipper* self); +extern (C) void ImGuiListClipper_Begin(scope ImGuiListClipper* self, int items_count, float items_height); +extern (C) void ImGuiListClipper_End(scope ImGuiListClipper* self); +extern (C) void igEndChild(); +extern (C) void igSeparator(); +extern (C) float igGetTextLineHeight(); +extern (C) bool ImGuiListClipper_Step(scope ImGuiListClipper* self); + +extern (C): +struct ImVec2 +{ + float x = 0.0f; + float y = 0.0f; +} + +enum ImGuiColorEditFlags_ +{ + ImGuiColorEditFlags_None = 0, + ImGuiColorEditFlags_NoAlpha = 1 << 1, + ImGuiColorEditFlags_NoPicker = 1 << 2, + ImGuiColorEditFlags_NoOptions = 1 << 3, + ImGuiColorEditFlags_NoSmallPreview = 1 << 4, + ImGuiColorEditFlags_NoInputs = 1 << 5, + ImGuiColorEditFlags_NoTooltip = 1 << 6, + ImGuiColorEditFlags_NoLabel = 1 << 7, + ImGuiColorEditFlags_NoSidePreview = 1 << 8, + ImGuiColorEditFlags_NoDragDrop = 1 << 9, + ImGuiColorEditFlags_NoBorder = 1 << 10, + ImGuiColorEditFlags_AlphaBar = 1 << 16, + ImGuiColorEditFlags_AlphaPreview = 1 << 17, + ImGuiColorEditFlags_AlphaPreviewHalf = 1 << 18, + ImGuiColorEditFlags_HDR = 1 << 19, + ImGuiColorEditFlags_DisplayRGB = 1 << 20, + ImGuiColorEditFlags_DisplayHSV = 1 << 21, + ImGuiColorEditFlags_DisplayHex = 1 << 22, + ImGuiColorEditFlags_Uint8 = 1 << 23, + ImGuiColorEditFlags_Float = 1 << 24, + ImGuiColorEditFlags_PickerHueBar = 1 << 25, + ImGuiColorEditFlags_PickerHueWheel = 1 << 26, + ImGuiColorEditFlags_InputRGB = 1 << 27, + ImGuiColorEditFlags_InputHSV = 1 << 28, + ImGuiColorEditFlags_DefaultOptions_ + = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayRGB + | ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_PickerHueBar, + ImGuiColorEditFlags_DisplayMask_ = ImGuiColorEditFlags_DisplayRGB + | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_DisplayHex, + ImGuiColorEditFlags_DataTypeMask_ = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_Float, + ImGuiColorEditFlags_PickerMask_ + = ImGuiColorEditFlags_PickerHueWheel | ImGuiColorEditFlags_PickerHueBar, + ImGuiColorEditFlags_InputMask_ = ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_InputHSV +} + +enum ImGuiWindowFlags_ +{ + ImGuiWindowFlags_None = 0, + ImGuiWindowFlags_NoTitleBar = 1 << 0, + ImGuiWindowFlags_NoResize = 1 << 1, + ImGuiWindowFlags_NoMove = 1 << 2, + ImGuiWindowFlags_NoScrollbar = 1 << 3, + ImGuiWindowFlags_NoScrollWithMouse = 1 << 4, + ImGuiWindowFlags_NoCollapse = 1 << 5, + ImGuiWindowFlags_AlwaysAutoResize = 1 << 6, + ImGuiWindowFlags_NoBackground = 1 << 7, + ImGuiWindowFlags_NoSavedSettings = 1 << 8, + ImGuiWindowFlags_NoMouseInputs = 1 << 9, + ImGuiWindowFlags_MenuBar = 1 << 10, + ImGuiWindowFlags_HorizontalScrollbar = 1 << 11, + ImGuiWindowFlags_NoFocusOnAppearing = 1 << 12, + ImGuiWindowFlags_NoBringToFrontOnFocus = 1 << 13, + ImGuiWindowFlags_AlwaysVerticalScrollbar = 1 << 14, + ImGuiWindowFlags_AlwaysHorizontalScrollbar = 1 << 15, + ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 16, + ImGuiWindowFlags_NoNavInputs = 1 << 18, + ImGuiWindowFlags_NoNavFocus = 1 << 19, + ImGuiWindowFlags_UnsavedDocument = 1 << 20, + ImGuiWindowFlags_NoNav = ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, + ImGuiWindowFlags_NoDecoration = ImGuiWindowFlags_NoTitleBar + | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse, + ImGuiWindowFlags_NoInputs = ImGuiWindowFlags_NoMouseInputs + | ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, + ImGuiWindowFlags_NavFlattened = 1 << 23, + ImGuiWindowFlags_ChildWindow = 1 << 24, + ImGuiWindowFlags_Tooltip = 1 << 25, + ImGuiWindowFlags_Popup = 1 << 26, + ImGuiWindowFlags_Modal = 1 << 27, + ImGuiWindowFlags_ChildMenu = 1 << 28 +} + +enum ImGuiCond_ +{ + ImGuiCond_None = 0, + ImGuiCond_Always = 1 << 0, + ImGuiCond_Once = 1 << 1, + ImGuiCond_FirstUseEver = 1 << 2, + ImGuiCond_Appearing = 1 << 3 +} + +alias ImGuiCond = int; +alias ImGuiColorEditFlags = int; +alias ImGuiWindowFlags = int; +struct ImGuiListClipper +{ + ImGuiContext* Ctx = null; + int DisplayStart = 0; + int DisplayEnd = 0; + int ItemsCount = 0; + float ItemsHeight = 0.0f; + float StartPosY = 0.0f; + void* TempData = null; +} + +struct ImGuiContext; diff --git a/src/examples/imgui.d b/src/examples/imgui.d new file mode 100644 index 0000000..c58f7c8 --- /dev/null +++ b/src/examples/imgui.d @@ -0,0 +1,203 @@ +//------------------------------------------------------------------------------ +// imgui.d +// +// Using cimgui + sokol, based on https://github.com/floooh/sokol-zig-imgui-sample +//------------------------------------------------------------------------------ + +module examples.imgui; + +import sg = sokol.gfx; +import sgapp = sokol.glue; +import sapp = sokol.app; +import imgui = sokol.imgui; +import log = sokol.log; + +extern (C): +@safe: + +struct State +{ + sg.PassAction pass_action = { + colors: [ + { + load_action: sg.LoadAction.Clear, + clear_value: {r: 0.0, g: 0.5, b: 1.0, a: 1.0}, + } + ] + }; +} + +static State state; + +void init() @safe @nogc nothrow +{ + sg.Desc gfx = { + environment: sgapp.environment, + logger: {func: &log.slog_func} + }; + sg.setup(gfx); + imgui.Desc imgui_desc = {0}; + imgui.setup(imgui_desc); +} + +void frame() @trusted @nogc nothrow +{ + imgui.FrameDesc imgui_desc = { + width: sapp.width(), + height: sapp.height(), + delta_time: sapp.frameDuration(), + dpi_scale: sapp.dpiScale(), + }; + imgui.newFrame(imgui_desc); + + /*=== UI CODE STARTS HERE ===*/ + const ImVec2 window_pos = {10, 10}; + const ImVec2 window_pos_pivot = {0, 0}; + const ImVec2 window_size = {400, 100}; + igSetNextWindowPos(window_pos, ImGuiCond_.ImGuiCond_Once, window_pos_pivot); + igSetNextWindowSize(window_size, ImGuiCond_.ImGuiCond_Once); + igBegin("Hello Dear ImGui!".ptr, null, ImGuiWindowFlags_.ImGuiWindowFlags_None); + const(char)* label = "Background"; + float[3] rgb = [ + state.pass_action.colors[0].clear_value.r, + state.pass_action.colors[0].clear_value.g, + state.pass_action.colors[0].clear_value.b + ]; + igColorEdit3(label, rgb, ImGuiColorEditFlags_.ImGuiColorEditFlags_None); + igEnd(); + /*=== UI CODE ENDS HERE ===*/ + + sg.Pass pass = {action: state.pass_action, swapchain: sgapp.swapchain}; + sg.beginPass(pass); + imgui.render; + sg.endPass; + sg.commit; +} + +void event(const(sapp.Event)* ev) @trusted @nogc nothrow +{ + imgui.simgui_handle_event(ev); +} + +void cleanup() @safe @nogc nothrow +{ + imgui.shutdown; + sg.shutdown; +} + +void main() @safe @nogc nothrow +{ + sapp.Desc runner = { + window_title: "imgui.d", + init_cb: &init, + frame_cb: &frame, + cleanup_cb: &cleanup, + event_cb: &event, + width: 800, + height: 600, + icon: {sokol_default: true}, + logger: {func: &log.func} + }; + sapp.run(runner); +} + +// -- CIMGUI -- // +@nogc nothrow: +extern (C) bool igBegin(scope const(char)* name, scope bool* p_open, ImGuiWindowFlags flags); +extern (C) void igEnd(); +extern (C) void igSetNextWindowPos(const ImVec2 pos, ImGuiCond cond, const ImVec2 pivot); +extern (C) void igSetNextWindowSize(const ImVec2 size, ImGuiCond cond); +extern (C) bool igColorEdit3(scope const(char)* label, ref float[3] col, ImGuiColorEditFlags flags); + +extern (C): +struct ImVec2 +{ + float x = 0.0f; + float y = 0.0f; +} + +enum ImGuiColorEditFlags_ +{ + ImGuiColorEditFlags_None = 0, + ImGuiColorEditFlags_NoAlpha = 1 << 1, + ImGuiColorEditFlags_NoPicker = 1 << 2, + ImGuiColorEditFlags_NoOptions = 1 << 3, + ImGuiColorEditFlags_NoSmallPreview = 1 << 4, + ImGuiColorEditFlags_NoInputs = 1 << 5, + ImGuiColorEditFlags_NoTooltip = 1 << 6, + ImGuiColorEditFlags_NoLabel = 1 << 7, + ImGuiColorEditFlags_NoSidePreview = 1 << 8, + ImGuiColorEditFlags_NoDragDrop = 1 << 9, + ImGuiColorEditFlags_NoBorder = 1 << 10, + ImGuiColorEditFlags_AlphaBar = 1 << 16, + ImGuiColorEditFlags_AlphaPreview = 1 << 17, + ImGuiColorEditFlags_AlphaPreviewHalf = 1 << 18, + ImGuiColorEditFlags_HDR = 1 << 19, + ImGuiColorEditFlags_DisplayRGB = 1 << 20, + ImGuiColorEditFlags_DisplayHSV = 1 << 21, + ImGuiColorEditFlags_DisplayHex = 1 << 22, + ImGuiColorEditFlags_Uint8 = 1 << 23, + ImGuiColorEditFlags_Float = 1 << 24, + ImGuiColorEditFlags_PickerHueBar = 1 << 25, + ImGuiColorEditFlags_PickerHueWheel = 1 << 26, + ImGuiColorEditFlags_InputRGB = 1 << 27, + ImGuiColorEditFlags_InputHSV = 1 << 28, + ImGuiColorEditFlags_DefaultOptions_ + = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayRGB + | ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_PickerHueBar, + ImGuiColorEditFlags_DisplayMask_ = ImGuiColorEditFlags_DisplayRGB + | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_DisplayHex, + ImGuiColorEditFlags_DataTypeMask_ = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_Float, + ImGuiColorEditFlags_PickerMask_ + = ImGuiColorEditFlags_PickerHueWheel | ImGuiColorEditFlags_PickerHueBar, + ImGuiColorEditFlags_InputMask_ = ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_InputHSV +} + +enum ImGuiWindowFlags_ +{ + ImGuiWindowFlags_None = 0, + ImGuiWindowFlags_NoTitleBar = 1 << 0, + ImGuiWindowFlags_NoResize = 1 << 1, + ImGuiWindowFlags_NoMove = 1 << 2, + ImGuiWindowFlags_NoScrollbar = 1 << 3, + ImGuiWindowFlags_NoScrollWithMouse = 1 << 4, + ImGuiWindowFlags_NoCollapse = 1 << 5, + ImGuiWindowFlags_AlwaysAutoResize = 1 << 6, + ImGuiWindowFlags_NoBackground = 1 << 7, + ImGuiWindowFlags_NoSavedSettings = 1 << 8, + ImGuiWindowFlags_NoMouseInputs = 1 << 9, + ImGuiWindowFlags_MenuBar = 1 << 10, + ImGuiWindowFlags_HorizontalScrollbar = 1 << 11, + ImGuiWindowFlags_NoFocusOnAppearing = 1 << 12, + ImGuiWindowFlags_NoBringToFrontOnFocus = 1 << 13, + ImGuiWindowFlags_AlwaysVerticalScrollbar = 1 << 14, + ImGuiWindowFlags_AlwaysHorizontalScrollbar = 1 << 15, + ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 16, + ImGuiWindowFlags_NoNavInputs = 1 << 18, + ImGuiWindowFlags_NoNavFocus = 1 << 19, + ImGuiWindowFlags_UnsavedDocument = 1 << 20, + ImGuiWindowFlags_NoNav = ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, + ImGuiWindowFlags_NoDecoration = ImGuiWindowFlags_NoTitleBar + | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse, + ImGuiWindowFlags_NoInputs = ImGuiWindowFlags_NoMouseInputs + | ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, + ImGuiWindowFlags_NavFlattened = 1 << 23, + ImGuiWindowFlags_ChildWindow = 1 << 24, + ImGuiWindowFlags_Tooltip = 1 << 25, + ImGuiWindowFlags_Popup = 1 << 26, + ImGuiWindowFlags_Modal = 1 << 27, + ImGuiWindowFlags_ChildMenu = 1 << 28 +} + +enum ImGuiCond_ +{ + ImGuiCond_None = 0, + ImGuiCond_Always = 1 << 0, + ImGuiCond_Once = 1 << 1, + ImGuiCond_FirstUseEver = 1 << 2, + ImGuiCond_Appearing = 1 << 3 +} + +alias ImGuiCond = int; +alias ImGuiColorEditFlags = int; +alias ImGuiWindowFlags = int; diff --git a/src/sokol/c/sokol_fetch.c b/src/sokol/c/sokol_fetch.c new file mode 100644 index 0000000..9e2415d --- /dev/null +++ b/src/sokol/c/sokol_fetch.c @@ -0,0 +1,5 @@ +#if defined(IMPL) +#define SOKOL_FETCH_IMPL +#endif +#include "sokol_defines.h" +#include "sokol_fetch.h" \ No newline at end of file diff --git a/src/sokol/c/sokol_fetch.h b/src/sokol/c/sokol_fetch.h new file mode 100644 index 0000000..f94f5c8 --- /dev/null +++ b/src/sokol/c/sokol_fetch.h @@ -0,0 +1,2809 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_FETCH_IMPL) +#define SOKOL_FETCH_IMPL +#endif +#ifndef SOKOL_FETCH_INCLUDED +/* + sokol_fetch.h -- asynchronous data loading/streaming + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_FETCH_IMPL + before you include this file in *one* C or C++ file to create the + implementation. + + Optionally provide the following defines with your own implementations: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) + SOKOL_FETCH_API_DECL - public function declaration prefix (default: extern) + SOKOL_API_DECL - same as SOKOL_FETCH_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + SFETCH_MAX_PATH - max length of UTF-8 filesystem path / URL (default: 1024 bytes) + SFETCH_MAX_USERDATA_UINT64 - max size of embedded userdata in number of uint64_t, userdata + will be copied into an 8-byte aligned memory region associated + with each in-flight request, default value is 16 (== 128 bytes) + SFETCH_MAX_CHANNELS - max number of IO channels (default is 16, also see sfetch_desc_t.num_channels) + + If sokol_fetch.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_FETCH_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + NOTE: The following documentation talks a lot about "IO threads". Actual + threads are only used on platforms where threads are available. The web + version (emscripten/wasm) doesn't use POSIX-style threads, but instead + asynchronous Javascript calls chained together by callbacks. The actual + source code differences between the two approaches have been kept to + a minimum though. + + FEATURE OVERVIEW + ================ + + - Asynchronously load complete files, or stream files incrementally via + HTTP (on web platform), or the local file system (on native platforms) + + - Request / response-callback model, user code sends a request + to initiate a file-load, sokol_fetch.h calls the response callback + on the same thread when data is ready or user-code needs + to respond otherwise + + - Not limited to the main-thread or a single thread: A sokol-fetch + "context" can live on any thread, and multiple contexts + can operate side-by-side on different threads. + + - Memory management for data buffers is under full control of user code. + sokol_fetch.h won't allocate memory after it has been setup. + + - Automatic rate-limiting guarantees that only a maximum number of + requests is processed at any one time, allowing a zero-allocation + model, where all data is streamed into fixed-size, pre-allocated + buffers. + + - Active Requests can be paused, continued and cancelled from anywhere + in the user-thread which sent this request. + + + TL;DR EXAMPLE CODE + ================== + This is the most-simple example code to load a single data file with a + known maximum size: + + (1) initialize sokol-fetch with default parameters (but NOTE that the + default setup parameters provide a safe-but-slow "serialized" + operation). In order to see any logging output in case or errors + you should always provide a logging function + (such as 'slog_func' from sokol_log.h): + + sfetch_setup(&(sfetch_desc_t){ .logger.func = slog_func }); + + (2) send a fetch-request to load a file from the current directory + into a buffer big enough to hold the entire file content: + + static uint8_t buf[MAX_FILE_SIZE]; + + sfetch_send(&(sfetch_request_t){ + .path = "my_file.txt", + .callback = response_callback, + .buffer = { + .ptr = buf, + .size = sizeof(buf) + } + }); + + If 'buf' is a value (e.g. an array or struct item), the .buffer item can + be initialized with the SFETCH_RANGE() helper macro: + + sfetch_send(&(sfetch_request_t){ + .path = "my_file.txt", + .callback = response_callback, + .buffer = SFETCH_RANGE(buf) + }); + + (3) write a 'response-callback' function, this will be called whenever + the user-code must respond to state changes of the request + (most importantly when data has been loaded): + + void response_callback(const sfetch_response_t* response) { + if (response->fetched) { + // data has been loaded, and is available via the + // sfetch_range_t struct item 'data': + const void* ptr = response->data.ptr; + size_t num_bytes = response->data.size; + } + if (response->finished) { + // the 'finished'-flag is the catch-all flag for when the request + // is finished, no matter if loading was successful or failed, + // so any cleanup-work should happen here... + ... + if (response->failed) { + // 'failed' is true in (addition to 'finished') if something + // went wrong (file doesn't exist, or less bytes could be + // read from the file than expected) + } + } + } + + (4) pump the sokol-fetch message queues, and invoke response callbacks + by calling: + + sfetch_dowork(); + + In an event-driven app this should be called in the event loop. If you + use sokol-app this would be in your frame_cb function. + + (5) finally, call sfetch_shutdown() at the end of the application: + + There's many other loading-scenarios, for instance one doesn't have to + provide a buffer upfront, this can also happen in the response callback. + + Or it's possible to stream huge files into small fixed-size buffer, + complete with pausing and continuing the download. + + It's also possible to improve the 'pipeline throughput' by fetching + multiple files in parallel, but at the same time limit the maximum + number of requests that can be 'in-flight'. + + For how this all works, please read the following documentation sections :) + + + API DOCUMENTATION + ================= + + void sfetch_setup(const sfetch_desc_t* desc) + -------------------------------------------- + First call sfetch_setup(const sfetch_desc_t*) on any thread before calling + any other sokol-fetch functions on the same thread. + + sfetch_setup() takes a pointer to an sfetch_desc_t struct with setup + parameters. Parameters which should use their default values must + be zero-initialized: + + - max_requests (uint32_t): + The maximum number of requests that can be alive at any time, the + default is 128. + + - num_channels (uint32_t): + The number of "IO channels" used to parallelize and prioritize + requests, the default is 1. + + - num_lanes (uint32_t): + The number of "lanes" on a single channel. Each request which is + currently 'inflight' on a channel occupies one lane until the + request is finished. This is used for automatic rate-limiting + (search below for CHANNELS AND LANES for more details). The + default number of lanes is 1. + + For example, to setup sokol-fetch for max 1024 active requests, 4 channels, + and 8 lanes per channel in C99: + + sfetch_setup(&(sfetch_desc_t){ + .max_requests = 1024, + .num_channels = 4, + .num_lanes = 8 + }); + + sfetch_setup() is the only place where sokol-fetch will allocate memory. + + NOTE that the default setup parameters of 1 channel and 1 lane per channel + has a very poor 'pipeline throughput' since this essentially serializes + IO requests (a new request will only be processed when the last one has + finished), and since each request needs at least one roundtrip between + the user- and IO-thread the throughput will be at most one request per + frame. Search for LATENCY AND THROUGHPUT below for more information on + how to increase throughput. + + NOTE that you can call sfetch_setup() on multiple threads, each thread + will get its own thread-local sokol-fetch instance, which will work + independently from sokol-fetch instances on other threads. + + void sfetch_shutdown(void) + -------------------------- + Call sfetch_shutdown() at the end of the application to stop any + IO threads and free all memory that was allocated in sfetch_setup(). + + sfetch_handle_t sfetch_send(const sfetch_request_t* request) + ------------------------------------------------------------ + Call sfetch_send() to start loading data, the function takes a pointer to an + sfetch_request_t struct with request parameters and returns a + sfetch_handle_t identifying the request for later calls. At least + a path/URL and callback must be provided: + + sfetch_handle_t h = sfetch_send(&(sfetch_request_t){ + .path = "my_file.txt", + .callback = my_response_callback + }); + + sfetch_send() will return an invalid handle if no request can be allocated + from the internal pool because all available request items are 'in-flight'. + + The sfetch_request_t struct contains the following parameters (optional + parameters that are not provided must be zero-initialized): + + - path (const char*, required) + Pointer to an UTF-8 encoded C string describing the filesystem + path or HTTP URL. The string will be copied into an internal data + structure, and passed "as is" (apart from any required + encoding-conversions) to fopen(), CreateFileW() or + XMLHttpRequest. The maximum length of the string is defined by + the SFETCH_MAX_PATH configuration define, the default is 1024 bytes + including the 0-terminator byte. + + - callback (sfetch_callback_t, required) + Pointer to a response-callback function which is called when the + request needs "user code attention". Search below for REQUEST + STATES AND THE RESPONSE CALLBACK for detailed information about + handling responses in the response callback. + + - channel (uint32_t, optional) + Index of the IO channel where the request should be processed. + Channels are used to parallelize and prioritize requests relative + to each other. Search below for CHANNELS AND LANES for more + information. The default channel is 0. + + - chunk_size (uint32_t, optional) + The chunk_size member is used for streaming data incrementally + in small chunks. After 'chunk_size' bytes have been loaded into + to the streaming buffer, the response callback will be called + with the buffer containing the fetched data for the current chunk. + If chunk_size is 0 (the default), than the whole file will be loaded. + Please search below for CHUNK SIZE AND HTTP COMPRESSION for + important information how streaming works if the web server + is serving compressed data. + + - buffer (sfetch_range_t) + This is a optional pointer/size pair describing a chunk of memory where + data will be loaded into (if no buffer is provided upfront, this + must happen in the response callback). If a buffer is provided, + it must be big enough to either hold the entire file (if chunk_size + is zero), or the *uncompressed* data for one downloaded chunk + (if chunk_size is > 0). + + - user_data (sfetch_range_t) + The user_data ptr/size range struct describe an optional POD blob + (plain-old-data) associated with the request which will be copied(!) + into an internal memory block. The maximum default size of this + memory block is 128 bytes (but can be overridden by defining + SFETCH_MAX_USERDATA_UINT64 before including the notification, note + that this define is in "number of uint64_t", not number of bytes). + The user-data block is 8-byte aligned, and will be copied via + memcpy() (so don't put any C++ "smart members" in there). + + NOTE that request handles are strictly thread-local and only unique + within the thread the handle was created on, and all function calls + involving a request handle must happen on that same thread. + + bool sfetch_handle_valid(sfetch_handle_t request) + ------------------------------------------------- + This checks if the provided request handle is valid, and is associated with + a currently active request. It will return false if: + + - sfetch_send() returned an invalid handle because it couldn't allocate + a new request from the internal request pool (because they're all + in flight) + - the request associated with the handle is no longer alive (because + it either finished successfully, or the request failed for some + reason) + + void sfetch_dowork(void) + ------------------------ + Call sfetch_dowork(void) in regular intervals (for instance once per frame) + on the same thread as sfetch_setup() to "turn the gears". If you are sending + requests but never hear back from them in the response callback function, then + the most likely reason is that you forgot to add the call to sfetch_dowork() + in the per-frame function. + + sfetch_dowork() roughly performs the following work: + + - any new requests that have been sent with sfetch_send() since the + last call to sfetch_dowork() will be dispatched to their IO channels + and assigned a free lane. If all lanes on that channel are occupied + by requests 'in flight', incoming requests must wait until + a lane becomes available + + - for all new requests which have been enqueued on a channel which + don't already have a buffer assigned the response callback will be + called with (response->dispatched == true) so that the response + callback can inspect the dynamically assigned lane and bind a buffer + to the request (search below for CHANNELS AND LANE for more info) + + - a state transition from "user side" to "IO thread side" happens for + each new request that has been dispatched to a channel. + + - requests dispatched to a channel are either forwarded into that + channel's worker thread (on native platforms), or cause an HTTP + request to be sent via an asynchronous XMLHttpRequest (on the web + platform) + + - for all requests which have finished their current IO operation a + state transition from "IO thread side" to "user side" happens, + and the response callback is called so that the fetched data + can be processed. + + - requests which are completely finished (either because the entire + file content has been loaded, or they are in the FAILED state) are + freed (this just changes their state in the 'request pool', no actual + memory is freed) + + - requests which are not yet finished are fed back into the + 'incoming' queue of their channel, and the cycle starts again, this + only happens for requests which perform data streaming (not load + the entire file at once). + + void sfetch_cancel(sfetch_handle_t request) + ------------------------------------------- + This cancels a request in the next sfetch_dowork() call and invokes the + response callback with (response.failed == true) and (response.finished + == true) to give user-code a chance to do any cleanup work for the + request. If sfetch_cancel() is called for a request that is no longer + alive, nothing bad will happen (the call will simply do nothing). + + void sfetch_pause(sfetch_handle_t request) + ------------------------------------------ + This pauses an active request in the next sfetch_dowork() call and puts + it into the PAUSED state. For all requests in PAUSED state, the response + callback will be called in each call to sfetch_dowork() to give user-code + a chance to CONTINUE the request (by calling sfetch_continue()). Pausing + a request makes sense for dynamic rate-limiting in streaming scenarios + (like video/audio streaming with a fixed number of streaming buffers. As + soon as all available buffers are filled with download data, downloading + more data must be prevented to allow video/audio playback to catch up and + free up empty buffers for new download data. + + void sfetch_continue(sfetch_handle_t request) + --------------------------------------------- + Continues a paused request, counterpart to the sfetch_pause() function. + + void sfetch_bind_buffer(sfetch_handle_t request, sfetch_range_t buffer) + ---------------------------------------------------------------------------------------- + This "binds" a new buffer (as pointer/size pair) to an active request. The + function *must* be called from inside the response-callback, and there + must not already be another buffer bound. + + void* sfetch_unbind_buffer(sfetch_handle_t request) + --------------------------------------------------- + This removes the current buffer binding from the request and returns + a pointer to the previous buffer (useful if the buffer was dynamically + allocated and it must be freed). + + sfetch_unbind_buffer() *must* be called from inside the response callback. + + The usual code sequence to bind a different buffer in the response + callback might look like this: + + void response_callback(const sfetch_response_t* response) { + if (response.fetched) { + ... + // switch to a different buffer (in the FETCHED state it is + // guaranteed that the request has a buffer, otherwise it + // would have gone into the FAILED state + void* old_buf_ptr = sfetch_unbind_buffer(response.handle); + free(old_buf_ptr); + void* new_buf_ptr = malloc(new_buf_size); + sfetch_bind_buffer(response.handle, new_buf_ptr, new_buf_size); + } + if (response.finished) { + // unbind and free the currently associated buffer, + // the buffer pointer could be null if the request has failed + // NOTE that it is legal to call free() with a nullptr, + // this happens if the request failed to open its file + // and never goes into the OPENED state + void* buf_ptr = sfetch_unbind_buffer(response.handle); + free(buf_ptr); + } + } + + sfetch_desc_t sfetch_desc(void) + ------------------------------- + sfetch_desc() returns a copy of the sfetch_desc_t struct passed to + sfetch_setup(), with zero-initialized values replaced with + their default values. + + int sfetch_max_userdata_bytes(void) + ----------------------------------- + This returns the value of the SFETCH_MAX_USERDATA_UINT64 config + define, but in number of bytes (so SFETCH_MAX_USERDATA_UINT64*8). + + int sfetch_max_path(void) + ------------------------- + Returns the value of the SFETCH_MAX_PATH config define. + + + REQUEST STATES AND THE RESPONSE CALLBACK + ======================================== + A request goes through a number of states during its lifetime. Depending + on the current state of a request, it will be 'owned' either by the + "user-thread" (where the request was sent) or an IO thread. + + You can think of a request as "ping-ponging" between the IO thread and + user thread, any actual IO work is done on the IO thread, while + invocations of the response-callback happen on the user-thread. + + All state transitions and callback invocations happen inside the + sfetch_dowork() function. + + An active request goes through the following states: + + ALLOCATED (user-thread) + + The request has been allocated in sfetch_send() and is + waiting to be dispatched into its IO channel. When this + happens, the request will transition into the DISPATCHED state. + + DISPATCHED (IO thread) + + The request has been dispatched into its IO channel, and a + lane has been assigned to the request. + + If a buffer was provided in sfetch_send() the request will + immediately transition into the FETCHING state and start loading + data into the buffer. + + If no buffer was provided in sfetch_send(), the response + callback will be called with (response->dispatched == true), + so that the response callback can bind a buffer to the + request. Binding the buffer in the response callback makes + sense if the buffer isn't dynamically allocated, but instead + a pre-allocated buffer must be selected from the request's + channel and lane. + + Note that it isn't possible to get a file size in the response callback + which would help with allocating a buffer of the right size, this is + because it isn't possible in HTTP to query the file size before the + entire file is downloaded (...when the web server serves files compressed). + + If opening the file failed, the request will transition into + the FAILED state with the error code SFETCH_ERROR_FILE_NOT_FOUND. + + FETCHING (IO thread) + + While a request is in the FETCHING state, data will be loaded into + the user-provided buffer. + + If no buffer was provided, the request will go into the FAILED + state with the error code SFETCH_ERROR_NO_BUFFER. + + If a buffer was provided, but it is too small to contain the + fetched data, the request will go into the FAILED state with + error code SFETCH_ERROR_BUFFER_TOO_SMALL. + + If less data can be read from the file than expected, the request + will go into the FAILED state with error code SFETCH_ERROR_UNEXPECTED_EOF. + + If loading data into the provided buffer works as expected, the + request will go into the FETCHED state. + + FETCHED (user thread) + + The request goes into the FETCHED state either when the entire file + has been loaded into the provided buffer (when request.chunk_size == 0), + or a chunk has been loaded (and optionally decompressed) into the + buffer (when request.chunk_size > 0). + + The response callback will be called so that the user-code can + process the loaded data using the following sfetch_response_t struct members: + + - data.ptr: pointer to the start of fetched data + - data.size: the number of bytes in the provided buffer + - data_offset: the byte offset of the loaded data chunk in the + overall file (this is only set to a non-zero value in a streaming + scenario) + + Once all file data has been loaded, the 'finished' flag will be set + in the response callback's sfetch_response_t argument. + + After the user callback returns, and all file data has been loaded + (response.finished flag is set) the request has reached its end-of-life + and will recycled. + + Otherwise, if there's still data to load (because streaming was + requested by providing a non-zero request.chunk_size), the request + will switch back to the FETCHING state to load the next chunk of data. + + Note that it is ok to associate a different buffer or buffer-size + with the request by calling sfetch_bind_buffer() in the response-callback. + + To check in the response callback for the FETCHED state, and + independently whether the request is finished: + + void response_callback(const sfetch_response_t* response) { + if (response->fetched) { + // request is in FETCHED state, the loaded data is available + // in .data.ptr, and the number of bytes that have been + // loaded in .data.size: + const void* data = response->data.ptr; + size_t num_bytes = response->data.size; + } + if (response->finished) { + // the finished flag is set either when all data + // has been loaded, the request has been cancelled, + // or the file operation has failed, this is where + // any required per-request cleanup work should happen + } + } + + + FAILED (user thread) + + A request will transition into the FAILED state in the following situations: + + - if the file doesn't exist or couldn't be opened for other + reasons (SFETCH_ERROR_FILE_NOT_FOUND) + - if no buffer is associated with the request in the FETCHING state + (SFETCH_ERROR_NO_BUFFER) + - if the provided buffer is too small to hold the entire file + (if request.chunk_size == 0), or the (potentially decompressed) + partial data chunk (SFETCH_ERROR_BUFFER_TOO_SMALL) + - if less bytes could be read from the file then expected + (SFETCH_ERROR_UNEXPECTED_EOF) + - if a request has been cancelled via sfetch_cancel() + (SFETCH_ERROR_CANCELLED) + + The response callback will be called once after a request goes into + the FAILED state, with the 'response->finished' and + 'response->failed' flags set to true. + + This gives the user-code a chance to cleanup any resources associated + with the request. + + To check for the failed state in the response callback: + + void response_callback(const sfetch_response_t* response) { + if (response->failed) { + // specifically check for the failed state... + } + // or you can do a catch-all check via the finished-flag: + if (response->finished) { + if (response->failed) { + // if more detailed error handling is needed: + switch (response->error_code) { + ... + } + } + } + } + + PAUSED (user thread) + + A request will transition into the PAUSED state after user-code + calls the function sfetch_pause() on the request's handle. Usually + this happens from within the response-callback in streaming scenarios + when the data streaming needs to wait for a data decoder (like + a video/audio player) to catch up. + + While a request is in PAUSED state, the response-callback will be + called in each sfetch_dowork(), so that the user-code can either + continue the request by calling sfetch_continue(), or cancel + the request by calling sfetch_cancel(). + + When calling sfetch_continue() on a paused request, the request will + transition into the FETCHING state. Otherwise if sfetch_cancel() is + called, the request will switch into the FAILED state. + + To check for the PAUSED state in the response callback: + + void response_callback(const sfetch_response_t* response) { + if (response->paused) { + // we can check here whether the request should + // continue to load data: + if (should_continue(response->handle)) { + sfetch_continue(response->handle); + } + } + } + + + CHUNK SIZE AND HTTP COMPRESSION + =============================== + TL;DR: for streaming scenarios, the provided chunk-size must be smaller + than the provided buffer-size because the web server may decide to + serve the data compressed and the chunk-size must be given in 'compressed + bytes' while the buffer receives 'uncompressed bytes'. It's not possible + in HTTP to query the uncompressed size for a compressed download until + that download has finished. + + With vanilla HTTP, it is not possible to query the actual size of a file + without downloading the entire file first (the Content-Length response + header only provides the compressed size). Furthermore, for HTTP + range-requests, the range is given on the compressed data, not the + uncompressed data. So if the web server decides to server the data + compressed, the content-length and range-request parameters don't + correspond to the uncompressed data that's arriving in the sokol-fetch + buffers, and there's no way from JS or WASM to either force uncompressed + downloads (e.g. by setting the Accept-Encoding field), or access the + compressed data. + + This has some implications for sokol_fetch.h, most notably that buffers + can't be provided in the exactly right size, because that size can't + be queried from HTTP before the data is actually downloaded. + + When downloading whole files at once, it is basically expected that you + know the maximum files size upfront through other means (for instance + through a separate meta-data-file which contains the file sizes and + other meta-data for each file that needs to be loaded). + + For streaming downloads the situation is a bit more complicated. These + use HTTP range-requests, and those ranges are defined on the (potentially) + compressed data which the JS/WASM side doesn't have access to. However, + the JS/WASM side only ever sees the uncompressed data, and it's not possible + to query the uncompressed size of a range request before that range request + has finished. + + If the provided buffer is too small to contain the uncompressed data, + the request will fail with error code SFETCH_ERROR_BUFFER_TOO_SMALL. + + + CHANNELS AND LANES + ================== + Channels and lanes are (somewhat artificial) concepts to manage + parallelization, prioritization and rate-limiting. + + Channels can be used to parallelize message processing for better 'pipeline + throughput', and to prioritize requests: user-code could reserve one + channel for streaming downloads which need to run in parallel to other + requests, another channel for "regular" downloads and yet another + high-priority channel which would only be used for small files which need + to start loading immediately. + + Each channel comes with its own IO thread and message queues for pumping + messages in and out of the thread. The channel where a request is + processed is selected manually when sending a message: + + sfetch_send(&(sfetch_request_t){ + .path = "my_file.txt", + .callback = my_response_callback, + .channel = 2 + }); + + The number of channels is configured at startup in sfetch_setup() and + cannot be changed afterwards. + + Channels are completely separate from each other, and a request will + never "hop" from one channel to another. + + Each channel consists of a fixed number of "lanes" for automatic rate + limiting: + + When a request is sent to a channel via sfetch_send(), a "free lane" will + be picked and assigned to the request. The request will occupy this lane + for its entire life time (also while it is paused). If all lanes of a + channel are currently occupied, new requests will need to wait until a + lane becomes unoccupied. + + Since the number of channels and lanes is known upfront, it is guaranteed + that there will never be more than "num_channels * num_lanes" requests + in flight at any one time. + + This guarantee eliminates unexpected load- and memory-spikes when + many requests are sent in very short time, and it allows to pre-allocate + a fixed number of memory buffers which can be reused for the entire + "lifetime" of a sokol-fetch context. + + In the most simple scenario - when a maximum file size is known - buffers + can be statically allocated like this: + + uint8_t buffer[NUM_CHANNELS][NUM_LANES][MAX_FILE_SIZE]; + + Then in the user callback pick a buffer by channel and lane, + and associate it with the request like this: + + void response_callback(const sfetch_response_t* response) { + if (response->dispatched) { + void* ptr = buffer[response->channel][response->lane]; + sfetch_bind_buffer(response->handle, ptr, MAX_FILE_SIZE); + } + ... + } + + + NOTES ON OPTIMIZING PIPELINE LATENCY AND THROUGHPUT + =================================================== + With the default configuration of 1 channel and 1 lane per channel, + sokol_fetch.h will appear to have a shockingly bad loading performance + if several files are loaded. + + This has two reasons: + + (1) all parallelization when loading data has been disabled. A new + request will only be processed, when the last request has finished. + + (2) every invocation of the response-callback adds one frame of latency + to the request, because callbacks will only be called from within + sfetch_dowork() + + sokol-fetch takes a few shortcuts to improve step (2) and reduce + the 'inherent latency' of a request: + + - if a buffer is provided upfront, the response-callback won't be + called in the DISPATCHED state, but start right with the FETCHED state + where data has already been loaded into the buffer + + - there is no separate CLOSED state where the callback is invoked + separately when loading has finished (or the request has failed), + instead the finished and failed flags will be set as part of + the last FETCHED invocation + + This means providing a big-enough buffer to fit the entire file is the + best case, the response callback will only be called once, ideally in + the next frame (or two calls to sfetch_dowork()). + + If no buffer is provided upfront, one frame of latency is added because + the response callback needs to be invoked in the DISPATCHED state so that + the user code can bind a buffer. + + This means the best case for a request without an upfront-provided + buffer is 2 frames (or 3 calls to sfetch_dowork()). + + That's about what can be done to improve the latency for a single request, + but the really important step is to improve overall throughput. If you + need to load thousands of files you don't want that to be completely + serialized. + + The most important action to increase throughput is to increase the + number of lanes per channel. This defines how many requests can be + 'in flight' on a single channel at the same time. The guiding decision + factor for how many lanes you can "afford" is the memory size you want + to set aside for buffers. Each lane needs its own buffer so that + the data loaded for one request doesn't scribble over the data + loaded for another request. + + Here's a simple example of sending 4 requests without upfront buffer + on a channel with 1, 2 and 4 lanes, each line is one frame: + + 1 LANE (8 frames): + Lane 0: + ------------- + REQ 0 DISPATCHED + REQ 0 FETCHED + REQ 1 DISPATCHED + REQ 1 FETCHED + REQ 2 DISPATCHED + REQ 2 FETCHED + REQ 3 DISPATCHED + REQ 3 FETCHED + + Note how the request don't overlap, so they can all use the same buffer. + + 2 LANES (4 frames): + Lane 0: Lane 1: + ------------------------------------ + REQ 0 DISPATCHED REQ 1 DISPATCHED + REQ 0 FETCHED REQ 1 FETCHED + REQ 2 DISPATCHED REQ 3 DISPATCHED + REQ 2 FETCHED REQ 3 FETCHED + + This reduces the overall time to 4 frames, but now you need 2 buffers so + that requests don't scribble over each other. + + 4 LANES (2 frames): + Lane 0: Lane 1: Lane 2: Lane 3: + ---------------------------------------------------------------------------- + REQ 0 DISPATCHED REQ 1 DISPATCHED REQ 2 DISPATCHED REQ 3 DISPATCHED + REQ 0 FETCHED REQ 1 FETCHED REQ 2 FETCHED REQ 3 FETCHED + + Now we're down to the same 'best-case' latency as sending a single + request. + + Apart from the memory requirements for the streaming buffers (which is + under your control), you can be generous with the number of lanes, + they don't add any processing overhead. + + The last option for tweaking latency and throughput is channels. Each + channel works independently from other channels, so while one + channel is busy working through a large number of requests (or one + very long streaming download), you can set aside a high-priority channel + for requests that need to start as soon as possible. + + On platforms with threading support, each channel runs on its own + thread, but this is mainly an implementation detail to work around + the blocking traditional file IO functions, not for performance reasons. + + + MEMORY ALLOCATION OVERRIDE + ========================== + You can override the memory allocation functions at initialization time + like this: + + void* my_alloc(size_t size, void* user_data) { + return malloc(size); + } + + void my_free(void* ptr, void* user_data) { + free(ptr); + } + + ... + sfetch_setup(&(sfetch_desc_t){ + // ... + .allocator = { + .alloc_fn = my_alloc, + .free_fn = my_free, + .user_data = ..., + } + }); + ... + + If no overrides are provided, malloc and free will be used. + + This only affects memory allocation calls done by sokol_fetch.h + itself though, not any allocations in OS libraries. + + Memory allocation will only happen on the same thread where sfetch_setup() + was called, so you don't need to worry about thread-safety. + + + 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" + + sfetch_setup(&(sfetch_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. 'sfetch' + uint32_t log_level, // 0=panic, 1=error, 2=warn, 3=info + uint32_t log_item_id, // SFETCH_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_fetch.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data) + { + ... + } + + ...and then setup sokol-fetch like this: + + sfetch_setup(&(sfetch_desc_t){ + .logger = { + .func = my_log, + .user_data = my_user_data, + } + }); + + 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. + + + FUTURE PLANS / V2.0 IDEA DUMP + ============================= + - An optional polling API (as alternative to callback API) + - Move buffer-management into the API? The "manual management" + can be quite tricky especially for dynamic allocation scenarios, + API support for buffer management would simplify cases like + preventing that requests scribble over each other's buffers, or + an automatic garbage collection for dynamically allocated buffers, + or automatically falling back to dynamic allocation if static + buffers aren't big enough. + - Pluggable request handlers to load data from other "sources" + (especially HTTP downloads on native platforms via e.g. libcurl + would be useful) + - I'm currently not happy how the user-data block is handled, this + should getting and updating the user-data should be wrapped by + API functions (similar to bind/unbind buffer) + + + LICENSE + ======= + zlib/libpng license + + Copyright (c) 2019 Andre Weissflog + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#define SOKOL_FETCH_INCLUDED (1) +#include // size_t +#include +#include + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_FETCH_API_DECL) +#define SOKOL_FETCH_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_FETCH_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_FETCH_IMPL) +#define SOKOL_FETCH_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_FETCH_API_DECL __declspec(dllimport) +#else +#define SOKOL_FETCH_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + sfetch_log_item_t + + Log items are defined via X-Macros, and expanded to an + enum 'sfetch_log_item', and in debug mode only, + corresponding strings. + + Used as parameter in the logging callback. +*/ +#define _SFETCH_LOG_ITEMS \ + _SFETCH_LOGITEM_XMACRO(OK, "Ok") \ + _SFETCH_LOGITEM_XMACRO(MALLOC_FAILED, "memory allocation failed") \ + _SFETCH_LOGITEM_XMACRO(FILE_PATH_UTF8_DECODING_FAILED, "failed converting file path from UTF8 to wide") \ + _SFETCH_LOGITEM_XMACRO(SEND_QUEUE_FULL, "send queue full (adjust via sfetch_desc_t.max_requests)") \ + _SFETCH_LOGITEM_XMACRO(REQUEST_CHANNEL_INDEX_TOO_BIG, "channel index too big (adjust via sfetch_desc_t.num_channels)") \ + _SFETCH_LOGITEM_XMACRO(REQUEST_PATH_IS_NULL, "file path is nullptr (sfetch_request_t.path)") \ + _SFETCH_LOGITEM_XMACRO(REQUEST_PATH_TOO_LONG, "file path is too long (SFETCH_MAX_PATH)") \ + _SFETCH_LOGITEM_XMACRO(REQUEST_CALLBACK_MISSING, "no callback provided (sfetch_request_t.callback)") \ + _SFETCH_LOGITEM_XMACRO(REQUEST_CHUNK_SIZE_GREATER_BUFFER_SIZE, "chunk size is greater buffer size (sfetch_request_t.chunk_size vs .buffer.size)") \ + _SFETCH_LOGITEM_XMACRO(REQUEST_USERDATA_PTR_IS_SET_BUT_USERDATA_SIZE_IS_NULL, "user data ptr is set but user data size is null (sfetch_request_t.user_data.ptr vs .size)") \ + _SFETCH_LOGITEM_XMACRO(REQUEST_USERDATA_PTR_IS_NULL_BUT_USERDATA_SIZE_IS_NOT, "user data ptr is null but size is not (sfetch_request_t.user_data.ptr vs .size)") \ + _SFETCH_LOGITEM_XMACRO(REQUEST_USERDATA_SIZE_TOO_BIG, "user data size too big (see SFETCH_MAX_USERDATA_UINT64)") \ + _SFETCH_LOGITEM_XMACRO(CLAMPING_NUM_CHANNELS_TO_MAX_CHANNELS, "clamping num channels to SFETCH_MAX_CHANNELS") \ + _SFETCH_LOGITEM_XMACRO(REQUEST_POOL_EXHAUSTED, "request pool exhausted (tweak via sfetch_desc_t.max_requests)") \ + +#define _SFETCH_LOGITEM_XMACRO(item,msg) SFETCH_LOGITEM_##item, +typedef enum sfetch_log_item_t { + _SFETCH_LOG_ITEMS +} sfetch_log_item_t; +#undef _SFETCH_LOGITEM_XMACRO + +/* + sfetch_logger_t + + Used in sfetch_desc_t to provide a custom logging and error reporting + callback to sokol-fetch. +*/ +typedef struct sfetch_logger_t { + void (*func)( + const char* tag, // always "sfetch" + uint32_t log_level, // 0=panic, 1=error, 2=warning, 3=info + uint32_t log_item_id, // SFETCH_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_fetch.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data); + void* user_data; +} sfetch_logger_t; + +/* + sfetch_range_t + + A pointer-size pair struct to pass memory ranges into and out of sokol-fetch. + When initialized from a value type (array or struct) you can use the + SFETCH_RANGE() helper macro to build an sfetch_range_t struct. +*/ +typedef struct sfetch_range_t { + const void* ptr; + size_t size; +} sfetch_range_t; + +// 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 +#endif +#if defined(__cplusplus) +#define SFETCH_RANGE(x) sfetch_range_t{ &x, sizeof(x) } +#else +#define SFETCH_RANGE(x) (sfetch_range_t){ &x, sizeof(x) } +#endif + +/* + sfetch_allocator_t + + Used in sfetch_desc_t to provide custom memory-alloc and -free functions + to sokol_fetch.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). +*/ +typedef struct sfetch_allocator_t { + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); + void* user_data; +} sfetch_allocator_t; + +/* configuration values for sfetch_setup() */ +typedef struct sfetch_desc_t { + uint32_t max_requests; // max number of active requests across all channels (default: 128) + uint32_t num_channels; // number of channels to fetch requests in parallel (default: 1) + uint32_t num_lanes; // max number of requests active on the same channel (default: 1) + sfetch_allocator_t allocator; // optional memory allocation overrides (default: malloc/free) + sfetch_logger_t logger; // optional log function overrides (default: NO LOGGING!) +} sfetch_desc_t; + +/* a request handle to identify an active fetch request, returned by sfetch_send() */ +typedef struct sfetch_handle_t { uint32_t id; } sfetch_handle_t; + +/* error codes */ +typedef enum sfetch_error_t { + SFETCH_ERROR_NO_ERROR, + SFETCH_ERROR_FILE_NOT_FOUND, + SFETCH_ERROR_NO_BUFFER, + SFETCH_ERROR_BUFFER_TOO_SMALL, + SFETCH_ERROR_UNEXPECTED_EOF, + SFETCH_ERROR_INVALID_HTTP_STATUS, + SFETCH_ERROR_CANCELLED +} sfetch_error_t; + +/* the response struct passed to the response callback */ +typedef struct sfetch_response_t { + sfetch_handle_t handle; // request handle this response belongs to + bool dispatched; // true when request is in DISPATCHED state (lane has been assigned) + bool fetched; // true when request is in FETCHED state (fetched data is available) + bool paused; // request is currently in paused state + bool finished; // this is the last response for this request + bool failed; // request has failed (always set together with 'finished') + bool cancelled; // request was cancelled (always set together with 'finished') + sfetch_error_t error_code; // more detailed error code when failed is true + uint32_t channel; // the channel which processes this request + uint32_t lane; // the lane this request occupies on its channel + const char* path; // the original filesystem path of the request + void* user_data; // pointer to read/write user-data area + uint32_t data_offset; // current offset of fetched data chunk in the overall file data + sfetch_range_t data; // the fetched data as ptr/size pair (data.ptr == buffer.ptr, data.size <= buffer.size) + sfetch_range_t buffer; // the user-provided buffer which holds the fetched data +} sfetch_response_t; + +/* request parameters passed to sfetch_send() */ +typedef struct sfetch_request_t { + uint32_t channel; // index of channel this request is assigned to (default: 0) + const char* path; // filesystem path or HTTP URL (required) + void (*callback) (const sfetch_response_t*); // response callback function pointer (required) + uint32_t chunk_size; // number of bytes to load per stream-block (optional) + sfetch_range_t buffer; // a memory buffer where the data will be loaded into (optional) + sfetch_range_t user_data; // ptr/size of a POD user data block which will be memcpy'd (optional) +} sfetch_request_t; + +/* setup sokol-fetch (can be called on multiple threads) */ +SOKOL_FETCH_API_DECL void sfetch_setup(const sfetch_desc_t* desc); +/* discard a sokol-fetch context */ +SOKOL_FETCH_API_DECL void sfetch_shutdown(void); +/* return true if sokol-fetch has been setup */ +SOKOL_FETCH_API_DECL bool sfetch_valid(void); +/* get the desc struct that was passed to sfetch_setup() */ +SOKOL_FETCH_API_DECL sfetch_desc_t sfetch_desc(void); +/* return the max userdata size in number of bytes (SFETCH_MAX_USERDATA_UINT64 * sizeof(uint64_t)) */ +SOKOL_FETCH_API_DECL int sfetch_max_userdata_bytes(void); +/* return the value of the SFETCH_MAX_PATH implementation config value */ +SOKOL_FETCH_API_DECL int sfetch_max_path(void); + +/* send a fetch-request, get handle to request back */ +SOKOL_FETCH_API_DECL sfetch_handle_t sfetch_send(const sfetch_request_t* request); +/* return true if a handle is valid *and* the request is alive */ +SOKOL_FETCH_API_DECL bool sfetch_handle_valid(sfetch_handle_t h); +/* do per-frame work, moves requests into and out of IO threads, and invokes response-callbacks */ +SOKOL_FETCH_API_DECL void sfetch_dowork(void); + +/* bind a data buffer to a request (request must not currently have a buffer bound, must be called from response callback */ +SOKOL_FETCH_API_DECL void sfetch_bind_buffer(sfetch_handle_t h, sfetch_range_t buffer); +/* clear the 'buffer binding' of a request, returns previous buffer pointer (can be 0), must be called from response callback */ +SOKOL_FETCH_API_DECL void* sfetch_unbind_buffer(sfetch_handle_t h); +/* cancel a request that's in flight (will call response callback with .cancelled + .finished) */ +SOKOL_FETCH_API_DECL void sfetch_cancel(sfetch_handle_t h); +/* pause a request (will call response callback each frame with .paused) */ +SOKOL_FETCH_API_DECL void sfetch_pause(sfetch_handle_t h); +/* continue a paused request */ +SOKOL_FETCH_API_DECL void sfetch_continue(sfetch_handle_t h); + +#ifdef __cplusplus +} /* extern "C" */ + +/* reference-based equivalents for c++ */ +inline void sfetch_setup(const sfetch_desc_t& desc) { return sfetch_setup(&desc); } +inline sfetch_handle_t sfetch_send(const sfetch_request_t& request) { return sfetch_send(&request); } + +#endif +#endif // SOKOL_FETCH_INCLUDED + +// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██ +// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ +// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████ +// +// >>implementation +#ifdef SOKOL_FETCH_IMPL +#define SOKOL_FETCH_IMPL_INCLUDED (1) + +#if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE) +#error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use sfetch_desc_t.allocator to override memory allocation functions" +#endif + +#include /* malloc, free */ +#include /* memset, memcpy */ + +#ifndef SFETCH_MAX_PATH +#define SFETCH_MAX_PATH (1024) +#endif +#ifndef SFETCH_MAX_USERDATA_UINT64 +#define SFETCH_MAX_USERDATA_UINT64 (16) +#endif +#ifndef SFETCH_MAX_CHANNELS +#define SFETCH_MAX_CHANNELS (16) +#endif + +#ifndef SOKOL_API_IMPL + #define SOKOL_API_IMPL +#endif +#ifndef SOKOL_DEBUG + #ifndef NDEBUG + #define SOKOL_DEBUG + #endif +#endif +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif + +#ifndef _SOKOL_PRIVATE + #if defined(__GNUC__) || defined(__clang__) + #define _SOKOL_PRIVATE __attribute__((unused)) static + #else + #define _SOKOL_PRIVATE static + #endif +#endif + +#ifndef _SOKOL_UNUSED + #define _SOKOL_UNUSED(x) (void)(x) +#endif + +#if defined(__EMSCRIPTEN__) + #include + #define _SFETCH_PLATFORM_EMSCRIPTEN (1) + #define _SFETCH_PLATFORM_WINDOWS (0) + #define _SFETCH_PLATFORM_POSIX (0) + #define _SFETCH_HAS_THREADS (0) +#elif defined(_WIN32) + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #ifndef NOMINMAX + #define NOMINMAX + #endif + #include + #define _SFETCH_PLATFORM_WINDOWS (1) + #define _SFETCH_PLATFORM_EMSCRIPTEN (0) + #define _SFETCH_PLATFORM_POSIX (0) + #define _SFETCH_HAS_THREADS (1) +#else + #include + #include /* fopen, fread, fseek, fclose */ + #define _SFETCH_PLATFORM_POSIX (1) + #define _SFETCH_PLATFORM_EMSCRIPTEN (0) + #define _SFETCH_PLATFORM_WINDOWS (0) + #define _SFETCH_HAS_THREADS (1) +#endif + +// ███████ ████████ ██████ ██ ██ ██████ ████████ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██████ ██ ██ ██ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██ ██ ██ ██████ ██████ ██ ███████ +// +// >>structs +typedef struct _sfetch_path_t { + char buf[SFETCH_MAX_PATH]; +} _sfetch_path_t; + +/* a thread with incoming and outgoing message queue syncing */ +#if _SFETCH_PLATFORM_POSIX +typedef struct { + pthread_t thread; + pthread_cond_t incoming_cond; + pthread_mutex_t incoming_mutex; + pthread_mutex_t outgoing_mutex; + pthread_mutex_t running_mutex; + pthread_mutex_t stop_mutex; + bool stop_requested; + bool valid; +} _sfetch_thread_t; +#elif _SFETCH_PLATFORM_WINDOWS +typedef struct { + HANDLE thread; + HANDLE incoming_event; + CRITICAL_SECTION incoming_critsec; + CRITICAL_SECTION outgoing_critsec; + CRITICAL_SECTION running_critsec; + CRITICAL_SECTION stop_critsec; + bool stop_requested; + bool valid; +} _sfetch_thread_t; +#endif + +/* file handle abstraction */ +#if _SFETCH_PLATFORM_POSIX +typedef FILE* _sfetch_file_handle_t; +#define _SFETCH_INVALID_FILE_HANDLE (0) +typedef void*(*_sfetch_thread_func_t)(void*); +#elif _SFETCH_PLATFORM_WINDOWS +typedef HANDLE _sfetch_file_handle_t; +#define _SFETCH_INVALID_FILE_HANDLE (INVALID_HANDLE_VALUE) +typedef LPTHREAD_START_ROUTINE _sfetch_thread_func_t; +#endif + +/* user-side per-request state */ +typedef struct { + bool pause; /* switch item to PAUSED state if true */ + bool cont; /* switch item back to FETCHING if true */ + bool cancel; /* cancel the request, switch into FAILED state */ + /* transfer IO => user thread */ + uint32_t fetched_offset; /* number of bytes fetched so far */ + uint32_t fetched_size; /* size of last fetched chunk */ + sfetch_error_t error_code; + bool finished; + /* user thread only */ + size_t user_data_size; + uint64_t user_data[SFETCH_MAX_USERDATA_UINT64]; +} _sfetch_item_user_t; + +/* thread-side per-request state */ +typedef struct { + /* transfer IO => user thread */ + uint32_t fetched_offset; + uint32_t fetched_size; + sfetch_error_t error_code; + bool failed; + bool finished; + /* IO thread only */ + #if _SFETCH_PLATFORM_EMSCRIPTEN + uint32_t http_range_offset; + #else + _sfetch_file_handle_t file_handle; + #endif + uint32_t content_size; +} _sfetch_item_thread_t; + +/* a request goes through the following states, ping-ponging between IO and user thread */ +typedef enum _sfetch_state_t { + _SFETCH_STATE_INITIAL, /* internal: request has just been initialized */ + _SFETCH_STATE_ALLOCATED, /* internal: request has been allocated from internal pool */ + _SFETCH_STATE_DISPATCHED, /* user thread: request has been dispatched to its IO channel */ + _SFETCH_STATE_FETCHING, /* IO thread: waiting for data to be fetched */ + _SFETCH_STATE_FETCHED, /* user thread: fetched data available */ + _SFETCH_STATE_PAUSED, /* user thread: request has been paused via sfetch_pause() */ + _SFETCH_STATE_FAILED, /* user thread: follow state or FETCHING if something went wrong */ +} _sfetch_state_t; + +/* an internal request item */ +#define _SFETCH_INVALID_LANE (0xFFFFFFFF) +typedef struct { + sfetch_handle_t handle; + _sfetch_state_t state; + uint32_t channel; + uint32_t lane; + uint32_t chunk_size; + void (*callback) (const sfetch_response_t*); + sfetch_range_t buffer; + + /* updated by IO-thread, off-limits to user thread */ + _sfetch_item_thread_t thread; + + /* accessible by user-thread, off-limits to IO thread */ + _sfetch_item_user_t user; + + /* big stuff at the end */ + _sfetch_path_t path; +} _sfetch_item_t; + +/* a pool of internal per-request items */ +typedef struct { + uint32_t size; + uint32_t free_top; + _sfetch_item_t* items; + uint32_t* free_slots; + uint32_t* gen_ctrs; + bool valid; +} _sfetch_pool_t; + +/* a ringbuffer for pool-slot ids */ +typedef struct { + uint32_t head; + uint32_t tail; + uint32_t num; + uint32_t* buf; +} _sfetch_ring_t; + +/* an IO channel with its own IO thread */ +struct _sfetch_t; +typedef struct { + struct _sfetch_t* ctx; // back-pointer to thread-local _sfetch state pointer, since this isn't accessible from the IO threads + _sfetch_ring_t free_lanes; + _sfetch_ring_t user_sent; + _sfetch_ring_t user_incoming; + _sfetch_ring_t user_outgoing; + #if _SFETCH_HAS_THREADS + _sfetch_ring_t thread_incoming; + _sfetch_ring_t thread_outgoing; + _sfetch_thread_t thread; + #endif + void (*request_handler)(struct _sfetch_t* ctx, uint32_t slot_id); + bool valid; +} _sfetch_channel_t; + +/* the sfetch global state */ +typedef struct _sfetch_t { + bool setup; + bool valid; + bool in_callback; + sfetch_desc_t desc; + _sfetch_pool_t pool; + _sfetch_channel_t chn[SFETCH_MAX_CHANNELS]; +} _sfetch_t; +#if _SFETCH_HAS_THREADS +#if defined(_MSC_VER) +static __declspec(thread) _sfetch_t* _sfetch; +#else +static __thread _sfetch_t* _sfetch; +#endif +#else +static _sfetch_t* _sfetch; +#endif +#define _sfetch_def(val, def) (((val) == 0) ? (def) : (val)) + +// ██ ██████ ██████ ██████ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ████ ██ ██ +// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██████ ██ ██ ████ ██████ +// +// >>logging +#if defined(SOKOL_DEBUG) +#define _SFETCH_LOGITEM_XMACRO(item,msg) #item ": " msg, +static const char* _sfetch_log_messages[] = { + _SFETCH_LOG_ITEMS +}; +#undef _SFETCH_LOGITEM_XMACRO +#endif // SOKOL_DEBUG + +#define _SFETCH_PANIC(code) _sfetch_log(SFETCH_LOGITEM_ ##code, 0, __LINE__) +#define _SFETCH_ERROR(code) _sfetch_log(SFETCH_LOGITEM_ ##code, 1, __LINE__) +#define _SFETCH_WARN(code) _sfetch_log(SFETCH_LOGITEM_ ##code, 2, __LINE__) +#define _SFETCH_INFO(code) _sfetch_log(SFETCH_LOGITEM_ ##code, 3, __LINE__) + +static void _sfetch_log(sfetch_log_item_t log_item, uint32_t log_level, uint32_t line_nr) { + if (_sfetch->desc.logger.func) { + #if defined(SOKOL_DEBUG) + const char* filename = __FILE__; + const char* message = _sfetch_log_messages[log_item]; + #else + const char* filename = 0; + const char* message = 0; + #endif + _sfetch->desc.logger.func("sfetch", log_level, log_item, message, line_nr, filename, _sfetch->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 _sfetch_clear(void* ptr, size_t size) { + SOKOL_ASSERT(ptr && (size > 0)); + memset(ptr, 0, size); +} + +_SOKOL_PRIVATE void* _sfetch_malloc_with_allocator(const sfetch_allocator_t* allocator, size_t size) { + SOKOL_ASSERT(size > 0); + void* ptr; + if (allocator->alloc_fn) { + ptr = allocator->alloc_fn(size, allocator->user_data); + } else { + ptr = malloc(size); + } + if (0 == ptr) { + _SFETCH_PANIC(MALLOC_FAILED); + } + return ptr; +} + +_SOKOL_PRIVATE void* _sfetch_malloc(size_t size) { + return _sfetch_malloc_with_allocator(&_sfetch->desc.allocator, size); +} + +_SOKOL_PRIVATE void* _sfetch_malloc_clear(size_t size) { + void* ptr = _sfetch_malloc(size); + _sfetch_clear(ptr, size); + return ptr; +} + +_SOKOL_PRIVATE void _sfetch_free(void* ptr) { + if (_sfetch->desc.allocator.free_fn) { + _sfetch->desc.allocator.free_fn(ptr, _sfetch->desc.allocator.user_data); + } else { + free(ptr); + } +} + +_SOKOL_PRIVATE _sfetch_t* _sfetch_ctx(void) { + return _sfetch; +} + +_SOKOL_PRIVATE void _sfetch_path_copy(_sfetch_path_t* dst, const char* src) { + SOKOL_ASSERT(dst); + if (src && (strlen(src) < SFETCH_MAX_PATH)) { + #if defined(_MSC_VER) + strncpy_s(dst->buf, SFETCH_MAX_PATH, src, (SFETCH_MAX_PATH-1)); + #else + strncpy(dst->buf, src, SFETCH_MAX_PATH); + #endif + dst->buf[SFETCH_MAX_PATH-1] = 0; + } + else { + _sfetch_clear(dst->buf, SFETCH_MAX_PATH); + } +} + +_SOKOL_PRIVATE _sfetch_path_t _sfetch_path_make(const char* str) { + _sfetch_path_t res; + _sfetch_path_copy(&res, str); + return res; +} + +// ███ ███ ███████ ███████ ███████ █████ ██████ ███████ ██████ ██ ██ ███████ ██ ██ ███████ +// ████ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ █████ ███████ ███████ ███████ ██ ███ █████ ██ ██ ██ ██ █████ ██ ██ █████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ▄▄ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ███████ ███████ ██ ██ ██████ ███████ ██████ ██████ ███████ ██████ ███████ +// ▀▀ +// >>message queue +_SOKOL_PRIVATE uint32_t _sfetch_ring_wrap(const _sfetch_ring_t* rb, uint32_t i) { + return i % rb->num; +} + +_SOKOL_PRIVATE void _sfetch_ring_discard(_sfetch_ring_t* rb) { + SOKOL_ASSERT(rb); + if (rb->buf) { + _sfetch_free(rb->buf); + rb->buf = 0; + } + rb->head = 0; + rb->tail = 0; + rb->num = 0; +} + +_SOKOL_PRIVATE bool _sfetch_ring_init(_sfetch_ring_t* rb, uint32_t num_slots) { + SOKOL_ASSERT(rb && (num_slots > 0)); + SOKOL_ASSERT(0 == rb->buf); + rb->head = 0; + rb->tail = 0; + /* one slot reserved to detect full vs empty */ + rb->num = num_slots + 1; + const size_t queue_size = rb->num * sizeof(sfetch_handle_t); + rb->buf = (uint32_t*) _sfetch_malloc_clear(queue_size); + if (rb->buf) { + return true; + } + else { + _sfetch_ring_discard(rb); + return false; + } +} + +_SOKOL_PRIVATE bool _sfetch_ring_full(const _sfetch_ring_t* rb) { + SOKOL_ASSERT(rb && rb->buf); + return _sfetch_ring_wrap(rb, rb->head + 1) == rb->tail; +} + +_SOKOL_PRIVATE bool _sfetch_ring_empty(const _sfetch_ring_t* rb) { + SOKOL_ASSERT(rb && rb->buf); + return rb->head == rb->tail; +} + +_SOKOL_PRIVATE uint32_t _sfetch_ring_count(const _sfetch_ring_t* rb) { + SOKOL_ASSERT(rb && rb->buf); + uint32_t count; + if (rb->head >= rb->tail) { + count = rb->head - rb->tail; + } + else { + count = (rb->head + rb->num) - rb->tail; + } + SOKOL_ASSERT(count < rb->num); + return count; +} + +_SOKOL_PRIVATE void _sfetch_ring_enqueue(_sfetch_ring_t* rb, uint32_t slot_id) { + SOKOL_ASSERT(rb && rb->buf); + SOKOL_ASSERT(!_sfetch_ring_full(rb)); + SOKOL_ASSERT(rb->head < rb->num); + rb->buf[rb->head] = slot_id; + rb->head = _sfetch_ring_wrap(rb, rb->head + 1); +} + +_SOKOL_PRIVATE uint32_t _sfetch_ring_dequeue(_sfetch_ring_t* rb) { + SOKOL_ASSERT(rb && rb->buf); + SOKOL_ASSERT(!_sfetch_ring_empty(rb)); + SOKOL_ASSERT(rb->tail < rb->num); + uint32_t slot_id = rb->buf[rb->tail]; + rb->tail = _sfetch_ring_wrap(rb, rb->tail + 1); + return slot_id; +} + +_SOKOL_PRIVATE uint32_t _sfetch_ring_peek(const _sfetch_ring_t* rb, uint32_t index) { + SOKOL_ASSERT(rb && rb->buf); + SOKOL_ASSERT(!_sfetch_ring_empty(rb)); + SOKOL_ASSERT(index < _sfetch_ring_count(rb)); + uint32_t rb_index = _sfetch_ring_wrap(rb, rb->tail + index); + return rb->buf[rb_index]; +} + +// ██████ ███████ ██████ ██ ██ ███████ ███████ ████████ ██████ ██████ ██████ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ █████ ██ ██ ██ ██ █████ ███████ ██ ██████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ▄▄ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██████ ██████ ███████ ███████ ██ ██ ██████ ██████ ███████ +// ▀▀ +// >>request pool +_SOKOL_PRIVATE uint32_t _sfetch_make_id(uint32_t index, uint32_t gen_ctr) { + return (gen_ctr<<16) | (index & 0xFFFF); +} + +_SOKOL_PRIVATE sfetch_handle_t _sfetch_make_handle(uint32_t slot_id) { + sfetch_handle_t h; + h.id = slot_id; + return h; +} + +_SOKOL_PRIVATE uint32_t _sfetch_slot_index(uint32_t slot_id) { + return slot_id & 0xFFFF; +} + +_SOKOL_PRIVATE void _sfetch_item_init(_sfetch_item_t* item, uint32_t slot_id, const sfetch_request_t* request) { + SOKOL_ASSERT(item && (0 == item->handle.id)); + SOKOL_ASSERT(request && request->path); + _sfetch_clear(item, sizeof(_sfetch_item_t)); + item->handle.id = slot_id; + item->state = _SFETCH_STATE_INITIAL; + item->channel = request->channel; + item->chunk_size = request->chunk_size; + item->lane = _SFETCH_INVALID_LANE; + item->callback = request->callback; + item->buffer = request->buffer; + item->path = _sfetch_path_make(request->path); + #if !_SFETCH_PLATFORM_EMSCRIPTEN + item->thread.file_handle = _SFETCH_INVALID_FILE_HANDLE; + #endif + if (request->user_data.ptr && + (request->user_data.size > 0) && + (request->user_data.size <= (SFETCH_MAX_USERDATA_UINT64*8))) + { + item->user.user_data_size = request->user_data.size; + memcpy(item->user.user_data, request->user_data.ptr, request->user_data.size); + } +} + +_SOKOL_PRIVATE void _sfetch_item_discard(_sfetch_item_t* item) { + SOKOL_ASSERT(item && (0 != item->handle.id)); + _sfetch_clear(item, sizeof(_sfetch_item_t)); +} + +_SOKOL_PRIVATE void _sfetch_pool_discard(_sfetch_pool_t* pool) { + SOKOL_ASSERT(pool); + if (pool->free_slots) { + _sfetch_free(pool->free_slots); + pool->free_slots = 0; + } + if (pool->gen_ctrs) { + _sfetch_free(pool->gen_ctrs); + pool->gen_ctrs = 0; + } + if (pool->items) { + _sfetch_free(pool->items); + pool->items = 0; + } + pool->size = 0; + pool->free_top = 0; + pool->valid = false; +} + +_SOKOL_PRIVATE bool _sfetch_pool_init(_sfetch_pool_t* pool, uint32_t num_items) { + SOKOL_ASSERT(pool && (num_items > 0) && (num_items < ((1<<16)-1))); + SOKOL_ASSERT(0 == pool->items); + /* NOTE: item slot 0 is reserved for the special "invalid" item index 0*/ + pool->size = num_items + 1; + pool->free_top = 0; + const size_t items_size = pool->size * sizeof(_sfetch_item_t); + pool->items = (_sfetch_item_t*) _sfetch_malloc_clear(items_size); + /* generation counters indexable by pool slot index, slot 0 is reserved */ + const size_t gen_ctrs_size = sizeof(uint32_t) * pool->size; + pool->gen_ctrs = (uint32_t*) _sfetch_malloc_clear(gen_ctrs_size); + SOKOL_ASSERT(pool->gen_ctrs); + /* NOTE: it's not a bug to only reserve num_items here */ + const size_t free_slots_size = num_items * sizeof(int); + pool->free_slots = (uint32_t*) _sfetch_malloc_clear(free_slots_size); + if (pool->items && pool->free_slots) { + /* never allocate the 0-th item, this is the reserved 'invalid item' */ + for (uint32_t i = pool->size - 1; i >= 1; i--) { + pool->free_slots[pool->free_top++] = i; + } + pool->valid = true; + } + else { + /* allocation error */ + _sfetch_pool_discard(pool); + } + return pool->valid; +} + +_SOKOL_PRIVATE uint32_t _sfetch_pool_item_alloc(_sfetch_pool_t* pool, const sfetch_request_t* request) { + SOKOL_ASSERT(pool && pool->valid); + if (pool->free_top > 0) { + uint32_t slot_index = pool->free_slots[--pool->free_top]; + SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size)); + uint32_t slot_id = _sfetch_make_id(slot_index, ++pool->gen_ctrs[slot_index]); + _sfetch_item_init(&pool->items[slot_index], slot_id, request); + pool->items[slot_index].state = _SFETCH_STATE_ALLOCATED; + return slot_id; + } + else { + /* pool exhausted, return the 'invalid handle' */ + return _sfetch_make_id(0, 0); + } +} + +_SOKOL_PRIVATE void _sfetch_pool_item_free(_sfetch_pool_t* pool, uint32_t slot_id) { + SOKOL_ASSERT(pool && pool->valid); + uint32_t slot_index = _sfetch_slot_index(slot_id); + SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size)); + SOKOL_ASSERT(pool->items[slot_index].handle.id == slot_id); + #if defined(SOKOL_DEBUG) + /* debug check against double-free */ + for (uint32_t i = 0; i < pool->free_top; i++) { + SOKOL_ASSERT(pool->free_slots[i] != slot_index); + } + #endif + _sfetch_item_discard(&pool->items[slot_index]); + pool->free_slots[pool->free_top++] = slot_index; + SOKOL_ASSERT(pool->free_top <= (pool->size - 1)); +} + +/* return pointer to item by handle without matching id check */ +_SOKOL_PRIVATE _sfetch_item_t* _sfetch_pool_item_at(_sfetch_pool_t* pool, uint32_t slot_id) { + SOKOL_ASSERT(pool && pool->valid); + uint32_t slot_index = _sfetch_slot_index(slot_id); + SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size)); + return &pool->items[slot_index]; +} + +/* return pointer to item by handle with matching id check */ +_SOKOL_PRIVATE _sfetch_item_t* _sfetch_pool_item_lookup(_sfetch_pool_t* pool, uint32_t slot_id) { + SOKOL_ASSERT(pool && pool->valid); + if (0 != slot_id) { + _sfetch_item_t* item = _sfetch_pool_item_at(pool, slot_id); + if (item->handle.id == slot_id) { + return item; + } + } + return 0; +} + +// ██████ ██████ ███████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ███████ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ███████ ██ ██ ██ +// +// >>posix +#if _SFETCH_PLATFORM_POSIX +_SOKOL_PRIVATE _sfetch_file_handle_t _sfetch_file_open(const _sfetch_path_t* path) { + return fopen(path->buf, "rb"); +} + +_SOKOL_PRIVATE void _sfetch_file_close(_sfetch_file_handle_t h) { + fclose(h); +} + +_SOKOL_PRIVATE bool _sfetch_file_handle_valid(_sfetch_file_handle_t h) { + return h != _SFETCH_INVALID_FILE_HANDLE; +} + +_SOKOL_PRIVATE uint32_t _sfetch_file_size(_sfetch_file_handle_t h) { + fseek(h, 0, SEEK_END); + return (uint32_t) ftell(h); +} + +_SOKOL_PRIVATE bool _sfetch_file_read(_sfetch_file_handle_t h, uint32_t offset, uint32_t num_bytes, void* ptr) { + fseek(h, (long)offset, SEEK_SET); + return num_bytes == fread(ptr, 1, num_bytes, h); +} + +_SOKOL_PRIVATE bool _sfetch_thread_init(_sfetch_thread_t* thread, _sfetch_thread_func_t thread_func, void* thread_arg) { + SOKOL_ASSERT(thread && !thread->valid && !thread->stop_requested); + + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutex_init(&thread->incoming_mutex, &attr); + pthread_mutexattr_destroy(&attr); + + pthread_mutexattr_init(&attr); + pthread_mutex_init(&thread->outgoing_mutex, &attr); + pthread_mutexattr_destroy(&attr); + + pthread_mutexattr_init(&attr); + pthread_mutex_init(&thread->running_mutex, &attr); + pthread_mutexattr_destroy(&attr); + + pthread_mutexattr_init(&attr); + pthread_mutex_init(&thread->stop_mutex, &attr); + pthread_mutexattr_destroy(&attr); + + pthread_condattr_t cond_attr; + pthread_condattr_init(&cond_attr); + pthread_cond_init(&thread->incoming_cond, &cond_attr); + pthread_condattr_destroy(&cond_attr); + + /* FIXME: in debug mode, the threads should be named */ + pthread_mutex_lock(&thread->running_mutex); + int res = pthread_create(&thread->thread, 0, thread_func, thread_arg); + thread->valid = (0 == res); + pthread_mutex_unlock(&thread->running_mutex); + return thread->valid; +} + +_SOKOL_PRIVATE void _sfetch_thread_request_stop(_sfetch_thread_t* thread) { + pthread_mutex_lock(&thread->stop_mutex); + thread->stop_requested = true; + pthread_mutex_unlock(&thread->stop_mutex); +} + +_SOKOL_PRIVATE bool _sfetch_thread_stop_requested(_sfetch_thread_t* thread) { + pthread_mutex_lock(&thread->stop_mutex); + bool stop_requested = thread->stop_requested; + pthread_mutex_unlock(&thread->stop_mutex); + return stop_requested; +} + +_SOKOL_PRIVATE void _sfetch_thread_join(_sfetch_thread_t* thread) { + SOKOL_ASSERT(thread); + if (thread->valid) { + pthread_mutex_lock(&thread->incoming_mutex); + _sfetch_thread_request_stop(thread); + pthread_cond_signal(&thread->incoming_cond); + pthread_mutex_unlock(&thread->incoming_mutex); + pthread_join(thread->thread, 0); + thread->valid = false; + } + pthread_mutex_destroy(&thread->stop_mutex); + pthread_mutex_destroy(&thread->running_mutex); + pthread_mutex_destroy(&thread->incoming_mutex); + pthread_mutex_destroy(&thread->outgoing_mutex); + pthread_cond_destroy(&thread->incoming_cond); +} + +/* called when the thread-func is entered, this blocks the thread func until + the _sfetch_thread_t object is fully initialized +*/ +_SOKOL_PRIVATE void _sfetch_thread_entered(_sfetch_thread_t* thread) { + pthread_mutex_lock(&thread->running_mutex); +} + +/* called by the thread-func right before it is left */ +_SOKOL_PRIVATE void _sfetch_thread_leaving(_sfetch_thread_t* thread) { + pthread_mutex_unlock(&thread->running_mutex); +} + +_SOKOL_PRIVATE void _sfetch_thread_enqueue_incoming(_sfetch_thread_t* thread, _sfetch_ring_t* incoming, _sfetch_ring_t* src) { + /* called from user thread */ + SOKOL_ASSERT(thread && thread->valid); + SOKOL_ASSERT(incoming && incoming->buf); + SOKOL_ASSERT(src && src->buf); + if (!_sfetch_ring_empty(src)) { + pthread_mutex_lock(&thread->incoming_mutex); + while (!_sfetch_ring_full(incoming) && !_sfetch_ring_empty(src)) { + _sfetch_ring_enqueue(incoming, _sfetch_ring_dequeue(src)); + } + pthread_cond_signal(&thread->incoming_cond); + pthread_mutex_unlock(&thread->incoming_mutex); + } +} + +_SOKOL_PRIVATE uint32_t _sfetch_thread_dequeue_incoming(_sfetch_thread_t* thread, _sfetch_ring_t* incoming) { + /* called from thread function */ + SOKOL_ASSERT(thread && thread->valid); + SOKOL_ASSERT(incoming && incoming->buf); + pthread_mutex_lock(&thread->incoming_mutex); + while (_sfetch_ring_empty(incoming) && !thread->stop_requested) { + pthread_cond_wait(&thread->incoming_cond, &thread->incoming_mutex); + } + uint32_t item = 0; + if (!thread->stop_requested) { + item = _sfetch_ring_dequeue(incoming); + } + pthread_mutex_unlock(&thread->incoming_mutex); + return item; +} + +_SOKOL_PRIVATE bool _sfetch_thread_enqueue_outgoing(_sfetch_thread_t* thread, _sfetch_ring_t* outgoing, uint32_t item) { + /* called from thread function */ + SOKOL_ASSERT(thread && thread->valid); + SOKOL_ASSERT(outgoing && outgoing->buf); + SOKOL_ASSERT(0 != item); + pthread_mutex_lock(&thread->outgoing_mutex); + bool result = false; + if (!_sfetch_ring_full(outgoing)) { + _sfetch_ring_enqueue(outgoing, item); + } + pthread_mutex_unlock(&thread->outgoing_mutex); + return result; +} + +_SOKOL_PRIVATE void _sfetch_thread_dequeue_outgoing(_sfetch_thread_t* thread, _sfetch_ring_t* outgoing, _sfetch_ring_t* dst) { + /* called from user thread */ + SOKOL_ASSERT(thread && thread->valid); + SOKOL_ASSERT(outgoing && outgoing->buf); + SOKOL_ASSERT(dst && dst->buf); + pthread_mutex_lock(&thread->outgoing_mutex); + while (!_sfetch_ring_full(dst) && !_sfetch_ring_empty(outgoing)) { + _sfetch_ring_enqueue(dst, _sfetch_ring_dequeue(outgoing)); + } + pthread_mutex_unlock(&thread->outgoing_mutex); +} +#endif /* _SFETCH_PLATFORM_POSIX */ + +// ██ ██ ██ ███ ██ ██████ ██████ ██ ██ ███████ +// ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ █ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ █ ██ ███████ +// ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ███ ██ ██ +// ███ ███ ██ ██ ████ ██████ ██████ ███ ███ ███████ +// +// >>windows +#if _SFETCH_PLATFORM_WINDOWS +_SOKOL_PRIVATE bool _sfetch_win32_utf8_to_wide(const char* src, wchar_t* dst, int dst_num_bytes) { + SOKOL_ASSERT(src && dst && (dst_num_bytes > 1)); + _sfetch_clear(dst, (size_t)dst_num_bytes); + const int dst_chars = dst_num_bytes / (int)sizeof(wchar_t); + const int dst_needed = MultiByteToWideChar(CP_UTF8, 0, src, -1, 0, 0); + if ((dst_needed > 0) && (dst_needed < dst_chars)) { + MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, dst_chars); + return true; + } + else { + /* input string doesn't fit into destination buffer */ + return false; + } +} + +_SOKOL_PRIVATE _sfetch_file_handle_t _sfetch_file_open(const _sfetch_path_t* path) { + wchar_t w_path[SFETCH_MAX_PATH]; + if (!_sfetch_win32_utf8_to_wide(path->buf, w_path, sizeof(w_path))) { + _SFETCH_ERROR(FILE_PATH_UTF8_DECODING_FAILED); + return 0; + } + _sfetch_file_handle_t h = CreateFileW( + w_path, /* lpFileName */ + GENERIC_READ, /* dwDesiredAccess */ + FILE_SHARE_READ, /* dwShareMode */ + NULL, /* lpSecurityAttributes */ + OPEN_EXISTING, /* dwCreationDisposition */ + FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, /* dwFlagsAndAttributes */ + NULL); /* hTemplateFile */ + return h; +} + +_SOKOL_PRIVATE void _sfetch_file_close(_sfetch_file_handle_t h) { + CloseHandle(h); +} + +_SOKOL_PRIVATE bool _sfetch_file_handle_valid(_sfetch_file_handle_t h) { + return h != _SFETCH_INVALID_FILE_HANDLE; +} + +_SOKOL_PRIVATE uint32_t _sfetch_file_size(_sfetch_file_handle_t h) { + return GetFileSize(h, NULL); +} + +_SOKOL_PRIVATE bool _sfetch_file_read(_sfetch_file_handle_t h, uint32_t offset, uint32_t num_bytes, void* ptr) { + LARGE_INTEGER offset_li; + offset_li.QuadPart = offset; + BOOL seek_res = SetFilePointerEx(h, offset_li, NULL, FILE_BEGIN); + if (seek_res) { + DWORD bytes_read = 0; + BOOL read_res = ReadFile(h, ptr, (DWORD)num_bytes, &bytes_read, NULL); + return read_res && (bytes_read == num_bytes); + } + else { + return false; + } +} + +_SOKOL_PRIVATE bool _sfetch_thread_init(_sfetch_thread_t* thread, _sfetch_thread_func_t thread_func, void* thread_arg) { + SOKOL_ASSERT(thread && !thread->valid && !thread->stop_requested); + + thread->incoming_event = CreateEventA(NULL, FALSE, FALSE, NULL); + SOKOL_ASSERT(NULL != thread->incoming_event); + InitializeCriticalSection(&thread->incoming_critsec); + InitializeCriticalSection(&thread->outgoing_critsec); + InitializeCriticalSection(&thread->running_critsec); + InitializeCriticalSection(&thread->stop_critsec); + + EnterCriticalSection(&thread->running_critsec); + const SIZE_T stack_size = 512 * 1024; + thread->thread = CreateThread(NULL, stack_size, thread_func, thread_arg, 0, NULL); + thread->valid = (NULL != thread->thread); + LeaveCriticalSection(&thread->running_critsec); + return thread->valid; +} + +_SOKOL_PRIVATE void _sfetch_thread_request_stop(_sfetch_thread_t* thread) { + EnterCriticalSection(&thread->stop_critsec); + thread->stop_requested = true; + LeaveCriticalSection(&thread->stop_critsec); +} + +_SOKOL_PRIVATE bool _sfetch_thread_stop_requested(_sfetch_thread_t* thread) { + EnterCriticalSection(&thread->stop_critsec); + bool stop_requested = thread->stop_requested; + LeaveCriticalSection(&thread->stop_critsec); + return stop_requested; +} + +_SOKOL_PRIVATE void _sfetch_thread_join(_sfetch_thread_t* thread) { + if (thread->valid) { + EnterCriticalSection(&thread->incoming_critsec); + _sfetch_thread_request_stop(thread); + BOOL set_event_res = SetEvent(thread->incoming_event); + _SOKOL_UNUSED(set_event_res); + SOKOL_ASSERT(set_event_res); + LeaveCriticalSection(&thread->incoming_critsec); + WaitForSingleObject(thread->thread, INFINITE); + CloseHandle(thread->thread); + thread->valid = false; + } + CloseHandle(thread->incoming_event); + DeleteCriticalSection(&thread->stop_critsec); + DeleteCriticalSection(&thread->running_critsec); + DeleteCriticalSection(&thread->outgoing_critsec); + DeleteCriticalSection(&thread->incoming_critsec); +} + +_SOKOL_PRIVATE void _sfetch_thread_entered(_sfetch_thread_t* thread) { + EnterCriticalSection(&thread->running_critsec); +} + +/* called by the thread-func right before it is left */ +_SOKOL_PRIVATE void _sfetch_thread_leaving(_sfetch_thread_t* thread) { + LeaveCriticalSection(&thread->running_critsec); +} + +_SOKOL_PRIVATE void _sfetch_thread_enqueue_incoming(_sfetch_thread_t* thread, _sfetch_ring_t* incoming, _sfetch_ring_t* src) { + /* called from user thread */ + SOKOL_ASSERT(thread && thread->valid); + SOKOL_ASSERT(incoming && incoming->buf); + SOKOL_ASSERT(src && src->buf); + if (!_sfetch_ring_empty(src)) { + EnterCriticalSection(&thread->incoming_critsec); + while (!_sfetch_ring_full(incoming) && !_sfetch_ring_empty(src)) { + _sfetch_ring_enqueue(incoming, _sfetch_ring_dequeue(src)); + } + LeaveCriticalSection(&thread->incoming_critsec); + BOOL set_event_res = SetEvent(thread->incoming_event); + _SOKOL_UNUSED(set_event_res); + SOKOL_ASSERT(set_event_res); + } +} + +_SOKOL_PRIVATE uint32_t _sfetch_thread_dequeue_incoming(_sfetch_thread_t* thread, _sfetch_ring_t* incoming) { + /* called from thread function */ + SOKOL_ASSERT(thread && thread->valid); + SOKOL_ASSERT(incoming && incoming->buf); + EnterCriticalSection(&thread->incoming_critsec); + while (_sfetch_ring_empty(incoming) && !thread->stop_requested) { + LeaveCriticalSection(&thread->incoming_critsec); + WaitForSingleObject(thread->incoming_event, INFINITE); + EnterCriticalSection(&thread->incoming_critsec); + } + uint32_t item = 0; + if (!thread->stop_requested) { + item = _sfetch_ring_dequeue(incoming); + } + LeaveCriticalSection(&thread->incoming_critsec); + return item; +} + +_SOKOL_PRIVATE bool _sfetch_thread_enqueue_outgoing(_sfetch_thread_t* thread, _sfetch_ring_t* outgoing, uint32_t item) { + /* called from thread function */ + SOKOL_ASSERT(thread && thread->valid); + SOKOL_ASSERT(outgoing && outgoing->buf); + EnterCriticalSection(&thread->outgoing_critsec); + bool result = false; + if (!_sfetch_ring_full(outgoing)) { + _sfetch_ring_enqueue(outgoing, item); + } + LeaveCriticalSection(&thread->outgoing_critsec); + return result; +} + +_SOKOL_PRIVATE void _sfetch_thread_dequeue_outgoing(_sfetch_thread_t* thread, _sfetch_ring_t* outgoing, _sfetch_ring_t* dst) { + /* called from user thread */ + SOKOL_ASSERT(thread && thread->valid); + SOKOL_ASSERT(outgoing && outgoing->buf); + SOKOL_ASSERT(dst && dst->buf); + EnterCriticalSection(&thread->outgoing_critsec); + while (!_sfetch_ring_full(dst) && !_sfetch_ring_empty(outgoing)) { + _sfetch_ring_enqueue(dst, _sfetch_ring_dequeue(outgoing)); + } + LeaveCriticalSection(&thread->outgoing_critsec); +} +#endif /* _SFETCH_PLATFORM_WINDOWS */ + +// ██████ ██ ██ █████ ███ ██ ███ ██ ███████ ██ ███████ +// ██ ██ ██ ██ ██ ████ ██ ████ ██ ██ ██ ██ +// ██ ███████ ███████ ██ ██ ██ ██ ██ ██ █████ ██ ███████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██ ██ ██ ████ ██ ████ ███████ ███████ ███████ +// +// >>channels + +/* per-channel request handler for native platforms accessing the local filesystem */ +#if _SFETCH_HAS_THREADS +_SOKOL_PRIVATE void _sfetch_request_handler(_sfetch_t* ctx, uint32_t slot_id) { + _sfetch_state_t state; + _sfetch_path_t* path; + _sfetch_item_thread_t* thread; + sfetch_range_t* buffer; + uint32_t chunk_size; + { + _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, slot_id); + if (!item) { + return; + } + state = item->state; + SOKOL_ASSERT((state == _SFETCH_STATE_FETCHING) || + (state == _SFETCH_STATE_PAUSED) || + (state == _SFETCH_STATE_FAILED)); + path = &item->path; + thread = &item->thread; + buffer = &item->buffer; + chunk_size = item->chunk_size; + } + if (thread->failed) { + return; + } + if (state == _SFETCH_STATE_FETCHING) { + if ((buffer->ptr == 0) || (buffer->size == 0)) { + thread->error_code = SFETCH_ERROR_NO_BUFFER; + thread->failed = true; + } + else { + /* open file if not happened yet */ + if (!_sfetch_file_handle_valid(thread->file_handle)) { + SOKOL_ASSERT(path->buf[0]); + SOKOL_ASSERT(thread->fetched_offset == 0); + SOKOL_ASSERT(thread->fetched_size == 0); + thread->file_handle = _sfetch_file_open(path); + if (_sfetch_file_handle_valid(thread->file_handle)) { + thread->content_size = _sfetch_file_size(thread->file_handle); + } + else { + thread->error_code = SFETCH_ERROR_FILE_NOT_FOUND; + thread->failed = true; + } + } + if (!thread->failed) { + uint32_t read_offset = 0; + uint32_t bytes_to_read = 0; + if (chunk_size == 0) { + /* load entire file */ + if (thread->content_size <= buffer->size) { + bytes_to_read = thread->content_size; + read_offset = 0; + } + else { + /* provided buffer to small to fit entire file */ + thread->error_code = SFETCH_ERROR_BUFFER_TOO_SMALL; + thread->failed = true; + } + } + else { + if (chunk_size <= buffer->size) { + bytes_to_read = chunk_size; + read_offset = thread->fetched_offset; + if ((read_offset + bytes_to_read) > thread->content_size) { + bytes_to_read = thread->content_size - read_offset; + } + } + else { + /* provided buffer to small to fit next chunk */ + thread->error_code = SFETCH_ERROR_BUFFER_TOO_SMALL; + thread->failed = true; + } + } + if (!thread->failed) { + if (_sfetch_file_read(thread->file_handle, read_offset, bytes_to_read, (void*)buffer->ptr)) { + thread->fetched_size = bytes_to_read; + thread->fetched_offset += bytes_to_read; + } + else { + thread->error_code = SFETCH_ERROR_UNEXPECTED_EOF; + thread->failed = true; + } + } + } + } + SOKOL_ASSERT(thread->fetched_offset <= thread->content_size); + if (thread->failed || (thread->fetched_offset == thread->content_size)) { + if (_sfetch_file_handle_valid(thread->file_handle)) { + _sfetch_file_close(thread->file_handle); + thread->file_handle = _SFETCH_INVALID_FILE_HANDLE; + } + thread->finished = true; + } + } + /* ignore items in PAUSED or FAILED state */ +} + +#if _SFETCH_PLATFORM_WINDOWS +_SOKOL_PRIVATE DWORD WINAPI _sfetch_channel_thread_func(LPVOID arg) { +#else +_SOKOL_PRIVATE void* _sfetch_channel_thread_func(void* arg) { +#endif + _sfetch_channel_t* chn = (_sfetch_channel_t*) arg; + _sfetch_thread_entered(&chn->thread); + while (!_sfetch_thread_stop_requested(&chn->thread)) { + /* block until work arrives */ + uint32_t slot_id = _sfetch_thread_dequeue_incoming(&chn->thread, &chn->thread_incoming); + /* slot_id will be invalid if the thread was woken up to join */ + if (!_sfetch_thread_stop_requested(&chn->thread)) { + SOKOL_ASSERT(0 != slot_id); + chn->request_handler(chn->ctx, slot_id); + SOKOL_ASSERT(!_sfetch_ring_full(&chn->thread_outgoing)); + _sfetch_thread_enqueue_outgoing(&chn->thread, &chn->thread_outgoing, slot_id); + } + } + _sfetch_thread_leaving(&chn->thread); + return 0; +} +#endif /* _SFETCH_HAS_THREADS */ + +#if _SFETCH_PLATFORM_EMSCRIPTEN +EM_JS(void, sfetch_js_send_head_request, (uint32_t slot_id, const char* path_cstr), { + const path_str = UTF8ToString(path_cstr); + const req = new XMLHttpRequest(); + req.open('HEAD', path_str); + req.onreadystatechange = function() { + if (req.readyState == XMLHttpRequest.DONE) { + if (req.status == 200) { + const content_length = req.getResponseHeader('Content-Length'); + __sfetch_emsc_head_response(slot_id, content_length); + } + else { + __sfetch_emsc_failed_http_status(slot_id, req.status); + } + } + }; + req.send(); +}); + +/* if bytes_to_read != 0, a range-request will be sent, otherwise a normal request */ +EM_JS(void, sfetch_js_send_get_request, (uint32_t slot_id, const char* path_cstr, uint32_t offset, uint32_t bytes_to_read, void* buf_ptr, uint32_t buf_size), { + const path_str = UTF8ToString(path_cstr); + const req = new XMLHttpRequest(); + req.open('GET', path_str); + req.responseType = 'arraybuffer'; + const need_range_request = (bytes_to_read > 0); + if (need_range_request) { + req.setRequestHeader('Range', 'bytes='+offset+'-'+(offset+bytes_to_read-1)); + } + req.onreadystatechange = function() { + if (req.readyState == XMLHttpRequest.DONE) { + if ((req.status == 206) || ((req.status == 200) && !need_range_request)) { + const u8_array = new Uint8Array(\x2F\x2A\x2A @type {!ArrayBuffer} \x2A\x2F (req.response)); + const content_fetched_size = u8_array.length; + if (content_fetched_size <= buf_size) { + HEAPU8.set(u8_array, buf_ptr); + __sfetch_emsc_get_response(slot_id, bytes_to_read, content_fetched_size); + } + else { + __sfetch_emsc_failed_buffer_too_small(slot_id); + } + } + else { + __sfetch_emsc_failed_http_status(slot_id, req.status); + } + } + }; + req.send(); +}); + +/*=== emscripten specific C helper functions =================================*/ +#ifdef __cplusplus +extern "C" { +#endif +void _sfetch_emsc_send_get_request(uint32_t slot_id, _sfetch_item_t* item) { + if ((item->buffer.ptr == 0) || (item->buffer.size == 0)) { + item->thread.error_code = SFETCH_ERROR_NO_BUFFER; + item->thread.failed = true; + } + else { + uint32_t offset = 0; + uint32_t bytes_to_read = 0; + if (item->chunk_size > 0) { + /* send HTTP range request */ + SOKOL_ASSERT(item->thread.content_size > 0); + SOKOL_ASSERT(item->thread.http_range_offset < item->thread.content_size); + bytes_to_read = item->thread.content_size - item->thread.http_range_offset; + if (bytes_to_read > item->chunk_size) { + bytes_to_read = item->chunk_size; + } + SOKOL_ASSERT(bytes_to_read > 0); + offset = item->thread.http_range_offset; + } + sfetch_js_send_get_request(slot_id, item->path.buf, offset, bytes_to_read, (void*)item->buffer.ptr, item->buffer.size); + } +} + +/* called by JS when an initial HEAD request finished successfully (only when streaming chunks) */ +EMSCRIPTEN_KEEPALIVE void _sfetch_emsc_head_response(uint32_t slot_id, uint32_t content_length) { + _sfetch_t* ctx = _sfetch_ctx(); + if (ctx && ctx->valid) { + _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, slot_id); + if (item) { + SOKOL_ASSERT(item->buffer.ptr && (item->buffer.size > 0)); + item->thread.content_size = content_length; + _sfetch_emsc_send_get_request(slot_id, item); + } + } +} + +/* called by JS when a followup GET request finished successfully */ +EMSCRIPTEN_KEEPALIVE void _sfetch_emsc_get_response(uint32_t slot_id, uint32_t range_fetched_size, uint32_t content_fetched_size) { + _sfetch_t* ctx = _sfetch_ctx(); + if (ctx && ctx->valid) { + _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, slot_id); + if (item) { + item->thread.fetched_size = content_fetched_size; + item->thread.fetched_offset += content_fetched_size; + item->thread.http_range_offset += range_fetched_size; + if (item->chunk_size == 0) { + item->thread.finished = true; + } + else if (item->thread.http_range_offset >= item->thread.content_size) { + item->thread.finished = true; + } + _sfetch_ring_enqueue(&ctx->chn[item->channel].user_outgoing, slot_id); + } + } +} + +/* called by JS when an error occurred */ +EMSCRIPTEN_KEEPALIVE void _sfetch_emsc_failed_http_status(uint32_t slot_id, uint32_t http_status) { + _sfetch_t* ctx = _sfetch_ctx(); + if (ctx && ctx->valid) { + _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, slot_id); + if (item) { + if (http_status == 404) { + item->thread.error_code = SFETCH_ERROR_FILE_NOT_FOUND; + } + else { + item->thread.error_code = SFETCH_ERROR_INVALID_HTTP_STATUS; + } + item->thread.failed = true; + item->thread.finished = true; + _sfetch_ring_enqueue(&ctx->chn[item->channel].user_outgoing, slot_id); + } + } +} + +EMSCRIPTEN_KEEPALIVE void _sfetch_emsc_failed_buffer_too_small(uint32_t slot_id) { + _sfetch_t* ctx = _sfetch_ctx(); + if (ctx && ctx->valid) { + _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, slot_id); + if (item) { + item->thread.error_code = SFETCH_ERROR_BUFFER_TOO_SMALL; + item->thread.failed = true; + item->thread.finished = true; + _sfetch_ring_enqueue(&ctx->chn[item->channel].user_outgoing, slot_id); + } + } +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +_SOKOL_PRIVATE void _sfetch_request_handler(_sfetch_t* ctx, uint32_t slot_id) { + _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, slot_id); + if (!item) { + return; + } + if (item->state == _SFETCH_STATE_FETCHING) { + if ((item->chunk_size > 0) && (item->thread.content_size == 0)) { + /* if streaming download is requested, and the content-length isn't known + yet, need to send a HEAD request first + */ + sfetch_js_send_head_request(slot_id, item->path.buf); + } + else { + /* otherwise, this is either a request to load the entire file, or + to load the next streaming chunk + */ + _sfetch_emsc_send_get_request(slot_id, item); + } + } + else { + /* just move all other items (e.g. paused or cancelled) + into the outgoing queue, so they won't get lost + */ + _sfetch_ring_enqueue(&ctx->chn[item->channel].user_outgoing, slot_id); + } + if (item->thread.failed) { + item->thread.finished = true; + } +} +#endif /* _SFETCH_PLATFORM_EMSCRIPTEN */ + +_SOKOL_PRIVATE void _sfetch_channel_discard(_sfetch_channel_t* chn) { + SOKOL_ASSERT(chn); + #if _SFETCH_HAS_THREADS + if (chn->valid) { + _sfetch_thread_join(&chn->thread); + } + _sfetch_ring_discard(&chn->thread_incoming); + _sfetch_ring_discard(&chn->thread_outgoing); + #endif + _sfetch_ring_discard(&chn->free_lanes); + _sfetch_ring_discard(&chn->user_sent); + _sfetch_ring_discard(&chn->user_incoming); + _sfetch_ring_discard(&chn->user_outgoing); + _sfetch_ring_discard(&chn->free_lanes); + chn->valid = false; +} + +_SOKOL_PRIVATE bool _sfetch_channel_init(_sfetch_channel_t* chn, _sfetch_t* ctx, uint32_t num_items, uint32_t num_lanes, void (*request_handler)(_sfetch_t* ctx, uint32_t)) { + SOKOL_ASSERT(chn && (num_items > 0) && request_handler); + SOKOL_ASSERT(!chn->valid); + bool valid = true; + chn->request_handler = request_handler; + chn->ctx = ctx; + valid &= _sfetch_ring_init(&chn->free_lanes, num_lanes); + for (uint32_t lane = 0; lane < num_lanes; lane++) { + _sfetch_ring_enqueue(&chn->free_lanes, lane); + } + valid &= _sfetch_ring_init(&chn->user_sent, num_items); + valid &= _sfetch_ring_init(&chn->user_incoming, num_lanes); + valid &= _sfetch_ring_init(&chn->user_outgoing, num_lanes); + #if _SFETCH_HAS_THREADS + valid &= _sfetch_ring_init(&chn->thread_incoming, num_lanes); + valid &= _sfetch_ring_init(&chn->thread_outgoing, num_lanes); + #endif + if (valid) { + chn->valid = true; + #if _SFETCH_HAS_THREADS + _sfetch_thread_init(&chn->thread, _sfetch_channel_thread_func, chn); + #endif + return true; + } + else { + _sfetch_channel_discard(chn); + return false; + } +} + +/* put a request into the channels sent-queue, this is where all new requests + are stored until a lane becomes free. +*/ +_SOKOL_PRIVATE bool _sfetch_channel_send(_sfetch_channel_t* chn, uint32_t slot_id) { + SOKOL_ASSERT(chn && chn->valid); + if (!_sfetch_ring_full(&chn->user_sent)) { + _sfetch_ring_enqueue(&chn->user_sent, slot_id); + return true; + } + else { + _SFETCH_ERROR(SEND_QUEUE_FULL); + return false; + } +} + +_SOKOL_PRIVATE void _sfetch_invoke_response_callback(_sfetch_item_t* item) { + sfetch_response_t response; + _sfetch_clear(&response, sizeof(response)); + response.handle = item->handle; + response.dispatched = (item->state == _SFETCH_STATE_DISPATCHED); + response.fetched = (item->state == _SFETCH_STATE_FETCHED); + response.paused = (item->state == _SFETCH_STATE_PAUSED); + response.finished = item->user.finished; + response.failed = (item->state == _SFETCH_STATE_FAILED); + response.cancelled = item->user.cancel; + response.error_code = item->user.error_code; + response.channel = item->channel; + response.lane = item->lane; + response.path = item->path.buf; + response.user_data = item->user.user_data; + response.data_offset = item->user.fetched_offset - item->user.fetched_size; + response.data.ptr = item->buffer.ptr; + response.data.size = item->user.fetched_size; + response.buffer = item->buffer; + item->callback(&response); +} + +_SOKOL_PRIVATE void _sfetch_cancel_item(_sfetch_item_t* item) { + item->state = _SFETCH_STATE_FAILED; + item->user.finished = true; + item->user.error_code = SFETCH_ERROR_CANCELLED; +} + +/* per-frame channel stuff: move requests in and out of the IO threads, call response callbacks */ +_SOKOL_PRIVATE void _sfetch_channel_dowork(_sfetch_channel_t* chn, _sfetch_pool_t* pool) { + + /* move items from sent- to incoming-queue permitting free lanes */ + const uint32_t num_sent = _sfetch_ring_count(&chn->user_sent); + const uint32_t avail_lanes = _sfetch_ring_count(&chn->free_lanes); + const uint32_t num_move = (num_sent < avail_lanes) ? num_sent : avail_lanes; + for (uint32_t i = 0; i < num_move; i++) { + const uint32_t slot_id = _sfetch_ring_dequeue(&chn->user_sent); + _sfetch_item_t* item = _sfetch_pool_item_lookup(pool, slot_id); + SOKOL_ASSERT(item); + SOKOL_ASSERT(item->state == _SFETCH_STATE_ALLOCATED); + // if the item was cancelled early, kick it out immediately + if (item->user.cancel) { + _sfetch_cancel_item(item); + _sfetch_invoke_response_callback(item); + _sfetch_pool_item_free(pool, slot_id); + continue; + } + item->state = _SFETCH_STATE_DISPATCHED; + item->lane = _sfetch_ring_dequeue(&chn->free_lanes); + // if no buffer provided yet, invoke response callback to do so + if (0 == item->buffer.ptr) { + _sfetch_invoke_response_callback(item); + } + _sfetch_ring_enqueue(&chn->user_incoming, slot_id); + } + + /* prepare incoming items for being moved into the IO thread */ + const uint32_t num_incoming = _sfetch_ring_count(&chn->user_incoming); + for (uint32_t i = 0; i < num_incoming; i++) { + const uint32_t slot_id = _sfetch_ring_peek(&chn->user_incoming, i); + _sfetch_item_t* item = _sfetch_pool_item_lookup(pool, slot_id); + SOKOL_ASSERT(item); + SOKOL_ASSERT(item->state != _SFETCH_STATE_INITIAL); + SOKOL_ASSERT(item->state != _SFETCH_STATE_FETCHING); + /* transfer input params from user- to thread-data */ + if (item->user.pause) { + item->state = _SFETCH_STATE_PAUSED; + item->user.pause = false; + } + if (item->user.cont) { + if (item->state == _SFETCH_STATE_PAUSED) { + item->state = _SFETCH_STATE_FETCHED; + } + item->user.cont = false; + } + if (item->user.cancel) { + _sfetch_cancel_item(item); + } + switch (item->state) { + case _SFETCH_STATE_DISPATCHED: + case _SFETCH_STATE_FETCHED: + item->state = _SFETCH_STATE_FETCHING; + break; + default: break; + } + } + + #if _SFETCH_HAS_THREADS + /* move new items into the IO threads and processed items out of IO threads */ + _sfetch_thread_enqueue_incoming(&chn->thread, &chn->thread_incoming, &chn->user_incoming); + _sfetch_thread_dequeue_outgoing(&chn->thread, &chn->thread_outgoing, &chn->user_outgoing); + #else + /* without threading just directly dequeue items from the user_incoming queue and + call the request handler, the user_outgoing queue will be filled as the + asynchronous HTTP requests sent by the request handler are completed + */ + while (!_sfetch_ring_empty(&chn->user_incoming)) { + uint32_t slot_id = _sfetch_ring_dequeue(&chn->user_incoming); + _sfetch_request_handler(chn->ctx, slot_id); + } + #endif + + /* drain the outgoing queue, prepare items for invoking the response + callback, and finally call the response callback, free finished items + */ + while (!_sfetch_ring_empty(&chn->user_outgoing)) { + const uint32_t slot_id = _sfetch_ring_dequeue(&chn->user_outgoing); + SOKOL_ASSERT(slot_id); + _sfetch_item_t* item = _sfetch_pool_item_lookup(pool, slot_id); + SOKOL_ASSERT(item && item->callback); + SOKOL_ASSERT(item->state != _SFETCH_STATE_INITIAL); + SOKOL_ASSERT(item->state != _SFETCH_STATE_ALLOCATED); + SOKOL_ASSERT(item->state != _SFETCH_STATE_DISPATCHED); + SOKOL_ASSERT(item->state != _SFETCH_STATE_FETCHED); + /* transfer output params from thread- to user-data */ + item->user.fetched_offset = item->thread.fetched_offset; + item->user.fetched_size = item->thread.fetched_size; + if (item->user.cancel) { + _sfetch_cancel_item(item); + } + else { + item->user.error_code = item->thread.error_code; + } + if (item->thread.finished) { + item->user.finished = true; + } + /* state transition */ + if (item->thread.failed) { + item->state = _SFETCH_STATE_FAILED; + } + else if (item->state == _SFETCH_STATE_FETCHING) { + item->state = _SFETCH_STATE_FETCHED; + } + _sfetch_invoke_response_callback(item); + + /* when the request is finished, free the lane for another request, + otherwise feed it back into the incoming queue + */ + if (item->user.finished) { + _sfetch_ring_enqueue(&chn->free_lanes, item->lane); + _sfetch_pool_item_free(pool, slot_id); + } + else { + _sfetch_ring_enqueue(&chn->user_incoming, slot_id); + } + } +} + +_SOKOL_PRIVATE bool _sfetch_validate_request(_sfetch_t* ctx, const sfetch_request_t* req) { + if (req->channel >= ctx->desc.num_channels) { + _SFETCH_ERROR(REQUEST_CHANNEL_INDEX_TOO_BIG); + return false; + } + if (!req->path) { + _SFETCH_ERROR(REQUEST_PATH_IS_NULL); + return false; + } + if (strlen(req->path) >= (SFETCH_MAX_PATH-1)) { + _SFETCH_ERROR(REQUEST_PATH_TOO_LONG); + return false; + } + if (!req->callback) { + _SFETCH_ERROR(REQUEST_CALLBACK_MISSING); + return false; + } + if (req->chunk_size > req->buffer.size) { + _SFETCH_ERROR(REQUEST_CHUNK_SIZE_GREATER_BUFFER_SIZE); + return false; + } + if (req->user_data.ptr && (req->user_data.size == 0)) { + _SFETCH_ERROR(REQUEST_USERDATA_PTR_IS_SET_BUT_USERDATA_SIZE_IS_NULL); + return false; + } + if (!req->user_data.ptr && (req->user_data.size > 0)) { + _SFETCH_ERROR(REQUEST_USERDATA_PTR_IS_NULL_BUT_USERDATA_SIZE_IS_NOT); + return false; + } + if (req->user_data.size > SFETCH_MAX_USERDATA_UINT64 * sizeof(uint64_t)) { + _SFETCH_ERROR(REQUEST_USERDATA_SIZE_TOO_BIG); + return false; + } + return true; +} + +_SOKOL_PRIVATE sfetch_desc_t _sfetch_desc_defaults(const sfetch_desc_t* desc) { + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); + sfetch_desc_t res = *desc; + res.max_requests = _sfetch_def(desc->max_requests, 128); + res.num_channels = _sfetch_def(desc->num_channels, 1); + res.num_lanes = _sfetch_def(desc->num_lanes, 1); + return res; +} + +// ██████ ██ ██ ██████ ██ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ ██ ██████ +// +// >>public +SOKOL_API_IMPL void sfetch_setup(const sfetch_desc_t* desc_) { + SOKOL_ASSERT(desc_); + SOKOL_ASSERT(0 == _sfetch); + + sfetch_desc_t desc = _sfetch_desc_defaults(desc_); + _sfetch = (_sfetch_t*) _sfetch_malloc_with_allocator(&desc.allocator, sizeof(_sfetch_t)); + SOKOL_ASSERT(_sfetch); + _sfetch_t* ctx = _sfetch_ctx(); + _sfetch_clear(ctx, sizeof(_sfetch_t)); + ctx->desc = desc; + ctx->setup = true; + ctx->valid = true; + + /* replace zero-init items with default values */ + if (ctx->desc.num_channels > SFETCH_MAX_CHANNELS) { + ctx->desc.num_channels = SFETCH_MAX_CHANNELS; + _SFETCH_WARN(CLAMPING_NUM_CHANNELS_TO_MAX_CHANNELS); + } + + /* setup the global request item pool */ + ctx->valid &= _sfetch_pool_init(&ctx->pool, ctx->desc.max_requests); + + /* setup IO channels (one thread per channel) */ + for (uint32_t i = 0; i < ctx->desc.num_channels; i++) { + ctx->valid &= _sfetch_channel_init(&ctx->chn[i], ctx, ctx->desc.max_requests, ctx->desc.num_lanes, _sfetch_request_handler); + } +} + +SOKOL_API_IMPL void sfetch_shutdown(void) { + _sfetch_t* ctx = _sfetch_ctx(); + SOKOL_ASSERT(ctx && ctx->setup); + ctx->valid = false; + /* IO threads must be shutdown first */ + for (uint32_t i = 0; i < ctx->desc.num_channels; i++) { + if (ctx->chn[i].valid) { + _sfetch_channel_discard(&ctx->chn[i]); + } + } + _sfetch_pool_discard(&ctx->pool); + ctx->setup = false; + _sfetch_free(ctx); + _sfetch = 0; +} + +SOKOL_API_IMPL bool sfetch_valid(void) { + _sfetch_t* ctx = _sfetch_ctx(); + return ctx && ctx->valid; +} + +SOKOL_API_IMPL sfetch_desc_t sfetch_desc(void) { + _sfetch_t* ctx = _sfetch_ctx(); + SOKOL_ASSERT(ctx && ctx->valid); + return ctx->desc; +} + +SOKOL_API_IMPL int sfetch_max_userdata_bytes(void) { + return SFETCH_MAX_USERDATA_UINT64 * 8; +} + +SOKOL_API_IMPL int sfetch_max_path(void) { + return SFETCH_MAX_PATH; +} + +SOKOL_API_IMPL bool sfetch_handle_valid(sfetch_handle_t h) { + _sfetch_t* ctx = _sfetch_ctx(); + SOKOL_ASSERT(ctx && ctx->valid); + /* shortcut invalid handle */ + if (h.id == 0) { + return false; + } + return 0 != _sfetch_pool_item_lookup(&ctx->pool, h.id); +} + +SOKOL_API_IMPL sfetch_handle_t sfetch_send(const sfetch_request_t* request) { + _sfetch_t* ctx = _sfetch_ctx(); + SOKOL_ASSERT(ctx && ctx->setup); + + const sfetch_handle_t invalid_handle = _sfetch_make_handle(0); + if (!ctx->valid) { + return invalid_handle; + } + if (!_sfetch_validate_request(ctx, request)) { + return invalid_handle; + } + SOKOL_ASSERT(request->channel < ctx->desc.num_channels); + + uint32_t slot_id = _sfetch_pool_item_alloc(&ctx->pool, request); + if (0 == slot_id) { + _SFETCH_WARN(REQUEST_POOL_EXHAUSTED); + return invalid_handle; + } + if (!_sfetch_channel_send(&ctx->chn[request->channel], slot_id)) { + /* send failed because the channels sent-queue overflowed */ + _sfetch_pool_item_free(&ctx->pool, slot_id); + return invalid_handle; + } + return _sfetch_make_handle(slot_id); +} + +SOKOL_API_IMPL void sfetch_dowork(void) { + _sfetch_t* ctx = _sfetch_ctx(); + SOKOL_ASSERT(ctx && ctx->setup); + if (!ctx->valid) { + return; + } + /* we're pumping each channel 2x so that unfinished request items coming out the + IO threads can be moved back into the IO-thread immediately without + having to wait a frame + */ + ctx->in_callback = true; + for (int pass = 0; pass < 2; pass++) { + for (uint32_t chn_index = 0; chn_index < ctx->desc.num_channels; chn_index++) { + _sfetch_channel_dowork(&ctx->chn[chn_index], &ctx->pool); + } + } + ctx->in_callback = false; +} + +SOKOL_API_IMPL void sfetch_bind_buffer(sfetch_handle_t h, sfetch_range_t buffer) { + _sfetch_t* ctx = _sfetch_ctx(); + SOKOL_ASSERT(ctx && ctx->valid); + SOKOL_ASSERT(ctx->in_callback); + SOKOL_ASSERT(buffer.ptr && (buffer.size > 0)); + _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, h.id); + if (item) { + SOKOL_ASSERT((0 == item->buffer.ptr) && (0 == item->buffer.size)); + item->buffer = buffer; + } +} + +SOKOL_API_IMPL void* sfetch_unbind_buffer(sfetch_handle_t h) { + _sfetch_t* ctx = _sfetch_ctx(); + SOKOL_ASSERT(ctx && ctx->valid); + SOKOL_ASSERT(ctx->in_callback); + _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, h.id); + if (item) { + void* prev_buf_ptr = (void*)item->buffer.ptr; + item->buffer.ptr = 0; + item->buffer.size = 0; + return prev_buf_ptr; + } + else { + return 0; + } +} + +SOKOL_API_IMPL void sfetch_pause(sfetch_handle_t h) { + _sfetch_t* ctx = _sfetch_ctx(); + SOKOL_ASSERT(ctx && ctx->valid); + _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, h.id); + if (item) { + item->user.pause = true; + item->user.cont = false; + } +} + +SOKOL_API_IMPL void sfetch_continue(sfetch_handle_t h) { + _sfetch_t* ctx = _sfetch_ctx(); + SOKOL_ASSERT(ctx && ctx->valid); + _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, h.id); + if (item) { + item->user.cont = true; + item->user.pause = false; + } +} + +SOKOL_API_IMPL void sfetch_cancel(sfetch_handle_t h) { + _sfetch_t* ctx = _sfetch_ctx(); + SOKOL_ASSERT(ctx && ctx->valid); + _sfetch_item_t* item = _sfetch_pool_item_lookup(&ctx->pool, h.id); + if (item) { + item->user.cont = false; + item->user.pause = false; + item->user.cancel = true; + } +} +#endif /* SOKOL_FETCH_IMPL */ diff --git a/src/sokol/c/sokol_imgui.c b/src/sokol/c/sokol_imgui.c new file mode 100644 index 0000000..12b29fa --- /dev/null +++ b/src/sokol/c/sokol_imgui.c @@ -0,0 +1,9 @@ +#if defined(IMPL) +#define SOKOL_IMGUI_IMPL +#endif +#include "sokol_defines.h" +#include "sokol_app.h" +#include "sokol_gfx.h" +#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS +#include "cimgui.h" +#include "sokol_imgui.h" \ No newline at end of file diff --git a/src/sokol/c/sokol_imgui.h b/src/sokol/c/sokol_imgui.h new file mode 100644 index 0000000..ad556e3 --- /dev/null +++ b/src/sokol/c/sokol_imgui.h @@ -0,0 +1,3150 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_IMGUI_IMPL) +#define SOKOL_IMGUI_IMPL +#endif +#ifndef SOKOL_IMGUI_INCLUDED +/* + sokol_imgui.h -- drop-in Dear ImGui renderer/event-handler for sokol_gfx.h + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_IMGUI_IMPL + + before you include this file in *one* C or C++ file to create the + implementation. + + NOTE that the implementation can be compiled either as C++ or as C. + When compiled as C++, sokol_imgui.h will directly call into the + Dear ImGui C++ API. When compiled as C, sokol_imgui.h will call + cimgui.h functions instead. + + NOTE that the formerly separate header sokol_cimgui.h has been + merged into sokol_imgui.h + + The following defines are used by the implementation to select the + platform-specific embedded shader code (these are the same defines as + used by sokol_gfx.h and sokol_app.h): + + SOKOL_GLCORE + SOKOL_GLES3 + SOKOL_D3D11 + SOKOL_METAL + SOKOL_WGPU + + Optionally provide the following configuration define both before including the + the declaration and implementation: + + SOKOL_IMGUI_NO_SOKOL_APP - don't depend on sokol_app.h (see below for details) + + Optionally provide the following macros before including the implementation + to override defaults: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_IMGUI_API_DECL- public function declaration prefix (default: extern) + SOKOL_API_DECL - same as SOKOL_IMGUI_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + + If sokol_imgui.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_IMGUI_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + Include the following headers before sokol_imgui.h (both before including + the declaration and implementation): + + sokol_gfx.h + sokol_app.h (except SOKOL_IMGUI_NO_SOKOL_APP) + + Additionally, include the following headers before including the + implementation: + + If the implementation is compiled as C++: + imgui.h + + If the implementation is compiled as C: + cimgui.h + + + FEATURE OVERVIEW: + ================= + sokol_imgui.h implements the initialization, rendering and event-handling + code for Dear ImGui (https://github.com/ocornut/imgui) on top of + sokol_gfx.h and (optionally) sokol_app.h. + + The sokol_app.h dependency is optional and used for input event handling. + If you only use sokol_gfx.h but not sokol_app.h in your application, + define SOKOL_IMGUI_NO_SOKOL_APP before including the implementation + of sokol_imgui.h, this will remove any dependency to sokol_app.h, but + you must feed input events into Dear ImGui yourself. + + sokol_imgui.h is not thread-safe, all calls must be made from the + same thread where sokol_gfx.h is running. + + HOWTO: + ====== + + --- To initialize sokol-imgui, call: + + simgui_setup(const simgui_desc_t* desc) + + This will initialize Dear ImGui and create sokol-gfx resources + (two buffers for vertices and indices, a font texture and a pipeline- + state-object). + + Use the following simgui_desc_t members to configure behaviour: + + int max_vertices + The maximum number of vertices used for UI rendering, default is 65536. + sokol-imgui will use this to compute the size of the vertex- + and index-buffers allocated via sokol_gfx.h + + int image_pool_size + Number of simgui_image_t objects which can be alive at the same time. + The default is 256. + + sg_pixel_format color_format + The color pixel format of the render pass where the UI + will be rendered. The default (0) matches sokoL_gfx.h's + default pass. + + sg_pixel_format depth_format + The depth-buffer pixel format of the render pass where + the UI will be rendered. The default (0) matches + sokol_gfx.h's default pass depth format. + + int sample_count + The MSAA sample-count of the render pass where the UI + will be rendered. The default (0) matches sokol_gfx.h's + default pass sample count. + + const char* ini_filename + Sets this path as ImGui::GetIO().IniFilename where ImGui will store + and load UI persistency data. By default this is 0, so that Dear ImGui + will not preserve state between sessions (and also won't do + any filesystem calls). Also see the ImGui functions: + - LoadIniSettingsFromMemory() + - SaveIniSettingsFromMemory() + These functions give you explicit control over loading and saving + UI state while using your own filesystem wrapper functions (in this + case keep simgui_desc.ini_filename zero) + + bool no_default_font + Set this to true if you don't want to use ImGui's default + font. In this case you need to initialize the font + yourself after simgui_setup() is called. + + bool disable_paste_override + If set to true, sokol_imgui.h will not 'emulate' a Dear Imgui + clipboard paste action on SAPP_EVENTTYPE_CLIPBOARD_PASTED event. + This is mainly a hack/workaround to allow external workarounds + for making copy/paste work on the web platform. In general, + copy/paste support isn't properly fleshed out in sokol_imgui.h yet. + + bool disable_set_mouse_cursor + If true, sokol_imgui.h will not control the mouse cursor type + by calling sapp_set_mouse_cursor(). + + bool disable_windows_resize_from_edges + If true, windows can only be resized from the bottom right corner. + The default is false, meaning windows can be resized from edges. + + bool write_alpha_channel + Set this to true if you want alpha values written to the + framebuffer. By default this behavior is disabled to prevent + undesired behavior on platforms like the web where the canvas is + always alpha-blended with the background. + + simgui_allocator_t allocator + Used to override memory allocation functions. See further below + for details. + + simgui_logger_t logger + A user-provided logging callback. Note that without logging + callback, sokol-imgui will be completely silent! + See the section about ERROR REPORTING AND LOGGING below + for more details. + + --- At the start of a frame, call: + + simgui_new_frame(&(simgui_frame_desc_t){ + .width = ..., + .height = ..., + .delta_time = ..., + .dpi_scale = ... + }); + + 'width' and 'height' are the dimensions of the rendering surface, + passed to ImGui::GetIO().DisplaySize. + + 'delta_time' is the frame duration passed to ImGui::GetIO().DeltaTime. + + 'dpi_scale' is the current DPI scale factor, if this is left zero-initialized, + 1.0f will be used instead. Typical values for dpi_scale are >= 1.0f. + + For example, if you're using sokol_app.h and render to the default framebuffer: + + simgui_new_frame(&(simgui_frame_desc_t){ + .width = sapp_width(), + .height = sapp_height(), + .delta_time = sapp_frame_duration(), + .dpi_scale = sapp_dpi_scale() + }); + + --- at the end of the frame, before the sg_end_pass() where you + want to render the UI, call: + + simgui_render() + + This will first call ImGui::Render(), and then render ImGui's draw list + through sokol_gfx.h + + --- if you're using sokol_app.h, from inside the sokol_app.h event callback, + call: + + bool simgui_handle_event(const sapp_event* ev); + + The return value is the value of ImGui::GetIO().WantCaptureKeyboard, + if this is true, you might want to skip keyboard input handling + in your own event handler. + + If you want to use the ImGui functions for checking if a key is pressed + (e.g. ImGui::IsKeyPressed()) the following helper function to map + an sapp_keycode to an ImGuiKey value may be useful: + + int simgui_map_keycode(sapp_keycode c); + + Note that simgui_map_keycode() can be called outside simgui_setup()/simgui_shutdown(). + + --- finally, on application shutdown, call + + simgui_shutdown() + + + ON USER-PROVIDED IMAGES AND SAMPLERS + ==================================== + To render your own images via ImGui::Image(), first create an simgui_image_t + object from a sokol-gfx image and sampler object. + + // create a sokol-imgui image object which associates an sg_image with an sg_sampler + simgui_image_t simgui_img = simgui_make_image(&(simgui_image_desc_t){ + .image = sg_make_image(...), + .sampler = sg_make_sampler(...), + }); + + // convert the returned image handle into a ImTextureID handle + ImTextureID tex_id = simgui_imtextureid(simgui_img); + + // use the ImTextureID handle in Dear ImGui calls: + ImGui::Image(tex_id, ...); + + simgui_image_t objects are small and cheap (literally just the image and sampler + handle). + + You can omit the sampler handle in the simgui_make_image() call, in this case a + default sampler will be used with nearest-filtering and clamp-to-edge. + + Trying to render with an invalid simgui_image_t handle will render a small 8x8 + white default texture instead. + + To destroy a sokol-imgui image object, call + + simgui_destroy_image(simgui_img); + + But please be aware that the image object needs to be around until simgui_render() is called + in a frame (if this turns out to be too much of a hassle we could introduce some sort + of garbage collection where destroyed simgui_image_t objects are kept around until + the simgui_render() call). + + You can call: + + simgui_image_desc_t desc = simgui_query_image_desc(img) + + ...to get the original desc struct, useful if you need to get the sokol-gfx image + and sampler handle of the simgui_image_t object. + + You can convert an ImTextureID back into an simgui_image_t handle: + + simgui_image_t img = simgui_image_from_imtextureid(tex_id); + + + MEMORY ALLOCATION OVERRIDE + ========================== + You can override the memory allocation functions at initialization time + like this: + + void* my_alloc(size_t size, void* user_data) { + return malloc(size); + } + + void my_free(void* ptr, void* user_data) { + free(ptr); + } + + ... + simgui_setup(&(simgui_desc_t){ + // ... + .allocator = { + .alloc_fn = my_alloc, + .free_fn = my_free, + .user_data = ...; + } + }); + ... + + If no overrides are provided, malloc and free will be used. + + This only affects memory allocation calls done by sokol_imgui.h + itself though, not any allocations in Dear ImGui. + + + 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" + + simgui_setup(&(simgui_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. 'simgui' + uint32_t log_level, // 0=panic, 1=error, 2=warn, 3=info + uint32_t log_item_id, // SIMGUI_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_imgui.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data) + { + ... + } + + ...and then setup sokol-imgui like this: + + simgui_setup(&(simgui_desc_t){ + .logger = { + .func = my_log, + .user_data = my_user_data, + } + }); + + 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. + + + IMGUI EVENT HANDLING + ==================== + You can call these functions from your platform's events to handle ImGui events + when SOKOL_IMGUI_NO_SOKOL_APP is defined. + + E.g. mouse position events can be dispatched like this: + + simgui_add_mouse_pos_event(100, 200); + + For adding key events, you're responsible to map your own key codes to ImGuiKey + values and pass those as int: + + simgui_add_key_event(imgui_key, true); + + Take note that modifiers (shift, ctrl, etc.) must be updated manually. + + If sokol_app is being used, ImGui events are handled for you. + + + LICENSE + ======= + + zlib/libpng license + + Copyright (c) 2018 Andre Weissflog + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#define SOKOL_IMGUI_INCLUDED (1) +#include +#include +#include // size_t + +#if !defined(SOKOL_GFX_INCLUDED) +#error "Please include sokol_gfx.h before sokol_imgui.h" +#endif +#if !defined(SOKOL_IMGUI_NO_SOKOL_APP) && !defined(SOKOL_APP_INCLUDED) +#error "Please include sokol_app.h before sokol_imgui.h" +#endif + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_IMGUI_API_DECL) +#define SOKOL_IMGUI_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_IMGUI_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_IMGUI_IMPL) +#define SOKOL_IMGUI_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_IMGUI_API_DECL __declspec(dllimport) +#else +#define SOKOL_IMGUI_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + SIMGUI_INVALID_ID = 0, +}; + +/* + simgui_image_t + + A combined image-sampler pair used to inject custom images and samplers into Dear ImGui. + + Create with simgui_make_image(), and convert to an ImTextureID handle via + simgui_imtextureid(). +*/ +typedef struct simgui_image_t { uint32_t id; } simgui_image_t; + +/* + simgui_image_desc_t + + Descriptor struct for simgui_make_image(). You must provide + at least an sg_image handle. Keeping the sg_sampler handle + zero-initialized will select the builtin default sampler + which uses linear filtering. +*/ +typedef struct simgui_image_desc_t { + sg_image image; + sg_sampler sampler; +} simgui_image_desc_t; + +/* + simgui_log_item + + An enum with a unique item for each log message, warning, error + and validation layer message. +*/ +#define _SIMGUI_LOG_ITEMS \ + _SIMGUI_LOGITEM_XMACRO(OK, "Ok") \ + _SIMGUI_LOGITEM_XMACRO(MALLOC_FAILED, "memory allocation failed") \ + _SIMGUI_LOGITEM_XMACRO(IMAGE_POOL_EXHAUSTED, "image pool exhausted") \ + +#define _SIMGUI_LOGITEM_XMACRO(item,msg) SIMGUI_LOGITEM_##item, +typedef enum simgui_log_item_t { + _SIMGUI_LOG_ITEMS +} simgui_log_item_t; +#undef _SIMGUI_LOGITEM_XMACRO + +/* + simgui_allocator_t + + Used in simgui_desc_t to provide custom memory-alloc and -free functions + to sokol_imgui.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 simgui_allocator_t { + void* (*alloc_fn)(size_t size, void* user_data); + void (*free_fn)(void* ptr, void* user_data); + void* user_data; +} simgui_allocator_t; + +/* + simgui_logger + + Used in simgui_desc_t to provide a logging function. Please be aware + that without logging function, sokol-imgui 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 simgui_logger_t { + void (*func)( + const char* tag, // always "simgui" + uint32_t log_level, // 0=panic, 1=error, 2=warning, 3=info + uint32_t log_item_id, // SIMGUI_LOGITEM_* + const char* message_or_null, // a message string, may be nullptr in release mode + uint32_t line_nr, // line number in sokol_imgui.h + const char* filename_or_null, // source filename, may be nullptr in release mode + void* user_data); + void* user_data; +} simgui_logger_t; + +typedef struct simgui_desc_t { + int max_vertices; // default: 65536 + int image_pool_size; // default: 256 + sg_pixel_format color_format; + sg_pixel_format depth_format; + int sample_count; + const char* ini_filename; + bool no_default_font; + bool disable_paste_override; // if true, don't send Ctrl-V on EVENTTYPE_CLIPBOARD_PASTED + bool disable_set_mouse_cursor; // if true, don't control the mouse cursor type via sapp_set_mouse_cursor() + bool disable_windows_resize_from_edges; // if true, only resize edges from the bottom right corner + bool write_alpha_channel; // if true, alpha values get written into the framebuffer + simgui_allocator_t allocator; // optional memory allocation overrides (default: malloc/free) + simgui_logger_t logger; // optional log function override +} simgui_desc_t; + +typedef struct simgui_frame_desc_t { + int width; + int height; + double delta_time; + float dpi_scale; +} simgui_frame_desc_t; + +typedef struct simgui_font_tex_desc_t { + sg_filter min_filter; + sg_filter mag_filter; +} simgui_font_tex_desc_t; + +SOKOL_IMGUI_API_DECL void simgui_setup(const simgui_desc_t* desc); +SOKOL_IMGUI_API_DECL void simgui_new_frame(const simgui_frame_desc_t* desc); +SOKOL_IMGUI_API_DECL void simgui_render(void); +SOKOL_IMGUI_API_DECL simgui_image_t simgui_make_image(const simgui_image_desc_t* desc); +SOKOL_IMGUI_API_DECL void simgui_destroy_image(simgui_image_t img); +SOKOL_IMGUI_API_DECL simgui_image_desc_t simgui_query_image_desc(simgui_image_t img); +SOKOL_IMGUI_API_DECL void* simgui_imtextureid(simgui_image_t img); +SOKOL_IMGUI_API_DECL simgui_image_t simgui_image_from_imtextureid(void* im_texture_id); +SOKOL_IMGUI_API_DECL void simgui_add_focus_event(bool focus); +SOKOL_IMGUI_API_DECL void simgui_add_mouse_pos_event(float x, float y); +SOKOL_IMGUI_API_DECL void simgui_add_touch_pos_event(float x, float y); +SOKOL_IMGUI_API_DECL void simgui_add_mouse_button_event(int mouse_button, bool down); +SOKOL_IMGUI_API_DECL void simgui_add_mouse_wheel_event(float wheel_x, float wheel_y); +SOKOL_IMGUI_API_DECL void simgui_add_key_event(int imgui_key, bool down); +SOKOL_IMGUI_API_DECL void simgui_add_input_character(uint32_t c); +SOKOL_IMGUI_API_DECL void simgui_add_input_characters_utf8(const char* c); +SOKOL_IMGUI_API_DECL void simgui_add_touch_button_event(int mouse_button, bool down); +#if !defined(SOKOL_IMGUI_NO_SOKOL_APP) +SOKOL_IMGUI_API_DECL bool simgui_handle_event(const sapp_event* ev); +SOKOL_IMGUI_API_DECL int simgui_map_keycode(sapp_keycode keycode); // returns ImGuiKey_* +#endif +SOKOL_IMGUI_API_DECL void simgui_shutdown(void); +SOKOL_IMGUI_API_DECL void simgui_create_fonts_texture(const simgui_font_tex_desc_t* desc); +SOKOL_IMGUI_API_DECL void simgui_destroy_fonts_texture(void); + +#ifdef __cplusplus +} // extern "C" + +// reference-based equivalents for C++ +inline void simgui_setup(const simgui_desc_t& desc) { return simgui_setup(&desc); } +inline simgui_image_t simgui_make_image(const simgui_image_desc_t& desc) { return simgui_make_image(&desc); } +inline void simgui_new_frame(const simgui_frame_desc_t& desc) { return simgui_new_frame(&desc); } +inline void simgui_create_fonts_texture(const simgui_font_tex_desc_t& desc) { return simgui_create_fonts_texture(&desc); } + +#endif +#endif /* SOKOL_IMGUI_INCLUDED */ + +//-- IMPLEMENTATION ------------------------------------------------------------ +#ifdef SOKOL_IMGUI_IMPL +#define SOKOL_IMGUI_IMPL_INCLUDED (1) + +#if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE) +#error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use simgui_desc_t.allocator to override memory allocation functions" +#endif + +#if defined(__cplusplus) + #if !defined(IMGUI_VERSION) + #error "Please include imgui.h before the sokol_imgui.h implementation" + #endif +#else + #if !defined(CIMGUI_INCLUDED) + #error "Please include cimgui.h before the sokol_imgui.h implementation" + #endif +#endif + +#include // memset +#include // malloc/free + +#if defined(__EMSCRIPTEN__) && !defined(SOKOL_DUMMY_BACKEND) +#include +#endif + +#ifndef SOKOL_API_IMPL +#define SOKOL_API_IMPL +#endif +#ifndef SOKOL_DEBUG + #ifndef NDEBUG + #define SOKOL_DEBUG + #endif +#endif +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif +#ifndef _SOKOL_PRIVATE + #if defined(__GNUC__) || defined(__clang__) + #define _SOKOL_PRIVATE __attribute__((unused)) static + #else + #define _SOKOL_PRIVATE static + #endif +#endif + +#define _SIMGUI_INIT_COOKIE (0xBABEBABE) +#define _SIMGUI_INVALID_SLOT_INDEX (0) +#define _SIMGUI_SLOT_SHIFT (16) +#define _SIMGUI_MAX_POOL_SIZE (1<<_SIMGUI_SLOT_SHIFT) +#define _SIMGUI_SLOT_MASK (_SIMGUI_MAX_POOL_SIZE-1) + +// helper macros and constants +#define _simgui_def(val, def) (((val) == 0) ? (def) : (val)) + +// workaround for missing ImDrawCallback_ResetRenderState in cimgui.h +// see: https://github.com/cimgui/cimgui/issues/261 +#ifndef ImDrawCallback_ResetRenderState +#define ImDrawCallback_ResetRenderState (ImDrawCallback)(-8) +#endif + +typedef struct { + ImVec2 disp_size; + uint8_t _pad_8[8]; +} _simgui_vs_params_t; + +typedef enum { + _SIMGUI_RESOURCESTATE_INITIAL, + _SIMGUI_RESOURCESTATE_ALLOC, + _SIMGUI_RESOURCESTATE_VALID, + _SIMGUI_RESOURCESTATE_FAILED, + _SIMGUI_RESOURCESTATE_INVALID, + _SIMGUI_RESOURCESTATE_FORCE_U32 = 0x7FFFFFFF +} _simgui_resource_state; + +typedef struct { + uint32_t id; + _simgui_resource_state state; +} _simgui_slot_t; + +typedef struct { + int size; + int queue_top; + uint32_t* gen_ctrs; + int* free_queue; +} _simgui_pool_t; + +typedef struct { + _simgui_slot_t slot; + sg_image image; + sg_sampler sampler; + sg_pipeline pip; // this will either be _simgui.def_pip or _simgui.pip_unfilterable +} _simgui_image_t; + +typedef struct { + _simgui_pool_t pool; + _simgui_image_t* items; +} _simgui_image_pool_t; + +typedef struct { + uint32_t init_cookie; + simgui_desc_t desc; + float cur_dpi_scale; + sg_buffer vbuf; + sg_buffer ibuf; + sg_image font_img; + sg_sampler font_smp; + simgui_image_t default_font; + sg_image def_img; // used as default image for user images + sg_sampler def_smp; // used as default sampler for user images + sg_shader def_shd; + sg_pipeline def_pip; + // separate shader and pipeline for unfilterable user images + sg_shader shd_unfilterable; + sg_pipeline pip_unfilterable; + sg_range vertices; + sg_range indices; + bool is_osx; + _simgui_image_pool_t image_pool; +} _simgui_state_t; +static _simgui_state_t _simgui; + +/* + Embedded source code compiled with: + + sokol-shdc -i simgui.glsl -o simgui.h -l glsl410: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) + + @vs vs + uniform vs_params { + vec2 disp_size; + }; + in vec2 position; + in vec2 texcoord0; + in vec4 color0; + out vec2 uv; + out vec4 color; + void main() { + gl_Position = vec4(((position/disp_size)-0.5)*vec2(2.0,-2.0), 0.5, 1.0); + uv = texcoord0; + color = color0; + } + @end + + @fs fs + uniform texture2D tex; + uniform sampler smp; + in vec2 uv; + in vec4 color; + out vec4 frag_color; + void main() { + frag_color = texture(sampler2D(tex, smp), uv) * color; + } + @end + + @program simgui vs fs +*/ +#if defined(SOKOL_GLCORE) +static const uint8_t _simgui_vs_source_glsl410[383] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,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,0x31,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,0x32,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,0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x32,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,0x6c,0x61,0x79,0x6f,0x75,0x74, + 0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31,0x29,0x20,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,0x76,0x65,0x63,0x34,0x28,0x28,0x28,0x70,0x6f,0x73,0x69, + 0x74,0x69,0x6f,0x6e,0x20,0x2f,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73, + 0x5b,0x30,0x5d,0x2e,0x78,0x79,0x29,0x20,0x2d,0x20,0x76,0x65,0x63,0x32,0x28,0x30, + 0x2e,0x35,0x29,0x29,0x20,0x2a,0x20,0x76,0x65,0x63,0x32,0x28,0x32,0x2e,0x30,0x2c, + 0x20,0x2d,0x32,0x2e,0x30,0x29,0x2c,0x20,0x30,0x2e,0x35,0x2c,0x20,0x31,0x2e,0x30, + 0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x74,0x65,0x78,0x63, + 0x6f,0x6f,0x72,0x64,0x30,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 uint8_t _simgui_fs_source_glsl410[219] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,0x30,0x0a,0x0a,0x75,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,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,0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,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,0x30,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x32, + 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, + 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,0x29,0x20,0x2a,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_GLES3) +static const uint8_t _simgui_vs_source_glsl300es[344] = { + 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,0x31,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,0x32,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x3b,0x0a,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x32,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,0x76,0x65, + 0x63,0x34,0x28,0x28,0x28,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x2f,0x20, + 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2e,0x78,0x79,0x29, + 0x20,0x2d,0x20,0x76,0x65,0x63,0x32,0x28,0x30,0x2e,0x35,0x29,0x29,0x20,0x2a,0x20, + 0x76,0x65,0x63,0x32,0x28,0x32,0x2e,0x30,0x2c,0x20,0x2d,0x32,0x2e,0x30,0x29,0x2c, + 0x20,0x30,0x2e,0x35,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20, + 0x75,0x76,0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,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 uint8_t _simgui_fs_source_glsl300es[250] = { + 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,0x32,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,0x29,0x20,0x2a,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_METAL) +static const uint8_t _simgui_vs_bytecode_metal_macos[3052] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xec,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, + 0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0xe0,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,0x7b,0x12,0x23,0x17,0xd9,0x25,0x1c, + 0x1b,0x42,0x42,0x9f,0xbf,0x31,0xd2,0x2c,0x3a,0x55,0x22,0x1d,0x40,0xd8,0xc4,0xf8, + 0x20,0x49,0x60,0x6d,0x3c,0xea,0x4e,0x1c,0x34,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,0x37,0x00,0x00,0x00,0x56,0x41,0x54, + 0x54,0x22,0x00,0x03,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x80, + 0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x01,0x80,0x63,0x6f,0x6c,0x6f, + 0x72,0x30,0x00,0x02,0x80,0x56,0x41,0x54,0x59,0x05,0x00,0x03,0x00,0x04,0x04,0x06, + 0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b, + 0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0xc8,0x0a,0x00,0x00,0xff,0xff,0xff,0xff, + 0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0xaf,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,0x10,0x45,0x02,0x42,0x92,0x0b,0x42,0x84,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,0x08,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00, + 0x51,0x18,0x00,0x00,0x81,0x00,0x00,0x00,0x1b,0xc8,0x25,0xf8,0xff,0xff,0xff,0xff, + 0x01,0x90,0x80,0x8a,0x18,0x87,0x77,0x90,0x07,0x79,0x28,0x87,0x71,0xa0,0x07,0x76, + 0xc8,0x87,0x36,0x90,0x87,0x77,0xa8,0x07,0x77,0x20,0x87,0x72,0x20,0x87,0x36,0x20, + 0x87,0x74,0xb0,0x87,0x74,0x20,0x87,0x72,0x68,0x83,0x79,0x88,0x07,0x79,0xa0,0x87, + 0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0xc0,0x1c,0xc2,0x81, + 0x1d,0xe6,0xa1,0x1c,0x00,0x82,0x1c,0xd2,0x61,0x1e,0xc2,0x41,0x1c,0xd8,0xa1,0x1c, + 0xda,0x80,0x1e,0xc2,0x21,0x1d,0xd8,0xa1,0x0d,0xc6,0x21,0x1c,0xd8,0x81,0x1d,0xe6, + 0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07,0x80,0x60,0x87,0x72,0x98,0x87,0x79, + 0x68,0x03,0x78,0x90,0x87,0x72,0x18,0x87,0x74,0x98,0x87,0x72,0x68,0x03,0x73,0x80, + 0x87,0x76,0x08,0x07,0x72,0x00,0xcc,0x21,0x1c,0xd8,0x61,0x1e,0xca,0x01,0x20,0xdc, + 0xe1,0x1d,0xda,0xc0,0x1c,0xe4,0x21,0x1c,0xda,0xa1,0x1c,0xda,0x00,0x1e,0xde,0x21, + 0x1d,0xdc,0x81,0x1e,0xca,0x41,0x1e,0xda,0xa0,0x1c,0xd8,0x21,0x1d,0xda,0x01,0xa0, + 0x07,0x79,0xa8,0x87,0x72,0x00,0x06,0x77,0x78,0x87,0x36,0x30,0x07,0x79,0x08,0x87, + 0x76,0x28,0x87,0x36,0x80,0x87,0x77,0x48,0x07,0x77,0xa0,0x87,0x72,0x90,0x87,0x36, + 0x28,0x07,0x76,0x48,0x87,0x76,0x68,0x03,0x77,0x78,0x07,0x77,0x68,0x03,0x76,0x28, + 0x87,0x70,0x30,0x07,0x80,0x70,0x87,0x77,0x68,0x83,0x74,0x70,0x07,0x73,0x98,0x87, + 0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1, + 0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0x40,0x1d,0xea,0xa1,0x1d,0xe0,0xa1,0x0d, + 0xe8,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87, + 0x72,0x00,0x08,0x77,0x78,0x87,0x36,0x70,0x87,0x70,0x70,0x87,0x79,0x68,0x03,0x73, + 0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c, + 0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xe6,0x21,0x1d,0xce,0xc1,0x1d,0xca,0x81,0x1c,0xda, + 0x40,0x1f,0xca,0x41,0x1e,0xde,0x61,0x1e,0xda,0xc0,0x1c,0xe0,0xa1,0x0d,0xda,0x21, + 0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80,0x70,0x87,0x77,0x68, + 0x03,0x7a,0x90,0x87,0x70,0x80,0x07,0x78,0x48,0x07,0x77,0x38,0x87,0x36,0x68,0x87, + 0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0x62,0x1e,0xe8,0x21, + 0x1c,0xc6,0x61,0x1d,0xda,0x00,0x1e,0xe4,0xe1,0x1d,0xe8,0xa1,0x1c,0xc6,0x81,0x1e, + 0xde,0x41,0x1e,0xda,0x40,0x1c,0xea,0xc1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x0d,0xe6, + 0x21,0x1d,0xf4,0xa1,0x1c,0x00,0x3c,0x00,0x88,0x7a,0x70,0x87,0x79,0x08,0x07,0x73, + 0x28,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e, + 0xe4,0xa1,0x1e,0xca,0x01,0x20,0xea,0x61,0x1e,0xca,0xa1,0x0d,0xe6,0xe1,0x1d,0xcc, + 0x81,0x1e,0xda,0xc0,0x1c,0xd8,0xe1,0x1d,0xc2,0x81,0x1e,0x00,0x73,0x08,0x07,0x76, + 0x98,0x87,0x72,0x00,0x36,0x18,0x02,0x01,0x2c,0x40,0x05,0x00,0x49,0x18,0x00,0x00, + 0x01,0x00,0x00,0x00,0x13,0x84,0x40,0x00,0x89,0x20,0x00,0x00,0x16,0x00,0x00,0x00, + 0x32,0x22,0x08,0x09,0x20,0x64,0x85,0x04,0x13,0x22,0xa4,0x84,0x04,0x13,0x22,0xe3, + 0x84,0xa1,0x90,0x14,0x12,0x4c,0x88,0x8c,0x0b,0x84,0x84,0x4c,0x10,0x3c,0x33,0x00, + 0xc3,0x08,0x02,0x30,0x8c,0x40,0x00,0x76,0x08,0x91,0x83,0xa4,0x29,0xa2,0x84,0xc9, + 0xaf,0xa4,0xff,0x01,0x22,0x80,0x91,0x50,0x10,0x83,0x08,0x84,0x50,0x8a,0x89,0x90, + 0x22,0x1b,0x08,0x98,0x23,0x00,0x83,0x14,0xc8,0x39,0x02,0x50,0x18,0x44,0x08,0x84, + 0x61,0x04,0x22,0x19,0x01,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,0x39,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,0xca,0x12,0x18,0x01,0x28,0x88,0x22,0x28,0x84, + 0x32,0xa0,0x1d,0x01,0x20,0x1d,0x4b,0x68,0x02,0x00,0x00,0x00,0x79,0x18,0x00,0x00, + 0xea,0x00,0x00,0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9, + 0xb9,0xb4,0x37,0xb7,0x21,0x46,0x42,0x20,0x80,0x82,0x50,0xb9,0x1b,0x43,0x0b,0x93, + 0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62,0x24,0x01,0x22,0x24,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,0x80,0x10,0x43,0x8c,0x24,0x48,0x86,0x44,0x60,0xd1,0x54,0x46,0x17,0xc6, + 0x36,0x04,0x41,0x8e,0x24,0x48,0x82,0x44,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,0x40,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,0x64,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,0x90,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,0x64,0xc8,0x84,0xa5,0xc9,0xb9,0x84, + 0xc9,0x9d,0x7d,0xb9,0x85,0xb5,0x95,0x51,0xa8,0xb3,0x1b,0xc2,0x20,0x0f,0x02,0x21, + 0x11,0x22,0x21,0x13,0x42,0x71,0xa9,0x9b,0x2b,0x93,0x43,0x61,0x7b,0x1b,0x73,0x8b, + 0x49,0xa1,0x61,0xc6,0xf6,0x16,0x46,0x47,0xc3,0x62,0xec,0x8d,0xed,0x4d,0x6e,0x08, + 0x83,0x3c,0x88,0x85,0x44,0xc8,0x85,0x4c,0x08,0x46,0x26,0x2c,0x4d,0xce,0x05,0xee, + 0x6d,0x2e,0x8d,0x2e,0xed,0xcd,0x8d,0xcb,0x19,0xdb,0x17,0xd4,0xdb,0x5c,0x1a,0x5d, + 0xda,0x9b,0xdb,0x10,0x05,0xd1,0x90,0x08,0xb9,0x90,0x09,0xd9,0x86,0x18,0x48,0x85, + 0x64,0x08,0x47,0x28,0x2c,0x4d,0xce,0xc5,0xae,0x4c,0x8e,0xae,0x0c,0xef,0x2b,0xcd, + 0x0d,0xae,0x8e,0x8e,0x52,0x58,0x9a,0x9c,0x0b,0xdb,0xdb,0x58,0x18,0x5d,0xda,0x9b, + 0xdb,0x57,0x9a,0x1b,0x59,0x19,0x1e,0xbd,0xb3,0x32,0xb7,0x32,0xb9,0x30,0xba,0x32, + 0x32,0x94,0xaf,0xaf,0xb0,0x34,0xb9,0x2f,0x38,0xb6,0xb0,0xb1,0x32,0xb4,0x37,0x36, + 0xb2,0x32,0xb9,0xaf,0xaf,0x14,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x43, + 0xa8,0x44,0x40,0x3c,0xe4,0x4b,0x84,0x24,0x40,0xc0,0x00,0x89,0x10,0x09,0x99,0x90, + 0x30,0x60,0x42,0x57,0x86,0x37,0xf6,0xf6,0x26,0x47,0x06,0x33,0x84,0x4a,0x02,0xc4, + 0x43,0xbe,0x24,0x48,0x02,0x04,0x0c,0x90,0x08,0x91,0x90,0x09,0x19,0x03,0x1a,0x63, + 0x6f,0x6c,0x6f,0x72,0x30,0x43,0xa8,0x84,0x40,0x3c,0xe4,0x4b,0x88,0x24,0x40,0xc0, + 0x00,0x89,0x90,0x0b,0x99,0x90,0x32,0xa0,0x12,0x96,0x26,0xe7,0x22,0x56,0x67,0x66, + 0x56,0x26,0xc7,0x27,0x2c,0x4d,0xce,0x45,0xac,0xce,0xcc,0xac,0x4c,0xee,0x6b,0x2e, + 0x4d,0xaf,0x8c,0x48,0x58,0x9a,0x9c,0x8b,0x5c,0x59,0x18,0x19,0xa9,0xb0,0x34,0x39, + 0x97,0x39,0x3a,0xb9,0xba,0x31,0xba,0x2f,0xba,0x3c,0xb8,0xb2,0xaf,0x34,0x37,0xb3, + 0x37,0x26,0x64,0x69,0x73,0x70,0x5f,0x73,0x69,0x7a,0x65,0x43,0x94,0x44,0x48,0x86, + 0x44,0x40,0x24,0x64,0x0d,0x18,0x85,0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xd1,0xe5, + 0xc1,0x95,0x7d,0xcd,0xa5,0xe9,0x95,0xf1,0x0a,0x4b,0x93,0x73,0x09,0x93,0x3b,0xfb, + 0xa2,0xcb,0x83,0x2b,0xfb,0x0a,0x63,0x4b,0x3b,0x73,0xfb,0x9a,0x4b,0xd3,0x2b,0x63, + 0x62,0x37,0xf7,0x05,0x17,0x26,0x17,0xd6,0x36,0xc7,0xe1,0x4b,0x46,0x66,0x08,0x19, + 0x24,0x06,0x72,0x06,0x08,0x1a,0x24,0x03,0xf2,0x25,0x42,0x12,0x20,0x69,0x80,0xa8, + 0x01,0xc2,0x06,0x48,0x1b,0x24,0x03,0xe2,0x06,0xc9,0x80,0x44,0xc8,0x1b,0x20,0x13, + 0x02,0x07,0x43,0x10,0x44,0x0c,0x10,0x32,0x40,0xcc,0x00,0x89,0x83,0x21,0xc6,0x01, + 0x20,0x1d,0x22,0x07,0x7c,0xde,0xda,0xdc,0xd2,0xe0,0xde,0xe8,0xca,0xdc,0xe8,0x40, + 0xc6,0xd0,0xc2,0xe4,0xf8,0x4c,0xa5,0xb5,0xc1,0xb1,0x95,0x81,0x0c,0xad,0xac,0x80, + 0x50,0x09,0x05,0x05,0x0d,0x11,0x90,0x3a,0x18,0x62,0x20,0x74,0x80,0xd8,0xc1,0x72, + 0x0c,0x31,0x90,0x3b,0x40,0xee,0x60,0x39,0x46,0x44,0xec,0xc0,0x0e,0xf6,0xd0,0x0e, + 0x6e,0xd0,0x0e,0xef,0x40,0x0e,0xf5,0xc0,0x0e,0xe5,0xe0,0x06,0xe6,0xc0,0x0e,0xe1, + 0x70,0x0e,0xf3,0x30,0x45,0x08,0x86,0x11,0x0a,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b, + 0xa4,0x03,0x39,0x94,0x83,0x3b,0xd0,0xc3,0x94,0xa0,0x18,0xb1,0x84,0x43,0x3a,0xc8, + 0x83,0x1b,0xd8,0x43,0x39,0xc8,0xc3,0x3c,0xa4,0xc3,0x3b,0xb8,0xc3,0x94,0xc0,0x18, + 0x41,0x85,0x43,0x3a,0xc8,0x83,0x1b,0xb0,0x43,0x38,0xb8,0xc3,0x39,0xd4,0x43,0x38, + 0x9c,0x43,0x39,0xfc,0x82,0x3d,0x94,0x83,0x3c,0xcc,0x43,0x3a,0xbc,0x83,0x3b,0x4c, + 0x09,0x90,0x11,0x53,0x38,0xa4,0x83,0x3c,0xb8,0xc1,0x38,0xbc,0x43,0x3b,0xc0,0x43, + 0x3a,0xb0,0x43,0x39,0xfc,0xc2,0x3b,0xc0,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x3c, + 0x4c,0x19,0x14,0xc6,0x19,0xa1,0x84,0x43,0x3a,0xc8,0x83,0x1b,0xd8,0x43,0x39,0xc8, + 0x03,0x3d,0x94,0x03,0x3e,0x4c,0x09,0xe6,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,0x23,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00, + 0x11,0x00,0x00,0x00,0xd4,0x63,0x11,0x40,0x60,0x1c,0x73,0x10,0x42,0xf0,0x3c,0x94, + 0x33,0x00,0x14,0x63,0x09,0x20,0x08,0x82,0xf0,0x2f,0x80,0x20,0x08,0xc2,0xbf,0x30, + 0x96,0x00,0x82,0x20,0x08,0x82,0x01,0x08,0x82,0x20,0x08,0x0e,0x33,0x00,0x24,0x73, + 0x10,0xd7,0x65,0x55,0x34,0x33,0x00,0x04,0x63,0x04,0x20,0x08,0x82,0xf8,0x37,0x46, + 0x00,0x82,0x20,0x08,0x7f,0x33,0x00,0x00,0xe3,0x0d,0x4c,0x64,0x51,0x40,0x2c,0x0a, + 0xe8,0x63,0xc1,0x02,0x1f,0x0b,0x16,0xf9,0x0c,0x32,0x04,0xcb,0x33,0xc8,0x10,0x2c, + 0xd1,0x6c,0xc3,0x52,0x01,0xb3,0x0d,0x41,0x15,0xcc,0x36,0x04,0x83,0x90,0x41,0x40, + 0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x5b,0x86,0x20,0xc0,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _simgui_fs_bytecode_metal_macos[2809] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,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,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0xa1,0xce,0x6b,0xd1,0x1f,0x32,0x9e, + 0x8d,0x8d,0x1c,0xcc,0x19,0xcb,0xd3,0xb6,0x21,0x99,0x0b,0xb6,0x46,0x8b,0x87,0x98, + 0x8e,0x2d,0xb5,0x98,0x92,0x0a,0x81,0x7d,0xf3,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,0x0c,0x0a,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0x80,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,0x30,0x8c,0x20,0x00,0x47,0x49, + 0x53,0x44,0x09,0x93,0xff,0x4f,0xc4,0x35,0x51,0x11,0xf1,0xdb,0xc3,0x3f,0x8d,0x11, + 0x00,0x83,0x08,0x44,0x70,0x91,0x34,0x45,0x94,0x30,0xf9,0xbf,0x04,0x30,0xcf,0x42, + 0x44,0xff,0x34,0x46,0x00,0x0c,0x22,0x18,0x42,0x29,0xc4,0x08,0xe5,0x10,0x9a,0x23, + 0x08,0xe6,0x08,0xc0,0x60,0x18,0x41,0x58,0x0a,0x12,0xca,0x19,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,0x49,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,0xb9,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,0x34,0xcc,0xd8,0xde,0xc2,0xe8,0x64,0x28, + 0xd4,0xd9,0x0d,0x91,0x96,0xe1,0xb1,0x9e,0xeb,0xc1,0x9e,0xec,0x81,0x1e,0xed,0x91, + 0x9e,0x8d,0x4b,0xdd,0x5c,0x99,0x1c,0x0a,0xdb,0xdb,0x98,0x5b,0x4c,0x0a,0x8b,0xb1, + 0x37,0xb6,0x37,0xb9,0x21,0xd2,0x22,0x3c,0xd6,0xd3,0x3d,0xd8,0x93,0x3d,0xd0,0x13, + 0x3d,0xd2,0xe3,0x71,0x09,0x4b,0x93,0x73,0xa1,0x2b,0xc3,0xa3,0xab,0x93,0x2b,0xa3, + 0x14,0x96,0x26,0xe7,0xc2,0xf6,0x36,0x16,0x46,0x97,0xf6,0xe6,0xf6,0x95,0xe6,0x46, + 0x56,0x86,0x47,0x25,0x2c,0x4d,0xce,0x65,0x2e,0xac,0x0d,0x8e,0xad,0x8c,0x18,0x5d, + 0x19,0x1e,0x5d,0x9d,0x5c,0x99,0x0c,0x19,0x8f,0x19,0xdb,0x5b,0x18,0x1d,0x0b,0xc8, + 0x5c,0x58,0x1b,0x1c,0x5b,0x99,0x0f,0x07,0xba,0x32,0xbc,0x21,0xd4,0x42,0x3c,0x60, + 0xf0,0x84,0xc1,0x32,0x2c,0xc2,0x23,0x06,0x0f,0xf4,0x8c,0xc1,0x23,0x3d,0x64,0xc0, + 0x25,0x2c,0x4d,0xce,0x65,0x2e,0xac,0x0d,0x8e,0xad,0x4c,0x8e,0xc7,0x5c,0x58,0x1b, + 0x1c,0x5b,0x99,0x1c,0x87,0xb9,0x36,0xb8,0x21,0xd2,0x72,0x3c,0x66,0xf0,0x84,0xc1, + 0x32,0x2c,0xc2,0x03,0x3d,0x67,0xf0,0x48,0x0f,0x1a,0x0c,0x41,0x1e,0xee,0xf9,0x9e, + 0x32,0x78,0xd2,0x60,0x88,0x91,0x00,0x4f,0xf5,0xa8,0xc1,0x88,0x88,0x1d,0xd8,0xc1, + 0x1e,0xda,0xc1,0x0d,0xda,0xe1,0x1d,0xc8,0xa1,0x1e,0xd8,0xa1,0x1c,0xdc,0xc0,0x1c, + 0xd8,0x21,0x1c,0xce,0x61,0x1e,0xa6,0x08,0xc1,0x30,0x42,0x61,0x07,0x76,0xb0,0x87, + 0x76,0x70,0x83,0x74,0x20,0x87,0x72,0x70,0x07,0x7a,0x98,0x12,0x14,0x23,0x96,0x70, + 0x48,0x07,0x79,0x70,0x03,0x7b,0x28,0x07,0x79,0x98,0x87,0x74,0x78,0x07,0x77,0x98, + 0x12,0x18,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,0x01,0x32,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,0x83,0xc2,0x38,0x23,0x98,0x70,0x48,0x07,0x79,0x70,0x03,0x73, + 0x90,0x87,0x70,0x38,0x87,0x76,0x28,0x07,0x77,0xa0,0x87,0x29,0xc1,0x1a,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,0x0c,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x04,0x00,0x00, + 0x00,0xc4,0x46,0x00,0x48,0x8d,0x00,0xd4,0x00,0x89,0x19,0x00,0x02,0x23,0x00,0x00, + 0x00,0x23,0x06,0x8a,0x10,0x44,0x87,0x91,0x0c,0x05,0x11,0x58,0x90,0xc8,0x67,0xb6, + 0x81,0x08,0x80,0x0c,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _simgui_vs_bytecode_metal_ios[3052] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xec,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, + 0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0xe0,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,0x69,0x97,0x6b,0xac,0xd8,0xa2,0x51, + 0x33,0x7c,0x5f,0x96,0xb2,0xb1,0x06,0x06,0x7c,0xbb,0x5f,0x88,0xa0,0xeb,0x9f,0xea, + 0x6e,0x6b,0x70,0xa9,0x6e,0xef,0xe6,0xa4,0xea,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,0x37,0x00,0x00,0x00,0x56,0x41,0x54, + 0x54,0x22,0x00,0x03,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x80, + 0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x01,0x80,0x63,0x6f,0x6c,0x6f, + 0x72,0x30,0x00,0x02,0x80,0x56,0x41,0x54,0x59,0x05,0x00,0x03,0x00,0x04,0x04,0x06, + 0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17,0x0b, + 0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0xc0,0x0a,0x00,0x00,0xff,0xff,0xff,0xff, + 0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0xad,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,0x10,0x45,0x02,0x42,0x92,0x0b,0x42,0x84,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,0x08,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00,0x00, + 0x51,0x18,0x00,0x00,0x82,0x00,0x00,0x00,0x1b,0xc8,0x25,0xf8,0xff,0xff,0xff,0xff, + 0x01,0x90,0x80,0x8a,0x18,0x87,0x77,0x90,0x07,0x79,0x28,0x87,0x71,0xa0,0x07,0x76, + 0xc8,0x87,0x36,0x90,0x87,0x77,0xa8,0x07,0x77,0x20,0x87,0x72,0x20,0x87,0x36,0x20, + 0x87,0x74,0xb0,0x87,0x74,0x20,0x87,0x72,0x68,0x83,0x79,0x88,0x07,0x79,0xa0,0x87, + 0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0xc0,0x1c,0xc2,0x81, + 0x1d,0xe6,0xa1,0x1c,0x00,0x82,0x1c,0xd2,0x61,0x1e,0xc2,0x41,0x1c,0xd8,0xa1,0x1c, + 0xda,0x80,0x1e,0xc2,0x21,0x1d,0xd8,0xa1,0x0d,0xc6,0x21,0x1c,0xd8,0x81,0x1d,0xe6, + 0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07,0x80,0x60,0x87,0x72,0x98,0x87,0x79, + 0x68,0x03,0x78,0x90,0x87,0x72,0x18,0x87,0x74,0x98,0x87,0x72,0x68,0x03,0x73,0x80, + 0x87,0x76,0x08,0x07,0x72,0x00,0xcc,0x21,0x1c,0xd8,0x61,0x1e,0xca,0x01,0x20,0xdc, + 0xe1,0x1d,0xda,0xc0,0x1c,0xe4,0x21,0x1c,0xda,0xa1,0x1c,0xda,0x00,0x1e,0xde,0x21, + 0x1d,0xdc,0x81,0x1e,0xca,0x41,0x1e,0xda,0xa0,0x1c,0xd8,0x21,0x1d,0xda,0x01,0xa0, + 0x07,0x79,0xa8,0x87,0x72,0x00,0x06,0x77,0x78,0x87,0x36,0x30,0x07,0x79,0x08,0x87, + 0x76,0x28,0x87,0x36,0x80,0x87,0x77,0x48,0x07,0x77,0xa0,0x87,0x72,0x90,0x87,0x36, + 0x28,0x07,0x76,0x48,0x87,0x76,0x68,0x03,0x77,0x78,0x07,0x77,0x68,0x03,0x76,0x28, + 0x87,0x70,0x30,0x07,0x80,0x70,0x87,0x77,0x68,0x83,0x74,0x70,0x07,0x73,0x98,0x87, + 0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1, + 0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0x40,0x1d,0xea,0xa1,0x1d,0xe0,0xa1,0x0d, + 0xe8,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87, + 0x72,0x00,0x08,0x77,0x78,0x87,0x36,0x70,0x87,0x70,0x70,0x87,0x79,0x68,0x03,0x73, + 0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c, + 0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xe6,0x21,0x1d,0xce,0xc1,0x1d,0xca,0x81,0x1c,0xda, + 0x40,0x1f,0xca,0x41,0x1e,0xde,0x61,0x1e,0xda,0xc0,0x1c,0xe0,0xa1,0x0d,0xda,0x21, + 0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80,0x70,0x87,0x77,0x68, + 0x03,0x7a,0x90,0x87,0x70,0x80,0x07,0x78,0x48,0x07,0x77,0x38,0x87,0x36,0x68,0x87, + 0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0x62,0x1e,0xe8,0x21, + 0x1c,0xc6,0x61,0x1d,0xda,0x00,0x1e,0xe4,0xe1,0x1d,0xe8,0xa1,0x1c,0xc6,0x81,0x1e, + 0xde,0x41,0x1e,0xda,0x40,0x1c,0xea,0xc1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x0d,0xe6, + 0x21,0x1d,0xf4,0xa1,0x1c,0x00,0x3c,0x00,0x88,0x7a,0x70,0x87,0x79,0x08,0x07,0x73, + 0x28,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e, + 0xe4,0xa1,0x1e,0xca,0x01,0x20,0xea,0x61,0x1e,0xca,0xa1,0x0d,0xe6,0xe1,0x1d,0xcc, + 0x81,0x1e,0xda,0xc0,0x1c,0xd8,0xe1,0x1d,0xc2,0x81,0x1e,0x00,0x73,0x08,0x07,0x76, + 0x98,0x87,0x72,0x00,0x36,0x20,0x02,0x01,0x24,0xc0,0x02,0x54,0x00,0x00,0x00,0x00, + 0x49,0x18,0x00,0x00,0x01,0x00,0x00,0x00,0x13,0x84,0x40,0x00,0x89,0x20,0x00,0x00, + 0x16,0x00,0x00,0x00,0x32,0x22,0x08,0x09,0x20,0x64,0x85,0x04,0x13,0x22,0xa4,0x84, + 0x04,0x13,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x88,0x8c,0x0b,0x84,0x84,0x4c, + 0x10,0x3c,0x33,0x00,0xc3,0x08,0x02,0x30,0x8c,0x40,0x00,0x76,0x08,0x91,0x83,0xa4, + 0x29,0xa2,0x84,0xc9,0xaf,0xa4,0xff,0x01,0x22,0x80,0x91,0x50,0x10,0x83,0x08,0x84, + 0x50,0x8a,0x89,0x90,0x22,0x1b,0x08,0x98,0x23,0x00,0x83,0x14,0xc8,0x39,0x02,0x50, + 0x18,0x44,0x08,0x84,0x61,0x04,0x22,0x19,0x01,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,0x03,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,0xca,0x12,0x18,0x01,0x28,0x88,0x22,0x28,0x84,0x32,0xa0,0x1d,0x01, + 0x20,0x1d,0x4b,0x80,0x04,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0xe9,0x00,0x00,0x00, + 0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7, + 0x21,0x46,0x42,0x20,0x80,0x82,0x50,0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3, + 0x2b,0x1b,0x62,0x24,0x01,0x22,0x24,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,0x80,0x10, + 0x43,0x8c,0x24,0x48,0x86,0x44,0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x41,0x8e, + 0x24,0x48,0x82,0x44,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,0x40,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,0x64,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,0x90,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,0x64,0xc8,0x84,0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xb9,0x85, + 0xb5,0x95,0x51,0xa8,0xb3,0x1b,0xc2,0x20,0x0f,0x02,0x21,0x11,0x22,0x21,0x13,0x42, + 0x71,0xa9,0x9b,0x2b,0x93,0x43,0x61,0x7b,0x1b,0x73,0x8b,0x49,0xa1,0x61,0xc6,0xf6, + 0x16,0x46,0x47,0xc3,0x62,0xec,0x8d,0xed,0x4d,0x6e,0x08,0x83,0x3c,0x88,0x85,0x44, + 0xc8,0x85,0x4c,0x08,0x46,0x26,0x2c,0x4d,0xce,0x05,0xee,0x6d,0x2e,0x8d,0x2e,0xed, + 0xcd,0x8d,0xcb,0x19,0xdb,0x17,0xd4,0xdb,0x5c,0x1a,0x5d,0xda,0x9b,0xdb,0x10,0x05, + 0xd1,0x90,0x08,0xb9,0x90,0x09,0xd9,0x86,0x18,0x48,0x85,0x64,0x08,0x47,0x28,0x2c, + 0x4d,0xce,0xc5,0xae,0x4c,0x8e,0xae,0x0c,0xef,0x2b,0xcd,0x0d,0xae,0x8e,0x8e,0x52, + 0x58,0x9a,0x9c,0x0b,0xdb,0xdb,0x58,0x18,0x5d,0xda,0x9b,0xdb,0x57,0x9a,0x1b,0x59, + 0x19,0x1e,0xbd,0xb3,0x32,0xb7,0x32,0xb9,0x30,0xba,0x32,0x32,0x94,0xaf,0xaf,0xb0, + 0x34,0xb9,0x2f,0x38,0xb6,0xb0,0xb1,0x32,0xb4,0x37,0x36,0xb2,0x32,0xb9,0xaf,0xaf, + 0x14,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x43,0xa8,0x44,0x40,0x3c,0xe4, + 0x4b,0x84,0x24,0x40,0xc0,0x00,0x89,0x10,0x09,0x99,0x90,0x30,0x60,0x42,0x57,0x86, + 0x37,0xf6,0xf6,0x26,0x47,0x06,0x33,0x84,0x4a,0x02,0xc4,0x43,0xbe,0x24,0x48,0x02, + 0x04,0x0c,0x90,0x08,0x91,0x90,0x09,0x19,0x03,0x1a,0x63,0x6f,0x6c,0x6f,0x72,0x30, + 0x43,0xa8,0x84,0x40,0x3c,0xe4,0x4b,0x88,0x24,0x40,0xc0,0x00,0x89,0x90,0x0b,0x99, + 0x90,0x32,0xa0,0x12,0x96,0x26,0xe7,0x22,0x56,0x67,0x66,0x56,0x26,0xc7,0x27,0x2c, + 0x4d,0xce,0x45,0xac,0xce,0xcc,0xac,0x4c,0xee,0x6b,0x2e,0x4d,0xaf,0x8c,0x48,0x58, + 0x9a,0x9c,0x8b,0x5c,0x59,0x18,0x19,0xa9,0xb0,0x34,0x39,0x97,0x39,0x3a,0xb9,0xba, + 0x31,0xba,0x2f,0xba,0x3c,0xb8,0xb2,0xaf,0x34,0x37,0xb3,0x37,0x26,0x64,0x69,0x73, + 0x70,0x5f,0x73,0x69,0x7a,0x65,0x43,0x94,0x44,0x48,0x86,0x44,0x40,0x24,0x64,0x0d, + 0x18,0x85,0xa5,0xc9,0xb9,0x84,0xc9,0x9d,0x7d,0xd1,0xe5,0xc1,0x95,0x7d,0xcd,0xa5, + 0xe9,0x95,0xf1,0x0a,0x4b,0x93,0x73,0x09,0x93,0x3b,0xfb,0xa2,0xcb,0x83,0x2b,0xfb, + 0x0a,0x63,0x4b,0x3b,0x73,0xfb,0x9a,0x4b,0xd3,0x2b,0x63,0x62,0x37,0xf7,0x05,0x17, + 0x26,0x17,0xd6,0x36,0xc7,0xe1,0x4b,0x46,0x66,0x08,0x19,0x24,0x06,0x72,0x06,0x08, + 0x1a,0x24,0x03,0xf2,0x25,0x42,0x12,0x20,0x69,0x80,0xa8,0x01,0xc2,0x06,0x48,0x1b, + 0x24,0x03,0xe2,0x06,0xc9,0x80,0x44,0xc8,0x1b,0x20,0x13,0x02,0x07,0x43,0x10,0x44, + 0x0c,0x10,0x32,0x40,0xcc,0x00,0x89,0x83,0x21,0xc6,0x01,0x20,0x1d,0x22,0x07,0x7c, + 0xde,0xda,0xdc,0xd2,0xe0,0xde,0xe8,0xca,0xdc,0xe8,0x40,0xc6,0xd0,0xc2,0xe4,0xf8, + 0x4c,0xa5,0xb5,0xc1,0xb1,0x95,0x81,0x0c,0xad,0xac,0x80,0x50,0x09,0x05,0x05,0x0d, + 0x11,0x90,0x3a,0x18,0x62,0x20,0x74,0x80,0xd8,0xc1,0x72,0x0c,0x31,0x90,0x3b,0x40, + 0xee,0x60,0x39,0x46,0x44,0xec,0xc0,0x0e,0xf6,0xd0,0x0e,0x6e,0xd0,0x0e,0xef,0x40, + 0x0e,0xf5,0xc0,0x0e,0xe5,0xe0,0x06,0xe6,0xc0,0x0e,0xe1,0x70,0x0e,0xf3,0x30,0x45, + 0x08,0x86,0x11,0x0a,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b,0xa4,0x03,0x39,0x94,0x83, + 0x3b,0xd0,0xc3,0x94,0xa0,0x18,0xb1,0x84,0x43,0x3a,0xc8,0x83,0x1b,0xd8,0x43,0x39, + 0xc8,0xc3,0x3c,0xa4,0xc3,0x3b,0xb8,0xc3,0x94,0xc0,0x18,0x41,0x85,0x43,0x3a,0xc8, + 0x83,0x1b,0xb0,0x43,0x38,0xb8,0xc3,0x39,0xd4,0x43,0x38,0x9c,0x43,0x39,0xfc,0x82, + 0x3d,0x94,0x83,0x3c,0xcc,0x43,0x3a,0xbc,0x83,0x3b,0x4c,0x09,0x90,0x11,0x53,0x38, + 0xa4,0x83,0x3c,0xb8,0xc1,0x38,0xbc,0x43,0x3b,0xc0,0x43,0x3a,0xb0,0x43,0x39,0xfc, + 0xc2,0x3b,0xc0,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x3c,0x4c,0x19,0x14,0xc6,0x19, + 0xa1,0x84,0x43,0x3a,0xc8,0x83,0x1b,0xd8,0x43,0x39,0xc8,0x03,0x3d,0x94,0x03,0x3e, + 0x4c,0x09,0xe6,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,0x23,0x00,0x00,0x00, + 0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0xd4,0x63,0x11,0x40, + 0x60,0x1c,0x73,0x10,0x42,0xf0,0x3c,0x94,0x33,0x00,0x14,0x63,0x09,0x20,0x08,0x82, + 0xf0,0x2f,0x80,0x20,0x08,0xc2,0xbf,0x30,0x96,0x00,0x82,0x20,0x08,0x82,0x01,0x08, + 0x82,0x20,0x08,0x0e,0x33,0x00,0x24,0x73,0x10,0xd7,0x65,0x55,0x34,0x33,0x00,0x04, + 0x63,0x04,0x20,0x08,0x82,0xf8,0x37,0x46,0x00,0x82,0x20,0x08,0x7f,0x33,0x00,0x00, + 0xe3,0x0d,0x4c,0x64,0x51,0x40,0x2c,0x0a,0xe8,0x63,0xc1,0x02,0x1f,0x0b,0x16,0xf9, + 0x0c,0x32,0x04,0xcb,0x33,0xc8,0x10,0x2c,0xd1,0x6c,0xc3,0x52,0x01,0xb3,0x0d,0x41, + 0x15,0xcc,0x36,0x04,0x83,0x90,0x41,0x40,0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x5b,0x86,0x20,0xc0,0x03,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 _simgui_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,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0xf0,0xa4,0xb3,0x95,0x4b,0xab,0x64, + 0x94,0xe7,0xa9,0x8a,0x69,0x27,0x6d,0x28,0x77,0x84,0x8d,0x3f,0xaf,0x7d,0x3c,0x39, + 0x31,0xc4,0xcb,0x53,0x6d,0xc0,0x0d,0xdf,0x08,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,0x04,0x0a,0x00,0x00,0xff,0xff,0xff,0xff,0x42,0x43,0xc0, + 0xde,0x21,0x0c,0x00,0x00,0x7e,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,0x30,0x8c,0x20,0x00,0x47,0x49, + 0x53,0x44,0x09,0x93,0xff,0x4f,0xc4,0x35,0x51,0x11,0xf1,0xdb,0xc3,0x3f,0x8d,0x11, + 0x00,0x83,0x08,0x44,0x70,0x91,0x34,0x45,0x94,0x30,0xf9,0xbf,0x04,0x30,0xcf,0x42, + 0x44,0xff,0x34,0x46,0x00,0x0c,0x22,0x18,0x42,0x29,0xc4,0x08,0xe5,0x10,0x9a,0x23, + 0x08,0xe6,0x08,0xc0,0x60,0x18,0x41,0x58,0x0a,0x12,0xca,0x19,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,0x98, + 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,0xb9,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,0x34, + 0xcc,0xd8,0xde,0xc2,0xe8,0x64,0x28,0xd4,0xd9,0x0d,0x91,0x96,0xe1,0xb1,0x9e,0xeb, + 0xc1,0x9e,0xec,0x81,0x1e,0xed,0x91,0x9e,0x8d,0x4b,0xdd,0x5c,0x99,0x1c,0x0a,0xdb, + 0xdb,0x98,0x5b,0x4c,0x0a,0x8b,0xb1,0x37,0xb6,0x37,0xb9,0x21,0xd2,0x22,0x3c,0xd6, + 0xd3,0x3d,0xd8,0x93,0x3d,0xd0,0x13,0x3d,0xd2,0xe3,0x71,0x09,0x4b,0x93,0x73,0xa1, + 0x2b,0xc3,0xa3,0xab,0x93,0x2b,0xa3,0x14,0x96,0x26,0xe7,0xc2,0xf6,0x36,0x16,0x46, + 0x97,0xf6,0xe6,0xf6,0x95,0xe6,0x46,0x56,0x86,0x47,0x25,0x2c,0x4d,0xce,0x65,0x2e, + 0xac,0x0d,0x8e,0xad,0x8c,0x18,0x5d,0x19,0x1e,0x5d,0x9d,0x5c,0x99,0x0c,0x19,0x8f, + 0x19,0xdb,0x5b,0x18,0x1d,0x0b,0xc8,0x5c,0x58,0x1b,0x1c,0x5b,0x99,0x0f,0x07,0xba, + 0x32,0xbc,0x21,0xd4,0x42,0x3c,0x60,0xf0,0x84,0xc1,0x32,0x2c,0xc2,0x23,0x06,0x0f, + 0xf4,0x8c,0xc1,0x23,0x3d,0x64,0xc0,0x25,0x2c,0x4d,0xce,0x65,0x2e,0xac,0x0d,0x8e, + 0xad,0x4c,0x8e,0xc7,0x5c,0x58,0x1b,0x1c,0x5b,0x99,0x1c,0x87,0xb9,0x36,0xb8,0x21, + 0xd2,0x72,0x3c,0x66,0xf0,0x84,0xc1,0x32,0x2c,0xc2,0x03,0x3d,0x67,0xf0,0x48,0x0f, + 0x1a,0x0c,0x41,0x1e,0xee,0xf9,0x9e,0x32,0x78,0xd2,0x60,0x88,0x91,0x00,0x4f,0xf5, + 0xa8,0xc1,0x88,0x88,0x1d,0xd8,0xc1,0x1e,0xda,0xc1,0x0d,0xda,0xe1,0x1d,0xc8,0xa1, + 0x1e,0xd8,0xa1,0x1c,0xdc,0xc0,0x1c,0xd8,0x21,0x1c,0xce,0x61,0x1e,0xa6,0x08,0xc1, + 0x30,0x42,0x61,0x07,0x76,0xb0,0x87,0x76,0x70,0x83,0x74,0x20,0x87,0x72,0x70,0x07, + 0x7a,0x98,0x12,0x14,0x23,0x96,0x70,0x48,0x07,0x79,0x70,0x03,0x7b,0x28,0x07,0x79, + 0x98,0x87,0x74,0x78,0x07,0x77,0x98,0x12,0x18,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,0x01,0x32,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,0x83,0xc2,0x38,0x23,0x98, + 0x70,0x48,0x07,0x79,0x70,0x03,0x73,0x90,0x87,0x70,0x38,0x87,0x76,0x28,0x07,0x77, + 0xa0,0x87,0x29,0xc1,0x1a,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,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,0x0c,0x00,0x00,0x00,0x13,0x04,0x41, + 0x2c,0x10,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0xc4,0x46,0x00,0x48,0x8d,0x00,0xd4, + 0x00,0x89,0x19,0x00,0x02,0x23,0x00,0x00,0x00,0x23,0x06,0x8a,0x10,0x44,0x87,0x91, + 0x0c,0x05,0x11,0x58,0x90,0xc8,0x67,0xb6,0x81,0x08,0x80,0x0c,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _simgui_vs_source_metal_sim[672] = { + 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, + 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, + 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x76, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x32,0x20,0x64,0x69,0x73,0x70,0x5f,0x73,0x69,0x7a,0x65,0x3b, + 0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e, + 0x30,0x5f,0x6f,0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61, + 0x74,0x32,0x20,0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63, + 0x6e,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74, + 0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c, + 0x6f,0x63,0x6e,0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, + 0x61,0x74,0x34,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20, + 0x5b,0x5b,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b, + 0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69, + 0x6e,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x70, + 0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62, + 0x75,0x74,0x65,0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c, + 0x6f,0x61,0x74,0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x20,0x5b, + 0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x31,0x29,0x5d,0x5d,0x3b, + 0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63,0x6f,0x6c,0x6f, + 0x72,0x30,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x32, + 0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,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, + 0x32,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,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x28,0x28,0x69,0x6e,0x2e,0x70,0x6f,0x73, + 0x69,0x74,0x69,0x6f,0x6e,0x20,0x2f,0x20,0x5f,0x32,0x32,0x2e,0x64,0x69,0x73,0x70, + 0x5f,0x73,0x69,0x7a,0x65,0x29,0x20,0x2d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28, + 0x30,0x2e,0x35,0x29,0x29,0x20,0x2a,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x32, + 0x2e,0x30,0x2c,0x20,0x2d,0x32,0x2e,0x30,0x29,0x2c,0x20,0x30,0x2e,0x35,0x2c,0x20, + 0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x75,0x76, + 0x20,0x3d,0x20,0x69,0x6e,0x2e,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,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 uint8_t _simgui_fs_source_metal_sim[436] = { + 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, + 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, + 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d, + 0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x5b,0x5b,0x63,0x6f,0x6c,0x6f,0x72,0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x7d, + 0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f, + 0x69,0x6e,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20, + 0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29, + 0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e, + 0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,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,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 _simgui_vs_bytecode_hlsl4[892] = { + 0x44,0x58,0x42,0x43,0x0d,0xbd,0x9e,0x9e,0x7d,0xc0,0x2b,0x54,0x88,0xf9,0xca,0x89, + 0x32,0xe4,0x0c,0x59,0x01,0x00,0x00,0x00,0x7c,0x03,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0xfc,0x00,0x00,0x00,0x60,0x01,0x00,0x00,0xd0,0x01,0x00,0x00, + 0x00,0x03,0x00,0x00,0x52,0x44,0x45,0x46,0xc0,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x48,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x04,0xfe,0xff, + 0x10,0x81,0x00,0x00,0x98,0x00,0x00,0x00,0x3c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x73,0x00,0xab,0xab,0x3c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x60,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x88,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x5f,0x32,0x32,0x5f,0x64,0x69,0x73,0x70,0x5f,0x73,0x69,0x7a, + 0x65,0x00,0xab,0xab,0x01,0x00,0x03,0x00,0x01,0x00,0x02,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,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, + 0x5c,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x50,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x03,0x00,0x00,0x50,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x03,0x00,0x00,0x50,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x0f,0x0f,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0xab,0xab,0xab, + 0x4f,0x53,0x47,0x4e,0x68,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x0c,0x00,0x00,0x50,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, + 0x59,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, + 0x00,0x53,0x56,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0xab,0xab,0xab, + 0x53,0x48,0x44,0x52,0x28,0x01,0x00,0x00,0x40,0x00,0x01,0x00,0x4a,0x00,0x00,0x00, + 0x59,0x00,0x00,0x04,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x5f,0x00,0x00,0x03,0x32,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x5f,0x00,0x00,0x03, + 0x32,0x10,0x10,0x00,0x01,0x00,0x00,0x00,0x5f,0x00,0x00,0x03,0xf2,0x10,0x10,0x00, + 0x02,0x00,0x00,0x00,0x65,0x00,0x00,0x03,0x32,0x20,0x10,0x00,0x00,0x00,0x00,0x00, + 0x65,0x00,0x00,0x03,0xf2,0x20,0x10,0x00,0x01,0x00,0x00,0x00,0x67,0x00,0x00,0x04, + 0xf2,0x20,0x10,0x00,0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x68,0x00,0x00,0x02, + 0x01,0x00,0x00,0x00,0x36,0x00,0x00,0x05,0x32,0x20,0x10,0x00,0x00,0x00,0x00,0x00, + 0x46,0x10,0x10,0x00,0x01,0x00,0x00,0x00,0x36,0x00,0x00,0x05,0xf2,0x20,0x10,0x00, + 0x01,0x00,0x00,0x00,0x46,0x1e,0x10,0x00,0x02,0x00,0x00,0x00,0x0e,0x00,0x00,0x08, + 0x32,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x10,0x10,0x00,0x00,0x00,0x00,0x00, + 0x46,0x80,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a, + 0x32,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x00,0x10,0x00,0x00,0x00,0x00,0x00, + 0x02,0x40,0x00,0x00,0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x0a,0x32,0x20,0x10,0x00,0x02,0x00,0x00,0x00, + 0x46,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x02,0x40,0x00,0x00,0x00,0x00,0x00,0x40, + 0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x08, + 0xc2,0x20,0x10,0x00,0x02,0x00,0x00,0x00,0x02,0x40,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0x00,0x00,0x80,0x3f,0x3e,0x00,0x00,0x01, + 0x53,0x54,0x41,0x54,0x74,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x03,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,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _simgui_fs_bytecode_hlsl4[608] = { + 0x44,0x58,0x42,0x43,0x3a,0xa7,0x41,0x21,0xb4,0x2d,0xa7,0x6e,0xfe,0x31,0xb0,0xe0, + 0x14,0xe0,0xdf,0x5a,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,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,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,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,0x03,0x03,0x00,0x00, + 0x38,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, + 0x00,0xab,0xab,0xab,0x4f,0x53,0x47,0x4e,0x2c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x53,0x56,0x5f,0x54, + 0x61,0x72,0x67,0x65,0x74,0x00,0xab,0xab,0x53,0x48,0x44,0x52,0x94,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x5a,0x00,0x00,0x03,0x00,0x60,0x10,0x00, + 0x00,0x00,0x00,0x00,0x58,0x18,0x00,0x04,0x00,0x70,0x10,0x00,0x00,0x00,0x00,0x00, + 0x55,0x55,0x00,0x00,0x62,0x10,0x00,0x03,0x32,0x10,0x10,0x00,0x00,0x00,0x00,0x00, + 0x62,0x10,0x00,0x03,0xf2,0x10,0x10,0x00,0x01,0x00,0x00,0x00,0x65,0x00,0x00,0x03, + 0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x68,0x00,0x00,0x02,0x01,0x00,0x00,0x00, + 0x45,0x00,0x00,0x09,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x10,0x10,0x00, + 0x00,0x00,0x00,0x00,0x46,0x7e,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x10,0x00, + 0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x07,0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00, + 0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x1e,0x10,0x00,0x01,0x00,0x00,0x00, + 0x3e,0x00,0x00,0x01,0x53,0x54,0x41,0x54,0x74,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + +}; +#elif defined(SOKOL_WGPU) +static const uint8_t _simgui_vs_source_wgsl[1083] = { + 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, + 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20, + 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x20,0x7b,0x0a,0x20,0x20,0x2f,0x2a, + 0x20,0x40,0x6f,0x66,0x66,0x73,0x65,0x74,0x28,0x30,0x29,0x20,0x2a,0x2f,0x0a,0x20, + 0x20,0x64,0x69,0x73,0x70,0x5f,0x73,0x69,0x7a,0x65,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x32,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61, + 0x74,0x65,0x3e,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31,0x20,0x3a, + 0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x0a,0x40,0x67,0x72,0x6f,0x75,0x70,0x28, + 0x30,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x30,0x29,0x20,0x76, + 0x61,0x72,0x3c,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x3e,0x20,0x78,0x5f,0x32,0x32, + 0x20,0x3a,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x3b,0x0a,0x0a,0x76, + 0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x75,0x76,0x20,0x3a, + 0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, + 0x76,0x61,0x74,0x65,0x3e,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x20, + 0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72, + 0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61, + 0x74,0x65,0x3e,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x34,0x66,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65, + 0x3e,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3a,0x20, + 0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f, + 0x31,0x28,0x29,0x20,0x7b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x31,0x39, + 0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x70,0x6f,0x73,0x69,0x74, + 0x69,0x6f,0x6e,0x5f,0x31,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32, + 0x35,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x78,0x5f,0x32,0x32, + 0x2e,0x64,0x69,0x73,0x70,0x5f,0x73,0x69,0x7a,0x65,0x3b,0x0a,0x20,0x20,0x6c,0x65, + 0x74,0x20,0x78,0x5f,0x33,0x33,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d, + 0x20,0x28,0x28,0x28,0x78,0x5f,0x31,0x39,0x20,0x2f,0x20,0x78,0x5f,0x32,0x35,0x29, + 0x20,0x2d,0x20,0x76,0x65,0x63,0x32,0x66,0x28,0x30,0x2e,0x35,0x66,0x2c,0x20,0x30, + 0x2e,0x35,0x66,0x29,0x29,0x20,0x2a,0x20,0x76,0x65,0x63,0x32,0x66,0x28,0x32,0x2e, + 0x30,0x66,0x2c,0x20,0x2d,0x32,0x2e,0x30,0x66,0x29,0x29,0x3b,0x0a,0x20,0x20,0x67, + 0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x76,0x65,0x63, + 0x34,0x66,0x28,0x78,0x5f,0x33,0x33,0x2e,0x78,0x2c,0x20,0x78,0x5f,0x33,0x33,0x2e, + 0x79,0x2c,0x20,0x30,0x2e,0x35,0x66,0x2c,0x20,0x31,0x2e,0x30,0x66,0x29,0x3b,0x0a, + 0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x34,0x33,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x32,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a, + 0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x78,0x5f,0x34,0x33,0x3b,0x0a,0x20,0x20,0x6c, + 0x65,0x74,0x20,0x78,0x5f,0x34,0x37,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20, + 0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f, + 0x72,0x20,0x3d,0x20,0x78,0x5f,0x34,0x37,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75, + 0x72,0x6e,0x3b,0x0a,0x7d,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61, + 0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x40,0x62,0x75,0x69,0x6c, + 0x74,0x69,0x6e,0x28,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x29,0x0a,0x20,0x20, + 0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3a,0x20,0x76,0x65, + 0x63,0x34,0x66,0x2c,0x0a,0x20,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e, + 0x28,0x30,0x29,0x0a,0x20,0x20,0x75,0x76,0x5f,0x31,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x32,0x66,0x2c,0x0a,0x20,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28, + 0x31,0x29,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x5f,0x31,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x34,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x40,0x76,0x65,0x72,0x74,0x65,0x78, + 0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x28,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69, + 0x6f,0x6e,0x28,0x30,0x29,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x2c,0x20, + 0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x31,0x29,0x20,0x74,0x65,0x78, + 0x63,0x6f,0x6f,0x72,0x64,0x30,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x32,0x66,0x2c,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28, + 0x32,0x29,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20, + 0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x29,0x20,0x2d,0x3e,0x20,0x6d,0x61,0x69,0x6e, + 0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x5f,0x31,0x20,0x3d,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5f,0x31, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f, + 0x72,0x64,0x30,0x20,0x3d,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x5f, + 0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x20, + 0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a, + 0x20,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x3b,0x0a,0x20,0x20,0x72,0x65, + 0x74,0x75,0x72,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x28,0x67,0x6c, + 0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x2c,0x20,0x75,0x76,0x2c,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x29,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +static const uint8_t _simgui_fs_source_wgsl[630] = { + 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, + 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, + 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, + 0x76,0x61,0x74,0x65,0x3e,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a,0x40,0x67,0x72,0x6f,0x75, + 0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x34,0x38, + 0x29,0x20,0x76,0x61,0x72,0x20,0x74,0x65,0x78,0x20,0x3a,0x20,0x74,0x65,0x78,0x74, + 0x75,0x72,0x65,0x5f,0x32,0x64,0x3c,0x66,0x33,0x32,0x3e,0x3b,0x0a,0x0a,0x40,0x67, + 0x72,0x6f,0x75,0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67, + 0x28,0x36,0x34,0x29,0x20,0x76,0x61,0x72,0x20,0x73,0x6d,0x70,0x20,0x3a,0x20,0x73, + 0x61,0x6d,0x70,0x6c,0x65,0x72,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, + 0x76,0x61,0x74,0x65,0x3e,0x20,0x75,0x76,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66, + 0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a, + 0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x20,0x7b,0x0a,0x20,0x20, + 0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x33,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66, + 0x20,0x3d,0x20,0x75,0x76,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32, + 0x34,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x74, + 0x75,0x72,0x65,0x53,0x61,0x6d,0x70,0x6c,0x65,0x28,0x74,0x65,0x78,0x2c,0x20,0x73, + 0x6d,0x70,0x2c,0x20,0x78,0x5f,0x32,0x33,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74, + 0x20,0x78,0x5f,0x32,0x37,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f, + 0x6c,0x6f,0x72,0x20,0x3d,0x20,0x28,0x78,0x5f,0x32,0x34,0x20,0x2a,0x20,0x78,0x5f, + 0x32,0x37,0x29,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x0a,0x7d, + 0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75, + 0x74,0x20,0x7b,0x0a,0x20,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28, + 0x30,0x29,0x0a,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x5f, + 0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x40,0x66, + 0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x28, + 0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x30,0x29,0x20,0x75,0x76,0x5f, + 0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x2c,0x20,0x40, + 0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x31,0x29,0x20,0x63,0x6f,0x6c,0x6f, + 0x72,0x5f,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x29, + 0x20,0x2d,0x3e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20, + 0x20,0x75,0x76,0x20,0x3d,0x20,0x75,0x76,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a, + 0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x5f, + 0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28, + 0x29,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6d,0x61,0x69,0x6e, + 0x5f,0x6f,0x75,0x74,0x28,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x29, + 0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_DUMMY_BACKEND) +static const char* _simgui_vs_source_dummy = ""; +static const char* _simgui_fs_source_dummy = ""; +#else +#error "Please define one of SOKOL_GLCORE, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND!" +#endif + +#if !defined(SOKOL_IMGUI_NO_SOKOL_APP) +static void _simgui_set_clipboard(void* user_data, const char* text) { + (void)user_data; + sapp_set_clipboard_string(text); +} + +static const char* _simgui_get_clipboard(void* user_data) { + (void)user_data; + return sapp_get_clipboard_string(); +} +#endif + +#if defined(__EMSCRIPTEN__) && !defined(SOKOL_DUMMY_BACKEND) +EM_JS(int, simgui_js_is_osx, (void), { + if (navigator.userAgent.includes('Macintosh')) { + return 1; + } else { + return 0; + } +}); +#endif + +// ██ ██████ ██████ ██████ ██ ███ ██ ██████ +// ██ ██ ██ ██ ██ ██ ████ ██ ██ +// ██ ██ ██ ██ ███ ██ ███ ██ ██ ██ ██ ██ ███ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ███████ ██████ ██████ ██████ ██ ██ ████ ██████ +// +// >>logging +#if defined(SOKOL_DEBUG) +#define _SIMGUI_LOGITEM_XMACRO(item,msg) #item ": " msg, +static const char* _simgui_log_messages[] = { + _SIMGUI_LOG_ITEMS +}; +#undef _SIMGUI_LOGITEM_XMACRO +#endif // SOKOL_DEBUG + +#define _SIMGUI_PANIC(code) _simgui_log(SIMGUI_LOGITEM_ ##code, 0, 0, __LINE__) +#define _SIMGUI_ERROR(code) _simgui_log(SIMGUI_LOGITEM_ ##code, 1, 0, __LINE__) +#define _SIMGUI_WARN(code) _simgui_log(SIMGUI_LOGITEM_ ##code, 2, 0, __LINE__) +#define _SIMGUI_INFO(code) _simgui_log(SIMGUI_LOGITEM_ ##code, 3, 0, __LINE__) +#define _SIMGUI_LOGMSG(code,msg) _simgui_log(SIMGUI_LOGITEM_ ##code, 3, msg, __LINE__) + +static void _simgui_log(simgui_log_item_t log_item, uint32_t log_level, const char* msg, uint32_t line_nr) { + if (_simgui.desc.logger.func) { + const char* filename = 0; + #if defined(SOKOL_DEBUG) + filename = __FILE__; + if (0 == msg) { + msg = _simgui_log_messages[log_item]; + } + #endif + _simgui.desc.logger.func("simgui", log_level, log_item, msg, line_nr, filename, _simgui.desc.logger.user_data); + } else { + // for log level PANIC it would be 'undefined behaviour' to continue + if (log_level == 0) { + abort(); + } + } +} + +// ███ ███ ███████ ███ ███ ██████ ██████ ██ ██ +// ████ ████ ██ ████ ████ ██ ██ ██ ██ ██ ██ +// ██ ████ ██ █████ ██ ████ ██ ██ ██ ██████ ████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██ ███████ ██ ██ ██████ ██ ██ ██ +// +// >>memory +static void _simgui_clear(void* ptr, size_t size) { + SOKOL_ASSERT(ptr && (size > 0)); + memset(ptr, 0, size); +} + +static void* _simgui_malloc(size_t size) { + SOKOL_ASSERT(size > 0); + void* ptr; + if (_simgui.desc.allocator.alloc_fn) { + ptr = _simgui.desc.allocator.alloc_fn(size, _simgui.desc.allocator.user_data); + } else { + ptr = malloc(size); + } + if (0 == ptr) { + _SIMGUI_PANIC(MALLOC_FAILED); + } + return ptr; +} + +static void* _simgui_malloc_clear(size_t size) { + void* ptr = _simgui_malloc(size); + _simgui_clear(ptr, size); + return ptr; +} + +static void _simgui_free(void* ptr) { + if (_simgui.desc.allocator.free_fn) { + _simgui.desc.allocator.free_fn(ptr, _simgui.desc.allocator.user_data); + } else { + free(ptr); + } +} + +// ██████ ██████ ██████ ██ +// ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ +// +// >>pool +static void _simgui_init_pool(_simgui_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 + pool->size = num + 1; + pool->queue_top = 0; + // 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*) _simgui_malloc_clear(gen_ctrs_size); + // it's not a bug to only reserve 'num' here + pool->free_queue = (int*) _simgui_malloc_clear(sizeof(int) * (size_t)num); + // 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; + } +} + +static void _simgui_discard_pool(_simgui_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + _simgui_free(pool->free_queue); + pool->free_queue = 0; + SOKOL_ASSERT(pool->gen_ctrs); + _simgui_free(pool->gen_ctrs); + pool->gen_ctrs = 0; + pool->size = 0; + pool->queue_top = 0; +} + +static int _simgui_pool_alloc_index(_simgui_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + if (pool->queue_top > 0) { + int slot_index = pool->free_queue[--pool->queue_top]; + SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size)); + return slot_index; + } else { + // pool exhausted + return _SIMGUI_INVALID_SLOT_INDEX; + } +} + +static void _simgui_pool_free_index(_simgui_pool_t* pool, int slot_index) { + SOKOL_ASSERT((slot_index > _SIMGUI_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + SOKOL_ASSERT(pool->queue_top < pool->size); + #ifdef SOKOL_DEBUG + // debug check against double-free + for (int i = 0; i < pool->queue_top; i++) { + SOKOL_ASSERT(pool->free_queue[i] != slot_index); + } + #endif + pool->free_queue[pool->queue_top++] = slot_index; + SOKOL_ASSERT(pool->queue_top <= (pool->size-1)); +} + +/* initialize a pool slot: + - bump the slot's generation counter + - create a resource id from the generation counter and slot index + - set the slot's id to this id + - set the slot's state to ALLOC + - return the handle id +*/ +static uint32_t _simgui_slot_init(_simgui_pool_t* pool, _simgui_slot_t* slot, int slot_index) { + /* FIXME: add handling for an overflowing generation counter, + for now, just overflow (another option is to disable + the slot) + */ + SOKOL_ASSERT(pool && pool->gen_ctrs); + SOKOL_ASSERT((slot_index > _SIMGUI_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT((slot->state == _SIMGUI_RESOURCESTATE_INITIAL) && (slot->id == SIMGUI_INVALID_ID)); + uint32_t ctr = ++pool->gen_ctrs[slot_index]; + slot->id = (ctr<<_SIMGUI_SLOT_SHIFT)|(slot_index & _SIMGUI_SLOT_MASK); + slot->state = _SIMGUI_RESOURCESTATE_ALLOC; + return slot->id; +} + +// extract slot index from id +static int _simgui_slot_index(uint32_t id) { + int slot_index = (int) (id & _SIMGUI_SLOT_MASK); + SOKOL_ASSERT(_SIMGUI_INVALID_SLOT_INDEX != slot_index); + return slot_index; +} + +static void _simgui_init_item_pool(_simgui_pool_t* pool, int pool_size, void** items_ptr, size_t item_size_bytes) { + // NOTE: the pools will have an additional item, since slot 0 is reserved + SOKOL_ASSERT(pool && (pool->size == 0)); + SOKOL_ASSERT((pool_size > 0) && (pool_size < _SIMGUI_MAX_POOL_SIZE)); + SOKOL_ASSERT(items_ptr && (*items_ptr == 0)); + SOKOL_ASSERT(item_size_bytes > 0); + _simgui_init_pool(pool, pool_size); + const size_t pool_size_bytes = item_size_bytes * (size_t)pool->size; + *items_ptr = _simgui_malloc_clear(pool_size_bytes); +} + +static void _simgui_discard_item_pool(_simgui_pool_t* pool, void** items_ptr) { + SOKOL_ASSERT(pool && (pool->size != 0)); + SOKOL_ASSERT(items_ptr && (*items_ptr != 0)); + _simgui_free(*items_ptr); *items_ptr = 0; + _simgui_discard_pool(pool); +} + +static void _simgui_setup_image_pool(int pool_size) { + _simgui_image_pool_t* p = &_simgui.image_pool; + _simgui_init_item_pool(&p->pool, pool_size, (void**)&p->items, sizeof(_simgui_image_t)); +} + +static void _simgui_discard_image_pool(void) { + _simgui_image_pool_t* p = &_simgui.image_pool; + _simgui_discard_item_pool(&p->pool, (void**)&p->items); +} + +static simgui_image_t _simgui_make_image_handle(uint32_t id) { + simgui_image_t handle = { id }; + return handle; +} + +static _simgui_image_t* _simgui_image_at(uint32_t id) { + SOKOL_ASSERT(SIMGUI_INVALID_ID != id); + const _simgui_image_pool_t* p = &_simgui.image_pool; + int slot_index = _simgui_slot_index(id); + SOKOL_ASSERT((slot_index > _SIMGUI_INVALID_SLOT_INDEX) && (slot_index < p->pool.size)); + return &p->items[slot_index]; +} + +static _simgui_image_t* _simgui_lookup_image(uint32_t id) { + if (SIMGUI_INVALID_ID != id) { + _simgui_image_t* img = _simgui_image_at(id); + if (img->slot.id == id) { + return img; + } + } + return 0; +} + +static simgui_image_t _simgui_alloc_image(void) { + _simgui_image_pool_t* p = &_simgui.image_pool; + int slot_index = _simgui_pool_alloc_index(&p->pool); + if (_SIMGUI_INVALID_SLOT_INDEX != slot_index) { + uint32_t id = _simgui_slot_init(&p->pool, &p->items[slot_index].slot, slot_index); + return _simgui_make_image_handle(id); + } else { + // pool exhausted + return _simgui_make_image_handle(SIMGUI_INVALID_ID); + } +} + +static _simgui_resource_state _simgui_init_image(_simgui_image_t* img, const simgui_image_desc_t* desc) { + SOKOL_ASSERT(img && (img->slot.state == _SIMGUI_RESOURCESTATE_ALLOC)); + SOKOL_ASSERT(desc); + SOKOL_ASSERT(_simgui.def_pip.id != SIMGUI_INVALID_ID); + SOKOL_ASSERT(_simgui.pip_unfilterable.id != SIMGUI_INVALID_ID); + img->image = desc->image; + img->sampler = desc->sampler; + if (sg_query_pixelformat(sg_query_image_desc(desc->image).pixel_format).filter) { + img->pip = _simgui.def_pip; + } else { + img->pip = _simgui.pip_unfilterable; + } + return _SIMGUI_RESOURCESTATE_VALID; +} + +static void _simgui_deinit_image(_simgui_image_t* img) { + SOKOL_ASSERT(img); + img->image.id = SIMGUI_INVALID_ID; + img->sampler.id = SIMGUI_INVALID_ID; + img->pip.id = SIMGUI_INVALID_ID; +} + +static void _simgui_destroy_image(simgui_image_t img_id) { + _simgui_image_t* img = _simgui_lookup_image(img_id.id); + if (img) { + _simgui_deinit_image(img); + _simgui_image_pool_t* p = &_simgui.image_pool; + _simgui_clear(img, sizeof(_simgui_image_t)); + _simgui_pool_free_index(&p->pool, _simgui_slot_index(img_id.id)); + } +} + +static void _simgui_destroy_all_images(void) { + _simgui_image_pool_t* p = &_simgui.image_pool; + for (int i = 0; i < p->pool.size; i++) { + _simgui_image_t* img = &p->items[i]; + _simgui_destroy_image(_simgui_make_image_handle(img->slot.id)); + } +} + +static simgui_image_desc_t _simgui_image_desc_defaults(const simgui_image_desc_t* desc) { + SOKOL_ASSERT(desc); + simgui_image_desc_t res = *desc; + res.image.id = _simgui_def(res.image.id, _simgui.def_img.id); + res.sampler.id = _simgui_def(res.sampler.id, _simgui.def_smp.id); + return res; +} + +static bool _simgui_is_osx(void) { + #if defined(SOKOL_DUMMY_BACKEND) + return false; + #elif defined(__EMSCRIPTEN__) + return simgui_js_is_osx(); + #elif defined(__APPLE__) + return true; + #else + return false; + #endif +} + +static simgui_desc_t _simgui_desc_defaults(const simgui_desc_t* desc) { + SOKOL_ASSERT((desc->allocator.alloc_fn && desc->allocator.free_fn) || (!desc->allocator.alloc_fn && !desc->allocator.free_fn)); + simgui_desc_t res = *desc; + res.max_vertices = _simgui_def(res.max_vertices, 65536); + res.image_pool_size = _simgui_def(res.image_pool_size, 256); + return res; +} + +// ██████ ██ ██ ██████ ██ ██ ██████ +// ██ ██ ██ ██ ██ ██ ██ ██ ██ +// ██████ ██ ██ ██████ ██ ██ ██ +// ██ ██ ██ ██ ██ ██ ██ ██ +// ██ ██████ ██████ ███████ ██ ██████ +// +// >>public +SOKOL_API_IMPL void simgui_setup(const simgui_desc_t* desc) { + SOKOL_ASSERT(desc); + _simgui_clear(&_simgui, sizeof(_simgui)); + _simgui.init_cookie = _SIMGUI_INIT_COOKIE; + _simgui.desc = _simgui_desc_defaults(desc); + _simgui.cur_dpi_scale = 1.0f; + #if !defined(SOKOL_IMGUI_NO_SOKOL_APP) + _simgui.is_osx = _simgui_is_osx(); + #endif + // can keep color_format, depth_format and sample_count as is, + // since sokol_gfx.h will do its own default-value handling + + // setup image pool + _simgui_setup_image_pool(_simgui.desc.image_pool_size); + + // allocate an intermediate vertex- and index-buffer + SOKOL_ASSERT(_simgui.desc.max_vertices > 0); + _simgui.vertices.size = (size_t)_simgui.desc.max_vertices * sizeof(ImDrawVert); + _simgui.vertices.ptr = _simgui_malloc(_simgui.vertices.size); + _simgui.indices.size = (size_t)_simgui.desc.max_vertices * 3 * sizeof(ImDrawIdx); + _simgui.indices.ptr = _simgui_malloc(_simgui.indices.size); + + // initialize Dear ImGui + #if defined(__cplusplus) + ImGui::CreateContext(); + ImGui::StyleColorsDark(); + ImGuiIO* io = &ImGui::GetIO(); + if (!_simgui.desc.no_default_font) { + io->Fonts->AddFontDefault(); + } + #else + igCreateContext(NULL); + igStyleColorsDark(igGetStyle()); + ImGuiIO* io = igGetIO(); + if (!_simgui.desc.no_default_font) { + ImFontAtlas_AddFontDefault(io->Fonts, NULL); + } + #endif + io->IniFilename = _simgui.desc.ini_filename; + io->ConfigMacOSXBehaviors = _simgui_is_osx(); + io->BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; + #if !defined(SOKOL_IMGUI_NO_SOKOL_APP) + if (!_simgui.desc.disable_set_mouse_cursor) { + io->BackendFlags |= ImGuiBackendFlags_HasMouseCursors; + } + io->SetClipboardTextFn = _simgui_set_clipboard; + io->GetClipboardTextFn = _simgui_get_clipboard; + #endif + io->ConfigWindowsResizeFromEdges = !_simgui.desc.disable_windows_resize_from_edges; + + // create sokol-gfx resources + sg_push_debug_group("sokol-imgui"); + + // shader object for using the embedded shader source (or bytecode) + sg_shader_desc shd_desc; + _simgui_clear(&shd_desc, sizeof(shd_desc)); + shd_desc.attrs[0].name = "position"; + shd_desc.attrs[1].name = "texcoord0"; + shd_desc.attrs[2].name = "color0"; + shd_desc.attrs[0].sem_name = "TEXCOORD"; + shd_desc.attrs[0].sem_index = 0; + shd_desc.attrs[1].sem_name = "TEXCOORD"; + shd_desc.attrs[1].sem_index = 1; + shd_desc.attrs[2].sem_name = "TEXCOORD"; + shd_desc.attrs[2].sem_index = 2; + sg_shader_uniform_block_desc* ub = &shd_desc.vs.uniform_blocks[0]; + ub->size = sizeof(_simgui_vs_params_t); + ub->uniforms[0].name = "vs_params"; + ub->uniforms[0].type = SG_UNIFORMTYPE_FLOAT4; + ub->uniforms[0].array_count = 1; + shd_desc.fs.images[0].used = true; + shd_desc.fs.images[0].image_type = SG_IMAGETYPE_2D; + 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_FILTERING; + 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 = "sokol-imgui-shader"; + #if defined(SOKOL_GLCORE) + shd_desc.vs.source = (const char*)_simgui_vs_source_glsl410; + shd_desc.fs.source = (const char*)_simgui_fs_source_glsl410; + #elif defined(SOKOL_GLES3) + shd_desc.vs.source = (const char*)_simgui_vs_source_glsl300es; + shd_desc.fs.source = (const char*)_simgui_fs_source_glsl300es; + #elif defined(SOKOL_METAL) + shd_desc.vs.entry = "main0"; + shd_desc.fs.entry = "main0"; + switch (sg_query_backend()) { + case SG_BACKEND_METAL_MACOS: + shd_desc.vs.bytecode = SG_RANGE(_simgui_vs_bytecode_metal_macos); + shd_desc.fs.bytecode = SG_RANGE(_simgui_fs_bytecode_metal_macos); + break; + case SG_BACKEND_METAL_IOS: + shd_desc.vs.bytecode = SG_RANGE(_simgui_vs_bytecode_metal_ios); + shd_desc.fs.bytecode = SG_RANGE(_simgui_fs_bytecode_metal_ios); + break; + default: + shd_desc.vs.source = (const char*)_simgui_vs_source_metal_sim; + shd_desc.fs.source = (const char*)_simgui_fs_source_metal_sim; + break; + } + #elif defined(SOKOL_D3D11) + shd_desc.vs.bytecode = SG_RANGE(_simgui_vs_bytecode_hlsl4); + shd_desc.fs.bytecode = SG_RANGE(_simgui_fs_bytecode_hlsl4); + #elif defined(SOKOL_WGPU) + shd_desc.vs.source = (const char*)_simgui_vs_source_wgsl; + shd_desc.fs.source = (const char*)_simgui_fs_source_wgsl; + #else + shd_desc.vs.source = _simgui_vs_source_dummy; + shd_desc.fs.source = _simgui_fs_source_dummy; + #endif + _simgui.def_shd = sg_make_shader(&shd_desc); + + // pipeline object for imgui rendering + sg_pipeline_desc pip_desc; + _simgui_clear(&pip_desc, sizeof(pip_desc)); + pip_desc.layout.buffers[0].stride = sizeof(ImDrawVert); + { + sg_vertex_attr_state* attr = &pip_desc.layout.attrs[0]; + attr->offset = offsetof(ImDrawVert, pos); + attr->format = SG_VERTEXFORMAT_FLOAT2; + } + { + sg_vertex_attr_state* attr = &pip_desc.layout.attrs[1]; + attr->offset = offsetof(ImDrawVert, uv); + attr->format = SG_VERTEXFORMAT_FLOAT2; + } + { + sg_vertex_attr_state* attr = &pip_desc.layout.attrs[2]; + attr->offset = offsetof(ImDrawVert, col); + attr->format = SG_VERTEXFORMAT_UBYTE4N; + } + pip_desc.shader = _simgui.def_shd; + pip_desc.index_type = SG_INDEXTYPE_UINT16; + pip_desc.sample_count = _simgui.desc.sample_count; + pip_desc.depth.pixel_format = _simgui.desc.depth_format; + pip_desc.colors[0].pixel_format = _simgui.desc.color_format; + pip_desc.colors[0].write_mask = _simgui.desc.write_alpha_channel ? SG_COLORMASK_RGBA : SG_COLORMASK_RGB; + pip_desc.colors[0].blend.enabled = true; + pip_desc.colors[0].blend.src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA; + pip_desc.colors[0].blend.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA; + if (_simgui.desc.write_alpha_channel) { + pip_desc.colors[0].blend.src_factor_alpha = SG_BLENDFACTOR_ONE; + pip_desc.colors[0].blend.dst_factor_alpha = SG_BLENDFACTOR_ONE; + } + pip_desc.label = "sokol-imgui-pipeline"; + _simgui.def_pip = sg_make_pipeline(&pip_desc); + + // create a unfilterable/nonfiltering variants of the shader and pipeline + shd_desc.fs.images[0].sample_type = SG_IMAGESAMPLETYPE_UNFILTERABLE_FLOAT; + shd_desc.fs.samplers[0].sampler_type = SG_SAMPLERTYPE_NONFILTERING; + shd_desc.label = "sokol-imgui-shader-unfilterable"; + _simgui.shd_unfilterable = sg_make_shader(&shd_desc); + pip_desc.shader = _simgui.shd_unfilterable; + pip_desc.label = "sokol-imgui-pipeline-unfilterable"; + _simgui.pip_unfilterable = sg_make_pipeline(&pip_desc); + + // NOTE: since we're in C++ mode here we can't use C99 designated init + sg_buffer_desc vb_desc; + _simgui_clear(&vb_desc, sizeof(vb_desc)); + vb_desc.usage = SG_USAGE_STREAM; + vb_desc.size = _simgui.vertices.size; + vb_desc.label = "sokol-imgui-vertices"; + _simgui.vbuf = sg_make_buffer(&vb_desc); + + sg_buffer_desc ib_desc; + _simgui_clear(&ib_desc, sizeof(ib_desc)); + ib_desc.type = SG_BUFFERTYPE_INDEXBUFFER; + ib_desc.usage = SG_USAGE_STREAM; + ib_desc.size = _simgui.indices.size; + ib_desc.label = "sokol-imgui-indices"; + _simgui.ibuf = sg_make_buffer(&ib_desc); + + // a default user-image sampler + sg_sampler_desc def_sampler_desc; + _simgui_clear(&def_sampler_desc, sizeof(def_sampler_desc)); + def_sampler_desc.min_filter = SG_FILTER_NEAREST; + def_sampler_desc.mag_filter = SG_FILTER_NEAREST; + def_sampler_desc.wrap_u = SG_WRAP_CLAMP_TO_EDGE; + def_sampler_desc.wrap_v = SG_WRAP_CLAMP_TO_EDGE; + def_sampler_desc.label = "sokol-imgui-default-sampler"; + _simgui.def_smp = sg_make_sampler(&def_sampler_desc); + + // a default user image + static uint32_t def_pixels[64]; + memset(def_pixels, 0xFF, sizeof(def_pixels)); + sg_image_desc def_image_desc; + _simgui_clear(&def_image_desc, sizeof(def_image_desc)); + def_image_desc.width = 8; + def_image_desc.height = 8; + def_image_desc.pixel_format = SG_PIXELFORMAT_RGBA8; + def_image_desc.data.subimage[0][0].ptr = def_pixels; + def_image_desc.data.subimage[0][0].size = sizeof(def_pixels); + def_image_desc.label = "sokol-imgui-default-image"; + _simgui.def_img = sg_make_image(&def_image_desc); + + // default font texture + if (!_simgui.desc.no_default_font) { + simgui_font_tex_desc_t simgui_font_smp_desc; + _simgui_clear(&simgui_font_smp_desc, sizeof(simgui_font_smp_desc)); + simgui_create_fonts_texture(&simgui_font_smp_desc); + } + + sg_pop_debug_group(); +} + +SOKOL_API_IMPL void simgui_create_fonts_texture(const simgui_font_tex_desc_t* desc) { + SOKOL_ASSERT(desc); + SOKOL_ASSERT(SG_INVALID_ID == _simgui.font_smp.id); + SOKOL_ASSERT(SG_INVALID_ID == _simgui.font_img.id); + SOKOL_ASSERT(SIMGUI_INVALID_ID == _simgui.default_font.id); + + #if defined(__cplusplus) + ImGuiIO* io = &ImGui::GetIO(); + #else + ImGuiIO* io = igGetIO(); + #endif + + // a default font sampler + sg_sampler_desc font_smp_desc; + _simgui_clear(&font_smp_desc, sizeof(font_smp_desc)); + font_smp_desc.wrap_u = SG_WRAP_CLAMP_TO_EDGE; + font_smp_desc.wrap_v = SG_WRAP_CLAMP_TO_EDGE; + font_smp_desc.min_filter = desc->min_filter; + font_smp_desc.mag_filter = desc->mag_filter; + font_smp_desc.mipmap_filter = SG_FILTER_NONE; + font_smp_desc.label = "sokol-imgui-font-sampler"; + _simgui.font_smp = sg_make_sampler(&font_smp_desc); + + unsigned char* font_pixels; + int font_width, font_height; + #if defined(__cplusplus) + io->Fonts->GetTexDataAsRGBA32(&font_pixels, &font_width, &font_height); + #else + int bytes_per_pixel; + ImFontAtlas_GetTexDataAsRGBA32(io->Fonts, &font_pixels, &font_width, &font_height, &bytes_per_pixel); + #endif + sg_image_desc font_img_desc; + _simgui_clear(&font_img_desc, sizeof(font_img_desc)); + font_img_desc.width = font_width; + font_img_desc.height = font_height; + font_img_desc.pixel_format = SG_PIXELFORMAT_RGBA8; + font_img_desc.data.subimage[0][0].ptr = font_pixels; + font_img_desc.data.subimage[0][0].size = (size_t)(font_width * font_height) * sizeof(uint32_t); + font_img_desc.label = "sokol-imgui-font-image"; + _simgui.font_img = sg_make_image(&font_img_desc); + + simgui_image_desc_t img_desc; + _simgui_clear(&img_desc, sizeof(img_desc)); + img_desc.image = _simgui.font_img; + img_desc.sampler = _simgui.font_smp; + _simgui.default_font = simgui_make_image(&img_desc); + io->Fonts->TexID = simgui_imtextureid(_simgui.default_font); +} + +SOKOL_API_IMPL void simgui_destroy_fonts_texture(void) { + // NOTE: it's valid to call the destroy funcs with SG_INVALID_ID + sg_destroy_sampler(_simgui.font_smp); + sg_destroy_image(_simgui.font_img); + simgui_destroy_image(_simgui.default_font); + _simgui.font_smp.id = SG_INVALID_ID; + _simgui.font_img.id = SG_INVALID_ID; + _simgui.default_font.id = SIMGUI_INVALID_ID; +} + +SOKOL_API_IMPL void simgui_shutdown(void) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + #if defined(__cplusplus) + ImGui::DestroyContext(); + #else + igDestroyContext(0); + #endif + // NOTE: it's valid to call the destroy funcs with SG_INVALID_ID + sg_destroy_pipeline(_simgui.pip_unfilterable); + sg_destroy_shader(_simgui.shd_unfilterable); + sg_destroy_pipeline(_simgui.def_pip); + sg_destroy_shader(_simgui.def_shd); + sg_destroy_sampler(_simgui.font_smp); + sg_destroy_image(_simgui.font_img); + sg_destroy_sampler(_simgui.def_smp); + sg_destroy_image(_simgui.def_img); + sg_destroy_buffer(_simgui.ibuf); + sg_destroy_buffer(_simgui.vbuf); + sg_pop_debug_group(); + sg_push_debug_group("sokol-imgui"); + _simgui_destroy_all_images(); + _simgui_discard_image_pool(); + SOKOL_ASSERT(_simgui.vertices.ptr); + _simgui_free((void*)_simgui.vertices.ptr); + SOKOL_ASSERT(_simgui.indices.ptr); + _simgui_free((void*)_simgui.indices.ptr); + _simgui.init_cookie = 0; +} + +SOKOL_API_IMPL simgui_image_t simgui_make_image(const simgui_image_desc_t* desc) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + SOKOL_ASSERT(desc); + const simgui_image_desc_t desc_def = _simgui_image_desc_defaults(desc); + simgui_image_t img_id = _simgui_alloc_image(); + _simgui_image_t* img = _simgui_lookup_image(img_id.id); + if (img) { + img->slot.state = _simgui_init_image(img, &desc_def); + SOKOL_ASSERT((img->slot.state == _SIMGUI_RESOURCESTATE_VALID) || (img->slot.state == _SIMGUI_RESOURCESTATE_FAILED)); + } else { + _SIMGUI_ERROR(IMAGE_POOL_EXHAUSTED); + } + return img_id; +} + +SOKOL_API_IMPL void simgui_destroy_image(simgui_image_t img_id) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + _simgui_destroy_image(img_id); +} + +SOKOL_API_IMPL simgui_image_desc_t simgui_query_image_desc(simgui_image_t img_id) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + _simgui_image_t* img = _simgui_lookup_image(img_id.id); + simgui_image_desc_t desc; + _simgui_clear(&desc, sizeof(desc)); + if (img) { + desc.image = img->image; + desc.sampler = img->sampler; + } + return desc; +} + +SOKOL_API_IMPL void* simgui_imtextureid(simgui_image_t img) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + return (void*)(uintptr_t)img.id; +} + +SOKOL_API_IMPL simgui_image_t simgui_image_from_imtextureid(void* im_texture_id) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + simgui_image_t img = { (uint32_t)(uintptr_t) im_texture_id }; + return img; +} + +SOKOL_API_IMPL void simgui_new_frame(const simgui_frame_desc_t* desc) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + SOKOL_ASSERT(desc); + SOKOL_ASSERT(desc->width > 0); + SOKOL_ASSERT(desc->height > 0); + _simgui.cur_dpi_scale = _simgui_def(desc->dpi_scale, 1.0f); + #if defined(__cplusplus) + ImGuiIO* io = &ImGui::GetIO(); + #else + ImGuiIO* io = igGetIO(); + #endif + if (!io->Fonts->TexReady) { + simgui_destroy_fonts_texture(); + simgui_font_tex_desc_t simgui_font_smp_desc; + _simgui_clear(&simgui_font_smp_desc, sizeof(simgui_font_smp_desc)); + simgui_create_fonts_texture(&simgui_font_smp_desc); + } + io->DisplaySize.x = ((float)desc->width) / _simgui.cur_dpi_scale; + io->DisplaySize.y = ((float)desc->height) / _simgui.cur_dpi_scale; + io->DeltaTime = (float)desc->delta_time; + #if !defined(SOKOL_IMGUI_NO_SOKOL_APP) + if (io->WantTextInput && !sapp_keyboard_shown()) { + sapp_show_keyboard(true); + } + if (!io->WantTextInput && sapp_keyboard_shown()) { + sapp_show_keyboard(false); + } + if (!_simgui.desc.disable_set_mouse_cursor) { + #if defined(__cplusplus) + ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); + #else + ImGuiMouseCursor imgui_cursor = igGetMouseCursor(); + #endif + sapp_mouse_cursor cursor = sapp_get_mouse_cursor(); + switch (imgui_cursor) { + case ImGuiMouseCursor_Arrow: cursor = SAPP_MOUSECURSOR_ARROW; break; + case ImGuiMouseCursor_TextInput: cursor = SAPP_MOUSECURSOR_IBEAM; break; + case ImGuiMouseCursor_ResizeAll: cursor = SAPP_MOUSECURSOR_RESIZE_ALL; break; + case ImGuiMouseCursor_ResizeNS: cursor = SAPP_MOUSECURSOR_RESIZE_NS; break; + case ImGuiMouseCursor_ResizeEW: cursor = SAPP_MOUSECURSOR_RESIZE_EW; break; + case ImGuiMouseCursor_ResizeNESW: cursor = SAPP_MOUSECURSOR_RESIZE_NESW; break; + case ImGuiMouseCursor_ResizeNWSE: cursor = SAPP_MOUSECURSOR_RESIZE_NWSE; break; + case ImGuiMouseCursor_Hand: cursor = SAPP_MOUSECURSOR_POINTING_HAND; break; + case ImGuiMouseCursor_NotAllowed: cursor = SAPP_MOUSECURSOR_NOT_ALLOWED; break; + default: break; + } + sapp_set_mouse_cursor(cursor); + } + #endif + #if defined(__cplusplus) + ImGui::NewFrame(); + #else + igNewFrame(); + #endif +} + +static const _simgui_image_t* _simgui_bind_image_sampler(sg_bindings* bindings, ImTextureID tex_id) { + const _simgui_image_t* img = _simgui_lookup_image((uint32_t)(uintptr_t)tex_id); + if (img) { + bindings->fs.images[0] = img->image; + bindings->fs.samplers[0] = img->sampler; + } else { + bindings->fs.images[0] = _simgui.def_img; + bindings->fs.samplers[0] = _simgui.def_smp; + } + return img; +} + +static ImDrawList* _simgui_imdrawlist_at(ImDrawData* draw_data, int cl_index) { + #if defined(__cplusplus) + return draw_data->CmdLists[cl_index]; + #else + return draw_data->CmdLists.Data[cl_index]; + #endif +} + +SOKOL_API_IMPL void simgui_render(void) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + #if defined(__cplusplus) + ImGui::Render(); + ImDrawData* draw_data = ImGui::GetDrawData(); + ImGuiIO* io = &ImGui::GetIO(); + #else + igRender(); + ImDrawData* draw_data = igGetDrawData(); + ImGuiIO* io = igGetIO(); + #endif + if (0 == draw_data) { + return; + } + if (draw_data->CmdListsCount == 0) { + return; + } + /* copy vertices and indices into an intermediate buffer so that + they can be updated with a single sg_update_buffer() call each + (sg_append_buffer() has performance problems on some GL platforms), + also keep track of valid number of command lists in case of a + buffer overflow + */ + size_t all_vtx_size = 0; + size_t all_idx_size = 0; + int cmd_list_count = 0; + for (int cl_index = 0; cl_index < draw_data->CmdListsCount; cl_index++, cmd_list_count++) { + ImDrawList* cl = _simgui_imdrawlist_at(draw_data, cl_index); + const size_t vtx_size = (size_t)cl->VtxBuffer.Size * sizeof(ImDrawVert); + const size_t idx_size = (size_t)cl->IdxBuffer.Size * sizeof(ImDrawIdx); + + // check for buffer overflow + if (((all_vtx_size + vtx_size) > _simgui.vertices.size) || + ((all_idx_size + idx_size) > _simgui.indices.size)) + { + break; + } + + // copy vertices and indices into common buffers + if (vtx_size > 0) { + const ImDrawVert* src_vtx_ptr = cl->VtxBuffer.Data; + void* dst_vtx_ptr = (void*) (((uint8_t*)_simgui.vertices.ptr) + all_vtx_size); + memcpy(dst_vtx_ptr, src_vtx_ptr, vtx_size); + } + if (idx_size > 0) { + const ImDrawIdx* src_idx_ptr = cl->IdxBuffer.Data; + void* dst_idx_ptr = (void*) (((uint8_t*)_simgui.indices.ptr) + all_idx_size); + memcpy(dst_idx_ptr, src_idx_ptr, idx_size); + } + all_vtx_size += vtx_size; + all_idx_size += idx_size; + } + if (0 == cmd_list_count) { + return; + } + + // update the sokol-gfx vertex- and index-buffer + sg_push_debug_group("sokol-imgui"); + if (all_vtx_size > 0) { + sg_range vtx_data = _simgui.vertices; + vtx_data.size = all_vtx_size; + sg_update_buffer(_simgui.vbuf, &vtx_data); + } + if (all_idx_size > 0) { + sg_range idx_data = _simgui.indices; + idx_data.size = all_idx_size; + sg_update_buffer(_simgui.ibuf, &idx_data); + } + + // render the ImGui command list + const float dpi_scale = _simgui.cur_dpi_scale; + const int fb_width = (int) (io->DisplaySize.x * dpi_scale); + const int fb_height = (int) (io->DisplaySize.y * dpi_scale); + sg_apply_viewport(0, 0, fb_width, fb_height, true); + sg_apply_scissor_rect(0, 0, fb_width, fb_height, true); + + sg_apply_pipeline(_simgui.def_pip); + _simgui_vs_params_t vs_params; + _simgui_clear((void*)&vs_params, sizeof(vs_params)); + vs_params.disp_size.x = io->DisplaySize.x; + vs_params.disp_size.y = io->DisplaySize.y; + sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(vs_params)); + sg_bindings bind; + _simgui_clear((void*)&bind, sizeof(bind)); + bind.vertex_buffers[0] = _simgui.vbuf; + bind.index_buffer = _simgui.ibuf; + ImTextureID tex_id = io->Fonts->TexID; + _simgui_bind_image_sampler(&bind, tex_id); + int vb_offset = 0; + int ib_offset = 0; + for (int cl_index = 0; cl_index < cmd_list_count; cl_index++) { + ImDrawList* cl = _simgui_imdrawlist_at(draw_data, cl_index); + + bind.vertex_buffer_offsets[0] = vb_offset; + bind.index_buffer_offset = ib_offset; + sg_apply_bindings(&bind); + + #if defined(__cplusplus) + const int num_cmds = cl->CmdBuffer.size(); + #else + const int num_cmds = cl->CmdBuffer.Size; + #endif + uint32_t vtx_offset = 0; + for (int cmd_index = 0; cmd_index < num_cmds; cmd_index++) { + ImDrawCmd* pcmd = &cl->CmdBuffer.Data[cmd_index]; + if (pcmd->UserCallback != 0) { + // User callback, registered via ImDrawList::AddCallback() + // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) + if (pcmd->UserCallback != ImDrawCallback_ResetRenderState) { + pcmd->UserCallback(cl, pcmd); + // need to re-apply all state after calling a user callback + sg_reset_state_cache(); + sg_apply_viewport(0, 0, fb_width, fb_height, true); + sg_apply_pipeline(_simgui.def_pip); + sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(vs_params)); + sg_apply_bindings(&bind); + } + } else { + if ((tex_id != pcmd->TextureId) || (vtx_offset != pcmd->VtxOffset)) { + tex_id = pcmd->TextureId; + vtx_offset = pcmd->VtxOffset; + const _simgui_image_t* img = _simgui_bind_image_sampler(&bind, tex_id); + if (img) { + sg_apply_pipeline(img->pip); + } else { + sg_apply_pipeline(_simgui.def_pip); + } + sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, SG_RANGE_REF(vs_params)); + bind.vertex_buffer_offsets[0] = vb_offset + (int)(pcmd->VtxOffset * sizeof(ImDrawVert)); + sg_apply_bindings(&bind); + } + const int scissor_x = (int) (pcmd->ClipRect.x * dpi_scale); + const int scissor_y = (int) (pcmd->ClipRect.y * dpi_scale); + const int scissor_w = (int) ((pcmd->ClipRect.z - pcmd->ClipRect.x) * dpi_scale); + const int scissor_h = (int) ((pcmd->ClipRect.w - pcmd->ClipRect.y) * dpi_scale); + sg_apply_scissor_rect(scissor_x, scissor_y, scissor_w, scissor_h, true); + sg_draw((int)pcmd->IdxOffset, (int)pcmd->ElemCount, 1); + } + } + #if defined(__cplusplus) + const size_t vtx_size = (size_t)cl->VtxBuffer.size() * sizeof(ImDrawVert); + const size_t idx_size = (size_t)cl->IdxBuffer.size() * sizeof(ImDrawIdx); + #else + const size_t vtx_size = (size_t)cl->VtxBuffer.Size * sizeof(ImDrawVert); + const size_t idx_size = (size_t)cl->IdxBuffer.Size * sizeof(ImDrawIdx); + #endif + vb_offset += (int)vtx_size; + ib_offset += (int)idx_size; + } + sg_apply_viewport(0, 0, fb_width, fb_height, true); + sg_apply_scissor_rect(0, 0, fb_width, fb_height, true); + sg_pop_debug_group(); +} + +SOKOL_API_IMPL void simgui_add_focus_event(bool focus) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + #if defined(__cplusplus) + ImGuiIO* io = &ImGui::GetIO(); + io->AddFocusEvent(focus); + #else + ImGuiIO* io = igGetIO(); + ImGuiIO_AddFocusEvent(io, focus); + #endif +} + +SOKOL_API_IMPL void simgui_add_mouse_pos_event(float x, float y) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + #if defined(__cplusplus) + ImGuiIO* io = &ImGui::GetIO(); + #if (IMGUI_VERSION_NUM >= 18950) + io->AddMouseSourceEvent(ImGuiMouseSource_Mouse); + #endif + io->AddMousePosEvent(x, y); + #else + ImGuiIO* io = igGetIO(); + #if (IMGUI_VERSION_NUM >= 18950) + ImGuiIO_AddMouseSourceEvent(io, ImGuiMouseSource_Mouse); + #endif + ImGuiIO_AddMousePosEvent(io, x, y); + #endif +} + +SOKOL_API_IMPL void simgui_add_touch_pos_event(float x, float y) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + #if defined(__cplusplus) + ImGuiIO* io = &ImGui::GetIO(); + #if (IMGUI_VERSION_NUM >= 18950) + io->AddMouseSourceEvent(ImGuiMouseSource_TouchScreen); + #endif + io->AddMousePosEvent(x, y); + #else + ImGuiIO* io = igGetIO(); + #if (IMGUI_VERSION_NUM >= 18950) + ImGuiIO_AddMouseSourceEvent(io, ImGuiMouseSource_TouchScreen); + #endif + ImGuiIO_AddMousePosEvent(io, x, y); + #endif +} + +SOKOL_API_IMPL void simgui_add_mouse_button_event(int mouse_button, bool down) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + #if defined(__cplusplus) + ImGuiIO* io = &ImGui::GetIO(); + #if (IMGUI_VERSION_NUM >= 18950) + io->AddMouseSourceEvent(ImGuiMouseSource_Mouse); + #endif + io->AddMouseButtonEvent(mouse_button, down); + #else + ImGuiIO* io = igGetIO(); + #if (IMGUI_VERSION_NUM >= 18950) + ImGuiIO_AddMouseSourceEvent(io, ImGuiMouseSource_Mouse); + #endif + ImGuiIO_AddMouseButtonEvent(io, mouse_button, down); + #endif +} + +SOKOL_API_IMPL void simgui_add_touch_button_event(int mouse_button, bool down) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + #if defined(__cplusplus) + ImGuiIO* io = &ImGui::GetIO(); + #if (IMGUI_VERSION_NUM >= 18950) + io->AddMouseSourceEvent(ImGuiMouseSource_TouchScreen); + #endif + io->AddMouseButtonEvent(mouse_button, down); + #else + ImGuiIO* io = igGetIO(); + #if (IMGUI_VERSION_NUM >= 18950) + ImGuiIO_AddMouseSourceEvent(io, ImGuiMouseSource_TouchScreen); + #endif + ImGuiIO_AddMouseButtonEvent(io, mouse_button, down); + #endif +} + +SOKOL_API_IMPL void simgui_add_mouse_wheel_event(float wheel_x, float wheel_y) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + #if defined(__cplusplus) + ImGuiIO* io = &ImGui::GetIO(); + #if (IMGUI_VERSION_NUM >= 18950) + io->AddMouseSourceEvent(ImGuiMouseSource_Mouse); + #endif + io->AddMouseWheelEvent(wheel_x, wheel_y); + #else + ImGuiIO* io = igGetIO(); + #if (IMGUI_VERSION_NUM >= 18950) + ImGuiIO_AddMouseSourceEvent(io, ImGuiMouseSource_Mouse); + #endif + ImGuiIO_AddMouseWheelEvent(io, wheel_x, wheel_y); + #endif +} + +SOKOL_API_IMPL void simgui_add_key_event(int imgui_key, bool down) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + #if defined(__cplusplus) + ImGuiIO* io = &ImGui::GetIO(); + io->AddKeyEvent((ImGuiKey)imgui_key, down); + #else + ImGuiIO* io = igGetIO(); + ImGuiIO_AddKeyEvent(io, (ImGuiKey)imgui_key, down); + #endif +} + +SOKOL_API_IMPL void simgui_add_input_character(uint32_t c) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + #if defined(__cplusplus) + ImGuiIO* io = &ImGui::GetIO(); + io->AddInputCharacter(c); + #else + ImGuiIO* io = igGetIO(); + ImGuiIO_AddInputCharacter(io, c); + #endif +} + +SOKOL_API_IMPL void simgui_add_input_characters_utf8(const char* c) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + #if defined(__cplusplus) + ImGuiIO* io = &ImGui::GetIO(); + io->AddInputCharactersUTF8(c); + #else + ImGuiIO* io = igGetIO(); + ImGuiIO_AddInputCharactersUTF8(io, c); + #endif +} + +#if !defined(SOKOL_IMGUI_NO_SOKOL_APP) +_SOKOL_PRIVATE bool _simgui_is_ctrl(uint32_t modifiers) { + if (_simgui.is_osx) { + return 0 != (modifiers & SAPP_MODIFIER_SUPER); + } else { + return 0 != (modifiers & SAPP_MODIFIER_CTRL); + } +} + +_SOKOL_PRIVATE ImGuiKey _simgui_map_keycode(sapp_keycode key) { + switch (key) { + case SAPP_KEYCODE_SPACE: return ImGuiKey_Space; + case SAPP_KEYCODE_APOSTROPHE: return ImGuiKey_Apostrophe; + case SAPP_KEYCODE_COMMA: return ImGuiKey_Comma; + case SAPP_KEYCODE_MINUS: return ImGuiKey_Minus; + case SAPP_KEYCODE_PERIOD: return ImGuiKey_Apostrophe; + case SAPP_KEYCODE_SLASH: return ImGuiKey_Slash; + case SAPP_KEYCODE_0: return ImGuiKey_0; + case SAPP_KEYCODE_1: return ImGuiKey_1; + case SAPP_KEYCODE_2: return ImGuiKey_2; + case SAPP_KEYCODE_3: return ImGuiKey_3; + case SAPP_KEYCODE_4: return ImGuiKey_4; + case SAPP_KEYCODE_5: return ImGuiKey_5; + case SAPP_KEYCODE_6: return ImGuiKey_6; + case SAPP_KEYCODE_7: return ImGuiKey_7; + case SAPP_KEYCODE_8: return ImGuiKey_8; + case SAPP_KEYCODE_9: return ImGuiKey_9; + case SAPP_KEYCODE_SEMICOLON: return ImGuiKey_Semicolon; + case SAPP_KEYCODE_EQUAL: return ImGuiKey_Equal; + case SAPP_KEYCODE_A: return ImGuiKey_A; + case SAPP_KEYCODE_B: return ImGuiKey_B; + case SAPP_KEYCODE_C: return ImGuiKey_C; + case SAPP_KEYCODE_D: return ImGuiKey_D; + case SAPP_KEYCODE_E: return ImGuiKey_E; + case SAPP_KEYCODE_F: return ImGuiKey_F; + case SAPP_KEYCODE_G: return ImGuiKey_G; + case SAPP_KEYCODE_H: return ImGuiKey_H; + case SAPP_KEYCODE_I: return ImGuiKey_I; + case SAPP_KEYCODE_J: return ImGuiKey_J; + case SAPP_KEYCODE_K: return ImGuiKey_K; + case SAPP_KEYCODE_L: return ImGuiKey_L; + case SAPP_KEYCODE_M: return ImGuiKey_M; + case SAPP_KEYCODE_N: return ImGuiKey_N; + case SAPP_KEYCODE_O: return ImGuiKey_O; + case SAPP_KEYCODE_P: return ImGuiKey_P; + case SAPP_KEYCODE_Q: return ImGuiKey_Q; + case SAPP_KEYCODE_R: return ImGuiKey_R; + case SAPP_KEYCODE_S: return ImGuiKey_S; + case SAPP_KEYCODE_T: return ImGuiKey_T; + case SAPP_KEYCODE_U: return ImGuiKey_U; + case SAPP_KEYCODE_V: return ImGuiKey_V; + case SAPP_KEYCODE_W: return ImGuiKey_W; + case SAPP_KEYCODE_X: return ImGuiKey_X; + case SAPP_KEYCODE_Y: return ImGuiKey_Y; + case SAPP_KEYCODE_Z: return ImGuiKey_Z; + case SAPP_KEYCODE_LEFT_BRACKET: return ImGuiKey_LeftBracket; + case SAPP_KEYCODE_BACKSLASH: return ImGuiKey_Backslash; + case SAPP_KEYCODE_RIGHT_BRACKET:return ImGuiKey_RightBracket; + case SAPP_KEYCODE_GRAVE_ACCENT: return ImGuiKey_GraveAccent; + case SAPP_KEYCODE_ESCAPE: return ImGuiKey_Escape; + case SAPP_KEYCODE_ENTER: return ImGuiKey_Enter; + case SAPP_KEYCODE_TAB: return ImGuiKey_Tab; + case SAPP_KEYCODE_BACKSPACE: return ImGuiKey_Backspace; + case SAPP_KEYCODE_INSERT: return ImGuiKey_Insert; + case SAPP_KEYCODE_DELETE: return ImGuiKey_Delete; + case SAPP_KEYCODE_RIGHT: return ImGuiKey_RightArrow; + case SAPP_KEYCODE_LEFT: return ImGuiKey_LeftArrow; + case SAPP_KEYCODE_DOWN: return ImGuiKey_DownArrow; + case SAPP_KEYCODE_UP: return ImGuiKey_UpArrow; + case SAPP_KEYCODE_PAGE_UP: return ImGuiKey_PageUp; + case SAPP_KEYCODE_PAGE_DOWN: return ImGuiKey_PageDown; + case SAPP_KEYCODE_HOME: return ImGuiKey_Home; + case SAPP_KEYCODE_END: return ImGuiKey_End; + case SAPP_KEYCODE_CAPS_LOCK: return ImGuiKey_CapsLock; + case SAPP_KEYCODE_SCROLL_LOCK: return ImGuiKey_ScrollLock; + case SAPP_KEYCODE_NUM_LOCK: return ImGuiKey_NumLock; + case SAPP_KEYCODE_PRINT_SCREEN: return ImGuiKey_PrintScreen; + case SAPP_KEYCODE_PAUSE: return ImGuiKey_Pause; + case SAPP_KEYCODE_F1: return ImGuiKey_F1; + case SAPP_KEYCODE_F2: return ImGuiKey_F2; + case SAPP_KEYCODE_F3: return ImGuiKey_F3; + case SAPP_KEYCODE_F4: return ImGuiKey_F4; + case SAPP_KEYCODE_F5: return ImGuiKey_F5; + case SAPP_KEYCODE_F6: return ImGuiKey_F6; + case SAPP_KEYCODE_F7: return ImGuiKey_F7; + case SAPP_KEYCODE_F8: return ImGuiKey_F8; + case SAPP_KEYCODE_F9: return ImGuiKey_F9; + case SAPP_KEYCODE_F10: return ImGuiKey_F10; + case SAPP_KEYCODE_F11: return ImGuiKey_F11; + case SAPP_KEYCODE_F12: return ImGuiKey_F12; + case SAPP_KEYCODE_KP_0: return ImGuiKey_Keypad0; + case SAPP_KEYCODE_KP_1: return ImGuiKey_Keypad1; + case SAPP_KEYCODE_KP_2: return ImGuiKey_Keypad2; + case SAPP_KEYCODE_KP_3: return ImGuiKey_Keypad3; + case SAPP_KEYCODE_KP_4: return ImGuiKey_Keypad4; + case SAPP_KEYCODE_KP_5: return ImGuiKey_Keypad5; + case SAPP_KEYCODE_KP_6: return ImGuiKey_Keypad6; + case SAPP_KEYCODE_KP_7: return ImGuiKey_Keypad7; + case SAPP_KEYCODE_KP_8: return ImGuiKey_Keypad8; + case SAPP_KEYCODE_KP_9: return ImGuiKey_Keypad9; + case SAPP_KEYCODE_KP_DECIMAL: return ImGuiKey_KeypadDecimal; + case SAPP_KEYCODE_KP_DIVIDE: return ImGuiKey_KeypadDivide; + case SAPP_KEYCODE_KP_MULTIPLY: return ImGuiKey_KeypadMultiply; + case SAPP_KEYCODE_KP_SUBTRACT: return ImGuiKey_KeypadSubtract; + case SAPP_KEYCODE_KP_ADD: return ImGuiKey_KeypadAdd; + case SAPP_KEYCODE_KP_ENTER: return ImGuiKey_KeypadEnter; + case SAPP_KEYCODE_KP_EQUAL: return ImGuiKey_KeypadEqual; + case SAPP_KEYCODE_LEFT_SHIFT: return ImGuiKey_LeftShift; + case SAPP_KEYCODE_LEFT_CONTROL: return ImGuiKey_LeftCtrl; + case SAPP_KEYCODE_LEFT_ALT: return ImGuiKey_LeftAlt; + case SAPP_KEYCODE_LEFT_SUPER: return ImGuiKey_LeftSuper; + case SAPP_KEYCODE_RIGHT_SHIFT: return ImGuiKey_RightShift; + case SAPP_KEYCODE_RIGHT_CONTROL:return ImGuiKey_RightCtrl; + case SAPP_KEYCODE_RIGHT_ALT: return ImGuiKey_RightAlt; + case SAPP_KEYCODE_RIGHT_SUPER: return ImGuiKey_RightSuper; + case SAPP_KEYCODE_MENU: return ImGuiKey_Menu; + default: return ImGuiKey_None; + } +} + +_SOKOL_PRIVATE void _simgui_add_sapp_key_event(ImGuiIO* io, sapp_keycode sapp_key, bool down) { + const ImGuiKey imgui_key = _simgui_map_keycode(sapp_key); + #if defined(__cplusplus) + io->AddKeyEvent(imgui_key, down); + #else + ImGuiIO_AddKeyEvent(io, imgui_key, down); + #endif +} + +_SOKOL_PRIVATE void _simgui_add_imgui_key_event(ImGuiIO* io, ImGuiKey imgui_key, bool down) { + #if defined(__cplusplus) + io->AddKeyEvent(imgui_key, down); + #else + ImGuiIO_AddKeyEvent(io, imgui_key, down); + #endif +} + +_SOKOL_PRIVATE void _simgui_update_modifiers(ImGuiIO* io, uint32_t mods) { + _simgui_add_imgui_key_event(io, ImGuiMod_Ctrl, (mods & SAPP_MODIFIER_CTRL) != 0); + _simgui_add_imgui_key_event(io, ImGuiMod_Shift, (mods & SAPP_MODIFIER_SHIFT) != 0); + _simgui_add_imgui_key_event(io, ImGuiMod_Alt, (mods & SAPP_MODIFIER_ALT) != 0); + _simgui_add_imgui_key_event(io, ImGuiMod_Super, (mods & SAPP_MODIFIER_SUPER) != 0); +} + +// returns Ctrl or Super, depending on platform +_SOKOL_PRIVATE ImGuiKey _simgui_copypaste_modifier(void) { + return _simgui.is_osx ? ImGuiMod_Super : ImGuiMod_Ctrl; +} + +SOKOL_API_IMPL int simgui_map_keycode(sapp_keycode keycode) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + return (int)_simgui_map_keycode(keycode); +} + +SOKOL_API_IMPL bool simgui_handle_event(const sapp_event* ev) { + SOKOL_ASSERT(_SIMGUI_INIT_COOKIE == _simgui.init_cookie); + const float dpi_scale = _simgui.cur_dpi_scale; + #if defined(__cplusplus) + ImGuiIO* io = &ImGui::GetIO(); + #else + ImGuiIO* io = igGetIO(); + #endif + switch (ev->type) { + case SAPP_EVENTTYPE_FOCUSED: + simgui_add_focus_event(true); + break; + case SAPP_EVENTTYPE_UNFOCUSED: + simgui_add_focus_event(false); + break; + case SAPP_EVENTTYPE_MOUSE_DOWN: + simgui_add_mouse_pos_event(ev->mouse_x / dpi_scale, ev->mouse_y / dpi_scale); + simgui_add_mouse_button_event((int)ev->mouse_button, true); + _simgui_update_modifiers(io, ev->modifiers); + break; + case SAPP_EVENTTYPE_MOUSE_UP: + simgui_add_mouse_pos_event(ev->mouse_x / dpi_scale, ev->mouse_y / dpi_scale); + simgui_add_mouse_button_event((int)ev->mouse_button, false); + _simgui_update_modifiers(io, ev->modifiers); + break; + case SAPP_EVENTTYPE_MOUSE_MOVE: + simgui_add_mouse_pos_event(ev->mouse_x / dpi_scale, ev->mouse_y / dpi_scale); + break; + case SAPP_EVENTTYPE_MOUSE_ENTER: + case SAPP_EVENTTYPE_MOUSE_LEAVE: + // FIXME: since the sokol_app.h emscripten backend doesn't support + // mouse capture, mouse buttons must be released when the mouse leaves the + // browser window, so that they don't "stick" when released outside the window. + // A cleaner solution would be a new sokol_app.h function to query + // "platform behaviour flags". + #if defined(__EMSCRIPTEN__) + for (int i = 0; i < SAPP_MAX_MOUSEBUTTONS; i++) { + simgui_add_mouse_button_event(i, false); + } + #endif + break; + case SAPP_EVENTTYPE_MOUSE_SCROLL: + simgui_add_mouse_wheel_event(ev->scroll_x, ev->scroll_y); + break; + case SAPP_EVENTTYPE_TOUCHES_BEGAN: + simgui_add_touch_pos_event(ev->touches[0].pos_x / dpi_scale, ev->touches[0].pos_y / dpi_scale); + simgui_add_touch_button_event(0, true); + break; + case SAPP_EVENTTYPE_TOUCHES_MOVED: + simgui_add_touch_pos_event(ev->touches[0].pos_x / dpi_scale, ev->touches[0].pos_y / dpi_scale); + break; + case SAPP_EVENTTYPE_TOUCHES_ENDED: + simgui_add_touch_pos_event(ev->touches[0].pos_x / dpi_scale, ev->touches[0].pos_y / dpi_scale); + simgui_add_touch_button_event(0, false); + break; + case SAPP_EVENTTYPE_TOUCHES_CANCELLED: + simgui_add_touch_button_event(0, false); + break; + case SAPP_EVENTTYPE_KEY_DOWN: + _simgui_update_modifiers(io, ev->modifiers); + // intercept Ctrl-V, this is handled via EVENTTYPE_CLIPBOARD_PASTED + if (!_simgui.desc.disable_paste_override) { + if (_simgui_is_ctrl(ev->modifiers) && (ev->key_code == SAPP_KEYCODE_V)) { + break; + } + } + // on web platform, don't forward Ctrl-X, Ctrl-V to the browser + if (_simgui_is_ctrl(ev->modifiers) && (ev->key_code == SAPP_KEYCODE_X)) { + sapp_consume_event(); + } + if (_simgui_is_ctrl(ev->modifiers) && (ev->key_code == SAPP_KEYCODE_C)) { + sapp_consume_event(); + } + // it's ok to add ImGuiKey_None key events + _simgui_add_sapp_key_event(io, ev->key_code, true); + break; + case SAPP_EVENTTYPE_KEY_UP: + _simgui_update_modifiers(io, ev->modifiers); + // intercept Ctrl-V, this is handled via EVENTTYPE_CLIPBOARD_PASTED + if (_simgui_is_ctrl(ev->modifiers) && (ev->key_code == SAPP_KEYCODE_V)) { + break; + } + // on web platform, don't forward Ctrl-X, Ctrl-V to the browser + if (_simgui_is_ctrl(ev->modifiers) && (ev->key_code == SAPP_KEYCODE_X)) { + sapp_consume_event(); + } + if (_simgui_is_ctrl(ev->modifiers) && (ev->key_code == SAPP_KEYCODE_C)) { + sapp_consume_event(); + } + // it's ok to add ImGuiKey_None key events + _simgui_add_sapp_key_event(io, ev->key_code, false); + break; + case SAPP_EVENTTYPE_CHAR: + /* on some platforms, special keys may be reported as + characters, which may confuse some ImGui widgets, + drop those, also don't forward characters if some + modifiers have been pressed + */ + _simgui_update_modifiers(io, ev->modifiers); + if ((ev->char_code >= 32) && + (ev->char_code != 127) && + (0 == (ev->modifiers & (SAPP_MODIFIER_ALT|SAPP_MODIFIER_CTRL|SAPP_MODIFIER_SUPER)))) + { + simgui_add_input_character(ev->char_code); + } + break; + case SAPP_EVENTTYPE_CLIPBOARD_PASTED: + // simulate a Ctrl-V key down/up + if (!_simgui.desc.disable_paste_override) { + _simgui_add_imgui_key_event(io, _simgui_copypaste_modifier(), true); + _simgui_add_imgui_key_event(io, ImGuiKey_V, true); + _simgui_add_imgui_key_event(io, ImGuiKey_V, false); + _simgui_add_imgui_key_event(io, _simgui_copypaste_modifier(), false); + } + break; + default: + break; + } + return io->WantCaptureKeyboard || io->WantCaptureMouse; +} +#endif // SOKOL_IMGUI_NO_SOKOL_APP + +#endif // SOKOL_IMPL \ No newline at end of file diff --git a/src/sokol/fetch.d b/src/sokol/fetch.d new file mode 100644 index 0000000..07b1184 --- /dev/null +++ b/src/sokol/fetch.d @@ -0,0 +1,140 @@ +// machine generated, do not edit + +module sokol.fetch; + +enum LogItem { + Ok, + Malloc_failed, + File_path_utf8_decoding_failed, + Send_queue_full, + Request_channel_index_too_big, + Request_path_is_null, + Request_path_too_long, + Request_callback_missing, + Request_chunk_size_greater_buffer_size, + Request_userdata_ptr_is_set_but_userdata_size_is_null, + Request_userdata_ptr_is_null_but_userdata_size_is_not, + Request_userdata_size_too_big, + Clamping_num_channels_to_max_channels, + Request_pool_exhausted, +} +extern(C) +struct Logger { + extern(C) void function(const(char)*, uint, uint, const(char)*, uint, const(char)*, void*) func = null; + void* user_data = null; +} +extern(C) +struct Range { + const(void)* ptr = null; + size_t size = 0; +} +extern(C) +struct Allocator { + extern(C) void* function(size_t, void*) alloc_fn = null; + extern(C) void function(void*, void*) free_fn = null; + void* user_data = null; +} +extern(C) +struct Desc { + uint max_requests = 0; + uint num_channels = 0; + uint num_lanes = 0; + Allocator allocator; + Logger logger; +} +extern(C) +struct Handle { + uint id = 0; +} +enum Error { + No_error, + File_not_found, + No_buffer, + Buffer_too_small, + Unexpected_eof, + Invalid_http_status, + Cancelled, +} +extern(C) +struct Response { + Handle handle; + bool dispatched = false; + bool fetched = false; + bool paused = false; + bool finished = false; + bool failed = false; + bool cancelled = false; + Error error_code; + uint channel = 0; + uint lane = 0; + const(char)* path = null; + void* user_data = null; + uint data_offset = 0; + Range data; + Range buffer; +} +extern(C) +struct Request { + uint channel = 0; + const(char)* path = null; + extern(C) void function(const Response *) callback = null; + uint chunk_size = 0; + Range buffer; + Range user_data; +} +extern(C) void sfetch_setup(const Desc *) @system @nogc nothrow; +void setup(scope ref Desc desc) @trusted @nogc nothrow { + sfetch_setup(&desc); +} +extern(C) void sfetch_shutdown() @system @nogc nothrow; +void shutdown() @trusted @nogc nothrow { + sfetch_shutdown(); +} +extern(C) bool sfetch_valid() @system @nogc nothrow; +bool valid() @trusted @nogc nothrow { + return sfetch_valid(); +} +extern(C) Desc sfetch_desc() @system @nogc nothrow; +Desc desc() @trusted @nogc nothrow { + return sfetch_desc(); +} +extern(C) int sfetch_max_userdata_bytes() @system @nogc nothrow; +int maxUserdataBytes() @trusted @nogc nothrow { + return sfetch_max_userdata_bytes(); +} +extern(C) int sfetch_max_path() @system @nogc nothrow; +int maxPath() @trusted @nogc nothrow { + return sfetch_max_path(); +} +extern(C) Handle sfetch_send(const Request *) @system @nogc nothrow; +Handle send(scope ref Request request) @trusted @nogc nothrow { + return sfetch_send(&request); +} +extern(C) bool sfetch_handle_valid(Handle) @system @nogc nothrow; +bool handleValid(Handle h) @trusted @nogc nothrow { + return sfetch_handle_valid(h); +} +extern(C) void sfetch_dowork() @system @nogc nothrow; +void dowork() @trusted @nogc nothrow { + sfetch_dowork(); +} +extern(C) void sfetch_bind_buffer(Handle, Range) @system @nogc nothrow; +void bindBuffer(Handle h, Range buffer) @trusted @nogc nothrow { + sfetch_bind_buffer(h, buffer); +} +extern(C) void* sfetch_unbind_buffer(Handle) @system @nogc nothrow; +scope void* unbindBuffer(Handle h) @trusted @nogc nothrow { + return sfetch_unbind_buffer(h); +} +extern(C) void sfetch_cancel(Handle) @system @nogc nothrow; +void cancel(Handle h) @trusted @nogc nothrow { + sfetch_cancel(h); +} +extern(C) void sfetch_pause(Handle) @system @nogc nothrow; +void pause(Handle h) @trusted @nogc nothrow { + sfetch_pause(h); +} +extern(C) void sfetch_continue(Handle) @system @nogc nothrow; +void continueFetching(Handle h) @trusted @nogc nothrow { + sfetch_continue(h); +} diff --git a/src/sokol/imgui.d b/src/sokol/imgui.d new file mode 100644 index 0000000..5420771 --- /dev/null +++ b/src/sokol/imgui.d @@ -0,0 +1,148 @@ +// machine generated, do not edit + +module sokol.imgui; +import sg = sokol.gfx; +import sapp = sokol.app; + +enum invalid_id = 0; +extern(C) +struct Image { + uint id = 0; +} +extern(C) +struct ImageDesc { + sg.Image image; + sg.Sampler sampler; +} +enum LogItem { + Ok, + Malloc_failed, + Image_pool_exhausted, +} +extern(C) +struct Allocator { + extern(C) void* function(size_t, void*) alloc_fn = null; + extern(C) void function(void*, void*) free_fn = null; + void* user_data = null; +} +extern(C) +struct Logger { + extern(C) void function(const(char)*, uint, uint, const(char)*, uint, const(char)*, void*) func = null; + void* user_data = null; +} +extern(C) +struct Desc { + int max_vertices = 0; + int image_pool_size = 0; + sg.PixelFormat color_format; + sg.PixelFormat depth_format; + int sample_count = 0; + const(char)* ini_filename = null; + bool no_default_font = false; + bool disable_paste_override = false; + bool disable_set_mouse_cursor = false; + bool disable_windows_resize_from_edges = false; + bool write_alpha_channel = false; + Allocator allocator; + Logger logger; +} +extern(C) +struct FrameDesc { + int width = 0; + int height = 0; + double delta_time = 0.0; + float dpi_scale = 0.0f; +} +extern(C) +struct FontTexDesc { + sg.Filter min_filter; + sg.Filter mag_filter; +} +extern(C) void simgui_setup(const Desc *) @system @nogc nothrow; +void setup(scope ref Desc desc) @trusted @nogc nothrow { + simgui_setup(&desc); +} +extern(C) void simgui_new_frame(const FrameDesc *) @system @nogc nothrow; +void newFrame(scope ref FrameDesc desc) @trusted @nogc nothrow { + simgui_new_frame(&desc); +} +extern(C) void simgui_render() @system @nogc nothrow; +void render() @trusted @nogc nothrow { + simgui_render(); +} +extern(C) Image simgui_make_image(const ImageDesc *) @system @nogc nothrow; +Image makeImage(scope ref ImageDesc desc) @trusted @nogc nothrow { + return simgui_make_image(&desc); +} +extern(C) void simgui_destroy_image(Image) @system @nogc nothrow; +void destroyImage(Image img) @trusted @nogc nothrow { + simgui_destroy_image(img); +} +extern(C) ImageDesc simgui_query_image_desc(Image) @system @nogc nothrow; +ImageDesc queryImageDesc(Image img) @trusted @nogc nothrow { + return simgui_query_image_desc(img); +} +extern(C) void* simgui_imtextureid(Image) @system @nogc nothrow; +scope void* imtextureid(Image img) @trusted @nogc nothrow { + return simgui_imtextureid(img); +} +extern(C) Image simgui_image_from_imtextureid(void*) @system @nogc nothrow; +Image imageFromImtextureid(scope void* im_texture_id) @trusted @nogc nothrow { + return simgui_image_from_imtextureid(im_texture_id); +} +extern(C) void simgui_add_focus_event(bool) @system @nogc nothrow; +void addFocusEvent(bool focus) @trusted @nogc nothrow { + simgui_add_focus_event(focus); +} +extern(C) void simgui_add_mouse_pos_event(float, float) @system @nogc nothrow; +void addMousePosEvent(float x, float y) @trusted @nogc nothrow { + simgui_add_mouse_pos_event(x, y); +} +extern(C) void simgui_add_touch_pos_event(float, float) @system @nogc nothrow; +void addTouchPosEvent(float x, float y) @trusted @nogc nothrow { + simgui_add_touch_pos_event(x, y); +} +extern(C) void simgui_add_mouse_button_event(int, bool) @system @nogc nothrow; +void addMouseButtonEvent(int mouse_button, bool down) @trusted @nogc nothrow { + simgui_add_mouse_button_event(mouse_button, down); +} +extern(C) void simgui_add_mouse_wheel_event(float, float) @system @nogc nothrow; +void addMouseWheelEvent(float wheel_x, float wheel_y) @trusted @nogc nothrow { + simgui_add_mouse_wheel_event(wheel_x, wheel_y); +} +extern(C) void simgui_add_key_event(int, bool) @system @nogc nothrow; +void addKeyEvent(int imgui_key, bool down) @trusted @nogc nothrow { + simgui_add_key_event(imgui_key, down); +} +extern(C) void simgui_add_input_character(uint) @system @nogc nothrow; +void addInputCharacter(uint c) @trusted @nogc nothrow { + simgui_add_input_character(c); +} +extern(C) void simgui_add_input_characters_utf8(const(char)*) @system @nogc nothrow; +void addInputCharactersUtf8(scope const(char)* c) @trusted @nogc nothrow { + simgui_add_input_characters_utf8(c); +} +extern(C) void simgui_add_touch_button_event(int, bool) @system @nogc nothrow; +void addTouchButtonEvent(int mouse_button, bool down) @trusted @nogc nothrow { + simgui_add_touch_button_event(mouse_button, down); +} +extern(C) bool simgui_handle_event(const sapp.Event *) @system @nogc nothrow; +bool handleEvent(scope ref sapp.Event ev) @trusted @nogc nothrow { + return simgui_handle_event(&ev); +} +extern(C) int simgui_map_keycode(sapp.Keycode) @system @nogc nothrow; +int mapKeycode(sapp.Keycode keycode) @trusted @nogc nothrow { + return simgui_map_keycode(keycode); +} +extern(C) void simgui_shutdown() @system @nogc nothrow; +void shutdown() @trusted @nogc nothrow { + simgui_shutdown(); +} +extern(C) void simgui_create_fonts_texture(const FontTexDesc *) @system @nogc nothrow; +void createFontsTexture(scope ref FontTexDesc desc) @trusted @nogc nothrow { + simgui_create_fonts_texture(&desc); +} +extern(C) void simgui_destroy_fonts_texture() @system @nogc nothrow; +void destroyFontsTexture() @trusted @nogc nothrow { + simgui_destroy_fonts_texture(); +}