floooh / sokol

minimal cross-platform standalone C headers
https://floooh.github.io/sokol-html5
zlib License
7.12k stars 501 forks source link

Any plans on supporting writing WGSL for all gfx_backends? #1094

Closed seivan closed 3 months ago

seivan commented 3 months ago

This has been done for Rusts wgpu project that also does cross platform rendering. Except it supports using WGSL without any modification for each platform and does so at runtime with naga. I am sure you can pre-compile at boot if necessary or even at build, but it would be nice for having it at runtime just for ease of use.

This means a single wgsl file can be used cross platform for all backends (dx11, vulkan, metal, webgl and wgpu)

struct Globals {
    mvp: mat4x4<f32>,
    size: vec2<f32>,
    _pad0: u32,
    _pad1: u32,
};

struct Locals {
    position: vec2<f32>,
    velocity: vec2<f32>,
    color: u32,
    _pad0: u32,
    _pad1: u32,
    _pad2: u32,
};

@group(0)
@binding(0)
var<uniform> globals: Globals;

@group(1)
@binding(0)
var<uniform> locals: Locals;

struct VertexOutput {
    @builtin(position) position: vec4<f32>,
    @location(0) tex_coords: vec2<f32>,
    @location(1) color: vec4<f32>,
};

@vertex
fn vs_main(@builtin(vertex_index) vi: u32) -> VertexOutput {
    let tc = vec2<f32>(f32(vi & 1u), 0.5 * f32(vi & 2u));
    let offset = vec2<f32>(tc.x * globals.size.x, tc.y * globals.size.y);
    let pos = globals.mvp * vec4<f32>(locals.position + offset, 0.0, 1.0);
    let color = vec4<f32>((vec4<u32>(locals.color) >> vec4<u32>(0u, 8u, 16u, 24u)) & vec4<u32>(255u)) / 255.0;
    return VertexOutput(pos, tc, color);
}

@group(0)
@binding(1)
var tex: texture_2d<f32>;
@group(0)
@binding(2)
var sam: sampler;

@fragment
fn fs_main(vertex: VertexOutput) -> @location(0) vec4<f32> {
    return vertex.color * textureSampleLevel(tex, sam, vertex.tex_coords, 0.0);
}

This is also much better than generating outputs per language as well. I am currently trying to make sokol work with Swift and having to deal with build times and outputting specific swift shader code is a hassle. Being runtime generated and working out of the box would be a much smoother experience.

floooh commented 3 months ago

Maybe some time in the future, but only as offline tool similar to sokol-shdc (https://github.com/floooh/sokol-tools/blob/master/docs/sokol-shdc.md)

Adding a runtime shader compiler to sokol-gfx would add too much bloat to the library. But there's a feature request to turn sokol-shdc into a library: https://github.com/floooh/sokol-tools/issues/34 - so sokol-gfx would still take backend specific shader input, but applications could link against this library to get a runtime compilation solution. This would easily increase the binary size of an application executable by at least 10x though (from a few hundred KBytes to several MBytes).

I'm rolling around the idea in my head to switch the offline shader authoring language from GLSL to WGSL one day, if Tint is up to the task. Currently Tint is already used in sokol-shdc, but only for translating SPIRV into WGSL.

But Tint could also be used to translate WGSL into the various target shader languages, it's just unclear at the moment if Tint can support all requires targets (mainly HLSL5 and GLSL es300), because Tint has been written for WebGPU, but D3D11 and GLES3 aren't exactly 'first class' WebGPU backends.

If Tint can completely replace the combination of glslangValidator, SPIRVTools and SPIRVCross currently used in sokol-shdc, that would be a pretty big win. It's not very high on the priority list though.

floooh commented 3 months ago

and outputting specific swift shader code is a hassle

Btw: sokol-shdc has the bare_yaml target for such situations where a target language isn't directly supported. This gives you the output shaders as files, and a reflection file in a simple YAML format (you don't need a full YAML parser to parse this file). You can then either load this data at runtime, or use your own offline tool to generate Swift code.