use std::collections::HashMap;
use twmap::{Envelope, Layer, Quad, TilesLayer, TwMap};
use vek::az::UnwrappedAs;
use vek::Rgba;
use wgpu::{
BindGroupLayoutEntry, BindingType, Device, Extent3d, Queue, ShaderStages,
TexelCopyBufferLayout, Texture, TextureDescriptor, TextureDimension, TextureFormat,
TextureSampleType, TextureUsages, TextureView, TextureViewDescriptor, TextureViewDimension,
};
use super::EnvelopeSampling;
const LABEL: Option<&str> = Some("Envelopes");
pub struct GpuEnvelopesData {
pub index: GpuEnvelopesIndex,
pub texture: Texture,
}
#[derive(Debug, Clone, Default)]
pub struct GpuEnvelopesIndex {
pub mapping: HashMap<(u16, i32), i32>,
pub ordered: Vec<(u16, i32)>,
}
pub const NEUTRAL_COLOR_INDEX: i32 = 0;
pub const NEUTRAL_POSITION_INDEX: i32 = 1;
impl GpuEnvelopesIndex {
pub fn register(&mut self, index: Option<u16>, offset: i32) {
let new_potential_env_index = self.size().unwrapped_as();
if let Some(identifier) = index.map(|n| (n, offset)) {
self.mapping.entry(identifier).or_insert_with(|| {
self.ordered.push(identifier);
new_potential_env_index
});
}
}
pub fn new(map: &TwMap) -> Self {
let mut index = GpuEnvelopesIndex::default();
for layer in map.groups.iter().flat_map(|g| &g.layers) {
match layer {
Layer::Tiles(l) => index.register(l.color_env, l.color_env_offset),
Layer::Quads(l) => {
for quad in &l.quads {
index.register(quad.color_env, quad.color_env_offset);
index.register(quad.position_env, quad.position_env_offset);
}
}
_ => {}
}
}
index
}
pub fn size(&self) -> usize {
self.ordered.len() + 2
}
}
impl GpuEnvelopesData {
pub fn layout_entry(binding: u32) -> BindGroupLayoutEntry {
BindGroupLayoutEntry {
binding,
visibility: ShaderStages::VERTEX,
ty: BindingType::Texture {
sample_type: TextureSampleType::Float { filterable: false },
view_dimension: TextureViewDimension::D1,
multisampled: false,
},
count: None,
}
}
pub fn view(&self) -> TextureView {
self.texture.create_view(&TextureViewDescriptor {
dimension: Some(TextureViewDimension::D1),
..TextureViewDescriptor::default()
})
}
pub fn quad_color_env_index(&self, quad: &Quad) -> i32 {
match quad.color_env {
Some(n) => *self.index.mapping.get(&(n, quad.color_env_offset)).unwrap(),
None => NEUTRAL_COLOR_INDEX,
}
}
pub fn quad_position_env_index(&self, quad: &Quad) -> i32 {
match quad.position_env {
Some(n) => *self
.index
.mapping
.get(&(n, quad.position_env_offset))
.unwrap(),
None => NEUTRAL_POSITION_INDEX,
}
}
pub fn tiles_color_env_index(&self, layer: &TilesLayer) -> i32 {
match layer.color_env {
Some(n) => *self
.index
.mapping
.get(&(n, layer.color_env_offset))
.unwrap(),
None => NEUTRAL_COLOR_INDEX,
}
}
pub fn upload(map: &TwMap, device: &Device) -> Self {
let index = GpuEnvelopesIndex::new(map);
let mut initial_data = Vec::with_capacity(index.size());
initial_data.extend([Rgba::white(), Rgba::zero()]);
for &(i, offset) in &index.ordered {
let value = map.envelopes[i.unwrapped_as::<usize>()].sample(0, 0, offset);
initial_data.push(value);
}
let texture = device.create_texture(&TextureDescriptor {
label: LABEL,
size: Extent3d {
width: index.size().unwrapped_as(),
height: 1,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D1,
format: TextureFormat::Rgba32Float,
usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
view_formats: &[],
});
Self { index, texture }
}
pub fn update(
&self,
envelopes: &[Envelope],
client_micros: i64,
server_micros: i64,
queue: &Queue,
) {
let mut data = Vec::with_capacity(self.index.size());
data.extend([Rgba::white(), Rgba::zero()]);
for &(i, offset) in &self.index.ordered {
let env = &envelopes[i.unwrapped_as::<usize>()];
let value = env.sample(client_micros, server_micros, offset);
data.push(value);
}
queue.write_texture(
self.texture.as_image_copy(),
bytemuck::cast_slice(&data),
TexelCopyBufferLayout {
offset: 0,
bytes_per_row: None,
rows_per_image: None,
},
Extent3d {
width: self.index.size().unwrapped_as(),
height: 1,
depth_or_array_layers: 1,
},
);
}
}