use super::index_array_helper::IndexBuffer;
use super::vertex_array_helper::FloatBuffer;
use crate::hal::{
scaler::{FontScaler, ScreenScaler},
Font, Shader, WgpuLink,
};
use crate::prelude::SparseTile;
use crate::BResult;
use bracket_color::prelude::RGBA;
use wgpu::{BufferUsages, RenderPipeline};
pub struct SparseConsoleBackend {
vao: FloatBuffer<f32>,
index: IndexBuffer,
render_pipeline: RenderPipeline,
previous_console: Option<Vec<SparseTile>>,
}
impl SparseConsoleBackend {
pub fn new(wgpu: &WgpuLink, shader: &Shader, font: &Font) -> SparseConsoleBackend {
let vao = SparseConsoleBackend::init_buffer_for_console(1000);
let index = IndexBuffer::new(1000);
let render_pipeline_layout =
wgpu.device
.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: None,
bind_group_layouts: &[font.bind_group_layout.as_ref().unwrap()],
push_constant_ranges: &[],
});
let render_pipeline = wgpu
.device
.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: None,
multiview: None,
layout: Some(&render_pipeline_layout),
vertex: wgpu::VertexState {
module: &shader.0,
entry_point: "vs_main",
buffers: &[vao.descriptor()],
},
fragment: Some(wgpu::FragmentState {
module: &shader.0,
entry_point: "fs_main",
targets: &[Some(wgpu::ColorTargetState {
format: wgpu.config.format,
blend: Some(wgpu::BlendState::ALPHA_BLENDING),
write_mask: wgpu::ColorWrites::ALL,
})],
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: None,
polygon_mode: wgpu::PolygonMode::Fill,
conservative: false,
unclipped_depth: false,
},
depth_stencil: None,
multisample: wgpu::MultisampleState {
count: 1,
mask: !0,
alpha_to_coverage_enabled: false,
},
});
SparseConsoleBackend {
vao,
index,
render_pipeline,
previous_console: None,
}
}
fn init_buffer_for_console(vertex_capacity: usize) -> FloatBuffer<f32> {
FloatBuffer::<f32>::new(
&[3, 4, 4, 2], vertex_capacity,
BufferUsages::VERTEX,
)
}
fn push_point(
vertex_buffer: &mut Vec<f32>,
x: f32,
y: f32,
fg: RGBA,
bg: RGBA,
ux: f32,
uy: f32,
) {
vertex_buffer.extend_from_slice(&[
x, y, 0.0, fg.r, fg.g, fg.b, fg.a, bg.r, bg.g, bg.b, bg.a, ux, uy,
]);
}
#[allow(clippy::too_many_arguments)]
pub fn rebuild_vertices(
&mut self,
wgpu: &WgpuLink,
height: u32,
width: u32,
offset_x: f32,
offset_y: f32,
scale: f32,
scale_center: (i32, i32),
tiles: &Vec<SparseTile>,
font_scaler: FontScaler,
screen_scaler: &ScreenScaler,
must_resize: bool,
) {
if !must_resize {
if let Some(old) = &self.previous_console {
if old.len() == tiles.len() {
let no_change = tiles.iter().zip(old.iter()).all(|(a, b)| *a == *b);
if no_change {
return;
}
}
}
}
self.vao.data.clear();
self.index.data.clear();
let (step_x, step_y, left_x, top_y) = {
let (step_x, step_y) = screen_scaler.calc_step(width, height, scale);
let (left_x, top_y) = screen_scaler.top_left_pixel();
(step_x, step_y, left_x, top_y)
};
let mut index_count: u16 = 0;
for t in tiles.iter() {
let x = t.idx % width as usize;
let y = t.idx / width as usize;
let screen_x = ((step_x * x as f32) + left_x) + offset_x;
let screen_y = ((step_y * y as f32) + top_y) + offset_y;
let fg = t.fg;
let bg = t.bg;
let glyph = t.glyph;
let gp = font_scaler.glyph_position(glyph);
SparseConsoleBackend::push_point(
&mut self.vao.data,
screen_x + step_x,
screen_y + step_y,
fg,
bg,
gp.glyph_right,
gp.glyph_top,
);
SparseConsoleBackend::push_point(
&mut self.vao.data,
screen_x + step_x,
screen_y,
fg,
bg,
gp.glyph_right,
gp.glyph_bottom,
);
SparseConsoleBackend::push_point(
&mut self.vao.data,
screen_x,
screen_y,
fg,
bg,
gp.glyph_left,
gp.glyph_bottom,
);
SparseConsoleBackend::push_point(
&mut self.vao.data,
screen_x,
screen_y + step_y,
fg,
bg,
gp.glyph_left,
gp.glyph_top,
);
self.index.data.push(index_count);
self.index.data.push(1 + index_count);
self.index.data.push(3 + index_count);
self.index.data.push(1 + index_count);
self.index.data.push(2 + index_count);
self.index.data.push(3 + index_count);
index_count += 4;
}
self.vao.build(wgpu);
self.index.build(wgpu);
self.previous_console = Some(tiles.clone());
}
pub fn wgpu_draw(&mut self, font: &Font) -> BResult<()> {
use crate::hal::BACKEND;
let mut be = BACKEND.lock();
if let Some(wgpu) = be.wgpu.as_mut() {
let mut encoder = wgpu
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"),
});
{
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Render Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: wgpu.backing_buffer.view(),
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: true,
},
})],
depth_stencil_attachment: None,
});
render_pass.set_pipeline(&self.render_pipeline);
render_pass.set_bind_group(0, font.bind_group.as_ref().unwrap(), &[]);
render_pass.set_vertex_buffer(0, self.vao.slice());
render_pass.set_index_buffer(self.index.slice(), wgpu::IndexFormat::Uint16);
render_pass.draw_indexed(0..self.index.len(), 0, 0..1);
}
wgpu.queue.submit(std::iter::once(encoder.finish()));
}
Ok(())
}
}