Skip to content

Commit

Permalink
Add an experimental vertex pulling flag to Metal pipelines.
Browse files Browse the repository at this point in the history
This proves a flag in msl::PipelineOptions that attempts to write all
metal vertex entry points to use a vertex pulling technique. It does
this by:

1) Forcing the _buffer_sizes structure to be generated for all vertex
entry points.
2) Adding new args to vertex entry points for the vertex id and for the
bound buffers.
3) Adding code at the beginning of the function for vertex entry points
to compare the vertex id against the lengths of all the bound buffers,
and force an early-exit if the bounds are violated.
4) Extracting the raw bytes from the vertex buffer(s) and unpacking
those bytes into the bound attributes with the expected types.
5) Replacing the varyings input and instead using the unpacked
attributes to fill any structs-as-args that are rebuilt in the entry
point.

A new naga test is added which exercises this flag and demonstrates the
effect of the transform. The msl generated by this test  passes
validation.

Future work will add reftests confirming that transformed shaders
produce the same result as untransformed shaders. Eventually this
transformation will be the default behavior and the flag will be
removed.
  • Loading branch information
bradwerth committed Apr 22, 2024
1 parent 7840e75 commit bfb562d
Show file tree
Hide file tree
Showing 9 changed files with 1,224 additions and 51 deletions.
1 change: 1 addition & 0 deletions naga/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ For changelogs after v0.14, see [the wgpu changelog](../CHANGELOG.md).

