Implement textures (#276)

This commit is contained in:
Ashley Hauck 2020-11-26 14:16:22 +01:00 committed by GitHub
parent 59869ec58b
commit a73f54aa86
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 416 additions and 22 deletions

View File

@ -305,6 +305,13 @@ impl<'tcx> ConvSpirvType<'tcx> for TyAndLayout<'tcx> {
}
fn trans_type_impl<'tcx>(cx: &CodegenCx<'tcx>, ty: TyAndLayout<'tcx>, is_immediate: bool) -> Word {
if let TyKind::Adt(adt, _) = *ty.ty.kind() {
for attr in parse_attrs(cx, cx.tcx.get_attrs(adt.did)) {
if let Some(image) = trans_image(cx, ty, attr) {
return image;
}
}
}
// 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.
@ -705,3 +712,49 @@ fn name_of_struct(ty: TyAndLayout<'_>) -> String {
}
name
}
fn trans_image<'tcx>(
cx: &CodegenCx<'tcx>,
ty: TyAndLayout<'tcx>,
attr: SpirvAttribute,
) -> Option<Word> {
match attr {
SpirvAttribute::Image {
dim,
depth,
arrayed,
multisampled,
sampled,
image_format,
access_qualifier,
} => {
// see SpirvType::sizeof
if ty.size != Size::from_bytes(4) {
cx.tcx.sess.err("#[spirv(image)] type must have size 4");
return None;
}
// Hardcode to float for now
let sampled_type = SpirvType::Float(32).def(cx);
let ty = SpirvType::Image {
sampled_type,
dim,
depth,
arrayed,
multisampled,
sampled,
image_format,
access_qualifier,
};
Some(ty.def(cx))
}
SpirvAttribute::Sampler => {
// see SpirvType::sizeof
if ty.size != Size::from_bytes(4) {
cx.tcx.sess.err("#[spirv(sampler)] type must have size 4");
return None;
}
Some(SpirvType::Sampler.def(cx))
}
_ => None,
}
}

View File

@ -185,6 +185,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
SpirvType::Pointer { .. } => self.fatal("memset on pointers not implemented yet"),
SpirvType::Function { .. } => self.fatal("memset on functions not implemented yet"),
SpirvType::Image { .. } => self.fatal("cannot memset image"),
SpirvType::Sampler => self.fatal("cannot memset sampler"),
}
}
@ -238,6 +240,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
SpirvType::Pointer { .. } => self.fatal("memset on pointers not implemented yet"),
SpirvType::Function { .. } => self.fatal("memset on functions not implemented yet"),
SpirvType::Image { .. } => self.fatal("cannot memset image"),
SpirvType::Sampler => self.fatal("cannot memset sampler"),
}
}

View File

