fix(imageresizer): improve AI initialization and ensure UI thread handling

This commit is contained in:
Yu Leng
2026-01-15 16:10:07 +08:00
parent 4d22126065
commit 890ca89909
3 changed files with 54 additions and 25 deletions

View File

@@ -107,33 +107,26 @@ namespace ImageResizer
// Temporary workaround for issue #1273
WindowHelpers.BringToForeground(new System.Windows.Interop.WindowInteropHelper(mainWindow).Handle);
// Check AI availability on UI thread after window is loaded
// Windows AI APIs require proper COM apartment and SynchronizationContext
// Check AI availability after window is loaded
// WinAiSuperResolutionService handles UI thread dispatching internally
if (!OSVersionHelper.IsWindows10())
{
mainWindow.Loaded += (s, args) =>
mainWindow.Loaded += async (s, args) =>
{
// Use Dispatcher to ensure we're on the UI thread
mainWindow.Dispatcher.InvokeAsync(async () =>
{
await InitializeAiOnUIThreadAsync();
});
await InitializeAiAsync();
};
}
}
/// <summary>
/// Initialize AI availability check on UI thread.
/// Windows AI APIs require proper COM apartment context.
/// Initialize AI availability check.
/// WinAiSuperResolutionService handles UI thread dispatching internally.
/// </summary>
private static async System.Threading.Tasks.Task InitializeAiOnUIThreadAsync()
private static async System.Threading.Tasks.Task InitializeAiAsync()
{
try
{
Logger.LogInfo($"Checking AI availability on UI thread (Thread: {System.Threading.Thread.CurrentThread.ManagedThreadId})");
var readyState = Services.WinAiSuperResolutionService.GetModelReadyState();
Logger.LogInfo($"ImageScaler.GetReadyState() returned: {readyState}");
switch (readyState)
{
@@ -144,21 +137,18 @@ namespace ImageResizer
if (aiService != null)
{
ResizeBatch.SetAiSuperResolutionService(aiService);
Logger.LogInfo("AI Super Resolution service initialized successfully on UI thread.");
}
break;
case Microsoft.Windows.AI.AIFeatureReadyState.NotReady:
AiAvailabilityState = AiAvailabilityState.ModelNotReady;
Logger.LogInfo("AI model not ready, user can download via UI.");
break;
case Microsoft.Windows.AI.AIFeatureReadyState.DisabledByUser:
case Microsoft.Windows.AI.AIFeatureReadyState.NotSupportedOnCurrentSystem:
default:
AiAvailabilityState = AiAvailabilityState.NotSupported;
Logger.LogInfo($"AI not supported: {readyState}");
break;
}
@@ -167,7 +157,7 @@ namespace ImageResizer
}
catch (Exception ex)
{
Logger.LogError($"Failed to check AI availability on UI thread: {ex.Message}");
Logger.LogError($"Failed to check AI availability: {ex.Message}");
AiAvailabilityState = AiAvailabilityState.NotSupported;
AiInitializationCompleted?.Invoke(null, AiAvailabilityState);
}

View File

@@ -10,6 +10,7 @@ using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using ManagedCommon;
using Microsoft.Windows.AI;
using Microsoft.Windows.AI.Imaging;
using Windows.Graphics.Imaging;
@@ -33,44 +34,78 @@ namespace ImageResizer.Services
/// <summary>
/// Async factory method to create and initialize WinAiSuperResolutionService.
/// Returns null if initialization fails.
/// Must be called on UI thread. Returns null if initialization fails.
/// </summary>
public static async Task<WinAiSuperResolutionService> CreateAsync()
{
// Ensure we're on UI thread for Windows AI API calls
if (Application.Current?.Dispatcher != null && !Application.Current.Dispatcher.CheckAccess())
{
return await Application.Current.Dispatcher.InvokeAsync(() => CreateAsync()).Task.Unwrap();
}
try
{
Logger.LogInfo($"ImageScaler.CreateAsync() called on thread {Thread.CurrentThread.ManagedThreadId}");
var imageScaler = await ImageScaler.CreateAsync();
if (imageScaler == null)
{
Logger.LogWarning("ImageScaler.CreateAsync() returned null");
return null;
}
Logger.LogInfo("ImageScaler created successfully");
return new WinAiSuperResolutionService(imageScaler);
}
catch
catch (Exception ex)
{
Logger.LogError($"ImageScaler.CreateAsync() failed: {ex.Message}");
return null;
}
}
/// <summary>
/// Get the ready state of the ImageScaler model.
/// Must be called on UI thread for accurate results.
/// </summary>
public static AIFeatureReadyState GetModelReadyState()
{
// Ensure we're on UI thread for Windows AI API calls
if (Application.Current?.Dispatcher != null && !Application.Current.Dispatcher.CheckAccess())
{
return Application.Current.Dispatcher.Invoke(() => GetModelReadyState());
}
try
{
return ImageScaler.GetReadyState();
Logger.LogInfo($"ImageScaler.GetReadyState() called on thread {Thread.CurrentThread.ManagedThreadId}");
var state = ImageScaler.GetReadyState();
Logger.LogInfo($"ImageScaler.GetReadyState() returned: {state}");
return state;
}
catch (Exception)
catch (Exception ex)
{
Logger.LogError($"ImageScaler.GetReadyState() failed: {ex.Message}");
// If we can't get the state, treat it as disabled by user
// The caller should check if it's Ready or NotReady
return AIFeatureReadyState.DisabledByUser;
}
}
/// <summary>
/// Ensure the AI model is ready (download if needed).
/// Must be called on UI thread.
/// </summary>
public static async Task<AIFeatureReadyResult> EnsureModelReadyAsync(IProgress<double> progress = null)
{
// Ensure we're on UI thread for Windows AI API calls
if (Application.Current?.Dispatcher != null && !Application.Current.Dispatcher.CheckAccess())
{
return await Application.Current.Dispatcher.InvokeAsync(() => EnsureModelReadyAsync(progress)).Task.Unwrap();
}
try
{
Logger.LogInfo($"ImageScaler.EnsureReadyAsync() called on thread {Thread.CurrentThread.ManagedThreadId}");
var operation = ImageScaler.EnsureReadyAsync();
// Register progress handler if provided
@@ -83,10 +118,13 @@ namespace ImageResizer.Services
};
}
return await operation;
var result = await operation;
Logger.LogInfo($"ImageScaler.EnsureReadyAsync() completed: Status={result?.Status}, ExtendedError={result?.ExtendedError}");
return result;
}
catch (Exception)
catch (Exception ex)
{
Logger.LogError($"ImageScaler.EnsureReadyAsync() failed: {ex.Message}");
return null;
}
}

View File

@@ -402,6 +402,7 @@ namespace ImageResizer.ViewModels
});
// Call EnsureReadyAsync to download and prepare the AI model
// Note: WinAiSuperResolutionService handles dispatching to UI thread internally
var result = await WinAiSuperResolutionService.EnsureModelReadyAsync(progress);
if (result?.Status == Microsoft.Windows.AI.AIFeatureReadyResultState.Success)