Basic image sampling support

This commit is contained in:
Dzmitry Malyshau 2020-02-26 17:10:42 -05:00
parent 20be6876d7
commit 515f878fa2
5 changed files with 250 additions and 23 deletions

View File

@ -5,6 +5,7 @@ authors = ["Dzmitry Malyshau <kvarkus@gmail.com>"]
edition = "2018"
[dependencies]
bitflags = "1"
fxhash = "0.2"
log = "0.4"
spirv_headers = "1"

View File

@ -12,7 +12,11 @@ fn main() {
let mut binding_map = naga::back::msl::BindingMap::default();
binding_map.insert(
naga::back::msl::BindSource { set: 0, binding: 0 },
naga::back::msl::BindTarget { buffer: None, texture: None, sampler: None },
naga::back::msl::BindTarget { buffer: None, texture: Some(1), sampler: None },
);
binding_map.insert(
naga::back::msl::BindSource { set: 0, binding: 1 },
naga::back::msl::BindTarget { buffer: None, texture: None, sampler: Some(1) },
);
let options = naga::back::msl::Options {
binding_map: &binding_map,

View File

@ -49,6 +49,7 @@ enum ResolvedBinding {
pub enum Error {
Format(FmtError),
UnsupportedExecutionModel(spirv::ExecutionModel),
UnexpectedLocation,
MixedExecutionModels(crate::Token<crate::Function>),
MissingBinding(crate::Token<crate::GlobalVariable>),
MissingBindTarget(BindSource),
@ -66,6 +67,7 @@ enum LocationMode {
VertexInput,
FragmentOutput,
Intermediate,
Uniform,
}
pub struct Options<'a> {
@ -76,14 +78,15 @@ impl Options<'_> {
fn resolve_binding(&self, binding: &crate::Binding, mode: LocationMode) -> Result<ResolvedBinding, Error> {
match *binding {
crate::Binding::BuiltIn(built_in) => Ok(ResolvedBinding::BuiltIn(built_in)),
crate::Binding::Location(index) => Ok(match mode {
LocationMode::VertexInput => ResolvedBinding::Attribute(index),
LocationMode::FragmentOutput => ResolvedBinding::Color(index),
LocationMode::Intermediate => ResolvedBinding::User {
crate::Binding::Location(index) => match mode {
LocationMode::VertexInput => Ok(ResolvedBinding::Attribute(index)),
LocationMode::FragmentOutput => Ok(ResolvedBinding::Color(index)),
LocationMode::Intermediate => Ok(ResolvedBinding::User {
prefix: "loc",
index,
},
}),
}),
LocationMode::Uniform => Err(Error::UnexpectedLocation),
},
crate::Binding::Descriptor { set, binding } => {
let source = BindSource { set, binding };
self.binding_map
@ -116,6 +119,14 @@ impl Indexed for crate::Token<crate::StructDeclaration> {
const CLASS: &'static str = "Struct";
fn id(&self) -> usize { self.index() }
}
impl Indexed for crate::Token<crate::ImageDeclaration> {
const CLASS: &'static str = "Image";
fn id(&self) -> usize { self.index() }
}
impl Indexed for crate::Token<crate::SamplerDeclaration> {
const CLASS: &'static str = "Sampler";
fn id(&self) -> usize { self.index() }
}
impl Indexed for crate::Token<crate::GlobalVariable> {
const CLASS: &'static str = "global";
fn id(&self) -> usize { self.index() }
@ -235,6 +246,14 @@ impl<T: Display> Display for TypedVar<'_, T> {
let decl = &self.2.structs[token];
write!(formatter, "{} {}", decl.name.or_index(token), self.1)
}
crate::Type::Image(token) => {
let decl = &self.2.images[token];
write!(formatter, "{} {}", decl.name.or_index(token), self.1)
}
crate::Type::Sampler(token) => {
let decl = &self.2.samplers[token];
write!(formatter, "{} {}", decl.name.or_index(token), self.1)
}
}
}
}
@ -252,7 +271,8 @@ impl Display for TypedGlobalVariable<'_> {
let decl = &self.module.complex_types.pointers[token];
let ty = match decl.class {
spirv::StorageClass::Input |
spirv::StorageClass::Output => &decl.base,
spirv::StorageClass::Output |
spirv::StorageClass::UniformConstant => &decl.base,
_ => &var.ty
};
let tv = TypedVar(ty, &name, &self.module.complex_types);
@ -288,7 +308,7 @@ impl Display for ResolvedBinding {
if let Some(id) = target.buffer {
write!(formatter, "buffer({})", id)
} else if let Some(id) = target.texture {
write!(formatter, "buffer({})", id)
write!(formatter, "texture({})", id)
} else if let Some(id) = target.sampler {
write!(formatter, "sampler({})", id)
} else {
@ -431,6 +451,18 @@ impl<W: Write> Writer<W> {
other => panic!("Unable to infer Mul for {:?}", other),
})
}
crate::Expression::ImageSample { image, sampler, coordinate } => {
let ty_image = self.put_expression(image, expressions, module)?;
write!(self.out, ".sample(")?;
self.put_expression(sampler, expressions, module)?;
write!(self.out, ", ")?;
self.put_expression(coordinate, expressions, module)?;
write!(self.out, ")")?;
match ty_image {
crate::Type::Image(token) => Ok(module.complex_types.images[token].ty.clone()),
other => panic!("Unexpected image type {:?}", other),
}
}
ref other => panic!("Unsupported {:?}", other),
}
}
@ -442,6 +474,26 @@ impl<W: Write> Writer<W> {
// write down complex types
writeln!(self.out, "")?;
for (token, decl) in module.complex_types.images.iter() {
let name = decl.name.or_index(token);
let dim = match decl.dim {
spirv::Dim::Dim1D => "1d",
spirv::Dim::Dim2D => "2d",
spirv::Dim::Dim3D => "3d",
spirv::Dim::DimCube => "Cube",
_ => panic!("Unsupported dim {:?}", decl.dim),
};
let base = match decl.ty {
crate::Type::Scalar { kind, .. } |
crate::Type::Vector { kind, .. } => scalar_kind_string(kind),
_ => panic!("Unsupported image type {:?}", decl.ty),
};
writeln!(self.out, "typedef texture{}<{}> {};", dim, base, name)?;
}
for (token, decl) in module.complex_types.samplers.iter() {
let name = decl.name.or_index(token);
writeln!(self.out, "typedef sampler {};", name)?;
}
for (token, decl) in module.complex_types.pointers.iter() {
let name = Starred(decl.name.or_index(token));
// Input and output are always provided as pointers,
@ -450,7 +502,7 @@ impl<W: Write> Writer<W> {
let class = match decl.class {
spirv::StorageClass::Input |
spirv::StorageClass::Output => continue,
spirv::StorageClass::Uniform => "constant",
spirv::StorageClass::UniformConstant => "constant",
other => {
log::warn!("Unexpected pointer class {:?}", other);
""
@ -513,7 +565,7 @@ impl<W: Write> Writer<W> {
let (em_str, in_mode, out_mode) = match em {
spirv::ExecutionModel::Vertex => ("vertex", LocationMode::VertexInput, LocationMode::Intermediate),
spirv::ExecutionModel::Fragment => ("fragment", LocationMode::Intermediate, LocationMode::FragmentOutput),
spirv::ExecutionModel::GLCompute => ("compute", LocationMode::Intermediate, LocationMode::Intermediate),
spirv::ExecutionModel::GLCompute => ("compute", LocationMode::Uniform, LocationMode::Uniform),
_ => return Err(Error::UnsupportedExecutionModel(em)),
};
for &token in var_inputs.iter() {
@ -569,10 +621,14 @@ impl<W: Write> Writer<W> {
for (_, expr) in fun.expressions.iter() {
if let crate::Expression::GlobalVariable(token) = *expr {
let var = &module.global_variables[token];
if var.class == spirv::StorageClass::Uniform && !uniforms_used.contains(&token) {
if var.class == spirv::StorageClass::UniformConstant && !uniforms_used.contains(&token) {
uniforms_used.insert(token);
let binding = var.binding
.as_ref()
.ok_or(Error::MissingBinding(token))?;
let resolved = options.resolve_binding(binding, LocationMode::Uniform)?;
let var = TypedGlobalVariable { module, token };
writeln!(self.out, "\t{},", var)?;
writeln!(self.out, "\t{} [[{}]],", var, resolved)?;
}
}
}

View File

@ -11,6 +11,7 @@ const LAST_KNOWN_EXECUTION_MODEL: spirv::ExecutionModel = spirv::ExecutionModel:
const LAST_KNOWN_STORAGE_CLASS: spirv::StorageClass = spirv::StorageClass::StorageBuffer;
const LAST_KNOWN_DECORATION: spirv::Decoration = spirv::Decoration::NonUniformEXT;
const LAST_KNOWN_BUILT_IN: spirv::BuiltIn = spirv::BuiltIn::FullyCoveredEXT;
const LAST_KNOWN_DIM: spirv::Dim = spirv::Dim::DimSubpassData;
pub const SUPPORTED_CAPABILITIES: &[spirv::Capability] = &[
spirv::Capability::Shader,
@ -35,6 +36,7 @@ pub enum ParseError {
UnsupportedExecutionModel(u32),
UnsupportedStorageClass(u32),
UnsupportedFunctionControl(u32),
UnsupportedDim(u32),
InvalidParameter(spirv::Op),
InvalidOperandCount(spirv::Op, u16),
InvalidOperand,
@ -205,6 +207,12 @@ struct LookupExpression {
type_id: spirv::Word,
}
#[derive(Clone, Debug)]
struct LookupSampledImage {
image: Token<crate::Expression>,
sampler: Token<crate::Expression>,
}
pub struct Parser<I> {
data: I,
state: ModuleState,
@ -216,6 +224,7 @@ pub struct Parser<I> {
lookup_constant: FastHashMap<spirv::Word, LookupConstant>,
lookup_variable: FastHashMap<spirv::Word, LookupVariable>,
lookup_expression: FastHashMap<spirv::Word, LookupExpression>,
lookup_sampled_image: FastHashMap<spirv::Word, LookupSampledImage>,
lookup_function_type: FastHashMap<spirv::Word, LookupFunctionType>,
lookup_function: FastHashMap<spirv::Word, Token<crate::Function>>,
}
@ -233,6 +242,7 @@ impl<I: Iterator<Item = u32>> Parser<I> {
lookup_constant: FastHashMap::default(),
lookup_variable: FastHashMap::default(),
lookup_expression: FastHashMap::default(),
lookup_sampled_image: FastHashMap::default(),
lookup_function_type: FastHashMap::default(),
lookup_function: FastHashMap::default(),
}
@ -571,6 +581,44 @@ impl<I: Iterator<Item = u32>> Parser<I> {
type_id: result_type_id,
});
}
Op::SampledImage => {
inst.expect(5)?;
let _result_type_id = self.next()?;
let result_id = self.next()?;
let image_id = self.next()?;
let sampler_id = self.next()?;
let image_lexp = self.lookup_expression.lookup(image_id)?;
let sampler_lexp = self.lookup_expression.lookup(sampler_id)?;
//TODO: compare the result type
self.lookup_sampled_image.insert(result_id, LookupSampledImage {
image: image_lexp.token,
sampler: sampler_lexp.token,
});
}
Op::ImageSampleImplicitLod => {
inst.expect_at_least(5)?;
let result_type_id = self.next()?;
let result_id = self.next()?;
let sampled_image_id = self.next()?;
let coordinate_id = self.next()?;
let si_lexp = self.lookup_sampled_image.lookup(sampled_image_id)?;
let coord_lexp = self.lookup_expression.lookup(coordinate_id)?;
match self.lookup_type.lookup(coord_lexp.type_id)?.value {
crate::Type::Scalar { kind: crate::ScalarKind::Float, .. } |
crate::Type::Vector { kind: crate::ScalarKind::Float, .. } => (),
ref other => return Err(ParseError::UnsupportedType(other.clone())),
}
//TODO: compare the result type
let expr = crate::Expression::ImageSample {
image: si_lexp.image,
sampler: si_lexp.sampler,
coordinate: coord_lexp.token,
};
self.lookup_expression.insert(result_id, LookupExpression {
token: fun.expressions.append(expr),
type_id: result_type_id,
});
}
_ => return Err(ParseError::UnsupportedInstruction(self.state, inst.op)),
}
}
@ -626,6 +674,8 @@ impl<I: Iterator<Item = u32>> Parser<I> {
pointers: Storage::new(),
arrays: Storage::new(),
structs: Storage::new(),
images: Storage::new(),
samplers: Storage::new(),
},
global_variables: Storage::new(),
functions: Storage::new(),
@ -873,14 +923,14 @@ impl<I: Iterator<Item = u32>> Parser<I> {
let id = self.next()?;
let storage = self.next()?;
let type_id = self.next()?;
let pd = crate::PointerDeclaration {
let decl = crate::PointerDeclaration {
name: self.future_decor
.remove(&id)
.and_then(|dec| dec.name),
base: self.lookup_type.lookup(type_id)?.value.clone(),
class: map_storage_class(storage)?,
};
let token = module.complex_types.pointers.append(pd);
let token = module.complex_types.pointers.append(decl);
self.lookup_type.insert(id, LookupType {
value: crate::Type::Pointer(token),
base_id: Some(type_id),
@ -892,14 +942,14 @@ impl<I: Iterator<Item = u32>> Parser<I> {
let id = self.next()?;
let type_id = self.next()?;
let length = self.next()?;
let ad = crate::ArrayDeclaration {
let decl = crate::ArrayDeclaration {
name: self.future_decor
.remove(&id)
.and_then(|dec| dec.name),
base: self.lookup_type.lookup(type_id)?.value.clone(),
length,
};
let token = module.complex_types.arrays.append(ad);
let token = module.complex_types.arrays.append(decl);
self.lookup_type.insert(id, LookupType {
value: crate::Type::Array(token),
base_id: Some(type_id),
@ -909,7 +959,7 @@ impl<I: Iterator<Item = u32>> Parser<I> {
self.switch(ModuleState::Type, inst.op)?;
inst.expect_at_least(2)?;
let id = self.next()?;
let mut sd = crate::StructDeclaration {
let mut decl = crate::StructDeclaration {
name: self.future_decor
.remove(&id)
.and_then(|dec| dec.name),
@ -919,22 +969,104 @@ impl<I: Iterator<Item = u32>> Parser<I> {
let type_id = self.next()?;
let ty = self.lookup_type.lookup(type_id)?.value.clone();
self.lookup_member_type_id.insert((id, i), type_id);
let dec = self.future_member_decor
let decor = self.future_member_decor
.remove(&(id, i))
.unwrap_or_default();
let binding = dec.get_binding();
sd.members.push(crate::StructMember {
name: dec.name,
let binding = decor.get_binding();
decl.members.push(crate::StructMember {
name: decor.name,
binding,
ty,
});
}
let token = module.complex_types.structs.append(sd);
let token = module.complex_types.structs.append(decl);
self.lookup_type.insert(id, LookupType {
value: crate::Type::Struct(token),
base_id: None,
});
}
Op::TypeImage => {
self.switch(ModuleState::Type, inst.op)?;
inst.expect_at_least(9)?;
let id = self.next()?;
let sample_type_id = self.next()?;
let dim = self.next()?;
let mut flags = crate::ImageFlags::empty();
let _is_depth = self.next()?;
if self.next()? != 0 {
flags |= crate::ImageFlags::ARRAYED;
}
if self.next()? != 0 {
flags |= crate::ImageFlags::MULTISAMPLED;
}
let is_sampled = self.next()?;
let _format = self.next()?;
let access = if inst.wc > 9 {
inst.expect(10)?;
self.next()?
} else if is_sampled == 1 {
0 // read-only
} else {
2 // read-write
};
if access == 0 || access == 2 {
flags |= crate::ImageFlags::READABLE;
}
if access == 1 || access == 2 {
flags |= crate::ImageFlags::WRITABLE;
}
let decor = self.future_decor
.remove(&id)
.unwrap_or_default();
let binding = decor.get_binding();
let decl = crate::ImageDeclaration {
name: decor.name,
binding,
ty: self.lookup_type.lookup(sample_type_id)?.value.clone(),
dim: if dim > LAST_KNOWN_DIM as u32 {
return Err(ParseError::UnsupportedDim(dim));
} else {
unsafe { std::mem::transmute(dim) }
},
flags,
};
let token = module.complex_types.images.append(decl);
self.lookup_type.insert(id, LookupType {
value: crate::Type::Image(token),
base_id: Some(sample_type_id),
});
}
Op::TypeSampledImage => {
self.switch(ModuleState::Type, inst.op)?;
inst.expect(3)?;
let id = self.next()?;
let image_id = self.next()?;
self.lookup_type.insert(id, LookupType {
value: self.lookup_type.lookup(image_id)?.value.clone(),
base_id: Some(image_id),
});
}
Op::TypeSampler => {
self.switch(ModuleState::Type, inst.op)?;
inst.expect(2)?;
let id = self.next()?;
let decor = self.future_decor
.remove(&id)
.unwrap_or_default();
let binding = decor.get_binding();
let decl = crate::SamplerDeclaration {
name: decor.name,
binding,
};
let token = module.complex_types.samplers.append(decl);
self.lookup_type.insert(id, LookupType {
value: crate::Type::Sampler(token),
base_id: None,
});
}
Op::Constant |
Op::SpecConstant => {
self.switch(ModuleState::Type, inst.op)?;
@ -1119,6 +1251,7 @@ impl<I: Iterator<Item = u32>> Parser<I> {
let token = module.functions.append(fun);
self.lookup_function.insert(fun_id, token);
self.lookup_expression.clear();
self.lookup_sampled_image.clear();
}
_ => return Err(ParseError::UnsupportedInstruction(self.state, inst.op))
//TODO

View File

@ -66,6 +66,30 @@ pub struct StructDeclaration {
pub members: Vec<StructMember>,
}
bitflags::bitflags! {
pub struct ImageFlags: u32 {
const ARRAYED = 0x1;
const MULTISAMPLED = 0x2;
const READABLE = 0x4;
const WRITABLE = 0x8;
}
}
#[derive(Debug)]
pub struct ImageDeclaration {
pub name: Option<String>,
pub binding: Option<Binding>,
pub ty: Type,
pub dim: spirv::Dim,
pub flags: ImageFlags,
}
#[derive(Debug)]
pub struct SamplerDeclaration {
pub name: Option<String>,
pub binding: Option<Binding>,
}
#[derive(Clone, Debug)]
pub enum Type {
Void,
@ -75,6 +99,8 @@ pub enum Type {
Pointer(Token<PointerDeclaration>),
Array(Token<ArrayDeclaration>),
Struct(Token<StructDeclaration>),
Image(Token<ImageDeclaration>),
Sampler(Token<SamplerDeclaration>),
}
#[derive(Clone, Debug)]
@ -120,6 +146,11 @@ pub enum Expression {
pointer: Token<Expression>,
},
Mul(Token<Expression>, Token<Expression>),
ImageSample {
image: Token<Expression>,
sampler: Token<Expression>,
coordinate: Token<Expression>,
},
}
pub type Block = Vec<Statement>;
@ -173,6 +204,8 @@ pub struct ComplexTypes {
pub pointers: Storage<PointerDeclaration>,
pub arrays: Storage<ArrayDeclaration>,
pub structs: Storage<StructDeclaration>,
pub images: Storage<ImageDeclaration>,
pub samplers: Storage<SamplerDeclaration>,
}
#[derive(Debug)]