Struct Framebuffer

Source
#[non_exhaustive]
pub struct Framebuffer { pub buffer_size: LogicalSize<i32>, pub vp_size: PhysicalSize<i32>, pub did_draw: bool, pub inverted_y: bool, pub internal: FramebufferInternal, }
Expand description

The Framebuffer struct manages the framebuffer of a MGlFb window. Through this struct, you can update the size and content of the buffer. Framebuffers are usually obtained through MiniGlFb::glutin_breakout, but they’re also returned by init_framebuffer.

§Basic usage

Firstly, one of the most important things to do when managing a Framebuffer manually is to make sure that whenever the window is resized, the Framebuffer is the first to know. Usually, this is handled for you by MiniGlFb, but that isn’t the case when using the GlutinBreakout.

Whenever you receive a resize event for your window, make sure to call Framebuffer::resize_viewport with the new physical dimensions of your window. You can also figure out some logical dimensions and call Framebuffer::resize_buffer too.

Additionally, when managing multiple framebuffers at once, you should make sure to call GlutinBreakout::make_current when appropriate, before calling any Framebuffer methods. Forgetting to call make_current can cause OpenGL to get confused and draw to the wrong window, which is probably not what you want.

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§buffer_size: LogicalSize<i32>

The logical size of the buffer. When you update the buffer via update_buffer, it is expected to contain buffer_size.width * buffer_size.height pixels.

§vp_size: PhysicalSize<i32>

The physical size of the viewport. This should always be kept up to date with the size of the window, and there is no reason to set it otherwise unless you’re drawing multiple buffers to one window or something funky like that.

§did_draw: bool

This is set to true every time draw is called. (or, by extension, update_buffer)

It’s safe to set this to false afterwards, it’s just a flag to let you know if code you’re calling into has updated the buffer or not.

§inverted_y: bool

True if the origin should be the bottom left of the screen instead of the top left. For historical reasons, this is the default. This should only be configured by changing the Config passed to get_fancy.

§internal: FramebufferInternal

Contains internal OpenGL things.

Accessing fields directly is not the intended usage. If a feature is missing please open an issue. The fields are public, however, so that while you are waiting for a feature to be exposed, if you need something in a pinch you can dig in easily and make it happen.

The internal fields may change.

Implementations§

Source§

impl Framebuffer

Source

pub fn update_buffer<T>(&mut self, image_data: &[T])

