├── .gitattributes ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug.md │ ├── dev_release.md │ ├── dev_zig_nomination.md │ └── feedback.md ├── pull_request_template.md └── workflows │ └── ci.yml ├── .gitignore ├── .zigversion ├── LICENSE ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── build.zig ├── build.zig.zon ├── examples ├── core-custom-entrypoint │ ├── App.zig │ ├── main.zig │ └── shader.wgsl ├── core-transparent-window │ ├── App.zig │ └── shader.wgsl ├── core-triangle │ ├── App.zig │ └── shader.wgsl ├── custom-renderer │ ├── App.zig │ ├── Renderer.zig │ └── shader.wgsl ├── glyphs │ └── App.zig ├── hardware-check │ └── App.zig ├── piano │ └── App.zig ├── play-opus │ └── App.zig ├── sprite │ └── App.zig └── text │ └── App.zig └── src ├── Audio.zig ├── Core.zig ├── StringTable.zig ├── core ├── Darwin.zig ├── Linux.zig ├── Windows.zig ├── linux │ ├── Wayland.zig │ ├── X11.zig │ └── wayland.c ├── windowmsg.zig └── windows │ └── win32.manifest ├── entrypoint └── main.zig ├── gamemode.zig ├── gfx ├── Sprite.zig ├── Text.zig ├── atlas │ ├── Atlas.zig │ └── LICENSE ├── font │ ├── main.zig │ └── native │ │ ├── Font.zig │ │ └── TextRun.zig ├── main.zig ├── sprite.wgsl ├── text.wgsl └── util.zig ├── graph.zig ├── main.zig ├── math ├── collision.zig ├── main.zig ├── mat.zig ├── quat.zig ├── ray.zig └── vec.zig ├── module.zig ├── mpsc.zig ├── sysaudio ├── alsa.zig ├── backends.zig ├── conv.zig ├── coreaudio.zig ├── dummy.zig ├── jack.zig ├── main.zig ├── pipewire.zig ├── pipewire │ └── sysaudio.c ├── pulseaudio.zig ├── tests │ ├── record.zig │ └── sine.zig ├── util.zig └── wasapi.zig ├── sysgpu ├── conventions.md ├── d3d12.zig ├── d3d12 │ ├── c.zig │ ├── conv.zig │ └── notes.md ├── gpu_allocator.zig ├── limits.zig ├── main.zig ├── metal.zig ├── metal │ └── conv.zig ├── opengl.zig ├── opengl │ ├── c.zig │ ├── conv.zig │ └── proc.zig ├── shader.zig ├── shader │ ├── Air.zig │ ├── Ast.zig │ ├── AstGen.zig │ ├── CodeGen.zig │ ├── ErrorList.zig │ ├── Parser.zig │ ├── Token.zig │ ├── Tokenizer.zig │ ├── codegen │ │ ├── glsl.zig │ │ ├── hlsl.zig │ │ ├── msl.zig │ │ ├── spirv.zig │ │ └── spirv │ │ │ ├── Section.zig │ │ │ └── spec.zig │ ├── print_air.zig │ ├── test.zig │ ├── test │ │ ├── boids-sprite-update.wgsl │ │ ├── boids-sprite.wgsl │ │ ├── builtins.wgsl │ │ ├── cube-map.wgsl │ │ ├── fractal-cube.wgsl │ │ ├── fragmentDeferredRendering.wgsl │ │ ├── fragmentGBuffersDebugView.wgsl │ │ ├── fragmentWriteGBuffers.wgsl │ │ ├── fullscreen-textured-quad.wgsl │ │ ├── gen-texture-light-cube.wgsl │ │ ├── gen-texture-light.wgsl │ │ ├── if-else.wgsl │ │ ├── image-blur.wgsl │ │ ├── instanced-cube.wgsl │ │ ├── lightUpdate.wgsl │ │ ├── map-async.wgsl │ │ ├── pbr-basic.wgsl │ │ ├── pixel-post-process-normal-frag.wgsl │ │ ├── pixel-post-process-pixel-frag.wgsl │ │ ├── pixel-post-process-pixel-vert.wgsl │ │ ├── pixel-post-process.wgsl │ │ ├── procedural-primitives.wgsl │ │ ├── rotating-cube.wgsl │ │ ├── sprite2d.wgsl │ │ ├── textured-cube.wgsl │ │ ├── triangle.wgsl │ │ ├── two-cubes.wgsl │ │ ├── vertexTextureQuad.wgsl │ │ └── vertexWriteGBuffers.wgsl │ └── wgsl.zig ├── sysgpu │ ├── adapter.zig │ ├── bind_group.zig │ ├── bind_group_layout.zig │ ├── buffer.zig │ ├── command_buffer.zig │ ├── command_encoder.zig │ ├── compute_pass_encoder.zig │ ├── compute_pipeline.zig │ ├── device.zig │ ├── external_texture.zig │ ├── instance.zig │ ├── interface.zig │ ├── main.zig │ ├── pipeline_layout.zig │ ├── query_set.zig │ ├── queue.zig │ ├── render_bundle.zig │ ├── render_bundle_encoder.zig │ ├── render_pass_encoder.zig │ ├── render_pipeline.zig │ ├── sampler.zig │ ├── shader_module.zig │ ├── shared_fence.zig │ ├── shared_texture_memory.zig │ ├── surface.zig │ ├── swap_chain.zig │ ├── texture.zig │ └── texture_view.zig ├── tools │ ├── gen_spirv_spec.zig │ ├── spirv │ │ └── grammar.zig │ └── validate_spirv.sh ├── utils.zig ├── vulkan.zig └── vulkan │ ├── conv.zig │ └── proc.zig ├── testing.zig ├── time ├── Frequency.zig ├── Timer.zig └── main.zig └── win32.zig /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | upstream/** linguist-vendored 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: emidoots 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug 3 | about: Report a bug 4 | title: ': ' 5 | labels: bug, needs-triage 6 | assignees: '' 7 | 8 | --- 9 | 10 | Before filing an issue, please make sure you're using a supported Zig version: https://machengine.org/about/zig-version 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/dev_release.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: (dev) Release checklist 3 | about: The checklist we follow to perform a Mach release 4 | title: 'all: Mach 0.3 release checklist' 5 | labels: all, zig-update 6 | assignees: 'emidoots' 7 | 8 | --- 9 | 10 | This is a tracking issue for preparing the next Mach release. 11 | 12 | ## Checklist 13 | 14 | * [ ] In `mach` repository, the release has been tagged (`git tag v0.3.0 && git push origin v0.3.0`) 15 | * [ ] In `machengine.org` repository `static/zig` folder, `wrench script nominate-zig-index-update tag 2024.1.0-mach 0.3.0-mach` has been ran to tag the Zig version that the Mach release will use, and the [`index.json`](https://machengine.org/zig/index.json) shows the new version. 16 | * [ ] In `machengine.org` main branch, `deploy.yml` has a new `hugo --minify` entry for the version to be released 17 | * [ ] In `machengine.org` main branch, `zig-version.md` has been updated with a new `Supported Zig versions` entry 18 | * [ ] 19 | * [ ] In `machengine.org` repository, a branch for the major release has been created `git checkout -B v0.3 && git push --set-upstream origin v0.3` 20 | * [ ] In `machengine.org` repository's new major release branch `v0.3`, `config.toml`'s `branch = 'main'` has been changed to `branch = 'v0.3'` and https://machengine.org/v0.3 now loads fine 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feedback.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Suggestion / feedback 3 | about: Suggest a feature, provide general feedback, etc. 4 | title: '' 5 | labels: feedback, needs-triage 6 | assignees: '' 7 | 8 | --- 9 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | - [ ] By selecting this checkbox, I agree to license my contributions to this project under the license(s) described in the LICENSE file, and I have the right to do so or have received permission to do so by an employer or client I am producing work for whom has this right. -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | paths-ignore: 5 | - "doc/**" 6 | - "README.md" 7 | - "**.md" 8 | - "LICENSE**" 9 | pull_request: 10 | paths-ignore: 11 | - "doc/**" 12 | - "README.md" 13 | - "**.md" 14 | - "LICENSE**" 15 | jobs: 16 | x86_64-linux: 17 | runs-on: ubuntu-latest 18 | # We want to run on external PRs, but not on our own internal PRs as they'll be run by the push 19 | # to the branch. 20 | if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository 21 | env: 22 | DISPLAY: ':99.0' 23 | steps: 24 | - name: Checkout 25 | uses: actions/checkout@v2 26 | - name: Setup Zig 27 | run: | 28 | sudo apt install xz-utils 29 | sudo sh -c 'wget -c https://pkg.machengine.org/zig/zig-linux-x86_64-0.14.0-dev.2577+271452d22.tar.xz -O - | tar -xJ --strip-components=1 -C /usr/local/bin' 30 | - name: launch xvfb 31 | run: Xvfb :99 -screen 0 1680x720x24 > /dev/null 2>&1 & 32 | # - name: x86_64-linux -> x86_64-linux-musl 33 | # run: zig build -Dtarget=x86_64-linux-musl 34 | # TODO(build): currently cannot link on macOS due to a bug in the Zig linker. https://github.com/hexops/mach/issues/1276 35 | # - name: x86_64-linux -> x86_64-macos 36 | # run: zig build -Dtarget=x86_64-macos 37 | # - name: x86_64-linux -> aarch64-macos 38 | # run: zig build -Dtarget=aarch64-macos 39 | - name: x86_64-linux -> x86_64-windows 40 | run: zig build -Dtarget=x86_64-windows-gnu 41 | - name: build 42 | run: zig build 43 | - name: test 44 | run: zig build test 45 | - name: test (with core deps only) 46 | run: zig build -Dcore test 47 | - name: test (with sysaudio deps only) 48 | run: zig build -Dsysaudio test 49 | - name: test (with sysgpu deps only) 50 | run: zig build -Dsysgpu test 51 | - name: test (specific deps only) 52 | run: zig build -Dcore -Dsysaudio -Dsysgpu test 53 | ## TODO: use hexops/spirv-tools for spirv validation instead OR if we are using ZLS finally, just remove this commented block 54 | ## The following block throws this error, so it is disabled for now: 55 | ## https://github.com/hexops/mach/actions/runs/11223060751/job/31196848244?pr=1279#step:14:79 56 | # - name: 'sysgpu: validate spirv code generation' 57 | # run: | 58 | # wget -qO- https://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo tee /etc/apt/trusted.gpg.d/lunarg.asc 59 | # sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-jammy.list http://packages.lunarg.com/vulkan/lunarg-vulkan-jammy.list 60 | # sudo apt update 61 | # sudo apt install libyaml-cpp-dev vulkan-sdk spirv-tools 62 | # - name: 'sysgpu: validate spirv output' 63 | # run: bash ./src/sysgpu/tools/validate_spirv.sh 64 | x86_64-windows: 65 | runs-on: windows-latest 66 | # We want to run on external PRs, but not on our own internal PRs as they'll be run by the push 67 | # to the branch. 68 | if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository 69 | steps: 70 | - name: Checkout 71 | uses: actions/checkout@v2 72 | - name: Setup Zig 73 | run: | 74 | $ProgressPreference = 'SilentlyContinue' 75 | Invoke-WebRequest -Uri 'https://pkg.machengine.org/zig/zig-windows-x86_64-0.14.0-dev.2577+271452d22.zip' -OutFile 'C:\zig.zip' 76 | cd C:\ 77 | 7z x zig.zip 78 | Add-Content $env:GITHUB_PATH 'C:\zig-windows-x86_64-0.14.0-dev.2577+271452d22\' 79 | - name: build 80 | run: zig build 81 | - name: test 82 | run: zig build test 83 | x86_64-macos: 84 | runs-on: macos-13 85 | # We want to run on external PRs, but not on our own internal PRs as they'll be run by the push 86 | # to the branch. 87 | if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository 88 | steps: 89 | - name: Checkout 90 | uses: actions/checkout@v2 91 | - name: Setup Zig 92 | run: | 93 | brew uninstall --ignore-dependencies libx11 # https://github.com/ziglang/zig/issues/11066 94 | brew install xz 95 | sudo sh -c 'wget -c https://pkg.machengine.org/zig/zig-macos-x86_64-0.14.0-dev.2577+271452d22.tar.xz -O - | tar -xJ --strip-components=1 -C /usr/local/bin' 96 | - name: build 97 | run: zig build 98 | - name: test 99 | run: zig build test 100 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # This file is for zig-specific build artifacts. 2 | # If you have OS-specific or editor-specific files to ignore, 3 | # such as *.swp or .DS_Store, put those in your global 4 | # ~/.gitignore and put this in your ~/.gitconfig: 5 | # 6 | # [core] 7 | # excludesfile = ~/.gitignore 8 | # 9 | # Cheers! 10 | # -andrewrk 11 | 12 | .zig-cache/ 13 | zig-out/ 14 | /release/ 15 | /debug/ 16 | /build/ 17 | /build-*/ 18 | /docgen_tmp/ 19 | 20 | src/core/examples/libs/ 21 | -------------------------------------------------------------------------------- /.zigversion: -------------------------------------------------------------------------------- 1 | 0.14.0-dev.2577+271452d22 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021, Hexops Contributors (given via the Git commit history). 2 | 3 | All documentation, image, sound, font, and 2D/3D model files are CC-BY-4.0 licensed unless 4 | otherwise noted. You may get a copy of this license at https://creativecommons.org/licenses/by/4.0 5 | 6 | Files in a directory with a separate LICENSE file may contain files under different license terms, 7 | described within that LICENSE file. 8 | 9 | All other files are licensed under the Apache License, Version 2.0 (see LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) 10 | or the MIT license (see LICENSE-MIT or http://opensource.org/licenses/MIT), at your option. 11 | 12 | All files in the project without exclusions may not be copied, modified, or distributed except 13 | according to the terms above. -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 Hexops Contributors (given via the Git commit history). 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | mach-opus 5 | 6 | 7 | 8 | Zig game engine & graphics toolkit for building high-performance, truly cross-platform, robust & modular games, visualizations, and desktop/mobile GUI apps. 9 | 10 | gen-texture-light 11 | boids 12 | textured-cube 13 | 14 | ## Learn more 15 | 16 | [machengine.org](https://machengine.org) 17 | 18 | ## Join the community 19 | 20 | Join the [Mach community on Discord](https://discord.gg/XNG3NZgCqp) to discuss this project, ask questions, get help, etc. 21 | 22 | **We're here to make games and have fun, so please help keep the community focused on that.** No politics/heated topics are allowed. Unfortunately, the political landscape today makes it such that we must also state fascists can go f*k themselves. :) Anyone else is very welcome here. 23 | 24 | -------------------------------------------------------------------------------- /build.zig.zon: -------------------------------------------------------------------------------- 1 | .{ 2 | .name = "mach", 3 | .version = "0.4.0", 4 | .paths = .{ 5 | "src", 6 | "build.zig", 7 | "build.zig.zon", 8 | "LICENSE", 9 | "LICENSE-APACHE", 10 | "LICENSE-MIT", 11 | "README.md", 12 | }, 13 | .dependencies = .{ 14 | .mach_freetype = .{ 15 | .url = "https://pkg.machengine.org/mach-freetype/d63efa5534c17f3a12ed3d327e0ad42a64adc20a.tar.gz", 16 | .hash = "1220adfccce3dbc4e4fa8650fdaec110a676f6b8a1462ed6ef422815207f8288e9d2", 17 | .lazy = true, 18 | }, 19 | .font_assets = .{ 20 | .url = "https://github.com/hexops/font-assets/archive/b2336a29b1ae633d47452a2041b258e9538ef5f0.tar.gz", 21 | .hash = "1220521ff5b01885da9b3cba75b607b315f60ae351594ee9e4a270b164374889fea0", 22 | .lazy = true, 23 | }, 24 | .mach_objc = .{ 25 | .url = "https://pkg.machengine.org/mach-objc/79b6f80c32b14948554958afe72dace261b14afc.tar.gz", 26 | .hash = "12203675829014e69be2ea7c126ecf25d403009d336b7ca5f6e7c4ccede826c8e597", 27 | .lazy = true, 28 | }, 29 | .xcode_frameworks = .{ 30 | .url = "https://pkg.machengine.org/xcode-frameworks/9a45f3ac977fd25dff77e58c6de1870b6808c4a7.tar.gz", 31 | .hash = "122098b9174895f9708bc824b0f9e550c401892c40a900006459acf2cbf78acd99bb", 32 | .lazy = true, 33 | }, 34 | .directx_headers = .{ 35 | .url = "https://pkg.machengine.org/directx-headers/eae9b3b8a84a32ae9e67025fd64e8d8b7755e628.tar.gz", 36 | .hash = "1220334a0e3480fef60443cef8b519d08b4dbf16ba37d98a7b77647fa4b7761b6048", 37 | .lazy = true, 38 | }, 39 | .opengl_headers = .{ 40 | .url = "https://pkg.machengine.org/opengl-headers/d0b37fdc3b039ba5b430110545f398f0278c5396.tar.gz", 41 | .hash = "1220c30350b7b45f4623aeaf1c5b2c9ceda5d626de2ff5a3f2f54e4a68a80358426b", 42 | .lazy = true, 43 | }, 44 | .vulkan_zig_generated = .{ 45 | .url = "https://pkg.machengine.org/vulkan-zig-generated/4134f910302a71731d8d32c91cfc1bc914e6d26b.tar.gz", 46 | .hash = "12208333c8b3551908b66b8a421e7127bdf0c1806063836576283860eff99c99c521", 47 | .lazy = true, 48 | }, 49 | .linux_audio_headers = .{ 50 | .url = "https://pkg.machengine.org/linux-audio-headers/81f1f13828a8b62467200ba2a0b0d911c4d089a7.tar.gz", 51 | .hash = "12209e63e7efddd85cca44cbb13d20ef30c72ff4c409372dd75a128a354b27e9dc4c", 52 | .lazy = true, 53 | }, 54 | .wayland_headers = .{ 55 | .url = "https://pkg.machengine.org/wayland-headers/7c53e7483c3cfb5c6780ae542c9f5cfa712a826a.tar.gz", 56 | .hash = "1220563c3d5603a02e61293c2c0223e01a3f298fb606bf0d108293b925434970a207", 57 | .lazy = true, 58 | }, 59 | .x11_headers = .{ 60 | .url = "https://pkg.machengine.org/x11-headers/bc049ff07d31014d4d409cc328c3feb57c9b9a83.tar.gz", 61 | .hash = "1220083b369e019a5efbd188a58a25759c09a126e1847200ff9e88f5ccbd2dc846e3", 62 | .lazy = true, 63 | }, 64 | 65 | // Dependencies used by examples only 66 | .zigimg = .{ 67 | .url = "https://github.com/zigimg/zigimg/archive/48bfe6d413a5b3723a7bcf36f1fabbba30efef68.tar.gz", 68 | .hash = "12201b874ac217853e59f55477b96cb7a66d43a5930ed9b4f0cfc7113efc8f5a6449", 69 | .lazy = true, 70 | }, 71 | .mach_opus = .{ 72 | .url = "https://pkg.machengine.org/mach-opus/32712fd091636037959720ee00036a060816a4f0.tar.gz", 73 | .hash = "12202e4cc38985a37bcf4804f29ec2fcba162410eee777d96ecdbb2c4260b14b7e68", 74 | .lazy = true, 75 | }, 76 | .mach_example_assets = .{ 77 | .url = "https://pkg.machengine.org/mach-example-assets/dbc61a1470579a7834530567011227b1e680f2af.tar.gz", 78 | .hash = "12204101e99906bee0fde5f82682bc822744a6f879dbf45374bebd92458492313df9", 79 | .lazy = true, 80 | }, 81 | }, 82 | } 83 | -------------------------------------------------------------------------------- /examples/core-custom-entrypoint/App.zig: -------------------------------------------------------------------------------- 1 | const mach = @import("mach"); 2 | const gpu = mach.gpu; 3 | 4 | const App = @This(); 5 | 6 | pub const mach_module = .app; 7 | 8 | pub const mach_systems = .{ .main, .init, .deinit, .tick }; 9 | 10 | window: mach.ObjectID, 11 | title_timer: mach.time.Timer, 12 | pipeline: *gpu.RenderPipeline, 13 | 14 | pub const main = mach.schedule(.{ 15 | .{ mach.Core, .init }, 16 | .{ App, .init }, 17 | .{ mach.Core, .main }, 18 | }); 19 | 20 | pub fn deinit(app: *App) void { 21 | app.pipeline.release(); 22 | } 23 | 24 | pub fn init( 25 | app: *App, 26 | core: *mach.Core, 27 | app_mod: mach.Mod(App), 28 | ) !void { 29 | core.on_tick = app_mod.id.tick; 30 | core.on_exit = app_mod.id.deinit; 31 | 32 | const window = try core.windows.new(.{ .title = "core-custom-entrypoint" }); 33 | 34 | // Store our render pipeline in our module's state, so we can access it later on. 35 | app.* = .{ 36 | .window = window, 37 | .title_timer = try mach.time.Timer.start(), 38 | .pipeline = undefined, 39 | }; 40 | 41 | // TODO(object): window-title 42 | // try updateWindowTitle(core); 43 | } 44 | 45 | fn setupPipeline(core: *mach.Core, app: *App, window_id: mach.ObjectID) !void { 46 | _ = window_id; // autofix 47 | const window = core.windows.getValue(app.window); 48 | 49 | // Create our shader module 50 | const shader_module = window.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.wgsl")); 51 | defer shader_module.release(); 52 | 53 | // Blend state describes how rendered colors get blended 54 | const blend = gpu.BlendState{}; 55 | 56 | // Color target describes e.g. the pixel format of the window we are rendering to. 57 | const color_target = gpu.ColorTargetState{ 58 | .format = window.framebuffer_format, 59 | .blend = &blend, 60 | }; 61 | 62 | // Fragment state describes which shader and entrypoint to use for rendering fragments. 63 | const fragment = gpu.FragmentState.init(.{ 64 | .module = shader_module, 65 | .entry_point = "frag_main", 66 | .targets = &.{color_target}, 67 | }); 68 | 69 | // Create our render pipeline that will ultimately get pixels onto the screen. 70 | const label = @tagName(mach_module) ++ ".init"; 71 | const pipeline_descriptor = gpu.RenderPipeline.Descriptor{ 72 | .label = label, 73 | .fragment = &fragment, 74 | .vertex = gpu.VertexState{ 75 | .module = shader_module, 76 | .entry_point = "vertex_main", 77 | }, 78 | }; 79 | 80 | app.pipeline = window.device.createRenderPipeline(&pipeline_descriptor); 81 | } 82 | 83 | pub fn tick(core: *mach.Core, app: *App) !void { 84 | while (core.nextEvent()) |event| { 85 | switch (event) { 86 | .window_open => |ev| { 87 | try setupPipeline(core, app, ev.window_id); 88 | }, 89 | .close => core.exit(), 90 | else => {}, 91 | } 92 | } 93 | 94 | const window = core.windows.getValue(app.window); 95 | 96 | // Grab the back buffer of the swapchain 97 | // TODO(Core) 98 | const back_buffer_view = window.swap_chain.getCurrentTextureView().?; 99 | defer back_buffer_view.release(); 100 | 101 | // Create a command encoder 102 | const label = @tagName(mach_module) ++ ".tick"; 103 | const encoder = window.device.createCommandEncoder(&.{ .label = label }); 104 | defer encoder.release(); 105 | 106 | // Begin render pass 107 | const sky_blue_background = gpu.Color{ .r = 0.776, .g = 0.988, .b = 1, .a = 1 }; 108 | const color_attachments = [_]gpu.RenderPassColorAttachment{.{ 109 | .view = back_buffer_view, 110 | .clear_value = sky_blue_background, 111 | .load_op = .clear, 112 | .store_op = .store, 113 | }}; 114 | const render_pass = encoder.beginRenderPass(&gpu.RenderPassDescriptor.init(.{ 115 | .label = label, 116 | .color_attachments = &color_attachments, 117 | })); 118 | defer render_pass.release(); 119 | 120 | // Draw 121 | render_pass.setPipeline(app.pipeline); 122 | render_pass.draw(3, 1, 0, 0); 123 | 124 | // Finish render pass 125 | render_pass.end(); 126 | 127 | // Submit our commands to the queue 128 | var command = encoder.finish(&.{ .label = label }); 129 | defer command.release(); 130 | window.queue.submit(&[_]*gpu.CommandBuffer{command}); 131 | 132 | // update the window title every second 133 | if (app.title_timer.read() >= 1.0) { 134 | app.title_timer.reset(); 135 | // TODO(object): window-title 136 | // try updateWindowTitle(core); 137 | } 138 | } 139 | 140 | // TODO(object): window-title 141 | // fn updateWindowTitle(core: *mach.Core) !void { 142 | // try core.printTitle( 143 | // core.main_window, 144 | // "core-custom-entrypoint [ {d}fps ] [ Input {d}hz ]", 145 | // .{ 146 | // // TODO(Core) 147 | // core.frameRate(), 148 | // core.inputRate(), 149 | // }, 150 | // ); 151 | // core.schedule(.update); 152 | // } 153 | -------------------------------------------------------------------------------- /examples/core-custom-entrypoint/main.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const mach = @import("mach"); 3 | 4 | // The set of Mach modules our application may use. 5 | const Modules = mach.Modules(.{ 6 | mach.Core, 7 | @import("App.zig"), 8 | }); 9 | 10 | pub fn main() !void { 11 | const allocator = std.heap.c_allocator; 12 | 13 | // The set of Mach modules our application may use. 14 | var mods: Modules = undefined; 15 | try mods.init(allocator); 16 | 17 | const app = mods.get(.app); 18 | app.run(.main); 19 | } 20 | -------------------------------------------------------------------------------- /examples/core-custom-entrypoint/shader.wgsl: -------------------------------------------------------------------------------- 1 | @vertex fn vertex_main( 2 | @builtin(vertex_index) VertexIndex : u32 3 | ) -> @builtin(position) vec4 { 4 | var pos = array, 3>( 5 | vec2( 0.0, 0.5), 6 | vec2(-0.5, -0.5), 7 | vec2( 0.5, -0.5) 8 | ); 9 | return vec4(pos[VertexIndex], 0.0, 1.0); 10 | } 11 | 12 | @fragment fn frag_main() -> @location(0) vec4 { 13 | return vec4(1.0, 0.0, 0.0, 1.0); 14 | } 15 | -------------------------------------------------------------------------------- /examples/core-transparent-window/shader.wgsl: -------------------------------------------------------------------------------- 1 | @vertex fn vertex_main( 2 | @builtin(vertex_index) VertexIndex : u32 3 | ) -> @builtin(position) vec4 { 4 | var pos = array, 3>( 5 | vec2( 0.0, 0.5), 6 | vec2(-0.5, -0.5), 7 | vec2( 0.5, -0.5) 8 | ); 9 | return vec4(pos[VertexIndex], 0.0, 1.0); 10 | } 11 | 12 | @fragment fn frag_main() -> @location(0) vec4 { 13 | return vec4(1.0, 0.0, 0.0, 1.0); 14 | } 15 | -------------------------------------------------------------------------------- /examples/core-triangle/App.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const mach = @import("mach"); 3 | const gpu = mach.gpu; 4 | 5 | const App = @This(); 6 | 7 | // The set of Mach modules our application may use. 8 | pub const Modules = mach.Modules(.{ 9 | mach.Core, 10 | App, 11 | }); 12 | 13 | pub const mach_module = .app; 14 | 15 | pub const mach_systems = .{ .main, .init, .tick, .deinit }; 16 | 17 | pub const main = mach.schedule(.{ 18 | .{ mach.Core, .init }, 19 | .{ App, .init }, 20 | .{ mach.Core, .main }, 21 | }); 22 | 23 | window: mach.ObjectID, 24 | title_timer: mach.time.Timer, 25 | pipeline: *gpu.RenderPipeline, 26 | 27 | pub fn init( 28 | core: *mach.Core, 29 | app: *App, 30 | app_mod: mach.Mod(App), 31 | ) !void { 32 | core.on_tick = app_mod.id.tick; 33 | core.on_exit = app_mod.id.deinit; 34 | 35 | const window = try core.windows.new(.{ 36 | .title = "core-triangle", 37 | }); 38 | 39 | // Store our render pipeline in our module's state, so we can access it later on. 40 | app.* = .{ 41 | .window = window, 42 | .title_timer = try mach.time.Timer.start(), 43 | .pipeline = undefined, 44 | }; 45 | } 46 | 47 | fn setupPipeline(core: *mach.Core, app: *App, window_id: mach.ObjectID) !void { 48 | var window = core.windows.getValue(window_id); 49 | defer core.windows.setValueRaw(window_id, window); 50 | 51 | // Create our shader module 52 | const shader_module = window.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.wgsl")); 53 | defer shader_module.release(); 54 | 55 | // Blend state describes how rendered colors get blended 56 | const blend = gpu.BlendState{}; 57 | 58 | // Color target describes e.g. the pixel format of the window we are rendering to. 59 | const color_target = gpu.ColorTargetState{ 60 | .format = window.framebuffer_format, 61 | .blend = &blend, 62 | }; 63 | 64 | // Fragment state describes which shader and entrypoint to use for rendering fragments. 65 | const fragment = gpu.FragmentState.init(.{ 66 | .module = shader_module, 67 | .entry_point = "frag_main", 68 | .targets = &.{color_target}, 69 | }); 70 | 71 | // Create our render pipeline that will ultimately get pixels onto the screen. 72 | const label = @tagName(mach_module) ++ ".init"; 73 | const pipeline_descriptor = gpu.RenderPipeline.Descriptor{ 74 | .label = label, 75 | .fragment = &fragment, 76 | .vertex = gpu.VertexState{ 77 | .module = shader_module, 78 | .entry_point = "vertex_main", 79 | }, 80 | }; 81 | app.pipeline = window.device.createRenderPipeline(&pipeline_descriptor); 82 | } 83 | 84 | // TODO(object): window-title 85 | // try updateWindowTitle(core); 86 | 87 | pub fn tick(app: *App, core: *mach.Core) void { 88 | while (core.nextEvent()) |event| { 89 | switch (event) { 90 | .window_open => |ev| { 91 | try setupPipeline(core, app, ev.window_id); 92 | }, 93 | .close => core.exit(), 94 | else => {}, 95 | } 96 | } 97 | 98 | const window = core.windows.getValue(app.window); 99 | 100 | // Grab the back buffer of the swapchain 101 | // TODO(Core) 102 | const back_buffer_view = window.swap_chain.getCurrentTextureView().?; 103 | defer back_buffer_view.release(); 104 | 105 | // Create a command encoder 106 | const label = @tagName(mach_module) ++ ".tick"; 107 | 108 | const encoder = window.device.createCommandEncoder(&.{ .label = label }); 109 | defer encoder.release(); 110 | 111 | // Begin render pass 112 | const sky_blue_background = gpu.Color{ .r = 0.776, .g = 0.988, .b = 1, .a = 1 }; 113 | const color_attachments = [_]gpu.RenderPassColorAttachment{.{ 114 | .view = back_buffer_view, 115 | .clear_value = sky_blue_background, 116 | .load_op = .clear, 117 | .store_op = .store, 118 | }}; 119 | const render_pass = encoder.beginRenderPass(&gpu.RenderPassDescriptor.init(.{ 120 | .label = label, 121 | .color_attachments = &color_attachments, 122 | })); 123 | defer render_pass.release(); 124 | 125 | // Draw 126 | render_pass.setPipeline(app.pipeline); 127 | render_pass.draw(3, 1, 0, 0); 128 | 129 | // Finish render pass 130 | render_pass.end(); 131 | 132 | // Submit our commands to the queue 133 | var command = encoder.finish(&.{ .label = label }); 134 | defer command.release(); 135 | window.queue.submit(&[_]*gpu.CommandBuffer{command}); 136 | 137 | // update the window title every second 138 | // if (app.title_timer.read() >= 1.0) { 139 | // app.title_timer.reset(); 140 | // // TODO(object): window-title 141 | // // try updateWindowTitle(core); 142 | // } 143 | } 144 | 145 | pub fn deinit(app: *App) void { 146 | app.pipeline.release(); 147 | } 148 | 149 | // TODO(object): window-title 150 | // fn updateWindowTitle(core: *mach.Core) !void { 151 | // try core.printTitle( 152 | // core.main_window, 153 | // "core-custom-entrypoint [ {d}fps ] [ Input {d}hz ]", 154 | // .{ 155 | // // TODO(Core) 156 | // core.frameRate(), 157 | // core.inputRate(), 158 | // }, 159 | // ); 160 | // core.schedule(.update); 161 | // } 162 | -------------------------------------------------------------------------------- /examples/core-triangle/shader.wgsl: -------------------------------------------------------------------------------- 1 | @vertex fn vertex_main( 2 | @builtin(vertex_index) VertexIndex : u32 3 | ) -> @builtin(position) vec4 { 4 | var pos = array, 3>( 5 | vec2( 0.0, 0.5), 6 | vec2(-0.5, -0.5), 7 | vec2( 0.5, -0.5) 8 | ); 9 | return vec4(pos[VertexIndex], 0.0, 1.0); 10 | } 11 | 12 | @fragment fn frag_main() -> @location(0) vec4 { 13 | return vec4(1.0, 0.0, 0.0, 1.0); 14 | } 15 | -------------------------------------------------------------------------------- /examples/custom-renderer/Renderer.zig: -------------------------------------------------------------------------------- 1 | const mach = @import("mach"); 2 | const gpu = mach.gpu; 3 | const math = mach.math; 4 | 5 | const Vec3 = math.Vec3; 6 | 7 | pub const mach_module = .renderer; 8 | 9 | pub const mach_systems = .{ .init, .deinit, .renderFrame }; 10 | 11 | const Renderer = @This(); 12 | 13 | const num_bind_groups = 1024 * 32; 14 | 15 | // uniform bind group offset must be 256-byte aligned 16 | const uniform_offset = 256; 17 | 18 | const UniformBufferObject = extern struct { 19 | offset: Vec3, 20 | scale: f32, 21 | }; 22 | 23 | window: mach.ObjectID, 24 | pipeline: *gpu.RenderPipeline, 25 | bind_groups: [num_bind_groups]*gpu.BindGroup, 26 | uniform_buffer: *gpu.Buffer, 27 | 28 | objects: mach.Objects(.{}, struct { 29 | position: Vec3, 30 | scale: f32, 31 | }), 32 | 33 | pub fn init( 34 | core: *mach.Core, 35 | renderer: *Renderer, 36 | ) !void { 37 | const window = core.windows.getValue(renderer.window); 38 | const device = window.device; 39 | const shader_module = device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.wgsl")); 40 | defer shader_module.release(); 41 | 42 | // Fragment state 43 | const blend = gpu.BlendState{}; 44 | const color_target = gpu.ColorTargetState{ 45 | .format = window.framebuffer_format, 46 | .blend = &blend, 47 | .write_mask = gpu.ColorWriteMaskFlags.all, 48 | }; 49 | const fragment = gpu.FragmentState.init(.{ 50 | .module = shader_module, 51 | .entry_point = "frag_main", 52 | .targets = &.{color_target}, 53 | }); 54 | 55 | const label = @tagName(mach_module) ++ ".init"; 56 | const uniform_buffer = device.createBuffer(&.{ 57 | .label = label ++ " uniform buffer", 58 | .usage = .{ .copy_dst = true, .uniform = true }, 59 | .size = @sizeOf(UniformBufferObject) * uniform_offset * num_bind_groups, 60 | .mapped_at_creation = .false, 61 | }); 62 | 63 | const bind_group_layout_entry = gpu.BindGroupLayout.Entry.initBuffer(0, .{ .vertex = true }, .uniform, true, 0); 64 | const bind_group_layout = device.createBindGroupLayout( 65 | &gpu.BindGroupLayout.Descriptor.init(.{ 66 | .label = label, 67 | .entries = &.{bind_group_layout_entry}, 68 | }), 69 | ); 70 | defer bind_group_layout.release(); 71 | 72 | var bind_groups: [num_bind_groups]*gpu.BindGroup = undefined; 73 | for (bind_groups, 0..) |_, i| { 74 | bind_groups[i] = device.createBindGroup( 75 | &gpu.BindGroup.Descriptor.init(.{ 76 | .label = label, 77 | .layout = bind_group_layout, 78 | .entries = &.{gpu.BindGroup.Entry.initBuffer(0, uniform_buffer, uniform_offset * i, @sizeOf(UniformBufferObject), @sizeOf(UniformBufferObject))}, 79 | }), 80 | ); 81 | } 82 | 83 | const bind_group_layouts = [_]*gpu.BindGroupLayout{bind_group_layout}; 84 | const pipeline_layout = device.createPipelineLayout(&gpu.PipelineLayout.Descriptor.init(.{ 85 | .label = label, 86 | .bind_group_layouts = &bind_group_layouts, 87 | })); 88 | defer pipeline_layout.release(); 89 | 90 | const pipeline = device.createRenderPipeline(&gpu.RenderPipeline.Descriptor{ 91 | .label = label, 92 | .fragment = &fragment, 93 | .layout = pipeline_layout, 94 | .vertex = gpu.VertexState{ 95 | .module = shader_module, 96 | .entry_point = "vertex_main", 97 | }, 98 | }); 99 | 100 | renderer.* = .{ 101 | .window = renderer.window, 102 | .objects = renderer.objects, 103 | .pipeline = pipeline, 104 | .bind_groups = bind_groups, 105 | .uniform_buffer = uniform_buffer, 106 | }; 107 | } 108 | 109 | pub fn deinit( 110 | renderer: *Renderer, 111 | ) !void { 112 | renderer.pipeline.release(); 113 | for (renderer.bind_groups) |bind_group| bind_group.release(); 114 | renderer.uniform_buffer.release(); 115 | } 116 | 117 | pub fn renderFrame( 118 | core: *mach.Core, 119 | renderer: *Renderer, 120 | ) !void { 121 | const window = core.windows.getValue(renderer.window); 122 | 123 | // Grab the back buffer of the swapchain 124 | // TODO(Core) 125 | const back_buffer_view = window.swap_chain.getCurrentTextureView().?; 126 | defer back_buffer_view.release(); 127 | 128 | // Create a command encoder 129 | const label = @tagName(mach_module) ++ ".tick"; 130 | const encoder = window.device.createCommandEncoder(&.{ .label = label }); 131 | defer encoder.release(); 132 | 133 | // Update uniform buffer 134 | var num_objects: usize = 0; 135 | var objs = renderer.objects.slice(); 136 | while (objs.next()) |obj_id| { 137 | const obj = renderer.objects.getValue(obj_id); 138 | const ubo = UniformBufferObject{ 139 | .offset = obj.position, 140 | .scale = obj.scale, 141 | }; 142 | encoder.writeBuffer(renderer.uniform_buffer, uniform_offset * num_objects, &[_]UniformBufferObject{ubo}); 143 | num_objects += 1; 144 | } 145 | 146 | // Begin render pass 147 | const sky_blue_background = gpu.Color{ .r = 0.776, .g = 0.988, .b = 1, .a = 1 }; 148 | const color_attachments = [_]gpu.RenderPassColorAttachment{.{ 149 | .view = back_buffer_view, 150 | .clear_value = sky_blue_background, 151 | .load_op = .clear, 152 | .store_op = .store, 153 | }}; 154 | const render_pass = encoder.beginRenderPass(&gpu.RenderPassDescriptor.init(.{ 155 | .label = label, 156 | .color_attachments = &color_attachments, 157 | })); 158 | defer render_pass.release(); 159 | 160 | // Draw 161 | for (renderer.bind_groups[0..num_objects]) |bind_group| { 162 | render_pass.setPipeline(renderer.pipeline); 163 | render_pass.setBindGroup(0, bind_group, &.{0}); 164 | render_pass.draw(3, 1, 0, 0); 165 | } 166 | 167 | // Finish render pass 168 | render_pass.end(); 169 | 170 | // Submit our commands to the queue 171 | var command = encoder.finish(&.{ .label = label }); 172 | defer command.release(); 173 | window.queue.submit(&[_]*gpu.CommandBuffer{command}); 174 | } 175 | -------------------------------------------------------------------------------- /examples/custom-renderer/shader.wgsl: -------------------------------------------------------------------------------- 1 | // TODO(important): docs 2 | struct Uniform { 3 | pos: vec4, 4 | scale: f32, 5 | }; 6 | 7 | @group(0) @binding(0) var in : Uniform; 8 | 9 | @vertex fn vertex_main( 10 | @builtin(vertex_index) VertexIndex : u32 11 | ) -> @builtin(position) vec4 { 12 | var positions = array, 3>( 13 | vec2( 0.0, 0.1), 14 | vec2(-0.1, -0.1), 15 | vec2( 0.1, -0.1) 16 | ); 17 | var pos = positions[VertexIndex]; 18 | return vec4((pos*in.scale)+in.pos.xy, 0.0, 1.0); 19 | } 20 | 21 | @fragment fn frag_main() -> @location(0) vec4 { 22 | return vec4(1.0, 0.0, 0.0, 0.0); 23 | } 24 | -------------------------------------------------------------------------------- /src/StringTable.zig: -------------------------------------------------------------------------------- 1 | //! Stores null-terminated strings and maps them to unique 32-bit indices. 2 | //! 3 | //! Lookups are omnidirectional: both (string -> index) and (index -> string) are supported 4 | //! operations. 5 | //! 6 | //! The implementation is based on: 7 | //! https://zig.news/andrewrk/how-to-use-hash-map-contexts-to-save-memory-when-doing-a-string-table-3l33 8 | 9 | const std = @import("std"); 10 | 11 | const StringTable = @This(); 12 | 13 | string_bytes: std.ArrayListUnmanaged(u8) = .{}, 14 | 15 | /// Key is string_bytes index. 16 | string_table: std.HashMapUnmanaged(u32, void, IndexContext, std.hash_map.default_max_load_percentage) = .{}, 17 | 18 | pub const Index = u32; 19 | 20 | /// Returns the index of a string key, if it exists 21 | /// complexity: hashmap lookup 22 | pub fn index(table: *StringTable, key: []const u8) ?Index { 23 | const slice_context: SliceAdapter = .{ .string_bytes = &table.string_bytes }; 24 | const found_entry = table.string_table.getEntryAdapted(key, slice_context); 25 | if (found_entry) |e| return e.key_ptr.*; 26 | return null; 27 | } 28 | 29 | /// Returns the index of a string key, inserting if not exists 30 | /// complexity: hashmap lookup / update 31 | pub fn indexOrPut(table: *StringTable, allocator: std.mem.Allocator, key: []const u8) !Index { 32 | const slice_context: SliceAdapter = .{ .string_bytes = &table.string_bytes }; 33 | const index_context: IndexContext = .{ .string_bytes = &table.string_bytes }; 34 | const entry = try table.string_table.getOrPutContextAdapted(allocator, key, slice_context, index_context); 35 | if (!entry.found_existing) { 36 | entry.key_ptr.* = @intCast(table.string_bytes.items.len); 37 | try table.string_bytes.appendSlice(allocator, key); 38 | try table.string_bytes.append(allocator, '\x00'); 39 | } 40 | return entry.key_ptr.*; 41 | } 42 | 43 | /// Returns a null-terminated string given the index 44 | /// complexity: O(1) 45 | pub fn string(table: *StringTable, idx: Index) [:0]const u8 { 46 | return std.mem.span(@as([*:0]const u8, @ptrCast(table.string_bytes.items.ptr)) + idx); 47 | } 48 | 49 | pub fn deinit(table: *StringTable, allocator: std.mem.Allocator) void { 50 | table.string_bytes.deinit(allocator); 51 | table.string_table.deinit(allocator); 52 | } 53 | 54 | const IndexContext = struct { 55 | string_bytes: *std.ArrayListUnmanaged(u8), 56 | 57 | pub fn eql(ctx: IndexContext, a: u32, b: u32) bool { 58 | _ = ctx; 59 | return a == b; 60 | } 61 | 62 | pub fn hash(ctx: IndexContext, x: u32) u64 { 63 | const x_slice = std.mem.span(@as([*:0]const u8, @ptrCast(ctx.string_bytes.items.ptr)) + x); 64 | return std.hash_map.hashString(x_slice); 65 | } 66 | }; 67 | 68 | const SliceAdapter = struct { 69 | string_bytes: *std.ArrayListUnmanaged(u8), 70 | 71 | pub fn eql(adapter: SliceAdapter, a_slice: []const u8, b: u32) bool { 72 | const b_slice = std.mem.span(@as([*:0]const u8, @ptrCast(adapter.string_bytes.items.ptr)) + b); 73 | return std.mem.eql(u8, a_slice, b_slice); 74 | } 75 | 76 | pub fn hash(adapter: SliceAdapter, adapted_key: []const u8) u64 { 77 | _ = adapter; 78 | return std.hash_map.hashString(adapted_key); 79 | } 80 | }; 81 | 82 | test { 83 | const gpa = std.testing.allocator; 84 | 85 | var table: StringTable = .{}; 86 | defer table.deinit(gpa); 87 | 88 | const index_context: IndexContext = .{ .string_bytes = &table.string_bytes }; 89 | _ = index_context; 90 | 91 | // "hello" -> index 0 92 | const hello_index = try table.indexOrPut(gpa, "hello"); 93 | try std.testing.expectEqual(@as(Index, 0), hello_index); 94 | 95 | try std.testing.expectEqual(@as(Index, 6), try table.indexOrPut(gpa, "world")); 96 | try std.testing.expectEqual(@as(Index, 12), try table.indexOrPut(gpa, "foo")); 97 | try std.testing.expectEqual(@as(Index, 16), try table.indexOrPut(gpa, "bar")); 98 | try std.testing.expectEqual(@as(Index, 20), try table.indexOrPut(gpa, "baz")); 99 | 100 | // index 0 -> "hello" 101 | try std.testing.expectEqualStrings("hello", table.string(hello_index)); 102 | 103 | // Lookup "hello" -> index 0 104 | try std.testing.expectEqual(hello_index, table.index("hello").?); 105 | 106 | // Lookup "foobar" -> null 107 | try std.testing.expectEqual(@as(?Index, null), table.index("foobar")); 108 | } 109 | -------------------------------------------------------------------------------- /src/core/linux/wayland.c: -------------------------------------------------------------------------------- 1 | #include "wayland-client-protocol-code.h" 2 | #include "wayland-xdg-shell-client-protocol-code.h" 3 | #include "wayland-xdg-decoration-client-protocol-code.h" 4 | -------------------------------------------------------------------------------- /src/core/windows/win32.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | true/pm 6 | PerMonitorV2 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/entrypoint/main.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn main() !void { 4 | const allocator = std.heap.c_allocator; 5 | 6 | // The set of Mach modules our application may use. 7 | var mods: @import("app").Modules = undefined; 8 | try mods.init(allocator); 9 | // TODO: enable mods.deinit(allocator); for allocator leak detection 10 | // defer mods.deinit(allocator); 11 | 12 | const app = mods.get(.app); 13 | app.run(.main); 14 | } 15 | -------------------------------------------------------------------------------- /src/gfx/atlas/LICENSE: -------------------------------------------------------------------------------- 1 | atlas.zig originally came from Mitchell Hashimoto: https://gist.github.com/mitchellh/0c023dbd381c42e145b5da8d58b1487f - it was further modified for our needs to be more general-purpose. The original code is licensed as follows, while subsequent changes are under our LICENSE at the root of this repository: 2 | 3 | Copyright 2022 Mitchell Hashimoto 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /src/gfx/font/native/Font.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const ft = @import("mach-freetype"); 3 | const harfbuzz = @import("mach-harfbuzz"); 4 | const TextRun = @import("TextRun.zig"); 5 | const px_per_pt = @import("../main.zig").px_per_pt; 6 | const RenderedGlyph = @import("../main.zig").RenderedGlyph; 7 | const RenderOptions = @import("../main.zig").RenderOptions; 8 | const RGBA32 = @import("../main.zig").RGBA32; 9 | 10 | const Font = @This(); 11 | 12 | var freetype_ready_mu: std.Thread.Mutex = .{}; 13 | var freetype_ready: bool = false; 14 | var freetype: ft.Library = undefined; 15 | 16 | face: ft.Face, 17 | bitmap: std.ArrayListUnmanaged(RGBA32) = .{}, 18 | 19 | pub fn initFreetype() !void { 20 | freetype_ready_mu.lock(); 21 | defer freetype_ready_mu.unlock(); 22 | if (!freetype_ready) { 23 | freetype = try ft.Library.init(); 24 | freetype_ready = true; 25 | } 26 | } 27 | 28 | pub fn initBytes(font_bytes: []const u8) anyerror!Font { 29 | try initFreetype(); 30 | return .{ 31 | .face = try freetype.createFaceMemory(font_bytes, 0), 32 | }; 33 | } 34 | 35 | pub fn shape(f: *const Font, r: *TextRun) anyerror!void { 36 | // Guess text segment properties. 37 | r.buffer.guessSegmentProps(); 38 | // TODO: Optionally override specific text segment properties? 39 | // r.buffer.setDirection(.ltr); 40 | // r.buffer.setScript(.latin); 41 | // r.buffer.setLanguage(harfbuzz.Language.fromString("en")); 42 | 43 | const font_size_pt = r.font_size_px / px_per_pt; 44 | const font_size_pt_frac: i32 = @intFromFloat(font_size_pt * 64.0); 45 | f.face.setCharSize(font_size_pt_frac, font_size_pt_frac, 0, 0) catch return error.RenderError; 46 | 47 | const hb_face = harfbuzz.Face.fromFreetypeFace(f.face); 48 | const hb_font = harfbuzz.Font.init(hb_face); 49 | defer hb_font.deinit(); 50 | 51 | hb_font.setScale(font_size_pt_frac, font_size_pt_frac); 52 | hb_font.setPTEM(font_size_pt); 53 | 54 | // TODO: optionally pass shaping features? 55 | hb_font.shape(r.buffer, null); 56 | 57 | r.index = 0; 58 | r.infos = r.buffer.getGlyphInfos(); 59 | r.positions = r.buffer.getGlyphPositions() orelse return error.OutOfMemory; 60 | 61 | for (r.positions, r.infos) |*pos, info| { 62 | const glyph_index = info.codepoint; 63 | f.face.loadGlyph(glyph_index, .{ .render = false }) catch return error.RenderError; 64 | const glyph = f.face.glyph(); 65 | const metrics = glyph.metrics(); 66 | pos.*.x_offset += @intCast(metrics.horiBearingX); 67 | pos.*.y_offset += @intCast(metrics.horiBearingY); 68 | // TODO: use vertBearingX / vertBearingY for vertical layouts 69 | } 70 | } 71 | 72 | pub fn render(f: *Font, allocator: std.mem.Allocator, glyph_index: u32, opt: RenderOptions) anyerror!RenderedGlyph { 73 | _ = opt; 74 | f.face.loadGlyph(glyph_index, .{ .render = true }) catch return error.RenderError; 75 | 76 | const glyph = f.face.glyph(); 77 | const glyph_bitmap = glyph.bitmap(); 78 | const buffer = glyph_bitmap.buffer(); 79 | const width = glyph_bitmap.width(); 80 | const height = glyph_bitmap.rows(); 81 | const margin = 1; 82 | 83 | if (buffer == null) return RenderedGlyph{ 84 | .bitmap = null, 85 | .width = width + (margin * 2), 86 | .height = height + (margin * 2), 87 | }; 88 | 89 | // Add 1 pixel padding to texture to avoid bleeding over other textures. This is part of the 90 | // render() API contract. 91 | f.bitmap.clearRetainingCapacity(); 92 | const num_pixels = (width + (margin * 2)) * (height + (margin * 2)); 93 | // TODO: handle OOM here 94 | f.bitmap.ensureTotalCapacity(allocator, num_pixels) catch return error.RenderError; 95 | f.bitmap.resize(allocator, num_pixels) catch return error.RenderError; 96 | for (f.bitmap.items, 0..) |*data, i| { 97 | const x = i % (width + (margin * 2)); 98 | const y = i / (width + (margin * 2)); 99 | if (x < margin or x > (width + margin) or y < margin or y > (height + margin)) { 100 | data.* = RGBA32{ .r = 0, .g = 0, .b = 0, .a = 0 }; 101 | } else { 102 | const alpha = buffer.?[((y - margin) * width + (x - margin)) % buffer.?.len]; 103 | data.* = RGBA32{ .r = 0, .g = 0, .b = 0, .a = alpha }; 104 | } 105 | } 106 | 107 | return RenderedGlyph{ 108 | .bitmap = f.bitmap.items, 109 | .width = width + (margin * 2), 110 | .height = height + (margin * 2), 111 | }; 112 | } 113 | 114 | pub fn deinit(f: *Font, allocator: std.mem.Allocator) void { 115 | f.face.deinit(); 116 | f.bitmap.deinit(allocator); 117 | } 118 | -------------------------------------------------------------------------------- /src/gfx/font/native/TextRun.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const harfbuzz = @import("mach-harfbuzz"); 3 | const math = @import("../../../main.zig").math; 4 | const vec2 = math.vec2; 5 | const Vec2 = math.Vec2; 6 | const Glyph = @import("../main.zig").Glyph; 7 | 8 | const TextRun = @This(); 9 | 10 | font_size_px: f32 = 16.0, 11 | px_density: u8 = 1, 12 | 13 | // Internal / private fields. 14 | buffer: harfbuzz.Buffer, 15 | index: usize = 0, 16 | infos: []harfbuzz.GlyphInfo = undefined, 17 | positions: []harfbuzz.GlyphPosition = undefined, 18 | 19 | pub fn init() anyerror!TextRun { 20 | return TextRun{ 21 | .buffer = harfbuzz.Buffer.init() orelse return error.OutOfMemory, 22 | }; 23 | } 24 | 25 | pub fn addText(s: *const TextRun, utf8_text: []const u8) void { 26 | s.buffer.addUTF8(utf8_text, 0, null); 27 | } 28 | 29 | pub fn next(s: *TextRun) ?Glyph { 30 | if (s.index >= s.infos.len) return null; 31 | const info = s.infos[s.index]; 32 | const pos = s.positions[s.index]; 33 | s.index += 1; 34 | return Glyph{ 35 | .glyph_index = info.codepoint, 36 | // TODO: should we expose this? Is there a browser equivalent? do we need it? 37 | // .var1 = @intCast(info.var1), 38 | // .var2 = @intCast(info.var2), 39 | .cluster = info.cluster, 40 | .advance = vec2(@floatFromInt(pos.x_advance), @floatFromInt(pos.y_advance)).div(&Vec2.splat(64.0)), 41 | .offset = vec2(@floatFromInt(pos.x_offset), @floatFromInt(pos.y_offset)).div(&Vec2.splat(64.0)), 42 | }; 43 | } 44 | 45 | pub fn deinit(s: *const TextRun) void { 46 | s.buffer.deinit(); 47 | return; 48 | } 49 | -------------------------------------------------------------------------------- /src/gfx/main.zig: -------------------------------------------------------------------------------- 1 | pub const util = @import("util.zig"); // TODO: banish 2-level deep namespaces 2 | pub const Atlas = @import("atlas/Atlas.zig"); 3 | 4 | // Mach modules 5 | pub const Sprite = @import("Sprite.zig"); 6 | pub const Text = @import("Text.zig"); 7 | 8 | /// All graphics modules 9 | pub const modules = .{ Sprite, Text }; 10 | 11 | // Fonts 12 | pub const Font = @import("font/main.zig").Font; 13 | pub const TextRun = @import("font/main.zig").TextRun; 14 | pub const Glyph = @import("font/main.zig").Glyph; 15 | pub const px_per_pt = @import("font/main.zig").px_per_pt; 16 | pub const font_weight_normal = 400; 17 | pub const font_weight_bold = 700; 18 | 19 | test { 20 | const std = @import("std"); 21 | // TODO: refactor code so we can use this here: 22 | // std.testing.refAllDeclsRecursive(@This()); 23 | std.testing.refAllDeclsRecursive(util); 24 | std.testing.refAllDeclsRecursive(Sprite); 25 | std.testing.refAllDeclsRecursive(Atlas); 26 | std.testing.refAllDeclsRecursive(Text); 27 | std.testing.refAllDeclsRecursive(Font); 28 | std.testing.refAllDeclsRecursive(TextRun); 29 | std.testing.refAllDeclsRecursive(Glyph); 30 | } 31 | -------------------------------------------------------------------------------- /src/gfx/sprite.wgsl: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // Vertex shader 3 | //----------------------------------------------------------------------------- 4 | struct VertexOutput { 5 | // Vertex position 6 | @builtin(position) Position : vec4, 7 | 8 | // UV coordinate 9 | @location(0) fragUV : vec2, 10 | }; 11 | 12 | // Our vertex shader will recieve these parameters 13 | struct Uniforms { 14 | // The view * orthographic projection matrix 15 | view_projection: mat4x4, 16 | 17 | // Total size of the sprite texture in pixels 18 | texture_size: vec2, 19 | }; 20 | 21 | @group(0) @binding(0) var uniforms : Uniforms; 22 | 23 | // Sprite model transformation matrices 24 | @group(0) @binding(1) var sprite_transforms: array>; 25 | 26 | // Sprite UV coordinate transformation matrices. Sprite UV coordinates are (0, 0) at the top-left 27 | // corner, and in pixels. 28 | // TODO(d3d12): #1217 29 | // changed the uv_transform to 4x4. The 3x3 ca@group(0) @binding(2) var sprite_uv_transforms: array>; 30 | @group(0) @binding(2) var sprite_uv_transforms: array>; 31 | 32 | // Sprite sizes, in pixels. 33 | @group(0) @binding(3) var sprite_sizes: array>; 34 | 35 | @vertex 36 | fn vertMain( 37 | @builtin(vertex_index) VertexIndex : u32 38 | ) -> VertexOutput { 39 | // Our vertex shader will be called six times per sprite (2 triangles make up a sprite, so six 40 | // vertices.) The VertexIndex tells us which vertex we need to render, so we know e.g. vertices 41 | // 0-5 correspond to the first sprite, vertices 6-11 correspond to the second sprite, and so on. 42 | let sprite_transform = sprite_transforms[VertexIndex / 6]; 43 | let sprite_uv_transform = sprite_uv_transforms[VertexIndex / 6]; 44 | let sprite_size = sprite_sizes[VertexIndex / 6]; 45 | 46 | // Imagine the vertices and UV coordinates of a card. There are two triangles, the UV coordinates 47 | // describe the corresponding location of each vertex on the texture. We hard-code the vertex 48 | // positions and UV coordinates here: 49 | let positions = array, 6>( 50 | vec2(0, 0), // left, bottom 51 | vec2(0, 1), // left, top 52 | vec2(1, 0), // right, bottom 53 | vec2(1, 0), // right, bottom 54 | vec2(0, 1), // left, top 55 | vec2(1, 1), // right, top 56 | ); 57 | let uvs = array, 6>( 58 | vec2(0, 1), // left, bottom 59 | vec2(0, 0), // left, top 60 | vec2(1, 1), // right, bottom 61 | vec2(1, 1), // right, bottom 62 | vec2(0, 0), // left, top 63 | vec2(1, 0), // right, top 64 | ); 65 | 66 | // Based on the vertex index, we determine which positions[n] and uvs[n] we need to use. Our 67 | // vertex shader is invoked 6 times per sprite, we need to produce the right vertex/uv coordinates 68 | // each time to produce a textured card. 69 | let pos_2d = positions[VertexIndex % 6]; 70 | var uv = uvs[VertexIndex % 6]; 71 | 72 | // Currently, our pos_2d and uv coordinates describe a card that covers 1px by 1px; and the UV 73 | // coordinates describe using the entire texture. We alter the coordinates to describe the 74 | // desired sprite location, size, and apply a subset of the texture instead of the entire texture. 75 | var pos = vec4(pos_2d * sprite_size, 0, 1); // normalized -> pixels 76 | pos = sprite_transform * pos; // apply sprite transform (pixels) 77 | pos = uniforms.view_projection * pos; // pixels -> normalized 78 | 79 | uv *= sprite_size; // normalized -> pixels 80 | uv = (sprite_uv_transform * vec4(uv, 1, 0)).xy; // apply sprite UV transform (pixels) 81 | uv /= uniforms.texture_size; // pixels -> normalized 82 | 83 | var output : VertexOutput; 84 | output.Position = pos; 85 | output.fragUV = uv; 86 | return output; 87 | } 88 | 89 | //----------------------------------------------------------------------------- 90 | // Fragment shader 91 | //----------------------------------------------------------------------------- 92 | @group(0) @binding(4) var spriteSampler: sampler; 93 | @group(0) @binding(5) var spriteTexture: texture_2d; 94 | 95 | @fragment 96 | fn fragMain( 97 | @location(0) fragUV: vec2 98 | ) -> @location(0) vec4 { 99 | var c = textureSample(spriteTexture, spriteSampler, fragUV); 100 | if (c.a <= 0.0) { 101 | discard; 102 | } 103 | return c; 104 | } -------------------------------------------------------------------------------- /src/gfx/text.wgsl: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // Vertex shader 3 | //----------------------------------------------------------------------------- 4 | struct VertexOutput { 5 | // Vertex position 6 | @builtin(position) Position : vec4, 7 | 8 | // UV coordinate into the glyph atlas 9 | @location(0) fragUV : vec2, 10 | 11 | // Color of the glyph 12 | @location(1) color : vec4, 13 | }; 14 | 15 | // Our vertex shader will recieve these parameters 16 | struct Uniforms { 17 | // The view * orthographic projection matrix 18 | view_projection: mat4x4, 19 | 20 | // Total size of the font atlas texture in pixels 21 | texture_size: vec2, 22 | }; 23 | 24 | struct Glyph { 25 | // Position of this glyph (top-left corner.) 26 | pos: vec2, 27 | 28 | // Size of the glyph in pixels. 29 | size: vec2, 30 | 31 | // Normalized position of the top-left UV coordinate 32 | uv_pos: vec2, 33 | 34 | // Which text this glyph belongs to; this is the index for transforms[i], colors[i] 35 | text_index: u32, 36 | 37 | // TODO(d3d12): #1217 38 | // Added padding for d3d12/hlsl. Having 7 floats before the color vec caused and error. 39 | text_index2: u32, // Padding for struct alignment 40 | 41 | // Color of the glyph 42 | color: vec4, 43 | } 44 | 45 | @group(0) @binding(0) var uniforms : Uniforms; 46 | 47 | @group(0) @binding(1) var transforms: array>; 48 | @group(0) @binding(2) var colors: array>; 49 | @group(0) @binding(3) var glyphs: array; 50 | 51 | @vertex 52 | fn vertMain( 53 | @builtin(vertex_index) VertexIndex : u32 54 | ) -> VertexOutput { 55 | var glyph = glyphs[VertexIndex / 6]; 56 | let transform = transforms[glyph.text_index]; 57 | let color = colors[glyph.text_index]; 58 | 59 | // Based on the vertex index, we determine which positions[n] and uvs[n] we need to use. Our 60 | // vertex shader is invoked 6 times per glyph, we need to produce the right vertex/uv coordinates 61 | // each time to produce a textured card. 62 | let positions = array, 6>( 63 | vec2(0, 0), // left, bottom 64 | vec2(0, 1), // left, top 65 | vec2(1, 0), // right, bottom 66 | vec2(1, 0), // right, bottom 67 | vec2(0, 1), // left, top 68 | vec2(1, 1), // right, top 69 | ); 70 | let uvs = array, 6>( 71 | vec2(0, 1), // left, bottom 72 | vec2(0, 0), // left, top 73 | vec2(1, 1), // right, bottom 74 | vec2(1, 1), // right, bottom 75 | vec2(0, 0), // left, top 76 | vec2(1, 0), // right, top 77 | ); 78 | let pos_2d = positions[VertexIndex % 6]; 79 | var uv = uvs[VertexIndex % 6]; 80 | 81 | // Currently, our pos_2d and uv coordinates describe a card that covers 1px by 1px; and the UV 82 | // coordinates describe using the entire texture. We alter the coordinates to describe the 83 | // desired glyph location, size, and apply a subset of the texture instead of the entire texture. 84 | var pos = vec4((pos_2d * glyph.size) + glyph.pos, 0, 1); // normalized -> pixels 85 | pos = transform * pos; // apply glyph transform (pixels) 86 | pos = uniforms.view_projection * pos; // pixels -> normalized 87 | 88 | // TODO: elevate px_density out of shader 89 | let px_density = 2.0; 90 | uv *= glyph.size*px_density; // normalized -> pixels 91 | uv += glyph.uv_pos; // apply glyph UV position offset (pixels) 92 | uv /= uniforms.texture_size; // pixels -> normalized 93 | 94 | var output : VertexOutput; 95 | output.Position = pos; 96 | output.fragUV = uv; 97 | output.color = glyph.color; 98 | return output; 99 | } 100 | 101 | //----------------------------------------------------------------------------- 102 | // Fragment shader 103 | //----------------------------------------------------------------------------- 104 | @group(0) @binding(4) var glyphSampler: sampler; 105 | @group(0) @binding(5) var glyphTexture: texture_2d; 106 | 107 | @fragment 108 | fn fragMain( 109 | @location(0) fragUV: vec2, 110 | @location(1) color: vec4 111 | ) -> @location(0) vec4 { 112 | var c = textureSample(glyphTexture, glyphSampler, fragUV); 113 | if (c.a <= 0.0) { 114 | discard; 115 | } 116 | return vec4(color.rgb * c.a, color.a); 117 | } 118 | -------------------------------------------------------------------------------- /src/main.zig: -------------------------------------------------------------------------------- 1 | //! The Mach standard library 2 | 3 | const build_options = @import("build-options"); 4 | const builtin = @import("builtin"); 5 | const std = @import("std"); 6 | 7 | const log = std.log.scoped(.mach); 8 | 9 | pub const is_debug = builtin.mode == .Debug; 10 | 11 | // Core 12 | pub const Core = if (build_options.want_core) @import("Core.zig") else struct {}; 13 | 14 | // note: gamemode requires libc on linux 15 | pub const gamemode = if (builtin.os.tag != .linux or builtin.link_libc) @import("gamemode.zig"); 16 | pub const gfx = if (build_options.want_mach) @import("gfx/main.zig") else struct {}; 17 | pub const Audio = if (build_options.want_sysaudio) @import("Audio.zig") else struct {}; 18 | pub const math = @import("math/main.zig"); 19 | pub const testing = @import("testing.zig"); 20 | pub const time = @import("time/main.zig"); 21 | 22 | pub const sysaudio = if (build_options.want_sysaudio) @import("sysaudio/main.zig") else struct {}; 23 | pub const sysgpu = if (build_options.want_sysgpu) @import("sysgpu/main.zig") else struct {}; 24 | pub const gpu = if (build_options.want_sysgpu) @import("sysgpu/main.zig").sysgpu else struct {}; 25 | 26 | pub const Modules = @import("module.zig").Modules; 27 | 28 | pub const ModuleID = @import("module.zig").ModuleID; 29 | pub const ModuleFunctionID = @import("module.zig").ModuleFunctionID; 30 | pub const FunctionID = @import("module.zig").FunctionID; 31 | pub const Mod = @import("module.zig").Mod; 32 | pub const ObjectID = @import("module.zig").ObjectID; 33 | pub const Objects = @import("module.zig").Objects; 34 | 35 | // TODO(object): remove this? 36 | pub fn schedule(v: anytype) @TypeOf(v) { 37 | return v; 38 | } 39 | 40 | // Instrumented function to load system libraries and print nicer error 41 | // messages. 42 | pub inline fn dynLibOpen(libName: []const u8) !std.DynLib { 43 | return std.DynLib.open(libName) catch |err| { 44 | switch (err) { 45 | error.FileNotFound => { 46 | log.err("Missing system library: '{s}'!", .{libName}); 47 | return error.LibraryNotFound; 48 | }, 49 | else => return err, 50 | } 51 | }; 52 | } 53 | 54 | test { 55 | // TODO: refactor code so we can use this here: 56 | // std.testing.refAllDeclsRecursive(@This()); 57 | _ = Core; 58 | _ = gpu; 59 | _ = sysaudio; 60 | _ = sysgpu; 61 | _ = gfx; 62 | _ = Audio; 63 | _ = Audio; 64 | _ = math; 65 | _ = testing; 66 | _ = time; 67 | _ = @import("mpsc.zig"); 68 | _ = @import("graph.zig"); 69 | std.testing.refAllDeclsRecursive(gamemode); 70 | std.testing.refAllDeclsRecursive(math); 71 | } 72 | -------------------------------------------------------------------------------- /src/sysaudio/backends.zig: -------------------------------------------------------------------------------- 1 | const builtin = @import("builtin"); 2 | const std = @import("std"); 3 | 4 | pub const Backend = std.meta.Tag(Context); 5 | 6 | pub const Context = switch (builtin.os.tag) { 7 | .linux => union(enum) { 8 | pulseaudio: *@import("pulseaudio.zig").Context, 9 | pipewire: *@import("pipewire.zig").Context, 10 | jack: *@import("jack.zig").Context, 11 | alsa: *@import("alsa.zig").Context, 12 | dummy: *@import("dummy.zig").Context, 13 | }, 14 | .freebsd, .netbsd, .openbsd, .solaris => union(enum) { 15 | pipewire: *@import("pipewire.zig").Context, 16 | pulseaudio: *@import("pulseaudio.zig").Context, 17 | jack: *@import("jack.zig").Context, 18 | dummy: *@import("dummy.zig").Context, 19 | }, 20 | .macos, .ios, .watchos, .tvos => union(enum) { 21 | coreaudio: *@import("coreaudio.zig").Context, 22 | dummy: *@import("dummy.zig").Context, 23 | }, 24 | .windows => union(enum) { 25 | wasapi: *@import("wasapi.zig").Context, 26 | dummy: *@import("dummy.zig").Context, 27 | }, 28 | .freestanding => switch (builtin.cpu.arch) { 29 | else => union(enum) { 30 | dummy: *@import("dummy.zig").Context, 31 | }, 32 | }, 33 | else => union(enum) { dummy: *@import("dummy.zig").Context }, 34 | }; 35 | 36 | pub const Player = switch (builtin.os.tag) { 37 | .linux => union(enum) { 38 | pulseaudio: *@import("pulseaudio.zig").Player, 39 | pipewire: *@import("pipewire.zig").Player, 40 | jack: *@import("jack.zig").Player, 41 | alsa: *@import("alsa.zig").Player, 42 | dummy: *@import("dummy.zig").Player, 43 | }, 44 | .freebsd, .netbsd, .openbsd, .solaris => union(enum) { 45 | pipewire: *@import("pipewire.zig").Player, 46 | pulseaudio: *@import("pulseaudio.zig").Player, 47 | jack: *@import("jack.zig").Player, 48 | dummy: *@import("dummy.zig").Player, 49 | }, 50 | .macos, .ios, .watchos, .tvos => union(enum) { 51 | coreaudio: *@import("coreaudio.zig").Player, 52 | dummy: *@import("dummy.zig").Player, 53 | }, 54 | .windows => union(enum) { 55 | wasapi: *@import("wasapi.zig").Player, 56 | dummy: *@import("dummy.zig").Player, 57 | }, 58 | .freestanding => switch (builtin.cpu.arch) { 59 | else => union(enum) { 60 | dummy: *@import("dummy.zig").Player, 61 | }, 62 | }, 63 | else => union(enum) { dummy: *@import("dummy.zig").Player }, 64 | }; 65 | 66 | pub const Recorder = switch (builtin.os.tag) { 67 | .linux => union(enum) { 68 | pulseaudio: *@import("pulseaudio.zig").Recorder, 69 | pipewire: *@import("pipewire.zig").Recorder, 70 | jack: *@import("jack.zig").Recorder, 71 | alsa: *@import("alsa.zig").Recorder, 72 | dummy: *@import("dummy.zig").Recorder, 73 | }, 74 | .freebsd, .netbsd, .openbsd, .solaris => union(enum) { 75 | pipewire: *@import("pipewire.zig").Recorder, 76 | pulseaudio: *@import("pulseaudio.zig").Recorder, 77 | jack: *@import("jack.zig").Recorder, 78 | dummy: *@import("dummy.zig").Recorder, 79 | }, 80 | .macos, .ios, .watchos, .tvos => union(enum) { 81 | coreaudio: *@import("coreaudio.zig").Recorder, 82 | dummy: *@import("dummy.zig").Recorder, 83 | }, 84 | .windows => union(enum) { 85 | wasapi: *@import("wasapi.zig").Recorder, 86 | dummy: *@import("dummy.zig").Recorder, 87 | }, 88 | .freestanding => switch (builtin.cpu.arch) { 89 | else => union(enum) { 90 | dummy: *@import("dummy.zig").Recorder, 91 | }, 92 | }, 93 | else => union(enum) { dummy: *@import("dummy.zig").Recorder }, 94 | }; 95 | -------------------------------------------------------------------------------- /src/sysaudio/dummy.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const main = @import("main.zig"); 3 | const backends = @import("backends.zig"); 4 | const util = @import("util.zig"); 5 | 6 | const default_sample_rate = 44_100; // Hz 7 | 8 | const dummy_playback = main.Device{ 9 | .id = "dummy-playback", 10 | .name = "Dummy Device", 11 | .mode = .playback, 12 | .channels = undefined, 13 | .formats = std.meta.tags(main.Format), 14 | .sample_rate = .{ 15 | .min = main.min_sample_rate, 16 | .max = main.max_sample_rate, 17 | }, 18 | }; 19 | 20 | const dummy_capture = main.Device{ 21 | .id = "dummy-capture", 22 | .name = "Dummy Device", 23 | .mode = .capture, 24 | .channels = undefined, 25 | .formats = std.meta.tags(main.Format), 26 | .sample_rate = .{ 27 | .min = main.min_sample_rate, 28 | .max = main.max_sample_rate, 29 | }, 30 | }; 31 | 32 | pub const Context = struct { 33 | allocator: std.mem.Allocator, 34 | devices_info: util.DevicesInfo, 35 | 36 | pub fn init(allocator: std.mem.Allocator, options: main.Context.Options) !backends.Context { 37 | _ = options; 38 | 39 | const ctx = try allocator.create(Context); 40 | errdefer allocator.destroy(ctx); 41 | ctx.* = .{ 42 | .allocator = allocator, 43 | .devices_info = util.DevicesInfo.init(), 44 | }; 45 | 46 | return .{ .dummy = ctx }; 47 | } 48 | 49 | pub fn deinit(ctx: *Context) void { 50 | for (ctx.devices_info.list.items) |d| 51 | freeDevice(ctx.allocator, d); 52 | ctx.devices_info.list.deinit(ctx.allocator); 53 | ctx.allocator.destroy(ctx); 54 | } 55 | 56 | pub fn refresh(ctx: *Context) !void { 57 | for (ctx.devices_info.list.items) |d| 58 | freeDevice(ctx.allocator, d); 59 | ctx.devices_info.clear(); 60 | 61 | try ctx.devices_info.list.append(ctx.allocator, dummy_playback); 62 | try ctx.devices_info.list.append(ctx.allocator, dummy_capture); 63 | 64 | ctx.devices_info.setDefault(.playback, 0); 65 | ctx.devices_info.setDefault(.capture, 1); 66 | 67 | ctx.devices_info.list.items[0].channels = try ctx.allocator.alloc(main.ChannelPosition, 1); 68 | ctx.devices_info.list.items[1].channels = try ctx.allocator.alloc(main.ChannelPosition, 1); 69 | 70 | ctx.devices_info.list.items[0].channels[0] = .front_center; 71 | ctx.devices_info.list.items[1].channels[0] = .front_center; 72 | } 73 | 74 | pub fn devices(ctx: Context) []const main.Device { 75 | return ctx.devices_info.list.items; 76 | } 77 | 78 | pub fn defaultDevice(ctx: Context, mode: main.Device.Mode) ?main.Device { 79 | return ctx.devices_info.default(mode); 80 | } 81 | 82 | pub fn createPlayer(ctx: *Context, device: main.Device, writeFn: main.WriteFn, options: main.StreamOptions) !backends.Player { 83 | _ = writeFn; 84 | const player = try ctx.allocator.create(Player); 85 | player.* = .{ 86 | .allocator = ctx.allocator, 87 | .is_paused = false, 88 | .vol = 1.0, 89 | .channels = device.channels, 90 | .format = options.format, 91 | .sample_rate = options.sample_rate orelse default_sample_rate, 92 | }; 93 | return .{ .dummy = player }; 94 | } 95 | 96 | pub fn createRecorder(ctx: *Context, device: main.Device, readFn: main.ReadFn, options: main.StreamOptions) !backends.Recorder { 97 | _ = readFn; 98 | const recorder = try ctx.allocator.create(Recorder); 99 | recorder.* = .{ 100 | .allocator = ctx.allocator, 101 | .is_paused = false, 102 | .vol = 1.0, 103 | .channels = device.channels, 104 | .format = options.format, 105 | .sample_rate = options.sample_rate orelse default_sample_rate, 106 | }; 107 | return .{ .dummy = recorder }; 108 | } 109 | }; 110 | 111 | pub const Player = struct { 112 | allocator: std.mem.Allocator, 113 | is_paused: bool, 114 | vol: f32, 115 | 116 | channels: []main.ChannelPosition, 117 | format: main.Format, 118 | sample_rate: u24, 119 | 120 | pub fn deinit(player: *Player) void { 121 | player.allocator.destroy(player); 122 | } 123 | 124 | pub fn start(player: *Player) !void { 125 | _ = player; 126 | } 127 | 128 | pub fn play(player: *Player) !void { 129 | player.is_paused = false; 130 | } 131 | 132 | pub fn pause(player: *Player) !void { 133 | player.is_paused = true; 134 | } 135 | 136 | pub fn paused(player: *Player) bool { 137 | return player.is_paused; 138 | } 139 | 140 | pub fn setVolume(player: *Player, vol: f32) !void { 141 | player.vol = vol; 142 | } 143 | 144 | pub fn volume(player: *Player) !f32 { 145 | return player.vol; 146 | } 147 | }; 148 | 149 | pub const Recorder = struct { 150 | allocator: std.mem.Allocator, 151 | is_paused: bool, 152 | vol: f32, 153 | 154 | channels: []main.ChannelPosition, 155 | format: main.Format, 156 | sample_rate: u24, 157 | 158 | pub fn deinit(recorder: *Recorder) void { 159 | recorder.allocator.destroy(recorder); 160 | } 161 | 162 | pub fn start(recorder: *Recorder) !void { 163 | _ = recorder; 164 | } 165 | 166 | pub fn record(recorder: *Recorder) !void { 167 | recorder.is_paused = false; 168 | } 169 | 170 | pub fn pause(recorder: *Recorder) !void { 171 | recorder.is_paused = true; 172 | } 173 | 174 | pub fn paused(recorder: *Recorder) bool { 175 | return recorder.is_paused; 176 | } 177 | 178 | pub fn setVolume(recorder: *Recorder, vol: f32) !void { 179 | recorder.vol = vol; 180 | } 181 | 182 | pub fn volume(recorder: *Recorder) !f32 { 183 | return recorder.vol; 184 | } 185 | }; 186 | 187 | fn freeDevice(allocator: std.mem.Allocator, device: main.Device) void { 188 | allocator.free(device.channels); 189 | } 190 | -------------------------------------------------------------------------------- /src/sysaudio/pipewire/sysaudio.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | struct spa_pod *sysaudio_spa_format_audio_raw_build(struct spa_pod_builder *builder, uint32_t id, struct spa_audio_info_raw *info) 5 | { 6 | return spa_format_audio_raw_build(builder, id, info); 7 | } 8 | 9 | void sysaudio_pw_registry_add_listener(struct pw_registry *reg, struct spa_hook *reg_listener, struct pw_registry_events *events) { 10 | pw_registry_add_listener(reg, reg_listener, events, NULL); 11 | } -------------------------------------------------------------------------------- /src/sysaudio/tests/record.zig: -------------------------------------------------------------------------------- 1 | //! Redirects input device into zig-out/raw_audio file. 2 | 3 | const std = @import("std"); 4 | const sysaudio = @import("mach").sysaudio; 5 | 6 | var recorder: sysaudio.Recorder = undefined; 7 | var file: std.fs.File = undefined; 8 | 9 | // Note: this Info.plist file gets embedded into the final binary __TEXT,__info_plist 10 | // linker section. On macOS this means that NSMicrophoneUsageDescription is set. Without 11 | // that being set, the application would be denied access to the microphone (the prompt 12 | // for microphone access would not even appear.) 13 | // 14 | // The linker is just a convenient way to specify this without building a .app bundle with 15 | // a separate Info.plist file. 16 | export var __info_plist: [663:0]u8 linksection("__TEXT,__info_plist") = 17 | ( 18 | \\ 19 | \\ 20 | \\ 21 | \\ 22 | \\ CFBundleDevelopmentRegion 23 | \\ English 24 | \\ CFBundleIdentifier 25 | \\ com.my.app 26 | \\ CFBundleInfoDictionaryVersion 27 | \\ 6.0 28 | \\ CFBundleName 29 | \\ myapp 30 | \\ CFBundleDisplayName 31 | \\ My App 32 | \\ CFBundleVersion 33 | \\ 1.0.0 34 | \\ NSMicrophoneUsageDescription 35 | \\ To record audio from your microphone 36 | \\ 37 | \\ 38 | ).*; 39 | 40 | pub fn main() !void { 41 | _ = __info_plist; 42 | 43 | var gpa = std.heap.GeneralPurposeAllocator(.{}){}; 44 | defer _ = gpa.deinit(); 45 | 46 | var ctx = try sysaudio.Context.init(null, gpa.allocator(), .{}); 47 | defer ctx.deinit(); 48 | try ctx.refresh(); 49 | 50 | const device = ctx.defaultDevice(.capture) orelse return error.NoDevice; 51 | 52 | recorder = try ctx.createRecorder(device, readCallback, .{}); 53 | defer recorder.deinit(); 54 | try recorder.start(); 55 | 56 | const zig_out = try std.fs.cwd().makeOpenPath("zig-out", .{}); 57 | file = try zig_out.createFile("raw_audio", .{}); 58 | 59 | std.debug.print( 60 | \\Recording to zig-out/raw_audio using: 61 | \\ 62 | \\ device: {s} 63 | \\ channels: {} 64 | \\ sample_rate: {} 65 | \\ 66 | \\You can play this recording back using e.g.: 67 | \\ $ ffplay -f f32le -ar {} -ac {} zig-out/raw_audio 68 | \\ 69 | , .{ 70 | device.name, 71 | device.channels.len, 72 | recorder.sampleRate(), 73 | recorder.sampleRate(), 74 | device.channels.len, 75 | }); 76 | // Note: you may also use e.g.: 77 | // 78 | // ``` 79 | // paplay -p --format=FLOAT32LE --rate 48000 --raw zig-out/raw_audio 80 | // aplay -f FLOAT_LE -r 48000 zig-out/raw_audio 81 | // ``` 82 | 83 | while (true) {} 84 | } 85 | 86 | fn readCallback(_: ?*anyopaque, input: []const u8) void { 87 | const format_size = recorder.format().size(); 88 | const samples = input.len / format_size; 89 | var buffer: [16 * 1024]f32 = undefined; 90 | sysaudio.convertFrom(f32, buffer[0..samples], recorder.format(), input); 91 | _ = file.write(std.mem.sliceAsBytes(buffer[0 .. input.len / format_size])) catch {}; 92 | } 93 | -------------------------------------------------------------------------------- /src/sysaudio/tests/sine.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const sysaudio = @import("mach").sysaudio; 3 | 4 | var player: sysaudio.Player = undefined; 5 | 6 | pub fn main() !void { 7 | var timer = try std.time.Timer.start(); 8 | var gpa = std.heap.GeneralPurposeAllocator(.{}){}; 9 | defer _ = gpa.deinit(); 10 | 11 | var ctx = try sysaudio.Context.init(null, gpa.allocator(), .{ .deviceChangeFn = deviceChange }); 12 | std.log.info("Took {} to initialize the context...", .{std.fmt.fmtDuration(timer.lap())}); 13 | defer ctx.deinit(); 14 | try ctx.refresh(); 15 | std.log.info("Took {} to refresh the context...", .{std.fmt.fmtDuration(timer.lap())}); 16 | 17 | const device = ctx.defaultDevice(.playback) orelse return error.NoDevice; 18 | std.log.info("Took {} to get the default playback device...", .{std.fmt.fmtDuration(timer.lap())}); 19 | 20 | player = try ctx.createPlayer(device, writeCallback, .{}); 21 | std.log.info("Took {} to create a player...", .{std.fmt.fmtDuration(timer.lap())}); 22 | defer player.deinit(); 23 | try player.start(); 24 | std.log.info("Took {} to start the player...", .{std.fmt.fmtDuration(timer.lap())}); 25 | 26 | try player.setVolume(0.85); 27 | std.log.info("Took {} to set the volume...", .{std.fmt.fmtDuration(timer.lap())}); 28 | 29 | var buf: [16]u8 = undefined; 30 | std.log.info("player created & entering i/o loop...", .{}); 31 | while (true) { 32 | std.debug.print("( paused = {}, volume = {d} )\n> ", .{ player.paused(), try player.volume() }); 33 | const line = (try std.io.getStdIn().reader().readUntilDelimiterOrEof(&buf, '\n')) orelse break; 34 | var iter = std.mem.splitScalar(u8, line, ':'); 35 | const cmd = std.mem.trimRight(u8, iter.first(), &std.ascii.whitespace); 36 | if (std.mem.eql(u8, cmd, "vol")) { 37 | const vol = try std.fmt.parseFloat(f32, std.mem.trim(u8, iter.next().?, &std.ascii.whitespace)); 38 | try player.setVolume(vol); 39 | } else if (std.mem.eql(u8, cmd, "pause")) { 40 | try player.pause(); 41 | try std.testing.expect(player.paused()); 42 | } else if (std.mem.eql(u8, cmd, "play")) { 43 | try player.play(); 44 | try std.testing.expect(!player.paused()); 45 | } else if (std.mem.eql(u8, cmd, "exit")) { 46 | break; 47 | } 48 | } 49 | } 50 | 51 | const pitch = 440.0; 52 | const radians_per_second = pitch * 2.0 * std.math.pi; 53 | var seconds_offset: f32 = 0.0; 54 | 55 | fn writeCallback(_: ?*anyopaque, output: []u8) void { 56 | const seconds_per_frame = 1.0 / @as(f32, @floatFromInt(player.sampleRate())); 57 | const frame_size = player.format().frameSize(@intCast(player.channels().len)); 58 | const frames = output.len / frame_size; 59 | 60 | var i: usize = 0; 61 | var src: [16]f32 = undefined; 62 | while (i < output.len) : (i += frame_size) { 63 | const frame_index: f32 = @floatFromInt(i / frame_size); 64 | const sample = @sin((seconds_offset + frame_index * seconds_per_frame) * radians_per_second); 65 | for (0..player.channels().len) |ch| src[ch] = sample; 66 | sysaudio.convertTo( 67 | f32, 68 | src[0..player.channels().len], 69 | player.format(), 70 | output[i..][0..frame_size], 71 | ); 72 | } 73 | 74 | seconds_offset = @mod(seconds_offset + seconds_per_frame * @as(f32, @floatFromInt(frames)), 1.0); 75 | } 76 | 77 | fn deviceChange(_: ?*anyopaque) void { 78 | std.log.info("device change detected!", .{}); 79 | } 80 | -------------------------------------------------------------------------------- /src/sysaudio/util.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const main = @import("main.zig"); 3 | 4 | pub const DevicesInfo = struct { 5 | list: std.ArrayListUnmanaged(main.Device), 6 | default_output: ?usize, 7 | default_input: ?usize, 8 | 9 | pub fn init() DevicesInfo { 10 | return .{ 11 | .list = .{}, 12 | .default_output = null, 13 | .default_input = null, 14 | }; 15 | } 16 | 17 | pub fn clear(device_info: *DevicesInfo) void { 18 | device_info.default_output = null; 19 | device_info.default_input = null; 20 | device_info.list.clearRetainingCapacity(); 21 | } 22 | 23 | pub fn get(device_info: DevicesInfo, i: usize) main.Device { 24 | return device_info.list.items[i]; 25 | } 26 | 27 | pub fn default(device_info: DevicesInfo, mode: main.Device.Mode) ?main.Device { 28 | const index = switch (mode) { 29 | .playback => device_info.default_output, 30 | .capture => device_info.default_input, 31 | } orelse { 32 | for (device_info.list.items) |device| { 33 | if (device.mode == mode) { 34 | return device; 35 | } 36 | } 37 | return null; 38 | }; 39 | return device_info.get(index); 40 | } 41 | 42 | pub fn setDefault(device_info: *DevicesInfo, mode: main.Device.Mode, i: usize) void { 43 | switch (mode) { 44 | .playback => device_info.default_output = i, 45 | .capture => device_info.default_input = i, 46 | } 47 | } 48 | }; 49 | 50 | pub fn Range(comptime T: type) type { 51 | return struct { 52 | min: T, 53 | max: T, 54 | 55 | pub fn clamp(range: @This(), val: T) T { 56 | return std.math.clamp(val, range.min, range.max); 57 | } 58 | }; 59 | } 60 | 61 | pub fn doNothing() callconv(.C) void {} 62 | -------------------------------------------------------------------------------- /src/sysgpu/conventions.md: -------------------------------------------------------------------------------- 1 | ### Object ordering 2 | 3 | Backends should be a single file with object in the following order: 4 | 5 | - Instance 6 | - Adapter 7 | - Surface 8 | - SurfaceCapabilities 9 | - Device 10 | - SwapChain 11 | - Buffer 12 | - Texture 13 | - TextureView 14 | - Sampler 15 | - BindGroupLayout 16 | - BindGroup 17 | - PipelineLayout 18 | - ShaderModule 19 | - ComputePipeline 20 | - RenderPipeline 21 | - CommandBuffer 22 | - CommandEncoder 23 | - ComputePassEncoder 24 | - RenderPassEncoder 25 | - RenderBundle 26 | - RenderBundleEncoder 27 | - Queue 28 | - QuerySet 29 | 30 | Utility objects (e.g. StateTracker should come after the closest object that "owns" them. 31 | -------------------------------------------------------------------------------- /src/sysgpu/d3d12/c.zig: -------------------------------------------------------------------------------- 1 | pub usingnamespace @cImport({ 2 | @cDefine("MIDL_INTERFACE", "struct"); 3 | @cInclude("d3d12.h"); 4 | @cInclude("dxgi1_6.h"); 5 | @cInclude("d3dcompiler.h"); 6 | @cInclude("dxgidebug.h"); 7 | }); 8 | -------------------------------------------------------------------------------- /src/sysgpu/d3d12/notes.md: -------------------------------------------------------------------------------- 1 | ### Interface Support 2 | 3 | Windows release version needed to use various functionality in DXGI and D3D12. 4 | 5 | DXGI 6 | - 1.4 - baseline 7 | - 1.5 - 1607 8 | - 1.6 - 1703/1803/1809 9 | 10 | CreateDXGIFactory 11 | 12 | - CreateDXGIFactory2 - baseline 13 | 14 | DXGIGetDebugInterface 15 | 16 | - DXGIGetDebugInterface1 - baseline 17 | 18 | IDXGIAdapter 19 | 20 | - IDXGIAdapter3 - baseline 21 | - IDXGIAdapter4 - 1703 22 | 23 | IDXGIDevice 24 | 25 | - IDXGIDevice3 - baseline 26 | - IDXGIDevice4 - 1607 27 | 28 | IDXGIFactory 29 | 30 | - IDXGIFactory4 - baseline 31 | - IDXGIFactory5 - 1607 32 | - IDXGIFactory6 - 1803 33 | - IDXGIFactory7 - 1809 34 | 35 | IDXGIOutput 36 | 37 | - IDXGIOutput4 - baseline 38 | - IDXGIOutput5 - 1607 39 | - IDXGIOutput6 - 1703 40 | 41 | IDXGISwapChain 42 | 43 | - IDXGISwapChain3 - baseline 44 | - IDXGISwapChain4 - 1607 45 | -------------------------------------------------------------------------------- /src/sysgpu/limits.zig: -------------------------------------------------------------------------------- 1 | pub const max_texture_dimension1d: u32 = 8192; 2 | pub const max_texture_dimension2d: u32 = 8192; 3 | pub const max_texture_dimension3d: u32 = 2048; 4 | pub const max_texture_array_layers: u32 = 256; 5 | pub const max_bind_groups: u32 = 4; 6 | pub const max_bind_groups_plus_vertex_buffers: u32 = 24; 7 | pub const max_bindings_per_bind_group: u32 = 1000; 8 | pub const max_dynamic_uniform_buffers_per_pipeline_layout: u32 = 8; 9 | pub const max_dynamic_storage_buffers_per_pipeline_layout: u32 = 4; 10 | pub const max_sampled_textures_per_shader_stage: u32 = 16; 11 | pub const max_samplers_per_shader_stage: u32 = 16; 12 | pub const max_storage_buffers_per_shader_stage: u32 = 8; 13 | pub const max_storage_textures_per_shader_stage: u32 = 4; 14 | pub const max_uniform_buffers_per_shader_stage: u32 = 12; 15 | pub const max_uniform_buffer_binding_size: u64 = 65536; 16 | pub const max_storage_buffer_binding_size: u64 = 134217728; 17 | pub const min_uniform_buffer_offset_alignment: u32 = 256; 18 | pub const min_storage_buffer_offset_alignment: u32 = 256; 19 | pub const max_vertex_buffers: u32 = 8; 20 | pub const max_buffer_size: u64 = 268435456; 21 | pub const max_vertex_attributes: u32 = 16; 22 | pub const max_vertex_buffer_array_stride: u32 = 2048; 23 | pub const max_inter_stage_shader_components: u32 = 60; 24 | pub const max_inter_stage_shader_variables: u32 = 16; 25 | pub const max_color_attachments: u32 = 8; 26 | pub const max_color_attachment_bytes_per_sample: u32 = 32; 27 | pub const max_compute_workgroup_storage_size: u32 = 16384; 28 | pub const max_compute_invocations_per_workgroup: u32 = 256; 29 | pub const max_compute_workgroup_size_x: u32 = 256; 30 | pub const max_compute_workgroup_size_y: u32 = 256; 31 | pub const max_compute_workgroup_size_z: u32 = 64; 32 | pub const max_compute_workgroups_per_dimension: u32 = 65535; 33 | 34 | pub const max_buffers_per_shader_stage = max_storage_buffers_per_shader_stage + max_uniform_buffers_per_shader_stage; 35 | -------------------------------------------------------------------------------- /src/sysgpu/opengl/c.zig: -------------------------------------------------------------------------------- 1 | pub usingnamespace @cImport({ 2 | @cInclude("windows.h"); 3 | @cInclude("GL/glcorearb.h"); 4 | @cInclude("GL/glext.h"); 5 | @cInclude("GL/wglext.h"); 6 | }); 7 | -------------------------------------------------------------------------------- /src/sysgpu/shader.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub const CodeGen = @import("shader/CodeGen.zig"); 4 | pub const Air = @import("shader/Air.zig"); 5 | pub const Ast = @import("shader/Ast.zig"); 6 | pub const Parser = @import("shader/Parser.zig"); 7 | pub const Token = @import("shader/Token.zig"); 8 | pub const Tokenizer = @import("shader/Tokenizer.zig"); 9 | pub const ErrorList = @import("shader/ErrorList.zig"); 10 | pub const printAir = @import("shader/print_air.zig").printAir; 11 | 12 | test "reference declarations" { 13 | std.testing.refAllDecls(CodeGen); 14 | std.testing.refAllDecls(Air); 15 | std.testing.refAllDecls(Ast); 16 | std.testing.refAllDecls(Parser); 17 | std.testing.refAllDecls(Token); 18 | std.testing.refAllDecls(Tokenizer); 19 | std.testing.refAllDecls(ErrorList); 20 | _ = printAir; 21 | _ = @import("shader/test.zig"); 22 | } 23 | -------------------------------------------------------------------------------- /src/sysgpu/shader/ErrorList.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const Token = @import("Token.zig"); 3 | pub const ErrorList = @This(); 4 | 5 | pub const ErrorMsg = struct { 6 | loc: Token.Loc, 7 | msg: []const u8, 8 | note: ?Note = null, 9 | 10 | pub const Note = struct { 11 | loc: ?Token.Loc = null, 12 | msg: []const u8, 13 | }; 14 | }; 15 | 16 | arena: std.heap.ArenaAllocator, 17 | list: std.ArrayListUnmanaged(ErrorMsg) = .{}, 18 | 19 | pub fn init(allocator: std.mem.Allocator) !ErrorList { 20 | return .{ 21 | .arena = std.heap.ArenaAllocator.init(allocator), 22 | }; 23 | } 24 | 25 | pub fn deinit(self: *ErrorList) void { 26 | self.arena.deinit(); 27 | self.* = undefined; 28 | } 29 | 30 | pub fn add( 31 | self: *ErrorList, 32 | loc: Token.Loc, 33 | comptime format: []const u8, 34 | args: anytype, 35 | note: ?ErrorMsg.Note, 36 | ) !void { 37 | try self.list.append(self.arena.allocator(), .{ 38 | .loc = loc, 39 | .msg = try std.fmt.allocPrint(self.arena.allocator(), comptime format, args), 40 | .note = note, 41 | }); 42 | } 43 | 44 | pub fn createNote( 45 | self: *ErrorList, 46 | loc: ?Token.Loc, 47 | comptime format: []const u8, 48 | args: anytype, 49 | ) !ErrorMsg.Note { 50 | return .{ 51 | .loc = loc, 52 | .msg = try std.fmt.allocPrint(self.arena.allocator(), comptime format, args), 53 | }; 54 | } 55 | 56 | pub fn print(self: ErrorList, source: []const u8, file_path: ?[]const u8) !void { 57 | const stderr = std.io.getStdErr(); 58 | var bw = std.io.bufferedWriter(stderr.writer()); 59 | const b = bw.writer(); 60 | const term = std.io.tty.detectConfig(stderr); 61 | 62 | for (self.list.items) |*err| { 63 | const loc_extra = err.loc.extraInfo(source); 64 | 65 | // 'file:line:column error: MSG' 66 | try term.setColor(b, .bold); 67 | try b.print("{?s}:{d}:{d} ", .{ file_path, loc_extra.line, loc_extra.col }); 68 | try term.setColor(b, .bright_red); 69 | try b.writeAll("error: "); 70 | try term.setColor(b, .reset); 71 | try term.setColor(b, .bold); 72 | try b.writeAll(err.msg); 73 | try b.writeByte('\n'); 74 | 75 | try printCode(b, term, source, err.loc); 76 | 77 | // note 78 | if (err.note) |note| { 79 | if (note.loc) |note_loc| { 80 | const note_loc_extra = note_loc.extraInfo(source); 81 | 82 | try term.setColor(b, .reset); 83 | try term.setColor(b, .bold); 84 | try b.print("{?s}:{d}:{d} ", .{ file_path, note_loc_extra.line, note_loc_extra.col }); 85 | } 86 | try term.setColor(b, .cyan); 87 | try b.writeAll("note: "); 88 | 89 | try term.setColor(b, .reset); 90 | try term.setColor(b, .bold); 91 | try b.writeAll(note.msg); 92 | try b.writeByte('\n'); 93 | 94 | if (note.loc) |note_loc| { 95 | try printCode(b, term, source, note_loc); 96 | } 97 | } 98 | 99 | try term.setColor(b, .reset); 100 | } 101 | try bw.flush(); 102 | } 103 | 104 | fn printCode(writer: anytype, term: std.io.tty.Config, source: []const u8, loc: Token.Loc) !void { 105 | const loc_extra = loc.extraInfo(source); 106 | try term.setColor(writer, .dim); 107 | try writer.print("{d} │ ", .{loc_extra.line}); 108 | try term.setColor(writer, .reset); 109 | try writer.writeAll(source[loc_extra.line_start..loc.start]); 110 | try term.setColor(writer, .green); 111 | try writer.writeAll(source[loc.start..loc.end]); 112 | try term.setColor(writer, .reset); 113 | try writer.writeAll(source[loc.end..loc_extra.line_end]); 114 | try writer.writeByte('\n'); 115 | 116 | // location pointer 117 | const line_number_len = (std.math.log10(loc_extra.line) + 1) + 3; 118 | if (line_number_len > loc_extra.col) { 119 | try writer.writeByteNTimes( 120 | ' ', 121 | line_number_len + (loc_extra.col - 1), 122 | ); 123 | } 124 | try term.setColor(writer, .bold); 125 | try term.setColor(writer, .green); 126 | try writer.writeByte('^'); 127 | if (loc.end > loc.start) { 128 | try writer.writeByteNTimes('~', loc.end - loc.start - 1); 129 | } 130 | try writer.writeByte('\n'); 131 | } 132 | -------------------------------------------------------------------------------- /src/sysgpu/shader/test/boids-sprite-update.wgsl: -------------------------------------------------------------------------------- 1 | struct Particle { 2 | pos : vec2, 3 | vel : vec2, 4 | }; 5 | struct SimParams { 6 | deltaT : f32, 7 | rule1Distance : f32, 8 | rule2Distance : f32, 9 | rule3Distance : f32, 10 | rule1Scale : f32, 11 | rule2Scale : f32, 12 | rule3Scale : f32, 13 | }; 14 | struct Particles { 15 | particles : array, 16 | }; 17 | @binding(0) @group(0) var params : SimParams; 18 | @binding(1) @group(0) var particlesA : Particles; 19 | @binding(2) @group(0) var particlesB : Particles; 20 | 21 | // https://github.com/austinEng/Project6-Vulkan-Flocking/blob/master/data/shaders/computeparticles/particle.comp 22 | @compute @workgroup_size(64) 23 | fn main(@builtin(global_invocation_id) GlobalInvocationID : vec3) { 24 | var index : u32 = GlobalInvocationID.x; 25 | 26 | if (index >= arrayLength(&particlesA.particles)) { 27 | return; 28 | } 29 | 30 | var vPos = particlesA.particles[index].pos; 31 | var vVel = particlesA.particles[index].vel; 32 | var cMass = vec2(0.0, 0.0); 33 | var cVel = vec2(0.0, 0.0); 34 | var colVel = vec2(0.0, 0.0); 35 | var cMassCount : u32 = 0u; 36 | var cVelCount : u32 = 0u; 37 | var pos : vec2; 38 | var vel : vec2; 39 | 40 | for (var i : u32 = 0u; i < arrayLength(&particlesA.particles); i = i + 1u) { 41 | if (i == index) { 42 | continue; 43 | } 44 | 45 | pos = particlesA.particles[i].pos.xy; 46 | vel = particlesA.particles[i].vel.xy; 47 | if (distance(pos, vPos) < params.rule1Distance) { 48 | cMass = cMass + pos; 49 | cMassCount = cMassCount + 1u; 50 | } 51 | if (distance(pos, vPos) < params.rule2Distance) { 52 | colVel = colVel - (pos - vPos); 53 | } 54 | if (distance(pos, vPos) < params.rule3Distance) { 55 | cVel = cVel + vel; 56 | cVelCount = cVelCount + 1u; 57 | } 58 | } 59 | if (cMassCount > 0u) { 60 | var temp = f32(cMassCount); 61 | cMass = (cMass / vec2(temp, temp)) - vPos; 62 | } 63 | if (cVelCount > 0u) { 64 | var temp = f32(cVelCount); 65 | cVel = cVel / vec2(temp, temp); 66 | } 67 | vVel = vVel + (cMass * params.rule1Scale) + (colVel * params.rule2Scale) + 68 | (cVel * params.rule3Scale); 69 | 70 | // clamp velocity for a more pleasing simulation 71 | vVel = normalize(vVel) * clamp(length(vVel), 0.0, 0.1); 72 | // kinematic update 73 | vPos = vPos + (vVel * params.deltaT); 74 | // Wrap around boundary 75 | if (vPos.x < -1.0) { 76 | vPos.x = 1.0; 77 | } 78 | if (vPos.x > 1.0) { 79 | vPos.x = -1.0; 80 | } 81 | if (vPos.y < -1.0) { 82 | vPos.y = 1.0; 83 | } 84 | if (vPos.y > 1.0) { 85 | vPos.y = -1.0; 86 | } 87 | // Write back 88 | particlesB.particles[index].pos = vPos; 89 | particlesB.particles[index].vel = vVel; 90 | } -------------------------------------------------------------------------------- /src/sysgpu/shader/test/boids-sprite.wgsl: -------------------------------------------------------------------------------- 1 | @vertex 2 | fn vert_main(@location(0) a_particlePos : vec2, 3 | @location(1) a_particleVel : vec2, 4 | @location(2) a_pos : vec2) -> @builtin(position) vec4 { 5 | let angle = -atan2(a_particleVel.x, a_particleVel.y); 6 | let pos = vec2( 7 | (a_pos.x * cos(angle)) - (a_pos.y * sin(angle)), 8 | (a_pos.x * sin(angle)) + (a_pos.y * cos(angle))); 9 | return vec4(pos + a_particlePos, 0.0, 1.0); 10 | } 11 | 12 | @fragment 13 | fn frag_main() -> @location(0) vec4 { 14 | return vec4(1.0, 1.0, 1.0, 1.0); 15 | } -------------------------------------------------------------------------------- /src/sysgpu/shader/test/builtins.wgsl: -------------------------------------------------------------------------------- 1 | @group(0) @binding(0) var _runtime_array : array; 2 | 3 | @fragment 4 | fn main() { 5 | // TODO: Add all builtins 6 | let _array_length = arrayLength(&_runtime_array); 7 | let _sin = sin(1.0); 8 | let _cos = cos(1.0); 9 | let _normalize = normalize(vec3(1.0)); 10 | let _length = length(1.0); 11 | let _floor = floor(1.0); 12 | let _abs = abs(1.0); 13 | let _all = all(vec3(true)); 14 | let _dpdx = dpdx(1.0); 15 | let _dpdy = dpdy(1.0); 16 | let _fwidth = fwidth(1.0); 17 | let _min = min(1.0, 1.0); 18 | let _max = max(1.0, 1.0); 19 | let _atan2 = atan2(1.0, 1.0); 20 | let _distance = distance(1.0, 1.0); 21 | let _dot = dot(vec3(1.0), vec3(1.0)); 22 | let _pow = pow(1.0, 1.0); 23 | let _step = step(1.0, 1.0); 24 | let _mix = mix(1.0, 1.0, 1.0); 25 | let _clamp = clamp(1.0, 1.0, 1.0); 26 | let _smoothstep = smoothstep(1.0, 1.0, 1.0); 27 | } -------------------------------------------------------------------------------- /src/sysgpu/shader/test/cube-map.wgsl: -------------------------------------------------------------------------------- 1 | struct Uniforms { 2 | modelViewProjectionMatrix : mat4x4, 3 | } 4 | @binding(0) @group(0) var uniforms : Uniforms; 5 | 6 | struct VertexOutput { 7 | @builtin(position) Position : vec4, 8 | @location(0) fragUV : vec2, 9 | @location(1) fragPosition: vec4, 10 | } 11 | 12 | @vertex 13 | fn vertex_main( 14 | @location(0) position : vec4, 15 | @location(1) uv : vec2 16 | ) -> VertexOutput { 17 | var output : VertexOutput; 18 | output.Position = uniforms.modelViewProjectionMatrix * position; 19 | output.fragUV = uv; 20 | output.fragPosition = 0.5 * (position + vec4(1.0, 1.0, 1.0, 1.0)); 21 | return output; 22 | } 23 | 24 | @group(0) @binding(1) var mySampler: sampler; 25 | @group(0) @binding(2) var myTexture: texture_cube; 26 | 27 | @fragment 28 | fn frag_main( 29 | @location(0) fragUV: vec2, 30 | @location(1) fragPosition: vec4 31 | ) -> @location(0) vec4 { 32 | var cubemapVec = fragPosition.xyz - vec3(0.5, 0.5, 0.5); 33 | return textureSample(myTexture, mySampler, cubemapVec); 34 | } -------------------------------------------------------------------------------- /src/sysgpu/shader/test/fractal-cube.wgsl: -------------------------------------------------------------------------------- 1 | struct Uniforms { 2 | matrix : mat4x4, 3 | }; 4 | 5 | @binding(0) @group(0) var ubo : Uniforms; 6 | 7 | struct VertexOut { 8 | @builtin(position) Position : vec4, 9 | @location(0) fragUV : vec2, 10 | @location(1) fragPosition: vec4, 11 | } 12 | 13 | @vertex fn vertex_main( 14 | @location(0) position : vec4, 15 | @location(1) uv: vec2 16 | ) -> VertexOut { 17 | var output : VertexOut; 18 | output.Position = position * ubo.matrix; 19 | output.fragUV = uv; 20 | output.fragPosition = 0.5 * (position + vec4(1.0, 1.0, 1.0, 1.0)); 21 | return output; 22 | } 23 | 24 | @binding(1) @group(0) var mySampler: sampler; 25 | @binding(2) @group(0) var myTexture: texture_2d; 26 | 27 | @fragment fn frag_main( 28 | @location(0) fragUV: vec2, 29 | @location(1) fragPosition: vec4 30 | ) -> @location(0) vec4 { 31 | let texColor = textureSample(myTexture, mySampler, fragUV * 0.8 + vec2(0.1, 0.1)); 32 | let f = f32(length(texColor.rgb - vec3(0.5, 0.5, 0.5)) < 0.01); 33 | return (1.0 - f) * texColor + f * fragPosition; 34 | // return vec4(texColor.rgb,1.0); 35 | } -------------------------------------------------------------------------------- /src/sysgpu/shader/test/fragmentDeferredRendering.wgsl: -------------------------------------------------------------------------------- 1 | 2 | @group(0) @binding(0) var gBufferNormal: texture_2d; 3 | @group(0) @binding(1) var gBufferAlbedo: texture_2d; 4 | @group(0) @binding(2) var gBufferDepth: texture_depth_2d; 5 | 6 | struct LightData { 7 | position : vec4, 8 | color : vec3, 9 | radius : f32, 10 | } 11 | struct LightsBuffer { 12 | lights: array, 13 | } 14 | @group(1) @binding(0) var lightsBuffer: LightsBuffer; 15 | 16 | struct Config { 17 | numLights : u32, 18 | } 19 | struct Camera { 20 | viewProjectionMatrix : mat4x4, 21 | invViewProjectionMatrix : mat4x4, 22 | } 23 | @group(1) @binding(1) var config: Config; 24 | @group(1) @binding(2) var camera: Camera; 25 | 26 | fn world_from_screen_coord(coord : vec2, depth_sample: f32) -> vec3 { 27 | // reconstruct world-space position from the screen coordinate. 28 | let posClip = vec4(coord.x * 2.0 - 1.0, (1.0 - coord.y) * 2.0 - 1.0, depth_sample, 1.0); 29 | let posWorldW = camera.invViewProjectionMatrix * posClip; 30 | let posWorld = posWorldW.xyz / posWorldW.www; 31 | return posWorld; 32 | } 33 | 34 | @fragment 35 | fn main( 36 | @builtin(position) coord : vec4 37 | ) -> @location(0) vec4 { 38 | var result : vec3; 39 | 40 | let depth = textureLoad( 41 | gBufferDepth, 42 | vec2(floor(coord.xy)), 43 | 0 44 | ); 45 | 46 | // Don't light the sky. 47 | if (depth >= 1.0) { 48 | discard; 49 | } 50 | 51 | let bufferSize = textureDimensions(gBufferDepth); 52 | let coordUV = coord.xy / vec2(bufferSize); 53 | let position = world_from_screen_coord(coordUV, depth); 54 | 55 | let normal = textureLoad( 56 | gBufferNormal, 57 | vec2(floor(coord.xy)), 58 | 0 59 | ).xyz; 60 | 61 | let albedo = textureLoad( 62 | gBufferAlbedo, 63 | vec2(floor(coord.xy)), 64 | 0 65 | ).rgb; 66 | 67 | for (var i = 0u; i < config.numLights; i++) { 68 | let L = lightsBuffer.lights[i].position.xyz - position; 69 | let distance = length(L); 70 | if (distance > lightsBuffer.lights[i].radius) { 71 | continue; 72 | } 73 | let lambert = max(dot(normal, normalize(L)), 0.0); 74 | result += vec3( 75 | lambert * pow(1.0 - distance / lightsBuffer.lights[i].radius, 2.0) * lightsBuffer.lights[i].color * albedo 76 | ); 77 | } 78 | 79 | // some manual ambient 80 | result += vec3(0.2); 81 | 82 | return vec4(result, 1.0); 83 | } -------------------------------------------------------------------------------- /src/sysgpu/shader/test/fragmentGBuffersDebugView.wgsl: -------------------------------------------------------------------------------- 1 | 2 | @group(0) @binding(0) var gBufferNormal: texture_2d; 3 | @group(0) @binding(1) var gBufferAlbedo: texture_2d; 4 | @group(0) @binding(2) var gBufferDepth: texture_depth_2d; 5 | 6 | @group(1) @binding(0) var canvas : CanvasConstants; 7 | 8 | struct CanvasConstants { 9 | size: vec2, 10 | } 11 | 12 | @fragment 13 | fn main( 14 | @builtin(position) coord : vec4 15 | ) -> @location(0) vec4 { 16 | var result : vec4; 17 | let c = coord.xy / vec2(canvas.size.x, canvas.size.y); 18 | if (c.x < 0.33333) { 19 | let rawDepth = textureLoad( 20 | gBufferDepth, 21 | vec2(floor(coord.xy)), 22 | 0 23 | ); 24 | // remap depth into something a bit more visible 25 | let depth = (1.0 - rawDepth) * 50.0; 26 | result = vec4(depth); 27 | } else if (c.x < 0.66667) { 28 | result = textureLoad( 29 | gBufferNormal, 30 | vec2(floor(coord.xy)), 31 | 0 32 | ); 33 | result.x = (result.x + 1.0) * 0.5; 34 | result.y = (result.y + 1.0) * 0.5; 35 | result.z = (result.z + 1.0) * 0.5; 36 | } else { 37 | result = textureLoad( 38 | gBufferAlbedo, 39 | vec2(floor(coord.xy)), 40 | 0 41 | ); 42 | } 43 | return result; 44 | } -------------------------------------------------------------------------------- /src/sysgpu/shader/test/fragmentWriteGBuffers.wgsl: -------------------------------------------------------------------------------- 1 | struct GBufferOutput { 2 | @location(0) normal : vec4, 3 | 4 | // Textures: diffuse color, specular color, smoothness, emissive etc. could go here 5 | @location(1) albedo : vec4, 6 | } 7 | 8 | @fragment 9 | fn main( 10 | @location(0) fragNormal: vec3, 11 | @location(1) fragUV : vec2 12 | ) -> GBufferOutput { 13 | // faking some kind of checkerboard texture 14 | let uv = floor(30.0 * fragUV); 15 | let c = 0.2 + 0.5 * ((uv.x + uv.y) - 2.0 * floor((uv.x + uv.y) / 2.0)); 16 | 17 | var output : GBufferOutput; 18 | output.normal = vec4(fragNormal, 1.0); 19 | output.albedo = vec4(c, c, c, 1.0); 20 | 21 | return output; 22 | } -------------------------------------------------------------------------------- /src/sysgpu/shader/test/fullscreen-textured-quad.wgsl: -------------------------------------------------------------------------------- 1 | @group(0) @binding(0) var mySampler : sampler; 2 | @group(0) @binding(1) var myTexture : texture_2d; 3 | 4 | struct VertexOutput { 5 | @builtin(position) Position : vec4, 6 | @location(0) fragUV : vec2, 7 | } 8 | 9 | @vertex 10 | fn vert_main(@builtin(vertex_index) VertexIndex : u32) -> VertexOutput { 11 | var pos = array, 6>( 12 | vec2( 1.0, 1.0), 13 | vec2( 1.0, -1.0), 14 | vec2(-1.0, -1.0), 15 | vec2( 1.0, 1.0), 16 | vec2(-1.0, -1.0), 17 | vec2(-1.0, 1.0) 18 | ); 19 | 20 | var uv = array, 6>( 21 | vec2(1.0, 0.0), 22 | vec2(1.0, 1.0), 23 | vec2(0.0, 1.0), 24 | vec2(1.0, 0.0), 25 | vec2(0.0, 1.0), 26 | vec2(0.0, 0.0) 27 | ); 28 | 29 | var output : VertexOutput; 30 | output.Position = vec4(pos[VertexIndex], 0.0, 1.0); 31 | output.fragUV = uv[VertexIndex]; 32 | return output; 33 | } 34 | 35 | @fragment 36 | fn frag_main(@location(0) fragUV : vec2) -> @location(0) vec4 { 37 | return textureSample(myTexture, mySampler, fragUV); 38 | } -------------------------------------------------------------------------------- /src/sysgpu/shader/test/gen-texture-light-cube.wgsl: -------------------------------------------------------------------------------- 1 | struct CameraUniform { 2 | pos: vec4, 3 | view_proj: mat4x4, 4 | }; 5 | 6 | struct InstanceInput { 7 | @location(3) model_matrix_0: vec4, 8 | @location(4) model_matrix_1: vec4, 9 | @location(5) model_matrix_2: vec4, 10 | @location(6) model_matrix_3: vec4, 11 | }; 12 | 13 | struct VertexInput { 14 | @location(0) position: vec3, 15 | @location(1) normal: vec3, 16 | @location(2) tex_coords: vec2, 17 | }; 18 | 19 | struct VertexOutput { 20 | @builtin(position) clip_position: vec4, 21 | @location(0) tex_coords: vec2, 22 | @location(1) normal: vec3, 23 | @location(2) position: vec3, 24 | }; 25 | 26 | struct Light { 27 | position: vec4, 28 | color: vec4, 29 | }; 30 | 31 | @group(0) @binding(0) var camera: CameraUniform; 32 | @group(1) @binding(0) var t_diffuse: texture_2d; 33 | @group(1) @binding(1) var s_diffuse: sampler; 34 | @group(2) @binding(0) var light: Light; 35 | 36 | @vertex 37 | fn vs_main(model: VertexInput, instance: InstanceInput) -> VertexOutput { 38 | let model_matrix = mat4x4( 39 | instance.model_matrix_0, 40 | instance.model_matrix_1, 41 | instance.model_matrix_2, 42 | instance.model_matrix_3, 43 | ); 44 | var out: VertexOutput; 45 | let world_pos = model_matrix * vec4(model.position, 1.0); 46 | out.position = world_pos.xyz; 47 | out.normal = (model_matrix * vec4(model.normal, 0.0)).xyz; 48 | out.clip_position = camera.view_proj * world_pos; 49 | out.tex_coords = model.tex_coords; 50 | return out; 51 | } 52 | 53 | @fragment 54 | fn fs_main(in: VertexOutput) -> @location(0) vec4 { 55 | let object_color = textureSample(t_diffuse, s_diffuse, in.tex_coords); 56 | 57 | let ambient = 0.1; 58 | let ambient_color = light.color.rbg * ambient; 59 | 60 | let light_dir = normalize(light.position.xyz - in.position); 61 | let diffuse = max(dot(in.normal, light_dir), 0.0); 62 | let diffuse_color = light.color.rgb * diffuse; 63 | 64 | let view_dir = normalize(camera.pos.xyz - in.position); 65 | let half_dir = normalize(view_dir + light_dir); 66 | let specular = pow(max(dot(in.normal, half_dir), 0.0), 32.0); 67 | let specular_color = light.color.rbg * specular; 68 | 69 | let all = ambient_color + diffuse_color + specular_color; 70 | 71 | let result = all * object_color.rgb; 72 | 73 | return vec4(result, object_color.a); 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/sysgpu/shader/test/gen-texture-light.wgsl: -------------------------------------------------------------------------------- 1 | struct CameraUniform { 2 | view_pos: vec4, 3 | view_proj: mat4x4, 4 | }; 5 | 6 | struct VertexInput { 7 | @location(0) position: vec3, 8 | @location(1) normal: vec3, 9 | @location(2) tex_coords: vec2, 10 | }; 11 | 12 | struct VertexOutput { 13 | @builtin(position) clip_position: vec4, 14 | }; 15 | 16 | struct Light { 17 | position: vec4, 18 | color: vec4, 19 | }; 20 | 21 | @group(0) @binding(0) var camera: CameraUniform; 22 | @group(1) @binding(0) var light: Light; 23 | 24 | @vertex 25 | fn vs_main(model: VertexInput) -> VertexOutput { 26 | var out: VertexOutput; 27 | let world_pos = vec4(model.position + light.position.xyz, 1.0); 28 | out.clip_position = camera.view_proj * world_pos; 29 | return out; 30 | } 31 | 32 | @fragment 33 | fn fs_main(in: VertexOutput) -> @location(0) vec4 { 34 | return vec4(1.0, 1.0, 1.0, 0.5); 35 | } -------------------------------------------------------------------------------- /src/sysgpu/shader/test/if-else.wgsl: -------------------------------------------------------------------------------- 1 | @fragment fn fs_main() -> @location(0) vec4 { 2 | var dummy = false; 3 | if dummy { 4 | let dummy_var_1 = 0.0; 5 | return vec4(dummy_var_1, 1, 1, 1); 6 | } else if !dummy { 7 | let dummy_var_2 = 0.0; 8 | return vec4(dummy_var_2, 1, 1, 1); 9 | } 10 | return vec4(0.0, 1, 1, 1); 11 | } -------------------------------------------------------------------------------- /src/sysgpu/shader/test/image-blur.wgsl: -------------------------------------------------------------------------------- 1 | struct Params { 2 | filterDim : i32, 3 | blockDim : u32, 4 | } 5 | 6 | @group(0) @binding(0) var samp : sampler; 7 | @group(0) @binding(1) var params : Params; 8 | @group(1) @binding(1) var inputTex : texture_2d; 9 | @group(1) @binding(2) var outputTex : texture_storage_2d; 10 | 11 | struct Flip { 12 | value : u32, 13 | } 14 | @group(1) @binding(3) var flip : Flip; 15 | 16 | // This shader blurs the input texture in one direction, depending on whether 17 | // |flip.value| is 0 or 1. 18 | // It does so by running (128 / 4) threads per workgroup to load 128 19 | // texels into 4 rows of shared memory. Each thread loads a 20 | // 4 x 4 block of texels to take advantage of the texture sampling 21 | // hardware. 22 | // Then, each thread computes the blur result by averaging the adjacent texel values 23 | // in shared memory. 24 | // Because we're operating on a subset of the texture, we cannot compute all of the 25 | // results since not all of the neighbors are available in shared memory. 26 | // Specifically, with 128 x 128 tiles, we can only compute and write out 27 | // square blocks of size 128 - (filterSize - 1). We compute the number of blocks 28 | // needed in Javascript and dispatch that amount. 29 | 30 | var tile : array, 128>, 4>; 31 | 32 | @compute @workgroup_size(32, 1, 1) 33 | fn main( 34 | @builtin(workgroup_id) WorkGroupID : vec3, 35 | @builtin(local_invocation_id) LocalInvocationID : vec3, 36 | @builtin(local_invocation_index) LocalInvocationIndex : u32 37 | ) { 38 | for (var idx = LocalInvocationIndex; idx < 512u; idx+=32) 39 | { 40 | tile[idx / 128u][idx % 128u] = vec3(0.0); 41 | } 42 | workgroupBarrier(); 43 | 44 | // TODO - mixed vector arithmetic (vec2 and vec2) 45 | let filterOffset = (params.filterDim - 1) / 2; 46 | let dims = textureDimensions(inputTex, 0); 47 | let baseIndex = vec2(WorkGroupID.xy * vec2(params.blockDim, 4) + 48 | LocalInvocationID.xy * vec2(4, 1)) 49 | - vec2(u32(filterOffset), 0); 50 | 51 | for (var r: u32 = 0; r < 4; r++) { 52 | for (var c: u32 = 0; c < 4; c++) { 53 | var loadIndex = baseIndex + vec2(c, r); 54 | if (flip.value != 0u) { 55 | loadIndex = loadIndex.yx; 56 | } 57 | 58 | tile[r][4 * LocalInvocationID.x + c] = textureSampleLevel( 59 | inputTex, 60 | samp, 61 | (vec2(loadIndex) + vec2(0.25, 0.25)) / vec2(dims), 62 | 0.0 63 | ).rgb; 64 | } 65 | } 66 | 67 | workgroupBarrier(); 68 | 69 | for (var r: u32 = 0; r < 4; r++) { 70 | for (var c: u32 = 0; c < 4; c++) { 71 | var writeIndex = baseIndex + vec2(c, r); 72 | if (flip.value != 0) { 73 | writeIndex = writeIndex.yx; 74 | } 75 | 76 | let center = u32(4 * LocalInvocationID.x) + c; 77 | if (center >= u32(filterOffset) && 78 | center < 128 - u32(filterOffset) && 79 | all(writeIndex < dims)) { 80 | var acc = vec3(0.0, 0.0, 0.0); 81 | for (var f = 0; f < params.filterDim; f++) { 82 | var i = i32(center) + f - filterOffset; 83 | acc = acc + (1.0 / f32(params.filterDim)) * tile[r][i]; 84 | } 85 | textureStore(outputTex, writeIndex, vec4(acc, 1.0)); 86 | } 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /src/sysgpu/shader/test/instanced-cube.wgsl: -------------------------------------------------------------------------------- 1 | @binding(0) @group(0) var ubos : array, 16>; 2 | 3 | struct VertexOutput { 4 | @builtin(position) position_clip : vec4, 5 | @location(0) fragUV : vec2, 6 | @location(1) fragPosition: vec4, 7 | }; 8 | 9 | @vertex 10 | fn vertex_main(@builtin(instance_index) instanceIdx : u32, 11 | @location(0) position : vec4, 12 | @location(1) uv : vec2) -> VertexOutput { 13 | var output : VertexOutput; 14 | output.position_clip = ubos[instanceIdx] * position; 15 | output.fragUV = uv; 16 | output.fragPosition = 0.5 * (position + vec4(1.0, 1.0, 1.0, 1.0)); 17 | return output; 18 | } 19 | 20 | @fragment fn frag_main( 21 | @location(0) fragUV: vec2, 22 | @location(1) fragPosition: vec4 23 | ) -> @location(0) vec4 { 24 | return fragPosition; 25 | } -------------------------------------------------------------------------------- /src/sysgpu/shader/test/lightUpdate.wgsl: -------------------------------------------------------------------------------- 1 | struct LightData { 2 | position : vec4, 3 | color : vec3, 4 | radius : f32, 5 | } 6 | struct LightsBuffer { 7 | lights: array, 8 | } 9 | @group(0) @binding(0) var lightsBuffer: LightsBuffer; 10 | 11 | struct Config { 12 | numLights : u32, 13 | } 14 | @group(0) @binding(1) var config: Config; 15 | 16 | struct LightExtent { 17 | min : vec4, 18 | max : vec4, 19 | } 20 | @group(0) @binding(2) var lightExtent: LightExtent; 21 | 22 | @compute @workgroup_size(64, 1, 1) 23 | fn main(@builtin(global_invocation_id) GlobalInvocationID : vec3) { 24 | var index = GlobalInvocationID.x; 25 | if (index >= config.numLights) { 26 | return; 27 | } 28 | 29 | lightsBuffer.lights[index].position.y = lightsBuffer.lights[index].position.y - 0.5 - 0.003 * (f32(index) - 64.0 * floor(f32(index) / 64.0)); 30 | 31 | if (lightsBuffer.lights[index].position.y < lightExtent.min.y) { 32 | lightsBuffer.lights[index].position.y = lightExtent.max.y; 33 | } 34 | } -------------------------------------------------------------------------------- /src/sysgpu/shader/test/map-async.wgsl: -------------------------------------------------------------------------------- 1 | @group(0) @binding(0) var output: array; 2 | 3 | @compute @workgroup_size(64, 1, 1) 4 | fn main( 5 | @builtin(global_invocation_id) 6 | global_id : vec3, 7 | 8 | @builtin(local_invocation_id) 9 | local_id : vec3, 10 | ) { 11 | if (global_id.x >= arrayLength(&output)) { 12 | return; 13 | } 14 | output[global_id.x] = 15 | f32(global_id.x) * 1000. + f32(local_id.x); 16 | } -------------------------------------------------------------------------------- /src/sysgpu/shader/test/pbr-basic.wgsl: -------------------------------------------------------------------------------- 1 | @group(0) @binding(0) var ubo : UBO; 2 | @group(0) @binding(1) var uboParams : UBOShared; 3 | @group(0) @binding(2) var material : MaterialParams; 4 | @group(0) @binding(3) var object : ObjectParams; 5 | 6 | struct VertexOut { 7 | @builtin(position) position_clip : vec4, 8 | @location(0) fragPosition : vec3, 9 | @location(1) fragNormal : vec3, 10 | } 11 | 12 | struct MaterialParams { 13 | roughness : f32, 14 | metallic : f32, 15 | r : f32, 16 | g : f32, 17 | b : f32 18 | } 19 | 20 | struct UBOShared { 21 | lights : array, 4>, 22 | } 23 | 24 | struct UBO { 25 | projection : mat4x4, 26 | model : mat4x4, 27 | view : mat4x4, 28 | camPos : vec3, 29 | } 30 | 31 | struct ObjectParams { 32 | position : vec3 33 | } 34 | 35 | @vertex fn vertex_main( 36 | @location(0) position : vec3, 37 | @location(1) normal : vec3 38 | ) -> VertexOut { 39 | var output : VertexOut; 40 | var locPos = vec4(ubo.model * vec4(position, 1.0)); 41 | output.fragPosition = locPos.xyz + object.position; 42 | output.fragNormal = mat3x3(ubo.model[0].xyz, ubo.model[1].xyz, ubo.model[2].xyz) * normal; 43 | output.position_clip = ubo.projection * ubo.view * vec4(output.fragPosition, 1.0); 44 | return output; 45 | } 46 | 47 | const PI : f32 = 3.14159265359; 48 | 49 | fn material_color() -> vec3 { 50 | return vec3(material.r, material.g, material.b); 51 | } 52 | 53 | // Normal Distribution function -------------------------------------- 54 | fn D_GGX(dotNH : f32, roughness : f32) -> f32 { 55 | var alpha : f32 = roughness * roughness; 56 | var alpha2 : f32 = alpha * alpha; 57 | var denom : f32 = dotNH * dotNH * (alpha2 - 1.0) + 1.0; 58 | return alpha2 / (PI * denom * denom); 59 | } 60 | 61 | // Geometric Shadowing function -------------------------------------- 62 | fn G_SchlicksmithGGX(dotNL : f32, dotNV : f32, roughness : f32) -> f32 { 63 | var r : f32 = roughness + 1.0; 64 | var k : f32 = (r * r) / 8.0; 65 | var GL : f32 = dotNL / (dotNL * (1.0 - k) + k); 66 | var GV : f32 = dotNV / (dotNV * (1.0 - k) + k); 67 | return GL * GV; 68 | } 69 | 70 | // Fresnel function ---------------------------------------------------- 71 | fn F_Schlick(cosTheta : f32, metallic : f32) -> vec3 { 72 | var F0 : vec3 = mix(vec3(0.04), material_color(), metallic); 73 | var F : vec3 = F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); 74 | return F; 75 | } 76 | 77 | // Specular BRDF composition -------------------------------------------- 78 | fn BRDF(L : vec3, V : vec3, N : vec3, metallic : f32, roughness : f32) -> vec3 { 79 | var H : vec3 = normalize(V + L); 80 | var dotNV : f32 = clamp(dot(N, V), 0.0, 1.0); 81 | var dotNL : f32 = clamp(dot(N, L), 0.0, 1.0); 82 | var dotLH : f32 = clamp(dot(L, H), 0.0, 1.0); 83 | var dotNH : f32 = clamp(dot(N, H), 0.0, 1.0); 84 | var lightColor = vec3(1.0); 85 | var color = vec3(0.0); 86 | if(dotNL > 0.0) { 87 | var rroughness : f32 = max(0.05, roughness); 88 | // D = Normal distribution (Distribution of the microfacets) 89 | var D : f32 = D_GGX(dotNH, roughness); 90 | // G = Geometric shadowing term (Microfacets shadowing) 91 | var G : f32 = G_SchlicksmithGGX(dotNL, dotNV, roughness); 92 | // F = Fresnel factor (Reflectance depending on angle of incidence) 93 | var F : vec3 = F_Schlick(dotNV, metallic); 94 | var spec : vec3 = (D * F * G) / (4.0 * dotNL * dotNV); 95 | color += spec * dotNL * lightColor; 96 | } 97 | return color; 98 | } 99 | 100 | // TODO - global variable declaration order 101 | @fragment fn frag_main( 102 | @location(0) position : vec3, 103 | @location(1) normal: vec3 104 | ) -> @location(0) vec4 { 105 | var N : vec3 = normalize(normal); 106 | var V : vec3 = normalize(ubo.camPos - position); 107 | var Lo = vec3(0.0); 108 | // Specular contribution 109 | for(var i: i32 = 0; i < 4; i++) { 110 | var L : vec3 = normalize(uboParams.lights[i].xyz - position); 111 | Lo += BRDF(L, V, N, material.metallic, material.roughness); 112 | } 113 | // Combine with ambient 114 | var color : vec3 = material_color() * 0.02; 115 | color += Lo; 116 | // Gamma correct 117 | color = pow(color, vec3(0.4545)); 118 | return vec4(color, 1.0); 119 | } 120 | -------------------------------------------------------------------------------- /src/sysgpu/shader/test/pixel-post-process-normal-frag.wgsl: -------------------------------------------------------------------------------- 1 | @fragment fn main( 2 | @location(0) normal: vec3, 3 | @location(1) uv: vec2, 4 | ) -> @location(0) vec4 { 5 | return vec4(normal / 2 + 0.5, 1.0); 6 | } -------------------------------------------------------------------------------- /src/sysgpu/shader/test/pixel-post-process-pixel-frag.wgsl: -------------------------------------------------------------------------------- 1 | @group(0) @binding(0) 2 | var draw_texture: texture_2d; 3 | @group(0) @binding(1) 4 | var draw_texture_sampler: sampler; 5 | 6 | @group(0) @binding(2) 7 | var depth_texture: texture_depth_2d; 8 | @group(0) @binding(3) 9 | var depth_texture_sampler: sampler; 10 | 11 | @group(0) @binding(4) 12 | var normal_texture: texture_2d; 13 | @group(0) @binding(5) 14 | var normal_texture_sampler: sampler; 15 | 16 | struct View { 17 | @location(0) width: u32, 18 | @location(1) height: u32, 19 | @location(2) pixel_size: u32, 20 | } 21 | @group(0) @binding(6) 22 | var view: View; 23 | 24 | fn sample_depth(uv: vec2, x: f32, y: f32) -> f32 { 25 | return textureSample( 26 | depth_texture, 27 | depth_texture_sampler, 28 | uv + vec2(x * f32(view.pixel_size) / f32(view.width), y * f32(view.pixel_size) / f32(view.height)) 29 | ); 30 | } 31 | 32 | fn sample_normal(uv: vec2, x: f32, y: f32) -> vec3 { 33 | return textureSample( 34 | normal_texture, 35 | normal_texture_sampler, 36 | uv + vec2(x * f32(view.pixel_size) / f32(view.width), y * f32(view.pixel_size) / f32(view.height)) 37 | ).xyz; 38 | } 39 | 40 | fn normal_indicator(uv: vec2, x: f32, y: f32) -> f32 { 41 | // TODO - integer promotion to float argument 42 | var depth_diff = sample_depth(uv, 0.0, 0.0) - sample_depth(uv, x, y); 43 | var dx = sample_normal(uv, 0.0, 0.0); 44 | var dy = sample_normal(uv, x, y); 45 | if (depth_diff > 0) { 46 | // only sample normals from closest pixel 47 | return 0; 48 | } 49 | return distance(dx, dy); 50 | } 51 | 52 | @fragment fn main( 53 | // TODO - vertex/fragment linkage 54 | @location(0) uv: vec2, 55 | @builtin(position) position: vec4 56 | ) -> @location(0) vec4 { 57 | // TODO - integer promotion to float argument 58 | var depth = sample_depth(uv, 0.0, 0.0); 59 | var depth_diff: f32 = 0; 60 | depth_diff += abs(depth - sample_depth(uv, -1.0, 0.0)); 61 | depth_diff += abs(depth - sample_depth(uv, 1.0, 0.0)); 62 | depth_diff += abs(depth - sample_depth(uv, 0.0, -1.0)); 63 | depth_diff += abs(depth - sample_depth(uv, 0.0, 1.0)); 64 | 65 | var normal_diff: f32 = 0; 66 | normal_diff += normal_indicator(uv, -1.0, 0.0); 67 | normal_diff += normal_indicator(uv, 1.0, 0.0); 68 | normal_diff += normal_indicator(uv, 0.0, -1.0); 69 | normal_diff += normal_indicator(uv, 0.0, 1.0); 70 | 71 | var color = textureSample(draw_texture, draw_texture_sampler, uv); 72 | if (depth_diff > 0.007) { // magic number from testing 73 | return color * 0.7; 74 | } 75 | // add instead of multiply so really dark pixels get brighter 76 | return color + (vec4(1) * step(0.1, normal_diff) * 0.7); 77 | } -------------------------------------------------------------------------------- /src/sysgpu/shader/test/pixel-post-process-pixel-vert.wgsl: -------------------------------------------------------------------------------- 1 | struct VertexOut { 2 | @builtin(position) position_clip: vec4, 3 | @location(0) uv: vec2 4 | } 5 | 6 | @vertex fn main( 7 | @location(0) position: vec3, 8 | @location(1) uv: vec2 9 | ) -> VertexOut { 10 | var output : VertexOut; 11 | output.position_clip = vec4(position.xy, 0.0, 1.0); 12 | output.uv = uv; 13 | return output; 14 | } -------------------------------------------------------------------------------- /src/sysgpu/shader/test/pixel-post-process.wgsl: -------------------------------------------------------------------------------- 1 | @group(0) @binding(0) var ubo: mat4x4; 2 | 3 | struct VertexOut { 4 | @builtin(position) position_clip: vec4, 5 | @location(0) normal: vec3, 6 | @location(1) uv: vec2, 7 | } 8 | 9 | @vertex fn vertex_main( 10 | @location(0) position: vec3, 11 | @location(1) normal: vec3, 12 | @location(2) uv: vec2 13 | ) -> VertexOut { 14 | var output: VertexOut; 15 | output.position_clip = vec4(position, 1) * ubo; 16 | output.normal = (vec4(normal, 0) * ubo).xyz; 17 | output.uv = uv; 18 | return output; 19 | } 20 | 21 | @fragment fn frag_main( 22 | @location(0) normal: vec3, 23 | @location(1) uv: vec2, 24 | ) -> @location(0) vec4 { 25 | var color = floor((uv * 0.5 + 0.25) * 32) / 32; 26 | return vec4(color, 1, 1); 27 | } -------------------------------------------------------------------------------- /src/sysgpu/shader/test/procedural-primitives.wgsl: -------------------------------------------------------------------------------- 1 | struct Uniforms { 2 | mvp_matrix : mat4x4, 3 | }; 4 | 5 | @binding(0) @group(0) var ubo : Uniforms; 6 | 7 | struct VertexOutput { 8 | @builtin(position) position: vec4, 9 | @location(0) normal: vec3, 10 | }; 11 | 12 | @vertex fn vertex_main( 13 | // TODO - struct input 14 | @location(0) position: vec3, 15 | @location(1) normal: vec3, 16 | ) -> VertexOutput { 17 | var out: VertexOutput; 18 | out.position = vec4(position, 1.0) * ubo.mvp_matrix; 19 | out.normal = normal; 20 | return out; 21 | } 22 | 23 | struct FragmentOutput { 24 | @location(0) pixel_color: vec4 25 | }; 26 | 27 | @fragment fn frag_main(in: VertexOutput) -> FragmentOutput { 28 | var out : FragmentOutput; 29 | 30 | out.pixel_color = vec4((in.normal + 1) / 2, 1.0); 31 | return out; 32 | } -------------------------------------------------------------------------------- /src/sysgpu/shader/test/rotating-cube.wgsl: -------------------------------------------------------------------------------- 1 | @group(0) @binding(0) var ubo : mat4x4; 2 | struct VertexOut { 3 | @builtin(position) position_clip : vec4, 4 | @location(0) fragUV : vec2, 5 | @location(1) fragPosition: vec4, 6 | } 7 | 8 | @vertex fn vertex_main( 9 | @location(0) position : vec4, 10 | @location(1) uv: vec2 11 | ) -> VertexOut { 12 | var output : VertexOut; 13 | output.position_clip = position * ubo; 14 | output.fragUV = uv; 15 | output.fragPosition = 0.5 * (position + vec4(1.0, 1.0, 1.0, 1.0)); 16 | return output; 17 | } 18 | 19 | @fragment fn frag_main( 20 | @location(0) fragUV: vec2, 21 | @location(1) fragPosition: vec4 22 | ) -> @location(0) vec4 { 23 | return fragPosition; 24 | } -------------------------------------------------------------------------------- /src/sysgpu/shader/test/sprite2d.wgsl: -------------------------------------------------------------------------------- 1 | struct Uniforms { 2 | modelViewProjectionMatrix : mat4x4, 3 | }; 4 | @binding(0) @group(0) var uniforms : Uniforms; 5 | 6 | struct VertexOutput { 7 | @builtin(position) Position : vec4, 8 | @location(0) fragUV : vec2, 9 | @location(1) spriteIndex : f32, 10 | }; 11 | 12 | struct Sprite { 13 | pos: vec2, 14 | size: vec2, 15 | world_pos: vec2, 16 | sheet_size: vec2, 17 | }; 18 | @binding(3) @group(0) var sprites: array; 19 | 20 | @vertex 21 | fn vertex_main( 22 | @builtin(vertex_index) VertexIndex : u32 23 | ) -> VertexOutput { 24 | var sprite = sprites[VertexIndex / 6]; 25 | 26 | // Calculate the vertex position 27 | var positions = array, 6>( 28 | vec2(0.0, 0.0), // bottom-left 29 | vec2(0.0, 1.0), // top-left 30 | vec2(1.0, 0.0), // bottom-right 31 | vec2(1.0, 0.0), // bottom-right 32 | vec2(0.0, 1.0), // top-left 33 | vec2(1.0, 1.0), // top-right 34 | ); 35 | var pos = positions[VertexIndex % 6]; 36 | pos.x *= sprite.size.x; 37 | pos.y *= sprite.size.y; 38 | pos.x += sprite.world_pos.x; 39 | pos.y += sprite.world_pos.y; 40 | 41 | // Calculate the UV coordinate 42 | var uvs = array, 6>( 43 | vec2(0.0, 1.0), // bottom-left 44 | vec2(0.0, 0.0), // top-left 45 | vec2(1.0, 1.0), // bottom-right 46 | vec2(1.0, 1.0), // bottom-right 47 | vec2(0.0, 0.0), // top-left 48 | vec2(1.0, 0.0), // top-right 49 | ); 50 | var uv = uvs[VertexIndex % 6]; 51 | uv.x *= sprite.size.x / sprite.sheet_size.x; 52 | uv.y *= sprite.size.y / sprite.sheet_size.y; 53 | uv.x += sprite.pos.x / sprite.sheet_size.x; 54 | uv.y += sprite.pos.y / sprite.sheet_size.y; 55 | 56 | var output : VertexOutput; 57 | output.Position = vec4(pos.x, 0.0, pos.y, 1.0) * uniforms.modelViewProjectionMatrix; 58 | output.fragUV = uv; 59 | output.spriteIndex = f32(VertexIndex / 6); 60 | return output; 61 | } 62 | 63 | @group(0) @binding(1) var spriteSampler: sampler; 64 | @group(0) @binding(2) var spriteTexture: texture_2d; 65 | 66 | @fragment 67 | fn frag_main( 68 | @location(0) fragUV: vec2, 69 | @location(1) spriteIndex: f32 70 | ) -> @location(0) vec4 { 71 | var color = textureSample(spriteTexture, spriteSampler, fragUV); 72 | if (spriteIndex == 0.0) { 73 | if (color[3] > 0.0) { 74 | color[0] = 0.3; 75 | color[1] = 0.2; 76 | color[2] = 0.5; 77 | color[3] = 1.0; 78 | } 79 | } 80 | 81 | return color; 82 | } -------------------------------------------------------------------------------- /src/sysgpu/shader/test/textured-cube.wgsl: -------------------------------------------------------------------------------- 1 | struct Uniforms { 2 | modelViewProjectionMatrix : mat4x4, 3 | }; 4 | @binding(0) @group(0) var uniforms : Uniforms; 5 | 6 | struct VertexOutput { 7 | @builtin(position) Position : vec4, 8 | @location(0) fragUV : vec2, 9 | @location(1) fragPosition: vec4, 10 | }; 11 | 12 | @vertex 13 | fn vertex_main(@location(0) position : vec4, 14 | @location(1) uv : vec2) -> VertexOutput { 15 | var output : VertexOutput; 16 | output.Position = position * uniforms.modelViewProjectionMatrix; 17 | output.fragUV = uv; 18 | output.fragPosition = 0.5 * (position + vec4(1.0, 1.0, 1.0, 1.0)); 19 | return output; 20 | } 21 | 22 | @group(0) @binding(1) var mySampler: sampler; 23 | @group(0) @binding(2) var myTexture: texture_2d; 24 | 25 | @fragment 26 | fn frag_main(@location(0) fragUV: vec2, 27 | @location(1) fragPosition: vec4) -> @location(0) vec4 { 28 | return textureSample(myTexture, mySampler, fragUV); 29 | } -------------------------------------------------------------------------------- /src/sysgpu/shader/test/triangle.wgsl: -------------------------------------------------------------------------------- 1 | @vertex fn vertex_main( 2 | @builtin(vertex_index) VertexIndex : u32 3 | ) -> @builtin(position) vec4 { 4 | var pos = array, 3>( 5 | vec2( 0.0, 0.5), 6 | vec2(-0.5, -0.5), 7 | vec2( 0.5, -0.5) 8 | ); 9 | return vec4(pos[VertexIndex], 0.0, 1.0); 10 | } 11 | 12 | @fragment fn frag_main() -> @location(0) vec4 { 13 | return vec4(1.0, 0.0, 0.0, 1.0); 14 | } -------------------------------------------------------------------------------- /src/sysgpu/shader/test/two-cubes.wgsl: -------------------------------------------------------------------------------- 1 | @group(0) @binding(0) var ubo : mat4x4; 2 | struct VertexOut { 3 | @builtin(position) position_clip : vec4, 4 | @location(0) fragUV : vec2, 5 | @location(1) fragPosition: vec4, 6 | } 7 | 8 | @vertex fn vertex_main( 9 | @location(0) position : vec4, 10 | @location(1) uv: vec2 11 | ) -> VertexOut { 12 | var output : VertexOut; 13 | output.position_clip = position * ubo; 14 | output.fragUV = uv; 15 | output.fragPosition = 0.5 * (position + vec4(1.0, 1.0, 1.0, 1.0)); 16 | return output; 17 | } 18 | 19 | @fragment fn frag_main( 20 | @location(0) fragUV: vec2, 21 | @location(1) fragPosition: vec4 22 | ) -> @location(0) vec4 { 23 | return fragPosition; 24 | } -------------------------------------------------------------------------------- /src/sysgpu/shader/test/vertexTextureQuad.wgsl: -------------------------------------------------------------------------------- 1 | @vertex 2 | fn main( 3 | @builtin(vertex_index) VertexIndex : u32 4 | ) -> @builtin(position) vec4 { 5 | const pos = array( 6 | vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0), 7 | vec2(-1.0, 1.0), vec2(1.0, -1.0), vec2(1.0, 1.0), 8 | ); 9 | 10 | return vec4(pos[VertexIndex], 0.0, 1.0); 11 | } -------------------------------------------------------------------------------- /src/sysgpu/shader/test/vertexWriteGBuffers.wgsl: -------------------------------------------------------------------------------- 1 | struct Uniforms { 2 | modelMatrix : mat4x4, 3 | normalModelMatrix : mat4x4, 4 | } 5 | struct Camera { 6 | viewProjectionMatrix : mat4x4, 7 | invViewProjectionMatrix : mat4x4, 8 | } 9 | @group(0) @binding(0) var uniforms : Uniforms; 10 | @group(0) @binding(1) var camera : Camera; 11 | 12 | struct VertexOutput { 13 | @builtin(position) Position : vec4, 14 | @location(0) fragNormal: vec3, // normal in world space 15 | @location(1) fragUV: vec2, 16 | } 17 | 18 | @vertex 19 | fn main( 20 | @location(0) position : vec3, 21 | @location(1) normal : vec3, 22 | @location(2) uv : vec2 23 | ) -> VertexOutput { 24 | var output : VertexOutput; 25 | let worldPosition = (uniforms.modelMatrix * vec4(position, 1.0)).xyz; 26 | output.Position = camera.viewProjectionMatrix * vec4(worldPosition, 1.0); 27 | output.fragNormal = normalize((uniforms.normalModelMatrix * vec4(normal, 1.0)).xyz); 28 | output.fragUV = uv; 29 | return output; 30 | } -------------------------------------------------------------------------------- /src/sysgpu/shader/wgsl.zig: -------------------------------------------------------------------------------- 1 | pub const Extensions = struct { 2 | f16: bool = false, 3 | }; 4 | -------------------------------------------------------------------------------- /src/sysgpu/sysgpu/adapter.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const testing = std.testing; 3 | const Bool32 = @import("main.zig").Bool32; 4 | const ChainedStructOut = @import("main.zig").ChainedStructOut; 5 | const Device = @import("device.zig").Device; 6 | const Instance = @import("instance.zig").Instance; 7 | const FeatureName = @import("main.zig").FeatureName; 8 | const SupportedLimits = @import("main.zig").SupportedLimits; 9 | const RequestDeviceStatus = @import("main.zig").RequestDeviceStatus; 10 | const BackendType = @import("main.zig").BackendType; 11 | const RequestDeviceCallback = @import("main.zig").RequestDeviceCallback; 12 | const PowerPreference = @import("main.zig").PowerPreference; 13 | const Impl = @import("interface.zig").Impl; 14 | 15 | pub const Adapter = opaque { 16 | pub const Type = enum(u32) { 17 | discrete_gpu, 18 | integrated_gpu, 19 | cpu, 20 | unknown, 21 | 22 | pub fn name(t: Type) []const u8 { 23 | return switch (t) { 24 | .discrete_gpu => "Discrete GPU", 25 | .integrated_gpu => "Integrated GPU", 26 | .cpu => "CPU", 27 | .unknown => "Unknown", 28 | }; 29 | } 30 | }; 31 | 32 | pub const PropertiesPowerPreference = extern struct { 33 | chain: ChainedStructOut = .{ 34 | .next = null, 35 | .s_type = .adapter_properties_power_preference, 36 | }, 37 | power_preference: PowerPreference = .undefined, 38 | }; 39 | 40 | pub const Properties = extern struct { 41 | pub const NextInChain = extern union { 42 | generic: ?*const ChainedStructOut, 43 | }; 44 | 45 | next_in_chain: NextInChain = .{ .generic = null }, 46 | vendor_id: u32, 47 | vendor_name: [*:0]const u8, 48 | architecture: [*:0]const u8, 49 | device_id: u32, 50 | name: [*:0]const u8, 51 | driver_description: [*:0]const u8, 52 | adapter_type: Type, 53 | backend_type: BackendType, 54 | compatibility_mode: Bool32 = .false, 55 | }; 56 | 57 | pub inline fn createDevice(adapter: *Adapter, descriptor: ?*const Device.Descriptor) ?*Device { 58 | return Impl.adapterCreateDevice(adapter, descriptor); 59 | } 60 | 61 | /// Call once with null to determine the array length, and again to fetch the feature list. 62 | /// 63 | /// Consider using the enumerateFeaturesOwned helper. 64 | pub inline fn enumerateFeatures(adapter: *Adapter, features: ?[*]FeatureName) usize { 65 | return Impl.adapterEnumerateFeatures(adapter, features); 66 | } 67 | 68 | /// Enumerates the adapter features, storing the result in an allocated slice which is owned by 69 | /// the caller. 70 | pub inline fn enumerateFeaturesOwned(adapter: *Adapter, allocator: std.mem.Allocator) ![]FeatureName { 71 | const count = adapter.enumerateFeatures(null); 72 | const data = try allocator.alloc(FeatureName, count); 73 | _ = adapter.enumerateFeatures(data.ptr); 74 | return data; 75 | } 76 | 77 | pub inline fn getInstance(adapter: *Adapter) *Instance { 78 | return Impl.adapterGetInstance(adapter); 79 | } 80 | 81 | pub inline fn getLimits(adapter: *Adapter, limits: *SupportedLimits) bool { 82 | return Impl.adapterGetLimits(adapter, limits); 83 | } 84 | 85 | pub inline fn getProperties(adapter: *Adapter, properties: *Adapter.Properties) void { 86 | Impl.adapterGetProperties(adapter, properties); 87 | } 88 | 89 | pub inline fn hasFeature(adapter: *Adapter, feature: FeatureName) bool { 90 | return Impl.adapterHasFeature(adapter, feature); 91 | } 92 | 93 | pub inline fn requestDevice( 94 | adapter: *Adapter, 95 | descriptor: ?*const Device.Descriptor, 96 | context: anytype, 97 | comptime callback: fn ( 98 | ctx: @TypeOf(context), 99 | status: RequestDeviceStatus, 100 | device: *Device, 101 | message: ?[*:0]const u8, 102 | ) callconv(.Inline) void, 103 | ) void { 104 | const Context = @TypeOf(context); 105 | const Helper = struct { 106 | pub fn cCallback(status: RequestDeviceStatus, device: *Device, message: ?[*:0]const u8, userdata: ?*anyopaque) callconv(.C) void { 107 | callback( 108 | if (Context == void) {} else @as(Context, @ptrCast(@alignCast(userdata))), 109 | status, 110 | device, 111 | message, 112 | ); 113 | } 114 | }; 115 | Impl.adapterRequestDevice(adapter, descriptor, Helper.cCallback, if (Context == void) null else context); 116 | } 117 | 118 | pub inline fn reference(adapter: *Adapter) void { 119 | Impl.adapterReference(adapter); 120 | } 121 | 122 | pub inline fn release(adapter: *Adapter) void { 123 | Impl.adapterRelease(adapter); 124 | } 125 | }; 126 | 127 | test "Adapter.Type name" { 128 | try testing.expectEqualStrings("Discrete GPU", Adapter.Type.discrete_gpu.name()); 129 | } 130 | -------------------------------------------------------------------------------- /src/sysgpu/sysgpu/bind_group.zig: -------------------------------------------------------------------------------- 1 | const Buffer = @import("buffer.zig").Buffer; 2 | const Sampler = @import("sampler.zig").Sampler; 3 | const TextureView = @import("texture_view.zig").TextureView; 4 | const ChainedStruct = @import("main.zig").ChainedStruct; 5 | const BindGroupLayout = @import("bind_group_layout.zig").BindGroupLayout; 6 | const ExternalTexture = @import("external_texture.zig").ExternalTexture; 7 | const Impl = @import("interface.zig").Impl; 8 | 9 | pub const BindGroup = opaque { 10 | pub const Entry = extern struct { 11 | pub const NextInChain = extern union { 12 | generic: ?*const ChainedStruct, 13 | external_texture_binding_entry: *const ExternalTexture.BindingEntry, 14 | }; 15 | 16 | next_in_chain: NextInChain = .{ .generic = null }, 17 | binding: u32, 18 | buffer: ?*Buffer = null, 19 | offset: u64 = 0, 20 | size: u64, 21 | elem_size: u32 = 0, // TEMP - using StructuredBuffer until we switch to DXC for templatized raw buffers 22 | sampler: ?*Sampler = null, 23 | texture_view: ?*TextureView = null, 24 | 25 | /// Helper to create a buffer BindGroup.Entry. 26 | pub fn initBuffer(binding: u32, buf: *Buffer, offset: u64, size: u64, elem_size: u32) Entry { 27 | return .{ 28 | .binding = binding, 29 | .buffer = buf, 30 | .offset = offset, 31 | .size = size, 32 | .elem_size = elem_size, 33 | }; 34 | } 35 | 36 | /// Helper to create a sampler BindGroup.Entry. 37 | pub fn initSampler(binding: u32, _sampler: *Sampler) Entry { 38 | return .{ 39 | .binding = binding, 40 | .sampler = _sampler, 41 | .size = 0, 42 | }; 43 | } 44 | 45 | /// Helper to create a texture view BindGroup.Entry. 46 | pub fn initTextureView(binding: u32, texture_view: *TextureView) Entry { 47 | return .{ 48 | .binding = binding, 49 | .texture_view = texture_view, 50 | .size = 0, 51 | }; 52 | } 53 | }; 54 | 55 | pub const Descriptor = extern struct { 56 | next_in_chain: ?*const ChainedStruct = null, 57 | label: ?[*:0]const u8 = null, 58 | layout: *BindGroupLayout, 59 | entry_count: usize = 0, 60 | entries: ?[*]const Entry = null, 61 | 62 | /// Provides a slightly friendlier Zig API to initialize this structure. 63 | pub inline fn init(v: struct { 64 | next_in_chain: ?*const ChainedStruct = null, 65 | label: ?[*:0]const u8 = null, 66 | layout: *BindGroupLayout, 67 | entries: ?[]const Entry = null, 68 | }) Descriptor { 69 | return .{ 70 | .next_in_chain = v.next_in_chain, 71 | .label = v.label, 72 | .layout = v.layout, 73 | .entry_count = if (v.entries) |e| e.len else 0, 74 | .entries = if (v.entries) |e| e.ptr else null, 75 | }; 76 | } 77 | }; 78 | 79 | pub inline fn setLabel(bind_group: *BindGroup, label: [*:0]const u8) void { 80 | Impl.bindGroupSetLabel(bind_group, label); 81 | } 82 | 83 | pub inline fn reference(bind_group: *BindGroup) void { 84 | Impl.bindGroupReference(bind_group); 85 | } 86 | 87 | pub inline fn release(bind_group: *BindGroup) void { 88 | Impl.bindGroupRelease(bind_group); 89 | } 90 | }; 91 | -------------------------------------------------------------------------------- /src/sysgpu/sysgpu/bind_group_layout.zig: -------------------------------------------------------------------------------- 1 | const Bool32 = @import("main.zig").Bool32; 2 | const ChainedStruct = @import("main.zig").ChainedStruct; 3 | const ShaderStageFlags = @import("main.zig").ShaderStageFlags; 4 | const Buffer = @import("buffer.zig").Buffer; 5 | const Sampler = @import("sampler.zig").Sampler; 6 | const Texture = @import("texture.zig").Texture; 7 | const TextureView = @import("texture_view.zig").TextureView; 8 | const StorageTextureBindingLayout = @import("main.zig").StorageTextureBindingLayout; 9 | const StorageTextureAccess = @import("main.zig").StorageTextureAccess; 10 | const ExternalTexture = @import("external_texture.zig").ExternalTexture; 11 | const Impl = @import("interface.zig").Impl; 12 | 13 | pub const BindGroupLayout = opaque { 14 | pub const Entry = extern struct { 15 | pub const NextInChain = extern union { 16 | generic: ?*const ChainedStruct, 17 | external_texture_binding_layout: *const ExternalTexture.BindingLayout, 18 | }; 19 | 20 | next_in_chain: NextInChain = .{ .generic = null }, 21 | binding: u32, 22 | visibility: ShaderStageFlags, 23 | buffer: Buffer.BindingLayout = .{}, 24 | sampler: Sampler.BindingLayout = .{}, 25 | texture: Texture.BindingLayout = .{}, 26 | storage_texture: StorageTextureBindingLayout = .{}, 27 | 28 | /// Helper to create a buffer BindGroupLayout.Entry. 29 | pub fn initBuffer( 30 | binding: u32, 31 | visibility: ShaderStageFlags, 32 | binding_type: Buffer.BindingType, 33 | has_dynamic_offset: bool, 34 | min_binding_size: u64, 35 | ) Entry { 36 | return .{ 37 | .binding = binding, 38 | .visibility = visibility, 39 | .buffer = .{ 40 | .type = binding_type, 41 | .has_dynamic_offset = Bool32.from(has_dynamic_offset), 42 | .min_binding_size = min_binding_size, 43 | }, 44 | }; 45 | } 46 | 47 | /// Helper to create a sampler BindGroupLayout.Entry. 48 | pub fn initSampler( 49 | binding: u32, 50 | visibility: ShaderStageFlags, 51 | binding_type: Sampler.BindingType, 52 | ) Entry { 53 | return .{ 54 | .binding = binding, 55 | .visibility = visibility, 56 | .sampler = .{ .type = binding_type }, 57 | }; 58 | } 59 | 60 | /// Helper to create a texture BindGroupLayout.Entry. 61 | pub fn initTexture( 62 | binding: u32, 63 | visibility: ShaderStageFlags, 64 | sample_type: Texture.SampleType, 65 | view_dimension: TextureView.Dimension, 66 | multisampled: bool, 67 | ) Entry { 68 | return .{ 69 | .binding = binding, 70 | .visibility = visibility, 71 | .texture = .{ 72 | .sample_type = sample_type, 73 | .view_dimension = view_dimension, 74 | .multisampled = Bool32.from(multisampled), 75 | }, 76 | }; 77 | } 78 | 79 | /// Helper to create a storage texture BindGroupLayout.Entry. 80 | pub fn initStorageTexture( 81 | binding: u32, 82 | visibility: ShaderStageFlags, 83 | access: StorageTextureAccess, 84 | format: Texture.Format, 85 | view_dimension: TextureView.Dimension, 86 | ) Entry { 87 | return .{ 88 | .binding = binding, 89 | .visibility = visibility, 90 | .storage_texture = .{ 91 | .access = access, 92 | .format = format, 93 | .view_dimension = view_dimension, 94 | }, 95 | }; 96 | } 97 | }; 98 | 99 | pub const Descriptor = extern struct { 100 | next_in_chain: ?*const ChainedStruct = null, 101 | label: ?[*:0]const u8 = null, 102 | entry_count: usize = 0, 103 | entries: ?[*]const Entry = null, 104 | 105 | /// Provides a slightly friendlier Zig API to initialize this structure. 106 | pub inline fn init(v: struct { 107 | next_in_chain: ?*const ChainedStruct = null, 108 | label: ?[*:0]const u8 = null, 109 | entries: ?[]const Entry = null, 110 | }) Descriptor { 111 | return .{ 112 | .next_in_chain = v.next_in_chain, 113 | .label = v.label, 114 | .entry_count = if (v.entries) |e| e.len else 0, 115 | .entries = if (v.entries) |e| e.ptr else null, 116 | }; 117 | } 118 | }; 119 | 120 | pub inline fn setLabel(bind_group_layout: *BindGroupLayout, label: [*:0]const u8) void { 121 | Impl.bindGroupLayoutSetLabel(bind_group_layout, label); 122 | } 123 | 124 | pub inline fn reference(bind_group_layout: *BindGroupLayout) void { 125 | Impl.bindGroupLayoutReference(bind_group_layout); 126 | } 127 | 128 | pub inline fn release(bind_group_layout: *BindGroupLayout) void { 129 | Impl.bindGroupLayoutRelease(bind_group_layout); 130 | } 131 | }; 132 | -------------------------------------------------------------------------------- /src/sysgpu/sysgpu/buffer.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const Bool32 = @import("main.zig").Bool32; 3 | const ChainedStruct = @import("main.zig").ChainedStruct; 4 | const MapModeFlags = @import("main.zig").MapModeFlags; 5 | const Impl = @import("interface.zig").Impl; 6 | 7 | pub const Buffer = opaque { 8 | pub const MapCallback = *const fn (status: MapAsyncStatus, userdata: ?*anyopaque) callconv(.C) void; 9 | 10 | pub const BindingType = enum(u32) { 11 | undefined = 0x00000000, 12 | uniform = 0x00000001, 13 | storage = 0x00000002, 14 | read_only_storage = 0x00000003, 15 | }; 16 | 17 | pub const MapState = enum(u32) { 18 | unmapped = 0x00000000, 19 | pending = 0x00000001, 20 | mapped = 0x00000002, 21 | }; 22 | 23 | pub const MapAsyncStatus = enum(u32) { 24 | success = 0x00000000, 25 | validation_error = 0x00000001, 26 | unknown = 0x00000002, 27 | device_lost = 0x00000003, 28 | destroyed_before_callback = 0x00000004, 29 | unmapped_before_callback = 0x00000005, 30 | mapping_already_pending = 0x00000006, 31 | offset_out_of_range = 0x00000007, 32 | size_out_of_range = 0x00000008, 33 | }; 34 | 35 | pub const UsageFlags = packed struct(u32) { 36 | map_read: bool = false, 37 | map_write: bool = false, 38 | copy_src: bool = false, 39 | copy_dst: bool = false, 40 | index: bool = false, 41 | vertex: bool = false, 42 | uniform: bool = false, 43 | storage: bool = false, 44 | indirect: bool = false, 45 | query_resolve: bool = false, 46 | 47 | _padding: u22 = 0, 48 | 49 | comptime { 50 | std.debug.assert( 51 | @sizeOf(@This()) == @sizeOf(u32) and 52 | @bitSizeOf(@This()) == @bitSizeOf(u32), 53 | ); 54 | } 55 | 56 | pub const none = UsageFlags{}; 57 | 58 | pub fn equal(a: UsageFlags, b: UsageFlags) bool { 59 | return @as(u10, @truncate(@as(u32, @bitCast(a)))) == @as(u10, @truncate(@as(u32, @bitCast(b)))); 60 | } 61 | }; 62 | 63 | pub const BindingLayout = extern struct { 64 | next_in_chain: ?*const ChainedStruct = null, 65 | type: BindingType = .undefined, 66 | has_dynamic_offset: Bool32 = .false, 67 | min_binding_size: u64 = 0, 68 | }; 69 | 70 | pub const Descriptor = extern struct { 71 | pub const NextInChain = extern union { 72 | generic: ?*const ChainedStruct, 73 | }; 74 | 75 | next_in_chain: NextInChain = .{ .generic = null }, 76 | label: ?[*:0]const u8 = null, 77 | usage: UsageFlags, 78 | size: u64, 79 | mapped_at_creation: Bool32 = .false, 80 | }; 81 | 82 | pub inline fn destroy(buffer: *Buffer) void { 83 | Impl.bufferDestroy(buffer); 84 | } 85 | 86 | pub inline fn getMapState(buffer: *Buffer) MapState { 87 | return Impl.bufferGetMapState(buffer); 88 | } 89 | 90 | /// Default `offset_bytes`: 0 91 | /// Default `len`: `gpu.whole_map_size` / `std.math.maxint(usize)` (whole range) 92 | pub inline fn getConstMappedRange( 93 | buffer: *Buffer, 94 | comptime T: type, 95 | offset_bytes: usize, 96 | len: usize, 97 | ) ?[]const T { 98 | const size = @sizeOf(T) * len; 99 | const data = Impl.bufferGetConstMappedRange( 100 | buffer, 101 | offset_bytes, 102 | size + size % 4, 103 | ); 104 | return if (data) |d| @as([*]const T, @ptrCast(@alignCast(d)))[0..len] else null; 105 | } 106 | 107 | /// Default `offset_bytes`: 0 108 | /// Default `len`: `gpu.whole_map_size` / `std.math.maxint(usize)` (whole range) 109 | pub inline fn getMappedRange( 110 | buffer: *Buffer, 111 | comptime T: type, 112 | offset_bytes: usize, 113 | len: usize, 114 | ) ?[]T { 115 | const size = @sizeOf(T) * len; 116 | const data = Impl.bufferGetMappedRange( 117 | buffer, 118 | offset_bytes, 119 | size + size % 4, 120 | ); 121 | return if (data) |d| @as([*]T, @ptrCast(@alignCast(d)))[0..len] else null; 122 | } 123 | 124 | pub inline fn getSize(buffer: *Buffer) u64 { 125 | return Impl.bufferGetSize(buffer); 126 | } 127 | 128 | pub inline fn getUsage(buffer: *Buffer) Buffer.UsageFlags { 129 | return Impl.bufferGetUsage(buffer); 130 | } 131 | 132 | pub inline fn mapAsync( 133 | buffer: *Buffer, 134 | mode: MapModeFlags, 135 | offset: usize, 136 | size: usize, 137 | context: anytype, 138 | comptime callback: fn (ctx: @TypeOf(context), status: MapAsyncStatus) callconv(.Inline) void, 139 | ) void { 140 | const Context = @TypeOf(context); 141 | const Helper = struct { 142 | pub fn cCallback(status: MapAsyncStatus, userdata: ?*anyopaque) callconv(.C) void { 143 | callback(if (Context == void) {} else @as(Context, @ptrCast(@alignCast(userdata))), status); 144 | } 145 | }; 146 | Impl.bufferMapAsync(buffer, mode, offset, size, Helper.cCallback, if (Context == void) null else context); 147 | } 148 | 149 | pub inline fn setLabel(buffer: *Buffer, label: [*:0]const u8) void { 150 | Impl.bufferSetLabel(buffer, label); 151 | } 152 | 153 | pub inline fn unmap(buffer: *Buffer) void { 154 | Impl.bufferUnmap(buffer); 155 | } 156 | 157 | pub inline fn reference(buffer: *Buffer) void { 158 | Impl.bufferReference(buffer); 159 | } 160 | 161 | pub inline fn release(buffer: *Buffer) void { 162 | Impl.bufferRelease(buffer); 163 | } 164 | }; 165 | -------------------------------------------------------------------------------- /src/sysgpu/sysgpu/command_buffer.zig: -------------------------------------------------------------------------------- 1 | const ChainedStruct = @import("main.zig").ChainedStruct; 2 | const Impl = @import("interface.zig").Impl; 3 | 4 | pub const CommandBuffer = opaque { 5 | pub const Descriptor = extern struct { 6 | next_in_chain: ?*const ChainedStruct = null, 7 | label: ?[*:0]const u8 = null, 8 | }; 9 | 10 | pub inline fn setLabel(command_buffer: *CommandBuffer, label: [*:0]const u8) void { 11 | Impl.commandBufferSetLabel(command_buffer, label); 12 | } 13 | 14 | pub inline fn reference(command_buffer: *CommandBuffer) void { 15 | Impl.commandBufferReference(command_buffer); 16 | } 17 | 18 | pub inline fn release(command_buffer: *CommandBuffer) void { 19 | Impl.commandBufferRelease(command_buffer); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /src/sysgpu/sysgpu/command_encoder.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const ComputePassEncoder = @import("compute_pass_encoder.zig").ComputePassEncoder; 3 | const RenderPassEncoder = @import("render_pass_encoder.zig").RenderPassEncoder; 4 | const CommandBuffer = @import("command_buffer.zig").CommandBuffer; 5 | const Buffer = @import("buffer.zig").Buffer; 6 | const QuerySet = @import("query_set.zig").QuerySet; 7 | const RenderPassDescriptor = @import("main.zig").RenderPassDescriptor; 8 | const ComputePassDescriptor = @import("main.zig").ComputePassDescriptor; 9 | const ChainedStruct = @import("main.zig").ChainedStruct; 10 | const ImageCopyBuffer = @import("main.zig").ImageCopyBuffer; 11 | const ImageCopyTexture = @import("main.zig").ImageCopyTexture; 12 | const Extent3D = @import("main.zig").Extent3D; 13 | const Impl = @import("interface.zig").Impl; 14 | 15 | pub const CommandEncoder = opaque { 16 | pub const Descriptor = extern struct { 17 | pub const NextInChain = extern union { 18 | generic: ?*const ChainedStruct, 19 | }; 20 | 21 | next_in_chain: NextInChain = .{ .generic = null }, 22 | label: ?[*:0]const u8 = null, 23 | }; 24 | 25 | pub inline fn beginComputePass(command_encoder: *CommandEncoder, descriptor: ?*const ComputePassDescriptor) *ComputePassEncoder { 26 | return Impl.commandEncoderBeginComputePass(command_encoder, descriptor); 27 | } 28 | 29 | pub inline fn beginRenderPass(command_encoder: *CommandEncoder, descriptor: *const RenderPassDescriptor) *RenderPassEncoder { 30 | return Impl.commandEncoderBeginRenderPass(command_encoder, descriptor); 31 | } 32 | 33 | /// Default `offset`: 0 34 | /// Default `size`: `gpu.whole_size` 35 | pub inline fn clearBuffer(command_encoder: *CommandEncoder, buffer: *Buffer, offset: u64, size: u64) void { 36 | Impl.commandEncoderClearBuffer(command_encoder, buffer, offset, size); 37 | } 38 | 39 | pub inline fn copyBufferToBuffer(command_encoder: *CommandEncoder, source: *Buffer, source_offset: u64, destination: *Buffer, destination_offset: u64, size: u64) void { 40 | Impl.commandEncoderCopyBufferToBuffer(command_encoder, source, source_offset, destination, destination_offset, size); 41 | } 42 | 43 | pub inline fn copyBufferToTexture(command_encoder: *CommandEncoder, source: *const ImageCopyBuffer, destination: *const ImageCopyTexture, copy_size: *const Extent3D) void { 44 | Impl.commandEncoderCopyBufferToTexture(command_encoder, source, destination, copy_size); 45 | } 46 | 47 | pub inline fn copyTextureToBuffer(command_encoder: *CommandEncoder, source: *const ImageCopyTexture, destination: *const ImageCopyBuffer, copy_size: *const Extent3D) void { 48 | Impl.commandEncoderCopyTextureToBuffer(command_encoder, source, destination, copy_size); 49 | } 50 | 51 | pub inline fn copyTextureToTexture(command_encoder: *CommandEncoder, source: *const ImageCopyTexture, destination: *const ImageCopyTexture, copy_size: *const Extent3D) void { 52 | Impl.commandEncoderCopyTextureToTexture(command_encoder, source, destination, copy_size); 53 | } 54 | 55 | pub inline fn finish(command_encoder: *CommandEncoder, descriptor: ?*const CommandBuffer.Descriptor) *CommandBuffer { 56 | return Impl.commandEncoderFinish(command_encoder, descriptor); 57 | } 58 | 59 | pub inline fn injectValidationError(command_encoder: *CommandEncoder, message: [*:0]const u8) void { 60 | Impl.commandEncoderInjectValidationError(command_encoder, message); 61 | } 62 | 63 | pub inline fn insertDebugMarker(command_encoder: *CommandEncoder, marker_label: [*:0]const u8) void { 64 | Impl.commandEncoderInsertDebugMarker(command_encoder, marker_label); 65 | } 66 | 67 | pub inline fn popDebugGroup(command_encoder: *CommandEncoder) void { 68 | Impl.commandEncoderPopDebugGroup(command_encoder); 69 | } 70 | 71 | pub inline fn pushDebugGroup(command_encoder: *CommandEncoder, group_label: [*:0]const u8) void { 72 | Impl.commandEncoderPushDebugGroup(command_encoder, group_label); 73 | } 74 | 75 | pub inline fn resolveQuerySet(command_encoder: *CommandEncoder, query_set: *QuerySet, first_query: u32, query_count: u32, destination: *Buffer, destination_offset: u64) void { 76 | Impl.commandEncoderResolveQuerySet(command_encoder, query_set, first_query, query_count, destination, destination_offset); 77 | } 78 | 79 | pub inline fn setLabel(command_encoder: *CommandEncoder, label: [*:0]const u8) void { 80 | Impl.commandEncoderSetLabel(command_encoder, label); 81 | } 82 | 83 | pub inline fn writeBuffer( 84 | command_encoder: *CommandEncoder, 85 | buffer: *Buffer, 86 | buffer_offset_bytes: u64, 87 | data_slice: anytype, 88 | ) void { 89 | Impl.commandEncoderWriteBuffer( 90 | command_encoder, 91 | buffer, 92 | buffer_offset_bytes, 93 | @as([*]const u8, @ptrCast(std.mem.sliceAsBytes(data_slice).ptr)), 94 | @as(u64, @intCast(data_slice.len)) * @sizeOf(std.meta.Elem(@TypeOf(data_slice))), 95 | ); 96 | } 97 | 98 | pub inline fn writeTimestamp(command_encoder: *CommandEncoder, query_set: *QuerySet, query_index: u32) void { 99 | Impl.commandEncoderWriteTimestamp(command_encoder, query_set, query_index); 100 | } 101 | 102 | pub inline fn reference(command_encoder: *CommandEncoder) void { 103 | Impl.commandEncoderReference(command_encoder); 104 | } 105 | 106 | pub inline fn release(command_encoder: *CommandEncoder) void { 107 | Impl.commandEncoderRelease(command_encoder); 108 | } 109 | }; 110 | -------------------------------------------------------------------------------- /src/sysgpu/sysgpu/compute_pass_encoder.zig: -------------------------------------------------------------------------------- 1 | const Buffer = @import("buffer.zig").Buffer; 2 | const BindGroup = @import("bind_group.zig").BindGroup; 3 | const ComputePipeline = @import("compute_pipeline.zig").ComputePipeline; 4 | const QuerySet = @import("query_set.zig").QuerySet; 5 | const Impl = @import("interface.zig").Impl; 6 | 7 | pub const ComputePassEncoder = opaque { 8 | /// Default `workgroup_count_y`: 1 9 | /// Default `workgroup_count_z`: 1 10 | pub inline fn dispatchWorkgroups(compute_pass_encoder: *ComputePassEncoder, workgroup_count_x: u32, workgroup_count_y: u32, workgroup_count_z: u32) void { 11 | Impl.computePassEncoderDispatchWorkgroups(compute_pass_encoder, workgroup_count_x, workgroup_count_y, workgroup_count_z); 12 | } 13 | 14 | pub inline fn dispatchWorkgroupsIndirect(compute_pass_encoder: *ComputePassEncoder, indirect_buffer: *Buffer, indirect_offset: u64) void { 15 | Impl.computePassEncoderDispatchWorkgroupsIndirect(compute_pass_encoder, indirect_buffer, indirect_offset); 16 | } 17 | 18 | pub inline fn end(compute_pass_encoder: *ComputePassEncoder) void { 19 | Impl.computePassEncoderEnd(compute_pass_encoder); 20 | } 21 | 22 | pub inline fn insertDebugMarker(compute_pass_encoder: *ComputePassEncoder, marker_label: [*:0]const u8) void { 23 | Impl.computePassEncoderInsertDebugMarker(compute_pass_encoder, marker_label); 24 | } 25 | 26 | pub inline fn popDebugGroup(compute_pass_encoder: *ComputePassEncoder) void { 27 | Impl.computePassEncoderPopDebugGroup(compute_pass_encoder); 28 | } 29 | 30 | pub inline fn pushDebugGroup(compute_pass_encoder: *ComputePassEncoder, group_label: [*:0]const u8) void { 31 | Impl.computePassEncoderPushDebugGroup(compute_pass_encoder, group_label); 32 | } 33 | 34 | /// Default `dynamic_offsets`: null 35 | pub inline fn setBindGroup(compute_pass_encoder: *ComputePassEncoder, group_index: u32, group: *BindGroup, dynamic_offsets: ?[]const u32) void { 36 | Impl.computePassEncoderSetBindGroup( 37 | compute_pass_encoder, 38 | group_index, 39 | group, 40 | if (dynamic_offsets) |v| v.len else 0, 41 | if (dynamic_offsets) |v| v.ptr else null, 42 | ); 43 | } 44 | 45 | pub inline fn setLabel(compute_pass_encoder: *ComputePassEncoder, label: [*:0]const u8) void { 46 | Impl.computePassEncoderSetLabel(compute_pass_encoder, label); 47 | } 48 | 49 | pub inline fn setPipeline(compute_pass_encoder: *ComputePassEncoder, pipeline: *ComputePipeline) void { 50 | Impl.computePassEncoderSetPipeline(compute_pass_encoder, pipeline); 51 | } 52 | 53 | pub inline fn writeTimestamp(compute_pass_encoder: *ComputePassEncoder, query_set: *QuerySet, query_index: u32) void { 54 | Impl.computePassEncoderWriteTimestamp(compute_pass_encoder, query_set, query_index); 55 | } 56 | 57 | pub inline fn reference(compute_pass_encoder: *ComputePassEncoder) void { 58 | Impl.computePassEncoderReference(compute_pass_encoder); 59 | } 60 | 61 | pub inline fn release(compute_pass_encoder: *ComputePassEncoder) void { 62 | Impl.computePassEncoderRelease(compute_pass_encoder); 63 | } 64 | }; 65 | -------------------------------------------------------------------------------- /src/sysgpu/sysgpu/compute_pipeline.zig: -------------------------------------------------------------------------------- 1 | const ChainedStruct = @import("main.zig").ChainedStruct; 2 | const ProgrammableStageDescriptor = @import("main.zig").ProgrammableStageDescriptor; 3 | const PipelineLayout = @import("pipeline_layout.zig").PipelineLayout; 4 | const BindGroupLayout = @import("bind_group_layout.zig").BindGroupLayout; 5 | const Impl = @import("interface.zig").Impl; 6 | 7 | pub const ComputePipeline = opaque { 8 | pub const Descriptor = extern struct { 9 | next_in_chain: ?*const ChainedStruct = null, 10 | label: ?[*:0]const u8 = null, 11 | layout: ?*PipelineLayout = null, 12 | compute: ProgrammableStageDescriptor, 13 | }; 14 | 15 | pub inline fn getBindGroupLayout(compute_pipeline: *ComputePipeline, group_index: u32) *BindGroupLayout { 16 | return Impl.computePipelineGetBindGroupLayout(compute_pipeline, group_index); 17 | } 18 | 19 | pub inline fn setLabel(compute_pipeline: *ComputePipeline, label: [*:0]const u8) void { 20 | Impl.computePipelineSetLabel(compute_pipeline, label); 21 | } 22 | 23 | pub inline fn reference(compute_pipeline: *ComputePipeline) void { 24 | Impl.computePipelineReference(compute_pipeline); 25 | } 26 | 27 | pub inline fn release(compute_pipeline: *ComputePipeline) void { 28 | Impl.computePipelineRelease(compute_pipeline); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /src/sysgpu/sysgpu/external_texture.zig: -------------------------------------------------------------------------------- 1 | const Bool32 = @import("main.zig").Bool32; 2 | const ChainedStruct = @import("main.zig").ChainedStruct; 3 | const TextureView = @import("texture_view.zig").TextureView; 4 | const Origin2D = @import("main.zig").Origin2D; 5 | const Extent2D = @import("main.zig").Extent2D; 6 | const Impl = @import("interface.zig").Impl; 7 | 8 | pub const ExternalTexture = opaque { 9 | pub const BindingEntry = extern struct { 10 | chain: ChainedStruct = .{ .next = null, .s_type = .external_texture_binding_entry }, 11 | external_texture: *ExternalTexture, 12 | }; 13 | 14 | pub const BindingLayout = extern struct { 15 | chain: ChainedStruct = .{ .next = null, .s_type = .external_texture_binding_layout }, 16 | }; 17 | 18 | const Rotation = enum(u32) { 19 | rotate_0_degrees = 0x00000000, 20 | rotate_90_degrees = 0x00000001, 21 | rotate_180_degrees = 0x00000002, 22 | rotate_270_degrees = 0x00000003, 23 | }; 24 | 25 | pub const Descriptor = extern struct { 26 | next_in_chain: ?*const ChainedStruct = null, 27 | label: ?[*:0]const u8 = null, 28 | plane0: *TextureView, 29 | plane1: ?*TextureView = null, 30 | visible_origin: Origin2D, 31 | visible_size: Extent2D, 32 | do_yuv_to_rgb_conversion_only: Bool32 = .false, 33 | yuv_to_rgb_conversion_matrix: ?*const [12]f32 = null, 34 | src_transform_function_parameters: *const [7]f32, 35 | dst_transform_function_parameters: *const [7]f32, 36 | gamut_conversion_matrix: *const [9]f32, 37 | flip_y: Bool32, 38 | rotation: Rotation, 39 | }; 40 | 41 | pub inline fn destroy(external_texture: *ExternalTexture) void { 42 | Impl.externalTextureDestroy(external_texture); 43 | } 44 | 45 | pub inline fn setLabel(external_texture: *ExternalTexture, label: [*:0]const u8) void { 46 | Impl.externalTextureSetLabel(external_texture, label); 47 | } 48 | 49 | pub inline fn reference(external_texture: *ExternalTexture) void { 50 | Impl.externalTextureReference(external_texture); 51 | } 52 | 53 | pub inline fn release(external_texture: *ExternalTexture) void { 54 | Impl.externalTextureRelease(external_texture); 55 | } 56 | }; 57 | -------------------------------------------------------------------------------- /src/sysgpu/sysgpu/instance.zig: -------------------------------------------------------------------------------- 1 | const ChainedStruct = @import("main.zig").ChainedStruct; 2 | const RequestAdapterStatus = @import("main.zig").RequestAdapterStatus; 3 | const Surface = @import("surface.zig").Surface; 4 | const Adapter = @import("adapter.zig").Adapter; 5 | const RequestAdapterOptions = @import("main.zig").RequestAdapterOptions; 6 | const RequestAdapterCallback = @import("main.zig").RequestAdapterCallback; 7 | const Impl = @import("interface.zig").Impl; 8 | 9 | pub const Instance = opaque { 10 | pub const Descriptor = extern struct { 11 | pub const NextInChain = extern union { 12 | generic: ?*const ChainedStruct, 13 | }; 14 | 15 | next_in_chain: NextInChain = .{ .generic = null }, 16 | }; 17 | 18 | pub inline fn createSurface(instance: *Instance, descriptor: *const Surface.Descriptor) *Surface { 19 | return Impl.instanceCreateSurface(instance, descriptor); 20 | } 21 | 22 | pub inline fn processEvents(instance: *Instance) void { 23 | Impl.instanceProcessEvents(instance); 24 | } 25 | 26 | pub inline fn requestAdapter( 27 | instance: *Instance, 28 | options: ?*const RequestAdapterOptions, 29 | context: anytype, 30 | comptime callback: fn ( 31 | ctx: @TypeOf(context), 32 | status: RequestAdapterStatus, 33 | adapter: ?*Adapter, 34 | message: ?[*:0]const u8, 35 | ) callconv(.Inline) void, 36 | ) void { 37 | const Context = @TypeOf(context); 38 | const Helper = struct { 39 | pub fn cCallback( 40 | status: RequestAdapterStatus, 41 | adapter: ?*Adapter, 42 | message: ?[*:0]const u8, 43 | userdata: ?*anyopaque, 44 | ) callconv(.C) void { 45 | callback( 46 | if (Context == void) {} else @as(Context, @ptrCast(@alignCast(userdata))), 47 | status, 48 | adapter, 49 | message, 50 | ); 51 | } 52 | }; 53 | Impl.instanceRequestAdapter(instance, options, Helper.cCallback, if (Context == void) null else context); 54 | } 55 | 56 | pub inline fn reference(instance: *Instance) void { 57 | Impl.instanceReference(instance); 58 | } 59 | 60 | pub inline fn release(instance: *Instance) void { 61 | Impl.instanceRelease(instance); 62 | } 63 | }; 64 | -------------------------------------------------------------------------------- /src/sysgpu/sysgpu/pipeline_layout.zig: -------------------------------------------------------------------------------- 1 | const ChainedStruct = @import("main.zig").ChainedStruct; 2 | const BindGroupLayout = @import("bind_group_layout.zig").BindGroupLayout; 3 | const Impl = @import("interface.zig").Impl; 4 | 5 | pub const PipelineLayout = opaque { 6 | pub const Descriptor = extern struct { 7 | next_in_chain: ?*const ChainedStruct = null, 8 | label: ?[*:0]const u8 = null, 9 | bind_group_layout_count: usize = 0, 10 | bind_group_layouts: ?[*]const *BindGroupLayout = null, 11 | 12 | /// Provides a slightly friendlier Zig API to initialize this structure. 13 | pub inline fn init(v: struct { 14 | next_in_chain: ?*const ChainedStruct = null, 15 | label: ?[*:0]const u8 = null, 16 | bind_group_layouts: ?[]const *BindGroupLayout = null, 17 | }) Descriptor { 18 | return .{ 19 | .next_in_chain = v.next_in_chain, 20 | .label = v.label, 21 | .bind_group_layout_count = if (v.bind_group_layouts) |e| e.len else 0, 22 | .bind_group_layouts = if (v.bind_group_layouts) |e| e.ptr else null, 23 | }; 24 | } 25 | }; 26 | 27 | pub inline fn setLabel(pipeline_layout: *PipelineLayout, label: [*:0]const u8) void { 28 | Impl.pipelineLayoutSetLabel(pipeline_layout, label); 29 | } 30 | 31 | pub inline fn reference(pipeline_layout: *PipelineLayout) void { 32 | Impl.pipelineLayoutReference(pipeline_layout); 33 | } 34 | 35 | pub inline fn release(pipeline_layout: *PipelineLayout) void { 36 | Impl.pipelineLayoutRelease(pipeline_layout); 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /src/sysgpu/sysgpu/query_set.zig: -------------------------------------------------------------------------------- 1 | const ChainedStruct = @import("main.zig").ChainedStruct; 2 | const PipelineStatisticName = @import("main.zig").PipelineStatisticName; 3 | const QueryType = @import("main.zig").QueryType; 4 | const Impl = @import("interface.zig").Impl; 5 | 6 | pub const QuerySet = opaque { 7 | pub const Descriptor = extern struct { 8 | next_in_chain: ?*const ChainedStruct = null, 9 | label: ?[*:0]const u8 = null, 10 | type: QueryType, 11 | count: u32, 12 | pipeline_statistics: ?[*]const PipelineStatisticName = null, 13 | pipeline_statistics_count: usize = 0, 14 | 15 | /// Provides a slightly friendlier Zig API to initialize this structure. 16 | pub inline fn init(v: struct { 17 | next_in_chain: ?*const ChainedStruct = null, 18 | label: ?[*:0]const u8 = null, 19 | type: QueryType, 20 | count: u32, 21 | pipeline_statistics: ?[]const PipelineStatisticName = null, 22 | }) Descriptor { 23 | return .{ 24 | .next_in_chain = v.next_in_chain, 25 | .label = v.label, 26 | .type = v.type, 27 | .count = v.count, 28 | .pipeline_statistics_count = if (v.pipeline_statistics) |e| e.len else 0, 29 | .pipeline_statistics = if (v.pipeline_statistics) |e| e.ptr else null, 30 | }; 31 | } 32 | }; 33 | 34 | pub inline fn destroy(query_set: *QuerySet) void { 35 | Impl.querySetDestroy(query_set); 36 | } 37 | 38 | pub inline fn getCount(query_set: *QuerySet) u32 { 39 | return Impl.querySetGetCount(query_set); 40 | } 41 | 42 | pub inline fn getType(query_set: *QuerySet) QueryType { 43 | return Impl.querySetGetType(query_set); 44 | } 45 | 46 | pub inline fn setLabel(query_set: *QuerySet, label: [*:0]const u8) void { 47 | Impl.querySetSetLabel(query_set, label); 48 | } 49 | 50 | pub inline fn reference(query_set: *QuerySet) void { 51 | Impl.querySetReference(query_set); 52 | } 53 | 54 | pub inline fn release(query_set: *QuerySet) void { 55 | Impl.querySetRelease(query_set); 56 | } 57 | }; 58 | -------------------------------------------------------------------------------- /src/sysgpu/sysgpu/queue.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const CommandBuffer = @import("command_buffer.zig").CommandBuffer; 3 | const Buffer = @import("buffer.zig").Buffer; 4 | const Texture = @import("texture.zig").Texture; 5 | const ImageCopyTexture = @import("main.zig").ImageCopyTexture; 6 | const ImageCopyExternalTexture = @import("main.zig").ImageCopyExternalTexture; 7 | const ChainedStruct = @import("main.zig").ChainedStruct; 8 | const Extent3D = @import("main.zig").Extent3D; 9 | const CopyTextureForBrowserOptions = @import("main.zig").CopyTextureForBrowserOptions; 10 | const Impl = @import("interface.zig").Impl; 11 | 12 | pub const Queue = opaque { 13 | pub const WorkDoneCallback = *const fn ( 14 | status: WorkDoneStatus, 15 | userdata: ?*anyopaque, 16 | ) callconv(.C) void; 17 | 18 | pub const WorkDoneStatus = enum(u32) { 19 | success = 0x00000000, 20 | err = 0x00000001, 21 | unknown = 0x00000002, 22 | device_lost = 0x00000003, 23 | }; 24 | 25 | pub const Descriptor = extern struct { 26 | next_in_chain: ?*const ChainedStruct = null, 27 | label: ?[*:0]const u8 = null, 28 | }; 29 | 30 | pub inline fn copyExternalTextureForBrowser(queue: *Queue, source: *const ImageCopyExternalTexture, destination: *const ImageCopyTexture, copy_size: *const Extent3D, options: *const CopyTextureForBrowserOptions) void { 31 | Impl.queueCopyExternalTextureForBrowser(queue, source, destination, copy_size, options); 32 | } 33 | 34 | pub inline fn copyTextureForBrowser(queue: *Queue, source: *const ImageCopyTexture, destination: *const ImageCopyTexture, copy_size: *const Extent3D, options: *const CopyTextureForBrowserOptions) void { 35 | Impl.queueCopyTextureForBrowser(queue, source, destination, copy_size, options); 36 | } 37 | 38 | pub inline fn onSubmittedWorkDone( 39 | queue: *Queue, 40 | signal_value: u64, 41 | context: anytype, 42 | comptime callback: fn (ctx: @TypeOf(context), status: WorkDoneStatus) callconv(.Inline) void, 43 | ) void { 44 | const Context = @TypeOf(context); 45 | const Helper = struct { 46 | pub fn cCallback(status: WorkDoneStatus, userdata: ?*anyopaque) callconv(.C) void { 47 | callback(if (Context == void) {} else @as(Context, @ptrCast(@alignCast(userdata))), status); 48 | } 49 | }; 50 | Impl.queueOnSubmittedWorkDone(queue, signal_value, Helper.cCallback, if (Context == void) null else context); 51 | } 52 | 53 | pub inline fn setLabel(queue: *Queue, label: [*:0]const u8) void { 54 | Impl.queueSetLabel(queue, label); 55 | } 56 | 57 | pub inline fn submit(queue: *Queue, commands: []const *const CommandBuffer) void { 58 | Impl.queueSubmit(queue, commands.len, commands.ptr); 59 | } 60 | 61 | pub inline fn writeBuffer( 62 | queue: *Queue, 63 | buffer: *Buffer, 64 | buffer_offset_bytes: u64, 65 | data_slice: anytype, 66 | ) void { 67 | Impl.queueWriteBuffer( 68 | queue, 69 | buffer, 70 | buffer_offset_bytes, 71 | @as(*const anyopaque, @ptrCast(std.mem.sliceAsBytes(data_slice).ptr)), 72 | data_slice.len * @sizeOf(std.meta.Elem(@TypeOf(data_slice))), 73 | ); 74 | } 75 | 76 | pub inline fn writeTexture( 77 | queue: *Queue, 78 | destination: *const ImageCopyTexture, 79 | data_layout: *const Texture.DataLayout, 80 | write_size: *const Extent3D, 81 | data_slice: anytype, 82 | ) void { 83 | Impl.queueWriteTexture( 84 | queue, 85 | destination, 86 | @as(*const anyopaque, @ptrCast(std.mem.sliceAsBytes(data_slice).ptr)), 87 | @as(usize, @intCast(data_slice.len)) * @sizeOf(std.meta.Elem(@TypeOf(data_slice))), 88 | data_layout, 89 | write_size, 90 | ); 91 | } 92 | 93 | pub inline fn reference(queue: *Queue) void { 94 | Impl.queueReference(queue); 95 | } 96 | 97 | pub inline fn release(queue: *Queue) void { 98 | Impl.queueRelease(queue); 99 | } 100 | }; 101 | -------------------------------------------------------------------------------- /src/sysgpu/sysgpu/render_bundle.zig: -------------------------------------------------------------------------------- 1 | const ChainedStruct = @import("main.zig").ChainedStruct; 2 | const Impl = @import("interface.zig").Impl; 3 | 4 | pub const RenderBundle = opaque { 5 | pub const Descriptor = extern struct { 6 | next_in_chain: ?*const ChainedStruct = null, 7 | label: ?[*:0]const u8 = null, 8 | }; 9 | 10 | pub inline fn setLabel(render_bundle: *RenderBundle, label: [*:0]const u8) void { 11 | Impl.renderBundleSetLabel(render_bundle, label); 12 | } 13 | 14 | pub inline fn reference(render_bundle: *RenderBundle) void { 15 | Impl.renderBundleReference(render_bundle); 16 | } 17 | 18 | pub inline fn release(render_bundle: *RenderBundle) void { 19 | Impl.renderBundleRelease(render_bundle); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /src/sysgpu/sysgpu/render_bundle_encoder.zig: -------------------------------------------------------------------------------- 1 | const Texture = @import("texture.zig").Texture; 2 | const Buffer = @import("buffer.zig").Buffer; 3 | const BindGroup = @import("bind_group.zig").BindGroup; 4 | const RenderPipeline = @import("render_pipeline.zig").RenderPipeline; 5 | const RenderBundle = @import("render_bundle.zig").RenderBundle; 6 | const Bool32 = @import("main.zig").Bool32; 7 | const ChainedStruct = @import("main.zig").ChainedStruct; 8 | const IndexFormat = @import("main.zig").IndexFormat; 9 | const Impl = @import("interface.zig").Impl; 10 | 11 | pub const RenderBundleEncoder = opaque { 12 | pub const Descriptor = extern struct { 13 | next_in_chain: ?*const ChainedStruct = null, 14 | label: ?[*:0]const u8 = null, 15 | color_formats_count: usize = 0, 16 | color_formats: ?[*]const Texture.Format = null, 17 | depth_stencil_format: Texture.Format = .undefined, 18 | sample_count: u32 = 1, 19 | depth_read_only: Bool32 = .false, 20 | stencil_read_only: Bool32 = .false, 21 | 22 | /// Provides a slightly friendlier Zig API to initialize this structure. 23 | pub inline fn init(v: struct { 24 | next_in_chain: ?*const ChainedStruct = null, 25 | label: ?[*:0]const u8 = null, 26 | color_formats: ?[]const Texture.Format = null, 27 | depth_stencil_format: Texture.Format = .undefined, 28 | sample_count: u32 = 1, 29 | depth_read_only: bool = false, 30 | stencil_read_only: bool = false, 31 | }) Descriptor { 32 | return .{ 33 | .next_in_chain = v.next_in_chain, 34 | .label = v.label, 35 | .color_formats_count = if (v.color_formats) |e| e.len else 0, 36 | .color_formats = if (v.color_formats) |e| e.ptr else null, 37 | .depth_stencil_format = v.depth_stencil_format, 38 | .sample_count = v.sample_count, 39 | .depth_read_only = Bool32.from(v.depth_read_only), 40 | .stencil_read_only = Bool32.from(v.stencil_read_only), 41 | }; 42 | } 43 | }; 44 | 45 | /// Default `instance_count`: 1 46 | /// Default `first_vertex`: 0 47 | /// Default `first_instance`: 0 48 | pub inline fn draw(render_bundle_encoder: *RenderBundleEncoder, vertex_count: u32, instance_count: u32, first_vertex: u32, first_instance: u32) void { 49 | Impl.renderBundleEncoderDraw(render_bundle_encoder, vertex_count, instance_count, first_vertex, first_instance); 50 | } 51 | 52 | /// Default `instance_count`: 1 53 | /// Default `first_index`: 0 54 | /// Default `base_vertex`: 0 55 | /// Default `first_instance`: 0 56 | pub inline fn drawIndexed(render_bundle_encoder: *RenderBundleEncoder, index_count: u32, instance_count: u32, first_index: u32, base_vertex: i32, first_instance: u32) void { 57 | Impl.renderBundleEncoderDrawIndexed(render_bundle_encoder, index_count, instance_count, first_index, base_vertex, first_instance); 58 | } 59 | 60 | pub inline fn drawIndexedIndirect(render_bundle_encoder: *RenderBundleEncoder, indirect_buffer: *Buffer, indirect_offset: u64) void { 61 | Impl.renderBundleEncoderDrawIndexedIndirect(render_bundle_encoder, indirect_buffer, indirect_offset); 62 | } 63 | 64 | pub inline fn drawIndirect(render_bundle_encoder: *RenderBundleEncoder, indirect_buffer: *Buffer, indirect_offset: u64) void { 65 | Impl.renderBundleEncoderDrawIndirect(render_bundle_encoder, indirect_buffer, indirect_offset); 66 | } 67 | 68 | pub inline fn finish(render_bundle_encoder: *RenderBundleEncoder, descriptor: ?*const RenderBundle.Descriptor) *RenderBundle { 69 | return Impl.renderBundleEncoderFinish(render_bundle_encoder, descriptor); 70 | } 71 | 72 | pub inline fn insertDebugMarker(render_bundle_encoder: *RenderBundleEncoder, marker_label: [*:0]const u8) void { 73 | Impl.renderBundleEncoderInsertDebugMarker(render_bundle_encoder, marker_label); 74 | } 75 | 76 | pub inline fn popDebugGroup(render_bundle_encoder: *RenderBundleEncoder) void { 77 | Impl.renderBundleEncoderPopDebugGroup(render_bundle_encoder); 78 | } 79 | 80 | pub inline fn pushDebugGroup(render_bundle_encoder: *RenderBundleEncoder, group_label: [*:0]const u8) void { 81 | Impl.renderBundleEncoderPushDebugGroup(render_bundle_encoder, group_label); 82 | } 83 | 84 | /// Default `dynamic_offsets`: `null` 85 | pub inline fn setBindGroup(render_bundle_encoder: *RenderBundleEncoder, group_index: u32, group: *BindGroup, dynamic_offsets: ?[]const u32) void { 86 | Impl.renderBundleEncoderSetBindGroup( 87 | render_bundle_encoder, 88 | group_index, 89 | group, 90 | if (dynamic_offsets) |v| v.len else 0, 91 | if (dynamic_offsets) |v| v.ptr else null, 92 | ); 93 | } 94 | 95 | /// Default `offset`: 0 96 | /// Default `size`: `gpu.whole_size` 97 | pub inline fn setIndexBuffer(render_bundle_encoder: *RenderBundleEncoder, buffer: *Buffer, format: IndexFormat, offset: u64, size: u64) void { 98 | Impl.renderBundleEncoderSetIndexBuffer(render_bundle_encoder, buffer, format, offset, size); 99 | } 100 | 101 | pub inline fn setLabel(render_bundle_encoder: *RenderBundleEncoder, label: [*:0]const u8) void { 102 | Impl.renderBundleEncoderSetLabel(render_bundle_encoder, label); 103 | } 104 | 105 | pub inline fn setPipeline(render_bundle_encoder: *RenderBundleEncoder, pipeline: *RenderPipeline) void { 106 | Impl.renderBundleEncoderSetPipeline(render_bundle_encoder, pipeline); 107 | } 108 | 109 | /// Default `offset`: 0 110 | /// Default `size`: `gpu.whole_size` 111 | pub inline fn setVertexBuffer(render_bundle_encoder: *RenderBundleEncoder, slot: u32, buffer: *Buffer, offset: u64, size: u64) void { 112 | Impl.renderBundleEncoderSetVertexBuffer(render_bundle_encoder, slot, buffer, offset, size); 113 | } 114 | 115 | pub inline fn reference(render_bundle_encoder: *RenderBundleEncoder) void { 116 | Impl.renderBundleEncoderReference(render_bundle_encoder); 117 | } 118 | 119 | pub inline fn release(render_bundle_encoder: *RenderBundleEncoder) void { 120 | Impl.renderBundleEncoderRelease(render_bundle_encoder); 121 | } 122 | }; 123 | -------------------------------------------------------------------------------- /src/sysgpu/sysgpu/render_pass_encoder.zig: -------------------------------------------------------------------------------- 1 | const Buffer = @import("buffer.zig").Buffer; 2 | const RenderBundle = @import("render_bundle.zig").RenderBundle; 3 | const BindGroup = @import("bind_group.zig").BindGroup; 4 | const RenderPipeline = @import("render_pipeline.zig").RenderPipeline; 5 | const QuerySet = @import("query_set.zig").QuerySet; 6 | const Color = @import("main.zig").Color; 7 | const IndexFormat = @import("main.zig").IndexFormat; 8 | const Impl = @import("interface.zig").Impl; 9 | 10 | pub const RenderPassEncoder = opaque { 11 | pub inline fn beginOcclusionQuery(render_pass_encoder: *RenderPassEncoder, query_index: u32) void { 12 | Impl.renderPassEncoderBeginOcclusionQuery(render_pass_encoder, query_index); 13 | } 14 | 15 | /// Default `instance_count`: 1 16 | /// Default `first_vertex`: 0 17 | /// Default `first_instance`: 0 18 | pub inline fn draw(render_pass_encoder: *RenderPassEncoder, vertex_count: u32, instance_count: u32, first_vertex: u32, first_instance: u32) void { 19 | Impl.renderPassEncoderDraw(render_pass_encoder, vertex_count, instance_count, first_vertex, first_instance); 20 | } 21 | 22 | /// Default `instance_count`: 1 23 | /// Default `first_index`: 0 24 | /// Default `base_vertex`: 0 25 | /// Default `first_instance`: 0 26 | pub inline fn drawIndexed(render_pass_encoder: *RenderPassEncoder, index_count: u32, instance_count: u32, first_index: u32, base_vertex: i32, first_instance: u32) void { 27 | Impl.renderPassEncoderDrawIndexed(render_pass_encoder, index_count, instance_count, first_index, base_vertex, first_instance); 28 | } 29 | 30 | pub inline fn drawIndexedIndirect(render_pass_encoder: *RenderPassEncoder, indirect_buffer: *Buffer, indirect_offset: u64) void { 31 | Impl.renderPassEncoderDrawIndexedIndirect(render_pass_encoder, indirect_buffer, indirect_offset); 32 | } 33 | 34 | pub inline fn drawIndirect(render_pass_encoder: *RenderPassEncoder, indirect_buffer: *Buffer, indirect_offset: u64) void { 35 | Impl.renderPassEncoderDrawIndirect(render_pass_encoder, indirect_buffer, indirect_offset); 36 | } 37 | 38 | pub inline fn end(render_pass_encoder: *RenderPassEncoder) void { 39 | Impl.renderPassEncoderEnd(render_pass_encoder); 40 | } 41 | 42 | pub inline fn endOcclusionQuery(render_pass_encoder: *RenderPassEncoder) void { 43 | Impl.renderPassEncoderEndOcclusionQuery(render_pass_encoder); 44 | } 45 | 46 | pub inline fn executeBundles( 47 | render_pass_encoder: *RenderPassEncoder, 48 | bundles: []*const RenderBundle, 49 | ) void { 50 | Impl.renderPassEncoderExecuteBundles( 51 | render_pass_encoder, 52 | bundles.len, 53 | bundles.ptr, 54 | ); 55 | } 56 | 57 | pub inline fn insertDebugMarker(render_pass_encoder: *RenderPassEncoder, marker_label: [*:0]const u8) void { 58 | Impl.renderPassEncoderInsertDebugMarker(render_pass_encoder, marker_label); 59 | } 60 | 61 | pub inline fn popDebugGroup(render_pass_encoder: *RenderPassEncoder) void { 62 | Impl.renderPassEncoderPopDebugGroup(render_pass_encoder); 63 | } 64 | 65 | pub inline fn pushDebugGroup(render_pass_encoder: *RenderPassEncoder, group_label: [*:0]const u8) void { 66 | Impl.renderPassEncoderPushDebugGroup(render_pass_encoder, group_label); 67 | } 68 | 69 | /// Default `dynamic_offsets_count`: 0 70 | /// Default `dynamic_offsets`: `null` 71 | pub inline fn setBindGroup(render_pass_encoder: *RenderPassEncoder, group_index: u32, group: *BindGroup, dynamic_offsets: ?[]const u32) void { 72 | Impl.renderPassEncoderSetBindGroup( 73 | render_pass_encoder, 74 | group_index, 75 | group, 76 | if (dynamic_offsets) |v| v.len else 0, 77 | if (dynamic_offsets) |v| v.ptr else null, 78 | ); 79 | } 80 | 81 | pub inline fn setBlendConstant(render_pass_encoder: *RenderPassEncoder, color: *const Color) void { 82 | Impl.renderPassEncoderSetBlendConstant(render_pass_encoder, color); 83 | } 84 | 85 | /// Default `offset`: 0 86 | /// Default `size`: `gpu.whole_size` 87 | pub inline fn setIndexBuffer(render_pass_encoder: *RenderPassEncoder, buffer: *Buffer, format: IndexFormat, offset: u64, size: u64) void { 88 | Impl.renderPassEncoderSetIndexBuffer(render_pass_encoder, buffer, format, offset, size); 89 | } 90 | 91 | pub inline fn setLabel(render_pass_encoder: *RenderPassEncoder, label: [*:0]const u8) void { 92 | Impl.renderPassEncoderSetLabel(render_pass_encoder, label); 93 | } 94 | 95 | pub inline fn setPipeline(render_pass_encoder: *RenderPassEncoder, pipeline: *RenderPipeline) void { 96 | Impl.renderPassEncoderSetPipeline(render_pass_encoder, pipeline); 97 | } 98 | 99 | pub inline fn setScissorRect(render_pass_encoder: *RenderPassEncoder, x: u32, y: u32, width: u32, height: u32) void { 100 | Impl.renderPassEncoderSetScissorRect(render_pass_encoder, x, y, width, height); 101 | } 102 | 103 | pub inline fn setStencilReference(render_pass_encoder: *RenderPassEncoder, _reference: u32) void { 104 | Impl.renderPassEncoderSetStencilReference(render_pass_encoder, _reference); 105 | } 106 | 107 | /// Default `offset`: 0 108 | /// Default `size`: `gpu.whole_size` 109 | pub inline fn setVertexBuffer(render_pass_encoder: *RenderPassEncoder, slot: u32, buffer: *Buffer, offset: u64, size: u64) void { 110 | Impl.renderPassEncoderSetVertexBuffer(render_pass_encoder, slot, buffer, offset, size); 111 | } 112 | 113 | pub inline fn setViewport(render_pass_encoder: *RenderPassEncoder, x: f32, y: f32, width: f32, height: f32, min_depth: f32, max_depth: f32) void { 114 | Impl.renderPassEncoderSetViewport(render_pass_encoder, x, y, width, height, min_depth, max_depth); 115 | } 116 | 117 | pub inline fn writeTimestamp(render_pass_encoder: *RenderPassEncoder, query_set: *QuerySet, query_index: u32) void { 118 | Impl.renderPassEncoderWriteTimestamp(render_pass_encoder, query_set, query_index); 119 | } 120 | 121 | pub inline fn reference(render_pass_encoder: *RenderPassEncoder) void { 122 | Impl.renderPassEncoderReference(render_pass_encoder); 123 | } 124 | 125 | pub inline fn release(render_pass_encoder: *RenderPassEncoder) void { 126 | Impl.renderPassEncoderRelease(render_pass_encoder); 127 | } 128 | }; 129 | -------------------------------------------------------------------------------- /src/sysgpu/sysgpu/render_pipeline.zig: -------------------------------------------------------------------------------- 1 | const ChainedStruct = @import("main.zig").ChainedStruct; 2 | const DepthStencilState = @import("main.zig").DepthStencilState; 3 | const MultisampleState = @import("main.zig").MultisampleState; 4 | const VertexState = @import("main.zig").VertexState; 5 | const PrimitiveState = @import("main.zig").PrimitiveState; 6 | const FragmentState = @import("main.zig").FragmentState; 7 | const PipelineLayout = @import("pipeline_layout.zig").PipelineLayout; 8 | const BindGroupLayout = @import("bind_group_layout.zig").BindGroupLayout; 9 | const Impl = @import("interface.zig").Impl; 10 | 11 | pub const RenderPipeline = opaque { 12 | pub const Descriptor = extern struct { 13 | next_in_chain: ?*const ChainedStruct = null, 14 | label: ?[*:0]const u8 = null, 15 | layout: ?*PipelineLayout = null, 16 | vertex: VertexState, 17 | primitive: PrimitiveState = .{}, 18 | depth_stencil: ?*const DepthStencilState = null, 19 | multisample: MultisampleState = .{}, 20 | fragment: ?*const FragmentState = null, 21 | }; 22 | 23 | pub inline fn getBindGroupLayout(render_pipeline: *RenderPipeline, group_index: u32) *BindGroupLayout { 24 | return Impl.renderPipelineGetBindGroupLayout(render_pipeline, group_index); 25 | } 26 | 27 | pub inline fn setLabel(render_pipeline: *RenderPipeline, label: [*:0]const u8) void { 28 | Impl.renderPipelineSetLabel(render_pipeline, label); 29 | } 30 | 31 | pub inline fn reference(render_pipeline: *RenderPipeline) void { 32 | Impl.renderPipelineReference(render_pipeline); 33 | } 34 | 35 | pub inline fn release(render_pipeline: *RenderPipeline) void { 36 | Impl.renderPipelineRelease(render_pipeline); 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /src/sysgpu/sysgpu/sampler.zig: -------------------------------------------------------------------------------- 1 | const ChainedStruct = @import("main.zig").ChainedStruct; 2 | const FilterMode = @import("main.zig").FilterMode; 3 | const MipmapFilterMode = @import("main.zig").MipmapFilterMode; 4 | const CompareFunction = @import("main.zig").CompareFunction; 5 | const Impl = @import("interface.zig").Impl; 6 | 7 | pub const Sampler = opaque { 8 | pub const AddressMode = enum(u32) { 9 | repeat = 0x00000000, 10 | mirror_repeat = 0x00000001, 11 | clamp_to_edge = 0x00000002, 12 | }; 13 | 14 | pub const BindingType = enum(u32) { 15 | undefined = 0x00000000, 16 | filtering = 0x00000001, 17 | non_filtering = 0x00000002, 18 | comparison = 0x00000003, 19 | }; 20 | 21 | pub const BindingLayout = extern struct { 22 | next_in_chain: ?*const ChainedStruct = null, 23 | type: BindingType = .undefined, 24 | }; 25 | 26 | pub const Descriptor = extern struct { 27 | next_in_chain: ?*const ChainedStruct = null, 28 | label: ?[*:0]const u8 = null, 29 | address_mode_u: AddressMode = .clamp_to_edge, 30 | address_mode_v: AddressMode = .clamp_to_edge, 31 | address_mode_w: AddressMode = .clamp_to_edge, 32 | mag_filter: FilterMode = .nearest, 33 | min_filter: FilterMode = .nearest, 34 | mipmap_filter: MipmapFilterMode = .nearest, 35 | lod_min_clamp: f32 = 0.0, 36 | lod_max_clamp: f32 = 32.0, 37 | compare: CompareFunction = .undefined, 38 | max_anisotropy: u16 = 1, 39 | }; 40 | 41 | pub inline fn setLabel(sampler: *Sampler, label: [*:0]const u8) void { 42 | Impl.samplerSetLabel(sampler, label); 43 | } 44 | 45 | pub inline fn reference(sampler: *Sampler) void { 46 | Impl.samplerReference(sampler); 47 | } 48 | 49 | pub inline fn release(sampler: *Sampler) void { 50 | Impl.samplerRelease(sampler); 51 | } 52 | }; 53 | -------------------------------------------------------------------------------- /src/sysgpu/sysgpu/shader_module.zig: -------------------------------------------------------------------------------- 1 | const ChainedStruct = @import("main.zig").ChainedStruct; 2 | const CompilationInfoCallback = @import("main.zig").CompilationInfoCallback; 3 | const CompilationInfoRequestStatus = @import("main.zig").CompilationInfoRequestStatus; 4 | const CompilationInfo = @import("main.zig").CompilationInfo; 5 | const Impl = @import("interface.zig").Impl; 6 | 7 | pub const ShaderModule = opaque { 8 | pub const Descriptor = extern struct { 9 | pub const NextInChain = extern union { 10 | generic: ?*const ChainedStruct, 11 | spirv_descriptor: ?*const SPIRVDescriptor, 12 | wgsl_descriptor: ?*const WGSLDescriptor, 13 | hlsl_descriptor: ?*const HLSLDescriptor, 14 | msl_descriptor: ?*const MSLDescriptor, 15 | }; 16 | 17 | next_in_chain: NextInChain = .{ .generic = null }, 18 | label: ?[*:0]const u8 = null, 19 | }; 20 | 21 | pub const SPIRVDescriptor = extern struct { 22 | chain: ChainedStruct = .{ .next = null, .s_type = .shader_module_spirv_descriptor }, 23 | code_size: u32, 24 | code: [*]const u32, 25 | }; 26 | 27 | pub const WGSLDescriptor = extern struct { 28 | chain: ChainedStruct = .{ .next = null, .s_type = .shader_module_wgsl_descriptor }, 29 | code: [*:0]const u8, 30 | }; 31 | 32 | pub const HLSLDescriptor = extern struct { 33 | chain: ChainedStruct = .{ .next = null, .s_type = .shader_module_hlsl_descriptor }, 34 | code: [*]const u8, 35 | code_size: u32, 36 | }; 37 | 38 | pub const MSLDescriptor = extern struct { 39 | chain: ChainedStruct = .{ .next = null, .s_type = .shader_module_msl_descriptor }, 40 | code: [*]const u8, 41 | code_size: u32, 42 | workgroup_size: WorkgroupSize, 43 | }; 44 | 45 | pub const WorkgroupSize = extern struct { x: u32 = 1, y: u32 = 1, z: u32 = 1 }; 46 | 47 | pub inline fn getCompilationInfo( 48 | shader_module: *ShaderModule, 49 | context: anytype, 50 | comptime callback: fn ( 51 | ctx: @TypeOf(context), 52 | status: CompilationInfoRequestStatus, 53 | compilation_info: *const CompilationInfo, 54 | ) callconv(.Inline) void, 55 | ) void { 56 | const Context = @TypeOf(context); 57 | const Helper = struct { 58 | pub fn cCallback( 59 | status: CompilationInfoRequestStatus, 60 | compilation_info: *const CompilationInfo, 61 | userdata: ?*anyopaque, 62 | ) callconv(.C) void { 63 | callback( 64 | if (Context == void) {} else @as(Context, @ptrCast(@alignCast(userdata))), 65 | status, 66 | compilation_info, 67 | ); 68 | } 69 | }; 70 | Impl.shaderModuleGetCompilationInfo(shader_module, Helper.cCallback, if (Context == void) null else context); 71 | } 72 | 73 | pub inline fn setLabel(shader_module: *ShaderModule, label: [*:0]const u8) void { 74 | Impl.shaderModuleSetLabel(shader_module, label); 75 | } 76 | 77 | pub inline fn reference(shader_module: *ShaderModule) void { 78 | Impl.shaderModuleReference(shader_module); 79 | } 80 | 81 | pub inline fn release(shader_module: *ShaderModule) void { 82 | Impl.shaderModuleRelease(shader_module); 83 | } 84 | }; 85 | -------------------------------------------------------------------------------- /src/sysgpu/sysgpu/shared_fence.zig: -------------------------------------------------------------------------------- 1 | const ChainedStruct = @import("main.zig").ChainedStruct; 2 | const ChainedStructOut = @import("main.zig").ChainedStructOut; 3 | 4 | pub const SharedFence = opaque { 5 | pub const Type = enum(u32) { 6 | shared_fence_type_undefined = 0x00000000, 7 | shared_fence_type_vk_semaphore_opaque_fd = 0x00000001, 8 | shared_fence_type_vk_semaphore_sync_fd = 0x00000002, 9 | shared_fence_type_vk_semaphore_zircon_handle = 0x00000003, 10 | shared_fence_type_dxgi_shared_handle = 0x00000004, 11 | shared_fence_type_mtl_shared_event = 0x00000005, 12 | }; 13 | 14 | pub const Descriptor = extern struct { 15 | pub const NextInChain = extern union { 16 | generic: ?*const ChainedStruct, 17 | vk_semaphore_opaque_fd_descriptor: *const VkSemaphoreOpaqueFDDescriptor, 18 | vk_semaphore_sync_fd_descriptor: *const VkSemaphoreSyncFDDescriptor, 19 | vk_semaphore_zircon_handle_descriptor: *const VkSemaphoreZirconHandleDescriptor, 20 | dxgi_shared_handle_descriptor: *const DXGISharedHandleDescriptor, 21 | mtl_shared_event_descriptor: *const MTLSharedEventDescriptor, 22 | }; 23 | 24 | next_in_chain: NextInChain = .{ .generic = null }, 25 | label: ?[*]const u8, 26 | }; 27 | 28 | pub const DXGISharedHandleDescriptor = extern struct { 29 | chain: ChainedStruct, 30 | handle: *anyopaque, 31 | }; 32 | 33 | pub const DXGISharedHandleExportInfo = extern struct { 34 | chain: ChainedStructOut, 35 | handle: *anyopaque, 36 | }; 37 | 38 | pub const ExportInfo = extern struct { 39 | pub const NextInChain = extern union { 40 | generic: ?*const ChainedStructOut, 41 | dxgi_shared_handle_export_info: *const DXGISharedHandleExportInfo, 42 | mtl_shared_event_export_info: *const MTLSharedEventExportInfo, 43 | vk_semaphore_opaque_fd_export_info: *const VkSemaphoreOpaqueFDExportInfo, 44 | vk_semaphore_sync_fd_export_info: *const VkSemaphoreSyncFDExportInfo, 45 | vk_semaphore_zircon_handle_export_info: *const VkSemaphoreZirconHandleExportInfo, 46 | }; 47 | 48 | next_in_chain: NextInChain = .{ .generic = null }, 49 | type: Type, 50 | }; 51 | 52 | pub const MTLSharedEventDescriptor = extern struct { 53 | chain: ChainedStruct, 54 | shared_event: *anyopaque, 55 | }; 56 | 57 | pub const MTLSharedEventExportInfo = extern struct { 58 | chain: ChainedStructOut, 59 | shared_event: *anyopaque, 60 | }; 61 | 62 | pub const VkSemaphoreOpaqueFDDescriptor = extern struct { 63 | chain: ChainedStruct, 64 | handle: c_int, 65 | }; 66 | 67 | pub const VkSemaphoreOpaqueFDExportInfo = extern struct { 68 | chain: ChainedStructOut, 69 | handle: c_int, 70 | }; 71 | 72 | pub const VkSemaphoreSyncFDDescriptor = extern struct { 73 | chain: ChainedStruct, 74 | handle: c_int, 75 | }; 76 | 77 | pub const VkSemaphoreSyncFDExportInfo = extern struct { 78 | chain: ChainedStructOut, 79 | handle: c_int, 80 | }; 81 | 82 | pub const VkSemaphoreZirconHandleDescriptor = extern struct { 83 | chain: ChainedStruct, 84 | handle: u32, 85 | }; 86 | 87 | pub const VkSemaphoreZirconHandleExportInfo = extern struct { 88 | chain: ChainedStructOut, 89 | handle: u32, 90 | }; 91 | }; 92 | -------------------------------------------------------------------------------- /src/sysgpu/sysgpu/shared_texture_memory.zig: -------------------------------------------------------------------------------- 1 | const Texture = @import("texture.zig").Texture; 2 | const Bool32 = @import("main.zig").Bool32; 3 | const Extent3D = @import("main.zig").Extent3D; 4 | const SharedFence = @import("shared_fence.zig").SharedFence; 5 | const ChainedStruct = @import("main.zig").ChainedStruct; 6 | const ChainedStructOut = @import("main.zig").ChainedStructOut; 7 | 8 | pub const SharedTextureMemory = opaque { 9 | pub const Properties = extern struct { 10 | next_in_chain: *const ChainedStruct, 11 | usage: Texture.UsageFlags, 12 | size: Extent3D, 13 | format: Texture.Format, 14 | }; 15 | 16 | pub const VkImageDescriptor = extern struct { 17 | chain: ChainedStruct = .{ .next = null, .s_type = .shared_texture_memory_vk_image_descriptor }, 18 | vk_format: i32, 19 | vk_usage_flags: Texture.UsageFlags, 20 | vk_extent3D: Extent3D, 21 | }; 22 | 23 | pub const AHardwareBufferDescriptor = extern struct { 24 | chain: ChainedStruct = .{ .next = null, .s_type = .shared_texture_memory_a_hardware_buffer_descriptor }, 25 | handle: *anyopaque, 26 | }; 27 | 28 | pub const BeginAccessDescriptor = extern struct { 29 | pub const NextInChain = extern union { 30 | generic: ?*const ChainedStruct, 31 | vk_image_layout_begin_state: *const VkImageLayoutBeginState, 32 | }; 33 | 34 | next_in_chain: NextInChain = .{ .generic = null }, 35 | initialized: Bool32, 36 | fence_count: usize, 37 | fences: *const SharedFence, 38 | signaled_values: *const u64, 39 | }; 40 | 41 | pub const Descriptor = extern struct { 42 | pub const NextInChain = extern union { 43 | generic: ?*const ChainedStruct, 44 | a_hardware_buffer_descriptor: *const AHardwareBufferDescriptor, 45 | dma_buf_descriptor: *const DmaBufDescriptor, 46 | dxgi_shared_handle_descriptor: *const DXGISharedHandleDescriptor, 47 | egl_image_descriptor: *const EGLImageDescriptor, 48 | io_surface_descriptor: *const IOSurfaceDescriptor, 49 | opaque_fd_descriptor: *const OpaqueFDDescriptor, 50 | vk_dedicated_allocation_descriptor: *const VkDedicatedAllocationDescriptor, 51 | zircon_handle_descriptor: *const ZirconHandleDescriptor, 52 | }; 53 | 54 | next_in_chain: NextInChain = .{ .generic = null }, 55 | label: ?[*]const u8, 56 | }; 57 | 58 | pub const DmaBufDescriptor = extern struct { 59 | chain: ChainedStruct = .{ .next = null, .s_type = .shared_texture_memory_dma_buf_descriptor }, 60 | memory_fd: c_int, 61 | allocation_size: u64, 62 | drm_modifier: u64, 63 | plane_count: usize, 64 | plane_offsets: *const u64, 65 | plane_strides: *const u32, 66 | }; 67 | 68 | pub const DXGISharedHandleDescriptor = extern struct { 69 | chain: ChainedStruct = .{ .next = null, .s_type = .shared_texture_memory_dxgi_shared_handle_descriptor }, 70 | handle: *anyopaque, 71 | }; 72 | 73 | pub const EGLImageDescriptor = extern struct { 74 | chain: ChainedStruct = .{ .next = null, .s_type = .shared_texture_memory_egl_image_descriptor }, 75 | image: *anyopaque, 76 | }; 77 | 78 | pub const EndAccessState = extern struct { 79 | pub const NextInChain = extern union { 80 | generic: ?*const ChainedStruct, 81 | vk_image_layout_end_state: *const VkImageLayoutEndState, 82 | }; 83 | 84 | next_in_chain: NextInChain = .{ .generic = null }, 85 | initialized: Bool32, 86 | fence_count: usize, 87 | fences: *const SharedFence, 88 | signaled_values: *const u64, 89 | }; 90 | 91 | pub const IOSurfaceDescriptor = extern struct { 92 | chain: ChainedStruct = .{ .next = null, .s_type = .shared_texture_memory_io_surface_descriptor }, 93 | ioSurface: *anyopaque, 94 | }; 95 | 96 | pub const OpaqueFDDescriptor = extern struct { 97 | chain: ChainedStruct = .{ .next = null, .s_type = .shared_texture_memory_opaque_fd_descriptor }, 98 | memory_fd: c_int, 99 | allocation_size: u64, 100 | }; 101 | 102 | pub const VkDedicatedAllocationDescriptor = extern struct { 103 | chain: ChainedStruct = .{ .next = null, .s_type = .shared_texture_memory_vk_dedicated_allocation_descriptor }, 104 | dedicated_allocation: Bool32, 105 | }; 106 | 107 | pub const VkImageLayoutBeginState = extern struct { 108 | chain: ChainedStruct = .{ .next = null, .s_type = .shared_texture_memory_vk_image_layout_begin_state }, 109 | old_layout: i32, 110 | new_layout: i32, 111 | }; 112 | 113 | pub const VkImageLayoutEndState = extern struct { 114 | chain: ChainedStruct = .{ .next = null, .s_type = .shared_texture_memory_vk_image_layout_end_state }, 115 | old_layout: i32, 116 | new_layout: i32, 117 | }; 118 | 119 | pub const ZirconHandleDescriptor = extern struct { 120 | chain: ChainedStruct = .{ .next = null, .s_type = .shared_texture_memory_zircon_handle_descriptor }, 121 | memory_fd: u32, 122 | allocation_size: u64, 123 | }; 124 | }; 125 | -------------------------------------------------------------------------------- /src/sysgpu/sysgpu/surface.zig: -------------------------------------------------------------------------------- 1 | const ChainedStruct = @import("main.zig").ChainedStruct; 2 | const Impl = @import("interface.zig").Impl; 3 | 4 | pub const Surface = opaque { 5 | pub const Descriptor = extern struct { 6 | pub const NextInChain = extern union { 7 | generic: ?*const ChainedStruct, 8 | from_android_native_window: *const DescriptorFromAndroidNativeWindow, 9 | from_canvas_html_selector: *const DescriptorFromCanvasHTMLSelector, 10 | from_metal_layer: *const DescriptorFromMetalLayer, 11 | from_wayland_surface: *const DescriptorFromWaylandSurface, 12 | from_windows_core_window: *const DescriptorFromWindowsCoreWindow, 13 | from_windows_hwnd: *const DescriptorFromWindowsHWND, 14 | from_windows_swap_chain_panel: *const DescriptorFromWindowsSwapChainPanel, 15 | from_xlib_window: *const DescriptorFromXlibWindow, 16 | }; 17 | 18 | next_in_chain: NextInChain = .{ .generic = null }, 19 | label: ?[*:0]const u8 = null, 20 | }; 21 | 22 | pub const DescriptorFromAndroidNativeWindow = extern struct { 23 | chain: ChainedStruct = .{ .next = null, .s_type = .surface_descriptor_from_android_native_window }, 24 | window: *anyopaque, 25 | }; 26 | 27 | pub const DescriptorFromCanvasHTMLSelector = extern struct { 28 | chain: ChainedStruct = .{ .next = null, .s_type = .surface_descriptor_from_canvas_html_selector }, 29 | selector: [*:0]const u8, 30 | }; 31 | 32 | pub const DescriptorFromMetalLayer = extern struct { 33 | chain: ChainedStruct = .{ .next = null, .s_type = .surface_descriptor_from_metal_layer }, 34 | layer: *anyopaque, 35 | }; 36 | 37 | pub const DescriptorFromWaylandSurface = extern struct { 38 | chain: ChainedStruct = .{ .next = null, .s_type = .surface_descriptor_from_wayland_surface }, 39 | display: *anyopaque, 40 | surface: *anyopaque, 41 | }; 42 | 43 | pub const DescriptorFromWindowsCoreWindow = extern struct { 44 | chain: ChainedStruct = .{ .next = null, .s_type = .surface_descriptor_from_windows_core_window }, 45 | core_window: *anyopaque, 46 | }; 47 | 48 | pub const DescriptorFromWindowsHWND = extern struct { 49 | chain: ChainedStruct = .{ .next = null, .s_type = .surface_descriptor_from_windows_hwnd }, 50 | hinstance: *anyopaque, 51 | hwnd: *anyopaque, 52 | }; 53 | 54 | pub const DescriptorFromWindowsSwapChainPanel = extern struct { 55 | chain: ChainedStruct = .{ .next = null, .s_type = .surface_descriptor_from_windows_swap_chain_panel }, 56 | swap_chain_panel: *anyopaque, 57 | }; 58 | 59 | pub const DescriptorFromXlibWindow = extern struct { 60 | chain: ChainedStruct = .{ .next = null, .s_type = .surface_descriptor_from_xlib_window }, 61 | display: *anyopaque, 62 | window: u32, 63 | }; 64 | 65 | pub inline fn reference(surface: *Surface) void { 66 | Impl.surfaceReference(surface); 67 | } 68 | 69 | pub inline fn release(surface: *Surface) void { 70 | Impl.surfaceRelease(surface); 71 | } 72 | }; 73 | -------------------------------------------------------------------------------- /src/sysgpu/sysgpu/swap_chain.zig: -------------------------------------------------------------------------------- 1 | const ChainedStruct = @import("main.zig").ChainedStruct; 2 | const PresentMode = @import("main.zig").PresentMode; 3 | const Texture = @import("texture.zig").Texture; 4 | const TextureView = @import("texture_view.zig").TextureView; 5 | const Impl = @import("interface.zig").Impl; 6 | 7 | pub const SwapChain = opaque { 8 | pub const Descriptor = extern struct { 9 | next_in_chain: ?*const ChainedStruct = null, 10 | label: ?[*:0]const u8 = null, 11 | usage: Texture.UsageFlags, 12 | format: Texture.Format, 13 | width: u32, 14 | height: u32, 15 | present_mode: PresentMode, 16 | }; 17 | 18 | pub inline fn getCurrentTexture(swap_chain: *SwapChain) ?*Texture { 19 | return Impl.swapChainGetCurrentTexture(swap_chain); 20 | } 21 | 22 | pub inline fn getCurrentTextureView(swap_chain: *SwapChain) ?*TextureView { 23 | return Impl.swapChainGetCurrentTextureView(swap_chain); 24 | } 25 | 26 | pub inline fn present(swap_chain: *SwapChain) void { 27 | Impl.swapChainPresent(swap_chain); 28 | } 29 | 30 | pub inline fn reference(swap_chain: *SwapChain) void { 31 | Impl.swapChainReference(swap_chain); 32 | } 33 | 34 | pub inline fn release(swap_chain: *SwapChain) void { 35 | Impl.swapChainRelease(swap_chain); 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /src/sysgpu/sysgpu/texture_view.zig: -------------------------------------------------------------------------------- 1 | const ChainedStruct = @import("main.zig").ChainedStruct; 2 | const Texture = @import("texture.zig").Texture; 3 | const Impl = @import("interface.zig").Impl; 4 | const types = @import("main.zig"); 5 | 6 | pub const TextureView = opaque { 7 | pub const Dimension = enum(u32) { 8 | dimension_undefined = 0x00000000, 9 | dimension_1d = 0x00000001, 10 | dimension_2d = 0x00000002, 11 | dimension_2d_array = 0x00000003, 12 | dimension_cube = 0x00000004, 13 | dimension_cube_array = 0x00000005, 14 | dimension_3d = 0x00000006, 15 | }; 16 | 17 | pub const Descriptor = extern struct { 18 | next_in_chain: ?*const ChainedStruct = null, 19 | label: ?[*:0]const u8 = null, 20 | format: Texture.Format = .undefined, 21 | dimension: Dimension = .dimension_undefined, 22 | base_mip_level: u32 = 0, 23 | mip_level_count: u32 = types.mip_level_count_undefined, 24 | base_array_layer: u32 = 0, 25 | array_layer_count: u32 = types.array_layer_count_undefined, 26 | aspect: Texture.Aspect = .all, 27 | }; 28 | 29 | pub inline fn setLabel(texture_view: *TextureView, label: [*:0]const u8) void { 30 | Impl.textureViewSetLabel(texture_view, label); 31 | } 32 | 33 | pub inline fn reference(texture_view: *TextureView) void { 34 | Impl.textureViewReference(texture_view); 35 | } 36 | 37 | pub inline fn release(texture_view: *TextureView) void { 38 | Impl.textureViewRelease(texture_view); 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /src/sysgpu/tools/spirv/grammar.zig: -------------------------------------------------------------------------------- 1 | //! Borrowed from Zig compiler codebase with changes. 2 | //! Licensed under LICENSE-ZIG 3 | //! 4 | //! See https://www.khronos.org/registry/spir-v/specs/unified1/MachineReadableGrammar.html 5 | //! and the files in https://github.com/KhronosGroup/SPIRV-Headers/blob/master/include/spirv/unified1/ 6 | //! Note: Non-canonical casing in these structs used to match SPIR-V spec json. 7 | 8 | const std = @import("std"); 9 | 10 | pub const Registry = union(enum) { 11 | core: CoreRegistry, 12 | extension: ExtensionRegistry, 13 | }; 14 | 15 | pub const CoreRegistry = struct { 16 | copyright: [][]const u8, 17 | /// Hexadecimal representation of the magic number 18 | magic_number: []const u8, 19 | major_version: u32, 20 | minor_version: u32, 21 | revision: u32, 22 | instruction_printing_class: []InstructionPrintingClass, 23 | instructions: []Instruction, 24 | operand_kinds: []OperandKind, 25 | }; 26 | 27 | pub const ExtensionRegistry = struct { 28 | copyright: [][]const u8, 29 | version: u32, 30 | revision: u32, 31 | instructions: []Instruction, 32 | operand_kinds: []OperandKind = &[_]OperandKind{}, 33 | }; 34 | 35 | pub const InstructionPrintingClass = struct { 36 | tag: []const u8, 37 | heading: ?[]const u8 = null, 38 | }; 39 | 40 | pub const Instruction = struct { 41 | opname: []const u8, 42 | class: ?[]const u8 = null, // Note: Only available in the core registry. 43 | opcode: u32, 44 | operands: []Operand = &[_]Operand{}, 45 | capabilities: [][]const u8 = &[_][]const u8{}, 46 | extensions: [][]const u8 = &[_][]const u8{}, 47 | version: ?[]const u8 = null, 48 | 49 | lastVersion: ?[]const u8 = null, 50 | }; 51 | 52 | pub const Operand = struct { 53 | kind: []const u8, 54 | /// If this field is 'null', the operand is only expected once. 55 | quantifier: ?Quantifier = null, 56 | name: []const u8 = "", 57 | }; 58 | 59 | pub const Quantifier = enum { 60 | /// zero or once 61 | @"?", 62 | /// zero or more 63 | @"*", 64 | }; 65 | 66 | pub const OperandCategory = enum { 67 | BitEnum, 68 | ValueEnum, 69 | Id, 70 | Literal, 71 | Composite, 72 | }; 73 | 74 | pub const OperandKind = struct { 75 | category: OperandCategory, 76 | /// The name 77 | kind: []const u8, 78 | doc: ?[]const u8 = null, 79 | enumerants: ?[]Enumerant = null, 80 | bases: ?[]const []const u8 = null, 81 | }; 82 | 83 | pub const Enumerant = struct { 84 | enumerant: []const u8, 85 | value: union(enum) { 86 | bitflag: []const u8, // Hexadecimal representation of the value 87 | int: u31, 88 | 89 | pub fn jsonParse( 90 | allocator: std.mem.Allocator, 91 | source: anytype, 92 | options: std.json.ParseOptions, 93 | ) std.json.ParseError(@TypeOf(source.*))!@This() { 94 | _ = options; 95 | switch (try source.nextAlloc(allocator, .alloc_if_needed)) { 96 | inline .string, .allocated_string => |s| return @This(){ .bitflag = s }, 97 | inline .number, .allocated_number => |s| return @This(){ .int = try std.fmt.parseInt(u31, s, 10) }, 98 | else => return error.UnexpectedToken, 99 | } 100 | } 101 | pub const jsonStringify = @compileError("not supported"); 102 | }, 103 | capabilities: [][]const u8 = &[_][]const u8{}, 104 | /// Valid for .ValueEnum and .BitEnum 105 | extensions: [][]const u8 = &[_][]const u8{}, 106 | /// `quantifier` will always be `null`. 107 | parameters: []Operand = &[_]Operand{}, 108 | version: ?[]const u8 = null, 109 | lastVersion: ?[]const u8 = null, 110 | }; 111 | -------------------------------------------------------------------------------- /src/sysgpu/tools/validate_spirv.sh: -------------------------------------------------------------------------------- 1 | ls zig-out/spirv/ | while read -r file 2 | do 3 | spirv-val zig-out/spirv/$file 4 | done -------------------------------------------------------------------------------- /src/sysgpu/vulkan/proc.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const builtin = @import("builtin"); 3 | const vk = @import("vulkan"); 4 | 5 | pub const BaseFunctions = vk.BaseWrapper(&.{ 6 | .{ 7 | .base_commands = .{ 8 | .createInstance = true, 9 | .enumerateInstanceExtensionProperties = true, 10 | .enumerateInstanceLayerProperties = true, 11 | .getInstanceProcAddr = true, 12 | }, 13 | }, 14 | }); 15 | 16 | pub const InstanceFunctions = vk.InstanceWrapper(&.{ 17 | .{ 18 | .instance_commands = .{ 19 | .createDevice = true, 20 | .createWaylandSurfaceKHR = builtin.target.os.tag == .linux, 21 | .createWin32SurfaceKHR = builtin.target.os.tag == .windows, 22 | .createXlibSurfaceKHR = builtin.target.os.tag == .linux, 23 | .destroyInstance = true, 24 | .destroySurfaceKHR = true, 25 | .enumerateDeviceExtensionProperties = true, 26 | .enumerateDeviceLayerProperties = true, 27 | .enumeratePhysicalDevices = true, 28 | .getDeviceProcAddr = true, 29 | .getPhysicalDeviceFeatures = true, 30 | .getPhysicalDeviceFormatProperties = true, 31 | .getPhysicalDeviceProperties = true, 32 | .getPhysicalDeviceMemoryProperties = true, 33 | .getPhysicalDeviceQueueFamilyProperties = true, 34 | .getPhysicalDeviceSurfaceCapabilitiesKHR = true, 35 | .getPhysicalDeviceSurfaceFormatsKHR = true, 36 | }, 37 | }, 38 | }); 39 | 40 | pub const DeviceFunctions = vk.DeviceWrapper(&.{ 41 | .{ 42 | .device_commands = .{ 43 | .acquireNextImageKHR = true, 44 | .allocateCommandBuffers = true, 45 | .allocateDescriptorSets = true, 46 | .allocateMemory = true, 47 | .beginCommandBuffer = true, 48 | .bindBufferMemory = true, 49 | .bindImageMemory = true, 50 | .cmdBeginRenderPass = true, 51 | .cmdBindDescriptorSets = true, 52 | .cmdBindIndexBuffer = true, 53 | .cmdBindPipeline = true, 54 | .cmdBindVertexBuffers = true, 55 | .cmdCopyBuffer = true, 56 | .cmdCopyBufferToImage = true, 57 | .cmdCopyImage = true, 58 | .cmdDispatch = true, 59 | .cmdDraw = true, 60 | .cmdDrawIndexed = true, 61 | .cmdEndRenderPass = true, 62 | .cmdPipelineBarrier = true, 63 | .cmdSetScissor = true, 64 | .cmdSetStencilReference = true, 65 | .cmdSetViewport = true, 66 | .createBuffer = true, 67 | .createCommandPool = true, 68 | .createComputePipelines = true, 69 | .createDescriptorPool = true, 70 | .createDescriptorSetLayout = true, 71 | .createFence = true, 72 | .createFramebuffer = true, 73 | .createGraphicsPipelines = true, 74 | .createImage = true, 75 | .createImageView = true, 76 | .createPipelineLayout = true, 77 | .createRenderPass = true, 78 | .createSampler = true, 79 | .createSemaphore = true, 80 | .createShaderModule = true, 81 | .createSwapchainKHR = true, 82 | .destroyBuffer = true, 83 | .destroyCommandPool = true, 84 | .destroyDescriptorPool = true, 85 | .destroyDescriptorSetLayout = true, 86 | .destroyDevice = true, 87 | .destroyFence = true, 88 | .destroyFramebuffer = true, 89 | .destroyImage = true, 90 | .destroyImageView = true, 91 | .destroyPipeline = true, 92 | .destroyPipelineLayout = true, 93 | .destroyRenderPass = true, 94 | .destroySampler = true, 95 | .destroySemaphore = true, 96 | .destroyShaderModule = true, 97 | .destroySwapchainKHR = true, 98 | .deviceWaitIdle = true, 99 | .endCommandBuffer = true, 100 | .freeCommandBuffers = true, 101 | .freeDescriptorSets = true, 102 | .freeMemory = true, 103 | .getBufferMemoryRequirements = true, 104 | .getDeviceQueue = true, 105 | .getFenceStatus = true, 106 | .getImageMemoryRequirements = true, 107 | .getSwapchainImagesKHR = true, 108 | .mapMemory = true, 109 | .queuePresentKHR = true, 110 | .queueSubmit = true, 111 | .queueWaitIdle = true, 112 | .resetCommandBuffer = true, 113 | .resetFences = true, 114 | .unmapMemory = true, 115 | .updateDescriptorSets = true, 116 | .waitForFences = true, 117 | }, 118 | }, 119 | }); 120 | 121 | pub const BaseLoader = *const fn (vk.Instance, [*:0]const u8) vk.PfnVoidFunction; 122 | 123 | pub fn loadBase(baseLoader: BaseLoader) !BaseFunctions { 124 | return BaseFunctions.load(baseLoader) catch return error.ProcLoadingFailed; 125 | } 126 | 127 | pub fn loadInstance(instance: vk.Instance, instanceLoader: vk.PfnGetInstanceProcAddr) !InstanceFunctions { 128 | return InstanceFunctions.load(instance, instanceLoader) catch return error.ProcLoadingFailed; 129 | } 130 | 131 | pub fn loadDevice(device: vk.Device, deviceLoader: vk.PfnGetDeviceProcAddr) !DeviceFunctions { 132 | return DeviceFunctions.load(device, deviceLoader) catch return error.ProcLoadingFailed; 133 | } 134 | -------------------------------------------------------------------------------- /src/time/Frequency.zig: -------------------------------------------------------------------------------- 1 | const mach = @import("../main.zig"); 2 | const Timer = @import("Timer.zig"); 3 | 4 | pub const Frequency = @This(); 5 | 6 | /// The target frequency (e.g. 60hz) or zero for unlimited 7 | target: u32 = 0, 8 | 9 | /// The estimated delay that is needed to achieve the target frequency. Updated during tick() 10 | delay_ns: u64 = 0, 11 | 12 | /// The actual measured frequency. This is updated every second. 13 | rate: u32 = 0, 14 | 15 | delta_time: ?*f32 = null, 16 | delta_time_ns: *u64 = undefined, 17 | 18 | /// Internal fields, this must be initialized via a call to start(). 19 | internal: struct { 20 | // The frame number in this second's cycle. e.g. zero to 59 21 | count: u32, 22 | timer: Timer, 23 | last_time: u64, 24 | } = undefined, 25 | 26 | /// Starts the timer used for frequency calculation. Must be called once before anything else. 27 | pub fn start(f: *Frequency) !void { 28 | f.internal = .{ 29 | .count = 0, 30 | .timer = try Timer.start(), 31 | .last_time = 0, 32 | }; 33 | } 34 | 35 | /// Tick should be called at each occurrence (e.g. frame) 36 | pub inline fn tick(f: *Frequency) void { 37 | var current_time = f.internal.timer.readPrecise(); 38 | 39 | if (f.delta_time) |delta_time| { 40 | f.delta_time_ns.* = current_time -| f.internal.last_time; 41 | delta_time.* = @as(f32, @floatFromInt(f.delta_time_ns.*)) / @as(f32, @floatFromInt(mach.time.ns_per_s)); 42 | } 43 | 44 | if (current_time >= mach.time.ns_per_s) { 45 | f.rate = f.internal.count; 46 | f.internal.count = 0; 47 | f.internal.timer.reset(); 48 | current_time -= mach.time.ns_per_s; 49 | } 50 | f.internal.last_time = current_time; 51 | f.internal.count += 1; 52 | 53 | if (f.target != 0) { 54 | const limited_count = @min(f.target, f.internal.count); 55 | const target_time_per_tick: u64 = (mach.time.ns_per_s / f.target); 56 | const target_time = target_time_per_tick * limited_count; 57 | if (current_time > target_time) { 58 | f.delay_ns = 0; 59 | } else { 60 | f.delay_ns = target_time - current_time; 61 | } 62 | } else { 63 | f.delay_ns = 0; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/time/Timer.zig: -------------------------------------------------------------------------------- 1 | const mach = @import("../main.zig"); 2 | const std = @import("std"); 3 | 4 | const Timer = @This(); 5 | 6 | // TODO: support a WASM-based timer as well, which is the primary reason this abstraction exists. 7 | 8 | timer: std.time.Timer, 9 | 10 | /// Initialize the timer. 11 | pub fn start() !Timer { 12 | return .{ .timer = try std.time.Timer.start() }; 13 | } 14 | 15 | /// Reads the timer value since start or the last reset in nanoseconds. 16 | pub inline fn readPrecise(timer: *Timer) u64 { 17 | return timer.timer.read(); 18 | } 19 | 20 | /// Reads the timer value since start or the last reset in seconds. 21 | pub inline fn read(timer: *Timer) f32 { 22 | return @as(f32, @floatFromInt(timer.readPrecise())) / @as(f32, @floatFromInt(mach.time.ns_per_s)); 23 | } 24 | 25 | /// Resets the timer value to 0/now. 26 | pub inline fn reset(timer: *Timer) void { 27 | timer.timer.reset(); 28 | } 29 | 30 | /// Returns the current value of the timer in nanoseconds, then resets it. 31 | pub inline fn lapPrecise(timer: *Timer) u64 { 32 | return timer.timer.lap(); 33 | } 34 | 35 | /// Returns the current value of the timer in seconds, then resets it. 36 | pub inline fn lap(timer: *Timer) f32 { 37 | return @as(f32, @floatFromInt(timer.lapPrecise())) / @as(f32, @floatFromInt(mach.time.ns_per_s)); 38 | } 39 | -------------------------------------------------------------------------------- /src/time/main.zig: -------------------------------------------------------------------------------- 1 | pub const std = @import("std"); 2 | 3 | pub const ns_per_s = std.time.ns_per_s; 4 | 5 | pub const Timer = @import("Timer.zig"); 6 | pub const Frequency = @import("Frequency.zig"); 7 | 8 | test { 9 | _ = Timer; 10 | _ = Frequency; 11 | } 12 | --------------------------------------------------------------------------------