Compare commits

...
62 changed files with 1754 additions and 152 deletions
+3 -1
View File
@@ -6,7 +6,9 @@ body:
- type: markdown
attributes:
value: |
Please use this template and include as many details as possible to help us reproduce and fix the issue.
Before submitting a bug report, please read the [Troubleshooting guide](https://github.com/leejet/stable-diffusion.cpp/blob/master/docs/troubleshooting.md) and try the steps relevant to your problem.
If the problem persists, complete this form and include what you tried and the results, along with enough details to help us reproduce and fix the issue.
- type: textarea
id: commit
attributes:
+4
View File
@@ -0,0 +1,4 @@
contact_links:
- name: Troubleshooting
url: https://github.com/leejet/stable-diffusion.cpp/blob/master/docs/troubleshooting.md
about: Read the troubleshooting guide first. If the problem persists, submit a bug report.
+2
View File
@@ -294,6 +294,8 @@ endif()
if(MSVC)
target_compile_options(${SD_LIB} PRIVATE $<$<COMPILE_LANGUAGE:CXX>:/bigobj>)
# ggml backends can throw C++ exceptions through their C API.
target_compile_options(${SD_LIB} PRIVATE $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:MSVC>>:/EHsc->)
endif()
if(APPLE)
+2
View File
@@ -51,6 +51,7 @@ API and command-line option may change frequently.***
- [LongCat Image](./docs/longcat_image.md)
- [Z-Image](./docs/z_image.md)
- [MiniT2I](./docs/minit2i.md)
- [SenseNova U1.5](./docs/sensenova_u1.md)
- [Ovis-Image](./docs/ovis_image.md)
- [Anima](./docs/anima.md)
- [ERNIE-Image](./docs/ernie_image.md)
@@ -147,6 +148,7 @@ For runtime and parameter backend placement, see the [backend selection guide](.
## More Guides
- [Troubleshooting](./docs/troubleshooting.md)
- [Backend selection](./docs/backend.md)
- [RPC](./docs/rpc.md)
- [LoRA](./docs/lora.md)
+30 -12
View File
@@ -5,7 +5,8 @@
- `--backend` selects the runtime backend used to execute model graphs.
- `--params-backend` selects where model parameters are kept.
If `--params-backend` is not set, parameters use the same backend as their module runtime backend.
If `--params-backend` is not set, auto-fit chooses parameter placement. With
`--auto-fit off`, parameters use the same backend as their module runtime backend.
## Syntax
@@ -129,17 +130,21 @@ warning.
## Automatic placement (`--auto-fit on|off`)
`--auto-fit` requires `on` or `off` and defaults to `on` when omitted.
Explicit `--backend` or `--params-backend` assignments disable auto-fit,
Explicit `--params-backend` assignments disable auto-fit,
regardless of argument order, even with `--auto-fit on`.
When enabled, auto-fit uses one GPU for `diffusion` / `te` / `vae` computation. It chooses
the GPU with the largest available memory budget (the first device on a tie),
then derives parameter placements from the model metadata and the remaining
memory budgets. The chosen backend specifications are printed.
Auto-fit preserves explicit `--backend` assignments, including per-module
assignments and device lists. For modules without a runtime assignment, it chooses
the GPU with the largest available memory budget (the first device on a tie).
It then derives parameter placements from the model metadata, each module's
compute devices, and the remaining memory budgets. The chosen backend
specifications are printed.
```shell
sd-cli -m model.safetensors -p "a cat" --auto-fit on
sd-cli -m model.safetensors -p "a cat" --auto-fit on --max-vram cuda0=8,cuda1=14
sd-cli -m model.safetensors -p "a cat" --backend cuda0
sd-cli -m model.safetensors -p "a cat" --backend diffusion=cuda0,te=cpu,vae=cuda1
sd-cli -m model.safetensors -p "a cat" --auto-fit off
```
@@ -149,11 +154,16 @@ GiB", and with no budget set each device's free memory minus a 512 MiB margin
is used. These resolved GPU budgets, including the safety margin, also drive
the runner's graph-cut capacity checks.
Runtime capacity checks also leave 512 MiB of currently free device memory for
backend scratch buffers and pipelines, including with explicit backend assignments.
They cap stale free-memory reports by the device's total memory minus tracked
resident allocations and reject reports that exceed the device's total memory.
Components are considered in `diffusion`, `te`, `vae` order so that repeatedly
used diffusion weights have priority. Each component's weights use the first
storage location with enough remaining budget:
1. The main GPU, leaving estimated space for computation and weight staging.
1. The component's compute GPU, leaving estimated space for computation and weight staging.
2. CPU RAM, reserving the larger of 2 GiB or 10% of available RAM for other work.
3. Another GPU, choosing the one with the largest remaining budget that fits.
4. Disk, reloading weights on demand.
@@ -170,10 +180,17 @@ weight to be copied again at every step.
RAM and GPU budgets are shared across components. Each component uses a single
parameter backend; several other GPUs' capacities are not combined to store
one component. If available RAM cannot be queried, RAM residency is skipped.
Other GPUs store weights only: weights are copied to the main GPU for execution.
Auto-fit does not select multi-GPU layer/row computation, so `--split-mode` does
not change its placements. Use explicit backend assignments for multi-GPU
computation.
Weights stored on another GPU are copied to the component's compute devices for
execution. CPU modules use RAM or disk. Compute reserves and cache priority are
accounted for separately on each device, so a CPU module does not reserve GPU
space. Storage on another module's GPU also leaves room for that module's work.
Auto-fit does not select multi-GPU layer/row computation itself. Explicit device
lists and `--split-mode` still control that computation. Before the runners have
built their split plans, auto-fit conservatively counts the full component size
on each listed GPU when checking residency and cache space. This can offload
parameters even when a split layout would fit; use `--auto-fit off` to keep the
default split-device parameter placement.
For example, a diffusion model whose full weights exceed the main GPU's budget
can use `--backend diffusion=cuda0 --params-backend diffusion=cpu` when RAM is
@@ -292,6 +309,7 @@ The example CLI/server still accepts these older CPU placement flags as compatib
Because this default is inserted first, later explicit `--params-backend` entries can still override it, for example `--offload-to-cpu --params-backend te=disk` keeps non-TE parameters on CPU and reloads TE parameters from disk.
Library callers should set `backend` and `params_backend` directly. `sd_ctx_params_init()`
enables `auto_fit` by default; nonempty `backend` or `params_backend` assignments disable it.
enables `auto_fit` by default; a nonempty `params_backend` assignment disables it.
The `backend` assignment constrains auto-fit's compute placement.
The old CPU/offload fields are no longer part of the C API. Explicit `--backend` and
`--params-backend` assignments are preferred for new commands.
+1 -1
View File
@@ -27,7 +27,7 @@ Using `--offload-to-cpu` allows you to offload weights to the CPU, saving VRAM w
## Use params backend to reduce VRAM or RAM usage.
`--params-backend` controls where model parameters are kept. If it is not set, parameters use the same backend as `--backend`, so a GPU runtime backend also keeps parameters in VRAM.
`--params-backend` controls where model parameters are kept. If it is not set, auto-fit chooses parameter placement while preserving `--backend`. With `--auto-fit off`, parameters use the same backend as `--backend`, so a GPU runtime backend also keeps parameters in VRAM.
Use CPU params to reduce VRAM usage:
+46
View File
@@ -0,0 +1,46 @@
# How to Use
SenseNova U1.5 is an 8B MoT model that performs diffusion directly in RGB pixel
space. It does not require a separate text encoder or VAE.
## Download weights
- Download SenseNova U1.5 8B MoT
- safetensors: https://huggingface.co/sensenova/SenseNova-U1.5-8B-MoT
Pass the complete downloaded repository directory to `--model`. The directory
must contain `model.safetensors.index.json`, every referenced Safetensors shard,
and the tokenizer files.
## Examples
### CUDA
```bash
./bin/sd-cli \
--model /path/to/SenseNova-U1.5-8B-MoT \
--prompt "a red cube on a white background" \
--width 2048 \
--height 2048 \
--steps 50 \
--cfg-scale 4 \
--flow-shift 3 \
--seed 42 \
--sampling-method euler \
--rng cuda \
--fa \
--output output.png
```
## Notes
- To match the official non-thinking text-to-image pipeline, use 50 Euler
steps, CFG 4, flow shift 3, seed 42, CUDA RNG, and an empty negative prompt.
- Width and height must be multiples of 32. The trained 1:1 resolution is
2048x2048; lower resolutions are useful for smoke tests but are outside the
training buckets.
- The SenseNova prompt template and unconditional prompt are built
automatically.
- This implementation supports non-thinking text-to-image generation. Image
editing, visual understanding, interleaved generation, and thinking-mode
prompt expansion are not implemented.
+45
View File
@@ -0,0 +1,45 @@
# Troubleshooting
## Completely black or white images or videos / NaNs
Some ggml backends can encounter numerical overflow during inference, producing
NaN (not-a-number) values. This can result in completely black or white images or videos.
Whether it happens can depend on the backend, device, model, and weight format.
Known overflow issues have been addressed as far as possible, but the maintainer
has limited hardware and cannot test every combination. Some cases may therefore
still need a manual workaround.
These options are supported by both `sd-cli` and `sd-server`. If you encounter
this problem, add them to your CLI generation command or server startup command:
```sh
--linear-scale 0.0078125 --attn-scale 0.0078125
```
For `sd-server`, restart the server after changing these startup options. Run the
same prompt and seed again to see whether the output recovers. If the problem
persists, try smaller positive values, for example:
```sh
--linear-scale 0.00390625 --attn-scale 0.00390625
```
These options reduce intermediate values and compensate afterwards to preserve
the intended output scale:
- `--linear-scale` scales Linear inputs before matrix multiplication and rescales
the result.
- `--attn-scale` scales attention keys and values (K/V). It takes effect only in
the Flash Attention path, where `--fa` or `--diffusion-fa` is enabled and the
backend supports it.
The two values can be set independently and apply across model components. The
default `0` preserves each model's built-in settings; `1` explicitly disables the
corresponding scaling. Overrides must be finite positive values. C API users can
set `linear_scale` and `attn_scale` in `sd_ctx_params_t`.
If the problem persists after trying the relevant steps above,
[submit a bug report](https://github.com/leejet/stable-diffusion.cpp/issues/new?template=bug_report.yml).
Include your full command, backend and hardware, model and weight format, logs,
and the scale values you tried with their results.
+3
View File
@@ -22,3 +22,6 @@ Metadata mode inspects PNG/JPEG container metadata without loading any model:
./bin/sd-cli -M metadata --image ./output.png --metadata-raw
./bin/sd-cli -M metadata --image ./output.png --metadata-all
```
For completely black or white images or videos, NaNs, and the `--linear-scale` /
`--attn-scale` workaround, see [Troubleshooting](../../docs/troubleshooting.md).
+51 -7
View File
@@ -302,8 +302,12 @@ bool parse_options(int argc, const char** argv, const std::vector<ArgOptions>& o
invalid_arg = true;
return;
}
*option.target = std::stoi(argv[i]);
found_arg = true;
try {
*option.target = std::stoi(argv[i]);
} catch (const std::invalid_argument&) {
invalid_arg = true;
}
found_arg = true;
}))
break;
@@ -312,8 +316,12 @@ bool parse_options(int argc, const char** argv, const std::vector<ArgOptions>& o
invalid_arg = true;
return;
}
*option.target = std::stof(argv[i]);
found_arg = true;
try {
*option.target = std::stof(argv[i]);
} catch (const std::invalid_argument&) {
invalid_arg = true;
}
found_arg = true;
}))
break;
@@ -337,7 +345,8 @@ bool parse_options(int argc, const char** argv, const std::vector<ArgOptions>& o
if (invalid_arg) {
if (!valid) {
LOG_ERROR("error: invalid parameter for argument: %s", arg.c_str());
LOG_ERROR("error: invalid parameter for argument \"%s\": \"%s\"",
arg.c_str(), (i >= argc) ? "" : argv[i]);
}
return false;
}
@@ -350,6 +359,25 @@ bool parse_options(int argc, const char** argv, const std::vector<ArgOptions>& o
return true;
}
static int parse_scale_override(int argc, const char** argv, int index, float& scale) {
if (++index >= argc) {
return -1;
}
try {
size_t end = 0;
const std::string value = argv[index];
float parsed = std::stof(value, &end);
if (end != value.size() || !std::isfinite(parsed) || parsed < 0.f ||
(parsed > 0.f && !std::isfinite(1.f / parsed))) {
return -1;
}
scale = parsed;
} catch (const std::exception&) {
return -1;
}
return 1;
}
ArgOptions SDContextParams::get_options() {
ArgOptions options;
options.string_options = {
@@ -678,11 +706,23 @@ ArgOptions SDContextParams::get_options() {
};
options.manual_options = {
{"",
"--linear-scale",
"linear input scale override (float, default: 0 = model default, 1 = no scaling)",
[this](int argc, const char** argv, int index) {
return parse_scale_override(argc, argv, index, linear_scale);
}},
{"",
"--attn-scale",
"flash-attention K/V scale override (float, default: 0 = model default, 1 = no scaling); requires --fa or --diffusion-fa",
[this](int argc, const char** argv, int index) {
return parse_scale_override(argc, argv, index, attn_scale);
}},
{"",
"--auto-fit",
"on|off (default: on). Use one GPU for diffusion/te/vae computation and place weights on that GPU, "
"on|off (default: on). Preserve --backend (otherwise select one GPU) and place weights on the compute GPU, "
"RAM, another GPU, or disk in that order, according to available memory (--max-vram limits GPU budgets). "
"Disabled by explicit --backend or --params-backend; uses automatic graph segmentation when needed",
"Disabled by explicit --params-backend; uses automatic graph segmentation when needed",
on_auto_fit_arg},
{"",
"--type",
@@ -886,6 +926,8 @@ std::string SDContextParams::to_string() const {
<< " vae_on_cpu: " << (vae_on_cpu ? "true" : "false") << ",\n"
<< " flash_attn: " << (flash_attn ? "true" : "false") << ",\n"
<< " diffusion_flash_attn: " << (diffusion_flash_attn ? "true" : "false") << ",\n"
<< " linear_scale: " << linear_scale << ",\n"
<< " attn_scale: " << attn_scale << ",\n"
<< " diffusion_conv_direct: " << (diffusion_conv_direct ? "true" : "false") << ",\n"
<< " vae_conv_direct: " << (vae_conv_direct ? "true" : "false") << ",\n"
<< " prediction: " << sd_prediction_name(prediction) << ",\n"
@@ -939,6 +981,8 @@ sd_ctx_params_t SDContextParams::to_sd_ctx_params_t(bool taesd_preview) {
sd_ctx_params.enable_mmap = enable_mmap;
sd_ctx_params.flash_attn = flash_attn;
sd_ctx_params.diffusion_flash_attn = diffusion_flash_attn;
sd_ctx_params.linear_scale = linear_scale;
sd_ctx_params.attn_scale = attn_scale;
sd_ctx_params.tae_preview_only = taesd_preview;
sd_ctx_params.diffusion_conv_direct = diffusion_conv_direct;
sd_ctx_params.vae_conv_direct = vae_conv_direct;
+2
View File
@@ -175,6 +175,8 @@ struct SDContextParams {
lora_apply_mode_t lora_apply_mode = LORA_APPLY_AUTO;
bool force_sdxl_vae_conv_scale = false;
float linear_scale = 0.f;
float attn_scale = 0.f;
float flow_shift = INFINITY;
ArgOptions get_options();
+3
View File
@@ -129,3 +129,6 @@ For detailed command-line arguments, run:
```bash
./bin/sd-server -h
```
For completely black or white images or videos, NaNs, and the `--linear-scale` /
`--attn-scale` startup options, see [Troubleshooting](../../docs/troubleshooting.md).
+6
View File
@@ -92,6 +92,7 @@ enum prediction_t {
FLUX_FLOW_PRED,
SEFI_FLOW_PRED,
MINIT2I_FLOW_PRED,
SENSENOVA_U1_FLOW_PRED,
PREDICTION_COUNT
};
@@ -240,6 +241,8 @@ typedef struct {
const char* rpc_servers;
const char* model_args;
bool disable_segmented_compute; // Force monolithic graph execution even when automatic graph cutting would fit memory better
float linear_scale; // Override linear input scaling; 0 keeps the model default
float attn_scale; // Override flash-attention K/V scaling; 0 keeps the model default
} sd_ctx_params_t;
typedef struct {
@@ -493,6 +496,9 @@ SD_API void free_sd_audio(sd_audio_t* audio);
SD_API void sd_sample_params_init(sd_sample_params_t* sample_params);
SD_API char* sd_sample_params_to_str(const sd_sample_params_t* sample_params);
// Requires a loaded context; returns a static string owned by the library, or "Unknown".
SD_API const char* sd_get_model_version_name(const sd_ctx_t* sd_ctx);
SD_API enum sample_method_t sd_get_default_sample_method(const sd_ctx_t* sd_ctx);
SD_API enum scheduler_t sd_get_default_scheduler(const sd_ctx_t* sd_ctx, enum sample_method_t sample_method);
+131 -2
View File
@@ -16,6 +16,7 @@
#include "model/te/llm.hpp"
#include "model/te/t5.hpp"
#include "model_loader.h"
#include "tokenizers/sensenova_u1_tokenizer.h"
struct SDCondition {
sd::Tensor<float> c_crossattn;
@@ -149,6 +150,7 @@ public:
virtual void set_graph_cut_layer_split_backend_vram_limits(const std::vector<size_t>& limits) {}
virtual void get_layer_split_param_tensors(std::map<std::string, ggml_tensor*>& tensors) {}
virtual void set_flash_attention_enabled(bool enabled) = 0;
virtual void set_scale_overrides(float linear_scale, float attn_scale) {}
virtual void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter) {}
virtual void runner_end() {}
};
@@ -231,6 +233,13 @@ struct FrozenCLIPEmbedderWithCustomWords : public Conditioner {
}
}
void set_scale_overrides(float linear_scale, float attn_scale) override {
text_model->set_scale_overrides(linear_scale, attn_scale);
if (sd_version_is_sdxl(version)) {
text_model2->set_scale_overrides(linear_scale, attn_scale);
}
}
void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter) override {
text_model->set_weight_adapter(adapter);
if (sd_version_is_sdxl(version)) {
@@ -736,6 +745,18 @@ struct SD3CLIPEmbedder : public Conditioner {
}
}
void set_scale_overrides(float linear_scale, float attn_scale) override {
if (clip_l) {
clip_l->set_scale_overrides(linear_scale, attn_scale);
}
if (clip_g) {
clip_g->set_scale_overrides(linear_scale, attn_scale);
}
if (t5) {
t5->set_scale_overrides(linear_scale, attn_scale);
}
}
void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter) override {
if (clip_l) {
clip_l->set_weight_adapter(adapter);
@@ -1106,6 +1127,15 @@ struct FluxCLIPEmbedder : public Conditioner {
}
}
void set_scale_overrides(float linear_scale, float attn_scale) override {
if (clip_l) {
clip_l->set_scale_overrides(linear_scale, attn_scale);
}
if (t5) {
t5->set_scale_overrides(linear_scale, attn_scale);
}
}
void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter) override {
if (clip_l) {
clip_l->set_weight_adapter(adapter);
@@ -1224,7 +1254,10 @@ struct FluxCLIPEmbedder : public Conditioner {
true,
clip_skip,
false);
GGML_ASSERT(!pooled.empty());
if (pooled.empty()) {
LOG_ERROR("Flux CLIP-L encoding failed");
return {};
}
} else {
pooled = sd::Tensor<float>::zeros({768});
}
@@ -1243,7 +1276,10 @@ struct FluxCLIPEmbedder : public Conditioner {
input_ids,
sd::Tensor<float>(),
false);
GGML_ASSERT(!chunk_hidden_states.empty());
if (chunk_hidden_states.empty()) {
LOG_ERROR("Flux T5 encoding failed at chunk %d/%zu", chunk_idx + 1, chunk_count);
return {};
}
chunk_hidden_states = ::apply_token_weights(std::move(chunk_hidden_states), chunk_weights);
if (zero_out_masked) {
chunk_hidden_states.fill_(0.0f);
@@ -1368,6 +1404,12 @@ struct T5CLIPEmbedder : public Conditioner {
}
}
void set_scale_overrides(float linear_scale, float attn_scale) override {
if (t5) {
t5->set_scale_overrides(linear_scale, attn_scale);
}
}
void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter) override {
if (t5) {
t5->set_weight_adapter(adapter);
@@ -1576,6 +1618,12 @@ struct MiniT2IConditioner : public Conditioner {
}
}
void set_scale_overrides(float linear_scale, float attn_scale) override {
if (t5) {
t5->set_scale_overrides(linear_scale, attn_scale);
}
}
void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter) override {
if (t5) {
t5->set_weight_adapter(adapter);
@@ -1623,6 +1671,71 @@ struct MiniT2IConditioner : public Conditioner {
}
};
struct SenseNovaU1Conditioner : public Conditioner {
static constexpr size_t kMaxPromptTokens = 12288;
SenseNovaU1Tokenizer tokenizer;
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors) override {
SD_UNUSED(tensors);
}
void set_flash_attention_enabled(bool enabled) override {
SD_UNUSED(enabled);
}
static std::string build_query(const std::string& text, bool is_negative) {
static const std::string kSystemMessage =
"You are an image generation and editing assistant that accurately understands and executes user intent.\n\n"
"You support two modes:\n\n1. Think Mode:\nIf the task requires reasoning, you MUST start with a "
"<think></think> block. Put all reasoning inside the block using plain text. DO NOT include any image tags. "
"Keep it reasonable and directly useful for producing the final image.\n\n2. Non-Think Mode:\nIf no reasoning "
"is needed, directly produce the final image.\n\nTask Types:\n\nA. Text-to-Image Generation:\n- Generate a "
"high-quality image based on the user's description.\n- Ensure visual clarity, semantic consistency, and "
"completeness.\n- DO NOT introduce elements that contradict or override the user's intent.\n\nB. Image Editing:\n"
"- Use the provided image(s) as input or reference for modification or transformation.\n- The result can be an "
"edited image or a new image based on the reference(s).\n- Preserve all unspecified attributes unless explicitly "
"changed.\n\nGeneral Rules:\n- For any visible text in the image, follow the language specified for the rendered "
"text in the user's description, not the language of the prompt. If no language is specified, use the user's input "
"language.";
std::string query;
if (!is_negative) {
query += "<|im_start|>system\n";
query += kSystemMessage;
query += "<|im_end|>\n";
}
query += "<|im_start|>user\n";
query += text;
query += "<|im_end|>\n<|im_start|>assistant\n";
query += is_negative ? "<img>" : "<think>\n\n</think>\n\n<img>";
return query;
}
SDCondition tokenize_condition(const std::string& text, bool is_negative) {
auto tokens = tokenizer.encode(build_query(text, is_negative));
if (tokens.empty() || tokens.size() > kMaxPromptTokens) {
LOG_ERROR("SenseNova U1.5 prompt token count %zu is outside [1, %zu]",
tokens.size(),
kMaxPromptTokens);
return {};
}
SDCondition result;
result.c_input_ids = sd::Tensor<int32_t>({static_cast<int64_t>(tokens.size())}, tokens);
return result;
}
SDCondition get_learned_condition(int n_threads,
const ConditionerParams& conditioner_params) override {
SD_UNUSED(n_threads);
return tokenize_condition(conditioner_params.text, false);
}
SDCondition get_unconditional_condition(const std::string& text) {
return tokenize_condition(text, true);
}
};
struct AnimaConditioner : public Conditioner {
std::shared_ptr<BPETokenizer> qwen_tokenizer;
T5UniGramTokenizer t5_tokenizer;
@@ -1672,6 +1785,10 @@ struct AnimaConditioner : public Conditioner {
llm->set_flash_attention_enabled(enabled);
}
void set_scale_overrides(float linear_scale, float attn_scale) override {
llm->set_scale_overrides(linear_scale, attn_scale);
}
void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter) override {
llm->set_weight_adapter(adapter);
}
@@ -1876,6 +1993,13 @@ struct LLMEmbedder : public Conditioner {
}
}
void set_scale_overrides(float linear_scale, float attn_scale) override {
llm->set_scale_overrides(linear_scale, attn_scale);
if (byt5) {
byt5->set_scale_overrides(linear_scale, attn_scale);
}
}
void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter) override {
if (llm) {
llm->set_weight_adapter(adapter);
@@ -2965,6 +3089,11 @@ struct LTXAVEmbedder : public Conditioner {
projector->set_flash_attention_enabled(enabled);
}
void set_scale_overrides(float linear_scale, float attn_scale) override {
llm->set_scale_overrides(linear_scale, attn_scale);
projector->set_scale_overrides(linear_scale, attn_scale);
}
void set_max_graph_vram_bytes(size_t max_vram_bytes) override {
llm->set_max_graph_vram_bytes(max_vram_bytes);
projector->set_max_graph_vram_bytes(max_vram_bytes);
+134 -39
View File
@@ -58,9 +58,15 @@ namespace sd::backend_fit {
size_t params_device = SIZE_MAX;
};
struct Runtime {
std::string name;
std::vector<size_t> devices;
};
struct Plan {
bool valid = false;
size_t main_device = SIZE_MAX;
std::vector<Runtime> runtimes;
std::vector<Decision> decisions;
};
@@ -121,11 +127,14 @@ namespace sd::backend_fit {
return name;
}
static std::vector<Device> enumerate_gpu_devices(const sd::ggml_graph_cut::MaxVramAssignment& budgets) {
static std::vector<Device> enumerate_gpu_devices(const sd::ggml_graph_cut::MaxVramAssignment& budgets,
bool include_other_devices) {
std::vector<Device> out;
for (size_t i = 0; i < ggml_backend_dev_count(); ++i) {
ggml_backend_dev_t dev = ggml_backend_dev_get(i);
if (ggml_backend_dev_type(dev) != GGML_BACKEND_DEVICE_TYPE_GPU) {
const auto type = ggml_backend_dev_type(dev);
if (type != GGML_BACKEND_DEVICE_TYPE_GPU &&
(!include_other_devices || type == GGML_BACKEND_DEVICE_TYPE_CPU)) {
continue;
}
Device device;
@@ -183,18 +192,29 @@ namespace sd::backend_fit {
return -1;
}
static Plan compute_plan(const std::vector<Component>& components,
const std::vector<Device>& devices,
int64_t ram_budget_bytes) {
Plan plan;
static size_t select_main_device(const std::vector<Device>& devices) {
size_t main_device = SIZE_MAX;
for (size_t di = 0; di < devices.size(); ++di) {
if (devices[di].budget_bytes > 0 &&
(plan.main_device == SIZE_MAX || devices[di].budget_bytes > devices[plan.main_device].budget_bytes)) {
plan.main_device = di;
(main_device == SIZE_MAX || devices[di].budget_bytes > devices[main_device].budget_bytes)) {
main_device = di;
}
}
if (plan.main_device == SIZE_MAX) {
return plan;
return main_device;
}
static Plan compute_plan(const std::vector<Component>& components,
const std::vector<Device>& devices,
int64_t ram_budget_bytes,
const std::vector<Runtime>& runtimes = {}) {
Plan plan;
plan.main_device = select_main_device(devices);
plan.runtimes = runtimes;
if (plan.runtimes.empty()) {
if (plan.main_device == SIZE_MAX) {
return plan;
}
plan.runtimes.resize(components.size(), {devices[plan.main_device].name, {plan.main_device}});
}
std::vector<size_t> order(components.size());
@@ -212,6 +232,27 @@ namespace sd::backend_fit {
ram_budget_bytes = std::max<int64_t>(ram_budget_bytes, 0);
plan.decisions.resize(components.size());
auto uses_device = [&](size_t ci, size_t di) {
const auto& runtime_devices = plan.runtimes[ci].devices;
return std::find(runtime_devices.begin(), runtime_devices.end(), di) != runtime_devices.end();
};
auto headroom_for = [&](size_t ci, size_t di) {
// Higher-priority offloaded weights need cache space on their compute devices.
int64_t headroom = 0;
for (size_t other = 0; other < components.size(); ++other) {
if (components[other].params_bytes == 0 || !uses_device(other, di)) {
continue;
}
const bool resident = other == ci || plan.decisions[other].params_location == ParamsLocation::MAIN_GPU;
const int64_t cached_weights = components[other].kind < components[ci].kind
? components[other].params_bytes
: components[other].staging_bytes;
headroom = std::max(headroom, components[other].reserve_bytes +
(resident ? 0 : cached_weights));
}
return headroom;
};
for (size_t ci : order) {
const Component& comp = components[ci];
Decision& decision = plan.decisions[ci];
@@ -219,24 +260,19 @@ namespace sd::backend_fit {
continue;
}
// Higher-priority offloaded weights need GPU cache space across graph runs.
int64_t headroom = 0;
for (size_t other = 0; other < components.size(); ++other) {
if (components[other].params_bytes == 0) {
continue;
}
const bool resident = other == ci || plan.decisions[other].params_location == ParamsLocation::MAIN_GPU;
const int64_t cached_weights = components[other].kind < comp.kind
? components[other].params_bytes
: components[other].staging_bytes;
headroom = std::max(headroom, components[other].reserve_bytes +
(resident ? 0 : cached_weights));
}
int64_t& main_remaining = remaining[plan.main_device];
if (headroom <= main_remaining && comp.params_bytes <= main_remaining - headroom) {
const auto& runtime_devices = plan.runtimes[ci].devices;
const bool fits_runtime = !runtime_devices.empty() &&
std::all_of(runtime_devices.begin(), runtime_devices.end(), [&](size_t di) {
const int64_t headroom = headroom_for(ci, di);
return headroom <= remaining[di] && comp.params_bytes <= remaining[di] - headroom;
});
if (fits_runtime) {
decision.params_location = ParamsLocation::MAIN_GPU;
decision.params_device = plan.main_device;
main_remaining -= comp.params_bytes;
decision.params_device = runtime_devices.front();
// Exact split allocations are unavailable until the runners build their plans.
for (size_t di : runtime_devices) {
remaining[di] -= comp.params_bytes;
}
continue;
}
if (comp.params_bytes <= ram_budget_bytes) {
@@ -244,10 +280,14 @@ namespace sd::backend_fit {
ram_budget_bytes -= comp.params_bytes;
continue;
}
if (runtime_devices.empty()) {
continue;
}
size_t best = SIZE_MAX;
for (size_t di = 0; di < devices.size(); ++di) {
if (di != plan.main_device && comp.params_bytes <= remaining[di] &&
const int64_t headroom = headroom_for(ci, di);
if (!uses_device(ci, di) && headroom <= remaining[di] && comp.params_bytes <= remaining[di] - headroom &&
(best == SIZE_MAX || remaining[di] > remaining[best])) {
best = di;
}
@@ -280,7 +320,7 @@ namespace sd::backend_fit {
const std::vector<Device>& devices,
int64_t free_ram,
int64_t ram_budget) {
LOG_INFO("auto-fit plan (single-GPU compute on %s):", devices[plan.main_device].name.c_str());
LOG_INFO("auto-fit plan:");
LOG_INFO(" devices:");
for (const Device& device : devices) {
LOG_INFO(" %-12s %-32s free %6lld MiB, budget %6lld MiB",
@@ -293,17 +333,19 @@ namespace sd::backend_fit {
LOG_INFO(" RAM free %6lld MiB, params budget %6lld MiB",
(long long)(free_ram / MiB), (long long)(ram_budget / MiB));
}
LOG_INFO(" main-GPU weight cache priority: diffusion > te > vae");
LOG_INFO(" components (params: main GPU -> RAM -> other GPU -> disk):");
LOG_INFO(" compute-device weight cache priority: diffusion > te > vae");
LOG_INFO(" components (params: compute device -> RAM -> other GPU -> disk):");
for (size_t ci = 0; ci < components.size(); ++ci) {
const Component& comp = components[ci];
if (comp.params_bytes == 0) {
continue;
}
const std::string params = params_backend_name(plan.decisions[ci], devices);
const std::string params = plan.decisions[ci].params_location == ParamsLocation::MAIN_GPU
? plan.runtimes[ci].name
: params_backend_name(plan.decisions[ci], devices);
LOG_INFO(" %-12s params %6lld MiB, compute reserve %5lld MiB -> compute %s, params %s",
comp.name, (long long)(comp.params_bytes / MiB), (long long)(comp.reserve_bytes / MiB),
devices[plan.main_device].name.c_str(), params.c_str());
plan.runtimes[ci].name.c_str(), params.c_str());
}
}
@@ -328,6 +370,51 @@ namespace sd::backend_fit {
return "";
}
static bool resolve_runtimes(const std::vector<Component>& components,
const std::vector<Device>& devices,
std::string& runtime_spec,
std::vector<Runtime>& runtimes,
std::string& error) {
SDBackendAssignment assignment;
if (!sd_parse_backend_assignment(runtime_spec, &assignment, &error)) {
return false;
}
const size_t main_device = select_main_device(devices);
const SDBackendModule modules[] = {SDBackendModule::DIFFUSION, SDBackendModule::TE, SDBackendModule::VAE};
for (const Component& comp : components) {
std::string name = assignment.get(modules[int(comp.kind)]);
if (name.empty()) {
name = main_device == SIZE_MAX ? "cpu" : devices[main_device].name;
if (comp.params_bytes > 0) {
append_assignment(runtime_spec, module_key(comp.kind), name);
}
}
Runtime runtime;
for (const std::string& part : split_string(name, '&')) {
if (trim(part).empty()) {
continue;
}
const std::string resolved = sd_backend_resolve_name(part);
if (resolved.empty()) {
error = "backend '" + part + "' was not found";
return false;
}
if (!runtime.name.empty()) {
runtime.name += "&";
}
runtime.name += resolved;
for (size_t di = 0; di < devices.size(); ++di) {
if (devices[di].name == resolved &&
std::find(runtime.devices.begin(), runtime.devices.end(), di) == runtime.devices.end()) {
runtime.devices.push_back(di);
}
}
}
runtimes.push_back(std::move(runtime));
}
return true;
}
bool derive_backend_specs(ModelLoader& loader,
ggml_type override_wtype,
sd::ggml_graph_cut::MaxVramAssignment& budgets,
@@ -339,12 +426,18 @@ namespace sd::backend_fit {
return false;
}
const auto components = estimate_components(loader, override_wtype);
const auto devices = enumerate_gpu_devices(budgets);
// Resolve once to ensure dynamic backends are loaded before enumerating devices.
sd_backend_resolve_name("");
const auto components = estimate_components(loader, override_wtype);
const auto devices = enumerate_gpu_devices(budgets, !runtime_spec.empty());
std::vector<Runtime> runtimes;
if (!runtime_spec.empty() && !resolve_runtimes(components, devices, runtime_spec, runtimes, error)) {
LOG_ERROR("%s", error.c_str());
return false;
}
const int64_t free_ram = available_ram_bytes();
const int64_t ram_budget = std::max<int64_t>(free_ram - std::max<int64_t>(2048 * MiB, free_ram / 10), 0);
const auto plan = compute_plan(components, devices, ram_budget);
runtime_spec.clear();
const auto plan = compute_plan(components, devices, ram_budget, runtimes);
params_spec.clear();
if (!plan.valid) {
if (devices.empty()) {
@@ -362,7 +455,9 @@ namespace sd::backend_fit {
continue;
}
const char* key = module_key(components[ci].kind);
append_assignment(runtime_spec, key, devices[plan.main_device].name);
if (runtimes.empty()) {
append_assignment(runtime_spec, key, plan.runtimes[ci].name);
}
if (plan.decisions[ci].params_location != ParamsLocation::MAIN_GPU) {
append_assignment(params_spec, key, params_backend_name(plan.decisions[ci], devices));
}
@@ -389,7 +484,7 @@ namespace sd::backend_fit {
tiling_params.temporal_tiling = true;
retry_mode = tiling_params.enabled ? "spatial+temporal" : "temporal";
} else if (!tiling_params.enabled) {
tiling_params.enabled = true;
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) {
+18 -4
View File
@@ -2,12 +2,14 @@
#include <algorithm>
#include <cstring>
#include <exception>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include "core/ggml_extend_backend.h"
#include "core/ggml_graph_cut.h"
#include "core/util.h"
#include "ggml-cpu.h"
#include "ggml/src/ggml-impl.h"
@@ -228,11 +230,23 @@ namespace sd {
}
}
void ComputeWorkspace::segment_end() {
if (active_) {
synchronize();
active_ = false;
bool ComputeWorkspace::segment_end() noexcept {
if (!active_) {
return true;
}
// Outer cleanup guards must not retry a failed backend submission.
active_ = false;
try {
synchronize();
return true;
} catch (const std::exception& error) {
LOG_ERROR("%s workspace synchronization failed during segment cleanup: %s",
ggml_backend_name(backend_), error.what());
} catch (...) {
LOG_ERROR("%s workspace synchronization failed during segment cleanup: unknown exception",
ggml_backend_name(backend_));
}
return false;
}
bool ComputeWorkspace::release() {
+1 -1
View File
@@ -51,7 +51,7 @@ namespace sd {
const std::function<ggml_backend_t(const ggml_tensor*)>& external_backend,
const AssignNodes& assign_nodes);
void synchronize() const;
void segment_end();
bool segment_end() noexcept;
bool release();
bool active() const { return active_; }
ggml_backend_sched_t scheduler() const { return scheduler_; }
+8 -2
View File
@@ -593,7 +593,7 @@ static ggml_backend_t sd_get_default_backend() {
return backend;
}
static bool sd_parse_backend_assignment(const std::string& spec, SDBackendAssignment* assignment, std::string* error) {
bool sd_parse_backend_assignment(const std::string& spec, SDBackendAssignment* assignment, std::string* error) {
if (assignment == nullptr) {
return false;
}
@@ -660,7 +660,13 @@ void SDBackendAssignment::set_module(SDBackendModule module, const std::string&
}
void SDBackendHandleDeleter::operator()(ggml_backend_t backend) const {
ggml_backend_free(backend);
try {
ggml_backend_free(backend);
} catch (const std::exception& error) {
LOG_ERROR("backend cleanup failed: %s", error.what());
} catch (...) {
LOG_ERROR("backend cleanup failed: unknown exception");
}
}
SDBackendManager::~SDBackendManager() {
+1
View File
@@ -93,6 +93,7 @@ ggml_status sd_backend_graph_compute_with_eval_callback(ggml_backend_t backend,
sd_graph_eval_callback_t callback_eval,
void* callback_eval_user_data);
std::string sd_backend_resolve_name(const std::string& name);
bool sd_parse_backend_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);
+14 -2
View File
@@ -482,6 +482,16 @@ namespace sd::ggml_graph_cut {
return ggml_nbytes(cache_src);
}
static bool can_ignore_op_params(ggml_op op) {
// Exempt only parameters that cannot affect graph layout or backend allocation size.
switch (op) {
case GGML_OP_SCALE:
return true;
default:
return false;
}
}
std::vector<uint64_t> graph_layout(ggml_cgraph* graph, bool include_bindings) {
std::vector<const ggml_tensor*> tensors;
std::unordered_map<const ggml_tensor*, size_t> indices;
@@ -530,8 +540,10 @@ namespace sd::ggml_graph_cut {
for (auto source : tensor->src) {
signature.push_back(source == nullptr ? 0 : indices.at(source));
}
for (int value : tensor->op_params) {
signature.push_back(static_cast<uint32_t>(value));
if (!can_ignore_op_params(tensor->op)) {
for (int value : tensor->op_params) {
signature.push_back(static_cast<uint32_t>(value));
}
}
}
return signature;
+31 -2
View File
@@ -1,7 +1,9 @@
#include <algorithm>
#include <exception>
#include <map>
#include <utility>
#include "core/ggml_extend.h"
#include "core/ggml_extend_backend.h"
#include "core/ggml_runner.h"
#include "core/ggml_tensor_utils.h"
@@ -11,6 +13,21 @@
using namespace sd;
ggml_tensor* ggml_ext_attention_ext(GGMLRunnerContext* ctx,
ggml_tensor* q,
ggml_tensor* k,
ggml_tensor* v,
int64_t n_head,
ggml_tensor* mask,
bool skip_reshape,
bool flash_attn,
float kv_scale) {
if (ctx->attn_scale > 0.f) {
kv_scale = ctx->attn_scale;
}
return ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, q, k, v, n_head, mask, skip_reshape, flash_attn, kv_scale);
}
void GGMLRunner::alloc_params_ctx() {
ggml_init_params params;
params.mem_size = static_cast<size_t>(MAX_PARAMS_TENSOR_NUM * ggml_tensor_overhead());
@@ -510,6 +527,8 @@ GGMLRunnerContext GGMLRunner::get_context() {
runner_ctx.ggml_ctx = compute_ctx;
runner_ctx.backend = runtime_backend;
runner_ctx.flash_attn_enabled = flash_attn_enabled;
runner_ctx.linear_scale = linear_scale;
runner_ctx.attn_scale = attn_scale;
runner_ctx.conv2d_direct_enabled = conv2d_direct_enabled;
runner_ctx.circular_x_enabled = circular_x_enabled;
runner_ctx.circular_y_enabled = circular_y_enabled;
@@ -624,8 +643,15 @@ std::optional<sd::Tensor<float>> GGMLRunner::compute(get_graph_cb_t get_graph,
params_tensor_set_.insert(parameter);
}
}
auto output = execute_graph(graph, n_threads, no_return, read_outputs);
success = output.has_value();
std::optional<sd::Tensor<float>> output;
try {
output = execute_graph(graph, n_threads, no_return, read_outputs);
} catch (const std::exception& error) {
LOG_ERROR("%s graph execution failed on %s: %s", get_desc().c_str(),
ggml_backend_name(runtime_backend), error.what());
return std::nullopt;
}
success = output.has_value();
if (success) {
cache_.graph_end(true);
}
@@ -938,6 +964,9 @@ std::optional<Tensor<float>> GGMLRunner::execute_graph(ggml_cgraph* graph, int n
}
}
}
if (!workspace_.segment_end()) {
return fail_segment("workspace synchronization");
}
// Final outputs and their callbacks may still be views of consumed cuts.
cut_cache_.prune(segment.future_cut_names);
}
+19
View File
@@ -68,6 +68,8 @@ struct GGMLRunnerContext {
ggml_backend_t backend = nullptr;
ggml_context* ggml_ctx = nullptr;
bool flash_attn_enabled = false;
float linear_scale = 0.f;
float attn_scale = 0.f;
bool conv2d_direct_enabled = false;
bool circular_x_enabled = false;
bool circular_y_enabled = false;
@@ -113,6 +115,16 @@ struct GGMLRunnerContext {
}
};
ggml_tensor* ggml_ext_attention_ext(GGMLRunnerContext* ctx,
ggml_tensor* q,
ggml_tensor* k,
ggml_tensor* v,
int64_t n_head,
ggml_tensor* mask = nullptr,
bool skip_reshape = false,
bool flash_attn = false,
float kv_scale = 1.f);
struct GGMLRunner {
private:
std::map<ggml_backend_t, size_t> logged_compute_bytes_;
@@ -163,6 +175,8 @@ protected:
const std::string final_result_name = "ggml_runner_final_result_tensor";
bool flash_attn_enabled = false;
float linear_scale = 0.f;
float attn_scale = 0.f;
bool conv2d_direct_enabled = false;
bool circular_x_enabled = false;
bool circular_y_enabled = false;
@@ -323,6 +337,11 @@ public:
flash_attn_enabled = enabled;
}
void set_scale_overrides(float linear_scale, float attn_scale) {
this->linear_scale = linear_scale;
this->attn_scale = attn_scale;
}
void set_conv2d_direct_enabled(bool enabled) {
conv2d_direct_enabled = enabled;
}
+1
View File
@@ -135,6 +135,7 @@ struct PhotoMakerExtension : public GenerationExtension {
pm_version,
20.f,
ctx.model_manager);
pmid_model->set_scale_overrides(ctx.params->linear_scale, ctx.params->attn_scale);
if (pm_version == PM_VERSION_2) {
LOG_INFO("using PhotoMaker Version 2");
}
+7 -1
View File
@@ -57,6 +57,7 @@ enum SDVersion {
VERSION_SEFI_IMAGE,
VERSION_KREA2,
VERSION_MAGE_FLOW,
VERSION_SENSENOVA_U1_5,
VERSION_ESRGAN,
VERSION_COUNT,
};
@@ -237,6 +238,10 @@ static inline bool sd_version_is_mage_flow(SDVersion version) {
return version == VERSION_MAGE_FLOW;
}
static inline bool sd_version_is_sensenova_u1(SDVersion version) {
return version == VERSION_SENSENOVA_U1_5;
}
static inline bool sd_version_uses_flux_vae(SDVersion version) {
if (sd_version_is_flux(version) || sd_version_is_z_image(version) || sd_version_is_boogu_image(version) || sd_version_is_longcat(version)) {
return true;
@@ -295,7 +300,8 @@ static inline bool sd_version_is_dit(SDVersion version) {
sd_version_is_ideogram4(version) ||
sd_version_is_sefi_image(version) ||
sd_version_is_krea2(version) ||
sd_version_is_mage_flow(version)) {
sd_version_is_mage_flow(version) ||
sd_version_is_sensenova_u1(version)) {
return true;
}
return false;
+1 -1
View File
@@ -95,7 +95,7 @@ namespace IPAdapter {
int64_t L = kv->ne[1];
ggml_tensor* k = ggml_cont(ctx->ggml_ctx, ggml_view_3d(ctx->ggml_ctx, kv, dim, L, N, kv->nb[1], kv->nb[2], 0));
ggml_tensor* v = ggml_cont(ctx->ggml_ctx, ggml_view_3d(ctx->ggml_ctx, kv, dim, L, N, kv->nb[1], kv->nb[2], dim * kv->nb[0]));
ggml_tensor* attn = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, q, k, v, heads, nullptr, false, false);
ggml_tensor* attn = ggml_ext_attention_ext(ctx, q, k, v, heads, nullptr, false, false);
attn = to_out->forward(ctx, attn);
latents = ggml_add(ctx->ggml_ctx, latents, attn);
+5 -6
View File
@@ -63,12 +63,11 @@ public:
k = ggml_cont(ctx->ggml_ctx, k);
v = ggml_cont(ctx->ggml_ctx, v);
ggml_tensor* attn_out = ggml_ext_attention_ext(
ctx->ggml_ctx, ctx->backend,
q, k, v,
heads,
/*mask=*/nullptr,
/*diag_mask_inf=*/false);
ggml_tensor* attn_out = ggml_ext_attention_ext(ctx,
q, k, v,
heads,
/*mask=*/nullptr,
/*diag_mask_inf=*/false);
ggml_tensor* out = to_out->forward(ctx, attn_out);
return out;
+2 -2
View File
@@ -380,14 +380,14 @@ public:
if (xtra_dim) {
context->ne[0] = 320; // reset dim to orig
}
x = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, q, k, v, n_head, nullptr, false, ctx->flash_attn_enabled); // [N, n_token, inner_dim]
x = ggml_ext_attention_ext(ctx, q, k, v, n_head, nullptr, false, ctx->flash_attn_enabled); // [N, n_token, inner_dim]
if (has_ip && ctx->ip_context != nullptr && ctx->ip_scale != 0.0f) {
auto to_k_ip = std::dynamic_pointer_cast<Linear>(blocks["to_k_ip"]);
auto to_v_ip = std::dynamic_pointer_cast<Linear>(blocks["to_v_ip"]);
auto k_ip = to_k_ip->forward(ctx, ctx->ip_context);
auto v_ip = to_v_ip->forward(ctx, ctx->ip_context);
auto x_ip = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, q, k_ip, v_ip, n_head, nullptr, false, ctx->flash_attn_enabled);
auto x_ip = ggml_ext_attention_ext(ctx, q, k_ip, v_ip, n_head, nullptr, false, ctx->flash_attn_enabled);
x = ggml_add(ctx->ggml_ctx, x, ggml_scale(ctx->ggml_ctx, x_ip, ctx->ip_scale));
}
+2 -1
View File
@@ -206,6 +206,7 @@ public:
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) override {
ggml_tensor* w = params["weight"];
const float scale = ctx->linear_scale > 0.f ? ctx->linear_scale : this->scale;
ggml_tensor* weight_scale = has_weight_scale ? params["weight_scale"] : nullptr;
if (w->type == GGML_TYPE_F8_E4M3 || w->type == GGML_TYPE_F8_E5M2) {
bool supports_fp8_matmul = false;
@@ -870,7 +871,7 @@ public:
v = v_proj->forward(ctx, x);
}
x = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, q, k, v, n_head, mask, false); // [N, n_token, embed_dim]
x = ggml_ext_attention_ext(ctx, q, k, v, n_head, mask, false); // [N, n_token, embed_dim]
x = out_proj->forward(ctx, x); // [N, n_token, embed_dim]
return x;
+1 -1
View File
@@ -1024,7 +1024,7 @@ namespace Rope {
q = apply_rope(ctx->ggml_ctx, q, pe, rope_interleaved); // [N*n_head, L, d_head]
k = apply_rope(ctx->ggml_ctx, k, pe, rope_interleaved); // [N*n_head, L, d_head]
auto x = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, q, k, v, n_head, mask, true, ctx->flash_attn_enabled, kv_scale); // [N, L, n_head*d_head]
auto x = ggml_ext_attention_ext(ctx, q, k, v, n_head, mask, true, ctx->flash_attn_enabled, kv_scale); // [N, L, n_head*d_head]
return x;
}
}; // namespace Rope
+2 -4
View File
@@ -237,8 +237,7 @@ namespace Anima {
}
auto q_rope = Rope::apply_rope(ctx->ggml_ctx, q4, pe_q, false);
auto k_rope = Rope::apply_rope(ctx->ggml_ctx, k4, pe_k, false);
attn_out = ggml_ext_attention_ext(ctx->ggml_ctx,
ctx->backend,
attn_out = ggml_ext_attention_ext(ctx,
q_rope,
k_rope,
v4,
@@ -249,8 +248,7 @@ namespace Anima {
} else {
auto q_flat = ggml_reshape_3d(ctx->ggml_ctx, q4, head_dim * num_heads, L_q, N);
auto k_flat = ggml_reshape_3d(ctx->ggml_ctx, k4, head_dim * num_heads, L_k, N);
attn_out = ggml_ext_attention_ext(ctx->ggml_ctx,
ctx->backend,
attn_out = ggml_ext_attention_ext(ctx,
q_flat,
k_flat,
v,
+1 -1
View File
@@ -61,7 +61,7 @@ namespace AnimateDiff {
auto k = to_k->forward(ctx, x_pe);
auto v = to_v->forward(ctx, x_pe);
auto a = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, q, k, v, (int)num_heads, nullptr, false);
auto a = ggml_ext_attention_ext(ctx, q, k, v, (int)num_heads, nullptr, false);
return to_out->forward(ctx, a);
}
};
+1 -1
View File
@@ -183,7 +183,7 @@ namespace ErnieImage {
k = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, k, 0, 2, 1, 3)); // [N, heads, S, head_dim]
k = ggml_reshape_3d(ctx->ggml_ctx, k, k->ne[0], k->ne[1], k->ne[2] * k->ne[3]);
x = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, q, k, v, num_heads, attention_mask, true, ctx->flash_attn_enabled); // [N, S, hidden_size]
x = ggml_ext_attention_ext(ctx, q, k, v, num_heads, attention_mask, true, ctx->flash_attn_enabled); // [N, S, hidden_size]
x = to_out_0->forward(ctx, x);
return x;
}
+4
View File
@@ -504,6 +504,10 @@ namespace HiDreamO1 {
vision_runner->set_flash_attention_enabled(enabled);
}
void set_scale_overrides(float linear_scale, float attn_scale) override {
vision_runner->set_scale_overrides(linear_scale, attn_scale);
}
void set_weight_adapter(const std::shared_ptr<WeightAdapter>& adapter) override {
vision_runner->set_weight_adapter(adapter);
}
+1 -1
View File
@@ -54,7 +54,7 @@ namespace Hunyuan {
auto k = qkv_vec[1];
auto v = qkv_vec[2];
auto attn_out = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, q, k, v, num_heads, mask, false, ctx->flash_attn_enabled);
auto attn_out = ggml_ext_attention_ext(ctx, q, k, v, num_heads, mask, false, ctx->flash_attn_enabled);
attn_out = self_attn_proj->forward(ctx, attn_out);
// adaLN_modulation
+1 -2
View File
@@ -232,8 +232,7 @@ namespace Krea2 {
q = ggml_reshape_3d(ctx->ggml_ctx, ggml_cont(ctx->ggml_ctx, q), head_dim_ * heads, Lq, N);
k = ggml_reshape_3d(ctx->ggml_ctx, ggml_cont(ctx->ggml_ctx, k), head_dim_ * kv_heads, Lk, N);
v = ggml_reshape_3d(ctx->ggml_ctx, ggml_cont(ctx->ggml_ctx, v), head_dim_ * kv_heads, Lk, N);
return ggml_ext_attention_ext(ctx->ggml_ctx,
ctx->backend,
return ggml_ext_attention_ext(ctx,
q,
k,
v,
+1 -2
View File
@@ -709,8 +709,7 @@ namespace LTXV {
k = apply_hidden_rope(ctx->ggml_ctx, k, k_pe, heads, dim_head, rope_interleaved);
}
auto out = ggml_ext_attention_ext(ctx->ggml_ctx,
ctx->backend,
auto out = ggml_ext_attention_ext(ctx,
q,
k,
v,
+1 -2
View File
@@ -215,8 +215,7 @@ namespace MiniMaxH3 {
q = attention_layout(ctx->ggml_ctx, q);
k = attention_layout(ctx->ggml_ctx, k);
}
auto out = ggml_ext_attention_ext(ctx->ggml_ctx,
ctx->backend,
auto out = ggml_ext_attention_ext(ctx,
q,
k,
v,
+7 -7
View File
@@ -365,8 +365,8 @@ public:
ggml_tensor* forward(GGMLRunnerContext* ctx,
ggml_tensor* x) {
auto qkv = pre_attention(ctx, x);
x = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, qkv[0], qkv[1], qkv[2], num_heads, nullptr, false, ctx->flash_attn_enabled); // [N, n_token, dim]
x = post_attention(ctx, x); // [N, n_token, dim]
x = ggml_ext_attention_ext(ctx, qkv[0], qkv[1], qkv[2], num_heads, nullptr, false, ctx->flash_attn_enabled); // [N, n_token, dim]
x = post_attention(ctx, x); // [N, n_token, dim]
return x;
}
};
@@ -587,8 +587,8 @@ public:
auto qkv2 = std::get<1>(qkv_intermediates);
auto intermediates = std::get<2>(qkv_intermediates);
auto attn_out = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, qkv[0], qkv[1], qkv[2], num_heads, nullptr, false, ctx->flash_attn_enabled); // [N, n_token, dim]
auto attn2_out = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, qkv2[0], qkv2[1], qkv2[2], num_heads, nullptr, false, ctx->flash_attn_enabled); // [N, n_token, dim]
auto attn_out = ggml_ext_attention_ext(ctx, qkv[0], qkv[1], qkv[2], num_heads, nullptr, false, ctx->flash_attn_enabled); // [N, n_token, dim]
auto attn2_out = ggml_ext_attention_ext(ctx, qkv2[0], qkv2[1], qkv2[2], num_heads, nullptr, false, ctx->flash_attn_enabled); // [N, n_token, dim]
x = post_attention_x(ctx,
attn_out,
attn2_out,
@@ -604,7 +604,7 @@ public:
auto qkv = qkv_intermediates.first;
auto intermediates = qkv_intermediates.second;
auto attn_out = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, qkv[0], qkv[1], qkv[2], num_heads, nullptr, false, ctx->flash_attn_enabled); // [N, n_token, dim]
auto attn_out = ggml_ext_attention_ext(ctx, qkv[0], qkv[1], qkv[2], num_heads, nullptr, false, ctx->flash_attn_enabled); // [N, n_token, dim]
x = post_attention(ctx,
attn_out,
intermediates[0],
@@ -648,7 +648,7 @@ block_mixing(GGMLRunnerContext* ctx,
qkv.push_back(ggml_concat(ctx->ggml_ctx, context_qkv[i], x_qkv[i], 1));
}
auto attn = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, qkv[0], qkv[1], qkv[2], x_block->num_heads, nullptr, false, ctx->flash_attn_enabled); // [N, n_context + n_token, hidden_size]
auto attn = ggml_ext_attention_ext(ctx, qkv[0], qkv[1], qkv[2], x_block->num_heads, nullptr, false, ctx->flash_attn_enabled); // [N, n_context + n_token, hidden_size]
auto context_attn = ggml_view_3d(ctx->ggml_ctx,
attn,
@@ -680,7 +680,7 @@ block_mixing(GGMLRunnerContext* ctx,
}
if (x_block->self_attn) {
auto attn2 = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, x_qkv2[0], x_qkv2[1], x_qkv2[2], x_block->num_heads, nullptr, false, ctx->flash_attn_enabled); // [N, n_token, hidden_size]
auto attn2 = ggml_ext_attention_ext(ctx, x_qkv2[0], x_qkv2[1], x_qkv2[2], x_block->num_heads, nullptr, false, ctx->flash_attn_enabled); // [N, n_token, hidden_size]
x = x_block->post_attention_x(ctx,
x_attn,
+5
View File
@@ -114,6 +114,10 @@ struct MiniT2IDiffusionExtra {
const sd::Tensor<float>* mask = nullptr;
};
struct SenseNovaU1DiffusionExtra {
const sd::Tensor<int32_t>* input_ids = nullptr;
};
struct HunyuanVideoDiffusionExtra {
const sd::Tensor<float>* guidance = nullptr;
const sd::Tensor<float>* byt5 = nullptr;
@@ -131,6 +135,7 @@ using DiffusionExtraParams = std::variant<std::monostate,
LTXAVDiffusionExtra,
MiniMaxH3DiffusionExtra,
MiniT2IDiffusionExtra,
SenseNovaU1DiffusionExtra,
HunyuanVideoDiffusionExtra>;
struct DiffusionParams {
+846
View File
@@ -0,0 +1,846 @@
#ifndef __SD_MODEL_DIFFUSION_SENSENOVA_U1_H__
#define __SD_MODEL_DIFFUSION_SENSENOVA_U1_H__
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <cstdlib>
#include <memory>
#include <string>
#include <unordered_set>
#include <vector>
#include "core/ggml_extend.h"
#include "model/diffusion/dit.hpp"
#include "model/diffusion/model.hpp"
#include "model/te/llm.hpp"
#include "model_loader.h"
namespace SenseNovaU1 {
constexpr int SENSENOVA_U1_GRAPH_SIZE = 327680;
struct SenseNovaU1Config {
int64_t hidden_size = 4096;
int64_t intermediate_size = 12288;
int64_t num_layers = 42;
int64_t num_heads = 32;
int64_t num_kv_heads = 8;
int64_t head_dim = 128;
int64_t vocab_size = 151936;
int64_t max_position_embeddings = 262144;
int64_t max_position_embeddings_hw = 10000;
int64_t vision_hidden_size = 1024;
int64_t patch_size = 16;
int64_t vision_downsample_factor = 2;
int64_t in_channels = 3;
int64_t timestep_embedding_size = 256;
float rms_norm_eps = 1e-6f;
float rope_theta = 5000000.f;
float rope_theta_hw = 10000.f;
float noise_scale_base_image_seq_len = 64.f;
float noise_scale_max_value = 16.f;
float t_eps = 0.02f;
bool add_noise_scale_embedding = true;
int64_t image_token_stride() const {
return patch_size * vision_downsample_factor;
}
static SenseNovaU1Config detect_from_weights(const String2TensorStorage& tensor_storage_map,
const std::string& prefix) {
SenseNovaU1Config config;
config.num_layers = 0;
const std::string root = prefix.empty() ? "" : prefix + ".";
for (const auto& [name, tensor_storage] : tensor_storage_map) {
if (!starts_with(name, root)) {
continue;
}
if (ends_with(name, "language_model.model.embed_tokens.weight") && tensor_storage.n_dims == 2) {
config.hidden_size = tensor_storage.ne[0];
config.vocab_size = tensor_storage.ne[1];
} else if (ends_with(name, "language_model.model.layers.0.mlp.gate_proj.weight") && tensor_storage.n_dims == 2) {
config.intermediate_size = tensor_storage.ne[1];
} else if (ends_with(name, "language_model.model.layers.0.self_attn.q_proj.weight") && tensor_storage.n_dims == 2) {
config.num_heads = tensor_storage.ne[1] / config.head_dim;
} else if (ends_with(name, "language_model.model.layers.0.self_attn.k_proj.weight") && tensor_storage.n_dims == 2) {
config.num_kv_heads = tensor_storage.ne[1] / config.head_dim;
} else if (ends_with(name, "fm_modules.vision_model_mot_gen.embeddings.patch_embedding.weight") && tensor_storage.n_dims == 4) {
config.patch_size = tensor_storage.ne[0];
config.in_channels = tensor_storage.ne[2];
config.vision_hidden_size = tensor_storage.ne[3];
} else if (ends_with(name, "fm_modules.vision_model_mot_gen.embeddings.dense_embedding.weight") && tensor_storage.n_dims == 4) {
config.vision_downsample_factor = tensor_storage.ne[0];
}
const std::string layer_prefix = root + "language_model.model.layers.";
if (starts_with(name, layer_prefix)) {
const char* index_begin = name.c_str() + layer_prefix.size();
config.num_layers = std::max<int64_t>(config.num_layers, std::strtoll(index_begin, nullptr, 10) + 1);
}
}
if (config.num_layers == 0) {
config.num_layers = 42;
}
config.add_noise_scale_embedding = tensor_storage_map.find(root + "fm_modules.noise_scale_embedder.mlp.0.weight") != tensor_storage_map.end();
LOG_DEBUG("sensenova-u1.5: layers=%" PRId64 ", hidden=%" PRId64 ", intermediate=%" PRId64 ", heads=%" PRId64 ", kv_heads=%" PRId64 ", patch=%" PRId64 "x%" PRId64,
config.num_layers,
config.hidden_size,
config.intermediate_size,
config.num_heads,
config.num_kv_heads,
config.patch_size,
config.vision_downsample_factor);
return config;
}
};
class StorageConv2d : public Conv2d {
protected:
void init_params(ggml_context* ctx,
const String2TensorStorage& tensor_storage_map = {},
const std::string prefix = "") override {
this->prefix = prefix;
ggml_type wtype = get_type(prefix + "weight", tensor_storage_map, GGML_TYPE_F16);
params["weight"] = ggml_new_tensor_4d(ctx,
wtype,
kernel_size.second,
kernel_size.first,
in_channels,
out_channels);
if (bias) {
params["bias"] = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, out_channels);
}
}
public:
StorageConv2d(int64_t in_channels,
int64_t out_channels,
std::pair<int, int> kernel_size,
std::pair<int, int> stride = {1, 1},
std::pair<int, int> padding = {0, 0},
bool bias = true)
: Conv2d(in_channels,
out_channels,
kernel_size,
stride,
padding,
{1, 1},
bias) {}
};
struct TimestepEmbedder : public GGMLBlock {
int64_t frequency_embedding_size;
TimestepEmbedder(int64_t hidden_size, int64_t frequency_embedding_size = 256)
: frequency_embedding_size(frequency_embedding_size) {
blocks["mlp.0"] = std::make_shared<Linear>(frequency_embedding_size, hidden_size, true);
blocks["mlp.2"] = std::make_shared<Linear>(hidden_size, hidden_size, true);
}
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* timesteps) {
auto mlp_0 = std::dynamic_pointer_cast<Linear>(blocks["mlp.0"]);
auto mlp_2 = std::dynamic_pointer_cast<Linear>(blocks["mlp.2"]);
auto x = ggml_ext_timestep_embedding(ctx->ggml_ctx,
timesteps,
static_cast<int>(frequency_embedding_size),
10000.f,
1.f);
x = mlp_0->forward(ctx, x);
x = ggml_silu_inplace(ctx->ggml_ctx, x);
return mlp_2->forward(ctx, x);
}
};
inline ggml_tensor* apply_vision_rope(GGMLRunnerContext* ctx,
ggml_tensor* x,
ggml_tensor* position_x,
ggml_tensor* position_y,
float theta,
int max_position) {
GGML_ASSERT(x->ne[0] % 2 == 0);
// ggml_rope_ext addresses positions through ne[2]. The vision
// embeddings arrive as [hidden, tokens, batch], so add the singleton
// head axis used by the RoPE kernel: [hidden, 1, tokens, batch].
x = ggml_reshape_4d(ctx->ggml_ctx, x, x->ne[0], 1, x->ne[1], x->ne[2]);
const int64_t half = x->ne[0] / 2;
auto x_part = ggml_ext_slice(ctx->ggml_ctx, x, 0, 0, half);
auto y_part = ggml_ext_slice(ctx->ggml_ctx, x, 0, half, x->ne[0]);
x_part = ggml_rope_ext(ctx->ggml_ctx,
x_part,
position_x,
nullptr,
static_cast<int>(half),
GGML_ROPE_TYPE_NORMAL,
max_position,
theta,
1.f,
0.f,
1.f,
32.f,
1.f);
y_part = ggml_rope_ext(ctx->ggml_ctx,
y_part,
position_y,
nullptr,
static_cast<int>(half),
GGML_ROPE_TYPE_NORMAL,
max_position,
theta,
1.f,
0.f,
1.f,
32.f,
1.f);
return ggml_concat(ctx->ggml_ctx, x_part, y_part, 0);
}
struct VisionEmbeddings : public GGMLBlock {
SenseNovaU1Config config;
explicit VisionEmbeddings(const SenseNovaU1Config& config)
: config(config) {
blocks["patch_embedding"] = std::make_shared<StorageConv2d>(config.in_channels,
config.vision_hidden_size,
std::pair<int, int>{static_cast<int>(config.patch_size), static_cast<int>(config.patch_size)},
std::pair<int, int>{static_cast<int>(config.patch_size), static_cast<int>(config.patch_size)},
std::pair<int, int>{0, 0},
true);
blocks["dense_embedding"] = std::make_shared<StorageConv2d>(config.vision_hidden_size,
config.hidden_size,
std::pair<int, int>{static_cast<int>(config.vision_downsample_factor), static_cast<int>(config.vision_downsample_factor)},
std::pair<int, int>{static_cast<int>(config.vision_downsample_factor), static_cast<int>(config.vision_downsample_factor)},
std::pair<int, int>{0, 0},
true);
}
ggml_tensor* forward(GGMLRunnerContext* ctx,
ggml_tensor* image,
ggml_tensor* position_x,
ggml_tensor* position_y) {
auto patch_embedding = std::dynamic_pointer_cast<StorageConv2d>(blocks["patch_embedding"]);
auto dense_embedding = std::dynamic_pointer_cast<StorageConv2d>(blocks["dense_embedding"]);
auto x = patch_embedding->forward(ctx, image);
x = ggml_gelu_erf(ctx->ggml_ctx, x);
const int64_t grid_w = x->ne[0];
const int64_t grid_h = x->ne[1];
const int64_t batch = x->ne[3];
x = ggml_reshape_3d(ctx->ggml_ctx, x, grid_w * grid_h, x->ne[2], batch);
x = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, x, 1, 0, 2, 3));
x = apply_vision_rope(ctx,
x,
position_x,
position_y,
config.rope_theta_hw,
static_cast<int>(config.max_position_embeddings_hw));
x = ggml_reshape_4d(ctx->ggml_ctx, x, config.vision_hidden_size, grid_w, grid_h, batch);
x = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, x, 2, 0, 1, 3));
x = dense_embedding->forward(ctx, x);
const int64_t token_w = x->ne[0];
const int64_t token_h = x->ne[1];
x = ggml_reshape_3d(ctx->ggml_ctx, x, token_w * token_h, x->ne[2], x->ne[3]);
return ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, x, 1, 0, 2, 3));
}
};
inline ggml_tensor* pixel_shuffle(GGMLRunnerContext* ctx,
ggml_tensor* x,
int upscale_factor) {
GGML_ASSERT(upscale_factor > 0);
const int64_t h = x->ne[1];
const int64_t w = x->ne[0];
GGML_ASSERT(x->ne[2] % (upscale_factor * upscale_factor) == 0);
x = ggml_ext_cont(ctx->ggml_ctx,
ggml_ext_torch_permute(ctx->ggml_ctx, x, 2, 0, 1, 3));
x = ggml_reshape_3d(ctx->ggml_ctx, x, x->ne[0], x->ne[1] * x->ne[2], x->ne[3]);
return DiT::unpatchify(ctx->ggml_ctx, x, h, w, upscale_factor, upscale_factor, true);
}
struct PixelDecoder : public GGMLBlock {
explicit PixelDecoder(const SenseNovaU1Config& config) {
blocks["conv1"] = std::make_shared<StorageConv2d>(config.hidden_size / 4,
1024,
std::pair<int, int>{3, 3},
std::pair<int, int>{1, 1},
std::pair<int, int>{1, 1},
true);
blocks["conv2"] = std::make_shared<StorageConv2d>(256,
192,
std::pair<int, int>{3, 3},
std::pair<int, int>{1, 1},
std::pair<int, int>{1, 1},
true);
}
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) {
auto conv1 = std::dynamic_pointer_cast<StorageConv2d>(blocks["conv1"]);
auto conv2 = std::dynamic_pointer_cast<StorageConv2d>(blocks["conv2"]);
x = pixel_shuffle(ctx, x, 2);
x = conv1->forward(ctx, x);
x = ggml_gelu_erf(ctx->ggml_ctx, x);
x = pixel_shuffle(ctx, x, 2);
x = conv2->forward(ctx, x);
return pixel_shuffle(ctx, x, 8);
}
};
enum class Branch {
UNDERSTANDING,
GENERATION,
};
struct Attention : public GGMLBlock {
SenseNovaU1Config config;
int layer_index;
Attention(const SenseNovaU1Config& config, int layer_index)
: config(config), layer_index(layer_index) {
blocks["q_proj"] = std::make_shared<Linear>(config.hidden_size, config.num_heads * config.head_dim, false);
blocks["k_proj"] = std::make_shared<Linear>(config.hidden_size, config.num_kv_heads * config.head_dim, false);
blocks["v_proj"] = std::make_shared<Linear>(config.hidden_size, config.num_kv_heads * config.head_dim, false);
blocks["o_proj"] = std::make_shared<Linear>(config.num_heads * config.head_dim, config.hidden_size, false);
blocks["q_proj_mot_gen"] = std::make_shared<Linear>(config.hidden_size, config.num_heads * config.head_dim, false);
blocks["k_proj_mot_gen"] = std::make_shared<Linear>(config.hidden_size, config.num_kv_heads * config.head_dim, false);
blocks["v_proj_mot_gen"] = std::make_shared<Linear>(config.hidden_size, config.num_kv_heads * config.head_dim, false);
blocks["o_proj_mot_gen"] = std::make_shared<Linear>(config.num_heads * config.head_dim, config.hidden_size, false);
const int64_t axis_dim = config.head_dim / 2;
blocks["q_norm"] = std::make_shared<LLM::LLMRMSNorm>(axis_dim, config.rms_norm_eps);
blocks["k_norm"] = std::make_shared<LLM::LLMRMSNorm>(axis_dim, config.rms_norm_eps);
blocks["q_norm_hw"] = std::make_shared<LLM::LLMRMSNorm>(axis_dim, config.rms_norm_eps);
blocks["k_norm_hw"] = std::make_shared<LLM::LLMRMSNorm>(axis_dim, config.rms_norm_eps);
blocks["q_norm_mot_gen"] = std::make_shared<LLM::LLMRMSNorm>(axis_dim, config.rms_norm_eps);
blocks["k_norm_mot_gen"] = std::make_shared<LLM::LLMRMSNorm>(axis_dim, config.rms_norm_eps);
blocks["q_norm_hw_mot_gen"] = std::make_shared<LLM::LLMRMSNorm>(axis_dim, config.rms_norm_eps);
blocks["k_norm_hw_mot_gen"] = std::make_shared<LLM::LLMRMSNorm>(axis_dim, config.rms_norm_eps);
}
ggml_tensor* apply_axis_rope(GGMLRunnerContext* ctx,
ggml_tensor* x,
ggml_tensor* positions,
int dimensions,
float theta,
int max_position) {
return ggml_rope_ext(ctx->ggml_ctx,
x,
positions,
nullptr,
dimensions,
GGML_ROPE_TYPE_NEOX,
max_position,
theta,
1.f,
0.f,
1.f,
32.f,
1.f);
}
ggml_tensor* normalize_and_rotate(GGMLRunnerContext* ctx,
ggml_tensor* x,
ggml_tensor* position_t,
ggml_tensor* position_h,
ggml_tensor* position_w,
const std::string& norm_name,
const std::string& norm_hw_name) {
const int64_t temporal_dim = config.head_dim / 2;
const int64_t spatial_dim = config.head_dim - temporal_dim;
const int64_t axis_dim = spatial_dim / 2;
auto temporal = ggml_ext_slice(ctx->ggml_ctx, x, 0, 0, temporal_dim);
auto spatial = ggml_ext_slice(ctx->ggml_ctx, x, 0, temporal_dim, config.head_dim);
temporal = std::dynamic_pointer_cast<LLM::LLMRMSNorm>(blocks[norm_name])->forward(ctx, temporal);
spatial = std::dynamic_pointer_cast<LLM::LLMRMSNorm>(blocks[norm_hw_name])->forward(ctx, spatial);
auto height = ggml_ext_slice(ctx->ggml_ctx, spatial, 0, 0, axis_dim);
auto width = ggml_ext_slice(ctx->ggml_ctx, spatial, 0, axis_dim, spatial_dim);
temporal = apply_axis_rope(ctx,
temporal,
position_t,
static_cast<int>(temporal_dim),
config.rope_theta,
static_cast<int>(config.max_position_embeddings));
height = apply_axis_rope(ctx,
height,
position_h,
static_cast<int>(axis_dim),
config.rope_theta_hw,
static_cast<int>(config.max_position_embeddings_hw));
width = apply_axis_rope(ctx,
width,
position_w,
static_cast<int>(axis_dim),
config.rope_theta_hw,
static_cast<int>(config.max_position_embeddings_hw));
return ggml_concat(ctx->ggml_ctx,
ggml_concat(ctx->ggml_ctx, temporal, height, 0),
width,
0);
}
ggml_tensor* forward(GGMLRunnerContext* ctx,
ggml_tensor* x,
ggml_tensor* position_t,
ggml_tensor* position_h,
ggml_tensor* position_w,
ggml_tensor* attention_mask,
Branch branch,
const std::string& cache_prefix) {
const bool generation = branch == Branch::GENERATION;
const std::string suffix = generation ? "_mot_gen" : "";
auto q_proj = std::dynamic_pointer_cast<Linear>(blocks["q_proj" + suffix]);
auto k_proj = std::dynamic_pointer_cast<Linear>(blocks["k_proj" + suffix]);
auto v_proj = std::dynamic_pointer_cast<Linear>(blocks["v_proj" + suffix]);
auto o_proj = std::dynamic_pointer_cast<Linear>(blocks["o_proj" + suffix]);
const int64_t n_tokens = x->ne[1];
const int64_t batch = x->ne[2];
auto q = ggml_reshape_4d(ctx->ggml_ctx,
q_proj->forward(ctx, x),
config.head_dim,
config.num_heads,
n_tokens,
batch);
auto k = ggml_reshape_4d(ctx->ggml_ctx,
k_proj->forward(ctx, x),
config.head_dim,
config.num_kv_heads,
n_tokens,
batch);
auto v = ggml_reshape_4d(ctx->ggml_ctx,
v_proj->forward(ctx, x),
config.head_dim,
config.num_kv_heads,
n_tokens,
batch);
q = normalize_and_rotate(ctx,
q,
position_t,
position_h,
position_w,
"q_norm" + suffix,
"q_norm_hw" + suffix);
k = normalize_and_rotate(ctx,
k,
position_t,
position_h,
position_w,
"k_norm" + suffix,
"k_norm_hw" + suffix);
const std::string layer_cache = cache_prefix + "." + std::to_string(layer_index);
if (generation) {
auto prefix_k = ctx->load_cache_tensor(layer_cache + ".k");
auto prefix_v = ctx->load_cache_tensor(layer_cache + ".v");
GGML_ASSERT(prefix_k != nullptr && prefix_v != nullptr);
k = ggml_concat(ctx->ggml_ctx, prefix_k, k, 2);
v = ggml_concat(ctx->ggml_ctx, prefix_v, v, 2);
} else {
// Keep dedicated graph outputs alive until the runner copies them
// into its persistent cache buffer after graph execution.
auto cache_k = ggml_dup_tensor(ctx->ggml_ctx, k);
cache_k = ggml_cpy(ctx->ggml_ctx, k, cache_k);
ggml_set_output(cache_k);
auto cache_v = ggml_dup_tensor(ctx->ggml_ctx, v);
cache_v = ggml_cpy(ctx->ggml_ctx, v, cache_v);
ggml_set_output(cache_v);
ctx->persist_cache_tensor(layer_cache + ".k", cache_k);
ctx->persist_cache_tensor(layer_cache + ".v", cache_v);
}
q = ggml_cont(ctx->ggml_ctx,
ggml_ext_torch_permute(ctx->ggml_ctx, q, 0, 2, 1, 3));
q = ggml_reshape_3d(ctx->ggml_ctx, q, q->ne[0], q->ne[1], q->ne[2] * q->ne[3]);
k = ggml_cont(ctx->ggml_ctx,
ggml_ext_torch_permute(ctx->ggml_ctx, k, 0, 2, 1, 3));
k = ggml_reshape_3d(ctx->ggml_ctx, k, k->ne[0], k->ne[1], k->ne[2] * k->ne[3]);
auto out = ggml_ext_attention_ext(ctx->ggml_ctx,
ctx->backend,
q,
k,
v,
config.num_heads,
attention_mask,
true,
ctx->flash_attn_enabled);
return o_proj->forward(ctx, out);
}
};
struct TransformerBlock : public GGMLBlock {
TransformerBlock(const SenseNovaU1Config& config, int layer_index) {
blocks["self_attn"] = std::make_shared<Attention>(config, layer_index);
blocks["mlp"] = std::make_shared<LLM::MLP>(config.hidden_size, config.intermediate_size, false);
blocks["mlp_mot_gen"] = std::make_shared<LLM::MLP>(config.hidden_size, config.intermediate_size, false);
blocks["input_layernorm"] = std::make_shared<LLM::LLMRMSNorm>(config.hidden_size, config.rms_norm_eps);
blocks["input_layernorm_mot_gen"] = std::make_shared<LLM::LLMRMSNorm>(config.hidden_size, config.rms_norm_eps);
blocks["post_attention_layernorm"] = std::make_shared<LLM::LLMRMSNorm>(config.hidden_size, config.rms_norm_eps);
blocks["post_attention_layernorm_mot_gen"] = std::make_shared<LLM::LLMRMSNorm>(config.hidden_size, config.rms_norm_eps);
}
ggml_tensor* forward(GGMLRunnerContext* ctx,
ggml_tensor* x,
ggml_tensor* position_t,
ggml_tensor* position_h,
ggml_tensor* position_w,
ggml_tensor* attention_mask,
Branch branch,
const std::string& cache_prefix) {
const bool generation = branch == Branch::GENERATION;
auto input_norm = std::dynamic_pointer_cast<LLM::LLMRMSNorm>(
blocks[generation ? "input_layernorm_mot_gen" : "input_layernorm"]);
auto post_norm = std::dynamic_pointer_cast<LLM::LLMRMSNorm>(
blocks[generation ? "post_attention_layernorm_mot_gen" : "post_attention_layernorm"]);
auto attention = std::dynamic_pointer_cast<Attention>(blocks["self_attn"]);
auto mlp = std::dynamic_pointer_cast<LLM::MLP>(blocks[generation ? "mlp_mot_gen" : "mlp"]);
auto residual = x;
x = input_norm->forward(ctx, x);
x = attention->forward(ctx,
x,
position_t,
position_h,
position_w,
attention_mask,
branch,
cache_prefix);
x = ggml_add_inplace(ctx->ggml_ctx, x, residual);
residual = x;
x = post_norm->forward(ctx, x);
x = mlp->forward(ctx, x);
return ggml_add_inplace(ctx->ggml_ctx, x, residual);
}
};
struct TextModel : public GGMLBlock {
SenseNovaU1Config config;
explicit TextModel(const SenseNovaU1Config& config)
: config(config) {
blocks["embed_tokens"] = std::make_shared<Embedding>(config.vocab_size, config.hidden_size);
for (int i = 0; i < config.num_layers; ++i) {
blocks["layers." + std::to_string(i)] = std::make_shared<TransformerBlock>(config, i);
}
blocks["norm"] = std::make_shared<LLM::LLMRMSNorm>(config.hidden_size, config.rms_norm_eps);
blocks["norm_mot_gen"] = std::make_shared<LLM::LLMRMSNorm>(config.hidden_size, config.rms_norm_eps);
}
ggml_tensor* embed(GGMLRunnerContext* ctx, ggml_tensor* input_ids) {
return std::dynamic_pointer_cast<Embedding>(blocks["embed_tokens"])->forward(ctx, input_ids);
}
ggml_tensor* forward(GGMLRunnerContext* ctx,
ggml_tensor* x,
ggml_tensor* position_t,
ggml_tensor* position_h,
ggml_tensor* position_w,
ggml_tensor* attention_mask,
Branch branch,
const std::string& cache_prefix) {
for (int i = 0; i < config.num_layers; ++i) {
auto layer = std::dynamic_pointer_cast<TransformerBlock>(blocks["layers." + std::to_string(i)]);
x = layer->forward(ctx,
x,
position_t,
position_h,
position_w,
attention_mask,
branch,
cache_prefix);
}
auto norm = std::dynamic_pointer_cast<LLM::LLMRMSNorm>(
blocks[branch == Branch::GENERATION ? "norm_mot_gen" : "norm"]);
return norm->forward(ctx, x);
}
};
struct SenseNovaU1Model : public GGMLBlock {
SenseNovaU1Config config;
explicit SenseNovaU1Model(const SenseNovaU1Config& config)
: config(config) {
blocks["language_model.model"] = std::make_shared<TextModel>(config);
blocks["fm_modules.vision_model_mot_gen.embeddings"] = std::make_shared<VisionEmbeddings>(config);
blocks["fm_modules.timestep_embedder"] = std::make_shared<TimestepEmbedder>(config.hidden_size,
config.timestep_embedding_size);
if (config.add_noise_scale_embedding) {
blocks["fm_modules.noise_scale_embedder"] = std::make_shared<TimestepEmbedder>(config.hidden_size,
config.timestep_embedding_size);
}
blocks["fm_modules.fm_head"] = std::make_shared<PixelDecoder>(config);
}
std::shared_ptr<TextModel> text_model() {
return std::dynamic_pointer_cast<TextModel>(blocks["language_model.model"]);
}
std::shared_ptr<VisionEmbeddings> vision_embeddings() {
return std::dynamic_pointer_cast<VisionEmbeddings>(blocks["fm_modules.vision_model_mot_gen.embeddings"]);
}
std::shared_ptr<TimestepEmbedder> timestep_embedder() {
return std::dynamic_pointer_cast<TimestepEmbedder>(blocks["fm_modules.timestep_embedder"]);
}
std::shared_ptr<TimestepEmbedder> noise_scale_embedder() {
if (!config.add_noise_scale_embedding) {
return nullptr;
}
return std::dynamic_pointer_cast<TimestepEmbedder>(blocks["fm_modules.noise_scale_embedder"]);
}
std::shared_ptr<PixelDecoder> pixel_decoder() {
return std::dynamic_pointer_cast<PixelDecoder>(blocks["fm_modules.fm_head"]);
}
};
struct SenseNovaU1Runner : public DiffusionModelRunner {
SenseNovaU1Config config;
SenseNovaU1Model model;
std::unordered_set<uint64_t> cached_prefix_hashes;
std::vector<int32_t> position_t_vec;
std::vector<int32_t> position_h_vec;
std::vector<int32_t> position_w_vec;
std::vector<float> attention_mask_vec;
std::vector<float> noise_scale_vec;
SenseNovaU1Runner(ggml_backend_t backend,
const String2TensorStorage& tensor_storage_map = {},
const std::string& prefix = "",
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
: DiffusionModelRunner(backend, prefix, weight_manager),
config(SenseNovaU1Config::detect_from_weights(tensor_storage_map, prefix)),
model(config) {
model.init(params_ctx, tensor_storage_map, prefix);
}
std::string get_desc() override {
return "SenseNova U1.5";
}
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors,
const std::string& prefix) override {
model.get_param_tensors(tensors, prefix);
}
static uint64_t hash_input_ids(const sd::Tensor<int32_t>& input_ids) {
uint64_t hash = 1469598103934665603ULL;
for (int32_t token : input_ids.values()) {
uint32_t value = static_cast<uint32_t>(token);
for (int byte = 0; byte < 4; ++byte) {
hash ^= static_cast<uint8_t>(value & 0xffU);
hash *= 1099511628211ULL;
value >>= 8;
}
}
hash ^= static_cast<uint64_t>(input_ids.numel());
hash *= 1099511628211ULL;
return hash;
}
static std::string cache_prefix(uint64_t hash) {
return "snu15." + std::to_string(hash);
}
ggml_tensor* make_position_tensor(const std::vector<int32_t>& values,
const std::string& name) {
auto tensor = ggml_new_tensor_1d(compute_ctx, GGML_TYPE_I32, values.size());
ggml_set_name(tensor, name.c_str());
set_backend_tensor_data(tensor, values.data());
return tensor;
}
ggml_cgraph* build_prefix_graph(const sd::Tensor<int32_t>& input_ids_tensor,
const std::string& prefix_cache) {
ggml_cgraph* graph = new_graph_custom(SENSENOVA_U1_GRAPH_SIZE);
ggml_tensor* ids = make_input(input_ids_tensor);
const int64_t length = input_ids_tensor.numel();
position_t_vec.resize(length);
position_h_vec.assign(length, 0);
position_w_vec.assign(length, 0);
for (int64_t i = 0; i < length; ++i) {
position_t_vec[i] = static_cast<int32_t>(i);
}
auto position_t = make_position_tensor(position_t_vec, "snu15.prefix.position_t");
auto position_h = make_position_tensor(position_h_vec, "snu15.prefix.position_h");
auto position_w = make_position_tensor(position_w_vec, "snu15.prefix.position_w");
attention_mask_vec.assign(static_cast<size_t>(length * length), 0.f);
for (int64_t query = 0; query < length; ++query) {
for (int64_t key = query + 1; key < length; ++key) {
attention_mask_vec[static_cast<size_t>(query * length + key)] = -INFINITY;
}
}
auto attention_mask = ggml_new_tensor_2d(compute_ctx,
GGML_TYPE_F32,
length,
length);
ggml_set_name(attention_mask, "snu15.prefix.attention_mask");
set_backend_tensor_data(attention_mask, attention_mask_vec.data());
auto runner_ctx = get_context();
auto text_model = model.text_model();
auto hidden = text_model->embed(&runner_ctx, ids);
hidden = text_model->forward(&runner_ctx,
hidden,
position_t,
position_h,
position_w,
attention_mask,
Branch::UNDERSTANDING,
prefix_cache);
ggml_build_forward_expand(graph, hidden);
return graph;
}
bool ensure_prefix_cache(int n_threads,
const sd::Tensor<int32_t>& input_ids,
std::string* prefix_cache) {
const uint64_t hash = hash_input_ids(input_ids);
*prefix_cache = cache_prefix(hash);
if (cached_prefix_hashes.find(hash) != cached_prefix_hashes.end() &&
get_cache_tensor_by_name(*prefix_cache + ".0.k") != nullptr) {
return true;
}
if (cached_prefix_hashes.size() >= 2) {
free_cache_ctx_and_buffer();
cached_prefix_hashes.clear();
}
auto get_graph = [&]() {
return build_prefix_graph(input_ids, *prefix_cache);
};
auto result = GGMLRunner::compute(get_graph, n_threads, false, true);
if (!result.has_value()) {
LOG_ERROR("SenseNova U1.5 prefix cache computation failed");
return false;
}
cached_prefix_hashes.insert(hash);
return true;
}
ggml_cgraph* build_graph(const sd::Tensor<float>& x_tensor,
const sd::Tensor<float>& timestep_tensor,
const std::string& prefix_cache,
int64_t prefix_length) {
ggml_cgraph* graph = new_graph_custom(SENSENOVA_U1_GRAPH_SIZE);
ggml_tensor* x = make_input(x_tensor);
ggml_tensor* t = make_input(timestep_tensor);
GGML_ASSERT(x->ne[3] == 1);
GGML_ASSERT(x->ne[0] % config.image_token_stride() == 0);
GGML_ASSERT(x->ne[1] % config.image_token_stride() == 0);
const int64_t grid_w = x->ne[0] / config.patch_size;
const int64_t grid_h = x->ne[1] / config.patch_size;
const int64_t token_w = grid_w / config.vision_downsample_factor;
const int64_t token_h = grid_h / config.vision_downsample_factor;
const int64_t tokens = token_w * token_h;
position_h_vec.resize(grid_w * grid_h);
position_w_vec.resize(grid_w * grid_h);
for (int64_t index = 0; index < grid_w * grid_h; ++index) {
position_h_vec[index] = static_cast<int32_t>(index / grid_w);
position_w_vec[index] = static_cast<int32_t>(index % grid_w);
}
auto vision_position_x = make_position_tensor(position_w_vec, "snu15.vision.position_x");
auto vision_position_y = make_position_tensor(position_h_vec, "snu15.vision.position_y");
auto runner_ctx = get_context();
auto hidden = model.vision_embeddings()->forward(&runner_ctx,
x,
vision_position_x,
vision_position_y);
auto time_embedding = model.timestep_embedder()->forward(&runner_ctx, t);
time_embedding = ggml_reshape_3d(compute_ctx, time_embedding, config.hidden_size, 1, 1);
hidden = ggml_add(compute_ctx, hidden, time_embedding);
if (config.add_noise_scale_embedding) {
const float image_tokens = static_cast<float>(tokens);
const float noise_scale = std::min(config.noise_scale_max_value,
std::sqrt(image_tokens / config.noise_scale_base_image_seq_len));
noise_scale_vec = {noise_scale / config.noise_scale_max_value};
auto noise_scale_tensor = ggml_new_tensor_1d(compute_ctx, GGML_TYPE_F32, 1);
ggml_set_name(noise_scale_tensor, "snu15.noise_scale");
set_backend_tensor_data(noise_scale_tensor, noise_scale_vec.data());
auto noise_embedding = model.noise_scale_embedder()->forward(&runner_ctx, noise_scale_tensor);
noise_embedding = ggml_reshape_3d(compute_ctx, noise_embedding, config.hidden_size, 1, 1);
hidden = ggml_add(compute_ctx, hidden, noise_embedding);
}
position_t_vec.assign(tokens, static_cast<int32_t>(prefix_length));
position_h_vec.resize(tokens);
position_w_vec.resize(tokens);
for (int64_t index = 0; index < tokens; ++index) {
position_h_vec[index] = static_cast<int32_t>(index / token_w);
position_w_vec[index] = static_cast<int32_t>(index % token_w);
}
auto position_t = make_position_tensor(position_t_vec, "snu15.image.position_t");
auto position_h = make_position_tensor(position_h_vec, "snu15.image.position_h");
auto position_w = make_position_tensor(position_w_vec, "snu15.image.position_w");
hidden = model.text_model()->forward(&runner_ctx,
hidden,
position_t,
position_h,
position_w,
nullptr,
Branch::GENERATION,
prefix_cache);
hidden = ggml_reshape_4d(compute_ctx,
hidden,
config.hidden_size,
token_w,
token_h,
x->ne[3]);
hidden = ggml_cont(compute_ctx, ggml_permute(compute_ctx, hidden, 2, 0, 1, 3));
auto x_prediction = model.pixel_decoder()->forward(&runner_ctx, hidden);
const float timestep = timestep_tensor.values()[0];
const float denom = std::max(1.f - timestep, config.t_eps);
auto velocity = ggml_scale(compute_ctx,
ggml_sub(compute_ctx, x_prediction, x),
1.f / denom);
ggml_build_forward_expand(graph, velocity);
return graph;
}
sd::Tensor<float> compute(int n_threads,
const sd::Tensor<float>& x,
const sd::Tensor<float>& timestep,
const sd::Tensor<int32_t>& input_ids) {
std::string prefix_cache;
if (!ensure_prefix_cache(n_threads, input_ids, &prefix_cache)) {
return {};
}
auto get_graph = [&]() {
return build_graph(x, timestep, prefix_cache, input_ids.numel());
};
return restore_trailing_singleton_dims(
GGMLRunner::compute(get_graph, n_threads, false),
x.dim());
}
sd::Tensor<float> compute(int n_threads,
const DiffusionParams& diffusion_params) override {
GGML_ASSERT(diffusion_params.x != nullptr);
GGML_ASSERT(diffusion_params.timesteps != nullptr);
const auto* extra = diffusion_extra_as<SenseNovaU1DiffusionExtra>(diffusion_params);
GGML_ASSERT(extra->input_ids != nullptr);
return compute(n_threads,
*diffusion_params.x,
*diffusion_params.timesteps,
*extra->input_ids);
}
};
} // namespace SenseNovaU1
#endif // __SD_MODEL_DIFFUSION_SENSENOVA_U1_H__
+3 -3
View File
@@ -193,7 +193,7 @@ namespace WAN {
k = norm_k->forward(ctx, k);
auto v = v_proj->forward(ctx, context); // [N, n_context, dim]
x = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, q, k, v, num_heads, nullptr, false, ctx->flash_attn_enabled); // [N, n_token, dim]
x = ggml_ext_attention_ext(ctx, q, k, v, num_heads, nullptr, false, ctx->flash_attn_enabled); // [N, n_token, dim]
x = o_proj->forward(ctx, x); // [N, n_token, dim]
return x;
@@ -255,8 +255,8 @@ namespace WAN {
k_img = norm_k_img->forward(ctx, k_img);
auto v_img = v_img_proj->forward(ctx, context_img); // [N, context_img_len, dim]
auto img_x = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, q, k_img, v_img, num_heads, nullptr, false, ctx->flash_attn_enabled); // [N, n_token, dim]
x = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, q, k, v, num_heads, nullptr, false, ctx->flash_attn_enabled); // [N, n_token, dim]
auto img_x = ggml_ext_attention_ext(ctx, q, k_img, v_img, num_heads, nullptr, false, ctx->flash_attn_enabled); // [N, n_token, dim]
x = ggml_ext_attention_ext(ctx, q, k, v, num_heads, nullptr, false, ctx->flash_attn_enabled); // [N, n_token, dim]
x = ggml_add(ctx->ggml_ctx, x, img_x);
+1 -1
View File
@@ -1359,7 +1359,7 @@ namespace LLM {
x = ggml_ext_cont(ctx->ggml_ctx, kqv);
x = ggml_reshape_3d(ctx->ggml_ctx, x, head_dim * num_heads, n_token, N);
} else {
x = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, q, k, v, num_heads, attention_mask, true, false); // [N, n_token, hidden_size]
x = ggml_ext_attention_ext(ctx, q, k, v, num_heads, attention_mask, true, false); // [N, n_token, hidden_size]
}
x = out_proj->forward(ctx, x); // [N, n_token, hidden_size]
+1 -1
View File
@@ -251,7 +251,7 @@ public:
k = ggml_ext_scale(ctx->ggml_ctx, k, ::sqrtf(static_cast<float>(d_head)), true);
x = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, q, k, v, num_heads, mask); // [N, n_token, d_head * n_head]
x = ggml_ext_attention_ext(ctx, q, k, v, num_heads, mask); // [N, n_token, d_head * n_head]
x = out_proj->forward(ctx, x); // [N, n_token, model_dim]
return {x, past_bias};
+1 -1
View File
@@ -142,7 +142,7 @@ public:
v = ggml_reshape_3d(ctx->ggml_ctx, v, c, h * w, n); // [N, h * w, in_channels]
}
h_ = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, q, k, v, 1, nullptr, false, ctx->flash_attn_enabled);
h_ = ggml_ext_attention_ext(ctx, q, k, v, 1, nullptr, false, ctx->flash_attn_enabled);
if (use_linear) {
h_ = proj_out->forward(ctx, h_); // [N, h * w, in_channels]
+1 -1
View File
@@ -193,7 +193,7 @@ namespace Hunyuan {
v = ggml_reshape_3d(ctx->ggml_ctx, v, w * h * t, c, b); // [b, c, t*h*w]
v = ggml_ext_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, v, 1, 0, 2, 3)); // [b, t*h*w, c]
x = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, q, k, v, 1, nullptr, false, ctx->flash_attn_enabled); // [b, t*h*w, c]
x = ggml_ext_attention_ext(ctx, q, k, v, 1, nullptr, false, ctx->flash_attn_enabled); // [b, t*h*w, c]
x = ggml_ext_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, x, 1, 0, 2, 3)); // [b, c, t*h*w]
x = ggml_reshape_4d(ctx->ggml_ctx, x, w, h, t, c * b); // [b*c, t, h, w]
+1 -1
View File
@@ -253,7 +253,7 @@ namespace MageVAE {
q = to_patches(ctx->ggml_ctx, q);
k = to_patches(ctx->ggml_ctx, k);
v = to_patches(ctx->ggml_ctx, v);
h = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, q, k, v, 1, nullptr, false, ctx->flash_attn_enabled);
h = ggml_ext_attention_ext(ctx, q, k, v, 1, nullptr, false, ctx->flash_attn_enabled);
h = from_patches(ctx->ggml_ctx, h, np, batch, hp, wp);
if (pad_h > 0) {
h = ggml_ext_slice(ctx->ggml_ctx, h, 1, 0, height);
+1 -2
View File
@@ -174,8 +174,7 @@ namespace MiniMaxH3 {
auto mask = ggml_diag_mask_inf(ctx->ggml_ctx,
ggml_ext_zeros(ctx->ggml_ctx, sequence, sequence, 1, 1),
0);
auto attn_out = ggml_ext_attention_ext(ctx->ggml_ctx,
ctx->backend,
auto attn_out = ggml_ext_attention_ext(ctx,
q,
k,
v,
+1 -2
View File
@@ -291,8 +291,7 @@ namespace MiniMaxH3VAE {
k = ggml_rms_norm(ctx->ggml_ctx, k, 1e-5f);
q = apply_partial_rope(ctx->ggml_ctx, q, pe);
k = apply_partial_rope(ctx->ggml_ctx, k, pe);
auto out = ggml_ext_attention_ext(ctx->ggml_ctx,
ctx->backend,
auto out = ggml_ext_attention_ext(ctx,
q,
k,
v,
+1 -1
View File
@@ -166,7 +166,7 @@ public:
scale_factor = 16;
} else if (sd_version_uses_flux2_vae(version)) {
scale_factor = 16;
} else if (version == VERSION_CHROMA_RADIANCE || version == VERSION_HIDREAM_O1 || sd_version_is_minit2i(version)) {
} else if (version == VERSION_CHROMA_RADIANCE || version == VERSION_HIDREAM_O1 || sd_version_is_minit2i(version) || sd_version_is_sensenova_u1(version)) {
scale_factor = 1;
}
return scale_factor;
+2 -2
View File
@@ -615,8 +615,8 @@ namespace WAN {
auto v = qkv_vec[2];
v = ggml_reshape_3d(ctx->ggml_ctx, v, h * w, c, n); // [t, c, h * w]
v = ggml_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, v, 1, 0, 2, 3)); // [t, h * w, c]
x = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, q, k, v, 1, nullptr, false, ctx->flash_attn_enabled); // [t, h * w, c]
v = ggml_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, v, 1, 0, 2, 3)); // [t, h * w, c]
x = ggml_ext_attention_ext(ctx, q, k, v, 1, nullptr, false, ctx->flash_attn_enabled); // [t, h * w, c]
x = ggml_ext_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, x, 1, 0, 2, 3)); // [t, c, h * w]
x = ggml_reshape_4d(ctx->ggml_ctx, x, w, h, c, n); // [t, c, h, w]
+14
View File
@@ -69,6 +69,8 @@ const char* unused_tensors[] = {
// "v_pred", // Used to detect SDXL vpred models
"text_encoders.llm.output.weight",
"text_encoders.llm.lm_head.",
"language_model.lm_head.",
"vision_model.",
};
bool is_unused_tensor(const std::string& name) {
@@ -185,6 +187,15 @@ bool ModelLoader::parse_file(const std::string& file_path, const std::string& pr
}
parsed_dependencies_.push_back(stamp);
if (is_directory(file_path)) {
const std::string diffusers_index_path = path_join(file_path, "model_index.json");
const std::string diffusers_unet_path = path_join(file_path, "unet/diffusion_pytorch_model.safetensors");
const bool has_diffusers_layout = file_exists(diffusers_index_path) || file_exists(diffusers_unet_path);
const std::string safetensors_index_path = path_join(file_path, "model.safetensors.index.json");
if (!has_diffusers_layout && file_exists(safetensors_index_path)) {
LOG_INFO("load %s using root safetensors index", file_path.c_str());
return parse_file(safetensors_index_path, prefix);
}
LOG_INFO("load %s using diffusers format", file_path.c_str());
return init_from_diffusers_file(file_path, prefix);
} else if (is_gguf_file(file_path)) {
@@ -463,6 +474,9 @@ SDVersion ModelLoader::get_sd_version() const {
if (tensor_storage.name.find("net.img_embedder.proj1.weight") != std::string::npos) {
return VERSION_MINIT2I;
}
if (tensor_storage.name.find("language_model.model.layers.0.self_attn.q_proj_mot_gen.weight") != std::string::npos) {
return VERSION_SENSENOVA_U1_5;
}
if (tensor_storage.name.find("model.diffusion_model.transformer_blocks.0.img_mod.1.weight") != std::string::npos) {
auto img_in = tensor_storage_map.find("model.diffusion_model.img_in.weight");
if (img_in != tensor_storage_map.end() && img_in->second.ne[0] == 128) {
+37 -18
View File
@@ -1584,18 +1584,35 @@ ModelManager::CapacityCheck ModelManager::check_capacity(
if (request.compute_backend == nullptr || sd_backend_is_cpu(request.compute_backend)) {
return result;
}
auto add = [](size_t a, size_t b) { return b > SIZE_MAX - a ? SIZE_MAX : a + b; };
const size_t missing = compute_backend_alloc_size(states, true);
result.required_device_bytes = add(request.pending_allocation_bytes, missing);
result.required_budget_bytes = add(request.runtime_peak_bytes(), missing);
auto device = ggml_backend_get_device(request.compute_backend);
if (device != nullptr) {
auto add = [](size_t a, size_t b) { return b > SIZE_MAX - a ? SIZE_MAX : a + b; };
const size_t missing = compute_backend_alloc_size(states, true);
// Backend scratch buffers and pipelines are not included in graph measurements.
constexpr size_t safety_margin = 512ULL * 1024ULL * 1024ULL;
result.required_device_bytes = add(add(request.pending_allocation_bytes, missing), safety_margin);
result.required_budget_bytes = add(request.runtime_peak_bytes(), missing);
auto available_device_bytes = [&](ggml_backend_t backend) {
auto device = ggml_backend_get_device(backend);
if (device == nullptr) {
return SIZE_MAX;
}
size_t free_bytes = 0, total_bytes = 0;
ggml_backend_dev_memory(device, &free_bytes, &total_bytes);
if (free_bytes != 0 || total_bytes != 0) {
result.available_device_bytes = free_bytes;
if (free_bytes == 0 && total_bytes == 0) {
return SIZE_MAX;
}
}
// Vulkan's heap budget subtraction can underflow when usage exceeds the budget.
if (total_bytes > 0 && free_bytes > total_bytes) {
return size_t{0};
}
const size_t resident = add(compute_backend_resident_bytes(backend),
add(other_runtime_resident_bytes(request.owner_id, backend),
request.runtime_resident_bytes));
if (total_bytes > 0) {
free_bytes = std::min(free_bytes, resident < total_bytes ? total_bytes - resident : 0);
}
return free_bytes;
};
result.available_device_bytes = available_device_bytes(request.compute_backend);
if (request.max_backend_bytes > 0) {
const size_t resident = add(compute_backend_resident_bytes(request.compute_backend),
other_runtime_resident_bytes(request.owner_id, request.compute_backend));
@@ -1619,11 +1636,7 @@ ModelManager::CapacityCheck ModelManager::check_capacity(
// GGML exposes only a split buffer's total size, not per-device allocations.
// Charge that upper bound on every participant instead of undercounting a shard.
for (const auto& entry : split_devices) {
size_t free_bytes = 0, total_bytes = 0;
ggml_backend_dev_memory(ggml_backend_get_device(entry.first), &free_bytes, &total_bytes);
if (free_bytes != 0 || total_bytes != 0) {
result.available_device_bytes = std::min(result.available_device_bytes, free_bytes);
}
result.available_device_bytes = std::min(result.available_device_bytes, available_device_bytes(entry.first));
if (entry.second > 0) {
const size_t resident = add(compute_backend_resident_bytes(entry.first),
other_runtime_resident_bytes(request.owner_id, entry.first));
@@ -1739,12 +1752,18 @@ bool ModelManager::ensure_compute_backend_capacity(
}
}
const auto capacity = check_capacity(request, required_states);
LOG_WARN("model manager cannot make enough memory available on %s: need %.2f MB device / %.2f MB budget, available %.2f MB device / %.2f MB budget",
const auto capacity = check_capacity(request, required_states);
const std::string available_device = capacity.available_device_bytes == SIZE_MAX
? "unknown"
: sd_format("%.2f MB", capacity.available_device_bytes / (1024.0 * 1024.0));
const std::string available_budget = capacity.available_budget_bytes == SIZE_MAX
? "unlimited"
: sd_format("%.2f MB", capacity.available_budget_bytes / (1024.0 * 1024.0));
LOG_WARN("model manager cannot make enough memory available on %s: need %.2f MB device / %.2f MB budget, available %s device / %s budget",
ggml_backend_name(compute_backend),
capacity.required_device_bytes / (1024.0 * 1024.0),
capacity.required_budget_bytes / (1024.0 * 1024.0),
capacity.available_device_bytes / (1024.0 * 1024.0),
capacity.available_budget_bytes / (1024.0 * 1024.0));
available_device.c_str(),
available_budget.c_str());
return false;
}
+1 -1
View File
@@ -31,7 +31,7 @@ public:
};
private:
static constexpr size_t MAX_RESIDENCY_BLOCK_BYTES = 64ULL * 1024ULL * 1024ULL;
static constexpr size_t MAX_RESIDENCY_BLOCK_BYTES = 1024ULL * 1024ULL * 1024ULL;
struct TensorState {
std::string name;
+27 -2
View File
@@ -96,9 +96,13 @@ const char* model_version_to_str[] = {
"SeFi-Image",
"Krea2",
"Mage Flow",
"SenseNova U1.5",
"ESRGAN",
};
static_assert(VERSION_COUNT == sizeof(model_version_to_str) / sizeof(model_version_to_str[0]),
"\nnumber of elements in model_version_to_str[] != VERSION_COUNT");
void calculate_alphas_cumprod(float* alphas_cumprod,
float linear_start = 0.00085f,
float linear_end = 0.0120f,
@@ -843,6 +847,12 @@ bool StableDiffusionGGML::init_model_loader(ModelLoader& model_loader, ModelConf
}
bool StableDiffusionGGML::init(const sd_ctx_params_t* sd_ctx_params) {
for (float scale : {sd_ctx_params->linear_scale, sd_ctx_params->attn_scale}) {
if (!std::isfinite(scale) || scale < 0.f || (scale > 0.f && !std::isfinite(1.f / scale))) {
LOG_ERROR("scale overrides must be finite positive values, or 0 to keep model defaults");
return false;
}
}
auto configuration = std::make_unique<ModelConfig>(*sd_ctx_params);
n_threads = sd_ctx_params->n_threads;
enable_mmap = sd_ctx_params->enable_mmap;
@@ -852,7 +862,7 @@ bool StableDiffusionGGML::init(const sd_ctx_params_t* sd_ctx_params) {
backend_spec = SAFE_STR(sd_ctx_params->backend);
params_backend_spec = SAFE_STR(sd_ctx_params->params_backend);
split_mode_spec = SAFE_STR(sd_ctx_params->split_mode);
auto_fit_enabled = sd_ctx_params->auto_fit && backend_spec.empty() && params_backend_spec.empty();
auto_fit_enabled = sd_ctx_params->auto_fit && params_backend_spec.empty();
max_vram_assignment.reset(0.f);
{
std::string error;
@@ -1306,6 +1316,9 @@ bool StableDiffusionGGML::build_denoiser() {
pred_type = SEFI_FLOW_PRED;
} else if (sd_version_is_minit2i(version)) {
pred_type = MINIT2I_FLOW_PRED;
} else if (sd_version_is_sensenova_u1(version)) {
pred_type = SENSENOVA_U1_FLOW_PRED;
default_flow_shift = 3.f;
} else {
pred_type = EPS_PRED;
}
@@ -1351,6 +1364,11 @@ bool StableDiffusionGGML::build_denoiser() {
denoiser = std::make_shared<MiniT2IFlowDenoiser>();
break;
}
case SENSENOVA_U1_FLOW_PRED: {
LOG_INFO("running in SenseNova U1.5 FLOW mode");
denoiser = std::make_shared<SenseNovaU1FlowDenoiser>(default_flow_shift);
break;
}
default: {
LOG_ERROR("Unknown predition type %i", pred_type);
return false;
@@ -2333,6 +2351,9 @@ sd::Tensor<float> StableDiffusionGGML::sample(const std::shared_ptr<DiffusionMod
} else if (sd_version_is_minit2i(version)) {
diffusion_params.extra = MiniT2IDiffusionExtra{
condition.c_vector.empty() ? nullptr : &condition.c_vector};
} else if (sd_version_is_sensenova_u1(version)) {
diffusion_params.extra = SenseNovaU1DiffusionExtra{
condition.c_input_ids.empty() ? nullptr : &condition.c_input_ids};
} else {
diffusion_params.extra = std::monostate{};
}
@@ -2489,7 +2510,9 @@ int StableDiffusionGGML::get_vae_scale_factor() {
int StableDiffusionGGML::get_diffusion_model_down_factor() {
int down_factor = 8; // unet
if (sd_version_is_dit(version)) {
if (sd_version_is_wan(version) || sd_version_is_lingbot_video(version) || sd_version_is_minimax_h3(version)) {
if (sd_version_is_sensenova_u1(version)) {
down_factor = 32;
} else if (sd_version_is_wan(version) || sd_version_is_lingbot_video(version) || sd_version_is_minimax_h3(version)) {
down_factor = 2;
} else {
down_factor = 1;
@@ -2515,6 +2538,8 @@ int StableDiffusionGGML::get_latent_channel() {
latent_channel = 3;
} else if (sd_version_is_minit2i(version)) {
latent_channel = 3;
} else if (sd_version_is_sensenova_u1(version)) {
latent_channel = 3;
} else if (sd_version_is_pid(version)) {
latent_channel = 3;
} else if (sd_version_is_sefi_image(version)) {
+15
View File
@@ -438,6 +438,10 @@ namespace sd::pipeline {
condition_params.zero_out_masked = false;
auto cond = sd->cond_stage_model->get_learned_condition(sd->n_threads,
condition_params);
if (cond.empty()) {
LOG_ERROR("failed to encode prompt");
return std::nullopt;
}
if (cond.c_concat.empty() && ref_image_params.pass_to_dit) {
cond.c_concat = latents->concat_latent; // TODO: optimize
}
@@ -455,6 +459,9 @@ namespace sd::pipeline {
// states with a zeroed prompt mask, so no extra text encode is needed.
uncond.c_crossattn = cond.c_crossattn;
uncond.c_vector = sd::Tensor<float>::zeros_like(cond.c_vector);
} else if (sd_version_is_sensenova_u1(sd->version)) {
auto* sensenova_conditioner = static_cast<SenseNovaU1Conditioner*>(sd->cond_stage_model.get());
uncond = sensenova_conditioner->get_unconditional_condition(request->negative_prompt);
} else {
bool zero_out_masked = false;
if (sd_version_is_sdxl(sd->version) &&
@@ -466,6 +473,10 @@ namespace sd::pipeline {
condition_params.zero_out_masked = zero_out_masked;
uncond = sd->cond_stage_model->get_learned_condition(sd->n_threads,
condition_params);
if (uncond.empty()) {
LOG_ERROR("failed to encode negative prompt");
return std::nullopt;
}
}
if (uncond.c_concat.empty() && ref_image_params.pass_to_dit) {
uncond.c_concat = latents->concat_latent; // TODO: optimize
@@ -491,6 +502,10 @@ namespace sd::pipeline {
}
img_uncond = sd->cond_stage_model->get_learned_condition(sd->n_threads,
condition_params);
if (img_uncond.empty()) {
LOG_ERROR("failed to encode image guidance prompt");
return std::nullopt;
}
if (img_uncond.c_concat.empty() && ref_image_params.pass_to_dit) {
img_uncond.c_concat = latents->img_uncond_concat_latent; // TODO: optimize
}
+33 -1
View File
@@ -27,6 +27,7 @@
#include "model/diffusion/model.hpp"
#include "model/diffusion/pid.hpp"
#include "model/diffusion/qwen_image.hpp"
#include "model/diffusion/sensenova_u1.h"
#include "model/diffusion/unet.hpp"
#include "model/diffusion/wan.hpp"
#include "model/diffusion/z_image.hpp"
@@ -306,6 +307,12 @@ namespace sd::model_builders {
tensor_storage_map,
"model.diffusion_model.model.net",
weight_manager);
} else if (sd_version_is_sensenova_u1(version)) {
result.conditioner = std::make_shared<SenseNovaU1Conditioner>();
result.diffusion = std::make_shared<SenseNovaU1::SenseNovaU1Runner>(ctx.backends.runtime_backend(SDBackendModule::DIFFUSION),
tensor_storage_map,
"",
weight_manager);
} else if (sd_version_is_anima(version)) {
result.conditioner = std::make_shared<AnimaConditioner>(ctx.backends.runtime_backend(SDBackendModule::TE),
tensor_storage_map,
@@ -396,6 +403,21 @@ namespace sd::model_builders {
"ip_adapter",
weight_manager);
}
if (result.conditioner) {
result.conditioner->set_scale_overrides(sd_ctx_params->linear_scale, sd_ctx_params->attn_scale);
}
if (result.diffusion) {
result.diffusion->set_scale_overrides(sd_ctx_params->linear_scale, sd_ctx_params->attn_scale);
}
if (result.high_noise_diffusion) {
result.high_noise_diffusion->set_scale_overrides(sd_ctx_params->linear_scale, sd_ctx_params->attn_scale);
}
if (result.clip_vision) {
result.clip_vision->set_scale_overrides(sd_ctx_params->linear_scale, sd_ctx_params->attn_scale);
}
if (result.ip_adapter) {
result.ip_adapter->set_scale_overrides(sd_ctx_params->linear_scale, sd_ctx_params->attn_scale);
}
runners = std::move(result);
return true;
}
@@ -493,7 +515,7 @@ namespace sd::model_builders {
}
};
if (version == VERSION_CHROMA_RADIANCE || version == VERSION_HIDREAM_O1 || sd_version_is_minit2i(version)) {
if (version == VERSION_CHROMA_RADIANCE || version == VERSION_HIDREAM_O1 || sd_version_is_minit2i(version) || sd_version_is_sensenova_u1(version)) {
LOG_INFO("using FakeVAE");
result.vae = std::make_shared<FakeVAE>(version,
ctx.backends.runtime_backend(SDBackendModule::VAE),
@@ -531,6 +553,15 @@ namespace sd::model_builders {
result.preview->set_conv2d_direct_enabled(true);
}
}
if (result.vae) {
result.vae->set_scale_overrides(sd_ctx_params->linear_scale, sd_ctx_params->attn_scale);
}
if (result.preview) {
result.preview->set_scale_overrides(sd_ctx_params->linear_scale, sd_ctx_params->attn_scale);
}
if (result.audio) {
result.audio->set_scale_overrides(sd_ctx_params->linear_scale, sd_ctx_params->attn_scale);
}
runners = std::move(result);
return true;
}
@@ -552,6 +583,7 @@ namespace sd::model_builders {
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;
}
+76
View File
@@ -1488,6 +1488,82 @@ struct MiniT2IFlowDenoiser : public Denoiser {
}
};
// SenseNova U1.5 integrates velocity over t=0..1 while the generic sampler
// integrates over descending sigma. With sigma=1-t, returning
// denoised=x+sigma*v makes the generic Euler derivative exactly -v, so the
// descending-sigma update is identical to the official ascending-time update.
struct SenseNovaU1FlowDenoiser : public DiscreteFlowDenoiser {
explicit SenseNovaU1FlowDenoiser(float shift = 3.f)
: DiscreteFlowDenoiser(shift) {}
float sigma_min() override {
return 0.f;
}
float sigma_max() override {
return 1.f;
}
float sigma_to_t(float sigma) override {
return 1.f - sigma;
}
float t_to_sigma(float t) override {
float sigma = 1.f - t;
return shift * sigma / (1.f + (shift - 1.f) * sigma);
}
std::vector<float> get_scalings(float sigma) override {
return {1.f, sigma, 1.f};
}
sd::Tensor<float> noise_scaling(float sigma,
const sd::Tensor<float>& noise,
const sd::Tensor<float>& latent) override {
SD_UNUSED(sigma);
SD_UNUSED(latent);
GGML_ASSERT(noise.dim() >= 2);
const float token_w = static_cast<float>(noise.shape()[0]) / 32.f;
const float token_h = static_cast<float>(noise.shape()[1]) / 32.f;
const float noise_scale = std::min(16.f, std::sqrt((token_w * token_h) / 64.f));
return noise * noise_scale;
}
sd::Tensor<float> inverse_noise_scaling(float sigma,
const sd::Tensor<float>& latent) override {
SD_UNUSED(sigma);
return latent;
}
float noise_level_to_sigma(float noise_level) override {
SD_UNUSED(noise_level);
return 1.f;
}
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 {
SD_UNUSED(image_seq_len);
SD_UNUSED(scheduler_type);
SD_UNUSED(version);
SD_UNUSED(extra_sample_args);
std::vector<float> sigmas;
sigmas.reserve(n + 1);
if (n == 0) {
sigmas.push_back(0.f);
return sigmas;
}
for (uint32_t i = 0; i <= n; ++i) {
const float t = static_cast<float>(i) / static_cast<float>(n);
sigmas.push_back(t_to_sigma(t));
}
sigmas.back() = 0.f;
return sigmas;
}
};
typedef std::function<sd::guidance::GuiderOutput(const sd::Tensor<float>&, float, int)> denoise_cb_t;
static std::pair<float, float> get_ancestral_step(float sigma_from,
+14
View File
@@ -156,6 +156,7 @@ const char* prediction_to_str[] = {
"flux_flow",
"sefi_flow",
"minit2i_flow",
"sensenova_u1_flow",
};
const char* sd_prediction_name(enum prediction_t prediction) {
@@ -322,6 +323,8 @@ 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->diffusion_flash_attn = false;
sd_ctx_params->linear_scale = 0.f;
sd_ctx_params->attn_scale = 0.f;
sd_ctx_params->vae_format = SD_VAE_FORMAT_AUTO;
sd_ctx_params->backend = nullptr;
sd_ctx_params->params_backend = nullptr;
@@ -373,6 +376,8 @@ char* sd_ctx_params_to_str(const sd_ctx_params_t* sd_ctx_params) {
"auto_fit: %s\n"
"flash_attn: %s\n"
"diffusion_flash_attn: %s\n"
"linear_scale: %g\n"
"attn_scale: %g\n"
"vae_format: %s\n",
SAFE_STR(sd_ctx_params->model_path),
SAFE_STR(sd_ctx_params->clip_l_path),
@@ -408,6 +413,8 @@ 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),
BOOL_STR(sd_ctx_params->diffusion_flash_attn),
sd_ctx_params->linear_scale,
sd_ctx_params->attn_scale,
sd_vae_format_name(sd_ctx_params->vae_format));
return buf;
@@ -695,6 +702,13 @@ SD_API bool sd_ctx_has_control_net(const sd_ctx_t* sd_ctx) {
return sd_ctx->sd->control_net != nullptr;
}
const char* sd_get_model_version_name(const sd_ctx_t* sd_ctx) {
if (sd_ctx == nullptr || sd_ctx->sd == nullptr || sd_ctx->sd->version >= VERSION_COUNT) {
return "Unknown";
}
return model_version_to_str[sd_ctx->sd->version];
}
enum sample_method_t sd_get_default_sample_method(const sd_ctx_t* sd_ctx) {
return sd::pipeline::default_sample_method(sd_ctx != nullptr ? sd_ctx->sd : nullptr);
}
+20 -10
View File
@@ -45,16 +45,8 @@ void Qwen2Tokenizer::load_from_merges(const std::string& merges_utf8_str) {
bpe_len = rank;
}
Qwen2Tokenizer::Qwen2Tokenizer(const std::string& merges_utf8_str) {
UNK_TOKEN = "<|endoftext|>";
EOS_TOKEN = "<|endoftext|>";
PAD_TOKEN = "<|endoftext|>";
UNK_TOKEN_ID = 151643;
EOS_TOKEN_ID = 151643;
PAD_TOKEN_ID = 151643;
special_tokens = {
static const std::vector<std::string>& qwen2_special_tokens() {
static const std::vector<std::string> tokens = {
"<|endoftext|>",
"<|im_start|>",
"<|im_end|>",
@@ -87,6 +79,24 @@ Qwen2Tokenizer::Qwen2Tokenizer(const std::string& merges_utf8_str) {
"<|bot_token|>",
"<|tms_token|>",
};
return tokens;
}
Qwen2Tokenizer::Qwen2Tokenizer(const std::string& merges_utf8_str)
: Qwen2Tokenizer(merges_utf8_str, qwen2_special_tokens()) {
}
Qwen2Tokenizer::Qwen2Tokenizer(const std::string& merges_utf8_str,
const std::vector<std::string>& special_tokens_override) {
UNK_TOKEN = "<|endoftext|>";
EOS_TOKEN = "<|endoftext|>";
PAD_TOKEN = "<|endoftext|>";
UNK_TOKEN_ID = 151643;
EOS_TOKEN_ID = 151643;
PAD_TOKEN_ID = 151643;
special_tokens = special_tokens_override;
if (merges_utf8_str.size() > 0) {
load_from_merges(merges_utf8_str);
+3
View File
@@ -2,12 +2,15 @@
#define __SD_TOKENIZERS_QWEN2_TOKENIZER_H__
#include <string>
#include <vector>
#include "bpe_tokenizer.h"
class Qwen2Tokenizer : public BPETokenizer {
protected:
void load_from_merges(const std::string& merges_utf8_str);
Qwen2Tokenizer(const std::string& merges_utf8_str,
const std::vector<std::string>& special_tokens_override);
public:
explicit Qwen2Tokenizer(const std::string& merges_utf8_str = "");
+44
View File
@@ -0,0 +1,44 @@
#include "sensenova_u1_tokenizer.h"
#include <vector>
static const std::vector<std::string>& sensenova_u1_special_tokens() {
static const std::vector<std::string> tokens = {
"<|endoftext|>",
"<|im_start|>",
"<|im_end|>",
"<|object_ref_start|>",
"<|object_ref_end|>",
"<|box_start|>",
"<|box_end|>",
"<|quad_start|>",
"<|quad_end|>",
"<|vision_start|>",
"<|vision_end|>",
"<|vision_pad|>",
"<|image_pad|>",
"<|video_pad|>",
"<tool_call>",
"</tool_call>",
"<|fim_prefix|>",
"<|fim_middle|>",
"<|fim_suffix|>",
"<|fim_pad|>",
"<|repo_name|>",
"<|file_sep|>",
"<tool_response>",
"</tool_response>",
"<think>",
"</think>",
"<IMG_CONTEXT>",
"<img>",
"</img>",
};
return tokens;
}
SenseNovaU1Tokenizer::SenseNovaU1Tokenizer(const std::string& merges_utf8_str)
: Qwen2Tokenizer(merges_utf8_str, sensenova_u1_special_tokens()) {
EOS_TOKEN = "<|im_end|>";
EOS_TOKEN_ID = 151645;
}
+13
View File
@@ -0,0 +1,13 @@
#ifndef __SD_TOKENIZERS_SENSENOVA_U1_TOKENIZER_H__
#define __SD_TOKENIZERS_SENSENOVA_U1_TOKENIZER_H__
#include <string>
#include "qwen2_tokenizer.h"
class SenseNovaU1Tokenizer : public Qwen2Tokenizer {
public:
explicit SenseNovaU1Tokenizer(const std::string& merges_utf8_str = "");
};
#endif // __SD_TOKENIZERS_SENSENOVA_U1_TOKENIZER_H__