Add Const Generic Image Type (#359)

* Add parameterized Image type

* nits

* Update crates/spirv-std/src/lib.rs

* Update crates/rustc_codegen_spirv/src/symbols.rs

* Update crates/rustc_codegen_spirv/src/symbols.rs

* Update symbols.rs
This commit is contained in:
XAMPPRocky 2021-04-28 09:47:40 +02:00 committed by GitHub
parent 4c8e50d943
commit f88ae5b28a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 1920 additions and 123 deletions

48
Cargo.lock generated
View File

@ -403,7 +403,6 @@ name = "compute-shader"
version = "0.4.0-alpha.5"
dependencies = [
"spirv-std",
"spirv-std-macros",
]
[[package]]
@ -792,7 +791,7 @@ checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8"
dependencies = [
"cfg-if 1.0.0",
"libc",
"redox_syscall 0.2.5",
"redox_syscall 0.2.6",
"winapi 0.3.9",
]
@ -992,9 +991,9 @@ dependencies = [
[[package]]
name = "gfx-backend-dx12"
version = "0.7.0"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36dc6ba2b7647e2c2b27b8f74ff5ccdd53c703776588eee5b1de515fdcbd6bc9"
checksum = "5032d716a2a5f4dafb4675a794c5dc32081af8fbc7303c93ad93ff5413c6559f"
dependencies = [
"arrayvec",
"bit-set",
@ -1008,6 +1007,7 @@ dependencies = [
"raw-window-handle",
"smallvec",
"spirv_cross",
"thunderdome",
"winapi 0.3.9",
]
@ -1262,9 +1262,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
[[package]]
name = "jobserver"
version = "0.1.21"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2"
checksum = "972f5ae5d1cb9c6ae417789196c803205313edde988685da5e3aae0827b9e7fd"
dependencies = [
"libc",
]
@ -1728,7 +1728,7 @@ checksum = "0c976c5018e7f1db4359616d8b31ef8ae7d9649b11803c0b38fff67fd2999fc8"
dependencies = [
"libc",
"raw-window-handle",
"redox_syscall 0.2.5",
"redox_syscall 0.2.6",
"sdl2",
"sdl2-sys",
"wasm-bindgen",
@ -1779,7 +1779,7 @@ dependencies = [
"cfg-if 1.0.0",
"instant",
"libc",
"redox_syscall 0.2.5",
"redox_syscall 0.2.6",
"smallvec",
"winapi 0.3.9",
]
@ -1855,9 +1855,9 @@ checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
[[package]]
name = "pretty_assertions"
version = "0.7.1"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f297542c27a7df8d45de2b0e620308ab883ad232d06c14b76ac3e144bda50184"
checksum = "1cab0e7c02cf376875e9335e0ba1da535775beb5450d21e1dffca068818ed98b"
dependencies = [
"ansi_term 0.12.1",
"ctor",
@ -2022,9 +2022,9 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
[[package]]
name = "redox_syscall"
version = "0.2.5"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9"
checksum = "8270314b5ccceb518e7e578952f0b72b88222d02e8f77f5ecf7abbb673539041"
dependencies = [
"bitflags",
]
@ -2036,7 +2036,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
dependencies = [
"getrandom",
"redox_syscall 0.2.5",
"redox_syscall 0.2.6",
]
[[package]]
@ -2078,12 +2078,12 @@ dependencies = [
[[package]]
name = "rspirv"
version = "0.7.0"
source = "git+https://github.com/gfx-rs/rspirv.git?rev=719cf08#719cf08e4af0436242707479e3509add5ec3d514"
source = "git+https://github.com/gfx-rs/rspirv?rev=719cf08#719cf08e4af0436242707479e3509add5ec3d514"
dependencies = [
"derive_more",
"fxhash",
"num-traits",
"spirv_headers 1.5.0 (git+https://github.com/gfx-rs/rspirv.git?rev=719cf08)",
"spirv_headers 1.5.0 (git+https://github.com/gfx-rs/rspirv?rev=719cf08)",
]
[[package]]
@ -2267,7 +2267,6 @@ version = "0.4.0-alpha.5"
dependencies = [
"glam",
"spirv-std",
"spirv-std-macros",
]
[[package]]
@ -2282,7 +2281,6 @@ version = "0.4.0-alpha.5"
dependencies = [
"shared",
"spirv-std",
"spirv-std-macros",
]
[[package]]
@ -2291,14 +2289,13 @@ version = "0.4.0-alpha.5"
dependencies = [
"shared",
"spirv-std",
"spirv-std-macros",
]
[[package]]
name = "slab"
version = "0.4.2"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527"
[[package]]
name = "slotmap"
@ -2351,14 +2348,17 @@ dependencies = [
"bitflags",
"num-traits",
"spirv-std-macros",
"spirv-types",
]
[[package]]
name = "spirv-std-macros"
version = "0.4.0-alpha.5"
dependencies = [
"heck",
"proc-macro2",
"quote",
"spirv-types",
"syn",
]
@ -2382,6 +2382,10 @@ dependencies = [
"cc",
]
[[package]]
name = "spirv-types"
version = "0.4.0-alpha.5"
[[package]]
name = "spirv_cross"
version = "0.23.1"
@ -2396,7 +2400,7 @@ dependencies = [
[[package]]
name = "spirv_headers"
version = "1.5.0"
source = "git+https://github.com/gfx-rs/rspirv.git?rev=719cf08#719cf08e4af0436242707479e3509add5ec3d514"
source = "git+https://github.com/gfx-rs/rspirv?rev=719cf08#719cf08e4af0436242707479e3509add5ec3d514"
dependencies = [
"bitflags",
"num-traits",
@ -2515,7 +2519,7 @@ dependencies = [
"cfg-if 1.0.0",
"libc",
"rand",
"redox_syscall 0.2.5",
"redox_syscall 0.2.6",
"remove_dir_all",
"winapi 0.3.9",
]

View File

@ -12,6 +12,8 @@ members = [
"crates/rustc_codegen_spirv",
"crates/spirv-builder",
"crates/spirv-std",
"crates/spirv-std/shared",
"crates/spirv-std/macros",
"tests",
"tests/deps-helper",
@ -34,7 +36,7 @@ codegen-units = 256
[patch.crates-io]
spirv-std = { path = "./crates/spirv-std" }
spirv-std-macros = { path = "./crates/spirv-std-macros" }
spirv-std-macros = { path = "./crates/spirv-std/macros" }
# TODO: Remove once next version is released - needed to include these two PRs:
# * Manishearth/compiletest-rs#240 (for handling SPIR-V extension across platforms)
# * Manishearth/compiletest-rs#241 (for the `$TEST_BUILD_DIR` path normalization)

View File

@ -10,7 +10,9 @@ use rustc_errors::ErrorReported;
use rustc_middle::bug;
use rustc_middle::ty::layout::{FnAbiExt, TyAndLayout};
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{GeneratorSubsts, PolyFnSig, Ty, TyKind, TypeAndMut};
use rustc_middle::ty::{
Const, FloatTy, GeneratorSubsts, IntTy, ParamEnv, PolyFnSig, Ty, TyKind, TypeAndMut, UintTy,
};
use rustc_span::def_id::DefId;
use rustc_span::Span;
use rustc_target::abi::call::{CastTarget, FnAbi, PassMode, Reg, RegKind};
@ -21,6 +23,9 @@ use std::cell::RefCell;
use std::collections::hash_map::Entry;
use std::fmt;
use num_traits::cast::FromPrimitive;
use rspirv::spirv;
/// If a struct contains a pointer to itself, even indirectly, then doing a naiive recursive walk
/// of the fields will result in an infinite loop. Because pointers are the only thing that are
/// allowed to be recursive, keep track of what pointers we've translated, or are currently in the
@ -768,6 +773,94 @@ fn trans_intrinsic_type<'tcx>(
}
// Hardcode to float for now
let sampled_type = SpirvType::Float(32).def(span, cx);
let ty = SpirvType::Image {
sampled_type,
dim,
depth,
arrayed,
multisampled,
sampled,
image_format,
access_qualifier,
};
Ok(ty.def(span, cx))
}
IntrinsicType::GenericImageType => {
// see SpirvType::sizeof
if ty.size != Size::from_bytes(4) {
cx.tcx
.sess
.err("#[spirv(generic_image)] type must have size 4");
return Err(ErrorReported);
}
fn type_from_variant_discriminant<'tcx, P: FromPrimitive>(
cx: &CodegenCx<'tcx>,
const_: &'tcx Const<'tcx>,
) -> P {
let adt_def = const_.ty.ty_adt_def().unwrap();
assert!(adt_def.is_enum());
let destructured = cx.tcx.destructure_const(ParamEnv::reveal_all().and(const_));
let idx = destructured.variant.unwrap();
let value = const_.ty.discriminant_for_variant(cx.tcx, idx).unwrap().val as u64;
<_>::from_u64(value).unwrap()
}
let sampled_type = match substs.type_at(0).kind() {
TyKind::Int(int) => match int {
IntTy::Isize => {
SpirvType::Integer(cx.tcx.data_layout.pointer_size.bits() as u32, true)
.def(span, cx)
}
IntTy::I8 => SpirvType::Integer(8, true).def(span, cx),
IntTy::I16 => SpirvType::Integer(16, true).def(span, cx),
IntTy::I32 => SpirvType::Integer(32, true).def(span, cx),
IntTy::I64 => SpirvType::Integer(64, true).def(span, cx),
IntTy::I128 => SpirvType::Integer(128, true).def(span, cx),
},
TyKind::Uint(uint) => match uint {
UintTy::Usize => {
SpirvType::Integer(cx.tcx.data_layout.pointer_size.bits() as u32, false)
.def(span, cx)
}
UintTy::U8 => SpirvType::Integer(8, false).def(span, cx),
UintTy::U16 => SpirvType::Integer(16, false).def(span, cx),
UintTy::U32 => SpirvType::Integer(32, false).def(span, cx),
UintTy::U64 => SpirvType::Integer(64, false).def(span, cx),
UintTy::U128 => SpirvType::Integer(128, false).def(span, cx),
},
TyKind::Float(FloatTy::F32) => SpirvType::Float(32).def(span, cx),
TyKind::Float(FloatTy::F64) => SpirvType::Float(64).def(span, cx),
_ => {
cx.tcx
.sess
.span_err(span, "Invalid sampled type to `Image`.");
return Err(ErrorReported);
}
};
let dim: spirv::Dim = type_from_variant_discriminant(cx, substs.const_at(1));
let depth: u32 = type_from_variant_discriminant(cx, substs.const_at(2));
let arrayed: u32 = type_from_variant_discriminant(cx, substs.const_at(3));
let multisampled: u32 = type_from_variant_discriminant(cx, substs.const_at(4));
let sampled: u32 = type_from_variant_discriminant(cx, substs.const_at(5));
let image_format: spirv::ImageFormat =
type_from_variant_discriminant(cx, substs.const_at(6));
let access_qualifier = {
let option = cx
.tcx
.destructure_const(ParamEnv::reveal_all().and(substs.const_at(7)));
match option.variant.map(|i| i.as_u32()).unwrap_or(0) {
0 => None,
1 => Some(type_from_variant_discriminant(cx, option.fields[0])),
_ => unreachable!(),
}
};
let ty = SpirvType::Image {
sampled_type,
dim,

View File

@ -61,6 +61,7 @@ impl From<ExecutionModel> for Entry {
/// `struct` types that are used to represent special SPIR-V types.
#[derive(Debug, Clone)]
pub enum IntrinsicType {
GenericImageType,
ImageType {
dim: Dim,
depth: u32,

View File

@ -317,6 +317,10 @@ impl Symbols {
"sampler",
SpirvAttribute::IntrinsicType(IntrinsicType::Sampler),
),
(
"generic_image_type",
SpirvAttribute::IntrinsicType(IntrinsicType::GenericImageType),
),
(
"acceleration_structure",
SpirvAttribute::IntrinsicType(IntrinsicType::AccelerationStructureKhr),

View File

@ -204,7 +204,7 @@ fn invoke_rustc(builder: &SpirvBuilder, multimodule: bool) -> Result<PathBuf, Sp
.unwrap_or_default();
let rustflags = format!(
"-Z codegen-backend={} -Z symbol-mangling-version=v0{}",
"-Z codegen-backend={} -Zsymbol-mangling-version=v0{}",
rustc_codegen_spirv.display(),
llvm_args,
);

View File

@ -10,7 +10,8 @@ description = "Standard functions and types for SPIR-V"
[dependencies]
bitflags = "1.2.1"
num-traits = { version = "0.2.14", default-features = false, features = ["libm"] }
spirv-std-macros = { path = "../spirv-std-macros", version = "0.4.0-alpha.0" }
spirv-types = { path = "./shared", version = "0.4.0-alpha.3" }
spirv-std-macros = { path = "./macros", version = "0.4.0-alpha.3" }
[features]
default = []

View File

@ -11,6 +11,8 @@ description = "Macros for spirv-std"
proc-macro = true
[dependencies]
spirv-types = { path = "../shared", version = "0.4.0-alpha.3" }
heck = "0.3.2"
proc-macro2 = "1.0.24"
quote = "1.0.8"
syn = { version = "1.0.58", features=["full"] }

View File

@ -0,0 +1,554 @@
use proc_macro2::Ident;
use quote::{quote, TokenStreamExt};
use spirv_types::image_params::*;
use syn::parse::{Parse, ParseStream};
use self::params::SampledType;
mod kw {
syn::custom_keyword!(u8);
syn::custom_keyword!(u16);
syn::custom_keyword!(u32);
syn::custom_keyword!(u64);
syn::custom_keyword!(i8);
syn::custom_keyword!(i16);
syn::custom_keyword!(i32);
syn::custom_keyword!(i64);
syn::custom_keyword!(f32);
syn::custom_keyword!(f64);
}
const MISSING_SAMPLE_ERROR: &str = "Expected either `type` or `format` to be \
specified. Use `type=<sampled_type>` (e.g. `type=f32`) to specify the sampled \
type, or use `format` to set the image to a specific image format.";
/// Creates an `Image` type using the following syntax.
pub struct ImageType {
access_qualifier: Option<AccessQualifier>,
arrayed: Arrayed,
crate_root: Option<syn::Path>,
depth: ImageDepth,
dimensionality: Dimensionality,
format: ImageFormat,
multisampled: Multisampled,
sampled: Sampled,
sampled_type: SampledType,
}
impl Parse for ImageType {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let mut access_qualifier = None;
let mut sampled_type = None;
let mut dimensionality = None;
let mut arrayed = None;
let mut depth: Option<ImageDepth> = None;
let mut format = None;
let mut multisampled = None;
let mut sampled: Option<Sampled> = None;
let mut crate_root = None;
let starting_span = input.span();
macro_rules! set_unique {
($id:ident = $ex:expr) => {{
if $id.replace($ex).is_some() {
return Err(syn::Error::new(
input.span(),
concat!("Unexpected duplicate parameter for `", stringify!($id), "`"),
));
}
}};
}
macro_rules! peek_and_eat_value {
($typ:ty) => {{
if input.peek(syn::Token![=]) {
input.parse::<syn::Token![=]>()?;
Some(input.parse::<$typ>()?)
} else {
None
}
}}
}
while !input.is_empty() {
if input.peek(syn::LitInt) {
let int = input.parse::<syn::LitInt>().unwrap();
set_unique!(
dimensionality = match (int.base10_digits(), int.suffix()) {
("1", "D") | ("1", "d") => Dimensionality::OneD,
("2", "D") | ("2", "d") => Dimensionality::TwoD,
("3", "D") | ("3", "d") => Dimensionality::ThreeD,
_ => return Err(syn::Error::new(int.span(), "Unexpected integer")),
}
);
} else if input.peek(syn::Ident) {
let ident = input.parse::<Ident>().unwrap();
if ident == "access" {
let value = peek_and_eat_value!(syn::Ident)
.as_ref()
.map(|i| params::access_qualifier_from_str(&i.to_string()));
if value.is_none() {
return Err(syn::Error::new(
ident.span(),
"Expected argument for `access`.",
));
}
access_qualifier = value.unwrap().ok();
} else if ident == "buffer" {
set_unique!(dimensionality = Dimensionality::Buffer);
} else if ident == "cube" {
set_unique!(dimensionality = Dimensionality::Cube);
} else if ident == "rect" {
set_unique!(dimensionality = Dimensionality::Rect);
} else if ident == "subpass" {
set_unique!(dimensionality = Dimensionality::SubpassData);
} else if ident == "arrayed" {
set_unique!(
arrayed = peek_and_eat_value!(syn::LitBool)
.as_ref()
.map(syn::LitBool::value)
.map_or(Arrayed::True, From::from)
);
} else if ident == "multisampled" {
set_unique!(
multisampled = peek_and_eat_value!(syn::LitBool)
.as_ref()
.map(syn::LitBool::value)
.map_or(Multisampled::True, From::from)
);
} else if ident == "sampled" {
set_unique!(
sampled = peek_and_eat_value!(syn::LitBool)
.as_ref()
.map(syn::LitBool::value)
.map_or(Sampled::Yes, From::from)
);
} else if ident == "depth" {
set_unique!(
depth = peek_and_eat_value!(syn::LitBool)
.as_ref()
.map(syn::LitBool::value)
.map_or(ImageDepth::True, From::from)
);
} else if ident == "format" {
let value = peek_and_eat_value!(syn::Ident);
if value.is_none() {
return Err(syn::Error::new(
ident.span(),
"Expected argument for `format`.",
));
}
let value = params::image_format_from_str(&value.unwrap().to_string());
if let Err(err) = value {
return Err(syn::Error::new(ident.span(), err));
}
format = value.ok();
} else if ident == "__crate_root" {
input.parse::<syn::Token![=]>()?;
crate_root = Some(input.parse::<syn::Path>()?);
}
} else if input.peek(syn::token::Type) {
input.parse::<syn::token::Type>()?;
input.parse::<syn::Token![=]>()?;
sampled_type = Some(if input.peek(kw::u8) {
input.parse::<kw::u8>()?;
SampledType::U8
} else if input.peek(kw::u16) {
input.parse::<kw::u16>()?;
SampledType::U16
} else if input.peek(kw::u32) {
input.parse::<kw::u32>()?;
SampledType::U32
} else if input.peek(kw::u64) {
input.parse::<kw::u64>()?;
SampledType::U64
} else if input.peek(kw::i8) {
input.parse::<kw::i8>()?;
SampledType::I8
} else if input.peek(kw::i16) {
input.parse::<kw::i16>()?;
SampledType::I16
} else if input.peek(kw::i32) {
input.parse::<kw::i32>()?;
SampledType::I32
} else if input.peek(kw::i64) {
input.parse::<kw::i64>()?;
SampledType::I64
} else if input.peek(kw::f32) {
input.parse::<kw::f32>()?;
SampledType::F32
} else if input.peek(kw::f64) {
input.parse::<kw::f64>()?;
SampledType::F64
} else {
return Err(syn::Error::new(
input.span(),
"Unknown value provided to `unknown(_)`.",
));
});
}
if input.peek(syn::Token![,]) {
input.parse::<syn::Token![,]>()?;
continue;
} else {
break;
}
}
if !input.is_empty() {
return Err(syn::Error::new(
input.span(),
"Unexpected trailing arguments.",
));
}
let dimensionality = dimensionality.ok_or_else(|| {
syn::Error::new(
starting_span,
"Expected either `1D`, `2D`, `3D`, `cube`, `rect`, `buffer`, \
or `subpass` to be present",
)
})?;
if format.is_some() && sampled_type.is_some() {
if format != Some(ImageFormat::Unknown) {
return Err(syn::Error::new(
starting_span,
"Can't specify `type` with a known image format. Either \
specify just the `format` or use `format=unknown`.",
));
}
} else if sampled_type.is_some() {
format = Some(ImageFormat::Unknown);
} else if let Some(format) = &format {
sampled_type = Some(match format {
ImageFormat::Rgba32f
| ImageFormat::Rgba16f
| ImageFormat::R32f
| ImageFormat::Rgba8
| ImageFormat::Rgba8Snorm
| ImageFormat::Rg32f
| ImageFormat::Rg16f
| ImageFormat::R11fG11fB10f
| ImageFormat::R16f
| ImageFormat::Rgba16
| ImageFormat::Rgb10A2
| ImageFormat::Rg16
| ImageFormat::Rg8
| ImageFormat::R16
| ImageFormat::R8
| ImageFormat::Rgba16Snorm
| ImageFormat::Rg16Snorm
| ImageFormat::Rg8Snorm
| ImageFormat::R16Snorm
| ImageFormat::R8Snorm => SampledType::F32,
ImageFormat::Rgba32i
| ImageFormat::Rgba16i
| ImageFormat::Rgba8i
| ImageFormat::R32i
| ImageFormat::Rg32i
| ImageFormat::Rg16i
| ImageFormat::Rg8i
| ImageFormat::R16i
| ImageFormat::R8i => SampledType::I32,
ImageFormat::Rgba32ui
| ImageFormat::Rgba16ui
| ImageFormat::Rgba8ui
| ImageFormat::R32ui
| ImageFormat::Rgb10A2ui
| ImageFormat::Rg32ui
| ImageFormat::Rg16ui
| ImageFormat::Rg8ui
| ImageFormat::R16ui
| ImageFormat::R8ui => SampledType::U32,
ImageFormat::R64ui => SampledType::U64,
ImageFormat::R64i => SampledType::I64,
ImageFormat::Unknown => unreachable!(),
});
}
let sampled_type =
sampled_type.ok_or_else(|| syn::Error::new(starting_span, MISSING_SAMPLE_ERROR))?;
let format = format.ok_or_else(|| syn::Error::new(starting_span, MISSING_SAMPLE_ERROR))?;
let depth = depth.unwrap_or(ImageDepth::Unknown);
let arrayed = arrayed.unwrap_or(Arrayed::False);
let multisampled = multisampled.unwrap_or(Multisampled::False);
let sampled = sampled.unwrap_or(Sampled::Unknown);
Ok(Self {
access_qualifier,
arrayed,
crate_root,
depth,
dimensionality,
format,
multisampled,
sampled,
sampled_type,
})
}
}
impl quote::ToTokens for ImageType {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let crate_root = self.crate_root.clone().unwrap_or_else(|| syn::Path {
leading_colon: None,
segments: {
let mut punct = syn::punctuated::Punctuated::new();
punct.push(Ident::new("spirv_std", proc_macro2::Span::mixed_site()).into());
punct
},
});
let access_qualifier = match self.access_qualifier {
Some(aq) => {
let aq = params::access_qualifier_to_tokens(&aq);
quote!(Some(#crate_root::image::#aq))
}
None => quote!(None),
};
let dimensionality = params::dimensionality_to_tokens(&self.dimensionality);
let arrayed = params::arrayed_to_tokens(&self.arrayed);
let depth = params::image_depth_to_tokens(&self.depth);
let format = params::image_format_to_tokens(&self.format);
let multisampled = params::multisampled_to_tokens(&self.multisampled);
let sampled = params::sampled_to_tokens(&self.sampled);
let sampled_type = &self.sampled_type;
tokens.append_all(quote::quote! {
#crate_root::image::Image<
#crate_root::image::__private::#sampled_type,
{ #crate_root::image::#dimensionality },
{ #crate_root::image::#depth },
{ #crate_root::image::#arrayed },
{ #crate_root::image::#multisampled },
{ #crate_root::image::#sampled },
{ #crate_root::image::#format },
{ #access_qualifier },
>
})
}
}
mod params {
use super::*;
use proc_macro2::TokenStream;
pub fn access_qualifier_from_str(s: &str) -> Result<AccessQualifier, &'static str> {
match s {
"read" => Ok(AccessQualifier::ReadOnly),
"write" => Ok(AccessQualifier::WriteOnly),
"read_write" => Ok(AccessQualifier::ReadWrite),
_ => Err("Invalid access qualifier."),
}
}
pub fn image_format_from_str(s: &str) -> Result<ImageFormat, &'static str> {
Ok(match s {
"rgba32f" => ImageFormat::Rgba32f,
"rgba16f" => ImageFormat::Rgba16f,
"r32f" => ImageFormat::R32f,
"rgba8" => ImageFormat::Rgba8,
"rgba8_snorm" => ImageFormat::Rgba8Snorm,
"rg32f" => ImageFormat::Rg32f,
"rg16f" => ImageFormat::Rg16f,
"r11f_g11f_b10f" => ImageFormat::R11fG11fB10f,
"r16f" => ImageFormat::R16f,
"rgba16" => ImageFormat::Rgba16,
"rgb10_a2" => ImageFormat::Rgb10A2,
"rg16" => ImageFormat::Rg16,
"rg8" => ImageFormat::Rg8,
"r16" => ImageFormat::R16,
"r8" => ImageFormat::R8,
"rgba16_snorm" => ImageFormat::Rgba16Snorm,
"rg16_snorm" => ImageFormat::Rg16Snorm,
"rg8_snorm" => ImageFormat::Rg8Snorm,
"r16_snorm" => ImageFormat::R16Snorm,
"r8_snorm" => ImageFormat::R8Snorm,
"rgba32i" => ImageFormat::Rgba32i,
"rgba16i" => ImageFormat::Rgba16i,
"rgba8i" => ImageFormat::Rgba8i,
"r32i" => ImageFormat::R32i,
"rg32i" => ImageFormat::Rg32i,
"rg16i" => ImageFormat::Rg16i,
"rg8i" => ImageFormat::Rg8i,
"r16i" => ImageFormat::R16i,
"r8i" => ImageFormat::R8i,
"rgba32ui" => ImageFormat::Rgba32ui,
"rgba16ui" => ImageFormat::Rgba16ui,
"rgba8ui" => ImageFormat::Rgba8ui,
"r32ui" => ImageFormat::R32ui,
"rgb10_a2ui" => ImageFormat::Rgb10A2ui,
"rg32ui" => ImageFormat::Rg32ui,
"rg16ui" => ImageFormat::Rg16ui,
"rg8ui" => ImageFormat::Rg8ui,
"r16ui" => ImageFormat::R16ui,
"r8ui" => ImageFormat::R8ui,
"r64ui" => ImageFormat::R64ui,
"r64i" => ImageFormat::R64i,
_ => return Err(
"Unknown specified image format. Use `type=<type>` instead if this is intentional.",
),
})
}
/// The sampled type of an unknown image format.
pub enum SampledType {
U8,
U16,
U32,
U64,
I8,
I16,
I32,
I64,
F32,
F64,
}
impl quote::ToTokens for SampledType {
fn to_tokens(&self, stream: &mut TokenStream) {
stream.append_all(match self {
Self::U8 => quote!(u8),
Self::U16 => quote!(u16),
Self::U32 => quote!(u32),
Self::U64 => quote!(u64),
Self::I8 => quote!(i8),
Self::I16 => quote!(i16),
Self::I32 => quote!(i32),
Self::I64 => quote!(i64),
Self::F32 => quote!(f32),
Self::F64 => quote!(f64),
});
}
}
pub fn access_qualifier_to_tokens(aq: &AccessQualifier) -> TokenStream {
match aq {
AccessQualifier::ReadOnly => quote!(AccessQualifier::ReadOnly),
AccessQualifier::WriteOnly => quote!(AccessQualifier::WriteOnly),
AccessQualifier::ReadWrite => quote!(AccessQualifier::ReadWrite),
}
}
pub fn image_depth_to_tokens(id: &ImageDepth) -> TokenStream {
match id {
ImageDepth::True => quote!(ImageDepth::True),
ImageDepth::False => quote!(ImageDepth::False),
ImageDepth::Unknown => quote!(ImageDepth::Unknown),
}
}
pub fn arrayed_to_tokens(arrayed: &Arrayed) -> TokenStream {
match arrayed {
Arrayed::True => quote!(Arrayed::True),
Arrayed::False => quote!(Arrayed::False),
}
}
pub fn dimensionality_to_tokens(dim: &Dimensionality) -> TokenStream {
match dim {
Dimensionality::OneD => quote!(Dimensionality::OneD),
Dimensionality::TwoD => quote!(Dimensionality::TwoD),
Dimensionality::ThreeD => quote!(Dimensionality::ThreeD),
Dimensionality::Rect => quote!(Dimensionality::Rect),
Dimensionality::Cube => quote!(Dimensionality::Cube),
Dimensionality::Buffer => quote!(Dimensionality::Buffer),
Dimensionality::SubpassData => quote!(Dimensionality::SubpassData),
}
}
pub fn multisampled_to_tokens(multisampled: &Multisampled) -> TokenStream {
match multisampled {
Multisampled::True => quote!(Multisampled::True),
Multisampled::False => quote!(Multisampled::False),
}
}
pub fn sampled_to_tokens(sampled: &Sampled) -> TokenStream {
match sampled {
Sampled::Yes => quote!(Sampled::Yes),
Sampled::No => quote!(Sampled::No),
Sampled::Unknown => quote!(Sampled::Unknown),
}
}
pub fn image_format_to_tokens(format: &ImageFormat) -> proc_macro2::TokenStream {
let variant = {
let variant = match format {
ImageFormat::Unknown => "Unknown",
ImageFormat::Rgba32f => "Rgba32f",
ImageFormat::Rgba16f => "Rgba16f",
ImageFormat::R32f => "R32f",
ImageFormat::Rgba8 => "Rgba8",
ImageFormat::Rgba8Snorm => "Rgba8Snorm",
ImageFormat::Rg32f => "Rg32f",
ImageFormat::Rg16f => "Rg16f",
ImageFormat::R11fG11fB10f => "R11fG11fB10f",
ImageFormat::R16f => "R16f",
ImageFormat::Rgba16 => "Rgba16",
ImageFormat::Rgb10A2 => "Rgb10A2",
ImageFormat::Rg16 => "Rg16",
ImageFormat::Rg8 => "Rg8",
ImageFormat::R16 => "R16",
ImageFormat::R8 => "R8",
ImageFormat::Rgba16Snorm => "Rgba16Snorm",
ImageFormat::Rg16Snorm => "Rg16Snorm",
ImageFormat::Rg8Snorm => "Rg8Snorm",
ImageFormat::R16Snorm => "R16Snorm",
ImageFormat::R8Snorm => "R8Snorm",
ImageFormat::Rgba32i => "Rgba32i",
ImageFormat::Rgba16i => "Rgba16i",
ImageFormat::Rgba8i => "Rgba8i",
ImageFormat::R32i => "R32i",
ImageFormat::Rg32i => "Rg32i",
ImageFormat::Rg16i => "Rg16i",
ImageFormat::Rg8i => "Rg8i",
ImageFormat::R16i => "R16i",
ImageFormat::R8i => "R8i",
ImageFormat::Rgba32ui => "Rgba32ui",
ImageFormat::Rgba16ui => "Rgba16ui",
ImageFormat::Rgba8ui => "Rgba8ui",
ImageFormat::R32ui => "R32ui",
ImageFormat::Rgb10A2ui => "Rgb10A2ui",
ImageFormat::Rg32ui => "Rg32ui",
ImageFormat::Rg16ui => "Rg16ui",
ImageFormat::Rg8ui => "Rg8ui",
ImageFormat::R16ui => "R16ui",
ImageFormat::R8ui => "R8ui",
ImageFormat::R64ui => "R64ui",
ImageFormat::R64i => "R64i",
};
let variant = proc_macro2::Ident::new(variant, proc_macro2::Span::mixed_site());
quote!(#variant)
};
quote!(ImageFormat::#variant)
}
}

View File

@ -53,11 +53,65 @@
// crate-specific exceptions:
#![allow()]
mod image;
use proc_macro::TokenStream;
use proc_macro2::{Delimiter, Group, Ident, Span, TokenTree};
use syn::{punctuated::Punctuated, spanned::Spanned, ItemFn, Token};
use quote::ToTokens;
/// A macro for creating SPIR-V `OpTypeImage` types.
///
/// The grammar for the macro is as follows:
///
/// ```no_compile
/// Image!(
/// <dimensionality>,
/// <type|format>,
/// [sampled[=<true|false>],]
/// [multisampled[=<true|false>],]
/// [arrayed[=<true|false>],]
/// [depth[=<true|false>],]
/// )
/// ```
///
/// A basic example looks like this:
/// ```no_compile
/// #[spirv(vertex)]
/// fn main(#[spirv(descriptor_set = 0, binding = 0)] image: &Image!(2D, type=f32)) {}
/// ```
///
/// ## Arguments
///
/// - `dimensionality` — Dimensionality of an image. Accepted values: `1D`,
/// `2D`, `3D`, `rect`, `cube`, `subpass`.
/// - `type` — The sampled type of an image, mutually exclusive with `format`,
/// when set the image format is unknown. Accepted values: `f32`, `f64`,
/// `u8`, `u16`, `u32`, `u64`, `i8`, `i16`, `i32`, `i64`.
/// - `format` — The image format of the image, mutually exclusive with `type`,
/// Accepted values: Camel case versions of [`ImageFormat`].
/// - `sampled` — Whether it is known that the image will be used with a sampler
/// at compile time, Accepted values: `true` or `false`. Default: `unknown`.
/// - `multisampled` — Whether the image contains multisampled content. Accepted
/// values: `true` or `false`. Default: `false`.
/// - `arrayed` — Whether the image contains arrayed content. Accepted
/// values: `true` or `false`. Default: `false`.
/// - `depth` — Whether it is known that the image is a depth image,
/// Accepted values: `true` or `false`. Default: `unknown`.
///
/// [`ImageFormat`]: spirv_types::image_params::ImageFormat
#[proc_macro]
// The `Image` is supposed to be used in the type position, which
// uses `PascalCase`.
#[allow(nonstandard_style)]
pub fn Image(item: TokenStream) -> TokenStream {
let output = syn::parse_macro_input!(item as image::ImageType).into_token_stream();
output.into()
}
#[proc_macro_attribute]
pub fn spirv(_attr: TokenStream, item: TokenStream) -> TokenStream {
let mut tokens = Vec::new();

View File

@ -0,0 +1,8 @@
[package]
name = "spirv-types"
description = "SPIR-V types shared between spirv-std and spirv-std-macros"
version = "0.4.0-alpha.5"
authors = ["Embark <opensource@embark-studios.com>"]
edition = "2018"
license = "MIT OR Apache-2.0"
repository = "https://github.com/EmbarkStudios/rust-gpu"

View File

@ -0,0 +1,228 @@
/// The access permissions for the image.
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum AccessQualifier {
/// A read only image.
ReadOnly = 0,
/// A write only image.
WriteOnly = 1,
/// A readable and writable image.
ReadWrite = 2,
}
/// Whether the image uses arrayed content.
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Arrayed {
/// The image uses not arrayed content.
False = 0,
/// The image uses arrayed content.
True = 1,
}
impl From<bool> for Arrayed {
fn from(val: bool) -> Self {
if val {
Self::True
} else {
Self::False
}
}
}
/// The dimension of the image.
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Dimensionality {
/// 1D
OneD = 0,
/// 2D
TwoD = 1,
/// 3D
ThreeD = 2,
/// 2D Cubemap texture
Cube = 3,
/// 2D Rectangle texture
Rect = 4,
/// 1D Buffer texture
Buffer = 5,
/// Vulkan subpass buffer
SubpassData = 6,
}
/// Whether a given image contains [depth] information. **Note** Whether or not
/// to perform depth comparisons is a property of the sampling code, not of this
/// type.
///
/// [depth]: https://en.wikipedia.org/wiki/Depth_map
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum ImageDepth {
/// Indicates that the image does not contain depth information.
False = 0,
/// Indicates that the image contains depth information.
True = 1,
/// Indicates that is not known ahead of time whether the image has depth
/// information or not.
Unknown = 2,
}
impl From<Option<bool>> for ImageDepth {
fn from(val: Option<bool>) -> Self {
match val {
Some(true) => Self::True,
Some(false) => Self::False,
None => Self::Unknown,
}
}
}
impl From<bool> for ImageDepth {
fn from(val: bool) -> Self {
match val {
true => Self::True,
false => Self::False,
}
}
}
/// Whether the image uses arrayed content.
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Multisampled {
/// The image contains single-sampled content.
False = 0,
/// The image contains multisampled content.
True = 1,
}
impl From<bool> for Multisampled {
fn from(val: bool) -> Self {
if val {
Self::True
} else {
Self::False
}
}
}
/// Whether or not the image will be accessed in combination with a sampler.
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Sampled {
/// Indicates that it is not known ahead of time whether the image will use
/// a sampler or not.
Unknown = 0,
/// The image will be used with a sampler.
Yes = 1,
/// The image will not be used with a sampler.
No = 2,
}
impl From<Option<bool>> for Sampled {
fn from(val: Option<bool>) -> Self {
match val {
Some(true) => Self::Yes,
Some(false) => Self::No,
None => Self::Unknown,
}
}
}
impl From<bool> for Sampled {
fn from(val: bool) -> Self {
match val {
true => Self::Yes,
false => Self::No,
}
}
}
/// The underlying internal representation of the image.
#[derive(PartialEq, Eq)]
pub enum ImageFormat {
/// Representation not known at compile time.
Unknown,
/// RGBA channels, 32 bit floating point integer.
Rgba32f,
/// RGBA channels, 16 bit floating point integer.
Rgba16f,
/// RGBA channels, 16 bit floating point integer.
R32f,
/// RGBA channels, 8 bit floating point integer.
Rgba8,
/// RGBA channels, 8 bit signed normalized integer.
Rgba8Snorm,
/// Red+Green channels, 32 bit floating point integer.
Rg32f,
/// Red+Green channels, 16 bit floating point integer.
Rg16f,
/// 32 bit unsigned integer containing two 11 bit floating point integers
/// for the Red and Green channels, and a 10 bit floating point integer for
/// the Blue channel.
R11fG11fB10f,
/// Red channel, 16 bit floating point.
R16f,
/// RGBA channel, 16 bit floating point.
Rgba16,
/// 32 bit unsigned integer containing three 10 bit unsigned normalized
/// integers for the Red, Green, and Blue channels; with a 2 unsigned
/// normalized integer for the Alpha channel.
Rgb10A2,
/// Red+Green channels, 16 bit floating point integer.
Rg16,
/// Red+Green channels, 8 bit floating point integer.
Rg8,
/// Red+Green channels, 16 bit floating point integer.
R16,
/// Red channel, 8 bit floating point integer.
R8,
/// RGBA channels, 16 bit signed normalized integer.
Rgba16Snorm,
/// RGB channels, 16 bit signed normalized integer.
Rg16Snorm,
/// Red+Green channels, 8 bit signed normalized integer.
Rg8Snorm,
/// Red channel, 16 bit signed normalized integer.
R16Snorm,
/// Red channel, 16 bit signed normalized integer.
R8Snorm,
/// RGBA channels, 32 bit signed integer.
Rgba32i,
/// RGBA channels, 16 bit signed integer.
Rgba16i,
/// RGBA channels, 8 bit signed integer.
Rgba8i,
/// Red channel, 32 bit signed integer.
R32i,
/// Red+Green channels, 32 bit signed integer.
Rg32i,
/// Red+Green channels, 16 bit signed integer.
Rg16i,
/// Red+Green channels, 8 bit signed integer.
Rg8i,
/// Red channel, 16 bit signed integer.
R16i,
/// Red channel, 8 bit signed integer.
R8i,
/// RGBA channels, 32 bit unsigned integer.
Rgba32ui,
/// RGBA channels, 16 bit unsigned integer.
Rgba16ui,
/// RGBA channels, 8 bit unsigned integer.
Rgba8ui,
/// Red channel, 32 bit unsigned integer.
R32ui,
/// 32 bit unsigned integer containing three 10 bit unsigned integers for
/// the Red, Green, and Blue channels, and a 2 bit unsigned integer for the
/// Alpha channel.
Rgb10A2ui,
/// Red+Green channels, 32 bit unsigned integer.
Rg32ui,
/// Red+Green channels, 16 bit unsigned integer.
Rg16ui,
/// Red+Green channels, 8 bit unsigned integer.
Rg8ui,
/// Red channel, 16 bit unsigned integer.
R16ui,
/// Red channel, 8 bit unsigned integer.
R8ui,
/// Red channel, 64 bit unsigned integer.
R64ui,
/// Red channel, 64 bit signed integer.
R64i,
}

View File

@ -0,0 +1,5 @@
//! Small shared crate, to share definitions between `spirv-std`
//! and `spirv-std-macros`.
#![no_std]
pub mod image_params;

View File

@ -29,8 +29,8 @@ macro_rules! deriv_fn {
/// Returns the partial derivative of `component` with respect to the window's X
/// coordinate. Returns the same result as either [`ddx_fine`] or
/// [`ddx_coarse`], selection of which one is dependent on external factors.
#[spirv_std_macros::vectorized]
#[spirv_std_macros::gpu_only]
#[crate::macros::vectorized]
#[crate::macros::gpu_only]
pub fn ddx<F: Float>(component: F) -> F {
deriv_fn!(component, OpDPdx, false)
}
@ -38,8 +38,8 @@ pub fn ddx<F: Float>(component: F) -> F {
/// Returns the partial derivative of `component` with respect to the window's X
/// coordinate. Uses local differencing based on the value of `component` for
/// the current fragment and its immediate neighbor(s).
#[spirv_std_macros::vectorized]
#[spirv_std_macros::gpu_only]
#[crate::macros::vectorized]
#[crate::macros::gpu_only]
pub fn ddx_fine<F: Float>(component: F) -> F {
deriv_fn!(component, OpDPdxFine, true)
}
@ -50,8 +50,8 @@ pub fn ddx_fine<F: Float>(component: F) -> F {
/// includes the value of `component` for the current fragment. That is, over a
/// given area, the implementation can compute X derivatives in fewer unique
/// locations than would be allowed by [`ddx_fine`].
#[spirv_std_macros::vectorized]
#[spirv_std_macros::gpu_only]
#[crate::macros::vectorized]
#[crate::macros::gpu_only]
pub fn ddx_coarse<F: Float>(component: F) -> F {
deriv_fn!(component, OpDPdxCoarse, true)
}
@ -59,8 +59,8 @@ pub fn ddx_coarse<F: Float>(component: F) -> F {
/// Returns the partial derivative of `component` with respect to the window's Y
/// coordinate. Returns the same result as either [`ddy_fine`] or
/// [`ddy_coarse`], selection of which one is dependent on external factors.
#[spirv_std_macros::vectorized]
#[spirv_std_macros::gpu_only]
#[crate::macros::vectorized]
#[crate::macros::gpu_only]
pub fn ddy<F: Float>(component: F) -> F {
deriv_fn!(component, OpDPdy, false)
}
@ -68,8 +68,8 @@ pub fn ddy<F: Float>(component: F) -> F {
/// Returns the partial derivative of `component` with respect to the window's Y
/// coordinate. Uses local differencing based on the value of `component` for
/// the current fragment and its immediate neighbor(s).
#[spirv_std_macros::vectorized]
#[spirv_std_macros::gpu_only]
#[crate::macros::vectorized]
#[crate::macros::gpu_only]
pub fn ddy_fine<F: Float>(component: F) -> F {
deriv_fn!(component, OpDPdyFine, true)
}
@ -80,32 +80,32 @@ pub fn ddy_fine<F: Float>(component: F) -> F {
/// includes the value of `component` for the current fragment. That is, over a
/// given area, the implementation can compute Y derivatives in fewer unique
/// locations than would be allowed by [`ddy_fine`].
#[spirv_std_macros::vectorized]
#[spirv_std_macros::gpu_only]
#[crate::macros::vectorized]
#[crate::macros::gpu_only]
pub fn ddy_coarse<F: Float>(component: F) -> F {
deriv_fn!(component, OpDPdyCoarse, true)
}
/// Returns the sum of the absolute values of [`ddx`] and [`ddy`] as a single
/// operation.
#[spirv_std_macros::vectorized]
#[spirv_std_macros::gpu_only]
#[crate::macros::vectorized]
#[crate::macros::gpu_only]
pub fn fwidth<F: Float>(component: F) -> F {
deriv_fn!(component, OpFwidth, false)
}
/// Returns the sum of the absolute values of [`ddx_fine`] and [`ddy_fine`] as a
/// single operation.
#[spirv_std_macros::vectorized]
#[spirv_std_macros::gpu_only]
#[crate::macros::vectorized]
#[crate::macros::gpu_only]
pub fn fwidth_fine<F: Float>(component: F) -> F {
deriv_fn!(component, OpFwidthFine, true)
}
/// Returns the sum of the absolute values of [`ddx_coarse`] and [`ddy_coarse`]
/// as a single operation.
#[spirv_std_macros::vectorized]
#[spirv_std_macros::gpu_only]
#[crate::macros::vectorized]
#[crate::macros::gpu_only]
pub fn fwidth_coarse<F: Float>(component: F) -> F {
deriv_fn!(component, OpFwidthCoarse, true)
}

View File

@ -0,0 +1,715 @@
//! Image types
// Rustfmt formats long marker trait impls over multiple lines which makes them
// harder to read.
#[rustfmt::skip]
mod params;
pub use self::params::{ImageCoordinate, SampleType};
pub use crate::macros::Image;
pub use spirv_types::image_params::{
AccessQualifier, Arrayed, Dimensionality, ImageDepth, ImageFormat, Multisampled, Sampled,
};
use crate::{float::Float, integer::Integer, vector::Vector, Sampler};
/// Re-export of primitive types to ensure the `Image` proc macro always points
/// to the right type.
#[doc(hidden)]
pub mod __private {
pub use {f32, f64, i16, i32, i64, i8, u16, u32, u64, u8};
}
pub type Image2d = crate::Image!(2D, type=f32, sampled, __crate_root=crate);
pub type Cubemap = crate::Image!(cube, type=f32, sampled, __crate_root=crate);
pub type Image2dArray = crate::Image!(cube, type=f32, sampled, arrayed, __crate_root=crate);
pub type StorageImage2d = crate::Image!(cube, type=f32, sampled=false, __crate_root=crate);
/// An opaque image type. Corresponds to `OpTypeImage`.
#[spirv(generic_image_type)]
#[derive(Copy, Clone)]
pub struct Image<
SampledType: SampleType<FORMAT>,
const DIM: Dimensionality,
const DEPTH: ImageDepth,
const ARRAYED: Arrayed,
const MULTISAMPLED: Multisampled,
const SAMPLED: Sampled,
const FORMAT: ImageFormat,
const ACCESS_QUALIFIER: Option<AccessQualifier>,
> {
_x: u32,
_marker: core::marker::PhantomData<SampledType>,
}
impl<
SampledType: SampleType<FORMAT>,
const DIM: Dimensionality,
const DEPTH: ImageDepth,
const ARRAYED: Arrayed,
const MULTISAMPLED: Multisampled,
const FORMAT: ImageFormat,
const ACCESS_QUALIFIER: Option<AccessQualifier>,
>
Image<
SampledType,
DIM,
DEPTH,
ARRAYED,
MULTISAMPLED,
{ Sampled::Yes },
FORMAT,
ACCESS_QUALIFIER,
>
{
/// Fetch a single texel with a sampler set at compile time
#[crate::macros::gpu_only]
#[doc(alias = "OpImageFetch")]
pub fn fetch<V, I>(&self, coordinate: impl ImageCoordinate<I, DIM, ARRAYED>) -> V
where
V: Vector<SampledType, 4>,
I: Integer,
{
let mut result = V::default();
unsafe {
asm! {
"%image = OpLoad _ {this}",
"%coordinate = OpLoad _ {coordinate}",
"%result = OpImageFetch typeof*{result} %image %coordinate",
"OpStore {result} %result",
result = in(reg) &mut result,
this = in(reg) self,
coordinate = in(reg) &coordinate,
}
}
result
}
}
impl<
SampledType: SampleType<FORMAT>,
const DIM: Dimensionality,
const DEPTH: ImageDepth,
const FORMAT: ImageFormat,
const ARRAYED: Arrayed,
const SAMPLED: Sampled,
const ACCESS_QUALIFIER: Option<AccessQualifier>,
>
Image<
SampledType,
DIM,
DEPTH,
ARRAYED,
{ Multisampled::False },
SAMPLED,
FORMAT,
ACCESS_QUALIFIER,
>
{
/// Sample texels at `coord` from the image using `sampler`.
#[crate::macros::gpu_only]
pub fn sample<F, V>(&self, sampler: Sampler, coord: impl ImageCoordinate<F, DIM, ARRAYED>) -> V
where
F: Float,
V: Vector<SampledType, 4>,
{
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
}
}
/// Fetch a single texel with a sampler set at compile time
#[crate::macros::gpu_only]
#[doc(alias = "OpImageSampleExplicitLod")]
/// Sample the image at a coordinate by a lod
pub fn sample_by_lod<F, V>(
&self,
sampler: Sampler,
coordinate: impl ImageCoordinate<F, DIM, ARRAYED>,
lod: f32,
) -> V
where
F: Float,
V: Vector<SampledType, 4>,
{
let mut result = Default::default();
unsafe {
asm!(
"%image = OpLoad _ {this}",
"%sampler = OpLoad _ {sampler}",
"%coordinate = OpLoad _ {coordinate}",
"%lod = OpLoad _ {lod}",
"%sampledImage = OpSampledImage _ %image %sampler",
"%result = OpImageSampleExplicitLod _ %sampledImage %coordinate Lod %lod",
"OpStore {result} %result",
result = in(reg) &mut result,
this = in(reg) self,
sampler = in(reg) &sampler,
coordinate = in(reg) &coordinate,
lod = in(reg) &lod
);
}
result
}
#[crate::macros::gpu_only]
#[doc(alias = "OpImageSampleExplicitLod")]
/// Sample the image based on a gradient formed by (dx, dy). Specifically, ([du/dx, dv/dx], [du/dy, dv/dy])
pub fn sample_by_gradient<F, V>(
&self,
sampler: Sampler,
coordinate: impl ImageCoordinate<F, DIM, ARRAYED>,
gradient_dx: impl ImageCoordinate<F, DIM, { Arrayed::False }>,
gradient_dy: impl ImageCoordinate<F, DIM, { Arrayed::False }>,
) -> V
where
F: Float,
V: Vector<SampledType, 4>,
{
let mut result = Default::default();
unsafe {
asm!(
"%image = OpLoad _ {this}",
"%sampler = OpLoad _ {sampler}",
"%coordinate = OpLoad _ {coordinate}",
"%gradient_dx = OpLoad _ {gradient_dx}",
"%gradient_dy = OpLoad _ {gradient_dy}",
"%sampledImage = OpSampledImage _ %image %sampler",
"%result = OpImageSampleExplicitLod _ %sampledImage %coordinate Grad %gradient_dx %gradient_dy",
"OpStore {result} %result",
result = in(reg) &mut result,
this = in(reg) self,
sampler = in(reg) &sampler,
coordinate = in(reg) &coordinate,
gradient_dx = in(reg) &gradient_dx,
gradient_dy = in(reg) &gradient_dy,
);
}
result
}
#[crate::macros::gpu_only]
#[doc(alias = "OpImageSampleDrefImplicitLod")]
/// Sample the image's depth reference
pub fn sample_depth_reference<F>(
&self,
sampler: Sampler,
coordinate: impl ImageCoordinate<F, DIM, ARRAYED>,
depth_reference: f32,
) -> SampledType
where
F: Float,
{
let mut result = Default::default();
unsafe {
asm!(
"%image = OpLoad _ {this}",
"%sampler = OpLoad _ {sampler}",
"%coordinate = OpLoad _ {coordinate}",
"%depth_reference = OpLoad _ {depth_reference}", // not required to do this way, but done for consistency
"%sampledImage = OpSampledImage _ %image %sampler",
"%result = OpImageSampleDrefImplicitLod _ %sampledImage %coordinate %depth_reference",
"OpStore {result} %result",
result = in(reg) &mut result,
this = in(reg) self,
sampler = in(reg) &sampler,
coordinate = in(reg) &coordinate,
depth_reference = in(reg) &depth_reference,
);
}
result
}
#[crate::macros::gpu_only]
#[doc(alias = "OpImageSampleDrefExplicitLod")]
/// Sample the image's depth reference based on an explicit lod
pub fn sample_depth_reference_by_lod<F>(
&self,
sampler: Sampler,
coordinate: impl ImageCoordinate<F, DIM, ARRAYED>,
depth_reference: f32,
lod: f32,
) -> SampledType
where
F: Float,
{
let mut result = Default::default();
unsafe {
asm!(
"%image = OpLoad _ {this}",
"%sampler = OpLoad _ {sampler}",
"%coordinate = OpLoad _ {coordinate}",
"%depth_reference = OpLoad _ {depth_reference}",
"%lod = OpLoad _ {lod}",
"%sampledImage = OpSampledImage _ %image %sampler",
"%result = OpImageSampleDrefExplicitLod _ %sampledImage %coordinate %depth_reference Lod %lod",
"OpStore {result} %result",
result = in(reg) &mut result,
this = in(reg) self,
sampler = in(reg) &sampler,
coordinate = in(reg) &coordinate,
depth_reference = in(reg) &depth_reference,
lod = in(reg) &lod,
)
}
result
}
#[crate::macros::gpu_only]
#[doc(alias = "OpImageSampleDrefExplicitLod")]
/// Sample the image's depth reference based on a gradient formed by (dx, dy).
/// Specifically, ([du/dx, dv/dx], [du/dy, dv/dy])
pub fn sample_depth_reference_by_gradient<F>(
&self,
sampler: Sampler,
coordinate: impl ImageCoordinate<F, DIM, ARRAYED>,
depth_reference: f32,
gradient_dx: impl ImageCoordinate<F, DIM, { Arrayed::False }>,
gradient_dy: impl ImageCoordinate<F, DIM, { Arrayed::False }>,
) -> SampledType
where
F: Float,
{
let mut result = Default::default();
unsafe {
asm!(
"%image = OpLoad _ {this}",
"%sampler = OpLoad _ {sampler}",
"%coordinate = OpLoad _ {coordinate}",
"%depth_reference = OpLoad _ {depth_reference}",
"%gradient_dx = OpLoad _ {gradient_dx}",
"%gradient_dy = OpLoad _ {gradient_dy}",
"%sampledImage = OpSampledImage _ %image %sampler",
"%result = OpImageSampleDrefExplicitLod _ %sampledImage %coordinate %depth_reference Grad %gradient_dx %gradient_dy",
"OpStore {result} %result",
result = in(reg) &mut result,
this = in(reg) self,
sampler = in(reg) &sampler,
coordinate = in(reg) &coordinate,
depth_reference = in(reg) &depth_reference,
gradient_dx = in(reg) &gradient_dx,
gradient_dy = in(reg) &gradient_dy,
);
}
result
}
}
impl<
SampledType: SampleType<FORMAT>,
const DIM: Dimensionality,
const DEPTH: ImageDepth,
const SAMPLED: Sampled,
const FORMAT: ImageFormat,
const ACCESS_QUALIFIER: Option<AccessQualifier>,
>
Image<
SampledType,
DIM,
DEPTH,
{ Arrayed::False },
{ Multisampled::False },
SAMPLED,
FORMAT,
ACCESS_QUALIFIER,
>
{
/// Fetch a single texel with a sampler set at compile time
#[crate::macros::gpu_only]
#[doc(alias = "OpImageFetch")]
pub fn sample_with_project_coordinate<F, V>(
&self,
sampler: Sampler,
project_coordinate: impl ImageCoordinate<F, DIM, { Arrayed::True }>,
) -> V
where
F: Float,
V: Vector<SampledType, 4>,
{
unsafe {
let mut result = Default::default();
asm!(
"%image = OpLoad _ {this}",
"%sampler = OpLoad _ {sampler}",
"%project_coordinate = OpLoad _ {project_coordinate}",
"%sampledImage = OpSampledImage _ %image %sampler",
"%result = OpImageSampleProjImplicitLod _ %sampledImage %project_coordinate",
"OpStore {result} %result",
result = in(reg) &mut result,
this = in(reg) self,
sampler = in(reg) &sampler,
project_coordinate = in(reg) &project_coordinate,
);
result
}
}
#[crate::macros::gpu_only]
#[doc(alias = "OpImageSampleProjExplicitLod")]
/// Sample the image with a project coordinate by a lod
pub fn sample_with_project_coordinate_by_lod<F, V>(
&self,
sampler: Sampler,
project_coordinate: impl ImageCoordinate<F, DIM, { Arrayed::True }>,
lod: f32,
) -> V
where
F: Float,
V: Vector<SampledType, 4>,
{
let mut result = Default::default();
unsafe {
asm!(
"%image = OpLoad _ {this}",
"%sampler = OpLoad _ {sampler}",
"%project_coordinate = OpLoad _ {project_coordinate}",
"%lod = OpLoad _ {lod}",
"%sampledImage = OpSampledImage _ %image %sampler",
"%result = OpImageSampleProjExplicitLod _ %sampledImage %project_coordinate Lod %lod",
"OpStore {result} %result",
result = in(reg) &mut result,
this = in(reg) self,
sampler = in(reg) &sampler,
project_coordinate = in(reg) &project_coordinate,
lod = in(reg) &lod
);
}
result
}
#[crate::macros::gpu_only]
#[doc(alias = "OpImageSampleProjExplicitLod")]
/// Sample the image with a project coordinate based on a gradient formed by (dx, dy). Specifically, ([du/dx, dv/dx], [du/dy, dv/dy])
pub fn sample_with_project_coordinate_by_gradient<F, V>(
&self,
sampler: Sampler,
project_coordinate: impl ImageCoordinate<F, DIM, { Arrayed::True }>,
gradient_dx: impl ImageCoordinate<F, DIM, { Arrayed::False }>,
gradient_dy: impl ImageCoordinate<F, DIM, { Arrayed::False }>,
) -> V
where
F: Float,
V: Vector<SampledType, 4>,
{
let mut result = Default::default();
unsafe {
asm!(
"%image = OpLoad _ {this}",
"%sampler = OpLoad _ {sampler}",
"%project_coordinate = OpLoad _ {project_coordinate}",
"%gradient_dx = OpLoad _ {gradient_dx}",
"%gradient_dy = OpLoad _ {gradient_dy}",
"%sampledImage = OpSampledImage _ %image %sampler",
"%result = OpImageSampleProjExplicitLod _ %sampledImage %project_coordinate Grad %gradient_dx %gradient_dy",
"OpStore {result} %result",
result = in(reg) &mut result,
this = in(reg) self,
sampler = in(reg) &sampler,
project_coordinate = in(reg) &project_coordinate,
gradient_dx = in(reg) &gradient_dx,
gradient_dy = in(reg) &gradient_dy,
);
}
result
}
#[crate::macros::gpu_only]
#[doc(alias = "OpImageSampleProjDrefImplicitLod")]
/// Sample the image's depth reference with the project coordinate
pub fn sample_depth_reference_with_project_coordinate<F>(
&self,
sampler: Sampler,
project_coordinate: impl ImageCoordinate<F, DIM, { Arrayed::True }>,
depth_reference: f32,
) -> SampledType
where
F: Float,
{
let mut result = Default::default();
unsafe {
asm!(
"%image = OpLoad _ {this}",
"%sampler = OpLoad _ {sampler}",
"%project_coordinate = OpLoad _ {project_coordinate}",
"%depth_reference = OpLoad _ {depth_reference}", // not required to do this way, but done for consistency
"%sampledImage = OpSampledImage _ %image %sampler",
"%result = OpImageSampleProjDrefImplicitLod _ %sampledImage %project_coordinate %depth_reference",
"OpStore {result} %result",
result = in(reg) &mut result,
this = in(reg) self,
sampler = in(reg) &sampler,
project_coordinate = in(reg) &project_coordinate,
depth_reference = in(reg) &depth_reference,
);
}
result
}
#[crate::macros::gpu_only]
#[doc(alias = "OpImageSampleProjDrefExplicitLod")]
/// Sample the image's depth reference with the project coordinate based on an explicit lod
pub fn sample_depth_reference_with_project_coordinate_by_lod<F>(
&self,
sampler: Sampler,
coordinate: impl ImageCoordinate<F, DIM, { Arrayed::True }>,
depth_reference: f32,
lod: f32,
) -> SampledType
where
F: Float,
{
let mut result = Default::default();
unsafe {
asm!(
"%image = OpLoad _ {this}",
"%sampler = OpLoad _ {sampler}",
"%coordinate = OpLoad _ {coordinate}",
"%depth_reference = OpLoad _ {depth_reference}",
"%lod = OpLoad _ {lod}",
"%sampledImage = OpSampledImage _ %image %sampler",
"%result = OpImageSampleProjDrefExplicitLod _ %sampledImage %coordinate %depth_reference Lod %lod",
"OpStore {result} %result",
result = in(reg) &mut result,
this = in(reg) self,
sampler = in(reg) &sampler,
coordinate = in(reg) &coordinate,
depth_reference = in(reg) &depth_reference,
lod = in(reg) &lod,
)
}
result
}
#[crate::macros::gpu_only]
#[doc(alias = "OpImageSampleProjDrefExplicitLod")]
/// Sample the image's depth reference with the project coordinate based on a gradient formed by (dx, dy).
/// Specifically, ([du/dx, dv/dx], [du/dy, dv/dy])
pub fn sample_depth_reference_with_project_coordinate_by_gradient<F>(
&self,
sampler: Sampler,
coordinate: impl ImageCoordinate<F, DIM, { Arrayed::True }>,
depth_reference: f32,
gradient_dx: impl ImageCoordinate<F, DIM, { Arrayed::False }>,
gradient_dy: impl ImageCoordinate<F, DIM, { Arrayed::False }>,
) -> SampledType
where
F: Float,
{
let mut result = Default::default();
unsafe {
asm!(
"%image = OpLoad _ {this}",
"%sampler = OpLoad _ {sampler}",
"%coordinate = OpLoad _ {coordinate}",
"%depth_reference = OpLoad _ {depth_reference}",
"%gradient_dx = OpLoad _ {gradient_dx}",
"%gradient_dy = OpLoad _ {gradient_dy}",
"%sampledImage = OpSampledImage _ %image %sampler",
"%result = OpImageSampleProjDrefExplicitLod _ %sampledImage %coordinate %depth_reference Grad %gradient_dx %gradient_dy",
"OpStore {result} %result",
result = in(reg) &mut result,
this = in(reg) self,
sampler = in(reg) &sampler,
coordinate = in(reg) &coordinate,
depth_reference = in(reg) &depth_reference,
gradient_dx = in(reg) &gradient_dx,
gradient_dy = in(reg) &gradient_dy,
);
}
result
}
}
impl<
SampledType: SampleType<FORMAT>,
const DIM: Dimensionality,
const DEPTH: ImageDepth,
const ARRAYED: Arrayed,
const MULTISAMPLED: Multisampled,
const FORMAT: ImageFormat,
const ACCESS_QUALIFIER: Option<AccessQualifier>,
>
Image<SampledType, DIM, DEPTH, ARRAYED, MULTISAMPLED, { Sampled::No }, FORMAT, ACCESS_QUALIFIER>
{
/// Read a texel from an image without a sampler.
#[crate::macros::gpu_only]
#[doc(alias = "OpImageRead")]
pub fn read<I, V, const N: usize>(&self, coordinate: impl ImageCoordinate<I, DIM, ARRAYED>) -> V
where
I: Integer,
V: Vector<SampledType, N>,
{
let mut result = V::default();
unsafe {
asm! {
"%image = OpLoad _ {this}",
"%coordinate = OpLoad _ {coordinate}",
"%result = OpImageRead typeof*{result} %image %coordinate",
"OpStore {result} %result",
this = in(reg) self,
coordinate = in(reg) &coordinate,
result = in(reg) &mut result,
}
}
result
}
/// Write a texel to an image without a sampler.
#[crate::macros::gpu_only]
#[doc(alias = "OpImageWrite")]
pub unsafe fn write<I, const N: usize>(
&self,
coordinate: impl ImageCoordinate<I, DIM, ARRAYED>,
texels: impl Vector<SampledType, N>,
) where
I: Integer,
{
asm! {
"%image = OpLoad _ {this}",
"%coordinate = OpLoad _ {coordinate}",
"%texels = OpLoad _ {texels}",
"OpImageWrite %image %coordinate %texels",
this = in(reg) self,
coordinate = in(reg) &coordinate,
texels = in(reg) &texels,
}
}
}
impl<
SampledType: SampleType<FORMAT>,
const DIM: Dimensionality,
const DEPTH: ImageDepth,
const FORMAT: ImageFormat,
const ARRAYED: Arrayed,
const MULTISAMPLED: Multisampled,
const ACCESS_QUALIFIER: Option<AccessQualifier>,
>
Image<
SampledType,
DIM,
DEPTH,
ARRAYED,
MULTISAMPLED,
{ Sampled::Unknown },
FORMAT,
ACCESS_QUALIFIER,
>
{
/// Read a texel from an image without a sampler.
#[crate::macros::gpu_only]
#[doc(alias = "OpImageRead")]
pub fn read<I, V, const N: usize>(&self, coordinate: impl ImageCoordinate<I, DIM, ARRAYED>) -> V
where
I: Integer,
V: Vector<SampledType, N>,
{
let mut result = V::default();
unsafe {
asm! {
"%image = OpLoad _ {this}",
"%coordinate = OpLoad _ {coordinate}",
"%result = OpImageRead typeof*{result} %image %coordinate",
"OpStore {result} %result",
this = in(reg) self,
coordinate = in(reg) &coordinate,
result = in(reg) &mut result,
}
}
result
}
/// Write a texel to an image without a sampler.
#[crate::macros::gpu_only]
#[doc(alias = "OpImageWrite")]
pub unsafe fn write<I, const N: usize>(
&self,
coordinate: impl ImageCoordinate<I, DIM, ARRAYED>,
texels: impl Vector<SampledType, N>,
) where
I: Integer,
{
asm! {
"%image = OpLoad _ {this}",
"%coordinate = OpLoad _ {coordinate}",
"%texels = OpLoad _ {texels}",
"OpImageWrite %image %coordinate %texels",
this = in(reg) self,
coordinate = in(reg) &coordinate,
texels = in(reg) &texels,
}
}
}
/// An image combined with a sampler, enabling filtered accesses of the
/// images contents.
#[spirv(sampled_image)]
#[derive(Copy, Clone)]
pub struct SampledImage<I> {
_image: I,
}
impl<
SampledType: SampleType<FORMAT>,
const DIM: Dimensionality,
const DEPTH: ImageDepth,
const ARRAYED: Arrayed,
const SAMPLED: Sampled,
const FORMAT: ImageFormat,
const ACCESS_QUALIFIER: Option<AccessQualifier>,
>
SampledImage<
Image<
SampledType,
DIM,
DEPTH,
ARRAYED,
{ Multisampled::False },
SAMPLED,
FORMAT,
ACCESS_QUALIFIER,
>,
>
{
/// Sample texels at `coord` from the sampled image.
///
/// # Safety
/// Sampling with a type (`S`) that doesn't match the image's image format
/// will result in undefined behaviour.
#[crate::macros::gpu_only]
pub unsafe fn sample<F, V>(&self, coord: impl ImageCoordinate<F, DIM, ARRAYED>) -> V
where
F: Float,
V: Vector<SampledType, 4>,
{
let mut result = Default::default();
asm!(
"%sampledImage = OpLoad typeof*{1} {1}",
"%coord = OpLoad typeof*{2} {2}",
"%result = OpImageSampleImplicitLod typeof*{0} %sampledImage %coord",
"OpStore {0} %result",
in(reg) &mut result,
in(reg) self,
in(reg) &coord
);
result
}
}

View File

@ -0,0 +1,74 @@
use super::{Arrayed, Dimensionality, ImageFormat};
use crate::{scalar::Scalar, vector::Vector};
/// Marker trait for arguments that accept single scalar values or vectors
/// of scalars.
pub trait SampleType<const FORMAT: ImageFormat>: Scalar {}
impl SampleType<{ ImageFormat::Unknown }> for i8 {}
impl SampleType<{ ImageFormat::Unknown }> for i16 {}
impl SampleType<{ ImageFormat::Unknown }> for i32 {}
impl SampleType<{ ImageFormat::Unknown }> for i64 {}
impl SampleType<{ ImageFormat::Unknown }> for u8 {}
impl SampleType<{ ImageFormat::Unknown }> for u16 {}
impl SampleType<{ ImageFormat::Unknown }> for u32 {}
impl SampleType<{ ImageFormat::Unknown }> for u64 {}
impl SampleType<{ ImageFormat::Unknown }> for f32 {}
impl SampleType<{ ImageFormat::Unknown }> for f64 {}
impl SampleType<{ ImageFormat::Rgba32f }> for f32 {}
impl SampleType<{ ImageFormat::Rgba16f }> for f32 {}
impl SampleType<{ ImageFormat::R32f }> for f32 {}
impl SampleType<{ ImageFormat::Rgba8 }> for f32 {}
impl SampleType<{ ImageFormat::Rgba8Snorm }> for f32 {}
impl SampleType<{ ImageFormat::Rg32f }> for f32 {}
impl SampleType<{ ImageFormat::Rg16f }> for f32 {}
impl SampleType<{ ImageFormat::R11fG11fB10f }> for f32 {}
impl SampleType<{ ImageFormat::R16f }> for f32 {}
impl SampleType<{ ImageFormat::Rgba16 }> for f32 {}
impl SampleType<{ ImageFormat::Rgb10A2 }> for f32 {}
impl SampleType<{ ImageFormat::Rg16 }> for f32 {}
impl SampleType<{ ImageFormat::Rg8 }> for f32 {}
impl SampleType<{ ImageFormat::R16 }> for f32 {}
impl SampleType<{ ImageFormat::R8 }> for f32 {}
impl SampleType<{ ImageFormat::Rgba16Snorm }> for f32 {}
impl SampleType<{ ImageFormat::Rg16Snorm }> for f32 {}
impl SampleType<{ ImageFormat::Rg8Snorm }> for f32 {}
impl SampleType<{ ImageFormat::R16Snorm }> for f32 {}
impl SampleType<{ ImageFormat::R8Snorm }> for f32 {}
impl SampleType<{ ImageFormat::Rgba32i }> for i32 {}
impl SampleType<{ ImageFormat::Rgba16i }> for i32 {}
impl SampleType<{ ImageFormat::Rgba8i }> for i32 {}
impl SampleType<{ ImageFormat::R32i }> for i32 {}
impl SampleType<{ ImageFormat::Rg32i }> for i32 {}
impl SampleType<{ ImageFormat::Rg16i }> for i32 {}
impl SampleType<{ ImageFormat::Rg8i }> for i32 {}
impl SampleType<{ ImageFormat::R16i }> for i32 {}
impl SampleType<{ ImageFormat::R8i }> for i32 {}
impl SampleType<{ ImageFormat::Rgba32ui }> for u32 {}
impl SampleType<{ ImageFormat::Rgba16ui }> for u32 {}
impl SampleType<{ ImageFormat::Rgba8ui }> for u32 {}
impl SampleType<{ ImageFormat::R32ui }> for u32 {}
impl SampleType<{ ImageFormat::Rgb10A2ui }> for u32 {}
impl SampleType<{ ImageFormat::Rg32ui }> for u32 {}
impl SampleType<{ ImageFormat::Rg16ui }> for u32 {}
impl SampleType<{ ImageFormat::Rg8ui }> for u32 {}
impl SampleType<{ ImageFormat::R16ui }> for u32 {}
impl SampleType<{ ImageFormat::R8ui }> for u32 {}
impl SampleType<{ ImageFormat::R64ui }> for u64 {}
impl SampleType<{ ImageFormat::R64i }> for i64 {}
/// Marker trait for arguments that accept a coordinate for an [`crate::Image`].
pub trait ImageCoordinate<T, const DIM: Dimensionality, const ARRAYED: Arrayed> {}
impl<S: Scalar> ImageCoordinate<S, { Dimensionality::OneD }, { Arrayed::False }> for S {}
impl<S: Scalar> ImageCoordinate<S, { Dimensionality::Buffer }, { Arrayed::False }> for S {}
impl<V: Vector<S, 2>, S: Scalar> ImageCoordinate<S, { Dimensionality::TwoD }, { Arrayed::False }> for V {}
impl<V: Vector<S, 2>, S: Scalar> ImageCoordinate<S, { Dimensionality::Rect }, { Arrayed::False }> for V {}
impl<V: Vector<S, 3>, S: Scalar> ImageCoordinate<S, { Dimensionality::Cube }, { Arrayed::False }> for V {}
impl<V: Vector<S, 3>, S: Scalar> ImageCoordinate<S, { Dimensionality::ThreeD }, { Arrayed::False }> for V {}
impl<V: Vector<S, 3>, S: Scalar> ImageCoordinate<S, { Dimensionality::TwoD }, { Arrayed::True }> for V {}
impl<V: Vector<S, 3>, S: Scalar> ImageCoordinate<S, { Dimensionality::Rect }, { Arrayed::True }> for V {}
impl<V: Vector<S, 4>, S: Scalar> ImageCoordinate<S, { Dimensionality::Cube }, { Arrayed::True }> for V {}
impl<V: Vector<S, 4>, S: Scalar> ImageCoordinate<S, { Dimensionality::ThreeD }, { Arrayed::True }> for V {}

View File

@ -68,22 +68,28 @@
// We deblierately provide an unimplemented version of our API on CPU
// platforms so that code completion still works.
clippy::unimplemented,
// The part of `const-generics` we're using (C-like enums) is not incomplete.
incomplete_features,
)]
#[macro_use]
#[cfg(not(target_arch = "spirv"))]
#[cfg_attr(not(target_arch = "spirv"), macro_use)]
pub extern crate spirv_std_macros as macros;
pub mod arch;
pub mod float;
#[cfg(feature = "const-generics")]
pub mod image;
pub mod integer;
pub mod memory;
pub mod ray_tracing;
mod sampler;
pub mod scalar;
pub(crate) mod sealed;
mod textures;
pub mod vector;
pub use self::sampler::Sampler;
pub use crate::macros::Image;
pub use num_traits;
pub use textures::*;

View File

@ -0,0 +1,7 @@
/// An opaque reference to settings that describe how to access, filter, or
/// sample an image.
#[spirv(sampler)]
#[derive(Copy, Clone)]
pub struct Sampler {
_x: u32,
}

View File

@ -1,11 +1,11 @@
use crate::{integer::Integer, vector::Vector};
#[spirv(sampler)]
#[derive(Copy, Clone)]
pub struct Sampler {
_x: u32,
}
pub use crate::sampler::Sampler;
#[cfg_attr(
feature = "const-generics",
deprecated = "Legacy image type. Use `spirv_std::image::Image2d` instead."
)]
#[spirv(image_type(
// sampled_type is hardcoded to f32 for now
dim = "Dim2D",
@ -20,6 +20,7 @@ pub struct Image2d {
_x: u32,
}
#[allow(deprecated)]
impl Image2d {
#[spirv_std_macros::gpu_only]
#[doc(alias = "OpImageSampleImplicitLod")]
@ -412,6 +413,10 @@ impl Image2d {
}
}
#[cfg_attr(
feature = "const-generics",
deprecated = "Legacy image type. Use `spirv_std::image::StorageImage2d` instead."
)]
#[spirv(image_type(
// sampled_type is hardcoded to f32 for now
dim = "Dim2D",
@ -426,6 +431,7 @@ pub struct StorageImage2d {
_x: u32,
}
#[allow(deprecated)]
impl StorageImage2d {
/// Read a texel from an image without a sampler.
#[spirv_std_macros::gpu_only]
@ -474,6 +480,10 @@ impl StorageImage2d {
}
}
#[cfg_attr(
feature = "const-generics",
deprecated = "Legacy image type. Use `spirv_std::image::Image2dArray` instead."
)]
#[spirv(image_type(
// sampled_type is hardcoded to f32 for now
dim = "Dim2D",
@ -488,6 +498,7 @@ pub struct Image2dArray {
_x: u32,
}
#[allow(deprecated)]
impl Image2dArray {
#[spirv_std_macros::gpu_only]
#[doc(alias = "OpImageSampleImplicitLod")]
@ -673,6 +684,10 @@ impl Image2dArray {
}
}
#[cfg_attr(
feature = "const-generics",
deprecated = "Legacy image type. Use `spirv_std::image::Cubemap` instead."
)]
#[spirv(image_type(
// sampled_type is hardcoded to f32 for now
dim = "DimCube",
@ -687,6 +702,7 @@ pub struct Cubemap {
_x: u32,
}
#[allow(deprecated)]
impl Cubemap {
#[spirv_std_macros::gpu_only]
#[doc(alias = "OpSampledImage")]
@ -872,12 +888,17 @@ impl Cubemap {
}
}
#[cfg_attr(
feature = "const-generics",
deprecated = "Legacy image type. Use `spirv_std::image::SampledImage` instead."
)]
#[spirv(sampled_image)]
#[derive(Copy, Clone)]
pub struct SampledImage<I> {
_image: I,
}
#[allow(deprecated)]
impl SampledImage<Image2d> {
#[spirv_std_macros::gpu_only]
#[doc(alias = "OpImageSampleImplicitLod")]

View File

@ -9,5 +9,4 @@ license = "MIT OR Apache-2.0"
crate-type = ["dylib"]
[dependencies]
spirv-std-macros = { path = "../../../crates/spirv-std-macros" }
spirv-std = { path = "../../../crates/spirv-std" }

View File

@ -10,8 +10,7 @@
extern crate spirv_std;
#[cfg(not(target_arch = "spirv"))]
#[macro_use]
pub extern crate spirv_std_macros;
use spirv_std::macros::spirv;
// LocalSize/numthreads of (x = 32, y = 1, z = 1)
#[spirv(compute(threads(32)))]

View File

@ -7,6 +7,5 @@ license = "MIT OR Apache-2.0"
publish = false
[dependencies]
spirv-std-macros = { path = "../../../crates/spirv-std-macros" }
spirv-std = { path = "../../../crates/spirv-std" }
glam = { version = "0.14", default-features = false, features = ["libm", "scalar-math", "spirv-std"] }

View File

@ -10,6 +10,5 @@ publish = false
crate-type = ["dylib"]
[dependencies]
spirv-std-macros = { path = "../../../crates/spirv-std-macros" }
spirv-std = { path = "../../../crates/spirv-std" }
shared = { path = "../shared" }

View File

@ -8,8 +8,8 @@
#![deny(warnings)]
#[cfg(not(target_arch = "spirv"))]
#[macro_use]
pub extern crate spirv_std_macros;
use spirv_std::macros::spirv;
use shared::glam::{vec4, Vec4};
#[spirv(fragment)]

View File

@ -11,5 +11,4 @@ crate-type = ["dylib"]
[dependencies]
shared = { path = "../../shaders/shared" }
spirv-std-macros = { path = "../../../crates/spirv-std-macros" }
spirv-std = { path = "../../../crates/spirv-std" }

View File

@ -10,8 +10,7 @@
#![deny(warnings)]
#[cfg(not(target_arch = "spirv"))]
#[macro_use]
pub extern crate spirv_std_macros;
use spirv_std::macros::spirv;
use core::f32::consts::PI;
use glam::{const_vec3, vec2, vec3, Vec2, Vec3, Vec4};

View File

@ -1,9 +1,12 @@
// build-pass
use spirv_std::{arch, Image2d};
use spirv_std::{arch, Image};
#[spirv(fragment)]
pub fn main(#[spirv(descriptor_set = 0, binding = 0)] image: &Image2d, output: &mut glam::Vec4) {
pub fn main(
#[spirv(descriptor_set = 0, binding = 0)] image: &Image!(2D, type=f32, sampled),
output: &mut glam::Vec4,
) {
let texel = image.fetch(glam::IVec2::new(0, 1));
*output = texel;
}

12
tests/ui/image/format.rs Normal file
View File

@ -0,0 +1,12 @@
// build-pass
use spirv_std::{arch, Image};
#[spirv(fragment)]
pub fn main(
#[spirv(descriptor_set = 0, binding = 0)] image: &Image!(2D, format=rgba32f, sampled),
output: &mut glam::Vec4,
) {
let texel = image.fetch(glam::IVec2::new(0, 1));
*output = texel;
}

View File

@ -4,7 +4,7 @@ use glam::*;
pub fn main_cs(
#[spirv(global_invocation_id)] id: UVec3,
#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] points_buffer: &mut [UVec2; 100],
#[spirv(descriptor_set = 1, binding = 1)] image: &spirv_std::StorageImage2d,
#[spirv(descriptor_set = 1, binding = 1)] image: &spirv_std::Image!(2D, type=f32, sampled=false),
) {
unsafe { asm!("OpCapability StorageImageWriteWithoutFormat") };
let position = id.xy();

View File

@ -1,11 +1,11 @@
// Test `OpImageRead`
// build-pass
use spirv_std::{arch, StorageImage2d};
use spirv_std::{arch, Image};
#[spirv(fragment)]
pub fn main(
#[spirv(descriptor_set = 0, binding = 0)] image: &StorageImage2d,
#[spirv(descriptor_set = 0, binding = 0)] image: &Image!(2D, type=f32, sampled=false),
output: &mut glam::Vec4,
) {
unsafe { asm!("OpCapability StorageImageReadWithoutFormat") };

View File

@ -1,13 +1,13 @@
// Test `OpImageSampleImplicitLod`
// build-pass
use spirv_std::{arch, Cubemap, Image2d, Image2dArray, Sampler};
use spirv_std::{arch, Image, Sampler};
#[spirv(fragment)]
pub fn main(
#[spirv(descriptor_set = 0, binding = 0)] image2d: &Image2d,
#[spirv(descriptor_set = 1, binding = 1)] image2d_array: &Image2dArray,
#[spirv(descriptor_set = 2, binding = 2)] cubemap: &Cubemap,
#[spirv(descriptor_set = 0, binding = 0)] image2d: &Image!(2D, type=f32, sampled),
#[spirv(descriptor_set = 1, binding = 1)] image2d_array: &Image!(2D, type=f32, arrayed, sampled),
#[spirv(descriptor_set = 2, binding = 2)] cubemap: &Image!(3D, type=f32, sampled),
#[spirv(descriptor_set = 3, binding = 3)] sampler: &Sampler,
output: &mut glam::Vec4,
) {

View File

@ -1,13 +1,13 @@
// Test `OpImageSampleDrefImplicitLod`
// build-pass
use spirv_std::{arch, Cubemap, Image2d, Image2dArray, Sampler};
use spirv_std::{arch, Image, Sampler};
#[spirv(fragment)]
pub fn main(
#[spirv(descriptor_set = 0, binding = 0)] image: &Image2d,
#[spirv(descriptor_set = 1, binding = 1)] image_array: &Image2dArray,
#[spirv(descriptor_set = 2, binding = 2)] cubemap: &Cubemap,
#[spirv(descriptor_set = 0, binding = 0)] image: &Image!(2D, type=f32, sampled),
#[spirv(descriptor_set = 1, binding = 1)] image_array: &Image!(2D, type=f32, arrayed, sampled),
#[spirv(descriptor_set = 2, binding = 2)] cubemap: &Image!(cube, type=f32, sampled),
#[spirv(descriptor_set = 3, binding = 3)] sampler: &Sampler,
output: &mut f32,
) {

View File

@ -1,14 +1,14 @@
// Test `OpImageSampleDrefExplicitLod`
// build-pass
use spirv_std::{arch, Cubemap, Image2d, Image2dArray, Sampler};
use spirv_std::{Image, Sampler};
#[spirv(fragment)]
pub fn main(
#[spirv(descriptor_set = 0, binding = 0)] image: &Image2d,
#[spirv(descriptor_set = 1, binding = 1)] image_array: &Image2dArray,
#[spirv(descriptor_set = 0, binding = 0)] image: &Image!(2D, type=f32, sampled),
#[spirv(descriptor_set = 1, binding = 1)] image_array: &Image!(2D, type=f32, arrayed, sampled),
#[spirv(descriptor_set = 2, binding = 2)] sampler: &Sampler,
#[spirv(descriptor_set = 3, binding = 3)] cubemap: &Cubemap,
#[spirv(descriptor_set = 3, binding = 3)] cubemap: &Image!(3D, type=f32, sampled),
output: &mut f32,
) {
let v2 = glam::Vec2::new(0.0, 1.0);

View File

@ -1,14 +1,14 @@
// Test `OpImageSampleDrefExplicitLod`
// build-pass
use spirv_std::{arch, Cubemap, Image2d, Image2dArray, Sampler};
use spirv_std::{Image, Sampler};
#[spirv(fragment)]
pub fn main(
#[spirv(descriptor_set = 0, binding = 0)] image: &Image2d,
#[spirv(descriptor_set = 1, binding = 1)] image_array: &Image2dArray,
#[spirv(descriptor_set = 0, binding = 0)] image: &Image!(2D, type=f32, sampled),
#[spirv(descriptor_set = 1, binding = 1)] image_array: &Image!(2D, type=f32, arrayed, sampled),
#[spirv(descriptor_set = 2, binding = 2)] sampler: &Sampler,
#[spirv(descriptor_set = 3, binding = 3)] cubemap: &Cubemap,
#[spirv(descriptor_set = 3, binding = 3)] cubemap: &Image!(3D, type=f32),
output: &mut f32,
) {
let v2 = glam::Vec2::new(0.0, 1.0);

View File

@ -1,11 +1,11 @@
// Test `OpImageSampleProjDrefImplicitLod`
// build-pass
use spirv_std::{arch, Image2d, Sampler};
use spirv_std::{arch, Image, Sampler};
#[spirv(fragment)]
pub fn main(
#[spirv(descriptor_set = 0, binding = 0)] image: &Image2d,
#[spirv(descriptor_set = 0, binding = 0)] image: &Image!(2D, type=f32, sampled),
#[spirv(descriptor_set = 1, binding = 1)] sampler: &Sampler,
output: &mut f32,
) {

View File

@ -1,11 +1,11 @@
// Test `OpImageSampleProjDrefExplicitLod`
// build-pass
use spirv_std::{arch, Image2d, Sampler};
use spirv_std::{Image, Sampler};
#[spirv(fragment)]
pub fn main(
#[spirv(descriptor_set = 0, binding = 0)] image: &Image2d,
#[spirv(descriptor_set = 0, binding = 0)] image: &Image!(2D, type=f32, sampled),
#[spirv(descriptor_set = 1, binding = 1)] sampler: &Sampler,
output: &mut f32,
) {

View File

@ -1,11 +1,11 @@
// Test `OpImageSampleProjDrefExplicitLod`
// build-pass
use spirv_std::{arch, Image2d, Sampler};
use spirv_std::{Image, Sampler};
#[spirv(fragment)]
pub fn main(
#[spirv(descriptor_set = 0, binding = 0)] image: &Image2d,
#[spirv(descriptor_set = 0, binding = 0)] image: &Image!(2D, type=f32, sampled),
#[spirv(descriptor_set = 1, binding = 1)] sampler: &Sampler,
output: &mut f32,
) {

View File

@ -1,13 +1,13 @@
// Test `OpImageSampleExplicitLod` Grad
// build-pass
use spirv_std::{arch, Cubemap, Image2d, Image2dArray, Sampler};
use spirv_std::{arch, Image, Sampler};
#[spirv(fragment)]
pub fn main(
#[spirv(descriptor_set = 0, binding = 0)] image2d: &Image2d,
#[spirv(descriptor_set = 1, binding = 1)] image2d_array: &Image2dArray,
#[spirv(descriptor_set = 2, binding = 2)] cubemap: &Cubemap,
#[spirv(descriptor_set = 0, binding = 0)] image2d: &Image!(2D, type=f32, sampled),
#[spirv(descriptor_set = 1, binding = 1)] image2d_array: &Image!(2D, type=f32, arrayed, sampled),
#[spirv(descriptor_set = 2, binding = 2)] cubemap: &Image!(3D, type=f32, sampled),
#[spirv(descriptor_set = 3, binding = 3)] sampler: &Sampler,
output: &mut glam::Vec4,
) {

View File

@ -1,13 +1,13 @@
// Test `OpImageSampleExplicitLod` Lod
// build-pass
use spirv_std::{arch, Cubemap, Image2d, Image2dArray, Sampler};
use spirv_std::{arch, Image, Sampler};
#[spirv(fragment)]
pub fn main(
#[spirv(descriptor_set = 0, binding = 0)] image2d: &Image2d,
#[spirv(descriptor_set = 1, binding = 1)] image2d_array: &Image2dArray,
#[spirv(descriptor_set = 2, binding = 2)] cubemap: &Cubemap,
#[spirv(descriptor_set = 0, binding = 0)] image2d: &Image!(2D, type=f32, sampled),
#[spirv(descriptor_set = 1, binding = 1)] image2d_array: &Image!(2D, type=f32, arrayed, sampled),
#[spirv(descriptor_set = 2, binding = 2)] cubemap: &Image!(3D, type=f32, sampled),
#[spirv(descriptor_set = 3, binding = 3)] sampler: &Sampler,
output: &mut glam::Vec4,
) {

View File

@ -1,11 +1,11 @@
// Test `OpImageSampleProjImplicitLod`
// build-pass
use spirv_std::{arch, Image2d, Sampler};
use spirv_std::{Image, Sampler};
#[spirv(fragment)]
pub fn main(
#[spirv(descriptor_set = 0, binding = 0)] image2d: &Image2d,
#[spirv(descriptor_set = 0, binding = 0)] image2d: &Image!(2D, type=f32, sampled),
#[spirv(descriptor_set = 1, binding = 1)] sampler: &Sampler,
output: &mut glam::Vec4,
) {

View File

@ -1,11 +1,11 @@
// Test `OpImageSampleProjExplicitLod`
// build-pass
use spirv_std::{arch, Image2d, Sampler};
use spirv_std::{arch, Image, Sampler};
#[spirv(fragment)]
pub fn main(
#[spirv(descriptor_set = 0, binding = 0)] image2d: &Image2d,
#[spirv(descriptor_set = 0, binding = 0)] image2d: &Image!(2D, type=f32, sampled),
#[spirv(descriptor_set = 1, binding = 1)] sampler: &Sampler,
output: &mut glam::Vec4,
) {

View File

@ -1,11 +1,11 @@
// Test `OpImageSampleProjExplicitLod`
// build-pass
use spirv_std::{arch, Image2d, Sampler};
use spirv_std::{arch, Image, Sampler};
#[spirv(fragment)]
pub fn main(
#[spirv(descriptor_set = 0, binding = 0)] image2d: &Image2d,
#[spirv(descriptor_set = 0, binding = 0)] image2d: &Image!(2D, type=f32, sampled),
#[spirv(descriptor_set = 1, binding = 1)] sampler: &Sampler,
output: &mut glam::Vec4,
) {

View File

@ -1,10 +1,13 @@
// Test `OpImageWrite`
// build-pass
use spirv_std::{arch, StorageImage2d};
use spirv_std::{arch, Image};
#[spirv(fragment)]
pub fn main(texels: glam::Vec2, #[spirv(descriptor_set = 0, binding = 0)] image: &StorageImage2d) {
pub fn main(
texels: glam::Vec2,
#[spirv(descriptor_set = 0, binding = 0)] image: &Image!(2D, type=f32, sampled=false),
) {
unsafe {
asm!("OpCapability StorageImageWriteWithoutFormat");
image.write(glam::UVec2::new(0, 1), texels);

View File

@ -1,11 +1,15 @@
// Tests that storage class inference fails correctly
// build-fail
use spirv_std::Image2d;
use spirv_std::Image;
#[spirv(vertex)]
pub fn main(#[spirv(uniform)] error: &Image2d, #[spirv(uniform_constant)] warning: &Image2d) {}
pub fn main(
#[spirv(uniform)] error: &Image!(2D, type=f32),
#[spirv(uniform_constant)] warning: &Image!(2D, type=f32),
) {
}
// https://github.com/EmbarkStudios/rust-gpu/issues/585
#[spirv(vertex)]
pub fn issue_585(invalid: Image2d) {}
pub fn issue_585(invalid: Image!(2D, type=f32)) {}

View File

@ -1,29 +1,31 @@
error: storage class mismatch
--> $DIR/bad-infer-storage-class.rs:7:13
--> $DIR/bad-infer-storage-class.rs:8:5
|
7 | pub fn main(#[spirv(uniform)] error: &Image2d, #[spirv(uniform_constant)] warning: &Image2d) {}
| ^^^^^^^^-------^^^^^^^^^^--------
| | |
| | UniformConstant inferred from type
| Uniform specified in attribute
8 | #[spirv(uniform)] error: &Image!(2D, type=f32),
| ^^^^^^^^-------^^^^^^^^^^---------------------
| | |
| | UniformConstant inferred from type
| Uniform specified in attribute
|
help: remove storage class attribute to use UniformConstant as storage class
--> $DIR/bad-infer-storage-class.rs:7:21
--> $DIR/bad-infer-storage-class.rs:8:13
|
7 | pub fn main(#[spirv(uniform)] error: &Image2d, #[spirv(uniform_constant)] warning: &Image2d) {}
| ^^^^^^^
8 | #[spirv(uniform)] error: &Image!(2D, type=f32),
| ^^^^^^^
warning: redundant storage class specifier, storage class is inferred from type
--> $DIR/bad-infer-storage-class.rs:7:56
--> $DIR/bad-infer-storage-class.rs:9:13
|
7 | pub fn main(#[spirv(uniform)] error: &Image2d, #[spirv(uniform_constant)] warning: &Image2d) {}
| ^^^^^^^^^^^^^^^^
9 | #[spirv(uniform_constant)] warning: &Image!(2D, type=f32),
| ^^^^^^^^^^^^^^^^
error: entry parameter type must be by-reference: `&spirv_std::Image2d`
--> $DIR/bad-infer-storage-class.rs:11:27
error: entry parameter type must be by-reference: `&spirv_std::image::Image<f32, spirv_std::image::Dimensionality::TwoD, spirv_std::image::ImageDepth::Unknown, spirv_std::image::Arrayed::False, spirv_std::image::Multisampled::False, spirv_std::image::Sampled::Unknown, spirv_std::image::ImageFormat::Unknown, core::option::Option::<spirv_std::image::AccessQualifier>::None>`
--> $DIR/bad-infer-storage-class.rs:15:27
|
11 | pub fn issue_585(invalid: Image2d) {}
| ^^^^^^^
15 | pub fn issue_585(invalid: Image!(2D, type=f32)) {}
| ^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 2 previous errors; 1 warning emitted