1use crate::gfx::raster;
18#[cfg(not(target_arch = "wasm32"))]
19use rayon::prelude::*;
20
21#[cfg(not(target_arch = "wasm32"))]
27fn render_bands(width: usize, height: usize, est_pixels: usize) -> usize {
28 let screen = width * height;
29 if width == 0 || height < 256 || est_pixels < screen {
30 return 1;
31 }
32 let by_rows = height / 96; let by_fill = est_pixels / screen; rayon::current_num_threads().min(by_rows).min(by_fill.max(1) + 1).max(1)
35}
36
37#[cfg(not(target_arch = "wasm32"))]
38fn estimate_fill(calls: &[DrawCall]) -> usize {
39 let mut px = 0.0f32;
40 for c in calls {
41 let (a, b) = match c.kind {
42 DrawKind::Triangle { x0, y0, x1, y1, x2, y2, .. }
43 | DrawKind::TriangleG { x0, y0, x1, y1, x2, y2, .. } => {
44 let w = x0.max(x1).max(x2) - x0.min(x1).min(x2);
45 let h = y0.max(y1).max(y2) - y0.min(y1).min(y2);
46 (w, h)
47 },
48 DrawKind::Line { .. } => (0.0, 0.0),
49 };
50 px += 0.5 * a * b;
51 }
52 px.max(0.0) as usize
53}
54
55#[cfg(target_arch = "wasm32")]
56fn render_bands(_w: usize, _h: usize, _n: usize) -> usize {
57 1
58}
59
60#[inline]
64fn rasterize_call(
65 call: &DrawCall,
66 buf: &mut [u32],
67 zbuf: Option<&mut [f32]>,
68 width: usize,
69 height: usize,
70 ysh: f32,
71) {
72 let blended = call.mode != 0 || call.alpha < 0.999;
73 match zbuf {
74 Some(z) => match call.kind {
75 DrawKind::Triangle { x0, y0, z0, x1, y1, z1, x2, y2, z2 } => {
76 if blended {
77 raster::fill_triangle_z_blend(
78 buf, z, width, height, call.color, call.mode, call.alpha, x0, y0 - ysh, z0,
79 x1, y1 - ysh, z1, x2, y2 - ysh, z2,
80 );
81 } else {
82 raster::fill_triangle_z(
83 buf, z, width, height, call.color, x0, y0 - ysh, z0, x1, y1 - ysh, z1, x2,
84 y2 - ysh, z2,
85 );
86 }
87 },
88 DrawKind::TriangleG {
89 x0, y0, z0, c0, x1, y1, z1, c1, x2, y2, z2, c2, bands,
90 } => raster::fill_triangle_gouraud_z(
91 buf, z, width, height, x0, y0 - ysh, z0, c0, x1, y1 - ysh, z1, c1, x2, y2 - ysh, z2,
92 c2, bands, call.alpha, call.mode,
93 ),
94 DrawKind::Line { x0, y0, x1, y1, .. } => {
95 if blended {
96 raster::draw_line_blend(
97 buf, width, height, call.color, call.mode, call.alpha, x0, y0 - ysh, x1,
98 y1 - ysh,
99 );
100 } else {
101 raster::draw_line(buf, width, height, call.color, x0, y0 - ysh, x1, y1 - ysh);
102 }
103 },
104 },
105 None => match call.kind {
106 DrawKind::Triangle { x0, y0, x1, y1, x2, y2, .. } => {
107 if blended {
108 raster::fill_triangle_blend(
109 buf, width, height, call.color, call.mode, call.alpha, x0, y0 - ysh, x1,
110 y1 - ysh, x2, y2 - ysh,
111 );
112 } else {
113 raster::fill_triangle(
114 buf, width, height, call.color, x0, y0 - ysh, x1, y1 - ysh, x2, y2 - ysh,
115 );
116 }
117 },
118 DrawKind::TriangleG { x0, y0, c0, x1, y1, c1, x2, y2, c2, bands, .. } => {
119 raster::fill_triangle_gouraud(
120 buf, width, height, x0, y0 - ysh, c0, x1, y1 - ysh, c1, x2, y2 - ysh, c2, bands,
121 call.alpha, call.mode,
122 )
123 },
124 DrawKind::Line { x0, y0, x1, y1, .. } => {
125 if blended {
126 raster::draw_line_blend(
127 buf, width, height, call.color, call.mode, call.alpha, x0, y0 - ysh, x1,
128 y1 - ysh,
129 );
130 } else {
131 raster::draw_line(buf, width, height, call.color, x0, y0 - ysh, x1, y1 - ysh);
132 }
133 },
134 },
135 }
136}
137
138#[cfg(not(target_arch = "wasm32"))]
139struct FlushTimer(std::time::Instant);
140#[cfg(not(target_arch = "wasm32"))]
141impl Drop for FlushTimer {
142 fn drop(&mut self) {
143 crate::runtime::ling_phase_add(crate::runtime::phase::FLUSH, self.0.elapsed().as_nanos());
144 }
145}
146
147#[derive(Debug, Clone)]
149pub struct DrawCall {
150 pub depth: f32,
152 pub color: u32,
154 pub mode: u8,
156 pub alpha: f32,
158 pub kind: DrawKind,
159}
160
161#[derive(Debug, Clone)]
162pub enum DrawKind {
163 Triangle {
164 x0: f32,
165 y0: f32,
166 z0: f32,
167 x1: f32,
168 y1: f32,
169 z1: f32,
170 x2: f32,
171 y2: f32,
172 z2: f32,
173 },
174 TriangleG {
176 x0: f32,
177 y0: f32,
178 z0: f32,
179 c0: u32,
180 x1: f32,
181 y1: f32,
182 z1: f32,
183 c1: u32,
184 x2: f32,
185 y2: f32,
186 z2: f32,
187 c2: u32,
188 bands: u32,
189 },
190 Line {
191 x0: f32,
192 y0: f32,
193 z0: f32,
194 x1: f32,
195 y1: f32,
196 z1: f32,
197 },
198}
199
200#[derive(Debug)]
202pub struct DepthQueue {
203 calls: Vec<DrawCall>,
204 cur_mode: u8,
206 cur_alpha: f32,
208}
209
210impl Default for DepthQueue {
211 fn default() -> Self {
212 Self { calls: Vec::new(), cur_mode: 0, cur_alpha: 1.0 }
213 }
214}
215
216impl DepthQueue {
217 pub fn set_state(&mut self, mode: u8, alpha: f32) {
220 self.cur_mode = mode;
221 self.cur_alpha = alpha.clamp(0.0, 1.0);
222 }
223
224 pub fn push_triangle(
226 &mut self,
227 depth: f32,
228 color: u32,
229 x0: f32,
230 y0: f32,
231 x1: f32,
232 y1: f32,
233 x2: f32,
234 y2: f32,
235 ) {
236 self.calls.push(DrawCall {
237 depth,
238 color,
239 mode: self.cur_mode,
240 alpha: self.cur_alpha,
241 kind: DrawKind::Triangle { x0, y0, z0: depth, x1, y1, z1: depth, x2, y2, z2: depth },
242 });
243 }
244
245 #[allow(clippy::too_many_arguments)]
248 pub fn push_triangle_zv(
249 &mut self,
250 color: u32,
251 x0: f32,
252 y0: f32,
253 z0: f32,
254 x1: f32,
255 y1: f32,
256 z1: f32,
257 x2: f32,
258 y2: f32,
259 z2: f32,
260 ) {
261 let depth = (z0 + z1 + z2) / 3.0;
262 self.calls.push(DrawCall {
263 depth,
264 color,
265 mode: self.cur_mode,
266 alpha: self.cur_alpha,
267 kind: DrawKind::Triangle { x0, y0, z0, x1, y1, z1, x2, y2, z2 },
268 });
269 }
270
271 #[allow(clippy::too_many_arguments)]
273 pub fn push_triangle_g(
274 &mut self,
275 depth: f32,
276 x0: f32,
277 y0: f32,
278 c0: u32,
279 x1: f32,
280 y1: f32,
281 c1: u32,
282 x2: f32,
283 y2: f32,
284 c2: u32,
285 bands: u32,
286 ) {
287 self.calls.push(DrawCall {
288 depth,
289 color: c0,
290 mode: self.cur_mode,
291 alpha: self.cur_alpha,
292 kind: DrawKind::TriangleG {
293 x0,
294 y0,
295 z0: depth,
296 c0,
297 x1,
298 y1,
299 z1: depth,
300 c1,
301 x2,
302 y2,
303 z2: depth,
304 c2,
305 bands,
306 },
307 });
308 }
309
310 #[allow(clippy::too_many_arguments)]
312 pub fn push_triangle_g_zv(
313 &mut self,
314 x0: f32,
315 y0: f32,
316 z0: f32,
317 c0: u32,
318 x1: f32,
319 y1: f32,
320 z1: f32,
321 c1: u32,
322 x2: f32,
323 y2: f32,
324 z2: f32,
325 c2: u32,
326 bands: u32,
327 ) {
328 let depth = (z0 + z1 + z2) / 3.0;
329 self.calls.push(DrawCall {
330 depth,
331 color: c0,
332 mode: self.cur_mode,
333 alpha: self.cur_alpha,
334 kind: DrawKind::TriangleG { x0, y0, z0, c0, x1, y1, z1, c1, x2, y2, z2, c2, bands },
335 });
336 }
337
338 pub fn push_line(&mut self, depth: f32, color: u32, x0: f32, y0: f32, x1: f32, y1: f32) {
340 self.calls.push(DrawCall {
341 depth,
342 color,
343 mode: self.cur_mode,
344 alpha: self.cur_alpha,
345 kind: DrawKind::Line { x0, y0, z0: depth, x1, y1, z1: depth },
346 });
347 }
348
349 pub fn flush(
363 mut self,
364 buf: &mut Vec<u32>,
365 zbuf: Option<&mut Vec<f32>>,
366 reset_z: bool,
367 width: usize,
368 height: usize,
369 ) {
370 #[cfg(not(target_arch = "wasm32"))]
373 let _s = std::time::Instant::now();
374 self.calls.sort_unstable_by(|a, b| {
375 b.depth
376 .partial_cmp(&a.depth)
377 .unwrap_or(std::cmp::Ordering::Equal)
378 });
379 #[cfg(not(target_arch = "wasm32"))]
380 crate::runtime::ling_phase_add(crate::runtime::phase::SORT, _s.elapsed().as_nanos());
381 #[cfg(not(target_arch = "wasm32"))]
382 let _r = std::time::Instant::now();
383 #[cfg(not(target_arch = "wasm32"))]
384 let _guard = FlushTimer(_r);
385 let calls = &self.calls;
386 #[cfg(not(target_arch = "wasm32"))]
392 let bands = render_bands(width, height, estimate_fill(calls));
393 #[cfg(target_arch = "wasm32")]
394 let bands = render_bands(width, height, 0);
395 match zbuf {
396 Some(z) => {
397 if z.len() != width * height {
398 z.clear();
399 z.resize(width * height, f32::INFINITY);
400 } else if reset_z {
401 z.iter_mut().for_each(|v| *v = f32::INFINITY);
402 }
403 #[cfg(not(target_arch = "wasm32"))]
404 if bands > 1 {
405 let rows = height.div_ceil(bands);
406 buf.par_chunks_mut(rows * width)
407 .zip(z.par_chunks_mut(rows * width))
408 .enumerate()
409 .for_each(|(b, (bbuf, bz))| {
410 let ysh = (b * rows) as f32;
411 let bh = bbuf.len() / width;
412 for call in calls {
413 rasterize_call(call, bbuf, Some(bz), width, bh, ysh);
414 }
415 });
416 return;
417 }
418 let _ = bands;
419 for call in calls {
420 rasterize_call(call, buf, Some(z), width, height, 0.0);
421 }
422 },
423 None => {
424 #[cfg(not(target_arch = "wasm32"))]
425 if bands > 1 {
426 let rows = height.div_ceil(bands);
427 buf.par_chunks_mut(rows * width).enumerate().for_each(|(b, bbuf)| {
428 let ysh = (b * rows) as f32;
429 let bh = bbuf.len() / width;
430 for call in calls {
431 rasterize_call(call, bbuf, None, width, bh, ysh);
432 }
433 });
434 return;
435 }
436 let _ = bands;
437 for call in calls {
438 rasterize_call(call, buf, None, width, height, 0.0);
439 }
440 },
441 }
442 }
443
444 pub fn is_empty(&self) -> bool {
445 self.calls.is_empty()
446 }
447
448 #[cfg(target_arch = "wasm32")]
451 pub fn flush_to_webgl(
452 mut self,
453 fill_r: f32,
454 fill_g: f32,
455 fill_b: f32,
456 width: usize,
457 height: usize,
458 ) {
459 self.calls.sort_unstable_by(|a, b| {
461 b.depth
462 .partial_cmp(&a.depth)
463 .unwrap_or(std::cmp::Ordering::Equal)
464 });
465 for call in &self.calls {
466 match call.kind {
467 DrawKind::Triangle { x0, y0, x1, y1, x2, y2, .. } => {
468 crate::gfx::webgl::push_triangle(call.color, x0, y0, x1, y1, x2, y2, call.depth)
469 },
470 DrawKind::TriangleG { x0, y0, c0, x1, y1, c1, x2, y2, c2, .. } => {
471 let avg = {
473 let r = ((c0 >> 16 & 0xFF) + (c1 >> 16 & 0xFF) + (c2 >> 16 & 0xFF)) / 3;
474 let g = ((c0 >> 8 & 0xFF) + (c1 >> 8 & 0xFF) + (c2 >> 8 & 0xFF)) / 3;
475 let b = ((c0 & 0xFF) + (c1 & 0xFF) + (c2 & 0xFF)) / 3;
476 (r << 16) | (g << 8) | b
477 };
478 crate::gfx::webgl::push_triangle(avg, x0, y0, x1, y1, x2, y2, call.depth);
479 },
480 DrawKind::Line { x0, y0, x1, y1, .. } => {
481 crate::gfx::webgl::push_line(call.color, x0, y0, x1, y1, call.depth)
482 },
483 }
484 }
485 crate::gfx::webgl::flush(fill_r, fill_g, fill_b, width, height);
486 }
487
488 #[cfg(feature = "gpu")]
495 pub fn flush_to_wgpu(
496 mut self,
497 buf: &mut Vec<u32>,
498 width: usize,
499 height: usize,
500 clear: [f32; 3],
501 ) -> bool {
502 use crate::gfx::wgpu_raster::Vert;
503 self.calls.sort_unstable_by(|a, b| {
504 b.depth
505 .partial_cmp(&a.depth)
506 .unwrap_or(std::cmp::Ordering::Equal)
507 });
508 let to_rgb = |c: u32| {
509 [
510 ((c >> 16) & 0xFF) as f32 / 255.0,
511 ((c >> 8) & 0xFF) as f32 / 255.0,
512 (c & 0xFF) as f32 / 255.0,
513 ]
514 };
515 let mut verts: Vec<Vert> = Vec::with_capacity(self.calls.len() * 3);
516 for call in &self.calls {
517 match call.kind {
518 DrawKind::Triangle {
519 x0, y0, x1, y1, x2, y2, ..
520 } => {
521 let c = to_rgb(call.color);
522 verts.push(Vert { pos: [x0, y0], color: c });
523 verts.push(Vert { pos: [x1, y1], color: c });
524 verts.push(Vert { pos: [x2, y2], color: c });
525 }
526 DrawKind::TriangleG {
527 x0, y0, c0, x1, y1, c1, x2, y2, c2, ..
528 } => {
529 verts.push(Vert { pos: [x0, y0], color: to_rgb(c0) });
530 verts.push(Vert { pos: [x1, y1], color: to_rgb(c1) });
531 verts.push(Vert { pos: [x2, y2], color: to_rgb(c2) });
532 }
533 DrawKind::Line { .. } => { }
534 }
535 }
536 if buf.len() < width * height {
537 buf.resize(width * height, 0);
538 }
539 crate::gfx::wgpu_raster::raster(&verts, width, height, clear, buf)
540 }
541}