├── .github └── workflows │ └── main.yml ├── .gitignore ├── .gitmodules ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── build.rs ├── compile-shaders.sh ├── examples ├── audio │ └── audio.rs ├── blend │ ├── blend.rs │ ├── math.rs │ ├── shader.glsl │ └── shader.rs ├── clear │ └── clear.rs ├── cube │ ├── cube.rs │ ├── math.rs │ ├── shader.glsl │ └── shader.rs ├── debugtext │ └── debugtext.rs ├── instancing-compute │ ├── instancing-compute.rs │ ├── math.rs │ ├── shader.glsl │ └── shader.rs ├── instancing │ ├── instancing.rs │ ├── math.rs │ ├── shader.glsl │ └── shader.rs ├── mrt │ ├── math.rs │ ├── mrt.rs │ ├── shader.glsl │ └── shader.rs ├── sgl-context │ └── sgl-context.rs ├── sgl-points │ └── sgl-points.rs ├── texcube │ ├── math.rs │ ├── shader.glsl │ ├── shader.rs │ └── texcube.rs ├── userdata │ └── userdata.rs └── vertexpull │ ├── math.rs │ ├── shader.glsl │ ├── shader.rs │ └── vertexpull.rs ├── fmt.sh ├── rustfmt.toml ├── src ├── app.rs ├── audio.rs ├── debugtext.rs ├── gfx.rs ├── gl.rs ├── glue.rs ├── lib.rs ├── log.rs ├── shape.rs ├── sokol │ └── c │ │ ├── sokol_app.c │ │ ├── sokol_app.h │ │ ├── sokol_audio.c │ │ ├── sokol_audio.h │ │ ├── sokol_debugtext.c │ │ ├── sokol_debugtext.h │ │ ├── sokol_defines.h │ │ ├── sokol_gfx.c │ │ ├── sokol_gfx.h │ │ ├── sokol_gl.c │ │ ├── sokol_gl.h │ │ ├── sokol_glue.c │ │ ├── sokol_glue.h │ │ ├── sokol_log.c │ │ ├── sokol_log.h │ │ ├── sokol_shape.c │ │ ├── sokol_shape.h │ │ ├── sokol_time.c │ │ └── sokol_time.h └── time.rs └── test.html /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | env: 8 | CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse 9 | strategy: 10 | matrix: 11 | os: [ubuntu-latest, macos-latest, windows-latest] 12 | runs-on: ${{matrix.os}} 13 | steps: 14 | - uses: actions/checkout@main 15 | - name: setup-rust 16 | uses: dtolnay/rust-toolchain@master 17 | with: 18 | toolchain: stable 19 | - name: prepare-linux 20 | if: runner.os == 'Linux' 21 | run: | 22 | sudo apt-get update 23 | sudo apt-get install libglu1-mesa-dev mesa-common-dev xorg-dev libasound-dev 24 | - name: Run cargo build --all-targets 25 | run: cargo build --examples --verbose 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | NUL 3 | target/ 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/floooh/sokol-rust/67dee28d29e37a700e591994ab95720ab6ef272a/.gitmodules -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "cc" 7 | version = "1.0.83" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" 10 | dependencies = [ 11 | "jobserver", 12 | "libc", 13 | ] 14 | 15 | [[package]] 16 | name = "jobserver" 17 | version = "0.1.28" 18 | source = "registry+https://github.com/rust-lang/crates.io-index" 19 | checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" 20 | dependencies = [ 21 | "libc", 22 | ] 23 | 24 | [[package]] 25 | name = "libc" 26 | version = "0.2.153" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" 29 | 30 | [[package]] 31 | name = "sokol" 32 | version = "0.1.0" 33 | dependencies = [ 34 | "cc", 35 | ] 36 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sokol" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Erik W. Gren "] 6 | build = "build.rs" 7 | license = "zlib/libpng" 8 | 9 | [features] 10 | default = [] 11 | imgui = [] 12 | 13 | [build-dependencies] 14 | cc = { version = "=1.0.83", features = ["parallel"] } 15 | 16 | [[example]] 17 | name = "clear" 18 | path = "examples/clear/clear.rs" 19 | 20 | [[example]] 21 | name = "userdata" 22 | path = "examples/userdata/userdata.rs" 23 | 24 | [[example]] 25 | name = "cube" 26 | path = "examples/cube/cube.rs" 27 | 28 | [[example]] 29 | name = "mrt" 30 | path = "examples/mrt/mrt.rs" 31 | 32 | [[example]] 33 | name = "debugtext" 34 | path = "examples/debugtext/debugtext.rs" 35 | 36 | [[example]] 37 | name = "sgl-context" 38 | path = "examples/sgl-context/sgl-context.rs" 39 | 40 | [[example]] 41 | name = "sgl-points" 42 | path = "examples/sgl-points/sgl-points.rs" 43 | 44 | [[example]] 45 | name = "blend" 46 | path = "examples/blend/blend.rs" 47 | 48 | [[example]] 49 | name = "audio" 50 | path = "examples/audio/audio.rs" 51 | 52 | [[example]] 53 | name = "instancing" 54 | path = "examples/instancing/instancing.rs" 55 | 56 | [[example]] 57 | name = "instancing-compute" 58 | path = "examples/instancing-compute/instancing-compute.rs" 59 | 60 | [[example]] 61 | name = "texcube" 62 | path = "examples/texcube/texcube.rs" 63 | 64 | [[example]] 65 | name = "vertexpull" 66 | path = "examples/vertexpull/vertexpull.rs" 67 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | zlib/libpng license 2 | 3 | Copyright (c) 2023 Erik Wilhelm Gren 4 | 5 | This software is provided 'as-is', without any express or implied warranty. 6 | In no event will the authors be held liable for any damages arising from the 7 | use of this software. 8 | 9 | Permission is granted to anyone to use this software for any purpose, 10 | including commercial applications, and to alter it and redistribute it 11 | freely, subject to the following restrictions: 12 | 13 | 1. The origin of this software must not be misrepresented; you must not 14 | claim that you wrote the original software. If you use this software in a 15 | product, an acknowledgment in the product documentation would be 16 | appreciated but is not required. 17 | 18 | 2. Altered source versions must be plainly marked as such, and must not 19 | be misrepresented as being the original software. 20 | 21 | 3. This notice may not be removed or altered from any source 22 | distribution. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Rust](https://github.com/floooh/sokol-rust/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-rust/actions/workflows/main.yml) 2 | 3 | > [!WARNING] always delete the `target/` directory before after updating the bindings, 4 | > somehow the cargo C build step can't figure out that the C source files have changed. 5 | > Not deleting target will result in weird runtime errors because the memory layout of 6 | > Rust binding structs doesn't match their C counterparts. 7 | 8 | ## sokol-rust 9 | Auto-generated Rust bindings for the [sokol headers](https://github.com/floooh/sokol). 10 | 11 | Add `sokol-rust` as a dependency to your `Cargo.toml` as such: 12 | ```toml 13 | sokol = { version="*", git="https://github.com/floooh/sokol-rust.git" } 14 | ``` 15 | 16 | Check out the `examples/` folder for more examples. Here is `examples/clear/clear.rs`: 17 | ```rust 18 | use sokol::{app as sapp, gfx as sg, glue as sglue}; 19 | use std::ffi; 20 | 21 | struct State { 22 | pass_action: sg::PassAction, 23 | } 24 | 25 | extern "C" fn init(user_data: *mut ffi::c_void) { 26 | let state = unsafe { &mut *(user_data as *mut State) }; 27 | 28 | sg::setup(&sg::Desc { 29 | environment: sglue::environment(), 30 | logger: sg::Logger { func: Some(sokol::log::slog_func), ..Default::default() }, 31 | ..Default::default() 32 | }); 33 | 34 | state.pass_action.colors[0] = sg::ColorAttachmentAction { 35 | load_action: sg::LoadAction::Clear, 36 | clear_value: sg::Color { r: 1.0, g: 0.0, b: 0.0, a: 1.0 }, 37 | ..Default::default() 38 | }; 39 | 40 | let backend = sg::query_backend(); 41 | match &backend { 42 | sg::Backend::Glcore | sg::Backend::Gles3 => { 43 | println!("Using GL Backend!"); 44 | println!("Specifically the {:?} backend!", backend); 45 | }, 46 | 47 | sg::Backend::D3d11 => { 48 | println!("Using D3d11 Backend!"); 49 | }, 50 | 51 | sg::Backend::MetalIos | sg::Backend::MetalMacos | sg::Backend::MetalSimulator => { 52 | println!("Using Metal Backend!"); 53 | println!("Specifically the {:?} backend!", backend); 54 | }, 55 | 56 | sg::Backend::Wgpu => { 57 | println!("Using Wgpu Backend!"); 58 | }, 59 | 60 | sg::Backend::Dummy => { 61 | println!("Using Dummy Backend!"); 62 | }, 63 | } 64 | } 65 | 66 | extern "C" fn frame(user_data: *mut ffi::c_void) { 67 | let state = unsafe { &mut *(user_data as *mut State) }; 68 | 69 | let g = state.pass_action.colors[0].clear_value.g + 0.01; 70 | state.pass_action.colors[0].clear_value.g = if g > 1.0 { 0.0 } else { g }; 71 | 72 | sg::begin_pass(&sg::Pass { 73 | action: state.pass_action, 74 | swapchain: sglue::swapchain(), 75 | ..Default::default() 76 | }); 77 | sg::end_pass(); 78 | sg::commit(); 79 | } 80 | 81 | extern "C" fn cleanup(user_data: *mut ffi::c_void) { 82 | sg::shutdown(); 83 | 84 | let _ = unsafe { Box::from_raw(user_data as *mut State) }; 85 | } 86 | 87 | fn main() { 88 | let state = Box::new(State { pass_action: sg::PassAction::new() }); 89 | 90 | let user_data = Box::into_raw(state) as *mut ffi::c_void; 91 | 92 | sapp::run(&sapp::Desc { 93 | init_userdata_cb: Some(init), 94 | frame_userdata_cb: Some(frame), 95 | cleanup_userdata_cb: Some(cleanup), 96 | user_data, 97 | window_title: c"clear.rs".as_ptr(), 98 | width: 800, 99 | height: 600, 100 | sample_count: 4, 101 | logger: sapp::Logger { func: Some(sokol::log::slog_func), ..Default::default() }, 102 | icon: sapp::IconDesc { sokol_default: true, ..Default::default() }, 103 | ..Default::default() 104 | }); 105 | } 106 | ``` 107 | 108 | ## Dependencies 109 | The Rust compiler and cargo can be installed using [rustup](https://rustup.rs/) 110 | 111 | The same dependencies apply as with sokol normally for each platform. 112 | 113 | ## Building with cargo 114 | Cargo will compile and link the sokol headers automatically during compilation thanks to the buildscript `build.rs` 115 | 116 | ## Examples 117 | Not all examples have been translated to Rust yet, but you can check the ones that have been in the `examples` directory. 118 | 119 | Rust 1.77 or later is required to build the examples. 120 | 121 | You can compile all examples using the following command: 122 | ```console 123 | cargo build --all-targets 124 | ``` 125 | 126 | Build and run individual examples as such: 127 | ```console 128 | cargo run --example clear 129 | cargo run --example cube 130 | cargo run --example mrt 131 | cargo run --example debugtext 132 | cargo run --example sgl-context 133 | cargo run --example sgl-points 134 | cargo run --example blend 135 | cargo run --example audio 136 | cargo run --example instancing 137 | cargo run --example userdata 138 | cargo run --example vertexpull 139 | ``` 140 | 141 | >NOTE: imgui support has been removed for now, the required cimgui submodule dependency caused trouble with 142 | > Github Actions. We'll need to solve this some other way in the future, and in a way that works for all 143 | > language bindings. 144 | 145 | ## Wasm/Emscripten 146 | To compile for wasm, you will need the emcc compiler which you can get at https://github.com/emscripten-core/emsdk 147 | 148 | You can then compile the examples like such: 149 | 150 | ```console 151 | cargo build --target wasm32-unknown-emscripten --example texcube 152 | ``` 153 | 154 | You will then need to create an html page which imports the game. Checkout `test.html` for how this can be done. It is specifically setup to 155 | run the texcube example in debug mode. 156 | 157 | It can be served with `basic-http-server`: 158 | ```console 159 | cargo install basic-http-server 160 | basic-http-server . 161 | # .. now go to localhost:4000/test.html 162 | ``` 163 | 164 | ## Shaders 165 | Checkout [sokol-tools](https://github.com/floooh/sokol-tools) for a sokol shader pipeline! It supports these Rust bindings and all shaders in the examples folder 166 | here have been compiled using it with `-f sokol_rust`! 167 | 168 | ## License and attributions 169 | This code is released under the zlib license (see `LICENSE` for info). Parts of `gen_rust.py` and `build.rs` have been copied and modified from 170 | the zig-bindings (https://github.com/floooh/sokol-odin/) and odin-bindings (https://github.com/floooh/sokol-odin/) for sokol. 171 | 172 | The sokol headers are created by Andre Weissflog (floooh) and sokol is released under its own license here: https://github.com/floooh/sokol/blob/master/LICENSE 173 | 174 | cimgui https://github.com/cimgui/cimgui is released under the MIT license 175 | 176 | The old Rust bindings by Daniel Ludwig (code-disaster) https://github.com/code-disaster/sokol-rs were used to figure out the `build.rs` script and it was released under the MIT license. 177 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | #[derive(Clone, Copy, PartialEq, Eq)] 4 | pub enum SokolBackend { 5 | D3d11, 6 | Metal, 7 | Gl, 8 | Gles2, 9 | Gles3, 10 | Wgpu, 11 | } 12 | 13 | #[derive(Clone, Copy, PartialEq, Eq)] 14 | enum BuildTarget { 15 | Linux, 16 | Windows, 17 | Macos, 18 | WasmEmscripten, 19 | } 20 | 21 | #[derive(Clone)] 22 | struct CompilationConfig { 23 | is_msvc: bool, 24 | desired_backend: String, 25 | debug_info_requested: bool, 26 | force_egl: bool, 27 | is_debug_build: bool, 28 | wayland_desired: bool, 29 | build_target: BuildTarget, 30 | } 31 | 32 | fn get_compilation_config(tool: &cc::Tool) -> CompilationConfig { 33 | let desired_backend = std::env::var("SOKOL_BACKEND").ok().unwrap_or("AUTO".to_owned()); 34 | let debug_info_requested = std::env::var("SOKOL_DEBUG").ok().is_some(); 35 | let is_msvc = tool.is_like_msvc(); 36 | let wayland_desired = std::env::var("SOKOL_WAYLAND").is_ok(); 37 | let force_egl = std::env::var("SOKOL_FORCE_EGL").is_ok(); 38 | let is_debug_build = cfg!(debug_assertions); 39 | 40 | /* 41 | HACK: #[cfg(target_arch)] doesnt give the build target in build.rs, 42 | only the host target. So we need to read the TARGET env variable to 43 | be able to cross compile. This is unacceptable. 44 | */ 45 | let target_var = std::env::var("TARGET").unwrap(); 46 | let split = target_var.split('-').collect::>(); 47 | let is_wasm_hack = split.contains(&"wasm32") || split.contains(&"emscripten"); 48 | 49 | let build_target = if is_wasm_hack { 50 | BuildTarget::WasmEmscripten 51 | } else if cfg!(target_os = "windows") && is_msvc { 52 | BuildTarget::Windows 53 | } else if cfg!(target_os = "macos") { 54 | BuildTarget::Macos 55 | } else if cfg!(target_os = "linux") { 56 | BuildTarget::Linux 57 | } else { 58 | todo!("Unsupported build target"); 59 | }; 60 | 61 | CompilationConfig { 62 | is_msvc, 63 | desired_backend, 64 | wayland_desired, 65 | force_egl, 66 | is_debug_build, 67 | debug_info_requested, 68 | build_target, 69 | } 70 | } 71 | 72 | fn select_sokol_backend(build: &mut cc::Build, config: &CompilationConfig) -> SokolBackend { 73 | println!("cargo:rerun-if-env-changed=SOKOL_BACKEND"); 74 | println!("cargo:rerun-if-env-changed=SOKOL_WAYLAND"); 75 | println!("cargo:rerun-if-env-changed=SOKOL_FORCE_EGL"); 76 | println!("cargo:rerun-if-env-changed=SOKOL_DEBUG"); 77 | 78 | let backend = match &config.desired_backend[..] { 79 | "AUTO" => match config.build_target { 80 | BuildTarget::Linux => SokolBackend::Gl, 81 | BuildTarget::Windows => SokolBackend::D3d11, 82 | BuildTarget::Macos => SokolBackend::Metal, 83 | BuildTarget::WasmEmscripten => SokolBackend::Gles3, 84 | }, 85 | 86 | "D3D11" => SokolBackend::D3d11, 87 | "METAL" => SokolBackend::Metal, 88 | "GL" => SokolBackend::Gl, 89 | "GLES2" => SokolBackend::Gles2, 90 | "GLES3" => SokolBackend::Gles3, 91 | "WGPU" => SokolBackend::Wgpu, 92 | 93 | _ => panic!("Unknown SOKOL_BACKEND: {}", &config.desired_backend), 94 | }; 95 | 96 | match backend { 97 | SokolBackend::D3d11 => { 98 | build.define("SOKOL_D3D11", None); 99 | }, 100 | SokolBackend::Metal => { 101 | build.define("SOKOL_METAL", None); 102 | }, 103 | SokolBackend::Gles3 => { 104 | build.define("SOKOL_GLES3", None); 105 | }, 106 | SokolBackend::Gles2 => { 107 | build.define("SOKOL_GLES2", None); 108 | }, 109 | SokolBackend::Gl => { 110 | build.define("SOKOL_GLCORE", None); 111 | }, 112 | SokolBackend::Wgpu => { 113 | build.define("SOKOL_WGPU", None); 114 | }, 115 | } 116 | 117 | backend 118 | } 119 | 120 | fn make_sokol() { 121 | let mut build = cc::Build::new(); 122 | let tool = build.try_get_compiler().unwrap(); 123 | let config = get_compilation_config(&tool); 124 | 125 | const BASE_C_DIR: &str = "src/sokol/c/"; 126 | 127 | if !config.is_debug_build { 128 | build.define("NDEBUG", None); 129 | build.opt_level(2); 130 | } 131 | 132 | let backend = select_sokol_backend(&mut build, &config); 133 | 134 | let files = [ 135 | "sokol_log.c", "sokol_app.c", "sokol_gfx.c", "sokol_glue.c", "sokol_time.c", "sokol_audio.c", 136 | "sokol_gl.c", "sokol_debugtext.c", "sokol_shape.c", 137 | ]; 138 | 139 | // 140 | // include paths 141 | // 142 | build.include(BASE_C_DIR); 143 | 144 | build.define("IMPL", None); 145 | 146 | // 147 | // silence some warnings 148 | // 149 | build.flag_if_supported("-Wno-unused-parameter"); 150 | build.flag_if_supported("-Wno-missing-field-initializers"); 151 | 152 | if config.debug_info_requested { 153 | build.define("_DEBUG", None).define("SOKOL_DEBUG", None); 154 | } 155 | 156 | for file in &files { 157 | let file = format!("{BASE_C_DIR}{file}"); 158 | 159 | println!("cargo:rerun-if-changed={file}"); 160 | 161 | if config.build_target == BuildTarget::Macos { 162 | build.flag("-ObjC"); 163 | } 164 | 165 | build.file(file); 166 | } 167 | 168 | println!("cargo:rustc-link-lib=static=sokol-rust"); 169 | println!("cargo:rustc-link-search=src/sokol/c/"); 170 | println!("cargo:rustc-link-search=target/debug/"); 171 | 172 | match config.build_target { 173 | BuildTarget::Windows => { 174 | if !config.is_msvc { 175 | build.define("_WIN32_WINNIT", Some("0x0601")); 176 | 177 | build 178 | .flag("-D_WIN32_WINNT=0x0601") 179 | .flag_if_supported("-Wno-cast-function-type") 180 | .flag_if_supported("-Wno-sign-compare") 181 | .flag_if_supported("-Wno-unknown-pragmas"); 182 | 183 | println!("cargo:rustc-link-lib=static=gdi32"); 184 | println!("cargo:rustc-link-lib=static=ole32"); 185 | println!("cargo:rustc-link-lib=static=kernel32"); 186 | println!("cargo:rustc-link-lib=static=user32"); 187 | 188 | if backend == SokolBackend::D3d11 { 189 | println!("cargo:rustc-link-lib=static=d3d11"); 190 | println!("cargo:rustc-link-lib=static=dxgi"); 191 | } 192 | } 193 | }, 194 | 195 | BuildTarget::Macos => { 196 | println!("cargo:rustc-link-lib=framework=Cocoa"); 197 | println!("cargo:rustc-link-lib=framework=QuartzCore"); 198 | println!("cargo:rustc-link-lib=framework=AudioToolbox"); 199 | 200 | match backend { 201 | SokolBackend::Metal => { 202 | println!("cargo:rustc-link-lib=framework=Metal"); 203 | println!("cargo:rustc-link-lib=framework=MetalKit"); 204 | }, 205 | 206 | SokolBackend::Gl | SokolBackend::Gles2 | SokolBackend::Gles3 => { 207 | println!("cargo:rustc-link-lib=framework=OpenGL"); 208 | }, 209 | 210 | _ => { 211 | todo!("unsupported target-backend combo") 212 | }, 213 | } 214 | }, 215 | 216 | BuildTarget::Linux => { 217 | if config.wayland_desired { 218 | build.define("SOKOL_DISABLE_X11 ", None); 219 | } else { 220 | build.define("SOKOL_DISABLE_WAYLAND", None); 221 | } 222 | 223 | if config.force_egl { 224 | build.define("SOKOL_FORCE_EGL", None); 225 | } 226 | 227 | match backend { 228 | SokolBackend::Gles2 => { 229 | println!("cargo:rustc-link-lib=dylib=glesv2"); 230 | assert!(config.force_egl || config.wayland_desired); 231 | }, 232 | SokolBackend::Gles3 | SokolBackend::Gl => { 233 | println!("cargo:rustc-link-lib=dylib=GL"); 234 | }, 235 | _ => {}, 236 | } 237 | 238 | if config.force_egl || config.wayland_desired { 239 | println!("cargo:rustc-link-lib=dylib=egl"); 240 | } 241 | 242 | println!("cargo:rustc-link-lib=dylib=asound"); 243 | 244 | if config.wayland_desired { 245 | println!("cargo:rustc-link-lib=dylib=wayland-client"); 246 | println!("cargo:rustc-link-lib=dylib=wayland-cursor"); 247 | println!("cargo:rustc-link-lib=dylib=wayland-egl"); 248 | println!("cargo:rustc-link-lib=dylib=xkbcommon"); 249 | } else { 250 | println!("cargo:rustc-link-lib=dylib=X11"); 251 | println!("cargo:rustc-link-lib=dylib=Xi"); 252 | println!("cargo:rustc-link-lib=dylib=Xcursor"); 253 | } 254 | }, 255 | 256 | BuildTarget::WasmEmscripten => match backend { 257 | SokolBackend::Gles3 => { 258 | println!("cargo:rustc-link-arg=-sUSE_WEBGL2"); 259 | }, 260 | SokolBackend::Wgpu => { 261 | println!("cargo:rustc-link-arg=-sUSE_WEBGPU"); 262 | }, 263 | _ => {}, 264 | }, 265 | } 266 | 267 | build.compile("sokol-rust"); 268 | } 269 | 270 | fn main() { 271 | println!("cargo:rerun-if-changed=build.rs"); 272 | make_sokol(); 273 | } 274 | -------------------------------------------------------------------------------- /compile-shaders.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | if [ -z "$1" ] 5 | then 6 | echo "usage: ./compile-shaders.sh [path-to-sokol-shdc]" 7 | exit 1 8 | fi 9 | 10 | shdc="$1" 11 | 12 | $shdc -i examples/blend/shader.glsl -o examples/blend/shader.rs -l glsl430:metal_macos:hlsl5 -f sokol_rust 13 | $shdc -i examples/cube/shader.glsl -o examples/cube/shader.rs -l glsl430:metal_macos:hlsl5 -f sokol_rust 14 | $shdc -i examples/instancing/shader.glsl -o examples/instancing/shader.rs -l glsl430:metal_macos:hlsl5 -f sokol_rust 15 | $shdc -i examples/mrt/shader.glsl -o examples/mrt/shader.rs -l glsl430:metal_macos:hlsl5 -f sokol_rust 16 | $shdc -i examples/texcube/shader.glsl -o examples/texcube/shader.rs -l glsl430:metal_macos:hlsl5 -f sokol_rust 17 | $shdc -i examples/vertexpull/shader.glsl -o examples/vertexpull/shader.rs -l glsl430:metal_macos:hlsl5 -f sokol_rust 18 | $shdc -i examples/instancing-compute/shader.glsl -o examples/instancing-compute/shader.rs -l glsl430:metal_macos:hlsl5 -f sokol_rust 19 | -------------------------------------------------------------------------------- /examples/audio/audio.rs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // saudio.rs 3 | // Test sokol-audio Rust bindings 4 | //------------------------------------------------------------------------------ 5 | 6 | use std::ffi; 7 | 8 | use sokol::{app as sapp, audio as saudio, gfx as sg, glue as sglue, log as slog}; 9 | 10 | const NUM_SAMPLES: usize = 32; 11 | 12 | struct State { 13 | pub pass_action: sg::PassAction, 14 | pub even_odd: u32, 15 | pub sample_pos: usize, 16 | pub samples: [f32; NUM_SAMPLES], 17 | } 18 | 19 | extern "C" fn init(user_data: *mut ffi::c_void) { 20 | let state = unsafe { &mut *(user_data as *mut State) }; 21 | 22 | sg::setup(&sg::Desc { 23 | environment: sglue::environment(), 24 | logger: sg::Logger { func: Some(slog::slog_func), ..Default::default() }, 25 | ..Default::default() 26 | }); 27 | saudio::setup(&saudio::Desc { 28 | logger: saudio::Logger { func: Some(slog::slog_func), ..Default::default() }, 29 | ..Default::default() 30 | }); 31 | state.pass_action.colors[0] = sg::ColorAttachmentAction { 32 | load_action: sg::LoadAction::Clear, 33 | clear_value: sg::Color { r: 1.0, g: 0.5, b: 0.0, a: 1.0 }, 34 | ..Default::default() 35 | }; 36 | } 37 | 38 | extern "C" fn frame(user_data: *mut ffi::c_void) { 39 | let state = unsafe { &mut *(user_data as *mut State) }; 40 | 41 | let num_frames = saudio::expect(); 42 | 43 | for _ in 0..num_frames { 44 | state.even_odd += 1; 45 | state.sample_pos += 1; 46 | 47 | if state.sample_pos == NUM_SAMPLES { 48 | state.sample_pos = 0; 49 | let _ = saudio::push(&(state.samples[0]), NUM_SAMPLES as _); 50 | } 51 | 52 | state.samples[state.sample_pos] = if 0 != (state.even_odd & 0x20) { 0.1 } else { -0.1 }; 53 | } 54 | 55 | sg::begin_pass(&sg::Pass { 56 | action: state.pass_action, 57 | swapchain: sglue::swapchain(), 58 | ..Default::default() 59 | }); 60 | sg::end_pass(); 61 | sg::commit(); 62 | } 63 | 64 | extern "C" fn cleanup(user_data: *mut ffi::c_void) { 65 | saudio::shutdown(); 66 | sg::shutdown(); 67 | 68 | let _ = unsafe { Box::from_raw(user_data as *mut State) }; 69 | } 70 | 71 | pub fn main() { 72 | let state = Box::new(State { 73 | pass_action: sg::PassAction::new(), 74 | even_odd: 0, 75 | sample_pos: 0, 76 | samples: [0.0; NUM_SAMPLES], 77 | }); 78 | 79 | let user_data = Box::into_raw(state) as *mut ffi::c_void; 80 | 81 | sapp::run(&sapp::Desc { 82 | init_userdata_cb: Some(init), 83 | frame_userdata_cb: Some(frame), 84 | cleanup_userdata_cb: Some(cleanup), 85 | user_data, 86 | width: 640, 87 | height: 480, 88 | icon: sapp::IconDesc { sokol_default: true, ..Default::default() }, 89 | window_title: c"audio.rs".as_ptr(), 90 | logger: sapp::Logger { func: Some(slog::slog_func), ..Default::default() }, 91 | ..Default::default() 92 | }); 93 | } 94 | -------------------------------------------------------------------------------- /examples/blend/blend.rs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // cube/cube.rs 3 | // 4 | // Test/demonstrate blend modes. 5 | //------------------------------------------------------------------------------ 6 | 7 | mod math; 8 | mod shader; 9 | 10 | use std::ffi; 11 | 12 | use math as m; 13 | use sokol::{app as sapp, gfx as sg, glue as sglue}; 14 | 15 | const NUM_BLEND_FACTORS: usize = 15; 16 | 17 | struct State { 18 | pass_action: sg::PassAction, 19 | bind: sg::Bindings, 20 | pips: [[sg::Pipeline; NUM_BLEND_FACTORS]; NUM_BLEND_FACTORS], 21 | bg_pip: sg::Pipeline, 22 | r: f32, 23 | quad_vs_params: shader::QuadVsParams, 24 | bg_fs_params: shader::BgFsParams, 25 | } 26 | 27 | extern "C" fn init(user_data: *mut ffi::c_void) { 28 | let state = unsafe { &mut *(user_data as *mut State) }; 29 | 30 | sg::setup(&sg::Desc { 31 | pipeline_pool_size: (NUM_BLEND_FACTORS * NUM_BLEND_FACTORS + 1) as _, 32 | environment: sglue::environment(), 33 | logger: sg::Logger { func: Some(sokol::log::slog_func), ..Default::default() }, 34 | ..Default::default() 35 | }); 36 | 37 | // a default pass action which does not clear, since the entire screen is overwritten anyway 38 | state.pass_action.colors[0].load_action = sg::LoadAction::Dontcare; 39 | state.pass_action.depth.load_action = sg::LoadAction::Dontcare; 40 | state.pass_action.stencil.load_action = sg::LoadAction::Dontcare; 41 | 42 | // a quad vertex buffer 43 | #[rustfmt::skip] 44 | const VERTICES: &[f32] = &[ 45 | // pos color 46 | -1.0, -1.0, 0.0, 1.0, 0.0, 0.0, 0.5, 47 | 1.0, -1.0, 0.0, 0.0, 1.0, 0.0, 0.5, 48 | -1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.5, 49 | 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.5, 50 | ]; 51 | state.bind.vertex_buffers[0] = 52 | sg::make_buffer(&sg::BufferDesc { data: sg::slice_as_range(VERTICES), ..Default::default() }); 53 | 54 | // shader and pipeline object for rendering the background quad 55 | state.bg_pip = sg::make_pipeline(&sg::PipelineDesc { 56 | shader: sg::make_shader(&shader::bg_shader_desc(sg::query_backend())), 57 | // we use the same vertex buffer as for the colored 3D quads, 58 | // but only the first two floats from the position, need to 59 | // provide a stride to skip the gap to the next vertex 60 | layout: { 61 | let mut layout = sg::VertexLayoutState::new(); 62 | layout.buffers[0].stride = 28; 63 | layout.attrs[shader::ATTR_BG_POSITION].format = sg::VertexFormat::Float2; 64 | layout 65 | }, 66 | primitive_type: sg::PrimitiveType::TriangleStrip, 67 | ..Default::default() 68 | }); 69 | 70 | // a shader for the blended quads 71 | let quad_shd = sg::make_shader(&shader::quad_shader_desc(sg::query_backend())); 72 | 73 | // one pipeline object per blend-factor combination 74 | let mut pip_desc = sg::PipelineDesc { 75 | layout: { 76 | let mut layout = sg::VertexLayoutState::new(); 77 | layout.attrs[shader::ATTR_QUAD_POSITION].format = sg::VertexFormat::Float3; 78 | layout.attrs[shader::ATTR_QUAD_COLOR0].format = sg::VertexFormat::Float4; 79 | layout 80 | }, 81 | shader: quad_shd, 82 | primitive_type: sg::PrimitiveType::TriangleStrip, 83 | blend_color: sg::Color { r: 1.0, g: 0.0, b: 0.0, a: 1.0 }, 84 | ..Default::default() 85 | }; 86 | for src in 0..NUM_BLEND_FACTORS { 87 | for dst in 0..NUM_BLEND_FACTORS { 88 | pip_desc.colors[0].blend = sg::BlendState { 89 | enabled: true, 90 | src_factor_rgb: unsafe { std::mem::transmute((src + 1) as u32) }, 91 | dst_factor_rgb: unsafe { std::mem::transmute((dst + 1) as u32) }, 92 | src_factor_alpha: sg::BlendFactor::One, 93 | dst_factor_alpha: sg::BlendFactor::Zero, 94 | ..Default::default() 95 | }; 96 | state.pips[src][dst] = sg::make_pipeline(&pip_desc); 97 | } 98 | } 99 | } 100 | 101 | extern "C" fn frame(user_data: *mut ffi::c_void) { 102 | let state = unsafe { &mut *(user_data as *mut State) }; 103 | 104 | let t = (sapp::frame_duration() * 60.0) as f32; 105 | state.r += 0.6 * t; 106 | state.bg_fs_params.tick += 1.0 * t; 107 | 108 | // view-projection matrix 109 | let proj = m::persp_mat4(90.0, sapp::widthf() / sapp::heightf(), 0.01, 100.0); 110 | let view = m::lookat_mat4(m::vec3(0.0, 0.0, 25.0), m::Vec3::ZERO, m::Vec3::UP); 111 | let view_proj = m::mul_mat4(proj, view); 112 | 113 | // start rendering 114 | sg::begin_pass(&sg::Pass { 115 | action: state.pass_action, 116 | swapchain: sglue::swapchain(), 117 | ..Default::default() 118 | }); 119 | 120 | // draw a background quad 121 | sg::apply_pipeline(state.bg_pip); 122 | sg::apply_bindings(&state.bind); 123 | sg::apply_uniforms(shader::UB_BG_FS_PARAMS, &sg::value_as_range(&state.bg_fs_params)); 124 | sg::draw(0, 4, 1); 125 | 126 | // draw the blended quads 127 | let r0 = state.r; 128 | for src in 0..NUM_BLEND_FACTORS { 129 | for dst in 0..NUM_BLEND_FACTORS { 130 | // compute model-view-proj matrix 131 | let rm = m::rotate_mat4(r0, m::vec3(0.0, 1.0, 0.0)); 132 | let x = (dst as i32 - (NUM_BLEND_FACTORS / 2) as i32) as f32 * 3.0; 133 | let y = (src as i32 - (NUM_BLEND_FACTORS / 2) as i32) as f32 * 2.2; 134 | let model = m::mul_mat4(m::translate_mat4(m::vec3(x, y, 0.0)), rm); 135 | state.quad_vs_params.mvp = m::mul_mat4(view_proj, model); 136 | 137 | sg::apply_pipeline(state.pips[src][dst]); 138 | sg::apply_bindings(&state.bind); 139 | sg::apply_uniforms(shader::UB_QUAD_VS_PARAMS, &sg::value_as_range(&state.quad_vs_params)); 140 | sg::draw(0, 4, 1); 141 | } 142 | } 143 | sg::end_pass(); 144 | sg::commit(); 145 | } 146 | 147 | extern "C" fn cleanup(user_data: *mut ffi::c_void) { 148 | sg::shutdown(); 149 | 150 | let _ = unsafe { Box::from_raw(user_data as *mut State) }; 151 | } 152 | 153 | fn main() { 154 | let state = Box::new(State { 155 | pass_action: sg::PassAction::new(), 156 | bind: sg::Bindings::new(), 157 | pips: [[sg::Pipeline::new(); NUM_BLEND_FACTORS]; NUM_BLEND_FACTORS], 158 | bg_pip: sg::Pipeline::new(), 159 | r: 0.0, 160 | quad_vs_params: shader::QuadVsParams { mvp: [[0.0; 4]; 4] }, 161 | bg_fs_params: shader::BgFsParams { tick: 0.0, _pad_4: [0; 12] }, 162 | }); 163 | 164 | let user_data = Box::into_raw(state) as *mut ffi::c_void; 165 | 166 | sapp::run(&sapp::Desc { 167 | init_userdata_cb: Some(init), 168 | frame_userdata_cb: Some(frame), 169 | cleanup_userdata_cb: Some(cleanup), 170 | user_data, 171 | width: 800, 172 | height: 600, 173 | sample_count: 4, 174 | window_title: c"blend.rs".as_ptr(), 175 | logger: sapp::Logger { func: Some(sokol::log::slog_func), ..Default::default() }, 176 | icon: sapp::IconDesc { sokol_default: true, ..Default::default() }, 177 | ..Default::default() 178 | }); 179 | } 180 | -------------------------------------------------------------------------------- /examples/blend/math.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | #[repr(C)] 4 | #[derive(Debug, Clone, Copy)] 5 | pub struct Vec3 { 6 | pub x: f32, 7 | pub y: f32, 8 | pub z: f32, 9 | } 10 | 11 | impl Vec3 { 12 | pub const UP: Vec3 = vec3(0.0, 1.0, 0.0); 13 | pub const ZERO: Vec3 = vec3(0.0, 0.0, 0.0); 14 | } 15 | 16 | #[inline] 17 | pub const fn vec3(x: f32, y: f32, z: f32) -> Vec3 { 18 | Vec3 { x, y, z } 19 | } 20 | 21 | pub type Mat4 = [[f32; 4]; 4]; 22 | 23 | impl std::ops::Sub for Vec3 { 24 | type Output = Vec3; 25 | 26 | #[inline] 27 | fn sub(self, rhs: Vec3) -> Self::Output { 28 | sub_vec3(self, rhs) 29 | } 30 | } 31 | 32 | pub fn sub_vec3(v0: Vec3, v1: Vec3) -> Vec3 { 33 | vec3(v0.x - v1.x, v0.y - v1.y, v0.z - v1.z) 34 | } 35 | 36 | pub fn dot_vec3(v0: Vec3, v1: Vec3) -> f32 { 37 | v0.x * v1.x + v0.y * v1.y + v0.z * v1.z 38 | } 39 | 40 | #[inline] 41 | pub fn len_vec3(v: Vec3) -> f32 { 42 | dot_vec3(v, v).sqrt() 43 | } 44 | 45 | pub fn norm_vec3(v: Vec3) -> Vec3 { 46 | let l = len_vec3(v); 47 | 48 | if l != 0.0 { vec3(v.x / l, v.y / l, v.z / l) } else { Vec3::ZERO } 49 | } 50 | 51 | pub fn cross_vec3(v0: Vec3, v1: Vec3) -> Vec3 { 52 | vec3((v0.y * v1.z) - (v0.z * v1.y), (v0.z * v1.x) - (v0.x * v1.z), (v0.x * v1.y) - (v0.y * v1.x)) 53 | } 54 | 55 | pub const fn identity_mat4() -> Mat4 { 56 | let mut m = [[0.0; 4]; 4]; 57 | 58 | m[0][0] = 1.0; 59 | m[1][1] = 1.0; 60 | m[2][2] = 1.0; 61 | m[3][3] = 1.0; 62 | 63 | m 64 | } 65 | 66 | pub fn rotate_mat4(angle: f32, axis_unorm: Vec3) -> Mat4 { 67 | let mut m = identity_mat4(); 68 | 69 | let axis = norm_vec3(axis_unorm); 70 | let sin_theta = angle.to_radians().sin(); 71 | let cos_theta = angle.to_radians().cos(); 72 | let cos_value = 1.0 - cos_theta; 73 | 74 | m[0][0] = (axis.x * axis.x * cos_value) + cos_theta; 75 | m[0][1] = (axis.x * axis.y * cos_value) + (axis.z * sin_theta); 76 | m[0][2] = (axis.x * axis.z * cos_value) - (axis.y * sin_theta); 77 | m[1][0] = (axis.y * axis.x * cos_value) - (axis.z * sin_theta); 78 | m[1][1] = (axis.y * axis.y * cos_value) + cos_theta; 79 | m[1][2] = (axis.y * axis.z * cos_value) + (axis.x * sin_theta); 80 | m[2][0] = (axis.z * axis.x * cos_value) + (axis.y * sin_theta); 81 | m[2][1] = (axis.z * axis.y * cos_value) - (axis.x * sin_theta); 82 | m[2][2] = (axis.z * axis.z * cos_value) + cos_theta; 83 | 84 | m 85 | } 86 | 87 | pub fn persp_mat4(fov: f32, aspect: f32, near: f32, far: f32) -> Mat4 { 88 | let mut m = identity_mat4(); 89 | let t = f32::tan(fov * (std::f32::consts::PI / 360.0)); 90 | 91 | m[0][0] = 1.0 / t; 92 | m[1][1] = aspect / t; 93 | m[2][3] = -1.0; 94 | m[2][2] = (near + far) / (near - far); 95 | m[3][2] = (2.0 * near * far) / (near - far); 96 | m[3][3] = 0.0; 97 | 98 | m 99 | } 100 | 101 | pub fn lookat_mat4(eye: Vec3, center: Vec3, up: Vec3) -> Mat4 { 102 | let mut m = [[0.0; 4]; 4]; 103 | 104 | let f = norm_vec3(center - eye); 105 | let s = norm_vec3(cross_vec3(f, up)); 106 | let u = cross_vec3(s, f); 107 | 108 | m[0][0] = s.x; 109 | m[0][1] = u.x; 110 | m[0][2] = -f.x; 111 | 112 | m[1][0] = s.y; 113 | m[1][1] = u.y; 114 | m[1][2] = -f.y; 115 | 116 | m[2][0] = s.z; 117 | m[2][1] = u.z; 118 | m[2][2] = -f.z; 119 | 120 | m[3][0] = -dot_vec3(s, eye); 121 | m[3][1] = -dot_vec3(u, eye); 122 | m[3][2] = dot_vec3(f, eye); 123 | m[3][3] = 1.0; 124 | 125 | m 126 | } 127 | 128 | pub fn translate_mat4(translation: Vec3) -> Mat4 { 129 | let mut m = identity_mat4(); 130 | 131 | m[3][0] = translation.x; 132 | m[3][1] = translation.y; 133 | m[3][2] = translation.z; 134 | 135 | m 136 | } 137 | 138 | pub fn mul_mat4(left: Mat4, right: Mat4) -> Mat4 { 139 | let mut m = [[0.0; 4]; 4]; 140 | 141 | for col in 0..4 { 142 | for row in 0..4 { 143 | m[col][row] = left[0][row] * right[col][0] 144 | + left[1][row] * right[col][1] 145 | + left[2][row] * right[col][2] 146 | + left[3][row] * right[col][3]; 147 | } 148 | } 149 | 150 | m 151 | } 152 | -------------------------------------------------------------------------------- /examples/blend/shader.glsl: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // shaders for blend-sapp sample 3 | //------------------------------------------------------------------------------ 4 | @header use crate::math as m 5 | 6 | @ctype mat4 m::Mat4 7 | 8 | @vs vs_bg 9 | in vec2 position; 10 | void main() { 11 | gl_Position = vec4(position, 0.5, 1.0); 12 | } 13 | @end 14 | 15 | @fs fs_bg 16 | layout(binding=0) uniform bg_fs_params { 17 | float tick; 18 | }; 19 | 20 | out vec4 frag_color; 21 | 22 | void main() { 23 | vec2 xy = fract((gl_FragCoord.xy-vec2(tick)) / 50.0); 24 | frag_color = vec4(vec3(xy.x*xy.y), 1.0); 25 | } 26 | @end 27 | 28 | @program bg vs_bg fs_bg 29 | 30 | @vs vs_quad 31 | layout(binding=0) uniform quad_vs_params { 32 | mat4 mvp; 33 | }; 34 | 35 | in vec4 position; 36 | in vec4 color0; 37 | 38 | out vec4 color; 39 | 40 | void main() { 41 | gl_Position = mvp * position; 42 | color = color0; 43 | } 44 | @end 45 | 46 | @fs fs_quad 47 | in vec4 color; 48 | out vec4 frag_color; 49 | void main() { 50 | frag_color = color; 51 | } 52 | @end 53 | 54 | @program quad vs_quad fs_quad 55 | -------------------------------------------------------------------------------- /examples/clear/clear.rs: -------------------------------------------------------------------------------- 1 | use std::ffi; 2 | 3 | use sokol::{app as sapp, gfx as sg, glue as sglue}; 4 | 5 | struct State { 6 | pass_action: sg::PassAction, 7 | } 8 | 9 | extern "C" fn init(user_data: *mut ffi::c_void) { 10 | let state = unsafe { &mut *(user_data as *mut State) }; 11 | 12 | sg::setup(&sg::Desc { 13 | environment: sglue::environment(), 14 | logger: sg::Logger { func: Some(sokol::log::slog_func), ..Default::default() }, 15 | ..Default::default() 16 | }); 17 | 18 | state.pass_action.colors[0] = sg::ColorAttachmentAction { 19 | load_action: sg::LoadAction::Clear, 20 | clear_value: sg::Color { r: 1.0, g: 0.0, b: 0.0, a: 1.0 }, 21 | ..Default::default() 22 | }; 23 | 24 | let backend = sg::query_backend(); 25 | match &backend { 26 | sg::Backend::Glcore | sg::Backend::Gles3 => { 27 | println!("Using GL Backend!"); 28 | println!("Specifically the {:?} backend!", backend); 29 | }, 30 | 31 | sg::Backend::D3d11 => { 32 | println!("Using D3d11 Backend!"); 33 | }, 34 | 35 | sg::Backend::MetalIos | sg::Backend::MetalMacos | sg::Backend::MetalSimulator => { 36 | println!("Using Metal Backend!"); 37 | println!("Specifically the {:?} backend!", backend); 38 | }, 39 | 40 | sg::Backend::Wgpu => { 41 | println!("Using Wgpu Backend!"); 42 | }, 43 | 44 | sg::Backend::Dummy => { 45 | println!("Using Dummy Backend!"); 46 | }, 47 | } 48 | } 49 | 50 | extern "C" fn frame(user_data: *mut ffi::c_void) { 51 | let state = unsafe { &mut *(user_data as *mut State) }; 52 | 53 | let g = state.pass_action.colors[0].clear_value.g + 0.01; 54 | state.pass_action.colors[0].clear_value.g = if g > 1.0 { 0.0 } else { g }; 55 | 56 | sg::begin_pass(&sg::Pass { 57 | action: state.pass_action, 58 | swapchain: sglue::swapchain(), 59 | ..Default::default() 60 | }); 61 | sg::end_pass(); 62 | sg::commit(); 63 | } 64 | 65 | extern "C" fn cleanup(user_data: *mut ffi::c_void) { 66 | sg::shutdown(); 67 | 68 | let _ = unsafe { Box::from_raw(user_data as *mut State) }; 69 | } 70 | 71 | fn main() { 72 | let state = Box::new(State { pass_action: sg::PassAction::new() }); 73 | 74 | let user_data = Box::into_raw(state) as *mut ffi::c_void; 75 | 76 | sapp::run(&sapp::Desc { 77 | init_userdata_cb: Some(init), 78 | frame_userdata_cb: Some(frame), 79 | cleanup_userdata_cb: Some(cleanup), 80 | user_data, 81 | window_title: c"clear.rs".as_ptr(), 82 | width: 800, 83 | height: 600, 84 | sample_count: 4, 85 | logger: sapp::Logger { func: Some(sokol::log::slog_func), ..Default::default() }, 86 | icon: sapp::IconDesc { sokol_default: true, ..Default::default() }, 87 | ..Default::default() 88 | }); 89 | } 90 | -------------------------------------------------------------------------------- /examples/cube/cube.rs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // cube/cube.rs 3 | // 4 | // Renders a rotating cube. 5 | //------------------------------------------------------------------------------ 6 | 7 | mod math; 8 | mod shader; 9 | 10 | use std::ffi; 11 | 12 | use math as m; 13 | use sokol::{app as sapp, gfx as sg, glue as sglue}; 14 | 15 | struct State { 16 | rx: f32, 17 | ry: f32, 18 | 19 | pip: sg::Pipeline, 20 | bind: sg::Bindings, 21 | } 22 | 23 | extern "C" fn init(user_data: *mut ffi::c_void) { 24 | // Cast user data to a borrowed state reference 25 | let state = unsafe { &mut *(user_data as *mut State) }; 26 | 27 | sg::setup(&sg::Desc { 28 | environment: sglue::environment(), 29 | logger: sg::Logger { func: Some(sokol::log::slog_func), ..Default::default() }, 30 | ..Default::default() 31 | }); 32 | 33 | // cube vertex buffer 34 | #[rustfmt::skip] 35 | const VERTICES: &[f32] = &[ 36 | -1.0, -1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 37 | 1.0, -1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 38 | 1.0, 1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 39 | -1.0, 1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 40 | 41 | -1.0, -1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 42 | 1.0, -1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 43 | 1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 44 | -1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 45 | 46 | -1.0, -1.0, -1.0, 0.0, 0.0, 1.0, 1.0, 47 | -1.0, 1.0, -1.0, 0.0, 0.0, 1.0, 1.0, 48 | -1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 49 | -1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 50 | 51 | 1.0, -1.0, -1.0, 1.0, 0.5, 0.0, 1.0, 52 | 1.0, 1.0, -1.0, 1.0, 0.5, 0.0, 1.0, 53 | 1.0, 1.0, 1.0, 1.0, 0.5, 0.0, 1.0, 54 | 1.0, -1.0, 1.0, 1.0, 0.5, 0.0, 1.0, 55 | 56 | -1.0, -1.0, -1.0, 0.0, 0.5, 1.0, 1.0, 57 | -1.0, -1.0, 1.0, 0.0, 0.5, 1.0, 1.0, 58 | 1.0, -1.0, 1.0, 0.0, 0.5, 1.0, 1.0, 59 | 1.0, -1.0, -1.0, 0.0, 0.5, 1.0, 1.0, 60 | 61 | -1.0, 1.0, -1.0, 1.0, 0.0, 0.5, 1.0, 62 | -1.0, 1.0, 1.0, 1.0, 0.0, 0.5, 1.0, 63 | 1.0, 1.0, 1.0, 1.0, 0.0, 0.5, 1.0, 64 | 1.0, 1.0, -1.0, 1.0, 0.0, 0.5, 1.0, 65 | ]; 66 | state.bind.vertex_buffers[0] = 67 | sg::make_buffer(&sg::BufferDesc { data: sg::slice_as_range(VERTICES), ..Default::default() }); 68 | 69 | // create an index buffer for the cube 70 | #[rustfmt::skip] 71 | const INDICES: &[u16] = &[ 72 | 0, 1, 2, 0, 2, 3, 73 | 6, 5, 4, 7, 6, 4, 74 | 8, 9, 10, 8, 10, 11, 75 | 14, 13, 12, 15, 14, 12, 76 | 16, 17, 18, 16, 18, 19, 77 | 22, 21, 20, 23, 22, 20, 78 | ]; 79 | 80 | state.bind.index_buffer = sg::make_buffer(&sg::BufferDesc { 81 | data: sg::slice_as_range(INDICES), 82 | usage: sg::BufferUsage { index_buffer: true, ..Default::default() }, 83 | ..Default::default() 84 | }); 85 | 86 | // shader and pipeline object 87 | 88 | state.pip = sg::make_pipeline(&sg::PipelineDesc { 89 | shader: sg::make_shader(&shader::cube_shader_desc(sg::query_backend())), 90 | layout: { 91 | let mut layout = sg::VertexLayoutState::new(); 92 | layout.buffers[0].stride = 28; 93 | 94 | layout.attrs[shader::ATTR_CUBE_POSITION].format = sg::VertexFormat::Float3; 95 | layout.attrs[shader::ATTR_CUBE_COLOR0].format = sg::VertexFormat::Float4; 96 | 97 | layout 98 | }, 99 | 100 | index_type: sg::IndexType::Uint16, 101 | cull_mode: sg::CullMode::Back, 102 | 103 | depth: sg::DepthState { 104 | write_enabled: true, 105 | compare: sg::CompareFunc::LessEqual, 106 | 107 | ..Default::default() 108 | }, 109 | 110 | ..Default::default() 111 | }); 112 | } 113 | 114 | extern "C" fn frame(user_data: *mut ffi::c_void) { 115 | // Cast user data to a borrowed state reference 116 | let state = unsafe { &mut *(user_data as *mut State) }; 117 | 118 | let t = (sapp::frame_duration() * 60.0) as f32; 119 | state.rx += 1.0 * t; 120 | state.ry += 2.0 * t; 121 | 122 | // vertex shader uniform with model-view-projection matrix 123 | let vs_params = shader::VsParams { mvp: compute_mvp(state.rx, state.ry) }; 124 | 125 | let mut pass_action = sg::PassAction::new(); 126 | pass_action.colors[0] = sg::ColorAttachmentAction { 127 | load_action: sg::LoadAction::Clear, 128 | clear_value: sg::Color { r: 0.25, g: 0.5, b: 0.75, a: 1.0 }, 129 | ..Default::default() 130 | }; 131 | 132 | sg::begin_pass(&sg::Pass { action: pass_action, swapchain: sglue::swapchain(), ..Default::default() }); 133 | sg::apply_pipeline(state.pip); 134 | sg::apply_bindings(&state.bind); 135 | sg::apply_uniforms(shader::UB_VS_PARAMS, &sg::value_as_range(&vs_params)); 136 | sg::draw(0, 36, 1); 137 | sg::end_pass(); 138 | sg::commit(); 139 | } 140 | 141 | pub fn compute_mvp(rx: f32, ry: f32) -> [[f32; 4]; 4] { 142 | let proj = m::persp_mat4(60.0, sapp::widthf() / sapp::heightf(), 0.01, 10.0); 143 | let view = m::lookat_mat4(m::vec3(0.0, 1.5, 6.0), m::Vec3::ZERO, m::Vec3::UP); 144 | let view_proj = m::mul_mat4(proj, view); 145 | let rxm = m::rotate_mat4(rx, m::vec3(1.0, 0.0, 0.0)); 146 | let rym = m::rotate_mat4(ry, m::vec3(0.0, 1.0, 0.0)); 147 | let model = m::mul_mat4(rxm, rym); 148 | 149 | m::mul_mat4(view_proj, model) 150 | } 151 | 152 | extern "C" fn cleanup(user_data: *mut ffi::c_void) { 153 | sg::shutdown(); 154 | 155 | // Convert the user_data back to an owned ptr so that it gets dropped correctly 156 | let _ = unsafe { Box::from_raw(user_data as *mut State) }; 157 | } 158 | 159 | fn main() { 160 | // Heap allocated state struct, passed to app callbacks via user_data 161 | let state = Box::new(State { rx: 0.0, ry: 0.0, pip: sg::Pipeline::new(), bind: sg::Bindings::new() }); 162 | 163 | // Forget the ownership so we can pass as a user_data pointer 164 | let user_data = Box::into_raw(state) as *mut ffi::c_void; 165 | 166 | sapp::run(&sapp::Desc { 167 | init_userdata_cb: Some(init), 168 | frame_userdata_cb: Some(frame), 169 | cleanup_userdata_cb: Some(cleanup), 170 | user_data, 171 | width: 800, 172 | height: 600, 173 | sample_count: 4, 174 | window_title: c"cube.rs".as_ptr(), 175 | logger: sapp::Logger { func: Some(sokol::log::slog_func), ..Default::default() }, 176 | icon: sapp::IconDesc { sokol_default: true, ..Default::default() }, 177 | 178 | ..Default::default() 179 | }); 180 | } 181 | -------------------------------------------------------------------------------- /examples/cube/math.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | #[repr(C)] 4 | #[derive(Debug, Clone, Copy)] 5 | pub struct Vec3 { 6 | pub x: f32, 7 | pub y: f32, 8 | pub z: f32, 9 | } 10 | 11 | impl Vec3 { 12 | pub const UP: Vec3 = vec3(0.0, 1.0, 0.0); 13 | pub const ZERO: Vec3 = vec3(0.0, 0.0, 0.0); 14 | } 15 | 16 | #[inline] 17 | pub const fn vec3(x: f32, y: f32, z: f32) -> Vec3 { 18 | Vec3 { x, y, z } 19 | } 20 | 21 | pub type Mat4 = [[f32; 4]; 4]; 22 | 23 | impl std::ops::Sub for Vec3 { 24 | type Output = Vec3; 25 | 26 | #[inline] 27 | fn sub(self, rhs: Vec3) -> Self::Output { 28 | sub_vec3(self, rhs) 29 | } 30 | } 31 | 32 | pub fn sub_vec3(v0: Vec3, v1: Vec3) -> Vec3 { 33 | vec3(v0.x - v1.x, v0.y - v1.y, v0.z - v1.z) 34 | } 35 | 36 | pub fn dot_vec3(v0: Vec3, v1: Vec3) -> f32 { 37 | v0.x * v1.x + v0.y * v1.y + v0.z * v1.z 38 | } 39 | 40 | #[inline] 41 | pub fn len_vec3(v: Vec3) -> f32 { 42 | dot_vec3(v, v).sqrt() 43 | } 44 | 45 | pub fn norm_vec3(v: Vec3) -> Vec3 { 46 | let l = len_vec3(v); 47 | 48 | if l != 0.0 { vec3(v.x / l, v.y / l, v.z / l) } else { Vec3::ZERO } 49 | } 50 | 51 | pub fn cross_vec3(v0: Vec3, v1: Vec3) -> Vec3 { 52 | vec3((v0.y * v1.z) - (v0.z * v1.y), (v0.z * v1.x) - (v0.x * v1.z), (v0.x * v1.y) - (v0.y * v1.x)) 53 | } 54 | 55 | pub const fn identity_mat4() -> Mat4 { 56 | let mut m = [[0.0; 4]; 4]; 57 | 58 | m[0][0] = 1.0; 59 | m[1][1] = 1.0; 60 | m[2][2] = 1.0; 61 | m[3][3] = 1.0; 62 | 63 | m 64 | } 65 | 66 | pub fn rotate_mat4(angle: f32, axis_unorm: Vec3) -> Mat4 { 67 | let mut m = identity_mat4(); 68 | 69 | let axis = norm_vec3(axis_unorm); 70 | let sin_theta = angle.to_radians().sin(); 71 | let cos_theta = angle.to_radians().cos(); 72 | let cos_value = 1.0 - cos_theta; 73 | 74 | m[0][0] = (axis.x * axis.x * cos_value) + cos_theta; 75 | m[0][1] = (axis.x * axis.y * cos_value) + (axis.z * sin_theta); 76 | m[0][2] = (axis.x * axis.z * cos_value) - (axis.y * sin_theta); 77 | m[1][0] = (axis.y * axis.x * cos_value) - (axis.z * sin_theta); 78 | m[1][1] = (axis.y * axis.y * cos_value) + cos_theta; 79 | m[1][2] = (axis.y * axis.z * cos_value) + (axis.x * sin_theta); 80 | m[2][0] = (axis.z * axis.x * cos_value) + (axis.y * sin_theta); 81 | m[2][1] = (axis.z * axis.y * cos_value) - (axis.x * sin_theta); 82 | m[2][2] = (axis.z * axis.z * cos_value) + cos_theta; 83 | 84 | m 85 | } 86 | 87 | pub fn persp_mat4(fov: f32, aspect: f32, near: f32, far: f32) -> Mat4 { 88 | let mut m = identity_mat4(); 89 | let t = f32::tan(fov * (std::f32::consts::PI / 360.0)); 90 | 91 | m[0][0] = 1.0 / t; 92 | m[1][1] = aspect / t; 93 | m[2][3] = -1.0; 94 | m[2][2] = (near + far) / (near - far); 95 | m[3][2] = (2.0 * near * far) / (near - far); 96 | m[3][3] = 0.0; 97 | 98 | m 99 | } 100 | 101 | pub fn lookat_mat4(eye: Vec3, center: Vec3, up: Vec3) -> Mat4 { 102 | let mut m = [[0.0; 4]; 4]; 103 | 104 | let f = norm_vec3(center - eye); 105 | let s = norm_vec3(cross_vec3(f, up)); 106 | let u = cross_vec3(s, f); 107 | 108 | m[0][0] = s.x; 109 | m[0][1] = u.x; 110 | m[0][2] = -f.x; 111 | 112 | m[1][0] = s.y; 113 | m[1][1] = u.y; 114 | m[1][2] = -f.y; 115 | 116 | m[2][0] = s.z; 117 | m[2][1] = u.z; 118 | m[2][2] = -f.z; 119 | 120 | m[3][0] = -dot_vec3(s, eye); 121 | m[3][1] = -dot_vec3(u, eye); 122 | m[3][2] = dot_vec3(f, eye); 123 | m[3][3] = 1.0; 124 | 125 | m 126 | } 127 | 128 | pub fn translate_mat4(translation: Vec3) -> Mat4 { 129 | let mut m = identity_mat4(); 130 | 131 | m[3][0] = translation.x; 132 | m[3][1] = translation.y; 133 | m[3][2] = translation.z; 134 | 135 | m 136 | } 137 | 138 | pub fn mul_mat4(left: Mat4, right: Mat4) -> Mat4 { 139 | let mut m = [[0.0; 4]; 4]; 140 | 141 | for col in 0..4 { 142 | for row in 0..4 { 143 | m[col][row] = left[0][row] * right[col][0] 144 | + left[1][row] * right[col][1] 145 | + left[2][row] * right[col][2] 146 | + left[3][row] * right[col][3]; 147 | } 148 | } 149 | 150 | m 151 | } 152 | -------------------------------------------------------------------------------- /examples/cube/shader.glsl: -------------------------------------------------------------------------------- 1 | @header use crate::math as m 2 | 3 | @ctype mat4 m::Mat4 4 | 5 | @vs vs 6 | layout(binding=0) uniform vs_params { 7 | mat4 mvp; 8 | }; 9 | 10 | in vec4 position; 11 | in vec4 color0; 12 | 13 | out vec4 color; 14 | 15 | void main() { 16 | gl_Position = mvp * position; 17 | color = color0; 18 | } 19 | @end 20 | 21 | @fs fs 22 | in vec4 color; 23 | out vec4 frag_color; 24 | 25 | void main() { 26 | frag_color = color; 27 | } 28 | @end 29 | 30 | @program cube vs fs 31 | -------------------------------------------------------------------------------- /examples/debugtext/debugtext.rs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // debugtext/main.rs 3 | // 4 | // Text rendering with sokol_debugtext.h, test builtin fonts. 5 | //------------------------------------------------------------------------------ 6 | 7 | use std::ffi; 8 | 9 | use sokol::{app as sapp, debugtext as sdtx, gfx as sg, glue as sglue}; 10 | 11 | const FONT_KC853: usize = 0; 12 | const FONT_KC854: usize = 1; 13 | const FONT_Z1013: usize = 2; 14 | const FONT_CPC: usize = 3; 15 | const FONT_C64: usize = 4; 16 | const FONT_ORIC: usize = 5; 17 | 18 | struct State { 19 | pass_action: sg::PassAction, 20 | } 21 | 22 | extern "C" fn init(user_data: *mut ffi::c_void) { 23 | let state = unsafe { &mut *(user_data as *mut State) }; 24 | state.pass_action.colors[0] = sg::ColorAttachmentAction { 25 | load_action: sg::LoadAction::Clear, 26 | clear_value: sg::Color { r: 0.0, g: 0.125, b: 0.25, a: 1.0 }, 27 | ..Default::default() 28 | }; 29 | 30 | sg::setup(&sg::Desc { 31 | environment: sglue::environment(), 32 | logger: sg::Logger { func: Some(sokol::log::slog_func), ..Default::default() }, 33 | ..Default::default() 34 | }); 35 | 36 | let mut desc = sdtx::Desc::new(); 37 | desc.fonts[FONT_KC853] = sdtx::font_kc853(); 38 | desc.fonts[FONT_KC854] = sdtx::font_kc854(); 39 | desc.fonts[FONT_Z1013] = sdtx::font_z1013(); 40 | desc.fonts[FONT_CPC] = sdtx::font_cpc(); 41 | desc.fonts[FONT_C64] = sdtx::font_c64(); 42 | desc.fonts[FONT_ORIC] = sdtx::font_oric(); 43 | sdtx::setup(&desc) 44 | } 45 | 46 | fn print_font(font_index: usize, title: &str, r: u8, g: u8, b: u8) { 47 | sdtx::font(font_index); 48 | sdtx::color3b(r, g, b); 49 | sdtx::puts(title); 50 | 51 | for c in 32_i32..=255 { 52 | sdtx::putc(c as _); 53 | if ((c + 1) & 63) == 0 { 54 | sdtx::crlf(); 55 | } 56 | } 57 | 58 | sdtx::crlf(); 59 | } 60 | 61 | extern "C" fn frame(user_data: *mut ffi::c_void) { 62 | let state = unsafe { &mut *(user_data as *mut State) }; 63 | // set virtual canvas size to half display size so that 64 | // glyphs are 16x16 display pixels 65 | sdtx::canvas(sapp::widthf() * 0.5, sapp::heightf() * 0.5); 66 | sdtx::origin(0.0, 2.0); 67 | sdtx::home(); 68 | 69 | print_font(FONT_KC853, "KC85/3:\n", 0xf4, 0x43, 0x36); 70 | print_font(FONT_KC854, "KC85/4:\n", 0x21, 0x96, 0xf3); 71 | print_font(FONT_Z1013, "Z1013:\n", 0x4c, 0xaf, 0x50); 72 | print_font(FONT_CPC, "Amstrad CPC:\n", 0xff, 0xeb, 0x3b); 73 | print_font(FONT_C64, "C64:\n", 0x79, 0x86, 0xcb); 74 | print_font(FONT_ORIC, "Oric Atmos:\n", 0xff, 0x98, 0x00); 75 | 76 | sg::begin_pass(&sg::Pass { 77 | action: state.pass_action, 78 | swapchain: sglue::swapchain(), 79 | ..Default::default() 80 | }); 81 | sdtx::draw(); 82 | sg::end_pass(); 83 | sg::commit(); 84 | } 85 | 86 | extern "C" fn cleanup(user_data: *mut ffi::c_void) { 87 | sdtx::shutdown(); 88 | sg::shutdown(); 89 | 90 | let _ = unsafe { Box::from_raw(user_data as *mut State) }; 91 | } 92 | 93 | fn main() { 94 | let state = Box::new(State { pass_action: sg::PassAction::new() }); 95 | 96 | let user_data = Box::into_raw(state) as *mut ffi::c_void; 97 | 98 | sapp::run(&sapp::Desc { 99 | init_userdata_cb: Some(init), 100 | frame_userdata_cb: Some(frame), 101 | cleanup_userdata_cb: Some(cleanup), 102 | user_data, 103 | width: 1024, 104 | height: 600, 105 | window_title: c"debugtext.rs".as_ptr(), 106 | logger: sapp::Logger { func: Some(sokol::log::slog_func), ..Default::default() }, 107 | icon: sapp::IconDesc { sokol_default: true, ..Default::default() }, 108 | ..Default::default() 109 | }) 110 | } 111 | -------------------------------------------------------------------------------- /examples/instancing-compute/instancing-compute.rs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // instancing-compute.rs 3 | // 4 | // Like instancing.rs, but update particles with compute shader. 5 | //------------------------------------------------------------------------------ 6 | 7 | mod math; 8 | mod shader; 9 | 10 | use std::ffi; 11 | 12 | use math as m; 13 | use sokol::{app as sapp, gfx as sg, glue as sglue, log as slog}; 14 | 15 | const MAX_PARTICLES: usize = 512 * 1024; 16 | const NUM_PARTICLES_EMITTED_PER_FRAME: usize = 10; 17 | 18 | struct ComputeState { 19 | pip: sg::Pipeline, 20 | bind: sg::Bindings, 21 | } 22 | 23 | struct DisplayState { 24 | pip: sg::Pipeline, 25 | bind: sg::Bindings, 26 | pass_action: sg::PassAction, 27 | } 28 | 29 | struct State { 30 | num_particles: usize, 31 | ry: f32, 32 | compute: ComputeState, 33 | display: DisplayState, 34 | } 35 | 36 | extern "C" fn init(user_data: *mut ffi::c_void) { 37 | let state = unsafe { &mut *(user_data as *mut State) }; 38 | 39 | sg::setup(&sg::Desc { 40 | environment: sglue::environment(), 41 | logger: sg::Logger { func: Some(slog::slog_func), ..Default::default() }, 42 | ..Default::default() 43 | }); 44 | 45 | // if compute shaders not supported, clear to red and early out 46 | if !sg::query_features().compute { 47 | state.display.pass_action.colors[0] = sg::ColorAttachmentAction { 48 | load_action: sg::LoadAction::Clear, 49 | clear_value: sg::Color { r: 1.0, g: 0.0, b: 0.0, a: 1.0 }, 50 | ..Default::default() 51 | }; 52 | return; 53 | } 54 | // regular clear color 55 | state.display.pass_action.colors[0] = sg::ColorAttachmentAction { 56 | load_action: sg::LoadAction::Clear, 57 | clear_value: sg::Color { r: 0.0, g: 0.1, b: 0.2, a: 1.0 }, 58 | ..Default::default() 59 | }; 60 | 61 | // a buffer and view for the particle state 62 | let sbuf = sg::make_buffer(&sg::BufferDesc { 63 | usage: sg::BufferUsage { storage_buffer: true, ..Default::default() }, 64 | size: MAX_PARTICLES * std::mem::size_of::(), 65 | ..Default::default() 66 | }); 67 | let sbuf_view = sg::make_view(&sg::ViewDesc { 68 | storage_buffer: sg::BufferViewDesc { buffer: sbuf, ..Default::default() }, 69 | ..Default::default() 70 | }); 71 | state.compute.bind.views[shader::VIEW_CS_SSBO] = sbuf_view; 72 | state.display.bind.views[shader::VIEW_VS_SSBO] = sbuf_view; 73 | 74 | // a compute shader and pipeline object for updating the particle state 75 | state.compute.pip = sg::make_pipeline(&sg::PipelineDesc { 76 | compute: true, 77 | shader: sg::make_shader(&shader::update_shader_desc(sg::query_backend())), 78 | ..Default::default() 79 | }); 80 | 81 | // vertex and index buffer for particle geometry 82 | let r = 0.05; 83 | #[rustfmt::skip] 84 | let vertices: &[f32] = &[ 85 | // positions colors 86 | 0.0, -r, 0.0, 1.0, 0.0, 0.0, 1.0, 87 | r, 0.0, r, 0.0, 1.0, 0.0, 1.0, 88 | r, 0.0, -r, 0.0, 0.0, 1.0, 1.0, 89 | -r, 0.0, -r, 1.0, 1.0, 0.0, 1.0, 90 | -r, 0.0, r, 0.0, 1.0, 1.0, 1.0, 91 | 0.0, r, 0.0, 1.0, 0.0, 1.0, 1.0, 92 | ]; 93 | state.display.bind.vertex_buffers[0] = 94 | sg::make_buffer(&sg::BufferDesc { data: sg::slice_as_range(vertices), ..Default::default() }); 95 | #[rustfmt::skip] 96 | let indices: &[u16] = &[ 97 | 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 1, 98 | 5, 1, 2, 5, 2, 3, 5, 3, 4, 5, 4, 1, 99 | ]; 100 | state.display.bind.index_buffer = sg::make_buffer(&sg::BufferDesc { 101 | usage: sg::BufferUsage { index_buffer: true, ..Default::default() }, 102 | data: sg::slice_as_range(indices), 103 | ..Default::default() 104 | }); 105 | 106 | // shader and pipeline object for rendering the particles, this uses 107 | // the compute-updated storage buffer to provide the particle positions 108 | state.display.pip = sg::make_pipeline(&sg::PipelineDesc { 109 | shader: sg::make_shader(&shader::display_shader_desc(sg::query_backend())), 110 | layout: sg::VertexLayoutState { 111 | attrs: { 112 | let mut attrs = [sg::VertexAttrState::new(); sg::MAX_VERTEX_ATTRIBUTES]; 113 | attrs[shader::ATTR_DISPLAY_POS] = 114 | sg::VertexAttrState { format: sg::VertexFormat::Float3, ..Default::default() }; 115 | attrs[shader::ATTR_DISPLAY_COLOR0] = 116 | sg::VertexAttrState { format: sg::VertexFormat::Float4, ..Default::default() }; 117 | attrs 118 | }, 119 | ..Default::default() 120 | }, 121 | index_type: sg::IndexType::Uint16, 122 | cull_mode: sg::CullMode::Back, 123 | depth: sg::DepthState { 124 | compare: sg::CompareFunc::LessEqual, 125 | write_enabled: true, 126 | ..Default::default() 127 | }, 128 | ..Default::default() 129 | }); 130 | 131 | // one-time init of particle velocities 132 | let pip = sg::make_pipeline(&sg::PipelineDesc { 133 | compute: true, 134 | shader: sg::make_shader(&shader::init_shader_desc(sg::query_backend())), 135 | ..Default::default() 136 | }); 137 | sg::begin_pass(&sg::Pass { compute: true, ..Default::default() }); 138 | sg::apply_pipeline(pip); 139 | sg::apply_bindings(&state.compute.bind); 140 | sg::dispatch(MAX_PARTICLES / 64, 1, 1); 141 | sg::end_pass(); 142 | sg::destroy_pipeline(pip); 143 | } 144 | 145 | extern "C" fn frame(user_data: *mut ffi::c_void) { 146 | let state = unsafe { &mut *(user_data as *mut State) }; 147 | 148 | if !sg::query_features().compute { 149 | draw_fallback(state); 150 | return; 151 | } 152 | 153 | state.num_particles += NUM_PARTICLES_EMITTED_PER_FRAME; 154 | if state.num_particles > MAX_PARTICLES { 155 | state.num_particles = MAX_PARTICLES; 156 | } 157 | let dt = sapp::frame_duration() as f32; 158 | 159 | // compute pass to update particle positions 160 | let cs_params = shader::CsParams { dt, num_particles: state.num_particles as i32, _pad_8: [0; 8] }; 161 | sg::begin_pass(&sg::Pass { compute: true, ..Default::default() }); 162 | sg::apply_pipeline(state.compute.pip); 163 | sg::apply_bindings(&state.compute.bind); 164 | sg::apply_uniforms(shader::UB_CS_PARAMS, &sg::value_as_range(&cs_params)); 165 | sg::dispatch((state.num_particles + 63) / 64, 1, 1); 166 | sg::end_pass(); 167 | 168 | // render pass to render the particles via instancing, with the 169 | // instance positions coming from the storage buffer 170 | state.ry += 60.0 * dt; 171 | let vs_params = compute_vs_params(state); 172 | sg::begin_pass(&sg::Pass { 173 | action: state.display.pass_action, 174 | swapchain: sglue::swapchain(), 175 | ..Default::default() 176 | }); 177 | sg::apply_pipeline(state.display.pip); 178 | sg::apply_bindings(&state.display.bind); 179 | sg::apply_uniforms(shader::UB_VS_PARAMS, &sg::value_as_range(&vs_params)); 180 | sg::draw(0, 24, state.num_particles); 181 | sg::end_pass(); 182 | sg::commit(); 183 | } 184 | 185 | extern "C" fn cleanup(user_data: *mut ffi::c_void) { 186 | sg::shutdown(); 187 | let _ = unsafe { Box::from_raw(user_data as *mut State) }; 188 | } 189 | 190 | fn draw_fallback(state: &State) { 191 | sg::begin_pass(&sg::Pass { 192 | action: state.display.pass_action, 193 | swapchain: sglue::swapchain(), 194 | ..Default::default() 195 | }); 196 | sg::end_pass(); 197 | sg::commit(); 198 | } 199 | 200 | fn compute_vs_params(state: &State) -> shader::VsParams { 201 | let proj = m::persp_mat4(60.0, sapp::widthf() / sapp::heightf(), 0.01, 50.0); 202 | let view = m::lookat_mat4(m::vec3(0.0, 1.5, 12.0), m::Vec3::ZERO, m::Vec3::UP); 203 | let view_proj = m::mul_mat4(proj, view); 204 | shader::VsParams { mvp: m::mul_mat4(view_proj, m::rotate_mat4(state.ry, m::vec3(0.0, 1.0, 0.0))) } 205 | } 206 | 207 | fn main() { 208 | let state = Box::new(State { 209 | num_particles: 0, 210 | ry: 0.0, 211 | compute: ComputeState { pip: sg::Pipeline::new(), bind: sg::Bindings::new() }, 212 | display: DisplayState { 213 | pip: sg::Pipeline::new(), 214 | bind: sg::Bindings::new(), 215 | pass_action: sg::PassAction::new(), 216 | }, 217 | }); 218 | let user_data = Box::into_raw(state) as *mut ffi::c_void; 219 | 220 | sapp::run(&sapp::Desc { 221 | init_userdata_cb: Some(init), 222 | frame_userdata_cb: Some(frame), 223 | cleanup_userdata_cb: Some(cleanup), 224 | user_data, 225 | width: 800, 226 | height: 600, 227 | sample_count: 4, 228 | window_title: c"instancing-compute.rs".as_ptr(), 229 | icon: sapp::IconDesc { sokol_default: true, ..Default::default() }, 230 | logger: sapp::Logger { func: Some(slog::slog_func), ..Default::default() }, 231 | ..Default::default() 232 | }); 233 | } 234 | -------------------------------------------------------------------------------- /examples/instancing-compute/math.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | #[repr(C)] 4 | #[derive(Debug, Clone, Copy)] 5 | pub struct Vec3 { 6 | pub x: f32, 7 | pub y: f32, 8 | pub z: f32, 9 | } 10 | 11 | impl Vec3 { 12 | pub const UP: Vec3 = vec3(0.0, 1.0, 0.0); 13 | pub const ZERO: Vec3 = vec3(0.0, 0.0, 0.0); 14 | } 15 | 16 | #[inline] 17 | pub const fn vec3(x: f32, y: f32, z: f32) -> Vec3 { 18 | Vec3 { x, y, z } 19 | } 20 | 21 | #[repr(C)] 22 | #[derive(Debug, Clone, Copy)] 23 | pub struct Vec2 { 24 | pub x: f32, 25 | pub y: f32, 26 | } 27 | 28 | impl Vec2 { 29 | pub const ZERO: Vec2 = vec2(0.0, 0.0); 30 | } 31 | 32 | #[inline] 33 | pub const fn vec2(x: f32, y: f32) -> Vec2 { 34 | Vec2 { x, y } 35 | } 36 | 37 | pub type Mat4 = [[f32; 4]; 4]; 38 | 39 | impl std::ops::Sub for Vec3 { 40 | type Output = Vec3; 41 | 42 | #[inline] 43 | fn sub(self, rhs: Vec3) -> Self::Output { 44 | sub_vec3(self, rhs) 45 | } 46 | } 47 | 48 | impl std::ops::Add for Vec3 { 49 | type Output = Vec3; 50 | 51 | #[inline] 52 | fn add(self, rhs: Vec3) -> Self::Output { 53 | add_vec3(self, rhs) 54 | } 55 | } 56 | 57 | impl std::ops::Mul for Vec3 { 58 | type Output = Vec3; 59 | 60 | #[inline] 61 | fn mul(self, rhs: f32) -> Self::Output { 62 | mul_vec3(self, rhs) 63 | } 64 | } 65 | 66 | pub fn sub_vec3(v0: Vec3, v1: Vec3) -> Vec3 { 67 | vec3(v0.x - v1.x, v0.y - v1.y, v0.z - v1.z) 68 | } 69 | 70 | pub fn add_vec3(v0: Vec3, v1: Vec3) -> Vec3 { 71 | vec3(v0.x + v1.x, v0.y + v1.y, v0.z + v1.z) 72 | } 73 | 74 | pub fn mul_vec3(v: Vec3, val: f32) -> Vec3 { 75 | vec3(v.x * val, v.y * val, v.z * val) 76 | } 77 | 78 | pub fn dot_vec3(v0: Vec3, v1: Vec3) -> f32 { 79 | v0.x * v1.x + v0.y * v1.y + v0.z * v1.z 80 | } 81 | 82 | #[inline] 83 | pub fn len_vec3(v: Vec3) -> f32 { 84 | dot_vec3(v, v).sqrt() 85 | } 86 | 87 | pub fn norm_vec3(v: Vec3) -> Vec3 { 88 | let l = len_vec3(v); 89 | 90 | if l != 0.0 { vec3(v.x / l, v.y / l, v.z / l) } else { Vec3::ZERO } 91 | } 92 | 93 | pub fn cross_vec3(v0: Vec3, v1: Vec3) -> Vec3 { 94 | vec3((v0.y * v1.z) - (v0.z * v1.y), (v0.z * v1.x) - (v0.x * v1.z), (v0.x * v1.y) - (v0.y * v1.x)) 95 | } 96 | 97 | pub const fn identity_mat4() -> Mat4 { 98 | let mut m = [[0.0; 4]; 4]; 99 | 100 | m[0][0] = 1.0; 101 | m[1][1] = 1.0; 102 | m[2][2] = 1.0; 103 | m[3][3] = 1.0; 104 | 105 | m 106 | } 107 | 108 | pub fn rotate_mat4(angle: f32, axis_unorm: Vec3) -> Mat4 { 109 | let mut m = identity_mat4(); 110 | 111 | let axis = norm_vec3(axis_unorm); 112 | let sin_theta = angle.to_radians().sin(); 113 | let cos_theta = angle.to_radians().cos(); 114 | let cos_value = 1.0 - cos_theta; 115 | 116 | m[0][0] = (axis.x * axis.x * cos_value) + cos_theta; 117 | m[0][1] = (axis.x * axis.y * cos_value) + (axis.z * sin_theta); 118 | m[0][2] = (axis.x * axis.z * cos_value) - (axis.y * sin_theta); 119 | m[1][0] = (axis.y * axis.x * cos_value) - (axis.z * sin_theta); 120 | m[1][1] = (axis.y * axis.y * cos_value) + cos_theta; 121 | m[1][2] = (axis.y * axis.z * cos_value) + (axis.x * sin_theta); 122 | m[2][0] = (axis.z * axis.x * cos_value) + (axis.y * sin_theta); 123 | m[2][1] = (axis.z * axis.y * cos_value) - (axis.x * sin_theta); 124 | m[2][2] = (axis.z * axis.z * cos_value) + cos_theta; 125 | 126 | m 127 | } 128 | 129 | pub fn persp_mat4(fov: f32, aspect: f32, near: f32, far: f32) -> Mat4 { 130 | let mut m = identity_mat4(); 131 | let t = f32::tan(fov * (std::f32::consts::PI / 360.0)); 132 | 133 | m[0][0] = 1.0 / t; 134 | m[1][1] = aspect / t; 135 | m[2][3] = -1.0; 136 | m[2][2] = (near + far) / (near - far); 137 | m[3][2] = (2.0 * near * far) / (near - far); 138 | m[3][3] = 0.0; 139 | 140 | m 141 | } 142 | 143 | pub fn lookat_mat4(eye: Vec3, center: Vec3, up: Vec3) -> Mat4 { 144 | let mut m = [[0.0; 4]; 4]; 145 | 146 | let f = norm_vec3(center - eye); 147 | let s = norm_vec3(cross_vec3(f, up)); 148 | let u = cross_vec3(s, f); 149 | 150 | m[0][0] = s.x; 151 | m[0][1] = u.x; 152 | m[0][2] = -f.x; 153 | 154 | m[1][0] = s.y; 155 | m[1][1] = u.y; 156 | m[1][2] = -f.y; 157 | 158 | m[2][0] = s.z; 159 | m[2][1] = u.z; 160 | m[2][2] = -f.z; 161 | 162 | m[3][0] = -dot_vec3(s, eye); 163 | m[3][1] = -dot_vec3(u, eye); 164 | m[3][2] = dot_vec3(f, eye); 165 | m[3][3] = 1.0; 166 | 167 | m 168 | } 169 | 170 | pub fn translate_mat4(translation: Vec3) -> Mat4 { 171 | let mut m = identity_mat4(); 172 | 173 | m[3][0] = translation.x; 174 | m[3][1] = translation.y; 175 | m[3][2] = translation.z; 176 | 177 | m 178 | } 179 | 180 | pub fn mul_mat4(left: Mat4, right: Mat4) -> Mat4 { 181 | let mut m = [[0.0; 4]; 4]; 182 | 183 | for col in 0..4 { 184 | for row in 0..4 { 185 | m[col][row] = left[0][row] * right[col][0] 186 | + left[1][row] * right[col][1] 187 | + left[2][row] * right[col][2] 188 | + left[3][row] * right[col][3]; 189 | } 190 | } 191 | 192 | m 193 | } 194 | -------------------------------------------------------------------------------- /examples/instancing-compute/shader.glsl: -------------------------------------------------------------------------------- 1 | @header use crate::math as m 2 | @ctype mat4 m::Mat4 3 | 4 | // shared data structures 5 | @block common 6 | struct particle { 7 | vec4 pos; 8 | vec4 vel; 9 | }; 10 | @end 11 | 12 | // compute-shader for initializing pseudo-random particle velocities 13 | @cs cs_init 14 | @include_block common 15 | 16 | layout(binding=0) buffer cs_ssbo { 17 | particle prt[]; 18 | }; 19 | 20 | layout(local_size_x=64, local_size_y=1, local_size_z=1) in; 21 | 22 | uint xorshift32(uint x) { 23 | x ^= x<<13; 24 | x ^= x>>17; 25 | x ^= x<<5; 26 | return x; 27 | } 28 | 29 | void main() { 30 | uint idx = gl_GlobalInvocationID.x; 31 | uint x = xorshift32(0x12345678 ^ idx); 32 | uint y = xorshift32(x); 33 | uint z = xorshift32(y); 34 | prt[idx].pos = vec4(0); 35 | prt[idx].vel = vec4( 36 | (float(x & 0x7FFF) / 0x7FFF) - 0.5f, 37 | (float(y & 0x7FFF) / 0x7FFF) * 0.5f + 2.0f, 38 | (float(z & 0x7FFF) / 0x7FFF) - 0.5f, 39 | 0); 40 | } 41 | @end 42 | @program init cs_init 43 | 44 | // compute-shader for updating particle positions 45 | @cs cs_update 46 | @include_block common 47 | 48 | layout(binding=0) uniform cs_params { 49 | float dt; 50 | int num_particles; 51 | }; 52 | 53 | layout(binding=0) buffer cs_ssbo { 54 | particle prt[]; 55 | }; 56 | 57 | layout(local_size_x=64, locaL_size_y=1, local_size_z=1) in; 58 | 59 | void main() { 60 | uint idx = gl_GlobalInvocationID.x; 61 | if (idx >= num_particles) { 62 | return; 63 | } 64 | vec4 pos = prt[idx].pos; 65 | vec4 vel = prt[idx].vel; 66 | vel.y -= 1.0 * dt; 67 | pos += vel * dt; 68 | if (pos.y < -2.0) { 69 | pos.y = -1.8; 70 | vel *= vec4(0.8, -0.8, 0.8, 0.0); 71 | } 72 | prt[idx].pos = pos; 73 | prt[idx].vel = vel; 74 | } 75 | @end 76 | @program update cs_update 77 | 78 | // vertex- and fragment-shader for rendering the particles 79 | @vs vs 80 | @include_block common 81 | 82 | layout(binding=0) uniform vs_params { 83 | mat4 mvp; 84 | }; 85 | 86 | layout(binding=0) readonly buffer vs_ssbo { 87 | particle prt[]; 88 | }; 89 | 90 | in vec3 pos; 91 | in vec4 color0; 92 | 93 | out vec4 color; 94 | 95 | void main() { 96 | vec3 inst_pos = prt[gl_InstanceIndex].pos.xyz; 97 | vec4 pos = vec4(pos + inst_pos, 1.0); 98 | gl_Position = mvp * pos; 99 | color = color0; 100 | } 101 | @end 102 | 103 | @fs fs 104 | in vec4 color; 105 | out vec4 frag_color; 106 | void main() { 107 | frag_color = color; 108 | } 109 | @end 110 | 111 | @program display vs fs 112 | -------------------------------------------------------------------------------- /examples/instancing/instancing.rs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // instancing.rs 3 | // 4 | // Demonstrate simple hardware-instancing using a static geometry buffer 5 | // and a dynamic instance-data buffer. 6 | //------------------------------------------------------------------------------ 7 | 8 | mod math; 9 | mod shader; 10 | 11 | use std::ffi; 12 | 13 | use math as m; 14 | use sokol::{app as sapp, gfx as sg, glue as sglue, log as slog}; 15 | 16 | pub const MAX_PARTICLES: usize = 512 * 1024; 17 | pub const NUM_PARTICLES_EMITTED_PER_FRAME: usize = 10; 18 | 19 | struct State { 20 | pub pass_action: sg::PassAction, 21 | pub pip: sg::Pipeline, 22 | pub bind: sg::Bindings, 23 | pub ry: f32, 24 | pub cur_num_particles: usize, 25 | pub pos: Vec, 26 | pub vel: Vec, 27 | } 28 | 29 | extern "C" fn init(user_data: *mut ffi::c_void) { 30 | let state = unsafe { &mut *(user_data as *mut State) }; 31 | 32 | sg::setup(&sg::Desc { 33 | environment: sglue::environment(), 34 | logger: sg::Logger { func: Some(slog::slog_func), ..Default::default() }, 35 | ..Default::default() 36 | }); 37 | 38 | // a pass action for the default render pass 39 | state.pass_action.colors[0] = sg::ColorAttachmentAction { 40 | load_action: sg::LoadAction::Clear, 41 | clear_value: sg::Color { r: 0.0, g: 0.0, b: 0.0, a: 1.0 }, 42 | ..Default::default() 43 | }; 44 | 45 | // vertex buffer for static geometry, goes into vertex-buffer-slot 0 46 | let r = 0.05; 47 | #[rustfmt::skip] 48 | let vertices: &[f32] = &[ 49 | // positions colors 50 | 0.0, -r, 0.0, 1.0, 0.0, 0.0, 1.0, 51 | r, 0.0, r, 0.0, 1.0, 0.0, 1.0, 52 | r, 0.0, -r, 0.0, 0.0, 1.0, 1.0, 53 | -r, 0.0, -r, 1.0, 1.0, 0.0, 1.0, 54 | -r, 0.0, r, 0.0, 1.0, 1.0, 1.0, 55 | 0.0, r, 0.0, 1.0, 0.0, 1.0, 1.0, 56 | ]; 57 | state.bind.vertex_buffers[0] = 58 | sg::make_buffer(&sg::BufferDesc { data: sg::slice_as_range(vertices), ..Default::default() }); 59 | 60 | // index buffer for static geometry 61 | #[rustfmt::skip] 62 | let indices: &[u16] = &[ 63 | 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 1, 64 | 5, 1, 2, 5, 2, 3, 5, 3, 4, 5, 4, 1, 65 | ]; 66 | state.bind.index_buffer = sg::make_buffer(&sg::BufferDesc { 67 | usage: sg::BufferUsage { index_buffer: true, ..Default::default() }, 68 | data: sg::slice_as_range(indices), 69 | ..Default::default() 70 | }); 71 | 72 | // empty, dynamic instance-data vertex buffer, goes into vertex-buffer-slot 1 73 | state.bind.vertex_buffers[1] = sg::make_buffer(&sg::BufferDesc { 74 | size: MAX_PARTICLES * std::mem::size_of::(), 75 | usage: sg::BufferUsage { stream_update: true, ..Default::default() }, 76 | ..Default::default() 77 | }); 78 | 79 | // a shader and pipeline object 80 | #[rustfmt::skip] 81 | let pip = sg::make_pipeline(&sg::PipelineDesc { 82 | shader: sg::make_shader(&shader::instancing_shader_desc(sg::query_backend())), 83 | layout: sg::VertexLayoutState { 84 | buffers: { 85 | let mut buffers = [sg::VertexBufferLayoutState::new(); sg::MAX_VERTEXBUFFER_BINDSLOTS]; 86 | 87 | // vertex buffer at slot 1 must step per instance 88 | buffers[1] = sg::VertexBufferLayoutState { step_func: sg::VertexStep::PerInstance, ..Default::default() }; 89 | 90 | buffers 91 | }, 92 | attrs: { 93 | let mut attrs = [sg::VertexAttrState::new(); sg::MAX_VERTEX_ATTRIBUTES]; 94 | 95 | attrs[shader::ATTR_INSTANCING_POS ] = sg::VertexAttrState { format: sg::VertexFormat::Float3, buffer_index: 0, ..Default::default() }; 96 | attrs[shader::ATTR_INSTANCING_COLOR0 ] = sg::VertexAttrState { format: sg::VertexFormat::Float4, buffer_index: 0, ..Default::default() }; 97 | attrs[shader::ATTR_INSTANCING_INST_POS] = sg::VertexAttrState { format: sg::VertexFormat::Float3, buffer_index: 1, ..Default::default() }; 98 | 99 | attrs 100 | }, 101 | }, 102 | index_type: sg::IndexType::Uint16, 103 | cull_mode: sg::CullMode::Back, 104 | depth: sg::DepthState { 105 | compare: sg::CompareFunc::LessEqual, 106 | write_enabled: true, 107 | ..Default::default() 108 | }, 109 | ..Default::default() 110 | }); 111 | state.pip = pip; 112 | } 113 | 114 | extern "C" fn frame(user_data: *mut ffi::c_void) { 115 | let state = unsafe { &mut *(user_data as *mut State) }; 116 | 117 | let frame_time = sapp::frame_duration() as f32; 118 | 119 | // emit new particles 120 | for _ in 0..NUM_PARTICLES_EMITTED_PER_FRAME { 121 | if state.cur_num_particles < MAX_PARTICLES { 122 | state.vel[state.cur_num_particles] = m::vec3(rand(-0.5, 0.5), rand(2.0, 2.5), rand(-0.5, 0.5)); 123 | state.cur_num_particles += 1; 124 | } else { 125 | break; 126 | } 127 | } 128 | 129 | // update particle positions 130 | for i in 0..state.cur_num_particles { 131 | state.vel[i].y -= 1.0 * frame_time; 132 | state.pos[i] = state.pos[i] + state.vel[i] * frame_time; 133 | 134 | // bounce back from ground 135 | if state.pos[i].y < -2.0 { 136 | state.pos[i].y = -1.8; 137 | state.vel[i].y = -state.vel[i].y; 138 | state.vel[i] = state.vel[i] * 0.8; 139 | } 140 | } 141 | 142 | // update instance data 143 | sg::update_buffer(state.bind.vertex_buffers[1], &sg::slice_as_range(state.pos.as_slice())); 144 | 145 | // vertex shader uniform data with model-view-projection matrix 146 | let proj = m::persp_mat4(60.0, sapp::widthf() / sapp::heightf(), 0.01, 50.0); 147 | let view = m::lookat_mat4(m::vec3(0.0, 1.5, 12.0), m::Vec3::ZERO, m::Vec3::UP); 148 | let view_proj = m::mul_mat4(proj, view); 149 | state.ry += 60.0 * frame_time; 150 | let vs_params = 151 | shader::VsParams { mvp: m::mul_mat4(view_proj, m::rotate_mat4(state.ry, m::vec3(0.0, 1.0, 0.0))) }; 152 | 153 | // ...and draw 154 | sg::begin_pass(&sg::Pass { 155 | action: state.pass_action, 156 | swapchain: sglue::swapchain(), 157 | ..Default::default() 158 | }); 159 | sg::apply_pipeline(state.pip); 160 | sg::apply_bindings(&state.bind); 161 | sg::apply_uniforms(shader::UB_VS_PARAMS, &sg::value_as_range(&vs_params)); 162 | sg::draw(0, 24, state.cur_num_particles); 163 | sg::end_pass(); 164 | sg::commit(); 165 | } 166 | 167 | extern "C" fn cleanup(user_data: *mut ffi::c_void) { 168 | sg::shutdown(); 169 | 170 | let _ = unsafe { Box::from_raw(user_data as *mut State) }; 171 | } 172 | 173 | fn main() { 174 | let state = Box::new(State { 175 | pass_action: sg::PassAction::new(), 176 | pip: sg::Pipeline::new(), 177 | bind: sg::Bindings::new(), 178 | ry: 0.0, 179 | cur_num_particles: 0, 180 | pos: vec![m::Vec3::ZERO; MAX_PARTICLES], 181 | vel: vec![m::Vec3::ZERO; MAX_PARTICLES], 182 | }); 183 | 184 | let user_data = Box::into_raw(state) as *mut ffi::c_void; 185 | 186 | sapp::run(&sapp::Desc { 187 | init_userdata_cb: Some(init), 188 | frame_userdata_cb: Some(frame), 189 | cleanup_userdata_cb: Some(cleanup), 190 | user_data, 191 | width: 800, 192 | height: 600, 193 | sample_count: 4, 194 | window_title: c"instancing.rs".as_ptr(), 195 | icon: sapp::IconDesc { sokol_default: true, ..Default::default() }, 196 | logger: sapp::Logger { func: Some(slog::slog_func), ..Default::default() }, 197 | ..Default::default() 198 | }); 199 | } 200 | 201 | fn xorshift32() -> u32 { 202 | static mut X: u32 = 0x12345678; 203 | 204 | unsafe { 205 | X ^= X << 13; 206 | X ^= X >> 17; 207 | X ^= X << 5; 208 | 209 | X 210 | } 211 | } 212 | 213 | fn rand(min_val: f32, max_val: f32) -> f32 { 214 | let r = xorshift32(); 215 | ((r & 0xFFFF) as f32 / 0x10000 as f32) * (max_val - min_val) + min_val 216 | } 217 | -------------------------------------------------------------------------------- /examples/instancing/math.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | #[repr(C)] 4 | #[derive(Debug, Clone, Copy)] 5 | pub struct Vec3 { 6 | pub x: f32, 7 | pub y: f32, 8 | pub z: f32, 9 | } 10 | 11 | impl Vec3 { 12 | pub const UP: Vec3 = vec3(0.0, 1.0, 0.0); 13 | pub const ZERO: Vec3 = vec3(0.0, 0.0, 0.0); 14 | } 15 | 16 | #[inline] 17 | pub const fn vec3(x: f32, y: f32, z: f32) -> Vec3 { 18 | Vec3 { x, y, z } 19 | } 20 | 21 | #[repr(C)] 22 | #[derive(Debug, Clone, Copy)] 23 | pub struct Vec2 { 24 | pub x: f32, 25 | pub y: f32, 26 | } 27 | 28 | impl Vec2 { 29 | pub const ZERO: Vec2 = vec2(0.0, 0.0); 30 | } 31 | 32 | #[inline] 33 | pub const fn vec2(x: f32, y: f32) -> Vec2 { 34 | Vec2 { x, y } 35 | } 36 | 37 | pub type Mat4 = [[f32; 4]; 4]; 38 | 39 | impl std::ops::Sub for Vec3 { 40 | type Output = Vec3; 41 | 42 | #[inline] 43 | fn sub(self, rhs: Vec3) -> Self::Output { 44 | sub_vec3(self, rhs) 45 | } 46 | } 47 | 48 | impl std::ops::Add for Vec3 { 49 | type Output = Vec3; 50 | 51 | #[inline] 52 | fn add(self, rhs: Vec3) -> Self::Output { 53 | add_vec3(self, rhs) 54 | } 55 | } 56 | 57 | impl std::ops::Mul for Vec3 { 58 | type Output = Vec3; 59 | 60 | #[inline] 61 | fn mul(self, rhs: f32) -> Self::Output { 62 | mul_vec3(self, rhs) 63 | } 64 | } 65 | 66 | pub fn sub_vec3(v0: Vec3, v1: Vec3) -> Vec3 { 67 | vec3(v0.x - v1.x, v0.y - v1.y, v0.z - v1.z) 68 | } 69 | 70 | pub fn add_vec3(v0: Vec3, v1: Vec3) -> Vec3 { 71 | vec3(v0.x + v1.x, v0.y + v1.y, v0.z + v1.z) 72 | } 73 | 74 | pub fn mul_vec3(v: Vec3, val: f32) -> Vec3 { 75 | vec3(v.x * val, v.y * val, v.z * val) 76 | } 77 | 78 | pub fn dot_vec3(v0: Vec3, v1: Vec3) -> f32 { 79 | v0.x * v1.x + v0.y * v1.y + v0.z * v1.z 80 | } 81 | 82 | #[inline] 83 | pub fn len_vec3(v: Vec3) -> f32 { 84 | dot_vec3(v, v).sqrt() 85 | } 86 | 87 | pub fn norm_vec3(v: Vec3) -> Vec3 { 88 | let l = len_vec3(v); 89 | 90 | if l != 0.0 { vec3(v.x / l, v.y / l, v.z / l) } else { Vec3::ZERO } 91 | } 92 | 93 | pub fn cross_vec3(v0: Vec3, v1: Vec3) -> Vec3 { 94 | vec3((v0.y * v1.z) - (v0.z * v1.y), (v0.z * v1.x) - (v0.x * v1.z), (v0.x * v1.y) - (v0.y * v1.x)) 95 | } 96 | 97 | pub const fn identity_mat4() -> Mat4 { 98 | let mut m = [[0.0; 4]; 4]; 99 | 100 | m[0][0] = 1.0; 101 | m[1][1] = 1.0; 102 | m[2][2] = 1.0; 103 | m[3][3] = 1.0; 104 | 105 | m 106 | } 107 | 108 | pub fn rotate_mat4(angle: f32, axis_unorm: Vec3) -> Mat4 { 109 | let mut m = identity_mat4(); 110 | 111 | let axis = norm_vec3(axis_unorm); 112 | let sin_theta = angle.to_radians().sin(); 113 | let cos_theta = angle.to_radians().cos(); 114 | let cos_value = 1.0 - cos_theta; 115 | 116 | m[0][0] = (axis.x * axis.x * cos_value) + cos_theta; 117 | m[0][1] = (axis.x * axis.y * cos_value) + (axis.z * sin_theta); 118 | m[0][2] = (axis.x * axis.z * cos_value) - (axis.y * sin_theta); 119 | m[1][0] = (axis.y * axis.x * cos_value) - (axis.z * sin_theta); 120 | m[1][1] = (axis.y * axis.y * cos_value) + cos_theta; 121 | m[1][2] = (axis.y * axis.z * cos_value) + (axis.x * sin_theta); 122 | m[2][0] = (axis.z * axis.x * cos_value) + (axis.y * sin_theta); 123 | m[2][1] = (axis.z * axis.y * cos_value) - (axis.x * sin_theta); 124 | m[2][2] = (axis.z * axis.z * cos_value) + cos_theta; 125 | 126 | m 127 | } 128 | 129 | pub fn persp_mat4(fov: f32, aspect: f32, near: f32, far: f32) -> Mat4 { 130 | let mut m = identity_mat4(); 131 | let t = f32::tan(fov * (std::f32::consts::PI / 360.0)); 132 | 133 | m[0][0] = 1.0 / t; 134 | m[1][1] = aspect / t; 135 | m[2][3] = -1.0; 136 | m[2][2] = (near + far) / (near - far); 137 | m[3][2] = (2.0 * near * far) / (near - far); 138 | m[3][3] = 0.0; 139 | 140 | m 141 | } 142 | 143 | pub fn lookat_mat4(eye: Vec3, center: Vec3, up: Vec3) -> Mat4 { 144 | let mut m = [[0.0; 4]; 4]; 145 | 146 | let f = norm_vec3(center - eye); 147 | let s = norm_vec3(cross_vec3(f, up)); 148 | let u = cross_vec3(s, f); 149 | 150 | m[0][0] = s.x; 151 | m[0][1] = u.x; 152 | m[0][2] = -f.x; 153 | 154 | m[1][0] = s.y; 155 | m[1][1] = u.y; 156 | m[1][2] = -f.y; 157 | 158 | m[2][0] = s.z; 159 | m[2][1] = u.z; 160 | m[2][2] = -f.z; 161 | 162 | m[3][0] = -dot_vec3(s, eye); 163 | m[3][1] = -dot_vec3(u, eye); 164 | m[3][2] = dot_vec3(f, eye); 165 | m[3][3] = 1.0; 166 | 167 | m 168 | } 169 | 170 | pub fn translate_mat4(translation: Vec3) -> Mat4 { 171 | let mut m = identity_mat4(); 172 | 173 | m[3][0] = translation.x; 174 | m[3][1] = translation.y; 175 | m[3][2] = translation.z; 176 | 177 | m 178 | } 179 | 180 | pub fn mul_mat4(left: Mat4, right: Mat4) -> Mat4 { 181 | let mut m = [[0.0; 4]; 4]; 182 | 183 | for col in 0..4 { 184 | for row in 0..4 { 185 | m[col][row] = left[0][row] * right[col][0] 186 | + left[1][row] * right[col][1] 187 | + left[2][row] * right[col][2] 188 | + left[3][row] * right[col][3]; 189 | } 190 | } 191 | 192 | m 193 | } 194 | -------------------------------------------------------------------------------- /examples/instancing/shader.glsl: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // shaders for instancing-sapp sample 3 | //------------------------------------------------------------------------------ 4 | 5 | @header use crate::math as m 6 | @ctype mat4 m::Mat4 7 | 8 | @vs vs 9 | layout(binding=0) uniform vs_params { 10 | mat4 mvp; 11 | }; 12 | 13 | in vec3 pos; 14 | in vec4 color0; 15 | in vec3 inst_pos; 16 | 17 | out vec4 color; 18 | 19 | void main() { 20 | vec4 pos = vec4(pos + inst_pos, 1.0); 21 | gl_Position = mvp * pos; 22 | color = color0; 23 | } 24 | @end 25 | 26 | @fs fs 27 | in vec4 color; 28 | out vec4 frag_color; 29 | void main() { 30 | frag_color = color; 31 | } 32 | @end 33 | 34 | @program instancing vs fs 35 | -------------------------------------------------------------------------------- /examples/mrt/math.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | #[repr(C)] 4 | #[derive(Debug, Clone, Copy)] 5 | pub struct Vec3 { 6 | pub x: f32, 7 | pub y: f32, 8 | pub z: f32, 9 | } 10 | 11 | impl Vec3 { 12 | pub const UP: Vec3 = vec3(0.0, 1.0, 0.0); 13 | pub const ZERO: Vec3 = vec3(0.0, 0.0, 0.0); 14 | } 15 | 16 | #[inline] 17 | pub const fn vec3(x: f32, y: f32, z: f32) -> Vec3 { 18 | Vec3 { x, y, z } 19 | } 20 | 21 | #[repr(C)] 22 | #[derive(Debug, Clone, Copy)] 23 | pub struct Vec2 { 24 | pub x: f32, 25 | pub y: f32, 26 | } 27 | 28 | impl Vec2 { 29 | pub const ZERO: Vec2 = vec2(0.0, 0.0); 30 | } 31 | 32 | #[inline] 33 | pub const fn vec2(x: f32, y: f32) -> Vec2 { 34 | Vec2 { x, y } 35 | } 36 | 37 | pub type Mat4 = [[f32; 4]; 4]; 38 | 39 | impl std::ops::Sub for Vec3 { 40 | type Output = Vec3; 41 | 42 | #[inline] 43 | fn sub(self, rhs: Vec3) -> Self::Output { 44 | sub_vec3(self, rhs) 45 | } 46 | } 47 | 48 | pub fn sub_vec3(v0: Vec3, v1: Vec3) -> Vec3 { 49 | vec3(v0.x - v1.x, v0.y - v1.y, v0.z - v1.z) 50 | } 51 | 52 | pub fn dot_vec3(v0: Vec3, v1: Vec3) -> f32 { 53 | v0.x * v1.x + v0.y * v1.y + v0.z * v1.z 54 | } 55 | 56 | #[inline] 57 | pub fn len_vec3(v: Vec3) -> f32 { 58 | dot_vec3(v, v).sqrt() 59 | } 60 | 61 | pub fn norm_vec3(v: Vec3) -> Vec3 { 62 | let l = len_vec3(v); 63 | 64 | if l != 0.0 { vec3(v.x / l, v.y / l, v.z / l) } else { Vec3::ZERO } 65 | } 66 | 67 | pub fn cross_vec3(v0: Vec3, v1: Vec3) -> Vec3 { 68 | vec3((v0.y * v1.z) - (v0.z * v1.y), (v0.z * v1.x) - (v0.x * v1.z), (v0.x * v1.y) - (v0.y * v1.x)) 69 | } 70 | 71 | pub const fn identity_mat4() -> Mat4 { 72 | let mut m = [[0.0; 4]; 4]; 73 | 74 | m[0][0] = 1.0; 75 | m[1][1] = 1.0; 76 | m[2][2] = 1.0; 77 | m[3][3] = 1.0; 78 | 79 | m 80 | } 81 | 82 | pub fn rotate_mat4(angle: f32, axis_unorm: Vec3) -> Mat4 { 83 | let mut m = identity_mat4(); 84 | 85 | let axis = norm_vec3(axis_unorm); 86 | let sin_theta = angle.to_radians().sin(); 87 | let cos_theta = angle.to_radians().cos(); 88 | let cos_value = 1.0 - cos_theta; 89 | 90 | m[0][0] = (axis.x * axis.x * cos_value) + cos_theta; 91 | m[0][1] = (axis.x * axis.y * cos_value) + (axis.z * sin_theta); 92 | m[0][2] = (axis.x * axis.z * cos_value) - (axis.y * sin_theta); 93 | m[1][0] = (axis.y * axis.x * cos_value) - (axis.z * sin_theta); 94 | m[1][1] = (axis.y * axis.y * cos_value) + cos_theta; 95 | m[1][2] = (axis.y * axis.z * cos_value) + (axis.x * sin_theta); 96 | m[2][0] = (axis.z * axis.x * cos_value) + (axis.y * sin_theta); 97 | m[2][1] = (axis.z * axis.y * cos_value) - (axis.x * sin_theta); 98 | m[2][2] = (axis.z * axis.z * cos_value) + cos_theta; 99 | 100 | m 101 | } 102 | 103 | pub fn persp_mat4(fov: f32, aspect: f32, near: f32, far: f32) -> Mat4 { 104 | let mut m = identity_mat4(); 105 | let t = f32::tan(fov * (std::f32::consts::PI / 360.0)); 106 | 107 | m[0][0] = 1.0 / t; 108 | m[1][1] = aspect / t; 109 | m[2][3] = -1.0; 110 | m[2][2] = (near + far) / (near - far); 111 | m[3][2] = (2.0 * near * far) / (near - far); 112 | m[3][3] = 0.0; 113 | 114 | m 115 | } 116 | 117 | pub fn lookat_mat4(eye: Vec3, center: Vec3, up: Vec3) -> Mat4 { 118 | let mut m = [[0.0; 4]; 4]; 119 | 120 | let f = norm_vec3(center - eye); 121 | let s = norm_vec3(cross_vec3(f, up)); 122 | let u = cross_vec3(s, f); 123 | 124 | m[0][0] = s.x; 125 | m[0][1] = u.x; 126 | m[0][2] = -f.x; 127 | 128 | m[1][0] = s.y; 129 | m[1][1] = u.y; 130 | m[1][2] = -f.y; 131 | 132 | m[2][0] = s.z; 133 | m[2][1] = u.z; 134 | m[2][2] = -f.z; 135 | 136 | m[3][0] = -dot_vec3(s, eye); 137 | m[3][1] = -dot_vec3(u, eye); 138 | m[3][2] = dot_vec3(f, eye); 139 | m[3][3] = 1.0; 140 | 141 | m 142 | } 143 | 144 | pub fn translate_mat4(translation: Vec3) -> Mat4 { 145 | let mut m = identity_mat4(); 146 | 147 | m[3][0] = translation.x; 148 | m[3][1] = translation.y; 149 | m[3][2] = translation.z; 150 | 151 | m 152 | } 153 | 154 | pub fn mul_mat4(left: Mat4, right: Mat4) -> Mat4 { 155 | let mut m = [[0.0; 4]; 4]; 156 | 157 | for col in 0..4 { 158 | for row in 0..4 { 159 | m[col][row] = left[0][row] * right[col][0] 160 | + left[1][row] * right[col][1] 161 | + left[2][row] * right[col][2] 162 | + left[3][row] * right[col][3]; 163 | } 164 | } 165 | 166 | m 167 | } 168 | -------------------------------------------------------------------------------- /examples/mrt/mrt.rs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // mrt.rs 3 | // 4 | // Rendering with multiple-rendertargets, and reallocate render targets 5 | // on window resize events. 6 | // 7 | // NOTE: the rotation direction will appear different on the different 8 | // backend 3D APIs. This is because of the different image origin conventions 9 | // in GL vs D3D vs Metal. We don't care about those differences in this sample 10 | // (using the sokol shader compiler allows to easily 'normalize' those differences. 11 | //------------------------------------------------------------------------------ 12 | mod math; 13 | mod shader; 14 | 15 | use std::ffi; 16 | 17 | use math as m; 18 | use sokol::{app as sapp, gfx as sg, glue as sglue, log as slog}; 19 | 20 | const OFFSCREEN_SAMPLE_COUNT: usize = 1; 21 | const NUM_MRTS: usize = 3; 22 | 23 | struct Images { 24 | pub color: [sg::Image; NUM_MRTS], 25 | pub resolve: [sg::Image; NUM_MRTS], 26 | pub depth: sg::Image, 27 | } 28 | 29 | struct Offscreen { 30 | pub pass: sg::Pass, 31 | pub pip: sg::Pipeline, 32 | pub bind: sg::Bindings, 33 | } 34 | 35 | struct Display { 36 | pub pip: sg::Pipeline, 37 | pub bind: sg::Bindings, 38 | pub pass_action: sg::PassAction, 39 | } 40 | 41 | struct Dbg { 42 | pub pip: sg::Pipeline, 43 | pub bind: sg::Bindings, 44 | } 45 | 46 | struct State { 47 | pub images: Images, 48 | pub offscreen: Offscreen, 49 | pub display: Display, 50 | pub dbg: Dbg, 51 | pub rx: f32, 52 | pub ry: f32, 53 | pub view: m::Mat4, 54 | } 55 | 56 | extern "C" fn init(user_data: *mut ffi::c_void) { 57 | let state = unsafe { &mut *(user_data as *mut State) }; 58 | 59 | state.view = m::lookat_mat4(m::vec3(0.0, 1.5, 6.0), m::Vec3::ZERO, m::vec3(0.0, 1.0, 0.0)); 60 | 61 | sg::setup(&sg::Desc { 62 | environment: sglue::environment(), 63 | logger: sg::Logger { func: Some(slog::slog_func), ..Default::default() }, 64 | ..Default::default() 65 | }); 66 | 67 | // setup the offscreen render pass resources, this will also be called when the window resizes 68 | recreate_offscreen_attachments(sapp::width(), sapp::height(), state); 69 | 70 | // setup pass action for default render pass 71 | state.display.pass_action.colors[0] = 72 | sg::ColorAttachmentAction { load_action: sg::LoadAction::Dontcare, ..Default::default() }; 73 | state.display.pass_action.depth = 74 | sg::DepthAttachmentAction { load_action: sg::LoadAction::Dontcare, ..Default::default() }; 75 | state.display.pass_action.stencil = 76 | sg::StencilAttachmentAction { load_action: sg::LoadAction::Dontcare, ..Default::default() }; 77 | 78 | // set pass action for offscreen render pass 79 | state.offscreen.pass.action.colors[0] = sg::ColorAttachmentAction { 80 | load_action: sg::LoadAction::Clear, 81 | clear_value: sg::Color { r: 0.25, g: 0.0, b: 0.0, a: 1.0 }, 82 | ..Default::default() 83 | }; 84 | state.offscreen.pass.action.colors[1] = sg::ColorAttachmentAction { 85 | load_action: sg::LoadAction::Clear, 86 | clear_value: sg::Color { r: 0.0, g: 0.25, b: 0.0, a: 1.0 }, 87 | ..Default::default() 88 | }; 89 | state.offscreen.pass.action.colors[2] = sg::ColorAttachmentAction { 90 | load_action: sg::LoadAction::Clear, 91 | clear_value: sg::Color { r: 0.0, g: 0.0, b: 0.25, a: 1.0 }, 92 | ..Default::default() 93 | }; 94 | 95 | #[rustfmt::skip] 96 | const VERTICES: &[f32] = &[ 97 | // positions brightness 98 | -1.0, -1.0, -1.0, 1.0, 99 | 1.0, -1.0, -1.0, 1.0, 100 | 1.0, 1.0, -1.0, 1.0, 101 | -1.0, 1.0, -1.0, 1.0, 102 | 103 | -1.0, -1.0, 1.0, 0.8, 104 | 1.0, -1.0, 1.0, 0.8, 105 | 1.0, 1.0, 1.0, 0.8, 106 | -1.0, 1.0, 1.0, 0.8, 107 | 108 | -1.0, -1.0, -1.0, 0.6, 109 | -1.0, 1.0, -1.0, 0.6, 110 | -1.0, 1.0, 1.0, 0.6, 111 | -1.0, -1.0, 1.0, 0.6, 112 | 113 | 1.0, -1.0, -1.0, 0.0, 114 | 1.0, 1.0, -1.0, 0.0, 115 | 1.0, 1.0, 1.0, 0.0, 116 | 1.0, -1.0, 1.0, 0.0, 117 | 118 | -1.0, -1.0, -1.0, 0.5, 119 | -1.0, -1.0, 1.0, 0.5, 120 | 1.0, -1.0, 1.0, 0.5, 121 | 1.0, -1.0, -1.0, 0.5, 122 | 123 | -1.0, 1.0, -1.0, 0.7, 124 | -1.0, 1.0, 1.0, 0.7, 125 | 1.0, 1.0, 1.0, 0.7, 126 | 1.0, 1.0, -1.0, 0.7, 127 | ]; 128 | 129 | // create vertex buffer for a cube 130 | state.offscreen.bind.vertex_buffers[0] = 131 | sg::make_buffer(&sg::BufferDesc { data: sg::slice_as_range(VERTICES), ..Default::default() }); 132 | 133 | #[rustfmt::skip] 134 | const INDICES: &[u16] = &[ 135 | 0, 1, 2, 0, 2, 3, 136 | 6, 5, 4, 7, 6, 4, 137 | 8, 9, 10, 8, 10, 11, 138 | 14, 13, 12, 15, 14, 12, 139 | 16, 17, 18, 16, 18, 19, 140 | 22, 21, 20, 23, 22, 20, 141 | ]; 142 | 143 | // index buffer for a cube 144 | state.offscreen.bind.index_buffer = sg::make_buffer(&sg::BufferDesc { 145 | usage: sg::BufferUsage { index_buffer: true, ..Default::default() }, 146 | data: sg::slice_as_range(INDICES), 147 | ..Default::default() 148 | }); 149 | 150 | // shader and pipeline state object for rendering cube into MRT render targets 151 | let mut offscreen_pip_desc = sg::PipelineDesc { 152 | shader: sg::make_shader(&shader::offscreen_shader_desc(sg::query_backend())), 153 | index_type: sg::IndexType::Uint16, 154 | cull_mode: sg::CullMode::Back, 155 | sample_count: OFFSCREEN_SAMPLE_COUNT as _, 156 | depth: sg::DepthState { 157 | pixel_format: sg::PixelFormat::Depth, 158 | compare: sg::CompareFunc::LessEqual, 159 | write_enabled: true, 160 | ..Default::default() 161 | }, 162 | color_count: 3, 163 | ..Default::default() 164 | }; 165 | offscreen_pip_desc.layout.attrs[shader::ATTR_OFFSCREEN_POS].format = sg::VertexFormat::Float3; 166 | offscreen_pip_desc.layout.attrs[shader::ATTR_OFFSCREEN_BRIGHT0].format = sg::VertexFormat::Float; 167 | state.offscreen.pip = sg::make_pipeline(&offscreen_pip_desc); 168 | 169 | const QUAD_VERTICES: &[f32] = &[0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0]; 170 | // a vertex buffer to render a fullscreen quad 171 | let quad_vbuf = 172 | sg::make_buffer(&sg::BufferDesc { data: sg::slice_as_range(QUAD_VERTICES), ..Default::default() }); 173 | state.display.bind.vertex_buffers[0] = quad_vbuf; 174 | state.dbg.bind.vertex_buffers[0] = quad_vbuf; 175 | 176 | // shader and pipeline object to render a fullscreen quad which composes 177 | // the 3 offscreen render targets into the default framebuffer 178 | let mut display_pip_desc = sg::PipelineDesc { 179 | shader: sg::make_shader(&shader::fsq_shader_desc(sg::query_backend())), 180 | primitive_type: sg::PrimitiveType::TriangleStrip, 181 | ..Default::default() 182 | }; 183 | display_pip_desc.layout.attrs[shader::ATTR_FSQ_POS].format = sg::VertexFormat::Float2; 184 | state.display.pip = sg::make_pipeline(&display_pip_desc); 185 | 186 | // a sampler to sample the offscreen render targets as texture 187 | let smp = sg::make_sampler(&sg::SamplerDesc { 188 | min_filter: sg::Filter::Linear, 189 | mag_filter: sg::Filter::Linear, 190 | wrap_u: sg::Wrap::ClampToEdge, 191 | wrap_v: sg::Wrap::ClampToEdge, 192 | ..Default::default() 193 | }); 194 | state.display.bind.samplers[shader::SMP_SMP] = smp; 195 | state.dbg.bind.samplers[shader::SMP_SMP] = smp; 196 | 197 | // shader, pipeline and resource bindings to render debug visualization quads 198 | let mut dbg_pip_desc = sg::PipelineDesc { 199 | shader: sg::make_shader(&shader::dbg_shader_desc(sg::query_backend())), 200 | primitive_type: sg::PrimitiveType::TriangleStrip, 201 | ..Default::default() 202 | }; 203 | dbg_pip_desc.layout.attrs[shader::ATTR_DBG_POS].format = sg::VertexFormat::Float2; 204 | state.dbg.pip = sg::make_pipeline(&dbg_pip_desc); 205 | } 206 | 207 | extern "C" fn frame(user_data: *mut ffi::c_void) { 208 | let state = unsafe { &mut *(user_data as *mut State) }; 209 | 210 | let dt = (sapp::frame_duration() * 60.0) as f32; 211 | state.rx += 1.0 * dt; 212 | state.ry += 2.0 * dt; 213 | 214 | // compute shader uniform data 215 | let offscreen_params = shader::OffscreenParams { mvp: compute_mvp(state.rx, state.ry) }; 216 | let fsq_params = shader::FsqParams { 217 | offset: m::vec2(f32::sin(state.rx * 0.01) * 0.1, f32::cos(state.ry * 0.01) * 0.1), 218 | _pad_8: [0; 8], 219 | }; 220 | 221 | // render cube into MRT offscreen render targets 222 | sg::begin_pass(&state.offscreen.pass); 223 | sg::apply_pipeline(state.offscreen.pip); 224 | sg::apply_bindings(&state.offscreen.bind); 225 | sg::apply_uniforms(shader::UB_OFFSCREEN_PARAMS, &sg::value_as_range(&offscreen_params)); 226 | sg::draw(0, 36, 1); 227 | sg::end_pass(); 228 | 229 | // render fullscreen quad with the composed offscreen-render images, 230 | // 3 a small debug view quads at the bottom of the screen 231 | sg::begin_pass(&sg::Pass { 232 | action: state.display.pass_action, 233 | swapchain: sglue::swapchain(), 234 | ..Default::default() 235 | }); 236 | sg::apply_pipeline(state.display.pip); 237 | sg::apply_bindings(&state.display.bind); 238 | sg::apply_uniforms(shader::UB_FSQ_PARAMS, &sg::value_as_range(&fsq_params)); 239 | sg::draw(0, 4, 1); 240 | sg::apply_pipeline(state.dbg.pip); 241 | for i in 0..NUM_MRTS { 242 | sg::apply_viewport(i as i32 * 100, 0, 100, 100, false); 243 | state.dbg.bind.views[shader::VIEW_TEX] = state.display.bind.views[i]; 244 | sg::apply_bindings(&state.dbg.bind); 245 | sg::draw(0, 4, 1); 246 | } 247 | sg::end_pass(); 248 | sg::commit(); 249 | } 250 | 251 | extern "C" fn event(event: *const sapp::Event, user_data: *mut ffi::c_void) { 252 | let state = unsafe { &mut *(user_data as *mut State) }; 253 | let event = unsafe { &*event }; 254 | 255 | if event._type == sapp::EventType::Resized { 256 | recreate_offscreen_attachments(event.framebuffer_width, event.framebuffer_height, state); 257 | } 258 | } 259 | 260 | // helper function to create or re-create attachment resources 261 | fn recreate_offscreen_attachments(width: i32, height: i32, state: &mut State) { 262 | for i in 0..NUM_MRTS { 263 | // color attachment images and views 264 | sg::destroy_image(state.images.color[i]); 265 | state.images.color[i] = sg::make_image(&sg::ImageDesc { 266 | usage: sg::ImageUsage { color_attachment: true, ..Default::default() }, 267 | width, 268 | height, 269 | sample_count: OFFSCREEN_SAMPLE_COUNT as _, 270 | ..Default::default() 271 | }); 272 | sg::destroy_view(state.offscreen.pass.attachments.colors[i]); 273 | state.offscreen.pass.attachments.colors[i] = sg::make_view(&sg::ViewDesc { 274 | color_attachment: sg::ImageViewDesc { image: state.images.color[i], ..Default::default() }, 275 | ..Default::default() 276 | }); 277 | 278 | // resolve attachment images and views 279 | sg::destroy_image(state.images.resolve[i]); 280 | state.images.resolve[i] = sg::make_image(&sg::ImageDesc { 281 | usage: sg::ImageUsage { resolve_attachment: true, ..Default::default() }, 282 | width, 283 | height, 284 | sample_count: 1, 285 | ..Default::default() 286 | }); 287 | sg::destroy_view(state.offscreen.pass.attachments.resolves[i]); 288 | state.offscreen.pass.attachments.resolves[i] = sg::make_view(&sg::ViewDesc { 289 | resolve_attachment: sg::ImageViewDesc { image: state.images.resolve[i], ..Default::default() }, 290 | ..Default::default() 291 | }); 292 | 293 | // the resolve images are also sampled as textures, so need a texture view 294 | sg::destroy_view(state.display.bind.views[i]); 295 | state.display.bind.views[i] = sg::make_view(&sg::ViewDesc { 296 | texture: sg::TextureViewDesc { image: state.images.resolve[i], ..Default::default() }, 297 | ..Default::default() 298 | }); 299 | } 300 | 301 | // depth-stencil attachment image and view 302 | sg::destroy_image(state.images.depth); 303 | state.images.depth = sg::make_image(&sg::ImageDesc { 304 | usage: sg::ImageUsage { depth_stencil_attachment: true, ..Default::default() }, 305 | width, 306 | height, 307 | sample_count: OFFSCREEN_SAMPLE_COUNT as _, 308 | pixel_format: sg::PixelFormat::Depth, 309 | ..Default::default() 310 | }); 311 | sg::destroy_view(state.offscreen.pass.attachments.depth_stencil); 312 | state.offscreen.pass.attachments.depth_stencil = sg::make_view(&sg::ViewDesc { 313 | depth_stencil_attachment: sg::ImageViewDesc { image: state.images.depth, ..Default::default() }, 314 | ..Default::default() 315 | }); 316 | } 317 | 318 | pub fn compute_mvp(rx: f32, ry: f32) -> [[f32; 4]; 4] { 319 | let proj = m::persp_mat4(60.0, sapp::widthf() / sapp::heightf(), 0.01, 10.0); 320 | let view = m::lookat_mat4(m::vec3(0.0, 1.5, 6.0), m::Vec3::ZERO, m::Vec3::UP); 321 | let view_proj = m::mul_mat4(proj, view); 322 | let rxm = m::rotate_mat4(rx, m::vec3(1.0, 0.0, 0.0)); 323 | let rym = m::rotate_mat4(ry, m::vec3(0.0, 1.0, 0.0)); 324 | let model = m::mul_mat4(rxm, rym); 325 | 326 | m::mul_mat4(view_proj, model) 327 | } 328 | 329 | extern "C" fn cleanup(user_data: *mut ffi::c_void) { 330 | sg::shutdown(); 331 | 332 | let _ = unsafe { Box::from_raw(user_data as *mut State) }; 333 | } 334 | 335 | fn main() { 336 | let state = Box::new(State { 337 | images: Images { 338 | color: [sg::Image::new(), sg::Image::new(), sg::Image::new()], 339 | resolve: [sg::Image::new(), sg::Image::new(), sg::Image::new()], 340 | depth: sg::Image::new(), 341 | }, 342 | offscreen: Offscreen { pass: sg::Pass::new(), pip: sg::Pipeline::new(), bind: sg::Bindings::new() }, 343 | display: Display { 344 | pip: sg::Pipeline::new(), 345 | bind: sg::Bindings::new(), 346 | pass_action: sg::PassAction::new(), 347 | }, 348 | dbg: Dbg { pip: sg::Pipeline::new(), bind: sg::Bindings::new() }, 349 | rx: 0.0, 350 | ry: 0.0, 351 | view: [[0.0; 4]; 4], 352 | }); 353 | 354 | let user_data = Box::into_raw(state) as *mut ffi::c_void; 355 | 356 | sapp::run(&sapp::Desc { 357 | init_userdata_cb: Some(init), 358 | frame_userdata_cb: Some(frame), 359 | cleanup_userdata_cb: Some(cleanup), 360 | event_userdata_cb: Some(event), 361 | user_data, 362 | width: 800, 363 | height: 600, 364 | sample_count: 1, 365 | window_title: c"mrt.rs".as_ptr(), 366 | logger: sapp::Logger { func: Some(sokol::log::slog_func), ..Default::default() }, 367 | icon: sapp::IconDesc { sokol_default: true, ..Default::default() }, 368 | 369 | ..Default::default() 370 | }); 371 | } 372 | -------------------------------------------------------------------------------- /examples/mrt/shader.glsl: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // shaders for mrt-sapp sample 3 | //------------------------------------------------------------------------------ 4 | @header use crate::math as m 5 | 6 | @ctype mat4 m::Mat4 7 | @ctype vec2 m::Vec2 8 | 9 | // shaders for offscreen-pass rendering 10 | @vs vs_offscreen 11 | 12 | layout(binding=0) uniform offscreen_params { 13 | mat4 mvp; 14 | }; 15 | 16 | in vec4 pos; 17 | in float bright0; 18 | 19 | out float bright; 20 | 21 | void main() { 22 | gl_Position = mvp * pos; 23 | bright = bright0; 24 | } 25 | @end 26 | 27 | @fs fs_offscreen 28 | in float bright; 29 | 30 | layout(location=0) out vec4 frag_color_0; 31 | layout(location=1) out vec4 frag_color_1; 32 | layout(location=2) out vec4 frag_color_2; 33 | 34 | void main() { 35 | frag_color_0 = vec4(bright, 0.0, 0.0, 1.0); 36 | frag_color_1 = vec4(0.0, bright, 0.0, 1.0); 37 | frag_color_2 = vec4(0.0, 0.0, bright, 1.0); 38 | } 39 | @end 40 | 41 | @program offscreen vs_offscreen fs_offscreen 42 | 43 | // shaders for rendering a fullscreen-quad in default pass 44 | @vs vs_fsq 45 | @glsl_options flip_vert_y 46 | 47 | layout(binding=0) uniform fsq_params { 48 | vec2 offset; 49 | }; 50 | 51 | in vec2 pos; 52 | 53 | out vec2 uv0; 54 | out vec2 uv1; 55 | out vec2 uv2; 56 | 57 | void main() { 58 | gl_Position = vec4(pos*2.0-1.0, 0.5, 1.0); 59 | uv0 = pos + vec2(offset.x, 0.0); 60 | uv1 = pos + vec2(0.0, offset.y); 61 | uv2 = pos; 62 | } 63 | @end 64 | 65 | @fs fs_fsq 66 | layout(binding=0) uniform texture2D tex0; 67 | layout(binding=1) uniform texture2D tex1; 68 | layout(binding=2) uniform texture2D tex2; 69 | layout(binding=0) uniform sampler smp; 70 | 71 | in vec2 uv0; 72 | in vec2 uv1; 73 | in vec2 uv2; 74 | 75 | out vec4 frag_color; 76 | 77 | void main() { 78 | vec3 c0 = texture(sampler2D(tex0, smp), uv0).xyz; 79 | vec3 c1 = texture(sampler2D(tex1, smp), uv1).xyz; 80 | vec3 c2 = texture(sampler2D(tex2, smp), uv2).xyz; 81 | frag_color = vec4(c0 + c1 + c2, 1.0); 82 | } 83 | @end 84 | 85 | @program fsq vs_fsq fs_fsq 86 | 87 | // shaders for rendering a debug visualization 88 | @vs vs_dbg 89 | @glsl_options flip_vert_y 90 | 91 | in vec2 pos; 92 | out vec2 uv; 93 | 94 | void main() { 95 | gl_Position = vec4(pos*2.0-1.0, 0.5, 1.0); 96 | uv = pos; 97 | } 98 | @end 99 | 100 | @fs fs_dbg 101 | layout(binding=0) uniform texture2D tex; 102 | layout(binding=0) uniform sampler smp; 103 | 104 | in vec2 uv; 105 | out vec4 frag_color; 106 | 107 | void main() { 108 | frag_color = vec4(texture(sampler2D(tex, smp), uv).xyz, 1.0); 109 | } 110 | @end 111 | 112 | @program dbg vs_dbg fs_dbg 113 | -------------------------------------------------------------------------------- /examples/sgl-context/sgl-context.rs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // sgl-context/sgl-context.rs 3 | // 4 | // Demonstrates how to render in different render passes with sokol/gl 5 | // using sokol/gl contexts. 6 | //------------------------------------------------------------------------------ 7 | 8 | use std::ffi; 9 | 10 | use sokol::{app as sapp, gfx as sg, gl as sgl, glue as sglue, log as slog}; 11 | 12 | const OFFSCREEN_PIXELFORMAT: sg::PixelFormat = sg::PixelFormat::Rgba8; 13 | const OFFSCREEN_SAMPLECOUNT: i32 = 1; 14 | const OFFSCREEN_WIDTH: i32 = 32; 15 | const OFFSCREEN_HEIGHT: i32 = 32; 16 | 17 | struct Offscreen { 18 | pass: sg::Pass, 19 | sgl_context: sgl::Context, 20 | } 21 | 22 | struct Display { 23 | pass_action: sg::PassAction, 24 | tex_view: sg::View, 25 | smp: sg::Sampler, 26 | sgl_pip: sgl::Pipeline, 27 | } 28 | 29 | struct State { 30 | offscreen: Offscreen, 31 | display: Display, 32 | } 33 | 34 | extern "C" fn init(user_data: *mut ffi::c_void) { 35 | let state = unsafe { &mut *(user_data as *mut State) }; 36 | 37 | sg::setup(&sg::Desc { 38 | environment: sglue::environment(), 39 | logger: sg::Logger { func: Some(slog::slog_func), ..Default::default() }, 40 | ..Default::default() 41 | }); 42 | 43 | sgl::setup(&sgl::Desc { 44 | max_vertices: 64, 45 | max_commands: 16, 46 | logger: sgl::Logger { func: Some(slog::slog_func), ..Default::default() }, 47 | ..Default::default() 48 | }); 49 | 50 | state.display.pass_action.colors[0] = sg::ColorAttachmentAction { 51 | load_action: sg::LoadAction::Clear, 52 | clear_value: sg::Color { r: 0.5, g: 0.7, b: 1.0, a: 1.0 }, 53 | ..Default::default() 54 | }; 55 | state.offscreen.pass.action.colors[0] = sg::ColorAttachmentAction { 56 | load_action: sg::LoadAction::Clear, 57 | clear_value: sg::Color { r: 0.0, g: 0.0, b: 0.0, a: 1.0 }, 58 | ..Default::default() 59 | }; 60 | 61 | state.display.sgl_pip = sgl::context_make_pipeline( 62 | sgl::default_context(), 63 | &sg::PipelineDesc { 64 | cull_mode: sg::CullMode::Back, 65 | depth: sg::DepthState { 66 | write_enabled: true, 67 | compare: sg::CompareFunc::LessEqual, 68 | ..Default::default() 69 | }, 70 | ..Default::default() 71 | }, 72 | ); 73 | 74 | // create a sokol-gl context compatible with the offscreen render pass 75 | // (specific color pixel format, no depth-stencil-surface, no MSAA) 76 | state.offscreen.sgl_context = sgl::make_context(&sgl::ContextDesc { 77 | max_vertices: 8, 78 | max_commands: 4, 79 | color_format: OFFSCREEN_PIXELFORMAT, 80 | depth_format: sg::PixelFormat::None, 81 | sample_count: OFFSCREEN_SAMPLECOUNT, 82 | ..Default::default() 83 | }); 84 | 85 | // an color attachment image and views 86 | let img = sg::make_image(&sg::ImageDesc { 87 | usage: sg::ImageUsage { color_attachment: true, ..Default::default() }, 88 | width: OFFSCREEN_WIDTH, 89 | height: OFFSCREEN_HEIGHT, 90 | pixel_format: OFFSCREEN_PIXELFORMAT, 91 | sample_count: OFFSCREEN_SAMPLECOUNT, 92 | ..Default::default() 93 | }); 94 | state.offscreen.pass.attachments.colors[0] = sg::make_view(&sg::ViewDesc { 95 | color_attachment: sg::ImageViewDesc { image: img, ..Default::default() }, 96 | ..Default::default() 97 | }); 98 | state.display.tex_view = sg::make_view(&sg::ViewDesc { 99 | texture: sg::TextureViewDesc { image: img, ..Default::default() }, 100 | ..Default::default() 101 | }); 102 | 103 | // a sampler to sample the offscreen render target as texture 104 | state.display.smp = sg::make_sampler(&sg::SamplerDesc { 105 | wrap_u: sg::Wrap::ClampToEdge, 106 | wrap_v: sg::Wrap::ClampToEdge, 107 | min_filter: sg::Filter::Nearest, 108 | mag_filter: sg::Filter::Nearest, 109 | ..Default::default() 110 | }); 111 | } 112 | 113 | extern "C" fn frame(user_data: *mut ffi::c_void) { 114 | let state = unsafe { &mut *(user_data as *mut State) }; 115 | 116 | let t = (sapp::frame_duration() * 60.0) as f32; 117 | let a = ((sapp::frame_count() as f32) * t).to_radians(); 118 | 119 | // draw a rotating quad into the offscreen render target texture 120 | sgl::set_context(state.offscreen.sgl_context); 121 | sgl::defaults(); 122 | sgl::matrix_mode_modelview(); 123 | sgl::rotate(a, 0.0, 0.0, 1.0); 124 | draw_quad(); 125 | 126 | // draw a rotating 3D cube, using the offscreen render target as texture 127 | sgl::set_context(sgl::default_context()); 128 | sgl::defaults(); 129 | sgl::enable_texture(); 130 | sgl::texture(state.display.tex_view, state.display.smp); 131 | sgl::load_pipeline(state.display.sgl_pip); 132 | sgl::matrix_mode_projection(); 133 | sgl::perspective(f32::to_radians(45.0), sapp::widthf() / sapp::heightf(), 0.1, 100.0); 134 | let eye = [f32::sin(a) * 6.0, f32::sin(a) * 3.0, f32::cos(a) * 6.0]; 135 | sgl::matrix_mode_modelview(); 136 | sgl::lookat(eye[0], eye[1], eye[2], 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); 137 | draw_cube(); 138 | 139 | // do the actual offscreen and display rendering in sokol-gfx passes 140 | sg::begin_pass(&state.offscreen.pass); 141 | sgl::context_draw(state.offscreen.sgl_context); 142 | sg::end_pass(); 143 | sg::begin_pass(&sg::Pass { 144 | action: state.display.pass_action, 145 | swapchain: sglue::swapchain(), 146 | ..Default::default() 147 | }); 148 | sgl::context_draw(sgl::default_context()); 149 | sg::end_pass(); 150 | sg::commit(); 151 | } 152 | 153 | // helper function to draw a colored quad with sokol-gl 154 | #[rustfmt::skip] 155 | fn draw_quad() { 156 | sgl::begin_quads(); 157 | sgl::v2f_c3b( 0.0, -1.0, 255, 000, 000); 158 | sgl::v2f_c3b( 1.0, 0.0, 000, 000, 255); 159 | sgl::v2f_c3b( 0.0, 1.0, 000, 255, 255); 160 | sgl::v2f_c3b(-1.0, 0.0, 000, 255, 000); 161 | sgl::end(); 162 | } 163 | 164 | // helper function to draw a textured cube with sokol-gl 165 | #[rustfmt::skip] 166 | fn draw_cube() { 167 | sgl::begin_quads(); 168 | sgl::v3f_t2f(-1.0, 1.0, -1.0, 0.0, 1.0); 169 | sgl::v3f_t2f( 1.0, 1.0, -1.0, 1.0, 1.0); 170 | sgl::v3f_t2f( 1.0, -1.0, -1.0, 1.0, 0.0); 171 | sgl::v3f_t2f(-1.0, -1.0, -1.0, 0.0, 0.0); 172 | sgl::v3f_t2f(-1.0, -1.0, 1.0, 0.0, 1.0); 173 | sgl::v3f_t2f( 1.0, -1.0, 1.0, 1.0, 1.0); 174 | sgl::v3f_t2f( 1.0, 1.0, 1.0, 1.0, 0.0); 175 | sgl::v3f_t2f(-1.0, 1.0, 1.0, 0.0, 0.0); 176 | sgl::v3f_t2f(-1.0, -1.0, 1.0, 0.0, 1.0); 177 | sgl::v3f_t2f(-1.0, 1.0, 1.0, 1.0, 1.0); 178 | sgl::v3f_t2f(-1.0, 1.0, -1.0, 1.0, 0.0); 179 | sgl::v3f_t2f(-1.0, -1.0, -1.0, 0.0, 0.0); 180 | sgl::v3f_t2f( 1.0, -1.0, 1.0, 0.0, 1.0); 181 | sgl::v3f_t2f( 1.0, -1.0, -1.0, 1.0, 1.0); 182 | sgl::v3f_t2f( 1.0, 1.0, -1.0, 1.0, 0.0); 183 | sgl::v3f_t2f( 1.0, 1.0, 1.0, 0.0, 0.0); 184 | sgl::v3f_t2f( 1.0, -1.0, -1.0, 0.0, 1.0); 185 | sgl::v3f_t2f( 1.0, -1.0, 1.0, 1.0, 1.0); 186 | sgl::v3f_t2f(-1.0, -1.0, 1.0, 1.0, 0.0); 187 | sgl::v3f_t2f(-1.0, -1.0, -1.0, 0.0, 0.0); 188 | sgl::v3f_t2f(-1.0, 1.0, -1.0, 0.0, 1.0); 189 | sgl::v3f_t2f(-1.0, 1.0, 1.0, 1.0, 1.0); 190 | sgl::v3f_t2f( 1.0, 1.0, 1.0, 1.0, 0.0); 191 | sgl::v3f_t2f( 1.0, 1.0, -1.0, 0.0, 0.0); 192 | sgl::end(); 193 | } 194 | 195 | extern "C" fn cleanup(user_data: *mut ffi::c_void) { 196 | sgl::shutdown(); 197 | sg::shutdown(); 198 | 199 | let _ = unsafe { Box::from_raw(user_data as *mut State) }; 200 | } 201 | 202 | fn main() { 203 | let state = Box::new(State { 204 | offscreen: Offscreen { pass: sg::Pass::new(), sgl_context: sgl::Context::new() }, 205 | display: Display { 206 | pass_action: sg::PassAction::new(), 207 | tex_view: sg::View::new(), 208 | smp: sg::Sampler::new(), 209 | sgl_pip: sgl::Pipeline::new(), 210 | }, 211 | }); 212 | 213 | let user_data = Box::into_raw(state) as *mut ffi::c_void; 214 | 215 | sapp::run(&sapp::Desc { 216 | init_userdata_cb: Some(init), 217 | frame_userdata_cb: Some(frame), 218 | cleanup_userdata_cb: Some(cleanup), 219 | user_data, 220 | width: 800, 221 | height: 600, 222 | sample_count: 4, 223 | window_title: c"sgl-context.rs".as_ptr(), 224 | logger: sapp::Logger { func: Some(sokol::log::slog_func), ..Default::default() }, 225 | icon: sapp::IconDesc { sokol_default: true, ..Default::default() }, 226 | ..Default::default() 227 | }) 228 | } 229 | -------------------------------------------------------------------------------- /examples/sgl-points/sgl-points.rs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // sgl-points/sgl-points.rs 3 | // 4 | // Test point rendering with sokol/gl 5 | //------------------------------------------------------------------------------ 6 | 7 | use sokol::{app as sapp, gfx as sg, gl as sgl, glue as sglue, log as slog}; 8 | 9 | #[rustfmt::skip] 10 | const PALETTE: [[f32; 3]; 16] = [ 11 | [ 0.957, 0.263, 0.212 ], 12 | [ 0.914, 0.118, 0.388 ], 13 | [ 0.612, 0.153, 0.690 ], 14 | [ 0.404, 0.227, 0.718 ], 15 | [ 0.247, 0.318, 0.710 ], 16 | [ 0.129, 0.588, 0.953 ], 17 | [ 0.012, 0.663, 0.957 ], 18 | [ 0.000, 0.737, 0.831 ], 19 | [ 0.000, 0.588, 0.533 ], 20 | [ 0.298, 0.686, 0.314 ], 21 | [ 0.545, 0.765, 0.290 ], 22 | [ 0.804, 0.863, 0.224 ], 23 | [ 1.000, 0.922, 0.231 ], 24 | [ 1.000, 0.757, 0.027 ], 25 | [ 1.000, 0.596, 0.000 ], 26 | [ 1.000, 0.341, 0.133 ], 27 | ]; 28 | 29 | extern "C" fn init() { 30 | sg::setup(&sg::Desc { 31 | environment: sglue::environment(), 32 | logger: sg::Logger { func: Some(slog::slog_func), ..Default::default() }, 33 | ..Default::default() 34 | }); 35 | 36 | sgl::setup(&sgl::Desc { 37 | logger: sgl::Logger { func: Some(slog::slog_func), ..Default::default() }, 38 | ..Default::default() 39 | }); 40 | } 41 | 42 | fn compute_color(t: f32) -> [f32; 3] { 43 | // t is expected to be 0.0 <= t <= 1.0 44 | let i0 = (t * 16.0) as usize % 16; 45 | let i1 = (i0 + 1) % 16; 46 | 47 | let l = (t * 16.0).fract(); 48 | let c0 = PALETTE[i0]; 49 | let c1 = PALETTE[i1]; 50 | 51 | macro_rules! lerp { 52 | ($a:expr, $b: expr, $t: expr) => {{ 53 | let t = $t; 54 | (1.0 - t) * $a + t * $b 55 | }}; 56 | } 57 | 58 | [lerp!(c0[0], c1[0], l), lerp!(c0[1], c1[1], l), lerp!(c0[2], c1[2], l)] 59 | } 60 | 61 | extern "C" fn frame() { 62 | let frame_count = sapp::frame_count(); 63 | let angle = frame_count % 360; 64 | 65 | sgl::defaults(); 66 | sgl::begin_points(); 67 | let mut psize: f32 = 5.0; 68 | for i in 0..360 { 69 | let a = ((angle + i) as f32).to_radians(); 70 | let color = compute_color(((frame_count + i) % 300) as f32 / 300.0); 71 | let r = f32::sin(a * 4.0); 72 | let s = f32::sin(a); 73 | let c = f32::cos(a); 74 | let x = s * r; 75 | let y = c * r; 76 | sgl::c3f(color[0], color[1], color[2]); 77 | sgl::point_size(psize); 78 | sgl::v2f(x, y); 79 | psize *= 1.005; 80 | } 81 | sgl::end(); 82 | 83 | let mut pass_action = sg::PassAction::new(); 84 | pass_action.colors[0] = sg::ColorAttachmentAction { 85 | load_action: sg::LoadAction::Clear, 86 | clear_value: sg::Color { r: 0.0, g: 0.0, b: 0.0, a: 1.0 }, 87 | ..Default::default() 88 | }; 89 | sg::begin_pass(&sg::Pass { action: pass_action, swapchain: sglue::swapchain(), ..Default::default() }); 90 | sgl::draw(); 91 | sg::end_pass(); 92 | sg::commit(); 93 | } 94 | 95 | extern "C" fn cleanup() { 96 | sgl::shutdown(); 97 | sg::shutdown(); 98 | } 99 | 100 | fn main() { 101 | sapp::run(&sapp::Desc { 102 | init_cb: Some(init), 103 | frame_cb: Some(frame), 104 | cleanup_cb: Some(cleanup), 105 | width: 512, 106 | height: 512, 107 | sample_count: 4, 108 | window_title: c"sgl-points.rs".as_ptr(), 109 | logger: sapp::Logger { func: Some(sokol::log::slog_func), ..Default::default() }, 110 | icon: sapp::IconDesc { sokol_default: true, ..Default::default() }, 111 | ..Default::default() 112 | }) 113 | } 114 | -------------------------------------------------------------------------------- /examples/texcube/math.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | #[repr(C)] 4 | #[derive(Debug, Clone, Copy)] 5 | pub struct Vec3 { 6 | pub x: f32, 7 | pub y: f32, 8 | pub z: f32, 9 | } 10 | 11 | impl Vec3 { 12 | pub const UP: Vec3 = vec3(0.0, 1.0, 0.0); 13 | pub const ZERO: Vec3 = vec3(0.0, 0.0, 0.0); 14 | } 15 | 16 | #[inline] 17 | pub const fn vec3(x: f32, y: f32, z: f32) -> Vec3 { 18 | Vec3 { x, y, z } 19 | } 20 | 21 | #[repr(C)] 22 | #[derive(Debug, Clone, Copy)] 23 | pub struct Vec2 { 24 | pub x: f32, 25 | pub y: f32, 26 | } 27 | 28 | impl Vec2 { 29 | pub const ZERO: Vec2 = vec2(0.0, 0.0); 30 | } 31 | 32 | #[inline] 33 | pub const fn vec2(x: f32, y: f32) -> Vec2 { 34 | Vec2 { x, y } 35 | } 36 | 37 | pub type Mat4 = [[f32; 4]; 4]; 38 | 39 | impl std::ops::Sub for Vec3 { 40 | type Output = Vec3; 41 | 42 | #[inline] 43 | fn sub(self, rhs: Vec3) -> Self::Output { 44 | sub_vec3(self, rhs) 45 | } 46 | } 47 | 48 | impl std::ops::Add for Vec3 { 49 | type Output = Vec3; 50 | 51 | #[inline] 52 | fn add(self, rhs: Vec3) -> Self::Output { 53 | add_vec3(self, rhs) 54 | } 55 | } 56 | 57 | impl std::ops::Mul for Vec3 { 58 | type Output = Vec3; 59 | 60 | #[inline] 61 | fn mul(self, rhs: f32) -> Self::Output { 62 | mul_vec3(self, rhs) 63 | } 64 | } 65 | 66 | pub fn sub_vec3(v0: Vec3, v1: Vec3) -> Vec3 { 67 | vec3(v0.x - v1.x, v0.y - v1.y, v0.z - v1.z) 68 | } 69 | 70 | pub fn add_vec3(v0: Vec3, v1: Vec3) -> Vec3 { 71 | vec3(v0.x + v1.x, v0.y + v1.y, v0.z + v1.z) 72 | } 73 | 74 | pub fn mul_vec3(v: Vec3, val: f32) -> Vec3 { 75 | vec3(v.x * val, v.y * val, v.z * val) 76 | } 77 | 78 | pub fn dot_vec3(v0: Vec3, v1: Vec3) -> f32 { 79 | v0.x * v1.x + v0.y * v1.y + v0.z * v1.z 80 | } 81 | 82 | #[inline] 83 | pub fn len_vec3(v: Vec3) -> f32 { 84 | dot_vec3(v, v).sqrt() 85 | } 86 | 87 | pub fn norm_vec3(v: Vec3) -> Vec3 { 88 | let l = len_vec3(v); 89 | 90 | if l != 0.0 { vec3(v.x / l, v.y / l, v.z / l) } else { Vec3::ZERO } 91 | } 92 | 93 | pub fn cross_vec3(v0: Vec3, v1: Vec3) -> Vec3 { 94 | vec3((v0.y * v1.z) - (v0.z * v1.y), (v0.z * v1.x) - (v0.x * v1.z), (v0.x * v1.y) - (v0.y * v1.x)) 95 | } 96 | 97 | pub const fn identity_mat4() -> Mat4 { 98 | let mut m = [[0.0; 4]; 4]; 99 | 100 | m[0][0] = 1.0; 101 | m[1][1] = 1.0; 102 | m[2][2] = 1.0; 103 | m[3][3] = 1.0; 104 | 105 | m 106 | } 107 | 108 | pub fn rotate_mat4(angle: f32, axis_unorm: Vec3) -> Mat4 { 109 | let mut m = identity_mat4(); 110 | 111 | let axis = norm_vec3(axis_unorm); 112 | let sin_theta = angle.to_radians().sin(); 113 | let cos_theta = angle.to_radians().cos(); 114 | let cos_value = 1.0 - cos_theta; 115 | 116 | m[0][0] = (axis.x * axis.x * cos_value) + cos_theta; 117 | m[0][1] = (axis.x * axis.y * cos_value) + (axis.z * sin_theta); 118 | m[0][2] = (axis.x * axis.z * cos_value) - (axis.y * sin_theta); 119 | m[1][0] = (axis.y * axis.x * cos_value) - (axis.z * sin_theta); 120 | m[1][1] = (axis.y * axis.y * cos_value) + cos_theta; 121 | m[1][2] = (axis.y * axis.z * cos_value) + (axis.x * sin_theta); 122 | m[2][0] = (axis.z * axis.x * cos_value) + (axis.y * sin_theta); 123 | m[2][1] = (axis.z * axis.y * cos_value) - (axis.x * sin_theta); 124 | m[2][2] = (axis.z * axis.z * cos_value) + cos_theta; 125 | 126 | m 127 | } 128 | 129 | pub fn persp_mat4(fov: f32, aspect: f32, near: f32, far: f32) -> Mat4 { 130 | let mut m = identity_mat4(); 131 | let t = f32::tan(fov * (std::f32::consts::PI / 360.0)); 132 | 133 | m[0][0] = 1.0 / t; 134 | m[1][1] = aspect / t; 135 | m[2][3] = -1.0; 136 | m[2][2] = (near + far) / (near - far); 137 | m[3][2] = (2.0 * near * far) / (near - far); 138 | m[3][3] = 0.0; 139 | 140 | m 141 | } 142 | 143 | pub fn lookat_mat4(eye: Vec3, center: Vec3, up: Vec3) -> Mat4 { 144 | let mut m = [[0.0; 4]; 4]; 145 | 146 | let f = norm_vec3(center - eye); 147 | let s = norm_vec3(cross_vec3(f, up)); 148 | let u = cross_vec3(s, f); 149 | 150 | m[0][0] = s.x; 151 | m[0][1] = u.x; 152 | m[0][2] = -f.x; 153 | 154 | m[1][0] = s.y; 155 | m[1][1] = u.y; 156 | m[1][2] = -f.y; 157 | 158 | m[2][0] = s.z; 159 | m[2][1] = u.z; 160 | m[2][2] = -f.z; 161 | 162 | m[3][0] = -dot_vec3(s, eye); 163 | m[3][1] = -dot_vec3(u, eye); 164 | m[3][2] = dot_vec3(f, eye); 165 | m[3][3] = 1.0; 166 | 167 | m 168 | } 169 | 170 | pub fn translate_mat4(translation: Vec3) -> Mat4 { 171 | let mut m = identity_mat4(); 172 | 173 | m[3][0] = translation.x; 174 | m[3][1] = translation.y; 175 | m[3][2] = translation.z; 176 | 177 | m 178 | } 179 | 180 | pub fn mul_mat4(left: Mat4, right: Mat4) -> Mat4 { 181 | let mut m = [[0.0; 4]; 4]; 182 | 183 | for col in 0..4 { 184 | for row in 0..4 { 185 | m[col][row] = left[0][row] * right[col][0] 186 | + left[1][row] * right[col][1] 187 | + left[2][row] * right[col][2] 188 | + left[3][row] * right[col][3]; 189 | } 190 | } 191 | 192 | m 193 | } 194 | -------------------------------------------------------------------------------- /examples/texcube/shader.glsl: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // Shader code for texcube-sapp sample. 3 | // 4 | // NOTE: This source file also uses the '#pragma sokol' form of the 5 | // custom tags. 6 | //------------------------------------------------------------------------------ 7 | @header use crate::math as m 8 | @ctype mat4 m::Mat4 9 | @ctype vec4 m::Vec4 10 | @ctype vec3 m::Vec3 11 | @ctype vec2 m::Vec2 12 | 13 | @vs vs 14 | layout(binding=0) uniform vs_params { 15 | mat4 mvp; 16 | }; 17 | 18 | in vec4 pos; 19 | in vec4 color0; 20 | in vec2 texcoord0; 21 | 22 | out vec4 color; 23 | out vec2 uv; 24 | 25 | void main() { 26 | gl_Position = mvp * pos; 27 | color = color0; 28 | uv = texcoord0 * 5.0; 29 | } 30 | @end 31 | 32 | @fs fs 33 | layout(binding=0) uniform texture2D tex; 34 | layout(binding=0) uniform sampler smp; 35 | 36 | in vec4 color; 37 | in vec2 uv; 38 | out vec4 frag_color; 39 | 40 | void main() { 41 | frag_color = texture(sampler2D(tex, smp), uv) * color; 42 | } 43 | @end 44 | 45 | @program texcube vs fs 46 | -------------------------------------------------------------------------------- /examples/texcube/texcube.rs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // texcube.rs 3 | // Texture creation, rendering with texture, packed vertex components. 4 | //------------------------------------------------------------------------------ 5 | 6 | mod math; 7 | mod shader; 8 | 9 | use std::ffi; 10 | 11 | use math as m; 12 | use sokol::{app as sapp, gfx as sg, glue as sglue, log as slog}; 13 | 14 | struct State { 15 | pub pass_action: sg::PassAction, 16 | pub pip: sg::Pipeline, 17 | pub bind: sg::Bindings, 18 | pub rx: f32, 19 | pub ry: f32, 20 | } 21 | 22 | pub struct Vertex { 23 | pub x: f32, 24 | pub y: f32, 25 | pub z: f32, 26 | 27 | pub color: u32, 28 | pub u: u16, 29 | pub v: u16, 30 | } 31 | 32 | extern "C" fn init(user_data: *mut ffi::c_void) { 33 | let state = unsafe { &mut *(user_data as *mut State) }; 34 | 35 | sg::setup(&sg::Desc { 36 | environment: sglue::environment(), 37 | logger: sg::Logger { func: Some(slog::slog_func), ..Default::default() }, 38 | ..Default::default() 39 | }); 40 | 41 | /* 42 | Cube vertex buffer with packed vertex formats for color and texture coords. 43 | Note that a vertex format which must be portable across all 44 | backends must only use the normalized integer formats 45 | (BYTE4N, UBYTE4N, SHORT2N, SHORT4N), which can be converted 46 | to floating point formats in the vertex shader inputs. 47 | The reason is that D3D11 cannot convert from non-normalized 48 | formats to floating point inputs (only to integer inputs), 49 | and WebGL2 / GLES2 don't support integer vertex shader inputs. 50 | */ 51 | #[rustfmt::skip] 52 | const VERTICES: &[Vertex] = &[ 53 | // pos color uvs 54 | Vertex { x: -1.0, y: -1.0, z: -1.0, color: 0xFF0000FF, u: 0, v: 0 }, 55 | Vertex { x: 1.0, y: -1.0, z: -1.0, color: 0xFF0000FF, u: 32767, v: 0 }, 56 | Vertex { x: 1.0, y: 1.0, z: -1.0, color: 0xFF0000FF, u: 32767, v: 32767 }, 57 | Vertex { x: -1.0, y: 1.0, z: -1.0, color: 0xFF0000FF, u: 0, v: 32767 }, 58 | 59 | Vertex { x: -1.0, y: -1.0, z: 1.0, color: 0xFF00FF00, u: 0, v: 0 }, 60 | Vertex { x: 1.0, y: -1.0, z: 1.0, color: 0xFF00FF00, u: 32767, v: 0 }, 61 | Vertex { x: 1.0, y: 1.0, z: 1.0, color: 0xFF00FF00, u: 32767, v: 32767 }, 62 | Vertex { x: -1.0, y: 1.0, z: 1.0, color: 0xFF00FF00, u: 0, v: 32767 }, 63 | 64 | Vertex { x: -1.0, y: -1.0, z: -1.0, color: 0xFFFF0000, u: 0, v: 0 }, 65 | Vertex { x: -1.0, y: 1.0, z: -1.0, color: 0xFFFF0000, u: 32767, v: 0 }, 66 | Vertex { x: -1.0, y: 1.0, z: 1.0, color: 0xFFFF0000, u: 32767, v: 32767 }, 67 | Vertex { x: -1.0, y: -1.0, z: 1.0, color: 0xFFFF0000, u: 0, v: 32767 }, 68 | 69 | Vertex { x: 1.0, y: -1.0, z: -1.0, color: 0xFFFF007F, u: 0, v: 0 }, 70 | Vertex { x: 1.0, y: 1.0, z: -1.0, color: 0xFFFF007F, u: 32767, v: 0 }, 71 | Vertex { x: 1.0, y: 1.0, z: 1.0, color: 0xFFFF007F, u: 32767, v: 32767 }, 72 | Vertex { x: 1.0, y: -1.0, z: 1.0, color: 0xFFFF007F, u: 0, v: 32767 }, 73 | 74 | Vertex { x: -1.0, y: -1.0, z: -1.0, color: 0xFFFF7F00, u: 0, v: 0 }, 75 | Vertex { x: -1.0, y: -1.0, z: 1.0, color: 0xFFFF7F00, u: 32767, v: 0 }, 76 | Vertex { x: 1.0, y: -1.0, z: 1.0, color: 0xFFFF7F00, u: 32767, v: 32767 }, 77 | Vertex { x: 1.0, y: -1.0, z: -1.0, color: 0xFFFF7F00, u: 0, v: 32767 }, 78 | 79 | Vertex { x: -1.0, y: 1.0, z: -1.0, color: 0xFF007FFF, u: 0, v: 0 }, 80 | Vertex { x: -1.0, y: 1.0, z: 1.0, color: 0xFF007FFF, u: 32767, v: 0 }, 81 | Vertex { x: 1.0, y: 1.0, z: 1.0, color: 0xFF007FFF, u: 32767, v: 32767 }, 82 | Vertex { x: 1.0, y: 1.0, z: -1.0, color: 0xFF007FFF, u: 0, v: 32767 }, 83 | ]; 84 | 85 | state.bind.vertex_buffers[0] = 86 | sg::make_buffer(&sg::BufferDesc { data: sg::slice_as_range(VERTICES), ..Default::default() }); 87 | 88 | // create an index buffer for the cube 89 | #[rustfmt::skip] 90 | const INDICES: &[u16] = &[ 91 | 0, 1, 2, 0, 2, 3, 92 | 6, 5, 4, 7, 6, 4, 93 | 8, 9, 10, 8, 10, 11, 94 | 14, 13, 12, 15, 14, 12, 95 | 16, 17, 18, 16, 18, 19, 96 | 22, 21, 20, 23, 22, 20, 97 | ]; 98 | state.bind.index_buffer = sg::make_buffer(&sg::BufferDesc { 99 | data: sg::slice_as_range(INDICES), 100 | usage: sg::BufferUsage { index_buffer: true, ..Default::default() }, 101 | ..Default::default() 102 | }); 103 | 104 | // create a checkerboard image and texture view 105 | let pixels: [u32; 4 * 4] = [ 106 | 0xFFFFFFFF, 0xFF000000, 0xFFFFFFFF, 0xFF000000, 0xFF000000, 0xFFFFFFFF, 0xFF000000, 0xFFFFFFFF, 107 | 0xFFFFFFFF, 0xFF000000, 0xFFFFFFFF, 0xFF000000, 0xFF000000, 0xFFFFFFFF, 0xFF000000, 0xFFFFFFFF, 108 | ]; 109 | // NOTE: SLOT_tex is provided by shader code generation 110 | let mut image_desc = sg::ImageDesc { width: 4, height: 4, ..Default::default() }; 111 | image_desc.data.mip_levels[0] = sg::slice_as_range(&pixels); 112 | state.bind.views[shader::VIEW_TEX] = sg::make_view(&sg::ViewDesc { 113 | texture: sg::TextureViewDesc { image: sg::make_image(&image_desc), ..Default::default() }, 114 | ..Default::default() 115 | }); 116 | 117 | // create a sampler object 118 | state.bind.samplers[shader::SMP_SMP] = sg::make_sampler(&sg::SamplerDesc { 119 | min_filter: sg::Filter::Nearest, 120 | mag_filter: sg::Filter::Nearest, 121 | wrap_u: sg::Wrap::Repeat, 122 | wrap_v: sg::Wrap::Repeat, 123 | ..Default::default() 124 | }); 125 | 126 | // shader and pipeline object 127 | #[rustfmt::skip] 128 | let pip = sg::make_pipeline(&sg::PipelineDesc { 129 | shader: sg::make_shader(&shader::texcube_shader_desc(sg::query_backend())), 130 | layout: sg::VertexLayoutState { 131 | attrs: { 132 | let mut attrs = [sg::VertexAttrState::new(); sg::MAX_VERTEX_ATTRIBUTES]; 133 | 134 | attrs[shader::ATTR_TEXCUBE_POS] = sg::VertexAttrState { format: sg::VertexFormat::Float3, ..Default::default() }; 135 | attrs[shader::ATTR_TEXCUBE_COLOR0] = sg::VertexAttrState { format: sg::VertexFormat::Ubyte4n, ..Default::default() }; 136 | attrs[shader::ATTR_TEXCUBE_TEXCOORD0] = sg::VertexAttrState { format: sg::VertexFormat::Short2n, ..Default::default() }; 137 | 138 | attrs 139 | }, 140 | ..Default::default() 141 | }, 142 | index_type: sg::IndexType::Uint16, 143 | cull_mode: sg::CullMode::Back, 144 | depth: sg::DepthState { 145 | compare: sg::CompareFunc::LessEqual, 146 | write_enabled: true, 147 | ..Default::default() 148 | }, 149 | ..Default::default() 150 | }); 151 | state.pip = pip; 152 | 153 | // default pass action, clear to blue-ish 154 | state.pass_action.colors[0] = sg::ColorAttachmentAction { 155 | load_action: sg::LoadAction::Clear, 156 | clear_value: sg::Color { r: 0.25, g: 0.5, b: 0.75, a: 1.0 }, 157 | ..Default::default() 158 | }; 159 | } 160 | 161 | extern "C" fn frame(user_data: *mut ffi::c_void) { 162 | let state = unsafe { &mut *(user_data as *mut State) }; 163 | let t = (sapp::frame_duration() * 60.0) as f32; 164 | state.rx += 1.0 * t; 165 | state.ry += 2.0 * t; 166 | 167 | // vertex shader uniform with model-view-projection matrix 168 | let vs_params = shader::VsParams { mvp: compute_mvp(state.rx, state.ry) }; 169 | 170 | sg::begin_pass(&sg::Pass { 171 | action: state.pass_action, 172 | swapchain: sglue::swapchain(), 173 | ..Default::default() 174 | }); 175 | sg::apply_pipeline(state.pip); 176 | sg::apply_bindings(&state.bind); 177 | sg::apply_uniforms(shader::UB_VS_PARAMS, &sg::value_as_range(&vs_params)); 178 | sg::draw(0, 36, 1); 179 | sg::end_pass(); 180 | sg::commit(); 181 | } 182 | 183 | pub fn compute_mvp(rx: f32, ry: f32) -> [[f32; 4]; 4] { 184 | let proj = m::persp_mat4(60.0, sapp::widthf() / sapp::heightf(), 0.01, 10.0); 185 | let view = m::lookat_mat4(m::vec3(0.0, 1.5, 6.0), m::Vec3::ZERO, m::Vec3::UP); 186 | let view_proj = m::mul_mat4(proj, view); 187 | let rxm = m::rotate_mat4(rx, m::vec3(1.0, 0.0, 0.0)); 188 | let rym = m::rotate_mat4(ry, m::vec3(0.0, 1.0, 0.0)); 189 | let model = m::mul_mat4(rxm, rym); 190 | 191 | m::mul_mat4(view_proj, model) 192 | } 193 | 194 | extern "C" fn cleanup(user_data: *mut ffi::c_void) { 195 | sg::shutdown(); 196 | 197 | let _ = unsafe { Box::from_raw(user_data as *mut State) }; 198 | } 199 | 200 | fn main() { 201 | let state = Box::new(State { 202 | pass_action: sg::PassAction::new(), 203 | pip: sg::Pipeline::new(), 204 | bind: sg::Bindings::new(), 205 | rx: 0.0, 206 | ry: 0.0, 207 | }); 208 | 209 | let user_data = Box::into_raw(state) as *mut ffi::c_void; 210 | 211 | sapp::run(&sapp::Desc { 212 | init_userdata_cb: Some(init), 213 | frame_userdata_cb: Some(frame), 214 | cleanup_userdata_cb: Some(cleanup), 215 | user_data, 216 | width: 800, 217 | height: 600, 218 | sample_count: 4, 219 | window_title: c"texcube.rs".as_ptr(), 220 | icon: sapp::IconDesc { sokol_default: true, ..Default::default() }, 221 | logger: sapp::Logger { func: Some(slog::slog_func), ..Default::default() }, 222 | ..Default::default() 223 | }); 224 | } 225 | -------------------------------------------------------------------------------- /examples/userdata/userdata.rs: -------------------------------------------------------------------------------- 1 | use sokol::{app as sapp, gfx as sg, glue as sglue}; 2 | 3 | #[derive(Debug)] 4 | pub struct ExampleUserData { 5 | data: u8, 6 | 7 | map: std::collections::HashMap, 8 | } 9 | 10 | extern "C" fn init() { 11 | sg::setup(&sg::Desc { 12 | environment: sokol::glue::environment(), 13 | logger: sg::Logger { func: Some(sokol::log::slog_func), ..Default::default() }, 14 | ..Default::default() 15 | }); 16 | } 17 | 18 | extern "C" fn frame_userdata(userdata: *mut core::ffi::c_void) { 19 | /* 20 | 3. We then need to turn the raw c pointer into a mutable reference to the same 21 | type as we created in main. This is safe as long as the data was indeed provided 22 | and the type is the same. 23 | */ 24 | let state: &mut ExampleUserData = unsafe { &mut *(userdata as *mut _) }; 25 | 26 | /* 27 | 4. Just randomly modifying the data here for demonstration 28 | */ 29 | state.data = state.data.wrapping_add(1); 30 | if state.data % 13 == 0 { 31 | let val = (state.data.wrapping_mul(13)) / 3; 32 | state.map.insert(state.data, val); 33 | } 34 | if state.data % 12 == 0 && state.data % 15 == 0 { 35 | state.map.clear(); 36 | } 37 | println!("{state:?}"); 38 | 39 | sg::begin_pass(&sg::Pass { swapchain: sglue::swapchain(), ..Default::default() }); 40 | sg::end_pass(); 41 | sg::commit(); 42 | } 43 | 44 | extern "C" fn cleanup() { 45 | sg::shutdown() 46 | } 47 | 48 | fn main() { 49 | let mut user_data = ExampleUserData { data: 0, map: std::collections::HashMap::default() }; 50 | 51 | sapp::run(&sapp::Desc { 52 | /* 53 | 1. 'user_data' is allocated on the stack in the main function and we take a 54 | mutable reference to it which we cast to a pointer and pass to sokol 55 | */ 56 | user_data: &mut user_data as *mut _ as _, 57 | init_cb: Some(init), 58 | cleanup_cb: Some(cleanup), 59 | /* 60 | 2. We can use the userdata callbacks to get the userdata passed as an argument, 61 | but we could also use the normal callbacks and call sapp::userdata() to 62 | fetch the data manually 63 | */ 64 | frame_userdata_cb: Some(frame_userdata), 65 | width: 800, 66 | height: 600, 67 | sample_count: 4, 68 | window_title: c"userdata.rs".as_ptr(), 69 | logger: sapp::Logger { func: Some(sokol::log::slog_func), ..Default::default() }, 70 | icon: sapp::IconDesc { sokol_default: true, ..Default::default() }, 71 | ..Default::default() 72 | }); 73 | } 74 | -------------------------------------------------------------------------------- /examples/vertexpull/math.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | #[repr(C)] 4 | #[derive(Debug, Clone, Copy)] 5 | pub struct Vec3 { 6 | pub x: f32, 7 | pub y: f32, 8 | pub z: f32, 9 | } 10 | 11 | impl Vec3 { 12 | pub const UP: Vec3 = vec3(0.0, 1.0, 0.0); 13 | pub const ZERO: Vec3 = vec3(0.0, 0.0, 0.0); 14 | } 15 | 16 | #[inline] 17 | pub const fn vec3(x: f32, y: f32, z: f32) -> Vec3 { 18 | Vec3 { x, y, z } 19 | } 20 | 21 | pub type Mat4 = [[f32; 4]; 4]; 22 | 23 | impl std::ops::Sub for Vec3 { 24 | type Output = Vec3; 25 | 26 | #[inline] 27 | fn sub(self, rhs: Vec3) -> Self::Output { 28 | sub_vec3(self, rhs) 29 | } 30 | } 31 | 32 | pub fn sub_vec3(v0: Vec3, v1: Vec3) -> Vec3 { 33 | vec3(v0.x - v1.x, v0.y - v1.y, v0.z - v1.z) 34 | } 35 | 36 | pub fn dot_vec3(v0: Vec3, v1: Vec3) -> f32 { 37 | v0.x * v1.x + v0.y * v1.y + v0.z * v1.z 38 | } 39 | 40 | #[inline] 41 | pub fn len_vec3(v: Vec3) -> f32 { 42 | dot_vec3(v, v).sqrt() 43 | } 44 | 45 | pub fn norm_vec3(v: Vec3) -> Vec3 { 46 | let l = len_vec3(v); 47 | 48 | if l != 0.0 { vec3(v.x / l, v.y / l, v.z / l) } else { Vec3::ZERO } 49 | } 50 | 51 | pub fn cross_vec3(v0: Vec3, v1: Vec3) -> Vec3 { 52 | vec3((v0.y * v1.z) - (v0.z * v1.y), (v0.z * v1.x) - (v0.x * v1.z), (v0.x * v1.y) - (v0.y * v1.x)) 53 | } 54 | 55 | pub const fn identity_mat4() -> Mat4 { 56 | let mut m = [[0.0; 4]; 4]; 57 | 58 | m[0][0] = 1.0; 59 | m[1][1] = 1.0; 60 | m[2][2] = 1.0; 61 | m[3][3] = 1.0; 62 | 63 | m 64 | } 65 | 66 | pub fn rotate_mat4(angle: f32, axis_unorm: Vec3) -> Mat4 { 67 | let mut m = identity_mat4(); 68 | 69 | let axis = norm_vec3(axis_unorm); 70 | let sin_theta = angle.to_radians().sin(); 71 | let cos_theta = angle.to_radians().cos(); 72 | let cos_value = 1.0 - cos_theta; 73 | 74 | m[0][0] = (axis.x * axis.x * cos_value) + cos_theta; 75 | m[0][1] = (axis.x * axis.y * cos_value) + (axis.z * sin_theta); 76 | m[0][2] = (axis.x * axis.z * cos_value) - (axis.y * sin_theta); 77 | m[1][0] = (axis.y * axis.x * cos_value) - (axis.z * sin_theta); 78 | m[1][1] = (axis.y * axis.y * cos_value) + cos_theta; 79 | m[1][2] = (axis.y * axis.z * cos_value) + (axis.x * sin_theta); 80 | m[2][0] = (axis.z * axis.x * cos_value) + (axis.y * sin_theta); 81 | m[2][1] = (axis.z * axis.y * cos_value) - (axis.x * sin_theta); 82 | m[2][2] = (axis.z * axis.z * cos_value) + cos_theta; 83 | 84 | m 85 | } 86 | 87 | pub fn persp_mat4(fov: f32, aspect: f32, near: f32, far: f32) -> Mat4 { 88 | let mut m = identity_mat4(); 89 | let t = f32::tan(fov * (std::f32::consts::PI / 360.0)); 90 | 91 | m[0][0] = 1.0 / t; 92 | m[1][1] = aspect / t; 93 | m[2][3] = -1.0; 94 | m[2][2] = (near + far) / (near - far); 95 | m[3][2] = (2.0 * near * far) / (near - far); 96 | m[3][3] = 0.0; 97 | 98 | m 99 | } 100 | 101 | pub fn lookat_mat4(eye: Vec3, center: Vec3, up: Vec3) -> Mat4 { 102 | let mut m = [[0.0; 4]; 4]; 103 | 104 | let f = norm_vec3(center - eye); 105 | let s = norm_vec3(cross_vec3(f, up)); 106 | let u = cross_vec3(s, f); 107 | 108 | m[0][0] = s.x; 109 | m[0][1] = u.x; 110 | m[0][2] = -f.x; 111 | 112 | m[1][0] = s.y; 113 | m[1][1] = u.y; 114 | m[1][2] = -f.y; 115 | 116 | m[2][0] = s.z; 117 | m[2][1] = u.z; 118 | m[2][2] = -f.z; 119 | 120 | m[3][0] = -dot_vec3(s, eye); 121 | m[3][1] = -dot_vec3(u, eye); 122 | m[3][2] = dot_vec3(f, eye); 123 | m[3][3] = 1.0; 124 | 125 | m 126 | } 127 | 128 | pub fn translate_mat4(translation: Vec3) -> Mat4 { 129 | let mut m = identity_mat4(); 130 | 131 | m[3][0] = translation.x; 132 | m[3][1] = translation.y; 133 | m[3][2] = translation.z; 134 | 135 | m 136 | } 137 | 138 | pub fn mul_mat4(left: Mat4, right: Mat4) -> Mat4 { 139 | let mut m = [[0.0; 4]; 4]; 140 | 141 | for col in 0..4 { 142 | for row in 0..4 { 143 | m[col][row] = left[0][row] * right[col][0] 144 | + left[1][row] * right[col][1] 145 | + left[2][row] * right[col][2] 146 | + left[3][row] * right[col][3]; 147 | } 148 | } 149 | 150 | m 151 | } 152 | -------------------------------------------------------------------------------- /examples/vertexpull/shader.glsl: -------------------------------------------------------------------------------- 1 | @header use crate::math as m 2 | 3 | @ctype mat4 m::Mat4 4 | 5 | @vs vs 6 | layout(binding=0) uniform vs_params { 7 | mat4 mvp; 8 | }; 9 | 10 | // NOTE: 'vertex' is a reserved name in MSL 11 | struct sb_vertex { 12 | vec3 pos; 13 | vec4 color; 14 | }; 15 | 16 | layout(binding=0) readonly buffer ssbo { 17 | sb_vertex vtx[]; 18 | }; 19 | 20 | out vec4 color; 21 | 22 | void main() { 23 | gl_Position = mvp * vec4(vtx[gl_VertexIndex].pos, 1.0); 24 | color = vtx[gl_VertexIndex].color; 25 | } 26 | @end 27 | 28 | @fs fs 29 | in vec4 color; 30 | out vec4 frag_color; 31 | 32 | void main() { 33 | frag_color = color; 34 | } 35 | @end 36 | 37 | @program vertexpull vs fs -------------------------------------------------------------------------------- /examples/vertexpull/vertexpull.rs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // vertexpull.rs 3 | // 4 | // Vertex-pulling from a storage buffer. 5 | //------------------------------------------------------------------------------ 6 | 7 | mod math; 8 | mod shader; 9 | 10 | use std::ffi; 11 | 12 | use math as m; 13 | use shader as shd; 14 | use sokol::{app as sapp, gfx as sg, glue as sglue, log as slog}; 15 | 16 | struct State { 17 | rx: f32, 18 | ry: f32, 19 | pip: sg::Pipeline, 20 | bind: sg::Bindings, 21 | pass_action: sg::PassAction, 22 | } 23 | 24 | extern "C" fn init(user_data: *mut ffi::c_void) { 25 | let state = unsafe { &mut *(user_data as *mut State) }; 26 | 27 | sg::setup(&sg::Desc { 28 | environment: sglue::environment(), 29 | logger: sg::Logger { func: Some(slog::slog_func), ..Default::default() }, 30 | ..Default::default() 31 | }); 32 | 33 | // if no storage buffers supported by 3D backend, set clear color to red and return now 34 | if !sg::query_features().compute { 35 | state.pass_action.colors[0] = sg::ColorAttachmentAction { 36 | load_action: sg::LoadAction::Clear, 37 | clear_value: sg::Color { r: 1.0, g: 0.0, b: 0.0, a: 1.0 }, 38 | ..Default::default() 39 | }; 40 | return; 41 | } 42 | // otherwise set to regular clear color 43 | state.pass_action.colors[0] = sg::ColorAttachmentAction { 44 | load_action: sg::LoadAction::Clear, 45 | clear_value: sg::Color { r: 0.75, g: 0.5, b: 0.25, a: 1.0 }, 46 | ..Default::default() 47 | }; 48 | 49 | // Vertices in a storage buffer, we *could* use the code-generated 50 | // SbVertex struct here, but the initialization code would look so gnarly 51 | // that I'm skipping that (seriously, wtf Rust?). Be aware that there's 52 | // 4 bytes of padding between the pos and color! 53 | #[rustfmt::skip] 54 | const VERTICES: &[f32] = &[ 55 | // pos padding color 56 | -1.0, -1.0, -1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 57 | 1.0, -1.0, -1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 58 | 1.0, 1.0, -1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 59 | -1.0, 1.0, -1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 60 | -1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 61 | 1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 62 | 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 63 | -1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 64 | -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 65 | -1.0, 1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 66 | -1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 67 | -1.0, -1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 68 | 1.0, -1.0, -1.0, 0.0, 1.0, 0.5, 0.0, 1.0, 69 | 1.0, 1.0, -1.0, 0.0, 1.0, 0.5, 0.0, 1.0, 70 | 1.0, 1.0, 1.0, 0.0, 1.0, 0.5, 0.0, 1.0, 71 | 1.0, -1.0, 1.0, 0.0, 1.0, 0.5, 0.0, 1.0, 72 | -1.0, -1.0, -1.0, 0.0, 0.0, 0.5, 1.0, 1.0, 73 | -1.0, -1.0, 1.0, 0.0, 0.0, 0.5, 1.0, 1.0, 74 | 1.0, -1.0, 1.0, 0.0, 0.0, 0.5, 1.0, 1.0, 75 | 1.0, -1.0, -1.0, 0.0, 0.0, 0.5, 1.0, 1.0, 76 | -1.0, 1.0, -1.0, 0.0, 1.0, 0.0, 0.5, 1.0, 77 | -1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.5, 1.0, 78 | 1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.5, 1.0, 79 | 1.0, 1.0, -1.0, 0.0, 1.0, 0.0, 0.5, 1.0, 80 | ]; 81 | state.bind.views[shd::VIEW_SSBO] = sg::make_view(&sg::ViewDesc { 82 | storage_buffer: sg::BufferViewDesc { 83 | buffer: sg::make_buffer(&sg::BufferDesc { 84 | usage: sg::BufferUsage { storage_buffer: true, ..Default::default() }, 85 | data: sg::slice_as_range(VERTICES), 86 | ..Default::default() 87 | }), 88 | ..Default::default() 89 | }, 90 | ..Default::default() 91 | }); 92 | 93 | // a regular index buffer 94 | #[rustfmt::skip] 95 | const INDICES: &[u16] = &[ 96 | 0, 1, 2, 0, 2, 3, 97 | 6, 5, 4, 7, 6, 4, 98 | 8, 9, 10, 8, 10, 11, 99 | 14, 13, 12, 15, 14, 12, 100 | 16, 17, 18, 16, 18, 19, 101 | 22, 21, 20, 23, 22, 20, 102 | ]; 103 | state.bind.index_buffer = sg::make_buffer(&sg::BufferDesc { 104 | usage: sg::BufferUsage { index_buffer: true, ..Default::default() }, 105 | data: sg::slice_as_range(INDICES), 106 | ..Default::default() 107 | }); 108 | 109 | // shader and pipeline object, note that the pipeline object has not vertex layout 110 | // (not needed when pulling vertices from a storage buffer) 111 | state.pip = sg::make_pipeline(&sg::PipelineDesc { 112 | shader: sg::make_shader(&shader::vertexpull_shader_desc(sg::query_backend())), 113 | index_type: sg::IndexType::Uint16, 114 | cull_mode: sg::CullMode::Back, 115 | depth: sg::DepthState { 116 | write_enabled: true, 117 | compare: sg::CompareFunc::LessEqual, 118 | ..Default::default() 119 | }, 120 | ..Default::default() 121 | }); 122 | } 123 | 124 | extern "C" fn frame(user_data: *mut ffi::c_void) { 125 | let state = unsafe { &mut *(user_data as *mut State) }; 126 | 127 | let t = (sapp::frame_duration() * 60.0) as f32; 128 | state.rx += 1.0 * t; 129 | state.ry += 2.0 * t; 130 | 131 | // vertex shader uniform with model-view-projection matrix 132 | let vs_params = shader::VsParams { mvp: compute_mvp(state.rx, state.ry) }; 133 | 134 | sg::begin_pass(&sg::Pass { 135 | action: state.pass_action, 136 | swapchain: sglue::swapchain(), 137 | ..Default::default() 138 | }); 139 | if sg::query_features().compute { 140 | sg::apply_pipeline(state.pip); 141 | sg::apply_bindings(&state.bind); 142 | sg::apply_uniforms(shd::UB_VS_PARAMS, &sg::value_as_range(&vs_params)); 143 | sg::draw(0, 36, 1); 144 | } 145 | sg::end_pass(); 146 | sg::commit(); 147 | } 148 | 149 | pub fn compute_mvp(rx: f32, ry: f32) -> [[f32; 4]; 4] { 150 | let proj = m::persp_mat4(60.0, sapp::widthf() / sapp::heightf(), 0.01, 10.0); 151 | let view = m::lookat_mat4(m::vec3(0.0, 1.5, 6.0), m::Vec3::ZERO, m::Vec3::UP); 152 | let view_proj = m::mul_mat4(proj, view); 153 | let rxm = m::rotate_mat4(rx, m::vec3(1.0, 0.0, 0.0)); 154 | let rym = m::rotate_mat4(ry, m::vec3(0.0, 1.0, 0.0)); 155 | let model = m::mul_mat4(rxm, rym); 156 | m::mul_mat4(view_proj, model) 157 | } 158 | 159 | extern "C" fn cleanup(user_data: *mut ffi::c_void) { 160 | sg::shutdown(); 161 | 162 | let _ = unsafe { Box::from_raw(user_data as *mut State) }; 163 | } 164 | 165 | fn main() { 166 | let state = Box::new(State { 167 | rx: 0.0, 168 | ry: 0.0, 169 | pip: sg::Pipeline::new(), 170 | bind: sg::Bindings::new(), 171 | pass_action: sg::PassAction::new(), 172 | }); 173 | 174 | let user_data = Box::into_raw(state) as *mut ffi::c_void; 175 | 176 | sapp::run(&sapp::Desc { 177 | init_userdata_cb: Some(init), 178 | frame_userdata_cb: Some(frame), 179 | cleanup_userdata_cb: Some(cleanup), 180 | user_data, 181 | width: 800, 182 | height: 600, 183 | sample_count: 4, 184 | window_title: c"vertexpull.rs".as_ptr(), 185 | logger: sapp::Logger { func: Some(sokol::log::slog_func), ..Default::default() }, 186 | icon: sapp::IconDesc { sokol_default: true, ..Default::default() }, 187 | 188 | ..Default::default() 189 | }); 190 | } 191 | -------------------------------------------------------------------------------- /fmt.sh: -------------------------------------------------------------------------------- 1 | cargo +nightly fmt 2 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | # 2 | # NOTE: For info about what these do see https://rust-lang.github.io/rustfmt/ 3 | # 4 | 5 | group_imports = "StdExternalCrate" 6 | match_block_trailing_comma = true 7 | use_field_init_shorthand = true 8 | normalize_doc_attributes = true 9 | overflow_delimited_expr = true 10 | imports_granularity = "Crate" 11 | use_small_heuristics = "Max" 12 | reorder_impl_items = true 13 | use_try_shorthand = true 14 | newline_style = "Unix" 15 | wrap_comments = true 16 | version = "Two" 17 | 18 | short_array_element_width_threshold = 20 19 | single_line_if_else_max_width = 80 20 | struct_lit_width = 80 21 | comment_width = 110 22 | max_width = 110 23 | -------------------------------------------------------------------------------- /src/audio.rs: -------------------------------------------------------------------------------- 1 | // machine generated, do not edit 2 | 3 | #![allow(dead_code)] 4 | #![allow(unused_imports)] 5 | 6 | /// Helper function to convert a C string to a Rust string slice 7 | #[inline] 8 | fn c_char_ptr_to_rust_str(c_char_ptr: *const core::ffi::c_char) -> &'static str { 9 | let c_str = unsafe { core::ffi::CStr::from_ptr(c_char_ptr) }; 10 | c_str.to_str().expect("c_char_ptr contained invalid Utf8 Data") 11 | } 12 | 13 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 14 | #[repr(i32)] 15 | pub enum LogItem { 16 | Ok, 17 | MallocFailed, 18 | AlsaSndPcmOpenFailed, 19 | AlsaFloatSamplesNotSupported, 20 | AlsaRequestedBufferSizeNotSupported, 21 | AlsaRequestedChannelCountNotSupported, 22 | AlsaSndPcmHwParamsSetRateNearFailed, 23 | AlsaSndPcmHwParamsFailed, 24 | AlsaPthreadCreateFailed, 25 | WasapiCreateEventFailed, 26 | WasapiCreateDeviceEnumeratorFailed, 27 | WasapiGetDefaultAudioEndpointFailed, 28 | WasapiDeviceActivateFailed, 29 | WasapiAudioClientInitializeFailed, 30 | WasapiAudioClientGetBufferSizeFailed, 31 | WasapiAudioClientGetServiceFailed, 32 | WasapiAudioClientSetEventHandleFailed, 33 | WasapiCreateThreadFailed, 34 | AaudioStreambuilderOpenStreamFailed, 35 | AaudioPthreadCreateFailed, 36 | AaudioRestartingStreamAfterError, 37 | UsingAaudioBackend, 38 | AaudioCreateStreambuilderFailed, 39 | CoreaudioNewOutputFailed, 40 | CoreaudioAllocateBufferFailed, 41 | CoreaudioStartFailed, 42 | BackendBufferSizeIsntMultipleOfPacketSize, 43 | VitaSceaudioOpenFailed, 44 | VitaPthreadCreateFailed, 45 | } 46 | impl LogItem { 47 | pub const fn new() -> Self { 48 | Self::Ok 49 | } 50 | } 51 | impl Default for LogItem { 52 | fn default() -> Self { 53 | Self::Ok 54 | } 55 | } 56 | #[repr(C)] 57 | #[derive(Copy, Clone, Debug)] 58 | pub struct Logger { 59 | pub func: Option< 60 | extern "C" fn( 61 | *const core::ffi::c_char, 62 | u32, 63 | u32, 64 | *const core::ffi::c_char, 65 | u32, 66 | *const core::ffi::c_char, 67 | *mut core::ffi::c_void, 68 | ), 69 | >, 70 | pub user_data: *mut core::ffi::c_void, 71 | } 72 | impl Logger { 73 | pub const fn new() -> Self { 74 | Self { func: None, user_data: core::ptr::null_mut() } 75 | } 76 | } 77 | impl Default for Logger { 78 | fn default() -> Self { 79 | Self::new() 80 | } 81 | } 82 | #[repr(C)] 83 | #[derive(Copy, Clone, Debug)] 84 | pub struct Allocator { 85 | pub alloc_fn: Option *mut core::ffi::c_void>, 86 | pub free_fn: Option, 87 | pub user_data: *mut core::ffi::c_void, 88 | } 89 | impl Allocator { 90 | pub const fn new() -> Self { 91 | Self { alloc_fn: None, free_fn: None, user_data: core::ptr::null_mut() } 92 | } 93 | } 94 | impl Default for Allocator { 95 | fn default() -> Self { 96 | Self::new() 97 | } 98 | } 99 | #[repr(C)] 100 | #[derive(Copy, Clone, Debug)] 101 | pub struct Desc { 102 | pub sample_rate: i32, 103 | pub num_channels: i32, 104 | pub buffer_frames: i32, 105 | pub packet_frames: i32, 106 | pub num_packets: i32, 107 | pub stream_cb: Option, 108 | pub stream_userdata_cb: Option, 109 | pub user_data: *mut core::ffi::c_void, 110 | pub allocator: Allocator, 111 | pub logger: Logger, 112 | } 113 | impl Desc { 114 | pub const fn new() -> Self { 115 | Self { 116 | sample_rate: 0, 117 | num_channels: 0, 118 | buffer_frames: 0, 119 | packet_frames: 0, 120 | num_packets: 0, 121 | stream_cb: None, 122 | stream_userdata_cb: None, 123 | user_data: core::ptr::null_mut(), 124 | allocator: Allocator::new(), 125 | logger: Logger::new(), 126 | } 127 | } 128 | } 129 | impl Default for Desc { 130 | fn default() -> Self { 131 | Self::new() 132 | } 133 | } 134 | pub mod ffi { 135 | #![allow(unused_imports)] 136 | use super::*; 137 | extern "C" { 138 | pub fn saudio_setup(desc: *const Desc); 139 | pub fn saudio_shutdown(); 140 | pub fn saudio_isvalid() -> bool; 141 | pub fn saudio_userdata() -> *mut core::ffi::c_void; 142 | pub fn saudio_query_desc() -> Desc; 143 | pub fn saudio_sample_rate() -> i32; 144 | pub fn saudio_buffer_frames() -> i32; 145 | pub fn saudio_channels() -> i32; 146 | pub fn saudio_suspended() -> bool; 147 | pub fn saudio_expect() -> i32; 148 | pub fn saudio_push(frames: *const f32, num_frames: i32) -> i32; 149 | } 150 | } 151 | #[inline] 152 | pub fn setup(desc: &Desc) { 153 | unsafe { ffi::saudio_setup(desc) } 154 | } 155 | #[inline] 156 | pub fn shutdown() { 157 | unsafe { ffi::saudio_shutdown() } 158 | } 159 | #[inline] 160 | pub fn isvalid() -> bool { 161 | unsafe { ffi::saudio_isvalid() } 162 | } 163 | #[inline] 164 | pub fn userdata() -> *mut core::ffi::c_void { 165 | unsafe { ffi::saudio_userdata() } 166 | } 167 | #[inline] 168 | pub fn query_desc() -> Desc { 169 | unsafe { ffi::saudio_query_desc() } 170 | } 171 | #[inline] 172 | pub fn sample_rate() -> i32 { 173 | unsafe { ffi::saudio_sample_rate() } 174 | } 175 | #[inline] 176 | pub fn buffer_frames() -> i32 { 177 | unsafe { ffi::saudio_buffer_frames() } 178 | } 179 | #[inline] 180 | pub fn channels() -> i32 { 181 | unsafe { ffi::saudio_channels() } 182 | } 183 | #[inline] 184 | pub fn suspended() -> bool { 185 | unsafe { ffi::saudio_suspended() } 186 | } 187 | #[inline] 188 | pub fn expect() -> i32 { 189 | unsafe { ffi::saudio_expect() } 190 | } 191 | #[inline] 192 | pub fn push(frames: &f32, num_frames: i32) -> i32 { 193 | unsafe { ffi::saudio_push(frames, num_frames) } 194 | } 195 | -------------------------------------------------------------------------------- /src/debugtext.rs: -------------------------------------------------------------------------------- 1 | // machine generated, do not edit 2 | 3 | #![allow(dead_code)] 4 | #![allow(unused_imports)] 5 | 6 | use crate::gfx as sg; 7 | 8 | /// Helper function to convert a C string to a Rust string slice 9 | #[inline] 10 | fn c_char_ptr_to_rust_str(c_char_ptr: *const core::ffi::c_char) -> &'static str { 11 | let c_str = unsafe { core::ffi::CStr::from_ptr(c_char_ptr) }; 12 | c_str.to_str().expect("c_char_ptr contained invalid Utf8 Data") 13 | } 14 | 15 | /// Helper function to cast a Rust slice into a sokol Range 16 | pub fn slice_as_range(data: &[T]) -> Range { 17 | Range { size: std::mem::size_of_val(data), ptr: data.as_ptr() as *const _ } 18 | } 19 | /// Helper function to cast a Rust reference into a sokol Range 20 | pub fn value_as_range(value: &T) -> Range { 21 | Range { size: std::mem::size_of::(), ptr: value as *const T as *const _ } 22 | } 23 | 24 | impl From<&[T]> for Range { 25 | #[inline] 26 | fn from(data: &[T]) -> Self { 27 | slice_as_range(data) 28 | } 29 | } 30 | impl From<&T> for Range { 31 | #[inline] 32 | fn from(value: &T) -> Self { 33 | value_as_range(value) 34 | } 35 | } 36 | 37 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 38 | #[repr(i32)] 39 | pub enum LogItem { 40 | Ok, 41 | MallocFailed, 42 | AddCommitListenerFailed, 43 | CommandBufferFull, 44 | ContextPoolExhausted, 45 | CannotDestroyDefaultContext, 46 | } 47 | impl LogItem { 48 | pub const fn new() -> Self { 49 | Self::Ok 50 | } 51 | } 52 | impl Default for LogItem { 53 | fn default() -> Self { 54 | Self::Ok 55 | } 56 | } 57 | #[repr(C)] 58 | #[derive(Copy, Clone, Debug)] 59 | pub struct Logger { 60 | pub func: Option< 61 | extern "C" fn( 62 | *const core::ffi::c_char, 63 | u32, 64 | u32, 65 | *const core::ffi::c_char, 66 | u32, 67 | *const core::ffi::c_char, 68 | *mut core::ffi::c_void, 69 | ), 70 | >, 71 | pub user_data: *mut core::ffi::c_void, 72 | } 73 | impl Logger { 74 | pub const fn new() -> Self { 75 | Self { func: None, user_data: core::ptr::null_mut() } 76 | } 77 | } 78 | impl Default for Logger { 79 | fn default() -> Self { 80 | Self::new() 81 | } 82 | } 83 | #[repr(C)] 84 | #[derive(Copy, Clone, Debug)] 85 | pub struct Context { 86 | pub id: u32, 87 | } 88 | impl Context { 89 | pub const fn new() -> Self { 90 | Self { id: 0 } 91 | } 92 | } 93 | impl Default for Context { 94 | fn default() -> Self { 95 | Self::new() 96 | } 97 | } 98 | #[repr(C)] 99 | #[derive(Copy, Clone, Debug)] 100 | pub struct Range { 101 | pub ptr: *const core::ffi::c_void, 102 | pub size: usize, 103 | } 104 | impl Range { 105 | pub const fn new() -> Self { 106 | Self { ptr: core::ptr::null(), size: 0 } 107 | } 108 | } 109 | impl Default for Range { 110 | fn default() -> Self { 111 | Self::new() 112 | } 113 | } 114 | #[repr(C)] 115 | #[derive(Copy, Clone, Debug)] 116 | pub struct FontDesc { 117 | pub data: Range, 118 | pub first_char: u8, 119 | pub last_char: u8, 120 | } 121 | impl FontDesc { 122 | pub const fn new() -> Self { 123 | Self { data: Range::new(), first_char: 0, last_char: 0 } 124 | } 125 | } 126 | impl Default for FontDesc { 127 | fn default() -> Self { 128 | Self::new() 129 | } 130 | } 131 | #[repr(C)] 132 | #[derive(Copy, Clone, Debug)] 133 | pub struct ContextDesc { 134 | pub max_commands: i32, 135 | pub char_buf_size: i32, 136 | pub canvas_width: f32, 137 | pub canvas_height: f32, 138 | pub tab_width: i32, 139 | pub color_format: sg::PixelFormat, 140 | pub depth_format: sg::PixelFormat, 141 | pub sample_count: i32, 142 | } 143 | impl ContextDesc { 144 | pub const fn new() -> Self { 145 | Self { 146 | max_commands: 0, 147 | char_buf_size: 0, 148 | canvas_width: 0.0, 149 | canvas_height: 0.0, 150 | tab_width: 0, 151 | color_format: sg::PixelFormat::new(), 152 | depth_format: sg::PixelFormat::new(), 153 | sample_count: 0, 154 | } 155 | } 156 | } 157 | impl Default for ContextDesc { 158 | fn default() -> Self { 159 | Self::new() 160 | } 161 | } 162 | #[repr(C)] 163 | #[derive(Copy, Clone, Debug)] 164 | pub struct Allocator { 165 | pub alloc_fn: Option *mut core::ffi::c_void>, 166 | pub free_fn: Option, 167 | pub user_data: *mut core::ffi::c_void, 168 | } 169 | impl Allocator { 170 | pub const fn new() -> Self { 171 | Self { alloc_fn: None, free_fn: None, user_data: core::ptr::null_mut() } 172 | } 173 | } 174 | impl Default for Allocator { 175 | fn default() -> Self { 176 | Self::new() 177 | } 178 | } 179 | #[repr(C)] 180 | #[derive(Copy, Clone, Debug)] 181 | pub struct Desc { 182 | pub context_pool_size: i32, 183 | pub printf_buf_size: i32, 184 | pub fonts: [FontDesc; 8], 185 | pub context: ContextDesc, 186 | pub allocator: Allocator, 187 | pub logger: Logger, 188 | } 189 | impl Desc { 190 | pub const fn new() -> Self { 191 | Self { 192 | context_pool_size: 0, 193 | printf_buf_size: 0, 194 | fonts: [FontDesc::new(); 8], 195 | context: ContextDesc::new(), 196 | allocator: Allocator::new(), 197 | logger: Logger::new(), 198 | } 199 | } 200 | } 201 | impl Default for Desc { 202 | fn default() -> Self { 203 | Self::new() 204 | } 205 | } 206 | pub mod ffi { 207 | #![allow(unused_imports)] 208 | use super::*; 209 | extern "C" { 210 | pub fn sdtx_setup(desc: *const Desc); 211 | pub fn sdtx_shutdown(); 212 | pub fn sdtx_font_kc853() -> FontDesc; 213 | pub fn sdtx_font_kc854() -> FontDesc; 214 | pub fn sdtx_font_z1013() -> FontDesc; 215 | pub fn sdtx_font_cpc() -> FontDesc; 216 | pub fn sdtx_font_c64() -> FontDesc; 217 | pub fn sdtx_font_oric() -> FontDesc; 218 | pub fn sdtx_make_context(desc: *const ContextDesc) -> Context; 219 | pub fn sdtx_destroy_context(ctx: Context); 220 | pub fn sdtx_set_context(ctx: Context); 221 | pub fn sdtx_get_context() -> Context; 222 | pub fn sdtx_default_context() -> Context; 223 | pub fn sdtx_draw(); 224 | pub fn sdtx_context_draw(ctx: Context); 225 | pub fn sdtx_draw_layer(layer_id: i32); 226 | pub fn sdtx_context_draw_layer(ctx: Context, layer_id: i32); 227 | pub fn sdtx_layer(layer_id: i32); 228 | pub fn sdtx_font(font_index: usize); 229 | pub fn sdtx_canvas(w: f32, h: f32); 230 | pub fn sdtx_origin(x: f32, y: f32); 231 | pub fn sdtx_home(); 232 | pub fn sdtx_pos(x: f32, y: f32); 233 | pub fn sdtx_pos_x(x: f32); 234 | pub fn sdtx_pos_y(y: f32); 235 | pub fn sdtx_move(dx: f32, dy: f32); 236 | pub fn sdtx_move_x(dx: f32); 237 | pub fn sdtx_move_y(dy: f32); 238 | pub fn sdtx_crlf(); 239 | pub fn sdtx_color3b(r: u8, g: u8, b: u8); 240 | pub fn sdtx_color3f(r: f32, g: f32, b: f32); 241 | pub fn sdtx_color4b(r: u8, g: u8, b: u8, a: u8); 242 | pub fn sdtx_color4f(r: f32, g: f32, b: f32, a: f32); 243 | pub fn sdtx_color1i(rgba: u32); 244 | pub fn sdtx_putc(c: core::ffi::c_char); 245 | pub fn sdtx_puts(str: *const core::ffi::c_char); 246 | pub fn sdtx_putr(str: *const core::ffi::c_char, len: i32); 247 | pub fn sdtx_get_cleared_fmt_buffer() -> Range; 248 | } 249 | } 250 | #[inline] 251 | pub fn setup(desc: &Desc) { 252 | unsafe { ffi::sdtx_setup(desc) } 253 | } 254 | #[inline] 255 | pub fn shutdown() { 256 | unsafe { ffi::sdtx_shutdown() } 257 | } 258 | #[inline] 259 | pub fn font_kc853() -> FontDesc { 260 | unsafe { ffi::sdtx_font_kc853() } 261 | } 262 | #[inline] 263 | pub fn font_kc854() -> FontDesc { 264 | unsafe { ffi::sdtx_font_kc854() } 265 | } 266 | #[inline] 267 | pub fn font_z1013() -> FontDesc { 268 | unsafe { ffi::sdtx_font_z1013() } 269 | } 270 | #[inline] 271 | pub fn font_cpc() -> FontDesc { 272 | unsafe { ffi::sdtx_font_cpc() } 273 | } 274 | #[inline] 275 | pub fn font_c64() -> FontDesc { 276 | unsafe { ffi::sdtx_font_c64() } 277 | } 278 | #[inline] 279 | pub fn font_oric() -> FontDesc { 280 | unsafe { ffi::sdtx_font_oric() } 281 | } 282 | #[inline] 283 | pub fn make_context(desc: &ContextDesc) -> Context { 284 | unsafe { ffi::sdtx_make_context(desc) } 285 | } 286 | #[inline] 287 | pub fn destroy_context(ctx: Context) { 288 | unsafe { ffi::sdtx_destroy_context(ctx) } 289 | } 290 | #[inline] 291 | pub fn set_context(ctx: Context) { 292 | unsafe { ffi::sdtx_set_context(ctx) } 293 | } 294 | #[inline] 295 | pub fn get_context() -> Context { 296 | unsafe { ffi::sdtx_get_context() } 297 | } 298 | #[inline] 299 | pub fn default_context() -> Context { 300 | unsafe { ffi::sdtx_default_context() } 301 | } 302 | #[inline] 303 | pub fn draw() { 304 | unsafe { ffi::sdtx_draw() } 305 | } 306 | #[inline] 307 | pub fn context_draw(ctx: Context) { 308 | unsafe { ffi::sdtx_context_draw(ctx) } 309 | } 310 | #[inline] 311 | pub fn draw_layer(layer_id: i32) { 312 | unsafe { ffi::sdtx_draw_layer(layer_id) } 313 | } 314 | #[inline] 315 | pub fn context_draw_layer(ctx: Context, layer_id: i32) { 316 | unsafe { ffi::sdtx_context_draw_layer(ctx, layer_id) } 317 | } 318 | #[inline] 319 | pub fn layer(layer_id: i32) { 320 | unsafe { ffi::sdtx_layer(layer_id) } 321 | } 322 | #[inline] 323 | pub fn font(font_index: usize) { 324 | unsafe { ffi::sdtx_font(font_index) } 325 | } 326 | #[inline] 327 | pub fn canvas(w: f32, h: f32) { 328 | unsafe { ffi::sdtx_canvas(w, h) } 329 | } 330 | #[inline] 331 | pub fn origin(x: f32, y: f32) { 332 | unsafe { ffi::sdtx_origin(x, y) } 333 | } 334 | #[inline] 335 | pub fn home() { 336 | unsafe { ffi::sdtx_home() } 337 | } 338 | #[inline] 339 | pub fn pos(x: f32, y: f32) { 340 | unsafe { ffi::sdtx_pos(x, y) } 341 | } 342 | #[inline] 343 | pub fn pos_x(x: f32) { 344 | unsafe { ffi::sdtx_pos_x(x) } 345 | } 346 | #[inline] 347 | pub fn pos_y(y: f32) { 348 | unsafe { ffi::sdtx_pos_y(y) } 349 | } 350 | #[inline] 351 | pub fn move_cursor(dx: f32, dy: f32) { 352 | unsafe { ffi::sdtx_move(dx, dy) } 353 | } 354 | #[inline] 355 | pub fn move_cursor_x(dx: f32) { 356 | unsafe { ffi::sdtx_move_x(dx) } 357 | } 358 | #[inline] 359 | pub fn move_cursor_y(dy: f32) { 360 | unsafe { ffi::sdtx_move_y(dy) } 361 | } 362 | #[inline] 363 | pub fn crlf() { 364 | unsafe { ffi::sdtx_crlf() } 365 | } 366 | #[inline] 367 | pub fn color3b(r: u8, g: u8, b: u8) { 368 | unsafe { ffi::sdtx_color3b(r, g, b) } 369 | } 370 | #[inline] 371 | pub fn color3f(r: f32, g: f32, b: f32) { 372 | unsafe { ffi::sdtx_color3f(r, g, b) } 373 | } 374 | #[inline] 375 | pub fn color4b(r: u8, g: u8, b: u8, a: u8) { 376 | unsafe { ffi::sdtx_color4b(r, g, b, a) } 377 | } 378 | #[inline] 379 | pub fn color4f(r: f32, g: f32, b: f32, a: f32) { 380 | unsafe { ffi::sdtx_color4f(r, g, b, a) } 381 | } 382 | #[inline] 383 | pub fn color1i(rgba: u32) { 384 | unsafe { ffi::sdtx_color1i(rgba) } 385 | } 386 | #[inline] 387 | pub fn putc(c: core::ffi::c_char) { 388 | unsafe { ffi::sdtx_putc(c) } 389 | } 390 | #[inline] 391 | pub fn puts(str: &str) { 392 | let tmp_0 = std::ffi::CString::new(str).unwrap(); 393 | unsafe { ffi::sdtx_puts(tmp_0.as_ptr()) } 394 | } 395 | #[inline] 396 | pub fn putr(str: &str, len: i32) { 397 | let tmp_0 = std::ffi::CString::new(str).unwrap(); 398 | unsafe { ffi::sdtx_putr(tmp_0.as_ptr(), len) } 399 | } 400 | #[inline] 401 | pub fn get_cleared_fmt_buffer() -> Range { 402 | unsafe { ffi::sdtx_get_cleared_fmt_buffer() } 403 | } 404 | -------------------------------------------------------------------------------- /src/glue.rs: -------------------------------------------------------------------------------- 1 | // machine generated, do not edit 2 | 3 | #![allow(dead_code)] 4 | #![allow(unused_imports)] 5 | 6 | use crate::gfx as sg; 7 | 8 | /// Helper function to convert a C string to a Rust string slice 9 | #[inline] 10 | fn c_char_ptr_to_rust_str(c_char_ptr: *const core::ffi::c_char) -> &'static str { 11 | let c_str = unsafe { core::ffi::CStr::from_ptr(c_char_ptr) }; 12 | c_str.to_str().expect("c_char_ptr contained invalid Utf8 Data") 13 | } 14 | 15 | pub mod ffi { 16 | #![allow(unused_imports)] 17 | use super::*; 18 | extern "C" { 19 | pub fn sglue_environment() -> sg::Environment; 20 | pub fn sglue_swapchain() -> sg::Swapchain; 21 | } 22 | } 23 | #[inline] 24 | pub fn environment() -> sg::Environment { 25 | unsafe { ffi::sglue_environment() } 26 | } 27 | #[inline] 28 | pub fn swapchain() -> sg::Swapchain { 29 | unsafe { ffi::sglue_swapchain() } 30 | } 31 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Automatically generated sokol bindings for Rust 2 | 3 | pub mod app; 4 | pub mod audio; 5 | pub mod debugtext; 6 | pub mod gfx; 7 | pub mod gl; 8 | pub mod glue; 9 | pub mod log; 10 | pub mod shape; 11 | pub mod time; 12 | -------------------------------------------------------------------------------- /src/log.rs: -------------------------------------------------------------------------------- 1 | // machine generated, do not edit 2 | 3 | #![allow(dead_code)] 4 | #![allow(unused_imports)] 5 | 6 | /// Helper function to convert a C string to a Rust string slice 7 | #[inline] 8 | fn c_char_ptr_to_rust_str(c_char_ptr: *const core::ffi::c_char) -> &'static str { 9 | let c_str = unsafe { core::ffi::CStr::from_ptr(c_char_ptr) }; 10 | c_str.to_str().expect("c_char_ptr contained invalid Utf8 Data") 11 | } 12 | 13 | pub mod ffi { 14 | #![allow(unused_imports)] 15 | use super::*; 16 | extern "C" { 17 | pub fn slog_func( 18 | tag: *const core::ffi::c_char, 19 | log_level: u32, 20 | log_item: u32, 21 | message: *const core::ffi::c_char, 22 | line_nr: u32, 23 | filename: *const core::ffi::c_char, 24 | user_data: *mut core::ffi::c_void, 25 | ); 26 | } 27 | } 28 | #[inline] 29 | pub extern "C" fn slog_func( 30 | tag: *const core::ffi::c_char, 31 | log_level: u32, 32 | log_item: u32, 33 | message: *const core::ffi::c_char, 34 | line_nr: u32, 35 | filename: *const core::ffi::c_char, 36 | user_data: *mut core::ffi::c_void, 37 | ) { 38 | unsafe { ffi::slog_func(tag, log_level, log_item, message, line_nr, filename, user_data) } 39 | } 40 | -------------------------------------------------------------------------------- /src/shape.rs: -------------------------------------------------------------------------------- 1 | // machine generated, do not edit 2 | 3 | #![allow(dead_code)] 4 | #![allow(unused_imports)] 5 | 6 | use crate::gfx as sg; 7 | 8 | /// Helper function to convert a C string to a Rust string slice 9 | #[inline] 10 | fn c_char_ptr_to_rust_str(c_char_ptr: *const core::ffi::c_char) -> &'static str { 11 | let c_str = unsafe { core::ffi::CStr::from_ptr(c_char_ptr) }; 12 | c_str.to_str().expect("c_char_ptr contained invalid Utf8 Data") 13 | } 14 | 15 | /// Helper function to cast a Rust slice into a sokol Range 16 | pub fn slice_as_range(data: &[T]) -> Range { 17 | Range { size: std::mem::size_of_val(data), ptr: data.as_ptr() as *const _ } 18 | } 19 | /// Helper function to cast a Rust reference into a sokol Range 20 | pub fn value_as_range(value: &T) -> Range { 21 | Range { size: std::mem::size_of::(), ptr: value as *const T as *const _ } 22 | } 23 | 24 | impl From<&[T]> for Range { 25 | #[inline] 26 | fn from(data: &[T]) -> Self { 27 | slice_as_range(data) 28 | } 29 | } 30 | impl From<&T> for Range { 31 | #[inline] 32 | fn from(value: &T) -> Self { 33 | value_as_range(value) 34 | } 35 | } 36 | 37 | #[repr(C)] 38 | #[derive(Copy, Clone, Debug)] 39 | pub struct Range { 40 | pub ptr: *const core::ffi::c_void, 41 | pub size: usize, 42 | } 43 | impl Range { 44 | pub const fn new() -> Self { 45 | Self { ptr: core::ptr::null(), size: 0 } 46 | } 47 | } 48 | impl Default for Range { 49 | fn default() -> Self { 50 | Self::new() 51 | } 52 | } 53 | #[repr(C)] 54 | #[derive(Copy, Clone, Debug)] 55 | pub struct Mat4 { 56 | pub m: [[f32; 4]; 4], 57 | } 58 | impl Mat4 { 59 | pub const fn new() -> Self { 60 | Self { m: [[0.0; 4]; 4] } 61 | } 62 | } 63 | impl Default for Mat4 { 64 | fn default() -> Self { 65 | Self::new() 66 | } 67 | } 68 | #[repr(C)] 69 | #[derive(Copy, Clone, Debug)] 70 | pub struct Vertex { 71 | pub x: f32, 72 | pub y: f32, 73 | pub z: f32, 74 | pub normal: u32, 75 | pub u: u16, 76 | pub v: u16, 77 | pub color: u32, 78 | } 79 | impl Vertex { 80 | pub const fn new() -> Self { 81 | Self { x: 0.0, y: 0.0, z: 0.0, normal: 0, u: 0, v: 0, color: 0 } 82 | } 83 | } 84 | impl Default for Vertex { 85 | fn default() -> Self { 86 | Self::new() 87 | } 88 | } 89 | #[repr(C)] 90 | #[derive(Copy, Clone, Debug)] 91 | pub struct ElementRange { 92 | pub base_element: usize, 93 | pub num_elements: usize, 94 | } 95 | impl ElementRange { 96 | pub const fn new() -> Self { 97 | Self { base_element: 0, num_elements: 0 } 98 | } 99 | } 100 | impl Default for ElementRange { 101 | fn default() -> Self { 102 | Self::new() 103 | } 104 | } 105 | #[repr(C)] 106 | #[derive(Copy, Clone, Debug)] 107 | pub struct SizesItem { 108 | pub num: u32, 109 | pub size: u32, 110 | } 111 | impl SizesItem { 112 | pub const fn new() -> Self { 113 | Self { num: 0, size: 0 } 114 | } 115 | } 116 | impl Default for SizesItem { 117 | fn default() -> Self { 118 | Self::new() 119 | } 120 | } 121 | #[repr(C)] 122 | #[derive(Copy, Clone, Debug)] 123 | pub struct Sizes { 124 | pub vertices: SizesItem, 125 | pub indices: SizesItem, 126 | } 127 | impl Sizes { 128 | pub const fn new() -> Self { 129 | Self { vertices: SizesItem::new(), indices: SizesItem::new() } 130 | } 131 | } 132 | impl Default for Sizes { 133 | fn default() -> Self { 134 | Self::new() 135 | } 136 | } 137 | #[repr(C)] 138 | #[derive(Copy, Clone, Debug)] 139 | pub struct BufferItem { 140 | pub buffer: Range, 141 | pub data_size: usize, 142 | pub shape_offset: usize, 143 | } 144 | impl BufferItem { 145 | pub const fn new() -> Self { 146 | Self { buffer: Range::new(), data_size: 0, shape_offset: 0 } 147 | } 148 | } 149 | impl Default for BufferItem { 150 | fn default() -> Self { 151 | Self::new() 152 | } 153 | } 154 | #[repr(C)] 155 | #[derive(Copy, Clone, Debug)] 156 | pub struct Buffer { 157 | pub valid: bool, 158 | pub vertices: BufferItem, 159 | pub indices: BufferItem, 160 | } 161 | impl Buffer { 162 | pub const fn new() -> Self { 163 | Self { valid: false, vertices: BufferItem::new(), indices: BufferItem::new() } 164 | } 165 | } 166 | impl Default for Buffer { 167 | fn default() -> Self { 168 | Self::new() 169 | } 170 | } 171 | #[repr(C)] 172 | #[derive(Copy, Clone, Debug)] 173 | pub struct Plane { 174 | pub width: f32, 175 | pub depth: f32, 176 | pub tiles: u16, 177 | pub color: u32, 178 | pub random_colors: bool, 179 | pub merge: bool, 180 | pub transform: Mat4, 181 | } 182 | impl Plane { 183 | pub const fn new() -> Self { 184 | Self { 185 | width: 0.0, 186 | depth: 0.0, 187 | tiles: 0, 188 | color: 0, 189 | random_colors: false, 190 | merge: false, 191 | transform: Mat4::new(), 192 | } 193 | } 194 | } 195 | impl Default for Plane { 196 | fn default() -> Self { 197 | Self::new() 198 | } 199 | } 200 | #[repr(C)] 201 | #[derive(Copy, Clone, Debug)] 202 | pub struct Box { 203 | pub width: f32, 204 | pub height: f32, 205 | pub depth: f32, 206 | pub tiles: u16, 207 | pub color: u32, 208 | pub random_colors: bool, 209 | pub merge: bool, 210 | pub transform: Mat4, 211 | } 212 | impl Box { 213 | pub const fn new() -> Self { 214 | Self { 215 | width: 0.0, 216 | height: 0.0, 217 | depth: 0.0, 218 | tiles: 0, 219 | color: 0, 220 | random_colors: false, 221 | merge: false, 222 | transform: Mat4::new(), 223 | } 224 | } 225 | } 226 | impl Default for Box { 227 | fn default() -> Self { 228 | Self::new() 229 | } 230 | } 231 | #[repr(C)] 232 | #[derive(Copy, Clone, Debug)] 233 | pub struct Sphere { 234 | pub radius: f32, 235 | pub slices: u16, 236 | pub stacks: u16, 237 | pub color: u32, 238 | pub random_colors: bool, 239 | pub merge: bool, 240 | pub transform: Mat4, 241 | } 242 | impl Sphere { 243 | pub const fn new() -> Self { 244 | Self { 245 | radius: 0.0, 246 | slices: 0, 247 | stacks: 0, 248 | color: 0, 249 | random_colors: false, 250 | merge: false, 251 | transform: Mat4::new(), 252 | } 253 | } 254 | } 255 | impl Default for Sphere { 256 | fn default() -> Self { 257 | Self::new() 258 | } 259 | } 260 | #[repr(C)] 261 | #[derive(Copy, Clone, Debug)] 262 | pub struct Cylinder { 263 | pub radius: f32, 264 | pub height: f32, 265 | pub slices: u16, 266 | pub stacks: u16, 267 | pub color: u32, 268 | pub random_colors: bool, 269 | pub merge: bool, 270 | pub transform: Mat4, 271 | } 272 | impl Cylinder { 273 | pub const fn new() -> Self { 274 | Self { 275 | radius: 0.0, 276 | height: 0.0, 277 | slices: 0, 278 | stacks: 0, 279 | color: 0, 280 | random_colors: false, 281 | merge: false, 282 | transform: Mat4::new(), 283 | } 284 | } 285 | } 286 | impl Default for Cylinder { 287 | fn default() -> Self { 288 | Self::new() 289 | } 290 | } 291 | #[repr(C)] 292 | #[derive(Copy, Clone, Debug)] 293 | pub struct Torus { 294 | pub radius: f32, 295 | pub ring_radius: f32, 296 | pub sides: u16, 297 | pub rings: u16, 298 | pub color: u32, 299 | pub random_colors: bool, 300 | pub merge: bool, 301 | pub transform: Mat4, 302 | } 303 | impl Torus { 304 | pub const fn new() -> Self { 305 | Self { 306 | radius: 0.0, 307 | ring_radius: 0.0, 308 | sides: 0, 309 | rings: 0, 310 | color: 0, 311 | random_colors: false, 312 | merge: false, 313 | transform: Mat4::new(), 314 | } 315 | } 316 | } 317 | impl Default for Torus { 318 | fn default() -> Self { 319 | Self::new() 320 | } 321 | } 322 | pub mod ffi { 323 | #![allow(unused_imports)] 324 | use super::*; 325 | extern "C" { 326 | pub fn sshape_build_plane(buf: *const Buffer, params: *const Plane) -> Buffer; 327 | pub fn sshape_build_box(buf: *const Buffer, params: *const Box) -> Buffer; 328 | pub fn sshape_build_sphere(buf: *const Buffer, params: *const Sphere) -> Buffer; 329 | pub fn sshape_build_cylinder(buf: *const Buffer, params: *const Cylinder) -> Buffer; 330 | pub fn sshape_build_torus(buf: *const Buffer, params: *const Torus) -> Buffer; 331 | pub fn sshape_plane_sizes(tiles: u32) -> Sizes; 332 | pub fn sshape_box_sizes(tiles: u32) -> Sizes; 333 | pub fn sshape_sphere_sizes(slices: u32, stacks: u32) -> Sizes; 334 | pub fn sshape_cylinder_sizes(slices: u32, stacks: u32) -> Sizes; 335 | pub fn sshape_torus_sizes(sides: u32, rings: u32) -> Sizes; 336 | pub fn sshape_element_range(buf: *const Buffer) -> ElementRange; 337 | pub fn sshape_vertex_buffer_desc(buf: *const Buffer) -> sg::BufferDesc; 338 | pub fn sshape_index_buffer_desc(buf: *const Buffer) -> sg::BufferDesc; 339 | pub fn sshape_vertex_buffer_layout_state() -> sg::VertexBufferLayoutState; 340 | pub fn sshape_position_vertex_attr_state() -> sg::VertexAttrState; 341 | pub fn sshape_normal_vertex_attr_state() -> sg::VertexAttrState; 342 | pub fn sshape_texcoord_vertex_attr_state() -> sg::VertexAttrState; 343 | pub fn sshape_color_vertex_attr_state() -> sg::VertexAttrState; 344 | pub fn sshape_color_4f(r: f32, g: f32, b: f32, a: f32) -> u32; 345 | pub fn sshape_color_3f(r: f32, g: f32, b: f32) -> u32; 346 | pub fn sshape_color_4b(r: u8, g: u8, b: u8, a: u8) -> u32; 347 | pub fn sshape_color_3b(r: u8, g: u8, b: u8) -> u32; 348 | pub fn sshape_mat4(m: *const f32) -> Mat4; 349 | pub fn sshape_mat4_transpose(m: *const f32) -> Mat4; 350 | } 351 | } 352 | #[inline] 353 | pub fn build_plane(buf: &Buffer, params: &Plane) -> Buffer { 354 | unsafe { ffi::sshape_build_plane(buf, params) } 355 | } 356 | #[inline] 357 | pub fn build_box(buf: &Buffer, params: &Box) -> Buffer { 358 | unsafe { ffi::sshape_build_box(buf, params) } 359 | } 360 | #[inline] 361 | pub fn build_sphere(buf: &Buffer, params: &Sphere) -> Buffer { 362 | unsafe { ffi::sshape_build_sphere(buf, params) } 363 | } 364 | #[inline] 365 | pub fn build_cylinder(buf: &Buffer, params: &Cylinder) -> Buffer { 366 | unsafe { ffi::sshape_build_cylinder(buf, params) } 367 | } 368 | #[inline] 369 | pub fn build_torus(buf: &Buffer, params: &Torus) -> Buffer { 370 | unsafe { ffi::sshape_build_torus(buf, params) } 371 | } 372 | #[inline] 373 | pub fn plane_sizes(tiles: u32) -> Sizes { 374 | unsafe { ffi::sshape_plane_sizes(tiles) } 375 | } 376 | #[inline] 377 | pub fn box_sizes(tiles: u32) -> Sizes { 378 | unsafe { ffi::sshape_box_sizes(tiles) } 379 | } 380 | #[inline] 381 | pub fn sphere_sizes(slices: u32, stacks: u32) -> Sizes { 382 | unsafe { ffi::sshape_sphere_sizes(slices, stacks) } 383 | } 384 | #[inline] 385 | pub fn cylinder_sizes(slices: u32, stacks: u32) -> Sizes { 386 | unsafe { ffi::sshape_cylinder_sizes(slices, stacks) } 387 | } 388 | #[inline] 389 | pub fn torus_sizes(sides: u32, rings: u32) -> Sizes { 390 | unsafe { ffi::sshape_torus_sizes(sides, rings) } 391 | } 392 | #[inline] 393 | pub fn element_range(buf: &Buffer) -> ElementRange { 394 | unsafe { ffi::sshape_element_range(buf) } 395 | } 396 | #[inline] 397 | pub fn vertex_buffer_desc(buf: &Buffer) -> sg::BufferDesc { 398 | unsafe { ffi::sshape_vertex_buffer_desc(buf) } 399 | } 400 | #[inline] 401 | pub fn index_buffer_desc(buf: &Buffer) -> sg::BufferDesc { 402 | unsafe { ffi::sshape_index_buffer_desc(buf) } 403 | } 404 | #[inline] 405 | pub fn vertex_buffer_layout_state() -> sg::VertexBufferLayoutState { 406 | unsafe { ffi::sshape_vertex_buffer_layout_state() } 407 | } 408 | #[inline] 409 | pub fn position_vertex_attr_state() -> sg::VertexAttrState { 410 | unsafe { ffi::sshape_position_vertex_attr_state() } 411 | } 412 | #[inline] 413 | pub fn normal_vertex_attr_state() -> sg::VertexAttrState { 414 | unsafe { ffi::sshape_normal_vertex_attr_state() } 415 | } 416 | #[inline] 417 | pub fn texcoord_vertex_attr_state() -> sg::VertexAttrState { 418 | unsafe { ffi::sshape_texcoord_vertex_attr_state() } 419 | } 420 | #[inline] 421 | pub fn color_vertex_attr_state() -> sg::VertexAttrState { 422 | unsafe { ffi::sshape_color_vertex_attr_state() } 423 | } 424 | #[inline] 425 | pub fn color_4f(r: f32, g: f32, b: f32, a: f32) -> u32 { 426 | unsafe { ffi::sshape_color_4f(r, g, b, a) } 427 | } 428 | #[inline] 429 | pub fn color_3f(r: f32, g: f32, b: f32) -> u32 { 430 | unsafe { ffi::sshape_color_3f(r, g, b) } 431 | } 432 | #[inline] 433 | pub fn color_4b(r: u8, g: u8, b: u8, a: u8) -> u32 { 434 | unsafe { ffi::sshape_color_4b(r, g, b, a) } 435 | } 436 | #[inline] 437 | pub fn color_3b(r: u8, g: u8, b: u8) -> u32 { 438 | unsafe { ffi::sshape_color_3b(r, g, b) } 439 | } 440 | #[inline] 441 | pub fn mat4(m: &f32) -> Mat4 { 442 | unsafe { ffi::sshape_mat4(m) } 443 | } 444 | #[inline] 445 | pub fn mat4_transpose(m: &f32) -> Mat4 { 446 | unsafe { ffi::sshape_mat4_transpose(m) } 447 | } 448 | -------------------------------------------------------------------------------- /src/sokol/c/sokol_app.c: -------------------------------------------------------------------------------- 1 | #if defined(IMPL) 2 | #define SOKOL_APP_IMPL 3 | #endif 4 | #include "sokol_defines.h" 5 | #include "sokol_app.h" 6 | -------------------------------------------------------------------------------- /src/sokol/c/sokol_audio.c: -------------------------------------------------------------------------------- 1 | #if defined(IMPL) 2 | #define SOKOL_AUDIO_IMPL 3 | #endif 4 | #include "sokol_audio.h" 5 | #include "sokol_defines.h" 6 | -------------------------------------------------------------------------------- /src/sokol/c/sokol_debugtext.c: -------------------------------------------------------------------------------- 1 | #if defined(IMPL) 2 | #define SOKOL_DEBUGTEXT_IMPL 3 | #endif 4 | #include "sokol_defines.h" 5 | #include "sokol_gfx.h" 6 | #include "sokol_debugtext.h" 7 | 8 | -------------------------------------------------------------------------------- /src/sokol/c/sokol_defines.h: -------------------------------------------------------------------------------- 1 | #define SOKOL_RUST_BINDINGS 2 | #define SOKOL_NO_ENTRY 3 | #if defined(_WIN32) 4 | #define SOKOL_WIN32_FORCE_MAIN 5 | #endif 6 | // FIXME: macOS Zig HACK without this, some C stdlib headers throw errors 7 | #if defined(__APPLE__) 8 | #include 9 | #endif 10 | -------------------------------------------------------------------------------- /src/sokol/c/sokol_gfx.c: -------------------------------------------------------------------------------- 1 | #if defined(IMPL) 2 | #define SOKOL_GFX_IMPL 3 | #endif 4 | #include "sokol_defines.h" 5 | #include "sokol_gfx.h" 6 | -------------------------------------------------------------------------------- /src/sokol/c/sokol_gl.c: -------------------------------------------------------------------------------- 1 | #if defined(IMPL) 2 | #define SOKOL_GL_IMPL 3 | #endif 4 | #include "sokol_defines.h" 5 | #include "sokol_gfx.h" 6 | #include "sokol_gl.h" 7 | -------------------------------------------------------------------------------- /src/sokol/c/sokol_glue.c: -------------------------------------------------------------------------------- 1 | #if defined(IMPL) 2 | #define SOKOL_GLUE_IMPL 3 | #endif 4 | #include "sokol_defines.h" 5 | #include "sokol_app.h" 6 | #include "sokol_gfx.h" 7 | #include "sokol_glue.h" 8 | -------------------------------------------------------------------------------- /src/sokol/c/sokol_glue.h: -------------------------------------------------------------------------------- 1 | #if defined(SOKOL_IMPL) && !defined(SOKOL_GLUE_IMPL) 2 | #define SOKOL_GLUE_IMPL 3 | #endif 4 | #ifndef SOKOL_GLUE_INCLUDED 5 | /* 6 | sokol_glue.h -- glue helper functions for sokol headers 7 | 8 | Project URL: https://github.com/floooh/sokol 9 | 10 | Do this: 11 | #define SOKOL_IMPL or 12 | #define SOKOL_GLUE_IMPL 13 | before you include this file in *one* C or C++ file to create the 14 | implementation. 15 | 16 | ...optionally provide the following macros to override defaults: 17 | 18 | SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) 19 | SOKOL_GLUE_API_DECL - public function declaration prefix (default: extern) 20 | SOKOL_API_DECL - same as SOKOL_GLUE_API_DECL 21 | SOKOL_API_IMPL - public function implementation prefix (default: -) 22 | 23 | If sokol_glue.h is compiled as a DLL, define the following before 24 | including the declaration or implementation: 25 | 26 | SOKOL_DLL 27 | 28 | On Windows, SOKOL_DLL will define SOKOL_GLUE_API_DECL as __declspec(dllexport) 29 | or __declspec(dllimport) as needed. 30 | 31 | OVERVIEW 32 | ======== 33 | sokol_glue.h provides glue helper functions between sokol_gfx.h and sokol_app.h, 34 | so that sokol_gfx.h doesn't need to depend on sokol_app.h but can be 35 | used with different window system glue libraries. 36 | 37 | PROVIDED FUNCTIONS 38 | ================== 39 | 40 | sg_environment sglue_environment(void) 41 | 42 | Returns an sg_environment struct initialized by calling sokol_app.h 43 | functions. Use this in the sg_setup() call like this: 44 | 45 | sg_setup(&(sg_desc){ 46 | .environment = sglue_environment(), 47 | ... 48 | }); 49 | 50 | sg_swapchain sglue_swapchain(void) 51 | 52 | Returns an sg_swapchain struct initialized by calling sokol_app.h 53 | functions. Use this in sg_begin_pass() for a 'swapchain pass' like 54 | this: 55 | 56 | sg_begin_pass(&(sg_pass){ .swapchain = sglue_swapchain(), ... }); 57 | 58 | LICENSE 59 | ======= 60 | zlib/libpng license 61 | 62 | Copyright (c) 2018 Andre Weissflog 63 | 64 | This software is provided 'as-is', without any express or implied warranty. 65 | In no event will the authors be held liable for any damages arising from the 66 | use of this software. 67 | 68 | Permission is granted to anyone to use this software for any purpose, 69 | including commercial applications, and to alter it and redistribute it 70 | freely, subject to the following restrictions: 71 | 72 | 1. The origin of this software must not be misrepresented; you must not 73 | claim that you wrote the original software. If you use this software in a 74 | product, an acknowledgment in the product documentation would be 75 | appreciated but is not required. 76 | 77 | 2. Altered source versions must be plainly marked as such, and must not 78 | be misrepresented as being the original software. 79 | 80 | 3. This notice may not be removed or altered from any source 81 | distribution. 82 | */ 83 | #define SOKOL_GLUE_INCLUDED 84 | 85 | #if defined(SOKOL_API_DECL) && !defined(SOKOL_GLUE_API_DECL) 86 | #define SOKOL_GLUE_API_DECL SOKOL_API_DECL 87 | #endif 88 | #ifndef SOKOL_GLUE_API_DECL 89 | #if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_GLUE_IMPL) 90 | #define SOKOL_GLUE_API_DECL __declspec(dllexport) 91 | #elif defined(_WIN32) && defined(SOKOL_DLL) 92 | #define SOKOL_GLUE_API_DECL __declspec(dllimport) 93 | #else 94 | #define SOKOL_GLUE_API_DECL extern 95 | #endif 96 | #endif 97 | 98 | #ifndef SOKOL_GFX_INCLUDED 99 | #error "Please include sokol_gfx.h before sokol_glue.h" 100 | #endif 101 | 102 | #ifdef __cplusplus 103 | extern "C" { 104 | #endif 105 | 106 | SOKOL_GLUE_API_DECL sg_environment sglue_environment(void); 107 | SOKOL_GLUE_API_DECL sg_swapchain sglue_swapchain(void); 108 | 109 | #ifdef __cplusplus 110 | } /* extern "C" */ 111 | #endif 112 | #endif /* SOKOL_GLUE_INCLUDED */ 113 | 114 | /*-- IMPLEMENTATION ----------------------------------------------------------*/ 115 | #ifdef SOKOL_GLUE_IMPL 116 | #define SOKOL_GLUE_IMPL_INCLUDED (1) 117 | #include /* memset */ 118 | 119 | #ifndef SOKOL_APP_INCLUDED 120 | #error "Please include sokol_app.h before the sokol_glue.h implementation" 121 | #endif 122 | 123 | #ifndef SOKOL_API_IMPL 124 | #define SOKOL_API_IMPL 125 | #endif 126 | 127 | 128 | SOKOL_API_IMPL sg_environment sglue_environment(void) { 129 | sg_environment env; 130 | memset(&env, 0, sizeof(env)); 131 | env.defaults.color_format = (sg_pixel_format) sapp_color_format(); 132 | env.defaults.depth_format = (sg_pixel_format) sapp_depth_format(); 133 | env.defaults.sample_count = sapp_sample_count(); 134 | env.metal.device = sapp_metal_get_device(); 135 | env.d3d11.device = sapp_d3d11_get_device(); 136 | env.d3d11.device_context = sapp_d3d11_get_device_context(); 137 | env.wgpu.device = sapp_wgpu_get_device(); 138 | return env; 139 | } 140 | 141 | SOKOL_API_IMPL sg_swapchain sglue_swapchain(void) { 142 | sg_swapchain swapchain; 143 | memset(&swapchain, 0, sizeof(swapchain)); 144 | swapchain.width = sapp_width(); 145 | swapchain.height = sapp_height(); 146 | swapchain.sample_count = sapp_sample_count(); 147 | swapchain.color_format = (sg_pixel_format)sapp_color_format(); 148 | swapchain.depth_format = (sg_pixel_format)sapp_depth_format(); 149 | swapchain.metal.current_drawable = sapp_metal_get_current_drawable(); 150 | swapchain.metal.depth_stencil_texture = sapp_metal_get_depth_stencil_texture(); 151 | swapchain.metal.msaa_color_texture = sapp_metal_get_msaa_color_texture(); 152 | swapchain.d3d11.render_view = sapp_d3d11_get_render_view(); 153 | swapchain.d3d11.resolve_view = sapp_d3d11_get_resolve_view(); 154 | swapchain.d3d11.depth_stencil_view = sapp_d3d11_get_depth_stencil_view(); 155 | swapchain.wgpu.render_view = sapp_wgpu_get_render_view(); 156 | swapchain.wgpu.resolve_view = sapp_wgpu_get_resolve_view(); 157 | swapchain.wgpu.depth_stencil_view = sapp_wgpu_get_depth_stencil_view(); 158 | swapchain.gl.framebuffer = sapp_gl_get_framebuffer(); 159 | return swapchain; 160 | } 161 | 162 | #endif /* SOKOL_GLUE_IMPL */ 163 | -------------------------------------------------------------------------------- /src/sokol/c/sokol_log.c: -------------------------------------------------------------------------------- 1 | #if defined(IMPL) 2 | #define SOKOL_LOG_IMPL 3 | #endif 4 | #include "sokol_defines.h" 5 | #include "sokol_log.h" 6 | -------------------------------------------------------------------------------- /src/sokol/c/sokol_log.h: -------------------------------------------------------------------------------- 1 | #if defined(SOKOL_IMPL) && !defined(SOKOL_LOG_IMPL) 2 | #define SOKOL_LOG_IMPL 3 | #endif 4 | #ifndef SOKOL_LOG_INCLUDED 5 | /* 6 | sokol_log.h -- common logging callback for sokol headers 7 | 8 | Project URL: https://github.com/floooh/sokol 9 | 10 | Example code: https://github.com/floooh/sokol-samples 11 | 12 | Do this: 13 | #define SOKOL_IMPL or 14 | #define SOKOL_LOG_IMPL 15 | before you include this file in *one* C or C++ file to create the 16 | implementation. 17 | 18 | Optionally provide the following defines when building the implementation: 19 | 20 | SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) 21 | SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) 22 | SOKOL_LOG_API_DECL - public function declaration prefix (default: extern) 23 | SOKOL_API_DECL - same as SOKOL_GFX_API_DECL 24 | SOKOL_API_IMPL - public function implementation prefix (default: -) 25 | 26 | Optionally define the following for verbose output: 27 | 28 | SOKOL_DEBUG - by default this is defined if NDEBUG is not defined 29 | 30 | 31 | OVERVIEW 32 | ======== 33 | sokol_log.h provides a default logging callback for other sokol headers. 34 | 35 | To use the default log callback, just include sokol_log.h and provide 36 | a function pointer to the 'slog_func' function when setting up the 37 | sokol library: 38 | 39 | For instance with sokol_audio.h: 40 | 41 | #include "sokol_log.h" 42 | ... 43 | saudio_setup(&(saudio_desc){ .logger.func = slog_func }); 44 | 45 | Logging output goes to stderr and/or a platform specific logging subsystem 46 | (which means that in some scenarios you might see logging messages duplicated): 47 | 48 | - Windows: stderr + OutputDebugStringA() 49 | - macOS/iOS/Linux: stderr + syslog() 50 | - Emscripten: console.info()/warn()/error() 51 | - Android: __android_log_write() 52 | 53 | On Windows with sokol_app.h also note the runtime config items to make 54 | stdout/stderr output visible on the console for WinMain() applications 55 | via sapp_desc.win32_console_attach or sapp_desc.win32_console_create, 56 | however when running in a debugger on Windows, the logging output should 57 | show up on the debug output UI panel. 58 | 59 | In debug mode, a log message might look like this: 60 | 61 | [sspine][error][id:12] /Users/floh/projects/sokol/util/sokol_spine.h:3472:0: 62 | SKELETON_DESC_NO_ATLAS: no atlas object provided in sspine_skeleton_desc.atlas 63 | 64 | The source path and line number is formatted like compiler errors, in some IDEs (like VSCode) 65 | such error messages are clickable. 66 | 67 | In release mode, logging is less verbose as to not bloat the executable with string data, but you still get 68 | enough information to identify the type and location of an error: 69 | 70 | [sspine][error][id:12][line:3472] 71 | 72 | RULES FOR WRITING YOUR OWN LOGGING FUNCTION 73 | =========================================== 74 | - must be re-entrant because it might be called from different threads 75 | - must treat **all** provided string pointers as optional (can be null) 76 | - don't store the string pointers, copy the string data instead 77 | - must not return for log level panic 78 | 79 | LICENSE 80 | ======= 81 | zlib/libpng license 82 | 83 | Copyright (c) 2023 Andre Weissflog 84 | 85 | This software is provided 'as-is', without any express or implied warranty. 86 | In no event will the authors be held liable for any damages arising from the 87 | use of this software. 88 | 89 | Permission is granted to anyone to use this software for any purpose, 90 | including commercial applications, and to alter it and redistribute it 91 | freely, subject to the following restrictions: 92 | 93 | 1. The origin of this software must not be misrepresented; you must not 94 | claim that you wrote the original software. If you use this software in a 95 | product, an acknowledgment in the product documentation would be 96 | appreciated but is not required. 97 | 98 | 2. Altered source versions must be plainly marked as such, and must not 99 | be misrepresented as being the original software. 100 | 101 | 3. This notice may not be removed or altered from any source 102 | distribution. 103 | */ 104 | #define SOKOL_LOG_INCLUDED (1) 105 | #include 106 | 107 | #if defined(SOKOL_API_DECL) && !defined(SOKOL_LOG_API_DECL) 108 | #define SOKOL_LOG_API_DECL SOKOL_API_DECL 109 | #endif 110 | #ifndef SOKOL_LOG_API_DECL 111 | #if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_LOG_IMPL) 112 | #define SOKOL_LOG_API_DECL __declspec(dllexport) 113 | #elif defined(_WIN32) && defined(SOKOL_DLL) 114 | #define SOKOL_LOG_API_DECL __declspec(dllimport) 115 | #else 116 | #define SOKOL_LOG_API_DECL extern 117 | #endif 118 | #endif 119 | 120 | #ifdef __cplusplus 121 | extern "C" { 122 | #endif 123 | 124 | /* 125 | Plug this function into the 'logger.func' struct item when initializing any of the sokol 126 | headers. For instance for sokol_audio.h it would look like this: 127 | 128 | saudio_setup(&(saudio_desc){ 129 | .logger = { 130 | .func = slog_func 131 | } 132 | }); 133 | */ 134 | SOKOL_LOG_API_DECL void slog_func(const char* tag, uint32_t log_level, uint32_t log_item, const char* message, uint32_t line_nr, const char* filename, void* user_data); 135 | 136 | #ifdef __cplusplus 137 | } // extern "C" 138 | #endif 139 | #endif // SOKOL_LOG_INCLUDED 140 | 141 | // ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██ 142 | // ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ 143 | // ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ 144 | // ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ 145 | // ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████ 146 | // 147 | // >>implementation 148 | #ifdef SOKOL_LOG_IMPL 149 | #define SOKOL_LOG_IMPL_INCLUDED (1) 150 | 151 | #ifndef SOKOL_API_IMPL 152 | #define SOKOL_API_IMPL 153 | #endif 154 | #ifndef SOKOL_DEBUG 155 | #ifndef NDEBUG 156 | #define SOKOL_DEBUG 157 | #endif 158 | #endif 159 | #ifndef SOKOL_ASSERT 160 | #include 161 | #define SOKOL_ASSERT(c) assert(c) 162 | #endif 163 | 164 | #ifndef _SOKOL_PRIVATE 165 | #if defined(__GNUC__) || defined(__clang__) 166 | #define _SOKOL_PRIVATE __attribute__((unused)) static 167 | #else 168 | #define _SOKOL_PRIVATE static 169 | #endif 170 | #endif 171 | 172 | #ifndef _SOKOL_UNUSED 173 | #define _SOKOL_UNUSED(x) (void)(x) 174 | #endif 175 | 176 | // platform detection 177 | #if defined(__APPLE__) 178 | #define _SLOG_APPLE (1) 179 | #elif defined(__EMSCRIPTEN__) 180 | #define _SLOG_EMSCRIPTEN (1) 181 | #elif defined(_WIN32) 182 | #define _SLOG_WINDOWS (1) 183 | #elif defined(__ANDROID__) 184 | #define _SLOG_ANDROID (1) 185 | #elif defined(__linux__) || defined(__unix__) 186 | #define _SLOG_LINUX (1) 187 | #else 188 | #error "sokol_log.h: unknown platform" 189 | #endif 190 | 191 | #include // abort 192 | #include // fputs 193 | #include // size_t 194 | 195 | #if defined(_SLOG_EMSCRIPTEN) 196 | #include 197 | #elif defined(_SLOG_WINDOWS) 198 | #ifndef WIN32_LEAN_AND_MEAN 199 | #define WIN32_LEAN_AND_MEAN 200 | #endif 201 | #ifndef NOMINMAX 202 | #define NOMINMAX 203 | #endif 204 | #include 205 | #elif defined(_SLOG_ANDROID) 206 | #include 207 | #elif defined(_SLOG_LINUX) || defined(_SLOG_APPLE) 208 | #include 209 | #endif 210 | 211 | // size of line buffer (on stack!) in bytes including terminating zero 212 | #define _SLOG_LINE_LENGTH (512) 213 | 214 | _SOKOL_PRIVATE char* _slog_append(const char* str, char* dst, char* end) { 215 | if (str) { 216 | char c; 217 | while (((c = *str++) != 0) && (dst < (end - 1))) { 218 | *dst++ = c; 219 | } 220 | } 221 | *dst = 0; 222 | return dst; 223 | } 224 | 225 | _SOKOL_PRIVATE char* _slog_itoa(uint32_t x, char* buf, size_t buf_size) { 226 | const size_t max_digits_and_null = 11; 227 | if (buf_size < max_digits_and_null) { 228 | return 0; 229 | } 230 | char* p = buf + max_digits_and_null; 231 | *--p = 0; 232 | do { 233 | *--p = '0' + (x % 10); 234 | x /= 10; 235 | } while (x != 0); 236 | return p; 237 | } 238 | 239 | #if defined(_SLOG_EMSCRIPTEN) 240 | EM_JS(void, slog_js_log, (uint32_t level, const char* c_str), { 241 | const str = UTF8ToString(c_str); 242 | switch (level) { 243 | case 0: console.error(str); break; 244 | case 1: console.error(str); break; 245 | case 2: console.warn(str); break; 246 | default: console.info(str); break; 247 | } 248 | }) 249 | #endif 250 | 251 | SOKOL_API_IMPL void slog_func(const char* tag, uint32_t log_level, uint32_t log_item, const char* message, uint32_t line_nr, const char* filename, void* user_data) { 252 | _SOKOL_UNUSED(user_data); 253 | 254 | const char* log_level_str; 255 | switch (log_level) { 256 | case 0: log_level_str = "panic"; break; 257 | case 1: log_level_str = "error"; break; 258 | case 2: log_level_str = "warning"; break; 259 | default: log_level_str = "info"; break; 260 | } 261 | 262 | // build log output line 263 | char line_buf[_SLOG_LINE_LENGTH]; 264 | char* str = line_buf; 265 | char* end = line_buf + sizeof(line_buf); 266 | char num_buf[32]; 267 | if (tag) { 268 | str = _slog_append("[", str, end); 269 | str = _slog_append(tag, str, end); 270 | str = _slog_append("]", str, end); 271 | } 272 | str = _slog_append("[", str, end); 273 | str = _slog_append(log_level_str, str, end); 274 | str = _slog_append("]", str, end); 275 | str = _slog_append("[id:", str, end); 276 | str = _slog_append(_slog_itoa(log_item, num_buf, sizeof(num_buf)), str, end); 277 | str = _slog_append("]", str, end); 278 | // if a filename is provided, build a clickable log message that's compatible with compiler error messages 279 | if (filename) { 280 | str = _slog_append(" ", str, end); 281 | #if defined(_MSC_VER) 282 | // MSVC compiler error format 283 | str = _slog_append(filename, str, end); 284 | str = _slog_append("(", str, end); 285 | str = _slog_append(_slog_itoa(line_nr, num_buf, sizeof(num_buf)), str, end); 286 | str = _slog_append("): ", str, end); 287 | #else 288 | // gcc/clang compiler error format 289 | str = _slog_append(filename, str, end); 290 | str = _slog_append(":", str, end); 291 | str = _slog_append(_slog_itoa(line_nr, num_buf, sizeof(num_buf)), str, end); 292 | str = _slog_append(":0: ", str, end); 293 | #endif 294 | } 295 | else { 296 | str = _slog_append("[line:", str, end); 297 | str = _slog_append(_slog_itoa(line_nr, num_buf, sizeof(num_buf)), str, end); 298 | str = _slog_append("] ", str, end); 299 | } 300 | if (message) { 301 | str = _slog_append("\n\t", str, end); 302 | str = _slog_append(message, str, end); 303 | } 304 | str = _slog_append("\n\n", str, end); 305 | if (0 == log_level) { 306 | str = _slog_append("ABORTING because of [panic]\n", str, end); 307 | (void)str; 308 | } 309 | 310 | // print to stderr? 311 | #if defined(_SLOG_LINUX) || defined(_SLOG_WINDOWS) || defined(_SLOG_APPLE) 312 | fputs(line_buf, stderr); 313 | #endif 314 | 315 | // platform specific logging calls 316 | #if defined(_SLOG_WINDOWS) 317 | OutputDebugStringA(line_buf); 318 | #elif defined(_SLOG_ANDROID) 319 | int prio; 320 | switch (log_level) { 321 | case 0: prio = ANDROID_LOG_FATAL; break; 322 | case 1: prio = ANDROID_LOG_ERROR; break; 323 | case 2: prio = ANDROID_LOG_WARN; break; 324 | default: prio = ANDROID_LOG_INFO; break; 325 | } 326 | __android_log_write(prio, "SOKOL", line_buf); 327 | #elif defined(_SLOG_EMSCRIPTEN) 328 | slog_js_log(log_level, line_buf); 329 | #endif 330 | if (0 == log_level) { 331 | abort(); 332 | } 333 | } 334 | #endif // SOKOL_LOG_IMPL 335 | -------------------------------------------------------------------------------- /src/sokol/c/sokol_shape.c: -------------------------------------------------------------------------------- 1 | #if defined(IMPL) 2 | #define SOKOL_SHAPE_IMPL 3 | #endif 4 | #include "sokol_defines.h" 5 | #include "sokol_gfx.h" 6 | #include "sokol_shape.h" 7 | -------------------------------------------------------------------------------- /src/sokol/c/sokol_time.c: -------------------------------------------------------------------------------- 1 | #if defined(IMPL) 2 | #define SOKOL_TIME_IMPL 3 | #endif 4 | #include "sokol_defines.h" 5 | #include "sokol_time.h" 6 | -------------------------------------------------------------------------------- /src/sokol/c/sokol_time.h: -------------------------------------------------------------------------------- 1 | #if defined(SOKOL_IMPL) && !defined(SOKOL_TIME_IMPL) 2 | #define SOKOL_TIME_IMPL 3 | #endif 4 | #ifndef SOKOL_TIME_INCLUDED 5 | /* 6 | sokol_time.h -- simple cross-platform time measurement 7 | 8 | Project URL: https://github.com/floooh/sokol 9 | 10 | Do this: 11 | #define SOKOL_IMPL or 12 | #define SOKOL_TIME_IMPL 13 | before you include this file in *one* C or C++ file to create the 14 | implementation. 15 | 16 | Optionally provide the following defines with your own implementations: 17 | SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) 18 | SOKOL_TIME_API_DECL - public function declaration prefix (default: extern) 19 | SOKOL_API_DECL - same as SOKOL_TIME_API_DECL 20 | SOKOL_API_IMPL - public function implementation prefix (default: -) 21 | 22 | If sokol_time.h is compiled as a DLL, define the following before 23 | including the declaration or implementation: 24 | 25 | SOKOL_DLL 26 | 27 | On Windows, SOKOL_DLL will define SOKOL_TIME_API_DECL as __declspec(dllexport) 28 | or __declspec(dllimport) as needed. 29 | 30 | void stm_setup(); 31 | Call once before any other functions to initialize sokol_time 32 | (this calls for instance QueryPerformanceFrequency on Windows) 33 | 34 | uint64_t stm_now(); 35 | Get current point in time in unspecified 'ticks'. The value that 36 | is returned has no relation to the 'wall-clock' time and is 37 | not in a specific time unit, it is only useful to compute 38 | time differences. 39 | 40 | uint64_t stm_diff(uint64_t new, uint64_t old); 41 | Computes the time difference between new and old. This will always 42 | return a positive, non-zero value. 43 | 44 | uint64_t stm_since(uint64_t start); 45 | Takes the current time, and returns the elapsed time since start 46 | (this is a shortcut for "stm_diff(stm_now(), start)") 47 | 48 | uint64_t stm_laptime(uint64_t* last_time); 49 | This is useful for measuring frame time and other recurring 50 | events. It takes the current time, returns the time difference 51 | to the value in last_time, and stores the current time in 52 | last_time for the next call. If the value in last_time is 0, 53 | the return value will be zero (this usually happens on the 54 | very first call). 55 | 56 | uint64_t stm_round_to_common_refresh_rate(uint64_t duration) 57 | This oddly named function takes a measured frame time and 58 | returns the closest "nearby" common display refresh rate frame duration 59 | in ticks. If the input duration isn't close to any common display 60 | refresh rate, the input duration will be returned unchanged as a fallback. 61 | The main purpose of this function is to remove jitter/inaccuracies from 62 | measured frame times, and instead use the display refresh rate as 63 | frame duration. 64 | NOTE: for more robust frame timing, consider using the 65 | sokol_app.h function sapp_frame_duration() 66 | 67 | Use the following functions to convert a duration in ticks into 68 | useful time units: 69 | 70 | double stm_sec(uint64_t ticks); 71 | double stm_ms(uint64_t ticks); 72 | double stm_us(uint64_t ticks); 73 | double stm_ns(uint64_t ticks); 74 | Converts a tick value into seconds, milliseconds, microseconds 75 | or nanoseconds. Note that not all platforms will have nanosecond 76 | or even microsecond precision. 77 | 78 | Uses the following time measurement functions under the hood: 79 | 80 | Windows: QueryPerformanceFrequency() / QueryPerformanceCounter() 81 | MacOS/iOS: mach_absolute_time() 82 | emscripten: emscripten_get_now() 83 | Linux+others: clock_gettime(CLOCK_MONOTONIC) 84 | 85 | zlib/libpng license 86 | 87 | Copyright (c) 2018 Andre Weissflog 88 | 89 | This software is provided 'as-is', without any express or implied warranty. 90 | In no event will the authors be held liable for any damages arising from the 91 | use of this software. 92 | 93 | Permission is granted to anyone to use this software for any purpose, 94 | including commercial applications, and to alter it and redistribute it 95 | freely, subject to the following restrictions: 96 | 97 | 1. The origin of this software must not be misrepresented; you must not 98 | claim that you wrote the original software. If you use this software in a 99 | product, an acknowledgment in the product documentation would be 100 | appreciated but is not required. 101 | 102 | 2. Altered source versions must be plainly marked as such, and must not 103 | be misrepresented as being the original software. 104 | 105 | 3. This notice may not be removed or altered from any source 106 | distribution. 107 | */ 108 | #define SOKOL_TIME_INCLUDED (1) 109 | #include 110 | 111 | #if defined(SOKOL_API_DECL) && !defined(SOKOL_TIME_API_DECL) 112 | #define SOKOL_TIME_API_DECL SOKOL_API_DECL 113 | #endif 114 | #ifndef SOKOL_TIME_API_DECL 115 | #if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_TIME_IMPL) 116 | #define SOKOL_TIME_API_DECL __declspec(dllexport) 117 | #elif defined(_WIN32) && defined(SOKOL_DLL) 118 | #define SOKOL_TIME_API_DECL __declspec(dllimport) 119 | #else 120 | #define SOKOL_TIME_API_DECL extern 121 | #endif 122 | #endif 123 | 124 | #ifdef __cplusplus 125 | extern "C" { 126 | #endif 127 | 128 | SOKOL_TIME_API_DECL void stm_setup(void); 129 | SOKOL_TIME_API_DECL uint64_t stm_now(void); 130 | SOKOL_TIME_API_DECL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks); 131 | SOKOL_TIME_API_DECL uint64_t stm_since(uint64_t start_ticks); 132 | SOKOL_TIME_API_DECL uint64_t stm_laptime(uint64_t* last_time); 133 | SOKOL_TIME_API_DECL uint64_t stm_round_to_common_refresh_rate(uint64_t frame_ticks); 134 | SOKOL_TIME_API_DECL double stm_sec(uint64_t ticks); 135 | SOKOL_TIME_API_DECL double stm_ms(uint64_t ticks); 136 | SOKOL_TIME_API_DECL double stm_us(uint64_t ticks); 137 | SOKOL_TIME_API_DECL double stm_ns(uint64_t ticks); 138 | 139 | #ifdef __cplusplus 140 | } /* extern "C" */ 141 | #endif 142 | #endif // SOKOL_TIME_INCLUDED 143 | 144 | /*-- IMPLEMENTATION ----------------------------------------------------------*/ 145 | #ifdef SOKOL_TIME_IMPL 146 | #define SOKOL_TIME_IMPL_INCLUDED (1) 147 | #include /* memset */ 148 | 149 | #ifndef SOKOL_API_IMPL 150 | #define SOKOL_API_IMPL 151 | #endif 152 | #ifndef SOKOL_ASSERT 153 | #include 154 | #define SOKOL_ASSERT(c) assert(c) 155 | #endif 156 | #ifndef _SOKOL_PRIVATE 157 | #if defined(__GNUC__) || defined(__clang__) 158 | #define _SOKOL_PRIVATE __attribute__((unused)) static 159 | #else 160 | #define _SOKOL_PRIVATE static 161 | #endif 162 | #endif 163 | 164 | #if defined(_WIN32) 165 | #ifndef WIN32_LEAN_AND_MEAN 166 | #define WIN32_LEAN_AND_MEAN 167 | #endif 168 | #include 169 | typedef struct { 170 | uint32_t initialized; 171 | LARGE_INTEGER freq; 172 | LARGE_INTEGER start; 173 | } _stm_state_t; 174 | #elif defined(__APPLE__) && defined(__MACH__) 175 | #include 176 | typedef struct { 177 | uint32_t initialized; 178 | mach_timebase_info_data_t timebase; 179 | uint64_t start; 180 | } _stm_state_t; 181 | #elif defined(__EMSCRIPTEN__) 182 | #include 183 | typedef struct { 184 | uint32_t initialized; 185 | double start; 186 | } _stm_state_t; 187 | #else /* anything else, this will need more care for non-Linux platforms */ 188 | #ifdef ESP8266 189 | // On the ESP8266, clock_gettime ignores the first argument and CLOCK_MONOTONIC isn't defined 190 | #define CLOCK_MONOTONIC 0 191 | #endif 192 | #include 193 | typedef struct { 194 | uint32_t initialized; 195 | uint64_t start; 196 | } _stm_state_t; 197 | #endif 198 | static _stm_state_t _stm; 199 | 200 | /* prevent 64-bit overflow when computing relative timestamp 201 | see https://gist.github.com/jspohr/3dc4f00033d79ec5bdaf67bc46c813e3 202 | */ 203 | #if defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__)) 204 | _SOKOL_PRIVATE int64_t _stm_int64_muldiv(int64_t value, int64_t numer, int64_t denom) { 205 | int64_t q = value / denom; 206 | int64_t r = value % denom; 207 | return q * numer + r * numer / denom; 208 | } 209 | #endif 210 | 211 | SOKOL_API_IMPL void stm_setup(void) { 212 | memset(&_stm, 0, sizeof(_stm)); 213 | _stm.initialized = 0xABCDABCD; 214 | #if defined(_WIN32) 215 | QueryPerformanceFrequency(&_stm.freq); 216 | QueryPerformanceCounter(&_stm.start); 217 | #elif defined(__APPLE__) && defined(__MACH__) 218 | mach_timebase_info(&_stm.timebase); 219 | _stm.start = mach_absolute_time(); 220 | #elif defined(__EMSCRIPTEN__) 221 | _stm.start = emscripten_get_now(); 222 | #else 223 | struct timespec ts; 224 | clock_gettime(CLOCK_MONOTONIC, &ts); 225 | _stm.start = (uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec; 226 | #endif 227 | } 228 | 229 | SOKOL_API_IMPL uint64_t stm_now(void) { 230 | SOKOL_ASSERT(_stm.initialized == 0xABCDABCD); 231 | uint64_t now; 232 | #if defined(_WIN32) 233 | LARGE_INTEGER qpc_t; 234 | QueryPerformanceCounter(&qpc_t); 235 | now = (uint64_t) _stm_int64_muldiv(qpc_t.QuadPart - _stm.start.QuadPart, 1000000000, _stm.freq.QuadPart); 236 | #elif defined(__APPLE__) && defined(__MACH__) 237 | const uint64_t mach_now = mach_absolute_time() - _stm.start; 238 | now = (uint64_t) _stm_int64_muldiv((int64_t)mach_now, (int64_t)_stm.timebase.numer, (int64_t)_stm.timebase.denom); 239 | #elif defined(__EMSCRIPTEN__) 240 | double js_now = emscripten_get_now() - _stm.start; 241 | now = (uint64_t) (js_now * 1000000.0); 242 | #else 243 | struct timespec ts; 244 | clock_gettime(CLOCK_MONOTONIC, &ts); 245 | now = ((uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec) - _stm.start; 246 | #endif 247 | return now; 248 | } 249 | 250 | SOKOL_API_IMPL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks) { 251 | if (new_ticks > old_ticks) { 252 | return new_ticks - old_ticks; 253 | } 254 | else { 255 | return 1; 256 | } 257 | } 258 | 259 | SOKOL_API_IMPL uint64_t stm_since(uint64_t start_ticks) { 260 | return stm_diff(stm_now(), start_ticks); 261 | } 262 | 263 | SOKOL_API_IMPL uint64_t stm_laptime(uint64_t* last_time) { 264 | SOKOL_ASSERT(last_time); 265 | uint64_t dt = 0; 266 | uint64_t now = stm_now(); 267 | if (0 != *last_time) { 268 | dt = stm_diff(now, *last_time); 269 | } 270 | *last_time = now; 271 | return dt; 272 | } 273 | 274 | // first number is frame duration in ns, second number is tolerance in ns, 275 | // the resulting min/max values must not overlap! 276 | static const uint64_t _stm_refresh_rates[][2] = { 277 | { 16666667, 1000000 }, // 60 Hz: 16.6667 +- 1ms 278 | { 13888889, 250000 }, // 72 Hz: 13.8889 +- 0.25ms 279 | { 13333333, 250000 }, // 75 Hz: 13.3333 +- 0.25ms 280 | { 11764706, 250000 }, // 85 Hz: 11.7647 +- 0.25 281 | { 11111111, 250000 }, // 90 Hz: 11.1111 +- 0.25ms 282 | { 10000000, 500000 }, // 100 Hz: 10.0000 +- 0.5ms 283 | { 8333333, 500000 }, // 120 Hz: 8.3333 +- 0.5ms 284 | { 6944445, 500000 }, // 144 Hz: 6.9445 +- 0.5ms 285 | { 4166667, 1000000 }, // 240 Hz: 4.1666 +- 1ms 286 | { 0, 0 }, // keep the last element always at zero 287 | }; 288 | 289 | SOKOL_API_IMPL uint64_t stm_round_to_common_refresh_rate(uint64_t ticks) { 290 | uint64_t ns; 291 | int i = 0; 292 | while (0 != (ns = _stm_refresh_rates[i][0])) { 293 | uint64_t tol = _stm_refresh_rates[i][1]; 294 | if ((ticks > (ns - tol)) && (ticks < (ns + tol))) { 295 | return ns; 296 | } 297 | i++; 298 | } 299 | // fallthrough: didn't fit into any buckets 300 | return ticks; 301 | } 302 | 303 | SOKOL_API_IMPL double stm_sec(uint64_t ticks) { 304 | return (double)ticks / 1000000000.0; 305 | } 306 | 307 | SOKOL_API_IMPL double stm_ms(uint64_t ticks) { 308 | return (double)ticks / 1000000.0; 309 | } 310 | 311 | SOKOL_API_IMPL double stm_us(uint64_t ticks) { 312 | return (double)ticks / 1000.0; 313 | } 314 | 315 | SOKOL_API_IMPL double stm_ns(uint64_t ticks) { 316 | return (double)ticks; 317 | } 318 | #endif /* SOKOL_TIME_IMPL */ 319 | 320 | -------------------------------------------------------------------------------- /src/time.rs: -------------------------------------------------------------------------------- 1 | // machine generated, do not edit 2 | 3 | #![allow(dead_code)] 4 | #![allow(unused_imports)] 5 | 6 | /// Helper function to convert a C string to a Rust string slice 7 | #[inline] 8 | fn c_char_ptr_to_rust_str(c_char_ptr: *const core::ffi::c_char) -> &'static str { 9 | let c_str = unsafe { core::ffi::CStr::from_ptr(c_char_ptr) }; 10 | c_str.to_str().expect("c_char_ptr contained invalid Utf8 Data") 11 | } 12 | 13 | pub mod ffi { 14 | #![allow(unused_imports)] 15 | use super::*; 16 | extern "C" { 17 | pub fn stm_setup(); 18 | pub fn stm_now() -> u64; 19 | pub fn stm_diff(new_ticks: u64, old_ticks: u64) -> u64; 20 | pub fn stm_since(start_ticks: u64) -> u64; 21 | pub fn stm_laptime(last_time: *mut u64) -> u64; 22 | pub fn stm_round_to_common_refresh_rate(frame_ticks: u64) -> u64; 23 | pub fn stm_sec(ticks: u64) -> f64; 24 | pub fn stm_ms(ticks: u64) -> f64; 25 | pub fn stm_us(ticks: u64) -> f64; 26 | pub fn stm_ns(ticks: u64) -> f64; 27 | } 28 | } 29 | #[inline] 30 | pub fn setup() { 31 | unsafe { ffi::stm_setup() } 32 | } 33 | #[inline] 34 | pub fn now() -> u64 { 35 | unsafe { ffi::stm_now() } 36 | } 37 | #[inline] 38 | pub fn diff(new_ticks: u64, old_ticks: u64) -> u64 { 39 | unsafe { ffi::stm_diff(new_ticks, old_ticks) } 40 | } 41 | #[inline] 42 | pub fn since(start_ticks: u64) -> u64 { 43 | unsafe { ffi::stm_since(start_ticks) } 44 | } 45 | #[inline] 46 | pub fn laptime(last_time: &mut u64) -> u64 { 47 | unsafe { ffi::stm_laptime(last_time) } 48 | } 49 | #[inline] 50 | pub fn round_to_common_refresh_rate(frame_ticks: u64) -> u64 { 51 | unsafe { ffi::stm_round_to_common_refresh_rate(frame_ticks) } 52 | } 53 | #[inline] 54 | pub fn sec(ticks: u64) -> f64 { 55 | unsafe { ffi::stm_sec(ticks) } 56 | } 57 | #[inline] 58 | pub fn ms(ticks: u64) -> f64 { 59 | unsafe { ffi::stm_ms(ticks) } 60 | } 61 | #[inline] 62 | pub fn us(ticks: u64) -> f64 { 63 | unsafe { ffi::stm_us(ticks) } 64 | } 65 | #[inline] 66 | pub fn ns(ticks: u64) -> f64 { 67 | unsafe { ffi::stm_ns(ticks) } 68 | } 69 | -------------------------------------------------------------------------------- /test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Texcube 6 | 54 | 55 | 56 | 57 | 83 | 84 | 85 | 86 | --------------------------------------------------------------------------------