pixelpwnr_render/renderer/
stats_renderer.rs1use draw_state::state::{Blend, BlendChannel, BlendValue, Equation, Factor};
2use gfx_glutin::WindowUpdateExt;
3use glutin::{PossiblyCurrent, WindowedContext};
4use parking_lot::Mutex;
5use std::cmp::max;
6use std::iter::Extend;
7use std::sync::Arc;
8
9use gfx::format::RenderFormat;
10use gfx::handle::{DepthStencilView, RenderTargetView};
11use gfx::traits::FactoryExt;
12use gfx::{self, *};
13
14use gfx_text::{Error as GfxTextError, HorizontalAnchor, Renderer as TextRenderer, VerticalAnchor};
15use old_school_gfx_glutin_ext as gfx_glutin;
16
17use super::ref_values::RefValuesWrapper;
18use crate::primitive::create_quad;
19use crate::renderer::{ColorFormat, DepthFormat, R};
20use crate::vertex::*;
21
22const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
24
25const BLEND: Blend = Blend {
26 color: BlendChannel {
27 equation: Equation::Add,
28 source: Factor::Zero,
29 destination: Factor::ZeroPlus(BlendValue::ConstAlpha),
30 },
31 alpha: BlendChannel {
32 equation: Equation::Add,
33 source: Factor::Zero,
34 destination: Factor::Zero,
35 },
36};
37
38gfx_defines! {
40 pipeline bg_pipe {
41 vbuf: gfx::VertexBuffer<Vertex> = (),
42 out: gfx::BlendTarget<ColorFormat> = (
43 "Target0",
44 gfx::state::ColorMask::all(),
45 BLEND,
46 ),
47 ref_values: RefValuesWrapper = RefValuesWrapper::new(),
48 }
49}
50
51pub struct StatsRenderer<F: Factory<R> + Clone> {
52 #[allow(unused)]
54 corner: Corner,
55
56 offset: (u32, u32),
58
59 padding: i32,
61
62 col_spacing: i32,
64
65 text: Arc<Mutex<String>>,
67
68 renderer: Option<TextRenderer<R, F>>,
70
71 factory: Option<F>,
73
74 window_dimensions: Option<(f32, f32)>,
76
77 bg_depth: Option<DepthStencilView<R, DepthFormat>>,
79
80 bg_pso: Option<PipelineState<R, bg_pipe::Meta>>,
82
83 bg_slice: Option<Slice<R>>,
85
86 bg_data: Option<bg_pipe::Data<R>>,
88}
89
90impl<F: Factory<R> + Clone> StatsRenderer<F> {
91 pub fn new(corner: Corner) -> Self {
93 StatsRenderer {
94 corner,
95 offset: (0, 0),
96 padding: 0,
97 col_spacing: 0,
98 text: Arc::new(Mutex::new(String::new())),
99 renderer: None,
100 factory: None,
101 window_dimensions: None,
102 bg_depth: None,
103 bg_pso: None,
104 bg_slice: None,
105 bg_data: None,
106 }
107 }
108
109 pub fn init(
111 &mut self,
112 mut factory: F,
113 window_dimensions: (f32, f32),
114 main_color: RenderTargetView<R, ColorFormat>,
115 main_depth: DepthStencilView<R, DepthFormat>,
116 font_size: u8,
117 offset: (u32, u32),
118 padding: i32,
119 col_spacing: i32,
120 ) -> Result<(), GfxTextError> {
121 self.window_dimensions = Some(window_dimensions);
123 self.offset = offset;
124 self.padding = padding;
125 self.col_spacing = col_spacing;
126
127 self.renderer = Some(
129 gfx_text::new(factory.clone())
130 .with_size(font_size)
131 .build()?,
132 );
133
134 self.bg_pso = Some(
136 factory
137 .create_pipeline_simple(
138 include_bytes!(concat!(
139 env!("CARGO_MANIFEST_DIR"),
140 "/shaders/stats_bg.glslv"
141 )),
142 include_bytes!(concat!(
143 env!("CARGO_MANIFEST_DIR"),
144 "/shaders/stats_bg.glslf"
145 )),
146 bg_pipe::new(),
147 )
148 .unwrap(),
149 );
150
151 let bg_plane = create_quad((-1f32, 0f32), (0.2f32, 0.95f32));
153 let (vertex_buffer, slice) = bg_plane.create_vertex_buffer(&mut factory);
154
155 self.bg_slice = Some(slice);
157 self.bg_data = Some(bg_pipe::Data {
158 vbuf: vertex_buffer,
159 out: main_color,
160 ref_values: (),
161 });
162
163 self.factory = Some(factory);
165 self.bg_depth = Some(main_depth);
166
167 Ok(())
168 }
169
170 pub fn text(&self) -> Arc<Mutex<String>> {
172 self.text.clone()
173 }
174
175 pub fn has_text(&self) -> bool {
177 self.text.lock().trim().is_empty()
178 }
179
180 pub fn set_text(&self, text: String) {
182 *self.text.lock() = text;
183 }
184
185 pub fn draw<C: CommandBuffer<R>, T: RenderFormat>(
190 &mut self,
191 encoder: &mut Encoder<R, C>,
192 target: &RenderTargetView<R, T>,
193 ) -> Result<(), GfxTextError> {
194 if self.renderer.is_none() || self.has_text() {
197 return Ok(());
198 }
199
200 let renderer = self.renderer.as_mut().unwrap();
202
203 let bounds = Self::scene_draw_format(
205 self.offset,
206 self.padding,
207 self.col_spacing,
208 renderer,
209 &self.text.lock(),
210 );
211
212 if bounds != (0f32, 0f32) {
214 if self.bg_slice.is_some() && self.bg_pso.is_some() && self.bg_data.is_some() {
215 let win = self.window_dimensions.unwrap();
217
218 let w = bounds.0 / win.0 * 2f32;
220 let h = bounds.1 / win.1 * 2f32;
221 let x = -1f32 + self.offset.0 as f32 / win.0 * 2f32;
222 let y = 1f32 - self.offset.1 as f32 / win.1 * 2f32 - h;
223
224 let (vertex_buffer, slice) = create_quad((x, y), (w, h))
226 .create_vertex_buffer(self.factory.as_mut().unwrap());
227
228 *self.bg_slice.as_mut().unwrap() = slice;
229 self.bg_data.as_mut().unwrap().vbuf = vertex_buffer;
230
231 encoder.draw(
232 self.bg_slice.as_ref().unwrap(),
233 self.bg_pso.as_ref().unwrap(),
234 self.bg_data.as_ref().unwrap(),
235 );
236 }
237 }
238
239 renderer.draw(encoder, target)
241 }
242
243 fn scene_draw_format(
249 pos: (u32, u32),
250 padding: i32,
251 col_spacing: i32,
252 renderer: &mut TextRenderer<R, F>,
253 text: &str,
254 ) -> (f32, f32) {
255 Self::scene_draw_table(
256 pos,
257 padding,
258 col_spacing,
259 renderer,
260 text.split("\n")
261 .map(|row| row.split("\t").collect())
262 .collect(),
263 )
264 }
265
266 fn scene_draw_table(
272 pos: (u32, u32),
273 padding: i32,
274 col_spacing: i32,
275 renderer: &mut TextRenderer<R, F>,
276 text: Vec<Vec<&str>>,
277 ) -> (f32, f32) {
278 let bounds: Vec<Vec<(i32, i32)>> = text
280 .iter()
281 .map(|col| col.iter().map(|text| renderer.measure(text)).collect())
282 .collect();
283
284 let rows_max: Vec<i32> = bounds
286 .iter()
287 .map(|col| col.iter().map(|size| size.1).max().unwrap_or(0))
288 .collect();
289
290 let mut cols_max: Vec<i32> = bounds
292 .iter()
293 .map(|row| row.iter().map(|size| size.0).collect())
294 .fold(Vec::new(), |acc: Vec<i32>, row: Vec<i32>| {
295 let mut out: Vec<i32> = acc
298 .iter()
299 .zip(row.iter())
300 .map(|(a, b)| max(*a, *b))
301 .collect();
302
303 let out_len = out.len();
305 if out_len < acc.len() || out_len < row.len() {
306 out.extend(acc.iter().skip(out_len));
307 out.extend(row.iter().skip(out_len));
308 }
309
310 out
311 });
312 cols_max
313 .iter_mut()
314 .rev()
315 .skip(1)
316 .map(|width| *width += col_spacing)
317 .count();
318
319 for (row, text) in text.iter().enumerate() {
321 for (col, text) in text.iter().enumerate() {
322 let (mut x, mut y): (i32, i32) = (
324 cols_max.iter().take(col).sum::<i32>(),
325 rows_max.iter().take(row).sum::<i32>(),
326 );
327
328 x += pos.0 as i32 + padding;
330 y += pos.1 as i32 + padding;
331
332 renderer.add_anchored(
334 text,
335 [x, y],
336 HorizontalAnchor::Left,
337 VerticalAnchor::Top,
338 WHITE,
339 );
340 }
341 }
342
343 (
345 cols_max.iter().sum::<i32>() as f32 + padding as f32 * 2f32,
346 rows_max.iter().sum::<i32>() as f32 + padding as f32 * 2f32,
347 )
348 }
349
350 pub fn update_views(
354 &mut self,
355 window: &WindowedContext<PossiblyCurrent>,
356 dimensions: (f32, f32),
357 ) {
358 if let Some(data) = self.bg_data.as_mut() {
360 window.update_gfx(&mut data.out, self.bg_depth.as_mut().unwrap());
361 }
362
363 self.window_dimensions = Some(dimensions);
365 }
366}
367
368pub enum Corner {
370 TopLeft,
372
373 TopRight,
375
376 BottomLeft,
378
379 BottomRight,
381}