use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::{Duration, Instant};
use crate::config::{
configured_decode_concurrency, configured_decode_lookahead, configured_decoder_sessions,
configured_reverse_hardware_surfaces, visual_decode_parallelism,
};
use crate::decode::DecodeWorkerConfig;
use crate::frames::NativeGpuFrames;
use crate::host::Host;
use alpha2_model::Project;
use alpha2_native::{CodecThreadAllocation, CodecThreadRequest, codec_thread_allocation};
use alpha2_render::RenderSize;
use alpha2_time::TimeRange;
pub struct ExportDecodePlan {
pub hardware_decode: bool,
pub decode_lookahead: usize,
pub decoder_sessions: usize,
pub codec_threads: CodecThreadAllocation,
pub source_frame_pixels: u64,
pub source_probe: Duration,
pub providers: Arc<crate::providers::ProviderMedia>,
}
impl ExportDecodePlan {
pub(crate) fn new(
split: ExportCodecSplit,
providers: Arc<crate::providers::ProviderMedia>,
) -> Self {
Self {
hardware_decode: split.hardware_decode,
decode_lookahead: split.decode_lookahead,
decoder_sessions: split.decoder_sessions,
codec_threads: split.codec_threads,
source_frame_pixels: split.source_frame_pixels,
source_probe: split.source_probe,
providers,
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct ExportCodecSplit {
pub hardware_decode: bool,
pub decode_lookahead: usize,
pub decoder_sessions: usize,
pub codec_threads: CodecThreadAllocation,
pub source_frame_pixels: u64,
pub source_probe: Duration,
}
impl Host {
#[cfg_attr(
not(all(
any(target_os = "linux", target_os = "macos", target_os = "windows"),
feature = "hwdecode"
)),
allow(clippy::unused_self)
)]
pub(crate) fn hardware_decode_ready(&self) -> bool {
#[cfg(all(target_os = "linux", feature = "hwdecode"))]
return self.decode_vulkan().is_some();
#[cfg(all(target_os = "macos", feature = "hwdecode"))]
return self.decode_metal().is_some();
#[cfg(all(target_os = "windows", feature = "hwdecode"))]
return self.decode_d3d12().is_some();
#[cfg(not(all(
any(target_os = "linux", target_os = "macos", target_os = "windows"),
feature = "hwdecode"
)))]
false
}
pub(crate) fn hardware_video_decode(&self) -> crate::assets::HardwareVideoDecode {
use crate::assets::HardwareVideoDecode;
#[cfg(all(target_os = "linux", feature = "hwdecode"))]
return self
.decode_vulkan()
.map_or(HardwareVideoDecode::None, |context| {
HardwareVideoDecode::Codecs(context.decode_codecs().to_vec())
});
#[cfg(not(all(target_os = "linux", feature = "hwdecode")))]
if self.hardware_decode_ready() {
HardwareVideoDecode::PerStream
} else {
HardwareVideoDecode::None
}
}
#[cfg(all(target_os = "macos", feature = "hwdecode"))]
pub(crate) fn decode_metal(&self) -> Option<alpha2_native_gpu::SharedMetalContext> {
self.shared_metal
.clone()
.filter(|_| self.decode_policy.allows_hardware())
}
#[cfg(all(target_os = "windows", feature = "hwdecode"))]
pub(crate) fn decode_d3d12(&self) -> Option<alpha2_native_gpu::SharedD3D12Context> {
self.shared_d3d12
.clone()
.filter(|context| self.decode_policy.allows_hardware() && context.decode_planes())
}
#[cfg(all(target_os = "linux", feature = "hwdecode"))]
pub(crate) fn decode_vulkan(&self) -> Option<alpha2_native_gpu::SharedVulkanContext> {
self.shared_vulkan.clone().filter(|context| {
self.decode_policy.allows_hardware() && context.video_session_hazard().is_none()
})
}
pub(crate) fn export_providers(
&self,
project: &Project,
size: RenderSize,
) -> Arc<crate::providers::ProviderMedia> {
let providers = Arc::new(crate::providers::ProviderMedia::new(
project,
self.provider_files.clone(),
(size.width, size.height),
self.memory.provider_sources.bytes,
));
self.failure.watch_providers(Arc::clone(&providers));
providers
}
pub(crate) fn export_codec_split(
&self,
project: &Project,
assets: &HashMap<String, PathBuf>,
range: TimeRange,
pipeline_enabled: bool,
software_video_encoder: bool,
) -> ExportCodecSplit {
let hardware_decode = pipeline_enabled && self.hardware_decode_ready();
let decode_lookahead = configured_decode_lookahead(pipeline_enabled);
let decoder_sessions = configured_decoder_sessions();
let workload_parallelism = visual_decode_parallelism(project, range);
let probe_started = Instant::now();
let source_frame_pixels = if hardware_decode || !software_video_encoder {
0
} else {
crate::host::max_video_source_pixels(
project,
assets,
self.conformed_video_pixels.as_ref(),
)
};
let source_probe = probe_started.elapsed();
let codec_threads = codec_thread_allocation(CodecThreadRequest {
decode_concurrency: configured_decode_concurrency(
pipeline_enabled,
hardware_decode,
workload_parallelism,
)
.min(decoder_sessions),
hardware_decode,
source_frame_pixels,
software_video_encoder,
});
ExportCodecSplit {
hardware_decode,
decode_lookahead,
decoder_sessions,
codec_threads,
source_frame_pixels,
source_probe,
}
}
pub(crate) fn export_gpu_frames(&self, size: RenderSize, project: &Project) -> NativeGpuFrames {
#[cfg(all(target_os = "linux", feature = "hwdecode"))]
let mut gpu_frames = NativeGpuFrames::with_vulkan(size, self.decode_vulkan());
#[cfg(all(target_os = "macos", feature = "hwdecode"))]
let mut gpu_frames = NativeGpuFrames::with_metal(size, self.decode_metal());
#[cfg(all(target_os = "windows", feature = "hwdecode"))]
let mut gpu_frames = NativeGpuFrames::with_d3d12(size, self.decode_d3d12());
#[cfg(not(all(
any(target_os = "linux", target_os = "macos", target_os = "windows"),
feature = "hwdecode"
)))]
let mut gpu_frames = NativeGpuFrames::new(size);
gpu_frames.import_fallback = !self.decode_policy.requires_hardware();
gpu_frames.color = alpha2_engine::color_policy(&project.settings.color);
#[cfg(test)]
if let Some(fault) = crate::frames::INJECT_SURFACE_FAULT.with(std::cell::Cell::take) {
gpu_frames.inject(fault);
}
gpu_frames
}
pub(crate) fn decode_worker_config(&self, plan: &ExportDecodePlan) -> DecodeWorkerConfig {
DecodeWorkerConfig {
providers: Arc::clone(&plan.providers),
decoding: self.failure.decoding(),
depth: plan.decode_lookahead,
byte_budget: self.memory.decode_queue.byte_budget(),
concurrency: plan.codec_threads.decode_concurrency,
decoder_threads: plan.codec_threads.decoder_threads,
max_sessions: plan.decoder_sessions,
reverse_cache_bytes: self.memory.reverse_cache.bytes,
reverse_hardware_surfaces: configured_reverse_hardware_surfaces(self.decode_policy),
stall_timeout: crate::config::configured_stall_timeout(),
#[cfg(all(target_os = "linux", feature = "hwdecode"))]
shared_vulkan: self.decode_vulkan(),
#[cfg(all(target_os = "macos", feature = "hwdecode"))]
shared_metal: self.decode_metal(),
#[cfg(all(target_os = "windows", feature = "hwdecode"))]
shared_d3d12: self.decode_d3d12(),
#[cfg(all(
any(target_os = "linux", target_os = "macos", target_os = "windows"),
feature = "hwdecode"
))]
hardware_required: self.decode_policy.requires_hardware(),
#[cfg(test)]
stall: None,
}
}
}