mirror of
https://github.com/gfx-rs/wgpu.git
synced 2025-12-08 21:26:17 +00:00
(Naga) Implement OpImageGather and OpImageDrefGather when ingesting SPIR-V (#8280)
This commit is contained in:
parent
ea80c7dbc3
commit
8c4aebc0c1
@ -201,6 +201,7 @@ By @cwfitzgerald in [#8162](https://github.com/gfx-rs/wgpu/pull/8162).
|
||||
- The [`source()`](https://doc.rust-lang.org/std/error/trait.Error.html#method.source) method of `ShaderError` no longer reports the error as its own source. By @andyleiserson in [#8258](https://github.com/gfx-rs/wgpu/pull/8258).
|
||||
- naga correctly ingests SPIR-V that use descriptor runtime indexing, which in turn is correctly converted into WGSLs binding array. By @hasenbanck in [8256](https://github.com/gfx-rs/wgpu/pull/8256).
|
||||
- naga correctly ingests SPIR-V that loads from multi-sampled textures, which in turn is correctly converted into WGSLs texture_multisampled_2d and load operations. By @hasenbanck in [8270](https://github.com/gfx-rs/wgpu/pull/8270).
|
||||
- naga implement OpImageGather and OpImageDrefGather operations when ingesting SPIR-V. By @hasenbanck in [8280](https://github.com/gfx-rs/wgpu/pull/8280).
|
||||
|
||||
#### DX12
|
||||
|
||||
|
||||
@ -48,6 +48,8 @@ pub struct SamplingOptions {
|
||||
pub project: bool,
|
||||
/// Depth comparison sampling with a reference value.
|
||||
pub compare: bool,
|
||||
/// Gather sampling: Operates on four samples of one channel.
|
||||
pub gather: bool,
|
||||
}
|
||||
|
||||
enum ExtraCoordinate {
|
||||
@ -500,10 +502,10 @@ impl<I: Iterator<Item = u32>> super::Frontend<I> {
|
||||
let result_id = self.next()?;
|
||||
let sampled_image_id = self.next()?;
|
||||
let coordinate_id = self.next()?;
|
||||
let dref_id = if options.compare {
|
||||
Some(self.next()?)
|
||||
} else {
|
||||
None
|
||||
let (component_id, dref_id) = match (options.gather, options.compare) {
|
||||
(true, false) => (Some(self.next()?), None),
|
||||
(_, true) => (None, Some(self.next()?)),
|
||||
(_, _) => (None, None),
|
||||
};
|
||||
let span = self.span_from_with_op(start);
|
||||
|
||||
@ -629,6 +631,58 @@ impl<I: Iterator<Item = u32>> super::Frontend<I> {
|
||||
self.get_expr_handle(coordinate_id, coord_lexp, ctx, emitter, block, body_idx);
|
||||
let coord_type_handle = self.lookup_type.lookup(coord_lexp.type_id)?.handle;
|
||||
|
||||
let gather = match (options.gather, component_id) {
|
||||
(true, Some(component_id)) => {
|
||||
let component_lexp = self.lookup_expression.lookup(component_id)?;
|
||||
|
||||
let component_value = match ctx.expressions[component_lexp.handle] {
|
||||
// VUID-StandaloneSpirv-OpImageGather-04664:
|
||||
// The “Component” operand of OpImageGather, and OpImageSparseGather must be the
|
||||
// <id> of a constant instruction.
|
||||
crate::Expression::Constant(const_handle) => {
|
||||
let constant = &ctx.module.constants[const_handle];
|
||||
match ctx.module.global_expressions[constant.init] {
|
||||
// SPIR-V specification: "It must be a 32-bit integer type scalar."
|
||||
crate::Expression::Literal(crate::Literal::U32(value)) => value,
|
||||
crate::Expression::Literal(crate::Literal::I32(value)) => value as u32,
|
||||
_ => {
|
||||
log::error!(
|
||||
"Image gather component constant must be a 32-bit integer literal"
|
||||
);
|
||||
return Err(Error::InvalidOperand);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
log::error!("Image gather component must be a constant");
|
||||
return Err(Error::InvalidOperand);
|
||||
}
|
||||
};
|
||||
|
||||
debug_assert_eq!(level, crate::SampleLevel::Auto);
|
||||
level = crate::SampleLevel::Zero;
|
||||
|
||||
// SPIR-V specification: "Behavior is undefined if its value is not 0, 1, 2 or 3."
|
||||
match component_value {
|
||||
0 => Some(crate::SwizzleComponent::X),
|
||||
1 => Some(crate::SwizzleComponent::Y),
|
||||
2 => Some(crate::SwizzleComponent::Z),
|
||||
3 => Some(crate::SwizzleComponent::W),
|
||||
other => {
|
||||
log::error!("Invalid gather component operand: {other}");
|
||||
return Err(Error::InvalidOperand);
|
||||
}
|
||||
}
|
||||
}
|
||||
(true, None) => {
|
||||
debug_assert_eq!(level, crate::SampleLevel::Auto);
|
||||
level = crate::SampleLevel::Zero;
|
||||
|
||||
Some(crate::SwizzleComponent::X)
|
||||
}
|
||||
(_, _) => None,
|
||||
};
|
||||
|
||||
let sampling_bit = if options.compare {
|
||||
SamplingFlags::COMPARISON
|
||||
} else {
|
||||
@ -745,7 +799,7 @@ impl<I: Iterator<Item = u32>> super::Frontend<I> {
|
||||
let expr = crate::Expression::ImageSample {
|
||||
image: si_lexp.image,
|
||||
sampler: si_lexp.sampler,
|
||||
gather: None, //TODO
|
||||
gather,
|
||||
coordinate,
|
||||
array_index,
|
||||
offset,
|
||||
|
||||
@ -2802,6 +2802,7 @@ impl<I: Iterator<Item = u32>> Frontend<I> {
|
||||
let options = image::SamplingOptions {
|
||||
compare: false,
|
||||
project: false,
|
||||
gather: false,
|
||||
};
|
||||
self.parse_image_sample(
|
||||
extra,
|
||||
@ -2818,6 +2819,7 @@ impl<I: Iterator<Item = u32>> Frontend<I> {
|
||||
let options = image::SamplingOptions {
|
||||
compare: false,
|
||||
project: true,
|
||||
gather: false,
|
||||
};
|
||||
self.parse_image_sample(
|
||||
extra,
|
||||
@ -2834,6 +2836,7 @@ impl<I: Iterator<Item = u32>> Frontend<I> {
|
||||
let options = image::SamplingOptions {
|
||||
compare: true,
|
||||
project: false,
|
||||
gather: false,
|
||||
};
|
||||
self.parse_image_sample(
|
||||
extra,
|
||||
@ -2850,6 +2853,41 @@ impl<I: Iterator<Item = u32>> Frontend<I> {
|
||||
let options = image::SamplingOptions {
|
||||
compare: true,
|
||||
project: true,
|
||||
gather: false,
|
||||
};
|
||||
self.parse_image_sample(
|
||||
extra,
|
||||
options,
|
||||
ctx,
|
||||
&mut emitter,
|
||||
&mut block,
|
||||
block_id,
|
||||
body_idx,
|
||||
)?;
|
||||
}
|
||||
Op::ImageGather => {
|
||||
let extra = inst.expect_at_least(6)?;
|
||||
let options = image::SamplingOptions {
|
||||
compare: false,
|
||||
project: false,
|
||||
gather: true,
|
||||
};
|
||||
self.parse_image_sample(
|
||||
extra,
|
||||
options,
|
||||
ctx,
|
||||
&mut emitter,
|
||||
&mut block,
|
||||
block_id,
|
||||
body_idx,
|
||||
)?;
|
||||
}
|
||||
Op::ImageDrefGather => {
|
||||
let extra = inst.expect_at_least(6)?;
|
||||
let options = image::SamplingOptions {
|
||||
compare: true,
|
||||
project: false,
|
||||
gather: true,
|
||||
};
|
||||
self.parse_image_sample(
|
||||
extra,
|
||||
|
||||
18
naga/tests/in/spv/gather-cmp.slang
Normal file
18
naga/tests/in/spv/gather-cmp.slang
Normal file
@ -0,0 +1,18 @@
|
||||
// Compiled with:
|
||||
// slangc -target spirv -profile spirv_1_5 -o naga/tests/in/spv/gather-cmp.spv naga/tests/in/spv/gather-cmp.slang
|
||||
// Disassembled with:
|
||||
// spirv-dis naga/tests/in/spv/gather-cmp.spv -o naga/tests/in/spv/gather-cmp.spvasm
|
||||
|
||||
#language slang 2026
|
||||
|
||||
[[vk::binding(0, 0)]] var texture: Texture2D;
|
||||
[[vk::binding(1, 0)]] var depth_sampler: SamplerComparisonState;
|
||||
|
||||
struct VertexOutput {
|
||||
var texture_coordinates: float2;
|
||||
};
|
||||
|
||||
[[shader("pixel")]]
|
||||
func main(input: VertexOutput) -> float4 {
|
||||
return texture.GatherCmp(depth_sampler, input.texture_coordinates, 0.5);
|
||||
}
|
||||
49
naga/tests/in/spv/gather-cmp.spvasm
Normal file
49
naga/tests/in/spv/gather-cmp.spvasm
Normal file
@ -0,0 +1,49 @@
|
||||
; SPIR-V
|
||||
; Version: 1.5
|
||||
; Generator: Khronos Slang Compiler; 0
|
||||
; Bound: 27
|
||||
; Schema: 0
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main" %texture %depth_sampler %entryPointParam_main %input_texture_coordinates
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpSource Slang 1
|
||||
OpName %input_texture_coordinates "input.texture_coordinates"
|
||||
OpName %texture "texture"
|
||||
OpName %depth_sampler "depth_sampler"
|
||||
OpName %sampledImage "sampledImage"
|
||||
OpName %entryPointParam_main "entryPointParam_main"
|
||||
OpName %main "main"
|
||||
OpDecorate %input_texture_coordinates Location 0
|
||||
OpDecorate %texture Binding 0
|
||||
OpDecorate %texture DescriptorSet 0
|
||||
OpDecorate %depth_sampler Binding 1
|
||||
OpDecorate %depth_sampler DescriptorSet 0
|
||||
OpDecorate %entryPointParam_main Location 0
|
||||
%void = OpTypeVoid
|
||||
%3 = OpTypeFunction %void
|
||||
%float = OpTypeFloat 32
|
||||
%v2float = OpTypeVector %float 2
|
||||
%_ptr_Input_v2float = OpTypePointer Input %v2float
|
||||
%10 = OpTypeImage %float 2D 2 0 0 1 Unknown
|
||||
%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10
|
||||
%14 = OpTypeSampler
|
||||
%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14
|
||||
%18 = OpTypeSampledImage %10
|
||||
%v4float = OpTypeVector %float 4
|
||||
%float_0_5 = OpConstant %float 0.5
|
||||
%_ptr_Output_v4float = OpTypePointer Output %v4float
|
||||
%input_texture_coordinates = OpVariable %_ptr_Input_v2float Input
|
||||
%texture = OpVariable %_ptr_UniformConstant_10 UniformConstant
|
||||
%depth_sampler = OpVariable %_ptr_UniformConstant_14 UniformConstant
|
||||
%entryPointParam_main = OpVariable %_ptr_Output_v4float Output
|
||||
%main = OpFunction %void None %3
|
||||
%4 = OpLabel
|
||||
%7 = OpLoad %v2float %input_texture_coordinates
|
||||
%11 = OpLoad %10 %texture
|
||||
%15 = OpLoad %14 %depth_sampler
|
||||
%sampledImage = OpSampledImage %18 %11 %15
|
||||
%21 = OpImageDrefGather %v4float %sampledImage %7 %float_0_5
|
||||
OpStore %entryPointParam_main %21
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
4
naga/tests/in/spv/gather-cmp.toml
Normal file
4
naga/tests/in/spv/gather-cmp.toml
Normal file
@ -0,0 +1,4 @@
|
||||
god_mode = true
|
||||
|
||||
[spv-in]
|
||||
adjust_coordinate_space = true
|
||||
18
naga/tests/in/spv/gather.slang
Normal file
18
naga/tests/in/spv/gather.slang
Normal file
@ -0,0 +1,18 @@
|
||||
// Compiled with:
|
||||
// slangc -target spirv -profile spirv_1_5 -o naga/tests/in/spv/gather.spv naga/tests/in/spv/gather.slang
|
||||
// Disassembled with:
|
||||
// spirv-dis naga/tests/in/spv/gather.spv -o naga/tests/in/spv/gather.spvasm
|
||||
|
||||
#language slang 2026
|
||||
|
||||
[[vk::binding(0, 0)]] var texture: Texture2D;
|
||||
[[vk::binding(1, 0)]] var linear_sampler: SamplerState;
|
||||
|
||||
struct VertexOutput {
|
||||
var texture_coordinates: float2;
|
||||
};
|
||||
|
||||
[[shader("pixel")]]
|
||||
func main(input: VertexOutput) -> float4 {
|
||||
return texture.GatherGreen(linear_sampler, input.texture_coordinates);
|
||||
}
|
||||
50
naga/tests/in/spv/gather.spvasm
Normal file
50
naga/tests/in/spv/gather.spvasm
Normal file
@ -0,0 +1,50 @@
|
||||
; SPIR-V
|
||||
; Version: 1.5
|
||||
; Generator: Khronos Slang Compiler; 0
|
||||
; Bound: 28
|
||||
; Schema: 0
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main" %texture %linear_sampler %entryPointParam_main %input_texture_coordinates
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpSource Slang 1
|
||||
OpName %input_texture_coordinates "input.texture_coordinates"
|
||||
OpName %texture "texture"
|
||||
OpName %linear_sampler "linear_sampler"
|
||||
OpName %sampledImage "sampledImage"
|
||||
OpName %entryPointParam_main "entryPointParam_main"
|
||||
OpName %main "main"
|
||||
OpDecorate %input_texture_coordinates Location 0
|
||||
OpDecorate %texture Binding 0
|
||||
OpDecorate %texture DescriptorSet 0
|
||||
OpDecorate %linear_sampler Binding 1
|
||||
OpDecorate %linear_sampler DescriptorSet 0
|
||||
OpDecorate %entryPointParam_main Location 0
|
||||
%void = OpTypeVoid
|
||||
%3 = OpTypeFunction %void
|
||||
%float = OpTypeFloat 32
|
||||
%v2float = OpTypeVector %float 2
|
||||
%_ptr_Input_v2float = OpTypePointer Input %v2float
|
||||
%10 = OpTypeImage %float 2D 2 0 0 1 Unknown
|
||||
%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10
|
||||
%14 = OpTypeSampler
|
||||
%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14
|
||||
%18 = OpTypeSampledImage %10
|
||||
%v4float = OpTypeVector %float 4
|
||||
%int = OpTypeInt 32 1
|
||||
%int_1 = OpConstant %int 1
|
||||
%_ptr_Output_v4float = OpTypePointer Output %v4float
|
||||
%input_texture_coordinates = OpVariable %_ptr_Input_v2float Input
|
||||
%texture = OpVariable %_ptr_UniformConstant_10 UniformConstant
|
||||
%linear_sampler = OpVariable %_ptr_UniformConstant_14 UniformConstant
|
||||
%entryPointParam_main = OpVariable %_ptr_Output_v4float Output
|
||||
%main = OpFunction %void None %3
|
||||
%4 = OpLabel
|
||||
%7 = OpLoad %v2float %input_texture_coordinates
|
||||
%11 = OpLoad %10 %texture
|
||||
%15 = OpLoad %14 %linear_sampler
|
||||
%sampledImage = OpSampledImage %18 %11 %15
|
||||
%21 = OpImageGather %v4float %sampledImage %7 %int_1
|
||||
OpStore %entryPointParam_main %21
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
4
naga/tests/in/spv/gather.toml
Normal file
4
naga/tests/in/spv/gather.toml
Normal file
@ -0,0 +1,4 @@
|
||||
god_mode = true
|
||||
|
||||
[spv-in]
|
||||
adjust_coordinate_space = true
|
||||
21
naga/tests/out/wgsl/spv-gather-cmp.wgsl
Normal file
21
naga/tests/out/wgsl/spv-gather-cmp.wgsl
Normal file
@ -0,0 +1,21 @@
|
||||
var<private> inputtexture_coordinates_1: vec2<f32>;
|
||||
@group(0) @binding(0)
|
||||
var texture: texture_depth_2d;
|
||||
@group(0) @binding(1)
|
||||
var depth_sampler: sampler_comparison;
|
||||
var<private> entryPointParam_main: vec4<f32>;
|
||||
|
||||
fn main_1() {
|
||||
let _e5 = inputtexture_coordinates_1;
|
||||
let _e6 = textureGatherCompare(texture, depth_sampler, _e5, 0.5f);
|
||||
entryPointParam_main = _e6;
|
||||
return;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn main(@location(0) inputtexture_coordinates: vec2<f32>) -> @location(0) vec4<f32> {
|
||||
inputtexture_coordinates_1 = inputtexture_coordinates;
|
||||
main_1();
|
||||
let _e3 = entryPointParam_main;
|
||||
return _e3;
|
||||
}
|
||||
21
naga/tests/out/wgsl/spv-gather.wgsl
Normal file
21
naga/tests/out/wgsl/spv-gather.wgsl
Normal file
@ -0,0 +1,21 @@
|
||||
var<private> inputtexture_coordinates_1: vec2<f32>;
|
||||
@group(0) @binding(0)
|
||||
var texture: texture_2d<f32>;
|
||||
@group(0) @binding(1)
|
||||
var linear_sampler: sampler;
|
||||
var<private> entryPointParam_main: vec4<f32>;
|
||||
|
||||
fn main_1() {
|
||||
let _e4 = inputtexture_coordinates_1;
|
||||
let _e5 = textureGather(1, texture, linear_sampler, _e4);
|
||||
entryPointParam_main = _e5;
|
||||
return;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn main(@location(0) inputtexture_coordinates: vec2<f32>) -> @location(0) vec4<f32> {
|
||||
inputtexture_coordinates_1 = inputtexture_coordinates;
|
||||
main_1();
|
||||
let _e3 = entryPointParam_main;
|
||||
return _e3;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user