use std::collections::HashMap;
use bytemuck::{Pod, Zeroable};
use crate::backends::coverage::RenderBitmap;
pub(super) const UNIFORM_SIZE: u32 = core::mem::size_of::<QuadUniform>() as u32;
#[repr(C)]
#[derive(Clone, Copy, Pod, Zeroable)]
pub(super) struct QuadUniform {
rect: [f32; 4],
color: [f32; 4],
mode: [f32; 4],
}
impl QuadUniform {
pub(super) fn full_frame_rgba() -> Self {
Self {
rect: [-1.0, 1.0, 1.0, -1.0],
color: [0.0; 4],
mode: [1.0, 0.0, 0.0, 0.0],
}
}
pub(super) fn as_bytes(&self) -> &[u8] {
bytemuck::bytes_of(self)
}
}
pub(super) struct TileDesc<'a> {
pub(super) uniform: QuadUniform,
pub(super) format: wgpu::TextureFormat,
pub(super) width: u32,
pub(super) height: u32,
pub(super) data: &'a [u8],
pub(super) bytes_per_row: u32,
}
pub(super) fn describe<'a>(bmp: &'a RenderBitmap, frame_w: u32, frame_h: u32) -> TileDesc<'a> {
match bmp {
RenderBitmap::Coverage {
width,
height,
coverage,
x,
y,
color,
} => TileDesc {
uniform: QuadUniform {
rect: tile_rect(*x, *y, *width, *height, frame_w, frame_h),
color: [
f32::from(color[0]) / 255.0,
f32::from(color[1]) / 255.0,
f32::from(color[2]) / 255.0,
f32::from(color[3]) / 255.0,
],
mode: [0.0, 0.0, 0.0, 0.0],
},
format: wgpu::TextureFormat::R8Unorm,
width: *width,
height: *height,
data: coverage.as_slice(),
bytes_per_row: *width,
},
RenderBitmap::Rgba {
width,
height,
pixels,
x,
y,
} => TileDesc {
uniform: QuadUniform {
rect: tile_rect(*x, *y, *width, *height, frame_w, frame_h),
color: [0.0; 4],
mode: [1.0, 0.0, 0.0, 0.0],
},
format: wgpu::TextureFormat::Rgba8Unorm,
width: *width,
height: *height,
data: pixels.as_slice(),
bytes_per_row: *width * 4,
},
}
}
pub(super) struct PooledTile {
pub(super) bind_group: wgpu::BindGroup,
key: (wgpu::TextureFormat, u32, u32),
texture: wgpu::Texture,
}
impl PooledTile {
pub(super) fn upload(&self, queue: &wgpu::Queue, data: &[u8], bytes_per_row: u32) {
let (_, width, height) = self.key;
queue.write_texture(
wgpu::ImageCopyTexture {
texture: &self.texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
},
data,
wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: Some(bytes_per_row),
rows_per_image: Some(height),
},
wgpu::Extent3d {
width,
height,
depth_or_array_layers: 1,
},
);
}
}
pub(super) struct TilePool {
free: HashMap<(wgpu::TextureFormat, u32, u32), Vec<PooledTile>>,
}
impl TilePool {
pub(super) fn new() -> Self {
Self {
free: HashMap::new(),
}
}
pub(super) fn acquire(
&mut self,
device: &wgpu::Device,
layout: &wgpu::BindGroupLayout,
format: wgpu::TextureFormat,
width: u32,
height: u32,
) -> PooledTile {
let key = (format, width, height);
if let Some(tile) = self.free.get_mut(&key).and_then(Vec::pop) {
return tile;
}
create_tile(device, layout, key)
}
pub(super) fn release(&mut self, tiles: Vec<PooledTile>) {
for tile in tiles {
self.free.entry(tile.key).or_default().push(tile);
}
}
}
fn create_tile(
device: &wgpu::Device,
layout: &wgpu::BindGroupLayout,
key: (wgpu::TextureFormat, u32, u32),
) -> PooledTile {
let (format, width, height) = key;
let texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("ass-gpu-tile"),
size: wgpu::Extent3d {
width,
height,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format,
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
view_formats: &[],
});
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("ass-gpu-tile-bind"),
layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&view),
}],
});
PooledTile {
bind_group,
key,
texture,
}
}
fn tile_rect(x: i32, y: i32, width: u32, height: u32, frame_w: u32, frame_h: u32) -> [f32; 4] {
let frame_w = frame_w as f32;
let frame_h = frame_h as f32;
let left = x as f32;
let right = (x + width as i32) as f32;
let top = y as f32;
let bottom = (y + height as i32) as f32;
[
left / frame_w * 2.0 - 1.0,
1.0 - top / frame_h * 2.0,
right / frame_w * 2.0 - 1.0,
1.0 - bottom / frame_h * 2.0,
]
}