tirbofish/dropbear
main / crates / kino-ui / src / rendering / batching.rs · 4397 bytes
crates/kino-ui/src/rendering/batching.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
use crate::rendering::vertex::Vertex;
use glam::Vec2;
use wgpu::{BufferUsages, IndexFormat};
/// Describes a primitive shape.
#[derive(Debug)]
pub struct VertexBatch {
vertices: Vec<Vertex>,
indices: Vec<u16>,
vertex_buffer: Option<wgpu::Buffer>,
index_buffer: Option<wgpu::Buffer>,
vertices_dirty: bool,
indices_dirty: bool,
}
impl Default for VertexBatch {
fn default() -> Self {
Self {
vertices: Vec::with_capacity(Self::MAX_VERTICES),
indices: Vec::with_capacity(Self::MAX_INDICES),
vertex_buffer: None,
index_buffer: None,
vertices_dirty: false,
indices_dirty: false,
}
}
}
impl VertexBatch {
const MAX_VERTICES: usize = u16::MAX as usize;
const MAX_INDICES: usize = Self::MAX_VERTICES * 6;
/// Returns true if adding verts/indices would exceed max allowed
fn would_overflow(&self, vert_count: usize, idx_count: usize) -> bool {
self.vertices.len() + vert_count > Self::MAX_VERTICES
|| self.indices.len() + idx_count > Self::MAX_INDICES
}
/// Adds vertices/indices, returns false if it would overflow
pub fn push(&mut self, verts: &[Vertex], indices: &[u16]) -> bool {
if self.would_overflow(verts.len(), indices.len()) {
return false;
}
let idx_offset = self.vertices.len() as u16;
self.vertices.extend_from_slice(verts);
self.indices.extend(indices.iter().map(|i| *i + idx_offset));
self.vertices_dirty = true;
self.indices_dirty = true;
true
}
pub fn upload(&mut self, device: &wgpu::Device, queue: &wgpu::Queue) {
if self.is_empty() || (!self.vertices_dirty && !self.indices_dirty) {
return;
}
if self.vertex_buffer.is_none() {
self.vertex_buffer = Some(device.create_buffer(&wgpu::BufferDescriptor {
label: Some("kino vertex buffer"),
size: (Self::MAX_VERTICES * std::mem::size_of::<Vertex>()) as u64,
usage: BufferUsages::VERTEX | BufferUsages::COPY_DST,
mapped_at_creation: false,
}));
}
if self.index_buffer.is_none() {
self.index_buffer = Some(device.create_buffer(&wgpu::BufferDescriptor {
label: Some("kino index buffer"),
size: (Self::MAX_INDICES * std::mem::size_of::<u16>()) as u64,
usage: BufferUsages::INDEX | BufferUsages::COPY_DST,
mapped_at_creation: false,
}));
}
if self.vertices_dirty {
queue.write_buffer(
self.vertex_buffer.as_ref().unwrap(),
0,
bytemuck::cast_slice(&self.vertices),
);
self.vertices_dirty = false;
}
if self.indices_dirty {
let mut indices_bytes: Vec<u8> = bytemuck::cast_slice(&self.indices).to_vec();
let remainder = indices_bytes.len() % wgpu::COPY_BUFFER_ALIGNMENT as usize;
if remainder != 0 {
let pad_len = wgpu::COPY_BUFFER_ALIGNMENT as usize - remainder;
indices_bytes.extend_from_slice(&vec![0u8; pad_len]);
}
queue.write_buffer(self.index_buffer.as_ref().unwrap(), 0, &indices_bytes);
self.indices_dirty = false;
}
}
pub fn is_empty(&self) -> bool {
self.vertices.is_empty() || self.indices.is_empty()
}
pub fn clear(&mut self) {
self.vertices.clear();
self.indices.clear();
self.vertices_dirty = true;
self.indices_dirty = true;
}
pub fn draw(&self, pass: &mut wgpu::RenderPass) {
if self.is_empty() {
return;
}
pass.set_vertex_buffer(0, self.vertex_buffer.as_ref().unwrap().slice(..));
pass.set_index_buffer(
self.index_buffer.as_ref().unwrap().slice(..),
IndexFormat::Uint16,
);
pass.draw_indexed(0..self.indices.len() as u32, 0, 0..1);
}
pub fn max_position(&self) -> Option<Vec2> {
self.vertices.iter().fold(None, |acc, vertex| {
let pos = Vec2::new(vertex.position[0], vertex.position[1]);
Some(match acc {
Some(current) => current.max(pos),
None => pos,
})
})
}
}