use {
crate::{
layer::Layer,
pipeline::Textures as Bindings,
render::State,
shader::{Shader, ShaderInfo},
shader_data::{data::TextureData, texture::Texture},
},
std::marker::PhantomData,
wgpu::{BindGroup, BindGroupLayout},
};
pub struct Textures<S> {
group: u32,
bind_group: BindGroup,
textures: Vec<Texture>,
ty: PhantomData<S>,
}
impl<S> Textures<S> {
fn new(params: Parameters, state: &State) -> Self {
use {
std::iter,
wgpu::{
AddressMode, BindGroupDescriptor, BindGroupEntry, BindingResource, FilterMode,
SamplerDescriptor,
},
};
let Parameters {
bindings,
variables,
layout,
} = params;
let textures: Vec<_> = variables
.maps
.into_iter()
.map(|map| match map {
Map::Data(data) => Texture::new(data, state),
Map::Texture(texture) => texture,
})
.collect();
let mut entries = Vec::with_capacity(textures.len() + 1);
for (texture, &binding) in iter::zip(&textures, &bindings.map.tmaps) {
entries.push(BindGroupEntry {
binding,
resource: BindingResource::TextureView(texture.view()),
});
}
let device = state.device();
let sampler = device.create_sampler(&SamplerDescriptor {
address_mode_u: AddressMode::ClampToEdge,
address_mode_v: AddressMode::ClampToEdge,
address_mode_w: AddressMode::ClampToEdge,
mag_filter: FilterMode::Nearest,
min_filter: FilterMode::Nearest,
..Default::default()
});
entries.push(BindGroupEntry {
binding: bindings.map.smap,
resource: BindingResource::Sampler(&sampler),
});
Self {
group: bindings.group,
bind_group: state.device().create_bind_group(&BindGroupDescriptor {
layout,
entries: &entries,
label: Some("texture bind group"),
}),
textures,
ty: PhantomData,
}
}
pub fn get_map(&self, index: usize) -> &Texture
where
S: Shader,
{
assert!(
index < self.textures.len(),
"the shader has no a texture map with index {index}",
);
&self.textures[index]
}
pub(crate) fn bind(&self) -> (u32, &BindGroup) {
(self.group, &self.bind_group)
}
}
struct Parameters<'a> {
variables: Variables<'a>,
bindings: &'a Bindings,
layout: &'a BindGroupLayout,
}
#[derive(Default)]
struct Variables<'a> {
maps: Vec<Map<'a>>,
}
#[must_use]
pub struct Builder<'a> {
state: &'a State,
variables: Variables<'a>,
}
impl<'a> Builder<'a> {
pub(crate) fn new(state: &'a State) -> Self {
Self {
state,
variables: Variables::default(),
}
}
pub fn with_map<M>(mut self, map: M) -> Self
where
M: Into<Map<'a>>,
{
self.variables.maps.push(map.into());
self
}
pub fn build<S, T>(self, layer: &Layer<S, T>) -> Textures<S>
where
S: Shader,
{
let actual = self.variables.maps.len();
let info = ShaderInfo::new::<S>();
let expected = info.texture_maps();
assert_eq!(
actual, expected,
"the shader requires {expected} texture maps, but {actual} is set",
);
let textures = layer
.pipeline()
.textures()
.expect("the shader has no textures");
let params = Parameters {
variables: self.variables,
bindings: &textures.bindings,
layout: &textures.layout,
};
Textures::new(params, self.state)
}
}
pub enum Map<'a> {
Data(TextureData<'a>),
Texture(Texture),
}
impl<'a> From<TextureData<'a>> for Map<'a> {
fn from(v: TextureData<'a>) -> Self {
Self::Data(v)
}
}
impl From<Texture> for Map<'_> {
fn from(v: Texture) -> Self {
Self::Texture(v)
}
}