mirror of
https://github.com/leejet/stable-diffusion.cpp.git
synced 2026-09-25 12:40:41 +00:00
refactor: define VAE tile dimensions in image pixels (#2059)
This commit is contained in:
parent
39ada0863b
commit
19bbbca1c7
@ -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
|
the summary is printed only on the first graph or when backend capacities or the
|
||||||
segment count change.
|
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.
|
## 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.
|
Using `--offload-to-cpu` allows you to offload weights to the CPU, saving VRAM without reducing generation speed.
|
||||||
|
|||||||
@ -1341,7 +1341,7 @@ ArgOptions SDGenerationParams::get_options() {
|
|||||||
&embed_image_metadata},
|
&embed_image_metadata},
|
||||||
{"",
|
{"",
|
||||||
"--vae-tiling",
|
"--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,
|
true,
|
||||||
&vae_tiling_params.enabled},
|
&vae_tiling_params.enabled},
|
||||||
{"",
|
{"",
|
||||||
@ -1605,12 +1605,12 @@ ArgOptions SDGenerationParams::get_options() {
|
|||||||
size_t x_pos = tile_size_str.find('x');
|
size_t x_pos = tile_size_str.find('x');
|
||||||
try {
|
try {
|
||||||
if (x_pos != std::string::npos) {
|
if (x_pos != std::string::npos) {
|
||||||
std::string tile_x_str = tile_size_str.substr(0, x_pos);
|
std::string tile_w_str = tile_size_str.substr(0, x_pos);
|
||||||
std::string tile_y_str = tile_size_str.substr(x_pos + 1);
|
std::string tile_h_str = tile_size_str.substr(x_pos + 1);
|
||||||
vae_tiling_params.tile_size_x = std::stoi(tile_x_str);
|
vae_tiling_params.tile_size_w = std::stoi(tile_w_str);
|
||||||
vae_tiling_params.tile_size_y = std::stoi(tile_y_str);
|
vae_tiling_params.tile_size_h = std::stoi(tile_h_str);
|
||||||
} else {
|
} 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&) {
|
} catch (const std::invalid_argument&) {
|
||||||
return -1;
|
return -1;
|
||||||
@ -1628,12 +1628,12 @@ ArgOptions SDGenerationParams::get_options() {
|
|||||||
size_t x_pos = rel_size_str.find('x');
|
size_t x_pos = rel_size_str.find('x');
|
||||||
try {
|
try {
|
||||||
if (x_pos != std::string::npos) {
|
if (x_pos != std::string::npos) {
|
||||||
std::string rel_x_str = rel_size_str.substr(0, x_pos);
|
std::string rel_w_str = rel_size_str.substr(0, x_pos);
|
||||||
std::string rel_y_str = rel_size_str.substr(x_pos + 1);
|
std::string rel_h_str = rel_size_str.substr(x_pos + 1);
|
||||||
vae_tiling_params.rel_size_x = std::stof(rel_x_str);
|
vae_tiling_params.rel_size_w = std::stof(rel_w_str);
|
||||||
vae_tiling_params.rel_size_y = std::stof(rel_y_str);
|
vae_tiling_params.rel_size_h = std::stof(rel_h_str);
|
||||||
} else {
|
} 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&) {
|
} catch (const std::invalid_argument&) {
|
||||||
return -1;
|
return -1;
|
||||||
@ -1763,11 +1763,11 @@ ArgOptions SDGenerationParams::get_options() {
|
|||||||
on_scm_policy_arg},
|
on_scm_policy_arg},
|
||||||
{"",
|
{"",
|
||||||
"--vae-tile-size",
|
"--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},
|
on_tile_size_arg},
|
||||||
{"",
|
{"",
|
||||||
"--vae-relative-tile-size",
|
"--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},
|
on_relative_tile_size_arg},
|
||||||
{"",
|
{"",
|
||||||
"--prompt-file",
|
"--prompt-file",
|
||||||
@ -2224,20 +2224,20 @@ bool SDGenerationParams::from_json_str(
|
|||||||
if (tiling_json.contains("temporal_tiling") && tiling_json["temporal_tiling"].is_boolean()) {
|
if (tiling_json.contains("temporal_tiling") && tiling_json["temporal_tiling"].is_boolean()) {
|
||||||
vae_tiling_params.temporal_tiling = tiling_json["temporal_tiling"];
|
vae_tiling_params.temporal_tiling = tiling_json["temporal_tiling"];
|
||||||
}
|
}
|
||||||
if (tiling_json.contains("tile_size_x") && tiling_json["tile_size_x"].is_number_integer()) {
|
if (tiling_json.contains("tile_size_w") && tiling_json["tile_size_w"].is_number_integer()) {
|
||||||
vae_tiling_params.tile_size_x = tiling_json["tile_size_x"];
|
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()) {
|
if (tiling_json.contains("tile_size_h") && tiling_json["tile_size_h"].is_number_integer()) {
|
||||||
vae_tiling_params.tile_size_y = tiling_json["tile_size_y"];
|
vae_tiling_params.tile_size_h = tiling_json["tile_size_h"];
|
||||||
}
|
}
|
||||||
if (tiling_json.contains("target_overlap") && tiling_json["target_overlap"].is_number()) {
|
if (tiling_json.contains("target_overlap") && tiling_json["target_overlap"].is_number()) {
|
||||||
vae_tiling_params.target_overlap = tiling_json["target_overlap"];
|
vae_tiling_params.target_overlap = tiling_json["target_overlap"];
|
||||||
}
|
}
|
||||||
if (tiling_json.contains("rel_size_x") && tiling_json["rel_size_x"].is_number()) {
|
if (tiling_json.contains("rel_size_w") && tiling_json["rel_size_w"].is_number()) {
|
||||||
vae_tiling_params.rel_size_x = tiling_json["rel_size_x"];
|
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()) {
|
if (tiling_json.contains("rel_size_h") && tiling_json["rel_size_h"].is_number()) {
|
||||||
vae_tiling_params.rel_size_y = tiling_json["rel_size_y"];
|
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()) {
|
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>();
|
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: { "
|
||||||
<< vae_tiling_params.enabled << ", "
|
<< vae_tiling_params.enabled << ", "
|
||||||
<< vae_tiling_params.temporal_tiling << ", "
|
<< vae_tiling_params.temporal_tiling << ", "
|
||||||
<< vae_tiling_params.tile_size_x << ", "
|
<< vae_tiling_params.tile_size_w << ", "
|
||||||
<< vae_tiling_params.tile_size_y << ", "
|
<< vae_tiling_params.tile_size_h << ", "
|
||||||
<< vae_tiling_params.target_overlap << ", "
|
<< vae_tiling_params.target_overlap << ", "
|
||||||
<< vae_tiling_params.rel_size_x << ", "
|
<< vae_tiling_params.rel_size_w << ", "
|
||||||
<< vae_tiling_params.rel_size_y << ", "
|
<< vae_tiling_params.rel_size_h << ", "
|
||||||
<< "\"" << extra_tiling_args << "\" },\n"
|
<< "\"" << extra_tiling_args << "\" },\n"
|
||||||
<< "}";
|
<< "}";
|
||||||
return oss.str();
|
return oss.str();
|
||||||
@ -3140,11 +3140,11 @@ std::string build_sdcpp_image_metadata_json(const SDContextParams& ctx_params,
|
|||||||
root["vae_tiling"] = {
|
root["vae_tiling"] = {
|
||||||
{"enabled", gen_params.vae_tiling_params.enabled},
|
{"enabled", gen_params.vae_tiling_params.enabled},
|
||||||
{"temporal_tiling", gen_params.vae_tiling_params.temporal_tiling},
|
{"temporal_tiling", gen_params.vae_tiling_params.temporal_tiling},
|
||||||
{"tile_size_x", gen_params.vae_tiling_params.tile_size_x},
|
{"tile_size_w", gen_params.vae_tiling_params.tile_size_w},
|
||||||
{"tile_size_y", gen_params.vae_tiling_params.tile_size_y},
|
{"tile_size_h", gen_params.vae_tiling_params.tile_size_h},
|
||||||
{"target_overlap", gen_params.vae_tiling_params.target_overlap},
|
{"target_overlap", gen_params.vae_tiling_params.target_overlap},
|
||||||
{"rel_size_x", gen_params.vae_tiling_params.rel_size_x},
|
{"rel_size_w", gen_params.vae_tiling_params.rel_size_w},
|
||||||
{"rel_size_y", gen_params.vae_tiling_params.rel_size_y},
|
{"rel_size_h", gen_params.vae_tiling_params.rel_size_h},
|
||||||
{"extra_tiling_args", gen_params.extra_tiling_args},
|
{"extra_tiling_args", gen_params.extra_tiling_args},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -524,11 +524,11 @@ Shared default fields used by both `img_gen` and `vid_gen`:
|
|||||||
| `vae_tiling_params` | `object` |
|
| `vae_tiling_params` | `object` |
|
||||||
| `vae_tiling_params.enabled` | `boolean` |
|
| `vae_tiling_params.enabled` | `boolean` |
|
||||||
| `vae_tiling_params.temporal_tiling` | `boolean` |
|
| `vae_tiling_params.temporal_tiling` | `boolean` |
|
||||||
| `vae_tiling_params.tile_size_x` | `integer` |
|
| `vae_tiling_params.tile_size_w` | `integer` |
|
||||||
| `vae_tiling_params.tile_size_y` | `integer` |
|
| `vae_tiling_params.tile_size_h` | `integer` |
|
||||||
| `vae_tiling_params.target_overlap` | `number` |
|
| `vae_tiling_params.target_overlap` | `number` |
|
||||||
| `vae_tiling_params.rel_size_x` | `number` |
|
| `vae_tiling_params.rel_size_w` | `number` |
|
||||||
| `vae_tiling_params.rel_size_y` | `number` |
|
| `vae_tiling_params.rel_size_h` | `number` |
|
||||||
| `vae_tiling_params.extra_tiling_args` | `string` |
|
| `vae_tiling_params.extra_tiling_args` | `string` |
|
||||||
| `cache_mode` | `string` |
|
| `cache_mode` | `string` |
|
||||||
| `cache_option` | `string` |
|
| `cache_option` | `string` |
|
||||||
@ -537,6 +537,8 @@ Shared default fields used by both `img_gen` and `vid_gen`:
|
|||||||
| `output_format` | `string` |
|
| `output_format` | `string` |
|
||||||
| `output_compression` | `integer` |
|
| `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`).
|
`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.
|
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": {
|
"vae_tiling_params": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"temporal_tiling": false,
|
"temporal_tiling": false,
|
||||||
"tile_size_x": 0,
|
"tile_size_w": 0,
|
||||||
"tile_size_y": 0,
|
"tile_size_h": 0,
|
||||||
"target_overlap": 0.5,
|
"target_overlap": 0.5,
|
||||||
"rel_size_x": 0.0,
|
"rel_size_w": 0.0,
|
||||||
"rel_size_y": 0.0,
|
"rel_size_h": 0.0,
|
||||||
"extra_tiling_args": ""
|
"extra_tiling_args": ""
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -900,11 +902,11 @@ Other native fields:
|
|||||||
| `vae_tiling_params` | `object` |
|
| `vae_tiling_params` | `object` |
|
||||||
| `vae_tiling_params.enabled` | `boolean` |
|
| `vae_tiling_params.enabled` | `boolean` |
|
||||||
| `vae_tiling_params.temporal_tiling` | `boolean` |
|
| `vae_tiling_params.temporal_tiling` | `boolean` |
|
||||||
| `vae_tiling_params.tile_size_x` | `integer` |
|
| `vae_tiling_params.tile_size_w` | `integer` |
|
||||||
| `vae_tiling_params.tile_size_y` | `integer` |
|
| `vae_tiling_params.tile_size_h` | `integer` |
|
||||||
| `vae_tiling_params.target_overlap` | `number` |
|
| `vae_tiling_params.target_overlap` | `number` |
|
||||||
| `vae_tiling_params.rel_size_x` | `number` |
|
| `vae_tiling_params.rel_size_w` | `number` |
|
||||||
| `vae_tiling_params.rel_size_y` | `number` |
|
| `vae_tiling_params.rel_size_h` | `number` |
|
||||||
| `vae_tiling_params.extra_tiling_args` | `string` |
|
| `vae_tiling_params.extra_tiling_args` | `string` |
|
||||||
| `cache_mode` | `string` |
|
| `cache_mode` | `string` |
|
||||||
| `cache_option` | `string` |
|
| `cache_option` | `string` |
|
||||||
@ -1115,11 +1117,11 @@ Example:
|
|||||||
"vae_tiling_params": {
|
"vae_tiling_params": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"temporal_tiling": false,
|
"temporal_tiling": false,
|
||||||
"tile_size_x": 0,
|
"tile_size_w": 0,
|
||||||
"tile_size_y": 0,
|
"tile_size_h": 0,
|
||||||
"target_overlap": 0.5,
|
"target_overlap": 0.5,
|
||||||
"rel_size_x": 0.0,
|
"rel_size_w": 0.0,
|
||||||
"rel_size_y": 0.0,
|
"rel_size_h": 0.0,
|
||||||
"extra_tiling_args": ""
|
"extra_tiling_args": ""
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1240,11 +1242,11 @@ Other native fields:
|
|||||||
| `vae_tiling_params` | `object` |
|
| `vae_tiling_params` | `object` |
|
||||||
| `vae_tiling_params.enabled` | `boolean` |
|
| `vae_tiling_params.enabled` | `boolean` |
|
||||||
| `vae_tiling_params.temporal_tiling` | `boolean` |
|
| `vae_tiling_params.temporal_tiling` | `boolean` |
|
||||||
| `vae_tiling_params.tile_size_x` | `integer` |
|
| `vae_tiling_params.tile_size_w` | `integer` |
|
||||||
| `vae_tiling_params.tile_size_y` | `integer` |
|
| `vae_tiling_params.tile_size_h` | `integer` |
|
||||||
| `vae_tiling_params.target_overlap` | `number` |
|
| `vae_tiling_params.target_overlap` | `number` |
|
||||||
| `vae_tiling_params.rel_size_x` | `number` |
|
| `vae_tiling_params.rel_size_w` | `number` |
|
||||||
| `vae_tiling_params.rel_size_y` | `number` |
|
| `vae_tiling_params.rel_size_h` | `number` |
|
||||||
| `vae_tiling_params.extra_tiling_args` | `string` |
|
| `vae_tiling_params.extra_tiling_args` | `string` |
|
||||||
| `cache_mode` | `string` |
|
| `cache_mode` | `string` |
|
||||||
| `cache_option` | `string` |
|
| `cache_option` | `string` |
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
Subproject commit c4bce3d6b3f236614cca21014f076083b7270ba8
|
Subproject commit dd74a8e808aaa8b26124217424b23058935184de
|
||||||
@ -78,11 +78,11 @@ static json make_vae_tiling_json(const sd_tiling_params_t& params) {
|
|||||||
return {
|
return {
|
||||||
{"enabled", params.enabled},
|
{"enabled", params.enabled},
|
||||||
{"temporal_tiling", params.temporal_tiling},
|
{"temporal_tiling", params.temporal_tiling},
|
||||||
{"tile_size_x", params.tile_size_x},
|
{"tile_size_w", params.tile_size_w},
|
||||||
{"tile_size_y", params.tile_size_y},
|
{"tile_size_h", params.tile_size_h},
|
||||||
{"target_overlap", params.target_overlap},
|
{"target_overlap", params.target_overlap},
|
||||||
{"rel_size_x", params.rel_size_x},
|
{"rel_size_w", params.rel_size_w},
|
||||||
{"rel_size_y", params.rel_size_y},
|
{"rel_size_h", params.rel_size_h},
|
||||||
{"extra_tiling_args", params.extra_tiling_args ? params.extra_tiling_args : ""},
|
{"extra_tiling_args", params.extra_tiling_args ? params.extra_tiling_args : ""},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -173,11 +173,13 @@ enum lora_apply_mode_t {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
bool enabled;
|
bool enabled;
|
||||||
bool temporal_tiling;
|
bool temporal_tiling;
|
||||||
int tile_size_x;
|
// Spatial tile dimensions in image pixels for both encode and decode; 0 uses 256.
|
||||||
int tile_size_y;
|
int tile_size_w;
|
||||||
|
int tile_size_h;
|
||||||
float target_overlap;
|
float target_overlap;
|
||||||
float rel_size_x;
|
// Positive values override tile_size: <= 1 is a dimension fraction, > 1 a target tile count.
|
||||||
float rel_size_y;
|
float rel_size_w;
|
||||||
|
float rel_size_h;
|
||||||
const char* extra_tiling_args;
|
const char* extra_tiling_args;
|
||||||
} sd_tiling_params_t;
|
} sd_tiling_params_t;
|
||||||
|
|
||||||
|
|||||||
@ -478,7 +478,12 @@ namespace sd::backend_fit {
|
|||||||
return true;
|
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.
|
// Execution failures can leave the device unusable; tiling only helps with allocation failures.
|
||||||
if (status != GGML_STATUS_ALLOC_FAILED) {
|
if (status != GGML_STATUS_ALLOC_FAILED) {
|
||||||
return false;
|
return false;
|
||||||
@ -487,20 +492,32 @@ namespace sd::backend_fit {
|
|||||||
if (prefer_temporal_tiling && !tiling_params.temporal_tiling) {
|
if (prefer_temporal_tiling && !tiling_params.temporal_tiling) {
|
||||||
tiling_params.temporal_tiling = true;
|
tiling_params.temporal_tiling = true;
|
||||||
retry_mode = tiling_params.enabled ? "spatial+temporal" : "temporal";
|
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 {
|
} else {
|
||||||
|
if (latent_tile_size_w <= 0 || latent_tile_size_h <= 0 || scale_factor <= 0) {
|
||||||
return false;
|
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",
|
LOG_WARN("VAE decode ran out of memory; retrying with %s tiling",
|
||||||
retry_mode);
|
retry_mode);
|
||||||
|
|||||||
@ -17,7 +17,10 @@ namespace sd::backend_fit {
|
|||||||
|
|
||||||
bool prepare_vae_decode_retry_tiling(sd_tiling_params_t& tiling_params,
|
bool prepare_vae_decode_retry_tiling(sd_tiling_params_t& tiling_params,
|
||||||
bool prefer_temporal_tiling,
|
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
|
} // namespace sd::backend_fit
|
||||||
|
|
||||||
|
|||||||
@ -556,12 +556,18 @@ namespace MiniMaxH3VAE {
|
|||||||
tensor.shape()[3]});
|
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.enabled = true;
|
||||||
params.temporal_tiling = false;
|
params.temporal_tiling = false;
|
||||||
params.tile_size_x = 16;
|
|
||||||
params.tile_size_y = 16;
|
|
||||||
params.target_overlap = 0.25f;
|
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -605,7 +611,7 @@ namespace MiniMaxH3VAE {
|
|||||||
bool circular_x = false,
|
bool circular_x = false,
|
||||||
bool circular_y = false) override {
|
bool circular_y = false) override {
|
||||||
auto input = ensure_video_shape(x);
|
auto input = ensure_video_shape(x);
|
||||||
auto tiling = h3_tiling(tiling_params);
|
auto tiling = resolve_tiling_params(tiling_params);
|
||||||
if (input.shape()[2] == 1) {
|
if (input.shape()[2] == 1) {
|
||||||
auto encoded = VAE::encode(n_threads, input, tiling, circular_x, circular_y);
|
auto encoded = VAE::encode(n_threads, input, tiling, circular_x, circular_y);
|
||||||
if (!encoded.empty() && encoded.shape()[2] > 1) {
|
if (!encoded.empty() && encoded.shape()[2] > 1) {
|
||||||
@ -646,7 +652,7 @@ namespace MiniMaxH3VAE {
|
|||||||
bool circular_y = false,
|
bool circular_y = false,
|
||||||
bool silent = false) override {
|
bool silent = false) override {
|
||||||
auto input = ensure_video_shape(x);
|
auto input = ensure_video_shape(x);
|
||||||
auto tiling = h3_tiling(tiling_params);
|
auto tiling = resolve_tiling_params(tiling_params);
|
||||||
if (input.shape()[2] == 1) {
|
if (input.shape()[2] == 1) {
|
||||||
auto decoded = VAE::decode(n_threads,
|
auto decoded = VAE::decode(n_threads,
|
||||||
input,
|
input,
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
#ifndef __SD_MODEL_VAE_VAE_HPP__
|
#ifndef __SD_MODEL_VAE_VAE_HPP__
|
||||||
#define __SD_MODEL_VAE_VAE_HPP__
|
#define __SD_MODEL_VAE_VAE_HPP__
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
#include "core/tensor_ggml.hpp"
|
#include "core/tensor_ggml.hpp"
|
||||||
#include "model/common/block.hpp"
|
#include "model/common/block.hpp"
|
||||||
#include "model/vae/vae_tiling.hpp"
|
#include "model/vae/vae_tiling.hpp"
|
||||||
@ -117,8 +120,8 @@ protected:
|
|||||||
int output_width,
|
int output_width,
|
||||||
int output_height,
|
int output_height,
|
||||||
int scale,
|
int scale,
|
||||||
int p_tile_size_x,
|
int p_tile_size_w,
|
||||||
int p_tile_size_y,
|
int p_tile_size_h,
|
||||||
float tile_overlap_factor,
|
float tile_overlap_factor,
|
||||||
bool circular_x,
|
bool circular_x,
|
||||||
bool circular_y,
|
bool circular_y,
|
||||||
@ -138,17 +141,28 @@ protected:
|
|||||||
}
|
}
|
||||||
return output_tile;
|
return output_tile;
|
||||||
};
|
};
|
||||||
return ::process_tiles_2d(input,
|
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_width,
|
||||||
output_height,
|
output_height,
|
||||||
scale,
|
scale,
|
||||||
p_tile_size_x,
|
p_tile_size_w,
|
||||||
p_tile_size_y,
|
p_tile_size_h,
|
||||||
tile_overlap_factor,
|
tile_overlap_factor,
|
||||||
circular_x,
|
circular_x && p_tile_size_w < latent_width,
|
||||||
circular_y,
|
circular_y && p_tile_size_h < latent_height,
|
||||||
on_processing,
|
on_processing,
|
||||||
silent);
|
silent);
|
||||||
|
set_circular_axes(original_circular_x, original_circular_y);
|
||||||
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -178,33 +192,48 @@ public:
|
|||||||
return supports_temporal_tiling(VAETemporalDirection::DECODE);
|
return supports_temporal_tiling(VAETemporalDirection::DECODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void get_tile_sizes(int& tile_size_x,
|
virtual sd_tiling_params_t resolve_tiling_params(sd_tiling_params_t params) const {
|
||||||
int& tile_size_y,
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get_tile_sizes(int& tile_size_w,
|
||||||
|
int& tile_size_h,
|
||||||
float& tile_overlap,
|
float& tile_overlap,
|
||||||
const sd_tiling_params_t& params,
|
const sd_tiling_params_t& params,
|
||||||
int64_t latent_x,
|
int64_t latent_w,
|
||||||
int64_t latent_y,
|
int64_t latent_h) {
|
||||||
float encoding_factor = 1.0f) {
|
const auto tiling = resolve_tiling_params(params);
|
||||||
tile_overlap = std::max(std::min(params.target_overlap, 0.5f), 0.0f);
|
if (latent_w <= 0 || latent_h <= 0 ||
|
||||||
auto get_tile_size = [&](int requested_size, float factor, int64_t latent_size) {
|
latent_w > std::numeric_limits<int>::max() || latent_h > std::numeric_limits<int>::max() ||
|
||||||
const int default_tile_size = 32;
|
!std::isfinite(tiling.target_overlap)) {
|
||||||
const int min_tile_dimension = 4;
|
LOG_ERROR("invalid VAE tiling dimensions or overlap");
|
||||||
int tile_size = default_tile_size;
|
return false;
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
tile_size = static_cast<int>(tile_size * encoding_factor);
|
const int scale_factor = get_scale_factor();
|
||||||
return std::max(std::min(tile_size, static_cast<int>(latent_size)), min_tile_dimension);
|
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;
|
||||||
|
}
|
||||||
|
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);
|
return get_tile_size(tiling.tile_size_w, tiling.rel_size_w, latent_w, tile_size_w) &&
|
||||||
tile_size_y = get_tile_size(params.tile_size_y, params.rel_size_y, latent_y);
|
get_tile_size(tiling.tile_size_h, tiling.rel_size_h, latent_h, tile_size_h);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual sd::Tensor<float> encode(int n_threads,
|
virtual sd::Tensor<float> encode(int n_threads,
|
||||||
@ -213,6 +242,7 @@ public:
|
|||||||
bool circular_x = false,
|
bool circular_x = false,
|
||||||
bool circular_y = false) {
|
bool circular_y = false) {
|
||||||
int64_t t0 = ggml_time_ms();
|
int64_t t0 = ggml_time_ms();
|
||||||
|
tiling_params = resolve_tiling_params(tiling_params);
|
||||||
sd::Tensor<float> input = x;
|
sd::Tensor<float> input = x;
|
||||||
sd::Tensor<float> output;
|
sd::Tensor<float> output;
|
||||||
if (scale_input) {
|
if (scale_input) {
|
||||||
@ -224,21 +254,19 @@ public:
|
|||||||
int64_t W = input.shape()[0] / scale_factor;
|
int64_t W = input.shape()[0] / scale_factor;
|
||||||
int64_t H = input.shape()[1] / scale_factor;
|
int64_t H = input.shape()[1] / scale_factor;
|
||||||
float tile_overlap;
|
float tile_overlap;
|
||||||
int tile_size_x, tile_size_y;
|
int tile_size_w, tile_size_h;
|
||||||
// Image VAE encode is more sensitive to tile boundary context than decode.
|
if (!get_tile_sizes(tile_size_w, tile_size_h, tile_overlap, tiling_params, W, H)) {
|
||||||
// Keep the smaller legacy factor for video VAEs, but default image encode
|
return {};
|
||||||
// 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
|
LOG_VERBOSE("VAE encode tile size: %dx%d pixels (%dx%d latent)",
|
||||||
: 2.0f;
|
tile_size_w * scale_factor, tile_size_h * scale_factor, tile_size_w, tile_size_h);
|
||||||
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);
|
|
||||||
output = tiled_compute(input,
|
output = tiled_compute(input,
|
||||||
n_threads,
|
n_threads,
|
||||||
static_cast<int>(W),
|
static_cast<int>(W),
|
||||||
static_cast<int>(H),
|
static_cast<int>(H),
|
||||||
scale_factor,
|
scale_factor,
|
||||||
tile_size_x,
|
tile_size_w,
|
||||||
tile_size_y,
|
tile_size_h,
|
||||||
tile_overlap,
|
tile_overlap,
|
||||||
circular_x,
|
circular_x,
|
||||||
circular_y,
|
circular_y,
|
||||||
@ -271,6 +299,7 @@ public:
|
|||||||
bool circular_y = false,
|
bool circular_y = false,
|
||||||
bool silent = false) {
|
bool silent = false) {
|
||||||
int64_t t0 = ggml_time_ms();
|
int64_t t0 = ggml_time_ms();
|
||||||
|
tiling_params = resolve_tiling_params(tiling_params);
|
||||||
sd::Tensor<float> input = x;
|
sd::Tensor<float> input = x;
|
||||||
sd::Tensor<float> output;
|
sd::Tensor<float> output;
|
||||||
|
|
||||||
@ -279,10 +308,13 @@ public:
|
|||||||
int64_t W = input.shape()[0] * scale_factor;
|
int64_t W = input.shape()[0] * scale_factor;
|
||||||
int64_t H = input.shape()[1] * scale_factor;
|
int64_t H = input.shape()[1] * scale_factor;
|
||||||
float tile_overlap;
|
float tile_overlap;
|
||||||
int tile_size_x, tile_size_y;
|
int tile_size_w, tile_size_h;
|
||||||
get_tile_sizes(tile_size_x, tile_size_y, tile_overlap, tiling_params, input.shape()[0], input.shape()[1]);
|
if (!get_tile_sizes(tile_size_w, tile_size_h, tile_overlap, tiling_params, input.shape()[0], input.shape()[1])) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
if (!silent) {
|
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(
|
output = tiled_compute(
|
||||||
input,
|
input,
|
||||||
@ -290,8 +322,8 @@ public:
|
|||||||
static_cast<int>(W),
|
static_cast<int>(W),
|
||||||
static_cast<int>(H),
|
static_cast<int>(H),
|
||||||
scale_factor,
|
scale_factor,
|
||||||
tile_size_x,
|
tile_size_w,
|
||||||
tile_size_y,
|
tile_size_h,
|
||||||
tile_overlap,
|
tile_overlap,
|
||||||
circular_x,
|
circular_x,
|
||||||
circular_y,
|
circular_y,
|
||||||
|
|||||||
@ -2883,15 +2883,27 @@ sd::Tensor<float> StableDiffusionGGML::decode_first_stage(const sd::Tensor<float
|
|||||||
return sd::ops::clamp((x + 1.f) * 0.5f, 0.0f, 1.0f);
|
return sd::ops::clamp((x + 1.f) * 0.5f, 0.0f, 1.0f);
|
||||||
}
|
}
|
||||||
auto latents = first_stage_model->diffusion_to_vae_latents(x);
|
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);
|
auto tiling_params = first_stage_model->resolve_tiling_params(vae_tiling_params);
|
||||||
const bool prefer_temporal_tiling = decode_video && first_stage_model->can_temporal_tile_decode();
|
const bool prefer_temporal_tiling = decode_video && latents.dim() == 5 && latents.shape()[2] > 1 &&
|
||||||
while (decoded.empty() &&
|
first_stage_model->can_temporal_tile_decode();
|
||||||
sd::backend_fit::prepare_vae_decode_retry_tiling(vae_tiling_params, prefer_temporal_tiling,
|
for (;;) {
|
||||||
first_stage_model->last_compute_status())) {
|
int tile_size_w = static_cast<int>(latents.shape()[0]);
|
||||||
decoded = first_stage_model->decode(n_threads, latents, vae_tiling_params, decode_video, circular_x, circular_y);
|
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) {
|
sd::Tensor<float> StableDiffusionGGML::normalize_ltx_video_latents(const sd::Tensor<float>& x) {
|
||||||
auto ltx_vae = std::dynamic_pointer_cast<LTXVideoVAE>(first_stage_model);
|
auto ltx_vae = std::dynamic_pointer_cast<LTXVideoVAE>(first_stage_model);
|
||||||
|
|||||||
@ -35,19 +35,21 @@ namespace sd::pipeline {
|
|||||||
return original_axes;
|
return original_axes;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tile_size_x, tile_size_y;
|
int tile_size_w, tile_size_h;
|
||||||
float overlap;
|
float overlap;
|
||||||
int latent_size_x = request.width / request.vae_scale_factor;
|
int latent_size_w = request.width / request.vae_scale_factor;
|
||||||
int latent_size_y = request.height / request.vae_scale_factor;
|
int latent_size_h = request.height / request.vae_scale_factor;
|
||||||
sd->first_stage_model->get_tile_sizes(tile_size_x,
|
if (!sd->first_stage_model->get_tile_sizes(tile_size_w,
|
||||||
tile_size_y,
|
tile_size_h,
|
||||||
overlap,
|
overlap,
|
||||||
sd_img_gen_params->vae_tiling_params,
|
sd_img_gen_params->vae_tiling_params,
|
||||||
latent_size_x,
|
latent_size_w,
|
||||||
latent_size_y);
|
latent_size_h)) {
|
||||||
|
return original_axes;
|
||||||
|
}
|
||||||
|
|
||||||
sd->circular_x = sd->circular_x && (tile_size_x >= latent_size_x);
|
sd->circular_x = sd->circular_x && (tile_size_w >= latent_size_w);
|
||||||
sd->circular_y = sd->circular_y && (tile_size_y >= latent_size_y);
|
sd->circular_y = sd->circular_y && (tile_size_h >= latent_size_h);
|
||||||
|
|
||||||
if (sd->first_stage_model) {
|
if (sd->first_stage_model) {
|
||||||
sd->first_stage_model->set_circular_axes(sd->circular_x, sd->circular_y);
|
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->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_x = original_axes.circular_x && (tile_size_w < latent_size_w);
|
||||||
sd->circular_y = original_axes.circular_y && (tile_size_y < latent_size_y);
|
sd->circular_y = original_axes.circular_y && (tile_size_h < latent_size_h);
|
||||||
|
|
||||||
return original_axes;
|
return original_axes;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -142,8 +142,8 @@ sd::Tensor<float> process_tiles_2d(const sd::Tensor<float>& input,
|
|||||||
int output_width,
|
int output_width,
|
||||||
int output_height,
|
int output_height,
|
||||||
int scale,
|
int scale,
|
||||||
int p_tile_size_x,
|
int p_tile_size_w,
|
||||||
int p_tile_size_y,
|
int p_tile_size_h,
|
||||||
float tile_overlap_factor,
|
float tile_overlap_factor,
|
||||||
bool circular_x,
|
bool circular_x,
|
||||||
bool circular_y,
|
bool circular_y,
|
||||||
@ -168,28 +168,28 @@ sd::Tensor<float> process_tiles_2d(const sd::Tensor<float>& input,
|
|||||||
|
|
||||||
int num_tiles_x;
|
int num_tiles_x;
|
||||||
float tile_overlap_factor_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;
|
int num_tiles_y;
|
||||||
float tile_overlap_factor_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 tile_overlap_x = static_cast<int32_t>(p_tile_size_w * tile_overlap_factor_x);
|
||||||
int non_tile_overlap_x = p_tile_size_x - tile_overlap_x;
|
int non_tile_overlap_x = p_tile_size_w - tile_overlap_x;
|
||||||
int tile_overlap_y = static_cast<int32_t>(p_tile_size_y * tile_overlap_factor_y);
|
int tile_overlap_y = static_cast<int32_t>(p_tile_size_h * tile_overlap_factor_y);
|
||||||
int non_tile_overlap_y = p_tile_size_y - tile_overlap_y;
|
int non_tile_overlap_y = p_tile_size_h - tile_overlap_y;
|
||||||
int tile_size_x = p_tile_size_x < small_width ? p_tile_size_x : small_width;
|
int tile_size_w = p_tile_size_w < small_width ? p_tile_size_w : small_width;
|
||||||
int tile_size_y = p_tile_size_y < small_height ? p_tile_size_y : small_height;
|
int tile_size_h = p_tile_size_h < small_height ? p_tile_size_h : small_height;
|
||||||
int input_tile_size_x = tile_size_x;
|
int input_tile_size_w = tile_size_w;
|
||||||
int input_tile_size_y = tile_size_y;
|
int input_tile_size_h = tile_size_h;
|
||||||
int output_tile_size_x = tile_size_x;
|
int output_tile_size_w = tile_size_w;
|
||||||
int output_tile_size_y = tile_size_y;
|
int output_tile_size_h = tile_size_h;
|
||||||
if (decode) {
|
if (decode) {
|
||||||
output_tile_size_x *= scale;
|
output_tile_size_w *= scale;
|
||||||
output_tile_size_y *= scale;
|
output_tile_size_h *= scale;
|
||||||
} else {
|
} else {
|
||||||
input_tile_size_x *= scale;
|
input_tile_size_w *= scale;
|
||||||
input_tile_size_y *= scale;
|
input_tile_size_h *= scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
int num_tiles = num_tiles_x * num_tiles_y;
|
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) {
|
for (int y = 0; y < small_height && !last_y; y += non_tile_overlap_y) {
|
||||||
int dy = 0;
|
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;
|
int original_y = y;
|
||||||
y = small_height - tile_size_y;
|
y = small_height - tile_size_h;
|
||||||
dy = original_y - y;
|
dy = original_y - y;
|
||||||
if (decode) {
|
if (decode) {
|
||||||
dy *= scale;
|
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) {
|
for (int x = 0; x < small_width && !last_x; x += non_tile_overlap_x) {
|
||||||
int dx = 0;
|
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;
|
int original_x = x;
|
||||||
x = small_width - tile_size_x;
|
x = small_width - tile_size_w;
|
||||||
dx = original_x - x;
|
dx = original_x - x;
|
||||||
if (decode) {
|
if (decode) {
|
||||||
dx *= scale;
|
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;
|
int overlap_y_out = decode ? tile_overlap_y * scale : tile_overlap_y;
|
||||||
|
|
||||||
int64_t t1 = ggml_time_ms();
|
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);
|
auto output_tile = on_processing(input_tile);
|
||||||
if (output_tile.empty()) {
|
if (output_tile.empty()) {
|
||||||
return {};
|
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()) {
|
if (output.empty()) {
|
||||||
std::vector<int64_t> output_shape = output_tile.shape();
|
std::vector<int64_t> output_shape = output_tile.shape();
|
||||||
output_shape[0] = output_width;
|
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_width,
|
||||||
int output_height,
|
int output_height,
|
||||||
int scale,
|
int scale,
|
||||||
int p_tile_size_x,
|
int p_tile_size_w,
|
||||||
int p_tile_size_y,
|
int p_tile_size_h,
|
||||||
float tile_overlap_factor,
|
float tile_overlap_factor,
|
||||||
bool circular_x,
|
bool circular_x,
|
||||||
bool circular_y,
|
bool circular_y,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user