mirror of
https://github.com/gfx-rs/wgpu.git
synced 2025-02-23 04:13:49 +00:00
Basic image sampling support
This commit is contained in:
parent
20be6876d7
commit
515f878fa2
@ -5,6 +5,7 @@ authors = ["Dzmitry Malyshau <kvarkus@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
bitflags = "1"
|
||||
fxhash = "0.2"
|
||||
log = "0.4"
|
||||
spirv_headers = "1"
|
||||
|
@ -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,
|
||||
|
@ -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)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
33
src/lib.rs
33
src/lib.rs
@ -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)]
|
||||
|
Loading…
Reference in New Issue
Block a user