Skip to content

Indexing glam vectors: "Using pointers with OpPhi requires capability VariablePointers or VariablePointersStorageBuffer" #432

@Firestar99

Description

@Firestar99

Compiletest

// build-pass
// compile-flags: -C llvm-args=--disassemble

use spirv_std::glam::*;
use spirv_std::spirv;

#[spirv(compute(threads(32)))]
pub fn main(
    #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] input: &UVec4,
    #[spirv(descriptor_set = 0, binding = 1, storage_buffer)] output: &mut [u32],
) {
    // works
    // output[0] = input.x;
    // fails
    output[0] = input[0];
    // works
    // output[0] = unsafe { spirv_std::arch::vector_extract_dynamic(*input, 0) };
}

Error

error: error:0:0 - Using pointers with OpPhi requires capability VariablePointers or VariablePointersStorageBuffer
         %37 = OpPhi %_ptr_StorageBuffer_uint %33 %29 %34 %30 %35 %31 %36 %32
Full logs and disassembly

; SPIR-V
; Version: 1.5
; Generator: rspirv
; Bound: 44
OpCapability Shader
OpCapability VulkanMemoryModel
OpMemoryModel Logical Vulkan
OpEntryPoint GLCompute %1 "main" %2 %3
OpExecutionMode %1 LocalSize 32 1 1
%4 = OpString "/home/firestar99/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/glam-0.30.5/src/u32/uvec4.rs"
%5 = OpString "$DIR/minify.rs"
OpSource Unknown 0 %4 "<removed>"
OpSource Unknown 0 %5 "<removed>"
OpName %2 "input"
OpName %3 "output"
OpDecorate %6 Block
OpMemberDecorate %6 0 Offset 0
OpDecorate %7 ArrayStride 4
OpDecorate %8 Block
OpMemberDecorate %8 0 Offset 0
OpDecorate %2 NonWritable
OpDecorate %2 Binding 0
OpDecorate %2 DescriptorSet 0
OpDecorate %3 Binding 1
OpDecorate %3 DescriptorSet 0
%9 = OpTypeInt 32 0
%10 = OpTypeVector %9 4
%6 = OpTypeStruct %10
%11 = OpTypePointer StorageBuffer %6
%7 = OpTypeRuntimeArray %9
%8 = OpTypeStruct %7
%12 = OpTypePointer StorageBuffer %8
%13 = OpTypeVoid
%14 = OpTypeFunction %13
%15 = OpTypePointer StorageBuffer %10
%2 = OpVariable  %11  StorageBuffer
%16 = OpConstant  %9  0
%17 = OpTypePointer StorageBuffer %7
%3 = OpVariable  %12  StorageBuffer
%18 = OpTypePointer StorageBuffer %9
%19 = OpConstant  %9  1
%20 = OpConstant  %9  2
%21 = OpConstant  %9  3
%22 = OpTypeBool
%1 = OpFunction  %13  None %14
%23 = OpLabel
OpLine %5 9 4
%24 = OpInBoundsAccessChain  %15  %2 %16
OpLine %5 10 4
%25 = OpInBoundsAccessChain  %17  %3 %16
%26 = OpArrayLength  %9  %3 0
OpNoLine
OpSelectionMerge %27 None
OpSwitch %16 %28 0 %29 1 %30 2 %31 3 %32
%28 = OpLabel
OpReturn
%29 = OpLabel
OpLine %4 2910 17
%33 = OpInBoundsAccessChain  %18  %24 %16
OpNoLine
OpBranch %27
%30 = OpLabel
OpLine %4 2911 17
%34 = OpInBoundsAccessChain  %18  %24 %19
OpNoLine
OpBranch %27
%31 = OpLabel
OpLine %4 2912 17
%35 = OpInBoundsAccessChain  %18  %24 %20
OpNoLine
OpBranch %27
%32 = OpLabel
OpLine %4 2913 17
%36 = OpInBoundsAccessChain  %18  %24 %21
OpNoLine
OpBranch %27
%27 = OpLabel
%37 = OpPhi  %18  %33 %29 %34 %30 %35 %31 %36 %32
OpLine %5 15 16
%38 = OpLoad  %9  %37
OpLine %5 15 4
%39 = OpULessThan  %22  %16 %26
OpNoLine
OpSelectionMerge %40 None
OpBranchConditional %39 %41 %42
%41 = OpLabel
OpBranch %40
%42 = OpLabel
OpReturn
%40 = OpLabel
OpLine %5 15 4
%43 = OpInBoundsAccessChain  %18  %25 %16
OpStore %43 %38
OpNoLine
OpReturn
OpFunctionEnd
error: error:0:0 - Using pointers with OpPhi requires capability VariablePointers or VariablePointersStorageBuffer
         %37 = OpPhi %_ptr_StorageBuffer_uint %33 %29 %34 %30 %35 %31 %36 %32
  |
  = note: spirv-val failed
  = note: module `$TEST_BUILD_DIR/arch/shared/minify.vulkan1.2`