- Add and fix minimum Metal version checks for optional functionality. ([#2486](https://github.com/gfx-rs/naga/pull/2486)) **@teoxoy**
- Make varyings' struct members unique. ([#2521](https://github.com/gfx-rs/naga/pull/2521)) **@evahop**
- Add experimental vertex pulling transform flag. ([#5254](https://github.com/gfx-rs/wgpu/pull/5254)) **@bradwerth**

#### GLSL-OUT

Expand Down
156 changes: 156 additions & 0 deletions naga/src/back/msl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,151 @@ impl Default for Options {
}
}

/// Corresponds to [WebGPU `GPUVertexFormat`](
/// https://gpuweb.github.io/gpuweb/#enumdef-gpuvertexformat).
#[repr(u32)]
#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
pub enum VertexFormat {
#[default]
/// Two unsigned bytes (u8). `vec2<u32>` in shaders.
Uint8x2 = 0,
/// Four unsigned bytes (u8). `vec4<u32>` in shaders.
Uint8x4 = 1,
/// Two signed bytes (i8). `vec2<i32>` in shaders.
Sint8x2 = 2,
/// Four signed bytes (i8). `vec4<i32>` in shaders.
Sint8x4 = 3,
/// Two unsigned bytes (u8). [0, 255] converted to float [0, 1] `vec2<f32>` in shaders.
Unorm8x2 = 4,
/// Four unsigned bytes (u8). [0, 255] converted to float [0, 1] `vec4<f32>` in shaders.
Unorm8x4 = 5,
/// Two signed bytes (i8). [-127, 127] converted to float [-1, 1] `vec2<f32>` in shaders.
Snorm8x2 = 6,
/// Four signed bytes (i8). [-127, 127] converted to float [-1, 1] `vec4<f32>` in shaders.
Snorm8x4 = 7,
/// Two unsigned shorts (u16). `vec2<u32>` in shaders.
Uint16x2 = 8,
/// Four unsigned shorts (u16). `vec4<u32>` in shaders.
Uint16x4 = 9,
/// Two signed shorts (i16). `vec2<i32>` in shaders.
Sint16x2 = 10,
/// Four signed shorts (i16). `vec4<i32>` in shaders.
Sint16x4 = 11,
/// Two unsigned shorts (u16). [0, 65535] converted to float [0, 1] `vec2<f32>` in shaders.
Unorm16x2 = 12,
/// Four unsigned shorts (u16). [0, 65535] converted to float [0, 1] `vec4<f32>` in shaders.
Unorm16x4 = 13,
/// Two signed shorts (i16). [-32767, 32767] converted to float [-1, 1] `vec2<f32>` in shaders.
Snorm16x2 = 14,
/// Four signed shorts (i16). [-32767, 32767] converted to float [-1, 1] `vec4<f32>` in shaders.
Snorm16x4 = 15,
/// Two half-precision floats (no Rust equiv). `vec2<f32>` in shaders.
Float16x2 = 16,
/// Four half-precision floats (no Rust equiv). `vec4<f32>` in shaders.
Float16x4 = 17,
/// One single-precision float (f32). `f32` in shaders.
Float32 = 18,
/// Two single-precision floats (f32). `vec2<f32>` in shaders.
Float32x2 = 19,
/// Three single-precision floats (f32). `vec3<f32>` in shaders.
Float32x3 = 20,
/// Four single-precision floats (f32). `vec4<f32>` in shaders.
Float32x4 = 21,
/// One unsigned int (u32). `u32` in shaders.
Uint32 = 22,
/// Two unsigned ints (u32). `vec2<u32>` in shaders.
Uint32x2 = 23,
/// Three unsigned ints (u32). `vec3<u32>` in shaders.
Uint32x3 = 24,
/// Four unsigned ints (u32). `vec4<u32>` in shaders.
Uint32x4 = 25,
/// One signed int (i32). `i32` in shaders.
Sint32 = 26,
/// Two signed ints (i32). `vec2<i32>` in shaders.
Sint32x2 = 27,
/// Three signed ints (i32). `vec3<i32>` in shaders.
Sint32x3 = 28,
/// Four signed ints (i32). `vec4<i32>` in shaders.
Sint32x4 = 29,
/// Three unsigned 10-bit integers and one 2-bit integer, packed into a 32-bit integer (u32). [0, 1024] converted to float [0, 1] `vec4<f32>` in shaders.
#[cfg_attr(feature = "serde", serde(rename = "unorm10-10-10-2"))]
Unorm10_10_10_2 = 34,
}

impl From<u32> for VertexFormat {
fn from(value: u32) -> Self {
use VertexFormat::*;
match value {
0 => Uint8x2,
1 => Uint8x4,
2 => Sint8x2,
3 => Sint8x4,
4 => Unorm8x2,
5 => Unorm8x4,
6 => Snorm8x2,
7 => Snorm8x4,
8 => Uint16x2,
9 => Uint16x4,
10 => Sint16x2,
11 => Sint16x4,
12 => Unorm16x2,
13 => Unorm16x4,
14 => Snorm16x2,
15 => Snorm16x4,
16 => Float16x2,
17 => Float16x4,
18 => Float32,
19 => Float32x2,
20 => Float32x3,
21 => Float32x4,
22 => Uint32,
23 => Uint32x2,
24 => Uint32x3,
25 => Uint32x4,
26 => Sint32,
27 => Sint32x2,
28 => Sint32x3,
29 => Sint32x4,
34 => Unorm10_10_10_2,
_ => VertexFormat::default(),
}
}
}

/// A mapping of vertex buffers and their attributes to shader
/// locations.
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
pub struct AttributeMapping {
/// Shader location associated with this attribute
pub shader_location: u32,
/// Offset in bytes from start of vertex buffer structure
pub offset: u32,
/// Format code to help us unpack the attribute into the type
/// used by the shader. Codes correspond to a 0-based index of
/// <https://gpuweb.github.io/gpuweb/#enumdef-gpuvertexformat>.
/// The conversion process is described by
/// <https://gpuweb.github.io/gpuweb/#vertex-processing>.
pub format: VertexFormat,
}

/// A description of a vertex buffer with all the information we
/// need to address the attributes within it.
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
pub struct VertexBufferMapping {
/// Shader location associated with this buffer
pub id: u32,
/// Size of the structure in bytes
pub stride: u32,
/// Vec of the attributes within the structure
pub attributes: Vec<AttributeMapping>,
}

/// A subset of options that are meant to be changed per pipeline.
#[derive(Debug, Default, Clone)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
Expand All @@ -234,6 +379,17 @@ pub struct PipelineOptions {
///
/// Enable this for vertex shaders with point primitive topologies.
pub allow_and_force_point_size: bool,

/// Experimental
/// If set, when generating the Metal vertex shader, transform it
/// to receive the vertex buffers, lengths, and vertex id as args,
/// and bounds-check the vertex id and use the index into the
/// vertex buffers to access attributes, rather than using Metal's
/// [[stage-in]] assembled attribute data.
pub experimental_vertex_pulling_transform: bool,

/// Only used if experimental_vertex_pulling_transform is set.
pub vertex_buffer_mappings: Vec<VertexBufferMapping>,
}

impl Options {
Expand Down
Loading

0 comments on commit bfb562d

Please sign in to comment.