Compare commits

...
14 changed files with 165 additions and 58 deletions
+34 -5
View File
@@ -14,6 +14,8 @@ on:
paths:
[
".github/workflows/**",
".dockerignore",
"Dockerfile*",
"**/CMakeLists.txt",
"**/Makefile",
"**/*.h",
@@ -29,6 +31,8 @@ on:
paths:
[
".github/workflows/**",
".dockerignore",
"Dockerfile*",
"**/CMakeLists.txt",
"**/Makefile",
"**/*.h",
@@ -177,7 +181,7 @@ jobs:
build-and-push-docker-images:
name: Build and push container images
if: ${{ github.event_name != 'pull_request' }}
runs-on: ubuntu-latest
runs-on: ${{ matrix.runner }}
permissions:
contents: read
@@ -189,6 +193,20 @@ jobs:
strategy:
matrix:
variant: [musa, sycl, vulkan, cuda]
platform: [linux/amd64]
runner: [ubuntu-latest]
build-args: [""]
tag-suffix: [""]
include:
- variant: cuda
platform: linux/arm64
runner: ubuntu-24.04-arm
tag-suffix: "-spark"
build-args: |
CUDA_VERSION=13.0.0
UBUNTU_VERSION=24.04
CUDA_ARCHITECTURES=121
GGML_CUDA_FA_ALL_QUANTS=ON
env:
REGISTRY: ghcr.io
@@ -243,12 +261,13 @@ jobs:
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64
platforms: ${{ matrix.platform }}
push: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
file: Dockerfile.${{ matrix.variant }}
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.BRANCH_NAME }}-${{ matrix.variant }}
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.BRANCH_NAME }}-${{ matrix.variant }}${{ matrix.tag-suffix }}
labels: ${{ steps.meta.outputs.labels }}
annotations: ${{ steps.meta.outputs.annotations }}
build-args: ${{ matrix.build-args }}
macOS-latest-cmake:
runs-on: macos-latest
@@ -449,13 +468,23 @@ jobs:
env:
ROCM_VERSION: "7.13.0"
GPU_TARGETS: "gfx906;gfx908;gfx90a;gfx942;gfx950;gfx1100;gfx1101;gfx1102;gfx1150;gfx1151;gfx1200;gfx1201"
GPU_TARGETS: "gfx906;gfx908;gfx90a;gfx942;gfx950;gfx1010;gfx1011;gfx1012;gfx1030;gfx1031;gfx1032;gfx1033;gfx1034;gfx1035;gfx1036;gfx1100;gfx1101;gfx1102;gfx1150;gfx1151;gfx1152;gfx1200;gfx1201"
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10.15.1
- name: Cache ROCm Installation
id: cache-rocm
uses: actions/cache@v4
@@ -648,7 +677,7 @@ jobs:
gpu_targets: "gfx908;gfx90a;gfx942;gfx1030;gfx1031;gfx1032;gfx1100;gfx1101;gfx1102;gfx1151;gfx1150;gfx1200;gfx1201"
build: 'x64'
- ROCM_VERSION: "7.13.0"
gpu_targets: "gfx906;gfx908;gfx90a;gfx942;gfx950;gfx1100;gfx1101;gfx1102;gfx1150;gfx1151;gfx1200;gfx1201"
gpu_targets: "gfx906;gfx908;gfx90a;gfx942;gfx950;gfx1010;gfx1011;gfx1012;gfx1030;gfx1031;gfx1032;gfx1033;gfx1034;gfx1035;gfx1036;gfx1100;gfx1101;gfx1102;gfx1150;gfx1151;gfx1152;gfx1200;gfx1201"
build: x64
steps:
+55
View File
@@ -0,0 +1,55 @@
name: Close inactive PRs
on:
schedule:
# Run daily. GitHub cron schedules use UTC.
- cron: "30 1 * * *"
workflow_dispatch:
inputs:
debug_only:
description: "Dry run: log intended actions without changing PRs"
required: false
default: false
type: boolean
permissions:
issues: write
pull-requests: write
concurrency:
group: ${{ github.workflow }}
cancel-in-progress: false
jobs:
stale-prs:
runs-on: ubuntu-latest
steps:
- name: Mark and close inactive PRs
uses: actions/stale@v10
with:
days-before-issue-stale: -1
days-before-issue-close: -1
days-before-pr-stale: 365
days-before-pr-close: 7
stale-pr-label: pr:inactive
close-pr-label: pr:auto-closed
exempt-pr-labels: pr:keep-open
stale-pr-message: >
This PR has been inactive for 365 days. If there is no new activity
within 7 days, it will be closed automatically. Comment, push new
commits, or remove the pr:inactive label to keep it open. Add
pr:keep-open to exempt it from future inactive PR cleanup.
close-pr-message: >
Closing this PR because it has had no activity for 7 days after
being marked inactive. If this is still useful or ready to move
forward, feel free to reopen it with fresh context or updated
details. Sorry for any inconvenience.
remove-pr-stale-when-updated: true
delete-branch: false
operations-per-run: 100
debug-only: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_only || false }}
+7 -1
View File
@@ -10,7 +10,13 @@ WORKDIR /sd.cpp
COPY . .
ARG CUDACXX=/usr/local/cuda/bin/nvcc
RUN cmake . -B ./build -DSD_CUDA=ON
ARG CUDA_ARCHITECTURES=""
ARG GGML_CUDA_FA_ALL_QUANTS=""
RUN cmake . -B ./build \
-DSD_CUDA=ON \
${CUDA_ARCHITECTURES:+-DCMAKE_CUDA_ARCHITECTURES="${CUDA_ARCHITECTURES}"} \
${GGML_CUDA_FA_ALL_QUANTS:+-DGGML_CUDA_FA_ALL_QUANTS=${GGML_CUDA_FA_ALL_QUANTS}}
RUN cmake --build ./build --config Release -j$(nproc)
FROM nvidia/cuda:${CUDA_VERSION}-cudnn-runtime-ubuntu${UBUNTU_VERSION} AS runtime
+2 -1
View File
@@ -1,4 +1,5 @@
ARG SYCL_VERSION=2025.1.0-0
# ggml SYCL hardware detection uses BMG G31/WCL architecture enums added in oneAPI 2025.3.
ARG SYCL_VERSION=2025.3.2-0
FROM intel/oneapi-basekit:${SYCL_VERSION}-devel-ubuntu24.04 AS build
+1 -15
View File
@@ -15,29 +15,15 @@ API and command-line option may change frequently.***
## 🔥Important News
* **2026/05/17** 🚀 stable-diffusion.cpp now supports **LTX-2.3**
* **2026/04/11** 🚀 stable-diffusion.cpp now uses a brand-new embedded web UI.
👉 Details: [PR #1408](https://github.com/leejet/stable-diffusion.cpp/pull/1408)
* **2026/01/18** 🚀 stable-diffusion.cpp now supports **FLUX.2-klein**
👉 Details: [PR #1193](https://github.com/leejet/stable-diffusion.cpp/pull/1193)
* **2025/12/01** 🚀 stable-diffusion.cpp now supports **Z-Image**
👉 Details: [PR #1020](https://github.com/leejet/stable-diffusion.cpp/pull/1020)
* **2025/11/30** 🚀 stable-diffusion.cpp now supports **FLUX.2-dev**
👉 Details: [PR #1016](https://github.com/leejet/stable-diffusion.cpp/pull/1016)
* **2025/10/13** 🚀 stable-diffusion.cpp now supports **Qwen-Image-Edit / Qwen-Image-Edit 2509**
👉 Details: [PR #877](https://github.com/leejet/stable-diffusion.cpp/pull/877)
* **2025/10/12** 🚀 stable-diffusion.cpp now supports **Qwen-Image**
👉 Details: [PR #851](https://github.com/leejet/stable-diffusion.cpp/pull/851)
* **2025/09/14** 🚀 stable-diffusion.cpp now supports **Wan2.1 Vace**
👉 Details: [PR #819](https://github.com/leejet/stable-diffusion.cpp/pull/819)
* **2025/09/06** 🚀 stable-diffusion.cpp now supports **Wan2.1 / Wan2.2**
👉 Details: [PR #778](https://github.com/leejet/stable-diffusion.cpp/pull/778)
## Features
+2 -1
View File
@@ -55,7 +55,8 @@ Context Options:
then threads will be set to the number of CPU physical cores
--chroma-t5-mask-pad <int> t5 mask pad size of chroma
--max-vram <float> maximum VRAM budget in GiB for graph-cut segmented execution. 0 disables
graph splitting; -1 auto-detects free VRAM minus 1 GiB
graph splitting; a negative value auto-detects free VRAM, sparing the
specified value (e.g. -0.5 will keep at least 0.5 GiB free)
--force-sdxl-vae-conv-scale force use of conv scale on sdxl vae
--offload-to-cpu place the weights in RAM to save VRAM, and automatically load them into VRAM
when needed
+1 -1
View File
@@ -413,7 +413,7 @@ ArgOptions SDContextParams::get_options() {
options.float_options = {
{"",
"--max-vram",
"maximum VRAM budget in GiB for graph-cut segmented execution. 0 disables graph splitting; -1 auto-detects free VRAM minus 1 GiB",
"maximum VRAM budget in GiB for graph-cut segmented execution. 0 disables graph splitting; a negative value auto-detects free VRAM, sparing the specified value (e.g. -0.5 will keep at least 0.5 GiB free)",
&max_vram},
};
+2 -1
View File
@@ -157,7 +157,8 @@ Context Options:
then threads will be set to the number of CPU physical cores
--chroma-t5-mask-pad <int> t5 mask pad size of chroma
--max-vram <float> maximum VRAM budget in GiB for graph-cut segmented execution. 0 disables
graph splitting; -1 auto-detects free VRAM minus 1 GiB
graph splitting; a negative value auto-detects free VRAM, sparing the
specified value (e.g. -0.5 will keep at least 0.5 GiB free)
--force-sdxl-vae-conv-scale force use of conv scale on sdxl vae
--offload-to-cpu place the weights in RAM to save VRAM, and automatically load them into VRAM
when needed
+1 -1
Submodule ggml updated: 7f4ab364b2...0ce7ad348a
+2 -1
View File
@@ -126,7 +126,8 @@ enum sd_type_t {
// SD_TYPE_IQ4_NL_8_8 = 38,
SD_TYPE_MXFP4 = 39, // MXFP4 (1 block)
SD_TYPE_NVFP4 = 40, // NVFP4 (4 blocks, E4M3 scale)
SD_TYPE_COUNT = 41,
SD_TYPE_Q1_0 = 41,
SD_TYPE_COUNT = 42,
};
enum sd_log_level_t {
+14 -13
View File
@@ -16,8 +16,7 @@
namespace sd::ggml_graph_cut {
static constexpr double MAX_VRAM_BYTES_PER_GIB = 1024.0 * 1024.0 * 1024.0;
static constexpr size_t MAX_VRAM_AUTO_RESERVE_BYTES = 1024ULL * 1024ULL * 1024ULL;
static constexpr double MAX_VRAM_BYTES_PER_GIB = 1024.0 * 1024.0 * 1024.0;
static std::string graph_cut_tensor_display_name(const ggml_tensor* tensor) {
if (tensor == nullptr) {
@@ -93,45 +92,47 @@ namespace sd::ggml_graph_cut {
return static_cast<float>(static_cast<double>(max_vram_bytes) / MAX_VRAM_BYTES_PER_GIB);
}
static size_t resolve_auto_max_vram_bytes(ggml_backend_t backend) {
static size_t resolve_auto_max_vram_bytes(float spare_vram, ggml_backend_t backend) {
if (backend == nullptr) {
LOG_WARN("--max-vram -1 requested, but no backend is available; disabling graph splitting");
LOG_WARN("--max-vram < 0 requested, but no backend is available; disabling graph splitting");
return 0;
}
ggml_backend_dev_t dev = ggml_backend_get_device(backend);
if (dev == nullptr) {
LOG_WARN("--max-vram -1 requested, but no backend device is available; disabling graph splitting");
LOG_WARN("--max-vram < 0 requested, but no backend device is available; disabling graph splitting");
return 0;
}
if (ggml_backend_dev_type(dev) == GGML_BACKEND_DEVICE_TYPE_CPU) {
LOG_WARN("--max-vram -1 requested, but the main backend is CPU; disabling graph splitting");
LOG_WARN("--max-vram < 0 requested, but the main backend is CPU; disabling graph splitting");
return 0;
}
size_t free_vram = 0;
size_t total_vram = 0;
ggml_backend_dev_memory(dev, &free_vram, &total_vram);
size_t spare_bytes = static_cast<size_t>(MAX_VRAM_BYTES_PER_GIB * spare_vram);
if (free_vram <= MAX_VRAM_AUTO_RESERVE_BYTES) {
LOG_WARN("--max-vram -1 requested, but free VRAM is %.2f GiB; reserving 1.00 GiB leaves no graph budget",
free_vram / MAX_VRAM_BYTES_PER_GIB);
if (free_vram <= spare_bytes) {
LOG_WARN("--max-vram < 0 requested, but free VRAM is %.2f GiB; reserving %.2f GiB leaves no graph budget",
free_vram / MAX_VRAM_BYTES_PER_GIB, spare_vram);
return 0;
}
const size_t max_vram_bytes = free_vram - MAX_VRAM_AUTO_RESERVE_BYTES;
LOG_INFO("--max-vram -1 auto-detected %.2f GiB free VRAM (%.2f GiB total), reserving 1.00 GiB; using %.2f GiB",
const size_t max_vram_bytes = free_vram - spare_bytes;
LOG_INFO("--max-vram < 0 auto-detected %.2f GiB free VRAM (%.2f GiB total), reserving %.2f GiB; using %.2f GiB",
free_vram / MAX_VRAM_BYTES_PER_GIB,
total_vram / MAX_VRAM_BYTES_PER_GIB,
spare_vram,
max_vram_bytes / MAX_VRAM_BYTES_PER_GIB);
return max_vram_bytes;
}
float resolve_max_vram_gib(float max_vram, ggml_backend_t backend) {
if (max_vram != -1.f) {
if (max_vram >= 0.f) {
return max_vram;
}
return max_vram_bytes_to_gib(resolve_auto_max_vram_bytes(backend));
return max_vram_bytes_to_gib(resolve_auto_max_vram_bytes(-max_vram, backend));
}
static Segment make_segment_seed(const Plan& plan,
+2 -1
View File
@@ -1403,7 +1403,8 @@ namespace LLM {
out_layers,
return_all_hidden_states);
};
return take_or_empty(GGMLRunner::compute<float>(get_graph, n_threads, true));
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, true),
input_ids.dim() + 1);
}
int64_t get_num_image_tokens(int64_t t, int64_t h, int64_t w) {
+39 -15
View File
@@ -349,42 +349,66 @@ namespace LTXV {
return type == GGML_TYPE_BF16 ? GGML_TYPE_F16 : type;
}
static ggml_tensor* repeat_1d_value(ggml_context* ctx, ggml_tensor* x, int64_t count) {
GGML_ASSERT(x->ne[0] == 1);
ggml_tensor* target = ggml_new_tensor_4d(ctx, x->type, count, x->ne[1], x->ne[2], x->ne[3]);
return ggml_repeat(ctx, x, target);
static ggml_tensor* repeat_with_vulkan_f32_workaround(ggml_backend_t backend,
ggml_context* ctx,
ggml_tensor* x,
int64_t ne0,
int64_t ne1,
int64_t ne2,
int64_t ne3) {
if (x->type != GGML_TYPE_F32 &&
(x->type == GGML_TYPE_F16 || x->type == GGML_TYPE_BF16) &&
sd_backend_is(backend, "vulkan")) {
auto x_f32 = ggml_cast(ctx, x, GGML_TYPE_F32);
auto repeated = ggml_repeat_4d(ctx,
x_f32,
ne0,
ne1,
ne2,
ne3);
return ggml_cast(ctx, repeated, x->type);
}
return ggml_repeat_4d(ctx, x, ne0, ne1, ne2, ne3);
}
static ggml_tensor* replicate_pad_1d(ggml_context* ctx, ggml_tensor* x, int64_t left, int64_t right) {
static ggml_tensor* repeat_1d_value(GGMLRunnerContext* runner_ctx, ggml_tensor* x, int64_t count) {
auto ctx = runner_ctx->ggml_ctx;
GGML_ASSERT(x->ne[0] == 1);
return repeat_with_vulkan_f32_workaround(runner_ctx->backend, ctx, x, count, x->ne[1], x->ne[2], x->ne[3]);
}
static ggml_tensor* replicate_pad_1d(GGMLRunnerContext* runner_ctx, ggml_tensor* x, int64_t left, int64_t right) {
auto ctx = runner_ctx->ggml_ctx;
if (left > 0) {
auto first = ggml_ext_slice(ctx, x, 0, 0, 1);
x = ggml_concat(ctx, repeat_1d_value(ctx, first, left), x, 0);
x = ggml_concat(ctx, repeat_1d_value(runner_ctx, first, left), x, 0);
}
if (right > 0) {
auto last = ggml_ext_slice(ctx, x, 0, x->ne[0] - 1, x->ne[0]);
x = ggml_concat(ctx, x, repeat_1d_value(ctx, last, right), 0);
x = ggml_concat(ctx, x, repeat_1d_value(runner_ctx, last, right), 0);
}
return x;
}
static ggml_tensor* tile_depthwise_filter_1d(ggml_context* ctx, ggml_tensor* filter, int64_t channels) {
static ggml_tensor* tile_depthwise_filter_1d(GGMLRunnerContext* runner_ctx, ggml_tensor* filter, int64_t channels) {
auto ctx = runner_ctx->ggml_ctx;
ggml_tensor* base = filter;
if (ggml_n_dims(base) == 3) {
base = ggml_reshape_4d(ctx, base, base->ne[0], 1, 1, 1);
} else if (ggml_n_dims(base) == 1) {
base = ggml_reshape_4d(ctx, base, base->ne[0], 1, 1, 1);
}
ggml_tensor* target = ggml_new_tensor_4d(ctx, base->type, base->ne[0], 1, channels, 1);
return ggml_repeat(ctx, base, target);
return repeat_with_vulkan_f32_workaround(runner_ctx->backend, ctx, base, base->ne[0], 1, channels, 1);
}
static ggml_tensor* depthwise_conv1d(ggml_context* ctx,
static ggml_tensor* depthwise_conv1d(GGMLRunnerContext* runner_ctx,
ggml_tensor* x,
ggml_tensor* filter,
int stride,
int padding) {
auto ctx = runner_ctx->ggml_ctx;
GGML_ASSERT(x->ne[3] == 1);
auto tiled = tile_depthwise_filter_1d(ctx, filter, x->ne[1]);
auto tiled = tile_depthwise_filter_1d(runner_ctx, filter, x->ne[1]);
auto out = ggml_conv_1d_dw(ctx, tiled, x, stride, padding, 1);
return ggml_reshape_4d(ctx, out, out->ne[0], out->ne[1], 1, 1);
}
@@ -654,7 +678,7 @@ namespace LTXV {
int up_pad_left = up_pad * up_ratio + (up_kernel_size - up_ratio) / 2;
int up_pad_right = up_pad * up_ratio + (up_kernel_size - up_ratio + 1) / 2;
x = replicate_pad_1d(ctx->ggml_ctx, x, up_pad, up_pad);
x = replicate_pad_1d(ctx, x, up_pad, up_pad);
x = depthwise_conv_transpose1d(ctx->ggml_ctx, x, up_filter, up_ratio);
x = ggml_ext_slice(ctx->ggml_ctx, x, 0, up_pad_left, x->ne[0] - up_pad_right);
@@ -662,8 +686,8 @@ namespace LTXV {
int down_pad_left = down_kernel_size / 2 - (down_kernel_size % 2 == 0 ? 1 : 0);
int down_pad_right = down_kernel_size / 2;
x = replicate_pad_1d(ctx->ggml_ctx, x, down_pad_left, down_pad_right);
x = depthwise_conv1d(ctx->ggml_ctx, x, down_filter, down_ratio, 0);
x = replicate_pad_1d(ctx, x, down_pad_left, down_pad_right);
x = depthwise_conv1d(ctx, x, down_filter, down_ratio, 0);
return x;
}
};
+3 -2
View File
@@ -327,8 +327,9 @@ public:
LOG_INFO("loading tae from '%s'", sd_ctx_params->taesd_path);
if (!model_loader.init_from_file(sd_ctx_params->taesd_path, "tae.")) {
LOG_WARN("loading tae from '%s' failed", sd_ctx_params->taesd_path);
} else {
use_tae = true;
}
use_tae = true;
}
if (strlen(SAFE_STR(sd_ctx_params->embeddings_connectors_path)) > 0) {
@@ -1210,7 +1211,7 @@ public:
}
auto lora = std::make_shared<LoraModel>(lora_id,
backend_for(module),
params_backend_for(module),
backend_for(module),
lora_path,
is_high_noise ? "model.high_noise_" : "",
version);