Examples found in repository?
examples/multi_window.rs (line 90)
89    fn redraw(&mut self) {
90        self.breakout.fb.update_buffer(&self.buffer);
91        self.breakout.context.swap_buffers().unwrap();
92    }
More examples
Hide additional examples
examples/glutin_breakout.rs (line 45)
10fn main() {
11    let (event_loop, mut fb) = mini_gl_fb::gotta_go_fast("Hello world!", 800.0, 600.0);
12    let mut buffer = vec![[128u8, 0, 0, 255]; 800 * 600];
13    fb.update_buffer(&buffer);
14
15    let GlutinBreakout {
16        context,
17        mut fb,
18    } = fb.glutin_breakout();
19
20    let mut mouse_down = false;
21
22    event_loop.run(move |event, _, flow| {
23        match event {
24            Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => {
25                *flow = ControlFlow::Exit;
26            }
27            Event::WindowEvent { event: KeyboardInput { input, .. }, .. } => {
28                if let Some(k) = input.virtual_keycode {
29                    if k == VirtualKeyCode::Escape && input.state == ElementState::Pressed {
30                        *flow = ControlFlow::Exit;
31                    }
32                }
33            }
34            Event::WindowEvent { event: WindowEvent::Resized(size), .. } => {
35                context.resize(size);
36                context.window().request_redraw();
37            }
38            Event::WindowEvent { event: WindowEvent::CursorMoved { position, .. }, .. } => {
39                let (x, y) = position.to_logical::<f64>(context.window().scale_factor()).into();
40                println!("({}, {})", x, y);
41                let mouse_x = min(max(x, 0), 800 - 1);
42                let mouse_y = min(max(fb.buffer_size.height - y, 0), 600 - 1);
43                if mouse_down {
44                    buffer[(mouse_x + mouse_y * 800) as usize] = [64, 128, 255, 255];
45                    fb.update_buffer(&buffer);
46                    context.window().request_redraw();
47                }
48            }
49            Event::WindowEvent { event: WindowEvent::MouseInput { state, .. }, .. } => {
50                if state == ElementState::Pressed {
51                    mouse_down = true;
52                } else {
53                    mouse_down = false;
54                }
55            }
56            Event::RedrawRequested(_) => {
57                fb.redraw();
58                context.swap_buffers().unwrap();
59            }
60            _ => {}
61        }
62    });
63}
examples/game_of_life.rs (line 56)
18fn main() {
19    let mut event_loop = EventLoop::new();
20    let mut fb = mini_gl_fb::get_fancy(config! {
21        window_title: String::from("PSA: Conway wants you to appreciate group theory instead"),
22        window_size: LogicalSize::new(800.0, 800.0),
23        buffer_size: Some(LogicalSize::new(WIDTH as _, HEIGHT as _))
24    }, &event_loop);
25
26    fb.change_buffer_format::<u8>(BufferFormat::R);
27    fb.use_post_process_shader(POST_PROCESS);
28
29    let mut neighbors = vec![0; WIDTH * HEIGHT];
30    let mut cells = vec![false; WIDTH * HEIGHT];
31
32    cells[5 * WIDTH + 10] = true;
33    cells[5 * WIDTH + 11] = true;
34    cells[5 * WIDTH + 12] = true;
35
36    cells[50 * WIDTH + 50] = true;
37    cells[51 * WIDTH + 51] = true;
38    cells[52 * WIDTH + 49] = true;
39    cells[52 * WIDTH + 50] = true;
40    cells[52 * WIDTH + 51] = true;
41
42    // ID of the Wakeup which means we should update the board
43    let mut update_id: Option<u32> = None;
44
45    fb.glutin_handle_basic_input(&mut event_loop, |fb, input| {
46        // We're going to use wakeups to update the grid
47        input.wait = true;
48
49        if update_id.is_none() {
50            update_id = Some(input.schedule_wakeup(Instant::now() + Duration::from_millis(500)))
51        } else if let Some(mut wakeup) = input.wakeup {
52            if Some(wakeup.id) == update_id {
53                // Time to update our grid
54                calculate_neighbors(&mut cells, &mut neighbors);
55                make_some_babies(&mut cells, &mut neighbors);
56                fb.update_buffer(&cells);
57
58                // Reschedule another update
59                wakeup.when = Instant::now() + Duration::from_millis(
60                    if input.key_is_down(VirtualKeyCode::LShift) {
61                        TURBO_SPEED
62                    } else {
63                        NORMAL_SPEED
64                    }
65                );
66
67                input.reschedule_wakeup(wakeup);
68            }
69
70            // We will get called again after all wakeups are handled
71            return true;
72        }
73
74        if input.key_is_down(VirtualKeyCode::Escape) {
75            return false;
76        }
77
78        if input.mouse_is_down(MouseButton::Left) || input.mouse_is_down(MouseButton::Right) {
79            // Mouse was pressed
80            let (x, y) = input.mouse_pos;
81            let x = x.min(WIDTH as f64 - 0.0001).max(0.0).floor() as usize;
82            let y = y.min(HEIGHT as f64 - 0.0001).max(0.0).floor() as usize;
83            cells[y * WIDTH + x] = input.mouse_is_down(MouseButton::Left);
84            fb.update_buffer(&cells);
85            // Give the user extra time to make something pretty each time they click
86            if !input.key_is_down(VirtualKeyCode::LShift) {
87                input.adjust_wakeup(update_id.unwrap(), Wakeup::after_millis(2000));
88            }
89        }
90
91        // TODO support right shift. Probably by querying modifiers somehow. (modifiers support)
92        if input.key_pressed(VirtualKeyCode::LShift) {
93            // immediately update
94            input.adjust_wakeup(update_id.unwrap(), Wakeup::after_millis(0));
95        } else if input.key_released(VirtualKeyCode::LShift) {
96            // immediately stop updating
97            input.adjust_wakeup(update_id.unwrap(), Wakeup::after_millis(NORMAL_SPEED));
98        }
99
100        true
101    });
102}
Source

pub fn use_vertex_shader(&mut self, source: &str)

Source

pub fn use_fragment_shader(&mut self, source: &str)