@ -113,13 +113,17 @@ impl<'a, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'tcx> {
);
}
let line = tokens.last_mut().unwrap();
if line
.last()
.map_or(false, |prev| matches!(prev, Token::Word("typeof")))
{
*line.last_mut().unwrap() = Token::Typeof(&operands[operand_idx], span);
} else {
line.push(Token::Placeholder(&operands[operand_idx], span));
let typeof_kind = line.last().and_then(|prev| match prev {
Token::Word("typeof") => Some(TypeofKind::Plain),
Token::Word("typeof*") => Some(TypeofKind::Dereference),
_ => None,
});
match typeof_kind {
Some(kind) => {
*line.last_mut().unwrap() =
Token::Typeof(&operands[operand_idx], span, kind)
}
None => line.push(Token::Placeholder(&operands[operand_idx], span)),
}
}
}
@ -132,10 +136,19 @@ impl<'a, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'tcx> {
}
}
enum TypeofKind {
Plain,
Dereference,
}
enum Token<'a, 'cx, 'tcx> {
Word(&'a str),
Placeholder(&'a InlineAsmOperandRef<'tcx, Builder<'cx, 'tcx>>, Span),
Typeof(&'a InlineAsmOperandRef<'tcx, Builder<'cx, 'tcx>>, Span),
Typeof(
&'a InlineAsmOperandRef<'tcx, Builder<'cx, 'tcx>>,
Span,
TypeofKind,
),
}
enum OutRegister<'a> {
@ -204,7 +217,7 @@ impl<'cx, 'tcx> Builder<'cx, 'tcx> {
Token::Placeholder(_, _) => true,
Token::Word(id_str) if id_str.starts_with('%') => true,
Token::Word(_) => false,
Token::Typeof(_, _) => false,
Token::Typeof(_, _, _) => false,
} {
let result_id = match self.parse_id_out(id_map, first_token) {
Some(result_id) => result_id,
@ -230,7 +243,7 @@ impl<'cx, 'tcx> Builder<'cx, 'tcx> {
};
let inst_name = match first_token {
Token::Word(inst_name) => inst_name,
Token::Placeholder(_, span) | Token::Typeof(_, span) => {
Token::Placeholder(_, span) | Token::Typeof(_, span, _) => {
self.tcx
.sess
.span_err(span, "cannot use a dynamic value as an instruction type");
@ -354,7 +367,7 @@ impl<'cx, 'tcx> Builder<'cx, 'tcx> {
None
}
},
Token::Typeof(_, span) => {
Token::Typeof(_, span, _) => {
self.tcx
.sess
.span_err(span, "cannot assign to a typeof expression");
@ -432,10 +445,26 @@ impl<'cx, 'tcx> Builder<'cx, 'tcx> {
None
}
},
Token::Typeof(hole, span) => match hole {
Token::Typeof(hole, span, kind) => match hole {
InlineAsmOperandRef::In { reg, value } => {
self.check_reg(span, reg);
Some(value.immediate().ty)
let ty = value.immediate().ty;
Some(match kind {
TypeofKind::Plain => ty,
TypeofKind::Dereference => match self.lookup_type(ty) {
SpirvType::Pointer { pointee, .. } => pointee,
other => {
self.tcx.sess.span_err(
span,
&format!(
"cannot use typeof* on non-pointer type: {}",
other.debug(ty, self)
),
);
ty
}
},
})
}
InlineAsmOperandRef::Out {
reg,
@ -559,7 +588,7 @@ impl<'cx, 'tcx> Builder<'cx, 'tcx> {
let word = match token {
Token::Word(word) => Some(word),
Token::Placeholder(_, _) => None,
Token::Typeof(_, _) => None,
Token::Typeof(_, _, _) => None,
};
match (kind, word) {
(OperandKind::IdResultType, _) => {
@ -663,7 +692,7 @@ impl<'cx, 'tcx> Builder<'cx, 'tcx> {
span,
&format!("expected a literal, not a dynamic value for a {:?}", kind),
),
Some(Token::Typeof(_, span)) => self.tcx.sess.span_err(
Some(Token::Typeof(_, span, _)) => self.tcx.sess.span_err(
span,
&format!("expected a literal, not a type for a {:?}", kind),
),
@ -858,7 +887,7 @@ impl<'cx, 'tcx> Builder<'cx, 'tcx> {
&format!("expected a literal, not a dynamic value for a {:?}", kind),
);
}
Token::Typeof(_, span) => {
Token::Typeof(_, span, _) => {
self.tcx.sess.span_err(
span,
&format!("expected a literal, not a type for a {:?}", kind),

View File

@ -482,6 +482,11 @@ impl<'tcx> CodegenCx<'tcx> {
.tcx
.sess
.fatal("TODO: SpirvType::Function not supported yet in create_const_alloc"),
SpirvType::Image { .. } => self.tcx.sess.fatal("Cannot create a constant image value"),
SpirvType::Sampler => self
.tcx
.sess
.fatal("Cannot create a constant sampler value"),
}
}

View File

@ -174,6 +174,8 @@ impl<'tcx> BaseTypeMethods<'tcx> for CodegenCx<'tcx> {
SpirvType::RuntimeArray { .. } => TypeKind::Array,
SpirvType::Pointer { .. } => TypeKind::Pointer,
SpirvType::Function { .. } => TypeKind::Function,
SpirvType::Image { .. } => TypeKind::Integer,
SpirvType::Sampler => TypeKind::Integer,
}
}
fn type_ptr_to(&self, ty: Self::Type) -> Self::Type {

View File

@ -3,7 +3,9 @@ use crate::builder_spirv::SpirvValue;
use crate::codegen_cx::CodegenCx;
use bimap::BiHashMap;
use rspirv::dr::Operand;
use rspirv::spirv::{Capability, Decoration, StorageClass, Word};
use rspirv::spirv::{
AccessQualifier, Capability, Decoration, Dim, ImageFormat, StorageClass, Word,
};
use rustc_target::abi::{Align, Size};
use std::cell::RefCell;
use std::fmt;
@ -56,6 +58,17 @@ pub enum SpirvType {
return_type: Word,
arguments: Vec<Word>,
},
Image {
sampled_type: Word,
dim: Dim,
depth: u32,
arrayed: u32,
multisampled: u32,
sampled: u32,
image_format: ImageFormat,
access_qualifier: Option<AccessQualifier>,
},
Sampler,
}
impl SpirvType {
@ -183,6 +196,26 @@ impl SpirvType {
} => cx
.emit_global()
.type_function(return_type, arguments.iter().cloned()),
Self::Image {
sampled_type,
dim,
depth,
arrayed,
multisampled,
sampled,
image_format,
access_qualifier,
} => cx.emit_global().type_image(
sampled_type,
dim,
depth,
arrayed,
multisampled,
sampled,
image_format,
access_qualifier,
),
Self::Sampler => cx.emit_global().type_sampler(),
};
cx.type_cache.def(result, self);
result
@ -244,6 +277,8 @@ impl SpirvType {
Self::RuntimeArray { .. } => return None,
Self::Pointer { .. } => cx.tcx.data_layout.pointer_size,
Self::Function { .. } => cx.tcx.data_layout.pointer_size,
Self::Image { .. } => Size::from_bytes(4),
Self::Sampler => Size::from_bytes(4),
};
Some(result)
}
@ -267,6 +302,8 @@ impl SpirvType {
Self::RuntimeArray { element } => cx.lookup_type(element).alignof(cx),
Self::Pointer { .. } => cx.tcx.data_layout.pointer_align.abi,
Self::Function { .. } => cx.tcx.data_layout.pointer_align.abi,
Self::Image { .. } => Align::from_bytes(4).unwrap(),
Self::Sampler => Align::from_bytes(4).unwrap(),
}
}
}
@ -380,6 +417,28 @@ impl fmt::Debug for SpirvTypePrinter<'_, '_> {
.field("arguments", &args)
.finish()
}
SpirvType::Image {
sampled_type,
dim,
depth,
arrayed,
multisampled,
sampled,
image_format,
access_qualifier,
} => f
.debug_struct("Image")
.field("id", &self.id)
.field("sampled_type", &self.cx.debug_type(sampled_type))
.field("dim", &dim)
.field("depth", &depth)
.field("arrayed", &arrayed)
.field("multisampled", &multisampled)
.field("sampled", &sampled)
.field("image_format", &image_format)
.field("access_qualifier", &access_qualifier)
.finish(),
SpirvType::Sampler => f.debug_struct("Sampler").field("id", &self.id).finish(),
};
{
let mut debug_stack = DEBUG_STACK.lock().unwrap();
@ -489,6 +548,27 @@ impl SpirvTypePrinter<'_, '_> {
f.write_str(") -> ")?;
ty(self.cx, stack, f, return_type)
}
SpirvType::Image {
sampled_type,
dim,
depth,
arrayed,
multisampled,
sampled,
image_format,
access_qualifier,
} => f
.debug_struct("Image")
.field("sampled_type", &self.cx.debug_type(sampled_type))
.field("dim", &dim)
.field("depth", &depth)
.field("arrayed", &arrayed)
.field("multisampled", &multisampled)
.field("sampled", &sampled)
.field("image_format", &image_format)
.field("access_qualifier", &access_qualifier)
.finish(),
SpirvType::Sampler => f.write_str("Sampler"),
}
}
}

View File

@ -1,6 +1,8 @@
use crate::builder::libm_intrinsics;
use crate::codegen_cx::CodegenCx;
use rspirv::spirv::{BuiltIn, ExecutionMode, ExecutionModel, StorageClass};
use rspirv::spirv::{
AccessQualifier, BuiltIn, Dim, ExecutionMode, ExecutionModel, ImageFormat, StorageClass,
};
use rustc_ast::ast::{AttrKind, Attribute, Lit, LitIntType, LitKind, NestedMetaItem};
use rustc_span::symbol::{Ident, Symbol};
use std::collections::HashMap;
@ -30,7 +32,14 @@ pub struct Symbols {
pub spirv15: Symbol,
descriptor_set: Symbol,
binding: Symbol,
really_unsafe_ignore_bitcasts: Symbol,
image: Symbol,
dim: Symbol,
depth: Symbol,
arrayed: Symbol,
multisampled: Symbol,
sampled: Symbol,
image_format: Symbol,
access_qualifier: Symbol,
attributes: HashMap<Symbol, SpirvAttribute>,
execution_modes: HashMap<Symbol, (ExecutionMode, ExecutionModeExtraDim)>,
pub libm_intrinsics: HashMap<Symbol, libm_intrinsics::LibmIntrinsic>,
@ -321,9 +330,19 @@ impl Symbols {
let execution_models = EXECUTION_MODELS
.iter()
.map(|&(a, b)| (a, SpirvAttribute::Entry(b.into())));
let custom_attributes = [
(
"really_unsafe_ignore_bitcasts",
SpirvAttribute::ReallyUnsafeIgnoreBitcasts,
),
("sampler", SpirvAttribute::Sampler),
]
.iter()
.cloned();
let attributes_iter = builtins
.chain(storage_classes)
.chain(execution_models)
.chain(custom_attributes)
.map(|(a, b)| (Symbol::intern(a), b));
let mut attributes = HashMap::new();
for (a, b) in attributes_iter {
@ -362,7 +381,14 @@ impl Symbols {
spirv15: Symbol::intern("spirv1.5"),
descriptor_set: Symbol::intern("descriptor_set"),
binding: Symbol::intern("binding"),
really_unsafe_ignore_bitcasts: Symbol::intern("really_unsafe_ignore_bitcasts"),
image: Symbol::intern("image"),
dim: Symbol::intern("dim"),
depth: Symbol::intern("depth"),
arrayed: Symbol::intern("arrayed"),
multisampled: Symbol::intern("multisampled"),
sampled: Symbol::intern("sampled"),
image_format: Symbol::intern("image_format"),
access_qualifier: Symbol::intern("access_qualifier"),
attributes,
execution_modes,
libm_intrinsics,
@ -415,6 +441,16 @@ pub enum SpirvAttribute {
DescriptorSet(u32),
Binding(u32),
ReallyUnsafeIgnoreBitcasts,
Image {
dim: Dim,
depth: u32,
arrayed: u32,
multisampled: u32,
sampled: u32,
image_format: ImageFormat,
access_qualifier: Option<AccessQualifier>,
},
Sampler,
}
// Note that we could mark the attr as used via cx.tcx.sess.mark_attr_used(attr), but unused
@ -448,8 +484,8 @@ pub fn parse_attrs(
Vec::new()
};
args.into_iter().filter_map(move |ref arg| {
if arg.has_name(cx.sym.really_unsafe_ignore_bitcasts) {
Some(SpirvAttribute::ReallyUnsafeIgnoreBitcasts)
if arg.has_name(cx.sym.image) {
parse_image(cx, arg)
} else if arg.has_name(cx.sym.descriptor_set) {
match parse_attr_int_value(cx, arg) {
Some(x) => Some(SpirvAttribute::DescriptorSet(x)),
@ -493,6 +529,136 @@ pub fn parse_attrs(
result.collect::<Vec<_>>().into_iter()
}
fn parse_image(cx: &CodegenCx<'_>, attr: &NestedMetaItem) -> Option<SpirvAttribute> {
let args = match attr.meta_item_list() {
Some(args) => args,
None => {
cx.tcx
.sess
.span_err(attr.span(), "image attribute must have arguments");
return None;
}
};
if args.len() != 6 && args.len() != 7 {
cx.tcx
.sess
.span_err(attr.span(), "image attribute must have 6 or 7 arguments");
return None;
}
let check = |idx: usize, sym: Symbol| -> bool {
if args[idx].has_name(sym) {
false
} else {
cx.tcx.sess.span_err(
args[idx].span(),
&format!("image attribute argument {} must be {}=...", idx + 1, sym),
);
true
}
};
if check(0, cx.sym.dim)
| check(1, cx.sym.depth)
| check(2, cx.sym.arrayed)
| check(3, cx.sym.multisampled)
| check(4, cx.sym.sampled)
| check(5, cx.sym.image_format)
| (args.len() == 7 && check(6, cx.sym.access_qualifier))
{
return None;
}
let arg_values = args
.iter()
.map(
|arg| match arg.meta_item().and_then(|arg| arg.name_value_literal()) {
Some(arg) => Some(arg),
None => {
cx.tcx
.sess
.span_err(arg.span(), "image attribute must be name=value");
None
}
},
)
.collect::<Option<Vec<_>>>()?;
let dim = match arg_values[0].kind {
LitKind::Str(dim, _) => match dim.with(|s| s.parse()) {
Ok(dim) => dim,
Err(()) => {
cx.tcx.sess.span_err(args[0].span(), "invalid dim value");
return None;
}
},
_ => {
cx.tcx
.sess
.span_err(args[0].span(), "dim value must be str");
return None;
}
};
let parse_lit = |idx: usize, name: &str| -> Option<u32> {
match arg_values[idx].kind {
LitKind::Int(v, _) => Some(v as u32),
_ => {
cx.tcx
.sess
.span_err(args[idx].span(), &format!("{} value must be int", name));
None
}
}
};
let depth = parse_lit(1, "depth")?;
let arrayed = parse_lit(2, "arrayed")?;
let multisampled = parse_lit(3, "multisampled")?;
let sampled = parse_lit(4, "sampled")?;
let image_format = match arg_values[5].kind {
LitKind::Str(dim, _) => match dim.with(|s| s.parse()) {
Ok(dim) => dim,
Err(()) => {
cx.tcx
.sess
.span_err(args[5].span(), "invalid image_format value");
return None;
}
},
_ => {
cx.tcx
.sess
.span_err(args[5].span(), "image_format value must be str");
return None;
}
};
let access_qualifier = if args.len() == 7 {
Some(match arg_values[6].kind {
LitKind::Str(dim, _) => match dim.with(|s| s.parse()) {
Ok(dim) => dim,
Err(()) => {
cx.tcx
.sess
.span_err(args[6].span(), "invalid access_qualifier value");
return None;
}
},
_ => {
cx.tcx
.sess
.span_err(args[6].span(), "access_qualifier value must be str");
return None;
}
})
} else {
None
};
Some(SpirvAttribute::Image {
dim,
depth,
arrayed,
multisampled,
sampled,
image_format,
access_qualifier,
})
}
fn parse_attr_int_value(cx: &CodegenCx<'_>, arg: &NestedMetaItem) -> Option<u32> {
let arg = match arg.meta_item() {
Some(arg) => arg,

View File

@ -37,8 +37,11 @@
nonstandard_style
)]
mod textures;
pub use glam;
pub use num_traits;
pub use textures::*;
#[cfg(all(not(test), target_arch = "spirv"))]
#[panic_handler]

View File

@ -0,0 +1,52 @@
use glam::{Vec2, Vec4};
#[allow(unused_attributes)]
#[spirv(sampler)]
#[derive(Copy, Clone)]
pub struct Sampler {
_x: u32,
}
#[allow(unused_attributes)]
#[spirv(image(
// sampled_type is hardcoded to f32 for now
dim = "Dim2D",
depth = 0,
arrayed = 0,
multisampled = 0,
sampled = 1,
image_format = "Unknown"
))]
#[derive(Copy, Clone)]
pub struct Image2d {
_x: u32,
}
impl Image2d {
pub fn sample(&self, sampler: Sampler, coord: Vec2) -> Vec4 {
#[cfg(not(target_arch = "spirv"))]
{
let _ = sampler;
let _ = coord;
panic!("Image sampling not supported on CPU");
}
#[cfg(target_arch = "spirv")]
unsafe {
let mut result = Default::default();
asm!(
"%typeSampledImage = OpTypeSampledImage typeof*{1}",
"%image = OpLoad typeof*{1} {1}",
"%sampler = OpLoad typeof*{2} {2}",
"%coord = OpLoad typeof*{3} {3}",
"%sampledImage = OpSampledImage %typeSampledImage %image %sampler",
"%result = OpImageSampleImplicitLod typeof*{0} %sampledImage %coord",
"OpStore {0} %result",
in(reg) &mut result,
in(reg) self,
in(reg) &sampler,
in(reg) &coord
);
result
}
}
}