• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lloydmeta / gol-rs / 6903528928

17 Nov 2023 11:43AM UTC coverage: 60.341% (-0.09%) from 60.435%
6903528928

push

github

lloydmeta
Use safe methods, which somehow speed up grid advancing by ~10%

Signed-off-by: lloydmeta <lloydmeta@gmail.com>

17 of 20 new or added lines in 2 files covered. (85.0%)

3 existing lines in 1 file now uncovered.

283 of 469 relevant lines covered (60.34%)

105049.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/rendering/mod.rs
1
use super::data::{Grid, GridIdx};
2
use gfx;
3
use gfx::traits::FactoryExt;
4
use gfx::Device;
5
use gfx::Factory;
6
use gfx_device_gl::{CommandBuffer, Device as GlDevice, Resources};
7
use gfx_window_glutin;
8
use glutin;
9
use glutin::dpi::LogicalSize;
10
use rayon::prelude::*;
11
use std::error::Error;
12
use std::sync::{Arc, Mutex};
13
use std::thread;
14
use std::time::{Duration, Instant};
15

16
const WINDOW_TITLE: &str = "Simple Life";
17

18
const QUAD_VERTICES: [Vertex; 4] = [
19
    Vertex {
20
        position: [-0.5, 0.5],
21
    },
22
    Vertex {
23
        position: [-0.5, -0.5],
24
    },
25
    Vertex {
26
        position: [0.5, -0.5],
27
    },
28
    Vertex {
29
        position: [0.5, 0.5],
30
    },
31
];
32

33
const QUAD_INDICES: [u16; 6] = [0, 1, 2, 2, 3, 0];
34

35
const CLEARING_COLOR: [f32; 4] = [0.1, 0.2, 0.3, 1.0];
36

37
const WHITE: [f32; 4] = [1., 1., 1., 1.];
38
const COLOURED: [f32; 4] = [0.2, 0.4, 0.5, 1.];
39

40
const SCALE_TOTAL: f32 = 2.0;
41
const INSTANCE_PORTION: f32 = 1.8;
42

43
pub type ColorFormat = gfx::format::Rgba8;
44
pub type DepthFormat = gfx::format::DepthStencil;
45

46
gfx_defines! {
×
47
    vertex Vertex {
×
48
        position: [f32; 2] = "a_Position",
×
49
    }
×
50

×
51
    vertex Instance {
×
52
        translate: [f32; 2] = "a_Translate",
×
53
        colour: [f32; 4] = "a_Color",
×
54
    }
×
55

×
56
    constant Locals {
×
57
        scale: [[f32;2];2] = "u_Scale",
×
58
    }
×
59

×
60
    pipeline pipe {
×
61
        vertex: gfx::VertexBuffer<Vertex> = (),
×
62
        instance: gfx::InstanceBuffer<Instance> = (),
×
63
        scale: gfx::Global<[[f32;2];2]> = "u_Scale",
×
64
        locals: gfx::ConstantBuffer<Locals> = "Locals",
×
65
        out: gfx::RenderTarget<ColorFormat> = "Target0",
×
66
    }
×
67
}
×
68

69
// Fills the provided instance buffer, but also returns a vector of instances for later
70
// manipulation, when we want to update the instances and update the buffer again.
71
fn fill_instances(instances: &mut [Instance], grid: &Grid, size: [[f32; 2]; 2]) -> Vec<Instance> {
×
72
    let width = grid.width();
×
73
    let height = grid.height();
×
74
    let cells = grid.cells();
×
75

×
76
    let size_x = size[0][0];
×
77
    let size_y = size[1][1];
×
78
    let scale_remaining = SCALE_TOTAL - INSTANCE_PORTION;
×
79
    let gap_x = scale_remaining / (width + 1) as f32;
×
80
    let gap_y = scale_remaining / (height + 1) as f32;
×
81
    let begin_x = -1. + gap_x + (size_x / 2.);
×
82
    let begin_y = -1. + gap_y + (size_y / 2.);
×
83

×
84
    let mut translate = [begin_x, begin_y];
×
85

×
86
    let mut v = Vec::with_capacity(grid.area());
×
87
    let mut index = 0;
×
88
    for row in cells {
×
89
        for cell in row {
×
90
            let colour = if cell.alive() { COLOURED } else { WHITE };
×
91
            let inst = Instance { translate, colour };
×
92
            v.push(inst);
×
93
            instances[index] = inst;
×
94
            translate[0] += size_x + gap_x;
×
95
            index += 1;
×
96
        }
97
        translate[1] += size_y + gap_y;
×
98
        translate[0] = begin_x;
×
99
    }
100
    v
×
101
}
×
102

