Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 64 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ At the moment, the following transformations are supported:
| --------------------------------- | ----------- | ------ | ------ |
| Combined Image Samplers | ✅ | ✅ | ✅ |
| Immediates (Push Constants) | ✅ | ✅\* | ✅ |
| Binding Arrays | ✅ | ✅ | ✅ |
| Mixed Depth / Comparison | ✅ | ⚠️\* | ❌ |
| isnan / isinf Patching | ✅ | ✅ | ✅ |
| Storage Cube Patching | ✅ | ✅ | ✅ |
Expand Down Expand Up @@ -89,7 +90,6 @@ layout(std140, set = N+1, binding = 0) uniform PushBlock {
} pc;

// where N is the max set in the shader.

```

### Additional Notes
Expand All @@ -110,6 +110,69 @@ layout(std140, set = N+1, binding = 0) uniform PushBlock {

> \* naga's SPIR-V front-end rejects `MatrixStride 16` for `mat2x2`, this should be fixed soon (?).

## Binding Arrays

Binding arrays are a feature commonly used in shaders and supported by WGSL compilers, just not on the web (yet?).
This patch takes fixed size arrays of size N containing opaque or concrete types and splits them into N individual bindings.
In `wgpu`, this covers following features:

- `TEXTURE_BINDING_ARRAY`
- `BUFFER_BINDING_ARRAY`
- `STORAGE_RESOURCE_BINDING_ARRAY`
- `SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING`
- `STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING`

```glsl
struct Thing {
float a;
};

#define MAX_RESOURCES 2
layout(set = 0, binding = 0) uniform sampler u_samplers[MAX_RESOURCES];
layout(set = 0, binding = 1) uniform texture2D u_textures[MAX_RESOURCES];
layout(set = 0, binding = 2) uniform texture2DArray u_texture_arrays[MAX_RESOURCES];
layout(set = 0, binding = 3) uniform image2D u_images[MAX_RESOURCES];
layout(set = 0, binding = 4) uniform Thing u_things[MAX_RESOURCES];
layout(set = 0, binding = 5, std140) buffer Thing u_buf_things[MAX_RESOURCES];

// is converted into...

struct Thing {
float a;
};

