Skip to content

Commit 85303e7

Browse files
committed
[RNE Rewrite] feat(tti): drop 8da4w, add SDXS CoreML fp16 + MLX int4
Remove the XNNPACK 8da4w SDXS variant (no speed win over fp32 since both are conv-bound, and noticeably poorer quality on this quant-sensitive model). Add iOS backends: CoreML fp16 (quality-preserving) and MLX int4. - models.textToImage.SDXS_512_DREAMSHAPER: XNNPACK_FP32 (default), COREML_FP16, MLX_INT4 - demo screen exposes the CoreML/MLX options (iOS-only) Refs #1244, #939
1 parent 8df8929 commit 85303e7

3 files changed

Lines changed: 42 additions & 27 deletions

File tree

apps/computer-vision/app/textToImage/index.tsx

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,14 @@ const MODEL_OPTIONS: ModelOption[] = [
2222
value: models.textToImage.SDXS_512_DREAMSHAPER.XNNPACK_FP32,
2323
},
2424
{
25-
label: 'SDXS-512-DreamShaper (XNNPACK 8DA4W)',
26-
value: models.textToImage.SDXS_512_DREAMSHAPER.XNNPACK_8DA4W,
25+
label: 'SDXS-512-DreamShaper (CoreML FP16)',
26+
value: models.textToImage.SDXS_512_DREAMSHAPER.COREML_FP16,
27+
disabled: Platform.OS !== 'ios',
28+
},
29+
{
30+
label: 'SDXS-512-DreamShaper (MLX INT4)',
31+
value: models.textToImage.SDXS_512_DREAMSHAPER.MLX_INT4,
32+
disabled: Platform.OS !== 'ios',
2733
},
2834
];
2935

