breakwater_parser/framebuffer/
shared_memory.rs1use core::slice;
2use std::{cell::UnsafeCell, pin::Pin};
3
4use color_eyre::eyre::{self, Context, bail};
5use shared_memory::{Shmem, ShmemConf, ShmemError};
6use tracing::{debug, info, instrument};
7
8use super::FrameBuffer;
9use crate::framebuffer::FB_BYTES_PER_PIXEL;
10
11const HEADER_SIZE: usize = 2 * std::mem::size_of::<u16>();
13
14unsafe impl Send for SharedMemoryFrameBuffer {}
15unsafe impl Sync for SharedMemoryFrameBuffer {}
16
17pub struct SharedMemoryFrameBuffer {
18 width: usize,
19 height: usize,
20
21 bytes: usize,
22
23 #[allow(unused)]
25 memory: MemoryType,
26
27 buffer: Pin<&'static [UnsafeCell<u8>]>,
30}
31
32#[allow(unused)]
34enum MemoryType {
35 Shared(Shmem),
36 Local(Pin<Box<[UnsafeCell<u8>]>>),
37}
38
39impl SharedMemoryFrameBuffer {
40 #[instrument]
41 pub fn new(
42 width: usize,
43 height: usize,
44 shared_memory_name: Option<&str>,
45 ) -> eyre::Result<Self> {
46 match shared_memory_name {
47 Some(shared_memory_name) => {
48 Self::new_from_shared_memory(width, height, shared_memory_name)
49 }
50 None => Self::new_with_local_memory(width, height),
51 }
52 }
53
54 #[instrument(skip_all)]
55 fn new_with_local_memory(width: usize, height: usize) -> eyre::Result<Self> {
56 let pixels = width * height;
57 let bytes = pixels * FB_BYTES_PER_PIXEL;
58
59 debug!("Using plain (non shared memory) framebuffer");
60
61 let memory: Pin<Box<[UnsafeCell<u8>]>> = Pin::new(
62 (0..(bytes))
63 .map(|_| UnsafeCell::new(0u8))
64 .collect::<Vec<_>>()
65 .into_boxed_slice(),
66 );
67 let buffer = unsafe {
68 std::mem::transmute::<Pin<&[UnsafeCell<u8>]>, Pin<&'static [UnsafeCell<u8>]>>(
69 memory.as_ref(),
70 )
71 };
72
73 Ok(Self {
74 width,
75 height,
76 bytes,
77 memory: MemoryType::Local(memory),
78 buffer,
79 })
80 }
81
82 #[instrument(skip_all)]
83 fn new_from_shared_memory(
84 width: usize,
85 height: usize,
86 shared_memory_name: &str,
87 ) -> eyre::Result<Self> {
88 let pixels = width * height;
89 let framebuffer_bytes = pixels * FB_BYTES_PER_PIXEL;
90 let target_size = HEADER_SIZE + framebuffer_bytes;
91
92 let mut shared_memory = match ShmemConf::new()
93 .os_id(shared_memory_name)
94 .size(target_size)
95 .create()
96 {
97 Ok(shared_memory) => shared_memory,
98 Err(ShmemError::LinkExists | ShmemError::MappingIdExists) => ShmemConf::new()
99 .os_id(shared_memory_name)
100 .open()
101 .with_context(|| {
102 format!("failed to open existing shared memory \"{shared_memory_name}\"")
103 })?,
104 Err(err) => Err(err).with_context(|| {
105 format!("failed to create shared memory \"{shared_memory_name}\"")
106 })?,
107 };
108
109 shared_memory.set_owner(false);
113
114 let actual_size = shared_memory.len();
115 if actual_size != target_size {
116 bail!(
117 "The shared memory had the wrong size! Expected {target_size} bytes, \
118 but it has {actual_size} bytes."
119 );
120 }
121
122 info!(
123 actual_size,
124 name = shared_memory_name,
125 target_size,
126 "Shared memory loaded"
127 );
128 let size_ptr = shared_memory.as_ptr() as *mut u16;
129 unsafe {
130 *size_ptr = width.try_into().context("Framebuffer width too high")?;
131 *size_ptr.add(1) = height.try_into().context("Framebuffer height too high")?;
132 }
133
134 let framebuffer_base_ptr = unsafe { shared_memory.as_ptr().add(HEADER_SIZE) };
136 let buffer = unsafe {
137 let data = framebuffer_base_ptr as *const UnsafeCell<u8>;
138 let slice = Pin::new(slice::from_raw_parts(data, framebuffer_bytes));
139 std::mem::transmute::<Pin<&[UnsafeCell<u8>]>, Pin<&'static [UnsafeCell<u8>]>>(slice)
140 };
141
142 Ok(Self {
143 width,
144 height,
145 bytes: framebuffer_bytes,
146 memory: MemoryType::Shared(shared_memory),
147 buffer,
148 })
149 }
150}
151
152impl FrameBuffer for SharedMemoryFrameBuffer {
153 #[inline(always)]
154 fn get_width(&self) -> usize {
155 self.width
156 }
157
158 #[inline(always)]
159 fn get_height(&self) -> usize {
160 self.height
161 }
162
163 #[inline(always)]
164 unsafe fn get_unchecked(&self, x: usize, y: usize) -> u32 {
165 debug_assert!(x < self.width);
166 debug_assert!(y < self.height);
167
168 let offset = (x + y * self.width) * FB_BYTES_PER_PIXEL;
169
170 let base_ptr = self.buffer.as_ptr() as *const u8;
171 let pixel_ptr = unsafe { base_ptr.add(offset) } as *const u32;
172
173 unsafe { pixel_ptr.read_unaligned() }
175 }
176
177 #[inline(always)]
178 fn set(&self, x: usize, y: usize, rgba: u32) {
179 if x < self.width && y < self.height {
181 let offset = (x + y * self.width) * FB_BYTES_PER_PIXEL;
182 let pixel_ptr = unsafe { self.buffer.get_unchecked(offset).get() } as *mut u32;
183
184 unsafe { pixel_ptr.write_unaligned(rgba) }
186 }
187 }
188
189 #[inline(always)]
190 fn set_multi_from_start_index(&self, starting_index: usize, pixels: &[u8]) -> usize {
191 let num_pixels = pixels.len() / FB_BYTES_PER_PIXEL;
192
193 if starting_index + num_pixels > self.get_size() {
194 debug!(
195 starting_index,
196 num_pixels,
197 buffer_bytes = self.bytes,
198 "Ignoring invalid set_multi call, which would exceed the screen",
199 );
200 return 0;
202 }
203
204 let starting_ptr = unsafe {
205 self.buffer
206 .get_unchecked(starting_index * FB_BYTES_PER_PIXEL)
207 }
208 .get();
209 let target_slice = unsafe { slice::from_raw_parts_mut(starting_ptr, pixels.len()) };
210 target_slice.copy_from_slice(pixels);
211
212 num_pixels
213 }
214
215 #[inline(always)]
216 fn as_bytes(&self) -> &[u8] {
217 let base_ptr = self.buffer.as_ptr() as *const u8;
218 unsafe { slice::from_raw_parts(base_ptr as *mut u8, self.bytes) }
219 }
220}