pixelpwnr_render/renderer/
mod.rs

1mod ref_values;
2pub mod stats_renderer;
3
4use std::sync::atomic::{AtomicBool, Ordering};
5use std::sync::Arc;
6use std::time::{Duration, Instant};
7
8use gfx::handle::ShaderResourceView;
9use gfx::texture::{AaMode, Kind, Mipmap};
10use gfx::traits::FactoryExt;
11use gfx_glutin::{ContextBuilderExt, WindowInitExt, WindowUpdateExt};
12use glutin::dpi::LogicalSize;
13use glutin::event::{Event, VirtualKeyCode, WindowEvent};
14use glutin::{ContextBuilder, GlProfile, GlRequest, Robustness};
15
16use gfx::{self, *};
17use glutin::event_loop::{ControlFlow, EventLoop};
18use glutin::window::{Fullscreen, WindowBuilder};
19use old_school_gfx_glutin_ext as gfx_glutin;
20
21use crate::fps_counter::FpsCounter;
22use crate::pixmap::Pixmap;
23use crate::primitive::create_quad_max;
24use crate::vertex::Vertex;
25use stats_renderer::{Corner, StatsRenderer};
26
27/// Define used types
28pub(crate) type ColorFormat = gfx::format::Rgba8;
29pub(crate) type DepthFormat = gfx::format::DepthStencil;
30type F = gfx_device_gl::Factory;
31pub(crate) type R = gfx_device_gl::Resources;
32
33/// Black color definition with 4 channels.
34const BLACK: [f32; 4] = [0.0, 0.0, 0.0, 1.0];
35
36// Screen shader data pipeline
37gfx_defines! {
38    pipeline pipe {
39        vbuf: gfx::VertexBuffer<Vertex> = (),
40        image: gfx::TextureSampler<[f32; 4]> = "t_Image",
41        out: gfx::RenderTarget<ColorFormat> = "Target0",
42    }
43}
44
45/// The renderer.
46pub struct Renderer<'a> {
47    // The window title.
48    title: &'a str,
49
50    // Pixel map holding the screen data.
51    pixmap: Arc<Pixmap>,
52
53    // Used to render statistics on the canvas.
54    stats: StatsRenderer<F>,
55
56    // Glutin events loop.
57    events_loop: EventLoop<()>,
58
59    // A FPS counter for the renderer.
60    #[allow(unused)]
61    fps: FpsCounter,
62}
63
64impl<'a> Renderer<'a> {
65    /// Construct a new renderer.
66    ///
67    /// The renderer window title should be given to `title`.
68    /// The pixel map that is rendered should be given to `pixmap`.
69    pub fn new(title: &'a str, pixmap: Arc<Pixmap>) -> Renderer<'a> {
70        // Construct and return the renderer
71        Renderer {
72            title,
73            pixmap,
74            stats: StatsRenderer::new(Corner::TopLeft),
75            events_loop: EventLoop::new(),
76            fps: FpsCounter::new(),
77        }
78    }
79
80    pub fn run(
81        mut self,
82        fullscreen: bool,
83        stats_size: u8,
84        stats_offset: (u32, u32),
85        stats_padding: i32,
86        stats_col_spacing: i32,
87        keep_running: Arc<AtomicBool>,
88    ) {
89        // Get the size of the canvas
90        let size = self.pixmap.dimensions();
91
92        // Select a monitor for full screening
93        // TODO: allow selecting a specific monitor
94        let monitor = if fullscreen {
95            Some(Fullscreen::Borderless(self.events_loop.primary_monitor()))
96        } else {
97            None
98        };
99
100        // Define a window builder
101        let builder = WindowBuilder::new()
102            .with_title(self.title.to_string())
103            .with_fullscreen(monitor)
104            .with_inner_size(LogicalSize {
105                width: size.0 as f64,
106                height: size.1 as f64,
107            });
108
109        // Define the graphics context
110        // TODO: properly configure this context
111        let (window, mut device, mut factory, mut main_color, mut main_depth) =
112            ContextBuilder::new()
113                .with_srgb(true)
114                .with_gl(GlRequest::Latest)
115                .with_gl_robustness(Robustness::TryRobustNoResetNotification)
116                .with_gl_profile(GlProfile::Core)
117                .with_multisampling(1)
118                .with_gfx_color_depth::<ColorFormat, DepthFormat>()
119                .with_vsync(true)
120                .build_windowed(builder, &self.events_loop)
121                .unwrap()
122                .init_gfx();
123
124        let my_window_id = window.window().id();
125
126        // command_buffer.set_ref_values(RefValues {
127        //     stencil: (0, 0),
128        //     blend: [0.5, 0.5, 0.5, 0.5],
129        // });
130
131        // Create the command encoder
132        let mut encoder: gfx::Encoder<_, _> = factory.create_command_buffer().into();
133
134        // Create a shader pipeline
135        let pso = factory
136            .create_pipeline_simple(
137                include_bytes!("../../shaders/screen.glslv"),
138                include_bytes!("../../shaders/screen.glslf"),
139                pipe::new(),
140            )
141            .unwrap();
142
143        // Create a full screen quad, plane, that is rendered on
144        let plane = create_quad_max();
145        let (vertex_buffer, slice) = plane.create_vertex_buffer(&mut factory);
146
147        // Define the texture kind
148        let texture_kind = Kind::D2(size.0 as u16, size.1 as u16, AaMode::Single);
149
150        // Create a base image
151        let base_image = (
152            Renderer::create_texture(&mut factory, self.pixmap.as_bytes(), texture_kind),
153            factory.create_sampler_linear(),
154        );
155
156        // Build pipe data
157        let mut data_depth = main_depth.clone();
158        let mut data = pipe::Data {
159            vbuf: vertex_buffer,
160            image: base_image,
161            out: main_color.clone(),
162        };
163
164        let dimensions = (size.0 as f32, size.1 as f32);
165        // Build the stats renderer
166        self.stats
167            .init(
168                factory.clone(),
169                dimensions,
170                main_color.clone(),
171                main_depth.clone(),
172                stats_size,
173                stats_offset,
174                stats_padding,
175                stats_col_spacing,
176            )
177            .expect("failed to initialize stats text renderer");
178
179        let mut next_frame_time = Instant::now();
180
181        self.events_loop.run(move |event, _target, control_flow| {
182            if !keep_running.load(Ordering::SeqCst) {
183                *control_flow = ControlFlow::Exit;
184                return;
185            }
186
187            let keycode = if let Event::WindowEvent { window_id, event } = &event {
188                if window_id == &my_window_id {
189                    if let WindowEvent::KeyboardInput { input, .. } = event {
190                        input.virtual_keycode
191                    } else {
192                        None
193                    }
194                } else {
195                    None
196                }
197            } else {
198                None
199            };
200
201            let is_close_request = if let Event::WindowEvent {
202                window_id,
203                event: WindowEvent::CloseRequested,
204            } = &event
205            {
206                window_id == &my_window_id
207            } else {
208                false
209            };
210
211            let exit = keycode == Some(VirtualKeyCode::Escape) || is_close_request;
212
213            if let Event::WindowEvent {
214                event: WindowEvent::Resized(s),
215                ..
216            } = event
217            {
218                let dimensions = (s.width as f32, s.height as f32);
219                // Update the main color and depth
220                window.update_gfx(&mut main_color, &mut main_depth);
221
222                // Update the pixel texture
223                window.update_gfx(&mut data.out, &mut data_depth);
224
225                // Update the stats text
226                self.stats.update_views(&window, dimensions);
227            }
228
229            // We don't want to re-render the whole frame each time someone moves their mouse, so let's
230            // put a time limit on it
231            if Instant::now() > next_frame_time || event == Event::MainEventsCleared {
232                data.image = (
233                    Renderer::create_texture(&mut factory, self.pixmap.as_bytes(), texture_kind),
234                    factory.create_sampler_linear(),
235                );
236
237                // Clear the buffer
238                encoder.clear(&data.out, BLACK);
239
240                // Draw through the pipeline
241                encoder.draw(&slice, &pso, &data);
242
243                // Draw the stats
244                self.stats.draw(&mut encoder, &main_color).unwrap();
245
246                encoder.flush(&mut device);
247
248                // Swap the frame buffers
249                window.swap_buffers().unwrap();
250
251                device.cleanup();
252                // Reserve at most 1 ms for processing input events
253                next_frame_time = Instant::now() + Duration::from_millis(1);
254            }
255
256            if exit || !keep_running.load(Ordering::SeqCst) {
257                keep_running.store(false, Ordering::SeqCst);
258            } else {
259                *control_flow = ControlFlow::WaitUntil(next_frame_time);
260            }
261        });
262    }
263
264    /// This will run forever, or until an escape character is input
265    pub fn run_default(self) {
266        self.run(false, 20, (10, 10), 12, 20, Arc::new(AtomicBool::new(true)));
267    }
268
269    pub fn stats(&self) -> &StatsRenderer<F> {
270        &self.stats
271    }
272
273    /// Load a texture from the given `path`.
274    fn create_texture(factory: &mut F, data: &[u8], kind: Kind) -> ShaderResourceView<R, [f32; 4]> {
275        // Create a GPU texture
276        // TODO: make sure the mipmap state is correct
277        let (_, view) = factory
278            .create_texture_immutable_u8::<ColorFormat>(kind, Mipmap::Provided, &[data])
279            .unwrap();
280
281        view
282    }
283}