Skip to content

Commit 070f66e

Browse files
committed
[WIP] add #[spirv(typed_buffer)] for explicit SpirvType::InterfaceBlocks.
1 parent 8cd2b63 commit 070f66e

File tree

10 files changed

+251
-87
lines changed

10 files changed

+251
-87
lines changed

crates/rustc_codegen_spirv/src/abi.rs

+34-4
Original file line numberDiff line numberDiff line change
@@ -935,18 +935,48 @@ fn trans_intrinsic_type<'tcx>(
935935
.err("#[spirv(runtime_array)] type must have size 4"));
936936
}
937937

938-
// We use a generic to indicate the underlying element type.
939-
// The spirv type of it will be generated by querying the type of the first generic.
938+
// We use a generic param to indicate the underlying element type.
939+
// The SPIR-V element type will be generated from the first generic param.
940940
if let Some(elem_ty) = args.types().next() {
941-
let element = cx.layout_of(elem_ty).spirv_type(span, cx);
942-
Ok(SpirvType::RuntimeArray { element }.def(span, cx))
941+
Ok(SpirvType::RuntimeArray {
942+
element: cx.layout_of(elem_ty).spirv_type(span, cx),
943+
}
944+
.def(span, cx))
943945
} else {
944946
Err(cx
945947
.tcx
946948
.dcx()
947949
.err("#[spirv(runtime_array)] type must have a generic element type"))
948950
}
949951
}
952+
IntrinsicType::TypedBuffer => {
953+
if ty.size != Size::from_bytes(4) {
954+
return Err(cx
955+
.tcx
956+
.sess
957+
.dcx()
958+
.err("#[spirv(typed_buffer)] type must have size 4"));
959+
}
960+
961+
// We use a generic param to indicate the underlying data type.
962+
// The SPIR-V data type will be generated from the first generic param.
963+
if let Some(data_ty) = args.types().next() {
964+
// HACK(eddyb) this should be a *pointer* to an "interface block",
965+
// but SPIR-V screwed up and used no explicit indirection for the
966+
// descriptor indexing case, and instead made a `RuntimeArray` of
967+
// `InterfaceBlock`s be an "array of typed buffer resources".
968+
Ok(SpirvType::InterfaceBlock {
969+
inner_type: cx.layout_of(data_ty).spirv_type(span, cx),
970+
}
971+
.def(span, cx))
972+
} else {
973+
Err(cx
974+
.tcx
975+
.sess
976+
.dcx()
977+
.err("#[spirv(typed_buffer)] type must have a generic data type"))
978+
}
979+
}
950980
IntrinsicType::Matrix => {
951981
let span = def_id_for_spirv_type_adt(ty)
952982
.map(|did| cx.tcx.def_span(did))

crates/rustc_codegen_spirv/src/attr.rs

+1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ pub enum IntrinsicType {
6565
SampledImage,
6666
RayQueryKhr,
6767
RuntimeArray,
68+
TypedBuffer,
6869
Matrix,
6970
}
7071

crates/rustc_codegen_spirv/src/builder/spirv_asm.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -700,7 +700,11 @@ impl<'cx, 'tcx> Builder<'cx, 'tcx> {
700700
};
701701
ty = match cx.lookup_type(ty) {
702702
SpirvType::Array { element, .. }
703-
| SpirvType::RuntimeArray { element } => element,
703+
| SpirvType::RuntimeArray { element }
704+
// HACK(eddyb) this is pretty bad because it's not
705+
// checking that the index is an `OpConstant 0`, but
706+
// there's no other valid choice anyway.
707+
| SpirvType::InterfaceBlock { inner_type: element } => element,
704708

705709
SpirvType::Adt { field_types, .. } => *index_to_usize()
706710
.and_then(|i| field_types.get(i))

crates/rustc_codegen_spirv/src/codegen_cx/entry.rs

+90-65
Original file line numberDiff line numberDiff line change
@@ -497,89 +497,107 @@ impl<'tcx> CodegenCx<'tcx> {
497497
.dcx()
498498
.span_fatal(hir_param.ty_span, "pair type not supported yet")
499499
}
500+
// FIXME(eddyb) should this talk about "typed buffers" instead of "interface blocks"?
501+
// FIXME(eddyb) should we talk about "descriptor indexing" or
502+
// actually use more reasonable terms like "resource arrays"?
503+
let needs_interface_block_and_supports_descriptor_indexing = matches!(
504+
storage_class,
505+
Ok(StorageClass::Uniform | StorageClass::StorageBuffer)
506+
);
507+
let needs_interface_block = needs_interface_block_and_supports_descriptor_indexing
508+
|| storage_class == Ok(StorageClass::PushConstant);
509+
// NOTE(eddyb) `#[spirv(typed_buffer)]` adds `SpirvType::InterfaceBlock`s
510+
// which must bypass the automated ones (i.e. the user is taking control).
511+
let has_explicit_interface_block = needs_interface_block_and_supports_descriptor_indexing
512+
&& {
513+
// Peel off arrays first (used for "descriptor indexing").
514+
let outermost_or_array_element = match self.lookup_type(value_spirv_type) {
515+
SpirvType::Array { element, .. } | SpirvType::RuntimeArray { element } => {
516+
element
517+
}
518+
_ => value_spirv_type,
519+
};
520+
matches!(
521+
self.lookup_type(outermost_or_array_element),
522+
SpirvType::InterfaceBlock { .. }
523+
)
524+
};
500525
let var_ptr_spirv_type;
501-
let (value_ptr, value_len) = match storage_class {
502-
Ok(
503-
StorageClass::PushConstant | StorageClass::Uniform | StorageClass::StorageBuffer,
504-
) => {
505-
let var_spirv_type = SpirvType::InterfaceBlock {
506-
inner_type: value_spirv_type,
507-
}
508-
.def(hir_param.span, self);
509-
var_ptr_spirv_type = self.type_ptr_to(var_spirv_type);
526+
let (value_ptr, value_len) = if needs_interface_block && !has_explicit_interface_block {
527+
let var_spirv_type = SpirvType::InterfaceBlock {
528+
inner_type: value_spirv_type,
529+
}
530+
.def(hir_param.span, self);
531+
var_ptr_spirv_type = self.type_ptr_to(var_spirv_type);
510532

511-
let value_ptr = bx.struct_gep(
512-
var_spirv_type,
513-
var_id.unwrap().with_type(var_ptr_spirv_type),
514-
0,
515-
);
533+
let value_ptr = bx.struct_gep(
534+
var_spirv_type,
535+
var_id.unwrap().with_type(var_ptr_spirv_type),
536+
0,
537+
);
516538

517-
let value_len = if is_unsized_with_len {
518-
match self.lookup_type(value_spirv_type) {
519-
SpirvType::RuntimeArray { .. } => {}
520-
_ => {
521-
self.tcx.dcx().span_err(
522-
hir_param.ty_span,
523-
"only plain slices are supported as unsized types",
524-
);
525-
}
539+
let value_len = if is_unsized_with_len {
540+
match self.lookup_type(value_spirv_type) {
541+
SpirvType::RuntimeArray { .. } => {}
542+
_ => {
543+
self.tcx.dcx().span_err(
544+
hir_param.ty_span,
545+
"only plain slices are supported as unsized types",
546+
);
526547
}
548+
}
527549

528-
// FIXME(eddyb) shouldn't this be `usize`?
529-
let len_spirv_type = self.type_isize();
530-
let len = bx
531-
.emit()
532-
.array_length(len_spirv_type, None, var_id.unwrap(), 0)
533-
.unwrap();
534-
535-
Some(len.with_type(len_spirv_type))
536-
} else {
537-
if is_unsized {
538-
// It's OK to use a RuntimeArray<u32> and not have a length parameter, but
539-
// it's just nicer ergonomics to use a slice.
540-
self.tcx
541-
.dcx()
542-
.span_warn(hir_param.ty_span, "use &[T] instead of &RuntimeArray<T>");
543-
}
544-
None
545-
};
550+
// FIXME(eddyb) shouldn't this be `usize`?
551+
let len_spirv_type = self.type_isize();
552+
let len = bx
553+
.emit()
554+
.array_length(len_spirv_type, None, var_id.unwrap(), 0)
555+
.unwrap();
546556

547-
(Ok(value_ptr), value_len)
548-
}
549-
Ok(StorageClass::UniformConstant) => {
550-
var_ptr_spirv_type = self.type_ptr_to(value_spirv_type);
557+
Some(len.with_type(len_spirv_type))
558+
} else {
559+
if is_unsized {
560+
// It's OK to use a RuntimeArray<u32> and not have a length parameter, but
561+
// it's just nicer ergonomics to use a slice.
562+
self.tcx
563+
.dcx()
564+
.span_warn(hir_param.ty_span, "use &[T] instead of &RuntimeArray<T>");
565+
}
566+
None
567+
};
551568

569+
(Ok(value_ptr), value_len)
570+
} else {
571+
var_ptr_spirv_type = self.type_ptr_to(value_spirv_type);
572+
573+
// FIXME(eddyb) should we talk about "descriptor indexing" or
574+
// actually use more reasonable terms like "resource arrays"?
575+
let unsized_is_descriptor_indexing =
576+
needs_interface_block_and_supports_descriptor_indexing
577+
|| storage_class == Ok(StorageClass::UniformConstant);
578+
if unsized_is_descriptor_indexing {
552579
match self.lookup_type(value_spirv_type) {
553580
SpirvType::RuntimeArray { .. } => {
554581
if is_unsized_with_len {
555582
self.tcx.dcx().span_err(
556583
hir_param.ty_span,
557-
"uniform_constant must use &RuntimeArray<T>, not &[T]",
584+
"descriptor indexing must use &RuntimeArray<T>, not &[T]",
558585
);
559586
}
560587
}
561588
_ => {
562589
if is_unsized {
563590
self.tcx.dcx().span_err(
564591
hir_param.ty_span,
565-
"only plain slices are supported as unsized types",
592+
"only RuntimeArray is supported, not other unsized types",
566593
);
567594
}
568595
}
569596
}
570-
571-
let value_len = if is_pair {
572-
// We've already emitted an error, fill in a placeholder value
573-
Some(bx.undef(self.type_isize()))
574-
} else {
575-
None
576-
};
577-
578-
(Ok(var_id.unwrap().with_type(var_ptr_spirv_type)), value_len)
579-
}
580-
_ => {
581-
var_ptr_spirv_type = self.type_ptr_to(value_spirv_type);
582-
597+
} else {
598+
// FIXME(eddyb) determine, based on the type, what kind of type
599+
// this is, to narrow it further to e.g. "buffer in a non-buffer
600+
// storage class" or "storage class expects fixed data sizes".
583601
if is_unsized {
584602
self.tcx.dcx().span_fatal(
585603
hir_param.ty_span,
@@ -592,12 +610,19 @@ impl<'tcx> CodegenCx<'tcx> {
592610
),
593611
);
594612
}
595-
596-
(
597-
var_id.map(|var_id| var_id.with_type(var_ptr_spirv_type)),
598-
None,
599-
)
600613
}
614+
615+
let value_len = if is_pair {
616+
// We've already emitted an error, fill in a placeholder value
617+
Some(bx.undef(self.type_isize()))
618+
} else {
619+
None
620+
};
621+
622+
(
623+
var_id.map(|var_id| var_id.with_type(var_ptr_spirv_type)),
624+
value_len,
625+
)
601626
};
602627

603628
// Compute call argument(s) to match what the Rust entry `fn` expects,

crates/rustc_codegen_spirv/src/spirv_type.rs

+4-6
Original file line numberDiff line numberDiff line change
@@ -342,9 +342,8 @@ impl SpirvType<'_> {
342342
| Self::AccelerationStructureKhr
343343
| Self::RayQueryKhr
344344
| Self::Sampler
345-
| Self::SampledImage { .. } => Size::from_bytes(4),
346-
347-
Self::InterfaceBlock { inner_type } => cx.lookup_type(inner_type).sizeof(cx)?,
345+
| Self::SampledImage { .. }
346+
| Self::InterfaceBlock { .. } => Size::from_bytes(4),
348347
};
349348
Some(result)
350349
}
@@ -372,9 +371,8 @@ impl SpirvType<'_> {
372371
| Self::AccelerationStructureKhr
373372
| Self::RayQueryKhr
374373
| Self::Sampler
375-
| Self::SampledImage { .. } => Align::from_bytes(4).unwrap(),
376-
377-
Self::InterfaceBlock { inner_type } => cx.lookup_type(inner_type).alignof(cx),
374+
| Self::SampledImage { .. }
375+
| Self::InterfaceBlock { .. } => Align::from_bytes(4).unwrap(),
378376
}
379377
}
380378

crates/rustc_codegen_spirv/src/symbols.rs

+4
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,10 @@ impl Symbols {
342342
"runtime_array",
343343
SpirvAttribute::IntrinsicType(IntrinsicType::RuntimeArray),
344344
),
345+
(
346+
"typed_buffer",
347+
SpirvAttribute::IntrinsicType(IntrinsicType::TypedBuffer),
348+
),
345349
(
346350
"matrix",
347351
SpirvAttribute::IntrinsicType(IntrinsicType::Matrix),

crates/spirv-std/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,15 @@ mod runtime_array;
107107
mod sampler;
108108
pub mod scalar;
109109
pub(crate) mod sealed;
110+
mod typed_buffer;
110111
pub mod vector;
111112

112113
pub use self::sampler::Sampler;
113114
pub use crate::macros::Image;
114115
pub use byte_addressable_buffer::ByteAddressableBuffer;
115116
pub use num_traits;
116117
pub use runtime_array::*;
118+
pub use typed_buffer::*;
117119

118120
pub use glam;
119121

0 commit comments

Comments
 (0)