Open Bug 1915297 Opened 21 days ago Updated 21 days ago

WebGPU: `Remove-bg` renders blank images

Categories

(Core :: Graphics: WebGPU, defect, P3)

defect

Tracking

()

People

(Reporter: ErichDonGubler, Unassigned)

References

(Depends on 2 open bugs, Blocks 1 open bug, )

Details

I noticed this page used WebGPU while browsing HN, and that it didn't work yet due to shader compilation failures: https://news.ycombinator.com/item?id=41358490

There seem to be two shader compilation issues in my testing on my M1 Macbook Pro:

  • A shader named Concat is blocked on dynamic indexing into constant array values (bug 1913424); source:

    
        struct Uniforms { outputSize:u32, sizeInConcatAxis0:u32, sizeInConcatAxis1:u32, input0_shape:vec4<u32>, input0_strides:vec4<u32>, input1_shape:vec4<u32>, input1_strides:vec4<u32>, output_shape:vec4<u32>, output_strides:vec4<u32> };
        @group(0) @binding(3) var<uniform> uniforms: Uniforms;
    fn i2o_input0(indices: vec4<u32>) -> u32 {
      return uniforms.input0_strides[3] * (indices[3])+uniforms.input0_strides[2] * (indices[2])+uniforms.input0_strides[1] * (indices[1])+uniforms.input0_strides[0] * (indices[0]);
    }
    
    fn get_input0ByIndices(indices: vec4<u32>) -> f32 {
      return input0[i2o_input0(indices)];
    }
    
    fn i2o_input1(indices: vec4<u32>) -> u32 {
      return uniforms.input1_strides[3] * (indices[3])+uniforms.input1_strides[2] * (indices[2])+uniforms.input1_strides[1] * (indices[1])+uniforms.input1_strides[0] * (indices[0]);
    }
    
    fn get_input1ByIndices(indices: vec4<u32>) -> f32 {
      return input1[i2o_input1(indices)];
    }
    
    fn o2i_output(offset: u32) -> vec4<u32> {
      var indices: vec4<u32>;
      var current = offset;
    
      let dim0 = current / uniforms.output_strides[0];
      let rest0 = current % uniforms.output_strides[0];
      indices[0] = dim0;
      current = rest0;
    
      let dim1 = current / uniforms.output_strides[1];
      let rest1 = current % uniforms.output_strides[1];
      indices[1] = dim1;
      current = rest1;
    
      let dim2 = current / uniforms.output_strides[2];
      let rest2 = current % uniforms.output_strides[2];
      indices[2] = dim2;
      current = rest2;
      indices[3] = current;
      return indices;
    }
    
    
    @group(0) @binding(0) var<storage, read> input0: array<f32>;
    @group(0) @binding(1) var<storage, read> input1: array<f32>;
    @group(0) @binding(2) var<storage, read_write> output: array<f32>;
    
    
    fn calculateInputIndex(index: u32) -> u32 {
      let sizeInConcatAxis = array<u32, 2u>(uniforms.sizeInConcatAxis0,uniforms.sizeInConcatAxis1);
      for (var i: u32 = 0u; i < 2; i += 1u ) {
        if (index < sizeInConcatAxis[i]) {
          return i;
        }
      }
      return 2u;
    }
    
    @compute @workgroup_size(64, 1, 1)
    fn main(@builtin(global_invocation_id) global_id : vec3<u32>,
      @builtin(workgroup_id) workgroup_id : vec3<u32>,
      @builtin(local_invocation_id) local_id : vec3<u32>) {
      let global_idx = global_id.x; let local_idx = local_id.x;
    
      if (global_idx >= uniforms.outputSize) { return; }
    
      var indices = o2i_output(global_idx);
    
      let inputIndex = calculateInputIndex(indices[3]);
      if (inputIndex != 0u) {
        let sizeInConcatAxis = array<u32, 2u>(uniforms.sizeInConcatAxis0,uniforms.sizeInConcatAxis1);
        indices[3] -= sizeInConcatAxis[inputIndex - 1u];
      }
    
      if (inputIndex == 0u) { output[global_idx]=get_input0ByIndices(indices); }
    else { output[global_idx]=get_input1ByIndices(indices); }
    }
    

    ...where the error returned by Naga states:

    error:
       ┌─ Concat.wgsl:49:3
       │
    49 │ ╭   fn calculateInputIndex(index: u32) -> u32 {
    50 │ │     let sizeInConcatAxis = array<u32, 2u>(uniforms.sizeInConcatAxis0,uniforms.sizeInConcatAxis1);
    51 │ │     for (var i: u32 = 0u; i < 2; i += 1u ) {
    52 │ │       if (index < sizeInConcatAxis[i]) {
       │ │                   ^^^^^^^^^^^^^^^^^^^ naga::Expression [14]
       · │
    55 │ │     }
    56 │ │     return 2u;
       │ ╰──────────────^ naga::Function [5]
    
    Function [5] 'calculateInputIndex' is invalid:
            Expression [14] is invalid
            The expression [7] may only be indexed by a constant
    
  • Another shader titled Resize has this source:

    
    
        struct Uniforms { output_size:u32, scales:vec4<f32>, roi:array<vec4<f32>, 2>, input_shape:vec4<u32>, input_strides:vec4<u32>, output_shape:vec4<u32>, output_strides:vec4<u32> };
        @group(0) @binding(2) var<uniform> uniforms: Uniforms;
    fn i2o_input(indices: vec4<u32>) -> u32 {
      return uniforms.input_strides[3] * (indices[3])+uniforms.input_strides[2] * (indices[2])+uniforms.input_strides[1] * (indices[1])+uniforms.input_strides[0] * (indices[0]);
    }
    
    fn get_inputByIndices(indices: vec4<u32>) -> f32 {
      return input[i2o_input(indices)];
    }
    
    fn o2i_output(offset: u32) -> vec4<u32> {
      var indices: vec4<u32>;
      var current = offset;
    
      let dim0 = current / uniforms.output_strides[0];
      let rest0 = current % uniforms.output_strides[0];
      indices[0] = dim0;
      current = rest0;
    
      let dim1 = current / uniforms.output_strides[1];
      let rest1 = current % uniforms.output_strides[1];
      indices[1] = dim1;
      current = rest1;
    
      let dim2 = current / uniforms.output_strides[2];
      let rest2 = current % uniforms.output_strides[2];
      indices[2] = dim2;
      current = rest2;
      indices[3] = current;
      return indices;
    }
    
    
        fn getOriginalCoordinateFromResizedCoordinate(xResized: u32, xScale: f32, lengthResized: u32,
       lengthOriginal: u32, roiStart: f32, roiEnd: f32) -> f32 { return ((f32(xResized) + 0.5) / f32(xScale)) - 0.5;};
    
    
      fn calculateOriginalIndicesFromOutputIndices(output_indices: vec4<u32>) -> array<f32, 4> {
        var original_indices: array<f32, 4>;
        for (var i:u32 = 0; i < 4; i++) {
          var output_index = output_indices[i];
          var scale = uniforms.scales[i];
          var roi_low = uniforms.roi[(i) / 4][(i) % 4];
          var roi_hi = uniforms.roi[(i + 4) / 4][(i + 4) % 4];
          if (scale == 1.0) {
            original_indices[i] = f32(output_index);
          } else {
            var input_shape_i = uniforms.input_shape[i];
            var output_shape_i = uniforms.output_shape[i];
            original_indices[i] = getOriginalCoordinateFromResizedCoordinate(output_index, scale, output_shape_i,
                                                                             input_shape_i, roi_low, roi_hi);
          }
        }
        return original_indices;
      };
    
      fn getInputValue(batch: u32, channel: u32, row: u32, col: u32) -> f32 {
        var input_indices: vec4<u32>;
        input_indices[2]=max(0, min(row, 16 - 1));;
        input_indices[3]=max(0, min(col, 16 - 1));;
    
      input_indices[1]=channel;;
      input_indices[0]=batch;;
    
        return get_inputByIndices(input_indices);
      }
    
      fn bilinearInterpolation(output_indices: vec4<u32>) -> f32 {
        var originalIndices = calculateOriginalIndicesFromOutputIndices(output_indices);
        var row:f32 = originalIndices[2];
        var col:f32 = originalIndices[3];
        ;
        row = max(0, min(row, 16 - 1));
        col = max(0, min(col, 16 - 1));
        var row1: u32 = u32(row);
        var col1: u32 = u32(col);
        var row2: u32 = u32(row + 1);
        var col2: u32 = u32(col + 1);
        var channel: u32 = u32(originalIndices[1]);
        var batch: u32 =  u32(originalIndices[0]);
        var x11: f32 = getInputValue(batch, channel, row1, col1);
        var x12: f32 = getInputValue(batch, channel, row1, col2);
        var x21: f32 = getInputValue(batch, channel, row2, col1);
        var x22: f32 = getInputValue(batch, channel, row2, col2);
        var dx1: f32 = abs(row - f32(row1));
        var dx2: f32 = abs(f32(row2) - row);
        var dy1: f32 = abs(col - f32(col1));
        var dy2: f32 = abs(f32(col2) - col);
        if (row1 == row2) {
          dx1 = 0.5;
          dx2 = 0.5;
        }
        if (col1 == col2) {
          dy1 = 0.5;
          dy2 = 0.5;
        }
        return (x11 * dx2 * dy2 + x12 * dx2 * dy1 + x21 * dx1 * dy2 + x22 * dx1 * dy1);
      };
              ;
    
        @group(0) @binding(0) var<storage, read> input: array<f32>;
    @group(0) @binding(1) var<storage, read_write> output: array<f32>;
        @compute @workgroup_size(64, 1, 1)
    fn main(@builtin(global_invocation_id) global_id : vec3<u32>,
      @builtin(workgroup_id) workgroup_id : vec3<u32>,
      @builtin(local_invocation_id) local_id : vec3<u32>) {
      let global_idx = global_id.x; let local_idx = local_id.x;
    
          if (global_idx >= uniforms.output_size) { return; }
    
          let output_indices = o2i_output(global_idx);
          var input_indices: vec4<u32>;
          output[global_idx] = bilinearInterpolation(output_indices);;
    
        }
    

    ...with this error in Naga (almost certainly bug 1829879):

    error:
       ┌─ Resize.wgsl:59:5
       │
    59 │ ╭     fn getInputValue(batch: u32, channel: u32, row: u32, col: u32) -> f32 {
    60 │ │       var input_indices: vec4<u32>;
    61 │ │       input_indices[2]=max(0, min(row, 16 - 1));;
       │ │                               ^^^ naga::Expression [8]
    62 │ │       input_indices[3]=max(0, min(col, 16 - 1));;
       · │
    66 │ │
    67 │ │       return get_inputByIndices(input_indices);
       │ ╰───────────────────────────────────────────────^ naga::Function [5]
    
    Function [5] 'getInputValue' is invalid:
            Expression [8] is invalid
            Argument [1] to Min as expression [7] has an invalid type.
    
Depends on: 1913424
Depends on: 1829879
You need to log in before you can comment on or make changes to this bug.