Skip to content

Commit

Permalink
Add runtime array type
Browse files Browse the repository at this point in the history
  • Loading branch information
khyperia committed Apr 16, 2021
1 parent bb7adc9 commit 078847c
Show file tree
Hide file tree
Showing 9 changed files with 155 additions and 2 deletions.
20 changes: 20 additions & 0 deletions crates/rustc_codegen_spirv/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -810,5 +810,25 @@ fn trans_intrinsic_type<'tcx>(
Err(ErrorReported)
}
}
IntrinsicType::RuntimeArray => {
if ty.size != Size::from_bytes(4) {
cx.tcx
.sess
.err("#[spirv(runtime_array)] type must have size 4");
return Err(ErrorReported);
}

// We use a generic to indicate the underlying element type.
// The spirv type of it will be generated by querying the type of the first generic.
if let Some(elem_ty) = substs.types().next() {
let element = trans_type_impl(cx, span, cx.layout_of(elem_ty), false);
Ok(SpirvType::RuntimeArray { element }.def(span, cx))
} else {
cx.tcx
.sess
.err("#[spirv(runtime_array)] type must have a generic element type");
Err(ErrorReported)
}
}
}
}
2 changes: 2 additions & 0 deletions crates/rustc_codegen_spirv/src/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ pub enum IntrinsicType {
},
Sampler,
SampledImage,

RuntimeArray,
}