Examples found in repository?
examples/custom_shaders.rs (line 118)
101fn main() {
102    let width = 800.0;
103    let height = 600.0;
104
105    let (mut event_loop, mut fb) = mini_gl_fb::gotta_go_fast("Hello shaders!", width, height);
106
107    let mut buffer = vec![[128u8, 0, 0, 255]; (width * height) as usize];
108    // let's write a red line into the buffer roughly along the diagonal (misses many pixels)
109    for i in 0..100 {
110        let j = i as f64 / 100.0;
111        let index = (width * j * (height + 1.0)).floor() as usize;
112        buffer[index] = [255, 0, 0, 255];
113    }
114
115    // Let's keep using the default vertex shader
116    // fb.internal.use_vertex_shader(...);
117    fb.internal.fb.use_geometry_shader(GEOMETRY_SOURCE);
118    fb.internal.fb.use_fragment_shader(FRAGMENT_SOURCE);
119
120    fb.update_buffer(&buffer);
121
122    fb.persist_and_redraw(&mut event_loop, true);
123}
Source

pub fn use_post_process_shader(&mut self, source: &str)

Source

pub fn use_geometry_shader(&mut self, source: &str)

Examples found in repository?
examples/custom_shaders.rs (line 117)
101fn main() {
102    let width = 800.0;
103    let height = 600.0;
104
105    let (mut event_loop, mut fb) = mini_gl_fb::gotta_go_fast("Hello shaders!", width, height);
106
107    let mut buffer = vec![[128u8, 0, 0, 255]; (width * height) as usize];
108    // let's write a red line into the buffer roughly along the diagonal (misses many pixels)
109    for i in 0..100 {
110        let j = i as f64 / 100.0;
111        let index = (width * j * (height + 1.0)).floor() as usize;
112        buffer[index] = [255, 0, 0, 255];
113    }
114
115    // Let's keep using the default vertex shader
116    // fb.internal.use_vertex_shader(...);
117    fb.internal.fb.use_geometry_shader(GEOMETRY_SOURCE);
118    fb.internal.fb.use_fragment_shader(FRAGMENT_SOURCE);
119
120    fb.update_buffer(&buffer);
121
122    fb.persist_and_redraw(&mut event_loop, true);
123}
Source

pub fn use_grayscale_shader(&mut self)

Source

pub fn change_buffer_format<T: ToGlType>(&mut self, format: BufferFormat)

Source

pub fn resize_buffer(&mut self, buffer_width: u32, buffer_height: u32)

Examples found in repository?
examples/multi_window.rs (line 118)
101    fn resize(&mut self, new_size: LogicalSize<u32>) {
102        let mut new_buffer = vec![self.bg; new_size.width as usize * new_size.height as usize];
103
104        if self.buffer_size.width > 0 {
105            // use rchunks for inverted y
106            for (old_line, new_line) in self.buffer.chunks_exact(self.buffer_size.width as usize)
107                .zip(new_buffer.chunks_exact_mut(new_size.width as usize)) {
108                if old_line.len() <= new_line.len() {
109                    new_line[0..old_line.len()].copy_from_slice(old_line)
110                } else {
111                    new_line.copy_from_slice(&old_line[0..new_line.len()])
112                }
113            }
114        }
115
116        self.buffer = new_buffer;
117        self.buffer_size = new_size;
118        self.breakout.fb.resize_buffer(new_size.width, new_size.height);
119    }
Source

pub fn resize_viewport(&mut self, width: u32, height: u32)

