1use kanal::{Receiver, Sender};
6use thiserror::Error;
7use tracing::info;
8
9use crate::{
10 action::UiAction,
11 audio::AudioPool,
12 engine::{self, EngineConfig, EngineConnector},
13 host_temperature,
14 input::{Input, artnet},
15 midi::{self, monitor::MidiMonitorEvent, runtime::TestCommand},
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_monitor: Receiver<MidiMonitorEvent>,
46 midi_learn: Receiver<[u8; 3]>,
47 midi_test: Sender<TestCommand>,
48}
49
50pub fn services() -> Result<EngineStart, StartError> {
53 let actions = UiAction::init_queue().ok_or(StartError::ActionQueue)?;
54 storage::start_thread();
55 let (midi_monitor, midi_test) = midi::start_threads();
56 let midi_learn = midi::learn::init();
57 let network_stats = network_stats::start_thread();
58 let temperature = host_temperature::start_thread();
59 let (extract_output, output_receiver, preview_output) = ExtractOutput::new();
60 let output_package_sender =
61 output_sender::start(output_receiver).map_err(StartError::Output)?;
62 let (artnet_bridge, artnet_control) = artnet::start_thread(output_package_sender);
63 Input::init(artnet_bridge);
64 output_state::init();
65 Ok(EngineStart {
66 actions,
67 extract_output,
68 artnet_control,
69 preview_output,
70 network_stats,
71 temperature,
72 midi_monitor,
73 midi_learn,
74 midi_test,
75 })
76}
77
78impl EngineStart {
79 pub fn spawn(self, adapter: wgpu::Adapter) -> Result<EngineConnector, StartError> {
81 engine::spawn(
82 EngineConfig {
83 adapter,
84 extract_output: self.extract_output,
85 artnet_control_receiver: self.artnet_control,
86 preview_output: self.preview_output,
87 network_stats: self.network_stats,
88 temperature: self.temperature,
89 midi_monitor: self.midi_monitor,
90 midi_learn: self.midi_learn,
91 midi_test: self.midi_test,
92 audio_pool: AudioPool::default(),
93 },
94 self.actions,
95 )
96 .map_err(StartError::Engine)
97 }
98}
99
100pub async fn headless_adapter() -> Result<wgpu::Adapter, StartError> {
104 let instance =
105 wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle_from_env());
106 let adapters = instance.enumerate_adapters(wgpu::Backends::all()).await;
107 let adapter = crate::gpu_choice::choose(&adapters, |_| true).ok_or(StartError::NoAdapter)?;
108 let adapter_info = adapter.get_info();
109 info!(
110 name = %adapter_info.name,
111 backend = ?adapter_info.backend,
112 "rendering on GPU adapter"
113 );
114 Ok(adapter)
115}