// NOTE(eddyb) when adding new `#[spirv(...)]` attributes, the tests found inside
Expand Down
51 changes: 49 additions & 2 deletions crates/rustc_codegen_spirv/src/codegen_cx/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,13 @@ impl<'tcx> CodegenCx<'tcx> {
};
let spirv_ty = self.layout_of(value_ty).spirv_type(hir_param.ty_span, self);
// Some types automatically specify a storage class. Compute that here.
let inferred_storage_class_from_ty = match self.lookup_type(spirv_ty) {
let element_ty = match self.lookup_type(spirv_ty) {
SpirvType::Array { element, .. } | SpirvType::RuntimeArray { element } => {
self.lookup_type(element)
}
ty => ty,
};
let inferred_storage_class_from_ty = match element_ty {
SpirvType::Image { .. } | SpirvType::Sampler | SpirvType::SampledImage { .. } => {
if is_ref {
Some(StorageClass::UniformConstant)
Expand Down Expand Up @@ -287,6 +293,9 @@ impl<'tcx> CodegenCx<'tcx> {
// 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();
let is_pair = matches!(entry_arg_abi.mode, PassMode::Pair(..));
// is_pair implies is_unsized (but is_unsized doesn't imply is_pair, for RuntimeArray<T>)
assert!(!is_pair || is_unsized);
let var_ptr_spirv_type;
let (value_ptr, value_len) = match storage_class {
StorageClass::PushConstant | StorageClass::Uniform | StorageClass::StorageBuffer => {
Expand All @@ -299,7 +308,7 @@ impl<'tcx> CodegenCx<'tcx> {

let value_ptr = bx.struct_gep(var.with_type(var_ptr_spirv_type), 0);

let value_len = if is_unsized {
let value_len = if is_pair {
match self.lookup_type(value_spirv_type) {
SpirvType::RuntimeArray { .. } => {}
_ => self.tcx.sess.span_err(
Expand All @@ -317,11 +326,49 @@ impl<'tcx> CodegenCx<'tcx> {

Some(len.with_type(len_spirv_type))
} else {
if is_unsized {
// It's OK to use a RuntimeArray<u32> and not have a length parameter, but
// it's just nicer ergonomics to use a slice.
self.tcx
.sess
.span_warn(hir_param.ty_span, "use &[T] instead of &RuntimeArray<T>");
}
None
};

(value_ptr, value_len)
}
StorageClass::UniformConstant => {
var_ptr_spirv_type = self.type_ptr_to(value_spirv_type);

match self.lookup_type(value_spirv_type) {
SpirvType::RuntimeArray { .. } => {
if is_pair {
self.tcx.sess.span_err(
hir_param.ty_span,
"uniform_constant buffers must use &RuntimeArray<T>, not &[T]",
)
}
}
_ => {
if is_unsized {
self.tcx.sess.span_err(
hir_param.ty_span,
"only plain slices are supported as unsized types",
)
}
}
}

let value_len = if is_pair {
// We've already emitted an error, fill in a placeholder value
Some(bx.undef(self.type_isize()))
} else {
None
};

(var.with_type(var_ptr_spirv_type), value_len)
}
_ => {
var_ptr_spirv_type = self.type_ptr_to(value_spirv_type);

Expand Down
4 changes: 4 additions & 0 deletions crates/rustc_codegen_spirv/src/symbols.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,10 @@ impl Symbols {
"sampled_image",
SpirvAttribute::IntrinsicType(IntrinsicType::SampledImage),
),
(
"runtime_array",
SpirvAttribute::IntrinsicType(IntrinsicType::RuntimeArray),
),
("unroll_loops", SpirvAttribute::UnrollLoops),
]
.iter()
Expand Down
2 changes: 2 additions & 0 deletions crates/spirv-std/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,14 @@ pub mod arch;
pub mod float;
pub mod integer;
pub mod memory;
mod runtime_array;
pub mod scalar;
pub(crate) mod sealed;
mod textures;
pub mod vector;

pub use num_traits;
pub use runtime_array::*;
pub use textures::*;

/// Calls the `OpDemoteToHelperInvocationEXT` instruction, which corresponds to discard() in HLSL
Expand Down
37 changes: 37 additions & 0 deletions crates/spirv-std/src/runtime_array.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use core::marker::PhantomData;

#[spirv(runtime_array)]
pub struct RuntimeArray<T> {
// spooky! this field does not exist, so if it's referenced in rust code, things will explode
_do_not_touch: u32,
_phantom: PhantomData<T>,
}

// It would be nice to use the Index/IndexMut traits here, but because we do not have the length of
// the array, it's impossible to make them be safe operations (indexing out of bounds), and
// Index/IndexMut are marked as safe functions.
impl<T> RuntimeArray<T> {
#[spirv_std_macros::gpu_only]
pub unsafe fn index(&self, index: usize) -> &T {
asm! {
"%result = OpAccessChain _ {arr} {index}",
"OpReturnValue %result",
"%unused = OpLabel",
arr = in(reg) self,
index = in(reg) index,
}
loop {}
}

#[spirv_std_macros::gpu_only]
pub unsafe fn index_mut(&mut self, index: usize) -> &mut T {
asm! {
"%result = OpAccessChain _ {arr} {index}",
"OpReturnValue %result",
"%unused = OpLabel",
arr = in(reg) self,
index = in(reg) index,
}
loop {}
}
}
17 changes: 17 additions & 0 deletions tests/ui/storage_class/runtime_descriptor_array.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// build-pass
use spirv_std::{Image2d, RuntimeArray, Sampler};

#[spirv(fragment)]
pub fn main(
#[spirv(descriptor_set = 0, binding = 0)] sampler: &Sampler,
#[spirv(descriptor_set = 0, binding = 1)] slice: &RuntimeArray<Image2d>,
#[spirv(descriptor_set = 0, binding = 2)] sized_slice: &[Image2d; 5],
output: &mut glam::Vec4,
) {
let img = unsafe { slice.index(5) };
let v2 = glam::Vec2::new(0.0, 1.0);
*output = img.sample(*sampler, v2);

let img_2 = &sized_slice[2];
*output += img_2.sample(*sampler, v2);
}
10 changes: 10 additions & 0 deletions tests/ui/storage_class/runtime_descriptor_array_error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// build-fail

use spirv_std::{Image2d, RuntimeArray};

#[spirv(fragment)]
pub fn main(
#[spirv(descriptor_set = 0, binding = 0)] one: &[Image2d],
#[spirv(uniform, descriptor_set = 0, binding = 0)] two: &RuntimeArray<u32>,
) {
}
14 changes: 14 additions & 0 deletions tests/ui/storage_class/runtime_descriptor_array_error.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error: uniform_constant buffers must use &RuntimeArray<T>, not &[T]
--> $DIR/runtime_descriptor_array_error.rs:7:52
|
7 | #[spirv(descriptor_set = 0, binding = 0)] one: &[Image2d],
| ^^^^^^^^^^

warning: use &[T] instead of &RuntimeArray<T>
--> $DIR/runtime_descriptor_array_error.rs:8:61
|
8 | #[spirv(uniform, descriptor_set = 0, binding = 0)] two: &RuntimeArray<u32>,
| ^^^^^^^^^^^^^^^^^^

error: aborting due to previous error; 1 warning emitted

0 comments on commit 078847c

Please sign in to comment.