Deprecate #[spirv(block)] and auto-wrap in "interface blocks" instead.

This commit is contained in:
Eduard-Mihai Burtescu 2021-04-05 14:33:35 +03:00 committed by Eduard-Mihai Burtescu
parent 561f0dd984
commit 4395b84114
15 changed files with 178 additions and 181 deletions

View File

@ -237,7 +237,6 @@ impl<'tcx> ConvSpirvType<'tcx> for CastTarget {
field_types: args, field_types: args,
field_offsets, field_offsets,
field_names: None, field_names: None,
is_block: false,
} }
.def(span, cx) .def(span, cx)
} }
@ -325,34 +324,6 @@ fn trans_type_impl<'tcx>(
) -> Word { ) -> Word {
if let TyKind::Adt(adt, substs) = *ty.ty.kind() { if let TyKind::Adt(adt, substs) = *ty.ty.kind() {
let attrs = AggregatedSpirvAttributes::parse(cx, cx.tcx.get_attrs(adt.did)); let attrs = AggregatedSpirvAttributes::parse(cx, cx.tcx.get_attrs(adt.did));
if attrs.block.is_some() {
if !adt.is_struct() {
cx.tcx.sess.span_err(
span,
&format!(
"`#[spirv(block)]` can only be used on a `struct`, \
but `{}` is a `{}`",
ty.ty,
adt.descr(),
),
);
}
if !matches!(ty.abi, Abi::Aggregate { .. }) {
cx.tcx.sess.span_err(
span,
&format!(
"`#[spirv(block)]` can only be used on aggregates, \
but `{}` has `Abi::{:?}`",
ty.ty, ty.abi,
),
);
}
assert!(matches!(ty.fields, FieldsShape::Arbitrary { .. }));
return trans_struct(cx, span, ty, true);
}
if let Some(intrinsic_type_attr) = attrs.intrinsic_type.map(|attr| attr.value) { if let Some(intrinsic_type_attr) = attrs.intrinsic_type.map(|attr| attr.value) {
if let Ok(spirv_type) = trans_intrinsic_type(cx, span, ty, substs, intrinsic_type_attr) if let Ok(spirv_type) = trans_intrinsic_type(cx, span, ty, substs, intrinsic_type_attr)
@ -361,6 +332,7 @@ fn trans_type_impl<'tcx>(
} }
} }
} }
// Note: ty.layout is orthogonal to ty.ty, e.g. `ManuallyDrop<Result<isize, isize>>` has abi // Note: ty.layout is orthogonal to ty.ty, e.g. `ManuallyDrop<Result<isize, isize>>` has abi
// `ScalarPair`. // `ScalarPair`.
// There's a few layers that we go through here. First we inspect layout.abi, then if relevant, layout.fields, etc. // There's a few layers that we go through here. First we inspect layout.abi, then if relevant, layout.fields, etc.
@ -372,7 +344,6 @@ fn trans_type_impl<'tcx>(
field_types: Vec::new(), field_types: Vec::new(),
field_offsets: Vec::new(), field_offsets: Vec::new(),
field_names: None, field_names: None,
is_block: false,
} }
.def_with_name(cx, span, TyLayoutNameKey::from(ty)), .def_with_name(cx, span, TyLayoutNameKey::from(ty)),
Abi::Scalar(ref scalar) => trans_scalar(cx, span, ty, scalar, None, is_immediate), Abi::Scalar(ref scalar) => trans_scalar(cx, span, ty, scalar, None, is_immediate),
@ -392,7 +363,6 @@ fn trans_type_impl<'tcx>(
field_types: vec![one_spirv, two_spirv], field_types: vec![one_spirv, two_spirv],
field_offsets: vec![one_offset, two_offset], field_offsets: vec![one_offset, two_offset],
field_names: None, field_names: None,
is_block: false,
} }
.def_with_name(cx, span, TyLayoutNameKey::from(ty)) .def_with_name(cx, span, TyLayoutNameKey::from(ty))
} }
@ -621,7 +591,6 @@ fn trans_aggregate<'tcx>(cx: &CodegenCx<'tcx>, span: Span, ty: TyAndLayout<'tcx>
field_types: Vec::new(), field_types: Vec::new(),
field_offsets: Vec::new(), field_offsets: Vec::new(),
field_names: None, field_names: None,
is_block: false,
} }
.def_with_name(cx, span, TyLayoutNameKey::from(ty)) .def_with_name(cx, span, TyLayoutNameKey::from(ty))
} else { } else {
@ -642,7 +611,7 @@ fn trans_aggregate<'tcx>(cx: &CodegenCx<'tcx>, span: Span, ty: TyAndLayout<'tcx>
FieldsShape::Arbitrary { FieldsShape::Arbitrary {
offsets: _, offsets: _,
memory_index: _, memory_index: _,
} => trans_struct(cx, span, ty, false), } => trans_struct(cx, span, ty),
} }
} }
@ -672,12 +641,7 @@ pub fn auto_struct_layout<'tcx>(
} }
// see struct_llfields in librustc_codegen_llvm for implementation hints // see struct_llfields in librustc_codegen_llvm for implementation hints
fn trans_struct<'tcx>( fn trans_struct<'tcx>(cx: &CodegenCx<'tcx>, span: Span, ty: TyAndLayout<'tcx>) -> Word {
cx: &CodegenCx<'tcx>,
span: Span,
ty: TyAndLayout<'tcx>,
is_block: bool,
) -> Word {
if let TyKind::Foreign(_) = ty.ty.kind() { if let TyKind::Foreign(_) = ty.ty.kind() {
// "An unsized FFI type that is opaque to Rust", `extern type A;` (currently unstable) // "An unsized FFI type that is opaque to Rust", `extern type A;` (currently unstable)
if cx.kernel_mode { if cx.kernel_mode {
@ -726,7 +690,6 @@ fn trans_struct<'tcx>(
field_types, field_types,
field_offsets, field_offsets,
field_names: Some(field_names), field_names: Some(field_names),
is_block,
} }
.def_with_name(cx, span, TyLayoutNameKey::from(ty)) .def_with_name(cx, span, TyLayoutNameKey::from(ty))
} }

View File

@ -367,6 +367,16 @@ impl CheckSpirvAttrVisitor<'_> {
}, },
} }
} }
// At this point we have all of the attributes (valid for this target),
// so we can perform further checks, emit warnings, etc.
if let Some(block_attr) = aggregated_attrs.block {
self.tcx.sess.span_warn(
block_attr.span,
"#[spirv(block)] is no longer needed and should be removed",
);
}
} }
} }

View File

@ -232,6 +232,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
SpirvType::Image { .. } => self.fatal("cannot memset image"), SpirvType::Image { .. } => self.fatal("cannot memset image"),
SpirvType::Sampler => self.fatal("cannot memset sampler"), SpirvType::Sampler => self.fatal("cannot memset sampler"),
SpirvType::SampledImage { .. } => self.fatal("cannot memset sampled image"), SpirvType::SampledImage { .. } => self.fatal("cannot memset sampled image"),
SpirvType::InterfaceBlock { .. } => self.fatal("cannot memset interface block"),
} }
} }
@ -288,6 +289,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
SpirvType::Image { .. } => self.fatal("cannot memset image"), SpirvType::Image { .. } => self.fatal("cannot memset image"),
SpirvType::Sampler => self.fatal("cannot memset sampler"), SpirvType::Sampler => self.fatal("cannot memset sampler"),
SpirvType::SampledImage { .. } => self.fatal("cannot memset sampled image"), SpirvType::SampledImage { .. } => self.fatal("cannot memset sampled image"),
SpirvType::InterfaceBlock { .. } => self.fatal("cannot memset interface block"),
} }
} }
@ -1041,6 +1043,10 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
SpirvType::Array { element, .. } SpirvType::Array { element, .. }
| SpirvType::RuntimeArray { element, .. } | SpirvType::RuntimeArray { element, .. }
| SpirvType::Vector { element, .. } => element, | SpirvType::Vector { element, .. } => element,
SpirvType::InterfaceBlock { inner_type } => {
assert_eq!(idx, 0);
inner_type
}
other => self.fatal(&format!( other => self.fatal(&format!(
"struct_gep not on struct, array, or vector type: {:?}, index {}", "struct_gep not on struct, array, or vector type: {:?}, index {}",
other, idx other, idx

View File

@ -180,7 +180,6 @@ impl<'tcx> ConstMethods<'tcx> for CodegenCx<'tcx> {
field_types, field_types,
field_offsets, field_offsets,
field_names: None, field_names: None,
is_block: false,
} }
.def(DUMMY_SP, self); .def(DUMMY_SP, self);
self.constant_composite(struct_ty, elts.iter().map(|f| f.def_cx(self)).collect()) self.constant_composite(struct_ty, elts.iter().map(|f| f.def_cx(self)).collect())
@ -524,6 +523,10 @@ impl<'tcx> CodegenCx<'tcx> {
.tcx .tcx
.sess .sess
.fatal("Cannot create a constant sampled image value"), .fatal("Cannot create a constant sampled image value"),
SpirvType::InterfaceBlock { .. } => self
.tcx
.sess
.fatal("Cannot create a constant interface block value"),
} }
} }
} }

View File

@ -8,7 +8,7 @@ use rspirv::dr::Operand;
use rspirv::spirv::{Decoration, ExecutionModel, FunctionControl, StorageClass, Word}; use rspirv::spirv::{Decoration, ExecutionModel, FunctionControl, StorageClass, Word};
use rustc_codegen_ssa::traits::{BaseTypeMethods, BuilderMethods}; use rustc_codegen_ssa::traits::{BaseTypeMethods, BuilderMethods};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_middle::ty::layout::{HasParamEnv, TyAndLayout}; use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::ty::{Instance, Ty, TyKind}; use rustc_middle::ty::{Instance, Ty, TyKind};
use rustc_span::Span; use rustc_span::Span;
use rustc_target::abi::{ use rustc_target::abi::{
@ -141,7 +141,8 @@ impl<'tcx> CodegenCx<'tcx> {
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let mut bx = Builder::new_block(self, stub_fn, ""); let mut bx = Builder::new_block(self, stub_fn, "");
// Adjust any global `OpVariable`s as needed (e.g. loading from `Input`s), // Adjust any global `OpVariable`s as needed (e.g. loading from `Input`s,
// or accessing the sole field of an "interface block" `OpTypeStruct`),
// to match the argument type we have to pass to the Rust entry `fn`. // to match the argument type we have to pass to the Rust entry `fn`.
let arguments: Vec<_> = interface_globals let arguments: Vec<_> = interface_globals
.iter() .iter()
@ -156,21 +157,48 @@ impl<'tcx> CodegenCx<'tcx> {
_ => unreachable!(), _ => unreachable!(),
}; };
let mut dst_len_arg = None; let (first, second) = match entry_fn_arg.layout.ty.kind() {
let arg = match entry_fn_arg.layout.ty.kind() {
TyKind::Ref(_, pointee_ty, _) => { TyKind::Ref(_, pointee_ty, _) => {
let arg_pointee_spirv_type = self let arg_pointee_spirv_type = self
.layout_of(pointee_ty) .layout_of(pointee_ty)
.spirv_type(hir_param.ty_span, self); .spirv_type(hir_param.ty_span, self);
assert_ty_eq!(self, arg_pointee_spirv_type, var_value_spirv_type); if let SpirvType::InterfaceBlock { inner_type } =
self.lookup_type(var_value_spirv_type)
{
assert_ty_eq!(self, arg_pointee_spirv_type, inner_type);
if !pointee_ty.is_sized(self.tcx.at(span), self.param_env()) { let inner = bx.struct_gep(global_var, 0);
dst_len_arg.replace(self.dst_length_argument(
&mut bx, pointee_ty, hir_param, global_var, match entry_fn_arg.mode {
)); PassMode::Direct(_) => (inner, None),
// Unsized pointee with length (i.e. `&[T]`).
PassMode::Pair(..) => {
// FIXME(eddyb) shouldn't this be `usize`?
let len_spirv_type = self.type_isize();
let len = bx
.emit()
.array_length(
len_spirv_type,
None,
global_var.def(&bx),
0,
)
.unwrap()
.with_type(len_spirv_type);
(inner, Some(len))
}
_ => unreachable!(),
}
} else {
assert_ty_eq!(self, arg_pointee_spirv_type, var_value_spirv_type);
assert_matches!(entry_fn_arg.mode, PassMode::Direct(_));
(global_var, None)
} }
global_var
} }
_ => { _ => {
assert_eq!(storage_class, StorageClass::Input); assert_eq!(storage_class, StorageClass::Input);
@ -181,15 +209,15 @@ impl<'tcx> CodegenCx<'tcx> {
assert_ty_eq!(self, arg_spirv_type, var_value_spirv_type); assert_ty_eq!(self, arg_spirv_type, var_value_spirv_type);
match entry_fn_arg.mode { match entry_fn_arg.mode {
PassMode::Indirect { .. } => global_var, PassMode::Indirect { .. } => (global_var, None),
PassMode::Direct(_) => { PassMode::Direct(_) => {
bx.load(global_var, entry_fn_arg.layout.align.abi) (bx.load(global_var, entry_fn_arg.layout.align.abi), None)
} }
_ => unreachable!(), _ => unreachable!(),
} }
} }
}; };
std::iter::once(arg).chain(dst_len_arg) std::iter::once(first).chain(second)
}, },
) )
.collect(); .collect();
@ -217,41 +245,6 @@ impl<'tcx> CodegenCx<'tcx> {
stub_fn_id stub_fn_id
} }
fn dst_length_argument(
&self,
bx: &mut Builder<'_, 'tcx>,
ty: Ty<'tcx>,
hir_param: &hir::Param<'tcx>,
global_var: SpirvValue,
) -> SpirvValue {
match ty.kind() {
TyKind::Adt(adt_def, substs) => {
let (member_idx, field_def) = adt_def.all_fields().enumerate().last().unwrap();
let field_ty = field_def.ty(self.tcx, substs);
if !matches!(field_ty.kind(), TyKind::Slice(..)) {
self.tcx.sess.span_fatal(
hir_param.ty_span,
"DST parameters are currently restricted to a reference to a struct whose last field is a slice.",
)
}
// FIXME(eddyb) shouldn't this be `usize`?
let len_t = self.type_isize();
bx.emit()
.array_length(len_t, None, global_var.def(bx), member_idx as u32)
.unwrap()
.with_type(len_t)
}
TyKind::Slice(..) | TyKind::Str => self.tcx.sess.span_fatal(
hir_param.ty_span,
"Straight slices are not yet supported, wrap the slice in a newtype.",
),
_ => self
.tcx
.sess
.span_fatal(hir_param.ty_span, "Unsupported parameter type."),
}
}
fn infer_param_ty_and_storage_class( fn infer_param_ty_and_storage_class(
&self, &self,
layout: TyAndLayout<'tcx>, layout: TyAndLayout<'tcx>,
@ -357,7 +350,7 @@ impl<'tcx> CodegenCx<'tcx> {
) -> (SpirvValue, StorageClass) { ) -> (SpirvValue, StorageClass) {
let attrs = AggregatedSpirvAttributes::parse(self, self.tcx.hir().attrs(hir_param.hir_id)); let attrs = AggregatedSpirvAttributes::parse(self, self.tcx.hir().attrs(hir_param.hir_id));
let (value_ty, storage_class) = let (mut value_spirv_type, storage_class) =
self.infer_param_ty_and_storage_class(layout, hir_param, &attrs); self.infer_param_ty_and_storage_class(layout, hir_param, &attrs);
// Pre-allocate the module-scoped `OpVariable`'s *Result* ID. // Pre-allocate the module-scoped `OpVariable`'s *Result* ID.
@ -407,6 +400,40 @@ impl<'tcx> CodegenCx<'tcx> {
} }
} }
// Certain storage classes require an `OpTypeStruct` decorated with `Block`,
// which we represent with `SpirvType::InterfaceBlock` (see its doc comment).
// This "interface block" construct is also required for "runtime arrays".
let is_unsized = self.lookup_type(value_spirv_type).sizeof(self).is_none();
match storage_class {
StorageClass::PushConstant | StorageClass::Uniform | StorageClass::StorageBuffer => {
if is_unsized {
match self.lookup_type(value_spirv_type) {
SpirvType::RuntimeArray { .. } => {}
_ => self.tcx.sess.span_err(
hir_param.ty_span,
"only plain slices are supported as unsized types",
),
}
}
value_spirv_type = SpirvType::InterfaceBlock {
inner_type: value_spirv_type,
}
.def(hir_param.span, self);
}
_ => {
if is_unsized {
self.tcx.sess.span_fatal(
hir_param.ty_span,
&format!(
"unsized types are not supported for storage class {:?}",
storage_class
),
);
}
}
}
// FIXME(eddyb) check whether the storage class is compatible with the // FIXME(eddyb) check whether the storage class is compatible with the
// specific shader stage of this entry-point, and any decorations // specific shader stage of this entry-point, and any decorations
// (e.g. Vulkan has specific rules for builtin storage classes). // (e.g. Vulkan has specific rules for builtin storage classes).
@ -433,7 +460,10 @@ impl<'tcx> CodegenCx<'tcx> {
} }
// Emit the `OpVariable` with its *Result* ID set to `variable`. // Emit the `OpVariable` with its *Result* ID set to `variable`.
let var_spirv_type = SpirvType::Pointer { pointee: value_ty }.def(hir_param.span, self); let var_spirv_type = SpirvType::Pointer {
pointee: value_spirv_type,
}
.def(hir_param.span, self);
self.emit_global() self.emit_global()
.variable(var_spirv_type, Some(variable), storage_class, None); .variable(var_spirv_type, Some(variable), storage_class, None);