103
pub struct App {
104
    grid: Arc<Mutex<Grid>>,
105
    updates_per_second: u16,
106
    window: glutin::WindowedContext,
107
    device: GlDevice,
108
    // main_depth: DepthStencilView<Resources, DepthFormat>,
109
    events_loop: glutin::EventsLoop,
110
    pso: gfx::PipelineState<Resources, pipe::Meta>,
111
    data: pipe::Data<Resources>,
112
    encoder: gfx::Encoder<Resources, CommandBuffer>,
113
    slice: gfx::Slice<Resources>,
114
    upload: gfx::handle::Buffer<Resources, Instance>,
115
    instances: Vec<Instance>,
116
    uploading: bool,
117
}
118

119
impl App {
120
    #[allow(clippy::missing_errors_doc)]
121
    pub fn new(
×
122
        grid: Grid,
×
123
        window_width: u32,
×
124
        window_height: u32,
×
125
        updates_per_second: u16,
×
126
    ) -> Result<Self, Box<dyn Error>> {
×
127
        let events_loop = glutin::EventsLoop::new();
×
128
        let window_size = LogicalSize::new(window_width.into(), window_height.into());
×
129
        let builder = glutin::WindowBuilder::new()
×
130
            .with_title(WINDOW_TITLE)
×
131
            .with_dimensions(window_size);
×
132
        let context = glutin::ContextBuilder::new().with_vsync(true);
×
133
        let (window, device, mut factory, main_color, _) =
×
134
            gfx_window_glutin::init::<ColorFormat, DepthFormat>(builder, context, &events_loop)?;
×
135
        let encoder = factory.create_command_buffer().into();
×
136

137
        let width: u32 = u32::try_from(grid.width())?;
×
138
        let height: u32 = u32::try_from(grid.height())?;
×
139
        let area = u32::try_from(grid.area())?;
×
140

141
        let size = [
×
142
            [INSTANCE_PORTION / width as f32, 0.],
×
143
            [0., INSTANCE_PORTION / height as f32],
×
144
        ];
×
145

146
        let upload = factory.create_upload_buffer(area as usize)?;
×
147
        let insts = {
×
148
            let mut writer = factory.write_mapping(&upload)?;
×
149
            fill_instances(&mut writer, &grid, size)
×
150
        };
151

152
        let instances = factory.create_buffer(
×
153
            area as usize,
×
154
            gfx::buffer::Role::Vertex,
×
155
            gfx::memory::Usage::Dynamic,
×
156
            gfx::memory::Bind::TRANSFER_DST,
×
157
        )?;
×
158

159
        let (quad_vertices, mut slice) =
×
160
            factory.create_vertex_buffer_with_slice(&QUAD_VERTICES, &QUAD_INDICES[..]);
×
161
        slice.instances = Some((area, 0));
×
162
        let locals = Locals { scale: size };
×
163

×
164
        Ok(Self {
×
165
            grid: Arc::new(Mutex::new(grid)),
×
166
            updates_per_second,
×
167
            window,
×
168
            device,
×
169
            events_loop,
×
170
            // main_depth: main_depth,
×
171
            pso: factory.create_pipeline_simple(
×
172
                include_bytes!("shaders/instancing.glslv"),
×
173
                include_bytes!("shaders/instancing.glslf"),
×
174
                pipe::new(),
×
175
            )?,
×
176
            encoder,
×
177
            data: pipe::Data {
×
178
                vertex: quad_vertices,
×
179
                instance: instances,
×
180
                scale: size,
×
181
                locals: factory.create_buffer_immutable(
×
182
                    &[locals],
×
183
                    gfx::buffer::Role::Constant,
×
184
                    gfx::memory::Bind::empty(),
×
185
                )?,
×
186
                out: main_color,
×
187
            },
×
188
            instances: insts,
×
189
            slice,
×
190
            upload,
×
191
            uploading: true,
192
        })
193
    }
×
194

195
    #[inline]
196
    fn render(&mut self) -> Result<(), Box<dyn Error>> {
×
197
        if self.uploading {
×
198
            self.encoder
×
199
                .copy_buffer(&self.upload, &self.data.instance, 0, 0, self.upload.len())?;
×
200
            self.uploading = false;
×
201
        } else {
202
            self.update_instances()?;
×
203
            self.encoder
×
204
                .update_buffer(&self.data.instance, &self.instances, 0)?;
×
205
        }
206
        self.encoder.clear(&self.data.out, CLEARING_COLOR);
×
207
        self.encoder.draw(&self.slice, &self.pso, &self.data);
×
208
        self.encoder.flush(&mut self.device);
×
209
        self.window.swap_buffers()?;
×
210
        self.device.cleanup();
×
211
        Ok(())
×
212
    }
×
213

214
    #[allow(clippy::significant_drop_tightening)]
215
    #[doc(hidden)]
216
    #[inline]
217
    pub fn update_instances(&mut self) -> Result<(), Box<dyn Error>> {
×
218
        let grid = self.grid.lock().map_err(|e| format!("{e}"))?;
×
219
        let op = |(idx, inst): (usize, &mut Instance)| {
×
220
            if let Some(cell) = grid.get_idx(&GridIdx(idx)) {
×
221
                let colour = if cell.alive() { COLOURED } else { WHITE };
×
222
                inst.colour = colour;
×
223
            }
×
224
        };
×
NEW
225
        if grid.area_requires_bool() {
×
226
            self.instances.par_iter_mut().enumerate().for_each(op);
×
227
        } else {
×
228
            for (idx, inst) in self.instances.iter_mut().enumerate() {
×
229
                op((idx, inst));
×
230
            }
×
231
        }
232
        Ok(())
×
233
    }
×
234

235
    #[allow(clippy::missing_errors_doc)]
236
    pub fn run(&mut self) -> Result<(), Box<dyn Error>> {
×
237
        // Do updates to the grid in another thread.
×
238
        {
×
239
            let grid = self.grid.clone();
×
240
            let updates_per_second = self.updates_per_second;
×
241
            thread::spawn(move || async_update_loop(&grid, updates_per_second));
×
242
        }
×
243

×
244
        let mut running = true;
×
245
        while running {
×
246
            // fetch events
247
            let currently_uploading = self.uploading;
×
248
            self.events_loop.poll_events(|polled_event| {
×
249
                if let glutin::Event::WindowEvent { event, .. } = polled_event {
×
250
                    match event {
×
251
                        glutin::WindowEvent::KeyboardInput {
252
                            input:
253
                                glutin::KeyboardInput {
254
                                    virtual_keycode: Some(glutin::VirtualKeyCode::Escape),
255
                                    ..
256
                                },
257
                            ..
258
                        }
259
                        | glutin::WindowEvent::CloseRequested => running = false,
×
260
                        glutin::WindowEvent::Resized(_) => running = currently_uploading,
×
261
                        _ => {}
×
262
                    }
263
                }
×
264
            });
×
265
            self.render()?;
×
266
        }
267
        Ok(())
×
268
    }
×
269
}
270

271
// Only used so we can use the ? macro...
272
fn async_update_loop(grid: &Arc<Mutex<Grid>>, updates_per_second: u16) -> Result<(), String> {
×
273
    let wait_duration = Duration::from_millis(1000 / u64::from(updates_per_second));
×
274
    let mut last_updated = Instant::now();
×
275
    loop {
×
276
        if last_updated.elapsed() > wait_duration {
×
277
            grid.lock().map_err(|e| format!("{e}"))?.advance();
×
278
            last_updated = Instant::now();
×
279
        }
×
280
    }
281
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc