mirror of
https://github.com/leejet/stable-diffusion.cpp.git
synced 2026-10-03 19:37:51 -05:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9947eebec8 | ||
|
|
168f7b88b9 | ||
|
|
2f886889e6 | ||
|
|
19bbbca1c7 | ||
|
|
39ada0863b |
@@ -64,6 +64,7 @@ API and command-line option may change frequently.***
|
||||
- [HiDream-O1-Image](./docs/hidream_o1_image.md)
|
||||
- [Ideogram4](./docs/ideogram4.md)
|
||||
- [LLaDA-Image](./docs/llada_image.md)
|
||||
- [PixArt](./docs/pixart.md)
|
||||
- [Image Edit Models](./docs/edit.md)
|
||||
- [FLUX.1-Kontext-dev](./docs/kontext.md)
|
||||
- [Qwen Image Edit series](./docs/qwen_image_edit.md)
|
||||
|
||||
Binary file not shown.
@@ -40,4 +40,6 @@ RUN printf '#!/bin/sh\nexec /sd.cpp/bin/sd-cli "$@"\n' > /sd-cli && \
|
||||
printf '#!/bin/sh\nexec /sd.cpp/bin/sd-server "$@"\n' > /sd-server && \
|
||||
chmod +x /sd-cli /sd-server
|
||||
|
||||
WORKDIR /sd.cpp
|
||||
|
||||
ENTRYPOINT [ "/sd-cli" ]
|
||||
|
||||
@@ -57,4 +57,6 @@ RUN printf '#!/bin/sh\nexec /sd.cpp/bin/sd-cli "$@"\n' > /sd-cli && \
|
||||
printf '#!/bin/sh\nexec /sd.cpp/bin/sd-server "$@"\n' > /sd-server && \
|
||||
chmod +x /sd-cli /sd-server
|
||||
|
||||
WORKDIR /sd.cpp
|
||||
|
||||
ENTRYPOINT [ "/sd-cli" ]
|
||||
|
||||
@@ -42,4 +42,6 @@ RUN printf '#!/bin/sh\nexec /sd.cpp/bin/sd-cli "$@"\n' > /sd-cli && \
|
||||
printf '#!/bin/sh\nexec /sd.cpp/bin/sd-server "$@"\n' > /sd-server && \
|
||||
chmod +x /sd-cli /sd-server
|
||||
|
||||
WORKDIR /sd.cpp
|
||||
|
||||
ENTRYPOINT [ "/sd-cli" ]
|
||||
|
||||
@@ -29,4 +29,6 @@ FROM intel/oneapi-basekit:${SYCL_VERSION}-devel-ubuntu24.04 AS runtime
|
||||
COPY --from=build /sd.cpp/build/bin/sd-cli /sd-cli
|
||||
COPY --from=build /sd.cpp/build/bin/sd-server /sd-server
|
||||
|
||||
WORKDIR /sd.cpp
|
||||
|
||||
ENTRYPOINT [ "/sd-cli" ]
|
||||
@@ -41,4 +41,6 @@ RUN printf '#!/bin/sh\nexec /sd.cpp/bin/sd-cli "$@"\n' > /sd-cli && \
|
||||
printf '#!/bin/sh\nexec /sd.cpp/bin/sd-server "$@"\n' > /sd-server && \
|
||||
chmod +x /sd-cli /sd-server
|
||||
|
||||
WORKDIR /sd.cpp
|
||||
|
||||
ENTRYPOINT [ "/sd-cli" ]
|
||||
|
||||
@@ -21,6 +21,54 @@ CPU fallback. It excludes weights and cache buffers. Within a runner lifecycle,
|
||||
the summary is printed only on the first graph or when backend capacities or the
|
||||
segment count change.
|
||||
|
||||
## Use VAE tiling to reduce encode and decode memory usage.
|
||||
|
||||
`--vae-tiling` enables spatial tiling for both VAE encoding and decoding. The
|
||||
default tile size is 256x256 **image pixels**, independent of the VAE scale factor:
|
||||
|
||||
```shell
|
||||
--vae-tiling --vae-tile-size 256x256 --vae-tile-overlap 0.5
|
||||
```
|
||||
|
||||
`--vae-tile-size` accepts one size or `WIDTHxHEIGHT`. A zero dimension uses the
|
||||
256-pixel default. Sizes are rounded down to a multiple of the VAE scale factor
|
||||
and capped at the current input dimensions. Explicit sizes below four latent
|
||||
pixels per axis (or the full axis when it is smaller) are rejected. Encoding and
|
||||
decoding use the same spatial sizes, without an additional encoding multiplier.
|
||||
Inputs that fit within a tile are processed as one tile.
|
||||
|
||||
For a 512x512 image with the default 50% overlap, both encoding and decoding use
|
||||
3x3 tiles. A 256-pixel tile corresponds to 32 latent pixels for an 8x VAE, 16 for
|
||||
a 16x VAE, and 8 for a 32x VAE. Smaller tiles reduce each graph's memory demand,
|
||||
but overlapping work can increase processing time and tiling can affect image
|
||||
quality, especially during encoding. Use larger tiles when more context is needed.
|
||||
|
||||
`--vae-relative-tile-size` overrides the absolute size on each axis with a positive
|
||||
value. Values up to and including 1 specify a fraction of the current input size;
|
||||
values greater than 1 specify a target number of tiles per axis, accounting for
|
||||
overlap. For example, `0.5x0.5` uses half the width and height in both encode and
|
||||
decode. The target overlap is clamped to 0 through 0.5 and the actual overlap is
|
||||
adjusted to fit the image. Size and overlap options require `--vae-tiling`.
|
||||
|
||||
**Migration:** `--vae-tile-size` and the C/JSON fields `tile_size_w` and
|
||||
`tile_size_h` now use image pixels instead of latent units. The C/JSON fields
|
||||
`tile_size_x/y` have been renamed to `tile_size_w/h`, and `rel_size_x/y` to
|
||||
`rel_size_w/h`. The command-line option names are unchanged. For example, an old
|
||||
decode tile size of 32 corresponds to 256 pixels for an 8x VAE or 512 pixels for a
|
||||
16x VAE. Encoding no longer enlarges explicit or relative tile sizes.
|
||||
|
||||
The main VAE decode path retries allocation failures with smaller tiles, even
|
||||
without `--vae-tiling`. Supported video VAEs first try temporal tiling; spatial
|
||||
retries use at most 256-pixel tiles initially and then halve the effective tile
|
||||
dimensions down to the minimum size. Each spatial retry must reduce the effective
|
||||
tile size. These runtime adjustments do not change the caller's parameters.
|
||||
Execution failures are not retried, and encoding has no automatic OOM retry.
|
||||
|
||||
`--temporal-tiling` remains independent of spatial tiling. MiniMax H3 always uses
|
||||
spatial tiling (256x256 pixels and 25% overlap by default) and its own temporal
|
||||
windows. With `--vae-tiling`, its overlap follows `--vae-tile-overlap`; explicit
|
||||
spatial sizes are honored.
|
||||
|
||||
## Offload weights to the CPU to save VRAM without reducing generation speed.
|
||||
|
||||
Using `--offload-to-cpu` allows you to offload weights to the CPU, saving VRAM without reducing generation speed.
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
# How to Use
|
||||
|
||||
You can run PixArt-α / PixArt-Σ with stable-diffusion.cpp.
|
||||
|
||||
PixArt is a DiT-based text-to-image model family conditioned by a T5-XXL text
|
||||
encoder and a 4-channel VAE: SDXL-style for PixArt-Σ and SD1.x-style for PixArt-α.
|
||||
|
||||
## Download weights
|
||||
|
||||
- Download the transformer (diffusion model)
|
||||
- PixArt-Σ XL-2 1024-MS: https://huggingface.co/PixArt-alpha/PixArt-Sigma-XL-2-1024-MS/tree/main/transformer
|
||||
- PixArt-α XL-2 1024-MS: https://huggingface.co/PixArt-alpha/PixArt-XL-2-1024-MS/tree/main/transformer
|
||||
- Download the T5-XXL text encoder
|
||||
- safetensors: https://huggingface.co/PixArt-alpha/PixArt-Sigma-XL-2-1024-MS/tree/main/text_encoder
|
||||
- Download the VAE
|
||||
- PixArt-Σ: https://huggingface.co/PixArt-alpha/PixArt-Sigma-XL-2-1024-MS/tree/main/vae
|
||||
- PixArt-α: https://huggingface.co/PixArt-alpha/PixArt-XL-2-1024-MS/tree/main/vae
|
||||
- Use the VAE matching the checkpoint's latent space. For TAE decoding or
|
||||
preview, use TAESDXL for PixArt-Σ and TAESD for PixArt-α.
|
||||
- Tokenizer: the T5 vocabulary is embedded; no extra tokenizer file is needed.
|
||||
|
||||
## Examples
|
||||
|
||||
```
|
||||
.\bin\Release\sd-cli.exe --diffusion-model ..\models\diffusion_models\pixart_sigma_xl2_1024_ms.safetensors --t5xxl ..\models\text_encoders\t5xxl.safetensors --vae ..\models\vae\pixart_vae.safetensors -p "a lovely cat" --cfg-scale 4.5 -W 1024 -H 1024 --steps 20 -v
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- The VAE scaling factor defaults to `0.13025` for PixArt-Σ. PixArt-α
|
||||
checkpoints with resolution micro-condition weights use `0.18215`.
|
||||
PixArt-α 512 has the same tensor layout as PixArt-Σ, so it requires an
|
||||
explicit override: `--model-args "pixart_vae_scale_factor=0.18215"`.
|
||||
This argument can also override the scale for other compatible checkpoints.
|
||||
- PixArt-Σ checkpoints compute 2D sincos positional embeddings at runtime;
|
||||
the trained grid is 64x64 patches with an interpolation scale of 2.
|
||||
For checkpoints trained at a different resolution, the positional embedding
|
||||
parameters can be adjusted via model args:
|
||||
`--model-args "pixart_pos_embed_base_size=<trained grid>,pixart_interpolation_scale=<scale>"`
|
||||
(e.g. `pixart_pos_embed_base_size=32,pixart_interpolation_scale=1,pixart_vae_scale_factor=0.18215` for
|
||||
PixArt-α XL-2 512).
|
||||
- Checkpoints carrying resolution/aspect-ratio micro-condition weights are
|
||||
detected but those conditions are not applied yet; a warning is logged and
|
||||
generation proceeds with the timestep embedding only.
|
||||
- The transformer predicts 8 channels (noise + learned variance); only the
|
||||
noise half is used for sampling, matching the reference implementation.
|
||||
+25
@@ -40,6 +40,9 @@ Wan models require `-M vid_gen`, including single-frame generation. `--video-fra
|
||||
- safetensors: https://huggingface.co/Comfy-Org/Wan_2.2_ComfyUI_Repackaged/tree/main/split_files/diffusion_models
|
||||
- gguf: https://huggingface.co/QuantStack/Wan2.2-S2V-14B-GGUF/tree/main
|
||||
- int8_convrot safetensors: https://huggingface.co/noctrex/Wan2.2-S2V-14B-int8_convrot
|
||||
- Wan2.2 VACE-Fun A14B
|
||||
- safetensors: https://huggingface.co/alibaba-pai/Wan2.2-VACE-Fun-A14B
|
||||
- gguf: https://huggingface.co/QuantStack/Wan2.2-VACE-Fun-A14B-GGUF/tree/main
|
||||
- Download vae
|
||||
- wan_2.1_vae (for all the wan model except Wan2.2 TI2V 5B)
|
||||
- safetensors: https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/blob/main/split_files/vae/wan_2.1_vae.safetensors
|
||||
@@ -256,3 +259,25 @@ ffmpeg -i ..\..\ComfyUI\input\post+depth.mp4 -qscale:v 1 -vf fps=8 post+depth\fr
|
||||
```
|
||||
|
||||
<video src=../assets/wan/Wan2.1_14B_vace_v2v.mp4 controls="controls" muted="muted" type="video/mp4"></video>
|
||||
|
||||
### Wan2.2 VACE-Fun A14B
|
||||
|
||||
VACE-Fun runs as a MoE pair: `--diffusion-model` takes the low-noise expert and
|
||||
`--high-noise-diffusion-model` the high-noise one. Reference-to-video uses `-i`
|
||||
for the reference image, same as Wan2.1 VACE.
|
||||
|
||||
#### R2V
|
||||
|
||||
```
|
||||
.\bin\Release\sd-cli.exe -M vid_gen --diffusion-model ..\models\diffusion_models\Wan2.2-VACE-Fun-A14B-low-noise-Q8_0.gguf --high-noise-diffusion-model ..\models\diffusion_models\Wan2.2-VACE-Fun-A14B-high-noise-Q8_0.gguf --vae ..\models\vae\wan_2.1_vae.safetensors --t5xxl ..\models\text_encoders\umt5-xxl-encoder-Q8_0.gguf -p "a lovely cat" --cfg-scale 3.5 --sampling-method euler --steps 10 --high-noise-cfg-scale 3.5 --high-noise-sampling-method euler --high-noise-steps 8 -v -n "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部, 畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" -W 832 -H 480 --diffusion-fa -i ..\assets\cat_with_sd_cpp_42.png --video-frames 33 --offload-to-cpu
|
||||
```
|
||||
|
||||
<video src=../assets/wan/Wan2.2_A14B_vace_r2v.mp4 controls="controls" muted="muted" type="video/mp4"></video>
|
||||
|
||||
#### T2V
|
||||
|
||||
Same command without `-i` (VACE context is synthesized from an empty control
|
||||
video, like Wan2.1 VACE t2v).
|
||||
|
||||
> On GPUs with ~12 GB VRAM, VACE also needs `--vae-tiling` — the control-video
|
||||
> encode can exceed the budget otherwise.
|
||||
|
||||
+29
-29
@@ -1341,7 +1341,7 @@ ArgOptions SDGenerationParams::get_options() {
|
||||
&embed_image_metadata},
|
||||
{"",
|
||||
"--vae-tiling",
|
||||
"process vae in tiles to reduce memory usage",
|
||||
"process vae encode and decode in spatial tiles to reduce memory usage (default: 256x256 image pixels)",
|
||||
true,
|
||||
&vae_tiling_params.enabled},
|
||||
{"",
|
||||
@@ -1605,12 +1605,12 @@ ArgOptions SDGenerationParams::get_options() {
|
||||
size_t x_pos = tile_size_str.find('x');
|
||||
try {
|
||||
if (x_pos != std::string::npos) {
|
||||
std::string tile_x_str = tile_size_str.substr(0, x_pos);
|
||||
std::string tile_y_str = tile_size_str.substr(x_pos + 1);
|
||||
vae_tiling_params.tile_size_x = std::stoi(tile_x_str);
|
||||
vae_tiling_params.tile_size_y = std::stoi(tile_y_str);
|
||||
std::string tile_w_str = tile_size_str.substr(0, x_pos);
|
||||
std::string tile_h_str = tile_size_str.substr(x_pos + 1);
|
||||
vae_tiling_params.tile_size_w = std::stoi(tile_w_str);
|
||||
vae_tiling_params.tile_size_h = std::stoi(tile_h_str);
|
||||
} else {
|
||||
vae_tiling_params.tile_size_x = vae_tiling_params.tile_size_y = std::stoi(tile_size_str);
|
||||
vae_tiling_params.tile_size_w = vae_tiling_params.tile_size_h = std::stoi(tile_size_str);
|
||||
}
|
||||
} catch (const std::invalid_argument&) {
|
||||
return -1;
|
||||
@@ -1628,12 +1628,12 @@ ArgOptions SDGenerationParams::get_options() {
|
||||
size_t x_pos = rel_size_str.find('x');
|
||||
try {
|
||||
if (x_pos != std::string::npos) {
|
||||
std::string rel_x_str = rel_size_str.substr(0, x_pos);
|
||||
std::string rel_y_str = rel_size_str.substr(x_pos + 1);
|
||||
vae_tiling_params.rel_size_x = std::stof(rel_x_str);
|
||||
vae_tiling_params.rel_size_y = std::stof(rel_y_str);
|
||||
std::string rel_w_str = rel_size_str.substr(0, x_pos);
|
||||
std::string rel_h_str = rel_size_str.substr(x_pos + 1);
|
||||
vae_tiling_params.rel_size_w = std::stof(rel_w_str);
|
||||
vae_tiling_params.rel_size_h = std::stof(rel_h_str);
|
||||
} else {
|
||||
vae_tiling_params.rel_size_x = vae_tiling_params.rel_size_y = std::stof(rel_size_str);
|
||||
vae_tiling_params.rel_size_w = vae_tiling_params.rel_size_h = std::stof(rel_size_str);
|
||||
}
|
||||
} catch (const std::invalid_argument&) {
|
||||
return -1;
|
||||
@@ -1763,11 +1763,11 @@ ArgOptions SDGenerationParams::get_options() {
|
||||
on_scm_policy_arg},
|
||||
{"",
|
||||
"--vae-tile-size",
|
||||
"tile size for vae tiling in latent units, not image pixels, format [X]x[Y] (default: 32x32)",
|
||||
"tile size for vae encode and decode in image pixels, format [W]x[H] or [S] (default: 256x256; requires --vae-tiling)",
|
||||
on_tile_size_arg},
|
||||
{"",
|
||||
"--vae-relative-tile-size",
|
||||
"relative tile size for vae tiling, format [X]x[Y], in fraction of image size if < 1, in number of tiles per dim if >=1 (overrides --vae-tile-size)",
|
||||
"relative tile size for vae encode and decode, format [W]x[H] or [S]: <=1 is a dimension fraction, >1 a target tile count (overrides --vae-tile-size; requires --vae-tiling)",
|
||||
on_relative_tile_size_arg},
|
||||
{"",
|
||||
"--prompt-file",
|
||||
@@ -2224,20 +2224,20 @@ bool SDGenerationParams::from_json_str(
|
||||
if (tiling_json.contains("temporal_tiling") && tiling_json["temporal_tiling"].is_boolean()) {
|
||||
vae_tiling_params.temporal_tiling = tiling_json["temporal_tiling"];
|
||||
}
|
||||
if (tiling_json.contains("tile_size_x") && tiling_json["tile_size_x"].is_number_integer()) {
|
||||
vae_tiling_params.tile_size_x = tiling_json["tile_size_x"];
|
||||
if (tiling_json.contains("tile_size_w") && tiling_json["tile_size_w"].is_number_integer()) {
|
||||
vae_tiling_params.tile_size_w = tiling_json["tile_size_w"];
|
||||
}
|
||||
if (tiling_json.contains("tile_size_y") && tiling_json["tile_size_y"].is_number_integer()) {
|
||||
vae_tiling_params.tile_size_y = tiling_json["tile_size_y"];
|
||||
if (tiling_json.contains("tile_size_h") && tiling_json["tile_size_h"].is_number_integer()) {
|
||||
vae_tiling_params.tile_size_h = tiling_json["tile_size_h"];
|
||||
}
|
||||
if (tiling_json.contains("target_overlap") && tiling_json["target_overlap"].is_number()) {
|
||||
vae_tiling_params.target_overlap = tiling_json["target_overlap"];
|
||||
}
|
||||
if (tiling_json.contains("rel_size_x") && tiling_json["rel_size_x"].is_number()) {
|
||||
vae_tiling_params.rel_size_x = tiling_json["rel_size_x"];
|
||||
if (tiling_json.contains("rel_size_w") && tiling_json["rel_size_w"].is_number()) {
|
||||
vae_tiling_params.rel_size_w = tiling_json["rel_size_w"];
|
||||
}
|
||||
if (tiling_json.contains("rel_size_y") && tiling_json["rel_size_y"].is_number()) {
|
||||
vae_tiling_params.rel_size_y = tiling_json["rel_size_y"];
|
||||
if (tiling_json.contains("rel_size_h") && tiling_json["rel_size_h"].is_number()) {
|
||||
vae_tiling_params.rel_size_h = tiling_json["rel_size_h"];
|
||||
}
|
||||
if (tiling_json.contains("extra_tiling_args") && tiling_json["extra_tiling_args"].is_string()) {
|
||||
extra_tiling_args = tiling_json["extra_tiling_args"].get<std::string>();
|
||||
@@ -2934,11 +2934,11 @@ std::string SDGenerationParams::to_string() const {
|
||||
<< " vae_tiling_params: { "
|
||||
<< vae_tiling_params.enabled << ", "
|
||||
<< vae_tiling_params.temporal_tiling << ", "
|
||||
<< vae_tiling_params.tile_size_x << ", "
|
||||
<< vae_tiling_params.tile_size_y << ", "
|
||||
<< vae_tiling_params.tile_size_w << ", "
|
||||
<< vae_tiling_params.tile_size_h << ", "
|
||||
<< vae_tiling_params.target_overlap << ", "
|
||||
<< vae_tiling_params.rel_size_x << ", "
|
||||
<< vae_tiling_params.rel_size_y << ", "
|
||||
<< vae_tiling_params.rel_size_w << ", "
|
||||
<< vae_tiling_params.rel_size_h << ", "
|
||||
<< "\"" << extra_tiling_args << "\" },\n"
|
||||
<< "}";
|
||||
return oss.str();
|
||||
@@ -3140,11 +3140,11 @@ std::string build_sdcpp_image_metadata_json(const SDContextParams& ctx_params,
|
||||
root["vae_tiling"] = {
|
||||
{"enabled", gen_params.vae_tiling_params.enabled},
|
||||
{"temporal_tiling", gen_params.vae_tiling_params.temporal_tiling},
|
||||
{"tile_size_x", gen_params.vae_tiling_params.tile_size_x},
|
||||
{"tile_size_y", gen_params.vae_tiling_params.tile_size_y},
|
||||
{"tile_size_w", gen_params.vae_tiling_params.tile_size_w},
|
||||
{"tile_size_h", gen_params.vae_tiling_params.tile_size_h},
|
||||
{"target_overlap", gen_params.vae_tiling_params.target_overlap},
|
||||
{"rel_size_x", gen_params.vae_tiling_params.rel_size_x},
|
||||
{"rel_size_y", gen_params.vae_tiling_params.rel_size_y},
|
||||
{"rel_size_w", gen_params.vae_tiling_params.rel_size_w},
|
||||
{"rel_size_h", gen_params.vae_tiling_params.rel_size_h},
|
||||
{"extra_tiling_args", gen_params.extra_tiling_args},
|
||||
};
|
||||
}
|
||||
|
||||
+22
-20
@@ -524,11 +524,11 @@ Shared default fields used by both `img_gen` and `vid_gen`:
|
||||
| `vae_tiling_params` | `object` |
|
||||
| `vae_tiling_params.enabled` | `boolean` |
|
||||
| `vae_tiling_params.temporal_tiling` | `boolean` |
|
||||
| `vae_tiling_params.tile_size_x` | `integer` |
|
||||
| `vae_tiling_params.tile_size_y` | `integer` |
|
||||
| `vae_tiling_params.tile_size_w` | `integer` |
|
||||
| `vae_tiling_params.tile_size_h` | `integer` |
|
||||
| `vae_tiling_params.target_overlap` | `number` |
|
||||
| `vae_tiling_params.rel_size_x` | `number` |
|
||||
| `vae_tiling_params.rel_size_y` | `number` |
|
||||
| `vae_tiling_params.rel_size_w` | `number` |
|
||||
| `vae_tiling_params.rel_size_h` | `number` |
|
||||
| `vae_tiling_params.extra_tiling_args` | `string` |
|
||||
| `cache_mode` | `string` |
|
||||
| `cache_option` | `string` |
|
||||
@@ -537,6 +537,8 @@ Shared default fields used by both `img_gen` and `vid_gen`:
|
||||
| `output_format` | `string` |
|
||||
| `output_compression` | `integer` |
|
||||
|
||||
`vae_tiling_params.tile_size_w` and `tile_size_h` are in **image pixels**, with `0` selecting the 256-pixel default. Both encode and decode use these sizes without an encoding multiplier. Positive `rel_size_w`/`rel_size_h` values override the corresponding absolute size: values up to 1 are dimension fractions, and values greater than 1 are target tile counts. Set `enabled` to use spatial tiling. Sizes are aligned down to the VAE scale factor and capped at the input dimensions; explicit sizes below the minimum supported tile size are rejected. These fields previously used latent units; see [VAE tiling](../../docs/performance.md#use-vae-tiling-to-reduce-encode-and-decode-memory-usage) for migration and OOM retry behavior.
|
||||
|
||||
`vae_tiling_params.extra_tiling_args` accepts a key=value list. Supported video VAEs accept `temporal_tile_frames` (alias `temporal_tile_size`, default `4`) and `temporal_tile_overlap` (default `1`).
|
||||
LTX and Wan preserve causal state between temporal tiles. Hunyuan Video and TAEHV use overlap blending. MiniMax H3 keeps its model-specific fixed temporal windows because its latent-to-frame mapping is non-linear.
|
||||
|
||||
@@ -767,11 +769,11 @@ Example:
|
||||
"vae_tiling_params": {
|
||||
"enabled": false,
|
||||
"temporal_tiling": false,
|
||||
"tile_size_x": 0,
|
||||
"tile_size_y": 0,
|
||||
"tile_size_w": 0,
|
||||
"tile_size_h": 0,
|
||||
"target_overlap": 0.5,
|
||||
"rel_size_x": 0.0,
|
||||
"rel_size_y": 0.0,
|
||||
"rel_size_w": 0.0,
|
||||
"rel_size_h": 0.0,
|
||||
"extra_tiling_args": ""
|
||||
},
|
||||
|
||||
@@ -900,11 +902,11 @@ Other native fields:
|
||||
| `vae_tiling_params` | `object` |
|
||||
| `vae_tiling_params.enabled` | `boolean` |
|
||||
| `vae_tiling_params.temporal_tiling` | `boolean` |
|
||||
| `vae_tiling_params.tile_size_x` | `integer` |
|
||||
| `vae_tiling_params.tile_size_y` | `integer` |
|
||||
| `vae_tiling_params.tile_size_w` | `integer` |
|
||||
| `vae_tiling_params.tile_size_h` | `integer` |
|
||||
| `vae_tiling_params.target_overlap` | `number` |
|
||||
| `vae_tiling_params.rel_size_x` | `number` |
|
||||
| `vae_tiling_params.rel_size_y` | `number` |
|
||||
| `vae_tiling_params.rel_size_w` | `number` |
|
||||
| `vae_tiling_params.rel_size_h` | `number` |
|
||||
| `vae_tiling_params.extra_tiling_args` | `string` |
|
||||
| `cache_mode` | `string` |
|
||||
| `cache_option` | `string` |
|
||||
@@ -1115,11 +1117,11 @@ Example:
|
||||
"vae_tiling_params": {
|
||||
"enabled": false,
|
||||
"temporal_tiling": false,
|
||||
"tile_size_x": 0,
|
||||
"tile_size_y": 0,
|
||||
"tile_size_w": 0,
|
||||
"tile_size_h": 0,
|
||||
"target_overlap": 0.5,
|
||||
"rel_size_x": 0.0,
|
||||
"rel_size_y": 0.0,
|
||||
"rel_size_w": 0.0,
|
||||
"rel_size_h": 0.0,
|
||||
"extra_tiling_args": ""
|
||||
},
|
||||
|
||||
@@ -1240,11 +1242,11 @@ Other native fields:
|
||||
| `vae_tiling_params` | `object` |
|
||||
| `vae_tiling_params.enabled` | `boolean` |
|
||||
| `vae_tiling_params.temporal_tiling` | `boolean` |
|
||||
| `vae_tiling_params.tile_size_x` | `integer` |
|
||||
| `vae_tiling_params.tile_size_y` | `integer` |
|
||||
| `vae_tiling_params.tile_size_w` | `integer` |
|
||||
| `vae_tiling_params.tile_size_h` | `integer` |
|
||||
| `vae_tiling_params.target_overlap` | `number` |
|
||||
| `vae_tiling_params.rel_size_x` | `number` |
|
||||
| `vae_tiling_params.rel_size_y` | `number` |
|
||||
| `vae_tiling_params.rel_size_w` | `number` |
|
||||
| `vae_tiling_params.rel_size_h` | `number` |
|
||||
| `vae_tiling_params.extra_tiling_args` | `string` |
|
||||
| `cache_mode` | `string` |
|
||||
| `cache_option` | `string` |
|
||||
|
||||
+1
-1
Submodule examples/server/frontend updated: c4bce3d6b3...dd74a8e808
@@ -78,11 +78,11 @@ static json make_vae_tiling_json(const sd_tiling_params_t& params) {
|
||||
return {
|
||||
{"enabled", params.enabled},
|
||||
{"temporal_tiling", params.temporal_tiling},
|
||||
{"tile_size_x", params.tile_size_x},
|
||||
{"tile_size_y", params.tile_size_y},
|
||||
{"tile_size_w", params.tile_size_w},
|
||||
{"tile_size_h", params.tile_size_h},
|
||||
{"target_overlap", params.target_overlap},
|
||||
{"rel_size_x", params.rel_size_x},
|
||||
{"rel_size_y", params.rel_size_y},
|
||||
{"rel_size_w", params.rel_size_w},
|
||||
{"rel_size_h", params.rel_size_h},
|
||||
{"extra_tiling_args", params.extra_tiling_args ? params.extra_tiling_args : ""},
|
||||
};
|
||||
}
|
||||
|
||||
+97
-45
@@ -255,27 +255,48 @@ void refresh_lora_cache(ServerRuntime& rt) {
|
||||
std::vector<LoraEntry> new_cache;
|
||||
|
||||
fs::path lora_dir = rt.ctx_params->lora_model_dir;
|
||||
if (fs::exists(lora_dir) && fs::is_directory(lora_dir)) {
|
||||
for (auto& entry : fs::recursive_directory_iterator(lora_dir, fs::directory_options::skip_permission_denied)) {
|
||||
if (!entry.is_regular_file()) {
|
||||
continue;
|
||||
}
|
||||
const fs::path& p = entry.path();
|
||||
if (!is_supported_model_ext(p)) {
|
||||
continue;
|
||||
}
|
||||
std::error_code ec;
|
||||
if (fs::exists(lora_dir, ec) && !ec && fs::is_directory(lora_dir, ec) && !ec) {
|
||||
try {
|
||||
auto it = fs::recursive_directory_iterator(
|
||||
lora_dir,
|
||||
fs::directory_options::skip_permission_denied,
|
||||
ec);
|
||||
auto end = fs::recursive_directory_iterator();
|
||||
while (!ec && it != end) {
|
||||
std::error_code entry_ec;
|
||||
bool is_reg = it->is_regular_file(entry_ec);
|
||||
if (entry_ec || !is_reg) {
|
||||
it.increment(ec);
|
||||
continue;
|
||||
}
|
||||
const fs::path& p = it->path();
|
||||
if (!is_supported_model_ext(p)) {
|
||||
it.increment(ec);
|
||||
continue;
|
||||
}
|
||||
|
||||
LoraEntry lora_entry;
|
||||
lora_entry.name = p.stem().u8string();
|
||||
lora_entry.fullpath = p.u8string();
|
||||
std::string rel = p.lexically_relative(lora_dir).u8string();
|
||||
std::replace(rel.begin(), rel.end(), '\\', '/');
|
||||
lora_entry.path = rel;
|
||||
LoraEntry lora_entry;
|
||||
lora_entry.name = p.stem().u8string();
|
||||
lora_entry.fullpath = p.u8string();
|
||||
std::string rel = p.lexically_relative(lora_dir).u8string();
|
||||
std::replace(rel.begin(), rel.end(), '\\', '/');
|
||||
lora_entry.path = rel;
|
||||
|
||||
new_cache.push_back(std::move(lora_entry));
|
||||
new_cache.push_back(std::move(lora_entry));
|
||||
it.increment(ec);
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
LOG_WARN("error while scanning lora directory '%s': %s", lora_dir.string().c_str(), e.what());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (ec) {
|
||||
LOG_WARN("error while scanning lora directory '%s': %s", lora_dir.string().c_str(), ec.message().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
std::sort(new_cache.begin(), new_cache.end(), [](const LoraEntry& a, const LoraEntry& b) {
|
||||
return a.path < b.path;
|
||||
});
|
||||
@@ -302,40 +323,71 @@ void refresh_upscaler_cache(ServerRuntime& rt) {
|
||||
}
|
||||
|
||||
fs::path upscaler_dir = rt.ctx_params->hires_upscalers_dir;
|
||||
if (fs::exists(upscaler_dir) && fs::is_directory(upscaler_dir)) {
|
||||
for (auto& entry : fs::directory_iterator(upscaler_dir)) {
|
||||
if (!entry.is_regular_file()) {
|
||||
continue;
|
||||
}
|
||||
const fs::path& p = entry.path();
|
||||
if (!is_supported_model_ext(p)) {
|
||||
continue;
|
||||
}
|
||||
std::error_code ec;
|
||||
if (fs::exists(upscaler_dir, ec) && !ec && fs::is_directory(upscaler_dir, ec) && !ec) {
|
||||
try {
|
||||
auto it = fs::directory_iterator(
|
||||
upscaler_dir,
|
||||
fs::directory_options::skip_permission_denied,
|
||||
ec);
|
||||
auto end = fs::directory_iterator();
|
||||
while (!ec && it != end) {
|
||||
std::error_code entry_ec;
|
||||
bool is_reg = it->is_regular_file(entry_ec);
|
||||
if (entry_ec || !is_reg) {
|
||||
it.increment(ec);
|
||||
continue;
|
||||
}
|
||||
const fs::path& p = it->path();
|
||||
if (!is_supported_model_ext(p)) {
|
||||
it.increment(ec);
|
||||
continue;
|
||||
}
|
||||
|
||||
UpscalerEntry upscaler_entry;
|
||||
upscaler_entry.name = p.stem().u8string();
|
||||
upscaler_entry.fullpath = fs::absolute(p).lexically_normal().u8string();
|
||||
upscaler_entry.model_name = "ESRGAN_4x";
|
||||
upscaler_entry.path = p.filename().u8string();
|
||||
upscaler_entry.file_size = entry.file_size();
|
||||
upscaler_entry.last_modified = entry.last_write_time();
|
||||
auto previous = std::find_if(previous_cache.begin(), previous_cache.end(), [&](const UpscalerEntry& cached) {
|
||||
return cached.fullpath == upscaler_entry.fullpath &&
|
||||
cached.file_size == upscaler_entry.file_size &&
|
||||
cached.last_modified == upscaler_entry.last_modified;
|
||||
});
|
||||
upscaler_entry.image_upscale_factor = previous != previous_cache.end()
|
||||
? previous->image_upscale_factor
|
||||
: get_upscaler_model_scale(upscaler_entry.fullpath.c_str());
|
||||
if (upscaler_entry.image_upscale_factor > 0) {
|
||||
upscaler_entry.scale = upscaler_entry.image_upscale_factor;
|
||||
upscaler_entry.model_name = "ESRGAN_" + std::to_string(upscaler_entry.scale) + "x";
|
||||
}
|
||||
UpscalerEntry upscaler_entry;
|
||||
upscaler_entry.name = p.stem().u8string();
|
||||
std::error_code abs_ec;
|
||||
fs::path abs_path = fs::absolute(p, abs_ec);
|
||||
upscaler_entry.fullpath = (abs_ec ? p : abs_path.lexically_normal()).u8string();
|
||||
upscaler_entry.model_name = "ESRGAN_4x";
|
||||
upscaler_entry.path = p.filename().u8string();
|
||||
|
||||
new_cache.push_back(std::move(upscaler_entry));
|
||||
std::error_code size_ec;
|
||||
upscaler_entry.file_size = it->file_size(size_ec);
|
||||
if (size_ec) {
|
||||
it.increment(ec);
|
||||
continue;
|
||||
}
|
||||
|
||||
std::error_code time_ec;
|
||||
upscaler_entry.last_modified = it->last_write_time(time_ec);
|
||||
auto previous = std::find_if(previous_cache.begin(), previous_cache.end(), [&](const UpscalerEntry& cached) {
|
||||
return cached.fullpath == upscaler_entry.fullpath &&
|
||||
cached.file_size == upscaler_entry.file_size &&
|
||||
(!time_ec && cached.last_modified == upscaler_entry.last_modified);
|
||||
});
|
||||
upscaler_entry.image_upscale_factor = previous != previous_cache.end()
|
||||
? previous->image_upscale_factor
|
||||
: get_upscaler_model_scale(upscaler_entry.fullpath.c_str());
|
||||
if (upscaler_entry.image_upscale_factor > 0) {
|
||||
upscaler_entry.scale = upscaler_entry.image_upscale_factor;
|
||||
upscaler_entry.model_name = "ESRGAN_" + std::to_string(upscaler_entry.scale) + "x";
|
||||
}
|
||||
|
||||
new_cache.push_back(std::move(upscaler_entry));
|
||||
it.increment(ec);
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
LOG_WARN("error while scanning upscalers directory '%s': %s", upscaler_dir.string().c_str(), e.what());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (ec) {
|
||||
LOG_WARN("error while scanning upscalers directory '%s': %s", upscaler_dir.string().c_str(), ec.message().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
std::sort(new_cache.begin(), new_cache.end(), [](const UpscalerEntry& a, const UpscalerEntry& b) {
|
||||
return a.name < b.name;
|
||||
});
|
||||
|
||||
@@ -173,11 +173,13 @@ enum lora_apply_mode_t {
|
||||
typedef struct {
|
||||
bool enabled;
|
||||
bool temporal_tiling;
|
||||
int tile_size_x;
|
||||
int tile_size_y;
|
||||
// Spatial tile dimensions in image pixels for both encode and decode; 0 uses 256.
|
||||
int tile_size_w;
|
||||
int tile_size_h;
|
||||
float target_overlap;
|
||||
float rel_size_x;
|
||||
float rel_size_y;
|
||||
// Positive values override tile_size: <= 1 is a dimension fraction, > 1 a target tile count.
|
||||
float rel_size_w;
|
||||
float rel_size_h;
|
||||
const char* extra_tiling_args;
|
||||
} sd_tiling_params_t;
|
||||
|
||||
|
||||
+30
-13
@@ -478,7 +478,12 @@ namespace sd::backend_fit {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool prepare_vae_decode_retry_tiling(sd_tiling_params_t& tiling_params, bool prefer_temporal_tiling, ggml_status status) {
|
||||
bool prepare_vae_decode_retry_tiling(sd_tiling_params_t& tiling_params,
|
||||
bool prefer_temporal_tiling,
|
||||
ggml_status status,
|
||||
int latent_tile_size_w,
|
||||
int latent_tile_size_h,
|
||||
int scale_factor) {
|
||||
// Execution failures can leave the device unusable; tiling only helps with allocation failures.
|
||||
if (status != GGML_STATUS_ALLOC_FAILED) {
|
||||
return false;
|
||||
@@ -487,19 +492,31 @@ namespace sd::backend_fit {
|
||||
if (prefer_temporal_tiling && !tiling_params.temporal_tiling) {
|
||||
tiling_params.temporal_tiling = true;
|
||||
retry_mode = tiling_params.enabled ? "spatial+temporal" : "temporal";
|
||||
} else if (!tiling_params.enabled) {
|
||||
tiling_params.enabled = true;
|
||||
tiling_params.rel_size_x = 0.5f;
|
||||
tiling_params.rel_size_y = 0.5f;
|
||||
if (tiling_params.tile_size_x <= 0) {
|
||||
tiling_params.tile_size_x = 256;
|
||||
}
|
||||
if (tiling_params.tile_size_y <= 0) {
|
||||
tiling_params.tile_size_y = 256;
|
||||
}
|
||||
retry_mode = tiling_params.temporal_tiling ? "spatial+temporal" : "spatial";
|
||||
} else {
|
||||
return false;
|
||||
if (latent_tile_size_w <= 0 || latent_tile_size_h <= 0 || scale_factor <= 0) {
|
||||
return false;
|
||||
}
|
||||
auto smaller_tile = [&](int size) {
|
||||
int next_size = size / 2;
|
||||
if (!tiling_params.enabled) {
|
||||
next_size = std::min(next_size, 256 / scale_factor);
|
||||
}
|
||||
return std::min(size, std::max(4, next_size));
|
||||
};
|
||||
const int tile_size_w = smaller_tile(latent_tile_size_w);
|
||||
const int tile_size_h = smaller_tile(latent_tile_size_h);
|
||||
if (tile_size_w == latent_tile_size_w && tile_size_h == latent_tile_size_h) {
|
||||
return false;
|
||||
}
|
||||
tiling_params.enabled = true;
|
||||
tiling_params.rel_size_w = 0.0f;
|
||||
tiling_params.rel_size_h = 0.0f;
|
||||
tiling_params.tile_size_w = tile_size_w * scale_factor;
|
||||
tiling_params.tile_size_h = tile_size_h * scale_factor;
|
||||
retry_mode = tiling_params.temporal_tiling ? "spatial+temporal" : "spatial";
|
||||
LOG_WARN("Reducing VAE decode tiles from %dx%d to %dx%d image pixels",
|
||||
latent_tile_size_w * scale_factor, latent_tile_size_h * scale_factor,
|
||||
tiling_params.tile_size_w, tiling_params.tile_size_h);
|
||||
}
|
||||
|
||||
LOG_WARN("VAE decode ran out of memory; retrying with %s tiling",
|
||||
|
||||
@@ -17,7 +17,10 @@ namespace sd::backend_fit {
|
||||
|
||||
bool prepare_vae_decode_retry_tiling(sd_tiling_params_t& tiling_params,
|
||||
bool prefer_temporal_tiling,
|
||||
ggml_status status);
|
||||
ggml_status status,
|
||||
int latent_tile_size_w,
|
||||
int latent_tile_size_h,
|
||||
int scale_factor);
|
||||
|
||||
} // namespace sd::backend_fit
|
||||
|
||||
|
||||
+7
-1
@@ -62,6 +62,7 @@ enum SDVersion {
|
||||
VERSION_SENSENOVA_U1_5,
|
||||
VERSION_LLADA_IMAGE,
|
||||
VERSION_ESRGAN,
|
||||
VERSION_PIXART,
|
||||
VERSION_COUNT,
|
||||
};
|
||||
|
||||
@@ -252,6 +253,10 @@ static inline bool sd_version_is_sensenova_u1(SDVersion version) {
|
||||
return version == VERSION_SENSENOVA_U1_5;
|
||||
}
|
||||
|
||||
static inline bool sd_version_is_pixart(SDVersion version) {
|
||||
return version == VERSION_PIXART;
|
||||
}
|
||||
|
||||
static inline bool sd_version_supports_video_generation(SDVersion version) {
|
||||
return version == VERSION_SVD || sd_version_is_wan(version) || sd_version_is_hunyuan_video(version) || sd_version_is_lingbot_video(version) || sd_version_is_ltxav(version) || sd_version_is_minimax_h3(version);
|
||||
}
|
||||
@@ -320,7 +325,8 @@ static inline bool sd_version_is_dit(SDVersion version) {
|
||||
sd_version_is_sefi_image(version) ||
|
||||
sd_version_is_krea2(version) ||
|
||||
sd_version_is_mage_flow(version) ||
|
||||
sd_version_is_sensenova_u1(version)) {
|
||||
sd_version_is_sensenova_u1(version) ||
|
||||
sd_version_is_pixart(version)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -0,0 +1,399 @@
|
||||
#ifndef __SD_MODEL_DIFFUSION_PIXART_HPP__
|
||||
#define __SD_MODEL_DIFFUSION_PIXART_HPP__
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
#include "core/ggml_extend.h"
|
||||
#include "core/ggml_runner.h"
|
||||
#include "core/util.h"
|
||||
#include "model/common/ggml_block.hpp"
|
||||
#include "model/diffusion/dit.hpp"
|
||||
#include "model/diffusion/mmdit.hpp"
|
||||
#include "model/diffusion/model.hpp"
|
||||
#include "model_loader.h"
|
||||
|
||||
// Ref: https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/transformers/pixart_transformer_2d.py
|
||||
// Ref: https://github.com/PixArt-alpha/PixArt-sigma
|
||||
|
||||
namespace PixArt {
|
||||
constexpr int PIXART_GRAPH_SIZE = 20480;
|
||||
constexpr int ADALN_EMBED_DIM = 256;
|
||||
|
||||
struct PixArtConfig {
|
||||
int64_t in_channels = 4;
|
||||
int64_t out_channels = 8; // learn_sigma: noise prediction + learned variance
|
||||
int64_t hidden_size = 1152;
|
||||
int64_t cross_attention_dim = 1152;
|
||||
int64_t caption_channels = 4096;
|
||||
int64_t num_heads = 16;
|
||||
int64_t patch_size = 2;
|
||||
int64_t ffn_dim = 4608;
|
||||
int64_t pos_embed_base_size = 64;
|
||||
float interpolation_scale = 2.f;
|
||||
int num_layers = 28;
|
||||
|
||||
static PixArtConfig detect_from_weights(const String2TensorStorage& weights, const std::string& prefix) {
|
||||
PixArtConfig config;
|
||||
auto find = [&](const std::string& suffix) -> const TensorStorage* {
|
||||
auto it = weights.find(prefix + "." + suffix);
|
||||
return it == weights.end() ? nullptr : &it->second;
|
||||
};
|
||||
if (auto w = find("x_embedder.proj.weight")) {
|
||||
config.hidden_size = w->ne[3];
|
||||
config.in_channels = w->ne[2];
|
||||
config.patch_size = w->ne[0];
|
||||
}
|
||||
if (auto w = find("final_layer.linear.weight")) {
|
||||
config.out_channels = w->ne[1] / (config.patch_size * config.patch_size);
|
||||
}
|
||||
if (auto w = find("y_embedder.y_proj.fc1.weight")) {
|
||||
config.caption_channels = w->ne[0];
|
||||
}
|
||||
if (auto w = find("blocks.0.cross_attn.kv_linear.weight")) {
|
||||
config.cross_attention_dim = w->ne[0];
|
||||
}
|
||||
if (auto w = find("blocks.0.mlp.fc1.weight")) {
|
||||
config.ffn_dim = w->ne[1];
|
||||
}
|
||||
if (find("csize_embedder.mlp.0.weight") != nullptr) {
|
||||
LOG_WARN("pixart: resolution/aspect-ratio micro conditions are not supported; output may differ from the reference");
|
||||
}
|
||||
int layers = 0;
|
||||
const std::string block_prefix = prefix + ".blocks.";
|
||||
for (const auto& [name, _] : weights) {
|
||||
if (starts_with(name, block_prefix)) {
|
||||
layers = std::max(layers, atoi(name.substr(block_prefix.size()).c_str()) + 1);
|
||||
}
|
||||
}
|
||||
if (layers > 0) {
|
||||
config.num_layers = layers;
|
||||
LOG_VERBOSE("pixart: layers = %d, hidden_size = %" PRId64,
|
||||
layers, config.hidden_size);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
};
|
||||
|
||||
// Mirrors diffusers get_2d_sincos_pos_embed for a (gh, gw) patch grid.
|
||||
static std::vector<float> gen_2d_sincos_pos_embed(int64_t dim,
|
||||
int64_t gh,
|
||||
int64_t gw,
|
||||
int64_t base_size,
|
||||
float interpolation_scale) {
|
||||
// diffusers: meshgrid(grid_w, grid_h, indexing="xy") -> grid[0]=w, grid[1]=h,
|
||||
// embedding = concat(sincos(w), sincos(h))
|
||||
std::vector<float> out(static_cast<size_t>(gh) * gw * dim);
|
||||
int64_t quarter = dim / 4;
|
||||
for (int64_t h = 0; h < gh; ++h) {
|
||||
float pos_h = static_cast<float>(h) / (static_cast<float>(gh) / base_size) / interpolation_scale;
|
||||
for (int64_t w = 0; w < gw; ++w) {
|
||||
float pos_w = static_cast<float>(w) / (static_cast<float>(gw) / base_size) / interpolation_scale;
|
||||
float* dst_w = out.data() + (h * gw + w) * dim;
|
||||
float* dst_h = dst_w + dim / 2;
|
||||
for (int64_t i = 0; i < quarter; ++i) {
|
||||
float omega = 1.f / powf(10000.f, static_cast<float>(i) / quarter);
|
||||
dst_w[i] = sinf(pos_w * omega);
|
||||
dst_w[i + quarter] = cosf(pos_w * omega);
|
||||
dst_h[i] = sinf(pos_h * omega);
|
||||
dst_h[i + quarter] = cosf(pos_h * omega);
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
class PixArtTimestepEmbedding : public GGMLBlock {
|
||||
public:
|
||||
PixArtTimestepEmbedding(int64_t in_channels, int64_t out_dim) {
|
||||
blocks["mlp.0"] = std::make_shared<Linear>(in_channels, out_dim);
|
||||
blocks["mlp.2"] = std::make_shared<Linear>(out_dim, out_dim);
|
||||
}
|
||||
|
||||
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) {
|
||||
x = std::dynamic_pointer_cast<Linear>(blocks["mlp.0"])->forward(ctx, x);
|
||||
x = ggml_silu(ctx->ggml_ctx, x);
|
||||
return std::dynamic_pointer_cast<Linear>(blocks["mlp.2"])->forward(ctx, x);
|
||||
}
|
||||
};
|
||||
|
||||
class PixArtAttention : public GGMLBlock {
|
||||
int64_t num_heads;
|
||||
bool self_attention;
|
||||
|
||||
public:
|
||||
PixArtAttention(int64_t dim, int64_t num_heads, int64_t context_dim, bool self_attention)
|
||||
: num_heads(num_heads), self_attention(self_attention) {
|
||||
if (self_attention) {
|
||||
blocks["qkv"] = std::make_shared<Linear>(dim, 3 * dim);
|
||||
} else {
|
||||
blocks["q_linear"] = std::make_shared<Linear>(dim, dim);
|
||||
blocks["kv_linear"] = std::make_shared<Linear>(context_dim, 2 * dim);
|
||||
}
|
||||
blocks["proj"] = std::make_shared<Linear>(dim, dim);
|
||||
}
|
||||
|
||||
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x, ggml_tensor* context, ggml_tensor* mask = nullptr) {
|
||||
std::vector<ggml_tensor*> qkv;
|
||||
if (self_attention) {
|
||||
auto projected = std::dynamic_pointer_cast<Linear>(blocks["qkv"])->forward(ctx, x);
|
||||
qkv = ggml_ext_chunk(ctx->ggml_ctx, projected, 3, 0);
|
||||
} else {
|
||||
auto q = std::dynamic_pointer_cast<Linear>(blocks["q_linear"])->forward(ctx, x);
|
||||
auto kv = std::dynamic_pointer_cast<Linear>(blocks["kv_linear"])->forward(ctx, context);
|
||||
auto parts = ggml_ext_chunk(ctx->ggml_ctx, kv, 2, 0);
|
||||
qkv = {q, parts[0], parts[1]};
|
||||
}
|
||||
auto out = ggml_ext_attention_ext(ctx, qkv[0], qkv[1], qkv[2], num_heads, mask, false, ctx->flash_attn_enabled);
|
||||
return std::dynamic_pointer_cast<Linear>(blocks["proj"])->forward(ctx, out);
|
||||
}
|
||||
};
|
||||
|
||||
class PixArtBlock : public GGMLBlock {
|
||||
int64_t dim;
|
||||
|
||||
void init_params(ggml_context* ctx,
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string prefix = "") override {
|
||||
ggml_type wtype = get_type(prefix + "scale_shift_table", tensor_storage_map, GGML_TYPE_F32);
|
||||
params["scale_shift_table"] = ggml_new_tensor_2d(ctx, wtype, dim, 6);
|
||||
}
|
||||
|
||||
public:
|
||||
PixArtBlock(int64_t dim, int64_t num_heads, int64_t context_dim, int64_t ffn_dim)
|
||||
: dim(dim) {
|
||||
blocks["attn"] = std::make_shared<PixArtAttention>(dim, num_heads, dim, true);
|
||||
blocks["cross_attn"] = std::make_shared<PixArtAttention>(dim, num_heads, context_dim, false);
|
||||
blocks["mlp.fc1"] = std::make_shared<Linear>(dim, ffn_dim);
|
||||
blocks["mlp.fc2"] = std::make_shared<Linear>(ffn_dim, dim);
|
||||
}
|
||||
|
||||
static ggml_tensor* norm(ggml_context* ctx, ggml_tensor* x) {
|
||||
return ggml_norm(ctx, x, 1e-6f);
|
||||
}
|
||||
|
||||
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x, ggml_tensor* mod, ggml_tensor* context, ggml_tensor* context_mask) {
|
||||
// x: [N, n_token, dim]
|
||||
// mod: [N, 6 * dim], shared adaLN-single output
|
||||
int64_t N = x->ne[2];
|
||||
|
||||
auto table = params["scale_shift_table"];
|
||||
if (table->type != GGML_TYPE_F32) {
|
||||
table = ggml_cast(ctx->ggml_ctx, table, GGML_TYPE_F32);
|
||||
}
|
||||
table = ggml_reshape_3d(ctx->ggml_ctx, table, dim, 6, 1);
|
||||
auto m = ggml_add(ctx->ggml_ctx, ggml_reshape_3d(ctx->ggml_ctx, mod, dim, 6, N), table);
|
||||
auto mv = ggml_ext_chunk(ctx->ggml_ctx, ggml_reshape_2d(ctx->ggml_ctx, ggml_ext_cont(ctx->ggml_ctx, m), dim * 6, N), 6, 0);
|
||||
|
||||
auto attn1 = std::dynamic_pointer_cast<PixArtAttention>(blocks["attn"]);
|
||||
auto attn2 = std::dynamic_pointer_cast<PixArtAttention>(blocks["cross_attn"]);
|
||||
auto proj = std::dynamic_pointer_cast<Linear>(blocks["mlp.fc1"]);
|
||||
auto fc2 = std::dynamic_pointer_cast<Linear>(blocks["mlp.fc2"]);
|
||||
|
||||
auto gate = [&](ggml_tensor* y, ggml_tensor* g) {
|
||||
g = ggml_reshape_3d(ctx->ggml_ctx, g, dim, 1, N);
|
||||
return ggml_add(ctx->ggml_ctx, x, ggml_mul(ctx->ggml_ctx, y, g));
|
||||
};
|
||||
|
||||
auto h = modulate(ctx->ggml_ctx, norm(ctx->ggml_ctx, x), mv[0], mv[1]);
|
||||
x = gate(attn1->forward(ctx, h, h), mv[2]);
|
||||
// ada_norm_single: no norm before cross-attention (PixArtMS.py)
|
||||
x = ggml_add(ctx->ggml_ctx, x, attn2->forward(ctx, x, context, context_mask));
|
||||
h = modulate(ctx->ggml_ctx, norm(ctx->ggml_ctx, x), mv[3], mv[4]);
|
||||
h = proj->forward(ctx, h);
|
||||
h = ggml_ext_gelu(ctx->ggml_ctx, h, true);
|
||||
h = fc2->forward(ctx, h);
|
||||
return gate(h, mv[5]);
|
||||
}
|
||||
};
|
||||
|
||||
class PixArtModel : public GGMLBlock {
|
||||
PixArtConfig config;
|
||||
|
||||
void init_params(ggml_context* ctx,
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string prefix = "") override {
|
||||
ggml_type wtype = get_type(prefix + "final_layer.scale_shift_table", tensor_storage_map, GGML_TYPE_F32);
|
||||
params["final_layer.scale_shift_table"] = ggml_new_tensor_2d(ctx, wtype, config.hidden_size, 2);
|
||||
}
|
||||
|
||||
public:
|
||||
PixArtModel() = default;
|
||||
PixArtModel(const PixArtConfig& config)
|
||||
: config(config) {
|
||||
blocks["x_embedder.proj"] = std::make_shared<Conv2d>(config.in_channels,
|
||||
config.hidden_size,
|
||||
std::pair<int, int>{static_cast<int>(config.patch_size), static_cast<int>(config.patch_size)},
|
||||
std::pair<int, int>{static_cast<int>(config.patch_size), static_cast<int>(config.patch_size)});
|
||||
blocks["t_embedder"] = std::make_shared<PixArtTimestepEmbedding>(ADALN_EMBED_DIM, config.hidden_size);
|
||||
blocks["t_block.1"] = std::make_shared<Linear>(config.hidden_size, 6 * config.hidden_size);
|
||||
blocks["y_embedder.y_proj.fc1"] = std::make_shared<Linear>(config.caption_channels, config.hidden_size);
|
||||
blocks["y_embedder.y_proj.fc2"] = std::make_shared<Linear>(config.hidden_size, config.cross_attention_dim);
|
||||
for (int i = 0; i < config.num_layers; ++i) {
|
||||
blocks["blocks." + std::to_string(i)] =
|
||||
std::make_shared<PixArtBlock>(config.hidden_size, config.num_heads, config.cross_attention_dim, config.ffn_dim);
|
||||
}
|
||||
blocks["final_layer.norm_final"] = std::make_shared<LayerNorm>(config.hidden_size, 1e-6f, false);
|
||||
blocks["final_layer.linear"] = std::make_shared<Linear>(config.hidden_size,
|
||||
config.patch_size * config.patch_size * config.out_channels);
|
||||
}
|
||||
|
||||
ggml_tensor* forward(GGMLRunnerContext* ctx,
|
||||
ggml_tensor* x,
|
||||
ggml_tensor* timesteps,
|
||||
ggml_tensor* context,
|
||||
ggml_tensor* pos_embed,
|
||||
ggml_tensor* context_mask) {
|
||||
// x: [N, C, H, W] latent, context: [N, n_ctx, caption_channels]
|
||||
int64_t W = x->ne[0];
|
||||
int64_t H = x->ne[1];
|
||||
int64_t N = x->ne[3];
|
||||
int64_t p = config.patch_size;
|
||||
int64_t wp = W / p;
|
||||
int64_t hp = H / p;
|
||||
|
||||
auto h = std::dynamic_pointer_cast<Conv2d>(blocks["x_embedder.proj"])->forward(ctx, x); // [N, hidden, hp, wp]
|
||||
h = ggml_ext_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, h, 1, 2, 0, 3)); // [N, hp, wp, hidden] -> [N, hp*wp, hidden]
|
||||
h = ggml_reshape_3d(ctx->ggml_ctx, h, config.hidden_size, wp * hp, N); // [N, hp*wp, hidden]
|
||||
h = ggml_add(ctx->ggml_ctx, h, pos_embed);
|
||||
|
||||
auto t = ggml_ext_timestep_embedding(ctx->ggml_ctx, timesteps, ADALN_EMBED_DIM, 10000);
|
||||
auto emb = std::dynamic_pointer_cast<PixArtTimestepEmbedding>(blocks["t_embedder"])->forward(ctx, t);
|
||||
|
||||
auto mod = std::dynamic_pointer_cast<Linear>(blocks["t_block.1"])
|
||||
->forward(ctx, ggml_silu(ctx->ggml_ctx, emb)); // [N, 6 * hidden]
|
||||
|
||||
auto ctx_emb = std::dynamic_pointer_cast<Linear>(blocks["y_embedder.y_proj.fc1"])->forward(ctx, context);
|
||||
ctx_emb = ggml_ext_gelu(ctx->ggml_ctx, ctx_emb, true);
|
||||
ctx_emb = std::dynamic_pointer_cast<Linear>(blocks["y_embedder.y_proj.fc2"])->forward(ctx, ctx_emb);
|
||||
|
||||
for (int i = 0; i < config.num_layers; ++i) {
|
||||
auto block = std::dynamic_pointer_cast<PixArtBlock>(blocks["blocks." + std::to_string(i)]);
|
||||
h = block->forward(ctx, h, mod, ctx_emb, context_mask);
|
||||
sd::ggml_graph_cut::mark_graph_cut(h, "pixart.blocks." + std::to_string(i), "h");
|
||||
}
|
||||
|
||||
// scale_shift_table + emb -> (shift, scale) for the affine-free final norm
|
||||
auto tail_table = params["final_layer.scale_shift_table"];
|
||||
if (tail_table->type != GGML_TYPE_F32) {
|
||||
tail_table = ggml_cast(ctx->ggml_ctx, tail_table, GGML_TYPE_F32);
|
||||
}
|
||||
auto ss = ggml_add(ctx->ggml_ctx,
|
||||
ggml_reshape_3d(ctx->ggml_ctx, tail_table, config.hidden_size, 2, 1),
|
||||
ggml_reshape_3d(ctx->ggml_ctx, emb, config.hidden_size, 1, N)); // [2, hidden, N]
|
||||
auto parts = ggml_ext_chunk(ctx->ggml_ctx,
|
||||
ggml_reshape_2d(ctx->ggml_ctx, ggml_ext_cont(ctx->ggml_ctx, ss), config.hidden_size * 2, N),
|
||||
2, 0);
|
||||
h = std::dynamic_pointer_cast<LayerNorm>(blocks["final_layer.norm_final"])->forward(ctx, h);
|
||||
h = modulate(ctx->ggml_ctx, h, parts[0], parts[1]);
|
||||
h = std::dynamic_pointer_cast<Linear>(blocks["final_layer.linear"])->forward(ctx, h); // [N, hp*wp, p*p*out_ch]
|
||||
h = DiT::unpatchify(ctx->ggml_ctx, h, hp, wp, static_cast<int>(p), static_cast<int>(p), false);
|
||||
return h; // [N, out_channels, H, W]
|
||||
}
|
||||
};
|
||||
|
||||
struct PixArtRunner : public DiffusionModelRunner {
|
||||
PixArtConfig config;
|
||||
PixArtModel model;
|
||||
std::vector<float> pos_vec;
|
||||
|
||||
PixArtRunner(ggml_backend_t backend,
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string prefix = "",
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr,
|
||||
const char* model_args = nullptr)
|
||||
: DiffusionModelRunner(backend, prefix, weight_manager),
|
||||
config(PixArtConfig::detect_from_weights(tensor_storage_map, prefix)) {
|
||||
for (const auto& [key, value] : parse_key_value_args(model_args, "model arg")) {
|
||||
if (key == "pixart_pos_embed_base_size") {
|
||||
int parsed = 0;
|
||||
if (parse_strict_int(value, parsed)) {
|
||||
config.pos_embed_base_size = parsed;
|
||||
} else {
|
||||
LOG_WARN("ignoring invalid PixArt model arg '%s=%s'", key.c_str(), value.c_str());
|
||||
}
|
||||
} else if (key == "pixart_interpolation_scale") {
|
||||
float parsed = 0.f;
|
||||
if (parse_strict_float(value, parsed)) {
|
||||
config.interpolation_scale = parsed;
|
||||
} else {
|
||||
LOG_WARN("ignoring invalid PixArt model arg '%s=%s'", key.c_str(), value.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
model = PixArtModel(config);
|
||||
model.init(params_ctx, tensor_storage_map, prefix);
|
||||
}
|
||||
|
||||
std::string get_desc() override {
|
||||
return "pixart";
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors, const std::string& prefix) override {
|
||||
model.get_param_tensors(tensors, prefix);
|
||||
}
|
||||
|
||||
ggml_cgraph* build_graph(const sd::Tensor<float>& x_tensor,
|
||||
const sd::Tensor<float>& timesteps_tensor,
|
||||
const sd::Tensor<float>& context_tensor,
|
||||
const sd::Tensor<float>& mask_tensor) {
|
||||
ggml_cgraph* gf = new_graph_custom(PIXART_GRAPH_SIZE);
|
||||
ggml_tensor* x = make_input(x_tensor);
|
||||
ggml_tensor* timesteps = make_input(timesteps_tensor);
|
||||
GGML_ASSERT(x->ne[3] == 1);
|
||||
GGML_ASSERT(!context_tensor.empty());
|
||||
ggml_tensor* context = make_input(context_tensor);
|
||||
|
||||
ggml_tensor* context_mask = nullptr;
|
||||
if (!mask_tensor.empty()) {
|
||||
// additive attention bias over context tokens: 0 keep / -inf discard
|
||||
context_mask = ggml_reshape_4d(compute_ctx, make_input(mask_tensor), mask_tensor.shape()[0], 1, 1, 1);
|
||||
}
|
||||
|
||||
int64_t W = x->ne[0];
|
||||
int64_t H = x->ne[1];
|
||||
int64_t wp = W / config.patch_size;
|
||||
int64_t hp = H / config.patch_size;
|
||||
|
||||
pos_vec = gen_2d_sincos_pos_embed(config.hidden_size, hp, wp,
|
||||
config.pos_embed_base_size, config.interpolation_scale);
|
||||
auto pos = ggml_new_tensor_3d(compute_ctx, GGML_TYPE_F32, config.hidden_size, wp * hp, 1);
|
||||
set_backend_tensor_data(pos, pos_vec.data());
|
||||
|
||||
auto runner_ctx = get_context();
|
||||
ggml_tensor* out = model.forward(&runner_ctx, x, timesteps, context, pos, context_mask);
|
||||
// learn_sigma: keep the noise prediction half of the output channels
|
||||
out = ggml_ext_slice(compute_ctx, out, 2, 0, config.in_channels);
|
||||
ggml_build_forward_expand(gf, out);
|
||||
return gf;
|
||||
}
|
||||
|
||||
sd::Tensor<float> compute(int n_threads,
|
||||
const sd::Tensor<float>& x,
|
||||
const sd::Tensor<float>& timesteps,
|
||||
const sd::Tensor<float>& context,
|
||||
const sd::Tensor<float>& context_mask) {
|
||||
auto get_graph = [&]() -> ggml_cgraph* {
|
||||
return build_graph(x, timesteps, context, context_mask);
|
||||
};
|
||||
return restore_trailing_singleton_dims(GGMLRunner::compute(get_graph, n_threads, false), x.dim());
|
||||
}
|
||||
|
||||
sd::Tensor<float> compute(int n_threads,
|
||||
const DiffusionParams& diffusion_params) override {
|
||||
GGML_ASSERT(diffusion_params.x != nullptr);
|
||||
GGML_ASSERT(diffusion_params.timesteps != nullptr);
|
||||
auto context = tensor_or_empty(diffusion_params.context);
|
||||
auto context_msk = tensor_or_empty(diffusion_params.y);
|
||||
return compute(n_threads,
|
||||
*diffusion_params.x,
|
||||
*diffusion_params.timesteps,
|
||||
context,
|
||||
context_msk);
|
||||
}
|
||||
};
|
||||
} // namespace PixArt
|
||||
|
||||
#endif // __SD_MODEL_DIFFUSION_PIXART_HPP__
|
||||
@@ -544,7 +544,7 @@ public:
|
||||
const String2TensorStorage& tensor_storage_map = {},
|
||||
const std::string& prefix = "")
|
||||
: version(version), decode_only(decode_only), use_video_decoder(use_video_decoder) {
|
||||
if (sd_version_is_dit(version)) {
|
||||
if (sd_version_is_dit(version) && version != VERSION_PIXART) {
|
||||
if (sd_version_uses_flux2_vae(version)) {
|
||||
dd_config.z_channels = 32;
|
||||
embed_dim = 32;
|
||||
@@ -678,7 +678,7 @@ struct AutoEncoderKL : public VAE {
|
||||
if (sd_version_is_sd1(version) || sd_version_is_sd2(version)) {
|
||||
scale_factor = 0.18215f;
|
||||
shift_factor = 0.f;
|
||||
} else if (sd_version_is_sdxl(version)) {
|
||||
} else if (sd_version_is_sdxl(version) || sd_version_is_pixart(version)) {
|
||||
scale_factor = 0.13025f;
|
||||
shift_factor = 0.f;
|
||||
} else if (sd_version_is_sd3(version)) {
|
||||
|
||||
@@ -556,12 +556,18 @@ namespace MiniMaxH3VAE {
|
||||
tensor.shape()[3]});
|
||||
}
|
||||
|
||||
static sd_tiling_params_t h3_tiling(sd_tiling_params_t params) {
|
||||
sd_tiling_params_t resolve_tiling_params(sd_tiling_params_t params) const override {
|
||||
if (!params.enabled) {
|
||||
params.target_overlap = 0.25f;
|
||||
}
|
||||
if (params.tile_size_w == 0 && params.rel_size_w == 0.f) {
|
||||
params.tile_size_w = 256;
|
||||
}
|
||||
if (params.tile_size_h == 0 && params.rel_size_h == 0.f) {
|
||||
params.tile_size_h = 256;
|
||||
}
|
||||
params.enabled = true;
|
||||
params.temporal_tiling = false;
|
||||
params.tile_size_x = 16;
|
||||
params.tile_size_y = 16;
|
||||
params.target_overlap = 0.25f;
|
||||
return params;
|
||||
}
|
||||
|
||||
@@ -605,7 +611,7 @@ namespace MiniMaxH3VAE {
|
||||
bool circular_x = false,
|
||||
bool circular_y = false) override {
|
||||
auto input = ensure_video_shape(x);
|
||||
auto tiling = h3_tiling(tiling_params);
|
||||
auto tiling = resolve_tiling_params(tiling_params);
|
||||
if (input.shape()[2] == 1) {
|
||||
auto encoded = VAE::encode(n_threads, input, tiling, circular_x, circular_y);
|
||||
if (!encoded.empty() && encoded.shape()[2] > 1) {
|
||||
@@ -646,7 +652,7 @@ namespace MiniMaxH3VAE {
|
||||
bool circular_y = false,
|
||||
bool silent = false) override {
|
||||
auto input = ensure_video_shape(x);
|
||||
auto tiling = h3_tiling(tiling_params);
|
||||
auto tiling = resolve_tiling_params(tiling_params);
|
||||
if (input.shape()[2] == 1) {
|
||||
auto decoded = VAE::decode(n_threads,
|
||||
input,
|
||||
|
||||
@@ -701,7 +701,7 @@ public:
|
||||
bool use_midblock_gn = false;
|
||||
taef2 = sd_version_uses_flux2_vae(version);
|
||||
|
||||
if (sd_version_is_dit(version)) {
|
||||
if (sd_version_is_dit(version) && !sd_version_is_pixart(version)) {
|
||||
z_channels = 16;
|
||||
}
|
||||
if (taef2) {
|
||||
|
||||
+82
-50
@@ -1,6 +1,9 @@
|
||||
#ifndef __SD_MODEL_VAE_VAE_HPP__
|
||||
#define __SD_MODEL_VAE_VAE_HPP__
|
||||
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
#include "core/tensor_ggml.hpp"
|
||||
#include "model/common/block.hpp"
|
||||
#include "model/vae/vae_tiling.hpp"
|
||||
@@ -117,8 +120,8 @@ protected:
|
||||
int output_width,
|
||||
int output_height,
|
||||
int scale,
|
||||
int p_tile_size_x,
|
||||
int p_tile_size_y,
|
||||
int p_tile_size_w,
|
||||
int p_tile_size_h,
|
||||
float tile_overlap_factor,
|
||||
bool circular_x,
|
||||
bool circular_y,
|
||||
@@ -138,17 +141,28 @@ protected:
|
||||
}
|
||||
return output_tile;
|
||||
};
|
||||
return ::process_tiles_2d(input,
|
||||
output_width,
|
||||
output_height,
|
||||
scale,
|
||||
p_tile_size_x,
|
||||
p_tile_size_y,
|
||||
tile_overlap_factor,
|
||||
circular_x,
|
||||
circular_y,
|
||||
on_processing,
|
||||
silent);
|
||||
const bool original_circular_x = circular_x_enabled;
|
||||
const bool original_circular_y = circular_y_enabled;
|
||||
const int64_t latent_width = decode_graph ? input.shape()[0] : output_width;
|
||||
const int64_t latent_height = decode_graph ? input.shape()[1] : output_height;
|
||||
circular_x = circular_x || original_circular_x;
|
||||
circular_y = circular_y || original_circular_y;
|
||||
// Full-width axes wrap in convolutions; split axes wrap between tiles.
|
||||
set_circular_axes(circular_x && p_tile_size_w >= latent_width,
|
||||
circular_y && p_tile_size_h >= latent_height);
|
||||
auto output = ::process_tiles_2d(input,
|
||||
output_width,
|
||||
output_height,
|
||||
scale,
|
||||
p_tile_size_w,
|
||||
p_tile_size_h,
|
||||
tile_overlap_factor,
|
||||
circular_x && p_tile_size_w < latent_width,
|
||||
circular_y && p_tile_size_h < latent_height,
|
||||
on_processing,
|
||||
silent);
|
||||
set_circular_axes(original_circular_x, original_circular_y);
|
||||
return output;
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -178,33 +192,48 @@ public:
|
||||
return supports_temporal_tiling(VAETemporalDirection::DECODE);
|
||||
}
|
||||
|
||||
void get_tile_sizes(int& tile_size_x,
|
||||
int& tile_size_y,
|
||||
virtual sd_tiling_params_t resolve_tiling_params(sd_tiling_params_t params) const {
|
||||
return params;
|
||||
}
|
||||
|
||||
bool get_tile_sizes(int& tile_size_w,
|
||||
int& tile_size_h,
|
||||
float& tile_overlap,
|
||||
const sd_tiling_params_t& params,
|
||||
int64_t latent_x,
|
||||
int64_t latent_y,
|
||||
float encoding_factor = 1.0f) {
|
||||
tile_overlap = std::max(std::min(params.target_overlap, 0.5f), 0.0f);
|
||||
auto get_tile_size = [&](int requested_size, float factor, int64_t latent_size) {
|
||||
const int default_tile_size = 32;
|
||||
const int min_tile_dimension = 4;
|
||||
int tile_size = default_tile_size;
|
||||
// factor <= 1 means simple fraction of the latent dimension
|
||||
// factor > 1 means number of tiles across that dimension
|
||||
if (factor > 0.f) {
|
||||
if (factor > 1.0)
|
||||
factor = 1 / (factor - factor * tile_overlap + tile_overlap);
|
||||
tile_size = static_cast<int>(std::round(latent_size * factor));
|
||||
} else if (requested_size >= min_tile_dimension) {
|
||||
tile_size = requested_size;
|
||||
int64_t latent_w,
|
||||
int64_t latent_h) {
|
||||
const auto tiling = resolve_tiling_params(params);
|
||||
if (latent_w <= 0 || latent_h <= 0 ||
|
||||
latent_w > std::numeric_limits<int>::max() || latent_h > std::numeric_limits<int>::max() ||
|
||||
!std::isfinite(tiling.target_overlap)) {
|
||||
LOG_ERROR("invalid VAE tiling dimensions or overlap");
|
||||
return false;
|
||||
}
|
||||
const int scale_factor = get_scale_factor();
|
||||
tile_overlap = std::max(std::min(tiling.target_overlap, 0.5f), 0.0f);
|
||||
auto get_tile_size = [&](int requested_size, double factor, int64_t latent_size, int& tile_size) {
|
||||
if (requested_size < 0 || !std::isfinite(factor) || factor < 0.0) {
|
||||
LOG_ERROR("VAE tile sizes and relative sizes must be finite and non-negative");
|
||||
return false;
|
||||
}
|
||||
tile_size = static_cast<int>(tile_size * encoding_factor);
|
||||
return std::max(std::min(tile_size, static_cast<int>(latent_size)), min_tile_dimension);
|
||||
const int min_tile_dimension = std::min(4, static_cast<int>(latent_size));
|
||||
double size = (requested_size > 0 ? requested_size : 256) / scale_factor;
|
||||
if (factor > 0.0) {
|
||||
if (factor > 1.0) {
|
||||
factor = 1.0 / (factor * (1.0 - tile_overlap) + tile_overlap);
|
||||
}
|
||||
size = std::floor(static_cast<double>(latent_size) * factor);
|
||||
}
|
||||
if (size < min_tile_dimension && (requested_size > 0 || factor > 0.0)) {
|
||||
LOG_ERROR("VAE tile size must be at least %d image pixels on this axis", min_tile_dimension * scale_factor);
|
||||
return false;
|
||||
}
|
||||
tile_size = static_cast<int>(std::min(static_cast<double>(latent_size), std::max<double>(min_tile_dimension, size)));
|
||||
return true;
|
||||
};
|
||||
|
||||
tile_size_x = get_tile_size(params.tile_size_x, params.rel_size_x, latent_x);
|
||||
tile_size_y = get_tile_size(params.tile_size_y, params.rel_size_y, latent_y);
|
||||
return get_tile_size(tiling.tile_size_w, tiling.rel_size_w, latent_w, tile_size_w) &&
|
||||
get_tile_size(tiling.tile_size_h, tiling.rel_size_h, latent_h, tile_size_h);
|
||||
}
|
||||
|
||||
virtual sd::Tensor<float> encode(int n_threads,
|
||||
@@ -213,6 +242,7 @@ public:
|
||||
bool circular_x = false,
|
||||
bool circular_y = false) {
|
||||
int64_t t0 = ggml_time_ms();
|
||||
tiling_params = resolve_tiling_params(tiling_params);
|
||||
sd::Tensor<float> input = x;
|
||||
sd::Tensor<float> output;
|
||||
if (scale_input) {
|
||||
@@ -224,21 +254,19 @@ public:
|
||||
int64_t W = input.shape()[0] / scale_factor;
|
||||
int64_t H = input.shape()[1] / scale_factor;
|
||||
float tile_overlap;
|
||||
int tile_size_x, tile_size_y;
|
||||
// Image VAE encode is more sensitive to tile boundary context than decode.
|
||||
// Keep the smaller legacy factor for video VAEs, but default image encode
|
||||
// tiles to 64 latent pixels so a 512px SD image is encoded as one tile.
|
||||
const float encode_tile_factor = sd_version_is_minimax_h3(version) ? 1.f : (sd_version_is_wan(version) || sd_version_is_hunyuan_video(version) || sd_version_is_ltxav(version)) ? 1.30539f
|
||||
: 2.0f;
|
||||
get_tile_sizes(tile_size_x, tile_size_y, tile_overlap, tiling_params, W, H, encode_tile_factor);
|
||||
LOG_VERBOSE("VAE Tile size: %dx%d", tile_size_x, tile_size_y);
|
||||
int tile_size_w, tile_size_h;
|
||||
if (!get_tile_sizes(tile_size_w, tile_size_h, tile_overlap, tiling_params, W, H)) {
|
||||
return {};
|
||||
}
|
||||
LOG_VERBOSE("VAE encode tile size: %dx%d pixels (%dx%d latent)",
|
||||
tile_size_w * scale_factor, tile_size_h * scale_factor, tile_size_w, tile_size_h);
|
||||
output = tiled_compute(input,
|
||||
n_threads,
|
||||
static_cast<int>(W),
|
||||
static_cast<int>(H),
|
||||
scale_factor,
|
||||
tile_size_x,
|
||||
tile_size_y,
|
||||
tile_size_w,
|
||||
tile_size_h,
|
||||
tile_overlap,
|
||||
circular_x,
|
||||
circular_y,
|
||||
@@ -271,6 +299,7 @@ public:
|
||||
bool circular_y = false,
|
||||
bool silent = false) {
|
||||
int64_t t0 = ggml_time_ms();
|
||||
tiling_params = resolve_tiling_params(tiling_params);
|
||||
sd::Tensor<float> input = x;
|
||||
sd::Tensor<float> output;
|
||||
|
||||
@@ -279,10 +308,13 @@ public:
|
||||
int64_t W = input.shape()[0] * scale_factor;
|
||||
int64_t H = input.shape()[1] * scale_factor;
|
||||
float tile_overlap;
|
||||
int tile_size_x, tile_size_y;
|
||||
get_tile_sizes(tile_size_x, tile_size_y, tile_overlap, tiling_params, input.shape()[0], input.shape()[1]);
|
||||
int tile_size_w, tile_size_h;
|
||||
if (!get_tile_sizes(tile_size_w, tile_size_h, tile_overlap, tiling_params, input.shape()[0], input.shape()[1])) {
|
||||
return {};
|
||||
}
|
||||
if (!silent) {
|
||||
LOG_VERBOSE("VAE Tile size: %dx%d", tile_size_x, tile_size_y);
|
||||
LOG_VERBOSE("VAE decode tile size: %dx%d pixels (%dx%d latent)",
|
||||
tile_size_w * scale_factor, tile_size_h * scale_factor, tile_size_w, tile_size_h);
|
||||
}
|
||||
output = tiled_compute(
|
||||
input,
|
||||
@@ -290,8 +322,8 @@ public:
|
||||
static_cast<int>(W),
|
||||
static_cast<int>(H),
|
||||
scale_factor,
|
||||
tile_size_x,
|
||||
tile_size_y,
|
||||
tile_size_w,
|
||||
tile_size_h,
|
||||
tile_overlap,
|
||||
circular_x,
|
||||
circular_y,
|
||||
|
||||
@@ -47,10 +47,14 @@ bool read_gguf_file(const std::string& file_path,
|
||||
gguf_context* ctx_gguf_ = nullptr;
|
||||
ggml_context* ctx_meta_ = nullptr;
|
||||
|
||||
ctx_gguf_ = gguf_init_from_file(file_path.c_str(), {true, &ctx_meta_});
|
||||
GGUFReader gguf_reader;
|
||||
bool probe_ok = gguf_reader.load(file_path);
|
||||
|
||||
if (!probe_ok || !gguf_reader.has_tensors_beyond_ggml_limits()) {
|
||||
ctx_gguf_ = gguf_init_from_file(file_path.c_str(), {true, &ctx_meta_});
|
||||
}
|
||||
if (!ctx_gguf_) {
|
||||
GGUFReader gguf_reader;
|
||||
if (!gguf_reader.load(file_path)) {
|
||||
if (!probe_ok && !gguf_reader.load(file_path)) {
|
||||
set_error(error, "failed to open '" + file_path + "' with GGUFReader");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -35,23 +35,72 @@ enum class GGUFMetadataType : uint32_t {
|
||||
class GGUFReader {
|
||||
private:
|
||||
std::vector<GGUFTensorInfo> tensors_;
|
||||
bool has_wide_tensors_ = false;
|
||||
uint64_t remaining_bytes_ = 0;
|
||||
size_t data_offset_;
|
||||
size_t alignment_ = 32; // default alignment is 32
|
||||
|
||||
template <typename T>
|
||||
bool safe_read(std::ifstream& fin, T& value) {
|
||||
fin.read(reinterpret_cast<char*>(&value), sizeof(T));
|
||||
return fin.good();
|
||||
return safe_read(fin, reinterpret_cast<char*>(&value), sizeof(T));
|
||||
}
|
||||
|
||||
bool safe_read(std::ifstream& fin, char* buffer, size_t size) {
|
||||
if (size > remaining_bytes_)
|
||||
return false;
|
||||
fin.read(buffer, size);
|
||||
return fin.good();
|
||||
if (!fin.good())
|
||||
return false;
|
||||
remaining_bytes_ -= size;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool safe_seek(std::ifstream& fin, std::streamoff offset, std::ios::seekdir dir) {
|
||||
fin.seekg(offset, dir);
|
||||
return fin.good();
|
||||
bool safe_skip(std::ifstream& fin, uint64_t count, uint64_t element_size = 1) {
|
||||
if (count > remaining_bytes_ / element_size)
|
||||
return false;
|
||||
uint64_t size = count * element_size;
|
||||
fin.seekg(static_cast<std::streamoff>(size), std::ios::cur);
|
||||
if (!fin.good())
|
||||
return false;
|
||||
remaining_bytes_ -= size;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool skip_metadata_values(std::ifstream& fin, GGUFMetadataType type, uint64_t count) {
|
||||
switch (type) {
|
||||
case GGUFMetadataType::UINT8:
|
||||
case GGUFMetadataType::INT8:
|
||||
case GGUFMetadataType::BOOL:
|
||||
return safe_skip(fin, count);
|
||||
|
||||
case GGUFMetadataType::UINT16:
|
||||
case GGUFMetadataType::INT16:
|
||||
return safe_skip(fin, count, 2);
|
||||
|
||||
case GGUFMetadataType::UINT32:
|
||||
case GGUFMetadataType::INT32:
|
||||
case GGUFMetadataType::FLOAT32:
|
||||
return safe_skip(fin, count, 4);
|
||||
|
||||
case GGUFMetadataType::UINT64:
|
||||
case GGUFMetadataType::INT64:
|
||||
case GGUFMetadataType::FLOAT64:
|
||||
return safe_skip(fin, count, 8);
|
||||
|
||||
case GGUFMetadataType::STRING:
|
||||
if (count > remaining_bytes_ / sizeof(uint64_t))
|
||||
return false;
|
||||
for (uint64_t i = 0; i < count; i++) {
|
||||
uint64_t len = 0;
|
||||
if (!safe_read(fin, len) || !safe_skip(fin, len))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
default:
|
||||
LOG_ERROR("Unknown metadata type=%u", static_cast<uint32_t>(type));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool read_metadata(std::ifstream& fin) {
|
||||
@@ -84,52 +133,12 @@ private:
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (static_cast<GGUFMetadataType>(type)) {
|
||||
case GGUFMetadataType::UINT8:
|
||||
case GGUFMetadataType::INT8:
|
||||
case GGUFMetadataType::BOOL:
|
||||
return safe_seek(fin, 1, std::ios::cur);
|
||||
|
||||
case GGUFMetadataType::UINT16:
|
||||
case GGUFMetadataType::INT16:
|
||||
return safe_seek(fin, 2, std::ios::cur);
|
||||
|
||||
case GGUFMetadataType::UINT32:
|
||||
case GGUFMetadataType::INT32:
|
||||
case GGUFMetadataType::FLOAT32:
|
||||
return safe_seek(fin, 4, std::ios::cur);
|
||||
|
||||
case GGUFMetadataType::UINT64:
|
||||
case GGUFMetadataType::INT64:
|
||||
case GGUFMetadataType::FLOAT64:
|
||||
return safe_seek(fin, 8, std::ios::cur);
|
||||
|
||||
case GGUFMetadataType::STRING: {
|
||||
uint64_t len = 0;
|
||||
if (!safe_read(fin, len))
|
||||
return false;
|
||||
return safe_seek(fin, len, std::ios::cur);
|
||||
}
|
||||
|
||||
case GGUFMetadataType::ARRAY: {
|
||||
uint32_t elem_type = 0;
|
||||
uint64_t len = 0;
|
||||
if (!safe_read(fin, elem_type))
|
||||
return false;
|
||||
if (!safe_read(fin, len))
|
||||
return false;
|
||||
|
||||
for (uint64_t i = 0; i < len; i++) {
|
||||
if (!read_metadata(fin))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
LOG_ERROR("Unknown metadata type=%u", type);
|
||||
uint64_t count = 1;
|
||||
if (type == static_cast<uint32_t>(GGUFMetadataType::ARRAY)) {
|
||||
if (!safe_read(fin, type) || !safe_read(fin, count))
|
||||
return false;
|
||||
}
|
||||
return skip_metadata_values(fin, static_cast<GGUFMetadataType>(type), count);
|
||||
}
|
||||
|
||||
GGUFTensorInfo read_tensor_info(std::ifstream& fin) {
|
||||
@@ -154,6 +163,7 @@ private:
|
||||
}
|
||||
|
||||
if (n_dims > GGML_MAX_DIMS) {
|
||||
has_wide_tensors_ = true;
|
||||
for (uint32_t i = GGML_MAX_DIMS; i < n_dims; i++) {
|
||||
info.shape[GGML_MAX_DIMS - 1] *= info.shape[i]; // stack to last dim;
|
||||
}
|
||||
@@ -174,12 +184,20 @@ private:
|
||||
|
||||
public:
|
||||
bool load(const std::string& file_path) {
|
||||
std::ifstream fin(file_path, std::ios::binary);
|
||||
std::ifstream fin(file_path, std::ios::binary | std::ios::ate);
|
||||
if (!fin) {
|
||||
LOG_ERROR("failed to open '%s'", file_path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
std::streamoff file_size = fin.tellg();
|
||||
if (file_size < 0)
|
||||
return false;
|
||||
remaining_bytes_ = static_cast<uint64_t>(file_size);
|
||||
fin.seekg(0, std::ios::beg);
|
||||
if (!fin.good())
|
||||
return false;
|
||||
|
||||
// --- Header ---
|
||||
char magic[4];
|
||||
if (!safe_read(fin, magic, 4) || strncmp(magic, "GGUF", 4) != 0) {
|
||||
@@ -228,6 +246,8 @@ public:
|
||||
}
|
||||
|
||||
const std::vector<GGUFTensorInfo>& tensors() const { return tensors_; }
|
||||
|
||||
bool has_tensors_beyond_ggml_limits() const { return has_wide_tensors_; }
|
||||
size_t data_offset() const { return data_offset_; }
|
||||
};
|
||||
|
||||
|
||||
+20
-4
@@ -532,7 +532,18 @@ SDVersion ModelLoader::get_sd_version() const {
|
||||
if (tensor_storage.name.find("model.diffusion_model.layers.0.adaLN_sa_ln.weight") != std::string::npos) {
|
||||
return VERSION_ERNIE_IMAGE;
|
||||
}
|
||||
if (tensor_storage.name.find("model.diffusion_model.t_block.1.weight") != std::string::npos &&
|
||||
tensor_storage_map.find("model.diffusion_model.x_embedder.proj.weight") != tensor_storage_map.end() &&
|
||||
tensor_storage_map.find("model.diffusion_model.audio_patchify_proj.weight") == tensor_storage_map.end()) {
|
||||
return VERSION_PIXART;
|
||||
}
|
||||
if (tensor_storage.name.find("model.diffusion_model.adaln_single.emb.timestep_embedder.linear_1.bias") != std::string::npos) {
|
||||
// PixArt shares this timestep embedding with LTX-AV.
|
||||
if (tensor_storage_map.find("model.diffusion_model.pos_embed.proj.weight") != tensor_storage_map.end() &&
|
||||
tensor_storage_map.find("model.diffusion_model.adaln_single.linear.weight") != tensor_storage_map.end() &&
|
||||
tensor_storage_map.find("model.diffusion_model.audio_patchify_proj.weight") == tensor_storage_map.end()) {
|
||||
return VERSION_PIXART;
|
||||
}
|
||||
return VERSION_LTXAV;
|
||||
}
|
||||
if (tensor_storage.name.find("model.diffusion_model.video_patch_proj.weight") != std::string::npos &&
|
||||
@@ -1090,10 +1101,12 @@ bool ModelLoader::load_tensors(on_new_tensor_cb_t on_new_tensor_cb,
|
||||
if (tensors_to_process.empty()) {
|
||||
continue;
|
||||
}
|
||||
LOG_VERBOSE("loading %zu/%zu tensors from %s",
|
||||
tensors_to_process.size(),
|
||||
file_tensors.size(),
|
||||
file_path.c_str());
|
||||
if (log_progress) {
|
||||
LOG_VERBOSE("loading %zu/%zu tensors from %s",
|
||||
tensors_to_process.size(),
|
||||
file_tensors.size(),
|
||||
file_path.c_str());
|
||||
}
|
||||
|
||||
bool is_zip = fdata.is_zip;
|
||||
|
||||
@@ -1597,6 +1610,9 @@ bool ModelLoader::tensor_should_be_converted(const TensorStorage& tensor_storage
|
||||
// Pass, do not convert. For Unet
|
||||
} else if (contains(name, "embedding")) {
|
||||
// Pass, do not convert embedding
|
||||
} else if (contains(name, "scale_shift_table")) {
|
||||
// Pass, do not convert. adaLN modulation tables (PixArt, LTXV) are sliced
|
||||
// element-wise, which is invalid on quantized block layouts.
|
||||
} else if (ends_with(name, "_pad_token")) {
|
||||
// Pass, do not convert. LLaDA-Image stores its pad tokens far outside the f16
|
||||
// range, so any format with an f16 scale or payload turns them into inf.
|
||||
|
||||
+93
-14
@@ -246,17 +246,13 @@ bool ModelManager::register_param_tensors(ModelComponent component,
|
||||
}
|
||||
ggml_set_name(tensor, name.c_str());
|
||||
|
||||
auto state = std::make_unique<TensorState>();
|
||||
state->name = name;
|
||||
state->tensor = tensor;
|
||||
state->component = component;
|
||||
state->source_file = source_file;
|
||||
state->source_version = source_version;
|
||||
auto source = sources.find(name);
|
||||
if (source != sources.end()) {
|
||||
state->source = source->second;
|
||||
state->has_source = true;
|
||||
}
|
||||
auto state = std::make_unique<TensorState>();
|
||||
state->name = name;
|
||||
state->tensor = tensor;
|
||||
state->component = component;
|
||||
state->source_file = source_file;
|
||||
state->source_version = source_version;
|
||||
state->sources = find_tensor_sources(*state, sources);
|
||||
state->residency_mode = residency_mode;
|
||||
state->compute_backend = compute_backend;
|
||||
state->params_backend = params_backend;
|
||||
@@ -757,12 +753,33 @@ bool ModelManager::validate_tensor(const TensorState& state) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!state.has_source) {
|
||||
if (state.sources.empty()) {
|
||||
LOG_ERROR("%s tensor '%s' not in model metadata", model_component_name(state.component), state.name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
const TensorStorage& tensor_storage = state.source;
|
||||
TensorStorage tensor_storage = state.sources.front();
|
||||
if (state.sources.size() > 1) {
|
||||
const int dim = tensor_storage.n_dims - 1;
|
||||
if (dim < 0 || dim >= GGML_MAX_DIMS) {
|
||||
return false;
|
||||
}
|
||||
tensor_storage.ne[dim] = 0;
|
||||
for (const auto& part : state.sources) {
|
||||
if (part.n_dims != tensor_storage.n_dims || part.ne[dim] < 0 ||
|
||||
part.ne[dim] > state.tensor->ne[dim] - tensor_storage.ne[dim]) {
|
||||
LOG_ERROR("invalid tensor part '%s' for '%s'", part.name.c_str(), state.name.c_str());
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < GGML_MAX_DIMS; ++i) {
|
||||
if (i != dim && part.ne[i] != tensor_storage.ne[i]) {
|
||||
LOG_ERROR("incompatible tensor part '%s' for '%s'", part.name.c_str(), state.name.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
tensor_storage.ne[dim] += part.ne[dim];
|
||||
}
|
||||
}
|
||||
if (state.tensor->ne[0] != tensor_storage.ne[0] ||
|
||||
state.tensor->ne[1] != tensor_storage.ne[1] ||
|
||||
state.tensor->ne[2] != tensor_storage.ne[2] ||
|
||||
@@ -831,7 +848,7 @@ bool ModelManager::mmap_params(const std::vector<TensorState*>& states,
|
||||
}
|
||||
|
||||
bool ModelManager::can_mmap_storage(const TensorState& state) const {
|
||||
if (state.source_file != 0 || !enable_mmap_ || state.residency_mode != ResidencyMode::ParamBackend) {
|
||||
if (state.sources.size() > 1 || state.source_file != 0 || !enable_mmap_ || state.residency_mode != ResidencyMode::ParamBackend) {
|
||||
return false;
|
||||
}
|
||||
if (state.compute_backend == nullptr || state.params_backend == nullptr) {
|
||||
@@ -941,6 +958,62 @@ bool ModelManager::alloc_params_buffers(const std::vector<TensorState*>& states,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModelManager::load_tensor_parts(TensorState& state) {
|
||||
auto ctx = std::unique_ptr<ggml_context, decltype(&ggml_free)>(
|
||||
ggml_init({state.sources.size() * ggml_tensor_overhead(), nullptr, true}), ggml_free);
|
||||
if (!ctx) {
|
||||
return false;
|
||||
}
|
||||
const size_t size = ggml_nbytes(state.tensor);
|
||||
std::vector<uint8_t> buffer;
|
||||
void* data = state.tensor->data;
|
||||
if (!ggml_backend_buffer_is_host(state.tensor->buffer)) {
|
||||
buffer.resize(size);
|
||||
data = buffer.data();
|
||||
}
|
||||
std::map<std::string, ggml_tensor*> parts;
|
||||
std::set<std::string> names;
|
||||
size_t offset = 0;
|
||||
for (const auto& source : state.sources) {
|
||||
auto part = ggml_new_tensor(ctx.get(), state.tensor->type, source.n_dims, source.ne);
|
||||
const size_t part_size = ggml_nbytes(part);
|
||||
if (part_size > size - offset) {
|
||||
return false;
|
||||
}
|
||||
part->data = static_cast<uint8_t*>(data) + offset;
|
||||
parts[source.name] = part;
|
||||
names.insert(source.name);
|
||||
offset += part_size;
|
||||
}
|
||||
if (offset != size) {
|
||||
return false;
|
||||
}
|
||||
std::set<std::string> loaded;
|
||||
std::mutex mutex;
|
||||
auto callback = [&](const TensorStorage& source, ggml_tensor** dst) {
|
||||
*dst = nullptr;
|
||||
auto part = parts.find(source.name);
|
||||
if (part != parts.end()) {
|
||||
*dst = part->second;
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
loaded.insert(source.name);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
const bool success = state.source_file == 0
|
||||
? model_loader_.load_tensors(callback, enable_mmap_, &names, false)
|
||||
: model_loader_.load_file_tensors(state.source_file, state.source_version, callback, names, enable_mmap_);
|
||||
if (!success || loaded != names) {
|
||||
return false;
|
||||
}
|
||||
if (!buffer.empty()) {
|
||||
// Upload the assembled tensor once, including for row-split backend buffers.
|
||||
ggml_backend_tensor_set(state.tensor, buffer.data(), 0, size);
|
||||
}
|
||||
state.loaded_to_params_backend = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModelManager::load_tensors(const std::vector<TensorState*>& states) {
|
||||
using ReadGroup = std::pair<ModelLoader::FileId, SDVersion>;
|
||||
using ReadBatch = std::map<std::string, std::vector<TensorState*>>;
|
||||
@@ -948,6 +1021,12 @@ bool ModelManager::load_tensors(const std::vector<TensorState*>& states) {
|
||||
for (auto* state : states) {
|
||||
if (state == nullptr)
|
||||
continue;
|
||||
if (state->sources.size() > 1) {
|
||||
if (!load_tensor_parts(*state)) {
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
auto& batches = groups[{state->source_file, state->source_version}];
|
||||
// The loader supplies one destination per name; only conflicting types need another batch.
|
||||
auto batch = std::find_if(batches.begin(), batches.end(), [&](const ReadBatch& candidate) {
|
||||
|
||||
+3
-2
@@ -38,8 +38,7 @@ private:
|
||||
std::string name;
|
||||
ggml_tensor* tensor = nullptr;
|
||||
ModelComponent component = ModelComponent::Count;
|
||||
TensorStorage source;
|
||||
bool has_source = false;
|
||||
std::vector<TensorStorage> sources;
|
||||
ModelLoader::FileId source_file = 0;
|
||||
SDVersion source_version = VERSION_COUNT;
|
||||
|
||||
@@ -138,6 +137,8 @@ private:
|
||||
bool apply_loras_to_params(const std::vector<TensorState*>& states);
|
||||
bool mmap_params(const std::vector<TensorState*>& states,
|
||||
std::vector<ParamsStorageBlock*>& created_storage_blocks);
|
||||
static std::vector<TensorStorage> find_tensor_sources(const TensorState& state, const String2TensorStorage& sources);
|
||||
bool load_tensor_parts(TensorState& state);
|
||||
bool can_mmap_storage(const TensorState& state) const;
|
||||
bool alloc_params_buffers(const std::vector<TensorState*>& states,
|
||||
std::vector<ParamsStorageBlock*>& created_storage_blocks);
|
||||
|
||||
+26
-11
@@ -15,6 +15,26 @@ static bool same_tensor_source(const TensorStorage& a, const TensorStorage& b) {
|
||||
a.int8_convrot_group_size == b.int8_convrot_group_size;
|
||||
}
|
||||
|
||||
std::vector<TensorStorage> ModelManager::find_tensor_sources(const TensorState& state, const String2TensorStorage& sources) {
|
||||
auto first = sources.find(state.name);
|
||||
if (first == sources.end()) {
|
||||
return {};
|
||||
}
|
||||
std::vector<TensorStorage> result{first->second};
|
||||
if (state.component == ModelComponent::LoRA ||
|
||||
std::equal(first->second.ne, first->second.ne + GGML_MAX_DIMS, state.tensor->ne)) {
|
||||
return result;
|
||||
}
|
||||
for (size_t i = 1;; ++i) {
|
||||
auto part = sources.find(state.name + "." + std::to_string(i));
|
||||
if (part == sources.end()) {
|
||||
break;
|
||||
}
|
||||
result.push_back(part->second);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void ModelManager::invalidate_sources(const std::unordered_set<TensorState*>& states) {
|
||||
auto affected = states;
|
||||
for (const auto& block : params_storage_blocks_) {
|
||||
@@ -76,20 +96,16 @@ bool ModelManager::set_loader(ModelLoader loader) {
|
||||
}
|
||||
std::unordered_set<TensorState*> changed;
|
||||
for (const auto& state : tensor_states_) {
|
||||
const auto& sources = sources_for(*state);
|
||||
auto source = sources.find(state->name);
|
||||
const bool found = source != sources.end();
|
||||
if (found != state->has_source || (found && !same_tensor_source(state->source, source->second)) ||
|
||||
const auto sources = find_tensor_sources(*state, sources_for(*state));
|
||||
if (sources.size() != state->sources.size() ||
|
||||
!std::equal(sources.begin(), sources.end(), state->sources.begin(), same_tensor_source) ||
|
||||
(lora_changed && state->component != ModelComponent::LoRA && state->applied_lora_epoch != UINT64_MAX)) {
|
||||
changed.insert(state.get());
|
||||
}
|
||||
}
|
||||
invalidate_sources(changed);
|
||||
for (auto* state : changed) {
|
||||
const auto& sources = sources_for(*state);
|
||||
auto source = sources.find(state->name);
|
||||
state->has_source = source != sources.end();
|
||||
state->source = state->has_source ? source->second : TensorStorage{};
|
||||
state->sources = find_tensor_sources(*state, sources_for(*state));
|
||||
}
|
||||
if (lora_changed) {
|
||||
++current_lora_epoch_;
|
||||
@@ -134,9 +150,8 @@ ModelLoader::FileVersions ModelManager::source_versions(const std::set<ModelComp
|
||||
versions[state->source_file] = loader.file_revision(state->source_file);
|
||||
continue;
|
||||
}
|
||||
auto source = sources.find(state->name);
|
||||
if (source != sources.end()) {
|
||||
versions[source->second.file_id] = source->second.file_revision;
|
||||
for (const auto& source : find_tensor_sources(*state, sources)) {
|
||||
versions[source.file_id] = source.file_revision;
|
||||
}
|
||||
}
|
||||
return versions;
|
||||
|
||||
@@ -932,6 +932,68 @@ static bool is_diffusers_controlnet_name(const std::string& name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::string convert_diffusers_dit_to_original_pixart(std::string name) {
|
||||
static const std::vector<std::pair<std::string, std::string>> prefix_map = {
|
||||
{"pos_embed.proj.", "x_embedder.proj."},
|
||||
{"adaln_single.emb.timestep_embedder.linear_1.", "t_embedder.mlp.0."},
|
||||
{"adaln_single.emb.timestep_embedder.linear_2.", "t_embedder.mlp.2."},
|
||||
{"adaln_single.emb.resolution_embedder.linear_1.", "csize_embedder.mlp.0."},
|
||||
{"adaln_single.emb.resolution_embedder.linear_2.", "csize_embedder.mlp.2."},
|
||||
{"adaln_single.emb.aspect_ratio_embedder.linear_1.", "ar_embedder.mlp.0."},
|
||||
{"adaln_single.emb.aspect_ratio_embedder.linear_2.", "ar_embedder.mlp.2."},
|
||||
{"adaln_single.linear.", "t_block.1."},
|
||||
{"caption_projection.linear_1.", "y_embedder.y_proj.fc1."},
|
||||
{"caption_projection.linear_2.", "y_embedder.y_proj.fc2."},
|
||||
{"proj_out.", "final_layer.linear."},
|
||||
};
|
||||
for (const auto& entry : prefix_map) {
|
||||
if (starts_with(name, entry.first)) {
|
||||
return entry.second + name.substr(entry.first.size());
|
||||
}
|
||||
}
|
||||
if (name == "scale_shift_table") {
|
||||
return "final_layer.scale_shift_table";
|
||||
}
|
||||
const std::string block_prefix = "transformer_blocks.";
|
||||
if (!starts_with(name, block_prefix)) {
|
||||
return name;
|
||||
}
|
||||
const size_t block_end = name.find('.', block_prefix.size());
|
||||
if (block_end == std::string::npos) {
|
||||
return name;
|
||||
}
|
||||
const std::string prefix = "blocks." + name.substr(block_prefix.size(), block_end - block_prefix.size()) + ".";
|
||||
name = name.substr(block_end + 1);
|
||||
static const std::vector<std::pair<std::string, std::string>> block_map = {
|
||||
{"attn1.to_q.", "attn.qkv."},
|
||||
{"attn1.to_out.0.", "attn.proj."},
|
||||
{"attn2.to_q.", "cross_attn.q_linear."},
|
||||
{"attn2.to_k.", "cross_attn.kv_linear."},
|
||||
{"attn2.to_out.0.", "cross_attn.proj."},
|
||||
{"ff.net.0.proj.", "mlp.fc1."},
|
||||
{"ff.net.2.", "mlp.fc2."},
|
||||
};
|
||||
for (const auto& entry : block_map) {
|
||||
if (starts_with(name, entry.first)) {
|
||||
return prefix + entry.second + name.substr(entry.first.size());
|
||||
}
|
||||
}
|
||||
static const std::vector<std::pair<std::string, std::string>> part_map = {
|
||||
{"attn1.to_k.weight", "attn.qkv.weight.1"},
|
||||
{"attn1.to_k.bias", "attn.qkv.bias.1"},
|
||||
{"attn1.to_v.weight", "attn.qkv.weight.2"},
|
||||
{"attn1.to_v.bias", "attn.qkv.bias.2"},
|
||||
{"attn2.to_v.weight", "cross_attn.kv_linear.weight.1"},
|
||||
{"attn2.to_v.bias", "cross_attn.kv_linear.bias.1"},
|
||||
};
|
||||
for (const auto& entry : part_map) {
|
||||
if (name == entry.first || starts_with(name, entry.first + ".")) {
|
||||
return prefix + entry.second + name.substr(entry.first.size());
|
||||
}
|
||||
}
|
||||
return prefix + name;
|
||||
}
|
||||
|
||||
std::string convert_diffusion_model_name(std::string name, std::string prefix, SDVersion version) {
|
||||
if (sd_version_is_sd1(version) || sd_version_is_sd2(version)) {
|
||||
name = convert_diffusers_unet_to_original_sd1(name);
|
||||
@@ -951,6 +1013,8 @@ std::string convert_diffusion_model_name(std::string name, std::string prefix, S
|
||||
name = convert_other_dit_to_original_anima(name);
|
||||
} else if (sd_version_is_krea2(version)) {
|
||||
name = convert_diffusers_dit_to_original_krea2(name);
|
||||
} else if (sd_version_is_pixart(version)) {
|
||||
name = convert_diffusers_dit_to_original_pixart(name);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
@@ -105,6 +105,7 @@ const char* model_version_to_str[] = {
|
||||
"SenseNova U1.5",
|
||||
"LLaDA-Image",
|
||||
"ESRGAN",
|
||||
"PixArt",
|
||||
};
|
||||
|
||||
static_assert(VERSION_COUNT == sizeof(model_version_to_str) / sizeof(model_version_to_str[0]),
|
||||
@@ -125,6 +126,18 @@ void calculate_alphas_cumprod(float* alphas_cumprod,
|
||||
}
|
||||
}
|
||||
|
||||
void calculate_alphas_cumprod_linear_beta(float* alphas_cumprod,
|
||||
float beta_start,
|
||||
float beta_end,
|
||||
int timesteps = TIMESTEPS) {
|
||||
float product = 1.0f;
|
||||
for (int i = 0; i < timesteps; i++) {
|
||||
float beta = beta_start + (beta_end - beta_start) * ((float)i / (timesteps - 1));
|
||||
product *= 1.0f - beta;
|
||||
alphas_cumprod[i] = product;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct has_set_runtime_backends : std::false_type {};
|
||||
template <typename T>
|
||||
@@ -666,6 +679,10 @@ void StableDiffusionGGML::refresh_compvis_denoiser_sigmas() {
|
||||
std::vector<float> alphas_cumprod(TIMESTEPS);
|
||||
if (file_alphas_cumprod.size() == TIMESTEPS) {
|
||||
alphas_cumprod = file_alphas_cumprod;
|
||||
} else if (sd_version_is_pixart(version)) {
|
||||
// PixArt checkpoints train with a linear beta schedule (0.0001 -> 0.02)
|
||||
// instead of the scaled_linear schedule used by SD1.x/SDXL.
|
||||
calculate_alphas_cumprod_linear_beta(alphas_cumprod.data(), 0.0001f, 0.02f);
|
||||
} else {
|
||||
calculate_alphas_cumprod(alphas_cumprod.data());
|
||||
}
|
||||
@@ -2731,7 +2748,7 @@ int StableDiffusionGGML::get_diffusion_model_down_factor() {
|
||||
if (sd_version_is_dit(version)) {
|
||||
if (sd_version_is_sensenova_u1(version)) {
|
||||
down_factor = 32;
|
||||
} else if (version == VERSION_QWEN_IMAGE_2_1 || sd_version_is_wan(version) || sd_version_is_lingbot_video(version) || sd_version_is_minimax_h3(version)) {
|
||||
} else if (version == VERSION_QWEN_IMAGE_2_1 || sd_version_is_wan(version) || sd_version_is_lingbot_video(version) || sd_version_is_minimax_h3(version) || sd_version_is_pixart(version)) {
|
||||
down_factor = 2;
|
||||
} else {
|
||||
down_factor = 1;
|
||||
@@ -2769,6 +2786,8 @@ int StableDiffusionGGML::get_latent_channel() {
|
||||
latent_channel = 128;
|
||||
} else if (sd_version_is_mage_flow(version)) {
|
||||
latent_channel = 128;
|
||||
} else if (sd_version_is_pixart(version)) {
|
||||
latent_channel = 4;
|
||||
} else {
|
||||
latent_channel = 16;
|
||||
}
|
||||
@@ -2864,14 +2883,26 @@ sd::Tensor<float> StableDiffusionGGML::decode_first_stage(const sd::Tensor<float
|
||||
return sd::ops::clamp((x + 1.f) * 0.5f, 0.0f, 1.0f);
|
||||
}
|
||||
auto latents = first_stage_model->diffusion_to_vae_latents(x);
|
||||
auto decoded = first_stage_model->decode(n_threads, latents, vae_tiling_params, decode_video, circular_x, circular_y);
|
||||
const bool prefer_temporal_tiling = decode_video && first_stage_model->can_temporal_tile_decode();
|
||||
while (decoded.empty() &&
|
||||
sd::backend_fit::prepare_vae_decode_retry_tiling(vae_tiling_params, prefer_temporal_tiling,
|
||||
first_stage_model->last_compute_status())) {
|
||||
decoded = first_stage_model->decode(n_threads, latents, vae_tiling_params, decode_video, circular_x, circular_y);
|
||||
auto tiling_params = first_stage_model->resolve_tiling_params(vae_tiling_params);
|
||||
const bool prefer_temporal_tiling = decode_video && latents.dim() == 5 && latents.shape()[2] > 1 &&
|
||||
first_stage_model->can_temporal_tile_decode();
|
||||
for (;;) {
|
||||
int tile_size_w = static_cast<int>(latents.shape()[0]);
|
||||
int tile_size_h = static_cast<int>(latents.shape()[1]);
|
||||
float tile_overlap;
|
||||
if (tiling_params.enabled &&
|
||||
!first_stage_model->get_tile_sizes(tile_size_w, tile_size_h, tile_overlap, tiling_params,
|
||||
latents.shape()[0], latents.shape()[1])) {
|
||||
return {};
|
||||
}
|
||||
auto decoded = first_stage_model->decode(n_threads, latents, tiling_params, decode_video, circular_x, circular_y);
|
||||
if (!decoded.empty() ||
|
||||
!sd::backend_fit::prepare_vae_decode_retry_tiling(tiling_params, prefer_temporal_tiling,
|
||||
first_stage_model->last_compute_status(),
|
||||
tile_size_w, tile_size_h, first_stage_model->get_scale_factor())) {
|
||||
return decoded;
|
||||
}
|
||||
}
|
||||
return decoded;
|
||||
}
|
||||
|
||||
sd::Tensor<float> StableDiffusionGGML::normalize_ltx_video_latents(const sd::Tensor<float>& x) {
|
||||
|
||||
+15
-13
@@ -35,19 +35,21 @@ namespace sd::pipeline {
|
||||
return original_axes;
|
||||
}
|
||||
|
||||
int tile_size_x, tile_size_y;
|
||||
int tile_size_w, tile_size_h;
|
||||
float overlap;
|
||||
int latent_size_x = request.width / request.vae_scale_factor;
|
||||
int latent_size_y = request.height / request.vae_scale_factor;
|
||||
sd->first_stage_model->get_tile_sizes(tile_size_x,
|
||||
tile_size_y,
|
||||
overlap,
|
||||
sd_img_gen_params->vae_tiling_params,
|
||||
latent_size_x,
|
||||
latent_size_y);
|
||||
int latent_size_w = request.width / request.vae_scale_factor;
|
||||
int latent_size_h = request.height / request.vae_scale_factor;
|
||||
if (!sd->first_stage_model->get_tile_sizes(tile_size_w,
|
||||
tile_size_h,
|
||||
overlap,
|
||||
sd_img_gen_params->vae_tiling_params,
|
||||
latent_size_w,
|
||||
latent_size_h)) {
|
||||
return original_axes;
|
||||
}
|
||||
|
||||
sd->circular_x = sd->circular_x && (tile_size_x >= latent_size_x);
|
||||
sd->circular_y = sd->circular_y && (tile_size_y >= latent_size_y);
|
||||
sd->circular_x = sd->circular_x && (tile_size_w >= latent_size_w);
|
||||
sd->circular_y = sd->circular_y && (tile_size_h >= latent_size_h);
|
||||
|
||||
if (sd->first_stage_model) {
|
||||
sd->first_stage_model->set_circular_axes(sd->circular_x, sd->circular_y);
|
||||
@@ -56,8 +58,8 @@ namespace sd::pipeline {
|
||||
sd->preview_vae->set_circular_axes(sd->circular_x, sd->circular_y);
|
||||
}
|
||||
|
||||
sd->circular_x = original_axes.circular_x && (tile_size_x < latent_size_x);
|
||||
sd->circular_y = original_axes.circular_y && (tile_size_y < latent_size_y);
|
||||
sd->circular_x = original_axes.circular_x && (tile_size_w < latent_size_w);
|
||||
sd->circular_y = original_axes.circular_y && (tile_size_h < latent_size_h);
|
||||
|
||||
return original_axes;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "model_builders.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
|
||||
@@ -28,6 +29,7 @@
|
||||
#include "model/diffusion/mmdit.hpp"
|
||||
#include "model/diffusion/model.hpp"
|
||||
#include "model/diffusion/pid.hpp"
|
||||
#include "model/diffusion/pixart.hpp"
|
||||
#include "model/diffusion/qwen_image.hpp"
|
||||
#include "model/diffusion/qwen_image_2_1.hpp"
|
||||
#include "model/diffusion/sensenova_u1.h"
|
||||
@@ -301,6 +303,19 @@ namespace sd::model_builders {
|
||||
weight_manager,
|
||||
sd_ctx_params->model_args);
|
||||
}
|
||||
} else if (version == VERSION_PIXART) {
|
||||
result.conditioner = std::make_shared<T5CLIPEmbedder>(ctx.backends.runtime_backend(SDBackendModule::TE),
|
||||
tensor_storage_map,
|
||||
true,
|
||||
0,
|
||||
false,
|
||||
weight_manager,
|
||||
sd_ctx_params->model_args);
|
||||
result.diffusion = std::make_shared<PixArt::PixArtRunner>(ctx.backends.runtime_backend(SDBackendModule::DIFFUSION),
|
||||
tensor_storage_map,
|
||||
"model.diffusion_model",
|
||||
weight_manager,
|
||||
sd_ctx_params->model_args);
|
||||
} else if (sd_version_is_mage_flow(version)) {
|
||||
result.conditioner = std::make_shared<LLMEmbedder>(ctx.backends.runtime_backend(SDBackendModule::TE),
|
||||
tensor_storage_map,
|
||||
@@ -562,6 +577,23 @@ namespace sd::model_builders {
|
||||
false,
|
||||
vae_version,
|
||||
weight_manager);
|
||||
if (sd_version_is_pixart(version)) {
|
||||
// Alpha-512 and Sigma share tensor layouts; Alpha-512 needs an explicit scale override.
|
||||
if (tensor_storage_map.count("model.diffusion_model.csize_embedder.mlp.0.weight") != 0) {
|
||||
model->scale_factor = 0.18215f;
|
||||
}
|
||||
for (const auto& [key, value] : parse_key_value_args(sd_ctx_params->model_args, "model arg")) {
|
||||
if (key == "pixart_vae_scale_factor") {
|
||||
float parsed = 0.f;
|
||||
if (parse_strict_float(value, parsed) && std::isfinite(parsed) && parsed > 0.f) {
|
||||
model->scale_factor = parsed;
|
||||
} else {
|
||||
LOG_WARN("ignoring invalid PixArt model arg '%s=%s'", key.c_str(), value.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG_VERBOSE("pixart: VAE scale factor = %.5f", model->scale_factor);
|
||||
}
|
||||
if (sd_version_is_sdxl(version) &&
|
||||
(strlen(SAFE_STR(sd_ctx_params->vae_path)) == 0 || sd_ctx_params->force_sdxl_vae_conv_scale || options.external_vae_is_invalid)) {
|
||||
float vae_conv_2d_scale = 1.f / 32.f;
|
||||
|
||||
+24
-24
@@ -142,8 +142,8 @@ sd::Tensor<float> process_tiles_2d(const sd::Tensor<float>& input,
|
||||
int output_width,
|
||||
int output_height,
|
||||
int scale,
|
||||
int p_tile_size_x,
|
||||
int p_tile_size_y,
|
||||
int p_tile_size_w,
|
||||
int p_tile_size_h,
|
||||
float tile_overlap_factor,
|
||||
bool circular_x,
|
||||
bool circular_y,
|
||||
@@ -168,28 +168,28 @@ sd::Tensor<float> process_tiles_2d(const sd::Tensor<float>& input,
|
||||
|
||||
int num_tiles_x;
|
||||
float tile_overlap_factor_x;
|
||||
sd_tiling_calc_tiles(num_tiles_x, tile_overlap_factor_x, small_width, p_tile_size_x, tile_overlap_factor, circular_x);
|
||||
sd_tiling_calc_tiles(num_tiles_x, tile_overlap_factor_x, small_width, p_tile_size_w, tile_overlap_factor, circular_x);
|
||||
|
||||
int num_tiles_y;
|
||||
float tile_overlap_factor_y;
|
||||
sd_tiling_calc_tiles(num_tiles_y, tile_overlap_factor_y, small_height, p_tile_size_y, tile_overlap_factor, circular_y);
|
||||
sd_tiling_calc_tiles(num_tiles_y, tile_overlap_factor_y, small_height, p_tile_size_h, tile_overlap_factor, circular_y);
|
||||
|
||||
int tile_overlap_x = static_cast<int32_t>(p_tile_size_x * tile_overlap_factor_x);
|
||||
int non_tile_overlap_x = p_tile_size_x - tile_overlap_x;
|
||||
int tile_overlap_y = static_cast<int32_t>(p_tile_size_y * tile_overlap_factor_y);
|
||||
int non_tile_overlap_y = p_tile_size_y - tile_overlap_y;
|
||||
int tile_size_x = p_tile_size_x < small_width ? p_tile_size_x : small_width;
|
||||
int tile_size_y = p_tile_size_y < small_height ? p_tile_size_y : small_height;
|
||||
int input_tile_size_x = tile_size_x;
|
||||
int input_tile_size_y = tile_size_y;
|
||||
int output_tile_size_x = tile_size_x;
|
||||
int output_tile_size_y = tile_size_y;
|
||||
int tile_overlap_x = static_cast<int32_t>(p_tile_size_w * tile_overlap_factor_x);
|
||||
int non_tile_overlap_x = p_tile_size_w - tile_overlap_x;
|
||||
int tile_overlap_y = static_cast<int32_t>(p_tile_size_h * tile_overlap_factor_y);
|
||||
int non_tile_overlap_y = p_tile_size_h - tile_overlap_y;
|
||||
int tile_size_w = p_tile_size_w < small_width ? p_tile_size_w : small_width;
|
||||
int tile_size_h = p_tile_size_h < small_height ? p_tile_size_h : small_height;
|
||||
int input_tile_size_w = tile_size_w;
|
||||
int input_tile_size_h = tile_size_h;
|
||||
int output_tile_size_w = tile_size_w;
|
||||
int output_tile_size_h = tile_size_h;
|
||||
if (decode) {
|
||||
output_tile_size_x *= scale;
|
||||
output_tile_size_y *= scale;
|
||||
output_tile_size_w *= scale;
|
||||
output_tile_size_h *= scale;
|
||||
} else {
|
||||
input_tile_size_x *= scale;
|
||||
input_tile_size_y *= scale;
|
||||
input_tile_size_w *= scale;
|
||||
input_tile_size_h *= scale;
|
||||
}
|
||||
|
||||
int num_tiles = num_tiles_x * num_tiles_y;
|
||||
@@ -205,9 +205,9 @@ sd::Tensor<float> process_tiles_2d(const sd::Tensor<float>& input,
|
||||
}
|
||||
for (int y = 0; y < small_height && !last_y; y += non_tile_overlap_y) {
|
||||
int dy = 0;
|
||||
if (!circular_y && y + tile_size_y >= small_height) {
|
||||
if (!circular_y && y + tile_size_h >= small_height) {
|
||||
int original_y = y;
|
||||
y = small_height - tile_size_y;
|
||||
y = small_height - tile_size_h;
|
||||
dy = original_y - y;
|
||||
if (decode) {
|
||||
dy *= scale;
|
||||
@@ -216,9 +216,9 @@ sd::Tensor<float> process_tiles_2d(const sd::Tensor<float>& input,
|
||||
}
|
||||
for (int x = 0; x < small_width && !last_x; x += non_tile_overlap_x) {
|
||||
int dx = 0;
|
||||
if (!circular_x && x + tile_size_x >= small_width) {
|
||||
if (!circular_x && x + tile_size_w >= small_width) {
|
||||
int original_x = x;
|
||||
x = small_width - tile_size_x;
|
||||
x = small_width - tile_size_w;
|
||||
dx = original_x - x;
|
||||
if (decode) {
|
||||
dx *= scale;
|
||||
@@ -235,12 +235,12 @@ sd::Tensor<float> process_tiles_2d(const sd::Tensor<float>& input,
|
||||
int overlap_y_out = decode ? tile_overlap_y * scale : tile_overlap_y;
|
||||
|
||||
int64_t t1 = ggml_time_ms();
|
||||
auto input_tile = sd_tensor_split_2d(input, input_tile_size_x, input_tile_size_y, x_in, y_in);
|
||||
auto input_tile = sd_tensor_split_2d(input, input_tile_size_w, input_tile_size_h, x_in, y_in);
|
||||
auto output_tile = on_processing(input_tile);
|
||||
if (output_tile.empty()) {
|
||||
return {};
|
||||
}
|
||||
GGML_ASSERT(output_tile.shape()[0] == output_tile_size_x && output_tile.shape()[1] == output_tile_size_y);
|
||||
GGML_ASSERT(output_tile.shape()[0] == output_tile_size_w && output_tile.shape()[1] == output_tile_size_h);
|
||||
if (output.empty()) {
|
||||
std::vector<int64_t> output_shape = output_tile.shape();
|
||||
output_shape[0] = output_width;
|
||||
|
||||
@@ -11,8 +11,8 @@ sd::Tensor<float> process_tiles_2d(const sd::Tensor<float>& input,
|
||||
int output_width,
|
||||
int output_height,
|
||||
int scale,
|
||||
int p_tile_size_x,
|
||||
int p_tile_size_y,
|
||||
int p_tile_size_w,
|
||||
int p_tile_size_h,
|
||||
float tile_overlap_factor,
|
||||
bool circular_x,
|
||||
bool circular_y,
|
||||
|
||||
@@ -295,7 +295,13 @@ bool T5UniGramTokenizer::encode(const std::string& input, std::vector<int>& resu
|
||||
std::vector<int32_t> tokens;
|
||||
std::vector<std::string> token_strs;
|
||||
std::string normalized = normalize(input);
|
||||
auto splited_texts = split_with_special_tokens(normalized, special_tokens);
|
||||
if (normalized.empty()) {
|
||||
// HF reference tokenizers emit no pieces for empty input; pad_tokens
|
||||
// still appends EOS so the sequence becomes [EOS] + padding.
|
||||
result = std::move(tokens);
|
||||
return true;
|
||||
}
|
||||
auto splited_texts = split_with_special_tokens(normalized, special_tokens);
|
||||
if (splited_texts.empty()) {
|
||||
splited_texts.push_back(normalized); // for empty string
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user