From 4629bf4b74d5b10c8c8422f56f9bb0ee69240326 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20S=C5=82uszniak?= Date: Tue, 1 Sep 2026 13:20:02 +0200 Subject: [PATCH 1/2] feat(models): add Vulkan fp16 for Supertonic All four sub-models lower to Vulkan. On a Galaxy S26 Ultra (Adreno 840), medians over interleaved rounds, at 512 text tokens and 1000 latent frames: vulkan xnnpack duration_predictor 18.7 33.2 1.78x text_encoder 72.3 140.9 1.95x vector_estimator 682.5 1253.7 1.84x vocoder 865.8 1284.3 1.48x 2.12x end to end at the default 8 flow-matching steps, where vector_estimator is 84% of the total. Outputs match the fp32 CPU references at cosine 1.000000, 0.999414, 0.999994 and 0.999977. Vulkan leads BACKEND_ORDER.android, so this becomes the Android default. textToSpeech now provisions vulkan in download-libs; without it the backend is never downloaded and the model silently falls back to XNNPACK. Needs the ExecuTorch fixes in pytorch/executorch#22399, #22401, #22402, #22403 and #22406, all cherry-picked into the labs fork and built into the 1.4.1 native libs. --- .../scripts/download-libs.js | 8 ++++++-- packages/react-native-executorch/src/models.ts | 13 +++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/react-native-executorch/scripts/download-libs.js b/packages/react-native-executorch/scripts/download-libs.js index 5c97a39cc1..1ae5d46af4 100644 --- a/packages/react-native-executorch/scripts/download-libs.js +++ b/packages/react-native-executorch/scripts/download-libs.js @@ -131,8 +131,12 @@ const FEATURE_MAP = { privacyFilter: { backends: ['xnnpack', 'mlx'], libs: [] }, // Whisper ships xnnpack, coreml, an MLX iOS export and a Vulkan Android one. speechToText: { backends: ['xnnpack', 'coreml', 'mlx', 'vulkan'], libs: [] }, - // Kokoro ships xnnpack + coreml; Supertonic adds an MLX iOS export. - textToSpeech: { backends: ['xnnpack', 'coreml', 'mlx'], libs: ['phonemis'] }, + // Kokoro ships xnnpack + coreml; Supertonic adds an MLX iOS export and a + // Vulkan Android one. + textToSpeech: { + backends: ['xnnpack', 'coreml', 'mlx', 'vulkan'], + libs: ['phonemis'], + }, // FSMN VAD — xnnpack only. vad: { backends: ['xnnpack'], libs: [] }, // The MiniLM/CLIP-text/distiluse family ships coreml alongside xnnpack, diff --git a/packages/react-native-executorch/src/models.ts b/packages/react-native-executorch/src/models.ts index 3e75228c90..c4b52d532b 100644 --- a/packages/react-native-executorch/src/models.ts +++ b/packages/react-native-executorch/src/models.ts @@ -1116,6 +1116,18 @@ const SUPERTONIC_3_MLX_FP32: SupertonicTtsModel = { voiceStyles: SUPERTONIC_DEFAULT_VOICE_STYLES, }; +const SUPERTONIC_3_VULKAN_FP16: SupertonicTtsModel = { + name: 'supertonic', + modelPaths: { + durationPredictor: `${BASE_URL}-supertonic/${NEXT_VERSION_TAG}/vulkan/duration_predictor_vulkan_fp16.pte`, + vectorEstimator: `${BASE_URL}-supertonic/${NEXT_VERSION_TAG}/vulkan/vector_estimator_vulkan_fp16.pte`, + textEncoder: `${BASE_URL}-supertonic/${NEXT_VERSION_TAG}/vulkan/text_encoder_vulkan_fp16.pte`, + vocoder: `${BASE_URL}-supertonic/${NEXT_VERSION_TAG}/vulkan/vocoder_vulkan_fp16.pte`, + }, + unicodeIndexerPath: `${BASE_URL}-supertonic/${NEXT_VERSION_TAG}/unicode_indexer.json`, + voiceStyles: SUPERTONIC_DEFAULT_VOICE_STYLES, +}; + const KOKORO_ROOT = `${BASE_URL}-kokoro/${NEXT_VERSION_TAG}`; const KOKORO_PHONEMIZER_ROOT = `${KOKORO_ROOT}/phonemizer`; @@ -2624,6 +2636,7 @@ export const models = { SUPERTONIC: variants({ XNNPACK_FP32: SUPERTONIC_3_XNNPACK_FP32, MLX_FP32: SUPERTONIC_3_MLX_FP32, + VULKAN_FP16: SUPERTONIC_3_VULKAN_FP16, }), /** From 5bef3409d3c01e1cde5bdc34cb3edadf41ad24fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20S=C5=82uszniak?= Date: Tue, 1 Sep 2026 13:26:21 +0200 Subject: [PATCH 2/2] feat(speech): offer the Vulkan Supertonic build in the demo Adds the Vulkan entry to the model picker and makes it the initial selection on Android, which is also what SUPERTONIC.DEFAULT resolves to there. --- apps/speech/app/text-to-speech/index.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/speech/app/text-to-speech/index.tsx b/apps/speech/app/text-to-speech/index.tsx index b6dcb2be1b..1e041a4a24 100644 --- a/apps/speech/app/text-to-speech/index.tsx +++ b/apps/speech/app/text-to-speech/index.tsx @@ -53,11 +53,14 @@ const STEPS_OPTIONS = [ const MODEL_OPTIONS = [ { label: 'XNNPACK (CPU)', value: 'XNNPACK_FP32' as const }, { label: 'MLX (Apple Silicon)', value: 'MLX_FP32' as const, disabled: Platform.OS !== 'ios' }, + { label: 'Vulkan (GPU)', value: 'VULKAN_FP16' as const, disabled: Platform.OS !== 'android' }, ]; function TTSContent() { const [text, setText] = useState(SAMPLE_TEXT); - const [selectedModel, setSelectedModel] = useState<'XNNPACK_FP32' | 'MLX_FP32'>('XNNPACK_FP32'); + const [selectedModel, setSelectedModel] = useState<'XNNPACK_FP32' | 'MLX_FP32' | 'VULKAN_FP16'>( + Platform.OS === 'android' ? 'VULKAN_FP16' : 'XNNPACK_FP32' + ); const [selectedVoice, setSelectedVoice] = useState('F1'); const [selectedLang, setSelectedLang] = useState('en'); const [speed, setSpeed] = useState(1.05);