layout(set = 0, binding = 0) uniform sampler u_samplers_0;
layout(set = 0, binding = 1) uniform sampler u_samplers_1;
layout(set = 0, binding = 2) uniform texture2D u_textures_0;
layout(set = 0, binding = 3) uniform texture2D u_textures_1;
layout(set = 0, binding = 4) uniform texture2DArray u_texture_arrays_0;
layout(set = 0, binding = 5) uniform texture2DArray u_texture_arrays_1;
layout(set = 0, binding = 6) uniform image2D u_images_0;
layout(set = 0, binding = 7) uniform image2D u_images_1;
layout(set = 0, binding = 8) uniform Thing u_things_0;
layout(set = 0, binding = 9) uniform Thing u_things_1;
layout(set = 0, binding = 10, std140) buffer Thing u_buf_things_0;
layout(set = 0, binding = 11, std140) buffer Thing u_buf_things_1;
```

### Additional Notes

- Combined image samplers are not supported, please run the combined image sampler pass first.
- Nested resources (`texture2D u[I][J][K]`) are not supported.
- Usage of additional SPIR-V capabilities such as `SparseResidency` or `ImageQuery` are not supported.

### Tests

| Test | `spirv-val` | Naga | Tint |
| ------------------------------------- | ----------- | ------ | ---- |
| `buffer_binding_array.frag` | ✅ | ✅ | ✅ |
| `storage_binding_array.frag` | ✅ | ✅ | ✅ |
| `texture_binding_array.frag` | ✅ | ✅ | ✅ |
| `nested_texture_binding_array.frag` | ✅ | ✅ | ✅ |
| `sampler_binding_array.frag` | ✅ | ✅ | ✅ |
| `sampler_stub.frag` | ✅ | ✅ | ✅ |
| `texture_array_binding_array.frag` | ✅ | ✅ | ✅ |

## Mixed Depth / Comparison

The WGSL spec differentiates between `sampler` and `sampler_comparison` as well as `texture2d<T>` and `texture_depth_2d`.
Expand Down
2 changes: 2 additions & 0 deletions ffi/bin/README.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Make sure to update this guy when a new patch is included in the API.
The main purpose of this file is not to test functionality but linking!
13 changes: 13 additions & 0 deletions ffi/bin/spv_webgpu_transform.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ int main() {
uint32_t pruneunuseddref_out_count;
spirv_webgpu_transform_pruneunuseddref_alloc(storagecube_out_spv, storagecube_out_count, &pruneunuseddref_out_spv, &pruneunuseddref_out_count);

uint32_t *immediates_out_spv;
uint32_t immediates_out_count;
spirv_webgpu_transform_immediatespatch_alloc(pruneunuseddref_out_spv, pruneunuseddref_out_count, &immediates_out_spv, &immediates_out_count);

uint32_t *splitbindingarray_out_spv;
uint32_t splitbindingarray_out_count;
spirv_webgpu_transform_splitbindingarray_alloc(immediates_out_spv, immediates_out_count, &splitbindingarray_out_spv, &splitbindingarray_out_count, &correction_map);

// 3. Observe the patched variables
print_set_binding(correction_map, 0, 0);
print_set_binding(correction_map, 0, 1);
Expand All @@ -51,6 +59,8 @@ int main() {
print_set_binding(correction_map, 3, 0);

// 4. Free memory
spirv_webgpu_transform_splitbindingarray_free(splitbindingarray_out_spv);
spirv_webgpu_transform_immediatespatch_free(immediates_out_spv);
spirv_webgpu_transform_pruneunuseddref_free(pruneunuseddref_out_spv);
spirv_webgpu_transform_storagecubepatch_free(storagecube_out_spv);
spirv_webgpu_transform_isnanisinfpatch_free(isnanisinf_out_spv);
Expand Down Expand Up @@ -88,6 +98,9 @@ void print_set_binding(TransformCorrectionMap map, uint32_t set, uint32_t bindin
case SPIRV_WEBGPU_TRANSFORM_CORRECTION_TYPE_CONVERT_STORAGE_CUBE:
printf("CONVERT_STORAGE_CUBE ");
break;
case SPIRV_WEBGPU_TRANSFORM_CORRECTION_TYPE_SPLIT_BINDING_ARRAY:
printf("SPLIT_BINDING_ARRAY ");
break;
}
}
printf("\n");
Expand Down
3 changes: 3 additions & 0 deletions ffi/spirv_webgpu_transform.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ void spirv_webgpu_transform_storagecubepatch_alloc(uint32_t *in_spv, uint32_t in
void spirv_webgpu_transform_storagecubepatch_free(uint32_t *out_spv);
void spirv_webgpu_transform_pruneunuseddref_alloc(uint32_t *int_spv, uint32_t in_count, uint32_t **out_spv, uint32_t *out_count);
void spirv_webgpu_transform_pruneunuseddref_free(uint32_t *out_spv);
void spirv_webgpu_transform_splitbindingarray_alloc(uint32_t *in_spv, uint32_t in_count, uint32_t **out_spv, uint32_t *out_count, TransformCorrectionMap *correction_map);
void spirv_webgpu_transform_splitbindingarray_free(uint32_t *out_spv);

void spirv_webgpu_transform_mirrorpatch_alloc(
uint32_t *in_left_spv, uint32_t in_left_count, TransformCorrectionMap *left_corrections,
Expand All @@ -41,6 +43,7 @@ typedef enum {
SPIRV_WEBGPU_TRANSFORM_CORRECTION_TYPE_SPLIT_DREF_REGULAR = 1,
SPIRV_WEBGPU_TRANSFORM_CORRECTION_TYPE_SPLIT_DREF_COMPARISON = 2,
SPIRV_WEBGPU_TRANSFORM_CORRECTION_TYPE_CONVERT_STORAGE_CUBE = 3,
SPIRV_WEBGPU_TRANSFORM_CORRECTION_TYPE_SPLIT_BINDING_ARRAY = 4,
} TransformCorrectionType;

// SAFETY: `corrections` invalidates when `correction_map` is written to.
Expand Down
33 changes: 32 additions & 1 deletion ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use core::{ffi, ptr, slice};
use spirv_webgpu_transform::{
CorrectionMap, combimgsampsplitter, drefsplitter, immediatespatch, isnanisinfpatch,
mirrorpatch, pruneunuseddref, storagecubepatch,
mirrorpatch, pruneunuseddref, splitbindingarray, storagecubepatch,
};

type TransformCorrectionMap = *mut ffi::c_void;
Expand Down Expand Up @@ -199,6 +199,35 @@ pub unsafe extern "C" fn spirv_webgpu_transform_pruneunuseddref_free(out_spv: *m
unsafe { drop(Box::from_raw(out_spv)) }
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn spirv_webgpu_transform_splitbindingarray_alloc(
in_spv: *const u32,
in_count: u32,
out_spv: *mut *const u32,
out_count: *mut u32,
correction_map: *mut TransformCorrectionMap,
) {
let correction_map = unsafe { alloc_or_pass_correction_map(correction_map) };

let in_spv = unsafe { slice::from_raw_parts(in_spv, in_count as usize) };
match splitbindingarray(in_spv, correction_map) {
Ok(spv) => unsafe {
*out_count = spv.len() as u32;
let leaked = Box::leak(spv.into_boxed_slice());
*out_spv = leaked.as_ptr();
},
Err(_) => unsafe {
*out_spv = ptr::null();
*out_count = 0;
},
}
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn spirv_webgpu_transform_splitbindingarray_free(out_spv: *mut u32) {
unsafe { drop(Box::from_raw(out_spv)) }
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn spirv_webgpu_transform_mirrorpatch_alloc(
in_left_spv: *const u32,
Expand Down Expand Up @@ -267,6 +296,8 @@ pub enum TransformCorrectionType {
SpirvWebgpuTransformCorrectionTypeSplitCombined = 0,
SpirvWebgpuTransformCorrectionTypeSplitDrefRegular = 1,
SpirvWebgpuTransformCorrectionTypeSplitDrefComparison = 2,
SpirvWebgpuTransformCorrectionTypeConvertStorageCube = 3,
SpirvWebgpuTransformCorrectionTypeSplitBindingArray = 4,
}

// TransformCorrectionStatus spirv_webgpu_transform_correction_map_index(uint32_t set, uint32_t binding, TransformCorrectionType** corrections_ptr, uint32_t* correction_count);
Expand Down
5 changes: 4 additions & 1 deletion src/bin/spv_webgpu_transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ fn main() {

if args.len() != 4 {
eprintln!(
"Usage: spv_webgpu_transform <combimg|dref|isnanisinf|storagecube|pruneunuseddref|immediates> <input.spv> <output.spv>"
"Usage: spv_webgpu_transform <combimg|dref|isnanisinf|storagecube|pruneunuseddref|immediates|bindingarray> <input.spv> <output.spv>"
);
process::exit(1);
}
Expand All @@ -32,6 +32,9 @@ fn main() {
}
"pruneunuseddref" => spirv_webgpu_transform::pruneunuseddref(&spv).unwrap(),
"immediates" => spirv_webgpu_transform::immediatespatch(&spv).unwrap(),
"bindingarray" => {
spirv_webgpu_transform::splitbindingarray(&spv, &mut out_correction_map).unwrap()
}
mode => {
eprintln!("unknown mode {:?}", mode);
process::exit(1)
Expand Down
12 changes: 7 additions & 5 deletions src/correction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,20 @@ use super::*;
// Q: Hey what happens when you stack corrections?
// A: I don't want to think about it... I will start thinking after a refactor...

#[repr(u16)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CorrectionType {
/// A combined image sampler has been split, a new `sampler` object should be inserted.
SplitCombined = 0,
SplitCombined,
/// A mixed depth texture / sampler has been duplicated, insert the same object again with a `Regular` bind type.
SplitDrefRegular = 1,
SplitDrefRegular,
/// A mixed depth texture / sampler has been duplicated, insert the same object again with a
/// `Comparison` bind type.
SplitDrefComparison = 2,
SplitDrefComparison,
/// A storage cube texture has been converted into a storage texture 2D array, change the dimension.
ConvertStorageCube = 3,
ConvertStorageCube,
/// A binding array has been split into new variables. Insert the same resource again.
/// For an `N` sized array, expect `N-1` entries.
SplitBindingArray,
}

#[derive(Debug, Clone, Default, PartialEq, Eq)]
Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
//! | --------------------------------- | ----------- | ------ | ------ |
//! | Combined Image Samplers | ✅ | ✅ | ✅ |
//! | Immediates (Push Constants) | ✅ | ✅\* | ✅ |
//! | Binding Arrays | ✅ | ✅ | ✅ |
//! | Mixed Depth / Comparison | ✅ | ⚠️\* | ❌ |
//! | isnan / isinf Patching | ✅ | ✅ | ✅ |
//! | Storage Cube Patching | ✅ | ✅ | ✅ |
Expand All @@ -35,6 +36,7 @@ mod immediatespatch;
mod isnanisinfpatch;
mod mirrorpatch;
mod pruneunuseddref;
mod splitbindingarray;
mod splitcombined;
mod splitdref;
mod spv;
Expand All @@ -52,6 +54,7 @@ pub use immediatespatch::*;
pub use isnanisinfpatch::*;
pub use mirrorpatch::*;
pub use pruneunuseddref::*;
pub use splitbindingarray::*;
pub use splitcombined::*;
pub use splitdref::*;
pub use storagecubepatch::*;
Expand Down
2 changes: 1 addition & 1 deletion src/mirrorpatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ fn patch_spv_decorations(
// Convert into affected decoration
Ok(AffectedDecoration {
original_res_id: original_variable_id,
new_res_id,
new_res_ids: vec![new_res_id],
correction_type,
})
})
Expand Down
1 change: 0 additions & 1 deletion src/pruneunuseddref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,6 @@ pub fn pruneunuseddref(in_spv: &[u32]) -> Result<Vec<u32>, ()> {

new_spv[spv_idx..spv_idx + word_count].fill(encode_word(1, SPV_INSTRUCTION_OP_NOP));
}

prune_noops(&mut new_spv);

// 10. Write New Header and New Code
Expand Down
Loading
Loading