Examples found in repository?
examples/multi_window.rs (line 217)
180    fn handle_event(&mut self, event: &Event<()>) -> bool {
181        match *event {
182            Event::WindowEvent {
183                window_id: id,
184                event: WindowEvent::CloseRequested,
185                ..
186            } if self.matches_id(id) => {
187                return false;
188            }
189            Event::WindowEvent {
190                window_id: id,
191                event: WindowEvent::KeyboardInput {
192                    input: KeyboardInput {
193                        virtual_keycode: Some(VirtualKeyCode::Escape),
194                        state: ElementState::Pressed,
195                        ..
196                    },
197                    ..
198                },
199                ..
200            } if self.matches_id(id) => {
201                if let Some(_) = self.window().fullscreen() {
202                    self.window().set_fullscreen(None);
203                } else {
204                    return false;
205                }
206            }
207            Event::RedrawRequested(id) if self.matches_id(id) => {
208                unsafe { self.breakout.make_current().unwrap(); }
209                self.redraw();
210            }
211            Event::WindowEvent {
212                window_id: id,
213                event: WindowEvent::Resized(size),
214                ..
215            } if self.matches_id(id) => {
216                unsafe { self.breakout.make_current().unwrap(); }
217                self.breakout.fb.resize_viewport(size.width, size.height);
218                self.resize(size.to_logical(self.window().scale_factor() * SCALE_FACTOR));
219                self.request_redraw();
220            }
221            Event::WindowEvent {
222                window_id: id,
223                event: WindowEvent::MouseInput {
224                    button: MouseButton::Left,
225                    state,
226                    ..
227                },
228                ..
229            } if self.matches_id(id) => {
230                self.mouse_state = state;
231                self.line_start = None;
232            }
233            Event::WindowEvent {
234                window_id: id,
235                event: WindowEvent::CursorMoved {
236                    position,
237                    ..
238                },
239                ..
240            } if self.matches_id(id) => {
241                if self.mouse_state == ElementState::Pressed {
242                    let inner_size = self.window().inner_size();
243                    let position = LogicalPosition::new(
244                        ((position.x / inner_size.width as f64) * self.buffer_size.width as f64).floor(),
245                        ((position.y / inner_size.height as f64) * self.buffer_size.height as f64).floor()
246                    ).cast::<i32>();
247
248                    if let Some(line_start) = self.line_start {
249                        self.plot_line(line_start, position);
250                    } else {
251                        self.plot(position);
252                    }
253
254                    self.line_start = Some(position);
255
256                    self.request_redraw();
257                }
258            }
259            _ => {}
260        }
261
262        true
263    }
Source

pub fn redraw(&mut self)

Examples found in repository?
examples/glutin_breakout.rs (line 57)
10fn main() {
11    let (event_loop, mut fb) = mini_gl_fb::gotta_go_fast("Hello world!", 800.0, 600.0);
12    let mut buffer = vec![[128u8, 0, 0, 255]; 800 * 600];
13    fb.update_buffer(&buffer);
14
15    let GlutinBreakout {
16        context,
17        mut fb,
18    } = fb.glutin_breakout();
19
20    let mut mouse_down = false;
21
22    event_loop.run(move |event, _, flow| {
23        match event {
24            Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => {
25                *flow = ControlFlow::Exit;
26            }
27            Event::WindowEvent { event: KeyboardInput { input, .. }, .. } => {
28                if let Some(k) = input.virtual_keycode {
29                    if k == VirtualKeyCode::Escape && input.state == ElementState::Pressed {
30                        *flow = ControlFlow::Exit;
31                    }
32                }
33            }
34            Event::WindowEvent { event: WindowEvent::Resized(size), .. } => {
35                context.resize(size);
36                context.window().request_redraw();
37            }
38            Event::WindowEvent { event: WindowEvent::CursorMoved { position, .. }, .. } => {
39                let (x, y) = position.to_logical::<f64>(context.window().scale_factor()).into();
40                println!("({}, {})", x, y);
41                let mouse_x = min(max(x, 0), 800 - 1);
42                let mouse_y = min(max(fb.buffer_size.height - y, 0), 600 - 1);
43                if mouse_down {
44                    buffer[(mouse_x + mouse_y * 800) as usize] = [64, 128, 255, 255];
45                    fb.update_buffer(&buffer);
46                    context.window().request_redraw();
47                }
48            }
49            Event::WindowEvent { event: WindowEvent::MouseInput { state, .. }, .. } => {
50                if state == ElementState::Pressed {
51                    mouse_down = true;
52                } else {
53                    mouse_down = false;
54                }
55            }
56            Event::RedrawRequested(_) => {
57                fb.redraw();
58                context.swap_buffers().unwrap();
59            }
60            _ => {}
61        }
62    });
63}
Source

pub fn draw<F: FnOnce(&Framebuffer)>(&mut self, f: F)

Draw the quad to the active context. Optionally issue other commands after binding everything but before drawing it.

You probably want redraw (equivalent to .draw(|_| {})).

Trait Implementations§

Source§

impl Debug for Framebuffer

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.