diff --git a/Cargo.lock b/Cargo.lock index 545d0d943e59..0a00da753fc8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1447,7 +1447,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core", + "windows-core 0.51.1", ] [[package]] @@ -3724,6 +3724,7 @@ dependencies = [ "wasi-common", "wasmtime", "wiggle", + "windows", ] [[package]] @@ -3983,6 +3984,25 @@ dependencies = [ "winch-test-macros", ] +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core 0.52.0", + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-core" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-core" version = "0.52.0" @@ -3998,7 +4018,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.0", + "windows-targets 0.48.5", ] [[package]] @@ -4012,17 +4032,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -4042,9 +4062,9 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" @@ -4054,9 +4074,9 @@ checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" [[package]] name = "windows_aarch64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" @@ -4066,9 +4086,9 @@ checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" [[package]] name = "windows_i686_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" @@ -4078,9 +4098,9 @@ checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" [[package]] name = "windows_i686_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" @@ -4090,9 +4110,9 @@ checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" [[package]] name = "windows_x86_64_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" @@ -4102,9 +4122,9 @@ checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" @@ -4114,9 +4134,9 @@ checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" [[package]] name = "windows_x86_64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" diff --git a/crates/test-programs/src/bin/nn_image_classification_winml.rs b/crates/test-programs/src/bin/nn_image_classification_winml.rs new file mode 100644 index 000000000000..6996ff25822c --- /dev/null +++ b/crates/test-programs/src/bin/nn_image_classification_winml.rs @@ -0,0 +1,58 @@ +use anyhow::Result; +use std::fs; +use std::time::Instant; +use wasi_nn::*; + +pub fn main() -> Result<()> { + // Graph is supposed to be preloaded by `nn-graph` argument. The path ends with "mobilenet". + let graph = + wasi_nn::GraphBuilder::new(wasi_nn::GraphEncoding::Onnx, wasi_nn::ExecutionTarget::CPU) + .build_from_cache("mobilenet") + .unwrap(); + + let mut context = graph.init_execution_context().unwrap(); + println!("Created an execution context."); + + // Convert image to tensor data. + let tensor_data = fs::read("fixture/kitten.rgb")?; + context + .set_input(0, TensorType::F32, &[1, 3, 224, 224], &tensor_data) + .unwrap(); + + // Execute the inference. + let before_compute = Instant::now(); + context.compute().unwrap(); + println!( + "Executed graph inference, took {} ms.", + before_compute.elapsed().as_millis() + ); + + // Retrieve the output. + let mut output_buffer = vec![0f32; 1000]; + context.get_output(0, &mut output_buffer[..]).unwrap(); + + let result = sort_results(&output_buffer); + println!("Found results, sorted top 5: {:?}", &result[..5]); + assert_eq!(result[0].0, 284); + Ok(()) +} + +// Sort the buffer of probabilities. The graph places the match probability for each class at the +// index for that class (e.g. the probability of class 42 is placed at buffer[42]). Here we convert +// to a wrapping InferenceResult and sort the results. It is unclear why the MobileNet output +// indices are "off by one" but the `.skip(1)` below seems necessary to get results that make sense +// (e.g. 763 = "revolver" vs 762 = "restaurant") +fn sort_results(buffer: &[f32]) -> Vec { + let mut results: Vec = buffer + .iter() + .skip(1) + .enumerate() + .map(|(c, p)| InferenceResult(c, *p)) + .collect(); + results.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); + results +} + +// A wrapper for class ID and match probabilities. +#[derive(Debug, PartialEq)] +struct InferenceResult(usize, f32); diff --git a/crates/wasi-nn/Cargo.toml b/crates/wasi-nn/Cargo.toml index 4c8734eec9b7..2b3005e98d6b 100644 --- a/crates/wasi-nn/Cargo.toml +++ b/crates/wasi-nn/Cargo.toml @@ -24,8 +24,19 @@ wasmtime = { workspace = true, features = ["component-model", "runtime"] } # These dependencies are necessary for the wasi-nn implementation: tracing = { workspace = true } -openvino = { version = "0.6.0", features = ["runtime-linking"] } thiserror = { workspace = true } +openvino = { version = "0.6.0", features = [ + "runtime-linking", +], optional = true } + +[target.'cfg(windows)'.dependencies.windows] +version = "0.52" +features = [ + "AI_MachineLearning", + "Storage_Streams", + "Foundation_Collections", +] +optional = true [build-dependencies] walkdir = { workspace = true } @@ -35,3 +46,10 @@ cap-std = { workspace = true } test-programs-artifacts = { workspace = true } wasi-common = { workspace = true, features = ["sync"] } wasmtime = { workspace = true, features = ["cranelift"] } + +[features] +default = ["openvino"] +# openvino is available on all platforms, it requires openvino installed. +openvino = ["dep:openvino"] +# winml is only available on Windows 10 1809 and later. +winml = ["dep:windows"] diff --git a/crates/wasi-nn/src/backend/mod.rs b/crates/wasi-nn/src/backend/mod.rs index e55b94aa411d..4b50fb241eaf 100644 --- a/crates/wasi-nn/src/backend/mod.rs +++ b/crates/wasi-nn/src/backend/mod.rs @@ -2,9 +2,15 @@ //! this crate. The `Box` types returned by these interfaces allow //! implementations to maintain backend-specific state between calls. +#[cfg(feature = "openvino")] pub mod openvino; +#[cfg(feature = "winml")] +pub mod winml; +#[cfg(feature = "openvino")] use self::openvino::OpenvinoBackend; +#[cfg(feature = "winml")] +use self::winml::WinMLBackend; use crate::wit::types::{ExecutionTarget, GraphEncoding, Tensor}; use crate::{Backend, ExecutionContext, Graph}; use std::path::Path; @@ -13,7 +19,16 @@ use wiggle::GuestError; /// Return a list of all available backend frameworks. pub fn list() -> Vec { - vec![Backend::from(OpenvinoBackend::default())] + let mut backends = vec![]; + #[cfg(feature = "openvino")] + { + backends.push(Backend::from(OpenvinoBackend::default())); + } + #[cfg(feature = "winml")] + { + backends.push(Backend::from(WinMLBackend::default())); + } + backends } /// A [Backend] contains the necessary state to load [Graph]s. diff --git a/crates/wasi-nn/src/backend/winml.rs b/crates/wasi-nn/src/backend/winml.rs new file mode 100644 index 000000000000..5940b901e6b5 --- /dev/null +++ b/crates/wasi-nn/src/backend/winml.rs @@ -0,0 +1,170 @@ +//! Implements a `wasi-nn` [`BackendInner`] using WinML. + +use super::{BackendError, BackendExecutionContext, BackendFromDir, BackendGraph, BackendInner}; +use crate::wit::types::{ExecutionTarget, GraphEncoding, Tensor}; +use crate::{ExecutionContext, Graph}; +use std::{fs::File, io::Read, path::Path}; +use windows::core::{ComInterface, HSTRING}; +use windows::Storage::Streams::{ + DataWriter, InMemoryRandomAccessStream, RandomAccessStreamReference, +}; +use windows::AI::MachineLearning::{ + LearningModel, LearningModelBinding, LearningModelDevice, LearningModelDeviceKind, + LearningModelEvaluationResult, LearningModelSession, TensorFeatureDescriptor, TensorFloat, +}; + +#[derive(Default)] +pub struct WinMLBackend(); + +impl BackendInner for WinMLBackend { + fn encoding(&self) -> GraphEncoding { + GraphEncoding::Onnx + } + + fn load(&mut self, builders: &[&[u8]], target: ExecutionTarget) -> Result { + if builders.len() != 1 { + return Err(BackendError::InvalidNumberOfBuilders(1, builders.len()).into()); + } + + let model_stream = InMemoryRandomAccessStream::new()?; + let model_writer = DataWriter::CreateDataWriter(&model_stream)?; + model_writer.WriteBytes(&builders[0])?; + model_writer.StoreAsync()?; + model_writer.FlushAsync()?; + let model = LearningModel::LoadFromStream(&RandomAccessStreamReference::CreateFromStream( + &model_stream, + )?)?; + let device_kind = match target { + ExecutionTarget::Cpu => LearningModelDeviceKind::Cpu, + ExecutionTarget::Gpu => LearningModelDeviceKind::DirectX, + ExecutionTarget::Tpu => unimplemented!(), + }; + let graph = WinMLGraph { model, device_kind }; + + let box_: Box = Box::new(graph); + Ok(box_.into()) + } + + fn as_dir_loadable(&mut self) -> Option<&mut dyn BackendFromDir> { + Some(self) + } +} + +impl BackendFromDir for WinMLBackend { + fn load_from_dir( + &mut self, + path: &Path, + target: ExecutionTarget, + ) -> Result { + let model = read(&path.join("model.onnx"))?; + self.load(&[&model], target) + } +} + +struct WinMLGraph { + model: LearningModel, + device_kind: LearningModelDeviceKind, +} + +unsafe impl Send for WinMLGraph {} +unsafe impl Sync for WinMLGraph {} + +impl BackendGraph for WinMLGraph { + fn init_execution_context(&self) -> Result { + let device = LearningModelDevice::Create(self.device_kind.clone())?; + let session = LearningModelSession::CreateFromModelOnDevice(&self.model, &device)?; + let box_: Box = Box::new(WinMLExecutionContext::new(session)); + Ok(box_.into()) + } +} + +struct WinMLExecutionContext { + session: LearningModelSession, + binding: LearningModelBinding, + result: Option, +} + +impl WinMLExecutionContext { + fn new(session: LearningModelSession) -> Self { + Self { + binding: LearningModelBinding::CreateFromSession(&session).unwrap(), + session, + result: None, + } + } +} + +impl BackendExecutionContext for WinMLExecutionContext { + fn set_input(&mut self, index: u32, tensor: &Tensor) -> Result<(), BackendError> { + // TODO: Support other tensor types. Only FP32 is supported right now. + match tensor.tensor_type { + crate::wit::types::TensorType::Fp32 => {} + _ => unimplemented!(), + } + + let input = self.session.Model()?.InputFeatures()?.GetAt(index)?; + unsafe { + let data = std::slice::from_raw_parts( + tensor.data.as_ptr() as *const f32, + tensor.data.len() / 4, + ); + + self.binding.Bind( + &input.Name()?, + &TensorFloat::CreateFromArray( + &input.cast::()?.Shape()?, + data, + )?, + )?; + } + Ok(()) + } + + fn compute(&mut self) -> Result<(), BackendError> { + self.result = Some(self.session.Evaluate(&self.binding, &HSTRING::new())?); + Ok(()) + } + + fn get_output(&mut self, index: u32, destination: &mut [u8]) -> Result { + if self.result.is_none() { + return Err(BackendError::BackendAccess(anyhow::Error::msg( + "Output is not ready.", + ))); + } + let output_name = self.session.Model()?.OutputFeatures()?.GetAt(index)?; + let output_name_hstring = output_name.Name()?; + + // Print results. + let vector_view = self + .result + .as_ref() + .unwrap() + .Outputs()? + .Lookup(&output_name_hstring)? + .cast::()? + .GetAsVectorView()?; + let output: Vec = vector_view.into_iter().collect(); + unsafe { + destination.copy_from_slice(std::slice::from_raw_parts( + output.as_ptr() as *const u8, + output.len() * 4, + )); + } + + Ok(0) + } +} + +/// Read a file into a byte vector. +fn read(path: &Path) -> anyhow::Result> { + let mut file = File::open(path)?; + let mut buffer = vec![]; + file.read_to_end(&mut buffer)?; + Ok(buffer) +} + +impl From for BackendError { + fn from(e: windows::core::Error) -> Self { + BackendError::BackendAccess(anyhow::Error::new(e)) + } +} diff --git a/crates/wasi-nn/src/testing.rs b/crates/wasi-nn/src/testing.rs index b960335dc500..a9abdef5d3f9 100644 --- a/crates/wasi-nn/src/testing.rs +++ b/crates/wasi-nn/src/testing.rs @@ -3,10 +3,13 @@ //! //! This module checks: //! - that OpenVINO can be found in the environment +//! - that WinML is available //! - that some ML model artifacts can be downloaded and cached. use anyhow::{anyhow, Context, Result}; use std::{env, fs, path::Path, path::PathBuf, process::Command, sync::Mutex}; +#[cfg(feature = "winml")] +use windows::AI::MachineLearning::{LearningModelDevice, LearningModelDeviceKind}; /// Return the directory in which the test artifacts are stored. pub fn artifacts_dir() -> PathBuf { @@ -34,12 +37,21 @@ macro_rules! check_test { /// Return `Ok` if all checks pass. pub fn check() -> Result<()> { - check_openvino_is_installed()?; - check_openvino_artifacts_are_available()?; + #[cfg(feature = "openvino")] + { + check_openvino_is_installed()?; + check_openvino_artifacts_are_available()?; + } + #[cfg(feature = "winml")] + { + check_winml_is_available()?; + check_winml_artifacts_are_available()?; + } Ok(()) } /// Return `Ok` if we find a working OpenVINO installation. +#[cfg(feature = "openvino")] fn check_openvino_is_installed() -> Result<()> { match std::panic::catch_unwind(|| println!("> found openvino version: {}", openvino::version())) { @@ -48,6 +60,19 @@ fn check_openvino_is_installed() -> Result<()> { } } +#[cfg(feature = "winml")] +fn check_winml_is_available() -> Result<()> { + match std::panic::catch_unwind(|| { + println!( + "> WinML learning device is available: {:?}", + LearningModelDevice::Create(LearningModelDeviceKind::Default) + ) + }) { + Ok(_) => Ok(()), + Err(e) => Err(anyhow!("WinML learning device is not available: {:?}", e)), + } +} + /// Protect `check_openvino_artifacts_are_available` from concurrent access; /// when running tests in parallel, we want to avoid two threads attempting to /// create the same directory or download the same file. @@ -55,6 +80,7 @@ static ARTIFACTS: Mutex<()> = Mutex::new(()); /// Return `Ok` if we find the cached MobileNet test artifacts; this will /// download the artifacts if necessary. +#[cfg(feature = "openvino")] fn check_openvino_artifacts_are_available() -> Result<()> { let _exclusively_retrieve_artifacts = ARTIFACTS.lock().unwrap(); const BASE_URL: &str = @@ -80,6 +106,31 @@ fn check_openvino_artifacts_are_available() -> Result<()> { Ok(()) } +#[cfg(feature = "winml")] +fn check_winml_artifacts_are_available() -> Result<()> { + let _exclusively_retrieve_artifacts = ARTIFACTS.lock().unwrap(); + let artifacts_dir = artifacts_dir(); + if !artifacts_dir.is_dir() { + fs::create_dir(&artifacts_dir)?; + } + const MODEL_URL: &str="https://github.com/onnx/models/raw/5faef4c33eba0395177850e1e31c4a6a9e634c82/vision/classification/mobilenet/model/mobilenetv2-12.onnx"; + for (from, to) in [(MODEL_URL, "model.onnx")] { + let local_path = artifacts_dir.join(to); + if !local_path.is_file() { + download(&from, &local_path).with_context(|| "unable to retrieve test artifact")?; + } else { + println!("> using cached artifact: {}", local_path.display()) + } + } + // kitten.rgb is converted from https://github.com/microsoft/Windows-Machine-Learning/blob/master/SharedContent/media/kitten_224.png?raw=true. + let tensor_path = env::current_dir()? + .join("tests") + .join("fixtures") + .join("kitten.rgb"); + fs::copy(tensor_path, artifacts_dir.join("kitten.rgb"))?; + Ok(()) +} + /// Retrieve the bytes at the `from` URL and place them in the `to` file. fn download(from: &str, to: &Path) -> anyhow::Result<()> { let mut curl = Command::new("curl"); diff --git a/crates/wasi-nn/tests/all.rs b/crates/wasi-nn/tests/all.rs index 185daec9fbf5..2f8c6aa7d612 100644 --- a/crates/wasi-nn/tests/all.rs +++ b/crates/wasi-nn/tests/all.rs @@ -6,7 +6,7 @@ use test_programs_artifacts::*; use wasi_common::sync::{Dir, WasiCtxBuilder}; use wasi_common::WasiCtx; use wasmtime::{Config, Engine, Linker, Module, Store}; -use wasmtime_wasi_nn::{backend, testing, InMemoryRegistry, WasiNnCtx}; +use wasmtime_wasi_nn::{backend, testing, Backend, InMemoryRegistry, WasiNnCtx}; const PREOPENED_DIR_NAME: &str = "fixture"; @@ -22,10 +22,24 @@ fn run(path: &str, preload_model: bool) -> Result<()> { wasmtime_wasi_nn::witx::add_to_linker(&mut linker, |s: &mut Ctx| &mut s.wasi_nn)?; wasi_common::sync::add_to_linker(&mut linker, |s: &mut Ctx| &mut s.wasi)?; let module = Module::from_file(&engine, path)?; - let mut store = Store::new(&engine, Ctx::new(&testing::artifacts_dir(), preload_model)?); - let instance = linker.instantiate(&mut store, &module)?; - let start = instance.get_typed_func::<(), ()>(&mut store, "_start")?; - start.call(&mut store, ())?; + let mut backends = vec![]; + #[cfg(feature = "openvino")] + { + backends.push(Backend::from(backend::openvino::OpenvinoBackend::default())); + } + #[cfg(feature = "winml")] + { + backends.push(Backend::from(backend::winml::WinMLBackend::default())); + } + for backend in backends { + let mut store = Store::new( + &engine, + Ctx::new(&testing::artifacts_dir(), preload_model, backend)?, + ); + let instance = linker.instantiate(&mut store, &module)?; + let start = instance.get_typed_func::<(), ()>(&mut store, "_start")?; + start.call(&mut store, ())?; + } Ok(()) } @@ -35,7 +49,7 @@ struct Ctx { wasi_nn: WasiNnCtx, } impl Ctx { - fn new(preopen_dir: &Path, preload_model: bool) -> Result { + fn new(preopen_dir: &Path, preload_model: bool, mut backend: Backend) -> Result { // Create the WASI context. let preopen_dir = Dir::open_ambient_dir(preopen_dir, cap_std::ambient_authority())?; let mut builder = WasiCtxBuilder::new(); @@ -44,14 +58,12 @@ impl Ctx { .preopened_dir(preopen_dir, PREOPENED_DIR_NAME)?; let wasi = builder.build(); - // Create the wasi-nn context. - let mut openvino = backend::openvino::OpenvinoBackend::default(); let mut registry = InMemoryRegistry::new(); let mobilenet_dir = testing::artifacts_dir(); if preload_model { - registry.load(&mut openvino, &mobilenet_dir)?; + registry.load((backend).as_dir_loadable().unwrap(), &mobilenet_dir)?; } - let wasi_nn = WasiNnCtx::new([openvino.into()], registry.into()); + let wasi_nn = WasiNnCtx::new([backend.into()], registry.into()); Ok(Self { wasi, wasi_nn }) } @@ -90,3 +102,9 @@ fn nn_image_classification() { fn nn_image_classification_named() { run(NN_IMAGE_CLASSIFICATION_NAMED, true).unwrap() } + +#[cfg_attr(not(feature = "winml"), ignore)] +#[test] +fn nn_image_classification_winml() { + run(NN_IMAGE_CLASSIFICATION_WINML, true).unwrap() +} diff --git a/crates/wasi-nn/tests/fixtures/kitten.rgb b/crates/wasi-nn/tests/fixtures/kitten.rgb new file mode 100644 index 000000000000..d39e9f9e20cb Binary files /dev/null and b/crates/wasi-nn/tests/fixtures/kitten.rgb differ diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock index 5ad0c81a87fe..1ef6b06afb42 100644 --- a/supply-chain/imports.lock +++ b/supply-chain/imports.lock @@ -1121,6 +1121,20 @@ when = "2024-02-07" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.windows]] +version = "0.52.0" +when = "2023-11-15" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows-core]] +version = "0.51.1" +when = "2023-08-17" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + [[publisher.windows-core]] version = "0.52.0" when = "2023-11-15" @@ -1149,6 +1163,13 @@ user-id = 64539 user-login = "kennykerr" user-name = "Kenny Kerr" +[[publisher.windows-targets]] +version = "0.48.5" +when = "2023-08-18" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + [[publisher.windows-targets]] version = "0.52.0" when = "2023-11-15" @@ -1163,6 +1184,13 @@ user-id = 64539 user-login = "kennykerr" user-name = "Kenny Kerr" +[[publisher.windows_aarch64_gnullvm]] +version = "0.48.5" +when = "2023-08-18" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + [[publisher.windows_aarch64_gnullvm]] version = "0.52.0" when = "2023-11-15" @@ -1177,6 +1205,13 @@ user-id = 64539 user-login = "kennykerr" user-name = "Kenny Kerr" +[[publisher.windows_aarch64_msvc]] +version = "0.48.5" +when = "2023-08-18" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + [[publisher.windows_aarch64_msvc]] version = "0.52.0" when = "2023-11-15" @@ -1191,6 +1226,13 @@ user-id = 64539 user-login = "kennykerr" user-name = "Kenny Kerr" +[[publisher.windows_i686_gnu]] +version = "0.48.5" +when = "2023-08-18" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + [[publisher.windows_i686_gnu]] version = "0.52.0" when = "2023-11-15" @@ -1205,6 +1247,13 @@ user-id = 64539 user-login = "kennykerr" user-name = "Kenny Kerr" +[[publisher.windows_i686_msvc]] +version = "0.48.5" +when = "2023-08-18" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + [[publisher.windows_i686_msvc]] version = "0.52.0" when = "2023-11-15" @@ -1219,6 +1268,13 @@ user-id = 64539 user-login = "kennykerr" user-name = "Kenny Kerr" +[[publisher.windows_x86_64_gnu]] +version = "0.48.5" +when = "2023-08-18" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + [[publisher.windows_x86_64_gnu]] version = "0.52.0" when = "2023-11-15" @@ -1233,6 +1289,13 @@ user-id = 64539 user-login = "kennykerr" user-name = "Kenny Kerr" +[[publisher.windows_x86_64_gnullvm]] +version = "0.48.5" +when = "2023-08-18" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + [[publisher.windows_x86_64_gnullvm]] version = "0.52.0" when = "2023-11-15" @@ -1247,6 +1310,13 @@ user-id = 64539 user-login = "kennykerr" user-name = "Kenny Kerr" +[[publisher.windows_x86_64_msvc]] +version = "0.48.5" +when = "2023-08-18" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + [[publisher.windows_x86_64_msvc]] version = "0.52.0" when = "2023-11-15"