use crate::std::{
ModuleVar, ModuleVarReadGuard, Vec2f, Vec2i, Vec2u, Vec3f, Vec3i, Vec3u, Vec4f, Vec4i, Vec4u,
Wgsl, WgslTextureScalar, vec2f, vec2u, vec3u, vec4f, vec4i, vec4u,
};
use std::marker::PhantomData;
mod builtins;
pub use builtins::*;
use wgsl_rs_ir as ir;
pub struct Sampler {
group: u32,
binding: u32,
data: ModuleVar<SamplerState>,
}
impl Wgsl for Sampler {
fn to_ir() -> wgsl_rs_ir::Type {
wgsl_rs_ir::Type::Sampler
}
}
impl Sampler {
pub const fn new(group: u32, binding: u32) -> Self {
Self {
group,
binding,
data: ModuleVar::new(),
}
}
pub fn group(&self) -> u32 {
self.group
}
pub fn binding(&self) -> u32 {
self.binding
}
pub fn get(&self) -> ModuleVarReadGuard<'_, SamplerState> {
self.data.read()
}
pub fn set(&self, state: SamplerState) {
self.data.set(state);
}
pub fn init(&self) {
self.set(SamplerState::default());
}
}
pub struct SamplerComparison {
group: u32,
binding: u32,
data: ModuleVar<SamplerComparisonState>,
}
impl Wgsl for SamplerComparison {
fn to_ir() -> wgsl_rs_ir::Type {
wgsl_rs_ir::Type::SamplerComparison
}
}
impl SamplerComparison {
pub const fn new(group: u32, binding: u32) -> Self {
Self {
group,
binding,
data: ModuleVar::new(),
}
}
pub fn group(&self) -> u32 {
self.group
}
pub fn binding(&self) -> u32 {
self.binding
}
pub fn get(&self) -> ModuleVarReadGuard<'_, SamplerComparisonState> {
self.data.read()
}
pub fn set(&self, state: SamplerComparisonState) {
self.data.set(state);
}
}
pub struct Texture1D<T> {
group: u32,
binding: u32,
data: ModuleVar<TextureData1D<T>>,
}
impl<T: WgslTextureScalar> Wgsl for Texture1D<T> {
fn to_ir() -> ir::Type {
ir::Type::Texture {
kind: ir::TextureKind::Texture1D,
sampled_type: T::to_scalar_ir(),
}
}
}
impl<T: WgslTextureScalar> Texture1D<T> {
pub const fn new(group: u32, binding: u32) -> Self {
Self {
group,
binding,
data: ModuleVar::new(),
}
}
pub fn group(&self) -> u32 {
self.group
}
pub fn binding(&self) -> u32 {
self.binding
}
pub fn get(&self) -> ModuleVarReadGuard<'_, TextureData1D<T>> {
self.data.read()
}
pub fn set(&self, data: TextureData1D<T>) {
self.data.set(data);
}
}
impl<T: WgslTextureScalar + Default + Copy> Texture1D<T> {
pub fn init(&self, width: u32) {
self.set(TextureData1D::new(width));
}
}
impl<T: WgslTextureScalar> TextureDimensionsQuery for Texture1D<T> {
type Output = u32;
fn query_dimensions(&self, level: u32) -> Self::Output {
self.get().width(level)
}
}
impl<T: WgslTextureScalar> TextureNumLevelsQuery for Texture1D<T> {
fn query_num_levels(&self) -> u32 {
self.get().num_levels()
}
}
impl<L: IntoLevel> TextureLoad<u32, L> for Texture1D<f32> {
type Output = Vec4f;
fn load(&self, coords: u32, level: L) -> Self::Output {
let level = level.into_level();
self.get()
.get_pixel(coords, level)
.map(|p| vec4f(p[0], p[1], p[2], p[3]))
.unwrap_or(vec4f(0.0, 0.0, 0.0, 0.0))
}
}
impl<L: IntoLevel> TextureLoad<i32, L> for Texture1D<f32> {
type Output = Vec4f;
fn load(&self, coords: i32, level: L) -> Self::Output {
let x = if coords < 0 { 0u32 } else { coords as u32 };
let level = level.into_level();
self.get()
.get_pixel(x, level)
.map(|p| vec4f(p[0], p[1], p[2], p[3]))
.unwrap_or(vec4f(0.0, 0.0, 0.0, 0.0))
}
}
impl<L: IntoLevel> TextureLoad<u32, L> for Texture1D<i32> {
type Output = Vec4i;
fn load(&self, coords: u32, level: L) -> Self::Output {
let level = level.into_level();
self.get()
.get_pixel(coords, level)
.map(|p| vec4i(p[0], p[1], p[2], p[3]))
.unwrap_or(vec4i(0, 0, 0, 0))
}
}
impl<L: IntoLevel> TextureLoad<i32, L> for Texture1D<i32> {
type Output = Vec4i;
fn load(&self, coords: i32, level: L) -> Self::Output {
let x = if coords < 0 { 0u32 } else { coords as u32 };
let level = level.into_level();
self.get()
.get_pixel(x, level)
.map(|p| vec4i(p[0], p[1], p[2], p[3]))
.unwrap_or(vec4i(0, 0, 0, 0))
}
}
impl<L: IntoLevel> TextureLoad<u32, L> for Texture1D<u32> {
type Output = Vec4u;
fn load(&self, coords: u32, level: L) -> Self::Output {
let level = level.into_level();
self.get()
.get_pixel(coords, level)
.map(|p| vec4u(p[0], p[1], p[2], p[3]))
.unwrap_or(vec4u(0, 0, 0, 0))
}
}
impl<L: IntoLevel> TextureLoad<i32, L> for Texture1D<u32> {
type Output = Vec4u;
fn load(&self, coords: i32, level: L) -> Self::Output {
let x = if coords < 0 { 0u32 } else { coords as u32 };
let level = level.into_level();
self.get()
.get_pixel(x, level)
.map(|p| vec4u(p[0], p[1], p[2], p[3]))
.unwrap_or(vec4u(0, 0, 0, 0))
}
}
impl TextureSample<f32> for Texture1D<f32> {
type Output = Vec4f;
fn sample(&self, sampler: &Sampler, coords: f32) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let result = sample_texture_1d(&data, &sampler_state, coords, 0);
vec4f(result[0], result[1], result[2], result[3])
}
}
impl TextureSampleLevel<f32, f32> for Texture1D<f32> {
type Output = Vec4f;
fn sample_level(&self, sampler: &Sampler, coords: f32, level: f32) -> Self::Output {
let sampler_state = sampler.get();
let level = level.floor() as u32;
let data = self.get();
let result = sample_texture_1d(&data, &sampler_state, coords, level);
vec4f(result[0], result[1], result[2], result[3])
}
}
impl TextureSampleBias<f32, f32> for Texture1D<f32> {
type Output = Vec4f;
fn sample_bias(&self, sampler: &Sampler, coords: f32, _bias: f32) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let result = sample_texture_1d(&data, &sampler_state, coords, 0);
vec4f(result[0], result[1], result[2], result[3])
}
}
impl TextureSampleGrad<f32, f32, f32> for Texture1D<f32> {
type Output = Vec4f;
fn sample_grad(&self, sampler: &Sampler, coords: f32, _ddx: f32, _ddy: f32) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let result = sample_texture_1d(&data, &sampler_state, coords, 0);
vec4f(result[0], result[1], result[2], result[3])
}
}
pub struct Texture2D<T> {
group: u32,
binding: u32,
data: ModuleVar<TextureData2D<T>>,
}
impl<T: WgslTextureScalar> Wgsl for Texture2D<T> {
fn to_ir() -> wgsl_rs_ir::Type {
wgsl_rs_ir::Type::Texture {
kind: wgsl_rs_ir::TextureKind::Texture2D,
sampled_type: T::to_scalar_ir(),
}
}
}
impl<T: WgslTextureScalar> Texture2D<T> {
pub const fn new(group: u32, binding: u32) -> Self {
Self {
group,
binding,
data: ModuleVar::new(),
}
}
pub fn group(&self) -> u32 {
self.group
}
pub fn binding(&self) -> u32 {
self.binding
}
pub fn get(&self) -> ModuleVarReadGuard<'_, TextureData2D<T>> {
self.data.read()
}
pub fn set(&self, data: TextureData2D<T>) {
self.data.set(data);
}
}
impl<T: WgslTextureScalar + Default + Copy> Texture2D<T> {
pub fn init(&self, width: u32, height: u32) {
self.set(TextureData2D::new(width, height));
}
pub fn set_pixel(&self, x: u32, y: u32, value: [T; 4]) {
self.data.write().set_pixel(x, y, 0, value);
}
}
impl<T: WgslTextureScalar> TextureDimensionsQuery for Texture2D<T> {
type Output = Vec2u;
fn query_dimensions(&self, level: u32) -> Self::Output {
let (w, h) = self.get().dimensions(level);
vec2u(w, h)
}
}
impl<T: WgslTextureScalar> TextureNumLevelsQuery for Texture2D<T> {
fn query_num_levels(&self) -> u32 {
self.get().num_levels()
}
}
impl<L: IntoLevel> TextureLoad<Vec2u, L> for Texture2D<f32> {
type Output = Vec4f;
fn load(&self, coords: Vec2u, level: L) -> Self::Output {
let level = level.into_level();
self.get()
.get_pixel(coords.x(), coords.y(), level)
.map(|p| vec4f(p[0], p[1], p[2], p[3]))
.unwrap_or(vec4f(0.0, 0.0, 0.0, 0.0))
}
}
impl<L: IntoLevel> TextureLoad<Vec2i, L> for Texture2D<f32> {
type Output = Vec4f;
fn load(&self, coords: Vec2i, level: L) -> Self::Output {
let x = if coords.x() < 0 {
0u32
} else {
coords.x() as u32
};
let y = if coords.y() < 0 {
0u32
} else {
coords.y() as u32
};
self.load(vec2u(x, y), level)
}
}
impl<L: IntoLevel> TextureLoad<Vec2u, L> for Texture2D<i32> {
type Output = Vec4i;
fn load(&self, coords: Vec2u, level: L) -> Self::Output {
let level = level.into_level();
self.get()
.get_pixel(coords.x(), coords.y(), level)
.map(|p| vec4i(p[0], p[1], p[2], p[3]))
.unwrap_or(vec4i(0, 0, 0, 0))
}
}
impl<L: IntoLevel> TextureLoad<Vec2i, L> for Texture2D<i32> {
type Output = Vec4i;
fn load(&self, coords: Vec2i, level: L) -> Self::Output {
let x = if coords.x() < 0 {
0u32
} else {
coords.x() as u32
};
let y = if coords.y() < 0 {
0u32
} else {
coords.y() as u32
};
self.load(vec2u(x, y), level)
}
}
impl<L: IntoLevel> TextureLoad<Vec2u, L> for Texture2D<u32> {
type Output = Vec4u;
fn load(&self, coords: Vec2u, level: L) -> Self::Output {
let level = level.into_level();
self.get()
.get_pixel(coords.x(), coords.y(), level)
.map(|p| vec4u(p[0], p[1], p[2], p[3]))
.unwrap_or(vec4u(0, 0, 0, 0))
}
}
impl<L: IntoLevel> TextureLoad<Vec2i, L> for Texture2D<u32> {
type Output = Vec4u;
fn load(&self, coords: Vec2i, level: L) -> Self::Output {
let x = if coords.x() < 0 {
0u32
} else {
coords.x() as u32
};
let y = if coords.y() < 0 {
0u32
} else {
coords.y() as u32
};
let level = level.into_level();
self.get()
.get_pixel(x, y, level)
.map(|p| vec4u(p[0], p[1], p[2], p[3]))
.unwrap_or(vec4u(0, 0, 0, 0))
}
}
impl TextureSample<Vec2f> for Texture2D<f32> {
type Output = Vec4f;
fn sample(&self, sampler: &Sampler, coords: Vec2f) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let result = sample_texture_2d(&data, &sampler_state, coords.x(), coords.y(), 0);
vec4f(result[0], result[1], result[2], result[3])
}
}
impl TextureSampleLevel<Vec2f, f32> for Texture2D<f32> {
type Output = Vec4f;
fn sample_level(&self, sampler: &Sampler, coords: Vec2f, level: f32) -> Self::Output {
let sampler_state = sampler.get();
let level = level.floor() as u32; let data = self.get();
let result = sample_texture_2d(&data, &sampler_state, coords.x(), coords.y(), level);
vec4f(result[0], result[1], result[2], result[3])
}
}
impl TextureSampleBaseClampToEdge<Vec2f> for Texture2D<f32> {
type Output = Vec4f;
fn sample_base_clamp_to_edge(&self, sampler: &Sampler, coords: Vec2f) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let (w, h) = data.dimensions(0);
let half_texel_u = if w > 0 { 0.5 / w as f32 } else { 0.0 };
let half_texel_v = if h > 0 { 0.5 / h as f32 } else { 0.0 };
let u = coords.x().clamp(half_texel_u, 1.0 - half_texel_u);
let v = coords.y().clamp(half_texel_v, 1.0 - half_texel_v);
let result = sample_texture_2d(&data, &sampler_state, u, v, 0);
vec4f(result[0], result[1], result[2], result[3])
}
}
impl TextureSampleOffset<Vec2f, Vec2i> for Texture2D<f32> {
type Output = Vec4f;
fn sample_offset(&self, sampler: &Sampler, coords: Vec2f, offset: Vec2i) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let (w, h) = data.dimensions(0);
let u = coords.x() + offset.x() as f32 / w.max(1) as f32;
let v = coords.y() + offset.y() as f32 / h.max(1) as f32;
let result = sample_texture_2d(&data, &sampler_state, u, v, 0);
vec4f(result[0], result[1], result[2], result[3])
}
}
impl TextureSampleLevelOffset<Vec2f, f32, Vec2i> for Texture2D<f32> {
type Output = Vec4f;
fn sample_level_offset(
&self,
sampler: &Sampler,
coords: Vec2f,
level: f32,
offset: Vec2i,
) -> Self::Output {
let sampler_state = sampler.get();
let level = level.floor() as u32;
let data = self.get();
let (w, h) = data.dimensions(level);
let u = coords.x() + offset.x() as f32 / w.max(1) as f32;
let v = coords.y() + offset.y() as f32 / h.max(1) as f32;
let result = sample_texture_2d(&data, &sampler_state, u, v, level);
vec4f(result[0], result[1], result[2], result[3])
}
}
impl TextureSampleBias<Vec2f, f32> for Texture2D<f32> {
type Output = Vec4f;
fn sample_bias(&self, sampler: &Sampler, coords: Vec2f, _bias: f32) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let result = sample_texture_2d(&data, &sampler_state, coords.x(), coords.y(), 0);
vec4f(result[0], result[1], result[2], result[3])
}
}
impl TextureSampleBiasOffset<Vec2f, f32, Vec2i> for Texture2D<f32> {
type Output = Vec4f;
fn sample_bias_offset(
&self,
sampler: &Sampler,
coords: Vec2f,
_bias: f32,
offset: Vec2i,
) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let (w, h) = data.dimensions(0);
let u = coords.x() + offset.x() as f32 / w.max(1) as f32;
let v = coords.y() + offset.y() as f32 / h.max(1) as f32;
let result = sample_texture_2d(&data, &sampler_state, u, v, 0);
vec4f(result[0], result[1], result[2], result[3])
}
}
impl TextureSampleGrad<Vec2f, Vec2f, Vec2f> for Texture2D<f32> {
type Output = Vec4f;
fn sample_grad(
&self,
sampler: &Sampler,
coords: Vec2f,
_ddx: Vec2f,
_ddy: Vec2f,
) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let result = sample_texture_2d(&data, &sampler_state, coords.x(), coords.y(), 0);
vec4f(result[0], result[1], result[2], result[3])
}
}
impl TextureSampleGradOffset<Vec2f, Vec2f, Vec2f, Vec2i> for Texture2D<f32> {
type Output = Vec4f;
fn sample_grad_offset(
&self,
sampler: &Sampler,
coords: Vec2f,
_ddx: Vec2f,
_ddy: Vec2f,
offset: Vec2i,
) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let (w, h) = data.dimensions(0);
let u = coords.x() + offset.x() as f32 / w.max(1) as f32;
let v = coords.y() + offset.y() as f32 / h.max(1) as f32;
let result = sample_texture_2d(&data, &sampler_state, u, v, 0);
vec4f(result[0], result[1], result[2], result[3])
}
}
impl TextureGather<Vec2f> for Texture2D<f32> {
type Output = Vec4f;
fn gather(&self, component: u32, sampler: &Sampler, coords: Vec2f) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let g = gather_texture_2d(&data, &sampler_state, coords.x(), coords.y(), component);
vec4f(g[0], g[1], g[2], g[3])
}
}
impl TextureGatherOffset<Vec2f, Vec2i> for Texture2D<f32> {
type Output = Vec4f;
fn gather_offset(
&self,
component: u32,
sampler: &Sampler,
coords: Vec2f,
offset: Vec2i,
) -> Self::Output {
let data = self.get();
let (w, h) = data.dimensions(0);
let offset_u = coords.x() + offset.x() as f32 / w.max(1) as f32;
let offset_v = coords.y() + offset.y() as f32 / h.max(1) as f32;
drop(data);
self.gather(component, sampler, vec2f(offset_u, offset_v))
}
}
impl TextureGather<Vec2f> for Texture2D<i32> {
type Output = Vec4i;
fn gather(&self, component: u32, sampler: &Sampler, coords: Vec2f) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let g = gather_texture_2d(&data, &sampler_state, coords.x(), coords.y(), component);
vec4i(g[0], g[1], g[2], g[3])
}
}
impl TextureGather<Vec2f> for Texture2D<u32> {
type Output = Vec4u;
fn gather(&self, component: u32, sampler: &Sampler, coords: Vec2f) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let g = gather_texture_2d(&data, &sampler_state, coords.x(), coords.y(), component);
vec4u(g[0], g[1], g[2], g[3])
}
}
impl TextureGatherOffset<Vec2f, Vec2i> for Texture2D<i32> {
type Output = Vec4i;
fn gather_offset(
&self,
component: u32,
sampler: &Sampler,
coords: Vec2f,
offset: Vec2i,
) -> Self::Output {
let data = self.get();
let (w, h) = data.dimensions(0);
let offset_u = coords.x() + offset.x() as f32 / w.max(1) as f32;
let offset_v = coords.y() + offset.y() as f32 / h.max(1) as f32;
drop(data);
self.gather(component, sampler, vec2f(offset_u, offset_v))
}
}
impl TextureGatherOffset<Vec2f, Vec2i> for Texture2D<u32> {
type Output = Vec4u;
fn gather_offset(
&self,
component: u32,
sampler: &Sampler,
coords: Vec2f,
offset: Vec2i,
) -> Self::Output {
let data = self.get();
let (w, h) = data.dimensions(0);
let offset_u = coords.x() + offset.x() as f32 / w.max(1) as f32;
let offset_v = coords.y() + offset.y() as f32 / h.max(1) as f32;
drop(data);
self.gather(component, sampler, vec2f(offset_u, offset_v))
}
}
pub struct Texture2DArray<T> {
group: u32,
binding: u32,
data: ModuleVar<TextureData2DArray<T>>,
}
impl<T: WgslTextureScalar> Wgsl for Texture2DArray<T> {
fn to_ir() -> wgsl_rs_ir::Type {
wgsl_rs_ir::Type::Texture {
kind: wgsl_rs_ir::TextureKind::Texture2DArray,
sampled_type: T::to_scalar_ir(),
}
}
}
impl<T: WgslTextureScalar> Texture2DArray<T> {
pub const fn new(group: u32, binding: u32) -> Self {
Self {
group,
binding,
data: ModuleVar::new(),
}
}
pub fn group(&self) -> u32 {
self.group
}
pub fn binding(&self) -> u32 {
self.binding
}
pub fn get(&self) -> ModuleVarReadGuard<'_, TextureData2DArray<T>> {
self.data.read()
}
pub fn set(&self, data: TextureData2DArray<T>) {
self.data.set(data);
}
}
impl<T: WgslTextureScalar + Default + Copy> Texture2DArray<T> {
pub fn init(&self, width: u32, height: u32, layers: u32) {
self.set(TextureData2DArray::new(width, height, layers))
}
}
impl<T: WgslTextureScalar> TextureDimensionsQuery for Texture2DArray<T> {
type Output = Vec2u;
fn query_dimensions(&self, level: u32) -> Self::Output {
let (w, h) = self.get().dimensions(level);
vec2u(w, h)
}
}
impl<T: WgslTextureScalar> TextureNumLayersQuery for Texture2DArray<T> {
fn query_num_layers(&self) -> u32 {
self.get().num_layers()
}
}
impl<T: WgslTextureScalar + Default + Clone> TextureNumLevelsQuery for Texture2DArray<T> {
fn query_num_levels(&self) -> u32 {
self.get().num_levels()
}
}
impl TextureSampleArray<Vec2f, u32> for Texture2DArray<f32> {
type Output = Vec4f;
fn sample_array(&self, sampler: &Sampler, coords: Vec2f, array_index: u32) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let layer = array_index.min(data.num_layers().saturating_sub(1));
if let Some(layer_data) = data.layers.get(layer as usize) {
let result = sample_texture_2d(layer_data, &sampler_state, coords.x(), coords.y(), 0);
vec4f(result[0], result[1], result[2], result[3])
} else {
vec4f(0.0, 0.0, 0.0, 0.0)
}
}
}
impl TextureSampleArray<Vec2f, i32> for Texture2DArray<f32> {
type Output = Vec4f;
fn sample_array(&self, sampler: &Sampler, coords: Vec2f, array_index: i32) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let layer = (array_index.max(0) as u32).min(data.num_layers().saturating_sub(1));
if let Some(layer_data) = data.layers.get(layer as usize) {
let result = sample_texture_2d(layer_data, &sampler_state, coords.x(), coords.y(), 0);
vec4f(result[0], result[1], result[2], result[3])
} else {
vec4f(0.0, 0.0, 0.0, 0.0)
}
}
}
impl TextureSampleArrayOffset<Vec2f, u32, Vec2i> for Texture2DArray<f32> {
type Output = Vec4f;
fn sample_array_offset(
&self,
sampler: &Sampler,
coords: Vec2f,
array_index: u32,
offset: Vec2i,
) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let layer = array_index.min(data.num_layers().saturating_sub(1));
if let Some(layer_data) = data.layers.get(layer as usize) {
let (w, h) = layer_data.dimensions(0);
let u = coords.x() + offset.x() as f32 / w.max(1) as f32;
let v = coords.y() + offset.y() as f32 / h.max(1) as f32;
let result = sample_texture_2d(layer_data, &sampler_state, u, v, 0);
vec4f(result[0], result[1], result[2], result[3])
} else {
vec4f(0.0, 0.0, 0.0, 0.0)
}
}
}
impl TextureSampleLevelArray<Vec2f, u32, f32> for Texture2DArray<f32> {
type Output = Vec4f;
fn sample_level_array(
&self,
sampler: &Sampler,
coords: Vec2f,
array_index: u32,
level: f32,
) -> Self::Output {
let sampler_state = sampler.get();
let level = level.floor() as u32;
let data = self.get();
let layer = array_index.min(data.num_layers().saturating_sub(1));
if let Some(layer_data) = data.layers.get(layer as usize) {
let result =
sample_texture_2d(layer_data, &sampler_state, coords.x(), coords.y(), level);
vec4f(result[0], result[1], result[2], result[3])
} else {
vec4f(0.0, 0.0, 0.0, 0.0)
}
}
}
impl TextureSampleLevelArrayOffset<Vec2f, u32, f32, Vec2i> for Texture2DArray<f32> {
type Output = Vec4f;
fn sample_level_array_offset(
&self,
sampler: &Sampler,
coords: Vec2f,
array_index: u32,
level: f32,
offset: Vec2i,
) -> Self::Output {
let sampler_state = sampler.get();
let level = level.floor() as u32;
let data = self.get();
let layer = array_index.min(data.num_layers().saturating_sub(1));
if let Some(layer_data) = data.layers.get(layer as usize) {
let (w, h) = layer_data.dimensions(level);
let u = coords.x() + offset.x() as f32 / w.max(1) as f32;
let v = coords.y() + offset.y() as f32 / h.max(1) as f32;
let result = sample_texture_2d(layer_data, &sampler_state, u, v, level);
vec4f(result[0], result[1], result[2], result[3])
} else {
vec4f(0.0, 0.0, 0.0, 0.0)
}
}
}
impl TextureSampleBiasArray<Vec2f, u32, f32> for Texture2DArray<f32> {
type Output = Vec4f;
fn sample_bias_array(
&self,
sampler: &Sampler,
coords: Vec2f,
array_index: u32,
_bias: f32,
) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let layer = array_index.min(data.num_layers().saturating_sub(1));
if let Some(layer_data) = data.layers.get(layer as usize) {
let result = sample_texture_2d(layer_data, &sampler_state, coords.x(), coords.y(), 0);
vec4f(result[0], result[1], result[2], result[3])
} else {
vec4f(0.0, 0.0, 0.0, 0.0)
}
}
}
impl TextureSampleBiasArrayOffset<Vec2f, u32, f32, Vec2i> for Texture2DArray<f32> {
type Output = Vec4f;
fn sample_bias_array_offset(
&self,
sampler: &Sampler,
coords: Vec2f,
array_index: u32,
_bias: f32,
offset: Vec2i,
) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let layer = array_index.min(data.num_layers().saturating_sub(1));
if let Some(layer_data) = data.layers.get(layer as usize) {
let (w, h) = layer_data.dimensions(0);
let u = coords.x() + offset.x() as f32 / w.max(1) as f32;
let v = coords.y() + offset.y() as f32 / h.max(1) as f32;
let result = sample_texture_2d(layer_data, &sampler_state, u, v, 0);
vec4f(result[0], result[1], result[2], result[3])
} else {
vec4f(0.0, 0.0, 0.0, 0.0)
}
}
}
impl TextureSampleGradArray<Vec2f, u32, Vec2f, Vec2f> for Texture2DArray<f32> {
type Output = Vec4f;
fn sample_grad_array(
&self,
sampler: &Sampler,
coords: Vec2f,
array_index: u32,
_ddx: Vec2f,
_ddy: Vec2f,
) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let layer = array_index.min(data.num_layers().saturating_sub(1));
if let Some(layer_data) = data.layers.get(layer as usize) {
let result = sample_texture_2d(layer_data, &sampler_state, coords.x(), coords.y(), 0);
vec4f(result[0], result[1], result[2], result[3])
} else {
vec4f(0.0, 0.0, 0.0, 0.0)
}
}
}
impl TextureSampleGradArrayOffset<Vec2f, u32, Vec2f, Vec2f, Vec2i> for Texture2DArray<f32> {
type Output = Vec4f;
fn sample_grad_array_offset(
&self,
sampler: &Sampler,
coords: Vec2f,
array_index: u32,
_ddx: Vec2f,
_ddy: Vec2f,
offset: Vec2i,
) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let layer = array_index.min(data.num_layers().saturating_sub(1));
if let Some(layer_data) = data.layers.get(layer as usize) {
let (w, h) = layer_data.dimensions(0);
let u = coords.x() + offset.x() as f32 / w.max(1) as f32;
let v = coords.y() + offset.y() as f32 / h.max(1) as f32;
let result = sample_texture_2d(layer_data, &sampler_state, u, v, 0);
vec4f(result[0], result[1], result[2], result[3])
} else {
vec4f(0.0, 0.0, 0.0, 0.0)
}
}
}
impl TextureGatherArray<Vec2f, u32> for Texture2DArray<f32> {
type Output = Vec4f;
fn gather_array(
&self,
component: u32,
sampler: &Sampler,
coords: Vec2f,
array_index: u32,
) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let g = gather_texture_2d_array(
&data,
&sampler_state,
coords.x(),
coords.y(),
array_index,
component,
);
vec4f(g[0], g[1], g[2], g[3])
}
}
impl TextureGatherArray<Vec2f, i32> for Texture2DArray<f32> {
type Output = Vec4f;
fn gather_array(
&self,
component: u32,
sampler: &Sampler,
coords: Vec2f,
array_index: i32,
) -> Self::Output {
self.gather_array(component, sampler, coords, array_index.max(0) as u32)
}
}
impl TextureGatherArrayOffset<Vec2f, u32, Vec2i> for Texture2DArray<f32> {
type Output = Vec4f;
fn gather_array_offset(
&self,
component: u32,
sampler: &Sampler,
coords: Vec2f,
array_index: u32,
offset: Vec2i,
) -> Self::Output {
let data = self.get();
let layer = array_index.min(data.num_layers().saturating_sub(1));
let (w, h) = data
.layers
.get(layer as usize)
.map(|layer_data| layer_data.dimensions(0))
.unwrap_or((1, 1));
let offset_u = coords.x() + offset.x() as f32 / w.max(1) as f32;
let offset_v = coords.y() + offset.y() as f32 / h.max(1) as f32;
drop(data);
self.gather_array(component, sampler, vec2f(offset_u, offset_v), array_index)
}
}
impl TextureGatherArrayOffset<Vec2f, i32, Vec2i> for Texture2DArray<f32> {
type Output = Vec4f;
fn gather_array_offset(
&self,
component: u32,
sampler: &Sampler,
coords: Vec2f,
array_index: i32,
offset: Vec2i,
) -> Self::Output {
self.gather_array_offset(
component,
sampler,
coords,
array_index.max(0) as u32,
offset,
)
}
}
impl TextureGatherArray<Vec2f, u32> for Texture2DArray<i32> {
type Output = Vec4i;
fn gather_array(
&self,
component: u32,
sampler: &Sampler,
coords: Vec2f,
array_index: u32,
) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let g = gather_texture_2d_array(
&data,
&sampler_state,
coords.x(),
coords.y(),
array_index,
component,
);
vec4i(g[0], g[1], g[2], g[3])
}
}
impl TextureGatherArray<Vec2f, i32> for Texture2DArray<i32> {
type Output = Vec4i;
fn gather_array(
&self,
component: u32,
sampler: &Sampler,
coords: Vec2f,
array_index: i32,
) -> Self::Output {
self.gather_array(component, sampler, coords, array_index.max(0) as u32)
}
}
impl TextureGatherArrayOffset<Vec2f, u32, Vec2i> for Texture2DArray<i32> {
type Output = Vec4i;
fn gather_array_offset(
&self,
component: u32,
sampler: &Sampler,
coords: Vec2f,
array_index: u32,
offset: Vec2i,
) -> Self::Output {
let data = self.get();
let layer = array_index.min(data.num_layers().saturating_sub(1));
let (w, h) = data
.layers
.get(layer as usize)
.map(|layer_data| layer_data.dimensions(0))
.unwrap_or((1, 1));
let offset_u = coords.x() + offset.x() as f32 / w.max(1) as f32;
let offset_v = coords.y() + offset.y() as f32 / h.max(1) as f32;
drop(data);
self.gather_array(component, sampler, vec2f(offset_u, offset_v), array_index)
}
}
impl TextureGatherArrayOffset<Vec2f, i32, Vec2i> for Texture2DArray<i32> {
type Output = Vec4i;
fn gather_array_offset(
&self,
component: u32,
sampler: &Sampler,
coords: Vec2f,
array_index: i32,
offset: Vec2i,
) -> Self::Output {
self.gather_array_offset(
component,
sampler,
coords,
array_index.max(0) as u32,
offset,
)
}
}
impl TextureGatherArray<Vec2f, u32> for Texture2DArray<u32> {
type Output = Vec4u;
fn gather_array(
&self,
component: u32,
sampler: &Sampler,
coords: Vec2f,
array_index: u32,
) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let g = gather_texture_2d_array(
&data,
&sampler_state,
coords.x(),
coords.y(),
array_index,
component,
);
vec4u(g[0], g[1], g[2], g[3])
}
}
impl TextureGatherArray<Vec2f, i32> for Texture2DArray<u32> {
type Output = Vec4u;
fn gather_array(
&self,
component: u32,
sampler: &Sampler,
coords: Vec2f,
array_index: i32,
) -> Self::Output {
self.gather_array(component, sampler, coords, array_index.max(0) as u32)
}
}
impl TextureGatherArrayOffset<Vec2f, u32, Vec2i> for Texture2DArray<u32> {
type Output = Vec4u;
fn gather_array_offset(
&self,
component: u32,
sampler: &Sampler,
coords: Vec2f,
array_index: u32,
offset: Vec2i,
) -> Self::Output {
let data = self.get();
let layer = array_index.min(data.num_layers().saturating_sub(1));
let (w, h) = data
.layers
.get(layer as usize)
.map(|layer_data| layer_data.dimensions(0))
.unwrap_or((1, 1));
let offset_u = coords.x() + offset.x() as f32 / w.max(1) as f32;
let offset_v = coords.y() + offset.y() as f32 / h.max(1) as f32;
drop(data);
self.gather_array(component, sampler, vec2f(offset_u, offset_v), array_index)
}
}
impl TextureGatherArrayOffset<Vec2f, i32, Vec2i> for Texture2DArray<u32> {
type Output = Vec4u;
fn gather_array_offset(
&self,
component: u32,
sampler: &Sampler,
coords: Vec2f,
array_index: i32,
offset: Vec2i,
) -> Self::Output {
self.gather_array_offset(
component,
sampler,
coords,
array_index.max(0) as u32,
offset,
)
}
}
pub struct Texture3D<T> {
group: u32,
binding: u32,
data: ModuleVar<TextureData3D<T>>,
}
impl<T: WgslTextureScalar> Wgsl for Texture3D<T> {
fn to_ir() -> wgsl_rs_ir::Type {
wgsl_rs_ir::Type::Texture {
kind: wgsl_rs_ir::TextureKind::Texture3D,
sampled_type: T::to_scalar_ir(),
}
}
}
impl<T: WgslTextureScalar> Texture3D<T> {
pub const fn new(group: u32, binding: u32) -> Self {
Self {
group,
binding,
data: ModuleVar::new(),
}
}
pub fn group(&self) -> u32 {
self.group
}
pub fn binding(&self) -> u32 {
self.binding
}
}
impl<T: WgslTextureScalar + Default + Copy> Texture3D<T> {
pub fn init(&self, width: u32, height: u32, depth: u32) {
self.set(TextureData3D::new(width, height, depth))
}
pub fn set_pixel(&self, x: u32, y: u32, z: u32, value: [T; 4]) {
self.data.write().set_pixel(x, y, z, 0, value);
}
}
impl<T: WgslTextureScalar> Texture3D<T> {
pub fn get(&self) -> ModuleVarReadGuard<'_, TextureData3D<T>> {
self.data.read()
}
pub fn set(&self, data: TextureData3D<T>) {
self.data.set(data);
}
}
impl<T: WgslTextureScalar + Default + Clone> TextureDimensionsQuery for Texture3D<T> {
type Output = Vec3u;
fn query_dimensions(&self, level: u32) -> Self::Output {
let (w, h, d) = self.get().dimensions(level);
vec3u(w, h, d)
}
}
impl<T: WgslTextureScalar + Default + Clone> TextureNumLevelsQuery for Texture3D<T> {
fn query_num_levels(&self) -> u32 {
self.get().num_levels()
}
}
impl<L: IntoLevel> TextureLoad<Vec3u, L> for Texture3D<f32> {
type Output = Vec4f;
fn load(&self, coords: Vec3u, level: L) -> Self::Output {
let level = level.into_level();
self.get()
.get_pixel(coords.x(), coords.y(), coords.z(), level)
.map(|p| vec4f(p[0], p[1], p[2], p[3]))
.unwrap_or(vec4f(0.0, 0.0, 0.0, 0.0))
}
}
impl<L: IntoLevel> TextureLoad<Vec3i, L> for Texture3D<f32> {
type Output = Vec4f;
fn load(&self, coords: Vec3i, level: L) -> Self::Output {
let x = coords.x().max(0) as u32;
let y = coords.y().max(0) as u32;
let z = coords.z().max(0) as u32;
self.load(vec3u(x, y, z), level)
}
}
impl<L: IntoLevel> TextureLoad<Vec3u, L> for Texture3D<i32> {
type Output = Vec4i;
fn load(&self, coords: Vec3u, level: L) -> Self::Output {
let level = level.into_level();
self.get()
.get_pixel(coords.x(), coords.y(), coords.z(), level)
.map(|p| vec4i(p[0], p[1], p[2], p[3]))
.unwrap_or(vec4i(0, 0, 0, 0))
}
}
impl<L: IntoLevel> TextureLoad<Vec3i, L> for Texture3D<i32> {
type Output = Vec4i;
fn load(&self, coords: Vec3i, level: L) -> Self::Output {
let x = coords.x().max(0) as u32;
let y = coords.y().max(0) as u32;
let z = coords.z().max(0) as u32;
self.load(vec3u(x, y, z), level)
}
}
impl<L: IntoLevel> TextureLoad<Vec3u, L> for Texture3D<u32> {
type Output = Vec4u;
fn load(&self, coords: Vec3u, level: L) -> Self::Output {
let level = level.into_level();
self.get()
.get_pixel(coords.x(), coords.y(), coords.z(), level)
.map(|p| vec4u(p[0], p[1], p[2], p[3]))
.unwrap_or(vec4u(0, 0, 0, 0))
}
}
impl<L: IntoLevel> TextureLoad<Vec3i, L> for Texture3D<u32> {
type Output = Vec4u;
fn load(&self, coords: Vec3i, level: L) -> Self::Output {
let x = coords.x().max(0) as u32;
let y = coords.y().max(0) as u32;
let z = coords.z().max(0) as u32;
self.load(vec3u(x, y, z), level)
}
}
impl TextureSample<Vec3f> for Texture3D<f32> {
type Output = Vec4f;
fn sample(&self, sampler: &Sampler, coords: Vec3f) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let result =
sample_texture_3d(&data, &sampler_state, coords.x(), coords.y(), coords.z(), 0);
vec4f(result[0], result[1], result[2], result[3])
}
}
impl TextureSampleOffset<Vec3f, Vec3i> for Texture3D<f32> {
type Output = Vec4f;
fn sample_offset(&self, sampler: &Sampler, coords: Vec3f, offset: Vec3i) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let (w, h, d) = data.dimensions(0);
let u = coords.x() + offset.x() as f32 / w.max(1) as f32;
let v = coords.y() + offset.y() as f32 / h.max(1) as f32;
let w_coord = coords.z() + offset.z() as f32 / d.max(1) as f32;
let result = sample_texture_3d(&data, &sampler_state, u, v, w_coord, 0);
vec4f(result[0], result[1], result[2], result[3])
}
}
impl TextureSampleLevel<Vec3f, f32> for Texture3D<f32> {
type Output = Vec4f;
fn sample_level(&self, sampler: &Sampler, coords: Vec3f, level: f32) -> Self::Output {
let sampler_state = sampler.get();
let level = level.floor() as u32;
let data = self.get();
let result = sample_texture_3d(
&data,
&sampler_state,
coords.x(),
coords.y(),
coords.z(),
level,
);
vec4f(result[0], result[1], result[2], result[3])
}
}
impl TextureSampleLevelOffset<Vec3f, f32, Vec3i> for Texture3D<f32> {
type Output = Vec4f;
fn sample_level_offset(
&self,
sampler: &Sampler,
coords: Vec3f,
level: f32,
offset: Vec3i,
) -> Self::Output {
let sampler_state = sampler.get();
let level = level.floor() as u32;
let data = self.get();
let (w, h, d) = data.dimensions(level);
let u = coords.x() + offset.x() as f32 / w.max(1) as f32;
let v = coords.y() + offset.y() as f32 / h.max(1) as f32;
let w_coord = coords.z() + offset.z() as f32 / d.max(1) as f32;
let result = sample_texture_3d(&data, &sampler_state, u, v, w_coord, level);
vec4f(result[0], result[1], result[2], result[3])
}
}
impl TextureSampleBias<Vec3f, f32> for Texture3D<f32> {
type Output = Vec4f;
fn sample_bias(&self, sampler: &Sampler, coords: Vec3f, _bias: f32) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let result =
sample_texture_3d(&data, &sampler_state, coords.x(), coords.y(), coords.z(), 0);
vec4f(result[0], result[1], result[2], result[3])
}
}
impl TextureSampleBiasOffset<Vec3f, f32, Vec3i> for Texture3D<f32> {
type Output = Vec4f;
fn sample_bias_offset(
&self,
sampler: &Sampler,
coords: Vec3f,
_bias: f32,
offset: Vec3i,
) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let (w, h, d) = data.dimensions(0);
let u = coords.x() + offset.x() as f32 / w.max(1) as f32;
let v = coords.y() + offset.y() as f32 / h.max(1) as f32;
let w_coord = coords.z() + offset.z() as f32 / d.max(1) as f32;
let result = sample_texture_3d(&data, &sampler_state, u, v, w_coord, 0);
vec4f(result[0], result[1], result[2], result[3])
}
}
impl TextureSampleGrad<Vec3f, Vec3f, Vec3f> for Texture3D<f32> {
type Output = Vec4f;
fn sample_grad(
&self,
sampler: &Sampler,
coords: Vec3f,
_ddx: Vec3f,
_ddy: Vec3f,
) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let result =
sample_texture_3d(&data, &sampler_state, coords.x(), coords.y(), coords.z(), 0);
vec4f(result[0], result[1], result[2], result[3])
}
}
impl TextureSampleGradOffset<Vec3f, Vec3f, Vec3f, Vec3i> for Texture3D<f32> {
type Output = Vec4f;
fn sample_grad_offset(
&self,
sampler: &Sampler,
coords: Vec3f,
_ddx: Vec3f,
_ddy: Vec3f,
offset: Vec3i,
) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let (w, h, d) = data.dimensions(0);
let u = coords.x() + offset.x() as f32 / w.max(1) as f32;
let v = coords.y() + offset.y() as f32 / h.max(1) as f32;
let w_coord = coords.z() + offset.z() as f32 / d.max(1) as f32;
let result = sample_texture_3d(&data, &sampler_state, u, v, w_coord, 0);
vec4f(result[0], result[1], result[2], result[3])
}
}
pub struct TextureCube<T> {
group: u32,
binding: u32,
data: ModuleVar<TextureDataCube<T>>,
}
impl<T: WgslTextureScalar> Wgsl for TextureCube<T> {
fn to_ir() -> wgsl_rs_ir::Type {
wgsl_rs_ir::Type::Texture {
kind: wgsl_rs_ir::TextureKind::TextureCube,
sampled_type: T::to_scalar_ir(),
}
}
}
impl<T: WgslTextureScalar> TextureCube<T> {
pub const fn new(group: u32, binding: u32) -> Self {
Self {
group,
binding,
data: ModuleVar::new(),
}
}
pub fn group(&self) -> u32 {
self.group
}
pub fn binding(&self) -> u32 {
self.binding
}
pub fn get(&self) -> ModuleVarReadGuard<'_, TextureDataCube<T>> {
self.data.read()
}
pub fn set(&self, data: TextureDataCube<T>) {
self.data.set(data);
}
}
impl<T: WgslTextureScalar + Default + Copy> TextureCube<T> {
pub fn init(&self, size: u32) {
self.set(TextureDataCube::new(size))
}
}
impl<T: WgslTextureScalar + Default + Copy> TextureDimensionsQuery for TextureCube<T> {
type Output = Vec2u;
fn query_dimensions(&self, level: u32) -> Self::Output {
let (w, h) = self.get().dimensions(level);
vec2u(w, h)
}
}
impl<T: WgslTextureScalar + Default + Clone> TextureNumLevelsQuery for TextureCube<T> {
fn query_num_levels(&self) -> u32 {
self.get().num_levels()
}
}
impl TextureSample<Vec3f> for TextureCube<f32> {
type Output = Vec4f;
fn sample(&self, sampler: &Sampler, coords: Vec3f) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let result =
sample_texture_cube(&data, &sampler_state, coords.x(), coords.y(), coords.z(), 0);
vec4f(result[0], result[1], result[2], result[3])
}
}
impl TextureSampleLevel<Vec3f, f32> for TextureCube<f32> {
type Output = Vec4f;
fn sample_level(&self, sampler: &Sampler, coords: Vec3f, level: f32) -> Self::Output {
let sampler_state = sampler.get();
let level = level.floor() as u32;
let data = self.get();
let result = sample_texture_cube(
&data,
&sampler_state,
coords.x(),
coords.y(),
coords.z(),
level,
);
vec4f(result[0], result[1], result[2], result[3])
}
}
impl TextureSampleBias<Vec3f, f32> for TextureCube<f32> {
type Output = Vec4f;
fn sample_bias(&self, sampler: &Sampler, coords: Vec3f, _bias: f32) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let result =
sample_texture_cube(&data, &sampler_state, coords.x(), coords.y(), coords.z(), 0);
vec4f(result[0], result[1], result[2], result[3])
}
}
impl TextureSampleGrad<Vec3f, Vec3f, Vec3f> for TextureCube<f32> {
type Output = Vec4f;
fn sample_grad(
&self,
sampler: &Sampler,
coords: Vec3f,
_ddx: Vec3f,
_ddy: Vec3f,
) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let result =
sample_texture_cube(&data, &sampler_state, coords.x(), coords.y(), coords.z(), 0);
vec4f(result[0], result[1], result[2], result[3])
}
}
impl TextureGather<Vec3f> for TextureCube<f32> {
type Output = Vec4f;
fn gather(&self, component: u32, sampler: &Sampler, coords: Vec3f) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let g = gather_texture_cube(
&data,
&sampler_state,
coords.x(),
coords.y(),
coords.z(),
component,
);
vec4f(g[0], g[1], g[2], g[3])
}
}
impl TextureGather<Vec3f> for TextureCube<i32> {
type Output = Vec4i;
fn gather(&self, component: u32, sampler: &Sampler, coords: Vec3f) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let g = gather_texture_cube(
&data,
&sampler_state,
coords.x(),
coords.y(),
coords.z(),
component,
);
vec4i(g[0], g[1], g[2], g[3])
}
}
impl TextureGather<Vec3f> for TextureCube<u32> {
type Output = Vec4u;
fn gather(&self, component: u32, sampler: &Sampler, coords: Vec3f) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let g = gather_texture_cube(
&data,
&sampler_state,
coords.x(),
coords.y(),
coords.z(),
component,
);
vec4u(g[0], g[1], g[2], g[3])
}
}
pub struct TextureCubeArray<T> {
group: u32,
binding: u32,
data: ModuleVar<TextureDataCubeArray<T>>,
}
impl<T: WgslTextureScalar> Wgsl for TextureCubeArray<T> {
fn to_ir() -> wgsl_rs_ir::Type {
wgsl_rs_ir::Type::Texture {
kind: wgsl_rs_ir::TextureKind::TextureCubeArray,
sampled_type: T::to_scalar_ir(),
}
}
}
impl<T: WgslTextureScalar> TextureCubeArray<T> {
pub const fn new(group: u32, binding: u32) -> Self {
Self {
group,
binding,
data: ModuleVar::new(),
}
}
pub fn group(&self) -> u32 {
self.group
}
pub fn binding(&self) -> u32 {
self.binding
}
pub fn get(&self) -> ModuleVarReadGuard<'_, TextureDataCubeArray<T>> {
self.data.read()
}
pub fn set(&self, data: TextureDataCubeArray<T>) {
self.data.set(data);
}
}
impl<T: WgslTextureScalar + Default + Copy> TextureCubeArray<T> {
pub fn init(&self, size: u32, layers: u32) {
self.set(TextureDataCubeArray::new(size, layers))
}
}
impl<T: WgslTextureScalar> TextureDimensionsQuery for TextureCubeArray<T> {
type Output = Vec2u;
fn query_dimensions(&self, level: u32) -> Self::Output {
let (w, h) = self.get().dimensions(level);
vec2u(w, h)
}
}
impl<T: WgslTextureScalar> TextureNumLayersQuery for TextureCubeArray<T> {
fn query_num_layers(&self) -> u32 {
self.get().num_layers()
}
}
impl<T: WgslTextureScalar> TextureNumLevelsQuery for TextureCubeArray<T> {
fn query_num_levels(&self) -> u32 {
self.get().num_levels()
}
}
impl TextureSampleArray<Vec3f, u32> for TextureCubeArray<f32> {
type Output = Vec4f;
fn sample_array(&self, sampler: &Sampler, coords: Vec3f, array_index: u32) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let result = sample_texture_cube_array(
&data,
&sampler_state,
coords.x(),
coords.y(),
coords.z(),
array_index,
0,
);
vec4f(result[0], result[1], result[2], result[3])
}
}
impl TextureSampleArray<Vec3f, i32> for TextureCubeArray<f32> {
type Output = Vec4f;
fn sample_array(&self, sampler: &Sampler, coords: Vec3f, array_index: i32) -> Self::Output {
self.sample_array(sampler, coords, array_index.max(0) as u32)
}
}
impl TextureSampleLevelArray<Vec3f, u32, f32> for TextureCubeArray<f32> {
type Output = Vec4f;
fn sample_level_array(
&self,
sampler: &Sampler,
coords: Vec3f,
array_index: u32,
level: f32,
) -> Self::Output {
let sampler_state = sampler.get();
let level = level.floor() as u32;
let data = self.get();
let result = sample_texture_cube_array(
&data,
&sampler_state,
coords.x(),
coords.y(),
coords.z(),
array_index,
level,
);
vec4f(result[0], result[1], result[2], result[3])
}
}
impl TextureSampleLevelArray<Vec3f, i32, f32> for TextureCubeArray<f32> {
type Output = Vec4f;
fn sample_level_array(
&self,
sampler: &Sampler,
coords: Vec3f,
array_index: i32,
level: f32,
) -> Self::Output {
self.sample_level_array(sampler, coords, array_index.max(0) as u32, level)
}
}
impl TextureSampleBiasArray<Vec3f, u32, f32> for TextureCubeArray<f32> {
type Output = Vec4f;
fn sample_bias_array(
&self,
sampler: &Sampler,
coords: Vec3f,
array_index: u32,
_bias: f32,
) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let result = sample_texture_cube_array(
&data,
&sampler_state,
coords.x(),
coords.y(),
coords.z(),
array_index,
0,
);
vec4f(result[0], result[1], result[2], result[3])
}
}
impl TextureSampleBiasArray<Vec3f, i32, f32> for TextureCubeArray<f32> {
type Output = Vec4f;
fn sample_bias_array(
&self,
sampler: &Sampler,
coords: Vec3f,
array_index: i32,
bias: f32,
) -> Self::Output {
self.sample_bias_array(sampler, coords, array_index.max(0) as u32, bias)
}
}
impl TextureSampleGradArray<Vec3f, u32, Vec3f, Vec3f> for TextureCubeArray<f32> {
type Output = Vec4f;
fn sample_grad_array(
&self,
sampler: &Sampler,
coords: Vec3f,
array_index: u32,
_ddx: Vec3f,
_ddy: Vec3f,
) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let result = sample_texture_cube_array(
&data,
&sampler_state,
coords.x(),
coords.y(),
coords.z(),
array_index,
0,
);
vec4f(result[0], result[1], result[2], result[3])
}
}
impl TextureSampleGradArray<Vec3f, i32, Vec3f, Vec3f> for TextureCubeArray<f32> {
type Output = Vec4f;
fn sample_grad_array(
&self,
sampler: &Sampler,
coords: Vec3f,
array_index: i32,
ddx: Vec3f,
ddy: Vec3f,
) -> Self::Output {
self.sample_grad_array(sampler, coords, array_index.max(0) as u32, ddx, ddy)
}
}
impl TextureGatherArray<Vec3f, u32> for TextureCubeArray<f32> {
type Output = Vec4f;
fn gather_array(
&self,
component: u32,
sampler: &Sampler,
coords: Vec3f,
array_index: u32,
) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let g = gather_texture_cube_array(
&data,
&sampler_state,
coords.x(),
coords.y(),
coords.z(),
array_index,
component,
);
vec4f(g[0], g[1], g[2], g[3])
}
}
impl TextureGatherArray<Vec3f, i32> for TextureCubeArray<f32> {
type Output = Vec4f;
fn gather_array(
&self,
component: u32,
sampler: &Sampler,
coords: Vec3f,
array_index: i32,
) -> Self::Output {
self.gather_array(component, sampler, coords, array_index.max(0) as u32)
}
}
impl TextureGatherArray<Vec3f, u32> for TextureCubeArray<i32> {
type Output = Vec4i;
fn gather_array(
&self,
component: u32,
sampler: &Sampler,
coords: Vec3f,
array_index: u32,
) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let g = gather_texture_cube_array(
&data,
&sampler_state,
coords.x(),
coords.y(),
coords.z(),
array_index,
component,
);
vec4i(g[0], g[1], g[2], g[3])
}
}
impl TextureGatherArray<Vec3f, i32> for TextureCubeArray<i32> {
type Output = Vec4i;
fn gather_array(
&self,
component: u32,
sampler: &Sampler,
coords: Vec3f,
array_index: i32,
) -> Self::Output {
self.gather_array(component, sampler, coords, array_index.max(0) as u32)
}
}
impl TextureGatherArray<Vec3f, u32> for TextureCubeArray<u32> {
type Output = Vec4u;
fn gather_array(
&self,
component: u32,
sampler: &Sampler,
coords: Vec3f,
array_index: u32,
) -> Self::Output {
let sampler_state = sampler.get();
let data = self.get();
let g = gather_texture_cube_array(
&data,
&sampler_state,
coords.x(),
coords.y(),
coords.z(),
array_index,
component,
);
vec4u(g[0], g[1], g[2], g[3])
}
}
impl TextureGatherArray<Vec3f, i32> for TextureCubeArray<u32> {
type Output = Vec4u;
fn gather_array(
&self,
component: u32,
sampler: &Sampler,
coords: Vec3f,
array_index: i32,
) -> Self::Output {
self.gather_array(component, sampler, coords, array_index.max(0) as u32)
}
}
pub struct TextureMultisampled2D<T> {
group: u32,
binding: u32,
data: ModuleVar<TextureDataMultisampled2D<T>>,
}
impl<T: WgslTextureScalar> Wgsl for TextureMultisampled2D<T> {
fn to_ir() -> wgsl_rs_ir::Type {
wgsl_rs_ir::Type::Texture {
kind: wgsl_rs_ir::TextureKind::TextureMultisampled2D,
sampled_type: T::to_scalar_ir(),
}
}
}
impl<T: WgslTextureScalar> TextureMultisampled2D<T> {
pub const fn new(group: u32, binding: u32) -> Self {
Self {
group,
binding,
data: ModuleVar::new(),
}
}
pub fn group(&self) -> u32 {
self.group
}
pub fn binding(&self) -> u32 {
self.binding
}
pub fn get(&self) -> ModuleVarReadGuard<'_, TextureDataMultisampled2D<T>> {
self.data.read()
}
pub fn set(&self, data: TextureDataMultisampled2D<T>) {
self.data.set(data);
}
}
impl<T: WgslTextureScalar + Default + Copy> TextureMultisampled2D<T> {
pub fn init(&self, width: u32, height: u32, sample_count: u32) {
self.set(TextureDataMultisampled2D::new(width, height, sample_count));
}
}
impl<T: WgslTextureScalar> TextureDimensionsQuery for TextureMultisampled2D<T> {
type Output = Vec2u;
fn query_dimensions(&self, _level: u32) -> Self::Output {
let (w, h) = self.get().dimensions();
vec2u(w, h)
}
}
impl<T: WgslTextureScalar + Default + Clone> TextureNumSamplesQuery for TextureMultisampled2D<T> {
fn query_num_samples(&self) -> u32 {
self.get().num_samples()
}
}
impl TextureLoadMultisampled<Vec2u, u32> for TextureMultisampled2D<f32> {
type Output = Vec4f;
fn load_multisampled(&self, coords: Vec2u, sample_index: u32) -> Self::Output {
self.get()
.get_sample(coords.x(), coords.y(), sample_index)
.map(|p| vec4f(p[0], p[1], p[2], p[3]))
.unwrap_or(vec4f(0.0, 0.0, 0.0, 0.0))
}
}
impl TextureLoadMultisampled<Vec2u, i32> for TextureMultisampled2D<f32> {
type Output = Vec4f;
fn load_multisampled(&self, coords: Vec2u, sample_index: i32) -> Self::Output {
self.load_multisampled(coords, sample_index.max(0) as u32)
}
}
impl TextureLoadMultisampled<Vec2i, u32> for TextureMultisampled2D<f32> {
type Output = Vec4f;
fn load_multisampled(&self, coords: Vec2i, sample_index: u32) -> Self::Output {
let x = coords.x().max(0) as u32;
let y = coords.y().max(0) as u32;
self.load_multisampled(vec2u(x, y), sample_index)
}
}
impl TextureLoadMultisampled<Vec2i, i32> for TextureMultisampled2D<f32> {
type Output = Vec4f;
fn load_multisampled(&self, coords: Vec2i, sample_index: i32) -> Self::Output {
let x = coords.x().max(0) as u32;
let y = coords.y().max(0) as u32;
self.load_multisampled(vec2u(x, y), sample_index.max(0) as u32)
}
}
impl TextureLoadMultisampled<Vec2u, u32> for TextureMultisampled2D<i32> {
type Output = Vec4i;
fn load_multisampled(&self, coords: Vec2u, sample_index: u32) -> Self::Output {
self.get()
.get_sample(coords.x(), coords.y(), sample_index)
.map(|p| vec4i(p[0], p[1], p[2], p[3]))
.unwrap_or(vec4i(0, 0, 0, 0))
}
}
impl TextureLoadMultisampled<Vec2u, i32> for TextureMultisampled2D<i32> {
type Output = Vec4i;
fn load_multisampled(&self, coords: Vec2u, sample_index: i32) -> Self::Output {
self.load_multisampled(coords, sample_index.max(0) as u32)
}
}
impl TextureLoadMultisampled<Vec2i, u32> for TextureMultisampled2D<i32> {
type Output = Vec4i;
fn load_multisampled(&self, coords: Vec2i, sample_index: u32) -> Self::Output {
let x = coords.x().max(0) as u32;
let y = coords.y().max(0) as u32;
self.load_multisampled(vec2u(x, y), sample_index)
}
}
impl TextureLoadMultisampled<Vec2i, i32> for TextureMultisampled2D<i32> {
type Output = Vec4i;
fn load_multisampled(&self, coords: Vec2i, sample_index: i32) -> Self::Output {
let x = coords.x().max(0) as u32;
let y = coords.y().max(0) as u32;
self.load_multisampled(vec2u(x, y), sample_index.max(0) as u32)
}
}
impl TextureLoadMultisampled<Vec2u, u32> for TextureMultisampled2D<u32> {
type Output = Vec4u;
fn load_multisampled(&self, coords: Vec2u, sample_index: u32) -> Self::Output {
self.get()
.get_sample(coords.x(), coords.y(), sample_index)
.map(|p| vec4u(p[0], p[1], p[2], p[3]))
.unwrap_or(vec4u(0, 0, 0, 0))
}
}
impl TextureLoadMultisampled<Vec2u, i32> for TextureMultisampled2D<u32> {
type Output = Vec4u;
fn load_multisampled(&self, coords: Vec2u, sample_index: i32) -> Self::Output {
self.load_multisampled(coords, sample_index.max(0) as u32)
}
}
impl TextureLoadMultisampled<Vec2i, u32> for TextureMultisampled2D<u32> {
type Output = Vec4u;
fn load_multisampled(&self, coords: Vec2i, sample_index: u32) -> Self::Output {
let x = coords.x().max(0) as u32;
let y = coords.y().max(0) as u32;
self.load_multisampled(vec2u(x, y), sample_index)
}
}
impl TextureLoadMultisampled<Vec2i, i32> for TextureMultisampled2D<u32> {
type Output = Vec4u;
fn load_multisampled(&self, coords: Vec2i, sample_index: i32) -> Self::Output {
let x = coords.x().max(0) as u32;
let y = coords.y().max(0) as u32;
self.load_multisampled(vec2u(x, y), sample_index.max(0) as u32)
}
}
pub struct TextureDepth2D {
group: u32,
binding: u32,
data: ModuleVar<TextureDataDepth2D>,
}
impl Wgsl for TextureDepth2D {
fn to_ir() -> wgsl_rs_ir::Type {
wgsl_rs_ir::Type::TextureDepth {
kind: wgsl_rs_ir::TextureDepthKind::Depth2D,
}
}
}
impl TextureDepth2D {
pub const fn new(group: u32, binding: u32) -> Self {
Self {
group,
binding,
data: ModuleVar::new(),
}
}
pub fn group(&self) -> u32 {
self.group
}
pub fn binding(&self) -> u32 {
self.binding
}
pub fn get(&self) -> ModuleVarReadGuard<'_, TextureDataDepth2D> {
self.data.read()
}
pub fn set(&self, data: TextureDataDepth2D) {
self.data.set(data);
}
pub fn init(&self, width: u32, height: u32) {
self.set(TextureDataDepth2D::new(width, height))
}
}
impl TextureDimensionsQuery for TextureDepth2D {
type Output = Vec2u;
fn query_dimensions(&self, level: u32) -> Self::Output {
let (w, h) = self.get().dimensions(level);
vec2u(w, h)
}
}
impl TextureNumLevelsQuery for TextureDepth2D {
fn query_num_levels(&self) -> u32 {
self.get().num_levels()
}
}
impl<L: IntoLevel> TextureLoad<Vec2u, L> for TextureDepth2D {
type Output = f32;
fn load(&self, coords: Vec2u, level: L) -> Self::Output {
let x = coords.x();
let y = coords.y();
let level = level.into_level();
self.get().get_depth(x, y, level).unwrap_or(0.0)
}
}
impl<L: IntoLevel> TextureLoad<Vec2i, L> for TextureDepth2D {
type Output = f32;
fn load(&self, coords: Vec2i, level: L) -> Self::Output {
let x = if coords.x() < 0 {
0u32
} else {
coords.x() as u32
};
let y = if coords.y() < 0 {
0u32
} else {
coords.y() as u32
};
self.load(vec2u(x, y), level)
}
}
impl TextureSampleCompare<Vec2f, f32> for TextureDepth2D {
fn sample_compare(&self, sampler: &SamplerComparison, coords: Vec2f, depth_ref: f32) -> f32 {
let sampler_state = sampler.get();
let sampled_depth = sample_depth_texture_2d(
&self.get(),
&sampler_state.sampler,
coords.x(),
coords.y(),
0,
);
sampler_state.compare.compare(sampled_depth, depth_ref)
}
}
impl TextureSampleCompareLevel<Vec2f, f32> for TextureDepth2D {
fn sample_compare_level(
&self,
sampler: &SamplerComparison,
coords: Vec2f,
depth_ref: f32,
) -> f32 {
let sampler_state = sampler.get();
let sampled_depth = sample_depth_texture_2d(
&self.get(),
&sampler_state.sampler,
coords.x(),
coords.y(),
0,
);
sampler_state.compare.compare(sampled_depth, depth_ref)
}
}
impl TextureGatherCompare<Vec2f, f32> for TextureDepth2D {
fn gather_compare(&self, sampler: &SamplerComparison, coords: Vec2f, depth_ref: f32) -> Vec4f {
let sampler_state = sampler.get();
let data = self.get();
let texels = gather_4_depth_texels(&data, &sampler_state.sampler, coords.x(), coords.y());
vec4f(
sampler_state.compare.compare(texels[0], depth_ref),
sampler_state.compare.compare(texels[1], depth_ref),
sampler_state.compare.compare(texels[2], depth_ref),
sampler_state.compare.compare(texels[3], depth_ref),
)
}
}
impl TextureSampleCompareOffset<Vec2f, f32, Vec2i> for TextureDepth2D {
fn sample_compare_offset(
&self,
sampler: &SamplerComparison,
coords: Vec2f,
depth_ref: f32,
offset: Vec2i,
) -> f32 {
let sampler_state = sampler.get();
let data = self.get();
let (w, h) = data.dimensions(0);
let u = coords.x() + offset.x() as f32 / w.max(1) as f32;
let v = coords.y() + offset.y() as f32 / h.max(1) as f32;
let sampled_depth = sample_depth_texture_2d(&data, &sampler_state.sampler, u, v, 0);
sampler_state.compare.compare(sampled_depth, depth_ref)
}
}
impl TextureSampleCompareLevelOffset<Vec2f, f32, Vec2i> for TextureDepth2D {
fn sample_compare_level_offset(
&self,
sampler: &SamplerComparison,
coords: Vec2f,
depth_ref: f32,
offset: Vec2i,
) -> f32 {
let sampler_state = sampler.get();
let data = self.get();
let (w, h) = data.dimensions(0);
let u = coords.x() + offset.x() as f32 / w.max(1) as f32;
let v = coords.y() + offset.y() as f32 / h.max(1) as f32;
let sampled_depth = sample_depth_texture_2d(&data, &sampler_state.sampler, u, v, 0);
sampler_state.compare.compare(sampled_depth, depth_ref)
}
}
impl TextureGatherDepth<Vec2f> for TextureDepth2D {
fn gather_depth(&self, sampler: &Sampler, coords: Vec2f) -> Vec4f {
let sampler_state = sampler.get();
let data = self.get();
let texels = gather_4_depth_texels(&data, &sampler_state, coords.x(), coords.y());
vec4f(texels[0], texels[1], texels[2], texels[3])
}
}
impl TextureGatherDepthOffset<Vec2f, Vec2i> for TextureDepth2D {
fn gather_depth_offset(&self, sampler: &Sampler, coords: Vec2f, offset: Vec2i) -> Vec4f {
let data = self.get();
let (w, h) = data.dimensions(0);
let u = coords.x() + offset.x() as f32 / w.max(1) as f32;
let v = coords.y() + offset.y() as f32 / h.max(1) as f32;
let sampler_state = sampler.get();
let texels = gather_4_depth_texels(&data, &sampler_state, u, v);
vec4f(texels[0], texels[1], texels[2], texels[3])
}
}
impl TextureGatherCompareOffset<Vec2f, f32, Vec2i> for TextureDepth2D {
fn gather_compare_offset(
&self,
sampler: &SamplerComparison,
coords: Vec2f,
depth_ref: f32,
offset: Vec2i,
) -> Vec4f {
let sampler_state = sampler.get();
let data = self.get();
let (w, h) = data.dimensions(0);
let u = coords.x() + offset.x() as f32 / w.max(1) as f32;
let v = coords.y() + offset.y() as f32 / h.max(1) as f32;
let texels = gather_4_depth_texels(&data, &sampler_state.sampler, u, v);
vec4f(
sampler_state.compare.compare(texels[0], depth_ref),
sampler_state.compare.compare(texels[1], depth_ref),
sampler_state.compare.compare(texels[2], depth_ref),
sampler_state.compare.compare(texels[3], depth_ref),
)
}
}
pub struct TextureDepth2DArray {
group: u32,
binding: u32,
data: ModuleVar<TextureDataDepth2DArray>,
}
impl Wgsl for TextureDepth2DArray {
fn to_ir() -> wgsl_rs_ir::Type {
wgsl_rs_ir::Type::TextureDepth {
kind: wgsl_rs_ir::TextureDepthKind::Depth2DArray,
}
}
}
impl TextureDepth2DArray {
pub const fn new(group: u32, binding: u32) -> Self {
Self {
group,
binding,
data: ModuleVar::new(),
}
}
pub fn group(&self) -> u32 {
self.group
}
pub fn binding(&self) -> u32 {
self.binding
}
pub fn get(&self) -> ModuleVarReadGuard<'_, TextureDataDepth2DArray> {
self.data.read()
}
pub fn set(&self, data: TextureDataDepth2DArray) {
self.data.set(data);
}
pub fn init(&self, width: u32, height: u32, layers: u32) {
self.set(TextureDataDepth2DArray::new(width, height, layers))
}
}
impl TextureDimensionsQuery for TextureDepth2DArray {
type Output = Vec2u;
fn query_dimensions(&self, level: u32) -> Self::Output {
let (w, h) = self.get().dimensions(level);
vec2u(w, h)
}
}
impl TextureNumLayersQuery for TextureDepth2DArray {
fn query_num_layers(&self) -> u32 {
self.get().num_layers()
}
}
impl TextureNumLevelsQuery for TextureDepth2DArray {
fn query_num_levels(&self) -> u32 {
self.get().num_levels()
}
}
impl TextureSampleCompareArray<Vec2f, u32, f32> for TextureDepth2DArray {
fn sample_compare_array(
&self,
sampler: &SamplerComparison,
coords: Vec2f,
array_index: u32,
depth_ref: f32,
) -> f32 {
let sampler_state = sampler.get();
let data = self.get();
let layer = array_index.min(data.num_layers().saturating_sub(1));
if let Some(layer_data) = data.layers.get(layer as usize) {
let sampled_depth = sample_depth_texture_2d(
layer_data,
&sampler_state.sampler,
coords.x(),
coords.y(),
0,
);
sampler_state.compare.compare(sampled_depth, depth_ref)
} else {
0.0
}
}
}
impl TextureSampleCompareArray<Vec2f, i32, f32> for TextureDepth2DArray {
fn sample_compare_array(
&self,
sampler: &SamplerComparison,
coords: Vec2f,
array_index: i32,
depth_ref: f32,
) -> f32 {
let sampler_state = sampler.get();
let data = self.get();
let layer = (array_index.max(0) as u32).min(data.num_layers().saturating_sub(1));
if let Some(layer_data) = data.layers.get(layer as usize) {
let sampled_depth = sample_depth_texture_2d(
layer_data,
&sampler_state.sampler,
coords.x(),
coords.y(),
0,
);
sampler_state.compare.compare(sampled_depth, depth_ref)
} else {
0.0
}
}
}
impl TextureSampleCompareArrayOffset<Vec2f, u32, f32, Vec2i> for TextureDepth2DArray {
fn sample_compare_array_offset(
&self,
sampler: &SamplerComparison,
coords: Vec2f,
array_index: u32,
depth_ref: f32,
offset: Vec2i,
) -> f32 {
let sampler_state = sampler.get();
let data = self.get();
let layer = array_index.min(data.num_layers().saturating_sub(1));
if let Some(layer_data) = data.layers.get(layer as usize) {
let (w, h) = layer_data.dimensions(0);
let u = coords.x() + offset.x() as f32 / w.max(1) as f32;
let v = coords.y() + offset.y() as f32 / h.max(1) as f32;
let sampled_depth =
sample_depth_texture_2d(layer_data, &sampler_state.sampler, u, v, 0);
sampler_state.compare.compare(sampled_depth, depth_ref)
} else {
0.0
}
}
}
impl TextureSampleCompareLevelArray<Vec2f, u32, f32> for TextureDepth2DArray {
fn sample_compare_level_array(
&self,
sampler: &SamplerComparison,
coords: Vec2f,
array_index: u32,
depth_ref: f32,
) -> f32 {
let sampler_state = sampler.get();
let data = self.get();
let layer = array_index.min(data.num_layers().saturating_sub(1));
if let Some(layer_data) = data.layers.get(layer as usize) {
let sampled_depth = sample_depth_texture_2d(
layer_data,
&sampler_state.sampler,
coords.x(),
coords.y(),
0,
);
sampler_state.compare.compare(sampled_depth, depth_ref)
} else {
0.0
}
}
}
impl TextureSampleCompareLevelArrayOffset<Vec2f, u32, f32, Vec2i> for TextureDepth2DArray {
fn sample_compare_level_array_offset(
&self,
sampler: &SamplerComparison,
coords: Vec2f,
array_index: u32,
depth_ref: f32,
offset: Vec2i,
) -> f32 {
let sampler_state = sampler.get();
let data = self.get();
let layer = array_index.min(data.num_layers().saturating_sub(1));
if let Some(layer_data) = data.layers.get(layer as usize) {
let (w, h) = layer_data.dimensions(0);
let u = coords.x() + offset.x() as f32 / w.max(1) as f32;
let v = coords.y() + offset.y() as f32 / h.max(1) as f32;
let sampled_depth =
sample_depth_texture_2d(layer_data, &sampler_state.sampler, u, v, 0);
sampler_state.compare.compare(sampled_depth, depth_ref)
} else {
0.0
}
}
}
impl TextureGatherCompareArray<Vec2f, u32, f32> for TextureDepth2DArray {
fn gather_compare_array(
&self,
sampler: &SamplerComparison,
coords: Vec2f,
array_index: u32,
depth_ref: f32,
) -> Vec4f {
let sampler_state = sampler.get();
let data = self.get();
let layer = array_index.min(data.num_layers().saturating_sub(1));
if let Some(layer_data) = data.layers.get(layer as usize) {
let texels =
gather_4_depth_texels(layer_data, &sampler_state.sampler, coords.x(), coords.y());
vec4f(
sampler_state.compare.compare(texels[0], depth_ref),
sampler_state.compare.compare(texels[1], depth_ref),
sampler_state.compare.compare(texels[2], depth_ref),
sampler_state.compare.compare(texels[3], depth_ref),
)
} else {
vec4f(0.0, 0.0, 0.0, 0.0)
}
}
}
impl TextureGatherCompareArray<Vec2f, i32, f32> for TextureDepth2DArray {
fn gather_compare_array(
&self,
sampler: &SamplerComparison,
coords: Vec2f,
array_index: i32,
depth_ref: f32,
) -> Vec4f {
self.gather_compare_array(sampler, coords, array_index.max(0) as u32, depth_ref)
}
}
impl TextureGatherCompareArrayOffset<Vec2f, u32, f32, Vec2i> for TextureDepth2DArray {
fn gather_compare_array_offset(
&self,
sampler: &SamplerComparison,
coords: Vec2f,
array_index: u32,
depth_ref: f32,
offset: Vec2i,
) -> Vec4f {
let sampler_state = sampler.get();
let data = self.get();
let layer = array_index.min(data.num_layers().saturating_sub(1));
if let Some(layer_data) = data.layers.get(layer as usize) {
let (w, h) = layer_data.dimensions(0);
let u = coords.x() + offset.x() as f32 / w.max(1) as f32;
let v = coords.y() + offset.y() as f32 / h.max(1) as f32;
let texels = gather_4_depth_texels(layer_data, &sampler_state.sampler, u, v);
vec4f(
sampler_state.compare.compare(texels[0], depth_ref),
sampler_state.compare.compare(texels[1], depth_ref),
sampler_state.compare.compare(texels[2], depth_ref),
sampler_state.compare.compare(texels[3], depth_ref),
)
} else {
vec4f(0.0, 0.0, 0.0, 0.0)
}
}
}
impl TextureGatherCompareArrayOffset<Vec2f, i32, f32, Vec2i> for TextureDepth2DArray {
fn gather_compare_array_offset(
&self,
sampler: &SamplerComparison,
coords: Vec2f,
array_index: i32,
depth_ref: f32,
offset: Vec2i,
) -> Vec4f {
self.gather_compare_array_offset(
sampler,
coords,
array_index.max(0) as u32,
depth_ref,
offset,
)
}
}
impl TextureGatherDepthArray<Vec2f, u32> for TextureDepth2DArray {
fn gather_depth_array(&self, sampler: &Sampler, coords: Vec2f, array_index: u32) -> Vec4f {
let sampler_state = sampler.get();
let data = self.get();
let layer = array_index.min(data.num_layers().saturating_sub(1));
if let Some(layer_data) = data.layers.get(layer as usize) {
let texels = gather_4_depth_texels(layer_data, &sampler_state, coords.x(), coords.y());
vec4f(texels[0], texels[1], texels[2], texels[3])
} else {
vec4f(0.0, 0.0, 0.0, 0.0)
}
}
}
impl TextureGatherDepthArray<Vec2f, i32> for TextureDepth2DArray {
fn gather_depth_array(&self, sampler: &Sampler, coords: Vec2f, array_index: i32) -> Vec4f {
self.gather_depth_array(sampler, coords, array_index.max(0) as u32)
}
}
impl TextureGatherDepthArrayOffset<Vec2f, u32, Vec2i> for TextureDepth2DArray {
fn gather_depth_array_offset(
&self,
sampler: &Sampler,
coords: Vec2f,
array_index: u32,
offset: Vec2i,
) -> Vec4f {
let sampler_state = sampler.get();
let data = self.get();
let layer = array_index.min(data.num_layers().saturating_sub(1));
if let Some(layer_data) = data.layers.get(layer as usize) {
let (w, h) = layer_data.dimensions(0);
let u = coords.x() + offset.x() as f32 / w.max(1) as f32;
let v = coords.y() + offset.y() as f32 / h.max(1) as f32;
let texels = gather_4_depth_texels(layer_data, &sampler_state, u, v);
vec4f(texels[0], texels[1], texels[2], texels[3])
} else {
vec4f(0.0, 0.0, 0.0, 0.0)
}
}
}
impl TextureGatherDepthArrayOffset<Vec2f, i32, Vec2i> for TextureDepth2DArray {
fn gather_depth_array_offset(
&self,
sampler: &Sampler,
coords: Vec2f,
array_index: i32,
offset: Vec2i,
) -> Vec4f {
self.gather_depth_array_offset(sampler, coords, array_index.max(0) as u32, offset)
}
}
pub struct TextureDepthCube {
group: u32,
binding: u32,
data: ModuleVar<TextureDataDepthCube>,
}
impl Wgsl for TextureDepthCube {
fn to_ir() -> wgsl_rs_ir::Type {
wgsl_rs_ir::Type::TextureDepth {
kind: wgsl_rs_ir::TextureDepthKind::DepthCube,
}
}
}
impl TextureDepthCube {
pub const fn new(group: u32, binding: u32) -> Self {
Self {
group,
binding,
data: ModuleVar::new(),
}
}
pub fn group(&self) -> u32 {
self.group
}
pub fn binding(&self) -> u32 {
self.binding
}
pub fn get(&self) -> ModuleVarReadGuard<'_, TextureDataDepthCube> {
self.data.read()
}
pub fn set(&self, data: TextureDataDepthCube) {
self.data.set(data);
}
pub fn init(&self, size: u32) {
self.set(TextureDataDepthCube::new(size))
}
}
impl TextureDimensionsQuery for TextureDepthCube {
type Output = Vec2u;
fn query_dimensions(&self, level: u32) -> Self::Output {
let (w, h) = self.get().dimensions(level);
vec2u(w, h)
}
}
impl TextureNumLevelsQuery for TextureDepthCube {
fn query_num_levels(&self) -> u32 {
self.get().num_levels()
}
}
pub struct TextureDepthCubeArray {
group: u32,
binding: u32,
data: ModuleVar<TextureDataDepthCubeArray>,
}
impl Wgsl for TextureDepthCubeArray {
fn to_ir() -> wgsl_rs_ir::Type {
wgsl_rs_ir::Type::TextureDepth {
kind: wgsl_rs_ir::TextureDepthKind::DepthCubeArray,
}
}
}
impl TextureDepthCubeArray {
pub const fn new(group: u32, binding: u32) -> Self {
Self {
group,
binding,
data: ModuleVar::new(),
}
}
pub fn group(&self) -> u32 {
self.group
}
pub fn binding(&self) -> u32 {
self.binding
}
pub fn get(&self) -> ModuleVarReadGuard<'_, TextureDataDepthCubeArray> {
self.data.read()
}
pub fn set(&self, data: TextureDataDepthCubeArray) {
self.data.set(data);
}
pub fn init(&self, size: u32, layers: u32) {
self.set(TextureDataDepthCubeArray::new(size, layers));
}
}
impl TextureDimensionsQuery for TextureDepthCubeArray {
type Output = Vec2u;
fn query_dimensions(&self, level: u32) -> Self::Output {
let (w, h) = self.get().dimensions(level);
vec2u(w, h)
}
}
impl TextureNumLayersQuery for TextureDepthCubeArray {
fn query_num_layers(&self) -> u32 {
self.get().num_layers()
}
}
impl TextureNumLevelsQuery for TextureDepthCubeArray {
fn query_num_levels(&self) -> u32 {
self.get().num_levels()
}
}
impl TextureGatherDepth<Vec3f> for TextureDepthCube {
fn gather_depth(&self, sampler: &Sampler, coords: Vec3f) -> Vec4f {
let sampler_state = sampler.get();
let data = self.get();
let texels =
gather_depth_texture_cube(&data, &sampler_state, coords.x(), coords.y(), coords.z());
vec4f(texels[0], texels[1], texels[2], texels[3])
}
}
impl TextureGatherDepthArray<Vec3f, u32> for TextureDepthCubeArray {
fn gather_depth_array(&self, sampler: &Sampler, coords: Vec3f, array_index: u32) -> Vec4f {
let sampler_state = sampler.get();
let data = self.get();
let texels = gather_depth_texture_cube_array(
&data,
&sampler_state,
coords.x(),
coords.y(),
coords.z(),
array_index,
);
vec4f(texels[0], texels[1], texels[2], texels[3])
}
}
impl TextureGatherDepthArray<Vec3f, i32> for TextureDepthCubeArray {
fn gather_depth_array(&self, sampler: &Sampler, coords: Vec3f, array_index: i32) -> Vec4f {
self.gather_depth_array(sampler, coords, array_index.max(0) as u32)
}
}
impl TextureGatherCompare<Vec3f, f32> for TextureDepthCube {
fn gather_compare(&self, sampler: &SamplerComparison, coords: Vec3f, depth_ref: f32) -> Vec4f {
let sampler_state = sampler.get();
let data = self.get();
let texels = gather_depth_texture_cube(
&data,
&sampler_state.sampler,
coords.x(),
coords.y(),
coords.z(),
);
vec4f(
sampler_state.compare.compare(texels[0], depth_ref),
sampler_state.compare.compare(texels[1], depth_ref),
sampler_state.compare.compare(texels[2], depth_ref),
sampler_state.compare.compare(texels[3], depth_ref),
)
}
}
impl TextureGatherCompareArray<Vec3f, u32, f32> for TextureDepthCubeArray {
fn gather_compare_array(
&self,
sampler: &SamplerComparison,
coords: Vec3f,
array_index: u32,
depth_ref: f32,
) -> Vec4f {
let sampler_state = sampler.get();
let data = self.get();
let texels = gather_depth_texture_cube_array(
&data,
&sampler_state.sampler,
coords.x(),
coords.y(),
coords.z(),
array_index,
);
vec4f(
sampler_state.compare.compare(texels[0], depth_ref),
sampler_state.compare.compare(texels[1], depth_ref),
sampler_state.compare.compare(texels[2], depth_ref),
sampler_state.compare.compare(texels[3], depth_ref),
)
}
}
impl TextureGatherCompareArray<Vec3f, i32, f32> for TextureDepthCubeArray {
fn gather_compare_array(
&self,
sampler: &SamplerComparison,
coords: Vec3f,
array_index: i32,
depth_ref: f32,
) -> Vec4f {
self.gather_compare_array(sampler, coords, array_index.max(0) as u32, depth_ref)
}
}
pub struct TextureDepthMultisampled2D {
group: u32,
binding: u32,
data: ModuleVar<TextureDataDepthMultisampled2D>,
}
impl Wgsl for TextureDepthMultisampled2D {
fn to_ir() -> wgsl_rs_ir::Type {
wgsl_rs_ir::Type::TextureDepth {
kind: wgsl_rs_ir::TextureDepthKind::DepthMultisampled2D,
}
}
}
impl TextureDepthMultisampled2D {
pub const fn new(group: u32, binding: u32) -> Self {
Self {
group,
binding,
data: ModuleVar::new(),
}
}
pub fn group(&self) -> u32 {
self.group
}
pub fn binding(&self) -> u32 {
self.binding
}
pub fn get(&self) -> ModuleVarReadGuard<'_, TextureDataDepthMultisampled2D> {
self.data.read()
}
pub fn set(&self, data: TextureDataDepthMultisampled2D) {
self.data.set(data);
}
pub fn init(&self, width: u32, height: u32, sample_count: u32) {
self.set(TextureDataDepthMultisampled2D::new(
width,
height,
sample_count,
))
}
}
impl TextureDimensionsQuery for TextureDepthMultisampled2D {
type Output = Vec2u;
fn query_dimensions(&self, _level: u32) -> Self::Output {
let (w, h) = self.get().dimensions();
vec2u(w, h)
}
}
impl TextureNumSamplesQuery for TextureDepthMultisampled2D {
fn query_num_samples(&self) -> u32 {
self.get().num_samples()
}
}
pub trait WgslTexelFormat: crate::std::AnySendSync {
fn to_ir() -> wgsl_rs_ir::TexelFormat;
type Value: Copy + Default + Into<[Self::Scalar; 4]> + From<[Self::Scalar; 4]>;
type Scalar: Copy + Default + crate::std::AnySendSync;
fn requires_tier1() -> bool;
}
pub trait WgslStorageAccess: crate::std::AnySendSync {
fn to_ir() -> wgsl_rs_ir::StorageTextureAccess;
}
pub trait ReadableStorageAccess: WgslStorageAccess {}
pub trait WritableStorageAccess: WgslStorageAccess {}
impl WgslStorageAccess for crate::std::Read {
fn to_ir() -> wgsl_rs_ir::StorageTextureAccess {
wgsl_rs_ir::StorageTextureAccess::Read
}
}
impl ReadableStorageAccess for crate::std::Read {}
impl WgslStorageAccess for crate::std::Write {
fn to_ir() -> wgsl_rs_ir::StorageTextureAccess {
wgsl_rs_ir::StorageTextureAccess::Write
}
}
impl WritableStorageAccess for crate::std::Write {}
impl WgslStorageAccess for crate::std::ReadWrite {
fn to_ir() -> wgsl_rs_ir::StorageTextureAccess {
wgsl_rs_ir::StorageTextureAccess::ReadWrite
}
}
impl ReadableStorageAccess for crate::std::ReadWrite {}
impl WritableStorageAccess for crate::std::ReadWrite {}
macro_rules! impl_texel_format {
($i:ident, $val:ty, $scalar:ty, $tier1:expr) => {
#[allow(dead_code)]
pub struct $i;
impl WgslTexelFormat for $i {
fn to_ir() -> wgsl_rs_ir::TexelFormat {
wgsl_rs_ir::TexelFormat::$i
}
type Value = $val;
type Scalar = $scalar;
fn requires_tier1() -> bool {
$tier1
}
}
};
}
impl_texel_format!(Rgba8unorm, Vec4f, f32, false);
impl_texel_format!(Rgba8snorm, Vec4f, f32, false);
impl_texel_format!(Rgba8uint, Vec4u, u32, false);
impl_texel_format!(Rgba8sint, Vec4i, i32, false);
impl_texel_format!(Rgba16uint, Vec4u, u32, false);
impl_texel_format!(Rgba16sint, Vec4i, i32, false);
impl_texel_format!(Rgba16float, Vec4f, f32, false);
impl_texel_format!(R32uint, Vec4u, u32, false);
impl_texel_format!(R32sint, Vec4i, i32, false);
impl_texel_format!(R32float, Vec4f, f32, false);
impl_texel_format!(Rg32uint, Vec4u, u32, false);
impl_texel_format!(Rg32sint, Vec4i, i32, false);
impl_texel_format!(Rg32float, Vec4f, f32, false);
impl_texel_format!(Rgba32uint, Vec4u, u32, false);
impl_texel_format!(Rgba32sint, Vec4i, i32, false);
impl_texel_format!(Rgba32float, Vec4f, f32, false);
impl_texel_format!(Bgra8unorm, Vec4f, f32, false);
impl_texel_format!(Rgba16unorm, Vec4f, f32, true);
impl_texel_format!(Rgba16snorm, Vec4f, f32, true);
impl_texel_format!(Rg8unorm, Vec4f, f32, true);
impl_texel_format!(Rg8snorm, Vec4f, f32, true);
impl_texel_format!(Rg8uint, Vec4u, u32, true);
impl_texel_format!(Rg8sint, Vec4i, i32, true);
impl_texel_format!(Rg16unorm, Vec4f, f32, true);
impl_texel_format!(Rg16snorm, Vec4f, f32, true);
impl_texel_format!(Rg16uint, Vec4u, u32, true);
impl_texel_format!(Rg16sint, Vec4i, i32, true);
impl_texel_format!(Rg16float, Vec4f, f32, true);
impl_texel_format!(R8unorm, Vec4f, f32, true);
impl_texel_format!(R8snorm, Vec4f, f32, true);
impl_texel_format!(R8uint, Vec4u, u32, true);
impl_texel_format!(R8sint, Vec4i, i32, true);
impl_texel_format!(R16unorm, Vec4f, f32, true);
impl_texel_format!(R16snorm, Vec4f, f32, true);
impl_texel_format!(R16uint, Vec4u, u32, true);
impl_texel_format!(R16sint, Vec4i, i32, true);
impl_texel_format!(R16float, Vec4f, f32, true);
impl_texel_format!(Rgb10a2unorm, Vec4f, f32, true);
impl_texel_format!(Rgb10a2uint, Vec4u, u32, true);
impl_texel_format!(Rg11b10ufloat, Vec4f, f32, true);
pub struct TextureStorage1D<F: WgslTexelFormat, A: WgslStorageAccess> {
group: u32,
binding: u32,
data: ModuleVar<TextureData1D<F::Scalar>>,
_phantom: PhantomData<A>,
}
impl<F: WgslTexelFormat, A: WgslStorageAccess> Wgsl for TextureStorage1D<F, A> {
fn to_ir() -> wgsl_rs_ir::Type {
wgsl_rs_ir::Type::TextureStorage {
kind: wgsl_rs_ir::TextureStorageKind::Storage1D,
format: F::to_ir(),
access: A::to_ir(),
}
}
}
impl<F: WgslTexelFormat, A: WgslStorageAccess> TextureStorage1D<F, A> {
pub const fn new(group: u32, binding: u32) -> Self {
Self {
group,
binding,
data: ModuleVar::new(),
_phantom: PhantomData,
}
}
pub fn group(&self) -> u32 {
self.group
}
pub fn binding(&self) -> u32 {
self.binding
}
pub fn get(&self) -> ModuleVarReadGuard<'_, TextureData1D<F::Scalar>> {
self.data.read()
}
pub fn set(&self, data: TextureData1D<F::Scalar>) {
self.data.set(data);
}
pub fn init(&self, width: u32) {
self.set(TextureData1D::new(width));
}
}
pub struct TextureStorage2D<F: WgslTexelFormat, A: WgslStorageAccess> {
group: u32,
binding: u32,
data: ModuleVar<TextureData2D<F::Scalar>>,
_phantom: PhantomData<A>,
}
impl<F: WgslTexelFormat, A: WgslStorageAccess> Wgsl for TextureStorage2D<F, A> {
fn to_ir() -> wgsl_rs_ir::Type {
wgsl_rs_ir::Type::TextureStorage {
kind: wgsl_rs_ir::TextureStorageKind::Storage2D,
format: F::to_ir(),
access: A::to_ir(),
}
}
}
impl<F: WgslTexelFormat, A: WgslStorageAccess> TextureStorage2D<F, A> {
pub const fn new(group: u32, binding: u32) -> Self {
Self {
group,
binding,
data: ModuleVar::new(),
_phantom: PhantomData,
}
}
pub fn group(&self) -> u32 {
self.group
}
pub fn binding(&self) -> u32 {
self.binding
}
pub fn get(&self) -> ModuleVarReadGuard<'_, TextureData2D<F::Scalar>> {
self.data.read()
}
pub fn set(&self, data: TextureData2D<F::Scalar>) {
self.data.set(data);
}
pub fn init(&self, width: u32, height: u32) {
self.set(TextureData2D::new(width, height));
}
pub fn set_pixel(&self, x: u32, y: u32, value: F::Value) {
let mut data = self.data.write();
data.set_pixel(x, y, 0, value.into());
}
}
pub struct TextureStorage2DArray<F: WgslTexelFormat, A: WgslStorageAccess> {
group: u32,
binding: u32,
data: ModuleVar<TextureData2DArray<F::Scalar>>,
_phantom: PhantomData<A>,
}
impl<F: WgslTexelFormat, A: WgslStorageAccess> Wgsl for TextureStorage2DArray<F, A> {
fn to_ir() -> wgsl_rs_ir::Type {
wgsl_rs_ir::Type::TextureStorage {
kind: wgsl_rs_ir::TextureStorageKind::Storage2DArray,
format: F::to_ir(),
access: A::to_ir(),
}
}
}
impl<F: WgslTexelFormat, A: WgslStorageAccess> TextureStorage2DArray<F, A> {
pub const fn new(group: u32, binding: u32) -> Self {
Self {
group,
binding,
data: ModuleVar::new(),
_phantom: PhantomData,
}
}
pub fn group(&self) -> u32 {
self.group
}
pub fn binding(&self) -> u32 {
self.binding
}
pub fn get(&self) -> ModuleVarReadGuard<'_, TextureData2DArray<F::Scalar>> {
self.data.read()
}
pub fn set(&self, data: TextureData2DArray<F::Scalar>) {
self.data.set(data);
}
pub fn init(&self, width: u32, height: u32, layers: u32) {
self.set(TextureData2DArray::new(width, height, layers));
}
}
pub struct TextureStorage3D<F: WgslTexelFormat, A: WgslStorageAccess> {
group: u32,
binding: u32,
data: ModuleVar<TextureData3D<F::Scalar>>,
_phantom: PhantomData<A>,
}
impl<F: WgslTexelFormat, A: WgslStorageAccess> Wgsl for TextureStorage3D<F, A> {
fn to_ir() -> wgsl_rs_ir::Type {
wgsl_rs_ir::Type::TextureStorage {
kind: wgsl_rs_ir::TextureStorageKind::Storage3D,
format: F::to_ir(),
access: A::to_ir(),
}
}
}
impl<F: WgslTexelFormat, A: WgslStorageAccess> TextureStorage3D<F, A> {
pub const fn new(group: u32, binding: u32) -> Self {
Self {
group,
binding,
data: ModuleVar::new(),
_phantom: PhantomData,
}
}
pub fn group(&self) -> u32 {
self.group
}
pub fn binding(&self) -> u32 {
self.binding
}
pub fn get(&self) -> ModuleVarReadGuard<'_, TextureData3D<F::Scalar>> {
self.data.read()
}
pub fn set(&self, data: TextureData3D<F::Scalar>) {
self.data.set(data);
}
pub fn init(&self, width: u32, height: u32, depth: u32) {
self.set(TextureData3D::new(width, height, depth));
}
}
impl<F, A> TextureLoadStorage<u32> for TextureStorage1D<F, A>
where
F: WgslTexelFormat,
A: ReadableStorageAccess,
{
type Output = F::Value;
fn load_storage(&self, coords: u32) -> Self::Output {
self.get()
.get_pixel(coords, 0)
.map(|p| F::Value::from(*p))
.unwrap_or_default()
}
}
impl<F, A> TextureLoadStorage<i32> for TextureStorage1D<F, A>
where
F: WgslTexelFormat,
A: ReadableStorageAccess,
{
type Output = F::Value;
fn load_storage(&self, coords: i32) -> Self::Output {
let x = coords.max(0) as u32;
self.get()
.get_pixel(x, 0)
.map(|p| F::Value::from(*p))
.unwrap_or_default()
}
}
impl<F, A> TextureLoadStorage<Vec2u> for TextureStorage2D<F, A>
where
F: WgslTexelFormat,
A: ReadableStorageAccess,
{
type Output = F::Value;
fn load_storage(&self, coords: Vec2u) -> Self::Output {
self.get()
.get_pixel(coords.x(), coords.y(), 0)
.map(|p| F::Value::from(*p))
.unwrap_or_default()
}
}
impl<F, A> TextureLoadStorage<Vec2i> for TextureStorage2D<F, A>
where
F: WgslTexelFormat,
A: ReadableStorageAccess,
{
type Output = F::Value;
fn load_storage(&self, coords: Vec2i) -> Self::Output {
let x = coords.x().max(0) as u32;
let y = coords.y().max(0) as u32;
self.get()
.get_pixel(x, y, 0)
.map(|p| F::Value::from(*p))
.unwrap_or_default()
}
}
impl<F, A> TextureLoadStorageArray<Vec2u, u32> for TextureStorage2DArray<F, A>
where
F: WgslTexelFormat,
A: ReadableStorageAccess,
{
type Output = F::Value;
fn load_storage_array(&self, coords: Vec2u, array_index: u32) -> Self::Output {
let data = self.get();
data.layers
.get(array_index as usize)
.and_then(|layer| layer.get_pixel(coords.x(), coords.y(), 0))
.map(|p| F::Value::from(*p))
.unwrap_or_default()
}
}
impl<F, A> TextureLoadStorageArray<Vec2i, i32> for TextureStorage2DArray<F, A>
where
F: WgslTexelFormat,
A: ReadableStorageAccess,
{
type Output = F::Value;
fn load_storage_array(&self, coords: Vec2i, array_index: i32) -> Self::Output {
let x = coords.x().max(0) as u32;
let y = coords.y().max(0) as u32;
let layer = array_index.max(0) as u32;
let data = self.get();
data.layers
.get(layer as usize)
.and_then(|l| l.get_pixel(x, y, 0))
.map(|p| F::Value::from(*p))
.unwrap_or_default()
}
}
impl<F, A> TextureLoadStorage<Vec3u> for TextureStorage3D<F, A>
where
F: WgslTexelFormat,
A: ReadableStorageAccess,
{
type Output = F::Value;
fn load_storage(&self, coords: Vec3u) -> Self::Output {
let data = self.get();
data.mips
.first()
.and_then(|mip| mip.get(coords.z() as usize))
.and_then(|slice| slice.get(coords.y() as usize))
.and_then(|row| row.get(coords.x() as usize))
.map(|p| F::Value::from(*p))
.unwrap_or_default()
}
}
impl<F, A> TextureStore<u32, F::Value> for TextureStorage1D<F, A>
where
F: WgslTexelFormat,
A: WritableStorageAccess,
{
fn store(&self, coords: u32, value: F::Value) {
self.data.write().set_pixel(coords, 0, value.into());
}
}
impl<F, A> TextureStore<i32, F::Value> for TextureStorage1D<F, A>
where
F: WgslTexelFormat,
A: WritableStorageAccess,
{
fn store(&self, coords: i32, value: F::Value) {
let x = coords.max(0) as u32;
self.data.write().set_pixel(x, 0, value.into());
}
}
impl<F, A> TextureStore<Vec2u, F::Value> for TextureStorage2D<F, A>
where
F: WgslTexelFormat,
A: WritableStorageAccess,
{
fn store(&self, coords: Vec2u, value: F::Value) {
self.data
.write()
.set_pixel(coords.x(), coords.y(), 0, value.into());
}
}
impl<F, A> TextureStore<Vec2i, F::Value> for TextureStorage2D<F, A>
where
F: WgslTexelFormat,
A: WritableStorageAccess,
{
fn store(&self, coords: Vec2i, value: F::Value) {
let x = coords.x().max(0) as u32;
let y = coords.y().max(0) as u32;
self.data.write().set_pixel(x, y, 0, value.into());
}
}
impl<F, A> TextureStoreArray<Vec2u, u32, F::Value> for TextureStorage2DArray<F, A>
where
F: WgslTexelFormat,
A: WritableStorageAccess,
{
fn store_array(&self, coords: Vec2u, array_index: u32, value: F::Value) {
let mut data = self.data.write();
if let Some(layer) = data.layers.get_mut(array_index as usize) {
layer.set_pixel(coords.x(), coords.y(), 0, value.into());
}
}
}
impl<F, A> TextureStoreArray<Vec2i, i32, F::Value> for TextureStorage2DArray<F, A>
where
F: WgslTexelFormat,
A: WritableStorageAccess,
{
fn store_array(&self, coords: Vec2i, array_index: i32, value: F::Value) {
let x = coords.x().max(0) as u32;
let y = coords.y().max(0) as u32;
let layer = array_index.max(0) as u32;
let mut data = self.data.write();
if let Some(l) = data.layers.get_mut(layer as usize) {
l.set_pixel(x, y, 0, value.into());
}
}
}
impl<F, A> TextureStore<Vec3u, F::Value> for TextureStorage3D<F, A>
where
F: WgslTexelFormat,
A: WritableStorageAccess,
{
fn store(&self, coords: Vec3u, value: F::Value) {
let mut data = self.data.write();
if let Some(mip) = data.mips.first_mut()
&& let Some(slice) = mip.get_mut(coords.z() as usize)
&& let Some(row) = slice.get_mut(coords.y() as usize)
&& let Some(pixel) = row.get_mut(coords.x() as usize)
{
*pixel = value.into();
}
}
}
impl<F: WgslTexelFormat, A: WgslStorageAccess> TextureDimensionsQuery for TextureStorage1D<F, A> {
type Output = u32;
fn query_dimensions(&self, _level: u32) -> Self::Output {
self.get().width(0)
}
}
impl<F: WgslTexelFormat, A: WgslStorageAccess> TextureDimensionsQuery for TextureStorage2D<F, A> {
type Output = Vec2u;
fn query_dimensions(&self, _level: u32) -> Self::Output {
let (w, h) = self.get().dimensions(0);
vec2u(w, h)
}
}
impl<F: WgslTexelFormat, A: WgslStorageAccess> TextureDimensionsQuery
for TextureStorage2DArray<F, A>
{
type Output = Vec2u;
fn query_dimensions(&self, _level: u32) -> Self::Output {
let (w, h) = self.get().dimensions(0);
vec2u(w, h)
}
}
impl<F: WgslTexelFormat, A: WgslStorageAccess> TextureNumLayersQuery
for TextureStorage2DArray<F, A>
{
fn query_num_layers(&self) -> u32 {
self.get().num_layers()
}
}
impl<F: WgslTexelFormat, A: WgslStorageAccess> TextureDimensionsQuery for TextureStorage3D<F, A> {
type Output = Vec3u;
fn query_dimensions(&self, _level: u32) -> Self::Output {
let (w, h, d) = self.get().dimensions(0);
vec3u(w, h, d)
}
}
#[cfg(test)]
mod tests {
use crate::std::{ReadWrite, Write, vec2f, vec2i, vec3f, vec3i};
use super::*;
#[test]
fn test_texture_2d_dimensions() {
let tex: Texture2D<f32> = Texture2D::new(0, 0);
tex.init(64, 32);
let dims = texture_dimensions(&tex);
assert_eq!(dims.x(), 64);
assert_eq!(dims.y(), 32);
}
#[test]
fn test_texture_2d_num_levels() {
let tex: Texture2D<f32> = Texture2D::new(0, 0);
tex.init(64, 32);
let levels = texture_num_levels(&tex);
assert_eq!(levels, 1);
}
#[test]
fn test_texture_2d_load_f32() {
let tex: Texture2D<f32> = Texture2D::new(0, 0);
tex.init(4, 4);
tex.set_pixel(1, 2, [0.5, 0.25, 0.125, 1.0]);
let pixel = texture_load(&tex, vec2u(1, 2), 0u32);
assert!((pixel.x() - 0.5).abs() < 0.001);
assert!((pixel.y() - 0.25).abs() < 0.001);
assert!((pixel.z() - 0.125).abs() < 0.001);
assert!((pixel.w() - 1.0).abs() < 0.001);
let pixel = texture_load(&tex, vec2i(1, 2), 0i32);
assert!((pixel.x() - 0.5).abs() < 0.001);
}
#[test]
fn test_texture_2d_load_i32() {
let tex: Texture2D<i32> = Texture2D::new(0, 0);
tex.init(4, 4);
tex.set_pixel(1, 2, [10, 20, 30, 40]);
let pixel = texture_load(&tex, vec2u(1, 2), 0u32);
assert_eq!(pixel.x(), 10);
assert_eq!(pixel.y(), 20);
assert_eq!(pixel.z(), 30);
assert_eq!(pixel.w(), 40);
}
#[test]
fn test_texture_2d_load_u32() {
let tex: Texture2D<u32> = Texture2D::new(0, 0);
tex.init(4, 4);
tex.set_pixel(1, 2, [100, 200, 150, 255]);
let pixel = texture_load(&tex, vec2u(1, 2), 0u32);
assert_eq!(pixel.x(), 100);
assert_eq!(pixel.y(), 200);
assert_eq!(pixel.z(), 150);
assert_eq!(pixel.w(), 255);
}
#[test]
fn test_texture_1d_load_f32() {
let tex: Texture1D<f32> = Texture1D::new(0, 0);
tex.init(8);
tex.data.write().set_pixel(3, 0, [0.1, 0.2, 0.3, 0.4]);
let pixel = texture_load(&tex, 3u32, 0u32);
assert!((pixel.x() - 0.1).abs() < 0.001);
assert!((pixel.y() - 0.2).abs() < 0.001);
}
#[test]
fn test_texture_2d_sample() {
let tex: Texture2D<f32> = Texture2D::new(0, 0);
tex.init(2, 2);
tex.set_pixel(0, 0, [1.0, 0.0, 0.0, 1.0]); tex.set_pixel(1, 0, [0.0, 1.0, 0.0, 1.0]); tex.set_pixel(0, 1, [0.0, 0.0, 1.0, 1.0]); tex.set_pixel(1, 1, [1.0, 1.0, 0.0, 1.0]);
let sampler: Sampler = Sampler::new(0, 0);
sampler.init();
let color = texture_sample(&tex, &sampler, vec2f(0.25, 0.25));
assert!((color.x() - 1.0).abs() < 0.001); assert!((color.y() - 0.0).abs() < 0.001);
assert!((color.z() - 0.0).abs() < 0.001);
}
#[test]
fn test_texture_2d_sample_level() {
let tex: Texture2D<f32> = Texture2D::new(0, 0);
tex.init(2, 2);
tex.set_pixel(0, 0, [0.5, 0.5, 0.5, 1.0]);
let sampler: Sampler = Sampler::new(0, 0);
sampler.init();
let color = texture_sample_level(&tex, &sampler, vec2f(0.25, 0.25), 0.0f32);
assert!((color.x() - 0.5).abs() < 0.001);
}
#[test]
fn test_texture_depth_2d_load() {
let tex: TextureDepth2D = TextureDepth2D::new(0, 0);
tex.init(4, 4);
{
let mut guard = tex.data.write();
guard.mips[0][2][1] = 0.75; }
let depth = texture_load(&tex, vec2u(1, 2), 0u32);
assert!((depth - 0.75).abs() < 0.001);
}
#[test]
fn test_texture_depth_2d_sample_compare() {
let tex: TextureDepth2D = TextureDepth2D::new(0, 0);
tex.init(4, 4);
{
let mut guard = tex.data.write();
for row in guard.mips[0].iter_mut() {
for depth in row.iter_mut() {
*depth = 0.5;
}
}
}
let sampler: SamplerComparison = SamplerComparison::new(0, 0);
sampler.set(SamplerComparisonState {
compare: CompareFunction::Less,
..Default::default()
});
let result = texture_sample_compare(&tex, &sampler, vec2f(0.5, 0.5), 0.3);
assert!((result - 1.0).abs() < 0.001);
let result = texture_sample_compare(&tex, &sampler, vec2f(0.5, 0.5), 0.6);
assert!((result - 0.0).abs() < 0.001);
}
#[test]
fn test_texture_2d_array_queries() {
let tex: Texture2DArray<f32> = Texture2DArray::new(0, 0);
tex.init(16, 8, 4);
let dims = texture_dimensions(&tex);
assert_eq!(dims.x(), 16);
assert_eq!(dims.y(), 8);
let layers = texture_num_layers(&tex);
assert_eq!(layers, 4);
let levels = texture_num_levels(&tex);
assert_eq!(levels, 1);
}
#[test]
fn test_texture_3d_queries() {
let tex: Texture3D<f32> = Texture3D::new(0, 0);
tex.init(8, 4, 2);
let dims = texture_dimensions(&tex);
assert_eq!(dims.x(), 8);
assert_eq!(dims.y(), 4);
assert_eq!(dims.z(), 2);
}
#[test]
fn test_texture_cube_queries() {
let tex: TextureCube<f32> = TextureCube::new(0, 0);
tex.init(32);
let dims = texture_dimensions(&tex);
assert_eq!(dims.x(), 32);
assert_eq!(dims.y(), 32);
}
#[test]
fn test_texture_multisampled_queries() {
let tex: TextureMultisampled2D<f32> = TextureMultisampled2D::new(0, 0);
tex.init(64, 64, 4);
let dims = texture_dimensions(&tex);
assert_eq!(dims.x(), 64);
assert_eq!(dims.y(), 64);
let samples = texture_num_samples(&tex);
assert_eq!(samples, 4);
}
#[test]
fn test_compare_function() {
assert!((CompareFunction::Less.compare(0.5, 0.3) - 1.0).abs() < 0.001); assert!((CompareFunction::Less.compare(0.3, 0.5) - 0.0).abs() < 0.001); assert!((CompareFunction::Greater.compare(0.3, 0.5) - 1.0).abs() < 0.001); assert!((CompareFunction::Greater.compare(0.5, 0.3) - 0.0).abs() < 0.001); assert!((CompareFunction::Equal.compare(0.5, 0.5) - 1.0).abs() < 0.001);
assert!((CompareFunction::LessEqual.compare(0.5, 0.5) - 1.0).abs() < 0.001);
assert!((CompareFunction::GreaterEqual.compare(0.5, 0.5) - 1.0).abs() < 0.001);
assert!((CompareFunction::Always.compare(0.0, 1.0) - 1.0).abs() < 0.001);
assert!((CompareFunction::Never.compare(0.5, 0.5) - 0.0).abs() < 0.001);
}
#[test]
fn test_address_modes() {
assert!(
(SamplerState::apply_address_mode(AddressMode::ClampToEdge, -0.5) - 0.0).abs() < 0.001
);
assert!(
(SamplerState::apply_address_mode(AddressMode::ClampToEdge, 1.5) - 1.0).abs() < 0.001
);
assert!(
(SamplerState::apply_address_mode(AddressMode::ClampToEdge, 0.5) - 0.5).abs() < 0.001
);
assert!((SamplerState::apply_address_mode(AddressMode::Repeat, 1.5) - 0.5).abs() < 0.001);
assert!((SamplerState::apply_address_mode(AddressMode::Repeat, 2.25) - 0.25).abs() < 0.001);
let mirror = SamplerState::apply_address_mode(AddressMode::MirrorRepeat, 1.5);
assert!((mirror - 0.5).abs() < 0.001); }
#[test]
fn test_texture_sample_base_clamp_to_edge() {
let tex: Texture2D<f32> = Texture2D::new(0, 0);
tex.init(4, 4);
tex.set_pixel(0, 0, [1.0, 0.0, 0.0, 1.0]); tex.set_pixel(3, 0, [0.0, 1.0, 0.0, 1.0]); tex.set_pixel(0, 3, [0.0, 0.0, 1.0, 1.0]); tex.set_pixel(3, 3, [1.0, 1.0, 0.0, 1.0]);
let sampler: Sampler = Sampler::new(0, 0);
sampler.init();
let color = texture_sample_base_clamp_to_edge(&tex, &sampler, vec2f(-0.5, -0.5));
assert!((color.x() - 1.0).abs() < 0.001); assert!((color.y() - 0.0).abs() < 0.001);
let color = texture_sample_base_clamp_to_edge(&tex, &sampler, vec2f(1.5, 1.5));
assert!((color.x() - 1.0).abs() < 0.001); assert!((color.y() - 1.0).abs() < 0.001); assert!((color.z() - 0.0).abs() < 0.001);
let clamped = texture_sample_base_clamp_to_edge(&tex, &sampler, vec2f(0.5, 0.5));
let normal = texture_sample(&tex, &sampler, vec2f(0.5, 0.5));
assert!((clamped.x() - normal.x()).abs() < 0.001);
assert!((clamped.y() - normal.y()).abs() < 0.001);
assert!((clamped.z() - normal.z()).abs() < 0.001);
assert!((clamped.w() - normal.w()).abs() < 0.001);
}
#[test]
fn test_texture_gather_compare() {
let tex: TextureDepth2D = TextureDepth2D::new(0, 0);
tex.init(4, 4);
{
let mut guard = tex.data.write();
guard.mips[0][0][0] = 0.2;
guard.mips[0][0][1] = 0.4;
guard.mips[0][1][0] = 0.6;
guard.mips[0][1][1] = 0.8;
}
let sampler: SamplerComparison = SamplerComparison::new(0, 0);
sampler.set(SamplerComparisonState {
compare: CompareFunction::Less,
..Default::default()
});
let result = texture_gather_compare(&tex, &sampler, vec2f(0.25, 0.25), 0.5);
assert!((result.x() - 1.0).abs() < 0.001); assert!((result.y() - 1.0).abs() < 0.001); assert!((result.z() - 0.0).abs() < 0.001); assert!((result.w() - 0.0).abs() < 0.001);
let result = texture_gather_compare(&tex, &sampler, vec2f(0.25, 0.25), 0.9);
assert!((result.x() - 0.0).abs() < 0.001);
assert!((result.y() - 0.0).abs() < 0.001);
assert!((result.z() - 0.0).abs() < 0.001);
assert!((result.w() - 0.0).abs() < 0.001);
let result = texture_gather_compare(&tex, &sampler, vec2f(0.25, 0.25), 0.1);
assert!((result.x() - 1.0).abs() < 0.001);
assert!((result.y() - 1.0).abs() < 0.001);
assert!((result.z() - 1.0).abs() < 0.001);
assert!((result.w() - 1.0).abs() < 0.001);
}
#[test]
fn test_texture_2d_sample_offset() {
let tex: Texture2D<f32> = Texture2D::new(0, 0);
tex.init(4, 4);
tex.set_pixel(2, 1, [0.8, 0.6, 0.4, 1.0]);
let sampler: Sampler = Sampler::new(0, 0);
sampler.init();
let color = texture_sample_offset(&tex, &sampler, vec2f(0.375, 0.375), vec2i(1, 0));
assert!((color.x() - 0.8).abs() < 0.001);
assert!((color.y() - 0.6).abs() < 0.001);
assert!((color.z() - 0.4).abs() < 0.001);
}
#[test]
fn test_texture_2d_sample_level_offset() {
let tex: Texture2D<f32> = Texture2D::new(0, 0);
tex.init(4, 4);
tex.set_pixel(3, 2, [0.1, 0.2, 0.3, 0.4]);
let sampler: Sampler = Sampler::new(0, 0);
sampler.init();
let color =
texture_sample_level_offset(&tex, &sampler, vec2f(0.625, 0.625), 0.0f32, vec2i(1, 0));
assert!((color.x() - 0.1).abs() < 0.001);
assert!((color.y() - 0.2).abs() < 0.001);
}
#[test]
fn test_texture_2d_sample_bias() {
let tex: Texture2D<f32> = Texture2D::new(0, 0);
tex.init(2, 2);
tex.set_pixel(0, 0, [1.0, 0.0, 0.0, 1.0]);
let sampler: Sampler = Sampler::new(0, 0);
sampler.init();
let color = texture_sample_bias(&tex, &sampler, vec2f(0.25, 0.25), 2.0f32);
assert!((color.x() - 1.0).abs() < 0.001);
}
#[test]
fn test_texture_2d_sample_grad() {
let tex: Texture2D<f32> = Texture2D::new(0, 0);
tex.init(2, 2);
tex.set_pixel(1, 1, [0.5, 0.5, 0.5, 1.0]);
let sampler: Sampler = Sampler::new(0, 0);
sampler.init();
let color = texture_sample_grad(
&tex,
&sampler,
vec2f(0.75, 0.75),
vec2f(1.0, 0.0),
vec2f(0.0, 1.0),
);
assert!((color.x() - 0.5).abs() < 0.001);
}
#[test]
fn test_texture_2d_array_sample_array() {
let tex: Texture2DArray<f32> = Texture2DArray::new(0, 0);
tex.init(2, 2, 3);
{
let mut guard = tex.data.write();
guard.layers[1].set_pixel(0, 0, 0, [0.0, 1.0, 0.0, 1.0]); }
let sampler: Sampler = Sampler::new(0, 0);
sampler.init();
let color = texture_sample_array(&tex, &sampler, vec2f(0.25, 0.25), 1u32);
assert!((color.x() - 0.0).abs() < 0.001);
assert!((color.y() - 1.0).abs() < 0.001); }
#[test]
fn test_texture_2d_array_sample_array_offset() {
let tex: Texture2DArray<f32> = Texture2DArray::new(0, 0);
tex.init(4, 4, 2);
{
let mut guard = tex.data.write();
guard.layers[0].set_pixel(2, 1, 0, [0.3, 0.6, 0.9, 1.0]);
}
let sampler: Sampler = Sampler::new(0, 0);
sampler.init();
let color =
texture_sample_array_offset(&tex, &sampler, vec2f(0.375, 0.375), 0u32, vec2i(1, 0));
assert!((color.x() - 0.3).abs() < 0.001);
assert!((color.y() - 0.6).abs() < 0.001);
assert!((color.z() - 0.9).abs() < 0.001);
}
#[test]
fn test_texture_depth_2d_sample_compare_offset() {
let tex: TextureDepth2D = TextureDepth2D::new(0, 0);
tex.init(4, 4);
{
let mut guard = tex.data.write();
for row in guard.mips[0].iter_mut() {
for depth in row.iter_mut() {
*depth = 0.5;
}
}
}
let sampler: SamplerComparison = SamplerComparison::new(0, 0);
sampler.set(SamplerComparisonState {
compare: CompareFunction::Less,
..Default::default()
});
let result =
texture_sample_compare_offset(&tex, &sampler, vec2f(0.5, 0.5), 0.3, vec2i(1, 0));
assert!((result - 1.0).abs() < 0.001);
let result =
texture_sample_compare_offset(&tex, &sampler, vec2f(0.5, 0.5), 0.7, vec2i(1, 0));
assert!((result - 0.0).abs() < 0.001);
}
#[test]
fn test_texture_depth_2d_sample_compare_level() {
let tex: TextureDepth2D = TextureDepth2D::new(0, 0);
tex.init(4, 4);
{
let mut guard = tex.data.write();
for row in guard.mips[0].iter_mut() {
for depth in row.iter_mut() {
*depth = 0.5;
}
}
}
let sampler: SamplerComparison = SamplerComparison::new(0, 0);
sampler.set(SamplerComparisonState {
compare: CompareFunction::Less,
..Default::default()
});
let result = texture_sample_compare_level(&tex, &sampler, vec2f(0.5, 0.5), 0.3);
assert!((result - 1.0).abs() < 0.001);
let result = texture_sample_compare_level(&tex, &sampler, vec2f(0.5, 0.5), 0.7);
assert!((result - 0.0).abs() < 0.001); }
#[test]
fn test_texture_depth_2d_gather_depth() {
let tex: TextureDepth2D = TextureDepth2D::new(0, 0);
tex.init(4, 4);
{
let mut guard = tex.data.write();
guard.mips[0][0][0] = 0.1;
guard.mips[0][0][1] = 0.2;
guard.mips[0][1][0] = 0.3;
guard.mips[0][1][1] = 0.4;
}
let sampler: Sampler = Sampler::new(0, 0);
sampler.init();
let result = texture_gather_depth(&tex, &sampler, vec2f(0.25, 0.25));
assert!((result.x() - 0.3).abs() < 0.001); assert!((result.y() - 0.4).abs() < 0.001); assert!((result.z() - 0.2).abs() < 0.001); assert!((result.w() - 0.1).abs() < 0.001); }
#[test]
fn test_texture_depth_2d_gather_depth_offset() {
let tex: TextureDepth2D = TextureDepth2D::new(0, 0);
tex.init(4, 4);
{
let mut guard = tex.data.write();
guard.mips[0][0][1] = 0.15;
guard.mips[0][0][2] = 0.25;
guard.mips[0][1][1] = 0.35;
guard.mips[0][1][2] = 0.45;
}
let sampler: Sampler = Sampler::new(0, 0);
sampler.init();
let result = texture_gather_depth_offset(&tex, &sampler, vec2f(0.25, 0.25), vec2i(1, 0));
assert!((result.x() - 0.35).abs() < 0.001); assert!((result.y() - 0.45).abs() < 0.001); assert!((result.z() - 0.25).abs() < 0.001); assert!((result.w() - 0.15).abs() < 0.001); }
#[test]
fn test_texture_depth_2d_array_sample_compare_array() {
let tex: TextureDepth2DArray = TextureDepth2DArray::new(0, 0);
tex.init(4, 4, 2);
{
let mut guard = tex.data.write();
for row in guard.layers[1].mips[0].iter_mut() {
for depth in row.iter_mut() {
*depth = 0.6;
}
}
}
let sampler: SamplerComparison = SamplerComparison::new(0, 0);
sampler.set(SamplerComparisonState {
compare: CompareFunction::Less,
..Default::default()
});
let result = texture_sample_compare_array(&tex, &sampler, vec2f(0.5, 0.5), 1u32, 0.4);
assert!((result - 1.0).abs() < 0.001);
let result = texture_sample_compare_array(&tex, &sampler, vec2f(0.5, 0.5), 1u32, 0.8);
assert!((result - 0.0).abs() < 0.001);
}
#[test]
fn test_texture_gather_compare_offset() {
let tex: TextureDepth2D = TextureDepth2D::new(0, 0);
tex.init(4, 4);
{
let mut guard = tex.data.write();
guard.mips[0][0][1] = 0.2;
guard.mips[0][0][2] = 0.4;
guard.mips[0][1][1] = 0.6;
guard.mips[0][1][2] = 0.8;
}
let sampler: SamplerComparison = SamplerComparison::new(0, 0);
sampler.set(SamplerComparisonState {
compare: CompareFunction::Less,
..Default::default()
});
let result =
texture_gather_compare_offset(&tex, &sampler, vec2f(0.25, 0.25), 0.5, vec2i(1, 0));
assert!((result.x() - 1.0).abs() < 0.001);
assert!((result.y() - 1.0).abs() < 0.001);
assert!((result.z() - 0.0).abs() < 0.001);
assert!((result.w() - 0.0).abs() < 0.001);
}
#[test]
fn test_texture_load_multisampled() {
let tex: TextureMultisampled2D<f32> = TextureMultisampled2D::new(0, 0);
tex.init(4, 4, 4);
{
let mut guard = tex.data.write();
guard.samples[2][1][2] = [0.7, 0.3, 0.1, 1.0]; }
let pixel = texture_load_multisampled(&tex, vec2u(1, 2), 2u32);
assert!((pixel.x() - 0.7).abs() < 0.001);
assert!((pixel.y() - 0.3).abs() < 0.001);
assert!((pixel.z() - 0.1).abs() < 0.001);
assert!((pixel.w() - 1.0).abs() < 0.001);
let pixel = texture_load_multisampled(&tex, vec2i(1, 2), 2i32);
assert!((pixel.x() - 0.7).abs() < 0.001);
let pixel = texture_load_multisampled(&tex, vec2u(10, 10), 0u32);
assert!((pixel.x() - 0.0).abs() < 0.001);
}
#[test]
fn test_texture_sample_compare_level_offset() {
let tex: TextureDepth2D = TextureDepth2D::new(0, 0);
tex.init(4, 4);
{
let mut guard = tex.data.write();
for row in guard.mips[0].iter_mut() {
for depth in row.iter_mut() {
*depth = 0.5;
}
}
}
let sampler: SamplerComparison = SamplerComparison::new(0, 0);
sampler.set(SamplerComparisonState {
compare: CompareFunction::Less,
..Default::default()
});
let result =
texture_sample_compare_level_offset(&tex, &sampler, vec2f(0.5, 0.5), 0.3, vec2i(0, 0));
assert!((result - 1.0).abs() < 0.001); }
#[test]
fn test_storage_texture_2d_store_and_load() {
let tex: TextureStorage2D<Rgba8unorm, ReadWrite> = TextureStorage2D::new(0, 0);
tex.init(4, 4);
texture_store(&tex, vec2u(1, 2), vec4f(0.5, 0.25, 0.125, 1.0));
let pixel = texture_load_storage(&tex, vec2u(1, 2));
assert!((pixel.x() - 0.5).abs() < 0.001);
assert!((pixel.y() - 0.25).abs() < 0.001);
assert!((pixel.z() - 0.125).abs() < 0.001);
assert!((pixel.w() - 1.0).abs() < 0.001);
}
#[test]
fn test_storage_texture_2d_dimensions() {
let tex: TextureStorage2D<Rgba8unorm, Write> = TextureStorage2D::new(0, 0);
tex.init(8, 4);
let dims = texture_dimensions(&tex);
assert_eq!(dims.x(), 8);
assert_eq!(dims.y(), 4);
}
#[test]
fn test_storage_texture_2d_store_i32_coords() {
let tex: TextureStorage2D<Rgba8uint, ReadWrite> = TextureStorage2D::new(0, 0);
tex.init(4, 4);
texture_store(&tex, vec2i(1, 2), vec4u(10, 20, 30, 40));
let pixel = texture_load_storage(&tex, vec2u(1, 2));
assert_eq!(pixel.x(), 10);
assert_eq!(pixel.y(), 20);
assert_eq!(pixel.z(), 30);
assert_eq!(pixel.w(), 40);
}
#[test]
fn test_storage_texture_2d_array_store_and_load() {
let tex: TextureStorage2DArray<Rgba8sint, ReadWrite> = TextureStorage2DArray::new(0, 0);
tex.init(4, 4, 2);
texture_store_array(&tex, vec2u(1, 2), 0u32, vec4i(10, -20, 30, -40));
texture_store_array(&tex, vec2u(3, 0), 1u32, vec4i(100, 200, -300, 400));
let p0 = texture_load_storage_array(&tex, vec2u(1, 2), 0u32);
assert_eq!(p0.x(), 10);
assert_eq!(p0.y(), -20);
assert_eq!(p0.z(), 30);
assert_eq!(p0.w(), -40);
let p1 = texture_load_storage_array(&tex, vec2u(3, 0), 1u32);
assert_eq!(p1.x(), 100);
assert_eq!(p1.y(), 200);
assert_eq!(p1.z(), -300);
assert_eq!(p1.w(), 400);
assert_eq!(texture_num_layers(&tex), 2);
}
#[test]
fn test_storage_texture_3d_store_and_load() {
let tex: TextureStorage3D<Rgba32float, ReadWrite> = TextureStorage3D::new(0, 0);
tex.init(4, 4, 4);
texture_store(&tex, vec3u(1, 2, 3), vec4f(0.1, 0.2, 0.3, 0.4));
let pixel = texture_load_storage(&tex, vec3u(1, 2, 3));
assert!((pixel.x() - 0.1).abs() < 0.001);
assert!((pixel.y() - 0.2).abs() < 0.001);
assert!((pixel.z() - 0.3).abs() < 0.001);
assert!((pixel.w() - 0.4).abs() < 0.001);
}
#[test]
fn test_storage_texture_1d_store_and_load() {
let tex: TextureStorage1D<Rgba8uint, ReadWrite> = TextureStorage1D::new(0, 0);
tex.init(8);
texture_store(&tex, 3u32, vec4u(10, 20, 30, 40));
let pixel = texture_load_storage(&tex, 3u32);
assert_eq!(pixel.x(), 10);
assert_eq!(pixel.y(), 20);
assert_eq!(pixel.z(), 30);
assert_eq!(pixel.w(), 40);
}
#[test]
fn test_texture_1d_sample() {
let tex: Texture1D<f32> = Texture1D::new(0, 0);
tex.init(4);
tex.data.write().set_pixel(0, 0, [1.0, 0.0, 0.0, 1.0]);
tex.data.write().set_pixel(1, 0, [0.0, 1.0, 0.0, 1.0]);
tex.data.write().set_pixel(2, 0, [0.0, 0.0, 1.0, 1.0]);
tex.data.write().set_pixel(3, 0, [1.0, 1.0, 0.0, 1.0]);
let sampler = Sampler::new(0, 0);
sampler.init();
let color = texture_sample(&tex, &sampler, 0.125f32);
assert!((color.x() - 1.0).abs() < 0.001);
assert!((color.y() - 0.0).abs() < 0.001);
let color = texture_sample(&tex, &sampler, 0.625f32);
assert!((color.z() - 1.0).abs() < 0.001);
}
#[test]
fn test_texture_1d_sample_level() {
let tex: Texture1D<f32> = Texture1D::new(0, 0);
tex.init(4);
tex.data.write().set_pixel(1, 0, [0.5, 0.25, 0.125, 1.0]);
let sampler = Sampler::new(0, 0);
sampler.init();
let color = texture_sample_level(&tex, &sampler, 0.375f32, 0.0f32);
assert!((color.x() - 0.5).abs() < 0.001);
}
#[test]
fn test_texture_3d_load() {
let tex: Texture3D<f32> = Texture3D::new(0, 0);
tex.init(4, 4, 2);
tex.set_pixel(1, 2, 0, [0.5, 0.25, 0.125, 1.0]);
tex.set_pixel(1, 2, 1, [0.9, 0.8, 0.7, 0.6]);
let pixel = texture_load(&tex, vec3u(1, 2, 0), 0u32);
assert!((pixel.x() - 0.5).abs() < 0.001);
assert!((pixel.y() - 0.25).abs() < 0.001);
let pixel = texture_load(&tex, vec3u(1, 2, 1), 0u32);
assert!((pixel.x() - 0.9).abs() < 0.001);
let pixel = texture_load(&tex, vec3i(1, 2, 1), 0i32);
assert!((pixel.x() - 0.9).abs() < 0.001);
}
#[test]
fn test_texture_3d_sample_nearest() {
let tex: Texture3D<f32> = Texture3D::new(0, 0);
tex.init(2, 2, 2);
tex.set_pixel(0, 0, 0, [1.0, 0.0, 0.0, 1.0]); tex.set_pixel(1, 0, 0, [0.0, 1.0, 0.0, 1.0]); tex.set_pixel(0, 1, 0, [0.0, 0.0, 1.0, 1.0]); tex.set_pixel(1, 1, 0, [1.0, 1.0, 0.0, 1.0]); tex.set_pixel(0, 0, 1, [1.0, 0.0, 1.0, 1.0]); tex.set_pixel(1, 0, 1, [0.0, 1.0, 1.0, 1.0]); tex.set_pixel(0, 1, 1, [0.5, 0.5, 0.5, 1.0]); tex.set_pixel(1, 1, 1, [1.0, 1.0, 1.0, 1.0]);
let sampler = Sampler::new(0, 0);
sampler.init();
let color = texture_sample(&tex, &sampler, vec3f(0.25, 0.25, 0.25));
assert!((color.x() - 1.0).abs() < 0.001); assert!((color.y() - 0.0).abs() < 0.001);
let color = texture_sample(&tex, &sampler, vec3f(0.75, 0.75, 0.75));
assert!((color.x() - 1.0).abs() < 0.001); assert!((color.y() - 1.0).abs() < 0.001);
assert!((color.z() - 1.0).abs() < 0.001);
let color = texture_sample(&tex, &sampler, vec3f(0.25, 0.75, 0.75));
assert!((color.x() - 0.5).abs() < 0.001);
}
#[test]
fn test_texture_3d_sample_level() {
let tex: Texture3D<f32> = Texture3D::new(0, 0);
tex.init(2, 2, 2);
tex.set_pixel(0, 0, 0, [0.5, 0.5, 0.5, 1.0]);
let sampler = Sampler::new(0, 0);
sampler.init();
let color = texture_sample_level(&tex, &sampler, vec3f(0.25, 0.25, 0.25), 0.0f32);
assert!((color.x() - 0.5).abs() < 0.001);
}
#[test]
fn test_texture_3d_sample_linear() {
let tex: Texture3D<f32> = Texture3D::new(0, 0);
tex.init(2, 2, 2);
tex.set_pixel(0, 0, 0, [1.0, 0.0, 0.0, 1.0]);
let sampler = Sampler::new(0, 0);
sampler.set(SamplerState::linear());
let color = texture_sample(&tex, &sampler, vec3f(0.0, 0.0, 0.0));
assert!(
(color.x() - 0.125).abs() < 0.001,
"expected r=0.125 from trilinear, got {}",
color.x()
);
assert!((color.y() - 0.0).abs() < 0.001);
assert!((color.z() - 0.0).abs() < 0.001);
assert!(
(color.w() - 0.125).abs() < 0.001,
"expected w=0.125 from trilinear, got {}",
color.w()
);
let color = texture_sample(&tex, &sampler, vec3f(1.0, 1.0, 1.0));
assert!((color.x() - 0.0).abs() < 0.001);
let color = texture_sample(&tex, &sampler, vec3f(0.25, 0.25, 0.25));
assert!(
(color.x() - 1.0).abs() < 0.001,
"expected r=1.0 at (0.25,0.25,0.25), got {}",
color.x()
);
}
#[test]
fn test_texture_cube_sample() {
let face_colors: [[f32; 4]; 6] = [
[1.0, 0.0, 0.0, 1.0], [0.0, 1.0, 0.0, 1.0], [0.0, 0.0, 1.0, 1.0], [1.0, 1.0, 0.0, 1.0], [1.0, 0.0, 1.0, 1.0], [0.0, 1.0, 1.0, 1.0], ];
let cases: [([f32; 3], usize); 6] = [
([1.0, 0.9, 0.9], 0), ([-1.0, 0.9, -0.9], 1), ([-0.9, 1.0, -0.9], 2), ([-0.9, -1.0, 0.9], 3), ([-0.9, 0.9, 1.0], 4), ([0.9, 0.9, -1.0], 5), ];
let tex: TextureCube<f32> = TextureCube::new(0, 0);
tex.init(4);
for (face, &color) in face_colors.iter().enumerate() {
tex.data.write().faces[face].set_pixel(0, 0, 0, color);
}
let sampler = Sampler::new(0, 0);
sampler.init();
for (dir, face) in cases {
let color = texture_sample(&tex, &sampler, vec3f(dir[0], dir[1], dir[2]));
let expected = face_colors[face];
assert!(
(color.x() - expected[0]).abs() < 0.001,
"face {face} (+{}/{}) expected r={:.3}, got {color:?} for dir {dir:?}",
if face % 2 == 0 { '+' } else { '-' },
['X', 'X', 'Y', 'Y', 'Z', 'Z'][face],
expected[0]
);
assert!(
(color.y() - expected[1]).abs() < 0.001,
"face {face} y mismatch"
);
assert!(
(color.z() - expected[2]).abs() < 0.001,
"face {face} z mismatch"
);
}
}
#[test]
fn test_texture_cube_sample_zero_direction() {
let tex: TextureCube<f32> = TextureCube::new(0, 0);
tex.init(4);
tex.data.write().faces[0].set_pixel(2, 2, 0, [0.5, 0.5, 0.5, 1.0]);
let sampler = Sampler::new(0, 0);
sampler.init();
let color = texture_sample(&tex, &sampler, vec3f(0.0, 0.0, 0.0));
assert!(
color.x().is_finite(),
"zero direction should not produce NaN"
);
assert!((color.x() - 0.5).abs() < 0.001);
}
#[test]
fn test_texture_cube_sample_level() {
let tex: TextureCube<f32> = TextureCube::new(0, 0);
tex.init(2);
tex.data.write().faces[0].set_pixel(0, 0, 0, [0.5, 0.5, 0.5, 1.0]);
let sampler = Sampler::new(0, 0);
sampler.init();
let color = texture_sample_level(&tex, &sampler, vec3f(1.0, 1.0, 1.0), 0.0f32);
assert!((color.x() - 0.5).abs() < 0.001);
}
#[test]
fn test_texture_cube_array_sample() {
let tex: TextureCubeArray<f32> = TextureCubeArray::new(0, 0);
tex.init(2, 2);
tex.data.write().cubes[0].faces[0].set_pixel(0, 0, 0, [1.0, 0.0, 0.0, 1.0]);
tex.data.write().cubes[1].faces[0].set_pixel(0, 0, 0, [0.0, 1.0, 0.0, 1.0]);
let sampler = Sampler::new(0, 0);
sampler.init();
let color = texture_sample_array(&tex, &sampler, vec3f(1.0, 1.0, 1.0), 0u32);
assert!((color.x() - 1.0).abs() < 0.001, "expected red on layer 0");
let color = texture_sample_array(&tex, &sampler, vec3f(1.0, 1.0, 1.0), 1u32);
assert!((color.y() - 1.0).abs() < 0.001, "expected green on layer 1");
}
#[test]
fn test_texture_cube_array_sample_level() {
let tex: TextureCubeArray<f32> = TextureCubeArray::new(0, 0);
tex.init(2, 2);
tex.data.write().cubes[0].faces[0].set_pixel(0, 0, 0, [0.3, 0.6, 0.9, 1.0]);
let sampler = Sampler::new(0, 0);
sampler.init();
let color = texture_sample_level_array(&tex, &sampler, vec3f(1.0, 1.0, 1.0), 0u32, 0.0f32);
assert!((color.x() - 0.3).abs() < 0.001);
assert!((color.y() - 0.6).abs() < 0.001);
assert!((color.z() - 0.9).abs() < 0.001);
}
#[test]
fn test_texture_cube_gather() {
let tex: TextureCube<f32> = TextureCube::new(0, 0);
tex.init(2);
tex.data.write().faces[0].set_pixel(0, 0, 0, [1.0, 0.0, 0.0, 1.0]); tex.data.write().faces[0].set_pixel(1, 0, 0, [0.0, 1.0, 0.0, 1.0]); tex.data.write().faces[0].set_pixel(0, 1, 0, [0.0, 0.0, 1.0, 1.0]); tex.data.write().faces[0].set_pixel(1, 1, 0, [1.0, 1.0, 0.0, 1.0]);
let sampler = Sampler::new(0, 0);
sampler.init();
let g = texture_gather(0u32, &tex, &sampler, vec3f(1.0, 0.0, 0.0));
assert!((g.x() - 0.0).abs() < 0.001, "expected blue's r");
assert!((g.y() - 1.0).abs() < 0.001, "expected yellow's r");
assert!((g.z() - 0.0).abs() < 0.001, "expected green's r");
assert!((g.w() - 1.0).abs() < 0.001, "expected red's r");
let g = texture_gather(1u32, &tex, &sampler, vec3f(1.0, 0.0, 0.0));
assert!((g.x() - 0.0).abs() < 0.001, "expected blue's g");
assert!((g.y() - 1.0).abs() < 0.001, "expected yellow's g");
assert!((g.z() - 1.0).abs() < 0.001, "expected green's g");
assert!((g.w() - 0.0).abs() < 0.001, "expected red's g");
let g = texture_gather(0u32, &tex, &sampler, vec3f(-1.0, 0.0, 0.0));
assert!((g.x() - 0.0).abs() < 0.001, "expected default -X texels");
assert!((g.y() - 0.0).abs() < 0.001, "expected default -X texels");
assert!((g.z() - 0.0).abs() < 0.001, "expected default -X texels");
assert!((g.w() - 0.0).abs() < 0.001, "expected default -X texels");
}
#[test]
fn test_texture_cube_gather_integer_sampled_types() {
let tex_i: TextureCube<i32> = TextureCube::new(0, 0);
tex_i.init(2);
tex_i.data.write().faces[0].set_pixel(0, 0, 0, [10, 0, 0, 100]);
tex_i.data.write().faces[0].set_pixel(1, 0, 0, [20, 0, 0, 100]);
tex_i.data.write().faces[0].set_pixel(0, 1, 0, [30, 0, 0, 100]);
tex_i.data.write().faces[0].set_pixel(1, 1, 0, [40, 0, 0, 100]);
let tex_u: TextureCube<u32> = TextureCube::new(0, 0);
tex_u.init(2);
tex_u.data.write().faces[0].set_pixel(0, 0, 0, [10, 0, 0, 100]);
tex_u.data.write().faces[0].set_pixel(1, 0, 0, [20, 0, 0, 100]);
tex_u.data.write().faces[0].set_pixel(0, 1, 0, [30, 0, 0, 100]);
tex_u.data.write().faces[0].set_pixel(1, 1, 0, [40, 0, 0, 100]);
let sampler = Sampler::new(0, 0);
sampler.init();
let g = texture_gather(0u32, &tex_i, &sampler, vec3f(1.0, 0.0, 0.0));
assert_eq!(g, vec4i(30, 40, 20, 10));
let g = texture_gather(0u32, &tex_u, &sampler, vec3f(1.0, 0.0, 0.0));
assert_eq!(g, vec4u(30, 40, 20, 10));
let g = texture_gather(3u32, &tex_i, &sampler, vec3f(1.0, 0.0, 0.0));
assert_eq!(g, vec4i(100, 100, 100, 100));
}
#[test]
fn test_texture_cube_gather_zero_direction() {
let tex: TextureCube<f32> = TextureCube::new(0, 0);
tex.init(2);
for y in 0..2 {
for x in 0..2 {
tex.data.write().faces[0].set_pixel(x, y, 0, [0.5, 0.0, 0.0, 1.0]);
}
}
let sampler = Sampler::new(0, 0);
sampler.init();
let g = texture_gather(0u32, &tex, &sampler, vec3f(0.0, 0.0, 0.0));
assert!(g.x().is_finite(), "zero direction should not produce NaN");
assert!((g.x() - 0.5).abs() < 0.001);
assert!((g.y() - 0.5).abs() < 0.001);
assert!((g.z() - 0.5).abs() < 0.001);
assert!((g.w() - 0.5).abs() < 0.001);
}
#[test]
fn test_texture_cube_array_gather() {
let tex: TextureCubeArray<f32> = TextureCubeArray::new(0, 0);
tex.init(2, 2);
for y in 0..2 {
for x in 0..2 {
tex.data.write().cubes[0].faces[0].set_pixel(x, y, 0, [0.5, 0.0, 0.0, 1.0]);
}
}
for y in 0..2 {
for x in 0..2 {
tex.data.write().cubes[1].faces[0].set_pixel(x, y, 0, [0.75, 0.0, 0.0, 1.0]);
}
}
let sampler = Sampler::new(0, 0);
sampler.init();
let g = texture_gather_array(0u32, &tex, &sampler, vec3f(1.0, 0.0, 0.0), 0u32);
assert!((g.x() - 0.5).abs() < 0.001, "expected layer 0");
assert!((g.w() - 0.5).abs() < 0.001, "expected layer 0");
let g = texture_gather_array(0u32, &tex, &sampler, vec3f(1.0, 0.0, 0.0), 1u32);
assert!((g.x() - 0.75).abs() < 0.001, "expected layer 1");
assert!((g.w() - 0.75).abs() < 0.001, "expected layer 1");
let g = texture_gather_array(0u32, &tex, &sampler, vec3f(1.0, 0.0, 0.0), 1i32);
assert!(
(g.x() - 0.75).abs() < 0.001,
"expected layer 1 via i32 index"
);
let g = texture_gather_array(0u32, &tex, &sampler, vec3f(1.0, 0.0, 0.0), -1i32);
assert!(
(g.x() - 0.5).abs() < 0.001,
"negative index clamps to layer 0"
);
let g = texture_gather_array(0u32, &tex, &sampler, vec3f(1.0, 0.0, 0.0), 99u32);
assert!(
(g.x() - 0.75).abs() < 0.001,
"out-of-range index clamps to last layer"
);
}
#[test]
fn test_texture_cube_array_gather_integer_sampled_types() {
let tex_i: TextureCubeArray<i32> = TextureCubeArray::new(0, 0);
tex_i.init(2, 1);
tex_i.data.write().cubes[0].faces[0].set_pixel(0, 0, 0, [7, 0, 0, 1]);
tex_i.data.write().cubes[0].faces[0].set_pixel(1, 0, 0, [8, 0, 0, 1]);
tex_i.data.write().cubes[0].faces[0].set_pixel(0, 1, 0, [9, 0, 0, 1]);
tex_i.data.write().cubes[0].faces[0].set_pixel(1, 1, 0, [11, 0, 0, 1]);
let tex_u: TextureCubeArray<u32> = TextureCubeArray::new(0, 0);
tex_u.init(2, 1);
tex_u.data.write().cubes[0].faces[0].set_pixel(0, 0, 0, [7, 0, 0, 1]);
tex_u.data.write().cubes[0].faces[0].set_pixel(1, 0, 0, [8, 0, 0, 1]);
tex_u.data.write().cubes[0].faces[0].set_pixel(0, 1, 0, [9, 0, 0, 1]);
tex_u.data.write().cubes[0].faces[0].set_pixel(1, 1, 0, [11, 0, 0, 1]);
let sampler = Sampler::new(0, 0);
sampler.init();
let g = texture_gather_array(0u32, &tex_i, &sampler, vec3f(1.0, 0.0, 0.0), 0u32);
assert_eq!(g, vec4i(9, 11, 8, 7));
let g = texture_gather_array(0i32, &tex_u, &sampler, vec3f(1.0, 0.0, 0.0), 0u32);
assert_eq!(g, vec4u(9, 11, 8, 7));
}
#[test]
fn test_texture_2d_gather_integer_sampled_types() {
let tex_i: Texture2D<i32> = Texture2D::new(0, 0);
tex_i.init(2, 2);
tex_i.set_pixel(0, 0, [10, 0, 0, 100]);
tex_i.set_pixel(1, 0, [20, 0, 0, 100]);
tex_i.set_pixel(0, 1, [30, 0, 0, 100]);
tex_i.set_pixel(1, 1, [40, 0, 0, 100]);
let tex_u: Texture2D<u32> = Texture2D::new(0, 0);
tex_u.init(2, 2);
tex_u.set_pixel(0, 0, [10, 0, 0, 100]);
tex_u.set_pixel(1, 0, [20, 0, 0, 100]);
tex_u.set_pixel(0, 1, [30, 0, 0, 100]);
tex_u.set_pixel(1, 1, [40, 0, 0, 100]);
let sampler = Sampler::new(0, 0);
sampler.init();
let g = texture_gather(0u32, &tex_i, &sampler, vec2f(0.5, 0.5));
assert_eq!(g, vec4i(30, 40, 20, 10));
let g = texture_gather(0u32, &tex_u, &sampler, vec2f(0.5, 0.5));
assert_eq!(g, vec4u(30, 40, 20, 10));
let g = texture_gather(3u32, &tex_i, &sampler, vec2f(0.5, 0.5));
assert_eq!(g, vec4i(100, 100, 100, 100));
let g = texture_gather(1u32, &tex_u, &sampler, vec2f(0.5, 0.5));
assert_eq!(g, vec4u(0, 0, 0, 0));
}
#[test]
fn test_texture_2d_gather_offset_integer_sampled_types() {
let tex_i: Texture2D<i32> = Texture2D::new(0, 0);
tex_i.init(4, 4);
tex_i.set_pixel(1, 0, [15, 0, 0, 100]);
tex_i.set_pixel(2, 0, [25, 0, 0, 100]);
tex_i.set_pixel(1, 1, [35, 0, 0, 100]);
tex_i.set_pixel(2, 1, [45, 0, 0, 100]);
let sampler = Sampler::new(0, 0);
sampler.init();
let g = texture_gather_offset(0u32, &tex_i, &sampler, vec2f(0.25, 0.25), vec2i(1, 0));
assert_eq!(g, vec4i(35, 45, 25, 15));
let g = texture_gather_offset(0u32, &tex_i, &sampler, vec2f(0.5, 0.25), vec2i(-1, 0));
assert_eq!(g, vec4i(0, 35, 15, 0));
let tex_u: Texture2D<u32> = Texture2D::new(0, 0);
tex_u.init(4, 4);
tex_u.set_pixel(1, 0, [15, 0, 0, 100]);
tex_u.set_pixel(2, 0, [25, 0, 0, 100]);
tex_u.set_pixel(1, 1, [35, 0, 0, 100]);
tex_u.set_pixel(2, 1, [45, 0, 0, 100]);
let g = texture_gather_offset(0u32, &tex_u, &sampler, vec2f(0.25, 0.25), vec2i(1, 0));
assert_eq!(g, vec4u(35, 45, 25, 15));
}
#[test]
fn test_texture_2d_array_gather() {
let tex: Texture2DArray<f32> = Texture2DArray::new(0, 0);
tex.init(2, 2, 2);
let values = [[0.1, 0.2, 0.3, 0.4], [0.5, 0.6, 0.7, 0.8]];
for (layer, vals) in values.iter().enumerate() {
for y in 0..2u32 {
for x in 0..2u32 {
tex.data.write().layers[layer].set_pixel(
x,
y,
0,
[vals[(x + 2 * y) as usize], 0.0, 0.0, 1.0],
);
}
}
}
let sampler = Sampler::new(0, 0);
sampler.init();
let g = texture_gather_array(0u32, &tex, &sampler, vec2f(0.5, 0.5), 0u32);
assert!((g.x() - 0.3).abs() < 0.001);
assert!((g.y() - 0.4).abs() < 0.001);
assert!((g.z() - 0.2).abs() < 0.001);
assert!((g.w() - 0.1).abs() < 0.001);
let g = texture_gather_array(0u32, &tex, &sampler, vec2f(0.5, 0.5), 1u32);
assert!((g.x() - 0.7).abs() < 0.001);
assert!((g.w() - 0.5).abs() < 0.001);
let g = texture_gather_array(0u32, &tex, &sampler, vec2f(0.5, 0.5), 1i32);
assert!(
(g.x() - 0.7).abs() < 0.001,
"expected layer 1 via i32 index"
);
let g = texture_gather_array(0u32, &tex, &sampler, vec2f(0.5, 0.5), -1i32);
assert!(
(g.x() - 0.3).abs() < 0.001,
"negative index clamps to layer 0"
);
let g = texture_gather_array(0u32, &tex, &sampler, vec2f(0.5, 0.5), 99u32);
assert!(
(g.x() - 0.7).abs() < 0.001,
"out-of-range index clamps to last layer"
);
}
#[test]
fn test_texture_2d_array_gather_integer_sampled_types() {
let tex_i: Texture2DArray<i32> = Texture2DArray::new(0, 0);
tex_i.init(2, 2, 1);
tex_i.data.write().layers[0].set_pixel(0, 0, 0, [10, 0, 0, 100]);
tex_i.data.write().layers[0].set_pixel(1, 0, 0, [20, 0, 0, 100]);
tex_i.data.write().layers[0].set_pixel(0, 1, 0, [30, 0, 0, 100]);
tex_i.data.write().layers[0].set_pixel(1, 1, 0, [40, 0, 0, 100]);
let tex_u: Texture2DArray<u32> = Texture2DArray::new(0, 0);
tex_u.init(2, 2, 1);
tex_u.data.write().layers[0].set_pixel(0, 0, 0, [10, 0, 0, 100]);
tex_u.data.write().layers[0].set_pixel(1, 0, 0, [20, 0, 0, 100]);
tex_u.data.write().layers[0].set_pixel(0, 1, 0, [30, 0, 0, 100]);
tex_u.data.write().layers[0].set_pixel(1, 1, 0, [40, 0, 0, 100]);
let sampler = Sampler::new(0, 0);
sampler.init();
let g = texture_gather_array(0u32, &tex_i, &sampler, vec2f(0.5, 0.5), 0u32);
assert_eq!(g, vec4i(30, 40, 20, 10));
let g = texture_gather_array(0u32, &tex_u, &sampler, vec2f(0.5, 0.5), 0u32);
assert_eq!(g, vec4u(30, 40, 20, 10));
}
#[test]
fn test_texture_2d_array_gather_offset() {
let tex: Texture2DArray<f32> = Texture2DArray::new(0, 0);
tex.init(4, 4, 2);
tex.data.write().layers[1].set_pixel(1, 0, 0, [0.15, 0.0, 0.0, 1.0]);
tex.data.write().layers[1].set_pixel(2, 0, 0, [0.25, 0.0, 0.0, 1.0]);
tex.data.write().layers[1].set_pixel(1, 1, 0, [0.35, 0.0, 0.0, 1.0]);
tex.data.write().layers[1].set_pixel(2, 1, 0, [0.45, 0.0, 0.0, 1.0]);
let sampler = Sampler::new(0, 0);
sampler.init();
let g =
texture_gather_array_offset(0u32, &tex, &sampler, vec2f(0.25, 0.25), 1u32, vec2i(1, 0));
assert!((g.x() - 0.35).abs() < 0.001);
assert!((g.y() - 0.45).abs() < 0.001);
assert!((g.z() - 0.25).abs() < 0.001);
assert!((g.w() - 0.15).abs() < 0.001);
let g =
texture_gather_array_offset(0u32, &tex, &sampler, vec2f(0.25, 0.25), 1i32, vec2i(1, 0));
assert!((g.x() - 0.35).abs() < 0.001);
let g = texture_gather_array_offset(
0u32,
&tex,
&sampler,
vec2f(0.25, 0.25),
-1i32,
vec2i(1, 0),
);
assert!(g.x().abs() < 0.001);
assert!(g.w().abs() < 0.001);
let g = texture_gather_array_offset(
0u32,
&tex,
&sampler,
vec2f(0.25, 0.25),
99u32,
vec2i(1, 0),
);
assert!((g.x() - 0.35).abs() < 0.001);
}
#[test]
fn test_texture_depth_2d_array_gather_forms() {
let tex: TextureDepth2DArray = TextureDepth2DArray::new(0, 0);
tex.init(4, 4, 2);
{
let mut guard = tex.data.write();
guard.layers[1].mips[0][0][1] = 0.15;
guard.layers[1].mips[0][0][2] = 0.25;
guard.layers[1].mips[0][1][1] = 0.35;
guard.layers[1].mips[0][1][2] = 0.45;
}
let sampler = Sampler::new(0, 0);
sampler.init();
let g = texture_gather_depth_array(&tex, &sampler, vec2f(0.5, 0.25), 1u32);
assert!((g.x() - 0.35).abs() < 0.001);
assert!((g.y() - 0.45).abs() < 0.001);
assert!((g.z() - 0.25).abs() < 0.001);
assert!((g.w() - 0.15).abs() < 0.001);
let g = texture_gather_depth_array(&tex, &sampler, vec2f(0.5, 0.25), 1i32);
assert!(
(g.x() - 0.35).abs() < 0.001,
"expected layer 1 via i32 index"
);
let g = texture_gather_depth_array(&tex, &sampler, vec2f(0.5, 0.25), -1i32);
assert!(g.x().abs() < 0.001, "negative index clamps to layer 0");
let g = texture_gather_depth_array(&tex, &sampler, vec2f(0.5, 0.25), 99u32);
assert!(
(g.x() - 0.35).abs() < 0.001,
"out-of-range index clamps to last layer"
);
let g =
texture_gather_depth_array_offset(&tex, &sampler, vec2f(0.25, 0.25), 1u32, vec2i(1, 0));
assert!((g.x() - 0.35).abs() < 0.001);
let g =
texture_gather_depth_array_offset(&tex, &sampler, vec2f(0.25, 0.25), 1i32, vec2i(1, 0));
assert!(
(g.x() - 0.35).abs() < 0.001,
"expected layer 1 via i32 index"
);
let cmp: SamplerComparison = SamplerComparison::new(0, 0);
cmp.set(SamplerComparisonState {
compare: CompareFunction::Less,
..Default::default()
});
let g = texture_gather_compare_array(&tex, &cmp, vec2f(0.5, 0.25), 1u32, 0.3);
assert_eq!(g, vec4f(1.0, 1.0, 0.0, 0.0));
let g = texture_gather_compare_array(&tex, &cmp, vec2f(0.5, 0.25), 1i32, 0.3);
assert_eq!(g, vec4f(1.0, 1.0, 0.0, 0.0));
let g = texture_gather_compare_array_offset(
&tex,
&cmp,
vec2f(0.25, 0.25),
1u32,
0.3,
vec2i(1, 0),
);
assert_eq!(g, vec4f(1.0, 1.0, 0.0, 0.0));
let g = texture_gather_compare_array_offset(
&tex,
&cmp,
vec2f(0.25, 0.25),
1i32,
0.3,
vec2i(1, 0),
);
assert_eq!(g, vec4f(1.0, 1.0, 0.0, 0.0));
}
#[test]
fn test_texture_depth_cube_gather_depth() {
let tex: TextureDepthCube = TextureDepthCube::new(0, 0);
tex.init(2);
tex.data.write().faces[0].mips[0][0][0] = 0.1;
tex.data.write().faces[0].mips[0][0][1] = 0.2;
tex.data.write().faces[0].mips[0][1][0] = 0.3;
tex.data.write().faces[0].mips[0][1][1] = 0.4;
tex.data.write().faces[2].mips[0][0][0] = 0.5;
tex.data.write().faces[2].mips[0][0][1] = 0.6;
tex.data.write().faces[2].mips[0][1][0] = 0.7;
tex.data.write().faces[2].mips[0][1][1] = 0.8;
let sampler = Sampler::new(0, 0);
sampler.init();
let g = texture_gather_depth(&tex, &sampler, vec3f(1.0, 0.0, 0.0));
assert!((g.x() - 0.3).abs() < 0.001);
assert!((g.y() - 0.4).abs() < 0.001);
assert!((g.z() - 0.2).abs() < 0.001);
assert!((g.w() - 0.1).abs() < 0.001);
let g = texture_gather_depth(&tex, &sampler, vec3f(0.0, 1.0, 0.0));
assert!((g.x() - 0.7).abs() < 0.001);
assert!((g.y() - 0.8).abs() < 0.001);
assert!((g.z() - 0.6).abs() < 0.001);
assert!((g.w() - 0.5).abs() < 0.001);
let g = texture_gather_depth(&tex, &sampler, vec3f(0.0, 0.0, -1.0));
assert!(g.x().abs() < 0.001);
assert!(g.w().abs() < 0.001);
}
#[test]
fn test_texture_depth_cube_array_gather_depth() {
let tex: TextureDepthCubeArray = TextureDepthCubeArray::new(0, 0);
tex.init(2, 2);
let values = [[0.1, 0.2, 0.3, 0.4], [0.5, 0.6, 0.7, 0.8]];
for (layer, vals) in values.iter().enumerate() {
let mut guard = tex.data.write();
guard.cubes[layer].faces[0].mips[0][0][0] = vals[0];
guard.cubes[layer].faces[0].mips[0][0][1] = vals[1];
guard.cubes[layer].faces[0].mips[0][1][0] = vals[2];
guard.cubes[layer].faces[0].mips[0][1][1] = vals[3];
}
let sampler = Sampler::new(0, 0);
sampler.init();
let g = texture_gather_depth_array(&tex, &sampler, vec3f(1.0, 0.0, 0.0), 0u32);
assert!((g.x() - 0.3).abs() < 0.001, "expected layer 0");
assert!((g.w() - 0.1).abs() < 0.001);
let g = texture_gather_depth_array(&tex, &sampler, vec3f(1.0, 0.0, 0.0), 1u32);
assert!((g.x() - 0.7).abs() < 0.001, "expected layer 1");
assert!((g.w() - 0.5).abs() < 0.001);
let g = texture_gather_depth_array(&tex, &sampler, vec3f(1.0, 0.0, 0.0), 99u32);
assert!(
(g.x() - 0.7).abs() < 0.001,
"out-of-range index clamps to last layer"
);
let g = texture_gather_depth_array(&tex, &sampler, vec3f(1.0, 0.0, 0.0), 1i32);
assert!(
(g.x() - 0.7).abs() < 0.001,
"expected layer 1 via i32 index"
);
let g = texture_gather_depth_array(&tex, &sampler, vec3f(1.0, 0.0, 0.0), -1i32);
assert!(
(g.x() - 0.3).abs() < 0.001,
"negative index clamps to layer 0"
);
}
#[test]
fn test_texture_depth_cube_gather_compare() {
let tex: TextureDepthCube = TextureDepthCube::new(0, 0);
tex.init(2);
tex.data.write().faces[0].mips[0][0][0] = 0.1;
tex.data.write().faces[0].mips[0][0][1] = 0.2;
tex.data.write().faces[0].mips[0][1][0] = 0.3;
tex.data.write().faces[0].mips[0][1][1] = 0.4;
let sampler: SamplerComparison = SamplerComparison::new(0, 0);
sampler.set(SamplerComparisonState {
compare: CompareFunction::Less,
..Default::default()
});
let g = texture_gather_compare(&tex, &sampler, vec3f(1.0, 0.0, 0.0), 0.25);
assert_eq!(g, vec4f(1.0, 1.0, 0.0, 0.0));
let g = texture_gather_compare(&tex, &sampler, vec3f(1.0, 0.0, 0.0), 0.05);
assert_eq!(g, vec4f(1.0, 1.0, 1.0, 1.0));
let g = texture_gather_compare(&tex, &sampler, vec3f(1.0, 0.0, 0.0), 0.9);
assert_eq!(g, vec4f(0.0, 0.0, 0.0, 0.0));
let g = texture_gather_compare(&tex, &sampler, vec3f(-1.0, 0.0, 0.0), 0.25);
assert_eq!(g, vec4f(0.0, 0.0, 0.0, 0.0));
}
#[test]
fn test_texture_depth_cube_array_gather_compare() {
let tex: TextureDepthCubeArray = TextureDepthCubeArray::new(0, 0);
tex.init(2, 2);
let values = [[0.1, 0.2, 0.3, 0.4], [0.5, 0.6, 0.7, 0.8]];
for (layer, vals) in values.iter().enumerate() {
let mut guard = tex.data.write();
guard.cubes[layer].faces[0].mips[0][0][0] = vals[0];
guard.cubes[layer].faces[0].mips[0][0][1] = vals[1];
guard.cubes[layer].faces[0].mips[0][1][0] = vals[2];
guard.cubes[layer].faces[0].mips[0][1][1] = vals[3];
}
let sampler: SamplerComparison = SamplerComparison::new(0, 0);
sampler.set(SamplerComparisonState {
compare: CompareFunction::LessEqual,
..Default::default()
});
let g = texture_gather_compare_array(&tex, &sampler, vec3f(1.0, 0.0, 0.0), 0u32, 0.3);
assert_eq!(g, vec4f(1.0, 1.0, 0.0, 0.0));
let g = texture_gather_compare_array(&tex, &sampler, vec3f(1.0, 0.0, 0.0), 1u32, 0.55);
assert_eq!(g, vec4f(1.0, 1.0, 1.0, 0.0));
let g = texture_gather_compare_array(&tex, &sampler, vec3f(1.0, 0.0, 0.0), 99u32, 0.55);
assert_eq!(g, vec4f(1.0, 1.0, 1.0, 0.0));
let g = texture_gather_compare_array(&tex, &sampler, vec3f(1.0, 0.0, 0.0), 1i32, 0.55);
assert_eq!(g, vec4f(1.0, 1.0, 1.0, 0.0));
}
}