@@ -36,9 +42,13 @@ function TextToImageContent() {
3642
const [latency, setLatency] = useState<number | null>(null);
3743
const [error, setError] = useState<string | null>(null);
3844

39-
const { isReady, downloadProgress, error: loadError, generate, generateWorklet } = useTextToImage(
40-
selectedModel
41-
);
45+
const {
46+
isReady,
47+
downloadProgress,
48+
error: loadError,
49+
generate,
50+
generateWorklet,
51+
} = useTextToImage(selectedModel);
4252

4353
const runGenerate = async (sync: boolean) => {
4454
if (!generate || !generateWorklet || !prompt.trim()) return;

packages/react-native-executorch/src/extensions/cv/tasks/sdxsTextToImage.ts

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,14 @@ export type SdxsOptions = {
3838
readonly initNoiseSigma: number;
3939
/** The single training timestep fed to the UNet for the distilled step. */
4040
readonly timestep: number;
41-
/** Cumulative alpha product at {@link timestep} (from the scheduler schedule). */
42-
readonly alphaCumprod: number;
43-
/** What the UNet output represents, which fixes the scheduler update formula. */
44-
readonly predictionType: 'epsilon' | 'v_prediction' | 'sample';
41+
/**
42+
* Scheduler-step coefficients. For the distilled single step the DEIS update
43+
* is exactly linear in the latents and the UNet output:
44+
* `clean = sampleCoeff * latents + noiseCoeff * modelOutput`. Both are pinned
45+
* from the reference scheduler during export.
46+
*/
47+
readonly sampleCoeff: number;
48+
readonly noiseCoeff: number;
4549
/** Per-channel or scalar scale applied to the decoder output when mapping to `[0..255]`. */
4650
readonly outAlpha: number | number[];
4751
/** Per-channel or scalar bias applied to the decoder output when mapping to `[0..255]`. */
@@ -101,23 +105,17 @@ function seededGaussian(size: number, seed: number): Float32Array {
101105
}
102106

103107
// Single-step scheduler update: turns the UNet output into the clean latent that
104-
// is fed to the decoder. For a distilled 1-step model the update to t=0 reduces
105-
// to recovering x0 from the model prediction.
108+
// is fed to the decoder. For the distilled single step the DEIS update is exactly
109+
// linear in the latents and the model output.
106110
function toCleanLatents(
107111
latents: Float32Array,
108112
modelOutput: Float32Array,
109113
opts: SdxsOptions
110114
): Float32Array {
111115
'worklet';
112-
if (opts.predictionType === 'sample') return modelOutput;
113-
const sqrtAlpha = Math.sqrt(opts.alphaCumprod);
114-
const sqrtBeta = Math.sqrt(1 - opts.alphaCumprod);
115116
const out = new Float32Array(latents.length);
116117
for (let i = 0; i < latents.length; i++) {
117-
out[i] =
118-
opts.predictionType === 'v_prediction'
119-
? sqrtAlpha * latents[i]! - sqrtBeta * modelOutput[i]!
120-
: (latents[i]! - sqrtBeta * modelOutput[i]!) / sqrtAlpha;
118+
out[i] = opts.sampleCoeff * latents[i]! + opts.noiseCoeff * modelOutput[i]!;
121119
}
122120
return out;
123121
}

packages/react-native-executorch/src/models.ts

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -594,18 +594,19 @@ const CLIP_VIT_BASE_PATCH32_IMAGE_XNNPACK_INT8: ImageEmbedderModel = {
594594
// =============================================================================
595595
// Text to Image
596596
// =============================================================================
597-
// NOTE: the numeric fields below (initNoiseSigma, timestep, alphaCumprod,
598-
// predictionType, outAlpha/outBeta) are pinned during the `.pte` export by
599-
// numerically matching the reference diffusers output; the values here are the
600-
// SDXS defaults and are updated once the export is finalized.
597+
// The numeric fields below are pinned from the SDXS reference pipeline
598+
// (DEISMultistepScheduler, single step at t=999, epsilon prediction, TAESD
599+
// decoder). The single-step scheduler update is exactly linear in the latents
600+
// and the UNet output, so it is captured as (sampleCoeff, noiseCoeff). The
601+
// exported `decode` method emits RGB in [0,1], hence outAlpha=255, outBeta=0.
601602
const SDXS_512_DREAMSHAPER_OPTS = {
602603
imageSize: 512,
603604
latentChannels: 4,
604605
numInferenceSteps: 1,
605606
initNoiseSigma: 1.0,
606607
timestep: 999,
607-
alphaCumprod: 0.00466,
608-
predictionType: 'epsilon' as const,
608+
sampleCoeff: 14.642591,
609+
noiseCoeff: -14.579279,
609610
outAlpha: 255.0,
610611
outBeta: 0.0,
611612
};
@@ -615,8 +616,13 @@ const SDXS_512_DREAMSHAPER_XNNPACK_FP32: SdxsModel = {
615616
tokenizerPath: SDXS_512_DREAMSHAPER_TOKENIZER,
616617
opts: SDXS_512_DREAMSHAPER_OPTS,
617618
};
618-
const SDXS_512_DREAMSHAPER_XNNPACK_8DA4W: SdxsModel = {
619-
modelPath: `${BASE_URL}-sdxs-512-dreamshaper/${NEXT_VERSION_TAG}/xnnpack/sdxs_512_dreamshaper_xnnpack_8da4w.pte`,
619+
const SDXS_512_DREAMSHAPER_COREML_FP16: SdxsModel = {
620+
modelPath: `${BASE_URL}-sdxs-512-dreamshaper/${NEXT_VERSION_TAG}/coreml/sdxs_512_dreamshaper_coreml_fp16.pte`,
621+
tokenizerPath: SDXS_512_DREAMSHAPER_TOKENIZER,
622+
opts: SDXS_512_DREAMSHAPER_OPTS,
623+
};
624+
const SDXS_512_DREAMSHAPER_MLX_INT4: SdxsModel = {
625+
modelPath: `${BASE_URL}-sdxs-512-dreamshaper/${NEXT_VERSION_TAG}/mlx/sdxs_512_dreamshaper_mlx_int4.pte`,
620626
tokenizerPath: SDXS_512_DREAMSHAPER_TOKENIZER,
621627
opts: SDXS_512_DREAMSHAPER_OPTS,
622628
};
@@ -880,7 +886,8 @@ export const models = {
880886
SDXS_512_DREAMSHAPER: {
881887
...SDXS_512_DREAMSHAPER_XNNPACK_FP32,
882888
XNNPACK_FP32: SDXS_512_DREAMSHAPER_XNNPACK_FP32,
883-
XNNPACK_8DA4W: SDXS_512_DREAMSHAPER_XNNPACK_8DA4W,
889+
COREML_FP16: SDXS_512_DREAMSHAPER_COREML_FP16,
890+
MLX_INT4: SDXS_512_DREAMSHAPER_MLX_INT4,
884891
},
885892
},
886893
};

0 commit comments

Comments
 (0)