Compare commits

...
52 changed files with 893 additions and 180 deletions
+3
View File
@@ -15,6 +15,7 @@ API and command-line option may change frequently.***
## 🔥Important News
* **2026/10/16** 🚀 stable-diffusion.cpp adds **Day-0 support for FLUX.3 Image Klein / Klein Base**
* **2026/09/20** 🚀 stable-diffusion.cpp adds **Day-0 support for Qwen-Image-2.1**
* **2026/08/20** 🚀 stable-diffusion.cpp now supports **LTX-2.5**
* **2026/08/04** 🚀 stable-diffusion.cpp adds **Day-1 support for MiniMax-H3**
@@ -44,6 +45,7 @@ API and command-line option may change frequently.***
- [SD3/SD3.5](./docs/sd3.md)
- [FLUX.1-dev/FLUX.1-schnell](./docs/flux.md)
- [FLUX.2-dev/FLUX.2-klein](./docs/flux2.md)
- [FLUX.3 Image Klein / Klein Base](./docs/flux3.md)
- [Lens](./docs/lens.md)
- [Chroma](./docs/chroma.md)
- [Chroma1-Radiance](./docs/chroma_radiance.md)
@@ -190,6 +192,7 @@ These projects use `stable-diffusion.cpp` as a backend for their image generatio
- [LocalAI](https://github.com/mudler/LocalAI)
- [Neural-Pixel](https://github.com/Luiz-Alcantara/Neural-Pixel)
- [KoboldCpp](https://github.com/LostRuins/koboldcpp)
- [Kokon](https://github.com/GabrieleLozupone/kokon)
## Contributors
Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 611 KiB

+44
View File
@@ -215,6 +215,50 @@ If a VAE decode fails, decoding retries with spatial tiling even when `--auto-fi
is off; supported video decoders try temporal tiling first and can then add
spatial tiling. Spatial retries use half-size tiles along each latent dimension.
## Direct convolution (`--conv-direct on|off|auto`)
`--conv-direct` defaults to `auto` and accepts module assignments for
`diffusion`, `vae` (including preview VAE), and `controlnet`, using the same
aliases and precedence rules as `--flash-attn`:
```shell
sd-cli -m model.safetensors -p "a cat" --conv-direct auto,vae=off
sd-cli -m model.safetensors -p "a cat" --conv-direct off,diffusion=on
```
- `on` requests direct 2D and 3D convolution.
- `off` uses the existing im2col-based convolution paths.
- `auto` first checks support for a representative F16-weight/F32-input direct
convolution graph. Vulkan then benchmarks direct convolution against im2col
using two small shapes per dimension, with warmup, output validation, and five
interleaved measurements per shape. Direct convolution must improve the sum of
median times by at least 5%; a failed probe defaults to `off`.
Other backends retain their implicit GEMM checks without benchmarking:
CUDA 2D requires a compiled Turing-or-newer MMA architecture; HIP 2D requires
the supported CDNA MFMA or RDNA WMMA architectures described below for flash
attention. MUSA 2D has a SIMT implementation. CUDA, HIP, and MUSA 3D also have
a SIMT implementation, so they do not require MMA. Other backends default to
`off`. Results and decision logs are cached per compute backend and dimension
in the context, as with flash attention. Each dimension is selected independently,
and all compute devices in a module must qualify. Parameter-storage devices do
not affect this choice.
These are backend-level defaults. GGML still chooses the actual kernel based on
weight type, shape, and hardware; enabling direct convolution does not guarantee
that every operation uses implicit GEMM. The CUDA-family implicit GEMM paths
require F16 weights and can use ordinary direct kernels for other cases.
This option does not change the separately created image upscaler context.
Repeating `--conv-direct` replaces the previous assignment string. Existing
`--diffusion-conv-direct` (diffusion and ControlNet) and `--vae-conv-direct`
flags are deprecated and emit a warning, but remain supported when
`--conv-direct` is omitted. Use `--conv-direct auto,diffusion=on,controlnet=on`
and `--conv-direct auto,vae=on`, respectively. An explicit
`--conv-direct` takes precedence over both legacy flags regardless of order.
Library callers can set the appended `sd_ctx_params_t::conv_direct_mode` field
to the same string; null or empty selects `auto` while honoring legacy true
flags. The context copies the string.
## Flash attention (`--flash-attn on|off|auto`)
`--flash-attn` defaults to `auto`. Selection happens while creating the model
+7 -3
View File
@@ -3,8 +3,10 @@
`src/core/probe/backend_probe.h` provides the shared measurement machinery. Operator
policy stays separate: `src/core/probe/flash_attn.cpp`, for example, defines attention
shapes and its CPU/CUDA/HIP/Vulkan shortcuts, then calls `sd_run_backend_probe` for unknown
configurations. A future convolution probe can use the same runner without
changing the attention implementation.
configurations. `src/core/probe/conv_direct.cpp` uses metadata-only probe graphs
to check direct 2D/3D convolution support, then enables known implicit GEMM
paths on non-Vulkan backends. Vulkan compares direct convolution with im2col
using the shared measurement machinery.
For each representative shape, create a `BackendProbeCase`:
@@ -40,7 +42,9 @@ cache by both operation and backend); an attention result must not select a
convolution implementation. If shapes or settings vary within one context,
include them in the corresponding cache key as well.
No convolution policy or CLI option is added by this infrastructure.
Convolution results are cached separately for 2D and 3D, once per compute backend
in the context, just like attention results. Each probe logs its backend, decision,
and reason; mixed-device modules log when their compute devices disagree.
Module assignment parsing remains in `src/core/ggml_extend_backend.cpp`, next
to backend assignment parsing. The probe directory does not parse CLI/API options.
+1
View File
@@ -14,6 +14,7 @@ Depending on the architecture, different models handle reference images differen
| [**Qwen Image Edit**](./qwen_image_edit.md) | `qwen` |
| **Qwen Image LAYERED** | `qwen_layered` |
| [**Flux.2 [Dev] / Flux.2 [Klein]**](./flux2.md) | `flux2` |
| [**FLUX.3 Image Klein / Klein Base**](./flux3.md) | `flux2` |
| [**Boogu Image Edit**](./boogu_image.md) | `z_image_omni` |
| **Krea2 (Community Edit LoRAs)** | `krea2_ostris_edit` |
| [**Mage-Flow-Edit**](./mage_flow.md#image-editing) | `mage_flow` |
+48
View File
@@ -0,0 +1,48 @@
# How to Use
FLUX.3 Image Klein uses a FLUX.3 diffusion transformer, the FLUX.2 VAE, and Qwen3-VL 4B as the LLM text encoder.
## Download weights
- Download FLUX.3 Image Klein
- safetensors: https://huggingface.co/black-forest-labs/flux-3-image-klein/tree/main
- Download FLUX.3 Image Klein Base
- safetensors: https://huggingface.co/black-forest-labs/flux-3-image-klein-base/tree/main
- Download vae
- safetensors: https://huggingface.co/black-forest-labs/FLUX.2-dev/tree/main
- The `ae.safetensors` included with FLUX.3 can also be used.
- Download Qwen3-VL 4B
- safetensors: https://huggingface.co/Comfy-Org/Krea-2/tree/main/text_encoders
- gguf: https://huggingface.co/Qwen/Qwen3-VL-4B-Instruct-GGUF/tree/main
Use the root `model.safetensors` for the diffusion model.
## Examples
### FLUX.3 Image Klein
The distilled model uses 16 steps and CFG 1.0.
```
.\bin\Release\sd-cli.exe --diffusion-model ..\models\diffusion_models\flux-3-image-klein.safetensors --llm ..\models\text_encoders\Qwen3-VL-4B-Instruct-Q4_K_M.gguf --vae ..\models\vae\flux2_ae.safetensors -p "a lovely cat holding a sign says 'flux3.cpp'" --cfg-scale 1.0 --steps 16 --sampling-method euler -v --offload-to-cpu
```
<img src="../assets/flux3/flux3-image-klein.png" />
```
.\bin\Release\sd-cli.exe --diffusion-model ..\models\diffusion_models\flux-3-image-klein.safetensors --vae ..\models\vae\flux2_ae.safetensors --llm ..\models\text_encoders\Qwen3-VL-4B-Instruct-Q4_K_M.gguf -r ..\assets\flux\flux1-dev-q8_0.png -p "change 'flux.cpp' to 'flux3-image-klein.cpp'" --cfg-scale 1.0 --sampling-method euler -v --offload-to-cpu --steps 16
```
<img src="../assets/flux3/flux3-image-klein-edit.png" />
Repeat `-r` to use multiple reference images, up to 10. See [Image Editing](./edit.md) for reference-image preprocessing options.
### FLUX.3 Image Klein Base
```
.\bin\Release\sd-cli.exe --diffusion-model ..\models\diffusion_models\flux-3-image-klein-base.safetensors --llm ..\models\text_encoders\Qwen3-VL-4B-Instruct-Q4_K_M.gguf --vae ..\models\vae\flux2_ae.safetensors -p "a lovely cat holding a sign says 'flux3.cpp'" --cfg-scale 3.5 --steps 20 --sampling-method euler -v --offload-to-cpu
```
<img src="../assets/flux3/flux3-image-klein-base.png" />
The default discrete scheduler uses the upstream time shift of 6.93 for both models. Use `--flow-shift` to override it.
+20 -2
View File
@@ -5,8 +5,13 @@ eg.:
- flux 768x768 ~600mb
- SD2 768x768 ~1400mb
For most backends, it slows things down, but for cuda it generally speeds it up too.
At the moment, it is only supported for some models and some backends (like cpu, cuda/rocm, metal).
`--flash-attn on|off|auto` controls flash attention. Its effect on speed depends
on GPU architecture, backend, driver, model, and tensor shapes. Automatic
selection uses backend rules and, for some paths, small probes; it does not
guarantee the fastest end-to-end generation. Compare `on` and `off` with the
same model and generation settings over repeated runs, separating startup/probe
time from steady-state generation time. See [backend selection](backend.md)
for automatic selection rules and per-module overrides.
Watch for:
```
@@ -21,6 +26,19 @@ 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.
## Tune direct convolution.
`--conv-direct on|off|auto` selects direct convolution or the im2col-based
path. Performance depends on GPU architecture, backend, driver, model, and
tensor shapes, so enabling direct convolution can either improve or reduce speed.
Automatic selection uses small benchmarks on Vulkan and implicit GEMM checks
on other backends. It does not guarantee the fastest end-to-end generation.
Compare `on` and `off` with the same model and generation settings over
repeated runs, keeping flash attention unchanged and separating startup/probe
time from steady-state generation time. See [backend selection](backend.md)
for automatic selection rules and per-module overrides.
## Run conditional and unconditional CFG in one batched UNet forward.
For UNet models, the conditional and unconditional guidance branches are
+5
View File
@@ -17,6 +17,11 @@ for probing behavior.
Use module assignments such as `--flash-attn auto,te=off,vae=on` to override
individual modules.
`--conv-direct on|off|auto` also defaults to `auto`, enabling direct convolution
on backends with implicit GEMM implementations. Use `--conv-direct auto,vae=off`
for module overrides; supported modules are `diffusion`, `vae`, and `controlnet`.
See [Direct convolution](../../docs/backend.md#direct-convolution---conv-direct-onoffauto).
For direct image repair or automatic post-generation YOLOv8 detection followed by cropped inpainting, see
[ADetailer](../../docs/adetailer.md).
+2 -1
View File
@@ -4,6 +4,7 @@
#include <cctype>
#include <cstdint>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <iomanip>
#include <iostream>
@@ -276,7 +277,7 @@ namespace {
}
bool read_file(const std::string& path, std::vector<uint8_t>& data, std::string& error) {
std::ifstream fin(path, std::ios::binary);
std::ifstream fin(std::filesystem::u8path(path), std::ios::binary);
if (!fin) {
error = "failed to open file: " + path;
return false;
+34 -33
View File
@@ -188,7 +188,7 @@ struct SDCliParams {
if (++index >= argc) {
return -1;
}
imatrix_in.push_back(argv[index]);
imatrix_in.push_back(argv_to_utf8(index, argv));
return 1;
};
@@ -323,13 +323,14 @@ void sd_log_cb(enum sd_log_level_t level, const char* log, void* data) {
bool load_images_from_dir(const std::string dir,
std::vector<SDImageOwner>& images,
int max_image_num = 0) {
if (!fs::exists(dir) || !fs::is_directory(dir)) {
const fs::path dir_path = fs::u8path(dir);
if (!fs::exists(dir_path) || !fs::is_directory(dir_path)) {
LOG_ERROR("'%s' is not a valid directory\n", dir.c_str());
return false;
}
std::vector<fs::directory_entry> entries;
for (const auto& entry : fs::directory_iterator(dir)) {
for (const auto& entry : fs::directory_iterator(dir_path)) {
if (entry.is_regular_file()) {
entries.push_back(entry);
}
@@ -337,12 +338,12 @@ bool load_images_from_dir(const std::string dir,
std::sort(entries.begin(), entries.end(),
[](const fs::directory_entry& a, const fs::directory_entry& b) {
return a.path().filename().string() < b.path().filename().string();
return a.path().filename().u8string() < b.path().filename().u8string();
});
for (const auto& entry : entries) {
std::string path = entry.path().string();
std::string ext = entry.path().extension().string();
std::string path = entry.path().u8string();
std::string ext = entry.path().extension().u8string();
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
if (ext == ".jpg" || ext == ".jpeg" || ext == ".png" || ext == ".bmp" || ext == ".webp") {
@@ -397,19 +398,19 @@ void step_callback(int step, int frame_count, sd_image_t* image, bool is_noisy,
// is_noisy is set to true if the preview corresponds to noisy latents, false if it's denoised latents
// unused in this app, it will either be always noisy or always denoised here
if (frame_count == 1) {
fs::path path = cli_params->preview_path;
if (encoded_image_format_from_path(path.string()) == EncodedImageFormat::UNKNOWN)
fs::path path = fs::u8path(cli_params->preview_path);
if (encoded_image_format_from_path(path.u8string()) == EncodedImageFormat::UNKNOWN)
path += ".png";
if (std::regex_search(path.string(), format_specifier_regex))
path = fs::path(format_frame_idx(path.string(), continuous_preview_counter++));
if (!write_image_to_file(path.string(),
if (std::regex_search(path.u8string(), format_specifier_regex))
path = fs::u8path(format_frame_idx(path.u8string(), continuous_preview_counter++));
if (!write_image_to_file(path.u8string(),
image->data,
image->width,
image->height,
image->channel,
"",
cli_params->compression_quality)) {
LOG_ERROR("save preview image to '%s' failed", path.string().c_str());
LOG_ERROR("save preview image to '%s' failed", path.u8string().c_str());
}
} else {
int fps = cli_params->preview_method == PREVIEW_PROJ ? cli_params->preview_fps / 4 : cli_params->preview_fps;
@@ -420,12 +421,12 @@ void step_callback(int step, int frame_count, sd_image_t* image, bool is_noisy,
}
static fs::path get_video_audio_sidecar_path(const SDCliParams& cli_params) {
fs::path out_path = cli_params.output_path;
fs::path out_path = fs::u8path(cli_params.output_path);
fs::path base_path = out_path;
fs::path ext = out_path.has_extension() ? out_path.extension() : fs::path{};
std::string ext_lower = ext.string();
std::string ext_lower = ext.u8string();
std::transform(ext_lower.begin(), ext_lower.end(), ext_lower.begin(), ::tolower);
const EncodedImageFormat output_format = encoded_image_format_from_path(out_path.string());
const EncodedImageFormat output_format = encoded_image_format_from_path(out_path.u8string());
if (!ext.empty()) {
if (output_format == EncodedImageFormat::JPEG ||
output_format == EncodedImageFormat::PNG ||
@@ -450,14 +451,14 @@ bool save_results(const SDCliParams& cli_params,
}
namespace fs = std::filesystem;
fs::path out_path = cli_params.output_path;
fs::path out_path = fs::u8path(cli_params.output_path);
if (!out_path.parent_path().empty()) {
std::error_code ec;
fs::create_directories(out_path.parent_path(), ec);
if (ec) {
LOG_ERROR("failed to create directory '%s': %s",
out_path.parent_path().string().c_str(), ec.message().c_str());
out_path.parent_path().u8string().c_str(), ec.message().c_str());
return false;
}
}
@@ -465,9 +466,9 @@ bool save_results(const SDCliParams& cli_params,
fs::path base_path = out_path;
fs::path ext = out_path.has_extension() ? out_path.extension() : fs::path{};
std::string ext_lower = ext.string();
std::string ext_lower = ext.u8string();
std::transform(ext_lower.begin(), ext_lower.end(), ext_lower.begin(), ::tolower);
const EncodedImageFormat output_format = encoded_image_format_from_path(out_path.string());
const EncodedImageFormat output_format = encoded_image_format_from_path(out_path.u8string());
if (!ext.empty()) {
if (output_format == EncodedImageFormat::JPEG ||
output_format == EncodedImageFormat::PNG ||
@@ -493,8 +494,8 @@ bool save_results(const SDCliParams& cli_params,
std::string params = gen_params.embed_image_metadata
? get_image_params(ctx_params, gen_params, metadata_seed, cli_params.mode)
: "";
const bool ok = write_image_to_file(path.string(), img.data, img.width, img.height, img.channel, params, cli_params.compression_quality);
LOG_INFO("save result image %d to '%s' (%s)", idx, path.string().c_str(), ok ? "success" : "failure");
const bool ok = write_image_to_file(path.u8string(), img.data, img.width, img.height, img.channel, params, cli_params.compression_quality);
LOG_INFO("save result image %d to '%s' (%s)", idx, path.u8string().c_str(), ok ? "success" : "failure");
return ok;
};
@@ -502,14 +503,14 @@ bool save_results(const SDCliParams& cli_params,
if (generated_audio == nullptr) {
return;
}
if (write_wav_to_file(wav_path.string(),
if (write_wav_to_file(wav_path.u8string(),
generated_audio->data,
generated_audio->sample_count,
generated_audio->channels,
generated_audio->sample_rate)) {
LOG_INFO("save result audio to '%s'", wav_path.string().c_str());
LOG_INFO("save result audio to '%s'", wav_path.u8string().c_str());
} else {
LOG_WARN("failed to save result audio to '%s'", wav_path.string().c_str());
LOG_WARN("failed to save result audio to '%s'", wav_path.u8string().c_str());
}
};
@@ -522,7 +523,7 @@ bool save_results(const SDCliParams& cli_params,
pattern += ext;
for (int i = 0; i < num_results; ++i) {
fs::path img_path = format_frame_idx(pattern.string(), output_begin_idx + i);
fs::path img_path = fs::u8path(format_frame_idx(pattern.u8string(), output_begin_idx + i));
if (write_image(img_path, i)) {
sucessful_reults++;
}
@@ -540,11 +541,11 @@ bool save_results(const SDCliParams& cli_params,
fs::path video_path = base_path;
video_path += ext;
std::string final_ext_lower = ext.string();
std::string final_ext_lower = ext.u8string();
std::transform(final_ext_lower.begin(), final_ext_lower.end(), final_ext_lower.begin(), ::tolower);
const bool mux_audio = generated_audio != nullptr && (final_ext_lower == ".avi" || final_ext_lower == ".webm");
if (create_video_from_sd_images(video_path.string().c_str(), results, num_results, gen_params.fps, cli_params.compression_quality, mux_audio ? generated_audio : nullptr, params) == 0) {
LOG_INFO("save result video to '%s'", video_path.string().c_str());
if (create_video_from_sd_images(video_path.u8string().c_str(), results, num_results, gen_params.fps, cli_params.compression_quality, mux_audio ? generated_audio : nullptr, params) == 0) {
LOG_INFO("save result video to '%s'", video_path.u8string().c_str());
if (generated_audio != nullptr && !mux_audio) {
fs::path wav_path = video_path;
wav_path.replace_extension(".wav");
@@ -552,7 +553,7 @@ bool save_results(const SDCliParams& cli_params,
}
return true;
} else {
LOG_ERROR("Failed to save result video to '%s'", video_path.string().c_str());
LOG_ERROR("Failed to save result video to '%s'", video_path.u8string().c_str());
return false;
}
}
@@ -696,12 +697,12 @@ int main(int argc, const char* argv[]) {
LOG_VERBOSE("version: %s", version_string().c_str());
LOG_VERBOSE("%s", sd_get_system_info());
LOG_VERBOSE("%s", cli_params.to_string().c_str());
LOG_VERBOSE("%s", ctx_params.to_string().c_str());
LOG_VERBOSE("%s", gen_params.to_string().c_str());
LOG_DEBUG("%s", cli_params.to_string().c_str());
LOG_DEBUG("%s", ctx_params.to_string().c_str());
LOG_DEBUG("%s", gen_params.to_string().c_str());
if (!cli_params.imatrix_out.empty()) {
if (fs::exists(cli_params.imatrix_out) &&
if (fs::exists(fs::u8path(cli_params.imatrix_out)) &&
std::find(cli_params.imatrix_in.begin(), cli_params.imatrix_in.end(), cli_params.imatrix_out) == cli_params.imatrix_in.end()) {
LOG_WARN("imatrix file '%s' already exists and will be overwritten", cli_params.imatrix_out.c_str());
}
+67 -37
View File
@@ -75,7 +75,7 @@ static std::string utf16_to_utf8(const std::wstring& wstr) {
return utf8;
}
static std::string argv_to_utf8(int index, const char** argv) {
std::string argv_to_utf8(int index, const char** argv) {
(void)argv;
int argc;
wchar_t** argv_w = CommandLineToArgvW(GetCommandLineW(), &argc);
@@ -91,7 +91,7 @@ static std::string argv_to_utf8(int index, const char** argv) {
}
#else // Linux / macOS
static std::string argv_to_utf8(int index, const char** argv) {
std::string argv_to_utf8(int index, const char** argv) {
return std::string(argv[index]);
}
@@ -636,14 +636,6 @@ ArgOptions SDContextParams::get_options() {
"--sage-attn",
"use native CUDA SageAttention in the diffusion model, with flash/default attention fallback",
true, &sage_attn},
{"",
"--diffusion-conv-direct",
"use ggml_conv2d_direct in the diffusion model",
true, &diffusion_conv_direct},
{"",
"--vae-conv-direct",
"use direct 2D and 3D convolutions in the vae model",
true, &vae_conv_direct},
};
auto on_type_arg = [&](int argc, const char** argv, int index) {
@@ -729,6 +721,33 @@ ArgOptions SDContextParams::get_options() {
[this](int argc, const char** argv, int index) {
return parse_scale_override(argc, argv, index, attn_scale);
}},
{"",
"--diffusion-conv-direct",
"deprecated; use --conv-direct auto,diffusion=on,controlnet=on",
[this](int, const char**, int) {
LOG_WARN("--diffusion-conv-direct is deprecated; use --conv-direct auto,diffusion=on,controlnet=on instead.");
diffusion_conv_direct = true;
return 0;
}},
{"",
"--vae-conv-direct",
"deprecated; use --conv-direct auto,vae=on",
[this](int, const char**, int) {
LOG_WARN("--vae-conv-direct is deprecated; use --conv-direct auto,vae=on instead.");
vae_conv_direct = true;
return 0;
}},
{"",
"--conv-direct",
"on|off|auto or diffusion/vae/controlnet assignments (default: auto), e.g. auto,vae=off. Overrides legacy convolution flags",
[this](int argc, const char** argv, int index) {
if (++index >= argc || argv[index][0] == '\0') {
LOG_ERROR("--conv-direct requires on, off, auto, or module assignments");
return -1;
}
conv_direct_mode = argv[index];
return 1;
}},
{"",
"--flash-attn",
"on|off|auto or module assignments (default: auto), e.g. auto,te=off,vae=on. Last repeated option wins",
@@ -820,16 +839,17 @@ ArgOptions SDContextParams::get_options() {
void SDContextParams::build_embedding_map() {
static const std::vector<std::string> valid_ext = {".gguf", ".safetensors", ".pt", ".ckpt"};
if (!fs::exists(embedding_dir) || !fs::is_directory(embedding_dir)) {
const fs::path embedding_dir_path = fs::u8path(embedding_dir);
if (!fs::exists(embedding_dir_path) || !fs::is_directory(embedding_dir_path)) {
return;
}
for (auto& p : fs::directory_iterator(embedding_dir)) {
for (auto& p : fs::directory_iterator(embedding_dir_path)) {
if (!p.is_regular_file())
continue;
auto path = p.path();
std::string ext = path.extension().string();
std::string ext = path.extension().u8string();
bool valid = false;
for (auto& e : valid_ext) {
@@ -841,8 +861,8 @@ void SDContextParams::build_embedding_map() {
if (!valid)
continue;
std::string key = path.stem().string();
std::string value = path.string();
std::string key = path.stem().u8string();
std::string value = path.u8string();
embedding_map[key] = value;
}
@@ -984,6 +1004,7 @@ std::string SDContextParams::to_string() const {
<< " clip_on_cpu: " << (clip_on_cpu ? "true" : "false") << ",\n"
<< " vae_on_cpu: " << (vae_on_cpu ? "true" : "false") << ",\n"
<< " flash_attn_mode: " << flash_attn_mode << ",\n"
<< " conv_direct_mode: " << (conv_direct_mode.empty() ? "auto (legacy flags honored)" : conv_direct_mode) << ",\n"
<< " sage_attn: " << (sage_attn ? "true" : "false") << ",\n"
<< " linear_scale: " << linear_scale << ",\n"
<< " attn_scale: " << attn_scale << ",\n"
@@ -1042,6 +1063,7 @@ sd_ctx_params_t SDContextParams::to_sd_ctx_params_t(bool taesd_preview) {
sd_ctx_params.lora_apply_mode = lora_apply_mode;
sd_ctx_params.enable_mmap = enable_mmap;
sd_ctx_params.flash_attn_mode = flash_attn_mode.c_str();
sd_ctx_params.conv_direct_mode = conv_direct_mode.c_str();
sd_ctx_params.sage_attn = sage_attn;
sd_ctx_params.linear_scale = linear_scale;
sd_ctx_params.attn_scale = attn_scale;
@@ -1551,7 +1573,7 @@ ArgOptions SDGenerationParams::get_options() {
if (++index >= argc) {
return -1;
}
ref_image_paths.push_back(argv[index]);
ref_image_paths.push_back(argv_to_utf8(index, argv));
return 1;
};
@@ -1559,7 +1581,7 @@ ArgOptions SDGenerationParams::get_options() {
if (++index >= argc) {
return -1;
}
ref_video_paths.push_back(argv[index]);
ref_video_paths.push_back(argv_to_utf8(index, argv));
return 1;
};
@@ -1567,7 +1589,7 @@ ArgOptions SDGenerationParams::get_options() {
if (++index >= argc) {
return -1;
}
ref_video_audio_paths.push_back(argv[index]);
ref_video_audio_paths.push_back(argv_to_utf8(index, argv));
return 1;
};
@@ -1575,7 +1597,7 @@ ArgOptions SDGenerationParams::get_options() {
if (++index >= argc) {
return -1;
}
ref_audio_paths.push_back(argv[index]);
ref_audio_paths.push_back(argv_to_utf8(index, argv));
return 1;
};
@@ -1583,7 +1605,7 @@ ArgOptions SDGenerationParams::get_options() {
if (++index >= argc) {
return -1;
}
ref_audio_paths.push_back(argv[index]);
ref_audio_paths.push_back(argv_to_utf8(index, argv));
return 1;
};
@@ -1682,15 +1704,15 @@ ArgOptions SDGenerationParams::get_options() {
if (++index >= argc) {
return -1;
}
const char* arg = argv[index];
std::ifstream f(arg, std::ios::binary);
const std::string arg = argv_to_utf8(index, argv);
std::ifstream f(fs::u8path(arg), std::ios::binary);
try {
prompt = std::string(std::istreambuf_iterator<char>{f}, {});
} catch (const std::ios_base::failure&) {
f.setstate(std::ios_base::failbit);
}
if (f.fail()) {
LOG_ERROR("error: failed to read prompt file '%s'\n", arg);
LOG_ERROR("error: failed to read prompt file '%s'\n", arg.c_str());
return -1;
}
return 1;
@@ -1700,15 +1722,15 @@ ArgOptions SDGenerationParams::get_options() {
if (++index >= argc) {
return -1;
}
const char* arg = argv[index];
std::ifstream f(arg, std::ios::binary);
const std::string arg = argv_to_utf8(index, argv);
std::ifstream f(fs::u8path(arg), std::ios::binary);
try {
negative_prompt = std::string(std::istreambuf_iterator<char>{f}, {});
} catch (const std::ios_base::failure&) {
f.setstate(std::ios_base::failbit);
}
if (f.fail()) {
LOG_ERROR("error: failed to read negative prompt file '%s'\n", arg);
LOG_ERROR("error: failed to read negative prompt file '%s'\n", arg.c_str());
return -1;
}
return 1;
@@ -2029,22 +2051,22 @@ static bool resolve_model_file_from_dir(const std::string& model_name,
if (model_name.empty() ||
model_name.find('/') != std::string::npos ||
model_name.find('\\') != std::string::npos ||
fs::path(model_name).has_root_path() ||
fs::u8path(model_name).has_root_path() ||
ends_with_valid_ext()) {
LOG_ERROR("%s must be a model name without path or extension: %s", label, model_name.c_str());
return false;
}
fs::path model_dir_path = model_dir;
fs::path model_dir_path = fs::u8path(model_dir);
for (const auto& ext : valid_ext) {
fs::path try_path = model_dir_path / (model_name + ext);
fs::path try_path = model_dir_path / fs::u8path(model_name + ext);
if (fs::exists(try_path) && fs::is_regular_file(try_path)) {
resolved_path = try_path.lexically_normal().string();
resolved_path = try_path.lexically_normal().u8string();
return true;
}
}
LOG_ERROR("can not find %s %s in %s", label, model_name.c_str(), model_dir_path.lexically_normal().string().c_str());
LOG_ERROR("can not find %s %s in %s", label, model_name.c_str(), model_dir_path.lexically_normal().u8string().c_str());
return false;
}
@@ -2338,10 +2360,18 @@ bool SDGenerationParams::extract_and_remove_lora(const std::string& lora_model_d
}
fs::path final_path;
if (is_absolute_path(raw_path)) {
final_path = raw_path;
} else {
final_path = fs::path(lora_model_dir) / raw_path;
try {
if (is_absolute_path(raw_path)) {
final_path = fs::u8path(raw_path);
} else {
final_path = fs::u8path(lora_model_dir) / fs::u8path(raw_path);
}
} catch (const std::exception&) { // not UTF-8, e.g. a prompt file in another encoding
LOG_WARN("invalid lora path %s", raw_path.c_str());
tmp = m.suffix().str();
prompt = std::regex_replace(prompt, re, "", std::regex_constants::format_first_only);
missing = true;
continue;
}
if (!fs::exists(final_path)) {
bool found = false;
@@ -2355,7 +2385,7 @@ bool SDGenerationParams::extract_and_remove_lora(const std::string& lora_model_d
}
}
if (!found) {
LOG_WARN("can not found lora %s", final_path.lexically_normal().string().c_str());
LOG_WARN("can not found lora %s", final_path.lexically_normal().u8string().c_str());
tmp = m.suffix().str();
prompt = std::regex_replace(prompt, re, "", std::regex_constants::format_first_only);
missing = true;
@@ -2363,7 +2393,7 @@ bool SDGenerationParams::extract_and_remove_lora(const std::string& lora_model_d
}
}
const std::string key = final_path.lexically_normal().string();
const std::string key = final_path.lexically_normal().u8string();
if (is_high_noise)
high_noise_lora_map[key] += mul;
+8 -3
View File
@@ -12,6 +12,10 @@
#include "resource_owners.hpp"
#include "stable-diffusion.h"
// The command-line argument at `index` as UTF-8 (on Windows, read again from
// the UTF-16 command line: argv itself is in the ANSI code page).
std::string argv_to_utf8(int index, const char** argv);
#define SAFE_STR(s) ((s) ? (s) : "")
#define BOOL_STR(b) ((b) ? "true" : "false")
@@ -171,9 +175,10 @@ struct SDContextParams {
bool clip_on_cpu = false;
bool vae_on_cpu = false;
std::string flash_attn_mode = "auto";
bool sage_attn = false;
bool diffusion_conv_direct = false;
bool vae_conv_direct = false;
std::string conv_direct_mode; // Empty selects auto while honoring legacy convolution flags.
bool sage_attn = false;
bool diffusion_conv_direct = false;
bool vae_conv_direct = false;
prediction_t prediction = PREDICTION_COUNT;
lora_apply_mode_t lora_apply_mode = LORA_APPLY_AUTO;
+7 -5
View File
@@ -15,10 +15,12 @@
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_STATIC
#define STBI_WINDOWS_UTF8
#include "stb_image.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#define STB_IMAGE_WRITE_STATIC
#define STBIW_WINDOWS_UTF8
#include "stb_image_write.h"
#define STB_IMAGE_RESIZE_IMPLEMENTATION
@@ -147,7 +149,7 @@ private:
#endif
bool read_binary_file_bytes(const char* path, std::vector<uint8_t>& data) {
std::ifstream fin(fs::path(path), std::ios::binary);
std::ifstream fin(fs::u8path(path), std::ios::binary);
if (!fin) {
return false;
}
@@ -170,7 +172,7 @@ bool read_binary_file_bytes(const char* path, std::vector<uint8_t>& data) {
}
bool write_binary_file_bytes(const std::string& path, const std::vector<uint8_t>& data) {
std::ofstream fout(fs::path(path), std::ios::binary);
std::ofstream fout(fs::u8path(path), std::ios::binary);
if (!fout) {
return false;
}
@@ -714,7 +716,7 @@ static std::pair<uint64_t, uint64_t> audio_sample_range_for_video_frame(const sd
}
EncodedImageFormat encoded_image_format_from_path(const std::string& path) {
std::string ext = fs::path(path).extension().string();
std::string ext = fs::u8path(path).extension().u8string();
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
if (ext == ".jpg" || ext == ".jpeg" || ext == ".jpe") {
@@ -1413,7 +1415,7 @@ bool write_wav_to_file(const std::string& path,
return false;
}
std::ofstream file(path, std::ios::binary);
std::ofstream file(fs::u8path(path), std::ios::binary);
if (!file.is_open()) {
return false;
}
@@ -1475,7 +1477,7 @@ bool load_wav_from_file(const std::string& path,
sample_rate = 0;
channels = 0;
std::ifstream file(path, std::ios::binary);
std::ifstream file(fs::u8path(path), std::ios::binary);
uint8_t riff_header[12];
if (!file.read(reinterpret_cast<char*>(riff_header), sizeof(riff_header)) ||
std::memcmp(riff_header, "RIFF", 4) != 0 ||
+2 -2
View File
@@ -85,8 +85,8 @@ int main(int argc, const char** argv) {
LOG_VERBOSE("version: %s", version_string().c_str());
LOG_VERBOSE("%s", sd_get_system_info());
LOG_VERBOSE("%s", svr_params.to_string().c_str());
LOG_VERBOSE("%s", ctx_params.to_string().c_str());
LOG_VERBOSE("%s", default_gen_params.to_string().c_str());
LOG_DEBUG("%s", ctx_params.to_string().c_str());
LOG_DEBUG("%s", default_gen_params.to_string().c_str());
sd_ctx_params_t sd_ctx_params = ctx_params.to_sd_ctx_params_t(false);
SDCtxPtr sd_ctx(new_sd_ctx(&sd_ctx_params));
+2 -1
View File
@@ -1,5 +1,6 @@
#include "routes.h"
#include <filesystem>
#include <fstream>
#include <iterator>
@@ -7,7 +8,7 @@ void register_index_endpoints(httplib::Server& svr, const SDSvrParams& svr_param
const std::string serve_html_path = svr_params.serve_html_path;
svr.Get("/", [serve_html_path, index_html](const httplib::Request&, httplib::Response& res) {
if (!serve_html_path.empty()) {
std::ifstream file(serve_html_path);
std::ifstream file(std::filesystem::u8path(serve_html_path));
if (file) {
std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
res.set_content(content, "text/html");
+6 -6
View File
@@ -28,10 +28,10 @@ static std::string extract_and_remove_sd_cpp_extra_args(std::string& text) {
static fs::path resolve_display_model_path(const ServerRuntime& runtime) {
const auto& ctx = *runtime.ctx_params;
if (!ctx.model_path.empty()) {
return fs::path(ctx.model_path);
return fs::u8path(ctx.model_path);
}
if (!ctx.diffusion_model_path.empty()) {
return fs::path(ctx.diffusion_model_path);
return fs::u8path(ctx.diffusion_model_path);
}
return {};
}
@@ -583,9 +583,9 @@ void register_sdapi_endpoints(httplib::Server& svr, ServerRuntime& rt) {
svr.Get("/sdapi/v1/sd-models", [runtime](const httplib::Request&, httplib::Response& res) {
fs::path model_path = resolve_display_model_path(*runtime);
json entry;
entry["title"] = model_path.stem();
entry["model_name"] = model_path.stem();
entry["filename"] = model_path.filename();
entry["title"] = model_path.stem().u8string();
entry["model_name"] = model_path.stem().u8string();
entry["filename"] = model_path.filename().u8string();
entry["hash"] = "8888888888";
entry["sha256"] = "8888888888888888888888888888888888888888888888888888888888888888";
entry["config"] = nullptr;
@@ -598,7 +598,7 @@ void register_sdapi_endpoints(httplib::Server& svr, ServerRuntime& rt) {
fs::path model_path = resolve_display_model_path(*runtime);
json r;
r["samples_format"] = "png";
r["sd_model_checkpoint"] = model_path.stem();
r["sd_model_checkpoint"] = model_path.stem().u8string();
res.set_content(r.dump(), "application/json");
});
+2 -2
View File
@@ -90,10 +90,10 @@ static json make_vae_tiling_json(const sd_tiling_params_t& params) {
static fs::path resolve_display_model_path(const ServerRuntime& runtime) {
const auto& ctx = *runtime.ctx_params;
if (!ctx.model_path.empty()) {
return fs::path(ctx.model_path);
return fs::u8path(ctx.model_path);
}
if (!ctx.diffusion_model_path.empty()) {
return fs::path(ctx.diffusion_model_path);
return fs::u8path(ctx.diffusion_model_path);
}
return {};
}
+8 -8
View File
@@ -22,7 +22,7 @@ static std::string lower_ascii(std::string value) {
}
static bool is_supported_model_ext(const fs::path& p) {
auto ext = lower_ascii(p.extension().string());
auto ext = lower_ascii(p.extension().u8string());
return ext == ".gguf" || ext == ".pt" || ext == ".pth" || ext == ".safetensors";
}
@@ -226,7 +226,7 @@ bool SDSvrParams::validate() {
return false;
}
if (!serve_html_path.empty() && !fs::exists(serve_html_path)) {
if (!serve_html_path.empty() && !fs::exists(fs::u8path(serve_html_path))) {
LOG_ERROR("error: serve_html_path file does not exist: %s", serve_html_path.c_str());
return false;
}
@@ -260,7 +260,7 @@ void refresh_lora_cache(ServerRuntime& rt) {
return;
}
fs::path lora_dir = rt.ctx_params->lora_model_dir;
fs::path lora_dir = fs::u8path(rt.ctx_params->lora_model_dir);
std::error_code ec;
if (fs::exists(lora_dir, ec) && !ec && fs::is_directory(lora_dir, ec) && !ec) {
try {
@@ -293,13 +293,13 @@ void refresh_lora_cache(ServerRuntime& rt) {
it.increment(ec);
}
} catch (const std::exception& e) {
LOG_WARN("error while scanning lora directory '%s': %s", lora_dir.string().c_str(), e.what());
LOG_WARN("error while scanning lora directory '%s': %s", lora_dir.u8string().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());
LOG_WARN("error while scanning lora directory '%s': %s", lora_dir.u8string().c_str(), ec.message().c_str());
return;
}
@@ -328,7 +328,7 @@ void refresh_upscaler_cache(ServerRuntime& rt) {
previous_cache = *rt.upscaler_cache;
}
fs::path upscaler_dir = rt.ctx_params->hires_upscalers_dir;
fs::path upscaler_dir = fs::u8path(rt.ctx_params->hires_upscalers_dir);
std::error_code ec;
if (fs::exists(upscaler_dir, ec) && !ec && fs::is_directory(upscaler_dir, ec) && !ec) {
try {
@@ -384,13 +384,13 @@ void refresh_upscaler_cache(ServerRuntime& rt) {
it.increment(ec);
}
} catch (const std::exception& e) {
LOG_WARN("error while scanning upscalers directory '%s': %s", upscaler_dir.string().c_str(), e.what());
LOG_WARN("error while scanning upscalers directory '%s': %s", upscaler_dir.u8string().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());
LOG_WARN("error while scanning upscalers directory '%s': %s", upscaler_dir.u8string().c_str(), ec.message().c_str());
return;
}
+1
View File
@@ -252,6 +252,7 @@ typedef struct {
bool sage_attn;
int conditioning_cache_size; // Maximum cached conditioning entries per context; 0 disables caching (default: 4)
const char* flash_attn_mode; // on/off/auto or module assignments (e.g. "auto,te=off"); NULL/empty honors legacy flags.
const char* conv_direct_mode; // on/off/auto or diffusion/vae/controlnet assignments; NULL/empty honors legacy flags.
} sd_ctx_params_t;
typedef struct {
+24 -1
View File
@@ -2006,6 +2006,7 @@ struct LLMEmbedder : public Conditioner {
} else if (sd_version_is_pid(version)) {
arch = LLM::LLMArch::GEMMA2_2B;
} else if (version == VERSION_QWEN_IMAGE_2_1 ||
sd_version_is_flux3(version) ||
sd_version_is_lingbot_video(version) ||
sd_version_is_ideogram4(version) ||
sd_version_is_boogu_image(version) ||
@@ -2201,6 +2202,9 @@ struct LLMEmbedder : public Conditioner {
weights.insert(weights.end(), suffix_tokens.size(), 1.f);
}
if (sd_version_is_flux3(version)) {
min_length = std::min(max_length, ((tokens.size() + 79) / 80) * 80);
}
std::vector<float> mask;
tokenizer->pad_tokens(tokens, &weights, &mask, min_length, max_length);
@@ -2254,6 +2258,15 @@ struct LLMEmbedder : public Conditioner {
}
}
sd::Tensor<int32_t> position_ids;
if (sd_version_is_flux3(version)) {
position_ids = sd::Tensor<int32_t>::zeros({static_cast<int64_t>(tokens.size() * 4)});
for (size_t axis = 0; axis < 4; ++axis) {
for (size_t i = 0; i < tokens.size(); ++i) {
position_ids[axis * tokens.size() + i] = mask[i] == 0.f ? 1 : static_cast<int32_t>(i);
}
}
}
auto hidden_states = llm->compute(n_threads,
input_ids,
attention_mask,
@@ -2262,7 +2275,8 @@ struct LLMEmbedder : public Conditioner {
false,
false,
deepstack_image_embeds,
image_grids);
image_grids,
position_ids);
if (hidden_states.empty()) {
LOG_ERROR("LLM prompt encoding failed");
return {};
@@ -3025,6 +3039,15 @@ struct LLMEmbedder : public Conditioner {
prompt += "<|im_end|>\n<|im_start|>assistant\n";
}
} else if (sd_version_is_flux3(version)) {
prompt_template_encode_start_idx = 0;
max_length = 4096;
out_layers = {4, 8, 12, 16, 20, 24, 28, 32};
prompt = "<|im_start|>user\n";
prompt_attn_range.first = static_cast<int>(prompt.size());
prompt += conditioner_params.text;
prompt_attn_range.second = static_cast<int>(prompt.size());
prompt += "<|im_end|>\n<|im_start|>assistant\n";
} else if (version == VERSION_FLUX2_KLEIN) {
prompt_template_encode_start_idx = 0;
min_length = 512;
+3 -2
View File
@@ -3,6 +3,7 @@
#include <cstdint>
#include <cstring>
#include <exception>
#include <filesystem>
#include <fstream>
#include <map>
#include <memory>
@@ -108,7 +109,7 @@ static bool preallocate_output_file(const std::string& output_path, uint64_t fil
return true;
}
std::fstream file(output_path, std::ios::binary | std::ios::in | std::ios::out);
std::fstream file(std::filesystem::u8path(output_path), std::ios::binary | std::ios::in | std::ios::out);
if (!file.is_open()) {
if (error != nullptr) {
*error = "failed to open output file '" + output_path + "' for preallocation";
@@ -251,7 +252,7 @@ static bool stream_tensor_data(ModelLoader& model_loader,
for (int worker = 0; worker < n_threads; worker++) {
workers.emplace_back([&]() {
std::fstream output_file(output_path, std::ios::binary | std::ios::in | std::ios::out);
std::fstream output_file(std::filesystem::u8path(output_path), std::ios::binary | std::ios::in | std::ios::out);
if (!output_file.is_open()) {
fail("failed to open output file '" + output_path + "' for tensor writing");
return;
+49 -17
View File
@@ -457,23 +457,23 @@ ggml_backend_buffer_t sd_backend_dev_buffer_from_host_ptr(ggml_backend_dev_t dev
return buffer;
}
bool sd_backend_supports_cuda_mma(ggml_backend_t backend) {
static int cuda_compiled_arch(ggml_backend_t backend) {
#ifdef SD_USE_CUDA
if (!sd_backend_is(backend, "CUDA")) {
return false;
return 0;
}
auto dev = ggml_backend_get_device(backend);
if (dev == nullptr) {
return false;
return 0;
}
static std::mutex mutex;
static std::unordered_map<ggml_backend_dev_t, bool> cache;
static std::unordered_map<ggml_backend_dev_t, int> cache;
std::lock_guard<std::mutex> lock(mutex);
auto it = cache.find(dev);
if (it != cache.end()) {
return it->second;
}
const bool supported = [&]() {
const int arch = [&]() {
ggml_backend_dev_props props{};
ggml_backend_dev_get_props(dev, &props);
CUdevice device;
@@ -482,13 +482,13 @@ bool sd_backend_supports_cuda_mma(ggml_backend_t backend) {
cuDeviceGetByPCIBusId(&device, props.device_id) != CUDA_SUCCESS ||
cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, device) != CUDA_SUCCESS ||
cuDeviceGetAttribute(&minor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, device) != CUDA_SUCCESS) {
return false;
return 0;
}
auto reg = ggml_backend_dev_backend_reg(dev);
auto get_features = reinterpret_cast<ggml_backend_get_features_t>(
ggml_backend_reg_get_proc_address(reg, "ggml_backend_get_features"));
if (get_features == nullptr) {
return false;
return 0;
}
// Match ggml's highest compiled architecture for this device, including PTX fallback.
const int cc = 100 * major + 10 * minor;
@@ -511,16 +511,21 @@ bool sd_backend_supports_cuda_mma(ggml_backend_t backend) {
arch = end;
}
}
return compiled_arch == 700 || compiled_arch >= 750;
return compiled_arch;
}();
cache.emplace(dev, supported);
return supported;
cache.emplace(dev, arch);
return arch;
#else
(void)backend;
return false;
return 0;
#endif
}
bool sd_backend_supports_cuda_mma(ggml_backend_t backend) {
const int arch = cuda_compiled_arch(backend);
return arch == 700 || arch >= 750;
}
bool sd_backend_supports_hip_mma(ggml_backend_t backend) {
#ifdef SD_USE_HIP
if (!sd_backend_is(backend, "ROCm")) {
@@ -564,6 +569,20 @@ bool sd_backend_supports_hip_mma(ggml_backend_t backend) {
#endif
}
bool sd_backend_supports_conv_implicit_gemm(ggml_backend_t backend, bool conv3d) {
if (backend == nullptr) {
return false;
}
if (sd_backend_is(backend, "Vulkan") || sd_backend_is(backend, "MUSA")) {
return true;
}
// CUDA/HIP Conv3d also implements SIMT implicit GEMM; Conv2d requires MMA.
if (conv3d) {
return sd_backend_is(backend, "CUDA") || sd_backend_is(backend, "ROCm");
}
return cuda_compiled_arch(backend) >= 750 || sd_backend_supports_hip_mma(backend);
}
ggml_backend_t sd_backend_cpu_init() {
ggml_backend_load_all_once();
return ggml_backend_init_by_type(GGML_BACKEND_DEVICE_TYPE_CPU, nullptr);
@@ -782,7 +801,8 @@ bool sd_parse_backend_assignment(const std::string& spec, SDBackendAssignment* a
return true;
}
bool sd_parse_flash_attention_assignment(const std::string& spec, SDBackendAssignment* assignment, std::string* error) {
static bool parse_operation_assignment(const std::string& spec, SDBackendAssignment* assignment, std::string* error, bool convolution) {
const std::string operation = convolution ? "direct convolution" : "flash attention";
if (assignment == nullptr) {
return false;
}
@@ -800,7 +820,7 @@ bool sd_parse_flash_attention_assignment(const std::string& spec, SDBackendAssig
return true;
}
if (error != nullptr) {
*error = "invalid flash attention mode '" + mode + "': expected on, off, or auto";
*error = "invalid " + operation + " mode '" + mode + "': expected on, off, or auto";
}
return false;
};
@@ -813,15 +833,19 @@ bool sd_parse_flash_attention_assignment(const std::string& spec, SDBackendAssig
for (const auto& setting : entry.module_names) {
switch (setting.first) {
case SDBackendModule::DIFFUSION:
case SDBackendModule::TE:
case SDBackendModule::CLIP_VISION:
case SDBackendModule::VAE:
case SDBackendModule::CONTROL_NET:
case SDBackendModule::AUDIO_ENCODER:
break;
case SDBackendModule::TE:
case SDBackendModule::CLIP_VISION:
case SDBackendModule::AUDIO_ENCODER:
if (!convolution) {
break;
}
[[fallthrough]];
default:
if (error != nullptr) {
*error = std::string("flash attention is not configurable for module '") + sd_backend_module_name(setting.first) + "'";
*error = operation + " is not configurable for module '" + sd_backend_module_name(setting.first) + "'";
}
return false;
}
@@ -835,6 +859,14 @@ bool sd_parse_flash_attention_assignment(const std::string& spec, SDBackendAssig
return true;
}
bool sd_parse_flash_attention_assignment(const std::string& spec, SDBackendAssignment* assignment, std::string* error) {
return parse_operation_assignment(spec, assignment, error, false);
}
bool sd_parse_conv_direct_assignment(const std::string& spec, SDBackendAssignment* assignment, std::string* error) {
return parse_operation_assignment(spec, assignment, error, true);
}
bool SDBackendAssignment::empty() const {
return default_name.empty() && module_names.empty();
}
+2
View File
@@ -89,6 +89,7 @@ bool sd_backend_is(ggml_backend_t backend, const std::string& name);
bool sd_backend_is_cpu(ggml_backend_t backend);
bool sd_backend_supports_cuda_mma(ggml_backend_t backend);
bool sd_backend_supports_hip_mma(ggml_backend_t backend);
bool sd_backend_supports_conv_implicit_gemm(ggml_backend_t backend, bool conv3d);
ggml_backend_buffer_t sd_backend_dev_buffer_from_host_ptr(ggml_backend_dev_t device,
void* ptr,
size_t size,
@@ -102,6 +103,7 @@ ggml_status sd_backend_graph_compute_with_eval_callback(ggml_backend_t backend,
std::string sd_backend_resolve_name(const std::string& name);
bool sd_parse_backend_assignment(const std::string& spec, SDBackendAssignment* assignment, std::string* error);
bool sd_parse_flash_attention_assignment(const std::string& spec, SDBackendAssignment* assignment, std::string* error);
bool sd_parse_conv_direct_assignment(const std::string& spec, SDBackendAssignment* assignment, std::string* error);
const char* sd_backend_module_name(SDBackendModule module);
void ggml_ext_im_set_f32_1d(const struct ggml_tensor* tensor, int i, float value);
bool add_rpc_devices(const std::string& servers);
+2 -1
View File
@@ -3,6 +3,7 @@
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <fstream>
#include "core/ggml_extend_backend.h"
@@ -100,7 +101,7 @@ void ggml_ext_tensor_diff(
}
ggml_tensor* load_tensor_from_file(ggml_context* ctx, const std::string& file_path) {
std::ifstream file(file_path, std::ios::binary);
std::ifstream file(std::filesystem::u8path(file_path), std::ios::binary);
if (!file.is_open()) {
LOG_ERROR("failed to open '%s'", file_path.c_str());
return nullptr;
+96
View File
@@ -0,0 +1,96 @@
#include "core/probe/conv_direct.h"
#include <cmath>
#include <exception>
#include <utility>
#include <vector>
#include "core/ggml_extend_backend.h"
#include "core/probe/backend_probe.h"
#include "core/util.h"
static void build_conv_direct_probe(BackendProbeGraph& graph, bool conv3d, bool direct, int size = 32, int channels = 64) {
if (conv3d) {
auto input = ggml_new_tensor_4d(graph.ctx, GGML_TYPE_F32, size / 2, size / 2, 4, channels);
auto weight = ggml_new_tensor_4d(graph.ctx, GGML_TYPE_F16, 3, 3, 3, channels * channels);
graph.inputs = {input, weight};
graph.output = direct ? ggml_conv_3d_direct(graph.ctx, weight, input, 1, 1, 1, 1, 1, 1, 1, 1, 1, channels, 1, channels)
: ggml_conv_3d(graph.ctx, weight, input, channels, 1, 1, 1, 1, 1, 1, 1, 1, 1);
} else {
auto input = ggml_new_tensor_4d(graph.ctx, GGML_TYPE_F32, size, size, channels, 1);
auto weight = ggml_new_tensor_4d(graph.ctx, GGML_TYPE_F16, 3, 3, channels, channels);
graph.inputs = {input, weight};
graph.output = direct ? ggml_conv_2d_direct(graph.ctx, weight, input, 1, 1, 1, 1, 1, 1)
: ggml_conv_2d(graph.ctx, weight, input, 1, 1, 1, 1, 1, 1);
}
}
static bool initialize_conv_probe(BackendProbeGraph& graph, bool conv3d) {
uint32_t state = 42;
for (auto* tensor : graph.inputs) {
const size_t count = static_cast<size_t>(ggml_nelements(tensor));
const float scale = tensor->type == GGML_TYPE_F16 ? 1.f / std::sqrt(float(graph.inputs[0]->ne[conv3d ? 3 : 2] * (conv3d ? 27 : 9))) : 1.f;
std::vector<ggml_fp16_t> values(count);
for (auto& value : values) {
state = state * 1664525u + 1013904223u;
value = ggml_fp32_to_fp16((static_cast<float>(state >> 8) / 16777216.f - 0.5f) * scale);
}
if (tensor->type == GGML_TYPE_F16) {
ggml_backend_tensor_set(tensor, values.data(), 0, count * sizeof(ggml_fp16_t));
} else {
// im2col rounds inputs to F16; use the same input precision for both paths.
std::vector<float> input(count);
ggml_fp16_to_fp32_row(values.data(), input.data(), count);
ggml_backend_tensor_set(tensor, input.data(), 0, count * sizeof(float));
}
}
return true;
}
bool sd_probe_conv_direct(ggml_backend_t backend, bool conv3d) {
if (backend == nullptr) {
return false;
}
const char* name = ggml_backend_name(backend);
const int dims = conv3d ? 3 : 2;
try {
BackendProbeGraph capability(backend);
if (capability.ctx == nullptr) {
LOG_INFO("conv%dd direct auto: %s -> off (probe graph unavailable)", dims, name);
return false;
}
build_conv_direct_probe(capability, conv3d, true);
if (!capability.supported()) {
LOG_INFO("conv%dd direct auto: %s -> off (unsupported probe graph)", dims, name);
return false;
}
if (sd_backend_is(backend, "Vulkan")) {
std::vector<BackendProbeCase> cases;
for (const auto& shape : {std::pair<int, int>{32, 64}, {16, 128}}) {
BackendProbeCase test;
test.build = [conv3d, shape](BackendProbeGraph& graph, bool direct) {
build_conv_direct_probe(graph, conv3d, direct, shape.first, shape.second);
};
test.initialize = [conv3d](BackendProbeGraph& graph) { return initialize_conv_probe(graph, conv3d); };
cases.push_back(std::move(test));
}
const auto result = sd_run_backend_probe(backend, 0, cases);
if (!result.completed) {
LOG_WARN("conv%dd direct auto: %s -> off (%s)", dims, name, result.error.c_str());
return false;
}
LOG_INFO("conv%dd direct auto: %s -> %s (probe: im2col %.3f ms, direct %.3f ms)",
dims, name, result.candidate_faster ? "on" : "off", result.reference_ms, result.candidate_ms);
return result.candidate_faster;
}
if (sd_backend_supports_conv_implicit_gemm(backend, conv3d)) {
LOG_INFO("conv%dd direct auto: %s -> on (implicit GEMM)", dims, name);
return true;
}
LOG_INFO("conv%dd direct auto: %s -> off (no implicit GEMM path)", dims, name);
return false;
} catch (const std::exception& error) {
LOG_WARN("conv%dd direct auto: %s -> off (probe failed: %s)", dims, name, error.what());
return false;
}
}
+8
View File
@@ -0,0 +1,8 @@
#ifndef __SD_CORE_PROBE_CONV_DIRECT_H__
#define __SD_CORE_PROBE_CONV_DIRECT_H__
#include "ggml-backend.h"
bool sd_probe_conv_direct(ggml_backend_t backend, bool conv3d);
#endif // __SD_CORE_PROBE_CONV_DIRECT_H__
+2 -1
View File
@@ -3,6 +3,7 @@
#include <array>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <stdexcept>
#include <string>
@@ -105,7 +106,7 @@ namespace sd {
template <typename T>
inline Tensor<T> load_tensor_from_file_as_tensor(const std::string& file_path) {
std::ifstream file(file_path, std::ios::binary);
std::ifstream file(std::filesystem::u8path(file_path), std::ios::binary);
if (!file.is_open()) {
throw std::runtime_error("failed to open tensor file: " + file_path);
}
+5 -4
View File
@@ -7,6 +7,7 @@
#include <cstdlib>
#include <cstring>
#include <exception>
#include <filesystem>
#include <fstream>
#include <locale>
#include <regex>
@@ -123,12 +124,12 @@ int round_up_to(int value, int base) {
#include <windows.h>
bool file_exists(const std::string& filename) {
DWORD attributes = GetFileAttributesA(filename.c_str());
DWORD attributes = GetFileAttributesW(std::filesystem::u8path(filename).c_str());
return (attributes != INVALID_FILE_ATTRIBUTES && !(attributes & FILE_ATTRIBUTE_DIRECTORY));
}
bool is_directory(const std::string& path) {
DWORD attributes = GetFileAttributesA(path.c_str());
DWORD attributes = GetFileAttributesW(std::filesystem::u8path(path).c_str());
return (attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY));
}
@@ -152,8 +153,8 @@ std::unique_ptr<MmapWrapper> MmapWrapper::create(const std::string& filename, bo
void* mapped_data = nullptr;
size_t file_size = 0;
HANDLE file_handle = CreateFileA(
filename.c_str(),
HANDLE file_handle = CreateFileW(
std::filesystem::u8path(filename).c_str(),
GENERIC_READ,
FILE_SHARE_READ,
nullptr,
+13 -1
View File
@@ -66,6 +66,7 @@ enum SDVersion {
VERSION_MING_IMAGE,
VERSION_Z_IMAGE_L2P,
VERSION_IRIS,
VERSION_FLUX3,
VERSION_COUNT,
};
@@ -118,6 +119,10 @@ static inline bool sd_version_is_flux(SDVersion version) {
return false;
}
static inline bool sd_version_is_flux3(SDVersion version) {
return version == VERSION_FLUX3;
}
static inline bool sd_version_is_flux2(SDVersion version) {
if (version == VERSION_FLUX2 || version == VERSION_FLUX2_KLEIN) {
return true;
@@ -280,7 +285,13 @@ static inline bool sd_version_uses_flux_vae(SDVersion version) {
}
static inline bool sd_version_uses_flux2_vae(SDVersion version) {
if (sd_version_is_flux2(version) || sd_version_is_ernie_image(version) || sd_version_is_lens(version) || sd_version_is_ideogram4(version) || sd_version_is_sefi_image(version) || sd_version_is_llada_image(version)) {
if (sd_version_is_flux2(version) ||
sd_version_is_flux3(version) ||
sd_version_is_ernie_image(version) ||
sd_version_is_lens(version) ||
sd_version_is_ideogram4(version) ||
sd_version_is_sefi_image(version) ||
sd_version_is_llada_image(version)) {
return true;
}
return false;
@@ -311,6 +322,7 @@ static inline bool sd_version_is_inpaint(SDVersion version) {
static inline bool sd_version_is_dit(SDVersion version) {
if (sd_version_is_flux(version) ||
sd_version_is_flux2(version) ||
sd_version_is_flux3(version) ||
sd_version_is_ltxav(version) ||
sd_version_is_minimax_h3(version) ||
sd_version_is_sd3(version) ||
+2 -1
View File
@@ -14,6 +14,7 @@
#include "core/ggml_extend.h"
#include "core/ggml_runner.h"
#include "core/ggml_extend_backend.h"
#include "model.h"
class GGMLBlock {
@@ -222,7 +223,7 @@ public:
if (force_prec_f32) {
ggml_prec_set_acc(fp8_matmul, GGML_PREC_F32);
}
supports_fp8_matmul = ggml_backend_supports_op(ctx->backend, fp8_matmul);
supports_fp8_matmul = ggml_backend_supports_op(ctx->backend, fp8_matmul) && !sd_backend_is_cpu(ctx->backend);
}
if (!supports_fp8_matmul) {
w = ggml_cast(ctx->ggml_ctx, w, GGML_TYPE_BF16);
+256
View File
@@ -0,0 +1,256 @@
#ifndef __SD_MODEL_DIFFUSION_FLUX3_HPP__
#define __SD_MODEL_DIFFUSION_FLUX3_HPP__
#include "model/diffusion/flux.hpp"
namespace Flux3 {
struct Flux3Config {
int64_t hidden_size = 3072;
int64_t context_dim = 20480;
int64_t channels = 128;
int64_t mlp_dim = 9216;
int heads = 24;
int depth = 5;
int single_depth = 28;
bool flow_map = false;
static Flux3Config detect_from_weights(const String2TensorStorage& tensors, const std::string& prefix) {
Flux3Config config;
int depth = 0, single_depth = 0;
bool detected = false;
for (const auto& [name, tensor] : tensors) {
if (!starts_with(name, prefix + ".")) {
continue;
}
std::string key = name.substr(prefix.size() + 1);
if (key == "txt_in.weight") {
detected = true;
config.context_dim = tensor.ne[0];
config.hidden_size = tensor.ne[1];
} else if (key == "emb_in.image.weight") {
config.channels = tensor.ne[0];
} else if (key == "single_blocks.0.mlp_out.weight") {
config.mlp_dim = tensor.ne[0];
} else if (key == "ref_time_in.in_layer.weight") {
config.flow_map = true;
}
if (starts_with(key, "txt_mode_blocks.")) {
depth = std::max(depth, atoi(key.substr(16).c_str()) + 1);
}
if (starts_with(key, "single_blocks.")) {
single_depth = std::max(single_depth, atoi(key.substr(14).c_str()) + 1);
}
}
if (depth > 0)
config.depth = depth;
if (single_depth > 0)
config.single_depth = single_depth;
config.heads = static_cast<int>(config.hidden_size / 128);
if (detected) {
LOG_VERBOSE("flux3: hidden_size = %" PRId64 ", context_dim = %" PRId64 ", depth = %d, single_depth = %d, flow_map = %s",
config.hidden_size, config.context_dim, config.depth, config.single_depth, config.flow_map ? "true" : "false");
}
return config;
}
};
static ggml_tensor* token_slice(ggml_context* ctx, ggml_tensor* x, int64_t start, int64_t length) {
return ggml_view_3d(ctx, x, x->ne[0], length, x->ne[2], x->nb[1], x->nb[2], start * x->nb[1]);
}
struct Block : public GGMLBlock {
int heads;
Block(const Flux3Config& config)
: heads(config.heads) {
for (const auto* name : {"q_proj", "k_proj", "v_proj", "attn_out"}) {
blocks[name] = std::make_shared<Linear>(config.hidden_size, config.hidden_size, false);
}
blocks["mlp_in"] = std::make_shared<Linear>(config.hidden_size, 2 * config.mlp_dim, false);
blocks["mlp_out"] = std::make_shared<Linear>(config.mlp_dim, config.hidden_size, false);
blocks["norm"] = std::make_shared<Flux::QKNorm>(config.hidden_size / heads);
}
ggml_tensor* linear(GGMLRunnerContext* ctx, const std::string& name, ggml_tensor* x) {
return std::dynamic_pointer_cast<Linear>(blocks[name])->forward(ctx, x);
}
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x, ggml_tensor* pe, const std::vector<Flux::ModulationOut>& mods, const std::vector<int64_t>& lengths) {
auto gc = ctx->ggml_ctx;
ggml_tensor* x_mod = nullptr;
int64_t offset = 0;
for (size_t i = 0; i < lengths.size(); ++i) {
auto part = token_slice(gc, x, offset, lengths[i]);
part = Flux::modulate(gc, ggml_norm(gc, part, 1e-6f), mods[i].shift, mods[i].scale);
x_mod = x_mod ? ggml_concat(gc, x_mod, part, 1) : part;
offset += lengths[i];
}
auto split = [&](ggml_tensor* v) {
return ggml_reshape_4d(gc, v, x->ne[0] / heads, heads, x->ne[1], x->ne[2]);
};
auto norm = std::dynamic_pointer_cast<Flux::QKNorm>(blocks["norm"]);
auto q = norm->query_norm(ctx, split(linear(ctx, "q_proj", x_mod)));
auto k = norm->key_norm(ctx, split(linear(ctx, "k_proj", x_mod)));
auto v = split(linear(ctx, "v_proj", x_mod));
auto attn = Rope::attention(ctx, q, k, v, pe, nullptr);
auto mlp = ggml_ext_silu_act(gc, linear(ctx, "mlp_in", x_mod));
auto update = ggml_add(gc, linear(ctx, "attn_out", attn), linear(ctx, "mlp_out", mlp));
ggml_tensor* out = nullptr;
offset = 0;
for (size_t i = 0; i < lengths.size(); ++i) {
auto part = ggml_mul(gc, token_slice(gc, update, offset, lengths[i]), mods[i].gate);
part = ggml_add(gc, token_slice(gc, x, offset, lengths[i]), part);
out = out ? ggml_concat(gc, out, part, 1) : part;
offset += lengths[i];
}
return out;
}
};
struct Model : public GGMLBlock {
Flux3Config config;
Model(const Flux3Config& config)
: config(config) {
blocks["txt_in"] = std::make_shared<Linear>(config.context_dim, config.hidden_size, false);
blocks["time_in"] = std::make_shared<Flux::MLPEmbedder>(256, config.hidden_size, false);
if (config.flow_map) {
blocks["ref_time_in"] = std::make_shared<Flux::MLPEmbedder>(256, config.hidden_size, false);
}
for (const std::string mode : {"image", "image_cond", "txt"}) {
blocks["early_stream_modulations." + mode] = std::make_shared<Flux::Modulation>(config.hidden_size, false, false);
blocks["single_stream_modulations." + mode] = std::make_shared<Flux::Modulation>(config.hidden_size, false, false);
const std::string base = mode == "txt" ? "txt_mode_blocks." : "content_mode_blocks." + mode + ".";
for (int i = 0; i < config.depth; ++i) {
blocks[base + std::to_string(i)] = std::make_shared<Block>(config);
}
if (mode != "txt") {
blocks["emb_in." + mode] = std::make_shared<Linear>(config.channels, config.hidden_size, false);
}
}
for (int i = 0; i < config.single_depth; ++i) {
blocks["single_blocks." + std::to_string(i)] = std::make_shared<Block>(config);
}
blocks["final_layer.image"] = std::make_shared<Flux::LastLayer>(config.hidden_size, 1, config.channels, false, false);
}
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* image, ggml_tensor* text, ggml_tensor* reference, ggml_tensor* timestep, ggml_tensor* zero, ggml_tensor* target, ggml_tensor* pe) {
auto gc = ctx->ggml_ctx;
const int64_t width = image->ne[0], height = image->ne[1];
auto time_in = std::dynamic_pointer_cast<Flux::MLPEmbedder>(blocks["time_in"]);
auto image_vec = time_in->forward(ctx, ggml_ext_timestep_embedding(gc, timestep, 256, 10000, 1000.f));
auto clean_vec = time_in->forward(ctx, ggml_ext_timestep_embedding(gc, zero, 256, 10000, 1000.f));
if (config.flow_map) {
auto ref_time = std::dynamic_pointer_cast<Flux::MLPEmbedder>(blocks["ref_time_in"]);
auto ref_vec = ref_time->forward(ctx, ggml_ext_timestep_embedding(gc, target, 256, 10000, 1000.f));
image_vec = ggml_add(gc, image_vec, ref_vec);
clean_vec = ggml_add(gc, clean_vec, ref_vec);
}
std::vector<std::string> modes{"image"};
std::vector<ggml_tensor*> inputs{DiT::patchify(gc, image, 1, 1)};
if (reference) {
modes.push_back("image_cond");
inputs.push_back(reference);
}
modes.push_back("txt");
inputs.push_back(text);
std::vector<Flux::ModulationOut> single_mods;
std::vector<int64_t> lengths;
ggml_tensor* joint = nullptr;
int64_t offset = 0;
for (size_t m = 0; m < modes.size(); ++m) {
const auto& mode = modes[m];
auto vec = m == 0 ? image_vec : clean_vec;
auto early = std::dynamic_pointer_cast<Flux::Modulation>(blocks["early_stream_modulations." + mode])->forward(ctx, vec)[0];
single_mods.push_back(std::dynamic_pointer_cast<Flux::Modulation>(blocks["single_stream_modulations." + mode])->forward(ctx, vec)[0]);
lengths.push_back(inputs[m]->ne[1]);
auto h = std::dynamic_pointer_cast<Linear>(blocks[mode == "txt" ? "txt_in" : "emb_in." + mode])->forward(ctx, inputs[m]);
auto mode_pe = ggml_view_4d(gc, pe, 2, 2, 64, lengths.back(), pe->nb[1], pe->nb[2], pe->nb[3], offset * pe->nb[3]);
const std::string base = mode == "txt" ? "txt_mode_blocks." : "content_mode_blocks." + mode + ".";
for (int i = 0; i < config.depth; ++i) {
h = std::dynamic_pointer_cast<Block>(blocks[base + std::to_string(i)])->forward(ctx, h, mode_pe, {early}, {lengths.back()});
}
joint = joint ? ggml_concat(gc, joint, h, 1) : h;
offset += lengths.back();
}
for (int i = 0; i < config.single_depth; ++i) {
joint = std::dynamic_pointer_cast<Block>(blocks["single_blocks." + std::to_string(i)])->forward(ctx, joint, pe, single_mods, lengths);
}
auto out = std::dynamic_pointer_cast<Flux::LastLayer>(blocks["final_layer.image"])->forward(ctx, token_slice(gc, joint, 0, lengths[0]), image_vec);
return DiT::unpatchify(gc, out, height, width, 1, 1);
}
};
struct Flux3Runner : public DiffusionModelRunner {
Flux3Config config;
Model model;
std::vector<float> pe_values;
sd::Tensor<float> zero = sd::Tensor<float>::zeros({1});
Flux3Runner(ggml_backend_t backend, const String2TensorStorage& tensors, const std::string& prefix, std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
: DiffusionModelRunner(backend, prefix, weight_manager),
config(Flux3Config::detect_from_weights(tensors, prefix)),
model(config) {
model.init(params_ctx, tensors, prefix);
}
std::string get_desc() override { return "flux3"; }
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors, const std::string& prefix) override {
model.get_param_tensors(tensors, prefix);
}
sd::Tensor<float> compute(int n_threads, const DiffusionParams& params) override {
if (params.ref_latents && params.ref_image_params.pass_to_dit && params.ref_latents->size() > 10) {
LOG_ERROR("FLUX.3 supports at most 10 reference images");
return {};
}
const auto* extra = diffusion_extra_as<Flux3DiffusionExtra>(params);
GGML_ASSERT(params.x && params.context && params.timesteps);
GGML_ASSERT(!config.flow_map || extra->target_timestep);
auto get_graph = [&]() {
auto gf = new_graph_custom(FLUX_GRAPH_SIZE);
auto x = make_input(*params.x);
auto text = make_input(*params.context);
GGML_ASSERT(x->ne[3] == 1 && text->ne[2] == 1);
Rope::Embedding embedding;
auto append_image = [&](int64_t w, int64_t h, float index) {
embedding.positions.append_image(static_cast<int>(h), static_cast<int>(w), 1, 0.5f, 0.5f);
for (int64_t y = 0; y < h; ++y) {
for (int64_t x = 0; x < w; ++x) {
embedding.ids.push_back({0.f, y * 0.5f, x * 0.5f, index});
}
}
};
append_image(x->ne[0], x->ne[1], 0.f);
ggml_tensor* references = nullptr;
if (params.ref_latents && params.ref_image_params.pass_to_dit) {
int index = 0;
for (const auto& ref : *params.ref_latents) {
auto r = make_input(ref);
append_image(r->ne[0], r->ne[1], -float(++index));
r = DiT::patchify(compute_ctx, r, 1, 1);
references = references ? ggml_concat(compute_ctx, references, r, 1) : r;
}
}
for (int64_t i = 0; i < text->ne[1]; ++i) {
embedding.ids.push_back({0.f, 0.f, 0.f, static_cast<float>(i)});
}
embedding.positions.append_tokens(text->ne[1]);
embedding.values = Rope::embed_nd(embedding.ids, 1, 10000.f, {32, 32, 32, 32}, embedding.layout, &embedding.frequencies);
pe_values = finish_rope_pe(std::move(embedding));
auto pe = ggml_new_tensor_4d(compute_ctx, GGML_TYPE_F32, 2, 2, 64, pe_values.size() / 256);
set_backend_tensor_data(pe, pe_values.data());
auto runner_ctx = get_context();
auto out = model.forward(&runner_ctx, x, text, references, make_input(*params.timesteps), make_input(zero),
config.flow_map ? make_input(*extra->target_timestep) : nullptr, pe);
ggml_build_forward_expand(gf, out);
return gf;
};
return restore_trailing_singleton_dims(GGMLRunner::compute(get_graph, n_threads, false), params.x->dim());
}
};
} // namespace Flux3
#endif // __SD_MODEL_DIFFUSION_FLUX3_HPP__
+5
View File
@@ -64,6 +64,10 @@ struct FluxDiffusionExtra {
float pulid_id_weight = 1.0f;
};
struct Flux3DiffusionExtra {
const sd::Tensor<float>* target_timestep = nullptr;
};
struct AnimaDiffusionExtra {
const sd::Tensor<int32_t>* t5_ids = nullptr;
const sd::Tensor<float>* t5_weights = nullptr;
@@ -149,6 +153,7 @@ using DiffusionExtraParams = std::variant<std::monostate,
UNetDiffusionExtra,
SkipLayerDiffusionExtra,
FluxDiffusionExtra,
Flux3DiffusionExtra,
AnimaDiffusionExtra,
QwenImage21DiffusionExtra,
WanDiffusionExtra,
+10 -3
View File
@@ -2338,7 +2338,8 @@ namespace LLM {
const DeepStackImageEmbeds& deepstack_image_embeds_tensor,
const std::vector<ImageGrid>& image_grids,
std::set<int> out_layers,
bool return_all_hidden_states = false) {
bool return_all_hidden_states = false,
const sd::Tensor<int32_t>& position_ids = {}) {
ggml_cgraph* gf = new_graph_custom(LLM_GRAPH_SIZE);
ggml_tensor* input_ids = make_input(input_ids_tensor);
std::vector<std::pair<int, ggml_tensor*>> image_embeds;
@@ -2402,6 +2403,10 @@ namespace LLM {
}
}
if (!position_ids.empty()) {
GGML_ASSERT(position_ids.numel() == static_cast<int64_t>(input_pos_vec.size()));
input_pos_vec = position_ids.values();
}
auto input_pos = ggml_new_tensor_1d(compute_ctx,
GGML_TYPE_I32,
input_pos_vec.size());
@@ -2475,7 +2480,8 @@ namespace LLM {
bool return_all_hidden_states = false,
bool auto_runner_end = true,
const DeepStackImageEmbeds& deepstack_image_embeds = {},
const std::vector<ImageGrid>& image_grids = {}) {
const std::vector<ImageGrid>& image_grids = {},
const sd::Tensor<int32_t>& position_ids = {}) {
auto get_graph = [&]() -> ggml_cgraph* {
return build_graph(input_ids,
attention_mask,
@@ -2483,7 +2489,8 @@ namespace LLM {
deepstack_image_embeds,
image_grids,
out_layers,
return_all_hidden_states);
return_all_hidden_states,
position_ids);
};
return restore_trailing_singleton_dims(GGMLRunner::compute(get_graph, n_threads, auto_runner_end),
input_ids.dim() + 1);
+3 -2
View File
@@ -3,6 +3,7 @@
#include <algorithm>
#include <cstdint>
#include <cstdio>
#include <filesystem>
#include <fstream>
#include <ostream>
#include <string>
@@ -19,7 +20,7 @@ static void set_error(std::string* error, const std::string& message) {
}
bool is_gguf_file(const std::string& file_path) {
std::ifstream file(file_path, std::ios::binary);
std::ifstream file(std::filesystem::u8path(file_path), std::ios::binary);
if (!file.is_open()) {
return false;
}
@@ -178,7 +179,7 @@ bool GGUFStreamingWriter::write_metadata(const std::string& file_path,
}
LOG_INFO("trying to save tensors to %s", file_path.c_str());
FILE* file = fopen(file_path.c_str(), "wb+");
FILE* file = ggml_fopen(file_path.c_str(), "wb+");
if (file == nullptr) {
set_error(error, "failed to open output file '" + file_path + "'");
close();
+2 -1
View File
@@ -2,6 +2,7 @@
#define __SD_MODEL_IO_GGUF_READER_EXT_H__
#include <cstdint>
#include <filesystem>
#include <fstream>
#include <string>
#include <vector>
@@ -184,7 +185,7 @@ private:
public:
bool load(const std::string& file_path) {
std::ifstream fin(file_path, std::ios::binary | std::ios::ate);
std::ifstream fin(std::filesystem::u8path(file_path), std::ios::binary | std::ios::ate);
if (!fin) {
LOG_ERROR("failed to open '%s'", file_path.c_str());
return false;
+8 -8
View File
@@ -26,15 +26,15 @@ static void set_error(std::string* error, const std::string& message) {
}
static std::string resolve_index_shard_path(const std::string& index_path, const std::string& shard_path) {
fs::path shard_fs_path(shard_path);
fs::path shard_fs_path = fs::u8path(shard_path);
if (shard_fs_path.is_absolute()) {
return shard_fs_path.lexically_normal().string();
return shard_fs_path.lexically_normal().u8string();
}
return (fs::path(index_path).parent_path() / shard_fs_path).lexically_normal().string();
return (fs::u8path(index_path).parent_path() / shard_fs_path).lexically_normal().u8string();
}
bool is_safetensors_file(const std::string& file_path) {
std::ifstream file(file_path, std::ios::binary);
std::ifstream file(fs::u8path(file_path), std::ios::binary);
if (!file.is_open()) {
return false;
}
@@ -153,7 +153,7 @@ bool read_safetensors_file(const std::string& file_path,
std::string* error,
std::map<std::string, std::string>* metadata,
std::set<std::string>* tensor_names) {
std::ifstream file(file_path, std::ios::binary);
std::ifstream file(fs::u8path(file_path), std::ios::binary);
if (!file.is_open()) {
set_error(error, "failed to open '" + file_path + "'");
return false;
@@ -418,7 +418,7 @@ bool read_safetensors_index_file(const std::string& file_path,
std::string* error) {
shard_paths.clear();
std::ifstream file(file_path);
std::ifstream file(fs::u8path(file_path));
if (!file.is_open()) {
set_error(error, "failed to open '" + file_path + "'");
return false;
@@ -522,7 +522,7 @@ bool write_safetensors_file(const std::string& file_path,
const std::string header_str = header.dump();
std::ofstream file(file_path, std::ios::binary);
std::ofstream file(fs::u8path(file_path), std::ios::binary);
if (!file.is_open()) {
set_error(error, "failed to open '" + file_path + "' for writing");
return false;
@@ -596,7 +596,7 @@ bool SafetensorsStreamingWriter::write_metadata(const std::string& file_path,
data_start_ = ST_HEADER_SIZE_LEN + header_str.size();
LOG_INFO("trying to save tensors to %s", file_path.c_str());
std::ofstream file(file_path, std::ios::binary | std::ios::trunc);
std::ofstream file(fs::u8path(file_path), std::ios::binary | std::ios::trunc);
if (!file.is_open()) {
set_error(error, "failed to open '" + file_path + "' for writing");
return false;
+2 -1
View File
@@ -2,6 +2,7 @@
#include <algorithm>
#include <cstdint>
#include <filesystem>
#include <fstream>
#include <string>
#include <unordered_map>
@@ -98,7 +99,7 @@ static std::string torch_legacy_diagnostics(const std::string& file_path, const
bool read_torch_legacy_file(const std::string& file_path,
std::vector<TensorStorage>& tensor_storages,
std::string* error) {
std::ifstream file(file_path, std::ios::binary);
std::ifstream file(std::filesystem::u8path(file_path), std::ios::binary);
if (!file.is_open()) {
set_error(error, "failed to open '" + file_path + "'");
return false;
+9 -3
View File
@@ -4,6 +4,7 @@
#include <cinttypes>
#include <cstdarg>
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <functional>
#include <mutex>
@@ -333,7 +334,7 @@ bool ModelLoader::init_from_safetensors_index_file(const std::string& file_path,
}
}
std::ifstream index_file(file_path);
std::ifstream index_file(std::filesystem::u8path(file_path));
const auto index = nlohmann::json::parse(index_file);
for (const auto& entry : index.at("weight_map").items()) {
const auto expected = (std::filesystem::u8path(file_path).parent_path() /
@@ -438,6 +439,11 @@ bool ModelLoader::init_from_diffusers_file(const std::string& file_path, const s
SDVersion ModelLoader::get_sd_version() const {
TensorStorage token_embedding_weight, input_block_weight, context_ebedding_weight;
if (tensor_storage_map.find("model.diffusion_model.emb_in.image.weight") != tensor_storage_map.end() &&
tensor_storage_map.find("model.diffusion_model.txt_mode_blocks.0.q_proj.weight") != tensor_storage_map.end()) {
return VERSION_FLUX3;
}
bool has_multiple_encoders = false;
bool is_unet = false;
@@ -630,7 +636,7 @@ SDVersion ModelLoader::get_sd_version() const {
}
}
if (is_wan) {
LOG_VERBOSE("patch_embedding_channels %d", patch_embedding_channels);
LOG_DEBUG("patch_embedding_channels %d", patch_embedding_channels);
if (is_s2v) {
return VERSION_WAN2_2_S2V;
}
@@ -1145,7 +1151,7 @@ bool ModelLoader::load_tensors(on_new_tensor_cb_t on_new_tensor_cb,
return;
}
} else if (!mmapped) {
file.open(file_path, std::ios::binary);
file.open(std::filesystem::u8path(file_path), std::ios::binary);
if (!file.is_open()) {
LOG_ERROR("failed to open '%s'", file_path.c_str());
failed = true;
+1 -1
View File
@@ -1762,7 +1762,7 @@ ModelManager::CapacityCheck ModelManager::check_capacity(
}
// A wrapped report may reflect real pressure; reclaim before accepting the tracked estimate.
LOG_VERBOSE("ignoring impossible free memory report on %s (%.2f MB free / %.2f MB total), using tracked residency",
ggml_backend_name(backend), free_bytes / (1024.0 * 1024.0), total_bytes / (1024.0 * 1024.0));
ggml_backend_name(backend), free_bytes / (1024.0 * 1024.0), total_bytes / (1024.0 * 1024.0));
free_bytes = total_bytes;
result.requires_reclamation = true;
}
+75 -2
View File
@@ -18,6 +18,7 @@
#include "core/ggml_runner.h"
#include "core/ggml_tensor_utils.h"
#include "core/layer_split_partition.h"
#include "core/probe/conv_direct.h"
#include "core/probe/flash_attn.h"
#include "model.h"
@@ -110,6 +111,7 @@ const char* model_version_to_str[] = {
"Ming-Image",
"Z-Image L2P",
"Iris-3B",
"Flux.3 Image Klein",
};
static_assert(VERSION_COUNT == sizeof(model_version_to_str) / sizeof(model_version_to_str[0]),
@@ -951,7 +953,15 @@ bool StableDiffusionGGML::init(const sd_ctx_params_t* sd_ctx_params) {
LOG_ERROR("invalid flash_attn_mode '%s': %s", flash_mode.c_str(), flash_error.c_str());
return false;
}
const std::string conv_mode = SAFE_STR(sd_ctx_params->conv_direct_mode);
std::string conv_error;
if (!sd_parse_conv_direct_assignment(conv_mode, &conv_direct_assignment, &conv_error)) {
LOG_ERROR("invalid conv_direct_mode '%s': %s", conv_mode.c_str(), conv_error.c_str());
return false;
}
flash_attn_by_backend.clear();
conv2d_direct_by_backend.clear();
conv3d_direct_by_backend.clear();
conditioning_cache_->set_capacity(static_cast<size_t>(sd_ctx_params->conditioning_cache_size));
auto configuration = std::make_unique<ModelConfig>(*sd_ctx_params);
n_threads = sd_ctx_params->n_threads;
@@ -1224,6 +1234,34 @@ bool StableDiffusionGGML::flash_attention_for(SDBackendModule module) {
return enabled;
}
bool StableDiffusionGGML::conv_direct_for(SDBackendModule module, bool conv3d) {
const auto& params = config_->params;
const std::string mode = conv_direct_assignment.get(module);
if (mode == "on" || mode == "off") {
return mode == "on";
}
if (strlen(SAFE_STR(params.conv_direct_mode)) == 0 &&
(module == SDBackendModule::VAE ? params.vae_conv_direct : params.diffusion_conv_direct)) {
return true;
}
auto& by_backend = conv3d ? conv3d_direct_by_backend : conv2d_direct_by_backend;
auto backends = backend_manager.runtime_backends(module);
bool enabled = !backends.empty();
bool any_enabled = false;
for (auto backend : backends) {
auto it = by_backend.find(backend);
if (it == by_backend.end()) {
it = by_backend.emplace(backend, sd_probe_conv_direct(backend, conv3d)).first;
}
enabled = enabled && it->second;
any_enabled = any_enabled || it->second;
}
if (any_enabled && !enabled) {
LOG_INFO("conv%dd direct auto: %s -> off (compute devices disagree)", conv3d ? 3 : 2, sd_backend_module_name(module));
}
return enabled;
}
bool StableDiffusionGGML::validate_and_load_runners() {
const auto* sd_ctx_params = &config_->params;
const bool use_tae = uses_tae();
@@ -1261,6 +1299,23 @@ bool StableDiffusionGGML::validate_and_load_runners() {
if (sd_ctx_params->sage_attn && !set_sage_attention_enabled(true)) {
return false;
}
const auto set_conv_direct = [this](SDBackendModule module, const std::vector<GGMLRunner*>& runners) {
const bool conv2d = conv_direct_for(module, false);
const bool conv3d = conv_direct_for(module, true);
for (auto runner : runners) {
if (runner != nullptr) {
runner->set_conv2d_direct_enabled(conv2d);
runner->set_conv3d_direct_enabled(conv3d);
}
}
};
set_conv_direct(SDBackendModule::DIFFUSION, {diffusion_model.get(), high_noise_diffusion_model.get()});
if (first_stage_model || preview_vae || audio_vae_model) {
set_conv_direct(SDBackendModule::VAE, {first_stage_model.get(), preview_vae.get(), audio_vae_model.get()});
}
if (control_net) {
set_conv_direct(SDBackendModule::CONTROL_NET, {control_net.get()});
}
LOG_VERBOSE("validating model metadata");
std::set<std::string> ignore_tensors;
@@ -1460,6 +1515,9 @@ bool StableDiffusionGGML::build_denoiser() {
} else {
default_flow_shift = 3.f;
}
} else if (sd_version_is_flux3(version)) {
pred_type = FLUX_FLOW_PRED;
default_flow_shift = 6.93f;
} else if (sd_version_is_flux(version) ||
sd_version_is_flux2(version) ||
sd_version_is_longcat(version) ||
@@ -1525,7 +1583,11 @@ bool StableDiffusionGGML::build_denoiser() {
}
case FLUX_FLOW_PRED: {
LOG_INFO("running in Flux FLOW mode");
denoiser = std::make_shared<FluxFlowDenoiser>();
if (sd_version_is_flux3(version)) {
denoiser = std::make_shared<Flux3FlowDenoiser>();
} else {
denoiser = std::make_shared<FluxFlowDenoiser>();
}
break;
}
case SEFI_FLOW_PRED: {
@@ -1579,7 +1641,7 @@ bool StableDiffusionGGML::build_runners(const RunnerGroups& groups) {
LOG_INFO("Diffusion model weight type stat: %s", wtype_stat_to_str(diffusion_model_wtype_stat).c_str());
LOG_INFO("VAE weight type stat: %s", wtype_stat_to_str(vae_wtype_stat).c_str());
LOG_VERBOSE("ggml tensor size = %d bytes", (int)sizeof(ggml_tensor));
LOG_DEBUG("ggml tensor size = %d bytes", (int)sizeof(ggml_tensor));
configure_weight_loading();
for (auto group : groups) {
@@ -2504,6 +2566,13 @@ sd::Tensor<float> StableDiffusionGGML::sample(const std::shared_ptr<DiffusionMod
if (sd_version_is_hunyuan_video(version) && step + 1 < sigmas.size()) {
hunyuan_timestep_r_tensor = sd::Tensor<float>::from_vector({sigmas[step + 1]});
}
sd::Tensor<float> flux3_target_timestep;
if (version == VERSION_FLUX3) {
// The sign marks intermediate evaluations; the magnitude is the one-based step.
const size_t target_index = static_cast<size_t>(std::abs(step));
GGML_ASSERT(target_index > 0 && target_index < sigmas.size());
flux3_target_timestep = sd::Tensor<float>::from_vector({sigmas[target_index]});
}
sd::Tensor<float> noised_input = x * c_in;
if (apply_denoise_mask && (version == VERSION_WAN2_2_TI2V || sd_version_is_ltxav(version) || sd_version_is_lingbot_video(version))) {
noised_input = noised_input * denoise_mask + sampling_init_latent * (1.0f - denoise_mask);
@@ -2580,6 +2649,8 @@ sd::Tensor<float> StableDiffusionGGML::sample(const std::shared_ptr<DiffusionMod
} else if (sd_version_is_flux(version) || sd_version_is_flux2(version) || sd_version_is_longcat(version) || sd_version_is_sefi_image(version)) {
diffusion_params.extra = FluxDiffusionExtra{&guidance_tensor,
local_skip_layers};
} else if (sd_version_is_flux3(version)) {
diffusion_params.extra = Flux3DiffusionExtra{flux3_target_timestep.empty() ? nullptr : &flux3_target_timestep};
} else if (version == VERSION_QWEN_IMAGE_2_1) {
diffusion_params.extra = QwenImage21DiffusionExtra{&condition.c_token_types};
} else if (sd_version_is_anima(version)) {
@@ -3126,6 +3197,8 @@ std::string StableDiffusionGGML::get_default_ref_image_preset(SDVersion version)
return "longcat";
} else if (sd_version_is_flux(version)) {
return "flux_kontext";
} else if (sd_version_is_flux3(version)) {
return "flux2";
} else if (sd_version_is_flux2(version) || sd_version_is_sefi_image(version)) {
return "flux2";
} else if (version == VERSION_QWEN_IMAGE_LAYERED) {
+5 -1
View File
@@ -51,6 +51,9 @@ public:
SDBackendManager backend_manager;
std::unordered_map<ggml_backend_t, bool> flash_attn_by_backend;
SDBackendAssignment flash_attn_assignment;
SDBackendAssignment conv_direct_assignment;
std::unordered_map<ggml_backend_t, bool> conv2d_direct_by_backend;
std::unordered_map<ggml_backend_t, bool> conv3d_direct_by_backend;
SDVersion version;
bool external_vae_is_invalid = false;
@@ -134,7 +137,7 @@ public:
&sd_ctx_params_t::clip_g_path, &sd_ctx_params_t::clip_vision_path,
&sd_ctx_params_t::t5xxl_path, &sd_ctx_params_t::llm_path,
&sd_ctx_params_t::llm_vision_path, &sd_ctx_params_t::diffusion_model_path,
&sd_ctx_params_t::tokenizer, &sd_ctx_params_t::flash_attn_mode,
&sd_ctx_params_t::tokenizer, &sd_ctx_params_t::flash_attn_mode, &sd_ctx_params_t::conv_direct_mode,
&sd_ctx_params_t::high_noise_diffusion_model_path, &sd_ctx_params_t::uncond_diffusion_model_path,
&sd_ctx_params_t::embeddings_connectors_path, &sd_ctx_params_t::vae_path,
&sd_ctx_params_t::audio_vae_path, &sd_ctx_params_t::taesd_path,
@@ -338,6 +341,7 @@ public:
bool validate_and_load_runners();
bool flash_attention_for(SDBackendModule module);
bool conv_direct_for(SDBackendModule module, bool conv3d);
bool build_denoiser();
+6 -17
View File
@@ -15,6 +15,7 @@
#include "model/diffusion/control.hpp"
#include "model/diffusion/ernie_image.hpp"
#include "model/diffusion/flux.hpp"
#include "model/diffusion/flux3.hpp"
#include "model/diffusion/hidream_o1.hpp"
#include "model/diffusion/hunyuan.hpp"
#include "model/diffusion/ideogram4.hpp"
@@ -173,6 +174,11 @@ namespace sd::model_builders {
version,
weight_manager,
sd_ctx_params->model_args);
} else if (sd_version_is_flux3(version)) {
result.conditioner = std::make_shared<LLMEmbedder>(ctx.backends.runtime_backend(SDBackendModule::TE),
tensor_storage_map, version, "", false, weight_manager, tokenizers);
result.diffusion = std::make_shared<Flux3::Flux3Runner>(ctx.backends.runtime_backend(SDBackendModule::DIFFUSION),
tensor_storage_map, "model.diffusion_model", weight_manager);
} else if (sd_version_is_flux2(version) || sd_version_is_sefi_image(version)) {
bool is_chroma = false;
result.conditioner = std::make_shared<LLMEmbedder>(ctx.backends.runtime_backend(SDBackendModule::TE),
@@ -478,10 +484,6 @@ namespace sd::model_builders {
"model.diffusion_model",
version,
weight_manager);
if (sd_ctx_params->diffusion_conv_direct) {
LOG_INFO("Using Conv2d direct in the diffusion model");
result.diffusion->set_conv2d_direct_enabled(true);
}
}
if (strlen(SAFE_STR(sd_ctx_params->ip_adapter_path)) > 0 && result.clip_vision == nullptr) {
@@ -668,15 +670,6 @@ namespace sd::model_builders {
}
}
if (sd_ctx_params->vae_conv_direct) {
LOG_INFO("Using Conv2d/Conv3d direct in the vae model");
result.vae->set_conv2d_direct_enabled(true);
result.vae->set_conv3d_direct_enabled(true);
if (result.preview) {
result.preview->set_conv2d_direct_enabled(true);
result.preview->set_conv3d_direct_enabled(true);
}
}
if (result.vae) {
result.vae->set_scale_overrides(sd_ctx_params->linear_scale, sd_ctx_params->attn_scale);
}
@@ -703,10 +696,6 @@ namespace sd::model_builders {
version,
"",
weight_manager);
if (sd_ctx_params->diffusion_conv_direct) {
LOG_INFO("Using Conv2d direct in the control net");
control_net->set_conv2d_direct_enabled(true);
}
control_net->set_scale_overrides(sd_ctx_params->linear_scale, sd_ctx_params->attn_scale);
runner = std::move(control_net);
return true;
+23
View File
@@ -1452,6 +1452,29 @@ struct FluxFlowDenoiser : public DiscreteFlowDenoiser {
}
};
struct Flux3FlowDenoiser : public FluxFlowDenoiser {
float t_to_sigma(float t) override {
float u = (t + 1.f) / TIMESTEPS;
return shift * u / (1.f + (shift - 1.f) * u);
}
std::vector<float> get_sigmas(uint32_t n, int image_seq_len, scheduler_t scheduler_type, SDVersion version, const char* extra_sample_args = nullptr) override {
if (scheduler_type != DISCRETE_SCHEDULER) {
return Denoiser::get_sigmas(n, image_seq_len, scheduler_type, version, extra_sample_args);
}
if (n == 0) {
return {};
}
std::vector<float> sigmas;
sigmas.reserve(n + 1);
for (uint32_t i = 0; i <= n; ++i) {
float u = 1.f - static_cast<float>(i) / n;
sigmas.push_back(shift * u / (1.f + (shift - 1.f) * u));
}
return sigmas;
}
};
struct SefiFlowDenoiser;
struct SefiFlowDenoiser : public FluxFlowDenoiser {
+3 -2
View File
@@ -10,6 +10,7 @@
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <filesystem>
static IMatrixCollector imatrix_collector;
@@ -227,7 +228,7 @@ void IMatrixCollector::save_imatrix(std::string fname, int ncall) const {
LOG_WARN("storing only %zu out of %zu entries\n", to_store.size(), stats_.size());
}
std::ofstream out(fname, std::ios::binary);
std::ofstream out(std::filesystem::u8path(fname), std::ios::binary);
out.write((const char*)&n_entries, sizeof(n_entries));
for (const auto& name : to_store) {
const auto& stat = stats_.at(name);
@@ -251,7 +252,7 @@ void IMatrixCollector::save_imatrix(std::string fname, int ncall) const {
}
bool IMatrixCollector::load_imatrix(const char* fname) {
std::ifstream in(fname, std::ios::binary);
std::ifstream in(std::filesystem::u8path(fname), std::ios::binary);
if (!in) {
LOG_ERROR("failed to open %s\n", fname);
return false;
+3
View File
@@ -339,6 +339,7 @@ void sd_ctx_params_init(sd_ctx_params_t* sd_ctx_params) {
sd_ctx_params->eager_load = false;
sd_ctx_params->enable_mmap = false;
sd_ctx_params->flash_attn_mode = nullptr;
sd_ctx_params->conv_direct_mode = nullptr;
sd_ctx_params->diffusion_flash_attn = false;
sd_ctx_params->sage_attn = false;
sd_ctx_params->linear_scale = 0.f;
@@ -397,6 +398,7 @@ char* sd_ctx_params_to_str(const sd_ctx_params_t* sd_ctx_params) {
"auto_fit: %s\n"
"flash_attn: %s\n"
"flash_attn_mode: %s\n"
"conv_direct_mode: %s\n"
"diffusion_flash_attn: %s\n"
"sage_attn: %s\n"
"linear_scale: %g\n"
@@ -439,6 +441,7 @@ char* sd_ctx_params_to_str(const sd_ctx_params_t* sd_ctx_params) {
BOOL_STR(sd_ctx_params->auto_fit),
BOOL_STR(sd_ctx_params->flash_attn),
sd_ctx_params->flash_attn_mode && sd_ctx_params->flash_attn_mode[0] ? sd_ctx_params->flash_attn_mode : "auto (legacy flags honored)",
sd_ctx_params->conv_direct_mode && sd_ctx_params->conv_direct_mode[0] ? sd_ctx_params->conv_direct_mode : "auto (legacy flags honored)",
BOOL_STR(sd_ctx_params->diffusion_flash_attn),
BOOL_STR(sd_ctx_params->sage_attn),
sd_ctx_params->linear_scale,
+1 -1
View File
@@ -63,7 +63,7 @@ void CLIPTokenizer::load_from_merges(const std::string& merges_utf8_str) {
}
vocab.push_back(utf8_to_utf32("<|startoftext|>"));
vocab.push_back(utf8_to_utf32("<|endoftext|>"));
LOG_VERBOSE("vocab size: %zu", vocab.size());
LOG_DEBUG("vocab size: %zu", vocab.size());
int i = 0;
for (const auto& token : vocab) {
encoder[token] = i;
+2 -2
View File
@@ -29,7 +29,7 @@ void GemmaTokenizer::load_from_merges(const std::string& merges_utf8_str, const
decoder[i] = token;
}
encoder_len = static_cast<int>(vocab.size());
LOG_VERBOSE("vocab size: %d", encoder_len);
LOG_DEBUG("vocab size: %d", encoder_len);
std::vector<std::u32string> merges = split_utf32(merges_utf8_str);
std::vector<std::pair<std::u32string, std::u32string>> merge_pairs;
@@ -37,7 +37,7 @@ void GemmaTokenizer::load_from_merges(const std::string& merges_utf8_str, const
size_t space_pos = merge.find(' ');
merge_pairs.emplace_back(merge.substr(0, space_pos), merge.substr(space_pos + 1));
}
LOG_VERBOSE("merges size %zu", merge_pairs.size());
LOG_DEBUG("merges size %zu", merge_pairs.size());
int rank = 0;
for (const auto& merge : merge_pairs) {
+2 -1
View File
@@ -4,6 +4,7 @@
#include <array>
#include <climits>
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <queue>
#include <sstream>
@@ -495,7 +496,7 @@ struct HFTokenizer::Impl {
HFTokenizer::HFTokenizer(const std::string& path)
: impl_(new Impl) {
std::ifstream stream(path, std::ios::binary);
std::ifstream stream(std::filesystem::u8path(path), std::ios::binary);
tokenizer_require(stream.good(), "cannot open " + path);
TokenizerJSON config;
stream >> config;
+2 -2
View File
@@ -20,7 +20,7 @@ void MistralTokenizer::load_from_merges(const std::string& merges_utf8_str, cons
decoder[i] = token;
}
encoder_len = static_cast<int>(vocab.size());
LOG_VERBOSE("vocab size: %d", encoder_len);
LOG_DEBUG("vocab size: %d", encoder_len);
auto byte_unicode_pairs = bytes_to_unicode();
byte_encoder = std::map<int, std::u32string>(byte_unicode_pairs.begin(), byte_unicode_pairs.end());
@@ -28,7 +28,7 @@ void MistralTokenizer::load_from_merges(const std::string& merges_utf8_str, cons
byte_decoder[pair.second] = pair.first;
}
std::vector<std::u32string> merges = split_utf32(merges_utf8_str);
LOG_VERBOSE("merges size %zu", merges.size());
LOG_DEBUG("merges size %zu", merges.size());
std::vector<std::pair<std::u32string, std::u32string>> merge_pairs;
for (const auto& merge : merges) {
size_t space_pos = merge.find(' ');
+2 -2
View File
@@ -11,7 +11,7 @@ void Qwen2Tokenizer::load_from_merges(const std::string& merges_utf8_str) {
}
std::vector<std::u32string> merges = split_utf32(merges_utf8_str);
LOG_VERBOSE("merges size %zu", merges.size());
LOG_DEBUG("merges size %zu", merges.size());
std::vector<std::pair<std::u32string, std::u32string>> merge_pairs;
for (const auto& merge : merges) {
size_t space_pos = merge.find(' ');
@@ -36,7 +36,7 @@ void Qwen2Tokenizer::load_from_merges(const std::string& merges_utf8_str) {
i++;
}
encoder_len = i;
LOG_VERBOSE("vocab size: %d", encoder_len);
LOG_DEBUG("vocab size: %d", encoder_len);
int rank = 0;
for (const auto& merge : merge_pairs) {