├── .github ├── FUNDING.yml ├── pull_request_template.md └── workflows │ └── ci.yml ├── .gitattributes ├── src ├── shader │ ├── wgsl.zig │ ├── test │ │ ├── pixel-post-process-normal-frag.wgsl │ │ ├── vertexTextureQuad.wgsl │ │ ├── if-else.wgsl │ │ ├── pixel-post-process-pixel-vert.wgsl │ │ ├── map-async.wgsl │ │ ├── triangle.wgsl │ │ ├── boids-sprite.wgsl │ │ ├── fragmentWriteGBuffers.wgsl │ │ ├── two-cubes.wgsl │ │ ├── rotating-cube.wgsl │ │ ├── instanced-cube.wgsl │ │ ├── pixel-post-process.wgsl │ │ ├── procedural-primitives.wgsl │ │ ├── builtins.wgsl │ │ ├── gen-texture-light.wgsl │ │ ├── textured-cube.wgsl │ │ ├── vertexWriteGBuffers.wgsl │ │ ├── lightUpdate.wgsl │ │ ├── cube-map.wgsl │ │ ├── fullscreen-textured-quad.wgsl │ │ ├── fractal-cube.wgsl │ │ ├── fragmentGBuffersDebugView.wgsl │ │ ├── fragmentDeferredRendering.wgsl │ │ ├── gen-texture-light-cube.wgsl │ │ ├── sprite2d.wgsl │ │ ├── pixel-post-process-pixel-frag.wgsl │ │ ├── boids-sprite-update.wgsl │ │ ├── image-blur.wgsl │ │ └── pbr-basic.wgsl │ ├── ErrorList.zig │ ├── CodeGen.zig │ ├── codegen │ │ └── spirv │ │ │ └── Section.zig │ └── Tokenizer.zig ├── d3d12 │ ├── c.zig │ └── notes.md ├── opengl │ ├── c.zig │ └── conv.zig ├── conventions.md ├── sysgpu │ ├── render_bundle.zig │ ├── command_buffer.zig │ ├── swap_chain.zig │ ├── compute_pipeline.zig │ ├── texture_view.zig │ ├── pipeline_layout.zig │ ├── render_pipeline.zig │ ├── sampler.zig │ ├── query_set.zig │ ├── external_texture.zig │ ├── instance.zig │ ├── surface.zig │ ├── compute_pass_encoder.zig │ ├── dawn.zig │ ├── shared_fence.zig │ ├── bind_group.zig │ ├── shader_module.zig │ ├── queue.zig │ ├── adapter.zig │ ├── shared_texture_memory.zig │ ├── bind_group_layout.zig │ ├── command_encoder.zig │ ├── buffer.zig │ ├── render_bundle_encoder.zig │ ├── render_pass_encoder.zig │ └── texture.zig ├── shader.zig ├── limits.zig └── vulkan │ └── proc.zig ├── tools ├── validate_spirv.sh └── spirv │ └── grammar.zig ├── .gitignore ├── README.md ├── LICENSE ├── LICENSE-ZIG ├── LICENSE-MIT └── LICENSE-APACHE /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: slimsag 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | upstream/** linguist-vendored 3 | -------------------------------------------------------------------------------- /src/shader/wgsl.zig: -------------------------------------------------------------------------------- 1 | pub const Extensions = struct { 2 | f16: bool = false, 3 | }; 4 | -------------------------------------------------------------------------------- /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/d3d12/c.zig: -------------------------------------------------------------------------------- 1 | pub usingnamespace @cImport({ 2 | @cInclude("d3d12.h"); 3 | @cInclude("dxgi1_6.h"); 4 | @cInclude("d3dcompiler.h"); 5 | @cInclude("dxgidebug.h"); 6 | }); 7 | -------------------------------------------------------------------------------- /src/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/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 | } -------------------------------------------------------------------------------- /.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. -------------------------------------------------------------------------------- /src/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/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/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 | } -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /src/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/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 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | mach-sysgpu 5 | 6 | 7 | 8 | Highly experimental, blazingly fast, lean & mean descendant of WebGPU written in Zig 9 | 10 | ## Moved 11 | 12 | This project has moved into the Mach standard library: https://github.com/hexops/mach/tree/main/src/sysgpu 13 | -------------------------------------------------------------------------------- /src/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/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/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/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 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /src/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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 | } -------------------------------------------------------------------------------- /LICENSE-ZIG: -------------------------------------------------------------------------------- 1 | The MIT License (Expat) 2 | 3 | Copyright (c) 2015-2023, Zig contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/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/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/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/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/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/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/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/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/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 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | - push 4 | - pull_request 5 | jobs: 6 | x86_64-linux: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout 10 | uses: actions/checkout@v2 11 | - name: Setup Zig 12 | run: | 13 | sudo apt install xz-utils 14 | sudo sh -c 'wget -c https://pkg.machengine.org/zig/zig-linux-x86_64-0.12.0-dev.2063+804cee3b9.tar.xz -O - | tar -xJ --strip-components=1 -C /usr/local/bin' 15 | - name: Setup Vulkan SDK and SpirV tools 16 | run: | 17 | wget -qO- https://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo tee /etc/apt/trusted.gpg.d/lunarg.asc 18 | sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-jammy.list http://packages.lunarg.com/vulkan/lunarg-vulkan-jammy.list 19 | sudo apt update 20 | sudo apt install vulkan-sdk spirv-tools 21 | - name: build 22 | run: zig build 23 | - name: test 24 | run: zig build test 25 | - name: validate spirv output 26 | run: bash tools/validate_spirv.sh 27 | x86_64-windows: 28 | runs-on: windows-latest 29 | steps: 30 | - name: Checkout 31 | uses: actions/checkout@v2 32 | - name: Setup Zig 33 | run: | 34 | $ProgressPreference = 'SilentlyContinue' 35 | Invoke-WebRequest -Uri 'https://pkg.machengine.org/zig/zig-windows-x86_64-0.12.0-dev.2063+804cee3b9.zip' -OutFile 'C:\zig.zip' 36 | cd C:\ 37 | 7z x zig.zip 38 | Add-Content $env:GITHUB_PATH 'C:\zig-windows-x86_64-0.12.0-dev.2063+804cee3b9\' 39 | - name: build 40 | run: zig build 41 | x86_64-macos: 42 | runs-on: macos-12 43 | steps: 44 | - name: Checkout 45 | uses: actions/checkout@v2 46 | - name: Setup Zig 47 | run: | 48 | brew uninstall --ignore-dependencies libx11 # https://github.com/ziglang/zig/issues/11066 49 | brew install xz 50 | sudo sh -c 'wget -c https://pkg.machengine.org/zig/zig-macos-x86_64-0.12.0-dev.2063+804cee3b9.tar.xz -O - | tar -xJ --strip-components=1 -C /usr/local/bin' 51 | - name: build 52 | run: zig build 53 | -------------------------------------------------------------------------------- /src/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/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/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/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/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 | const dawn = @import("dawn.zig"); 9 | 10 | pub const Instance = opaque { 11 | pub const Descriptor = extern struct { 12 | pub const NextInChain = extern union { 13 | generic: ?*const ChainedStruct, 14 | dawn_toggles_descriptor: *const dawn.TogglesDescriptor, 15 | }; 16 | 17 | next_in_chain: NextInChain = .{ .generic = null }, 18 | }; 19 | 20 | pub inline fn createSurface(instance: *Instance, descriptor: *const Surface.Descriptor) *Surface { 21 | return Impl.instanceCreateSurface(instance, descriptor); 22 | } 23 | 24 | pub inline fn processEvents(instance: *Instance) void { 25 | Impl.instanceProcessEvents(instance); 26 | } 27 | 28 | pub inline fn requestAdapter( 29 | instance: *Instance, 30 | options: ?*const RequestAdapterOptions, 31 | context: anytype, 32 | comptime callback: fn ( 33 | ctx: @TypeOf(context), 34 | status: RequestAdapterStatus, 35 | adapter: ?*Adapter, 36 | message: ?[*:0]const u8, 37 | ) callconv(.Inline) void, 38 | ) void { 39 | const Context = @TypeOf(context); 40 | const Helper = struct { 41 | pub fn cCallback( 42 | status: RequestAdapterStatus, 43 | adapter: ?*Adapter, 44 | message: ?[*:0]const u8, 45 | userdata: ?*anyopaque, 46 | ) callconv(.C) void { 47 | callback( 48 | if (Context == void) {} else @as(Context, @ptrCast(@alignCast(userdata))), 49 | status, 50 | adapter, 51 | message, 52 | ); 53 | } 54 | }; 55 | Impl.instanceRequestAdapter(instance, options, Helper.cCallback, if (Context == void) null else context); 56 | } 57 | 58 | pub inline fn reference(instance: *Instance) void { 59 | Impl.instanceReference(instance); 60 | } 61 | 62 | pub inline fn release(instance: *Instance) void { 63 | Impl.instanceRelease(instance); 64 | } 65 | }; 66 | -------------------------------------------------------------------------------- /src/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/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/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/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/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/dawn.zig: -------------------------------------------------------------------------------- 1 | const Bool32 = @import("main.zig").Bool32; 2 | const ChainedStruct = @import("main.zig").ChainedStruct; 3 | const ChainedStructOut = @import("main.zig").ChainedStructOut; 4 | const PowerPreference = @import("main.zig").PowerPreference; 5 | const Texture = @import("texture.zig").Texture; 6 | 7 | pub const CacheDeviceDescriptor = extern struct { 8 | chain: ChainedStruct = .{ .next = null, .s_type = .dawn_cache_device_descriptor }, 9 | isolation_key: [*:0]const u8 = "", 10 | }; 11 | 12 | pub const EncoderInternalUsageDescriptor = extern struct { 13 | chain: ChainedStruct = .{ .next = null, .s_type = .dawn_encoder_internal_usage_descriptor }, 14 | use_internal_usages: Bool32 = .false, 15 | }; 16 | 17 | pub const MultisampleStateRenderToSingleSampled = extern struct { 18 | chain: ChainedStruct = .{ .next = null, .s_type = .dawn_multisample_state_render_to_single_sampled }, 19 | enabled: Bool32 = .false, 20 | }; 21 | 22 | pub const RenderPassColorAttachmentRenderToSingleSampled = extern struct { 23 | chain: ChainedStruct = .{ .next = null, .s_type = .dawn_render_pass_color_attachment_render_to_single_sampled }, 24 | implicit_sample_count: u32 = 1, 25 | }; 26 | 27 | pub const TextureInternalUsageDescriptor = extern struct { 28 | chain: ChainedStruct = .{ .next = null, .s_type = .dawn_texture_internal_usage_descriptor }, 29 | internal_usage: Texture.UsageFlags = Texture.UsageFlags.none, 30 | }; 31 | 32 | pub const TogglesDescriptor = extern struct { 33 | chain: ChainedStruct = .{ .next = null, .s_type = .dawn_toggles_descriptor }, 34 | enabled_toggles_count: usize = 0, 35 | enabled_toggles: ?[*]const [*:0]const u8 = null, 36 | disabled_toggles_count: usize = 0, 37 | disabled_toggles: ?[*]const [*:0]const u8 = null, 38 | 39 | /// Provides a slightly friendlier Zig API to initialize this structure. 40 | pub inline fn init(v: struct { 41 | chain: ChainedStruct = .{ .next = null, .s_type = .dawn_toggles_descriptor }, 42 | enabled_toggles: ?[]const [*:0]const u8 = null, 43 | disabled_toggles: ?[]const [*:0]const u8 = null, 44 | }) TogglesDescriptor { 45 | return .{ 46 | .chain = v.chain, 47 | .enabled_toggles_count = if (v.enabled_toggles) |e| e.len else 0, 48 | .enabled_toggles = if (v.enabled_toggles) |e| e.ptr else null, 49 | .disabled_toggles_count = if (v.disabled_toggles) |e| e.len else 0, 50 | .disabled_toggles = if (v.disabled_toggles) |e| e.ptr else null, 51 | }; 52 | } 53 | }; 54 | 55 | pub const ShaderModuleSPIRVOptionsDescriptor = extern struct { 56 | chain: ChainedStruct = .{ .next = null, .s_type = .dawn_shader_module_spirv_options_descriptor }, 57 | allow_non_uniform_derivatives: Bool32 = .false, 58 | }; 59 | 60 | pub const AdapterPropertiesPowerPreference = extern struct { 61 | chain: ChainedStructOut = .{ 62 | .next = null, 63 | .s_type = .dawn_adapter_properties_power_preference, 64 | }, 65 | power_preference: PowerPreference = .undefined, 66 | }; 67 | 68 | pub const BufferDescriptorErrorInfoFromWireClient = extern struct { 69 | chain: ChainedStruct = .{ 70 | .next = null, 71 | .s_type = .dawn_buffer_descriptor_error_info_from_wire_client, 72 | }, 73 | out_of_memory: Bool32 = .false, 74 | }; 75 | -------------------------------------------------------------------------------- /src/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/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/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 buffer(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 sampler(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 textureView(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/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 | const dawn = @import("dawn.zig"); 7 | 8 | pub const ShaderModule = opaque { 9 | pub const Descriptor = extern struct { 10 | pub const NextInChain = extern union { 11 | generic: ?*const ChainedStruct, 12 | spirv_descriptor: ?*const SPIRVDescriptor, 13 | wgsl_descriptor: ?*const WGSLDescriptor, 14 | hlsl_descriptor: ?*const HLSLDescriptor, 15 | msl_descriptor: ?*const MSLDescriptor, 16 | dawn_shader_module_spirv_options_descriptor: ?*const dawn.ShaderModuleSPIRVOptionsDescriptor, 17 | }; 18 | 19 | next_in_chain: NextInChain = .{ .generic = null }, 20 | label: ?[*:0]const u8 = null, 21 | }; 22 | 23 | pub const SPIRVDescriptor = extern struct { 24 | chain: ChainedStruct = .{ .next = null, .s_type = .shader_module_spirv_descriptor }, 25 | code_size: u32, 26 | code: [*]const u32, 27 | }; 28 | 29 | pub const WGSLDescriptor = extern struct { 30 | chain: ChainedStruct = .{ .next = null, .s_type = .shader_module_wgsl_descriptor }, 31 | code: [*:0]const u8, 32 | }; 33 | 34 | pub const HLSLDescriptor = extern struct { 35 | chain: ChainedStruct = .{ .next = null, .s_type = .shader_module_hlsl_descriptor }, 36 | code: [*]const u8, 37 | code_size: u32, 38 | }; 39 | 40 | pub const MSLDescriptor = extern struct { 41 | chain: ChainedStruct = .{ .next = null, .s_type = .shader_module_msl_descriptor }, 42 | code: [*]const u8, 43 | code_size: u32, 44 | workgroup_size: WorkgroupSize, 45 | }; 46 | 47 | pub const WorkgroupSize = extern struct { x: u32 = 1, y: u32 = 1, z: u32 = 1 }; 48 | 49 | pub inline fn getCompilationInfo( 50 | shader_module: *ShaderModule, 51 | context: anytype, 52 | comptime callback: fn ( 53 | ctx: @TypeOf(context), 54 | status: CompilationInfoRequestStatus, 55 | compilation_info: *const CompilationInfo, 56 | ) callconv(.Inline) void, 57 | ) void { 58 | const Context = @TypeOf(context); 59 | const Helper = struct { 60 | pub fn cCallback( 61 | status: CompilationInfoRequestStatus, 62 | compilation_info: *const CompilationInfo, 63 | userdata: ?*anyopaque, 64 | ) callconv(.C) void { 65 | callback( 66 | if (Context == void) {} else @as(Context, @ptrCast(@alignCast(userdata))), 67 | status, 68 | compilation_info, 69 | ); 70 | } 71 | }; 72 | Impl.shaderModuleGetCompilationInfo(shader_module, Helper.cCallback, if (Context == void) null else context); 73 | } 74 | 75 | pub inline fn setLabel(shader_module: *ShaderModule, label: [*:0]const u8) void { 76 | Impl.shaderModuleSetLabel(shader_module, label); 77 | } 78 | 79 | pub inline fn reference(shader_module: *ShaderModule) void { 80 | Impl.shaderModuleReference(shader_module); 81 | } 82 | 83 | pub inline fn release(shader_module: *ShaderModule) void { 84 | Impl.shaderModuleRelease(shader_module); 85 | } 86 | }; 87 | -------------------------------------------------------------------------------- /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/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 | // TODO: dawn: does not allow unsetting this callback to null 39 | pub inline fn onSubmittedWorkDone( 40 | queue: *Queue, 41 | signal_value: u64, 42 | context: anytype, 43 | comptime callback: fn (ctx: @TypeOf(context), status: WorkDoneStatus) callconv(.Inline) void, 44 | ) void { 45 | const Context = @TypeOf(context); 46 | const Helper = struct { 47 | pub fn cCallback(status: WorkDoneStatus, userdata: ?*anyopaque) callconv(.C) void { 48 | callback(if (Context == void) {} else @as(Context, @ptrCast(@alignCast(userdata))), status); 49 | } 50 | }; 51 | Impl.queueOnSubmittedWorkDone(queue, signal_value, Helper.cCallback, if (Context == void) null else context); 52 | } 53 | 54 | pub inline fn setLabel(queue: *Queue, label: [*:0]const u8) void { 55 | Impl.queueSetLabel(queue, label); 56 | } 57 | 58 | pub inline fn submit(queue: *Queue, commands: []const *const CommandBuffer) void { 59 | Impl.queueSubmit(queue, commands.len, commands.ptr); 60 | } 61 | 62 | pub inline fn writeBuffer( 63 | queue: *Queue, 64 | buffer: *Buffer, 65 | buffer_offset_bytes: u64, 66 | data_slice: anytype, 67 | ) void { 68 | Impl.queueWriteBuffer( 69 | queue, 70 | buffer, 71 | buffer_offset_bytes, 72 | @as(*const anyopaque, @ptrCast(std.mem.sliceAsBytes(data_slice).ptr)), 73 | data_slice.len * @sizeOf(std.meta.Elem(@TypeOf(data_slice))), 74 | ); 75 | } 76 | 77 | pub inline fn writeTexture( 78 | queue: *Queue, 79 | destination: *const ImageCopyTexture, 80 | data_layout: *const Texture.DataLayout, 81 | write_size: *const Extent3D, 82 | data_slice: anytype, 83 | ) void { 84 | Impl.queueWriteTexture( 85 | queue, 86 | destination, 87 | @as(*const anyopaque, @ptrCast(std.mem.sliceAsBytes(data_slice).ptr)), 88 | @as(usize, @intCast(data_slice.len)) * @sizeOf(std.meta.Elem(@TypeOf(data_slice))), 89 | data_layout, 90 | write_size, 91 | ); 92 | } 93 | 94 | pub inline fn reference(queue: *Queue) void { 95 | Impl.queueReference(queue); 96 | } 97 | 98 | pub inline fn release(queue: *Queue) void { 99 | Impl.queueRelease(queue); 100 | } 101 | }; 102 | -------------------------------------------------------------------------------- /src/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 | const err_msg = .{ 38 | .loc = loc, 39 | .msg = try std.fmt.allocPrint(self.arena.allocator(), comptime format, args), 40 | .note = note, 41 | }; 42 | try self.list.append(self.arena.allocator(), err_msg); 43 | } 44 | 45 | pub fn createNote( 46 | self: *ErrorList, 47 | loc: ?Token.Loc, 48 | comptime format: []const u8, 49 | args: anytype, 50 | ) !ErrorMsg.Note { 51 | return .{ 52 | .loc = loc, 53 | .msg = try std.fmt.allocPrint(self.arena.allocator(), comptime format, args), 54 | }; 55 | } 56 | 57 | pub fn print(self: ErrorList, source: []const u8, file_path: ?[]const u8) !void { 58 | const stderr = std.io.getStdErr(); 59 | var bw = std.io.bufferedWriter(stderr.writer()); 60 | const b = bw.writer(); 61 | const term = std.io.tty.detectConfig(stderr); 62 | 63 | for (self.list.items) |*err| { 64 | const loc_extra = err.loc.extraInfo(source); 65 | 66 | // 'file:line:column error: MSG' 67 | try term.setColor(b, .bold); 68 | try b.print("{?s}:{d}:{d} ", .{ file_path, loc_extra.line, loc_extra.col }); 69 | try term.setColor(b, .bright_red); 70 | try b.writeAll("error: "); 71 | try term.setColor(b, .reset); 72 | try term.setColor(b, .bold); 73 | try b.writeAll(err.msg); 74 | try b.writeByte('\n'); 75 | 76 | try printCode(b, term, source, err.loc); 77 | 78 | // note 79 | if (err.note) |note| { 80 | if (note.loc) |note_loc| { 81 | const note_loc_extra = note_loc.extraInfo(source); 82 | 83 | try term.setColor(b, .reset); 84 | try term.setColor(b, .bold); 85 | try b.print("{?s}:{d}:{d} ", .{ file_path, note_loc_extra.line, note_loc_extra.col }); 86 | } 87 | try term.setColor(b, .cyan); 88 | try b.writeAll("note: "); 89 | 90 | try term.setColor(b, .reset); 91 | try term.setColor(b, .bold); 92 | try b.writeAll(note.msg); 93 | try b.writeByte('\n'); 94 | 95 | if (note.loc) |note_loc| { 96 | try printCode(b, term, source, note_loc); 97 | } 98 | } 99 | 100 | try term.setColor(b, .reset); 101 | } 102 | try bw.flush(); 103 | } 104 | 105 | fn printCode(writer: anytype, term: std.io.tty.Config, source: []const u8, loc: Token.Loc) !void { 106 | const loc_extra = loc.extraInfo(source); 107 | try term.setColor(writer, .dim); 108 | try writer.print("{d} │ ", .{loc_extra.line}); 109 | try term.setColor(writer, .reset); 110 | try writer.writeAll(source[loc_extra.line_start..loc.start]); 111 | try term.setColor(writer, .green); 112 | try writer.writeAll(source[loc.start..loc.end]); 113 | try term.setColor(writer, .reset); 114 | try writer.writeAll(source[loc.end..loc_extra.line_end]); 115 | try writer.writeByte('\n'); 116 | 117 | // location pointer 118 | const line_number_len = (std.math.log10(loc_extra.line) + 1) + 3; 119 | try writer.writeByteNTimes( 120 | ' ', 121 | line_number_len + (loc_extra.col - 1), 122 | ); 123 | try term.setColor(writer, .bold); 124 | try term.setColor(writer, .green); 125 | try writer.writeByte('^'); 126 | try writer.writeByteNTimes('~', loc.end - loc.start - 1); 127 | try writer.writeByte('\n'); 128 | } 129 | -------------------------------------------------------------------------------- /src/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/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 | .createInstance = true, 7 | .enumerateInstanceExtensionProperties = true, 8 | .enumerateInstanceLayerProperties = true, 9 | .getInstanceProcAddr = true, 10 | }); 11 | 12 | pub const InstanceFunctions = vk.InstanceWrapper(.{ 13 | .createDevice = true, 14 | // TODO: renderdoc will not work with wayland 15 | // .createWaylandSurfaceKHR = builtin.target.os.tag == .linux, 16 | .createWin32SurfaceKHR = builtin.target.os.tag == .windows, 17 | .createXlibSurfaceKHR = builtin.target.os.tag == .linux, 18 | .destroyInstance = true, 19 | .destroySurfaceKHR = true, 20 | .enumerateDeviceExtensionProperties = true, 21 | .enumerateDeviceLayerProperties = true, 22 | .enumeratePhysicalDevices = true, 23 | .getDeviceProcAddr = true, 24 | .getPhysicalDeviceFeatures = true, 25 | .getPhysicalDeviceFormatProperties = true, 26 | .getPhysicalDeviceProperties = true, 27 | .getPhysicalDeviceMemoryProperties = true, 28 | .getPhysicalDeviceQueueFamilyProperties = true, 29 | .getPhysicalDeviceSurfaceCapabilitiesKHR = true, 30 | .getPhysicalDeviceSurfaceFormatsKHR = true, 31 | }); 32 | 33 | pub const DeviceFunctions = vk.DeviceWrapper(.{ 34 | .acquireNextImageKHR = true, 35 | .allocateCommandBuffers = true, 36 | .allocateDescriptorSets = true, 37 | .allocateMemory = true, 38 | .beginCommandBuffer = true, 39 | .bindBufferMemory = true, 40 | .bindImageMemory = true, 41 | .cmdBeginRenderPass = true, 42 | .cmdBindDescriptorSets = true, 43 | .cmdBindIndexBuffer = true, 44 | .cmdBindPipeline = true, 45 | .cmdBindVertexBuffers = true, 46 | .cmdCopyBuffer = true, 47 | .cmdCopyBufferToImage = true, 48 | .cmdCopyImage = true, 49 | .cmdDispatch = true, 50 | .cmdDraw = true, 51 | .cmdDrawIndexed = true, 52 | .cmdEndRenderPass = true, 53 | .cmdPipelineBarrier = true, 54 | .cmdSetScissor = true, 55 | .cmdSetStencilReference = true, 56 | .cmdSetViewport = true, 57 | .createBuffer = true, 58 | .createCommandPool = true, 59 | .createComputePipelines = true, 60 | .createDescriptorPool = true, 61 | .createDescriptorSetLayout = true, 62 | .createFence = true, 63 | .createFramebuffer = true, 64 | .createGraphicsPipelines = true, 65 | .createImage = true, 66 | .createImageView = true, 67 | .createPipelineLayout = true, 68 | .createRenderPass = true, 69 | .createSampler = true, 70 | .createSemaphore = true, 71 | .createShaderModule = true, 72 | .createSwapchainKHR = true, 73 | .destroyBuffer = true, 74 | .destroyCommandPool = true, 75 | .destroyDescriptorPool = true, 76 | .destroyDescriptorSetLayout = true, 77 | .destroyDevice = true, 78 | .destroyFence = true, 79 | .destroyFramebuffer = true, 80 | .destroyImage = true, 81 | .destroyImageView = true, 82 | .destroyPipeline = true, 83 | .destroyPipelineLayout = true, 84 | .destroyRenderPass = true, 85 | .destroySampler = true, 86 | .destroySemaphore = true, 87 | .destroyShaderModule = true, 88 | .destroySwapchainKHR = true, 89 | .deviceWaitIdle = true, 90 | .endCommandBuffer = true, 91 | .freeCommandBuffers = true, 92 | .freeDescriptorSets = true, 93 | .freeMemory = true, 94 | .getBufferMemoryRequirements = true, 95 | .getDeviceQueue = true, 96 | .getFenceStatus = true, 97 | .getImageMemoryRequirements = true, 98 | .getSwapchainImagesKHR = true, 99 | .mapMemory = true, 100 | .queuePresentKHR = true, 101 | .queueSubmit = true, 102 | .queueWaitIdle = true, 103 | .resetCommandBuffer = true, 104 | .resetFences = true, 105 | .unmapMemory = true, 106 | .updateDescriptorSets = true, 107 | .waitForFences = true, 108 | }); 109 | 110 | pub const BaseLoader = *const fn (vk.Instance, [*:0]const u8) vk.PfnVoidFunction; 111 | 112 | pub fn loadBase(baseLoader: BaseLoader) !BaseFunctions { 113 | return BaseFunctions.load(baseLoader) catch return error.ProcLoadingFailed; 114 | } 115 | 116 | pub fn loadInstance(instance: vk.Instance, instanceLoader: vk.PfnGetInstanceProcAddr) !InstanceFunctions { 117 | return InstanceFunctions.load(instance, instanceLoader) catch return error.ProcLoadingFailed; 118 | } 119 | 120 | pub fn loadDevice(device: vk.Device, deviceLoader: vk.PfnGetDeviceProcAddr) !DeviceFunctions { 121 | return DeviceFunctions.load(device, deviceLoader) catch return error.ProcLoadingFailed; 122 | } 123 | -------------------------------------------------------------------------------- /src/sysgpu/adapter.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const testing = std.testing; 3 | const dawn = @import("dawn.zig"); 4 | const Bool32 = @import("main.zig").Bool32; 5 | const ChainedStructOut = @import("main.zig").ChainedStructOut; 6 | const Device = @import("device.zig").Device; 7 | const Instance = @import("instance.zig").Instance; 8 | const FeatureName = @import("main.zig").FeatureName; 9 | const SupportedLimits = @import("main.zig").SupportedLimits; 10 | const RequestDeviceStatus = @import("main.zig").RequestDeviceStatus; 11 | const BackendType = @import("main.zig").BackendType; 12 | const RequestDeviceCallback = @import("main.zig").RequestDeviceCallback; 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 Properties = extern struct { 33 | pub const NextInChain = extern union { 34 | generic: ?*const ChainedStructOut, 35 | dawn_adapter_properties_power_preference: *const dawn.AdapterPropertiesPowerPreference, 36 | }; 37 | 38 | next_in_chain: NextInChain = .{ .generic = null }, 39 | vendor_id: u32, 40 | vendor_name: [*:0]const u8, 41 | architecture: [*:0]const u8, 42 | device_id: u32, 43 | name: [*:0]const u8, 44 | driver_description: [*:0]const u8, 45 | adapter_type: Type, 46 | backend_type: BackendType, 47 | compatibility_mode: Bool32 = .false, 48 | }; 49 | 50 | pub inline fn createDevice(adapter: *Adapter, descriptor: ?*const Device.Descriptor) ?*Device { 51 | return Impl.adapterCreateDevice(adapter, descriptor); 52 | } 53 | 54 | /// Call once with null to determine the array length, and again to fetch the feature list. 55 | /// 56 | /// Consider using the enumerateFeaturesOwned helper. 57 | pub inline fn enumerateFeatures(adapter: *Adapter, features: ?[*]FeatureName) usize { 58 | return Impl.adapterEnumerateFeatures(adapter, features); 59 | } 60 | 61 | /// Enumerates the adapter features, storing the result in an allocated slice which is owned by 62 | /// the caller. 63 | pub inline fn enumerateFeaturesOwned(adapter: *Adapter, allocator: std.mem.Allocator) ![]FeatureName { 64 | const count = adapter.enumerateFeatures(null); 65 | const data = try allocator.alloc(FeatureName, count); 66 | _ = adapter.enumerateFeatures(data.ptr); 67 | return data; 68 | } 69 | 70 | pub inline fn getInstance(adapter: *Adapter) *Instance { 71 | return Impl.adapterGetInstance(adapter); 72 | } 73 | 74 | pub inline fn getLimits(adapter: *Adapter, limits: *SupportedLimits) bool { 75 | return Impl.adapterGetLimits(adapter, limits); 76 | } 77 | 78 | pub inline fn getProperties(adapter: *Adapter, properties: *Adapter.Properties) void { 79 | Impl.adapterGetProperties(adapter, properties); 80 | } 81 | 82 | pub inline fn hasFeature(adapter: *Adapter, feature: FeatureName) bool { 83 | return Impl.adapterHasFeature(adapter, feature); 84 | } 85 | 86 | pub inline fn requestDevice( 87 | adapter: *Adapter, 88 | descriptor: ?*const Device.Descriptor, 89 | context: anytype, 90 | comptime callback: fn ( 91 | ctx: @TypeOf(context), 92 | status: RequestDeviceStatus, 93 | device: *Device, 94 | message: ?[*:0]const u8, 95 | ) callconv(.Inline) void, 96 | ) void { 97 | const Context = @TypeOf(context); 98 | const Helper = struct { 99 | pub fn cCallback(status: RequestDeviceStatus, device: *Device, message: ?[*:0]const u8, userdata: ?*anyopaque) callconv(.C) void { 100 | callback( 101 | if (Context == void) {} else @as(Context, @ptrCast(@alignCast(userdata))), 102 | status, 103 | device, 104 | message, 105 | ); 106 | } 107 | }; 108 | Impl.adapterRequestDevice(adapter, descriptor, Helper.cCallback, if (Context == void) null else context); 109 | } 110 | 111 | pub inline fn reference(adapter: *Adapter) void { 112 | Impl.adapterReference(adapter); 113 | } 114 | 115 | pub inline fn release(adapter: *Adapter) void { 116 | Impl.adapterRelease(adapter); 117 | } 118 | }; 119 | 120 | test "Adapter.Type name" { 121 | try testing.expectEqualStrings("Discrete GPU", Adapter.Type.discrete_gpu.name()); 122 | } 123 | -------------------------------------------------------------------------------- /src/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/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 buffer( 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 sampler( 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 texture( 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 storageTexture( 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/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 | const dawn = @import("dawn.zig"); 15 | 16 | pub const CommandEncoder = opaque { 17 | pub const Descriptor = extern struct { 18 | pub const NextInChain = extern union { 19 | generic: ?*const ChainedStruct, 20 | dawn_encoder_internal_usage_descriptor: *const dawn.EncoderInternalUsageDescriptor, 21 | }; 22 | 23 | next_in_chain: NextInChain = .{ .generic = null }, 24 | label: ?[*:0]const u8 = null, 25 | }; 26 | 27 | pub inline fn beginComputePass(command_encoder: *CommandEncoder, descriptor: ?*const ComputePassDescriptor) *ComputePassEncoder { 28 | return Impl.commandEncoderBeginComputePass(command_encoder, descriptor); 29 | } 30 | 31 | pub inline fn beginRenderPass(command_encoder: *CommandEncoder, descriptor: *const RenderPassDescriptor) *RenderPassEncoder { 32 | return Impl.commandEncoderBeginRenderPass(command_encoder, descriptor); 33 | } 34 | 35 | /// Default `offset`: 0 36 | /// Default `size`: `gpu.whole_size` 37 | pub inline fn clearBuffer(command_encoder: *CommandEncoder, buffer: *Buffer, offset: u64, size: u64) void { 38 | Impl.commandEncoderClearBuffer(command_encoder, buffer, offset, size); 39 | } 40 | 41 | pub inline fn copyBufferToBuffer(command_encoder: *CommandEncoder, source: *Buffer, source_offset: u64, destination: *Buffer, destination_offset: u64, size: u64) void { 42 | Impl.commandEncoderCopyBufferToBuffer(command_encoder, source, source_offset, destination, destination_offset, size); 43 | } 44 | 45 | pub inline fn copyBufferToTexture(command_encoder: *CommandEncoder, source: *const ImageCopyBuffer, destination: *const ImageCopyTexture, copy_size: *const Extent3D) void { 46 | Impl.commandEncoderCopyBufferToTexture(command_encoder, source, destination, copy_size); 47 | } 48 | 49 | pub inline fn copyTextureToBuffer(command_encoder: *CommandEncoder, source: *const ImageCopyTexture, destination: *const ImageCopyBuffer, copy_size: *const Extent3D) void { 50 | Impl.commandEncoderCopyTextureToBuffer(command_encoder, source, destination, copy_size); 51 | } 52 | 53 | pub inline fn copyTextureToTexture(command_encoder: *CommandEncoder, source: *const ImageCopyTexture, destination: *const ImageCopyTexture, copy_size: *const Extent3D) void { 54 | Impl.commandEncoderCopyTextureToTexture(command_encoder, source, destination, copy_size); 55 | } 56 | 57 | pub inline fn finish(command_encoder: *CommandEncoder, descriptor: ?*const CommandBuffer.Descriptor) *CommandBuffer { 58 | return Impl.commandEncoderFinish(command_encoder, descriptor); 59 | } 60 | 61 | pub inline fn injectValidationError(command_encoder: *CommandEncoder, message: [*:0]const u8) void { 62 | Impl.commandEncoderInjectValidationError(command_encoder, message); 63 | } 64 | 65 | pub inline fn insertDebugMarker(command_encoder: *CommandEncoder, marker_label: [*:0]const u8) void { 66 | Impl.commandEncoderInsertDebugMarker(command_encoder, marker_label); 67 | } 68 | 69 | pub inline fn popDebugGroup(command_encoder: *CommandEncoder) void { 70 | Impl.commandEncoderPopDebugGroup(command_encoder); 71 | } 72 | 73 | pub inline fn pushDebugGroup(command_encoder: *CommandEncoder, group_label: [*:0]const u8) void { 74 | Impl.commandEncoderPushDebugGroup(command_encoder, group_label); 75 | } 76 | 77 | pub inline fn resolveQuerySet(command_encoder: *CommandEncoder, query_set: *QuerySet, first_query: u32, query_count: u32, destination: *Buffer, destination_offset: u64) void { 78 | Impl.commandEncoderResolveQuerySet(command_encoder, query_set, first_query, query_count, destination, destination_offset); 79 | } 80 | 81 | pub inline fn setLabel(command_encoder: *CommandEncoder, label: [*:0]const u8) void { 82 | Impl.commandEncoderSetLabel(command_encoder, label); 83 | } 84 | 85 | pub inline fn writeBuffer( 86 | command_encoder: *CommandEncoder, 87 | buffer: *Buffer, 88 | buffer_offset_bytes: u64, 89 | data_slice: anytype, 90 | ) void { 91 | Impl.commandEncoderWriteBuffer( 92 | command_encoder, 93 | buffer, 94 | buffer_offset_bytes, 95 | @as([*]const u8, @ptrCast(std.mem.sliceAsBytes(data_slice).ptr)), 96 | @as(u64, @intCast(data_slice.len)) * @sizeOf(std.meta.Elem(@TypeOf(data_slice))), 97 | ); 98 | } 99 | 100 | pub inline fn writeTimestamp(command_encoder: *CommandEncoder, query_set: *QuerySet, query_index: u32) void { 101 | Impl.commandEncoderWriteTimestamp(command_encoder, query_set, query_index); 102 | } 103 | 104 | pub inline fn reference(command_encoder: *CommandEncoder) void { 105 | Impl.commandEncoderReference(command_encoder); 106 | } 107 | 108 | pub inline fn release(command_encoder: *CommandEncoder) void { 109 | Impl.commandEncoderRelease(command_encoder); 110 | } 111 | }; 112 | -------------------------------------------------------------------------------- /src/sysgpu/buffer.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const Bool32 = @import("main.zig").Bool32; 3 | const ChainedStruct = @import("main.zig").ChainedStruct; 4 | const dawn = @import("dawn.zig"); 5 | const MapModeFlags = @import("main.zig").MapModeFlags; 6 | const Impl = @import("interface.zig").Impl; 7 | 8 | pub const Buffer = opaque { 9 | pub const MapCallback = *const fn (status: MapAsyncStatus, userdata: ?*anyopaque) callconv(.C) void; 10 | 11 | pub const BindingType = enum(u32) { 12 | undefined = 0x00000000, 13 | uniform = 0x00000001, 14 | storage = 0x00000002, 15 | read_only_storage = 0x00000003, 16 | }; 17 | 18 | pub const MapState = enum(u32) { 19 | unmapped = 0x00000000, 20 | pending = 0x00000001, 21 | mapped = 0x00000002, 22 | }; 23 | 24 | pub const MapAsyncStatus = enum(u32) { 25 | success = 0x00000000, 26 | validation_error = 0x00000001, 27 | unknown = 0x00000002, 28 | device_lost = 0x00000003, 29 | destroyed_before_callback = 0x00000004, 30 | unmapped_before_callback = 0x00000005, 31 | mapping_already_pending = 0x00000006, 32 | offset_out_of_range = 0x00000007, 33 | size_out_of_range = 0x00000008, 34 | }; 35 | 36 | pub const UsageFlags = packed struct(u32) { 37 | map_read: bool = false, 38 | map_write: bool = false, 39 | copy_src: bool = false, 40 | copy_dst: bool = false, 41 | index: bool = false, 42 | vertex: bool = false, 43 | uniform: bool = false, 44 | storage: bool = false, 45 | indirect: bool = false, 46 | query_resolve: bool = false, 47 | 48 | _padding: u22 = 0, 49 | 50 | comptime { 51 | std.debug.assert( 52 | @sizeOf(@This()) == @sizeOf(u32) and 53 | @bitSizeOf(@This()) == @bitSizeOf(u32), 54 | ); 55 | } 56 | 57 | pub const none = UsageFlags{}; 58 | 59 | pub fn equal(a: UsageFlags, b: UsageFlags) bool { 60 | return @as(u10, @truncate(@as(u32, @bitCast(a)))) == @as(u10, @truncate(@as(u32, @bitCast(b)))); 61 | } 62 | }; 63 | 64 | pub const BindingLayout = extern struct { 65 | next_in_chain: ?*const ChainedStruct = null, 66 | type: BindingType = .undefined, 67 | has_dynamic_offset: Bool32 = .false, 68 | min_binding_size: u64 = 0, 69 | }; 70 | 71 | pub const Descriptor = extern struct { 72 | pub const NextInChain = extern union { 73 | generic: ?*const ChainedStruct, 74 | dawn_buffer_descriptor_error_info_from_wire_client: *const dawn.BufferDescriptorErrorInfoFromWireClient, 75 | }; 76 | 77 | next_in_chain: NextInChain = .{ .generic = null }, 78 | label: ?[*:0]const u8 = null, 79 | usage: UsageFlags, 80 | size: u64, 81 | mapped_at_creation: Bool32 = .false, 82 | }; 83 | 84 | pub inline fn destroy(buffer: *Buffer) void { 85 | Impl.bufferDestroy(buffer); 86 | } 87 | 88 | pub inline fn getMapState(buffer: *Buffer) MapState { 89 | return Impl.bufferGetMapState(buffer); 90 | } 91 | 92 | /// Default `offset_bytes`: 0 93 | /// Default `len`: `gpu.whole_map_size` / `std.math.maxint(usize)` (whole range) 94 | pub inline fn getConstMappedRange( 95 | buffer: *Buffer, 96 | comptime T: type, 97 | offset_bytes: usize, 98 | len: usize, 99 | ) ?[]const T { 100 | const size = @sizeOf(T) * len; 101 | const data = Impl.bufferGetConstMappedRange( 102 | buffer, 103 | offset_bytes, 104 | size + size % 4, 105 | ); 106 | return if (data) |d| @as([*]const T, @ptrCast(@alignCast(d)))[0..len] else null; 107 | } 108 | 109 | /// Default `offset_bytes`: 0 110 | /// Default `len`: `gpu.whole_map_size` / `std.math.maxint(usize)` (whole range) 111 | pub inline fn getMappedRange( 112 | buffer: *Buffer, 113 | comptime T: type, 114 | offset_bytes: usize, 115 | len: usize, 116 | ) ?[]T { 117 | const size = @sizeOf(T) * len; 118 | const data = Impl.bufferGetMappedRange( 119 | buffer, 120 | offset_bytes, 121 | size + size % 4, 122 | ); 123 | return if (data) |d| @as([*]T, @ptrCast(@alignCast(d)))[0..len] else null; 124 | } 125 | 126 | pub inline fn getSize(buffer: *Buffer) u64 { 127 | return Impl.bufferGetSize(buffer); 128 | } 129 | 130 | pub inline fn getUsage(buffer: *Buffer) Buffer.UsageFlags { 131 | return Impl.bufferGetUsage(buffer); 132 | } 133 | 134 | pub inline fn mapAsync( 135 | buffer: *Buffer, 136 | mode: MapModeFlags, 137 | offset: usize, 138 | size: usize, 139 | context: anytype, 140 | comptime callback: fn (ctx: @TypeOf(context), status: MapAsyncStatus) callconv(.Inline) void, 141 | ) void { 142 | const Context = @TypeOf(context); 143 | const Helper = struct { 144 | pub fn cCallback(status: MapAsyncStatus, userdata: ?*anyopaque) callconv(.C) void { 145 | callback(if (Context == void) {} else @as(Context, @ptrCast(@alignCast(userdata))), status); 146 | } 147 | }; 148 | Impl.bufferMapAsync(buffer, mode, offset, size, Helper.cCallback, if (Context == void) null else context); 149 | } 150 | 151 | pub inline fn setLabel(buffer: *Buffer, label: [*:0]const u8) void { 152 | Impl.bufferSetLabel(buffer, label); 153 | } 154 | 155 | pub inline fn unmap(buffer: *Buffer) void { 156 | Impl.bufferUnmap(buffer); 157 | } 158 | 159 | pub inline fn reference(buffer: *Buffer) void { 160 | Impl.bufferReference(buffer); 161 | } 162 | 163 | pub inline fn release(buffer: *Buffer) void { 164 | Impl.bufferRelease(buffer); 165 | } 166 | }; 167 | -------------------------------------------------------------------------------- /src/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/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/opengl/conv.zig: -------------------------------------------------------------------------------- 1 | const sysgpu = @import("../sysgpu/main.zig"); 2 | const utils = @import("../utils.zig"); 3 | const c = @import("c.zig"); 4 | 5 | fn stencilEnable(stencil: sysgpu.StencilFaceState) bool { 6 | return stencil.compare != .always or stencil.fail_op != .keep or stencil.depth_fail_op != .keep or stencil.pass_op != .keep; 7 | } 8 | 9 | pub fn glAttributeCount(format: sysgpu.VertexFormat) c.GLint { 10 | return switch (format) { 11 | .undefined => unreachable, 12 | .uint8x2 => 2, 13 | .uint8x4 => 4, 14 | .sint8x2 => 2, 15 | .sint8x4 => 4, 16 | .unorm8x2 => 2, 17 | .unorm8x4 => 4, 18 | .snorm8x2 => 2, 19 | .snorm8x4 => 4, 20 | .uint16x2 => 2, 21 | .uint16x4 => 4, 22 | .sint16x2 => 2, 23 | .sint16x4 => 4, 24 | .unorm16x2 => 2, 25 | .unorm16x4 => 4, 26 | .snorm16x2 => 2, 27 | .snorm16x4 => 4, 28 | .float16x2 => 2, 29 | .float16x4 => 4, 30 | .float32 => 1, 31 | .float32x2 => 2, 32 | .float32x3 => 3, 33 | .float32x4 => 4, 34 | .uint32 => 1, 35 | .uint32x2 => 2, 36 | .uint32x3 => 3, 37 | .uint32x4 => 4, 38 | .sint32 => 1, 39 | .sint32x2 => 2, 40 | .sint32x3 => 3, 41 | .sint32x4 => 4, 42 | }; 43 | } 44 | 45 | pub fn glAttributeIsNormalized(format_type: utils.FormatType) c.GLboolean { 46 | return switch (format_type) { 47 | .unorm, .unorm_srgb, .snorm => c.GL_TRUE, 48 | else => c.GL_FALSE, 49 | }; 50 | } 51 | 52 | pub fn glAttributeIsInt(format_type: utils.FormatType) bool { 53 | return switch (format_type) { 54 | .uint, .sint => true, 55 | else => false, 56 | }; 57 | } 58 | 59 | pub fn glAttributeType(format: sysgpu.VertexFormat) c.GLenum { 60 | return switch (format) { 61 | .undefined => unreachable, 62 | .uint8x2 => c.GL_UNSIGNED_BYTE, 63 | .uint8x4 => c.GL_UNSIGNED_BYTE, 64 | .sint8x2 => c.GL_BYTE, 65 | .sint8x4 => c.GL_BYTE, 66 | .unorm8x2 => c.GL_UNSIGNED_BYTE, 67 | .unorm8x4 => c.GL_UNSIGNED_BYTE, 68 | .snorm8x2 => c.GL_BYTE, 69 | .snorm8x4 => c.GL_BYTE, 70 | .uint16x2 => c.GL_UNSIGNED_SHORT, 71 | .uint16x4 => c.GL_UNSIGNED_SHORT, 72 | .sint16x2 => c.GL_SHORT, 73 | .sint16x4 => c.GL_SHORT, 74 | .unorm16x2 => c.GL_UNSIGNED_SHORT, 75 | .unorm16x4 => c.GL_UNSIGNED_SHORT, 76 | .snorm16x2 => c.GL_SHORT, 77 | .snorm16x4 => c.GL_SHORT, 78 | .float16x2 => c.GL_HALF_FLOAT, 79 | .float16x4 => c.GL_HALF_FLOAT, 80 | .float32 => c.GL_FLOAT, 81 | .float32x2 => c.GL_FLOAT, 82 | .float32x3 => c.GL_FLOAT, 83 | .float32x4 => c.GL_FLOAT, 84 | .uint32 => c.GL_UNSIGNED_INT, 85 | .uint32x2 => c.GL_UNSIGNED_INT, 86 | .uint32x3 => c.GL_UNSIGNED_INT, 87 | .uint32x4 => c.GL_UNSIGNED_INT, 88 | .sint32 => c.GL_INT, 89 | .sint32x2 => c.GL_INT, 90 | .sint32x3 => c.GL_INT, 91 | .sint32x4 => c.GL_INT, 92 | }; 93 | } 94 | 95 | pub fn glBlendFactor(factor: sysgpu.BlendFactor, color: bool) c.GLenum { 96 | return switch (factor) { 97 | .zero => c.GL_ZERO, 98 | .one => c.GL_ONE, 99 | .src => c.GL_SRC_COLOR, 100 | .one_minus_src => c.GL_ONE_MINUS_SRC_COLOR, 101 | .src_alpha => c.GL_SRC_ALPHA, 102 | .one_minus_src_alpha => c.GL_ONE_MINUS_SRC_ALPHA, 103 | .dst => c.GL_DST_COLOR, 104 | .one_minus_dst => c.GL_ONE_MINUS_DST_COLOR, 105 | .dst_alpha => c.GL_DST_ALPHA, 106 | .one_minus_dst_alpha => c.GL_ONE_MINUS_DST_ALPHA, 107 | .src_alpha_saturated => c.GL_SRC_ALPHA_SATURATE, 108 | .constant => if (color) c.GL_CONSTANT_COLOR else c.GL_CONSTANT_ALPHA, 109 | .one_minus_constant => if (color) c.GL_ONE_MINUS_CONSTANT_COLOR else c.GL_ONE_MINUS_CONSTANT_ALPHA, 110 | .src1 => c.GL_SRC1_COLOR, 111 | .one_minus_src1 => c.GL_ONE_MINUS_SRC1_COLOR, 112 | .src1_alpha => c.GL_SRC1_ALPHA, 113 | .one_minus_src1_alpha => c.GL_ONE_MINUS_SRC1_ALPHA, 114 | }; 115 | } 116 | 117 | pub fn glBlendOp(op: sysgpu.BlendOperation) c.GLenum { 118 | return switch (op) { 119 | .add => c.GL_FUNC_ADD, 120 | .subtract => c.GL_FUNC_SUBTRACT, 121 | .reverse_subtract => c.GL_FUNC_REVERSE_SUBTRACT, 122 | .min => c.GL_MIN, 123 | .max => c.GL_MAX, 124 | }; 125 | } 126 | 127 | //pub fn glBufferDataUsage(usage: sysgpu.Buffer.UsageFlags, mapped_at_creation: sysgpu.Bool32) c.GLenum {} 128 | 129 | pub fn glBufferStorageFlags(usage: sysgpu.Buffer.UsageFlags, mapped_at_creation: sysgpu.Bool32) c.GLbitfield { 130 | var flags: c.GLbitfield = 0; 131 | if (mapped_at_creation == .true) 132 | flags |= c.GL_MAP_WRITE_BIT; 133 | if (usage.map_read) 134 | flags |= c.GL_MAP_PERSISTENT_BIT | c.GL_MAP_READ_BIT; 135 | if (usage.map_write) 136 | flags |= c.GL_MAP_PERSISTENT_BIT | c.GL_MAP_COHERENT_BIT | c.GL_MAP_WRITE_BIT; 137 | return flags; 138 | } 139 | 140 | pub fn glCompareFunc(func: sysgpu.CompareFunction) c.GLenum { 141 | return switch (func) { 142 | .undefined => unreachable, 143 | .never => c.GL_NEVER, 144 | .less => c.GL_LESS, 145 | .less_equal => c.GL_LEQUAL, 146 | .greater => c.GL_GREATER, 147 | .greater_equal => c.GL_GEQUAL, 148 | .equal => c.GL_EQUAL, 149 | .not_equal => c.GL_NOTEQUAL, 150 | .always => c.GL_ALWAYS, 151 | }; 152 | } 153 | 154 | pub fn glCullEnabled(cull_mode: sysgpu.CullMode) bool { 155 | return switch (cull_mode) { 156 | .none => false, 157 | else => true, 158 | }; 159 | } 160 | 161 | pub fn glCullFace(cull_mode: sysgpu.CullMode) c.GLenum { 162 | return switch (cull_mode) { 163 | .none => c.GL_BACK, 164 | .front => c.GL_FRONT, 165 | .back => c.GL_BACK, 166 | }; 167 | } 168 | 169 | pub fn glDepthMask(ds: *const sysgpu.DepthStencilState) c.GLboolean { 170 | return if (ds.depth_write_enabled == .true) c.GL_TRUE else c.GL_FALSE; 171 | } 172 | 173 | pub fn glDepthTestEnabled(ds: *const sysgpu.DepthStencilState) bool { 174 | return ds.depth_compare != .always or ds.depth_write_enabled == .true; 175 | } 176 | 177 | pub fn glFrontFace(front_face: sysgpu.FrontFace) c.GLenum { 178 | return switch (front_face) { 179 | .ccw => c.GL_CCW, 180 | .cw => c.GL_CW, 181 | }; 182 | } 183 | 184 | pub fn glIndexType(format: sysgpu.IndexFormat) c.GLenum { 185 | return switch (format) { 186 | .undefined => unreachable, 187 | .uint16 => c.GL_UNSIGNED_SHORT, 188 | .uint32 => c.GL_UNSIGNED_INT, 189 | }; 190 | } 191 | 192 | pub fn glIndexElementSize(format: sysgpu.IndexFormat) usize { 193 | return switch (format) { 194 | .undefined => unreachable, 195 | .uint16 => 2, 196 | .uint32 => 4, 197 | }; 198 | } 199 | 200 | pub fn glMapAccess(usage: sysgpu.Buffer.UsageFlags, mapped_at_creation: sysgpu.Bool32) c.GLbitfield { 201 | var flags: c.GLbitfield = 0; 202 | if (mapped_at_creation == .true) 203 | flags |= c.GL_MAP_WRITE_BIT; 204 | if (usage.map_read) 205 | flags |= c.GL_MAP_PERSISTENT_BIT | c.GL_MAP_READ_BIT; 206 | if (usage.map_write) 207 | flags |= c.GL_MAP_PERSISTENT_BIT | c.GL_MAP_WRITE_BIT; 208 | return flags; 209 | } 210 | 211 | pub fn glPrimitiveMode(topology: sysgpu.PrimitiveTopology) c.GLenum { 212 | return switch (topology) { 213 | .point_list => c.GL_POINTS, 214 | .line_list => c.GL_LINES, 215 | .line_strip => c.GL_LINE_STRIP, 216 | .triangle_list => c.GL_TRIANGLES, 217 | .triangle_strip => c.GL_TRIANGLE_STRIP, 218 | }; 219 | } 220 | 221 | pub fn glStencilOp(op: sysgpu.StencilOperation) c.GLenum { 222 | return switch (op) { 223 | .keep => c.GL_KEEP, 224 | .zero => c.GL_ZERO, 225 | .replace => c.GL_REPLACE, 226 | .invert => c.GL_INVERT, 227 | .increment_clamp => c.GL_INCR, 228 | .decrement_clamp => c.GL_DECR, 229 | .increment_wrap => c.GL_INCR_WRAP, 230 | .decrement_wrap => c.GL_DECR_WRAP, 231 | }; 232 | } 233 | 234 | pub fn glStencilTestEnabled(ds: *const sysgpu.DepthStencilState) bool { 235 | return stencilEnable(ds.stencil_front) or stencilEnable(ds.stencil_back); 236 | } 237 | 238 | pub fn glTargetForBuffer(usage: sysgpu.Buffer.UsageFlags) c.GLenum { 239 | // Not sure if this matters anymore - only get to pick one anyway 240 | if (usage.index) 241 | return c.GL_ELEMENT_ARRAY_BUFFER; 242 | if (usage.vertex) 243 | return c.GL_ARRAY_BUFFER; 244 | if (usage.uniform) 245 | return c.GL_UNIFORM_BUFFER; 246 | if (usage.storage) 247 | return c.GL_SHADER_STORAGE_BUFFER; 248 | if (usage.indirect) 249 | return c.GL_DRAW_INDIRECT_BUFFER; 250 | if (usage.query_resolve) 251 | return c.GL_QUERY_BUFFER; 252 | 253 | return c.GL_ARRAY_BUFFER; 254 | } 255 | 256 | pub fn glTargetForBufferBinding(binding_type: sysgpu.Buffer.BindingType) c.GLenum { 257 | return switch (binding_type) { 258 | .undefined => unreachable, 259 | .uniform => c.GL_UNIFORM_BUFFER, 260 | .storage => c.GL_SHADER_STORAGE_BUFFER, 261 | .read_only_storage => c.GL_SHADER_STORAGE_BUFFER, 262 | }; 263 | } 264 | -------------------------------------------------------------------------------- /src/shader/CodeGen.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const Air = @import("Air.zig"); 3 | const c = @cImport({ 4 | @cInclude("spirv-cross/spirv_cross_c.h"); 5 | @cInclude("spirv-tools/libspirv.h"); 6 | }); 7 | const genGlsl = @import("codegen/glsl.zig").gen; 8 | const genHlsl = @import("codegen/hlsl.zig").gen; 9 | const genMsl = @import("codegen/msl.zig").gen; 10 | const genSpirv = @import("codegen/spirv.zig").gen; 11 | 12 | pub const Language = enum { 13 | glsl, 14 | hlsl, 15 | msl, 16 | spirv, 17 | }; 18 | 19 | pub const DebugInfo = struct { 20 | emit_source_file: ?[]const u8 = null, 21 | emit_names: bool = true, 22 | }; 23 | 24 | pub const Stage = enum { 25 | vertex, 26 | fragment, 27 | compute, 28 | }; 29 | 30 | pub const Entrypoint = struct { 31 | stage: Stage, 32 | name: [*:0]const u8, 33 | }; 34 | 35 | pub const BindingPoint = struct { group: u32, binding: u32 }; 36 | pub const BindingTable = std.AutoHashMapUnmanaged(BindingPoint, u32); 37 | 38 | pub fn generate( 39 | allocator: std.mem.Allocator, 40 | air: *const Air, 41 | out_lang: Language, 42 | use_spirv_cross: bool, 43 | debug_info: DebugInfo, 44 | entrypoint: ?Entrypoint, 45 | bindings: ?*const BindingTable, 46 | label: ?[*:0]const u8, 47 | ) ![]const u8 { 48 | _ = use_spirv_cross; 49 | // if (!use_spirv_cross) { 50 | // const spirv_data = try genSpirv(allocator, air, .{}); 51 | // const spirv_data_z = try allocator.dupeZ(u8, spirv_data); 52 | // defer allocator.free(spirv_data_z); 53 | // allocator.free(spirv_data); 54 | 55 | // const spirv_words_ptr = @as([*]const u32, @ptrCast(@alignCast(spirv_data_z.ptr))); 56 | // const spirv_words = spirv_words_ptr[0 .. spirv_data_z.len / @sizeOf(u32)]; 57 | 58 | // // Optimize 59 | // var optimized_spirv: c.spv_binary = undefined; 60 | // const target_env = spvTargetEnv(out_lang); 61 | // const optimizer = c.spvOptimizerCreate(target_env); 62 | // defer c.spvOptimizerDestroy(optimizer); 63 | 64 | // c.spvOptimizerSetMessageConsumer(optimizer, spvMessageConsumer); 65 | // c.spvOptimizerRegisterPerformancePasses(optimizer); 66 | // c.spvOptimizerRegisterLegalizationPasses(optimizer); 67 | 68 | // const opt_options = c.spvOptimizerOptionsCreate(); 69 | // defer c.spvOptimizerOptionsDestroy(opt_options); 70 | // c.spvOptimizerOptionsSetRunValidator(opt_options, false); 71 | 72 | // var res = c.spvOptimizerRun( 73 | // optimizer, 74 | // spirv_words.ptr, 75 | // spirv_words.len, 76 | // &optimized_spirv, 77 | // opt_options, 78 | // ); 79 | // switch (res) { 80 | // c.SPV_SUCCESS => {}, 81 | // else => return error.SpvOptimizerFailed, 82 | // } 83 | 84 | // if (out_lang == .spirv) { 85 | // const code_bytes_ptr = @as([*]const u8, @ptrCast(optimized_spirv.*.code)); 86 | // const code_bytes = code_bytes_ptr[0 .. optimized_spirv.*.wordCount * @sizeOf(u32)]; 87 | // return allocator.dupe(u8, code_bytes); 88 | // } 89 | 90 | // // Translate 91 | // var context: c.spvc_context = undefined; 92 | // _ = c.spvc_context_create(&context); 93 | // defer c.spvc_context_destroy(context); 94 | // c.spvc_context_set_error_callback(context, spvcErrorCallback, null); 95 | 96 | // var ir: c.spvc_parsed_ir = undefined; 97 | // _ = c.spvc_context_parse_spirv(context, optimized_spirv.*.code, optimized_spirv.*.wordCount, &ir); 98 | 99 | // var compiler: c.spvc_compiler = undefined; 100 | // _ = c.spvc_context_create_compiler( 101 | // context, 102 | // spvcBackend(out_lang), 103 | // ir, 104 | // c.SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, 105 | // &compiler, 106 | // ); 107 | 108 | // var resources: c.spvc_resources = undefined; 109 | // _ = c.spvc_compiler_create_shader_resources(compiler, &resources); 110 | 111 | // var options: c.spvc_compiler_options = undefined; 112 | // _ = c.spvc_compiler_create_compiler_options(compiler, &options); 113 | // switch (out_lang) { 114 | // .glsl => { 115 | // const resource_types = [_]c.spvc_resource_type{ 116 | // c.SPVC_RESOURCE_TYPE_UNIFORM_BUFFER, 117 | // c.SPVC_RESOURCE_TYPE_STORAGE_BUFFER, 118 | // c.SPVC_RESOURCE_TYPE_STORAGE_IMAGE, 119 | // c.SPVC_RESOURCE_TYPE_SAMPLED_IMAGE, 120 | // c.SPVC_RESOURCE_TYPE_SEPARATE_IMAGE, 121 | // c.SPVC_RESOURCE_TYPE_SEPARATE_SAMPLERS, 122 | // }; 123 | // for (resource_types) |resource_type| { 124 | // glslRemapResources(compiler, resources, resource_type, bindings orelse &.{}); 125 | // } 126 | 127 | // _ = c.spvc_compiler_options_set_uint(options, c.SPVC_COMPILER_OPTION_GLSL_VERSION, 450); 128 | // _ = c.spvc_compiler_options_set_bool(options, c.SPVC_COMPILER_OPTION_GLSL_ES, c.SPVC_FALSE); 129 | // if (entrypoint) |e| { 130 | // _ = c.spvc_compiler_set_entry_point(compiler, e.name, spvExecutionModel(e.stage)); 131 | // } 132 | 133 | // // combiner samplers/textures 134 | // var id: c.spvc_variable_id = undefined; 135 | // res = c.spvc_compiler_build_dummy_sampler_for_combined_images(compiler, &id); 136 | // if (res == c.SPVC_SUCCESS) { 137 | // c.spvc_compiler_set_decoration(compiler, id, c.SpvDecorationDescriptorSet, 0); 138 | // c.spvc_compiler_set_decoration(compiler, id, c.SpvDecorationBinding, 0); 139 | // } 140 | // _ = c.spvc_compiler_build_combined_image_samplers(compiler); 141 | // }, 142 | // else => @panic("TODO"), 143 | // } 144 | // _ = c.spvc_compiler_install_compiler_options(compiler, options); 145 | 146 | // var source: [*c]const u8 = undefined; 147 | // _ = c.spvc_compiler_compile(compiler, &source); 148 | 149 | // return allocator.dupe(u8, std.mem.span(source)); 150 | // } 151 | 152 | // Direct translation 153 | return switch (out_lang) { 154 | .spirv => try genSpirv(allocator, air, debug_info), 155 | .hlsl => try genHlsl(allocator, air, debug_info), 156 | .msl => try genMsl(allocator, air, debug_info, entrypoint, bindings, label orelse ""), 157 | .glsl => try genGlsl(allocator, air, debug_info, entrypoint, bindings), 158 | }; 159 | } 160 | 161 | fn spvMessageConsumer( 162 | level: c.spv_message_level_t, 163 | src: [*c]const u8, 164 | pos: [*c]const c.spv_position_t, 165 | msg: [*c]const u8, 166 | ) callconv(.C) void { 167 | switch (level) { 168 | c.SPV_MSG_FATAL, 169 | c.SPV_MSG_INTERNAL_ERROR, 170 | c.SPV_MSG_ERROR, 171 | => { 172 | // TODO - don't panic 173 | std.debug.panic("{s} at :{d}:{d}\n{s}", .{ 174 | std.mem.span(msg), 175 | pos.*.line, 176 | pos.*.column, 177 | std.mem.span(src), 178 | }); 179 | }, 180 | else => {}, 181 | } 182 | } 183 | 184 | fn spvTargetEnv(language: Language) c.spv_target_env { 185 | return switch (language) { 186 | .glsl => c.SPV_ENV_OPENGL_4_5, 187 | .spirv => c.SPV_ENV_VULKAN_1_0, 188 | else => unreachable, 189 | }; 190 | } 191 | 192 | fn spvExecutionModel(stage: Stage) c.SpvExecutionModel { 193 | return switch (stage) { 194 | .vertex => c.SpvExecutionModelVertex, 195 | .fragment => c.SpvExecutionModelFragment, 196 | .compute => c.SpvExecutionModelGLCompute, 197 | }; 198 | } 199 | 200 | fn spvcErrorCallback(userdata: ?*anyopaque, err: [*c]const u8) callconv(.C) void { 201 | _ = userdata; 202 | // TODO - don't panic 203 | @panic(std.mem.span(err)); 204 | } 205 | 206 | fn spvcBackend(language: Language) c_uint { 207 | return switch (language) { 208 | .glsl => c.SPVC_BACKEND_GLSL, 209 | .hlsl => c.SPVC_BACKEND_HLSL, 210 | .msl => c.SPVC_BACKEND_MSL, 211 | .spirv => unreachable, 212 | }; 213 | } 214 | 215 | fn glslRemapResources( 216 | compiler: c.spvc_compiler, 217 | resources: c.spvc_resources, 218 | resource_type: c.spvc_resource_type, 219 | bindings: *const BindingTable, 220 | ) void { 221 | var resource_list: [*c]c.spvc_reflected_resource = undefined; 222 | var resource_size: usize = undefined; 223 | _ = c.spvc_resources_get_resource_list_for_type(resources, resource_type, &resource_list, &resource_size); 224 | 225 | for (resource_list[0..resource_size]) |resource| { 226 | const key = BindingPoint{ 227 | .group = c.spvc_compiler_get_decoration(compiler, resource.id, c.SpvDecorationDescriptorSet), 228 | .binding = c.spvc_compiler_get_decoration(compiler, resource.id, c.SpvDecorationBinding), 229 | }; 230 | 231 | if (bindings.get(key)) |slot| { 232 | _ = c.spvc_compiler_unset_decoration(compiler, resource.id, c.SpvDecorationDescriptorSet); 233 | _ = c.spvc_compiler_set_decoration(compiler, resource.id, c.SpvDecorationBinding, slot); 234 | } 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /src/sysgpu/texture.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const Bool32 = @import("main.zig").Bool32; 3 | const ChainedStruct = @import("main.zig").ChainedStruct; 4 | const TextureView = @import("texture_view.zig").TextureView; 5 | const Extent3D = @import("main.zig").Extent3D; 6 | const Impl = @import("interface.zig").Impl; 7 | const types = @import("main.zig"); 8 | const dawn = @import("dawn.zig"); 9 | 10 | pub const Texture = opaque { 11 | pub const Aspect = enum(u32) { 12 | all = 0x00000000, 13 | stencil_only = 0x00000001, 14 | depth_only = 0x00000002, 15 | plane0_only = 0x00000003, 16 | plane1_only = 0x00000004, 17 | }; 18 | 19 | pub const Dimension = enum(u32) { 20 | dimension_1d = 0x00000000, 21 | dimension_2d = 0x00000001, 22 | dimension_3d = 0x00000002, 23 | }; 24 | 25 | pub const Format = enum(u32) { 26 | undefined = 0x00000000, 27 | r8_unorm = 0x00000001, 28 | r8_snorm = 0x00000002, 29 | r8_uint = 0x00000003, 30 | r8_sint = 0x00000004, 31 | r16_uint = 0x00000005, 32 | r16_sint = 0x00000006, 33 | r16_float = 0x00000007, 34 | rg8_unorm = 0x00000008, 35 | rg8_snorm = 0x00000009, 36 | rg8_uint = 0x0000000a, 37 | rg8_sint = 0x0000000b, 38 | r32_float = 0x0000000c, 39 | r32_uint = 0x0000000d, 40 | r32_sint = 0x0000000e, 41 | rg16_uint = 0x0000000f, 42 | rg16_sint = 0x00000010, 43 | rg16_float = 0x00000011, 44 | rgba8_unorm = 0x00000012, 45 | rgba8_unorm_srgb = 0x00000013, 46 | rgba8_snorm = 0x00000014, 47 | rgba8_uint = 0x00000015, 48 | rgba8_sint = 0x00000016, 49 | bgra8_unorm = 0x00000017, 50 | bgra8_unorm_srgb = 0x00000018, 51 | rgb10_a2_unorm = 0x00000019, 52 | rg11_b10_ufloat = 0x0000001a, 53 | rgb9_e5_ufloat = 0x0000001b, 54 | rg32_float = 0x0000001c, 55 | rg32_uint = 0x0000001d, 56 | rg32_sint = 0x0000001e, 57 | rgba16_uint = 0x0000001f, 58 | rgba16_sint = 0x00000020, 59 | rgba16_float = 0x00000021, 60 | rgba32_float = 0x00000022, 61 | rgba32_uint = 0x00000023, 62 | rgba32_sint = 0x00000024, 63 | stencil8 = 0x00000025, 64 | depth16_unorm = 0x00000026, 65 | depth24_plus = 0x00000027, 66 | depth24_plus_stencil8 = 0x00000028, 67 | depth32_float = 0x00000029, 68 | depth32_float_stencil8 = 0x0000002a, 69 | bc1_rgba_unorm = 0x0000002b, 70 | bc1_rgba_unorm_srgb = 0x0000002c, 71 | bc2_rgba_unorm = 0x0000002d, 72 | bc2_rgba_unorm_srgb = 0x0000002e, 73 | bc3_rgba_unorm = 0x0000002f, 74 | bc3_rgba_unorm_srgb = 0x00000030, 75 | bc4_runorm = 0x00000031, 76 | bc4_rsnorm = 0x00000032, 77 | bc5_rg_unorm = 0x00000033, 78 | bc5_rg_snorm = 0x00000034, 79 | bc6_hrgb_ufloat = 0x00000035, 80 | bc6_hrgb_float = 0x00000036, 81 | bc7_rgba_unorm = 0x00000037, 82 | bc7_rgba_unorm_srgb = 0x00000038, 83 | etc2_rgb8_unorm = 0x00000039, 84 | etc2_rgb8_unorm_srgb = 0x0000003a, 85 | etc2_rgb8_a1_unorm = 0x0000003b, 86 | etc2_rgb8_a1_unorm_srgb = 0x0000003c, 87 | etc2_rgba8_unorm = 0x0000003d, 88 | etc2_rgba8_unorm_srgb = 0x0000003e, 89 | eacr11_unorm = 0x0000003f, 90 | eacr11_snorm = 0x00000040, 91 | eacrg11_unorm = 0x00000041, 92 | eacrg11_snorm = 0x00000042, 93 | astc4x4_unorm = 0x00000043, 94 | astc4x4_unorm_srgb = 0x00000044, 95 | astc5x4_unorm = 0x00000045, 96 | astc5x4_unorm_srgb = 0x00000046, 97 | astc5x5_unorm = 0x00000047, 98 | astc5x5_unorm_srgb = 0x00000048, 99 | astc6x5_unorm = 0x00000049, 100 | astc6x5_unorm_srgb = 0x0000004a, 101 | astc6x6_unorm = 0x0000004b, 102 | astc6x6_unorm_srgb = 0x0000004c, 103 | astc8x5_unorm = 0x0000004d, 104 | astc8x5_unorm_srgb = 0x0000004e, 105 | astc8x6_unorm = 0x0000004f, 106 | astc8x6_unorm_srgb = 0x00000050, 107 | astc8x8_unorm = 0x00000051, 108 | astc8x8_unorm_srgb = 0x00000052, 109 | astc10x5_unorm = 0x00000053, 110 | astc10x5_unorm_srgb = 0x00000054, 111 | astc10x6_unorm = 0x00000055, 112 | astc10x6_unorm_srgb = 0x00000056, 113 | astc10x8_unorm = 0x00000057, 114 | astc10x8_unorm_srgb = 0x00000058, 115 | astc10x10_unorm = 0x00000059, 116 | astc10x10_unorm_srgb = 0x0000005a, 117 | astc12x10_unorm = 0x0000005b, 118 | astc12x10_unorm_srgb = 0x0000005c, 119 | astc12x12_unorm = 0x0000005d, 120 | astc12x12_unorm_srgb = 0x0000005e, 121 | r8_bg8_biplanar420_unorm = 0x0000005f, 122 | }; 123 | 124 | pub const SampleType = enum(u32) { 125 | undefined = 0x00000000, 126 | float = 0x00000001, 127 | unfilterable_float = 0x00000002, 128 | depth = 0x00000003, 129 | sint = 0x00000004, 130 | uint = 0x00000005, 131 | }; 132 | 133 | pub const UsageFlags = packed struct(u32) { 134 | copy_src: bool = false, 135 | copy_dst: bool = false, 136 | texture_binding: bool = false, 137 | storage_binding: bool = false, 138 | render_attachment: bool = false, 139 | transient_attachment: bool = false, 140 | 141 | _padding: u26 = 0, 142 | 143 | comptime { 144 | std.debug.assert( 145 | @sizeOf(@This()) == @sizeOf(u32) and 146 | @bitSizeOf(@This()) == @bitSizeOf(u32), 147 | ); 148 | } 149 | 150 | pub const none = UsageFlags{}; 151 | 152 | pub fn equal(a: UsageFlags, b: UsageFlags) bool { 153 | return @as(u6, @truncate(@as(u32, @bitCast(a)))) == @as(u6, @truncate(@as(u32, @bitCast(b)))); 154 | } 155 | }; 156 | 157 | pub const BindingLayout = extern struct { 158 | next_in_chain: ?*const ChainedStruct = null, 159 | sample_type: SampleType = .undefined, 160 | view_dimension: TextureView.Dimension = .dimension_undefined, 161 | multisampled: Bool32 = .false, 162 | }; 163 | 164 | pub const DataLayout = extern struct { 165 | next_in_chain: ?*const ChainedStruct = null, 166 | offset: u64 = 0, 167 | bytes_per_row: u32 = types.copy_stride_undefined, 168 | rows_per_image: u32 = types.copy_stride_undefined, 169 | }; 170 | 171 | pub const Descriptor = extern struct { 172 | pub const NextInChain = extern union { 173 | generic: ?*const ChainedStruct, 174 | dawn_texture_internal_usage_descriptor: *const dawn.TextureInternalUsageDescriptor, 175 | }; 176 | 177 | next_in_chain: NextInChain = .{ .generic = null }, 178 | label: ?[*:0]const u8 = null, 179 | usage: UsageFlags, 180 | dimension: Dimension = .dimension_2d, 181 | size: Extent3D, 182 | format: Format, 183 | mip_level_count: u32 = 1, 184 | sample_count: u32 = 1, 185 | view_format_count: usize = 0, 186 | view_formats: ?[*]const Format = null, 187 | 188 | /// Provides a slightly friendlier Zig API to initialize this structure. 189 | pub inline fn init(v: struct { 190 | next_in_chain: NextInChain = .{ .generic = null }, 191 | label: ?[*:0]const u8 = null, 192 | usage: UsageFlags, 193 | dimension: Dimension = .dimension_2d, 194 | size: Extent3D, 195 | format: Format, 196 | mip_level_count: u32 = 1, 197 | sample_count: u32 = 1, 198 | view_formats: ?[]const Format = null, 199 | }) Descriptor { 200 | return .{ 201 | .next_in_chain = v.next_in_chain, 202 | .label = v.label, 203 | .usage = v.usage, 204 | .dimension = v.dimension, 205 | .size = v.size, 206 | .format = v.format, 207 | .mip_level_count = v.mip_level_count, 208 | .sample_count = v.sample_count, 209 | .view_format_count = if (v.view_formats) |e| e.len else 0, 210 | .view_formats = if (v.view_formats) |e| e.ptr else null, 211 | }; 212 | } 213 | }; 214 | 215 | pub inline fn createView(texture: *Texture, descriptor: ?*const TextureView.Descriptor) *TextureView { 216 | return Impl.textureCreateView(texture, descriptor); 217 | } 218 | 219 | pub inline fn destroy(texture: *Texture) void { 220 | Impl.textureDestroy(texture); 221 | } 222 | 223 | pub inline fn getDepthOrArrayLayers(texture: *Texture) u32 { 224 | return Impl.textureGetDepthOrArrayLayers(texture); 225 | } 226 | 227 | pub inline fn getDimension(texture: *Texture) Dimension { 228 | return Impl.textureGetDimension(texture); 229 | } 230 | 231 | pub inline fn getFormat(texture: *Texture) Format { 232 | return Impl.textureGetFormat(texture); 233 | } 234 | 235 | pub inline fn getHeight(texture: *Texture) u32 { 236 | return Impl.textureGetHeight(texture); 237 | } 238 | 239 | pub inline fn getMipLevelCount(texture: *Texture) u32 { 240 | return Impl.textureGetMipLevelCount(texture); 241 | } 242 | 243 | pub inline fn getSampleCount(texture: *Texture) u32 { 244 | return Impl.textureGetSampleCount(texture); 245 | } 246 | 247 | pub inline fn getUsage(texture: *Texture) UsageFlags { 248 | return Impl.textureGetUsage(texture); 249 | } 250 | 251 | pub inline fn getWidth(texture: *Texture) u32 { 252 | return Impl.textureGetWidth(texture); 253 | } 254 | 255 | pub inline fn setLabel(texture: *Texture, label: [*:0]const u8) void { 256 | Impl.textureSetLabel(texture, label); 257 | } 258 | 259 | pub inline fn reference(texture: *Texture) void { 260 | Impl.textureReference(texture); 261 | } 262 | 263 | pub inline fn release(texture: *Texture) void { 264 | Impl.textureRelease(texture); 265 | } 266 | }; 267 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /src/shader/codegen/spirv/Section.zig: -------------------------------------------------------------------------------- 1 | //! Borrowed from Zig compiler codebase with changes. 2 | //! Licensed under LICENSE-ZIG 3 | //! 4 | //! Represents a section or subsection of instructions in a SPIR-V binary. Instructions can be append 5 | //! to separate sections, which can then later be merged into the final binary. 6 | 7 | const std = @import("std"); 8 | const spec = @import("spec.zig"); 9 | const Opcode = spec.Opcode; 10 | const Word = spec.Word; 11 | 12 | const DoubleWord = std.meta.Int(.unsigned, @bitSizeOf(Word) * 2); 13 | const Log2Word = std.math.Log2Int(Word); 14 | 15 | const Section = @This(); 16 | 17 | allocator: std.mem.Allocator, 18 | words: std.ArrayListUnmanaged(Word) = .{}, 19 | 20 | pub fn deinit(section: *Section) void { 21 | section.words.deinit(section.allocator); 22 | } 23 | 24 | pub fn toWords(section: Section) []Word { 25 | return section.words.items; 26 | } 27 | 28 | /// Append the words from another section into this section. 29 | pub fn append(section: *Section, other_section: Section) !void { 30 | try section.words.appendSlice(section.allocator, other_section.words.items); 31 | } 32 | 33 | /// Ensure capacity of at least `capacity` more words in this section. 34 | pub fn ensureUnusedCapacity(section: *Section, capacity: usize) !void { 35 | try section.words.ensureUnusedCapacity(section.allocator, capacity); 36 | } 37 | 38 | /// Write an instruction and size, operands are to be inserted manually. 39 | pub fn emitRaw( 40 | section: *Section, 41 | opcode: Opcode, 42 | operand_words: usize, // opcode itself not included 43 | ) !void { 44 | const word_count = 1 + operand_words; 45 | try section.words.ensureUnusedCapacity(section.allocator, word_count); 46 | section.writeWord(@as(Word, @intCast(word_count << 16)) | @intFromEnum(opcode)); 47 | } 48 | 49 | pub fn emit( 50 | section: *Section, 51 | comptime opcode: spec.Opcode, 52 | operands: opcode.Operands(), 53 | ) !void { 54 | const word_count = instructionSize(opcode, operands); 55 | try section.ensureUnusedCapacity(word_count); 56 | section.writeWord(@as(Word, @intCast(word_count << 16)) | @intFromEnum(opcode)); 57 | section.writeOperands(opcode.Operands(), operands); 58 | } 59 | 60 | pub fn emitSpecConstantOp( 61 | section: *Section, 62 | comptime opcode: spec.Opcode, 63 | operands: opcode.Operands(), 64 | ) !void { 65 | const word_count = operandsSize(opcode.Operands(), operands); 66 | try section.emitRaw(.OpSpecConstantOp, 1 + word_count); 67 | section.writeOperand(spec.IdRef, operands.id_result_type); 68 | section.writeOperand(spec.IdRef, operands.id_result); 69 | section.writeOperand(Opcode, opcode); 70 | 71 | const fields = @typeInfo(opcode.Operands()).Struct.fields; 72 | // First 2 fields are always id_result_type and id_result. 73 | inline for (fields[2..]) |field| { 74 | section.writeOperand(field.type, @field(operands, field.name)); 75 | } 76 | } 77 | 78 | pub fn writeWord(section: *Section, word: Word) void { 79 | section.words.appendAssumeCapacity(word); 80 | } 81 | 82 | pub fn writeWords(section: *Section, words: []const Word) void { 83 | section.words.appendSliceAssumeCapacity(words); 84 | } 85 | 86 | pub fn writeDoubleWord(section: *Section, dword: DoubleWord) void { 87 | section.writeWords(&[_]Word{ 88 | @truncate(dword), 89 | @truncate(dword >> @bitSizeOf(Word)), 90 | }); 91 | } 92 | 93 | fn writeOperands(section: *Section, comptime Operands: type, operands: Operands) void { 94 | const fields = switch (@typeInfo(Operands)) { 95 | .Struct => |info| info.fields, 96 | .Void => return, 97 | else => unreachable, 98 | }; 99 | 100 | inline for (fields) |field| { 101 | section.writeOperand(field.type, @field(operands, field.name)); 102 | } 103 | } 104 | 105 | pub fn writeOperand(section: *Section, comptime Operand: type, operand: Operand) void { 106 | switch (Operand) { 107 | spec.IdResult => section.writeWord(operand.id), 108 | spec.LiteralInteger => section.writeWord(operand), 109 | spec.LiteralString => section.writeString(operand), 110 | spec.LiteralContextDependentNumber => section.writeContextDependentNumber(operand), 111 | spec.LiteralExtInstInteger => section.writeWord(operand.inst), 112 | 113 | // TODO: Where this type is used (OpSpecConstantOp) is currently not correct in the spec json, 114 | // so it most likely needs to be altered into something that can actually describe the entire 115 | // instruction in which it is used. 116 | spec.LiteralSpecConstantOpInteger => section.writeWord(@intFromEnum(operand.opcode)), 117 | 118 | spec.PairLiteralIntegerIdRef => section.writeWords(&.{ operand.value, operand.label.id }), 119 | spec.PairIdRefLiteralInteger => section.writeWords(&.{ operand.target.id, operand.member }), 120 | spec.PairIdRefIdRef => section.writeWords(&.{ operand[0].id, operand[1].id }), 121 | else => switch (@typeInfo(Operand)) { 122 | .Enum => section.writeWord(@intFromEnum(operand)), 123 | .Optional => |info| if (operand) |child| { 124 | section.writeOperand(info.child, child); 125 | }, 126 | .Pointer => |info| { 127 | std.debug.assert(info.size == .Slice); // Should be no other pointer types in the spec. 128 | for (operand) |item| { 129 | section.writeOperand(info.child, item); 130 | } 131 | }, 132 | .Struct => |info| { 133 | if (info.layout == .Packed) { 134 | section.writeWord(@bitCast(operand)); 135 | } else { 136 | section.writeExtendedMask(Operand, operand); 137 | } 138 | }, 139 | .Union => section.writeExtendedUnion(Operand, operand), 140 | else => unreachable, 141 | }, 142 | } 143 | } 144 | 145 | fn writeString(section: *Section, str: []const u8) void { 146 | // TODO: Not actually sure whether this is correct for big-endian. 147 | // See https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#Literal 148 | const zero_terminated_len = str.len + 1; 149 | var i: usize = 0; 150 | while (i < zero_terminated_len) : (i += @sizeOf(Word)) { 151 | var word: Word = 0; 152 | 153 | var j: usize = 0; 154 | while (j < @sizeOf(Word) and i + j < str.len) : (j += 1) { 155 | word |= @as(Word, str[i + j]) << @as(Log2Word, @intCast(j * @bitSizeOf(u8))); 156 | } 157 | 158 | section.words.appendAssumeCapacity(word); 159 | } 160 | } 161 | 162 | fn writeContextDependentNumber(section: *Section, operand: spec.LiteralContextDependentNumber) void { 163 | switch (operand) { 164 | .int32 => |int| section.writeWord(@bitCast(int)), 165 | .uint32 => |int| section.writeWord(@bitCast(int)), 166 | .int64 => |int| section.writeDoubleWord(@bitCast(int)), 167 | .uint64 => |int| section.writeDoubleWord(@bitCast(int)), 168 | .float32 => |float| section.writeWord(@bitCast(float)), 169 | .float64 => |float| section.writeDoubleWord(@bitCast(float)), 170 | } 171 | } 172 | 173 | fn writeExtendedMask(section: *Section, comptime Operand: type, operand: Operand) void { 174 | var mask: Word = 0; 175 | inline for (@typeInfo(Operand).Struct.fields, 0..) |field, bit| { 176 | switch (@typeInfo(field.type)) { 177 | .Optional => if (@field(operand, field.name) != null) { 178 | mask |= 1 << @intCast(bit); 179 | }, 180 | .Bool => if (@field(operand, field.name)) { 181 | mask |= 1 << @intCast(bit); 182 | }, 183 | else => unreachable, 184 | } 185 | } 186 | 187 | section.writeWord(mask); 188 | 189 | inline for (@typeInfo(Operand).Struct.fields) |field| { 190 | switch (@typeInfo(field.type)) { 191 | .Optional => |info| if (@field(operand, field.name)) |child| { 192 | section.writeOperands(info.child, child); 193 | }, 194 | .Bool => {}, 195 | else => unreachable, 196 | } 197 | } 198 | } 199 | 200 | fn writeExtendedUnion(section: *Section, comptime Operand: type, operand: Operand) void { 201 | const tag = std.meta.activeTag(operand); 202 | section.writeWord(@intFromEnum(tag)); 203 | 204 | inline for (@typeInfo(Operand).Union.fields) |field| { 205 | if (@field(Operand, field.name) == tag) { 206 | section.writeOperands(field.type, @field(operand, field.name)); 207 | return; 208 | } 209 | } 210 | unreachable; 211 | } 212 | 213 | fn instructionSize(comptime opcode: spec.Opcode, operands: opcode.Operands()) usize { 214 | return 1 + operandsSize(opcode.Operands(), operands); 215 | } 216 | 217 | fn operandsSize(comptime Operands: type, operands: Operands) usize { 218 | const fields = switch (@typeInfo(Operands)) { 219 | .Struct => |info| info.fields, 220 | .Void => return 0, 221 | else => unreachable, 222 | }; 223 | 224 | var total: usize = 0; 225 | inline for (fields) |field| { 226 | total += operandSize(field.type, @field(operands, field.name)); 227 | } 228 | 229 | return total; 230 | } 231 | 232 | fn operandSize(comptime Operand: type, operand: Operand) usize { 233 | return switch (Operand) { 234 | spec.IdResult, 235 | spec.LiteralInteger, 236 | spec.LiteralExtInstInteger, 237 | => 1, 238 | // Add one for zero-terminator 239 | spec.LiteralString => std.math.divCeil(usize, operand.len + 1, @sizeOf(Word)) catch unreachable, 240 | spec.LiteralContextDependentNumber => switch (operand) { 241 | .int32, .uint32, .float32 => @as(usize, 1), 242 | .int64, .uint64, .float64 => @as(usize, 2), 243 | }, 244 | 245 | // TODO: Where this type is used (OpSpecConstantOp) is currently not correct in the spec 246 | // json, so it most likely needs to be altered into something that can actually 247 | // describe the entire insturction in which it is used. 248 | spec.LiteralSpecConstantOpInteger => 1, 249 | 250 | spec.PairLiteralIntegerIdRef, 251 | spec.PairIdRefLiteralInteger, 252 | spec.PairIdRefIdRef, 253 | => 2, 254 | else => switch (@typeInfo(Operand)) { 255 | .Enum => 1, 256 | .Optional => |info| if (operand) |child| operandSize(info.child, child) else 0, 257 | .Pointer => |info| blk: { 258 | std.debug.assert(info.size == .Slice); // Should be no other pointer types in the spec. 259 | var total: usize = 0; 260 | for (operand) |item| { 261 | total += operandSize(info.child, item); 262 | } 263 | break :blk total; 264 | }, 265 | .Struct => |info| if (info.layout == .Packed) 1 else extendedMaskSize(Operand, operand), 266 | .Union => extendedUnionSize(Operand, operand), 267 | else => unreachable, 268 | }, 269 | }; 270 | } 271 | 272 | fn extendedMaskSize(comptime Operand: type, operand: Operand) usize { 273 | var total: usize = 0; 274 | var any_set = false; 275 | inline for (@typeInfo(Operand).Struct.fields) |field| { 276 | switch (@typeInfo(field.type)) { 277 | .Optional => |info| if (@field(operand, field.name)) |child| { 278 | total += operandsSize(info.child, child); 279 | any_set = true; 280 | }, 281 | .Bool => if (@field(operand, field.name)) { 282 | any_set = true; 283 | }, 284 | else => unreachable, 285 | } 286 | } 287 | if (!any_set) { 288 | return 0; 289 | } 290 | return total + 1; // Add one for the mask itself. 291 | } 292 | 293 | fn extendedUnionSize(comptime Operand: type, operand: Operand) usize { 294 | const tag = std.meta.activeTag(operand); 295 | inline for (@typeInfo(Operand).Union.fields) |field| { 296 | if (@field(Operand, field.name) == tag) { 297 | // Add one for the tag itself. 298 | return 1 + operandsSize(field.type, @field(operand, field.name)); 299 | } 300 | } 301 | unreachable; 302 | } 303 | -------------------------------------------------------------------------------- /src/shader/Tokenizer.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const Token = @import("Token.zig"); 3 | 4 | const Tokenizer = @This(); 5 | 6 | source: [:0]const u8, 7 | index: u32 = 0, 8 | 9 | const State = union(enum) { 10 | start, 11 | ident, 12 | underscore, 13 | number: struct { 14 | is_hex: bool = false, 15 | allow_leading_sign: bool = false, 16 | has_dot: bool = false, 17 | }, 18 | block_comment, 19 | ampersand, 20 | bang, 21 | equal, 22 | angle_bracket_left, 23 | angle_bracket_angle_bracket_left, 24 | angle_bracket_right, 25 | angle_bracket_angle_bracket_right, 26 | minus, 27 | percent, 28 | dot, 29 | pipe, 30 | plus, 31 | slash, 32 | asterisk, 33 | xor, 34 | }; 35 | 36 | pub fn init(source: [:0]const u8) Tokenizer { 37 | // skip the UTF-8 BOM if present 38 | const src_start: u32 = if (std.mem.startsWith(u8, source, "\xEF\xBB\xBF")) 3 else 0; 39 | return Tokenizer{ .source = source[src_start..] }; 40 | } 41 | 42 | pub fn peek(tokenizer: *Tokenizer) Token { 43 | var index = tokenizer.index; 44 | var state: State = .start; 45 | var result = Token{ 46 | .tag = .eof, 47 | .loc = .{ 48 | .start = index, 49 | .end = undefined, 50 | }, 51 | }; 52 | 53 | while (true) : (index += 1) { 54 | const c = tokenizer.source[index]; 55 | switch (state) { 56 | .start => switch (c) { 57 | 0 => { 58 | if (index != tokenizer.source.len) { 59 | result.tag = .invalid; 60 | index += 1; 61 | } 62 | break; 63 | }, 64 | ' ', '\n', '\t', '\r' => result.loc.start = index + 1, 65 | 66 | 'a'...'z', 'A'...'Z' => state = .ident, 67 | '0'...'9' => state = .{ .number = .{} }, 68 | '&' => state = .ampersand, 69 | '!' => state = .bang, 70 | '=' => state = .equal, 71 | '<' => state = .angle_bracket_left, 72 | '>' => state = .angle_bracket_right, 73 | '-' => state = .minus, 74 | '%' => state = .percent, 75 | '.' => state = .dot, 76 | '|' => state = .pipe, 77 | '+' => state = .plus, 78 | '/' => state = .slash, 79 | '*' => state = .asterisk, 80 | '_' => state = .underscore, 81 | '^' => state = .xor, 82 | 83 | '@' => { 84 | result.tag = .attr; 85 | index += 1; 86 | break; 87 | }, 88 | '[' => { 89 | result.tag = .bracket_left; 90 | index += 1; 91 | break; 92 | }, 93 | ']' => { 94 | result.tag = .bracket_right; 95 | index += 1; 96 | break; 97 | }, 98 | '{' => { 99 | result.tag = .brace_left; 100 | index += 1; 101 | break; 102 | }, 103 | '}' => { 104 | result.tag = .brace_right; 105 | index += 1; 106 | break; 107 | }, 108 | ':' => { 109 | result.tag = .colon; 110 | index += 1; 111 | break; 112 | }, 113 | ',' => { 114 | result.tag = .comma; 115 | index += 1; 116 | break; 117 | }, 118 | '(' => { 119 | result.tag = .paren_left; 120 | index += 1; 121 | break; 122 | }, 123 | ')' => { 124 | result.tag = .paren_right; 125 | index += 1; 126 | break; 127 | }, 128 | ';' => { 129 | result.tag = .semicolon; 130 | index += 1; 131 | break; 132 | }, 133 | '~' => { 134 | result.tag = .tilde; 135 | index += 1; 136 | break; 137 | }, 138 | 139 | else => { 140 | result.tag = .invalid; 141 | index += 1; 142 | break; 143 | }, 144 | }, 145 | .ident => switch (c) { 146 | 'a'...'z', 'A'...'Z', '0'...'9', '_' => {}, 147 | else => { 148 | result.tag = .ident; 149 | if (Token.keywords.get(tokenizer.source[result.loc.start..index])) |tag| { 150 | result.tag = tag; 151 | } else if (Token.reserved.get(tokenizer.source[result.loc.start..index])) |_| { 152 | result.tag = .invalid; 153 | } 154 | break; 155 | }, 156 | }, 157 | .underscore => switch (c) { // TODO: two underscore `__` https://www.w3.org/TR/WGSL/#identifiers 158 | 'a'...'z', 'A'...'Z', '_', '0'...'9' => state = .ident, 159 | else => { 160 | result.tag = .underscore; 161 | break; 162 | }, 163 | }, 164 | .number => |*number| { 165 | result.tag = .number; 166 | switch (c) { 167 | '0'...'9' => {}, 168 | 'a'...'d', 'A'...'D' => if (!number.is_hex) break, 169 | 'x', 'X' => number.is_hex = true, 170 | '.' => { 171 | if (number.has_dot) break; 172 | number.has_dot = true; 173 | }, 174 | '+', '-' => { 175 | if (!number.allow_leading_sign) break; 176 | number.allow_leading_sign = false; 177 | number.is_hex = false; 178 | }, 179 | 'e', 'E' => if (!number.is_hex) { 180 | number.allow_leading_sign = true; 181 | }, 182 | 'p', 'P' => if (number.is_hex) { 183 | number.allow_leading_sign = true; 184 | }, 185 | 'i', 'u' => { 186 | index += 1; 187 | break; 188 | }, 189 | 'f', 'h' => if (!number.is_hex) { 190 | index += 1; 191 | break; 192 | }, 193 | else => break, 194 | } 195 | }, 196 | .block_comment => switch (c) { 197 | 0 => break, 198 | '\n' => { 199 | state = .start; 200 | result.loc.start = index + 1; 201 | }, 202 | else => {}, 203 | }, 204 | .ampersand => switch (c) { 205 | '&' => { 206 | result.tag = .ampersand_ampersand; 207 | index += 1; 208 | break; 209 | }, 210 | '=' => { 211 | result.tag = .ampersand_equal; 212 | index += 1; 213 | break; 214 | }, 215 | else => { 216 | result.tag = .ampersand; 217 | break; 218 | }, 219 | }, 220 | .bang => switch (c) { 221 | '=' => { 222 | result.tag = .bang_equal; 223 | index += 1; 224 | break; 225 | }, 226 | else => { 227 | result.tag = .bang; 228 | break; 229 | }, 230 | }, 231 | .equal => switch (c) { 232 | '=' => { 233 | result.tag = .equal_equal; 234 | index += 1; 235 | break; 236 | }, 237 | else => { 238 | result.tag = .equal; 239 | break; 240 | }, 241 | }, 242 | .angle_bracket_left => switch (c) { 243 | '<' => state = .angle_bracket_angle_bracket_left, 244 | '=' => { 245 | result.tag = .angle_bracket_left_equal; 246 | index += 1; 247 | break; 248 | }, 249 | else => { 250 | result.tag = .angle_bracket_left; 251 | break; 252 | }, 253 | }, 254 | .angle_bracket_angle_bracket_left => switch (c) { 255 | '=' => { 256 | result.tag = .angle_bracket_angle_bracket_left_equal; 257 | index += 1; 258 | break; 259 | }, 260 | else => { 261 | result.tag = .angle_bracket_angle_bracket_left; 262 | break; 263 | }, 264 | }, 265 | .angle_bracket_right => switch (c) { 266 | '>' => state = .angle_bracket_angle_bracket_right, 267 | '=' => { 268 | result.tag = .angle_bracket_right_equal; 269 | index += 1; 270 | break; 271 | }, 272 | else => { 273 | result.tag = .angle_bracket_right; 274 | break; 275 | }, 276 | }, 277 | .angle_bracket_angle_bracket_right => switch (c) { 278 | '=' => { 279 | result.tag = .angle_bracket_angle_bracket_right_equal; 280 | index += 1; 281 | break; 282 | }, 283 | else => { 284 | result.tag = .angle_bracket_angle_bracket_right; 285 | break; 286 | }, 287 | }, 288 | .minus => switch (c) { 289 | '-' => { 290 | result.tag = .minus_minus; 291 | index += 1; 292 | break; 293 | }, 294 | '=' => { 295 | result.tag = .minus_equal; 296 | index += 1; 297 | break; 298 | }, 299 | '>' => { 300 | result.tag = .arrow; 301 | index += 1; 302 | break; 303 | }, 304 | '0'...'9' => { 305 | // workaround for x-1 being tokenized as [x] [-1] 306 | // TODO: maybe it's user fault? :^) 307 | // duplicated at .plus too 308 | if (index >= 2 and std.ascii.isAlphabetic(tokenizer.source[index - 2])) { 309 | result.tag = .minus; 310 | break; 311 | } 312 | state = .{ .number = .{} }; 313 | }, 314 | else => { 315 | result.tag = .minus; 316 | break; 317 | }, 318 | }, 319 | .percent => switch (c) { 320 | '=' => { 321 | result.tag = .percent_equal; 322 | index += 1; 323 | break; 324 | }, 325 | else => { 326 | result.tag = .percent; 327 | break; 328 | }, 329 | }, 330 | .pipe => switch (c) { 331 | '|' => { 332 | result.tag = .pipe_pipe; 333 | index += 1; 334 | break; 335 | }, 336 | '=' => { 337 | result.tag = .pipe_equal; 338 | index += 1; 339 | break; 340 | }, 341 | else => { 342 | result.tag = .pipe; 343 | break; 344 | }, 345 | }, 346 | .dot => switch (c) { 347 | '0'...'9' => state = .{ .number = .{} }, 348 | else => { 349 | result.tag = .dot; 350 | break; 351 | }, 352 | }, 353 | .plus => switch (c) { 354 | '+' => { 355 | result.tag = .plus_plus; 356 | index += 1; 357 | break; 358 | }, 359 | '=' => { 360 | result.tag = .plus_equal; 361 | index += 1; 362 | break; 363 | }, 364 | '0'...'9' => { 365 | if (index >= 2 and std.ascii.isAlphabetic(tokenizer.source[index - 2])) { 366 | result.tag = .plus; 367 | break; 368 | } 369 | state = .{ .number = .{} }; 370 | }, 371 | else => { 372 | result.tag = .plus; 373 | break; 374 | }, 375 | }, 376 | .slash => switch (c) { 377 | '/' => state = .block_comment, 378 | '=' => { 379 | result.tag = .slash_equal; 380 | index += 1; 381 | break; 382 | }, 383 | else => { 384 | result.tag = .slash; 385 | break; 386 | }, 387 | }, 388 | .asterisk => switch (c) { 389 | '=' => { 390 | result.tag = .asterisk_equal; 391 | index += 1; 392 | break; 393 | }, 394 | else => { 395 | result.tag = .asterisk; 396 | break; 397 | }, 398 | }, 399 | .xor => switch (c) { 400 | '=' => { 401 | result.tag = .xor_equal; 402 | index += 1; 403 | break; 404 | }, 405 | else => { 406 | result.tag = .xor; 407 | break; 408 | }, 409 | }, 410 | } 411 | } 412 | 413 | result.loc.end = index; 414 | return result; 415 | } 416 | 417 | pub fn next(tokenizer: *Tokenizer) Token { 418 | const tok = tokenizer.peek(); 419 | tokenizer.index = tok.loc.end; 420 | return tok; 421 | } 422 | 423 | // test "tokenize identifier and numbers" { 424 | // const str = 425 | // \\_ __ _iden iden -100i 100.8i // cc 426 | // \\// comment 427 | // \\ 428 | // ; 429 | // var tokenizer = Tokenizer.init(str); 430 | // try std.testing.expect(tokenizer.next().tag == .underscore); 431 | // try std.testing.expect(tokenizer.next().tag == .ident); 432 | // try std.testing.expect(tokenizer.next().tag == .ident); 433 | // try std.testing.expect(tokenizer.next().tag == .ident); 434 | // try std.testing.expectEqualStrings("-100i", tokenizer.next().loc.slice(str)); 435 | // try std.testing.expect(tokenizer.next().tag == .number); 436 | // try std.testing.expect(tokenizer.next().tag == .eof); 437 | // } 438 | --------------------------------------------------------------------------------