error: aborting due to 1 previous error

Notes

Analysis

  • The assembly looks very much like this function:
impl Index<usize> for UVec4 {
    type Output = u32;
    #[inline]
    fn index(&self, index: usize) -> &Self::Output {
        match index {
            0 => &self.x,
            1 => &self.y,
            2 => &self.z,
            3 => &self.w,
            _ => panic!("index out of bounds"),
        }
    }
}
  • The function is inlined, but the returned &u32 is still forwarded as a pointer
  • The OpPhi is after the switch, merging the different branches all returning the &u32
  • The deref to u32 only happens afterwards

System Info

rust-gpu main dbf3737

More compiletests

vec

// build-pass

use spirv_std::arch::workgroup_memory_barrier_with_group_sync;
use spirv_std::glam::*;
use spirv_std::spirv;

#[spirv(compute(threads(32)))]
pub fn main(
    #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] input: &UVec4,
    #[spirv(descriptor_set = 0, binding = 1, storage_buffer)] output: &mut [u32],
    #[spirv(workgroup)] shared: &mut UVec4,
    #[spirv(local_invocation_index)] inv_id: UVec3,
) {
    unsafe {
        // copy from input to shared
        if (inv_id.x == 0) {
            *shared = *input;
        }
        workgroup_memory_barrier_with_group_sync();

        // accumulate random values
        let mut accum = 0;
        for i in 0..inv_id.x {
            accum += shared[i as usize % 4];
        }

        // write out
        output[inv_id.x as usize] = accum;
    }
}

BigStruct

// build-pass

use spirv_std::arch::workgroup_memory_barrier_with_group_sync;
use spirv_std::glam::*;
use spirv_std::spirv;

#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct MyStruct {
    pub a: Nested,
    pub b: Mat4,
    pub c: UVec2,
}

#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct Nested(pub f32);

#[spirv(compute(threads(32)))]
pub fn main(
    #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] input: &MyStruct,
    #[spirv(descriptor_set = 0, binding = 1, storage_buffer)] output: &mut [MyStruct],
    #[spirv(workgroup)] shared: &mut MyStruct,
    #[spirv(local_invocation_index)] inv_id: UVec3,
) {
    unsafe {
        // copy from input to shared
        if (inv_id.x == 0) {
            *shared = *input;
        }
        workgroup_memory_barrier_with_group_sync();

        // accumulate random values
        let mut accum = 0.;
        accum += shared.a.0;
        accum += shared.c.x as f32;
        for axis_id in 0..4 {
            let axis = [shared.b.x_axis, shared.b.y_axis, shared.b.z_axis, shared.b.w_axis][axis_id];
            accum += axis[inv_id.x as usize % 4];
        }

        // write out
        output[inv_id.x as usize].a.0 = accum;
    }
}

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions