From c557581ec7110c0559f4ad01566a1c7972969c53 Mon Sep 17 00:00:00 2001 From: "Milly Wei (from Dev Box)" Date: Wed, 29 Oct 2025 18:58:43 +0800 Subject: [PATCH 01/11] clean code --- .../Pages/Scenarios/ScenarioPage.xaml.cs | 11 +++- .../Samples/SharedCode/WinMLHelpers.cs | 52 +++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/AIDevGallery/Pages/Scenarios/ScenarioPage.xaml.cs b/AIDevGallery/Pages/Scenarios/ScenarioPage.xaml.cs index acbf21d4..b96b9886 100644 --- a/AIDevGallery/Pages/Scenarios/ScenarioPage.xaml.cs +++ b/AIDevGallery/Pages/Scenarios/ScenarioPage.xaml.cs @@ -169,7 +169,16 @@ private static async Task> GetSupportedHardwareAccelerators() break; case "DmlExecutionProvider": - supportedHardwareAccelerators.Add(new([HardwareAccelerator.DML, HardwareAccelerator.GPU], "DmlExecutionProvider", "DML", "GPU")); + // Test if DML is actually available before adding it to the list + // This prevents showing DML option on systems without compatible GPU + if (await WinMLHelpers.TestDmlAvailability()) + { + supportedHardwareAccelerators.Add(new([HardwareAccelerator.DML, HardwareAccelerator.GPU], "DmlExecutionProvider", "DML", "GPU")); + } + else + { + System.Diagnostics.Debug.WriteLine("[ScenarioPage] DML is reported by ONNX Runtime but not available. Skipping DML option."); + } break; case "NvTensorRTRTXExecutionProvider": diff --git a/AIDevGallery/Samples/SharedCode/WinMLHelpers.cs b/AIDevGallery/Samples/SharedCode/WinMLHelpers.cs index 5538b473..a1f71724 100644 --- a/AIDevGallery/Samples/SharedCode/WinMLHelpers.cs +++ b/AIDevGallery/Samples/SharedCode/WinMLHelpers.cs @@ -4,8 +4,10 @@ using Microsoft.ML.OnnxRuntime; using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; +using System.Threading.Tasks; namespace AIDevGallery.Samples.SharedCode; @@ -92,4 +94,54 @@ public static Dictionary> GetEpDeviceMap(OrtEnv? envir return epDeviceMap; } + + /// + /// Tests if DirectML execution provider is available on the system. + /// + /// True if DML is available, false otherwise. + public static async Task TestDmlAvailability() + { + try + { + await Task.Run(() => + { + // Create a minimal ONNX model in memory (identity operation) + byte[] minimalModel = new byte[] + { + 0x08, 0x07, 0x12, 0x07, 0x62, 0x61, 0x63, 0x6B, 0x65, 0x6E, 0x64, 0x1A, 0x0D, 0x62, 0x61, 0x63, 0x6B, + 0x65, 0x6E, 0x64, 0x2D, 0x74, 0x65, 0x73, 0x74, 0x22, 0x46, 0x0A, 0x08, 0x0A, 0x01, 0x78, 0x12, 0x01, + 0x79, 0x22, 0x08, 0x49, 0x64, 0x65, 0x6E, 0x74, 0x69, 0x74, 0x79, 0x2A, 0x14, 0x0A, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x12, 0x0C, 0x0A, 0x01, 0x69, 0x12, 0x07, 0x0A, 0x05, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x3A, + 0x18, 0x0A, 0x07, 0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x3A, 0x0D, 0x49, 0x64, 0x65, 0x6E, 0x74, + 0x69, 0x74, 0x79, 0x20, 0x74, 0x65, 0x73, 0x74, 0x0A, 0x16, 0x0A, 0x01, 0x78, 0x12, 0x11, 0x0A, 0x0F, + 0x08, 0x01, 0x12, 0x0B, 0x0A, 0x01, 0x4E, 0x0A, 0x01, 0x43, 0x0A, 0x01, 0x48, 0x0A, 0x01, 0x57, 0x0A, + 0x16, 0x0A, 0x01, 0x79, 0x12, 0x11, 0x0A, 0x0F, 0x08, 0x01, 0x12, 0x0B, 0x0A, 0x01, 0x4E, 0x0A, 0x01, + 0x43, 0x0A, 0x01, 0x48, 0x0A, 0x01, 0x57, 0x10, 0x01 + }; + + using var sessionOptions = new SessionOptions(); + sessionOptions.AppendExecutionProvider("DML"); + using var session = new InferenceSession(minimalModel, sessionOptions); + + Debug.WriteLine("[WinMLHelpers] DML is available and working."); + }); + + return true; + } + catch (OnnxRuntimeException ex) when (ex.Message.Contains("No devices detected")) + { + Debug.WriteLine("[WinMLHelpers] DirectML execution provider is not available on this system."); + Debug.WriteLine("[WinMLHelpers] This could be due to:"); + Debug.WriteLine("[WinMLHelpers] (1) No compatible GPU is present"); + Debug.WriteLine("[WinMLHelpers] (2) GPU drivers are outdated or not installed"); + Debug.WriteLine("[WinMLHelpers] (3) DirectX 12 is not supported"); + Debug.WriteLine($"[WinMLHelpers] Original error: {ex.Message}"); + return false; + } + catch (Exception ex) + { + Debug.WriteLine($"[WinMLHelpers] DML test failed: {ex.Message}"); + return false; + } + } } \ No newline at end of file From 385b5b4c082997249200517135973a661d9e0774 Mon Sep 17 00:00:00 2001 From: "Milly Wei (from Dev Box)" Date: Wed, 29 Oct 2025 21:12:15 +0800 Subject: [PATCH 02/11] update --- .../Pages/Scenarios/ScenarioPage.xaml | 1 + .../Pages/Scenarios/ScenarioPage.xaml.cs | 21 +++++++ .../Samples/SharedCode/WinMLHelpers.cs | 60 +++++++++++++++++++ 3 files changed, 82 insertions(+) diff --git a/AIDevGallery/Pages/Scenarios/ScenarioPage.xaml b/AIDevGallery/Pages/Scenarios/ScenarioPage.xaml index 6f604816..e36da8b4 100644 --- a/AIDevGallery/Pages/Scenarios/ScenarioPage.xaml +++ b/AIDevGallery/Pages/Scenarios/ScenarioPage.xaml @@ -222,6 +222,7 @@ diff --git a/AIDevGallery/Pages/Scenarios/ScenarioPage.xaml.cs b/AIDevGallery/Pages/Scenarios/ScenarioPage.xaml.cs index b96b9886..089b0723 100644 --- a/AIDevGallery/Pages/Scenarios/ScenarioPage.xaml.cs +++ b/AIDevGallery/Pages/Scenarios/ScenarioPage.xaml.cs @@ -179,6 +179,7 @@ private static async Task> GetSupportedHardwareAccelerators() { System.Diagnostics.Debug.WriteLine("[ScenarioPage] DML is reported by ONNX Runtime but not available. Skipping DML option."); } + break; case "NvTensorRTRTXExecutionProvider": @@ -305,6 +306,9 @@ private void UpdateWinMLFlyout() CompileModelCheckBox.IsChecked = options.CompileModel; WinMlModelOptionsButtonText.Text = (DeviceComboBox.SelectedItem as WinMlEp)?.ShortName; segmentedControl.SelectedIndex = 1; + + // Update compile model checkbox visibility based on selected EP + UpdateCompileModelVisibility(); } // in case already saved options do not apply to this sample @@ -475,5 +479,22 @@ private async Task UpdateSampleOptions() private void WinMLOptionsFlyout_Opening(object sender, object e) { UpdateWinMLFlyout(); + UpdateCompileModelVisibility(); + } + + private void DeviceComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + UpdateCompileModelVisibility(); + } + + private void UpdateCompileModelVisibility() + { + var device = DeviceComboBox.SelectedItem as WinMlEp; + bool supported = device != null && WinMLHelpers.IsCompileModelSupported(device.Name); + CompileModelCheckBox.Visibility = supported ? Visibility.Visible : Visibility.Collapsed; + if (!supported) + { + CompileModelCheckBox.IsChecked = false; + } } } \ No newline at end of file diff --git a/AIDevGallery/Samples/SharedCode/WinMLHelpers.cs b/AIDevGallery/Samples/SharedCode/WinMLHelpers.cs index a1f71724..5f17c9f6 100644 --- a/AIDevGallery/Samples/SharedCode/WinMLHelpers.cs +++ b/AIDevGallery/Samples/SharedCode/WinMLHelpers.cs @@ -55,6 +55,24 @@ public static bool AppendExecutionProviderFromEpName(this SessionOptions session public static string? GetCompiledModel(this SessionOptions sessionOptions, string modelPath, string device) { + // NOTE: Skip compilation for the CPU execution provider. + // Rationale: + // - EPContext is an EP-specific offline-compiled/partitioned graph artifact that requires the + // execution provider to implement serialization/deserialization of its optimized graph. + // - ONNX Runtime's CPU EP does NOT implement EPContext model generation or loading. Invoking + // OrtModelCompilationOptions.CompileModel() for CPU attempts to emit a "*.CPU.onnx" EPContext + // artifact, which fails (commonly with InvalidProtobuf) because no EPContext is produced/understood + // by the CPU EP. + // Behavior: + // - For CPU, we return null here so callers fall back to the original ONNX model without attempting + // EPContext compilation. + // - Other EPs (e.g., DirectML, OpenVINO, QNN) may support EPContext depending on the ORT build, + // platform drivers, and hardware; for those we allow compilation to proceed. + if (string.Equals(device, "CPU", StringComparison.OrdinalIgnoreCase)) + { + return null; + } + var compiledModelPath = Path.Combine(Path.GetDirectoryName(modelPath) ?? string.Empty, Path.GetFileNameWithoutExtension(modelPath)) + $".{device}.onnx"; if (!File.Exists(compiledModelPath)) @@ -95,6 +113,48 @@ public static Dictionary> GetEpDeviceMap(OrtEnv? envir return epDeviceMap; } + /// + /// Determines whether EPContext compilation is supported for the specified execution provider + /// in the current runtime environment. + /// + /// The execution provider name returned by ONNX Runtime (e.g., "DmlExecutionProvider"). + /// Optional ORT environment; if null, the singleton instance is used. + /// True if EPContext compilation is supported; otherwise, false. + public static bool IsCompileModelSupported(string? epName, OrtEnv? environment = null) + { + if (string.IsNullOrWhiteSpace(epName)) + { + return false; + } + + // CPU EP does not implement EPContext serialization/deserialization + if (string.Equals(epName, "CPU", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + // Known EPs that (in appropriate builds/drivers) can support EPContext compilation + // This is a conservative allow-list to avoid surfacing the option when unsupported. + HashSet knownSupportingEps = new(StringComparer.OrdinalIgnoreCase) + { + "DmlExecutionProvider", + "OpenVINOExecutionProvider", + "QNNExecutionProvider", + "VitisAIExecutionProvider", + "NvTensorRTRTXExecutionProvider", + }; + + if (!knownSupportingEps.Contains(epName)) + { + return false; + } + + // Verify the EP is present with at least one device on this machine + environment ??= OrtEnv.Instance(); + var epMap = GetEpDeviceMap(environment); + return epMap.TryGetValue(epName, out var devices) && devices.Count > 0; + } + /// /// Tests if DirectML execution provider is available on the system. /// From 4ae718a5b0c5a7634285c4b8e37cb37689f02df9 Mon Sep 17 00:00:00 2001 From: "Milly Wei (from Dev Box)" Date: Thu, 30 Oct 2025 16:19:37 +0800 Subject: [PATCH 03/11] update --- .../Samples/SharedCode/StableDiffusionCode/VaeDecoder.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AIDevGallery/Samples/SharedCode/StableDiffusionCode/VaeDecoder.cs b/AIDevGallery/Samples/SharedCode/StableDiffusionCode/VaeDecoder.cs index 5304bee6..88fe3b16 100644 --- a/AIDevGallery/Samples/SharedCode/StableDiffusionCode/VaeDecoder.cs +++ b/AIDevGallery/Samples/SharedCode/StableDiffusionCode/VaeDecoder.cs @@ -59,8 +59,8 @@ private Task GetInferenceSession(StableDiffusionConfig config, sessionOptions.AddFreeDimensionOverrideByName("batch", 1); sessionOptions.AddFreeDimensionOverrideByName("channels", 4); - sessionOptions.AddFreeDimensionOverrideByName("height", config.Height / 8); - sessionOptions.AddFreeDimensionOverrideByName("width", config.Width / 8); + //sessionOptions.AddFreeDimensionOverrideByName("height", config.Height / 8); + //sessionOptions.AddFreeDimensionOverrideByName("width", config.Width / 8); if (policy != null) { From 779c42658555fb6201db3fe1a028dbced691bd11 Mon Sep 17 00:00:00 2001 From: "Milly Wei (from Dev Box)" Date: Thu, 30 Oct 2025 18:54:45 +0800 Subject: [PATCH 04/11] update --- .../Samples/SharedCode/WinMLHelpers.cs | 38 ++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/AIDevGallery/Samples/SharedCode/WinMLHelpers.cs b/AIDevGallery/Samples/SharedCode/WinMLHelpers.cs index 5f17c9f6..8c002a45 100644 --- a/AIDevGallery/Samples/SharedCode/WinMLHelpers.cs +++ b/AIDevGallery/Samples/SharedCode/WinMLHelpers.cs @@ -77,15 +77,43 @@ public static bool AppendExecutionProviderFromEpName(this SessionOptions session if (!File.Exists(compiledModelPath)) { - using OrtModelCompilationOptions compilationOptions = new(sessionOptions); - compilationOptions.SetInputModelPath(modelPath); - compilationOptions.SetOutputModelPath(compiledModelPath); - compilationOptions.CompileModel(); + try + { + using OrtModelCompilationOptions compilationOptions = new(sessionOptions); + compilationOptions.SetInputModelPath(modelPath); + compilationOptions.SetOutputModelPath(compiledModelPath); + compilationOptions.CompileModel(); + } + catch (Exception ex) + { + Debug.WriteLine($"WARNING: Model compilation failed for {device}: {ex.Message}"); + + // Clean up any empty or corrupted files that may have been created + if (File.Exists(compiledModelPath)) + { + try + { + File.Delete(compiledModelPath); + Debug.WriteLine($"Deleted corrupted compiled model file: {compiledModelPath}"); + } + catch + { + // Ignore deletion errors + } + } + + return null; + } } + // Validate that the compiled model file exists and is not empty if (File.Exists(compiledModelPath)) { - return compiledModelPath; + var fileInfo = new FileInfo(compiledModelPath); + if (fileInfo.Length > 0) + { + return compiledModelPath; + } } return null; From c9bce068b729be89f2b8f41140b30a25a6baa1ed Mon Sep 17 00:00:00 2001 From: MillyWei Date: Thu, 30 Oct 2025 19:17:58 +0800 Subject: [PATCH 05/11] update --- .../Pages/Scenarios/ScenarioPage.xaml.cs | 996 +++++++++--------- .../Samples/SharedCode/WinMLHelpers.cs | 450 ++++---- 2 files changed, 713 insertions(+), 733 deletions(-) diff --git a/AIDevGallery/Pages/Scenarios/ScenarioPage.xaml.cs b/AIDevGallery/Pages/Scenarios/ScenarioPage.xaml.cs index 089b0723..05fd00fb 100644 --- a/AIDevGallery/Pages/Scenarios/ScenarioPage.xaml.cs +++ b/AIDevGallery/Pages/Scenarios/ScenarioPage.xaml.cs @@ -1,500 +1,498 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using AIDevGallery.Controls; -using AIDevGallery.Helpers; -using AIDevGallery.Models; -using AIDevGallery.ProjectGenerator; -using AIDevGallery.Samples; -using AIDevGallery.Samples.SharedCode; -using AIDevGallery.Telemetry.Events; -using AIDevGallery.Utils; -using Microsoft.ML.OnnxRuntime; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Navigation; -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Threading.Tasks; -using Windows.ApplicationModel.DataTransfer; - -namespace AIDevGallery.Pages; - -internal record WinMlEp(List HardwareAccelerators, string Name, string ShortName, string DeviceType); - -internal sealed partial class ScenarioPage : Page -{ - private readonly Dictionary executionProviderDevicePolicies = new() - { - { "Default", ExecutionProviderDevicePolicy.DEFAULT }, - { "Max Efficency", ExecutionProviderDevicePolicy.MAX_EFFICIENCY }, - { "Max Performance", ExecutionProviderDevicePolicy.MAX_PERFORMANCE }, - { "Minimize Overall Power", ExecutionProviderDevicePolicy.MIN_OVERALL_POWER }, - { "Prefer NPU", ExecutionProviderDevicePolicy.PREFER_NPU }, - { "Prefer GPU", ExecutionProviderDevicePolicy.PREFER_GPU }, - { "Prefer CPU", ExecutionProviderDevicePolicy.PREFER_CPU }, - }; - - private Scenario? scenario; - private List? samples; - private Sample? sample; - private ObservableCollection modelDetails = new(); - private static List? supportedHardwareAccelerators; - - public ScenarioPage() - { - this.InitializeComponent(); - this.Loaded += (s, e) => - BackgroundShadow.Receivers.Add(ShadowCastGrid); - App.MainWindow.ModelPicker.SelectedModelsChanged += ModelOrApiPicker_SelectedModelsChanged; - this.Unloaded += (s, e) => App.MainWindow.ModelPicker.SelectedModelsChanged -= ModelOrApiPicker_SelectedModelsChanged; - } - - protected override void OnNavigatedTo(NavigationEventArgs e) - { - VisualStateManager.GoToState(this, "PageLoading", true); - base.OnNavigatedTo(e); - _ = LoadPage(e.Parameter); - } - - private async Task LoadPage(object parameter) - { - if (parameter is Scenario scenario) - { - this.scenario = scenario; - await LoadPicker(); - } - else if (parameter is SampleNavigationArgs sampleArgs) - { - this.scenario = ScenarioCategoryHelpers.AllScenarioCategories.SelectMany(sc => sc.Scenarios).FirstOrDefault(s => s.ScenarioType == sampleArgs.Sample.Scenario); - await LoadPicker(sampleArgs.ModelDetails); - - if (sampleArgs.OpenCodeView.HasValue && sampleArgs.OpenCodeView.Value) - { - CodeToggle.IsChecked = true; - HandleCodePane(); - } - } - - samples = SampleDetails.Samples.Where(sample => sample.Scenario == this.scenario!.ScenarioType).ToList(); - } - - private async Task LoadPicker(ModelDetails? initialModelToLoad = null) - { - if (scenario == null) - { - return; - } - - samples = [.. SampleDetails.Samples.Where(sample => sample.Scenario == scenario.ScenarioType)]; - - if (samples.Count == 0) - { - return; - } - - List> modelDetailsList = [samples.SelectMany(s => s.Model1Types).ToList()]; - - // assume if first sample has two models, then all of them should need two models - if (samples[0].Model2Types != null) - { - modelDetailsList.Add(samples.SelectMany(s => s.Model2Types!).ToList()); - } - - var preSelectedModels = await App.MainWindow.ModelPicker.Load(modelDetailsList, initialModelToLoad); - HandleModelSelectionChanged(preSelectedModels); - - if (preSelectedModels.Contains(null) || preSelectedModels.Count == 0) - { - // user needs to select a model if one is not selected at first - App.MainWindow.ModelPicker.Show(preSelectedModels); - return; - } - } - - private static async Task> GetSupportedHardwareAccelerators() - { - if (supportedHardwareAccelerators != null) - { - return supportedHardwareAccelerators; - } - - OrtEnv.Instance(); - var catalog = Microsoft.Windows.AI.MachineLearning.ExecutionProviderCatalog.GetDefault(); - - try - { - var registeredProviders = await catalog.EnsureAndRegisterCertifiedAsync(); - } - catch (Exception) - { - } - - supportedHardwareAccelerators = [new([HardwareAccelerator.CPU], "CPU", "CPU", "CPU")]; - - foreach (var keyValuePair in WinMLHelpers.GetEpDeviceMap()) - { - var epName = keyValuePair.Key; - var epDevices = keyValuePair.Value; - var epDeviceTypes = epDevices.Select(d => d.HardwareDevice.Type.ToString()); - - switch (epName) - { - case "VitisAIExecutionProvider": - supportedHardwareAccelerators.Add(new([HardwareAccelerator.VitisAI, HardwareAccelerator.NPU], "VitisAIExecutionProvider", "VitisAI", "NPU")); - break; - - case "OpenVINOExecutionProvider": - if (epDeviceTypes.Contains("CPU")) - { - supportedHardwareAccelerators.Add(new([HardwareAccelerator.OpenVINO, HardwareAccelerator.CPU], "OpenVINOExecutionProvider", "OpenVINO", "CPU")); - } - - if (epDeviceTypes.Contains("GPU")) - { - supportedHardwareAccelerators.Add(new([HardwareAccelerator.OpenVINO, HardwareAccelerator.GPU], "OpenVINOExecutionProvider", "OpenVINO", "GPU")); - } - - if (epDeviceTypes.Contains("NPU")) - { - supportedHardwareAccelerators.Add(new([HardwareAccelerator.OpenVINO, HardwareAccelerator.NPU], "OpenVINOExecutionProvider", "OpenVINO", "NPU")); - } - - break; - - case "QNNExecutionProvider": - supportedHardwareAccelerators.Add(new([HardwareAccelerator.QNN, HardwareAccelerator.NPU], "QNNExecutionProvider", "QNN", "NPU")); - break; - - case "DmlExecutionProvider": - // Test if DML is actually available before adding it to the list - // This prevents showing DML option on systems without compatible GPU - if (await WinMLHelpers.TestDmlAvailability()) - { - supportedHardwareAccelerators.Add(new([HardwareAccelerator.DML, HardwareAccelerator.GPU], "DmlExecutionProvider", "DML", "GPU")); - } - else - { - System.Diagnostics.Debug.WriteLine("[ScenarioPage] DML is reported by ONNX Runtime but not available. Skipping DML option."); - } - - break; - - case "NvTensorRTRTXExecutionProvider": - supportedHardwareAccelerators.Add(new([HardwareAccelerator.NvTensorRT, HardwareAccelerator.GPU], "NvTensorRTRTXExecutionProvider", "NvTensorRT", "GPU")); - break; - } - } - - return supportedHardwareAccelerators; - } - - private async void HandleModelSelectionChanged(List selectedModels) - { - if (selectedModels.Contains(null) || selectedModels.Count == 0) - { - // user needs to select a model - VisualStateManager.GoToState(this, "NoModelSelected", true); - return; - } - - VisualStateManager.GoToState(this, "PageLoading", true); - - modelDetails.Clear(); - selectedModels.ForEach(modelDetails.Add); - - // temporary fix EP dropdown list for useradded local languagemodel - if (selectedModels.Any(m => m != null && m.IsOnnxModel() && string.IsNullOrEmpty(m.ParameterSize) && m.Id.StartsWith("useradded-local-languagemodel", System.StringComparison.InvariantCultureIgnoreCase) == false)) - { - var delayTask = Task.Delay(1000); - var supportedHardwareAcceleratorsTask = GetSupportedHardwareAccelerators(); - - if (await Task.WhenAny(delayTask, supportedHardwareAcceleratorsTask) == delayTask) - { - VisualStateManager.GoToState(this, "PageLoadingWithMessage", true); - } - - var supportedHardwareAccelerators = await supportedHardwareAcceleratorsTask; - - HashSet eps = [supportedHardwareAccelerators[0]]; - - DeviceComboBox.Items.Clear(); - - foreach (var hardwareAccelerator in selectedModels.SelectMany(m => m!.HardwareAccelerators).Distinct()) - { - foreach (var ep in supportedHardwareAccelerators.Where(ep => ep.HardwareAccelerators.Contains(hardwareAccelerator))) - { - eps.Add(ep); - } - } - - foreach (var ep in eps.OrderBy(ep => ep.Name)) - { - DeviceComboBox.Items.Add(ep); - } - - UpdateWinMLFlyout(); - - WinMlModelOptionsButton.Visibility = Visibility.Visible; - } - else - { - WinMlModelOptionsButton.Visibility = Visibility.Collapsed; - } - - if (selectedModels.Count == 1) - { - // add the second model with null - selectedModels = [selectedModels[0], null]; - } - - List viableSamples = samples!.Where(s => - IsModelFromTypes(s.Model1Types, selectedModels[0]) && - IsModelFromTypes(s.Model2Types, selectedModels[1])).ToList(); - - if (viableSamples.Count == 0) - { - // this should never happen - App.MainWindow.ModelPicker.Show(selectedModels); - return; - } - - if (viableSamples.Count > 1) - { - SampleSelection.Items.Clear(); - foreach (var sample in viableSamples) - { - SampleSelection.Items.Add(sample); - } - - SampleSelection.SelectedItem = viableSamples[0]; - SampleContainer.ShowFooter = true; - } - else - { - SampleContainer.ShowFooter = false; - LoadSample(viableSamples[0]); - } - } - - private void UpdateWinMLFlyout() - { - var options = App.AppData.WinMLSampleOptions; - if (options.Policy != null) - { - var key = executionProviderDevicePolicies.FirstOrDefault(kvp => kvp.Value == options.Policy).Key; - ExecutionPolicyComboBox.SelectedItem = key; - WinMlModelOptionsButtonText.Text = key; - DeviceComboBox.SelectedIndex = 0; - segmentedControl.SelectedIndex = 0; - } - else if (options.EpName != null) - { - var selectedDevice = DeviceComboBox.Items.Where(i => (i as WinMlEp)?.Name == options.EpName && (i as WinMlEp)?.DeviceType == options.DeviceType).FirstOrDefault(); - if (selectedDevice != null) - { - DeviceComboBox.SelectedItem = selectedDevice; - } - else - { - DeviceComboBox.SelectedIndex = 0; - } - - ExecutionPolicyComboBox.SelectedIndex = 0; - CompileModelCheckBox.IsChecked = options.CompileModel; - WinMlModelOptionsButtonText.Text = (DeviceComboBox.SelectedItem as WinMlEp)?.ShortName; - segmentedControl.SelectedIndex = 1; - - // Update compile model checkbox visibility based on selected EP - UpdateCompileModelVisibility(); - } - - // in case already saved options do not apply to this sample - _ = UpdateSampleOptions(); - } - - private void LoadSample(Sample? sampleToLoad) - { - sample = sampleToLoad; - - if (sample == null) - { - return; - } - - VisualStateManager.GoToState(this, "ModelSelected", true); - - // TODO: don't load sample if model is not cached, but still let code to be seen - // this would probably be handled in the SampleContainer - _ = SampleContainer.LoadSampleAsync(sample, [.. modelDetails], App.AppData.WinMLSampleOptions); - _ = App.AppData.AddMru( - new MostRecentlyUsedItem() - { - Type = MostRecentlyUsedItemType.Scenario, - ItemId = scenario!.Id, - Icon = scenario.Icon, - Description = scenario.Description, - SubItemId = modelDetails[0]!.Id, - DisplayName = scenario.Name - }, - modelDetails.Select(m => (m!.Id, m.HardwareAccelerators.First())).ToList()); - } - - private bool IsModelFromTypes(List? types, ModelDetails? model) - { - if (types == null && model == null) - { - return true; - } - - if (types == null || model == null) - { - return false; - } - - if (types.Contains(ModelType.LanguageModels) && model.IsLanguageModel()) - { - return true; - } - - List modelIds = []; - - foreach (var type in types) - { - modelIds.AddRange(ModelDetailsHelper.GetModelDetailsForModelType(type).Select(m => m.Id)); - if (App.AppData.TryGetUserAddedModelIds(type, out var ids)) - { - modelIds.AddRange(ids!); - } - } - - return modelIds.Any(id => id == model.Id); - } - - private void CopyButton_Click(object sender, RoutedEventArgs e) - { - var dataPackage = new DataPackage(); - dataPackage.SetText("aidevgallery://scenarios/" + scenario!.Id); - Clipboard.SetContentWithOptions(dataPackage, null); - } - - private void CodeToggle_Click(object sender, RoutedEventArgs args) - { - HandleCodePane(); - } - - private void HandleCodePane() - { - if (sample != null) - { - ToggleCodeButtonEvent.Log(sample.Name ?? string.Empty, CodeToggle.IsChecked == true); - } - - if (CodeToggle.IsChecked == true) - { - SampleContainer.ShowCode(); - } - else - { - SampleContainer.HideCode(); - } - } - - private void ExportSampleToggle_Click(object sender, RoutedEventArgs e) - { - if (sender is not Button button || sample == null) - { - return; - } - - _ = Generator.AskGenerateAndOpenAsync(sample, modelDetails.Where(m => m != null).Select(m => m!), App.AppData.WinMLSampleOptions, XamlRoot); - } - - private void ActionButtonsGrid_SizeChanged(object sender, SizeChangedEventArgs e) - { - // Calculate if the modelselectors collide with the export/code buttons - if ((ActionsButtonHolderPanel.ActualWidth + ButtonsPanel.ActualWidth) >= e.NewSize.Width) - { - VisualStateManager.GoToState(this, "NarrowLayout", true); - } - else - { - VisualStateManager.GoToState(this, "WideLayout", true); - } - } - - private void ModelBtn_Click(object sender, RoutedEventArgs e) - { - App.MainWindow.ModelPicker.Show(modelDetails.ToList()); - } - - private void ModelOrApiPicker_SelectedModelsChanged(object sender, List modelDetails) - { - HandleModelSelectionChanged(modelDetails); - } - - private void SampleSelection_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - var selectedSample = e.AddedItems - .OfType() - .ToList().FirstOrDefault(); - - LoadSample(selectedSample); - } - - private async void ApplySampleOptions(object sender, RoutedEventArgs e) - { - WinMLOptionsFlyout.Hide(); - await UpdateSampleOptions(); - LoadSample(sample); - } - - private async Task UpdateSampleOptions() - { - var oldOptions = App.AppData.WinMLSampleOptions; - - if (segmentedControl.SelectedIndex == 0) - { - var key = (ExecutionPolicyComboBox.SelectedItem as string) ?? executionProviderDevicePolicies.Keys.First(); - WinMlModelOptionsButtonText.Text = key; - App.AppData.WinMLSampleOptions = new WinMlSampleOptions(executionProviderDevicePolicies[key], null, false, null); - } - else - { - var device = (DeviceComboBox.SelectedItem as WinMlEp) ?? (DeviceComboBox.Items.First() as WinMlEp); - WinMlModelOptionsButtonText.Text = device!.ShortName; - App.AppData.WinMLSampleOptions = new WinMlSampleOptions(null, device.Name, CompileModelCheckBox.IsChecked!.Value, device.DeviceType); - } - - if (oldOptions == App.AppData.WinMLSampleOptions) - { - return; - } - - await App.AppData.SaveAsync(); - } - - private void WinMLOptionsFlyout_Opening(object sender, object e) - { - UpdateWinMLFlyout(); - UpdateCompileModelVisibility(); - } - - private void DeviceComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - UpdateCompileModelVisibility(); - } - - private void UpdateCompileModelVisibility() - { - var device = DeviceComboBox.SelectedItem as WinMlEp; - bool supported = device != null && WinMLHelpers.IsCompileModelSupported(device.Name); - CompileModelCheckBox.Visibility = supported ? Visibility.Visible : Visibility.Collapsed; - if (!supported) - { - CompileModelCheckBox.IsChecked = false; - } - } +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using AIDevGallery.Controls; +using AIDevGallery.Helpers; +using AIDevGallery.Models; +using AIDevGallery.ProjectGenerator; +using AIDevGallery.Samples; +using AIDevGallery.Samples.SharedCode; +using AIDevGallery.Telemetry.Events; +using AIDevGallery.Utils; +using Microsoft.ML.OnnxRuntime; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Navigation; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Threading.Tasks; +using Windows.ApplicationModel.DataTransfer; + +namespace AIDevGallery.Pages; + +internal record WinMlEp(List HardwareAccelerators, string Name, string ShortName, string DeviceType); + +internal sealed partial class ScenarioPage : Page +{ + private readonly Dictionary executionProviderDevicePolicies = new() + { + { "Default", ExecutionProviderDevicePolicy.DEFAULT }, + { "Max Efficency", ExecutionProviderDevicePolicy.MAX_EFFICIENCY }, + { "Max Performance", ExecutionProviderDevicePolicy.MAX_PERFORMANCE }, + { "Minimize Overall Power", ExecutionProviderDevicePolicy.MIN_OVERALL_POWER }, + { "Prefer NPU", ExecutionProviderDevicePolicy.PREFER_NPU }, + { "Prefer GPU", ExecutionProviderDevicePolicy.PREFER_GPU }, + { "Prefer CPU", ExecutionProviderDevicePolicy.PREFER_CPU }, + }; + + private Scenario? scenario; + private List? samples; + private Sample? sample; + private ObservableCollection modelDetails = new(); + private static List? supportedHardwareAccelerators; + + public ScenarioPage() + { + this.InitializeComponent(); + this.Loaded += (s, e) => + BackgroundShadow.Receivers.Add(ShadowCastGrid); + App.MainWindow.ModelPicker.SelectedModelsChanged += ModelOrApiPicker_SelectedModelsChanged; + this.Unloaded += (s, e) => App.MainWindow.ModelPicker.SelectedModelsChanged -= ModelOrApiPicker_SelectedModelsChanged; + } + + protected override void OnNavigatedTo(NavigationEventArgs e) + { + VisualStateManager.GoToState(this, "PageLoading", true); + base.OnNavigatedTo(e); + _ = LoadPage(e.Parameter); + } + + private async Task LoadPage(object parameter) + { + if (parameter is Scenario scenario) + { + this.scenario = scenario; + await LoadPicker(); + } + else if (parameter is SampleNavigationArgs sampleArgs) + { + this.scenario = ScenarioCategoryHelpers.AllScenarioCategories.SelectMany(sc => sc.Scenarios).FirstOrDefault(s => s.ScenarioType == sampleArgs.Sample.Scenario); + await LoadPicker(sampleArgs.ModelDetails); + + if (sampleArgs.OpenCodeView.HasValue && sampleArgs.OpenCodeView.Value) + { + CodeToggle.IsChecked = true; + HandleCodePane(); + } + } + + samples = SampleDetails.Samples.Where(sample => sample.Scenario == this.scenario!.ScenarioType).ToList(); + } + + private async Task LoadPicker(ModelDetails? initialModelToLoad = null) + { + if (scenario == null) + { + return; + } + + samples = [.. SampleDetails.Samples.Where(sample => sample.Scenario == scenario.ScenarioType)]; + + if (samples.Count == 0) + { + return; + } + + List> modelDetailsList = [samples.SelectMany(s => s.Model1Types).ToList()]; + + // assume if first sample has two models, then all of them should need two models + if (samples[0].Model2Types != null) + { + modelDetailsList.Add(samples.SelectMany(s => s.Model2Types!).ToList()); + } + + var preSelectedModels = await App.MainWindow.ModelPicker.Load(modelDetailsList, initialModelToLoad); + HandleModelSelectionChanged(preSelectedModels); + + if (preSelectedModels.Contains(null) || preSelectedModels.Count == 0) + { + // user needs to select a model if one is not selected at first + App.MainWindow.ModelPicker.Show(preSelectedModels); + return; + } + } + + private static async Task> GetSupportedHardwareAccelerators() + { + if (supportedHardwareAccelerators != null) + { + return supportedHardwareAccelerators; + } + + OrtEnv.Instance(); + var catalog = Microsoft.Windows.AI.MachineLearning.ExecutionProviderCatalog.GetDefault(); + + try + { + var registeredProviders = await catalog.EnsureAndRegisterCertifiedAsync(); + } + catch (Exception) + { + } + + supportedHardwareAccelerators = [new([HardwareAccelerator.CPU], "CPU", "CPU", "CPU")]; + + foreach (var keyValuePair in WinMLHelpers.GetEpDeviceMap()) + { + var epName = keyValuePair.Key; + var epDevices = keyValuePair.Value; + var epDeviceTypes = epDevices.Select(d => d.HardwareDevice.Type.ToString()); + + switch (epName) + { + case "VitisAIExecutionProvider": + supportedHardwareAccelerators.Add(new([HardwareAccelerator.VitisAI, HardwareAccelerator.NPU], "VitisAIExecutionProvider", "VitisAI", "NPU")); + break; + + case "OpenVINOExecutionProvider": + if (epDeviceTypes.Contains("CPU")) + { + supportedHardwareAccelerators.Add(new([HardwareAccelerator.OpenVINO, HardwareAccelerator.CPU], "OpenVINOExecutionProvider", "OpenVINO", "CPU")); + } + + if (epDeviceTypes.Contains("GPU")) + { + supportedHardwareAccelerators.Add(new([HardwareAccelerator.OpenVINO, HardwareAccelerator.GPU], "OpenVINOExecutionProvider", "OpenVINO", "GPU")); + } + + if (epDeviceTypes.Contains("NPU")) + { + supportedHardwareAccelerators.Add(new([HardwareAccelerator.OpenVINO, HardwareAccelerator.NPU], "OpenVINOExecutionProvider", "OpenVINO", "NPU")); + } + + break; + + case "QNNExecutionProvider": + supportedHardwareAccelerators.Add(new([HardwareAccelerator.QNN, HardwareAccelerator.NPU], "QNNExecutionProvider", "QNN", "NPU")); + break; + + case "DmlExecutionProvider": + // Test if DML is actually available before adding it to the list + // This prevents showing DML option on systems without compatible GPU + if (await WinMLHelpers.TestDmlAvailability()) + { + supportedHardwareAccelerators.Add(new([HardwareAccelerator.DML, HardwareAccelerator.GPU], "DmlExecutionProvider", "DML", "GPU")); + } + else + { + System.Diagnostics.Debug.WriteLine("[ScenarioPage] DML is reported by ONNX Runtime but not available. Skipping DML option."); + } + + break; + + case "NvTensorRTRTXExecutionProvider": + supportedHardwareAccelerators.Add(new([HardwareAccelerator.NvTensorRT, HardwareAccelerator.GPU], "NvTensorRTRTXExecutionProvider", "NvTensorRT", "GPU")); + break; + } + } + + return supportedHardwareAccelerators; + } + + private async void HandleModelSelectionChanged(List selectedModels) + { + if (selectedModels.Contains(null) || selectedModels.Count == 0) + { + // user needs to select a model + VisualStateManager.GoToState(this, "NoModelSelected", true); + return; + } + + VisualStateManager.GoToState(this, "PageLoading", true); + + modelDetails.Clear(); + selectedModels.ForEach(modelDetails.Add); + + // temporary fix EP dropdown list for useradded local languagemodel + if (selectedModels.Any(m => m != null && m.IsOnnxModel() && string.IsNullOrEmpty(m.ParameterSize) && m.Id.StartsWith("useradded-local-languagemodel", System.StringComparison.InvariantCultureIgnoreCase) == false)) + { + var delayTask = Task.Delay(1000); + var supportedHardwareAcceleratorsTask = GetSupportedHardwareAccelerators(); + + if (await Task.WhenAny(delayTask, supportedHardwareAcceleratorsTask) == delayTask) + { + VisualStateManager.GoToState(this, "PageLoadingWithMessage", true); + } + + var supportedHardwareAccelerators = await supportedHardwareAcceleratorsTask; + + HashSet eps = [supportedHardwareAccelerators[0]]; + + DeviceComboBox.Items.Clear(); + + foreach (var hardwareAccelerator in selectedModels.SelectMany(m => m!.HardwareAccelerators).Distinct()) + { + foreach (var ep in supportedHardwareAccelerators.Where(ep => ep.HardwareAccelerators.Contains(hardwareAccelerator))) + { + eps.Add(ep); + } + } + + foreach (var ep in eps.OrderBy(ep => ep.Name)) + { + DeviceComboBox.Items.Add(ep); + } + + UpdateWinMLFlyout(); + + WinMlModelOptionsButton.Visibility = Visibility.Visible; + } + else + { + WinMlModelOptionsButton.Visibility = Visibility.Collapsed; + } + + if (selectedModels.Count == 1) + { + // add the second model with null + selectedModels = [selectedModels[0], null]; + } + + List viableSamples = samples!.Where(s => + IsModelFromTypes(s.Model1Types, selectedModels[0]) && + IsModelFromTypes(s.Model2Types, selectedModels[1])).ToList(); + + if (viableSamples.Count == 0) + { + // this should never happen + App.MainWindow.ModelPicker.Show(selectedModels); + return; + } + + if (viableSamples.Count > 1) + { + SampleSelection.Items.Clear(); + foreach (var sample in viableSamples) + { + SampleSelection.Items.Add(sample); + } + + SampleSelection.SelectedItem = viableSamples[0]; + SampleContainer.ShowFooter = true; + } + else + { + SampleContainer.ShowFooter = false; + LoadSample(viableSamples[0]); + } + } + + private void UpdateWinMLFlyout() + { + var options = App.AppData.WinMLSampleOptions; + if (options.Policy != null) + { + var key = executionProviderDevicePolicies.FirstOrDefault(kvp => kvp.Value == options.Policy).Key; + ExecutionPolicyComboBox.SelectedItem = key; + WinMlModelOptionsButtonText.Text = key; + DeviceComboBox.SelectedIndex = 0; + segmentedControl.SelectedIndex = 0; + } + else if (options.EpName != null) + { + var selectedDevice = DeviceComboBox.Items.Where(i => (i as WinMlEp)?.Name == options.EpName && (i as WinMlEp)?.DeviceType == options.DeviceType).FirstOrDefault(); + if (selectedDevice != null) + { + DeviceComboBox.SelectedItem = selectedDevice; + } + else + { + DeviceComboBox.SelectedIndex = 0; + } + + ExecutionPolicyComboBox.SelectedIndex = 0; + CompileModelCheckBox.IsChecked = options.CompileModel; + WinMlModelOptionsButtonText.Text = (DeviceComboBox.SelectedItem as WinMlEp)?.ShortName; + segmentedControl.SelectedIndex = 1; + UpdateCompileModelVisibility(); + } + + // in case already saved options do not apply to this sample + _ = UpdateSampleOptions(); + } + + private void LoadSample(Sample? sampleToLoad) + { + sample = sampleToLoad; + + if (sample == null) + { + return; + } + + VisualStateManager.GoToState(this, "ModelSelected", true); + + // TODO: don't load sample if model is not cached, but still let code to be seen + // this would probably be handled in the SampleContainer + _ = SampleContainer.LoadSampleAsync(sample, [.. modelDetails], App.AppData.WinMLSampleOptions); + _ = App.AppData.AddMru( + new MostRecentlyUsedItem() + { + Type = MostRecentlyUsedItemType.Scenario, + ItemId = scenario!.Id, + Icon = scenario.Icon, + Description = scenario.Description, + SubItemId = modelDetails[0]!.Id, + DisplayName = scenario.Name + }, + modelDetails.Select(m => (m!.Id, m.HardwareAccelerators.First())).ToList()); + } + + private bool IsModelFromTypes(List? types, ModelDetails? model) + { + if (types == null && model == null) + { + return true; + } + + if (types == null || model == null) + { + return false; + } + + if (types.Contains(ModelType.LanguageModels) && model.IsLanguageModel()) + { + return true; + } + + List modelIds = []; + + foreach (var type in types) + { + modelIds.AddRange(ModelDetailsHelper.GetModelDetailsForModelType(type).Select(m => m.Id)); + if (App.AppData.TryGetUserAddedModelIds(type, out var ids)) + { + modelIds.AddRange(ids!); + } + } + + return modelIds.Any(id => id == model.Id); + } + + private void CopyButton_Click(object sender, RoutedEventArgs e) + { + var dataPackage = new DataPackage(); + dataPackage.SetText("aidevgallery://scenarios/" + scenario!.Id); + Clipboard.SetContentWithOptions(dataPackage, null); + } + + private void CodeToggle_Click(object sender, RoutedEventArgs args) + { + HandleCodePane(); + } + + private void HandleCodePane() + { + if (sample != null) + { + ToggleCodeButtonEvent.Log(sample.Name ?? string.Empty, CodeToggle.IsChecked == true); + } + + if (CodeToggle.IsChecked == true) + { + SampleContainer.ShowCode(); + } + else + { + SampleContainer.HideCode(); + } + } + + private void ExportSampleToggle_Click(object sender, RoutedEventArgs e) + { + if (sender is not Button button || sample == null) + { + return; + } + + _ = Generator.AskGenerateAndOpenAsync(sample, modelDetails.Where(m => m != null).Select(m => m!), App.AppData.WinMLSampleOptions, XamlRoot); + } + + private void ActionButtonsGrid_SizeChanged(object sender, SizeChangedEventArgs e) + { + // Calculate if the modelselectors collide with the export/code buttons + if ((ActionsButtonHolderPanel.ActualWidth + ButtonsPanel.ActualWidth) >= e.NewSize.Width) + { + VisualStateManager.GoToState(this, "NarrowLayout", true); + } + else + { + VisualStateManager.GoToState(this, "WideLayout", true); + } + } + + private void ModelBtn_Click(object sender, RoutedEventArgs e) + { + App.MainWindow.ModelPicker.Show(modelDetails.ToList()); + } + + private void ModelOrApiPicker_SelectedModelsChanged(object sender, List modelDetails) + { + HandleModelSelectionChanged(modelDetails); + } + + private void SampleSelection_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + var selectedSample = e.AddedItems + .OfType() + .ToList().FirstOrDefault(); + + LoadSample(selectedSample); + } + + private async void ApplySampleOptions(object sender, RoutedEventArgs e) + { + WinMLOptionsFlyout.Hide(); + await UpdateSampleOptions(); + LoadSample(sample); + } + + private async Task UpdateSampleOptions() + { + var oldOptions = App.AppData.WinMLSampleOptions; + + if (segmentedControl.SelectedIndex == 0) + { + var key = (ExecutionPolicyComboBox.SelectedItem as string) ?? executionProviderDevicePolicies.Keys.First(); + WinMlModelOptionsButtonText.Text = key; + App.AppData.WinMLSampleOptions = new WinMlSampleOptions(executionProviderDevicePolicies[key], null, false, null); + } + else + { + var device = (DeviceComboBox.SelectedItem as WinMlEp) ?? (DeviceComboBox.Items.First() as WinMlEp); + WinMlModelOptionsButtonText.Text = device!.ShortName; + App.AppData.WinMLSampleOptions = new WinMlSampleOptions(null, device.Name, CompileModelCheckBox.IsChecked!.Value, device.DeviceType); + } + + if (oldOptions == App.AppData.WinMLSampleOptions) + { + return; + } + + await App.AppData.SaveAsync(); + } + + private void WinMLOptionsFlyout_Opening(object sender, object e) + { + UpdateWinMLFlyout(); + UpdateCompileModelVisibility(); + } + + private void DeviceComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + UpdateCompileModelVisibility(); + } + + private void UpdateCompileModelVisibility() + { + var device = DeviceComboBox.SelectedItem as WinMlEp; + bool supported = device != null && WinMLHelpers.IsCompileModelSupported(device.DeviceType); + CompileModelCheckBox.Visibility = supported ? Visibility.Visible : Visibility.Collapsed; + if (!supported) + { + CompileModelCheckBox.IsChecked = false; + } + } } \ No newline at end of file diff --git a/AIDevGallery/Samples/SharedCode/WinMLHelpers.cs b/AIDevGallery/Samples/SharedCode/WinMLHelpers.cs index 8c002a45..b25a8583 100644 --- a/AIDevGallery/Samples/SharedCode/WinMLHelpers.cs +++ b/AIDevGallery/Samples/SharedCode/WinMLHelpers.cs @@ -1,235 +1,217 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using Microsoft.ML.OnnxRuntime; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Threading.Tasks; - -namespace AIDevGallery.Samples.SharedCode; - -internal static class WinMLHelpers -{ - public static bool AppendExecutionProviderFromEpName(this SessionOptions sessionOptions, string epName, string? deviceType, OrtEnv? environment = null) - { - if (epName == "CPU") - { - // No need to append CPU execution provider - return true; - } - - environment ??= OrtEnv.Instance(); - var epDeviceMap = GetEpDeviceMap(environment); - - if (epDeviceMap.TryGetValue(epName, out var devices)) - { - Dictionary epOptions = new(StringComparer.OrdinalIgnoreCase); - switch (epName) - { - case "DmlExecutionProvider": - // Configure performance mode for Dml EP - // Dml some times have multiple devices which cause exception, we pick the first one here - sessionOptions.AppendExecutionProvider(environment, [devices[0]], epOptions); - return true; - case "OpenVINOExecutionProvider": - var device = devices.Where(d => d.HardwareDevice.Type.ToString().Equals(deviceType, StringComparison.Ordinal)).FirstOrDefault(); - sessionOptions.AppendExecutionProvider(environment, [device], epOptions); - return true; - case "QNNExecutionProvider": - // Configure performance mode for QNN EP - epOptions["htp_performance_mode"] = "high_performance"; - break; - default: - break; - } - - sessionOptions.AppendExecutionProvider(environment, devices, epOptions); - return true; - } - - return false; - } - - public static string? GetCompiledModel(this SessionOptions sessionOptions, string modelPath, string device) - { - // NOTE: Skip compilation for the CPU execution provider. - // Rationale: - // - EPContext is an EP-specific offline-compiled/partitioned graph artifact that requires the - // execution provider to implement serialization/deserialization of its optimized graph. - // - ONNX Runtime's CPU EP does NOT implement EPContext model generation or loading. Invoking - // OrtModelCompilationOptions.CompileModel() for CPU attempts to emit a "*.CPU.onnx" EPContext - // artifact, which fails (commonly with InvalidProtobuf) because no EPContext is produced/understood - // by the CPU EP. - // Behavior: - // - For CPU, we return null here so callers fall back to the original ONNX model without attempting - // EPContext compilation. - // - Other EPs (e.g., DirectML, OpenVINO, QNN) may support EPContext depending on the ORT build, - // platform drivers, and hardware; for those we allow compilation to proceed. - if (string.Equals(device, "CPU", StringComparison.OrdinalIgnoreCase)) - { - return null; - } - - var compiledModelPath = Path.Combine(Path.GetDirectoryName(modelPath) ?? string.Empty, Path.GetFileNameWithoutExtension(modelPath)) + $".{device}.onnx"; - - if (!File.Exists(compiledModelPath)) - { - try - { - using OrtModelCompilationOptions compilationOptions = new(sessionOptions); - compilationOptions.SetInputModelPath(modelPath); - compilationOptions.SetOutputModelPath(compiledModelPath); - compilationOptions.CompileModel(); - } - catch (Exception ex) - { - Debug.WriteLine($"WARNING: Model compilation failed for {device}: {ex.Message}"); - - // Clean up any empty or corrupted files that may have been created - if (File.Exists(compiledModelPath)) - { - try - { - File.Delete(compiledModelPath); - Debug.WriteLine($"Deleted corrupted compiled model file: {compiledModelPath}"); - } - catch - { - // Ignore deletion errors - } - } - - return null; - } - } - - // Validate that the compiled model file exists and is not empty - if (File.Exists(compiledModelPath)) - { - var fileInfo = new FileInfo(compiledModelPath); - if (fileInfo.Length > 0) - { - return compiledModelPath; - } - } - - return null; - } - - public static Dictionary> GetEpDeviceMap(OrtEnv? environment = null) - { - environment ??= OrtEnv.Instance(); - IReadOnlyList epDevices = environment.GetEpDevices(); - Dictionary> epDeviceMap = new(StringComparer.OrdinalIgnoreCase); - - foreach (OrtEpDevice device in epDevices) - { - string name = device.EpName; - - if (!epDeviceMap.TryGetValue(name, out List? value)) - { - value = []; - epDeviceMap[name] = value; - } - - value.Add(device); - } - - return epDeviceMap; - } - - /// - /// Determines whether EPContext compilation is supported for the specified execution provider - /// in the current runtime environment. - /// - /// The execution provider name returned by ONNX Runtime (e.g., "DmlExecutionProvider"). - /// Optional ORT environment; if null, the singleton instance is used. - /// True if EPContext compilation is supported; otherwise, false. - public static bool IsCompileModelSupported(string? epName, OrtEnv? environment = null) - { - if (string.IsNullOrWhiteSpace(epName)) - { - return false; - } - - // CPU EP does not implement EPContext serialization/deserialization - if (string.Equals(epName, "CPU", StringComparison.OrdinalIgnoreCase)) - { - return false; - } - - // Known EPs that (in appropriate builds/drivers) can support EPContext compilation - // This is a conservative allow-list to avoid surfacing the option when unsupported. - HashSet knownSupportingEps = new(StringComparer.OrdinalIgnoreCase) - { - "DmlExecutionProvider", - "OpenVINOExecutionProvider", - "QNNExecutionProvider", - "VitisAIExecutionProvider", - "NvTensorRTRTXExecutionProvider", - }; - - if (!knownSupportingEps.Contains(epName)) - { - return false; - } - - // Verify the EP is present with at least one device on this machine - environment ??= OrtEnv.Instance(); - var epMap = GetEpDeviceMap(environment); - return epMap.TryGetValue(epName, out var devices) && devices.Count > 0; - } - - /// - /// Tests if DirectML execution provider is available on the system. - /// - /// True if DML is available, false otherwise. - public static async Task TestDmlAvailability() - { - try - { - await Task.Run(() => - { - // Create a minimal ONNX model in memory (identity operation) - byte[] minimalModel = new byte[] - { - 0x08, 0x07, 0x12, 0x07, 0x62, 0x61, 0x63, 0x6B, 0x65, 0x6E, 0x64, 0x1A, 0x0D, 0x62, 0x61, 0x63, 0x6B, - 0x65, 0x6E, 0x64, 0x2D, 0x74, 0x65, 0x73, 0x74, 0x22, 0x46, 0x0A, 0x08, 0x0A, 0x01, 0x78, 0x12, 0x01, - 0x79, 0x22, 0x08, 0x49, 0x64, 0x65, 0x6E, 0x74, 0x69, 0x74, 0x79, 0x2A, 0x14, 0x0A, 0x04, 0x74, 0x79, - 0x70, 0x65, 0x12, 0x0C, 0x0A, 0x01, 0x69, 0x12, 0x07, 0x0A, 0x05, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x3A, - 0x18, 0x0A, 0x07, 0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x3A, 0x0D, 0x49, 0x64, 0x65, 0x6E, 0x74, - 0x69, 0x74, 0x79, 0x20, 0x74, 0x65, 0x73, 0x74, 0x0A, 0x16, 0x0A, 0x01, 0x78, 0x12, 0x11, 0x0A, 0x0F, - 0x08, 0x01, 0x12, 0x0B, 0x0A, 0x01, 0x4E, 0x0A, 0x01, 0x43, 0x0A, 0x01, 0x48, 0x0A, 0x01, 0x57, 0x0A, - 0x16, 0x0A, 0x01, 0x79, 0x12, 0x11, 0x0A, 0x0F, 0x08, 0x01, 0x12, 0x0B, 0x0A, 0x01, 0x4E, 0x0A, 0x01, - 0x43, 0x0A, 0x01, 0x48, 0x0A, 0x01, 0x57, 0x10, 0x01 - }; - - using var sessionOptions = new SessionOptions(); - sessionOptions.AppendExecutionProvider("DML"); - using var session = new InferenceSession(minimalModel, sessionOptions); - - Debug.WriteLine("[WinMLHelpers] DML is available and working."); - }); - - return true; - } - catch (OnnxRuntimeException ex) when (ex.Message.Contains("No devices detected")) - { - Debug.WriteLine("[WinMLHelpers] DirectML execution provider is not available on this system."); - Debug.WriteLine("[WinMLHelpers] This could be due to:"); - Debug.WriteLine("[WinMLHelpers] (1) No compatible GPU is present"); - Debug.WriteLine("[WinMLHelpers] (2) GPU drivers are outdated or not installed"); - Debug.WriteLine("[WinMLHelpers] (3) DirectX 12 is not supported"); - Debug.WriteLine($"[WinMLHelpers] Original error: {ex.Message}"); - return false; - } - catch (Exception ex) - { - Debug.WriteLine($"[WinMLHelpers] DML test failed: {ex.Message}"); - return false; - } - } +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.ML.OnnxRuntime; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace AIDevGallery.Samples.SharedCode; + +internal static class WinMLHelpers +{ + public static bool AppendExecutionProviderFromEpName(this SessionOptions sessionOptions, string epName, string? deviceType, OrtEnv? environment = null) + { + if (epName == "CPU") + { + // No need to append CPU execution provider + return true; + } + + environment ??= OrtEnv.Instance(); + var epDeviceMap = GetEpDeviceMap(environment); + + if (epDeviceMap.TryGetValue(epName, out var devices)) + { + Dictionary epOptions = new(StringComparer.OrdinalIgnoreCase); + switch (epName) + { + case "DmlExecutionProvider": + // Configure performance mode for Dml EP + // Dml some times have multiple devices which cause exception, we pick the first one here + sessionOptions.AppendExecutionProvider(environment, [devices[0]], epOptions); + return true; + case "OpenVINOExecutionProvider": + var device = devices.Where(d => d.HardwareDevice.Type.ToString().Equals(deviceType, StringComparison.Ordinal)).FirstOrDefault(); + sessionOptions.AppendExecutionProvider(environment, [device], epOptions); + return true; + case "QNNExecutionProvider": + // Configure performance mode for QNN EP + epOptions["htp_performance_mode"] = "high_performance"; + break; + default: + break; + } + + sessionOptions.AppendExecutionProvider(environment, devices, epOptions); + return true; + } + + return false; + } + + public static string? GetCompiledModel(this SessionOptions sessionOptions, string modelPath, string device) + { + // NOTE: Skip compilation for the CPU execution provider. + // Rationale: + // - EPContext is an EP-specific offline-compiled/partitioned graph artifact that requires the + // execution provider to implement serialization/deserialization of its optimized graph. + // - ONNX Runtime's CPU EP does NOT implement EPContext model generation or loading. Invoking + // OrtModelCompilationOptions.CompileModel() for CPU attempts to emit a "*.CPU.onnx" EPContext + // artifact, which fails (commonly with InvalidProtobuf) because no EPContext is produced/understood + // by the CPU EP. + // Behavior: + // - For CPU, we return null here so callers fall back to the original ONNX model without attempting + // EPContext compilation. + // - Other EPs (e.g., DirectML, OpenVINO, QNN) may support EPContext depending on the ORT build, + // platform drivers, and hardware; for those we allow compilation to proceed. + if (string.Equals(device, "CPU", StringComparison.OrdinalIgnoreCase)) + { + return null; + } + + var compiledModelPath = Path.Combine(Path.GetDirectoryName(modelPath) ?? string.Empty, Path.GetFileNameWithoutExtension(modelPath)) + $".{device}.onnx"; + + if (!File.Exists(compiledModelPath)) + { + try + { + using OrtModelCompilationOptions compilationOptions = new(sessionOptions); + compilationOptions.SetInputModelPath(modelPath); + compilationOptions.SetOutputModelPath(compiledModelPath); + compilationOptions.CompileModel(); + } + catch (Exception ex) + { + Debug.WriteLine($"WARNING: Model compilation failed for {device}: {ex.Message}"); + + // Clean up any empty or corrupted files that may have been created + if (File.Exists(compiledModelPath)) + { + try + { + File.Delete(compiledModelPath); + Debug.WriteLine($"Deleted corrupted compiled model file: {compiledModelPath}"); + } + catch + { + // Ignore deletion errors + } + } + + return null; + } + } + + // Validate that the compiled model file exists and is not empty + if (File.Exists(compiledModelPath)) + { + var fileInfo = new FileInfo(compiledModelPath); + if (fileInfo.Length > 0) + { + return compiledModelPath; + } + } + + return null; + } + + public static Dictionary> GetEpDeviceMap(OrtEnv? environment = null) + { + environment ??= OrtEnv.Instance(); + IReadOnlyList epDevices = environment.GetEpDevices(); + Dictionary> epDeviceMap = new(StringComparer.OrdinalIgnoreCase); + + foreach (OrtEpDevice device in epDevices) + { + string name = device.EpName; + + if (!epDeviceMap.TryGetValue(name, out List? value)) + { + value = []; + epDeviceMap[name] = value; + } + + value.Add(device); + } + + return epDeviceMap; + } + + /// + /// Determines whether model compilation should be surfaced based on device type. + /// + /// Device type string (e.g., "CPU", "GPU", "NPU"). + /// Unused; kept for signature stability if needed later. + /// False for CPU; true for other known accelerator types. + public static bool IsCompileModelSupported(string? deviceType, OrtEnv? environment = null) + { + if (string.IsNullOrWhiteSpace(deviceType)) + { + return false; + } + + // Do not allow compilation for CPU + if (string.Equals(deviceType, "CPU", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + // Allow for accelerators (GPU/NPU). Additional filtering can be added if needed. + return string.Equals(deviceType, "GPU", StringComparison.OrdinalIgnoreCase) + || string.Equals(deviceType, "NPU", StringComparison.OrdinalIgnoreCase); + } + + /// + /// Tests if DirectML execution provider is available on the system. + /// + /// True if DML is available, false otherwise. + public static async Task TestDmlAvailability() + { + try + { + await Task.Run(() => + { + // Create a minimal ONNX model in memory (identity operation) + byte[] minimalModel = new byte[] + { + 0x08, 0x07, 0x12, 0x07, 0x62, 0x61, 0x63, 0x6B, 0x65, 0x6E, 0x64, 0x1A, 0x0D, 0x62, 0x61, 0x63, 0x6B, + 0x65, 0x6E, 0x64, 0x2D, 0x74, 0x65, 0x73, 0x74, 0x22, 0x46, 0x0A, 0x08, 0x0A, 0x01, 0x78, 0x12, 0x01, + 0x79, 0x22, 0x08, 0x49, 0x64, 0x65, 0x6E, 0x74, 0x69, 0x74, 0x79, 0x2A, 0x14, 0x0A, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x12, 0x0C, 0x0A, 0x01, 0x69, 0x12, 0x07, 0x0A, 0x05, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x3A, + 0x18, 0x0A, 0x07, 0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x3A, 0x0D, 0x49, 0x64, 0x65, 0x6E, 0x74, + 0x69, 0x74, 0x79, 0x20, 0x74, 0x65, 0x73, 0x74, 0x0A, 0x16, 0x0A, 0x01, 0x78, 0x12, 0x11, 0x0A, 0x0F, + 0x08, 0x01, 0x12, 0x0B, 0x0A, 0x01, 0x4E, 0x0A, 0x01, 0x43, 0x0A, 0x01, 0x48, 0x0A, 0x01, 0x57, 0x0A, + 0x16, 0x0A, 0x01, 0x79, 0x12, 0x11, 0x0A, 0x0F, 0x08, 0x01, 0x12, 0x0B, 0x0A, 0x01, 0x4E, 0x0A, 0x01, + 0x43, 0x0A, 0x01, 0x48, 0x0A, 0x01, 0x57, 0x10, 0x01 + }; + + using var sessionOptions = new SessionOptions(); + sessionOptions.AppendExecutionProvider("DML"); + using var session = new InferenceSession(minimalModel, sessionOptions); + + Debug.WriteLine("[WinMLHelpers] DML is available and working."); + }); + + return true; + } + catch (OnnxRuntimeException ex) when (ex.Message.Contains("No devices detected")) + { + Debug.WriteLine("[WinMLHelpers] DirectML execution provider is not available on this system."); + Debug.WriteLine("[WinMLHelpers] This could be due to:"); + Debug.WriteLine("[WinMLHelpers] (1) No compatible GPU is present"); + Debug.WriteLine("[WinMLHelpers] (2) GPU drivers are outdated or not installed"); + Debug.WriteLine("[WinMLHelpers] (3) DirectX 12 is not supported"); + Debug.WriteLine($"[WinMLHelpers] Original error: {ex.Message}"); + return false; + } + catch (Exception ex) + { + Debug.WriteLine($"[WinMLHelpers] DML test failed: {ex.Message}"); + return false; + } + } } \ No newline at end of file From 779c0062f8cec29a86679cb003f4cb3162686381 Mon Sep 17 00:00:00 2001 From: MillyWei Date: Thu, 30 Oct 2025 19:18:21 +0800 Subject: [PATCH 06/11] update --- AIDevGallery/Samples/SharedCode/WinMLHelpers.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/AIDevGallery/Samples/SharedCode/WinMLHelpers.cs b/AIDevGallery/Samples/SharedCode/WinMLHelpers.cs index b25a8583..07709687 100644 --- a/AIDevGallery/Samples/SharedCode/WinMLHelpers.cs +++ b/AIDevGallery/Samples/SharedCode/WinMLHelpers.cs @@ -154,13 +154,11 @@ public static bool IsCompileModelSupported(string? deviceType, OrtEnv? environme return false; } - // Do not allow compilation for CPU if (string.Equals(deviceType, "CPU", StringComparison.OrdinalIgnoreCase)) { return false; } - // Allow for accelerators (GPU/NPU). Additional filtering can be added if needed. return string.Equals(deviceType, "GPU", StringComparison.OrdinalIgnoreCase) || string.Equals(deviceType, "NPU", StringComparison.OrdinalIgnoreCase); } From 4d55afc7b7aa286b618729e773b4cedfcdb3952f Mon Sep 17 00:00:00 2001 From: "Milly Wei (from Dev Box)" Date: Wed, 5 Nov 2025 14:10:01 +0800 Subject: [PATCH 07/11] update --- .../Pages/Scenarios/ScenarioPage.xaml.cs | 8 +-- .../Samples/SharedCode/WinMLHelpers.cs | 49 ------------------- 2 files changed, 1 insertion(+), 56 deletions(-) diff --git a/AIDevGallery/Pages/Scenarios/ScenarioPage.xaml.cs b/AIDevGallery/Pages/Scenarios/ScenarioPage.xaml.cs index 05fd00fb..b20cc1dd 100644 --- a/AIDevGallery/Pages/Scenarios/ScenarioPage.xaml.cs +++ b/AIDevGallery/Pages/Scenarios/ScenarioPage.xaml.cs @@ -169,16 +169,10 @@ private static async Task> GetSupportedHardwareAccelerators() break; case "DmlExecutionProvider": - // Test if DML is actually available before adding it to the list - // This prevents showing DML option on systems without compatible GPU - if (await WinMLHelpers.TestDmlAvailability()) + if (epDeviceTypes.Contains("GPU")) { supportedHardwareAccelerators.Add(new([HardwareAccelerator.DML, HardwareAccelerator.GPU], "DmlExecutionProvider", "DML", "GPU")); } - else - { - System.Diagnostics.Debug.WriteLine("[ScenarioPage] DML is reported by ONNX Runtime but not available. Skipping DML option."); - } break; diff --git a/AIDevGallery/Samples/SharedCode/WinMLHelpers.cs b/AIDevGallery/Samples/SharedCode/WinMLHelpers.cs index 07709687..187f1485 100644 --- a/AIDevGallery/Samples/SharedCode/WinMLHelpers.cs +++ b/AIDevGallery/Samples/SharedCode/WinMLHelpers.cs @@ -163,53 +163,4 @@ public static bool IsCompileModelSupported(string? deviceType, OrtEnv? environme || string.Equals(deviceType, "NPU", StringComparison.OrdinalIgnoreCase); } - /// - /// Tests if DirectML execution provider is available on the system. - /// - /// True if DML is available, false otherwise. - public static async Task TestDmlAvailability() - { - try - { - await Task.Run(() => - { - // Create a minimal ONNX model in memory (identity operation) - byte[] minimalModel = new byte[] - { - 0x08, 0x07, 0x12, 0x07, 0x62, 0x61, 0x63, 0x6B, 0x65, 0x6E, 0x64, 0x1A, 0x0D, 0x62, 0x61, 0x63, 0x6B, - 0x65, 0x6E, 0x64, 0x2D, 0x74, 0x65, 0x73, 0x74, 0x22, 0x46, 0x0A, 0x08, 0x0A, 0x01, 0x78, 0x12, 0x01, - 0x79, 0x22, 0x08, 0x49, 0x64, 0x65, 0x6E, 0x74, 0x69, 0x74, 0x79, 0x2A, 0x14, 0x0A, 0x04, 0x74, 0x79, - 0x70, 0x65, 0x12, 0x0C, 0x0A, 0x01, 0x69, 0x12, 0x07, 0x0A, 0x05, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x3A, - 0x18, 0x0A, 0x07, 0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x3A, 0x0D, 0x49, 0x64, 0x65, 0x6E, 0x74, - 0x69, 0x74, 0x79, 0x20, 0x74, 0x65, 0x73, 0x74, 0x0A, 0x16, 0x0A, 0x01, 0x78, 0x12, 0x11, 0x0A, 0x0F, - 0x08, 0x01, 0x12, 0x0B, 0x0A, 0x01, 0x4E, 0x0A, 0x01, 0x43, 0x0A, 0x01, 0x48, 0x0A, 0x01, 0x57, 0x0A, - 0x16, 0x0A, 0x01, 0x79, 0x12, 0x11, 0x0A, 0x0F, 0x08, 0x01, 0x12, 0x0B, 0x0A, 0x01, 0x4E, 0x0A, 0x01, - 0x43, 0x0A, 0x01, 0x48, 0x0A, 0x01, 0x57, 0x10, 0x01 - }; - - using var sessionOptions = new SessionOptions(); - sessionOptions.AppendExecutionProvider("DML"); - using var session = new InferenceSession(minimalModel, sessionOptions); - - Debug.WriteLine("[WinMLHelpers] DML is available and working."); - }); - - return true; - } - catch (OnnxRuntimeException ex) when (ex.Message.Contains("No devices detected")) - { - Debug.WriteLine("[WinMLHelpers] DirectML execution provider is not available on this system."); - Debug.WriteLine("[WinMLHelpers] This could be due to:"); - Debug.WriteLine("[WinMLHelpers] (1) No compatible GPU is present"); - Debug.WriteLine("[WinMLHelpers] (2) GPU drivers are outdated or not installed"); - Debug.WriteLine("[WinMLHelpers] (3) DirectX 12 is not supported"); - Debug.WriteLine($"[WinMLHelpers] Original error: {ex.Message}"); - return false; - } - catch (Exception ex) - { - Debug.WriteLine($"[WinMLHelpers] DML test failed: {ex.Message}"); - return false; - } - } } \ No newline at end of file From db92b813da0ef5416231bfe34a276f11e7978350 Mon Sep 17 00:00:00 2001 From: "Milly Wei (from Dev Box)" Date: Wed, 5 Nov 2025 14:14:10 +0800 Subject: [PATCH 08/11] update --- .../Samples/SharedCode/StableDiffusionCode/VaeDecoder.cs | 4 ++-- AIDevGallery/Samples/SharedCode/WinMLHelpers.cs | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/AIDevGallery/Samples/SharedCode/StableDiffusionCode/VaeDecoder.cs b/AIDevGallery/Samples/SharedCode/StableDiffusionCode/VaeDecoder.cs index 88fe3b16..2a196d82 100644 --- a/AIDevGallery/Samples/SharedCode/StableDiffusionCode/VaeDecoder.cs +++ b/AIDevGallery/Samples/SharedCode/StableDiffusionCode/VaeDecoder.cs @@ -59,9 +59,9 @@ private Task GetInferenceSession(StableDiffusionConfig config, sessionOptions.AddFreeDimensionOverrideByName("batch", 1); sessionOptions.AddFreeDimensionOverrideByName("channels", 4); - //sessionOptions.AddFreeDimensionOverrideByName("height", config.Height / 8); - //sessionOptions.AddFreeDimensionOverrideByName("width", config.Width / 8); + // sessionOptions.AddFreeDimensionOverrideByName("height", config.Height / 8); + // sessionOptions.AddFreeDimensionOverrideByName("width", config.Width / 8); if (policy != null) { sessionOptions.SetEpSelectionPolicy(policy.Value); diff --git a/AIDevGallery/Samples/SharedCode/WinMLHelpers.cs b/AIDevGallery/Samples/SharedCode/WinMLHelpers.cs index 187f1485..c7e2691a 100644 --- a/AIDevGallery/Samples/SharedCode/WinMLHelpers.cs +++ b/AIDevGallery/Samples/SharedCode/WinMLHelpers.cs @@ -7,7 +7,6 @@ using System.Diagnostics; using System.IO; using System.Linq; -using System.Threading.Tasks; namespace AIDevGallery.Samples.SharedCode; @@ -162,5 +161,4 @@ public static bool IsCompileModelSupported(string? deviceType, OrtEnv? environme return string.Equals(deviceType, "GPU", StringComparison.OrdinalIgnoreCase) || string.Equals(deviceType, "NPU", StringComparison.OrdinalIgnoreCase); } - } \ No newline at end of file From b42b2453a066d29166381e089dd447b88a44ed7f Mon Sep 17 00:00:00 2001 From: "Milly Wei (from Dev Box)" Date: Wed, 5 Nov 2025 14:16:31 +0800 Subject: [PATCH 09/11] update --- AIDevGallery/Pages/Scenarios/ScenarioPage.xaml.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/AIDevGallery/Pages/Scenarios/ScenarioPage.xaml.cs b/AIDevGallery/Pages/Scenarios/ScenarioPage.xaml.cs index b20cc1dd..cbd360cb 100644 --- a/AIDevGallery/Pages/Scenarios/ScenarioPage.xaml.cs +++ b/AIDevGallery/Pages/Scenarios/ScenarioPage.xaml.cs @@ -169,11 +169,7 @@ private static async Task> GetSupportedHardwareAccelerators() break; case "DmlExecutionProvider": - if (epDeviceTypes.Contains("GPU")) - { - supportedHardwareAccelerators.Add(new([HardwareAccelerator.DML, HardwareAccelerator.GPU], "DmlExecutionProvider", "DML", "GPU")); - } - + supportedHardwareAccelerators.Add(new([HardwareAccelerator.DML, HardwareAccelerator.GPU], "DmlExecutionProvider", "DML", "GPU")); break; case "NvTensorRTRTXExecutionProvider": From 76f1cd2373c60bcedf2a1e42218b72afbbb99ae6 Mon Sep 17 00:00:00 2001 From: "Milly Wei (from Dev Box)" Date: Wed, 5 Nov 2025 14:17:53 +0800 Subject: [PATCH 10/11] update --- .../Samples/SharedCode/StableDiffusionCode/VaeDecoder.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/AIDevGallery/Samples/SharedCode/StableDiffusionCode/VaeDecoder.cs b/AIDevGallery/Samples/SharedCode/StableDiffusionCode/VaeDecoder.cs index 2a196d82..65409240 100644 --- a/AIDevGallery/Samples/SharedCode/StableDiffusionCode/VaeDecoder.cs +++ b/AIDevGallery/Samples/SharedCode/StableDiffusionCode/VaeDecoder.cs @@ -59,9 +59,6 @@ private Task GetInferenceSession(StableDiffusionConfig config, sessionOptions.AddFreeDimensionOverrideByName("batch", 1); sessionOptions.AddFreeDimensionOverrideByName("channels", 4); - - // sessionOptions.AddFreeDimensionOverrideByName("height", config.Height / 8); - // sessionOptions.AddFreeDimensionOverrideByName("width", config.Width / 8); if (policy != null) { sessionOptions.SetEpSelectionPolicy(policy.Value); From 1675400f00fd3a03577d785406a80c3463b22a30 Mon Sep 17 00:00:00 2001 From: "Milly Wei (from Dev Box)" Date: Wed, 5 Nov 2025 14:31:02 +0800 Subject: [PATCH 11/11] update --- .../Samples/SharedCode/WinMLHelpers.cs | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/AIDevGallery/Samples/SharedCode/WinMLHelpers.cs b/AIDevGallery/Samples/SharedCode/WinMLHelpers.cs index c7e2691a..2e76c189 100644 --- a/AIDevGallery/Samples/SharedCode/WinMLHelpers.cs +++ b/AIDevGallery/Samples/SharedCode/WinMLHelpers.cs @@ -54,20 +54,7 @@ public static bool AppendExecutionProviderFromEpName(this SessionOptions session public static string? GetCompiledModel(this SessionOptions sessionOptions, string modelPath, string device) { - // NOTE: Skip compilation for the CPU execution provider. - // Rationale: - // - EPContext is an EP-specific offline-compiled/partitioned graph artifact that requires the - // execution provider to implement serialization/deserialization of its optimized graph. - // - ONNX Runtime's CPU EP does NOT implement EPContext model generation or loading. Invoking - // OrtModelCompilationOptions.CompileModel() for CPU attempts to emit a "*.CPU.onnx" EPContext - // artifact, which fails (commonly with InvalidProtobuf) because no EPContext is produced/understood - // by the CPU EP. - // Behavior: - // - For CPU, we return null here so callers fall back to the original ONNX model without attempting - // EPContext compilation. - // - Other EPs (e.g., DirectML, OpenVINO, QNN) may support EPContext depending on the ORT build, - // platform drivers, and hardware; for those we allow compilation to proceed. - if (string.Equals(device, "CPU", StringComparison.OrdinalIgnoreCase)) + if (IsCompileModelSupported(device) == false) { return null; } @@ -144,15 +131,26 @@ public static Dictionary> GetEpDeviceMap(OrtEnv? envir /// Determines whether model compilation should be surfaced based on device type. /// /// Device type string (e.g., "CPU", "GPU", "NPU"). - /// Unused; kept for signature stability if needed later. /// False for CPU; true for other known accelerator types. - public static bool IsCompileModelSupported(string? deviceType, OrtEnv? environment = null) + public static bool IsCompileModelSupported(string? deviceType) { if (string.IsNullOrWhiteSpace(deviceType)) { return false; } + // NOTE: Skip compilation for the CPU execution provider. + // - EPContext is an EP-specific offline-compiled/partitioned graph artifact that requires the + // execution provider to implement serialization/deserialization of its optimized graph. + // - ONNX Runtime's CPU EP does NOT implement EPContext model generation or loading. Invoking + // OrtModelCompilationOptions.CompileModel() for CPU attempts to emit a "*.CPU.onnx" EPContext + // artifact, which fails (commonly with InvalidProtobuf) because no EPContext is produced/understood + // by the CPU EP. + // Behavior: + // - For CPU, we return null here so callers fall back to the original ONNX model without attempting + // EPContext compilation. + // - Other EPs (e.g., DirectML, OpenVINO, QNN) may support EPContext depending on the ORT build, + // platform drivers, and hardware; for those we allow compilation to proceed. if (string.Equals(deviceType, "CPU", StringComparison.OrdinalIgnoreCase)) { return false;