1use gled_midi::{HostEvent, native::NativeHost};
6use kanal::Receiver;
7use thiserror::Error;
8use tracing::info;
9
10use crate::{
11 action::UiAction,
12 audio::AudioPool,
13 engine::{self, EngineConfig, EngineConnector},
14 host_temperature,
15 input::{Input, artnet},
16 network_stats, output_state,
17 pipeline::{
18 extract_output::{ExtractOutput, PreviewOutput},
19 output_sender,
20 },
21 storage,
22};
23
24#[derive(Debug, Error)]
25pub enum StartError {
26 #[error("the action queue was already set up")]
27 ActionQueue,
28 #[error("could not start the output: {0:#}")]
29 Output(anyhow::Error),
30 #[error("no GPU works")]
31 NoAdapter,
32 #[error("could not start the engine: {0:#}")]
33 Engine(anyhow::Error),
34}
35
36#[derive(Debug)]
38pub struct EngineStart {
39 actions: Receiver<UiAction>,
40 extract_output: ExtractOutput,
41 artnet_control: Receiver<Vec<u8>>,
42 preview_output: Receiver<PreviewOutput>,
43 network_stats: Receiver<(f64, f64)>,
44 temperature: Receiver<Option<f32>>,
45 midi: NativeHost,
46 midi_events: Receiver<HostEvent>,
47}
48
49pub fn services() -> Result<EngineStart, StartError> {
52 let actions = UiAction::init_queue().ok_or(StartError::ActionQueue)?;
53 storage::start_thread();
54 let (midi_events, midi_event_receiver) = kanal::unbounded();
55 let midi = NativeHost::start(midi_events);
56 let network_stats = network_stats::start_thread();
57 let temperature = host_temperature::start_thread();
58 let (extract_output, output_receiver, preview_output) = ExtractOutput::new();
59 let artnet_inputs = output_sender::start(output_receiver).map_err(StartError::Output)?;
60 let (artnet_bridge, artnet_control) = artnet::start_thread(artnet_inputs);
61 Input::init(artnet_bridge);
62 output_state::init();
63 Ok(EngineStart {
64 actions,
65 extract_output,
66 artnet_control,
67 preview_output,
68 network_stats,
69 temperature,
70 midi,
71 midi_events: midi_event_receiver,
72 })
73}
74
75impl EngineStart {
76 pub fn spawn(self, adapter: wgpu::Adapter) -> Result<EngineConnector, StartError> {
78 engine::spawn(
79 EngineConfig {
80 adapter,
81 extract_output: self.extract_output,
82 artnet_control_receiver: self.artnet_control,
83 preview_output: self.preview_output,
84 network_stats: self.network_stats,
85 temperature: self.temperature,
86 midi: self.midi,
87 midi_events: self.midi_events,
88 audio_pool: AudioPool::default(),
89 },
90 self.actions,
91 )
92 .map_err(StartError::Engine)
93 }
94}
95
96pub async fn headless_adapter() -> Result<wgpu::Adapter, StartError> {
100 let instance =
101 wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle_from_env());
102 let adapters = instance.enumerate_adapters(wgpu::Backends::all()).await;
103 let adapter = crate::gpu_choice::choose(&adapters, |_| true).ok_or(StartError::NoAdapter)?;
104 let adapter_info = adapter.get_info();
105 info!(
106 name = %adapter_info.name,
107 backend = ?adapter_info.backend,
108 "rendering on GPU adapter"
109 );
110 Ok(adapter)
111}