mirror of
https://github.com/gfx-rs/wgpu.git
synced 2026-04-22 03:02:01 -04:00
[spv-in] Support binding arrays (#2199)
This commit is contained in:
committed by
GitHub
parent
f70d8ec51f
commit
954cbaaff3
@@ -118,5 +118,10 @@ pub enum Error {
|
||||
InvalidBarrierScope(spirv::Word),
|
||||
#[error("invalid barrier memory semantics %{0}")]
|
||||
InvalidBarrierMemorySemantics(spirv::Word),
|
||||
// incomplete implementation errors
|
||||
#[error(
|
||||
"arrays of images / samplers are supported only through bindings for \
|
||||
now (i.e. you can't create an array of images or samplers that doesn't \
|
||||
come from a binding)"
|
||||
)]
|
||||
NonBindingArrayOfImageOrSamplers,
|
||||
}
|
||||
|
||||
@@ -536,19 +536,47 @@ impl<I: Iterator<Item = u32>> super::Parser<I> {
|
||||
|
||||
ctx.global_arena[handle].ty
|
||||
}
|
||||
|
||||
crate::Expression::FunctionArgument(i) => {
|
||||
ctx.parameter_sampling[i as usize] |= sampling_bit;
|
||||
ctx.arguments[i as usize].ty
|
||||
}
|
||||
|
||||
crate::Expression::Access { base, .. } => match ctx.expressions[base] {
|
||||
crate::Expression::GlobalVariable(handle) => {
|
||||
if let Some(flags) = self.handle_sampling.get_mut(&handle) {
|
||||
*flags |= sampling_bit;
|
||||
}
|
||||
|
||||
match ctx.type_arena[ctx.global_arena[handle].ty].inner {
|
||||
crate::TypeInner::BindingArray { base, .. } => base,
|
||||
_ => return Err(Error::InvalidGlobalVar(ctx.expressions[base].clone())),
|
||||
}
|
||||
}
|
||||
|
||||
ref other => return Err(Error::InvalidGlobalVar(other.clone())),
|
||||
},
|
||||
|
||||
ref other => return Err(Error::InvalidGlobalVar(other.clone())),
|
||||
};
|
||||
|
||||
match ctx.expressions[si_lexp.sampler] {
|
||||
crate::Expression::GlobalVariable(handle) => {
|
||||
*self.handle_sampling.get_mut(&handle).unwrap() |= sampling_bit
|
||||
*self.handle_sampling.get_mut(&handle).unwrap() |= sampling_bit;
|
||||
}
|
||||
|
||||
crate::Expression::FunctionArgument(i) => {
|
||||
ctx.parameter_sampling[i as usize] |= sampling_bit;
|
||||
}
|
||||
|
||||
crate::Expression::Access { base, .. } => match ctx.expressions[base] {
|
||||
crate::Expression::GlobalVariable(handle) => {
|
||||
*self.handle_sampling.get_mut(&handle).unwrap() |= sampling_bit;
|
||||
}
|
||||
|
||||
ref other => return Err(Error::InvalidGlobalVar(other.clone())),
|
||||
},
|
||||
|
||||
ref other => return Err(Error::InvalidGlobalVar(other.clone())),
|
||||
}
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ impl Instruction {
|
||||
}
|
||||
|
||||
impl crate::TypeInner {
|
||||
const fn can_comparison_sample(&self) -> bool {
|
||||
fn can_comparison_sample(&self, module: &crate::Module) -> bool {
|
||||
match *self {
|
||||
crate::TypeInner::Image {
|
||||
class:
|
||||
@@ -117,6 +117,9 @@ impl crate::TypeInner {
|
||||
..
|
||||
} => true,
|
||||
crate::TypeInner::Sampler { .. } => true,
|
||||
crate::TypeInner::BindingArray { base, .. } => {
|
||||
module.types[base].inner.can_comparison_sample(module)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@@ -1428,17 +1431,38 @@ impl<I: Iterator<Item = u32>> Parser<I> {
|
||||
let result_id = self.next()?;
|
||||
let base_id = self.next()?;
|
||||
log::trace!("\t\t\tlooking up expr {:?}", base_id);
|
||||
|
||||
let mut acex = {
|
||||
// the base type has to be a pointer,
|
||||
// so we dereference it here for the traversal
|
||||
let lexp = self.lookup_expression.lookup(base_id)?;
|
||||
let lty = self.lookup_type.lookup(lexp.type_id)?;
|
||||
|
||||
// HACK `OpAccessChain` and `OpInBoundsAccessChain`
|
||||
// require for the result type to be a pointer, but if
|
||||
// we're given a pointer to an image / sampler, it will
|
||||
// be *already* dereferenced, since we do that early
|
||||
// during `parse_type_pointer()`.
|
||||
//
|
||||
// This can happen only through `BindingArray`, since
|
||||
// that's the only case where one can obtain a pointer
|
||||
// to an image / sampler, and so let's match on that:
|
||||
let dereference = match ctx.type_arena[lty.handle].inner {
|
||||
crate::TypeInner::BindingArray { .. } => false,
|
||||
_ => true,
|
||||
};
|
||||
|
||||
let type_id = if dereference {
|
||||
lty.base_id.ok_or(Error::InvalidAccessType(lexp.type_id))?
|
||||
} else {
|
||||
lexp.type_id
|
||||
};
|
||||
|
||||
AccessExpression {
|
||||
base_handle: get_expr_handle!(base_id, lexp),
|
||||
type_id: lty.base_id.ok_or(Error::InvalidAccessType(lexp.type_id))?,
|
||||
type_id,
|
||||
load_override: self.lookup_load_override.get(&base_id).cloned(),
|
||||
}
|
||||
};
|
||||
|
||||
for _ in 4..inst.wc {
|
||||
let access_id = self.next()?;
|
||||
log::trace!("\t\t\tlooking up index expr {:?}", access_id);
|
||||
@@ -4220,6 +4244,7 @@ impl<I: Iterator<Item = u32>> Parser<I> {
|
||||
let decor = self.future_decor.remove(&id);
|
||||
let base_lookup_ty = self.lookup_type.lookup(type_id)?;
|
||||
let base_inner = &module.types[base_lookup_ty.handle].inner;
|
||||
|
||||
let space = if let Some(space) = base_inner.pointer_space() {
|
||||
space
|
||||
} else if self
|
||||
@@ -4289,17 +4314,60 @@ impl<I: Iterator<Item = u32>> Parser<I> {
|
||||
|
||||
let decor = self.future_decor.remove(&id).unwrap_or_default();
|
||||
let base = self.lookup_type.lookup(type_id)?.handle;
|
||||
|
||||
self.layouter
|
||||
.update(&module.types, &module.constants)
|
||||
.unwrap();
|
||||
let inner = crate::TypeInner::Array {
|
||||
base,
|
||||
size: crate::ArraySize::Constant(length_const.handle),
|
||||
stride: match decor.array_stride {
|
||||
Some(stride) => stride.get(),
|
||||
None => self.layouter[base].to_stride(),
|
||||
},
|
||||
|
||||
// HACK if the underlying type is an image or a sampler, let's assume
|
||||
// that we're dealing with a binding-array
|
||||
//
|
||||
// Note that it's not a strictly correct assumption, but rather a trade
|
||||
// off caused by an impedance mismatch between SPIR-V's and Naga's type
|
||||
// systems - Naga distinguishes between arrays and binding-arrays via
|
||||
// types (i.e. both kinds of arrays are just different types), while
|
||||
// SPIR-V distinguishes between them through usage - e.g. given:
|
||||
//
|
||||
// ```
|
||||
// %image = OpTypeImage %float 2D 2 0 0 2 Rgba16f
|
||||
// %uint_256 = OpConstant %uint 256
|
||||
// %image_array = OpTypeArray %image %uint_256
|
||||
// ```
|
||||
//
|
||||
// ```
|
||||
// %image = OpTypeImage %float 2D 2 0 0 2 Rgba16f
|
||||
// %uint_256 = OpConstant %uint 256
|
||||
// %image_array = OpTypeArray %image %uint_256
|
||||
// %image_array_ptr = OpTypePointer UniformConstant %image_array
|
||||
// ```
|
||||
//
|
||||
// ... in the first case, `%image_array` should technically correspond
|
||||
// to `TypeInner::Array`, while in the second case it should say
|
||||
// `TypeInner::BindingArray` (kinda, depending on whether `%image_array`
|
||||
// is ever used as a freestanding type or rather always through the
|
||||
// pointer-indirection).
|
||||
//
|
||||
// Anyway, at the moment we don't support other kinds of image / sampler
|
||||
// arrays than those binding-based, so this assumption is pretty safe
|
||||
// for now.
|
||||
let inner = if let crate::TypeInner::Image { .. } | crate::TypeInner::Sampler { .. } =
|
||||
module.types[base].inner
|
||||
{
|
||||
crate::TypeInner::BindingArray {
|
||||
base,
|
||||
size: crate::ArraySize::Constant(length_const.handle),
|
||||
}
|
||||
} else {
|
||||
crate::TypeInner::Array {
|
||||
base,
|
||||
size: crate::ArraySize::Constant(length_const.handle),
|
||||
stride: match decor.array_stride {
|
||||
Some(stride) => stride.get(),
|
||||
None => self.layouter[base].to_stride(),
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
self.lookup_type.insert(
|
||||
id,
|
||||
LookupType {
|
||||
@@ -4329,17 +4397,30 @@ impl<I: Iterator<Item = u32>> Parser<I> {
|
||||
|
||||
let decor = self.future_decor.remove(&id).unwrap_or_default();
|
||||
let base = self.lookup_type.lookup(type_id)?.handle;
|
||||
|
||||
self.layouter
|
||||
.update(&module.types, &module.constants)
|
||||
.unwrap();
|
||||
let inner = crate::TypeInner::Array {
|
||||
base: self.lookup_type.lookup(type_id)?.handle,
|
||||
size: crate::ArraySize::Dynamic,
|
||||
stride: match decor.array_stride {
|
||||
Some(stride) => stride.get(),
|
||||
None => self.layouter[base].to_stride(),
|
||||
},
|
||||
|
||||
// HACK same case as in `parse_type_array()`
|
||||
let inner = if let crate::TypeInner::Image { .. } | crate::TypeInner::Sampler { .. } =
|
||||
module.types[base].inner
|
||||
{
|
||||
crate::TypeInner::BindingArray {
|
||||
base: self.lookup_type.lookup(type_id)?.handle,
|
||||
size: crate::ArraySize::Dynamic,
|
||||
}
|
||||
} else {
|
||||
crate::TypeInner::Array {
|
||||
base: self.lookup_type.lookup(type_id)?.handle,
|
||||
size: crate::ArraySize::Dynamic,
|
||||
stride: match decor.array_stride {
|
||||
Some(stride) => stride.get(),
|
||||
None => self.layouter[base].to_stride(),
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
self.lookup_type.insert(
|
||||
id,
|
||||
LookupType {
|
||||
@@ -4793,32 +4874,45 @@ impl<I: Iterator<Item = u32>> Parser<I> {
|
||||
let mut dec = self.future_decor.remove(&id).unwrap_or_default();
|
||||
|
||||
let original_ty = self.lookup_type.lookup(type_id)?.handle;
|
||||
let mut effective_ty = original_ty;
|
||||
let mut ty = original_ty;
|
||||
|
||||
if let crate::TypeInner::Pointer { base, space: _ } = module.types[original_ty].inner {
|
||||
effective_ty = base;
|
||||
};
|
||||
ty = base;
|
||||
}
|
||||
|
||||
if let crate::TypeInner::BindingArray { .. } = module.types[original_ty].inner {
|
||||
// Inside `parse_type_array()` we guess that an array of images or
|
||||
// samplers must be a binding array, and here we validate that guess
|
||||
if dec.desc_set.is_none() || dec.desc_index.is_none() {
|
||||
return Err(Error::NonBindingArrayOfImageOrSamplers);
|
||||
}
|
||||
}
|
||||
|
||||
if let crate::TypeInner::Image {
|
||||
dim,
|
||||
arrayed,
|
||||
class: crate::ImageClass::Storage { format, access: _ },
|
||||
} = module.types[effective_ty].inner
|
||||
} = module.types[ty].inner
|
||||
{
|
||||
// Storage image types in IR have to contain the access, but not in the SPIR-V.
|
||||
// The same image type in SPIR-V can be used (and has to be used) for multiple images.
|
||||
// So we copy the type out and apply the variable access decorations.
|
||||
let access = dec.flags.to_storage_access();
|
||||
let ty = crate::Type {
|
||||
name: None,
|
||||
inner: crate::TypeInner::Image {
|
||||
dim,
|
||||
arrayed,
|
||||
class: crate::ImageClass::Storage { format, access },
|
||||
|
||||
ty = module.types.insert(
|
||||
crate::Type {
|
||||
name: None,
|
||||
inner: crate::TypeInner::Image {
|
||||
dim,
|
||||
arrayed,
|
||||
class: crate::ImageClass::Storage { format, access },
|
||||
},
|
||||
},
|
||||
};
|
||||
effective_ty = module.types.insert(ty, Default::default());
|
||||
Default::default(),
|
||||
);
|
||||
}
|
||||
|
||||
let ext_class = match self.lookup_storage_buffer_types.get(&effective_ty) {
|
||||
let ext_class = match self.lookup_storage_buffer_types.get(&ty) {
|
||||
Some(&access) => ExtendedClass::Global(crate::AddressSpace::Storage { access }),
|
||||
None => map_storage_class(storage_class)?,
|
||||
};
|
||||
@@ -4843,14 +4937,14 @@ impl<I: Iterator<Item = u32>> Parser<I> {
|
||||
binding: dec.resource_binding(),
|
||||
name: dec.name,
|
||||
space,
|
||||
ty: effective_ty,
|
||||
ty,
|
||||
init,
|
||||
};
|
||||
(Variable::Global, var)
|
||||
}
|
||||
ExtendedClass::Input => {
|
||||
let mut binding = dec.io_binding()?;
|
||||
let mut unsigned_ty = effective_ty;
|
||||
let mut unsigned_ty = ty;
|
||||
if let crate::Binding::BuiltIn(built_in) = binding {
|
||||
let needs_inner_uint = match built_in {
|
||||
crate::BuiltIn::BaseInstance
|
||||
@@ -4873,10 +4967,9 @@ impl<I: Iterator<Item = u32>> Parser<I> {
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
if let (Some(inner), Some(crate::ScalarKind::Sint)) = (
|
||||
needs_inner_uint,
|
||||
module.types[effective_ty].inner.scalar_kind(),
|
||||
) {
|
||||
if let (Some(inner), Some(crate::ScalarKind::Sint)) =
|
||||
(needs_inner_uint, module.types[ty].inner.scalar_kind())
|
||||
{
|
||||
unsigned_ty = module
|
||||
.types
|
||||
.insert(crate::Type { name: None, inner }, Default::default());
|
||||
@@ -4887,7 +4980,7 @@ impl<I: Iterator<Item = u32>> Parser<I> {
|
||||
name: dec.name.clone(),
|
||||
space: crate::AddressSpace::Private,
|
||||
binding: None,
|
||||
ty: effective_ty,
|
||||
ty,
|
||||
init: None,
|
||||
};
|
||||
|
||||
@@ -4907,7 +5000,7 @@ impl<I: Iterator<Item = u32>> Parser<I> {
|
||||
Some(crate::Binding::BuiltIn(built_in)) => {
|
||||
match null::generate_default_built_in(
|
||||
Some(built_in),
|
||||
effective_ty,
|
||||
ty,
|
||||
&module.types,
|
||||
&mut module.constants,
|
||||
span,
|
||||
@@ -4920,7 +5013,7 @@ impl<I: Iterator<Item = u32>> Parser<I> {
|
||||
}
|
||||
}
|
||||
Some(crate::Binding::Location { .. }) => None,
|
||||
None => match module.types[effective_ty].inner {
|
||||
None => match module.types[ty].inner {
|
||||
crate::TypeInner::Struct { ref members, .. } => {
|
||||
// A temporary to avoid borrowing `module.types`
|
||||
let pairs = members
|
||||
@@ -4949,10 +5042,7 @@ impl<I: Iterator<Item = u32>> Parser<I> {
|
||||
crate::Constant {
|
||||
name: None,
|
||||
specialization: None,
|
||||
inner: crate::ConstantInner::Composite {
|
||||
ty: effective_ty,
|
||||
components,
|
||||
},
|
||||
inner: crate::ConstantInner::Composite { ty, components },
|
||||
},
|
||||
span,
|
||||
))
|
||||
@@ -4965,23 +5055,22 @@ impl<I: Iterator<Item = u32>> Parser<I> {
|
||||
name: dec.name,
|
||||
space: crate::AddressSpace::Private,
|
||||
binding: None,
|
||||
ty: effective_ty,
|
||||
ty,
|
||||
init,
|
||||
};
|
||||
if let Some(ref mut binding) = binding {
|
||||
binding.apply_default_interpolation(&module.types[effective_ty].inner);
|
||||
binding.apply_default_interpolation(&module.types[ty].inner);
|
||||
}
|
||||
let inner = Variable::Output(crate::FunctionResult {
|
||||
ty: effective_ty,
|
||||
binding,
|
||||
});
|
||||
let inner = Variable::Output(crate::FunctionResult { ty, binding });
|
||||
(inner, var)
|
||||
}
|
||||
};
|
||||
|
||||
let handle = module.global_variables.append(var, span);
|
||||
if module.types[effective_ty].inner.can_comparison_sample() {
|
||||
|
||||
if module.types[ty].inner.can_comparison_sample(module) {
|
||||
log::debug!("\t\ttracking {:?} for sampling properties", handle);
|
||||
|
||||
self.handle_sampling
|
||||
.insert(handle, image::SamplingFlags::empty());
|
||||
}
|
||||
|
||||
BIN
tests/in/spv/binding-arrays.dynamic.spv
Normal file
BIN
tests/in/spv/binding-arrays.dynamic.spv
Normal file
Binary file not shown.
75
tests/in/spv/binding-arrays.dynamic.spvasm
Normal file
75
tests/in/spv/binding-arrays.dynamic.spvasm
Normal file
@@ -0,0 +1,75 @@
|
||||
;; Make sure that we promote `OpTypeRuntimeArray` of textures and samplers into
|
||||
;; `TypeInner::BindingArray` and support indexing it through `OpAccessChain`
|
||||
;; and `OpInBoundsAccessChain`.
|
||||
;;
|
||||
;; Code in here corresponds to, more or less:
|
||||
;;
|
||||
;; ```rust
|
||||
;; #[spirv(fragment)]
|
||||
;; pub fn main(
|
||||
;; #[spirv(descriptor_set = 0, binding = 0)]
|
||||
;; images: &RuntimeArray<Image!(2D, type=f32, sampled)>,
|
||||
;; #[spirv(descriptor_set = 0, binding = 1)]
|
||||
;; samplers: &RuntimeArray<Sampler>,
|
||||
;; out: &mut Vec4,
|
||||
;; ) {
|
||||
;; let image = images[1];
|
||||
;; let sampler = samplers[1];
|
||||
;;
|
||||
;; *out = image.sample_by_lod(sampler, vec2(0.5, 0.5), 0.0);
|
||||
;; }
|
||||
;; ```
|
||||
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical Simple
|
||||
OpEntryPoint Fragment %main "main" %fn_param_images %fn_param_samplers %fn_param_out
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpDecorate %images ArrayStride 4
|
||||
OpDecorate %samplers ArrayStride 4
|
||||
OpDecorate %fn_param_images DescriptorSet 0
|
||||
OpDecorate %fn_param_images Binding 0
|
||||
OpDecorate %fn_param_samplers DescriptorSet 0
|
||||
OpDecorate %fn_param_samplers Binding 1
|
||||
OpDecorate %fn_param_out Location 0
|
||||
|
||||
%void = OpTypeVoid
|
||||
|
||||
%float = OpTypeFloat 32
|
||||
%v2float = OpTypeVector %float 2
|
||||
%v4float = OpTypeVector %float 4
|
||||
%v4float_ptr = OpTypePointer Output %v4float
|
||||
%float_0_5 = OpConstant %float 0.5
|
||||
%float_0_5_0_5 = OpConstantComposite %v2float %float_0_5 %float_0_5
|
||||
%float_0 = OpConstant %float 0
|
||||
|
||||
%int = OpTypeInt 32 1
|
||||
%int_1 = OpConstant %int 1
|
||||
|
||||
%image = OpTypeImage %float 2D 2 0 0 1 Unknown
|
||||
%image_ptr = OpTypePointer UniformConstant %image
|
||||
%images = OpTypeRuntimeArray %image
|
||||
%images_ptr = OpTypePointer UniformConstant %images
|
||||
|
||||
%sampler = OpTypeSampler
|
||||
%sampler_ptr = OpTypePointer UniformConstant %sampler
|
||||
%samplers = OpTypeRuntimeArray %sampler
|
||||
%samplers_ptr = OpTypePointer UniformConstant %samplers
|
||||
|
||||
%sampled_image = OpTypeSampledImage %image
|
||||
|
||||
%fn_void = OpTypeFunction %void
|
||||
%fn_param_images = OpVariable %images_ptr UniformConstant
|
||||
%fn_param_samplers = OpVariable %samplers_ptr UniformConstant
|
||||
%fn_param_out = OpVariable %v4float_ptr Output
|
||||
|
||||
%main = OpFunction %void None %fn_void
|
||||
%main_prelude = OpLabel
|
||||
%1 = OpAccessChain %image_ptr %fn_param_images %int_1
|
||||
%2 = OpInBoundsAccessChain %sampler_ptr %fn_param_samplers %int_1
|
||||
%3 = OpLoad %sampler %2
|
||||
%4 = OpLoad %image %1
|
||||
%5 = OpSampledImage %sampled_image %4 %3
|
||||
%6 = OpImageSampleExplicitLod %v4float %5 %float_0_5_0_5 Lod %float_0
|
||||
OpStore %fn_param_out %6
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
BIN
tests/in/spv/binding-arrays.static.spv
Normal file
BIN
tests/in/spv/binding-arrays.static.spv
Normal file
Binary file not shown.
78
tests/in/spv/binding-arrays.static.spvasm
Normal file
78
tests/in/spv/binding-arrays.static.spvasm
Normal file
@@ -0,0 +1,78 @@
|
||||
;; Make sure that we promote `OpTypeArray` of textures and samplers into
|
||||
;; `TypeInner::BindingArray` and support indexing it through `OpAccessChain`
|
||||
;; and `OpInBoundsAccessChain`.
|
||||
;;
|
||||
;; Code in here corresponds to, more or less:
|
||||
;;
|
||||
;; ```rust
|
||||
;; #[spirv(fragment)]
|
||||
;; pub fn main(
|
||||
;; #[spirv(descriptor_set = 0, binding = 0)]
|
||||
;; images: &[Image!(2D, type=f32, sampled); 256],
|
||||
;; #[spirv(descriptor_set = 0, binding = 1)]
|
||||
;; samplers: &[Sampler; 256],
|
||||
;; out: &mut Vec4,
|
||||
;; ) {
|
||||
;; let image = images[1];
|
||||
;; let sampler = samplers[1];
|
||||
;;
|
||||
;; *out = image.sample_by_lod(sampler, vec2(0.5, 0.5), 0.0);
|
||||
;; }
|
||||
;; ```
|
||||
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical Simple
|
||||
OpEntryPoint Fragment %main "main" %fn_param_images %fn_param_samplers %fn_param_out
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpDecorate %images ArrayStride 4
|
||||
OpDecorate %samplers ArrayStride 4
|
||||
OpDecorate %fn_param_images DescriptorSet 0
|
||||
OpDecorate %fn_param_images Binding 0
|
||||
OpDecorate %fn_param_samplers DescriptorSet 0
|
||||
OpDecorate %fn_param_samplers Binding 1
|
||||
OpDecorate %fn_param_out Location 0
|
||||
|
||||
%void = OpTypeVoid
|
||||
|
||||
%float = OpTypeFloat 32
|
||||
%v2float = OpTypeVector %float 2
|
||||
%v4float = OpTypeVector %float 4
|
||||
%v4float_ptr = OpTypePointer Output %v4float
|
||||
%float_0_5 = OpConstant %float 0.5
|
||||
%float_0_5_0_5 = OpConstantComposite %v2float %float_0_5 %float_0_5
|
||||
%float_0 = OpConstant %float 0
|
||||
|
||||
%int = OpTypeInt 32 1
|
||||
%int_1 = OpConstant %int 1
|
||||
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint_256 = OpConstant %uint 256
|
||||
|
||||
%image = OpTypeImage %float 2D 2 0 0 1 Unknown
|
||||
%image_ptr = OpTypePointer UniformConstant %image
|
||||
%images = OpTypeArray %image %uint_256
|
||||
%images_ptr = OpTypePointer UniformConstant %images
|
||||
|
||||
%sampler = OpTypeSampler
|
||||
%sampler_ptr = OpTypePointer UniformConstant %sampler
|
||||
%samplers = OpTypeArray %sampler %uint_256
|
||||
%samplers_ptr = OpTypePointer UniformConstant %samplers
|
||||
|
||||
%sampled_image = OpTypeSampledImage %image
|
||||
|
||||
%fn_void = OpTypeFunction %void
|
||||
%fn_param_images = OpVariable %images_ptr UniformConstant
|
||||
%fn_param_samplers = OpVariable %samplers_ptr UniformConstant
|
||||
%fn_param_out = OpVariable %v4float_ptr Output
|
||||
|
||||
%main = OpFunction %void None %fn_void
|
||||
%main_prelude = OpLabel
|
||||
%1 = OpAccessChain %image_ptr %fn_param_images %int_1
|
||||
%2 = OpInBoundsAccessChain %sampler_ptr %fn_param_samplers %int_1
|
||||
%3 = OpLoad %sampler %2
|
||||
%4 = OpLoad %image %1
|
||||
%5 = OpSampledImage %sampled_image %4 %3
|
||||
%6 = OpImageSampleExplicitLod %v4float %5 %float_0_5_0_5 Lod %float_0
|
||||
OpStore %fn_param_out %6
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
18
tests/out/wgsl/binding-arrays.dynamic.wgsl
Normal file
18
tests/out/wgsl/binding-arrays.dynamic.wgsl
Normal file
@@ -0,0 +1,18 @@
|
||||
@group(0) @binding(0)
|
||||
var global: binding_array<texture_2d<f32>>;
|
||||
@group(0) @binding(1)
|
||||
var global_1: binding_array<sampler>;
|
||||
var<private> global_2: vec4<f32>;
|
||||
|
||||
fn function() {
|
||||
let _e13 = textureSampleLevel(global[1], global_1[1], vec2<f32>(0.5, 0.5), 0.0);
|
||||
global_2 = _e13;
|
||||
return;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn main() -> @location(0) vec4<f32> {
|
||||
function();
|
||||
let _e1 = global_2;
|
||||
return _e1;
|
||||
}
|
||||
18
tests/out/wgsl/binding-arrays.static.wgsl
Normal file
18
tests/out/wgsl/binding-arrays.static.wgsl
Normal file
@@ -0,0 +1,18 @@
|
||||
@group(0) @binding(0)
|
||||
var global: binding_array<texture_2d<f32>,256u>;
|
||||
@group(0) @binding(1)
|
||||
var global_1: binding_array<sampler,256u>;
|
||||
var<private> global_2: vec4<f32>;
|
||||
|
||||
fn function() {
|
||||
let _e14 = textureSampleLevel(global[1], global_1[1], vec2<f32>(0.5, 0.5), 0.0);
|
||||
global_2 = _e14;
|
||||
return;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn main() -> @location(0) vec4<f32> {
|
||||
function();
|
||||
let _e1 = global_2;
|
||||
return _e1;
|
||||
}
|
||||
@@ -607,12 +607,9 @@ fn convert_spv_all() {
|
||||
true,
|
||||
Targets::HLSL | Targets::WGSL | Targets::METAL,
|
||||
);
|
||||
convert_spv(
|
||||
"empty-global-name",
|
||||
true,
|
||||
Targets::HLSL | Targets::WGSL | Targets::METAL,
|
||||
);
|
||||
convert_spv("degrees", false, Targets::empty());
|
||||
convert_spv("binding-arrays.dynamic", true, Targets::WGSL);
|
||||
convert_spv("binding-arrays.static", true, Targets::WGSL);
|
||||
}
|
||||
|
||||
#[cfg(feature = "glsl-in")]
|
||||
|
||||
Reference in New Issue
Block a user