Commit Graph
126 Commits
Author SHA1 Message Date
Pascal 7b6ed4f6db codec: drop the fused streaming tail
The fused mode appended the codec stream tail to the predictor frame
graph, so one compute produced both a frame's codes and its 80 ms of
audio with no host round trip. The experiment applied to max_batch 1
with a streaming synthesis only, it cost throughput against the
buffered flush that stays the default, and it kept a second frame
graph, its ring inputs and an init flag alive for that single case. It
is not worth keeping.

Remove the tail helpers, the fused graph of CodePredGraphSet, the
codec_fused field of qt_init_params, the --codec-fused flag of both
tools and the harness switch that exercised it. The predictor frame
unroll and the in graph sampler are untouched.
2026-08-05 18:20:08 +02:00
Pascal abab6b3bf3 tests: refresh the clone grid logs 2026-07-30 23:05:37 +02:00
Pascal 9a9a425260 tests: exercise the fused decode in every cossim harness
Each harness re-runs the C++ side with --codec-fused after the
buffered run and gates on codes-full.bin equality between the two:
the decode path cannot change the predictor, so any divergence is a
bug. The fused run's perf lines land in the log as [Perf Fused] next
to the buffered ones, so every grid cell carries the buffered vs
fused comparison per backend and quant.

The fused audio itself is not scored here: greedy amplitudes vanish
in the streamed PCM_16 and the fused FP envelope has its own
validation against the stream path.
2026-07-30 22:54:44 +02:00
Pascal 26dd8adbf0 predictor: unroll the frame into one cgraph and sample in standard ops
One static frame graph per batch width replaces the per step chain:
prefill and the 15 acoustic steps run in a single backend compute.
This is the target architecture for the llama.cpp Qwen3-TTS port and
serves as its working GGML reference while under test.

Sampling is a plain op chain batched over slots: temperature, argsort
top_k (descending order is guaranteed on every backend, unlike top_k),
softmax, cumsum, cdf crossing against a per step philox uniform.
Greedy draws with u = 0 and lands on the argmax. Faster than the
fused sampling op under CUDA graph capture, greedy codes stay exact
against the Python reference on CPU, CUDA and Vulkan.

Opt in single slot latency mode (--codec-fused on qwen-tts and
tts-server, codec_fused in qt_init_params): the codec stream tail
joins the frame graph at T=1, codes read through a device view, one
80 ms chunk per compute with no host round trip.

Predictor 3.34 -> 3.11 ms/frame on CUDA, end to end -4%.
2026-07-30 22:53:58 +02:00
Pascal 35ebe5376b nit: drop obsolete ABI comments 2026-07-25 19:06:43 +02:00
Pascal 13686e8d34 logs 2026-07-25 18:56:39 +02:00
Pascal d03ffb97f9 api: derived codec left context, chunk width hoisted to qt_init
The left context of the buffered chunked decode is no longer a caller
knob: it derives from the codec's own sliding window (2x144 frames),
placing the default decode at the residual floor of the split.
codec_chunk_sec moves from qt_tts_params to qt_init_params, resolved
once to frames at load. The mid-struct removal bumps the ABI to a
closed range [QT_ABI_MIN_VERSION, QT_ABI_VERSION] = [4, 4]; the probe
asserts both bounds reject through the range check.
2026-07-25 18:56:28 +02:00
Pascal 710a52af75 ./format.sh (clang-format) 2026-07-25 11:26:59 +02:00
PascalandGitHub fb9d097946 Merge pull request #14 from DerGary/codec-chunk-dur-server-flags
tts-server: expose --codec-chunk-dur / --codec-left-dur
2026-07-25 11:25:34 +02:00
Gary 144a79f5cc tts-server: expose --codec-chunk-dur / --codec-left-dur
The CLI tool already exposes these flags for controlling the vocoder's
chunked-decode window. tts-server always used the hardcoded 24.0s
chunk / 2.0s left-context defaults, with no way to override them at
the server binary's CLI, unlike qwen-tts.

This matters on memory-constrained GPUs: utterances shorter than the
chunk duration decode in a single pass, which can OOM on a small GPU
shared with other processes. Tightening these values (e.g. 4.0/1.5)
forces genuine chunked decode with bounded peak memory per chunk.
2026-07-23 09:11:10 +02:00
Pascal 82cd05b9f3 ggml: fork update 2026-07-21 19:34:30 +02:00
Pascal ba4c7f7838 logs 2026-07-20 20:36:48 +02:00
Pascal 521503f50b vulkan: support misaligned get_rows operands
get_rows.comp already applies all three misalign offsets; the assert
predates that. Apply the b and d offsets in the quant variant too.
Only reject a quantized src0 with a sub-block offset, which cannot
be expressed in elements.
2026-07-20 20:06:02 +02:00
Pascal 877d8e1389 logs 2026-07-20 19:27:39 +02:00
Pascal 53c84fe1be engine: batched multi lane codec streaming, shared lockstep chunk ramp
One codec stream state set per lane plus a staging set, static graphs
per (chunk class, lane count) decoding every streaming lane in one
compute. The engine drops the per slot decoders and ownership
save/load for a shared 1 -> 2 -> 4 -> 8 ramp: lanes accumulate and
flush together, an admit drains and restarts at width 1, a retirement
drains then compacts the lane span device side. ICL references prime
through the staging set into per set snapshots. Depthwise and
transposed convs fold lanes into the channel axis; the dense convs
rely on the ggml conv_1d batched layout fix (submodule bump).
2026-07-20 19:13:05 +02:00
Pascal 9d7768e8ee ggml: fix conv_1d result layout for batched input
The GEMM result is [OL, N, OC], not [OL, OC, N].
Reshape and permute accordingly. N == 1 path is unchanged.
2026-07-20 19:12:51 +02:00
Pascal b2e36056eb graphs: express the SwiGLU MLPs through ggml_swiglu_split
The talker, code predictor, and tokenizer transformer built their MLPs
as two mul_mats followed by a separate silu and mul. Replacing the
silu + mul pair with the GGML_OP_GLU node keeps the gate and up
mul_mats adjacent to it, which is the exact pattern the CUDA backend
fusion pass matches (ggml_cuda_should_fuse_mul_mat): gate, up, and
activation collapse into one kernel on the GEMV decode shapes.

Applies to all five MLP sites: talker prefill and batched decode,
code predictor layer, tokenizer transformer offline and stream paths.
Validated bit-exact against HEAD on greedy and seed 42 (identical
WAVs, F32 master). Around +3 percent on the F32 AR frame; neutral on
correctness, two lines simpler per site.
2026-07-20 16:54:11 +02:00
Pascal 37ea692be6 engine: true parallel batching of the talker and predictor, per slot codec streams 2026-07-20 16:53:36 +02:00
Pascal fbff3317a4 ggml: bump submodule for full CUDA GET_ROWS type coverage
i-quants and mxfp4 join the k-quants on the direct device path, bringing
CUDA to parity with the Metal, Vulkan and SYCL backends.
2026-07-19 22:30:22 +02:00
Pascal e93292bee1 ggml: bump submodule for CUDA k-quant GET_ROWS support
Device-side embedding and codebook lookups now cover q2_K to q6_K, so a
quantized token_embd no longer drops the graph out of the direct device
path. i-quants are left as a TODO.
2026-07-19 21:29:06 +02:00
Pascal 7c4fea29ce ggml: fork update 2026-07-19 20:30:30 +02:00
Pascal 95b4840ad3 loader: validate raw u32 counts before the (int) cast 2026-07-17 21:46:24 +02:00
Pascal d5b05efff7 nit: realign endpoint tables on the widened voice routes 2026-07-17 21:13:57 +02:00
Pascal 0d1b25b863 clang-format 2026-07-17 21:13:57 +02:00
karlandPascal 8d556c9806 server: compatible with OpenAI API, add model alias 2026-07-17 21:13:57 +02:00
RaduAdumitroaeiandPascal 19bbf94dcc pipeline-codec: reset sched before encode alloc
The codec encode path allocated its graph without first resetting the
shared scheduler, unlike every sibling path (decode, prompt-builder,
speaker_encoder_extract). After a synthesis call leaves the codec sched
allocated, the next RVQ encode — e.g. registering a second cloned voice
after a generation — trips GGML_ASSERT(!sched->is_alloc) in
ggml_backend_sched_alloc_graph and crashes the server.

Reset the sched before alloc to match the other call sites.

Repro: register a voice (base model) -> generate speech -> register
another voice. Second register crashes without this fix.
2026-07-17 21:13:57 +02:00
Pascal 0e2b672329 backend: drop the process-wide backend cache inherited from acestep.cpp
backend_init returned a refcounted global BackendPair shared by every
context in the process. That cache is load bearing in acestep.cpp where
each module inits its own backend, but here the pipeline already shares
one BackendPair explicitly, so the cache never hit and only made
independent contexts collide: one CUDA VMM pool is a strict LIFO stack,
so two contexts interleaving alloc/free abort on the pool assert.

Each backend_init call now returns a fresh backend pair with its own
device context and memory pool, backend_release frees it directly, and
the one-time ggml_log_set + ggml_backend_load_all setup moves under a
magic static so concurrent context creation stays safe.

Single-context binaries (CLI, server) are bit-identical. Multi-context
embedders (python bindings running parallel pipelines) no longer crash.
2026-07-17 21:13:57 +02:00
Pascal 7bb91886f6 ggml: fork update 2026-07-14 15:14:03 +02:00
Pascal d17c33d4ee tts: reuse decode scratch buffers across streaming steps
Keep the talker causal mask in each static attention window graph and
reuse positions, KV rows and sliding attention mask buffers in each codec
stream graph class.

This removes the remaining host allocations from the talker and codec
streaming hot loops without changing graph execution.
2026-07-12 09:25:38 +02:00
Pascal bb250f57e4 backend: dedup consecutive identical ggml log lines
Install a ggml log callback that collapses exact consecutive
duplicates and reports the total count when the run ends. The CUDA
graph capture logs one reused line per replay step, flooding stderr
and stalling a reader that blocks on a full pipe.
2026-07-09 15:28:07 +02:00
Pascal e93b3fedd5 ggml: fork update 2026-07-08 19:51:08 +02:00
Pascal 31e1eb648e ggml: fork update 2026-07-08 19:23:44 +02:00
Pascal 0725d2e53b codec: adaptive chunk width on the streaming decode
The stream graphs come in width classes T in {1, 2, 4, 8} sharing the
state and KV ring tensors, built lazily per class. The decoder ramps
1 -> 2 -> 4 -> 8 so the first frame keeps its latency while the steady
state interleaves the codec 8x less often and the batch amortizes the
kernel count; drain flushes the tail at EOS and the reference priming
runs in max width chunks.
2026-07-06 22:43:52 +02:00
Pascal 206ad06fde talker: static decode graphs per attention window class
The decode flavor builds one static graph per 256 step kv window,
lazily on the first step entering the span, and replays directly on
the backend: ids, overlay, positions, kv row, and mask re-upload each
step since n_past moves. The prefill keeps the dynamic arena and sched
path, and the frame id assembly leaves the shared core.
2026-07-06 22:26:19 +02:00
Pascal 2862be4152 predictor: static graphs replayed with a single id upload per step
The 15 predictor flavors build and allocate once at load, positions,
kv rows, and mask baked as never freed graph outputs, and replay
directly on the backend. The prefill slices the last position before
lm_head so every flavor reads one logits row at offset zero. Replaces
the per step graph rebuild, sched allocation, and debug prints.
2026-07-06 21:46:08 +02:00
Pascal 66ebf32b2b ggml: fork update 2026-07-06 15:32:34 +02:00
Pascal 73fe0c67bb server: sampling overrides on the speech endpoint
The speech body accepts seed, max_new_tokens, temperature, top_k,
top_p, and repetition_penalty. Unset fields keep the engine defaults,
a temperature of zero selects greedy decoding, and the subtalker
mirrors the talker knobs. A fixed seed makes a request reproducible.
2026-07-05 15:01:37 +02:00
Pascal 1a680e8816 codec: primed state snapshot LRU keyed by the reference content
seed_reference hashes the ICL reference codes and restores the conv
contexts, KV ring, and position from a per reference snapshot slot on
a repeat, saving the primed state device to device after a fresh
prime. The reference priming cost amortizes across repeated cloned
voice requests.
2026-07-05 14:34:27 +02:00
Pascal 7a4c799f9c server: reject unknown voice names on base models
A speech request naming a voice that matches neither the registry nor
a model speaker returns 400 instead of silently generating without a
voice.
2026-07-05 13:45:49 +02:00
Pascal 0f1c8572c7 qwen: lazy speaker encoder load in the voice ref extraction path
qt_extract_voice_ref now pays the speaker encoder weight load on its
first call, mirroring the qt_synthesize ref_audio path. The server
extraction endpoint works without a prior ref wav synthesis.
2026-07-05 13:45:38 +02:00
Pascal 62dec12580 server: cloned voice registry over the OpenAI surface
POST /v1/voices registers a voice from a WAV extracted server side
through qt_extract_voice_ref or from pre extracted .spk and .rvq
latents taken verbatim, DELETE drops it and GET lists it alongside the
model speakers. A registered voice wins over a speaker of the same
name and injects the reference latents into qt_tts_params, ref_text
present selects ICL clone mode. The registry lives in process RAM
under the synthesis mutex, so registration and lookups never race a
running synthesis. The audio and rvq readers gain buffer variants
factored from the file paths. The README and the architecture
document catch up on the streaming decode, the hidden bridge, and the
server endpoints.
2026-07-05 12:28:55 +02:00
Pascal a37ff074ff codec: guard the KV ring against models with a wider sliding window 2026-07-05 10:36:58 +02:00
Pascal c39ef15bab logs 2026-07-05 10:25:31 +02:00
Pascal 2700d4c746 codec: stateful frame by frame streaming decode on a static graph
Every causal conv carries its left context in a persistent backend
tensor and every transposed conv its overlap tail, so a T=1 frame
decode reproduces the offline full decode exactly with zero re decoded
context. The tokenizer transformer attends over a sliding window KV
ring written through set_rows. The frame graph builds and allocates
once, then every frame is input uploads, one direct backend compute,
and one readback. The quantizer conts each codebook id view so the
Vulkan get_rows path accepts the direct compute. Each generated frame
emits its samples immediately and ICL priming feeds the full reference
through the same state. The buffered path keeps the chunked decode and
both codec framing knobs now apply to it alone.
2026-07-05 10:25:21 +02:00
Pascal bcac46352e logs 2026-07-05 07:47:07 +02:00
Pascal 415ef56330 tts: static decode graphs and device resident hidden bridge
KV writes go through set_rows with the destination rows carried as data
and the code predictor steps get one arena per sub step, so every
decode graph keeps a fixed topology and fixed tensor addresses step
after step. The talker last hidden stays resident on device in a
persistent bridge tensor, written by the talker graph and concatenated
as a leaf by the predictor prefill. The hot loop uploads sixteen code
ids and one overlay row and reads back the logits alone, host hidden
readbacks survive only under the dump path. Fewer nodes, transfers and
syncs pay on every backend, and the stable topology lets the CUDA
backend replay its captured graphs without an update.
2026-07-05 07:43:09 +02:00
Pascal e2e381f4f0 codec: persistent graph arena for the streaming decode path
pipeline_codec_decode builds its graph in a persistent arena instead of
a fresh ggml context per call. Constant size streaming slices rebuild
every node at the same address with identical shapes, which trims host
side graph churn on every backend and lets the CUDA backend replay its
captured graph executable without an update. The encode path keeps its
per call context.
2026-07-05 07:42:50 +02:00
Pascal f84fc05292 logs 2026-07-04 22:47:51 +02:00
Pascal 4057d0331f tts: seed the vocoder left context with the ICL reference tail
The upstream pipeline decodes reference plus generated codes then trims,
giving the first generated frames causal context from the reference; the
generated only decode started the vocoder from an empty state and
colored the clone onset. Seed both decode paths with the last
min(ref_T, left_ctx_frames) reference frames: the streaming decoder
takes them below its emit cursor so they are never emitted, the buffered
path prepends them and strips their samples. Raising
codec_left_context_sec past the reference duration reproduces the
upstream full reference decode exactly.
2026-07-04 22:47:41 +02:00
Pascal 3e3b6c1712 tts: lazy encoder loading and in graph embedding gathers
Load the codec encoder half (seanet, enc_transformer, enc_downsample,
qenc) and the speaker encoder lazily on their first real use: synthesis
from a pre extracted reference (--ref-spk --ref-rvq) now brings up only
the talker and the codec decoder, matching the load profile of a preset
voice server. Assemble the AR inputs on device: the talker decode graph
gathers and sums the 16 frame code embeddings plus the trailing text or
pad overlay via get_rows, and the code predictor gathers c0 and each
sampled sub code from its group table in graph. Per frame host traffic
drops from 16 gguf row reads plus a CPU sum plus 15 synchronous backend
readbacks to 16 code ids and one overlay row uploaded. The next-emb
parity dump reproduces the in graph composition on host under --dump
only, staying byte comparable against the Python hook.
2026-07-04 21:35:03 +02:00