est_render/
software.rs

1//! Implementation of the software renderer using softbuffer crate.
2//!
3//! This module provides a software renderer that can be used for rendering graphics without relying on a GPU.
4//! Does not provided any high-level abstractions such drawing quad or image, but rather low-level access to the softbuffer crate. \
5//! Provided as it, without any guarantees of performance or correctness.
6
7use std::{num::NonZero, sync::Arc};
8
9use winit::dpi::PhysicalSize;
10
11use crate::{math::Point2, utils::ArcRef, window::Window};
12
13/// Creates a new [software::PixelBuffer] instance. \
14/// This is not thread-safe and must be called from the same thread as the window.
15pub fn new<'a>(window: Option<&'a mut super::window::Window>) -> PixelBufferBuilder<'a> {
16    let builder = PixelBufferBuilder::new();
17
18    if let Some(window) = window {
19        builder.with_window(window)
20    } else {
21        builder
22    }
23}
24
25/// A wrapper around softbuffer to provide a soft buffer for pixel manipulation
26#[derive(Clone, Debug)]
27pub struct PixelBuffer {
28    pub(crate) inner: ArcRef<PixelBufferInner>,
29}
30
31impl PixelBuffer {
32    pub(crate) fn new(window: &Window) -> Result<Self, PixelBufferError> {
33        let window_inner = window.inner.wait_borrow_mut();
34
35        let window_handle = {
36            let handle = window_inner.window_pointer.as_ref();
37
38            if handle.is_none() {
39                return Err(PixelBufferError::WindowPointerIsNull);
40            }
41
42            let handle = handle.unwrap();
43            let window_handle = handle.lock().get_window().clone();
44
45            window_handle
46        };
47
48        let context = SoftbufferContext::new(window_handle.clone());
49
50        if context.is_err() {
51            return Err(PixelBufferError::ContextCreationFailed);
52        }
53
54        let context = context.unwrap();
55        let surface = SoftbufferSurface::new(&context, window_handle);
56
57        if surface.is_err() {
58            return Err(PixelBufferError::SurfaceCreationFailed);
59        }
60
61        let surface = surface.unwrap();
62        let softbuffer_inner = PixelBufferInner {
63            _context: context,
64            surface,
65            surface_size: Point2::new(0.0, 0.0),
66        };
67
68        let softbuffer_inner = ArcRef::new(softbuffer_inner);
69
70        Ok(PixelBuffer {
71            inner: softbuffer_inner,
72        })
73    }
74
75    /// Get the size of the soft buffer surface
76    /// Returns the size of the soft buffer surface in pixels
77    pub fn size(&self) -> Point2 {
78        let inner = self.inner.wait_borrow();
79        inner.surface_size
80    }
81
82    /// Write pixels to the soft buffer surface
83    pub fn write_buffers(&mut self, pixels: &[u32], size: Point2) -> Result<(), PixelBufferError> {
84        let mut inner = self.inner.wait_borrow_mut();
85
86        if pixels.len() != (size.x * size.y) as usize {
87            return Err(PixelBufferError::InvalidSize(size.x as u32, size.y as u32));
88        }
89
90        if inner.surface_size == Point2::new(0.0, 0.0) {
91            return Err(PixelBufferError::InvalidSurfaceSize);
92        }
93
94        let pixel_buffers = inner.surface.buffer_mut();
95        if pixel_buffers.is_err() {
96            return Err(PixelBufferError::BufferFetchFailed);
97        }
98
99        let mut pixel_buffers = pixel_buffers.unwrap();
100        if pixel_buffers.len() < pixels.len() {
101            return Err(PixelBufferError::BufferTooSmall);
102        }
103
104        for (i, pixel) in pixels.iter().enumerate() {
105            pixel_buffers[i] = *pixel;
106        }
107
108        let res = pixel_buffers.present();
109        if res.is_err() {
110            return Err(PixelBufferError::PresentFailed);
111        }
112
113        Ok(())
114    }
115}
116
117pub type SoftbufferSurface = softbuffer::Surface<Arc<winit::window::Window>, Arc<winit::window::Window>>;
118pub type SoftbufferContext = softbuffer::Context<Arc<winit::window::Window>>;
119
120pub(crate) struct PixelBufferInner {
121    pub _context: SoftbufferContext,
122    pub surface: SoftbufferSurface,
123    pub surface_size: Point2,
124}
125
126impl PixelBufferInner {
127    pub fn resize(&mut self, size: PhysicalSize<u32>) -> Result<(), String> {
128        if size.width == 0 || size.height == 0 {
129            return Err("Invalid size".to_string());
130        }
131
132        self.surface_size = Point2::new(size.width as f32, size.height as f32);
133
134        let width: NonZero<u32> = NonZero::new(size.width).ok_or("Width cannot be zero")?;
135        let height: NonZero<u32> = NonZero::new(size.height).ok_or("Height cannot be zero")?;
136
137        let result = self.surface.resize(width, height);
138        if result.is_err() {
139            return Err(format!(
140                "Failed to resize softbuffer surface: {:?}",
141                result.err()
142            ));
143        }
144
145        Ok(())
146    }
147}
148
149
150pub struct PixelBufferBuilder<'a> {
151    window: Option<&'a mut Window>,
152}
153
154impl<'a> PixelBufferBuilder<'a> {
155    pub(crate) fn new() -> Self {
156        PixelBufferBuilder { window: None }
157    }
158
159    /// Sets the window for this PixelBuffer instance. \
160    /// This is useful for creating a PixelBuffer instance that is bound to a specific window.
161    pub fn with_window(mut self, window: &'a mut Window) -> Self {
162        self.window = Some(window);
163        self
164    }
165
166    pub fn build(self) -> Result<PixelBuffer, PixelBufferBuilderError> {
167        if self.window.is_none() {
168            return Err(PixelBufferBuilderError::WindowIsNull);
169        }
170
171        let window = self.window.unwrap();
172
173        let is_graphics_exist = {
174            let window_inner = window.inner.borrow();
175            window_inner.graphics.is_some()
176        };
177
178        if is_graphics_exist {
179            return Err(PixelBufferBuilderError::CannotUseWithGPUWindow);
180        }
181
182        let pixel_buffer =
183            PixelBuffer::new(window).map_err(|e| PixelBufferBuilderError::PixelBufferError(e))?;
184
185        let mut window_inner = window.inner.borrow_mut();
186        window_inner.pixelbuffer = Some(pixel_buffer.inner.clone());
187
188        Ok(pixel_buffer)
189    }
190}
191
192#[derive(Clone, Copy, Debug)]
193pub enum PixelWriteMode {
194    // Append to the existing pixel value
195    Copy,
196    // Replace the existing pixel value with the new one
197    Clear,
198    // Blend the new pixel value with the existing one, such as alpha blending
199    Blend,
200}
201
202#[derive(Clone, Copy, Debug)]
203pub enum PixelBlendMode {
204    // Alpha blending
205    Alpha,
206    // Additive blending
207    Add,
208    // Subtractive blending
209    Subtract,
210    // Multiplicative blending
211    Multiply,
212}
213
214#[derive(Clone, Copy, Debug)]
215pub enum PixelBufferError {
216    WindowPointerIsNull,
217    ContextCreationFailed,
218    SurfaceCreationFailed,
219    InvalidSize(u32, u32),
220    InvalidSurfaceSize,
221    BufferFetchFailed,
222    BufferTooSmall,
223    PresentFailed,
224}
225
226impl std::fmt::Display for PixelBufferError {
227    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
228        match self {
229            PixelBufferError::WindowPointerIsNull => write!(f, "Window pointer is null"),
230            PixelBufferError::ContextCreationFailed => {
231                write!(f, "Failed to create pixel buffer context")
232            }
233            PixelBufferError::SurfaceCreationFailed => {
234                write!(f, "Failed to create pixel buffer surface")
235            }
236            PixelBufferError::InvalidSize(width, height) => {
237                write!(f, "Invalid size: {}x{}", width, height)
238            }
239            PixelBufferError::InvalidSurfaceSize => write!(f, "Pixel buffer surface size is zero"),
240            PixelBufferError::BufferFetchFailed => write!(f, "Failed to fetch pixel buffer"),
241            PixelBufferError::BufferTooSmall => write!(f, "Pixel buffer is too small"),
242            PixelBufferError::PresentFailed => write!(f, "Failed to present pixel buffer"),
243        }
244    }
245}
246
247#[derive(Clone, Copy, Debug)]
248pub enum PixelBufferBuilderError {
249    WindowIsNull,
250    CannotUseWithGPUWindow,
251    PixelBufferError(PixelBufferError),
252}
253
254impl std::fmt::Display for PixelBufferBuilderError {
255    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
256        match self {
257            PixelBufferBuilderError::WindowIsNull => {
258                write!(f, "PixelBuffer must be created with a window")
259            }
260            PixelBufferBuilderError::CannotUseWithGPUWindow => write!(
261                f,
262                "PixelBuffer cannot be created alongside GPU (hardware rendering)"
263            ),
264            PixelBufferBuilderError::PixelBufferError(e) => write!(f, "PixelBuffer error: {}", e),
265        }
266    }
267}