View File

@ -149,7 +149,6 @@ impl<'tcx> BaseTypeMethods<'tcx> for CodegenCx<'tcx> {
field_types: els.to_vec(), field_types: els.to_vec(),
field_offsets, field_offsets,
field_names: None, field_names: None,
is_block: false,
} }
.def(DUMMY_SP, self) .def(DUMMY_SP, self)
} }
@ -167,16 +166,18 @@ impl<'tcx> BaseTypeMethods<'tcx> for CodegenCx<'tcx> {
.sess .sess
.fatal(&format!("Invalid float width in type_kind: {}", other)), .fatal(&format!("Invalid float width in type_kind: {}", other)),
}, },
SpirvType::Adt { .. } | SpirvType::Opaque { .. } => TypeKind::Struct, SpirvType::Adt { .. } | SpirvType::Opaque { .. } | SpirvType::InterfaceBlock { .. } => {
TypeKind::Struct
}
SpirvType::Vector { .. } => TypeKind::Vector, SpirvType::Vector { .. } => TypeKind::Vector,
SpirvType::Array { .. } | SpirvType::RuntimeArray { .. } => TypeKind::Array, SpirvType::Array { .. } | SpirvType::RuntimeArray { .. } => TypeKind::Array,
SpirvType::Pointer { .. } => TypeKind::Pointer, SpirvType::Pointer { .. } => TypeKind::Pointer,
SpirvType::Function { .. } => TypeKind::Function, SpirvType::Function { .. } => TypeKind::Function,
// HACK(eddyb) this is probably the closest `TypeKind` (which is still // HACK(eddyb) this is probably the closest `TypeKind` (which is still
// very much LLVM-specific, sadly) has to offer to "resource handle". // very much LLVM-specific, sadly) has to offer to "resource handle".
SpirvType::Image { .. } | SpirvType::Image { .. } | SpirvType::Sampler | SpirvType::SampledImage { .. } => {
SpirvType::Sampler | TypeKind::Token
SpirvType::SampledImage { .. } => TypeKind::Token, }
} }
} }
fn type_ptr_to(&self, ty: Self::Type) -> Self::Type { fn type_ptr_to(&self, ty: Self::Type) -> Self::Type {

View File

@ -16,6 +16,7 @@
//! [`spirv-tools`]: https://embarkstudios.github.io/rust-gpu/api/spirv_tools //! [`spirv-tools`]: https://embarkstudios.github.io/rust-gpu/api/spirv_tools
//! [`spirv-tools-sys`]: https://embarkstudios.github.io/rust-gpu/api/spirv_tools_sys //! [`spirv-tools-sys`]: https://embarkstudios.github.io/rust-gpu/api/spirv_tools_sys
#![feature(rustc_private)] #![feature(rustc_private)]
#![feature(assert_matches)]
#![feature(once_cell)] #![feature(once_cell)]
// BEGIN - Embark standard lints v0.3 // BEGIN - Embark standard lints v0.3
// do not change or add/remove here, but one can add exceptions after this section // do not change or add/remove here, but one can add exceptions after this section

View File

@ -41,7 +41,6 @@ pub enum SpirvType {
field_types: Vec<Word>, field_types: Vec<Word>,
field_offsets: Vec<Size>, field_offsets: Vec<Size>,
field_names: Option<Vec<String>>, field_names: Option<Vec<String>>,
is_block: bool,
}, },
Opaque { Opaque {
name: String, name: String,
@ -80,6 +79,12 @@ pub enum SpirvType {
SampledImage { SampledImage {
image_type: Word, image_type: Word,
}, },
/// `OpTypeStruct` decorated with `Block`, required by Vulkan (and OpenGL)
/// for `PushConstant`, `Uniform` and `StorageBuffer` interface variables.
InterfaceBlock {
inner_type: Word,
},
} }
impl SpirvType { impl SpirvType {
@ -136,7 +141,6 @@ impl SpirvType {
ref field_types, ref field_types,
ref field_offsets, ref field_offsets,
ref field_names, ref field_names,
is_block,
} => { } => {
let mut emit = cx.emit_global(); let mut emit = cx.emit_global();
// Ensure a unique struct is emitted each time, due to possibly having different OpMemberDecorates // Ensure a unique struct is emitted each time, due to possibly having different OpMemberDecorates
@ -161,9 +165,6 @@ impl SpirvType {
emit.member_name(result, index as u32, field_name); emit.member_name(result, index as u32, field_name);
} }
} }
if is_block {
emit.decorate(result, Decoration::Block, iter::empty());
}
result result
} }
Self::Opaque { ref name } => cx.emit_global().type_opaque(name), Self::Opaque { ref name } => cx.emit_global().type_opaque(name),
@ -248,6 +249,20 @@ impl SpirvType {
), ),
Self::Sampler => cx.emit_global().type_sampler(), Self::Sampler => cx.emit_global().type_sampler(),
Self::SampledImage { image_type } => cx.emit_global().type_sampled_image(image_type), Self::SampledImage { image_type } => cx.emit_global().type_sampled_image(image_type),
Self::InterfaceBlock { inner_type } => {
let mut emit = cx.emit_global();
let id = emit.id();
let result = emit.type_struct_id(Some(id), iter::once(inner_type));
emit.decorate(result, Decoration::Block, iter::empty());
emit.member_decorate(
result,
0,
Decoration::Offset,
[Operand::LiteralInt32(0)].iter().cloned(),
);
result
}
}; };
cx.type_cache.def(result, self); cx.type_cache.def(result, self);
result result
@ -333,6 +348,8 @@ impl SpirvType {
} }
Self::Pointer { .. } => cx.tcx.data_layout.pointer_size, Self::Pointer { .. } => cx.tcx.data_layout.pointer_size,
Self::Image { .. } | Self::Sampler | Self::SampledImage { .. } => Size::from_bytes(4), Self::Image { .. } | Self::Sampler | Self::SampledImage { .. } => Size::from_bytes(4),
Self::InterfaceBlock { inner_type } => cx.lookup_type(inner_type).sizeof(cx)?,
}; };
Some(result) Some(result)
} }
@ -361,6 +378,8 @@ impl SpirvType {
Self::Image { .. } | Self::Sampler | Self::SampledImage { .. } => { Self::Image { .. } | Self::Sampler | Self::SampledImage { .. } => {
Align::from_bytes(4).unwrap() Align::from_bytes(4).unwrap()
} }
Self::InterfaceBlock { inner_type } => cx.lookup_type(inner_type).alignof(cx),
} }
} }
} }
@ -407,7 +426,6 @@ impl fmt::Debug for SpirvTypePrinter<'_, '_> {
ref field_types, ref field_types,
ref field_offsets, ref field_offsets,
ref field_names, ref field_names,
is_block,
} => { } => {
let fields = field_types let fields = field_types
.iter() .iter()
@ -421,7 +439,6 @@ impl fmt::Debug for SpirvTypePrinter<'_, '_> {
.field("field_types", &fields) .field("field_types", &fields)
.field("field_offsets", field_offsets) .field("field_offsets", field_offsets)
.field("field_names", field_names) .field("field_names", field_names)
.field("is_block", &is_block)
.finish() .finish()
} }
SpirvType::Opaque { ref name } => f SpirvType::Opaque { ref name } => f
@ -499,6 +516,12 @@ impl fmt::Debug for SpirvTypePrinter<'_, '_> {
.field("id", &self.id) .field("id", &self.id)
.field("image_type", &self.cx.debug_type(image_type)) .field("image_type", &self.cx.debug_type(image_type))
.finish(), .finish(),
SpirvType::InterfaceBlock { inner_type } => f
.debug_struct("SampledImage")
.field("id", &self.id)
.field("inner_type", &self.cx.debug_type(inner_type))
.finish(),
}; };
{ {
let mut debug_stack = DEBUG_STACK.lock().unwrap(); let mut debug_stack = DEBUG_STACK.lock().unwrap();
@ -551,12 +574,7 @@ impl SpirvTypePrinter<'_, '_> {
ref field_types, ref field_types,
field_offsets: _, field_offsets: _,
ref field_names, ref field_names,
is_block,
} => { } => {
if is_block {
write!(f, "#[spirv(block)] ")?;
}
write!(f, "struct")?; write!(f, "struct")?;
// HACK(eddyb) use the first name (in insertion order, i.e. // HACK(eddyb) use the first name (in insertion order, i.e.
@ -575,8 +593,7 @@ impl SpirvTypePrinter<'_, '_> {
write!(f, " {}", name)?; write!(f, " {}", name)?;
} }
write!(f, " {{ ")?; f.write_str(" { ")?;
for (index, &field) in field_types.iter().enumerate() { for (index, &field) in field_types.iter().enumerate() {
let suffix = if index + 1 == field_types.len() { let suffix = if index + 1 == field_types.len() {
"" ""
@ -654,6 +671,12 @@ impl SpirvTypePrinter<'_, '_> {
.debug_struct("SampledImage") .debug_struct("SampledImage")
.field("image_type", &self.cx.debug_type(image_type)) .field("image_type", &self.cx.debug_type(image_type))
.finish(), .finish(),
SpirvType::InterfaceBlock { inner_type } => {
f.write_str("interface block { ")?;
ty(self.cx, stack, f, inner_type)?;
f.write_str(" }")
}
} }
} }
} }

View File

@ -461,44 +461,39 @@ fn index_user_dst() {
r#" r#"
#[spirv(fragment)] #[spirv(fragment)]
pub fn main( pub fn main(
#[spirv(uniform, descriptor_set = 0, binding = 0)] slice: &SliceF32, #[spirv(storage_buffer, descriptor_set = 0, binding = 0)] slice: &mut [f32],
) { ) {
let float: f32 = slice.rta[0]; let float: f32 = slice[0];
let _ = float; let _ = float;
} }
pub struct SliceF32 {
rta: [f32],
}
"#, "#,
"main", "main",
r#"%1 = OpFunction %2 None %3 r#"%1 = OpFunction %2 None %3
%4 = OpLabel %4 = OpLabel
%5 = OpArrayLength %6 %7 0 %5 = OpAccessChain %6 %7 %8
%8 = OpCompositeInsert %9 %7 %10 0 %9 = OpArrayLength %10 %7 0
%11 = OpCompositeInsert %9 %5 %8 1 %11 = OpCompositeInsert %12 %5 %13 0
%12 = OpAccessChain %13 %7 %14 %14 = OpCompositeInsert %12 %9 %11 1
%15 = OpULessThan %16 %14 %5 %15 = OpULessThan %16 %8 %9
OpSelectionMerge %17 None OpSelectionMerge %17 None
OpBranchConditional %15 %18 %19 OpBranchConditional %15 %18 %19
%18 = OpLabel %18 = OpLabel
%20 = OpAccessChain %13 %7 %14 %20 = OpInBoundsAccessChain %21 %5 %8
%21 = OpInBoundsAccessChain %22 %20 %14 %22 = OpLoad %23 %20
%23 = OpLoad %24 %21
OpReturn OpReturn
%19 = OpLabel %19 = OpLabel
OpBranch %24
%24 = OpLabel
OpBranch %25 OpBranch %25
%25 = OpLabel %25 = OpLabel
OpBranch %26 %26 = OpPhi %16 %27 %24 %27 %28
%26 = OpLabel OpLoopMerge %29 %28 None
%27 = OpPhi %16 %28 %25 %28 %29 OpBranchConditional %26 %30 %29
OpLoopMerge %30 %29 None
OpBranchConditional %27 %31 %30
%31 = OpLabel
OpBranch %29
%29 = OpLabel
OpBranch %26
%30 = OpLabel %30 = OpLabel
OpBranch %28
%28 = OpLabel
OpBranch %25
%29 = OpLabel
OpUnreachable OpUnreachable
%17 = OpLabel %17 = OpLabel
OpUnreachable OpUnreachable

View File

@ -68,24 +68,6 @@ fn main(
Both descriptor_set and binding take an integer argument that specifies the uniform's index. Both descriptor_set and binding take an integer argument that specifies the uniform's index.
## Block
This attribute is a temporary quick fix before we implement a more fully-featured binding model. If you get validation errors about missing a Block decoration on a struct due to being used as uniform block data, try adding this attribute to the struct definition. If you get errors around the struct definition not being an aggregate, but rather the type of the field, try adding `#[repr(C)]` to the struct definition.
Example:
```rust
#[spirv(block)]
struct Thing {
a: Vec4,
b: Vec4,
c: Vec4,
}
#[spirv(fragment)]
fn main(#[spirv(push_constant)] obj: &ShaderConstants) { }
```
## Flat ## Flat
The flat attribute corresponds to the flat keyword in glsl - in other words, the data is not interpolated across the triangle when invoking the fragment shader. The flat attribute corresponds to the flat keyword in glsl - in other words, the data is not interpolated across the triangle when invoking the fragment shader.

View File

@ -1,15 +1,6 @@
//! Ported to Rust from <https://github.com/Tw1ddle/Sky-Shader/blob/master/src/shaders/glsl/sky.fragment> //! Ported to Rust from <https://github.com/Tw1ddle/Sky-Shader/blob/master/src/shaders/glsl/sky.fragment>
#![cfg_attr( #![cfg_attr(target_arch = "spirv", no_std, feature(lang_items))]
target_arch = "spirv",
no_std,
feature(register_attr, lang_items),
register_attr(spirv)
)]
#[cfg(not(target_arch = "spirv"))]
#[macro_use]
pub extern crate spirv_std_macros;
use core::f32::consts::PI; use core::f32::consts::PI;
use glam::{vec3, Vec3}; use glam::{vec3, Vec3};
@ -21,7 +12,6 @@ pub use glam;
#[cfg(target_arch = "spirv")] #[cfg(target_arch = "spirv")]
use spirv_std::num_traits::Float; use spirv_std::num_traits::Float;
#[spirv(block)]
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
#[repr(C)] #[repr(C)]
pub struct ShaderConstants { pub struct ShaderConstants {

View File

@ -1,21 +1,16 @@
use glam::*; use glam::*;
#[spirv(block)]
pub struct PointsBuffer {
points: [UVec2; 100],
}
#[spirv(compute(threads(1)))] #[spirv(compute(threads(1)))]
pub fn main_cs( pub fn main_cs(
#[spirv(global_invocation_id)] id: UVec3, #[spirv(global_invocation_id)] id: UVec3,
#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] points_buffer: &mut PointsBuffer, #[spirv(storage_buffer, descriptor_set = 0, binding = 0)] points_buffer: &mut [UVec2; 100],
#[spirv(descriptor_set = 1, binding = 1)] image: &spirv_std::StorageImage2d, #[spirv(descriptor_set = 1, binding = 1)] image: &spirv_std::StorageImage2d,
) { ) {
unsafe { asm!("OpCapability StorageImageWriteWithoutFormat") }; unsafe { asm!("OpCapability StorageImageWriteWithoutFormat") };
let position = id.xy(); let position = id.xy();
for i in 0..100usize { for i in 0..100usize {
let p0 = &points_buffer.points[i]; let p0 = &points_buffer[i];
let p1 = &points_buffer.points[i + 1]; let p1 = &points_buffer[i + 1];
if p0.x == position.x && p1.y == position.y { if p0.x == position.x && p1.y == position.y {
unsafe { unsafe {
image.write(position, vec2(1.0, 0.0)); image.write(position, vec2(1.0, 0.0));

View File

@ -40,6 +40,12 @@ note: previous #[spirv(block)] attribute
24 | #[spirv(block, block)] 24 | #[spirv(block, block)]
| ^^^^^ | ^^^^^
warning: #[spirv(block)] is no longer needed and should be removed
--> $DIR/multiple.rs:24:9
|
24 | #[spirv(block, block)]
| ^^^^^
error: only one entry-point attribute is allowed on a function error: only one entry-point attribute is allowed on a function
--> $DIR/multiple.rs:27:17 --> $DIR/multiple.rs:27:17
| |
@ -196,5 +202,5 @@ note: previous #[spirv(unroll_loops)] attribute
53 | #[spirv(unroll_loops, unroll_loops)] 53 | #[spirv(unroll_loops, unroll_loops)]
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
error: aborting due to 16 previous errors error: aborting due to 16 previous errors; 1 warning emitted

View File

@ -4,8 +4,6 @@
use spirv_std as _; use spirv_std as _;
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
// `Block` decoration is required for push constants when compiling for Vulkan.
#[cfg_attr(not(target_env = "unknown"), spirv(block))]
pub struct ShaderConstants { pub struct ShaderConstants {
pub width: u32, pub width: u32,
pub height: u32, pub height: u32,

View File

@ -3,14 +3,8 @@
// build-pass // build-pass
use spirv_std as _; use spirv_std as _;
// `Block` decoration is required for storage buffers when compiling for Vulkan.
#[cfg_attr(not(target_env = "unknown"), spirv(block))]
pub struct SliceF32 {
rta: [f32],
}
#[spirv(fragment)] #[spirv(fragment)]
pub fn main(#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] slice: &mut SliceF32) { pub fn main(#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] slice: &mut [f32]) {
let float: f32 = slice.rta[0]; let float: f32 = slice[0];
let _ = float; let _ = float;
} }