mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-02-23 19:49:43 +01:00
fix(imageresizer): improve AI initialization and ensure UI thread handling
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user