use std::num::NonZeroU32;
use glyph_brush::{
Rectangle,
ab_glyph::{Rect, point},
};
use wgpu::util::DeviceExt;
use crate::{Matrix, cache::Cache};
#[derive(Debug)]
pub struct Pipeline {
inner: wgpu::RenderPipeline,
cache: Cache,
vertex_buffer: wgpu::Buffer,
vertex_buffer_len: usize,
vertices: u32,
}
impl Pipeline {
pub fn new(
device: &wgpu::Device,
render_format: wgpu::TextureFormat,
depth_stencil: Option<wgpu::DepthStencilState>,
multisample: wgpu::MultisampleState,
multiview_mask: Option<NonZeroU32>,
tex_dimensions: (u32, u32),
matrix: Matrix,
) -> Pipeline {
let cache = Cache::new(device, tex_dimensions, matrix);
let shader =
device.create_shader_module(wgpu::include_wgsl!("shader/shader.wgsl"));
let vertex_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("wgpu-text Vertex Buffer"),
size: 0,
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("wgpu-text Render Pipeline Layout"),
bind_group_layouts: &[Some(&cache.bind_group_layout)],
immediate_size: 0,
});
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("wgpu-text Render Pipeline"),
layout: Some(&pipeline_layout),
vertex: wgpu::VertexState {
module: &shader,
entry_point: Some("vs_main"),
buffers: &[Vertex::buffer_layout()],
compilation_options: Default::default(),
},
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleStrip,
strip_index_format: Some(wgpu::IndexFormat::Uint16),
..Default::default()
},
depth_stencil,
multisample,
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: Some("fs_main"),
targets: &[Some(wgpu::ColorTargetState {
format: render_format,
blend: Some(wgpu::BlendState::ALPHA_BLENDING),
write_mask: wgpu::ColorWrites::ALL,
})],
compilation_options: Default::default(),
}),
cache: None,
multiview_mask,
});
Self {
inner: pipeline,
cache,
vertex_buffer,
vertex_buffer_len: 0,
vertices: 0,
}
}
pub fn draw(&self, rpass: &mut wgpu::RenderPass) {
if self.vertices != 0 {
rpass.set_pipeline(&self.inner);
rpass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
rpass.set_bind_group(0, &self.cache.bind_group, &[]);
rpass.draw(0..4, 0..self.vertices);
}
}
pub fn update_vertex_buffer(
&mut self,
vertices: Vec<Vertex>,
device: &wgpu::Device,
queue: &wgpu::Queue,
) {
self.vertices = vertices.len() as u32;
let data: &[u8] = bytemuck::cast_slice(&vertices);
if vertices.len() > self.vertex_buffer_len {
self.vertex_buffer_len = vertices.len();
self.vertex_buffer =
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("wgpu-text Vertex Buffer"),
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
contents: data,
});
return;
}
queue.write_buffer(&self.vertex_buffer, 0, data);
}
#[inline]
pub fn update_matrix(&self, matrix: Matrix, queue: &wgpu::Queue) {
self.cache.update_matrix(matrix, queue);
}
#[inline]
pub fn update_texture(&self, size: Rectangle<u32>, data: &[u8], queue: &wgpu::Queue) {
self.cache.update_texture(size, data, queue);
}
#[inline]
pub fn resize_texture(&mut self, device: &wgpu::Device, tex_dimensions: (u32, u32)) {
self.cache.recreate_texture(device, tex_dimensions);
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
pub struct Vertex {
top_left: [f32; 3],
bottom_right: [f32; 2],
tex_top_left: [f32; 2],
tex_bottom_right: [f32; 2],
color: [f32; 4],
}
impl Vertex {
pub fn to_vertex(
glyph_brush::GlyphVertex {
mut tex_coords,
pixel_coords,
bounds,
extra,
}: glyph_brush::GlyphVertex,
) -> Vertex {
let mut rect = Rect {
min: point(pixel_coords.min.x, pixel_coords.min.y),
max: point(pixel_coords.max.x, pixel_coords.max.y),
};
if rect.max.x > bounds.max.x {
let old_width = rect.width();
rect.max.x = bounds.max.x;
tex_coords.max.x =
tex_coords.min.x + tex_coords.width() * rect.width() / old_width;
}
if rect.min.x < bounds.min.x {
let old_width = rect.width();
rect.min.x = bounds.min.x;
tex_coords.min.x =
tex_coords.max.x - tex_coords.width() * rect.width() / old_width;
}
if rect.max.y > bounds.max.y {
let old_height = rect.height();
rect.max.y = bounds.max.y;
tex_coords.max.y =
tex_coords.min.y + tex_coords.height() * rect.height() / old_height;
}
if rect.min.y < bounds.min.y {
let old_height = rect.height();
rect.min.y = bounds.min.y;
tex_coords.min.y =
tex_coords.max.y - tex_coords.height() * rect.height() / old_height;
}
Vertex {
top_left: [rect.min.x, rect.min.y, extra.z],
bottom_right: [rect.max.x, rect.max.y],
tex_top_left: [tex_coords.min.x, tex_coords.min.y],
tex_bottom_right: [tex_coords.max.x, tex_coords.max.y],
color: extra.color,
}
}
pub fn buffer_layout() -> wgpu::VertexBufferLayout<'static> {
wgpu::VertexBufferLayout {
array_stride: std::mem::size_of::<Self>() as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Instance,
attributes: &[
wgpu::VertexAttribute {
format: wgpu::VertexFormat::Float32x3,
offset: 0,
shader_location: 0,
},
wgpu::VertexAttribute {
format: wgpu::VertexFormat::Float32x2,
offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress,
shader_location: 1,
},
wgpu::VertexAttribute {
format: wgpu::VertexFormat::Float32x2,
offset: std::mem::size_of::<[f32; 5]>() as wgpu::BufferAddress,
shader_location: 2,
},
wgpu::VertexAttribute {
format: wgpu::VertexFormat::Float32x2,
offset: std::mem::size_of::<[f32; 7]>() as wgpu::BufferAddress,
shader_location: 3,
},
wgpu::VertexAttribute {
format: wgpu::VertexFormat::Float32x4,
offset: std::mem::size_of::<[f32; 9]>() as wgpu::BufferAddress,
shader_location: 4,
},
],
}
}
}