Skip to content

Commit 8ee360c

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

File tree

10 files changed

+259
-95
lines changed

10 files changed

+259
-95
lines changed

crates/rustc_codegen_spirv/src/abi.rs

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

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

+98-73
Original file line numberDiff line numberDiff line change
@@ -495,96 +495,114 @@ impl<'tcx> CodegenCx<'tcx> {
495495
.dcx()
496496
.span_fatal(hir_param.ty_span, "pair type not supported yet")
497497
}
498+
// FIXME(eddyb) should this talk about "typed buffers" instead of "interface blocks"?
499+
// FIXME(eddyb) should we talk about "descriptor indexing" or
500+
// actually use more reasonable terms like "resource arrays"?
501+
let needs_interface_block_and_supports_descriptor_indexing = matches!(
502+
storage_class,
503+
Ok(StorageClass::Uniform | StorageClass::StorageBuffer)
504+
);
505+
let needs_interface_block = needs_interface_block_and_supports_descriptor_indexing
506+
|| storage_class == Ok(StorageClass::PushConstant);
507+
// NOTE(eddyb) `#[spirv(typed_buffer)]` adds `SpirvType::InterfaceBlock`s
508+
// which must bypass the automated ones (i.e. the user is taking control).
509+
let has_explicit_interface_block = needs_interface_block_and_supports_descriptor_indexing
510+
&& {
511+
// Peel off arrays first (used for "descriptor indexing").
512+
let outermost_or_array_element = match self.lookup_type(value_spirv_type) {
513+
SpirvType::Array { element, .. } | SpirvType::RuntimeArray { element } => {
514+
element
515+
}
516+
_ => value_spirv_type,
517+
};
518+
matches!(
519+
self.lookup_type(outermost_or_array_element),
520+
SpirvType::InterfaceBlock { .. }
521+
)
522+
};
498523
let var_ptr_spirv_type;
499-
let (value_ptr, value_len) = match storage_class {
500-
Ok(
501-
StorageClass::PushConstant | StorageClass::Uniform | StorageClass::StorageBuffer,
502-
) => {
503-
let var_spirv_type = SpirvType::InterfaceBlock {
504-
inner_type: value_spirv_type,
505-
}
506-
.def(hir_param.span, self);
507-
var_ptr_spirv_type = self.type_ptr_to(var_spirv_type);
508-
509-
let zero_u32 = self.constant_u32(hir_param.span, 0).def_cx(self);
510-
let value_ptr_spirv_type = self.type_ptr_to(value_spirv_type);
511-
let value_ptr = bx
512-
.emit()
513-
.in_bounds_access_chain(
514-
value_ptr_spirv_type,
515-
None,
516-
var_id.unwrap(),
517-
[zero_u32].iter().cloned(),
518-
)
519-
.unwrap()
520-
.with_type(value_ptr_spirv_type);
524+
let (value_ptr, value_len) = if needs_interface_block && !has_explicit_interface_block {
525+
let var_spirv_type = SpirvType::InterfaceBlock {
526+
inner_type: value_spirv_type,
527+
}
528+
.def(hir_param.span, self);
529+
var_ptr_spirv_type = self.type_ptr_to(var_spirv_type);
530+
531+
let zero_u32 = self.constant_u32(hir_param.span, 0).def_cx(self);
532+
let value_ptr_spirv_type = self.type_ptr_to(value_spirv_type);
533+
let value_ptr = bx
534+
.emit()
535+
.in_bounds_access_chain(
536+
value_ptr_spirv_type,
537+
None,
538+
var_id.unwrap(),
539+
[zero_u32].iter().cloned(),
540+
)
541+
.unwrap()
542+
.with_type(value_ptr_spirv_type);
521543

522-
let value_len = if is_unsized_with_len {
523-
match self.lookup_type(value_spirv_type) {
524-
SpirvType::RuntimeArray { .. } => {}
525-
_ => {
526-
self.tcx.dcx().span_err(
527-
hir_param.ty_span,
528-
"only plain slices are supported as unsized types",
529-
);
530-
}
544+
let value_len = if is_unsized_with_len {
545+
match self.lookup_type(value_spirv_type) {
546+
SpirvType::RuntimeArray { .. } => {}
547+
_ => {
548+
self.tcx.dcx().span_err(
549+
hir_param.ty_span,
550+
"only plain slices are supported as unsized types",
551+
);
531552
}
553+
}
532554

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

552-
(Ok(value_ptr), value_len)
553-
}
554-
Ok(StorageClass::UniformConstant) => {
555-
var_ptr_spirv_type = self.type_ptr_to(value_spirv_type);
562+
Some(len.with_type(len_spirv_type))
563+
} else {
564+
if is_unsized {
565+
// It's OK to use a RuntimeArray<u32> and not have a length parameter, but
566+
// it's just nicer ergonomics to use a slice.
567+
self.tcx
568+
.dcx()
569+
.span_warn(hir_param.ty_span, "use &[T] instead of &RuntimeArray<T>");
570+
}
571+
None
572+
};
556573

574+
(Ok(value_ptr), value_len)
575+
} else {
576+
var_ptr_spirv_type = self.type_ptr_to(value_spirv_type);
577+
578+
// FIXME(eddyb) should we talk about "descriptor indexing" or
579+
// actually use more reasonable terms like "resource arrays"?
580+
let unsized_is_descriptor_indexing =
581+
needs_interface_block_and_supports_descriptor_indexing
582+
|| storage_class == Ok(StorageClass::UniformConstant);
583+
if unsized_is_descriptor_indexing {
557584
match self.lookup_type(value_spirv_type) {
558585
SpirvType::RuntimeArray { .. } => {
559586
if is_unsized_with_len {
560587
self.tcx.dcx().span_err(
561588
hir_param.ty_span,
562-
"uniform_constant must use &RuntimeArray<T>, not &[T]",
589+
"descriptor indexing must use &RuntimeArray<T>, not &[T]",
563590
);
564591
}
565592
}
566593
_ => {
567594
if is_unsized {
568595
self.tcx.dcx().span_err(
569596
hir_param.ty_span,
570-
"only plain slices are supported as unsized types",
597+
"only RuntimeArray is supported, not other unsized types",
571598
);
572599
}
573600
}
574601
}
575-
576-
let value_len = if is_pair {
577-
// We've already emitted an error, fill in a placeholder value
578-
Some(bx.undef(self.type_isize()))
579-
} else {
580-
None
581-
};
582-
583-
(Ok(var_id.unwrap().with_type(var_ptr_spirv_type)), value_len)
584-
}
585-
_ => {
586-
var_ptr_spirv_type = self.type_ptr_to(value_spirv_type);
587-
602+
} else {
603+
// FIXME(eddyb) determine, based on the type, what kind of type
604+
// this is, to narrow it further to e.g. "buffer in a non-buffer
605+
// storage class" or "storage class expects fixed data sizes".
588606
if is_unsized {
589607
self.tcx.dcx().span_fatal(
590608
hir_param.ty_span,
@@ -597,12 +615,19 @@ impl<'tcx> CodegenCx<'tcx> {
597615
),
598616
);
599617
}
600-
601-
(
602-
var_id.map(|var_id| var_id.with_type(var_ptr_spirv_type)),
603-
None,
604-
)
605618
}
619+
620+
let value_len = if is_pair {
621+
// We've already emitted an error, fill in a placeholder value
622+
Some(bx.undef(self.type_isize()))
623+
} else {
624+
None
625+
};
626+
627+
(
628+
var_id.map(|var_id| var_id.with_type(var_ptr_spirv_type)),
629+
value_len,
630+
)
606631
};
607632

608633
// 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
@@ -347,9 +347,8 @@ impl SpirvType<'_> {
347347
| Self::AccelerationStructureKhr
348348
| Self::RayQueryKhr
349349
| Self::Sampler
350-
| Self::SampledImage { .. } => Size::from_bytes(4),
351-
352-
Self::InterfaceBlock { inner_type } => cx.lookup_type(inner_type).sizeof(cx)?,
350+
| Self::SampledImage { .. }
351+
| Self::InterfaceBlock { .. } => Size::from_bytes(4),
353352
};
354353
Some(result)
355354
}
@@ -377,9 +376,8 @@ impl SpirvType<'_> {
377376
| Self::AccelerationStructureKhr
378377
| Self::RayQueryKhr
379378
| Self::Sampler
380-
| Self::SampledImage { .. } => Align::from_bytes(4).unwrap(),
381-
382-
Self::InterfaceBlock { inner_type } => cx.lookup_type(inner_type).alignof(cx),
379+
| Self::SampledImage { .. }
380+
| Self::InterfaceBlock { .. } => Align::from_bytes(4).unwrap(),
383381
}
384382
}
385383

crates/rustc_codegen_spirv/src/symbols.rs

+4
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,10 @@ impl Symbols {
340340
"runtime_array",
341341
SpirvAttribute::IntrinsicType(IntrinsicType::RuntimeArray),
342342
),
343+
(
344+
"typed_buffer",
345+
SpirvAttribute::IntrinsicType(IntrinsicType::TypedBuffer),
346+
),
343347
(
344348
"matrix",
345349
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)