WGSL数组用变量索引报错,求Bevy 0.12计算着色器解决办法
在Bevy 0.12的WGSL计算着色器中使用变量索引固定大小数组的问题
我正在为Bevy 0.12编写计算着色器,使用WGSL语言。编写了如下is_boxed_in函数,尝试通过变量索引固定大小数组array<ShaderVoxel, 4096>:
fn is_boxed_in(x: u32, y: u32, z: u32, voxels: array<ShaderVoxel, 4096>) -> bool { if check_for_voxel(voxels[voxel_to_1d(min(CHUNK_SIZE - 1u, x + 1u), y, z)]) && check_for_voxel(voxels[voxel_to_1d(max(0u, x - 1u), y, z)]) && check_for_voxel(voxels[voxel_to_1d(x, min(CHUNK_SIZE - 1u, y + 1u), z)]) && check_for_voxel(voxels[voxel_to_1d(x, max(0u, y - 1u), z)]) && check_for_voxel(voxels[voxel_to_1d(x, y, min(CHUNK_SIZE - 1u, z + 1u))]) && check_for_voxel(voxels[voxel_to_1d(x, y, max(0u, z - 1u))]) { return true; } return false; }
但触发了如下错误:
Function [6] 'is_boxed_in' is invalid. WithSpan { inner: Function { handle: [6], name: "is_boxed_in", source: Expression { handle: [10], source: IndexMustBeConstant( [4], ), }, }, spans: [ ( Span { start: 2094, end: 2700, }, "naga::Function [6]", ), ( Span { start: 2201, end: 2256, }, "naga::Expression [10]", ), ], }
该函数的调用代码如下:
@compute @workgroup_size(16, 16, 1) fn init(@builtin(global_invocation_id) global_id: vec3<u32>){ let index = voxel_to_1d(global_id.x, global_id.y, global_id.z); var iter = 0u; while iter < arrayLength(&chunks.chunks) { var chunk = chunks.chunks[iter]; var voxels = chunk.voxels; for (var i = 0; i < 4096; i += 1){ var voxel = voxels[i]; if voxel.id != 0u && !is_boxed_in(voxel_to_3d(i).x, voxel_to_3d(i).y, voxel_to_3d(i).z, voxels){ //gen normal } } iter += 1u; } }
请问是否可以使用变量索引该数组?若可行,正确的实现方式是什么?
解答
可以使用变量索引固定大小数组,当前报错是因为WGSL中直接传递固定大小数组作为函数参数时,编译器会将其视为值传递的常量大小数组,要求索引必须是编译期常量,而你的索引是运行时计算的,因此Naga(Bevy使用的WGSL编译器)抛出错误。
推荐两种解决方法:
方法1:将数组参数改为指针类型
把is_boxed_in的voxels参数从array<ShaderVoxel, 4096>改为指针类型,调用时传递数组的指针:
修改后的is_boxed_in函数:
fn is_boxed_in(x: u32, y: u32, z: u32, voxels: ptr<function, array<ShaderVoxel, 4096>>) -> bool { if check_for_voxel((*voxels)[voxel_to_1d(min(CHUNK_SIZE - 1u, x + 1u), y, z)]) && check_for_voxel((*voxels)[voxel_to_1d(max(0u, x - 1u), y, z)]) && check_for_voxel((*voxels)[voxel_to_1d(x, min(CHUNK_SIZE - 1u, y + 1u), z)]) && check_for_voxel((*voxels)[voxel_to_1d(x, max(0u, y - 1u), z)]) && check_for_voxel((*voxels)[voxel_to_1d(x, y, min(CHUNK_SIZE - 1u, z + 1u))]) && check_for_voxel((*voxels)[voxel_to_1d(x, y, max(0u, z - 1u))]) { return true; } return false; }
调用时修改为传递指针:
if voxel.id != 0u && !is_boxed_in(voxel_to_3d(i).x, voxel_to_3d(i).y, voxel_to_3d(i).z, &voxels){ //gen normal }
这种方式让编译器明确数组是外部分配的,允许使用运行时变量索引。
方法2:将数组包装成结构体
如果指针方式不匹配你的代码结构,可以把数组放到结构体中传递:
首先定义结构体:
struct VoxelArray { data: array<ShaderVoxel, 4096>, };
修改is_boxed_in参数:
fn is_boxed_in(x: u32, y: u32, z: u32, voxels: VoxelArray) -> bool { if check_for_voxel(voxels.data[voxel_to_1d(min(CHUNK_SIZE - 1u, x + 1u), y, z)]) && check_for_voxel(voxels.data[voxel_to_1d(max(0u, x - 1u), y, z)]) && check_for_voxel(voxels.data[voxel_to_1d(x, min(CHUNK_SIZE - 1u, y + 1u), z)]) && check_for_voxel(voxels.data[voxel_to_1d(x, max(0u, y - 1u), z)]) && check_for_voxel(voxels.data[voxel_to_1d(x, y, min(CHUNK_SIZE - 1u, z + 1u))]) && check_for_voxel(voxels.data[voxel_to_1d(x, y, max(0u, z - 1u))]) { return true; } return false; }
调用时确保chunk.voxels是VoxelArray类型,直接传递即可。
内容的提问来源于stack exchange,提问作者NewUser69420
相关产品推荐
相关产品推荐

