Compare commits

...

3 Commits

5 changed files with 105 additions and 75 deletions

View File

@@ -79,7 +79,6 @@
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.6901" />
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.8.251106002" />
<PackageVersion Include="Microsoft.WindowsAppSDK.Foundation" Version="1.8.251104000" />
<PackageVersion Include="Microsoft.WindowsAppSDK.AI" Version="1.8.39" />
<PackageVersion Include="Microsoft.WindowsAppSDK.Runtime" Version="1.8.251106002" />
<PackageVersion Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="2.0.9" />
<PackageVersion Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" />

View File

@@ -84,7 +84,7 @@ namespace ImageResizer
return;
}
// AI Super Resolution is not supported on Windows 10 - skip cache check entirely
// AI Super Resolution is not supported on Windows 10 - skip check entirely
if (OSVersionHelper.IsWindows10())
{
AiAvailabilityState = AiAvailabilityState.NotSupported;
@@ -93,31 +93,9 @@ namespace ImageResizer
}
else
{
// Load AI availability from cache (written by Runner's background detection)
var cachedState = Services.AiAvailabilityCacheService.LoadCache();
if (cachedState.HasValue)
{
AiAvailabilityState = cachedState.Value;
Logger.LogInfo($"AI state loaded from cache: {AiAvailabilityState}");
}
else
{
// No valid cache - default to NotSupported (Runner will detect and cache for next startup)
AiAvailabilityState = AiAvailabilityState.NotSupported;
Logger.LogInfo("No AI cache found, defaulting to NotSupported");
}
// If AI is potentially available, start background initialization (non-blocking)
if (AiAvailabilityState == AiAvailabilityState.Ready)
{
_ = InitializeAiServiceAsync(); // Fire and forget - don't block UI
}
else
{
// AI not available - set NoOp service immediately
ResizeBatch.SetAiSuperResolutionService(Services.NoOpAiSuperResolutionService.Instance);
}
// Default to ModelNotReady initially, will be updated after window loads
AiAvailabilityState = AiAvailabilityState.ModelNotReady;
ResizeBatch.SetAiSuperResolutionService(Services.NoOpAiSuperResolutionService.Instance);
}
var batch = ResizeBatch.FromCommandLine(Console.In, e?.Args);
@@ -128,6 +106,61 @@ namespace ImageResizer
// Temporary workaround for issue #1273
WindowHelpers.BringToForeground(new System.Windows.Interop.WindowInteropHelper(mainWindow).Handle);
// Check AI availability after window is loaded
// WinAiSuperResolutionService handles UI thread dispatching internally
if (!OSVersionHelper.IsWindows10())
{
mainWindow.Loaded += async (s, args) =>
{
await InitializeAiAsync();
};
}
}
/// <summary>
/// Initialize AI availability check.
/// WinAiSuperResolutionService handles UI thread dispatching internally.
/// </summary>
private static async System.Threading.Tasks.Task InitializeAiAsync()
{
try
{
var readyState = Services.WinAiSuperResolutionService.GetModelReadyState();
switch (readyState)
{
case Microsoft.Windows.AI.AIFeatureReadyState.Ready:
AiAvailabilityState = AiAvailabilityState.Ready;
// Initialize the AI service
var aiService = await Services.WinAiSuperResolutionService.CreateAsync();
if (aiService != null)
{
ResizeBatch.SetAiSuperResolutionService(aiService);
}
break;
case Microsoft.Windows.AI.AIFeatureReadyState.NotReady:
AiAvailabilityState = AiAvailabilityState.ModelNotReady;
break;
case Microsoft.Windows.AI.AIFeatureReadyState.DisabledByUser:
case Microsoft.Windows.AI.AIFeatureReadyState.NotSupportedOnCurrentSystem:
default:
AiAvailabilityState = AiAvailabilityState.NotSupported;
break;
}
// Notify UI that AI state has been determined
AiInitializationCompleted?.Invoke(null, AiAvailabilityState);
}
catch (Exception ex)
{
Logger.LogError($"Failed to check AI availability: {ex.Message}");
AiAvailabilityState = AiAvailabilityState.NotSupported;
AiInitializationCompleted?.Invoke(null, AiAvailabilityState);
}
}
/// <summary>
@@ -200,46 +233,6 @@ namespace ImageResizer
}
}
/// <summary>
/// Initialize AI Super Resolution service asynchronously in background.
/// Runs without blocking UI startup - state change event notifies completion.
/// </summary>
private static async System.Threading.Tasks.Task InitializeAiServiceAsync()
{
AiAvailabilityState finalState;
try
{
// Create and initialize AI service using async factory
var aiService = await Services.WinAiSuperResolutionService.CreateAsync();
if (aiService != null)
{
ResizeBatch.SetAiSuperResolutionService(aiService);
Logger.LogInfo("AI Super Resolution service initialized successfully.");
finalState = AiAvailabilityState.Ready;
}
else
{
// Initialization failed - use default NoOp service
ResizeBatch.SetAiSuperResolutionService(Services.NoOpAiSuperResolutionService.Instance);
Logger.LogWarning("AI Super Resolution service initialization failed. Using default service.");
finalState = AiAvailabilityState.NotSupported;
}
}
catch (Exception ex)
{
// Log error and use default NoOp service
ResizeBatch.SetAiSuperResolutionService(Services.NoOpAiSuperResolutionService.Instance);
Logger.LogError($"Exception during AI service initialization: {ex.Message}");
finalState = AiAvailabilityState.NotSupported;
}
// Update cached state and notify listeners
AiAvailabilityState = finalState;
AiInitializationCompleted?.Invoke(null, finalState);
}
public void Dispose()
{
// Dispose AI Super Resolution service

View File

@@ -50,7 +50,6 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.WindowsAppSDK" />
<PackageReference Include="Microsoft.WindowsAppSDK.AI" />
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" />
<PackageReference Include="System.CommandLine" />
<PackageReference Include="System.IO.Abstractions" />

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)