use kanal::{Receiver, Sender};
use thiserror::Error;
use tracing::info;
use crate::{
action::UiAction,
audio::AudioPool,
engine::{self, EngineConfig, EngineConnector},
host_temperature,
input::{Input, artnet},
midi::{self, monitor::MidiMonitorEvent, runtime::TestCommand},
network_stats, output_state,
pipeline::{
extract_output::{ExtractOutput, PreviewOutput},
output_sender,
},
storage,
};
#[derive(Debug, Error)]
pub enum StartError {
#[error("the action queue was already set up")]
ActionQueue,
#[error("could not start the output: {0:#}")]
Output(anyhow::Error),
#[error("no GPU works")]
NoAdapter,
#[error("could not start the engine: {0:#}")]
Engine(anyhow::Error),
}
#[derive(Debug)]
pub struct EngineStart {
actions: Receiver<UiAction>,
extract_output: ExtractOutput,
artnet_control: Receiver<Vec<u8>>,
preview_output: Receiver<PreviewOutput>,
network_stats: Receiver<(f64, f64)>,
temperature: Receiver<Option<f32>>,
midi_monitor: Receiver<MidiMonitorEvent>,
midi_learn: Receiver<[u8; 3]>,
midi_test: Sender<TestCommand>,
}
pub fn services() -> Result<EngineStart, StartError> {
let actions = UiAction::init_queue().ok_or(StartError::ActionQueue)?;
storage::start_thread();
let (midi_monitor, midi_test) = midi::start_threads();
let midi_learn = midi::learn::init();
let network_stats = network_stats::start_thread();
let temperature = host_temperature::start_thread();
let (extract_output, output_receiver, preview_output) = ExtractOutput::new();
let output_package_sender =
output_sender::start(output_receiver).map_err(StartError::Output)?;
let (artnet_bridge, artnet_control) = artnet::start_thread(output_package_sender);
Input::init(artnet_bridge);
output_state::init();
Ok(EngineStart {
actions,
extract_output,
artnet_control,
preview_output,
network_stats,
temperature,
midi_monitor,
midi_learn,
midi_test,
})
}
impl EngineStart {
pub fn spawn(self, adapter: wgpu::Adapter) -> Result<EngineConnector, StartError> {
engine::spawn(
EngineConfig {
adapter,
extract_output: self.extract_output,
artnet_control_receiver: self.artnet_control,
preview_output: self.preview_output,
network_stats: self.network_stats,
temperature: self.temperature,
midi_monitor: self.midi_monitor,
midi_learn: self.midi_learn,
midi_test: self.midi_test,
audio_pool: AudioPool::default(),
},
self.actions,
)
.map_err(StartError::Engine)
}
}
pub async fn headless_adapter() -> Result<wgpu::Adapter, StartError> {
let instance =
wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle_from_env());
let adapters = instance.enumerate_adapters(wgpu::Backends::all()).await;
let adapter = crate::gpu_choice::choose(&adapters, |_| true).ok_or(StartError::NoAdapter)?;
let adapter_info = adapter.get_info();
info!(
name = %adapter_info.name,
backend = ?adapter_info.backend,
"rendering on GPU adapter"
);
Ok(adapter)
}