gled 2.29.0

gled is an application for creating animations and effects on artnet or dmx installations
//! GPU devices used by gled.
//!
//! gled uses two wgpu devices on the same adapter:
//! - the **UI device** owned by egui, for everything the window samples
//!   (effect thumbnails, LED preview, editor previews);
//! - the **output device** owned by the render thread, for the LED output that
//!   is read back and sent as Art-Net/DMX.
//!
//! They must stay separate: wgpu fails a window surface reconfigure when another
//! thread submits on the same device meanwhile, and egui-wgpu panics on that.

use crate::{
    pipeline::{constants::OUTPUT_BUFFER_SIZE, output_clear::OutputClear},
    wgpu_render_state,
};
use wgpu::{Adapter, Buffer, BufferDescriptor, BufferUsages, Device, Queue};

/// A device and its queue.
#[derive(Debug, Clone)]
pub struct Gpu {
    pub device: Device,
    pub queue: Queue,
}

impl Gpu {
    /// The UI device owned by egui. Only use it on the UI thread.
    pub fn ui() -> Self {
        let render_state = wgpu_render_state();
        Self {
            device: render_state.device,
            queue: render_state.queue,
        }
    }
}

/// The output device with the resources shared by all effects on it.
#[derive(Debug)]
pub struct OutputContext {
    pub gpu: Gpu,
    /// Mixed output of all active effects, read back for Art-Net/DMX.
    pub output_buffer: Buffer,
    pub output_clear: OutputClear,
}

impl OutputContext {
    /// Create a second device on `adapter` for the render thread. Blocks until
    /// the device exists, which on native backends is immediate.
    pub fn new(adapter: &Adapter) -> Result<Self, wgpu::RequestDeviceError> {
        let (device, queue) =
            pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor {
                label: Some("gled output device"),
                #[cfg(feature = "profiling")]
                required_features: adapter.features()
                    & wgpu_profiler::GpuProfiler::ALL_WGPU_TIMER_FEATURES,
                required_limits: adapter.limits(),
                ..Default::default()
            }))?;
        device.on_uncaptured_error(std::sync::Arc::new(|error| {
            tracing::error!("WGPU error on the output device: {error:?}");
        }));

        let output_buffer = device.create_buffer(&BufferDescriptor {
            size: OUTPUT_BUFFER_SIZE,
            usage: BufferUsages::STORAGE | BufferUsages::COPY_SRC | BufferUsages::COPY_DST,
            label: Some("Output buffer"),
            mapped_at_creation: false,
        });
        let output_clear = OutputClear::new(&device, &output_buffer);

        Ok(Self {
            gpu: Gpu { device, queue },
            output_buffer,
            output_clear,
        })
    }
}