Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 47 additions & 1 deletion src-tauri/src/ai_processing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ pub async fn get_or_init_denoise_model(

let _ = ort::init().with_name("AI-Denoise").commit();
let model_path = models_dir.join(DENOISE_FILENAME);
let session = Session::builder()?.commit_from_file(model_path)?;
let session = create_session(app_handle, model_path)?;
let denoise_model = Arc::new(Mutex::new(session));

crate::register_exit_handler();
Expand Down Expand Up @@ -1508,3 +1508,49 @@ pub struct AiDepthMaskParameters {
#[serde(default)]
pub orientation_steps: Option<u8>,
}

fn create_session<P: AsRef<Path>>(
app_handle: &tauri::AppHandle,
model_path: P,
) -> Result<Session, ort::Error> {
let settings = crate::app_settings::load_settings(app_handle.clone()).unwrap_or_default();
let enable_hardware_acceleration = settings.enable_denoise_hardware_acceleration;
let path_ref = model_path.as_ref();

#[cfg(target_os = "macos")]
if enable_hardware_acceleration {
use ort::execution_providers::{
CoreMLExecutionProvider,
coreml::{CoreMLComputeUnits, CoreMLModelFormat},
};

// Attempt CoreML compilation
if let Ok(builder) = Session::builder() {
let provider = CoreMLExecutionProvider::default()
.with_subgraphs(true)
.with_model_format(CoreMLModelFormat::MLProgram)
.with_compute_units(CoreMLComputeUnits::All)
.build();

if let Ok(builder_with_ep) = builder.with_execution_providers([provider]) {
match builder_with_ep.commit_from_file(path_ref) {
Ok(session) => return Ok(session),
Err(e) => {
log::warn!(
"Hardware acceleration failed for {:?}, falling back to CPU. Error: {}",
path_ref.file_name().unwrap_or_default(),
e
);
}
}
}
}
}

#[cfg(not(target_os = "macos"))]
let _ = enable_hardware_acceleration;

// Fallback to CPU execution
let builder = Session::builder()?;
builder.commit_from_file(path_ref)
}
3 changes: 3 additions & 0 deletions src-tauri/src/app_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,8 @@ pub struct AppSettings {
#[serde(default)]
pub use_full_dpi_rendering: Option<bool>,
#[serde(default)]
pub enable_denoise_hardware_acceleration: bool,
#[serde(default)]
pub high_res_zoom_multiplier: Option<f32>,
#[serde(default)]
pub enable_live_previews: Option<bool>,
Expand Down Expand Up @@ -427,6 +429,7 @@ impl Default for AppSettings {
editor_preview_resolution: Some(1920),
enable_zoom_hifi: Some(true),
use_full_dpi_rendering: Some(false),
enable_denoise_hardware_acceleration: false,
enable_live_previews: Some(true),
live_preview_quality: Some("high".to_string()),
sort_criteria: None,
Expand Down
48 changes: 48 additions & 0 deletions src/components/modals/DenoiseModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,38 @@ export default function DenoiseModal({
const isBatch = targetPaths.length > 1;
const mouseDownTarget = useRef<EventTarget | null>(null);

const [elapsedTime, setElapsedTime] = useState<number>(0);
const startTimeRef = useRef<number | null>(null);
const timerIntervalRef = useRef<NodeJS.Timeout | null>(null);

useEffect(() => {
if (isProcessing) {
startTimeRef.current = Date.now();
setElapsedTime(0);
timerIntervalRef.current = setInterval(() => {
if (startTimeRef.current !== null) {
setElapsedTime(Date.now() - startTimeRef.current);
}
}, 50);
} else {
if (startTimeRef.current !== null) {
const duration = Date.now() - startTimeRef.current;
setElapsedTime(duration);
startTimeRef.current = null;
}
if (timerIntervalRef.current) {
clearInterval(timerIntervalRef.current);
timerIntervalRef.current = null;
}
}

return () => {
if (timerIntervalRef.current) {
clearInterval(timerIntervalRef.current);
}
};
}, [isProcessing]);

const methodOptions = useMemo<Array<{ label: string; value: 'ai' | 'bm3d' }>>(
() => [
{ label: t('modals.denoise.methodAi'), value: 'ai' },
Expand Down Expand Up @@ -264,6 +296,7 @@ export default function DenoiseModal({
setMethod(isRaw ? 'ai' : 'bm3d');
setIntensity(isRaw ? 50 : 15);
setIsMounted(true);
setElapsedTime(0);
const timer = setTimeout(() => setShow(true), 10);
return () => clearTimeout(timer);
} else {
Expand Down Expand Up @@ -352,6 +385,15 @@ export default function DenoiseModal({
return (
<div className="w-full h-[500px]">
<ImageCompare original={originalBase64} denoised={previewBase64} />
{elapsedTime > 0 && (
<motion.div initial={{ opacity: 0, y: 4 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.3 }}>
<div className="text-center mt-3">
<Text color={TextColors.success}>
{t('modals.denoise.completedIn', { time: (elapsedTime / 1000).toFixed(2) })}
</Text>
</div>
</motion.div>
)}
{savedPath && (
<motion.div initial={{ opacity: 0, y: 8 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.3 }}>
<Text
Expand Down Expand Up @@ -391,6 +433,12 @@ export default function DenoiseModal({
</Text>
<Text className="text-center font-mono h-6 flex justify-center items-center">{currentStatusText}</Text>

{elapsedTime > 0 && (
<Text variant={TextVariants.small} className="text-center font-mono mt-2 opacity-80">
{t('modals.denoise.elapsedTime', { time: (elapsedTime / 1000).toFixed(2) })}
</Text>
)}

<div className="mt-8 w-64 relative">
<div className="h-1 bg-surface rounded-full overflow-hidden relative w-full shadow-xs">
<motion.div
Expand Down
17 changes: 17 additions & 0 deletions src/components/panel/SettingsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,7 @@ export default function SettingsPanel({
thumbnailResolution: appSettings?.thumbnailResolution || 720,
rawHighlightCompression: appSettings?.rawHighlightCompression ?? 2.5,
processingBackend: appSettings?.processingBackend || 'auto',
enableDenoiseHardwareAcceleration: appSettings?.enableDenoiseHardwareAcceleration ?? false,
linuxGpuOptimization: appSettings?.linuxGpuOptimization ?? false,
highResZoomMultiplier: appSettings?.highResZoomMultiplier || 1.0,
useFullDpiRendering: appSettings?.useFullDpiRendering ?? false,
Expand Down Expand Up @@ -642,6 +643,7 @@ export default function SettingsPanel({
thumbnailResolution: appSettings?.thumbnailResolution || 720,
rawHighlightCompression: appSettings?.rawHighlightCompression ?? 2.5,
processingBackend: appSettings?.processingBackend || 'auto',
enableDenoiseHardwareAcceleration: appSettings?.enableDenoiseHardwareAcceleration ?? false,
linuxGpuOptimization: appSettings?.linuxGpuOptimization ?? false,
highResZoomMultiplier: appSettings?.highResZoomMultiplier || 1.0,
useFullDpiRendering: appSettings?.useFullDpiRendering ?? false,
Expand Down Expand Up @@ -679,6 +681,7 @@ export default function SettingsPanel({

if (
key === 'processingBackend' ||
key === 'enableDenoiseHardwareAcceleration' ||
key === 'linuxGpuOptimization' ||
key === 'useWgpuRenderer' ||
key === 'thumbnailWorkerThreads'
Expand Down Expand Up @@ -1902,6 +1905,20 @@ export default function SettingsPanel({
/>
</SettingItem>

{osPlatform === 'macos' && (
<SettingItem
label={t('settings.processing.denoiseHardwareAcceleration')}
description={t('settings.processing.denoiseHardwareAccelerationDescMacos')}
>
<Switch
checked={processingSettings.enableDenoiseHardwareAcceleration}
id="ai-gpu-toggle"
label={t('settings.processing.denoiseHardwareAccelerationLabel')}
onChange={(checked) => handleProcessingSettingChange('enableDenoiseHardwareAcceleration', checked)}
/>
</SettingItem>
)}

{osPlatform !== 'macos' && osPlatform !== 'windows' && (
<SettingItem
label={t('settings.processing.linuxCompat')}
Expand Down
1 change: 1 addition & 0 deletions src/components/ui/AppProperties.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ export interface AppSettings {
editorPreviewResolution?: number;
enableZoomHifi?: boolean;
useFullDpiRendering?: boolean;
enableDenoiseHardwareAcceleration?: boolean;
highResZoomMultiplier?: number;
enableLivePreviews?: boolean;
livePreviewQuality?: string;
Expand Down
5 changes: 5 additions & 0 deletions src/i18n/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -1136,6 +1136,8 @@
"btnStart": "Start",
"cancel": "Abbrechen",
"close": "Schließen",
"completedIn": "Rauschminderung abgeschlossen in {{time}}s",
"elapsedTime": "Vergangene Zeit: {{time}}s",
"denoised": "Entrauscht",
"denoisingProgress": "Entrauschen in Arbeit",
"description": "Entfernen Sie Rauschen aus Ihrem Bild mithilfe von KI-gestütztem oder traditionellem Entrauschen.",
Expand Down Expand Up @@ -1535,6 +1537,9 @@
},
"title": "Generative KI"
},
"denoiseHardwareAcceleration": "Denoising-Hardwarebeschleunigung",
"denoiseHardwareAccelerationDescMacos": "Nutzt Apples CoreML (Neural Engine & GPU) zur erheblichen Beschleunigung von AI Denoising. Das Deaktivieren verwendet reine CPU-Ausführung, was langsamer ist, aber absolute numerische Präzision bietet und mögliche Treiber- oder Stabilitätsprobleme vermeidet.",
"denoiseHardwareAccelerationLabel": "Denoising-Hardwarebeschleunigung aktivieren",
"backend": "Verarbeitungs-Backend",
"backendDesc": "Wählen Sie die Grafik-API. 'Auto' wird empfohlen. Kann Abstürze auf einigen Systemen beheben.",
"backends": {
Expand Down
5 changes: 5 additions & 0 deletions src/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1136,6 +1136,8 @@
"btnStart": "Start",
"cancel": "Cancel",
"close": "Close",
"completedIn": "Denoising completed in {{time}}s",
"elapsedTime": "Time elapsed: {{time}}s",
"denoised": "Denoised",
"denoisingProgress": "Denoising in Progress",
"description": "Remove noise from your image using AI-powered or traditional denoising.",
Expand Down Expand Up @@ -1535,6 +1537,9 @@
},
"title": "Generative AI"
},
"denoiseHardwareAcceleration": "Denoise Hardware Acceleration",
"denoiseHardwareAccelerationDescMacos": "Uses Apple's CoreML (Neural Engine & GPU) to vastly accelerate AI Denoising. Disabling this uses pure CPU execution, which has slower performance but offers absolute numerical precision and avoids potential driver/stability issues.",
"denoiseHardwareAccelerationLabel": "Enable Denoise Hardware Acceleration",
"backend": "Processing Backend",
"backendDesc": "Select the graphics API. 'Auto' is recommended. May fix crashes on some systems.",
"backends": {
Expand Down