1 | //! Graphics output protocol. |
2 | //! |
3 | //! The UEFI GOP is meant to replace existing [VGA][vga] hardware interfaces. |
4 | //! |
5 | //! The GOP provides access to a hardware frame buffer and allows UEFI apps |
6 | //! to draw directly to the graphics output device. |
7 | //! |
8 | //! The advantage of the GOP over legacy VGA is that it allows multiple GPUs |
9 | //! to exist and be used on the system. There is a GOP implementation for every |
10 | //! unique GPU in the system which supports UEFI. |
11 | //! |
12 | //! [vga]: https://en.wikipedia.org/wiki/Video_Graphics_Array |
13 | //! |
14 | //! # Definitions |
15 | //! |
16 | //! All graphics operations use a coordinate system where the top-left of the screen |
17 | //! is mapped to the point (0, 0), and `y` increases going down. |
18 | //! |
19 | //! Rectangles are defined by their top-left corner, and their width and height. |
20 | //! |
21 | //! The stride is understood as the length in bytes of a scan line / row of a buffer. |
22 | //! In theory, a buffer with a width of 640 should have (640 * 4) bytes per row, |
23 | //! but in practice there might be some extra padding used for efficiency. |
24 | //! |
25 | //! Frame buffers represent the graphics card's image buffers, backing the displays. |
26 | //! |
27 | //! Blits (**bl**ock **t**ransfer) can do high-speed memory copy between |
28 | //! the frame buffer and itself, or to and from some other buffers. |
29 | //! |
30 | //! # Blitting |
31 | //! |
32 | //! On certain hardware, the frame buffer is in a opaque format, |
33 | //! or cannot be accessed by the CPU. In those cases, it is not possible |
34 | //! to draw directly to the frame buffer. You must draw to another buffer |
35 | //! with a known pixel format, and then submit a blit command to copy that buffer |
36 | //! into the back buffer. |
37 | //! |
38 | //! Blitting can also copy a rectangle from the frame buffer to |
39 | //! another rectangle in the frame buffer, or move data out of the frame buffer |
40 | //! into a CPU-visible buffer. It can also do very fast color fills. |
41 | //! |
42 | //! The source and destination rectangles must always be of the same size: |
43 | //! no stretching / squashing will be done. |
44 | //! |
45 | //! # Animations |
46 | //! |
47 | //! UEFI does not mention if double buffering is used, nor how often |
48 | //! the frame buffer gets sent to the screen, but it's safe to assume that |
49 | //! the graphics card will re-draw the buffer at around the monitor's refresh rate. |
50 | //! You will have to implement your own double buffering if you want to |
51 | //! avoid tearing with animations. |
52 | |
53 | use crate::proto::unsafe_protocol ; |
54 | use crate::util::usize_from_u32; |
55 | use crate::{Result, Status}; |
56 | use core::marker::PhantomData; |
57 | use core::mem; |
58 | use core::ptr; |
59 | |
60 | /// Provides access to the video hardware's frame buffer. |
61 | /// |
62 | /// The GOP can be used to set the properties of the frame buffer, |
63 | /// and also allows the app to access the in-memory buffer. |
64 | #[repr (C)] |
65 | #[unsafe_protocol ("9042a9de-23dc-4a38-96fb-7aded080516a" )] |
66 | pub struct GraphicsOutput { |
67 | query_mode: extern "efiapi" fn( |
68 | &GraphicsOutput, |
69 | mode: u32, |
70 | info_sz: &mut usize, |
71 | &mut *const ModeInfo, |
72 | ) -> Status, |
73 | set_mode: extern "efiapi" fn(&mut GraphicsOutput, mode: u32) -> Status, |
74 | // Clippy correctly complains that this is too complicated, but we can't change the spec. |
75 | blt: unsafe extern "efiapi" fn( |
76 | this: &mut GraphicsOutput, |
77 | buffer: *mut BltPixel, |
78 | op: u32, |
79 | source_x: usize, |
80 | source_y: usize, |
81 | dest_x: usize, |
82 | dest_y: usize, |
83 | width: usize, |
84 | height: usize, |
85 | stride: usize, |
86 | ) -> Status, |
87 | mode: *const ModeData, |
88 | } |
89 | |
90 | impl GraphicsOutput { |
91 | /// Returns information for an available graphics mode that the graphics |
92 | /// device and the set of active video output devices supports. |
93 | pub fn query_mode(&self, index: u32) -> Result<Mode> { |
94 | let mut info_sz = 0; |
95 | let mut info = ptr::null(); |
96 | |
97 | (self.query_mode)(self, index, &mut info_sz, &mut info).into_with_val(|| { |
98 | let info = unsafe { *info }; |
99 | Mode { |
100 | index, |
101 | info_sz, |
102 | info, |
103 | } |
104 | }) |
105 | } |
106 | |
107 | /// Returns information about all available graphics modes. |
108 | #[must_use ] |
109 | pub fn modes(&self) -> ModeIter { |
110 | ModeIter { |
111 | gop: self, |
112 | current: 0, |
113 | max: self.mode().max_mode, |
114 | } |
115 | } |
116 | |
117 | /// Sets the video device into the specified mode, clearing visible portions |
118 | /// of the output display to black. |
119 | /// |
120 | /// This function will invalidate the current framebuffer. |
121 | pub fn set_mode(&mut self, mode: &Mode) -> Result { |
122 | (self.set_mode)(self, mode.index).into() |
123 | } |
124 | |
125 | /// Performs a blt (block transfer) operation on the frame buffer. |
126 | /// |
127 | /// Every operation requires different parameters. |
128 | pub fn blt(&mut self, op: BltOp) -> Result { |
129 | // Demultiplex the operation type. |
130 | unsafe { |
131 | match op { |
132 | BltOp::VideoFill { |
133 | color, |
134 | dest: (dest_x, dest_y), |
135 | dims: (width, height), |
136 | } => { |
137 | self.check_framebuffer_region((dest_x, dest_y), (width, height)); |
138 | (self.blt)( |
139 | self, |
140 | &color as *const _ as *mut _, |
141 | 0, |
142 | 0, |
143 | 0, |
144 | dest_x, |
145 | dest_y, |
146 | width, |
147 | height, |
148 | 0, |
149 | ) |
150 | .into() |
151 | } |
152 | BltOp::VideoToBltBuffer { |
153 | buffer, |
154 | src: (src_x, src_y), |
155 | dest: dest_region, |
156 | dims: (width, height), |
157 | } => { |
158 | self.check_framebuffer_region((src_x, src_y), (width, height)); |
159 | self.check_blt_buffer_region(dest_region, (width, height), buffer.len()); |
160 | match dest_region { |
161 | BltRegion::Full => (self.blt)( |
162 | self, |
163 | buffer.as_mut_ptr(), |
164 | 1, |
165 | src_x, |
166 | src_y, |
167 | 0, |
168 | 0, |
169 | width, |
170 | height, |
171 | 0, |
172 | ) |
173 | .into(), |
174 | BltRegion::SubRectangle { |
175 | coords: (dest_x, dest_y), |
176 | px_stride, |
177 | } => (self.blt)( |
178 | self, |
179 | buffer.as_mut_ptr(), |
180 | 1, |
181 | src_x, |
182 | src_y, |
183 | dest_x, |
184 | dest_y, |
185 | width, |
186 | height, |
187 | px_stride * core::mem::size_of::<BltPixel>(), |
188 | ) |
189 | .into(), |
190 | } |
191 | } |
192 | BltOp::BufferToVideo { |
193 | buffer, |
194 | src: src_region, |
195 | dest: (dest_x, dest_y), |
196 | dims: (width, height), |
197 | } => { |
198 | self.check_blt_buffer_region(src_region, (width, height), buffer.len()); |
199 | self.check_framebuffer_region((dest_x, dest_y), (width, height)); |
200 | match src_region { |
201 | BltRegion::Full => (self.blt)( |
202 | self, |
203 | buffer.as_ptr() as *mut _, |
204 | 2, |
205 | 0, |
206 | 0, |
207 | dest_x, |
208 | dest_y, |
209 | width, |
210 | height, |
211 | 0, |
212 | ) |
213 | .into(), |
214 | BltRegion::SubRectangle { |
215 | coords: (src_x, src_y), |
216 | px_stride, |
217 | } => (self.blt)( |
218 | self, |
219 | buffer.as_ptr() as *mut _, |
220 | 2, |
221 | src_x, |
222 | src_y, |
223 | dest_x, |
224 | dest_y, |
225 | width, |
226 | height, |
227 | px_stride * core::mem::size_of::<BltPixel>(), |
228 | ) |
229 | .into(), |
230 | } |
231 | } |
232 | BltOp::VideoToVideo { |
233 | src: (src_x, src_y), |
234 | dest: (dest_x, dest_y), |
235 | dims: (width, height), |
236 | } => { |
237 | self.check_framebuffer_region((src_x, src_y), (width, height)); |
238 | self.check_framebuffer_region((dest_x, dest_y), (width, height)); |
239 | (self.blt)( |
240 | self, |
241 | ptr::null_mut(), |
242 | 3, |
243 | src_x, |
244 | src_y, |
245 | dest_x, |
246 | dest_y, |
247 | width, |
248 | height, |
249 | 0, |
250 | ) |
251 | .into() |
252 | } |
253 | } |
254 | } |
255 | } |
256 | |
257 | /// Memory-safety check for accessing a region of the framebuffer |
258 | fn check_framebuffer_region(&self, coords: (usize, usize), dims: (usize, usize)) { |
259 | let (width, height) = self.current_mode_info().resolution(); |
260 | assert!( |
261 | coords.0.saturating_add(dims.0) <= width, |
262 | "Horizontal framebuffer coordinate out of bounds" |
263 | ); |
264 | assert!( |
265 | coords.1.saturating_add(dims.1) <= height, |
266 | "Vertical framebuffer coordinate out of bounds" |
267 | ); |
268 | } |
269 | |
270 | /// Memory-safety check for accessing a region of a user-provided buffer |
271 | fn check_blt_buffer_region(&self, region: BltRegion, dims: (usize, usize), buf_length: usize) { |
272 | match region { |
273 | BltRegion::Full => assert!( |
274 | dims.1.saturating_mul(dims.0) <= buf_length, |
275 | "BltBuffer access out of bounds" |
276 | ), |
277 | BltRegion::SubRectangle { |
278 | coords: (x, y), |
279 | px_stride, |
280 | } => { |
281 | assert!( |
282 | x.saturating_add(dims.0) <= px_stride, |
283 | "Horizontal BltBuffer coordinate out of bounds" |
284 | ); |
285 | assert!( |
286 | y.saturating_add(dims.1).saturating_mul(px_stride) <= buf_length, |
287 | "Vertical BltBuffer coordinate out of bounds" |
288 | ); |
289 | } |
290 | } |
291 | } |
292 | |
293 | /// Returns the frame buffer information for the current mode. |
294 | #[must_use ] |
295 | pub const fn current_mode_info(&self) -> ModeInfo { |
296 | *self.mode().info() |
297 | } |
298 | |
299 | /// Access the frame buffer directly |
300 | pub fn frame_buffer(&mut self) -> FrameBuffer { |
301 | assert!( |
302 | self.mode().info().format != PixelFormat::BltOnly, |
303 | "Cannot access the framebuffer in a Blt-only mode" |
304 | ); |
305 | let base = self.mode().fb_address as *mut u8; |
306 | let size = self.mode().fb_size; |
307 | |
308 | FrameBuffer { |
309 | base, |
310 | size, |
311 | _lifetime: PhantomData, |
312 | } |
313 | } |
314 | |
315 | const fn mode(&self) -> &ModeData { |
316 | unsafe { &*self.mode } |
317 | } |
318 | } |
319 | |
320 | #[repr (C)] |
321 | struct ModeData { |
322 | // Number of modes which the GOP supports. |
323 | max_mode: u32, |
324 | // Current mode. |
325 | mode: u32, |
326 | // Information about the current mode. |
327 | info: *const ModeInfo, |
328 | // Size of the above structure. |
329 | info_sz: usize, |
330 | // Physical address of the frame buffer. |
331 | fb_address: u64, |
332 | // Size in bytes. Equal to (pixel size) * height * stride. |
333 | fb_size: usize, |
334 | } |
335 | |
336 | impl ModeData { |
337 | const fn info(&self) -> &ModeInfo { |
338 | unsafe { &*self.info } |
339 | } |
340 | } |
341 | |
342 | /// Represents the format of the pixels in a frame buffer. |
343 | #[derive (Debug, Copy, Clone, Eq, PartialEq)] |
344 | #[repr (u32)] |
345 | pub enum PixelFormat { |
346 | /// Each pixel is 32-bit long, with 24-bit RGB, and the last byte is reserved. |
347 | Rgb = 0, |
348 | /// Each pixel is 32-bit long, with 24-bit BGR, and the last byte is reserved. |
349 | Bgr, |
350 | /// Custom pixel format, check the associated bitmask. |
351 | Bitmask, |
352 | /// The graphics mode does not support drawing directly to the frame buffer. |
353 | /// |
354 | /// This means you will have to use the `blt` function which will |
355 | /// convert the graphics data to the device's internal pixel format. |
356 | BltOnly, |
357 | // SAFETY: UEFI also defines a PixelFormatMax variant, and states that all |
358 | // valid enum values are guaranteed to be smaller. Since that is the |
359 | // case, adding a new enum variant would be a breaking change, so it |
360 | // is safe to model this C enum as a Rust enum. |
361 | } |
362 | |
363 | /// Bitmask used to indicate which bits of a pixel represent a given color. |
364 | #[derive (Debug, Copy, Clone, Eq, PartialEq)] |
365 | #[repr (C)] |
366 | pub struct PixelBitmask { |
367 | /// The bits indicating the red channel. |
368 | pub red: u32, |
369 | /// The bits indicating the green channel. |
370 | pub green: u32, |
371 | /// The bits indicating the blue channel. |
372 | pub blue: u32, |
373 | /// The reserved bits, which are ignored by the video hardware. |
374 | pub reserved: u32, |
375 | } |
376 | |
377 | /// Represents a graphics mode compatible with a given graphics device. |
378 | pub struct Mode { |
379 | index: u32, |
380 | info_sz: usize, |
381 | info: ModeInfo, |
382 | } |
383 | |
384 | impl Mode { |
385 | /// The size of the info structure in bytes. |
386 | /// |
387 | /// Newer versions of the spec might add extra information, in a backwards compatible way. |
388 | #[must_use ] |
389 | pub const fn info_size(&self) -> usize { |
390 | self.info_sz |
391 | } |
392 | |
393 | /// Returns a reference to the mode info structure. |
394 | #[must_use ] |
395 | pub const fn info(&self) -> &ModeInfo { |
396 | &self.info |
397 | } |
398 | } |
399 | |
400 | /// Information about a graphics output mode. |
401 | #[derive (Debug, Copy, Clone)] |
402 | #[repr (C)] |
403 | pub struct ModeInfo { |
404 | // The only known version, associated with the current spec, is 0. |
405 | version: u32, |
406 | hor_res: u32, |
407 | ver_res: u32, |
408 | format: PixelFormat, |
409 | mask: PixelBitmask, |
410 | stride: u32, |
411 | } |
412 | |
413 | impl ModeInfo { |
414 | /// Returns the (horizontal, vertical) resolution. |
415 | /// |
416 | /// On desktop monitors, this usually means (width, height). |
417 | #[must_use ] |
418 | pub const fn resolution(&self) -> (usize, usize) { |
419 | (usize_from_u32(self.hor_res), usize_from_u32(self.ver_res)) |
420 | } |
421 | |
422 | /// Returns the format of the frame buffer. |
423 | #[must_use ] |
424 | pub const fn pixel_format(&self) -> PixelFormat { |
425 | self.format |
426 | } |
427 | |
428 | /// Returns the bitmask of the custom pixel format, if available. |
429 | #[must_use ] |
430 | pub const fn pixel_bitmask(&self) -> Option<PixelBitmask> { |
431 | match self.format { |
432 | PixelFormat::Bitmask => Some(self.mask), |
433 | _ => None, |
434 | } |
435 | } |
436 | |
437 | /// Returns the number of pixels per scanline. |
438 | /// |
439 | /// Due to performance reasons, the stride might not be equal to the width, |
440 | /// instead the stride might be bigger for better alignment. |
441 | #[must_use ] |
442 | pub const fn stride(&self) -> usize { |
443 | usize_from_u32(self.stride) |
444 | } |
445 | } |
446 | |
447 | /// Iterator for [`Mode`]s of the [`GraphicsOutput`] protocol. |
448 | pub struct ModeIter<'gop> { |
449 | gop: &'gop GraphicsOutput, |
450 | current: u32, |
451 | max: u32, |
452 | } |
453 | |
454 | impl<'gop> Iterator for ModeIter<'gop> { |
455 | type Item = Mode; |
456 | |
457 | fn next(&mut self) -> Option<Self::Item> { |
458 | let index: u32 = self.current; |
459 | if index < self.max { |
460 | let m: Result = self.gop.query_mode(index); |
461 | self.current += 1; |
462 | |
463 | m.ok().or_else(|| self.next()) |
464 | } else { |
465 | None |
466 | } |
467 | } |
468 | |
469 | fn size_hint(&self) -> (usize, Option<usize>) { |
470 | let size: usize = (self.max - self.current) as usize; |
471 | (size, Some(size)) |
472 | } |
473 | } |
474 | |
475 | impl ExactSizeIterator for ModeIter<'_> {} |
476 | |
477 | /// Format of pixel data used for blitting. |
478 | /// |
479 | /// This is a BGR 24-bit format with an 8-bit padding, to keep each pixel 32-bit in size. |
480 | #[allow (missing_docs)] |
481 | #[derive (Debug, Copy, Clone)] |
482 | #[repr (C)] |
483 | pub struct BltPixel { |
484 | pub blue: u8, |
485 | pub green: u8, |
486 | pub red: u8, |
487 | _reserved: u8, |
488 | } |
489 | |
490 | impl BltPixel { |
491 | /// Create a new pixel from RGB values. |
492 | #[must_use ] |
493 | pub const fn new(red: u8, green: u8, blue: u8) -> Self { |
494 | Self { |
495 | red, |
496 | green, |
497 | blue, |
498 | _reserved: 0, |
499 | } |
500 | } |
501 | } |
502 | |
503 | impl From<u32> for BltPixel { |
504 | fn from(color: u32) -> Self { |
505 | Self { |
506 | blue: (color & 0x00_00_FF) as u8, |
507 | green: ((color & 0x00_FF_00) >> 8) as u8, |
508 | red: ((color & 0xFF_00_00) >> 16) as u8, |
509 | _reserved: 0, |
510 | } |
511 | } |
512 | } |
513 | |
514 | /// Region of the `BltBuffer` which we are operating on |
515 | /// |
516 | /// Some `Blt` operations can operate on either the full `BltBuffer` or a |
517 | /// sub-rectangle of it, but require the stride to be known in the latter case. |
518 | #[derive (Clone, Copy, Debug)] |
519 | pub enum BltRegion { |
520 | /// Operate on the full `BltBuffer` |
521 | Full, |
522 | |
523 | /// Operate on a sub-rectangle of the `BltBuffer` |
524 | SubRectangle { |
525 | /// Coordinate of the rectangle in the `BltBuffer` |
526 | coords: (usize, usize), |
527 | |
528 | /// Stride (length of each row of the `BltBuffer`) in **pixels** |
529 | px_stride: usize, |
530 | }, |
531 | } |
532 | |
533 | /// Blit operation to perform. |
534 | #[derive (Debug)] |
535 | pub enum BltOp<'buf> { |
536 | /// Fills a rectangle of video display with a pixel color. |
537 | VideoFill { |
538 | /// The color to fill with. |
539 | color: BltPixel, |
540 | /// The X / Y coordinates of the destination rectangle. |
541 | dest: (usize, usize), |
542 | /// The width / height of the rectangle. |
543 | dims: (usize, usize), |
544 | }, |
545 | /// Reads data from the video display to the buffer. |
546 | VideoToBltBuffer { |
547 | /// Buffer into which to copy data. |
548 | buffer: &'buf mut [BltPixel], |
549 | /// Coordinates of the source rectangle, in the frame buffer. |
550 | src: (usize, usize), |
551 | /// Location of the destination rectangle in the user-provided buffer |
552 | dest: BltRegion, |
553 | /// Width / height of the rectangles. |
554 | dims: (usize, usize), |
555 | }, |
556 | /// Write data from the buffer to the video rectangle. |
557 | /// Delta must be the stride (count of bytes in a row) of the buffer. |
558 | BufferToVideo { |
559 | /// Buffer from which to copy data. |
560 | buffer: &'buf [BltPixel], |
561 | /// Location of the source rectangle in the user-provided buffer. |
562 | src: BltRegion, |
563 | /// Coordinates of the destination rectangle, in the frame buffer. |
564 | dest: (usize, usize), |
565 | /// Width / height of the rectangles. |
566 | dims: (usize, usize), |
567 | }, |
568 | /// Copy from the source rectangle in video memory to |
569 | /// the destination rectangle, also in video memory. |
570 | VideoToVideo { |
571 | /// Coordinates of the source rectangle, in the frame buffer. |
572 | src: (usize, usize), |
573 | /// Coordinates of the destination rectangle, also in the frame buffer. |
574 | dest: (usize, usize), |
575 | /// Width / height of the rectangles. |
576 | dims: (usize, usize), |
577 | }, |
578 | } |
579 | |
580 | /// Direct access to a memory-mapped frame buffer |
581 | pub struct FrameBuffer<'gop> { |
582 | base: *mut u8, |
583 | size: usize, |
584 | _lifetime: PhantomData<&'gop mut u8>, |
585 | } |
586 | |
587 | impl<'gop> FrameBuffer<'gop> { |
588 | /// Access the raw framebuffer pointer |
589 | /// |
590 | /// To use this pointer safely and correctly, you must... |
591 | /// - Honor the pixel format and stride specified by the mode info |
592 | /// - Keep memory accesses in bound |
593 | /// - Use volatile reads and writes |
594 | /// - Make sure that the pointer does not outlive the FrameBuffer |
595 | /// |
596 | /// On some implementations this framebuffer pointer can be used after |
597 | /// exiting boot services, but that is not guaranteed by the UEFI Specification. |
598 | pub fn as_mut_ptr(&mut self) -> *mut u8 { |
599 | self.base |
600 | } |
601 | |
602 | /// Query the framebuffer size in bytes |
603 | #[must_use ] |
604 | pub const fn size(&self) -> usize { |
605 | self.size |
606 | } |
607 | |
608 | /// Modify the i-th byte of the frame buffer |
609 | /// |
610 | /// # Safety |
611 | /// |
612 | /// This operation is unsafe because... |
613 | /// - You must honor the pixel format and stride specified by the mode info |
614 | /// - There is no bound checking on memory accesses in release mode |
615 | #[inline ] |
616 | pub unsafe fn write_byte(&mut self, index: usize, value: u8) { |
617 | debug_assert!(index < self.size, "Frame buffer accessed out of bounds" ); |
618 | self.base.add(index).write_volatile(value) |
619 | } |
620 | |
621 | /// Read the i-th byte of the frame buffer |
622 | /// |
623 | /// # Safety |
624 | /// |
625 | /// This operation is unsafe because... |
626 | /// - You must honor the pixel format and stride specified by the mode info |
627 | /// - There is no bound checking on memory accesses in release mode |
628 | #[inline ] |
629 | #[must_use ] |
630 | pub unsafe fn read_byte(&self, index: usize) -> u8 { |
631 | debug_assert!(index < self.size, "Frame buffer accessed out of bounds" ); |
632 | self.base.add(index).read_volatile() |
633 | } |
634 | |
635 | /// Write a value in the frame buffer, starting at the i-th byte |
636 | /// |
637 | /// We only recommend using this method with [u8; N] arrays. Once Rust has |
638 | /// const generics, it will be deprecated and replaced with a write_bytes() |
639 | /// method that only accepts [u8; N] input. |
640 | /// |
641 | /// # Safety |
642 | /// |
643 | /// This operation is unsafe because... |
644 | /// - It is your responsibility to make sure that the value type makes sense |
645 | /// - You must honor the pixel format and stride specified by the mode info |
646 | /// - There is no bound checking on memory accesses in release mode |
647 | #[inline ] |
648 | pub unsafe fn write_value<T>(&mut self, index: usize, value: T) { |
649 | debug_assert!( |
650 | index.saturating_add(mem::size_of::<T>()) <= self.size, |
651 | "Frame buffer accessed out of bounds" |
652 | ); |
653 | let ptr = self.base.add(index).cast::<T>(); |
654 | ptr.write_volatile(value) |
655 | } |
656 | |
657 | /// Read a value from the frame buffer, starting at the i-th byte |
658 | /// |
659 | /// We only recommend using this method with [u8; N] arrays. Once Rust has |
660 | /// const generics, it will be deprecated and replaced with a read_bytes() |
661 | /// method that only accepts [u8; N] input. |
662 | /// |
663 | /// # Safety |
664 | /// |
665 | /// This operation is unsafe because... |
666 | /// - It is your responsibility to make sure that the value type makes sense |
667 | /// - You must honor the pixel format and stride specified by the mode info |
668 | /// - There is no bound checking on memory accesses in release mode |
669 | #[inline ] |
670 | #[must_use ] |
671 | pub unsafe fn read_value<T>(&self, index: usize) -> T { |
672 | debug_assert!( |
673 | index.saturating_add(mem::size_of::<T>()) <= self.size, |
674 | "Frame buffer accessed out of bounds" |
675 | ); |
676 | (self.base.add(index) as *const T).read_volatile() |
677 | } |
678 | } |
679 | |