mirror of
https://github.com/EmbarkStudios/rust-gpu.git
synced 2024-11-22 06:45:13 +00:00
Deprecate #[spirv(block)]
and auto-wrap in "interface blocks" instead.
This commit is contained in:
parent
561f0dd984
commit
4395b84114
@ -237,7 +237,6 @@ impl<'tcx> ConvSpirvType<'tcx> for CastTarget {
|
||||
field_types: args,
|
||||
field_offsets,
|
||||
field_names: None,
|
||||
is_block: false,
|
||||
}
|
||||
.def(span, cx)
|
||||
}
|
||||
@ -325,34 +324,6 @@ fn trans_type_impl<'tcx>(
|
||||
) -> Word {
|
||||
if let TyKind::Adt(adt, substs) = *ty.ty.kind() {
|
||||
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 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
|
||||
// `ScalarPair`.
|
||||
// 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_offsets: Vec::new(),
|
||||
field_names: None,
|
||||
is_block: false,
|
||||
}
|
||||
.def_with_name(cx, span, TyLayoutNameKey::from(ty)),
|
||||
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_offsets: vec![one_offset, two_offset],
|
||||
field_names: None,
|
||||
is_block: false,
|
||||
}
|
||||
.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_offsets: Vec::new(),
|
||||
field_names: None,
|
||||
is_block: false,
|
||||
}
|
||||
.def_with_name(cx, span, TyLayoutNameKey::from(ty))
|
||||
} else {
|
||||
@ -642,7 +611,7 @@ fn trans_aggregate<'tcx>(cx: &CodegenCx<'tcx>, span: Span, ty: TyAndLayout<'tcx>
|
||||
FieldsShape::Arbitrary {
|
||||
offsets: _,
|
||||
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
|
||||
fn trans_struct<'tcx>(
|
||||
cx: &CodegenCx<'tcx>,
|
||||
span: Span,
|
||||
ty: TyAndLayout<'tcx>,
|
||||
is_block: bool,
|
||||
) -> Word {
|
||||
fn trans_struct<'tcx>(cx: &CodegenCx<'tcx>, span: Span, ty: TyAndLayout<'tcx>) -> Word {
|
||||
if let TyKind::Foreign(_) = ty.ty.kind() {
|
||||
// "An unsized FFI type that is opaque to Rust", `extern type A;` (currently unstable)
|
||||
if cx.kernel_mode {
|
||||
@ -726,7 +690,6 @@ fn trans_struct<'tcx>(
|
||||
field_types,
|
||||
field_offsets,
|
||||
field_names: Some(field_names),
|
||||
is_block,
|
||||
}
|
||||
.def_with_name(cx, span, TyLayoutNameKey::from(ty))
|
||||
}
|
||||
|
@ -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",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -232,6 +232,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
SpirvType::Image { .. } => self.fatal("cannot memset image"),
|
||||
SpirvType::Sampler => self.fatal("cannot memset sampler"),
|
||||
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::Sampler => self.fatal("cannot memset sampler"),
|
||||
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::RuntimeArray { element, .. }
|
||||
| SpirvType::Vector { element, .. } => element,
|
||||
SpirvType::InterfaceBlock { inner_type } => {
|
||||
assert_eq!(idx, 0);
|
||||
inner_type
|
||||
}
|
||||
other => self.fatal(&format!(
|
||||
"struct_gep not on struct, array, or vector type: {:?}, index {}",
|
||||
other, idx
|
||||
|
@ -180,7 +180,6 @@ impl<'tcx> ConstMethods<'tcx> for CodegenCx<'tcx> {
|
||||
field_types,
|
||||
field_offsets,
|
||||
field_names: None,
|
||||
is_block: false,
|
||||
}
|
||||
.def(DUMMY_SP, self);
|
||||
self.constant_composite(struct_ty, elts.iter().map(|f| f.def_cx(self)).collect())
|
||||
@ -524,6 +523,10 @@ impl<'tcx> CodegenCx<'tcx> {
|
||||
.tcx
|
||||
.sess
|
||||
.fatal("Cannot create a constant sampled image value"),
|
||||
SpirvType::InterfaceBlock { .. } => self
|
||||
.tcx
|
||||
.sess
|
||||
.fatal("Cannot create a constant interface block value"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use rspirv::dr::Operand;
|
||||
use rspirv::spirv::{Decoration, ExecutionModel, FunctionControl, StorageClass, Word};
|
||||
use rustc_codegen_ssa::traits::{BaseTypeMethods, BuilderMethods};
|
||||
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_span::Span;
|
||||
use rustc_target::abi::{
|
||||
@ -141,7 +141,8 @@ impl<'tcx> CodegenCx<'tcx> {
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
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`.
|
||||
let arguments: Vec<_> = interface_globals
|
||||
.iter()
|
||||
@ -156,21 +157,48 @@ impl<'tcx> CodegenCx<'tcx> {
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let mut dst_len_arg = None;
|
||||
let arg = match entry_fn_arg.layout.ty.kind() {
|
||||
let (first, second) = match entry_fn_arg.layout.ty.kind() {
|
||||
TyKind::Ref(_, pointee_ty, _) => {
|
||||
let arg_pointee_spirv_type = self
|
||||
.layout_of(pointee_ty)
|
||||
.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()) {
|
||||
dst_len_arg.replace(self.dst_length_argument(
|
||||
&mut bx, pointee_ty, hir_param, global_var,
|
||||
));
|
||||
let inner = bx.struct_gep(global_var, 0);
|
||||
|
||||
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);
|
||||
@ -181,15 +209,15 @@ impl<'tcx> CodegenCx<'tcx> {
|
||||
assert_ty_eq!(self, arg_spirv_type, var_value_spirv_type);
|
||||
|
||||
match entry_fn_arg.mode {
|
||||
PassMode::Indirect { .. } => global_var,
|
||||
PassMode::Indirect { .. } => (global_var, None),
|
||||
PassMode::Direct(_) => {
|
||||
bx.load(global_var, entry_fn_arg.layout.align.abi)
|
||||
(bx.load(global_var, entry_fn_arg.layout.align.abi), None)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
};
|
||||
std::iter::once(arg).chain(dst_len_arg)
|
||||
std::iter::once(first).chain(second)
|
||||
},
|
||||
)
|
||||
.collect();
|
||||
@ -217,41 +245,6 @@ impl<'tcx> CodegenCx<'tcx> {
|
||||
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(
|
||||
&self,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
@ -357,7 +350,7 @@ impl<'tcx> CodegenCx<'tcx> {
|
||||
) -> (SpirvValue, StorageClass) {
|
||||
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);
|
||||
|
||||
// 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
|
||||
// specific shader stage of this entry-point, and any decorations
|
||||
// (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`.
|
||||
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()
|
||||
.variable(var_spirv_type, Some(variable), storage_class, None);
|
||||
|
||||
|
@ -149,7 +149,6 @@ impl<'tcx> BaseTypeMethods<'tcx> for CodegenCx<'tcx> {
|
||||
field_types: els.to_vec(),
|
||||
field_offsets,
|
||||
field_names: None,
|
||||
is_block: false,
|
||||
}
|
||||
.def(DUMMY_SP, self)
|
||||
}
|
||||
@ -167,16 +166,18 @@ impl<'tcx> BaseTypeMethods<'tcx> for CodegenCx<'tcx> {
|
||||
.sess
|
||||
.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::Array { .. } | SpirvType::RuntimeArray { .. } => TypeKind::Array,
|
||||
SpirvType::Pointer { .. } => TypeKind::Pointer,
|
||||
SpirvType::Function { .. } => TypeKind::Function,
|
||||
// HACK(eddyb) this is probably the closest `TypeKind` (which is still
|
||||
// very much LLVM-specific, sadly) has to offer to "resource handle".
|
||||
SpirvType::Image { .. } |
|
||||
SpirvType::Sampler |
|
||||
SpirvType::SampledImage { .. } => TypeKind::Token,
|
||||
SpirvType::Image { .. } | SpirvType::Sampler | SpirvType::SampledImage { .. } => {
|
||||
TypeKind::Token
|
||||
}
|
||||
}
|
||||
}
|
||||
fn type_ptr_to(&self, ty: Self::Type) -> Self::Type {
|
||||
|
@ -16,6 +16,7 @@
|
||||
//! [`spirv-tools`]: https://embarkstudios.github.io/rust-gpu/api/spirv_tools
|
||||
//! [`spirv-tools-sys`]: https://embarkstudios.github.io/rust-gpu/api/spirv_tools_sys
|
||||
#![feature(rustc_private)]
|
||||
#![feature(assert_matches)]
|
||||
#![feature(once_cell)]
|
||||
// BEGIN - Embark standard lints v0.3
|
||||
// do not change or add/remove here, but one can add exceptions after this section
|
||||
|
@ -41,7 +41,6 @@ pub enum SpirvType {
|
||||
field_types: Vec<Word>,
|
||||
field_offsets: Vec<Size>,
|
||||
field_names: Option<Vec<String>>,
|
||||
is_block: bool,
|
||||
},
|
||||
Opaque {
|
||||
name: String,
|
||||
@ -80,6 +79,12 @@ pub enum SpirvType {
|
||||
SampledImage {
|
||||
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 {
|
||||
@ -136,7 +141,6 @@ impl SpirvType {
|
||||
ref field_types,
|
||||
ref field_offsets,
|
||||
ref field_names,
|
||||
is_block,
|
||||
} => {
|
||||
let mut emit = cx.emit_global();
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
if is_block {
|
||||
emit.decorate(result, Decoration::Block, iter::empty());
|
||||
}
|
||||
result
|
||||
}
|
||||
Self::Opaque { ref name } => cx.emit_global().type_opaque(name),
|
||||
@ -248,6 +249,20 @@ impl SpirvType {
|
||||
),
|
||||
Self::Sampler => cx.emit_global().type_sampler(),
|
||||
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);
|
||||
result
|
||||
@ -333,6 +348,8 @@ impl SpirvType {
|
||||
}
|
||||
Self::Pointer { .. } => cx.tcx.data_layout.pointer_size,
|
||||
Self::Image { .. } | Self::Sampler | Self::SampledImage { .. } => Size::from_bytes(4),
|
||||
|
||||
Self::InterfaceBlock { inner_type } => cx.lookup_type(inner_type).sizeof(cx)?,
|
||||
};
|
||||
Some(result)
|
||||
}
|
||||
@ -361,6 +378,8 @@ impl SpirvType {
|
||||
Self::Image { .. } | Self::Sampler | Self::SampledImage { .. } => {
|
||||
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_offsets,
|
||||
ref field_names,
|
||||
is_block,
|
||||
} => {
|
||||
let fields = field_types
|
||||
.iter()
|
||||
@ -421,7 +439,6 @@ impl fmt::Debug for SpirvTypePrinter<'_, '_> {
|
||||
.field("field_types", &fields)
|
||||
.field("field_offsets", field_offsets)
|
||||
.field("field_names", field_names)
|
||||
.field("is_block", &is_block)
|
||||
.finish()
|
||||
}
|
||||
SpirvType::Opaque { ref name } => f
|
||||
@ -499,6 +516,12 @@ impl fmt::Debug for SpirvTypePrinter<'_, '_> {
|
||||
.field("id", &self.id)
|
||||
.field("image_type", &self.cx.debug_type(image_type))
|
||||
.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();
|
||||
@ -551,12 +574,7 @@ impl SpirvTypePrinter<'_, '_> {
|
||||
ref field_types,
|
||||
field_offsets: _,
|
||||
ref field_names,
|
||||
is_block,
|
||||
} => {
|
||||
if is_block {
|
||||
write!(f, "#[spirv(block)] ")?;
|
||||
}
|
||||
|
||||
write!(f, "struct")?;
|
||||
|
||||
// HACK(eddyb) use the first name (in insertion order, i.e.
|
||||
@ -575,8 +593,7 @@ impl SpirvTypePrinter<'_, '_> {
|
||||
write!(f, " {}", name)?;
|
||||
}
|
||||
|
||||
write!(f, " {{ ")?;
|
||||
|
||||
f.write_str(" { ")?;
|
||||
for (index, &field) in field_types.iter().enumerate() {
|
||||
let suffix = if index + 1 == field_types.len() {
|
||||
""
|
||||
@ -654,6 +671,12 @@ impl SpirvTypePrinter<'_, '_> {
|
||||
.debug_struct("SampledImage")
|
||||
.field("image_type", &self.cx.debug_type(image_type))
|
||||
.finish(),
|
||||
|
||||
SpirvType::InterfaceBlock { inner_type } => {
|
||||
f.write_str("interface block { ")?;
|
||||
ty(self.cx, stack, f, inner_type)?;
|
||||
f.write_str(" }")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -461,44 +461,39 @@ fn index_user_dst() {
|
||||
r#"
|
||||
#[spirv(fragment)]
|
||||
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;
|
||||
}
|
||||
|
||||
pub struct SliceF32 {
|
||||
rta: [f32],
|
||||
}
|
||||
"#,
|
||||
"main",
|
||||
r#"%1 = OpFunction %2 None %3
|
||||
%4 = OpLabel
|
||||
%5 = OpArrayLength %6 %7 0
|
||||
%8 = OpCompositeInsert %9 %7 %10 0
|
||||
%11 = OpCompositeInsert %9 %5 %8 1
|
||||
%12 = OpAccessChain %13 %7 %14
|
||||
%15 = OpULessThan %16 %14 %5
|
||||
%5 = OpAccessChain %6 %7 %8
|
||||
%9 = OpArrayLength %10 %7 0
|
||||
%11 = OpCompositeInsert %12 %5 %13 0
|
||||
%14 = OpCompositeInsert %12 %9 %11 1
|
||||
%15 = OpULessThan %16 %8 %9
|
||||
OpSelectionMerge %17 None
|
||||
OpBranchConditional %15 %18 %19
|
||||
%18 = OpLabel
|
||||
%20 = OpAccessChain %13 %7 %14
|
||||
%21 = OpInBoundsAccessChain %22 %20 %14
|
||||
%23 = OpLoad %24 %21
|
||||
%20 = OpInBoundsAccessChain %21 %5 %8
|
||||
%22 = OpLoad %23 %20
|
||||
OpReturn
|
||||
%19 = OpLabel
|
||||
OpBranch %24
|
||||
%24 = OpLabel
|
||||
OpBranch %25
|
||||
%25 = OpLabel
|
||||
OpBranch %26
|
||||
%26 = OpLabel
|
||||
%27 = OpPhi %16 %28 %25 %28 %29
|
||||
OpLoopMerge %30 %29 None
|
||||
OpBranchConditional %27 %31 %30
|
||||
%31 = OpLabel
|
||||
OpBranch %29
|
||||
%29 = OpLabel
|
||||
OpBranch %26
|
||||
%26 = OpPhi %16 %27 %24 %27 %28
|
||||
OpLoopMerge %29 %28 None
|
||||
OpBranchConditional %26 %30 %29
|
||||
%30 = OpLabel
|
||||
OpBranch %28
|
||||
%28 = OpLabel
|
||||
OpBranch %25
|
||||
%29 = OpLabel
|
||||
OpUnreachable
|
||||
%17 = OpLabel
|
||||
OpUnreachable
|
||||
|
@ -68,24 +68,6 @@ fn main(
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
|
@ -1,15 +1,6 @@
|
||||
//! Ported to Rust from <https://github.com/Tw1ddle/Sky-Shader/blob/master/src/shaders/glsl/sky.fragment>
|
||||
|
||||
#![cfg_attr(
|
||||
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;
|
||||
#![cfg_attr(target_arch = "spirv", no_std, feature(lang_items))]
|
||||
|
||||
use core::f32::consts::PI;
|
||||
use glam::{vec3, Vec3};
|
||||
@ -21,7 +12,6 @@ pub use glam;
|
||||
#[cfg(target_arch = "spirv")]
|
||||
use spirv_std::num_traits::Float;
|
||||
|
||||
#[spirv(block)]
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct ShaderConstants {
|
||||
|
@ -1,21 +1,16 @@
|
||||
use glam::*;
|
||||
|
||||
#[spirv(block)]
|
||||
pub struct PointsBuffer {
|
||||
points: [UVec2; 100],
|
||||
}
|
||||
|
||||
#[spirv(compute(threads(1)))]
|
||||
pub fn main_cs(
|
||||
#[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,
|
||||
) {
|
||||
unsafe { asm!("OpCapability StorageImageWriteWithoutFormat") };
|
||||
let position = id.xy();
|
||||
for i in 0..100usize {
|
||||
let p0 = &points_buffer.points[i];
|
||||
let p1 = &points_buffer.points[i + 1];
|
||||
let p0 = &points_buffer[i];
|
||||
let p1 = &points_buffer[i + 1];
|
||||
if p0.x == position.x && p1.y == position.y {
|
||||
unsafe {
|
||||
image.write(position, vec2(1.0, 0.0));
|
||||
|
@ -40,6 +40,12 @@ note: previous #[spirv(block)] attribute
|
||||
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
|
||||
--> $DIR/multiple.rs:27:17
|
||||
|
|
||||
@ -196,5 +202,5 @@ note: previous #[spirv(unroll_loops)] attribute
|
||||
53 | #[spirv(unroll_loops, unroll_loops)]
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 16 previous errors
|
||||
error: aborting due to 16 previous errors; 1 warning emitted
|
||||
|
||||
|
@ -4,8 +4,6 @@
|
||||
use spirv_std as _;
|
||||
|
||||
#[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 width: u32,
|
||||
pub height: u32,
|
||||
|
@ -3,14 +3,8 @@
|
||||
// build-pass
|
||||
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)]
|
||||
pub fn main(#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] slice: &mut SliceF32) {
|
||||
let float: f32 = slice.rta[0];
|
||||
pub fn main(#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] slice: &mut [f32]) {
|
||||
let float: f32 = slice[0];
|
||||
let _ = float;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user